Add stereo debugger for visually comparing rendered frames, ref #43

This commit is contained in:
kaetemi 2013-07-04 00:09:10 +02:00
parent 7cd5a94e4c
commit 834f478700
11 changed files with 576 additions and 6 deletions

View file

@ -0,0 +1,130 @@
/**
* \file stereo_debugger.h
* \brief CStereoDebugger
* \date 2013-07-03 20:17GMT
* \author Jan Boon (Kaetemi)
* CStereoDebugger
*/
/*
* Copyright (C) 2013 by authors
*
* This file is part of NL3D.
* NL3D is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* NL3D is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
* Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with NL3D. If not, see
* <http://www.gnu.org/licenses/>.
*/
#if !FINAL_VERSION
#ifndef NL3D_STEREO_DEBUGGER_H
#define NL3D_STEREO_DEBUGGER_H
#include <nel/misc/types_nl.h>
// STL includes
// NeL includes
#include <nel/misc/smart_ptr.h>
#include <nel/misc/geom_ext.h>
// Project includes
#include <nel/3d/stereo_display.h>
#include <nel/3d/frustum.h>
#include <nel/3d/viewport.h>
#include <nel/3d/u_material.h>
#define NL_STEREO_MAX_USER_CAMERAS 8
namespace NL3D {
class ITexture;
class CTextureUser;
class CPixelProgram;
/**
* \brief CStereoDebugger
* \date 2013-07-03 20:17GMT
* \author Jan Boon (Kaetemi)
* CStereoDebugger
*/
class CStereoDebugger : public IStereoDisplay
{
public:
CStereoDebugger();
virtual ~CStereoDebugger();
/// Sets driver and generates necessary render targets
virtual void setDriver(NL3D::UDriver *driver);
/// Gets the required screen resolution for this device
virtual bool getScreenResolution(uint &width, uint &height);
/// Set latest camera position etcetera
virtual void updateCamera(uint cid, const NL3D::UCamera *camera);
/// Get the frustum to use for clipping
virtual void getClippingFrustum(uint cid, NL3D::UCamera *camera) const;
/// Is there a next pass
virtual bool nextPass();
/// Gets the current viewport
virtual const NL3D::CViewport &getCurrentViewport() const;
/// Gets the current camera frustum
virtual const NL3D::CFrustum &getCurrentFrustum(uint cid) const;
/// Gets the current camera frustum
virtual void getCurrentFrustum(uint cid, NL3D::UCamera *camera) const;
/// Gets the current camera matrix
virtual void getCurrentMatrix(uint cid, NL3D::UCamera *camera) const;
/// At the start of a new render target
virtual bool wantClear();
/// The 3D scene
virtual bool wantScene();
/// Interface within the 3D scene
virtual bool wantInterface3D();
/// 2D Interface
virtual bool wantInterface2D();
/// Returns true if a new render target was set, always fase if not using render targets
virtual bool beginRenderTarget();
/// Returns true if a render target was fully drawn, always false if not using render targets
virtual bool endRenderTarget();
static void listDevices(std::vector<CStereoDeviceInfo> &devicesOut);
private:
UDriver *m_Driver;
int m_Stage;
int m_SubStage;
CViewport m_LeftViewport;
CViewport m_RightViewport;
CFrustum m_Frustum[NL_STEREO_MAX_USER_CAMERAS];
CMatrix m_CameraMatrix[NL_STEREO_MAX_USER_CAMERAS];
NLMISC::CSmartPtr<NL3D::ITexture> m_LeftTex;
NL3D::CTextureUser *m_LeftTexU;
NLMISC::CSmartPtr<NL3D::ITexture> m_RightTex;
NL3D::CTextureUser *m_RightTexU;
NL3D::UMaterial m_Mat;
NLMISC::CQuadUV m_QuadUV;
CPixelProgram *m_PixelProgram;
}; /* class CStereoDebugger */
} /* namespace NL3D */
#endif /* #ifndef NL3D_STEREO_DEBUGGER_H */
#endif /* #if !FINAL_VERSION */
/* end of file */

View file

@ -95,7 +95,7 @@ public:
virtual void setDriver(NL3D::UDriver *driver) = 0;
/// Gets the required screen resolution for this device
virtual void getScreenResolution(uint &width, uint &height) = 0;
virtual bool getScreenResolution(uint &width, uint &height) = 0;
/// Set latest camera position etcetera
virtual void updateCamera(uint cid, const NL3D::UCamera *camera) = 0;
/// Get the frustum to use for clipping

View file

@ -83,7 +83,7 @@ public:
virtual void setDriver(NL3D::UDriver *driver);
/// Gets the required screen resolution for this device
virtual void getScreenResolution(uint &width, uint &height);
virtual bool getScreenResolution(uint &width, uint &height);
/// Set latest camera position etcetera
virtual void updateCamera(uint cid, const NL3D::UCamera *camera);
/// Get the frustum to use for clipping

View file

@ -164,7 +164,9 @@ SOURCE_GROUP(Driver FILES
vertex_program.cpp
../../include/nel/3d/vertex_program.h
vertex_program_parse.cpp
../../include/nel/3d/vertex_program_parse.h)
../../include/nel/3d/vertex_program_parse.h
pixel_program.cpp
../../include/nel/3d/pixel_program.h)
SOURCE_GROUP(Font FILES
computed_string.cpp
@ -693,7 +695,9 @@ SOURCE_GROUP(Stereo FILES
../../include/nel/3d/stereo_hmd.h
stereo_ovr.cpp
stereo_ovr_fp.cpp
../../include/nel/3d/stereo_ovr.h)
../../include/nel/3d/stereo_ovr.h
stereo_debugger.cpp
../../include/nel/3d/stereo_debugger.h)
NL_TARGET_LIB(nel3d ${HEADERS} ${SRC})

View file

@ -0,0 +1,350 @@
/**
* \file stereo_debugger.cpp
* \brief CStereoDebugger
* \date 2013-07-03 20:17GMT
* \author Jan Boon (Kaetemi)
* CStereoDebugger
*/
/*
* Copyright (C) 2013 by authors
*
* This file is part of NL3D.
* NL3D is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* NL3D is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
* Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with NL3D. If not, see
* <http://www.gnu.org/licenses/>.
*/
#if !FINAL_VERSION
#include <nel/misc/types_nl.h>
#include <nel/3d/stereo_debugger.h>
// STL includes
// NeL includes
// #include <nel/misc/debug.h>
// Project includes
#include <nel/3d/u_camera.h>
#include <nel/3d/u_driver.h>
#include <nel/3d/material.h>
#include <nel/3d/texture_bloom.h>
#include <nel/3d/texture_user.h>
#include <nel/3d/driver_user.h>
#include <nel/3d/u_texture.h>
using namespace std;
// using namespace NLMISC;
namespace NL3D {
namespace {
const char *a_arbfp1 =
"!!ARBfp1.0\n"
"PARAM c[1] = { { 1, 0, 0.5 } };\n"
"TEMP R0;\n"
"TEMP R1;\n"
"TEMP R2;\n"
"TEX R0, fragment.texcoord[0], texture[0], 2D;\n"
"TEX R1, fragment.texcoord[0], texture[1], 2D;\n"
"ADD R2, R0, -R1;\n"
"ADD R1, R0, R1;\n"
"MUL R1, R1, c[0].z;\n"
"ABS R2, R2;\n"
"CMP R2, -R2, c[0].x, c[0].y;\n"
"ADD_SAT R2.x, R2, R2.y;\n"
"ADD_SAT R2.x, R2, R2.z;\n"
"ADD_SAT R2.x, R2, R2.w;\n"
"ABS R2.x, R2;\n"
"CMP R2.x, -R2, c[0].y, c[0];\n"
"ABS R0.x, R2;\n"
"CMP R2.x, -R0, c[0].y, c[0];\n"
"MOV R0.xzw, R1;\n"
"MAD R0.y, R1, c[0].z, c[0].z;\n"
"CMP R0, -R2.x, R1, R0;\n"
"MAD R1.x, R0, c[0].z, c[0].z;\n"
"CMP result.color.x, -R2, R1, R0;\n"
"MOV result.color.yzw, R0;\n"
"END\n";
class CStereoDebuggerFactory : public IStereoDeviceFactory
{
public:
IStereoDisplay *createDevice() const
{
return new CStereoDebugger();
}
};
} /* anonymous namespace */
CStereoDebugger::CStereoDebugger() : m_Driver(NULL), m_Stage(0), m_SubStage(0), m_LeftTexU(NULL), m_RightTexU(NULL), m_PixelProgram(NULL)
{
}
CStereoDebugger::~CStereoDebugger()
{
if (!m_Mat.empty())
{
m_Mat.getObjectPtr()->setTexture(0, NULL);
m_Mat.getObjectPtr()->setTexture(1, NULL);
m_Driver->deleteMaterial(m_Mat);
}
delete m_LeftTexU;
m_LeftTexU = NULL;
m_LeftTex = NULL; // CSmartPtr
delete m_RightTexU;
m_RightTexU = NULL;
m_RightTex = NULL; // CSmartPtr
delete m_PixelProgram;
m_PixelProgram = NULL;
m_Driver = NULL;
}
/// Sets driver and generates necessary render targets
void CStereoDebugger::setDriver(NL3D::UDriver *driver)
{
nlassert(!m_PixelProgram);
NL3D::IDriver *drvInternal = (static_cast<CDriverUser *>(driver))->getDriver();
/*if (drvInternal->supportPixelProgram(CPixelProgram::fp40) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures())
{
nldebug("VR: fp40");
m_PixelProgram = new CPixelProgram(a_fp40);
}
else*/ if (drvInternal->supportPixelProgram(CPixelProgram::arbfp1) && drvInternal->supportBloomEffect() && drvInternal->supportNonPowerOfTwoTextures())
{
nldebug("VR: arbfp1");
m_PixelProgram = new CPixelProgram(a_arbfp1);
}
/*else if (drvInternal->supportPixelProgram(CPixelProgram::ps_2_0))
{
nldebug("VR: ps_2_0");
m_PixelProgram = new CPixelProgram(a_ps_2_0);
}*/
if (m_PixelProgram)
{
m_Driver = driver;
// todo: handle reso change!
uint32 width, height;
driver->getWindowSize(width, height);
m_LeftTex = new CTextureBloom();
m_LeftTex->setRenderTarget(true);
m_LeftTex->setReleasable(false);
m_LeftTex->resize(width, height);
m_LeftTex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff);
m_LeftTex->setWrapS(ITexture::Clamp);
m_LeftTex->setWrapT(ITexture::Clamp);
drvInternal->setupTexture(*m_LeftTex);
m_LeftTexU = new CTextureUser(m_LeftTex);
nlassert(!drvInternal->isTextureRectangle(m_LeftTex)); // not allowed
m_RightTex = new CTextureBloom();
m_RightTex->setRenderTarget(true);
m_RightTex->setReleasable(false);
m_RightTex->resize(width, height);
m_RightTex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff);
m_RightTex->setWrapS(ITexture::Clamp);
m_RightTex->setWrapT(ITexture::Clamp);
drvInternal->setupTexture(*m_RightTex);
m_RightTexU = new CTextureUser(m_RightTex);
nlassert(!drvInternal->isTextureRectangle(m_RightTex)); // not allowed
m_Mat = m_Driver->createMaterial();
m_Mat.initUnlit();
m_Mat.setColor(CRGBA::White);
m_Mat.setBlend (false);
m_Mat.setAlphaTest (false);
NL3D::CMaterial *mat = m_Mat.getObjectPtr();
mat->setShader(NL3D::CMaterial::PostProcessing);
mat->setBlendFunc(CMaterial::one, CMaterial::zero);
mat->setZWrite(false);
mat->setZFunc(CMaterial::always);
mat->setDoubleSided(true);
mat->setTexture(0, m_LeftTex);
mat->setTexture(1, m_RightTex);
m_QuadUV.V0 = CVector(0.f, 0.f, 0.5f);
m_QuadUV.V1 = CVector(1.f, 0.f, 0.5f);
m_QuadUV.V2 = CVector(1.f, 1.f, 0.5f);
m_QuadUV.V3 = CVector(0.f, 1.f, 0.5f);
m_QuadUV.Uv0 = CUV(0.f, 0.f);
m_QuadUV.Uv1 = CUV(1.f, 0.f);
m_QuadUV.Uv2 = CUV(1.f, 1.f);
m_QuadUV.Uv3 = CUV(0.f, 1.f);
}
}
/// Gets the required screen resolution for this device
bool CStereoDebugger::getScreenResolution(uint &width, uint &height)
{
return false;
}
/// Set latest camera position etcetera
void CStereoDebugger::updateCamera(uint cid, const NL3D::UCamera *camera)
{
m_Frustum[cid] = camera->getFrustum();
}
/// Get the frustum to use for clipping
void CStereoDebugger::getClippingFrustum(uint cid, NL3D::UCamera *camera) const
{
// do nothing
}
/// Is there a next pass
bool CStereoDebugger::nextPass()
{
switch (m_Stage)
{
case 0:
++m_Stage;
m_SubStage = 0;
return true;
case 1:
++m_Stage;
m_SubStage = 0;
return true;
case 2:
m_Stage = 0;
m_SubStage = 0;
return false;
}
}
/// Gets the current viewport
const NL3D::CViewport &CStereoDebugger::getCurrentViewport() const
{
if (m_Stage % 2) return m_LeftViewport;
else return m_RightViewport;
}
/// Gets the current camera frustum
const NL3D::CFrustum &CStereoDebugger::getCurrentFrustum(uint cid) const
{
return m_Frustum[cid];
}
/// Gets the current camera frustum
void CStereoDebugger::getCurrentFrustum(uint cid, NL3D::UCamera *camera) const
{
// do nothing
}
/// Gets the current camera matrix
void CStereoDebugger::getCurrentMatrix(uint cid, NL3D::UCamera *camera) const
{
// do nothing
}
/// At the start of a new render target
bool CStereoDebugger::wantClear()
{
m_SubStage = 1;
return true;
}
/// The 3D scene
bool CStereoDebugger::wantScene()
{
m_SubStage = 2;
return true;
}
/// Interface within the 3D scene
bool CStereoDebugger::wantInterface3D()
{
m_SubStage = 3;
return true;
}
/// 2D Interface
bool CStereoDebugger::wantInterface2D()
{
m_SubStage = 4;
return true;
}
/// Returns true if a new render target was set, always fase if not using render targets
bool CStereoDebugger::beginRenderTarget()
{
if (m_Driver)
{
if (m_Stage % 2) static_cast<CDriverUser *>(m_Driver)->setRenderTarget(*m_RightTexU, 0, 0, 0, 0);
else static_cast<CDriverUser *>(m_Driver)->setRenderTarget(*m_LeftTexU, 0, 0, 0, 0);
return true;
}
return false;
}
/// Returns true if a render target was fully drawn, always false if not using render targets
bool CStereoDebugger::endRenderTarget()
{
if (m_Driver)
{
CTextureUser cu;
(static_cast<CDriverUser *>(m_Driver))->setRenderTarget(cu);
bool fogEnabled = m_Driver->fogEnabled();
m_Driver->enableFog(false);
m_Driver->setMatrixMode2D11();
CViewport vp = CViewport();
m_Driver->setViewport(vp);
uint32 width, height;
NL3D::IDriver *drvInternal = (static_cast<CDriverUser *>(m_Driver))->getDriver();
NL3D::CMaterial *mat = m_Mat.getObjectPtr();
mat->setTexture(0, m_LeftTex);
mat->setTexture(1, m_RightTex);
drvInternal->activePixelProgram(m_PixelProgram);
m_Driver->drawQuad(m_QuadUV, m_Mat);
drvInternal->activePixelProgram(NULL);
m_Driver->enableFog(fogEnabled);
return true;
}
return false;
}
void CStereoDebugger::listDevices(std::vector<CStereoDeviceInfo> &devicesOut)
{
CStereoDeviceInfo devInfo;
devInfo.Factory = new CStereoDebuggerFactory();
devInfo.Library = CStereoDeviceInfo::NeL3D;
devInfo.Class = CStereoDeviceInfo::StereoDisplay;
devInfo.Manufacturer = "NeL";
devInfo.ProductName = "Stereo Debugger";
devInfo.Serial = "NL-3D-DEBUG";
devicesOut.push_back(devInfo);
}
} /* namespace NL3D */
#endif /* #if !FINAL_VERSION */
/* end of file */

View file

@ -35,6 +35,7 @@
// Project includes
#include <nel/3d/stereo_ovr.h>
#include <nel/3d/stereo_debugger.h>
using namespace std;
// using namespace NLMISC;
@ -75,6 +76,9 @@ const char *IStereoDisplay::getLibraryName(CStereoDeviceInfo::TStereoDeviceLibra
void IStereoDisplay::listDevices(std::vector<CStereoDeviceInfo> &devicesOut)
{
CStereoOVR::listDevices(devicesOut);
#if !FINAL_VERSION
CStereoDebugger::listDevices(devicesOut);
#endif
}
IStereoDisplay *IStereoDisplay::createDevice(const CStereoDeviceInfo &deviceInfo)

View file

@ -145,6 +145,7 @@ sint s_DeviceCounter = 0;
class CStereoOVRDeviceHandle : public IStereoDeviceFactory
{
public:
// fixme: virtual destructor???
OVR::DeviceEnumerator<OVR::HMDDevice> DeviceHandle;
IStereoDisplay *createDevice() const
{
@ -311,10 +312,11 @@ void CStereoOVR::setDriver(NL3D::UDriver *driver)
}
}
void CStereoOVR::getScreenResolution(uint &width, uint &height)
bool CStereoOVR::getScreenResolution(uint &width, uint &height)
{
width = m_DevicePtr->HMDInfo.HResolution;
height = m_DevicePtr->HMDInfo.VResolution;
return true;
}
void CStereoOVR::initCamera(uint cid, const NL3D::UCamera *camera)

View file

@ -3,6 +3,7 @@
Filename : stereo_ovf_fp.cpp
Content : Barrel fragment program compiled to a blob of assembly
Created : July 01, 2013
Modified by : Jan Boon (Kaetemi)
Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.

View file

@ -0,0 +1,54 @@
/************************************************************************************
Filename : pp_oculus_vr.cg
Content : Barrel fragment program ported from Oculus SDK Samples to Cg
Created : July 01, 2013
Modified by : Jan Boon (Kaetemi)
Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************************/
void pp_oculus_vr(
// Per fragment parameters
float2 texCoord : TEXCOORD0,
// Fragment program constants
uniform float2 cLensCenter,
uniform float2 cScreenCenter,
uniform float2 cScale,
uniform float2 cScaleIn,
uniform float4 cHmdWarpParam,
uniform sampler2D cTex0 : TEX0,
// Output color
out float4 oCol : COLOR)
{
float2 theta = (texCoord - cLensCenter) * cScaleIn; // Scales to [-1, 1]
float rSq = theta.x * theta.x + theta.y * theta.y;
float2 theta1 = theta * (cHmdWarpParam.x + cHmdWarpParam.y * rSq +
cHmdWarpParam.z * rSq * rSq + cHmdWarpParam.w * rSq * rSq * rSq);
float2 tc = cLensCenter + cScale * theta1;
if (!all(equal(clamp(tc, cScreenCenter-float2(0.25,0.5), cScreenCenter+float2(0.25,0.5)), tc)))
{
oCol = float4(0, 0, 0, 0);
}
else
{
oCol = tex2D(cTex0, tc);
}
}

View file

@ -0,0 +1,25 @@
void pp_stereo_debug(
// Per fragment parameters
float2 texCoord : TEXCOORD0,
// Fragment program constants
uniform sampler2D cTex0 : TEX0,
uniform sampler2D cTex1 : TEX1,
// Output color
out float4 oCol : COLOR)
{
float4 t1 = tex2D(cTex0, texCoord);
float4 t2 = tex2D(cTex1, texCoord);
if (!any(t1 - t2))
{
oCol = (t1 * 0.5) + (t2 * 0.5);
oCol.g = 0.5 + (oCol.g * 0.5);
}
else
{
oCol = (t1 * 0.5) + (t2 * 0.5);
oCol.r = 0.5 + (oCol.r * 0.5);
}
}

View file

@ -124,8 +124,8 @@ void initCamera()
StereoDisplay->setDriver(Driver); // move after driver creation, move stereodisplay before driver creation
}
}
IStereoDisplay::releaseUnusedLibraries();
}
IStereoDisplay::releaseUnusedLibraries();
// Set up directly the camera
Camera = Scene->getCam();