4 * \date 2013-06-25 22:22GMT
5 * \author Jan Boon (Kaetemi)
9 // NeL - MMORPG Framework <https://wiki.ryzom.dev/>
10 // Copyright (C) 2013-2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
12 // This program is free software: you can redistribute it and/or modify
13 // it under the terms of the GNU Affero General Public License as
14 // published by the Free Software Foundation, either version 3 of the
15 // License, or (at your option) any later version.
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU Affero General Public License for more details.
22 // You should have received a copy of the GNU Affero General Public License
23 // along with this program. If not, see <http://www.gnu.org/licenses/>.
25 // Linking this library statically or dynamically with other modules
26 // is making a combined work based on this library. Thus, the terms
27 // and conditions of the GNU General Public License cover the whole
30 // As a special exception, the copyright holders of this library give
31 // you permission to link this library with the Oculus SDK to produce
32 // an executable, regardless of the license terms of the Oculus SDK,
33 // and distribute linked combinations including the two, provided that
34 // you also meet the terms and conditions of the license of the Oculus
35 // SDK. You must obey the GNU General Public License in all respects
36 // for all of the code used other than the Oculus SDK. If you modify
37 // this file, you may extend this exception to your version of the
38 // file, but you are not obligated to do so. If you do not wish to do
39 // so, delete this exception statement from your version.
44 #include "nel/3d/stereo_ovr.h"
54 // #include <nel/misc/debug.h>
55 #include "nel/3d/u_camera.h"
56 #include "nel/3d/u_driver.h"
57 #include "nel/3d/material.h"
58 #include "nel/3d/texture_bloom.h"
59 #include "nel/3d/texture_user.h"
60 #include "nel/3d/driver_user.h"
61 #include "nel/3d/u_texture.h"
66 // using namespace NLMISC;
74 extern const char *g_StereoOVR_fp40
;
75 extern const char *g_StereoOVR_arbfp1
;
76 extern const char *g_StereoOVR_ps_2_0
;
77 extern const char *g_StereoOVR_glsl330f
;
81 class CStereoOVRLog
: public OVR::Log
84 CStereoOVRLog(unsigned logMask
= OVR::LogMask_All
) : OVR::Log(logMask
)
89 virtual void LogMessageVarg(OVR::LogMessageType messageType
, const char* fmt
, va_list argList
)
91 if (NLMISC::INelContext::isContextInitialised())
93 char buffer
[MaxLogBufferMessageSize
];
94 FormatLog(buffer
, MaxLogBufferMessageSize
, messageType
, fmt
, argList
);
95 if (IsDebugMessage(messageType
))
96 NLMISC::INelContext::getInstance().getDebugLog()->displayNL("OVR: %s", buffer
);
98 NLMISC::INelContext::getInstance().getInfoLog()->displayNL("OVR: %s", buffer
);
103 CStereoOVRLog
*s_StereoOVRLog
= NULL
;
104 OVR::Ptr
<OVR::DeviceManager
> s_DeviceManager
;
106 class CStereoOVRSystem
118 nldebug("Initialize OVR");
119 s_StereoOVRLog
= new CStereoOVRLog();
121 if (!OVR::System::IsInitialized())
122 OVR::System::Init(s_StereoOVRLog
);
123 if (!s_DeviceManager
)
124 s_DeviceManager
= OVR::DeviceManager::Create();
131 nldebug("Release OVR");
132 s_DeviceManager
->Release();
134 s_DeviceManager
.Clear();
135 if (OVR::System::IsInitialized())
136 OVR::System::Destroy();
138 nldebug("Release OVR Ok");
139 delete s_StereoOVRLog
;
140 s_StereoOVRLog
= NULL
;
144 CStereoOVRSystem s_StereoOVRSystem
;
146 sint s_DeviceCounter
= 0;
150 class CStereoOVRDeviceHandle
: public IStereoDeviceFactory
153 // fixme: virtual destructor???
154 OVR::DeviceEnumerator
<OVR::HMDDevice
> DeviceHandle
;
155 IStereoDisplay
*createDevice() const
157 CStereoOVR
*stereo
= new CStereoOVR(this);
158 if (stereo
->isDeviceCreated())
165 class CStereoOVRDevicePtr
168 OVR::Ptr
<OVR::HMDDevice
> HMDDevice
;
169 OVR::Ptr
<OVR::SensorDevice
> SensorDevice
;
170 OVR::SensorFusion SensorFusion
;
171 OVR::HMDInfo HMDInfo
;
174 CStereoOVR::CStereoOVR(const CStereoOVRDeviceHandle
*handle
) : m_Stage(0), m_SubStage(0), m_OrientationCached(false), m_Driver(NULL
), m_SceneTexture(NULL
), m_GUITexture(NULL
), m_PixelProgram(NULL
), m_EyePosition(0.0f
, 0.09f
, 0.15f
), m_Scale(1.0f
)
177 m_DevicePtr
= new CStereoOVRDevicePtr();
179 OVR::DeviceEnumerator
<OVR::HMDDevice
> dh
= handle
->DeviceHandle
;
180 m_DevicePtr
->HMDDevice
= dh
.CreateDevice();
182 if (m_DevicePtr
->HMDDevice
)
184 m_DevicePtr
->HMDDevice
->GetDeviceInfo(&m_DevicePtr
->HMDInfo
);
185 nldebug("OVR: HScreenSize: %f, VScreenSize: %f", m_DevicePtr
->HMDInfo
.HScreenSize
, m_DevicePtr
->HMDInfo
.VScreenSize
);
186 nldebug("OVR: VScreenCenter: %f", m_DevicePtr
->HMDInfo
.VScreenCenter
);
187 nldebug("OVR: EyeToScreenDistance: %f", m_DevicePtr
->HMDInfo
.EyeToScreenDistance
);
188 nldebug("OVR: LensSeparationDistance: %f", m_DevicePtr
->HMDInfo
.LensSeparationDistance
);
189 nldebug("OVR: InterpupillaryDistance: %f", m_DevicePtr
->HMDInfo
.InterpupillaryDistance
);
190 nldebug("OVR: HResolution: %i, VResolution: %i", m_DevicePtr
->HMDInfo
.HResolution
, m_DevicePtr
->HMDInfo
.VResolution
);
191 nldebug("OVR: DistortionK[0]: %f, DistortionK[1]: %f", m_DevicePtr
->HMDInfo
.DistortionK
[0], m_DevicePtr
->HMDInfo
.DistortionK
[1]);
192 nldebug("OVR: DistortionK[2]: %f, DistortionK[3]: %f", m_DevicePtr
->HMDInfo
.DistortionK
[2], m_DevicePtr
->HMDInfo
.DistortionK
[3]);
193 //2013/06/26 05:31:51 DBG 17a0 snowballs_client.exe stereo_ovr.cpp 160 NL3D::CStereoOVR::CStereoOVR : OVR: HScreenSize: 0.149760, VScreenSize: 0.093600
194 //2013/06/26 05:31:51 DBG 17a0 snowballs_client.exe stereo_ovr.cpp 161 NL3D::CStereoOVR::CStereoOVR : OVR: VScreenCenter: 0.046800
195 //2013/06/26 05:31:51 DBG 17a0 snowballs_client.exe stereo_ovr.cpp 162 NL3D::CStereoOVR::CStereoOVR : OVR: EyeToScreenDistance: 0.041000
196 //2013/06/26 05:31:51 DBG 17a0 snowballs_client.exe stereo_ovr.cpp 163 NL3D::CStereoOVR::CStereoOVR : OVR: LensSeparationDistance: 0.063500
197 //2013/06/26 05:31:51 DBG 17a0 snowballs_client.exe stereo_ovr.cpp 164 NL3D::CStereoOVR::CStereoOVR : OVR: InterpupillaryDistance: 0.064000
198 //2013/06/26 05:31:51 DBG 17a0 snowballs_client.exe stereo_ovr.cpp 165 NL3D::CStereoOVR::CStereoOVR : OVR: HResolution: 1280, VResolution: 800
199 //2013/06/26 05:31:51 DBG 17a0 snowballs_client.exe stereo_ovr.cpp 166 NL3D::CStereoOVR::CStereoOVR : OVR: DistortionK[0]: 1.000000, DistortionK[1]: 0.220000
200 //2013/06/26 05:31:51 DBG 17a0 snowballs_client.exe stereo_ovr.cpp 167 NL3D::CStereoOVR::CStereoOVR : OVR: DistortionK[2]: 0.240000, DistortionK[3]: 0.000000
201 m_DevicePtr
->SensorDevice
= m_DevicePtr
->HMDDevice
->GetSensor();
202 m_DevicePtr
->SensorFusion
.AttachToSensor(m_DevicePtr
->SensorDevice
);
203 m_DevicePtr
->SensorFusion
.SetGravityEnabled(true);
204 m_DevicePtr
->SensorFusion
.SetPredictionEnabled(true);
205 m_DevicePtr
->SensorFusion
.SetYawCorrectionEnabled(true);
206 m_LeftViewport
.init(0.f
, 0.f
, 0.5f
, 1.0f
);
207 m_RightViewport
.init(0.5f
, 0.f
, 0.5f
, 1.0f
);
211 CStereoOVR::~CStereoOVR()
213 if (!m_BarrelMat
.empty())
215 m_BarrelMat
.getObjectPtr()->setTexture(0, NULL
);
216 m_Driver
->deleteMaterial(m_BarrelMat
);
219 delete m_PixelProgram
;
220 m_PixelProgram
= NULL
;
224 if (m_DevicePtr
->SensorDevice
)
225 m_DevicePtr
->SensorDevice
->Release();
226 m_DevicePtr
->SensorDevice
.Clear();
227 if (m_DevicePtr
->HMDDevice
)
228 m_DevicePtr
->HMDDevice
->Release();
229 m_DevicePtr
->HMDDevice
.Clear();
236 class CPixelProgramOVR
: public CPixelProgram
251 CSource
*source
= new CSource();
252 source
->Profile
= glsl330f
;
253 source
->Features
.MaterialFlags
= CProgramFeatures::TextureStages
;
254 source
->setSourcePtr(g_StereoOVR_glsl330f
);
258 CSource
*source
= new CSource();
259 source
->Profile
= fp40
;
260 source
->Features
.MaterialFlags
= CProgramFeatures::TextureStages
;
261 source
->setSourcePtr(g_StereoOVR_fp40
);
262 source
->ParamIndices
["cLensCenter"] = 0;
263 source
->ParamIndices
["cScreenCenter"] = 1;
264 source
->ParamIndices
["cScale"] = 2;
265 source
->ParamIndices
["cScaleIn"] = 3;
266 source
->ParamIndices
["cHmdWarpParam"] = 4;
270 CSource
*source
= new CSource();
271 source
->Profile
= arbfp1
;
272 source
->Features
.MaterialFlags
= CProgramFeatures::TextureStages
;
273 source
->setSourcePtr(g_StereoOVR_arbfp1
);
274 source
->ParamIndices
["cLensCenter"] = 0;
275 source
->ParamIndices
["cScreenCenter"] = 1;
276 source
->ParamIndices
["cScale"] = 2;
277 source
->ParamIndices
["cScaleIn"] = 3;
278 source
->ParamIndices
["cHmdWarpParam"] = 4;
282 CSource
*source
= new CSource();
283 source
->Profile
= ps_2_0
;
284 source
->Features
.MaterialFlags
= CProgramFeatures::TextureStages
;
285 source
->setSourcePtr(g_StereoOVR_ps_2_0
);
286 source
->ParamIndices
["cLensCenter"] = 0;
287 source
->ParamIndices
["cScreenCenter"] = 1;
288 source
->ParamIndices
["cScale"] = 2;
289 source
->ParamIndices
["cScaleIn"] = 3;
290 source
->ParamIndices
["cHmdWarpParam"] = 4;
295 virtual ~CPixelProgramOVR()
300 virtual void buildInfo()
302 CPixelProgram::buildInfo();
304 m_OVRIndices
.LensCenter
= getUniformIndex("cLensCenter");
305 nlassert(m_OVRIndices
.LensCenter
!= std::numeric_limits
<uint
>::max());
306 m_OVRIndices
.ScreenCenter
= getUniformIndex("cScreenCenter");
307 nlassert(m_OVRIndices
.ScreenCenter
!= std::numeric_limits
<uint
>::max());
308 m_OVRIndices
.Scale
= getUniformIndex("cScale");
309 nlassert(m_OVRIndices
.Scale
!= std::numeric_limits
<uint
>::max());
310 m_OVRIndices
.ScaleIn
= getUniformIndex("cScaleIn");
311 nlassert(m_OVRIndices
.ScaleIn
!= std::numeric_limits
<uint
>::max());
312 m_OVRIndices
.HmdWarpParam
= getUniformIndex("cHmdWarpParam");
313 nlassert(m_OVRIndices
.HmdWarpParam
!= std::numeric_limits
<uint
>::max());
316 inline const COVRIndices
&ovrIndices() { return m_OVRIndices
; }
319 COVRIndices m_OVRIndices
;
323 void CStereoOVR::setDriver(NL3D::UDriver
*driver
)
325 nlassert(!m_PixelProgram
);
327 NL3D::IDriver
*drvInternal
= (static_cast<CDriverUser
*>(driver
))->getDriver();
329 if (drvInternal
->supportBloomEffect() && drvInternal
->supportNonPowerOfTwoTextures())
331 m_PixelProgram
= new CPixelProgramOVR();
332 if (!drvInternal
->compilePixelProgram(m_PixelProgram
))
334 m_PixelProgram
.kill();
342 /*m_BarrelTex = new CTextureBloom(); // lol bloom
343 m_BarrelTex->setRenderTarget(true);
344 m_BarrelTex->setReleasable(false);
345 m_BarrelTex->resize(m_DevicePtr->HMDInfo.HResolution, m_DevicePtr->HMDInfo.VResolution);
346 m_BarrelTex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff);
347 m_BarrelTex->setWrapS(ITexture::Clamp);
348 m_BarrelTex->setWrapT(ITexture::Clamp);
349 drvInternal->setupTexture(*m_BarrelTex);
350 m_BarrelTexU = new CTextureUser(m_BarrelTex);*/
352 m_BarrelMat
= m_Driver
->createMaterial();
353 m_BarrelMat
.initUnlit();
354 m_BarrelMat
.setColor(CRGBA::White
);
355 m_BarrelMat
.setBlend (false);
356 m_BarrelMat
.setAlphaTest (false);
357 NL3D::CMaterial
*barrelMat
= m_BarrelMat
.getObjectPtr();
358 barrelMat
->setShader(NL3D::CMaterial::Normal
);
359 barrelMat
->setBlendFunc(CMaterial::one
, CMaterial::zero
);
360 barrelMat
->setZWrite(false);
361 barrelMat
->setZFunc(CMaterial::always
);
362 barrelMat
->setDoubleSided(true);
363 // barrelMat->setTexture(0, m_BarrelTex);
365 m_BarrelQuadLeft
.V0
= CVector(0.f
, 0.f
, 0.5f
);
366 m_BarrelQuadLeft
.V1
= CVector(0.5f
, 0.f
, 0.5f
);
367 m_BarrelQuadLeft
.V2
= CVector(0.5f
, 1.f
, 0.5f
);
368 m_BarrelQuadLeft
.V3
= CVector(0.f
, 1.f
, 0.5f
);
370 m_BarrelQuadRight
.V0
= CVector(0.5f
, 0.f
, 0.5f
);
371 m_BarrelQuadRight
.V1
= CVector(1.f
, 0.f
, 0.5f
);
372 m_BarrelQuadRight
.V2
= CVector(1.f
, 1.f
, 0.5f
);
373 m_BarrelQuadRight
.V3
= CVector(0.5f
, 1.f
, 0.5f
);
375 // nlassert(!drvInternal->isTextureRectangle(m_BarrelTex)); // not allowed
377 m_BarrelQuadLeft
.Uv0
= CUV(0.f
, 0.f
);
378 m_BarrelQuadLeft
.Uv1
= CUV(0.5f
, 0.f
);
379 m_BarrelQuadLeft
.Uv2
= CUV(0.5f
, 1.f
);
380 m_BarrelQuadLeft
.Uv3
= CUV(0.f
, 1.f
);
382 m_BarrelQuadRight
.Uv0
= CUV(0.5f
, 0.f
);
383 m_BarrelQuadRight
.Uv1
= CUV(1.f
, 0.f
);
384 m_BarrelQuadRight
.Uv2
= CUV(1.f
, 1.f
);
385 m_BarrelQuadRight
.Uv3
= CUV(0.5f
, 1.f
);
389 nlwarning("VR: No pixel program support");
393 bool CStereoOVR::getScreenResolution(uint
&width
, uint
&height
)
395 width
= m_DevicePtr
->HMDInfo
.HResolution
;
396 height
= m_DevicePtr
->HMDInfo
.VResolution
;
400 void CStereoOVR::initCamera(uint cid
, const NL3D::UCamera
*camera
)
402 m_OriginalFrustum
[cid
] = camera
->getFrustum();
404 float ar
= (float)m_DevicePtr
->HMDInfo
.HResolution
/ ((float)m_DevicePtr
->HMDInfo
.VResolution
* 2.0f
);
405 float fov
= 2.0f
* atanf((m_DevicePtr
->HMDInfo
.HScreenSize
* 0.5f
* 0.5f
) / (m_DevicePtr
->HMDInfo
.EyeToScreenDistance
)); //(float)NLMISC::Pi/2.f; // 2.0f * atanf(m_DevicePtr->HMDInfo.VScreenSize / 2.0f * m_DevicePtr->HMDInfo.EyeToScreenDistance);
406 m_LeftFrustum
[cid
].initPerspective(fov
, ar
, camera
->getFrustum().Near
, camera
->getFrustum().Far
);
407 m_RightFrustum
[cid
] = m_LeftFrustum
[cid
];
409 float viewCenter
= m_DevicePtr
->HMDInfo
.HScreenSize
* 0.25f
;
410 float eyeProjectionShift
= viewCenter
- m_DevicePtr
->HMDInfo
.LensSeparationDistance
* 0.5f
; // docs say LensSeparationDistance, why not InterpupillaryDistance? related to how the lenses work?
411 float projectionCenterOffset
= (eyeProjectionShift
/ (m_DevicePtr
->HMDInfo
.HScreenSize
* 0.5f
)) * (m_LeftFrustum
[cid
].Right
- m_LeftFrustum
[cid
].Left
); // used logic for this one, but it ends up being the same as the one i made up
412 nldebug("OVR: projectionCenterOffset = %f", projectionCenterOffset
);
414 m_LeftFrustum
[cid
].Left
-= projectionCenterOffset
;
415 m_LeftFrustum
[cid
].Right
-= projectionCenterOffset
;
416 m_RightFrustum
[cid
].Left
+= projectionCenterOffset
;
417 m_RightFrustum
[cid
].Right
+= projectionCenterOffset
;
419 // TODO: Clipping frustum should also take into account the IPD
420 m_ClippingFrustum
[cid
] = m_LeftFrustum
[cid
];
421 m_ClippingFrustum
[cid
].Left
= min(m_LeftFrustum
[cid
].Left
, m_RightFrustum
[cid
].Left
);
422 m_ClippingFrustum
[cid
].Right
= max(m_LeftFrustum
[cid
].Right
, m_RightFrustum
[cid
].Right
);
425 /// Get the frustum to use for clipping
426 void CStereoOVR::getClippingFrustum(uint cid
, NL3D::UCamera
*camera
) const
428 camera
->setFrustum(m_ClippingFrustum
[cid
]);
431 /// Get the original frustum of the camera
432 void CStereoOVR::getOriginalFrustum(uint cid
, NL3D::UCamera
*camera
) const
434 camera
->setFrustum(m_OriginalFrustum
[cid
]);
437 void CStereoOVR::updateCamera(uint cid
, const NL3D::UCamera
*camera
)
439 if (camera
->getFrustum().Near
!= m_LeftFrustum
[cid
].Near
440 || camera
->getFrustum().Far
!= m_LeftFrustum
[cid
].Far
)
441 CStereoOVR::initCamera(cid
, camera
);
442 m_CameraMatrix
[cid
] = camera
->getMatrix();
445 bool CStereoOVR::nextPass()
447 // Do not allow weird stuff.
448 uint32 width
, height
;
449 m_Driver
->getWindowSize(width
, height
);
450 // nlassert(width == m_DevicePtr->HMDInfo.HResolution);
451 // nlassert(height == m_DevicePtr->HMDInfo.VResolution);
453 if (m_Driver
->getPolygonMode() == UDriver::Filled
)
455 switch (m_Stage
) // Previous stage
461 // draw interface 2d (onto render target)
482 // draw interface 3d left
488 // draw interface 3d right
494 // (endInterfacesDisplayBloom)
495 // draw interface 2d left
501 // draw interface 2d right
507 m_OrientationCached
= false;
525 nlerror("Invalid stage");
528 m_OrientationCached
= false;
532 const NL3D::CViewport
&CStereoOVR::getCurrentViewport() const
534 if (m_Stage
== 2) return m_RegularViewport
;
535 else if (m_Stage
% 2) return m_LeftViewport
;
536 else return m_RightViewport
;
539 const NL3D::CFrustum
&CStereoOVR::getCurrentFrustum(uint cid
) const
541 if (m_Stage
== 2) return m_OriginalFrustum
[cid
];
542 else if (m_Stage
% 2) return m_LeftFrustum
[cid
];
543 else return m_RightFrustum
[cid
];
546 void CStereoOVR::getCurrentFrustum(uint cid
, NL3D::UCamera
*camera
) const
548 if (m_Stage
== 2) camera
->setFrustum(m_OriginalFrustum
[cid
]);
549 else if (m_Stage
% 2) camera
->setFrustum(m_LeftFrustum
[cid
]);
550 else camera
->setFrustum(m_RightFrustum
[cid
]);
553 void CStereoOVR::getCurrentMatrix(uint cid
, NL3D::UCamera
*camera
) const
556 if (m_Stage
== 2) { }
557 else if (m_Stage
% 2) translate
.translate(CVector((m_DevicePtr
->HMDInfo
.InterpupillaryDistance
* m_Scale
) * -0.5f
, 0.f
, 0.f
));
558 else translate
.translate(CVector((m_DevicePtr
->HMDInfo
.InterpupillaryDistance
* m_Scale
) * 0.5f
, 0.f
, 0.f
));
559 CMatrix mat
= m_CameraMatrix
[cid
] * translate
;
560 if (camera
->getTransformMode() == NL3D::UTransformable::RotQuat
)
562 camera
->setPos(mat
.getPos());
563 camera
->setRotQuat(mat
.getRot());
567 // camera->setTransformMode(NL3D::UTransformable::DirectMatrix);
568 camera
->setMatrix(mat
);
572 bool CStereoOVR::wantClear()
580 return m_Driver
->getPolygonMode() != UDriver::Filled
;
583 bool CStereoOVR::wantScene()
592 return m_Driver
->getPolygonMode() != UDriver::Filled
;
595 bool CStereoOVR::wantInterface3D()
604 return m_Driver
->getPolygonMode() != UDriver::Filled
;
607 bool CStereoOVR::wantInterface2D()
615 return m_Driver
->getPolygonMode() != UDriver::Filled
;
618 /// Returns non-NULL if a new render target was set
619 bool CStereoOVR::beginRenderTarget()
621 // render target always set before driver clear
622 // nlassert(m_SubStage <= 1);
624 // Set GUI render target
625 if (m_Driver
&& m_Stage
== 2 && (m_Driver
->getPolygonMode() == UDriver::Filled
))
627 nlassert(!m_GUITexture
);
628 uint32 width
, height
;
629 m_Driver
->getWindowSize(width
, height
);
630 m_GUITexture
= m_Driver
->getRenderTargetManager().getRenderTarget(width
, height
, true, UTexture::RGBA8888
);
631 static_cast<CDriverUser
*>(m_Driver
)->setRenderTarget(*m_GUITexture
);
632 m_Driver
->clearBuffers(NLMISC::CRGBA(0, 0, 0, 0));
636 // Begin 3D scene render target
637 if (m_Driver
&& m_Stage
== 3 && (m_Driver
->getPolygonMode() == UDriver::Filled
))
639 nlassert(!m_SceneTexture
);
640 uint32 width
, height
;
641 m_Driver
->getWindowSize(width
, height
); // Temporary limitation, TODO: scaling!
642 m_SceneTexture
= m_Driver
->getRenderTargetManager().getRenderTarget(width
, height
);
643 static_cast<CDriverUser
*>(m_Driver
)->setRenderTarget(*m_SceneTexture
);
650 void CStereoOVR::setInterfaceMatrix(const NL3D::CMatrix
&matrix
)
652 m_InterfaceCameraMatrix
= matrix
;
655 void CStereoOVR::renderGUI()
659 mat.translate(m_InterfaceCameraMatrix.getPos());
660 CVector dir = m_InterfaceCameraMatrix.getJ();
664 mat.rotateZ(float(NLMISC::Pi+asin(dir.x)));
666 mat.rotateZ(float(NLMISC::Pi+NLMISC::Pi-asin(dir.x)));
667 m_Driver->setModelMatrix(mat);*/
668 m_Driver
->setModelMatrix(m_InterfaceCameraMatrix
);
671 NLMISC::CLine
line(NLMISC::CVector(0, 5, 2), NLMISC::CVector(0, 5, 3));
673 NL3D::UMaterial mat
= m_Driver
->createMaterial();
674 mat
.setZWrite(false);
675 // mat.setZFunc(UMaterial::always); // Not nice!
676 mat
.setDoubleSided(true);
677 mat
.setColor(NLMISC::CRGBA::Red
);
680 m_Driver
->drawLine(line
, mat
);
682 m_Driver
->deleteMaterial(mat
);
686 nlassert(m_GUITexture
);
688 NLMISC::CQuadUV quad
;
690 NL3D::UMaterial umat
= m_Driver
->createMaterial();
692 umat
.setColor(NLMISC::CRGBA::White
);
693 umat
.setDoubleSided(true);
695 umat
.setAlphaTest(false);
696 NL3D::CMaterial
*mat
= umat
.getObjectPtr();
697 mat
->setShader(NL3D::CMaterial::Normal
);
698 mat
->setBlendFunc(CMaterial::one
, CMaterial::TBlend::invsrcalpha
);
699 mat
->setZWrite(false);
700 // mat->setZFunc(CMaterial::always); // Not nice
701 mat
->setDoubleSided(true);
702 mat
->setTexture(0, m_GUITexture
->getITexture());
706 float distance
= 1.5f
;
707 float offcenter
= 0.75f
;
709 float height
= scale
* distance
* 2.0f
;
712 m_Driver
->getWindowSize(winw
, winh
);
713 float width
= height
* (float)winw
/ (float)winh
;
715 float bottom
= -(height
* 0.5f
);
716 float top
= (height
* 0.5f
);
718 NLMISC::CQuadUV quadUV
;
719 quadUV
.V0
= CVector(-(width
* 0.5f
), distance
, -(height
* 0.5f
));
720 quadUV
.V1
= CVector((width
* 0.5f
), distance
, -(height
* 0.5f
));
721 quadUV
.V2
= CVector((width
* 0.5f
), distance
, (height
* 0.5f
));
722 quadUV
.V3
= CVector(-(width
* 0.5f
), distance
, (height
* 0.5f
));
723 quadUV
.Uv0
= CUV(0.f
, 0.f
);
724 quadUV
.Uv1
= CUV(1.f
, 0.f
);
725 quadUV
.Uv2
= CUV(1.f
, 1.f
);
726 quadUV
.Uv3
= CUV(0.f
, 1.f
);
728 const uint nbQuads
= 128;
729 static CVertexBuffer vb
;
730 static CIndexBuffer ib
;
732 vb
.setVertexFormat(CVertexBuffer::PositionFlag
| CVertexBuffer::TexCoord0Flag
);
733 vb
.setPreferredMemory(CVertexBuffer::RAMVolatile
, false);
734 vb
.setNumVertices((nbQuads
+ 1) * 2);
737 CVertexBufferReadWrite vba
;
739 float radius
= distance
+ offcenter
;
740 float relWidth
= width
/ radius
;
741 float quadWidth
= relWidth
/ (float)nbQuads
;
742 for (uint i
= 0; i
< nbQuads
+ 1; ++i
)
746 float lineH
= -(relWidth
* 0.5f
) + quadWidth
* (float)i
;
747 float lineUV
= (float)i
/ (float)(nbQuads
);
748 float left
= sin(lineH
) * radius
;
749 float forward
= cos(lineH
) * radius
;
750 vba
.setVertexCoord(vi0
, left
, forward
- offcenter
, bottom
);
751 vba
.setTexCoord(vi0
, 0, lineUV
, 0.0f
);
752 vba
.setVertexCoord(vi1
, left
, forward
- offcenter
, top
);
753 vba
.setTexCoord(vi1
, 0, lineUV
, 1.0f
);
757 ib
.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT
);
758 ib
.setPreferredMemory(CIndexBuffer::RAMVolatile
, false);
759 ib
.setNumIndexes(nbQuads
* 6);
762 CIndexBufferReadWrite iba
;
764 for (uint i
= 0; i
< nbQuads
; ++i
)
772 iba
.setTri(ti0
* 3, bl
, tl
, br
);
773 iba
.setTri(ti1
* 3, br
, tl
, tr
);
777 IDriver
*driver
= static_cast<CDriverUser
*>(m_Driver
)->getDriver();
778 // m_Driver->setPolygonMode(UDriver::Line);
779 driver
->activeVertexBuffer(vb
);
780 driver
->activeIndexBuffer(ib
);
781 driver
->renderTriangles(*umat
.getObjectPtr(), 0, nbQuads
* 2); //renderRawQuads(umat, 0, 128);
782 // m_Driver->setPolygonMode(UDriver::Filled);
784 // m_Driver->drawQuad(quadUV, umat);
786 m_Driver
->deleteMaterial(umat
);
790 /// Returns true if a render target was fully drawn
791 bool CStereoOVR::endRenderTarget()
793 // after rendering of course
794 // nlassert(m_SubStage > 1);
796 // End GUI render target
797 if (m_Driver
&& m_Stage
== 2 && (m_Driver
->getPolygonMode() == UDriver::Filled
))
799 // End GUI render target
800 nlassert(m_GUITexture
);
801 CTextureUser texNull
;
802 (static_cast<CDriverUser
*>(m_Driver
))->setRenderTarget(texNull
);
805 // End of 3D Interface pass left
806 if (m_Driver
&& m_Stage
== 5 && (m_Driver
->getPolygonMode() == UDriver::Filled
))
808 // Render 2D GUI in 3D space, assume existing camera is OK
812 // End of 3D Interface pass right
813 if (m_Driver
&& m_Stage
== 6 && (m_Driver
->getPolygonMode() == UDriver::Filled
))
815 // Render 2D GUI in 3D space, assume existing camera is OK
818 // Recycle render target
819 m_Driver
->getRenderTargetManager().recycleRenderTarget(m_GUITexture
);
823 // End 3D scene render target
824 if (m_Driver
&& m_Stage
== 6 && (m_Driver
->getPolygonMode() == UDriver::Filled
)) // set to 4 to turn off distortion of 2d gui
826 nlassert(m_SceneTexture
);
828 CTextureUser texNull
;
829 (static_cast<CDriverUser
*>(m_Driver
))->setRenderTarget(texNull
);
830 bool fogEnabled
= m_Driver
->fogEnabled();
831 m_Driver
->enableFog(false);
833 m_Driver
->setMatrixMode2D11();
834 CViewport vp
= CViewport();
835 m_Driver
->setViewport(vp
);
836 uint32 width
, height
;
837 m_Driver
->getWindowSize(width
, height
);
838 NL3D::IDriver
*drvInternal
= (static_cast<CDriverUser
*>(m_Driver
))->getDriver();
839 NL3D::CMaterial
*barrelMat
= m_BarrelMat
.getObjectPtr();
840 barrelMat
->setTexture(0, m_SceneTexture
->getITexture());
842 drvInternal
->activePixelProgram(m_PixelProgram
);
844 float w
= float(m_BarrelQuadLeft
.V1
.x
),// / float(width),
845 h
= float(m_BarrelQuadLeft
.V2
.y
),// / float(height),
846 x
= float(m_BarrelQuadLeft
.V0
.x
),/// / float(width),
847 y
= float(m_BarrelQuadLeft
.V0
.y
);// / float(height);
849 float lensOffset
= m_DevicePtr
->HMDInfo
.LensSeparationDistance
* 0.5f
;
850 float lensShift
= m_DevicePtr
->HMDInfo
.HScreenSize
* 0.25f
- lensOffset
;
851 float lensViewportShift
= 4.0f
* lensShift
/ m_DevicePtr
->HMDInfo
.HScreenSize
;
853 float lensCenterX
= x
+ (w
+ lensViewportShift
* 0.5f
) * 0.5f
;
854 float lensCenterY
= y
+ h
* 0.5f
;
855 float screenCenterX
= x
+ w
* 0.5f
;
856 float screenCenterY
= y
+ h
* 0.5f
;
857 float scaleX
= (w
/ 2);
858 float scaleY
= (h
/ 2);
859 float scaleInX
= (2 / w
);
860 float scaleInY
= (2 / h
);
863 drvInternal
->setUniform2f(IDriver::PixelProgram
,
864 m_PixelProgram
->ovrIndices().LensCenter
,
865 lensCenterX
, lensCenterY
);
867 drvInternal
->setUniform2f(IDriver::PixelProgram
,
868 m_PixelProgram
->ovrIndices().ScreenCenter
,
869 screenCenterX
, screenCenterY
);
871 drvInternal
->setUniform2f(IDriver::PixelProgram
,
872 m_PixelProgram
->ovrIndices().Scale
,
875 drvInternal
->setUniform2f(IDriver::PixelProgram
,
876 m_PixelProgram
->ovrIndices().ScaleIn
,
880 drvInternal
->setUniform4fv(IDriver::PixelProgram
,
881 m_PixelProgram
->ovrIndices().HmdWarpParam
,
882 1, m_DevicePtr
->HMDInfo
.DistortionK
);
884 m_Driver
->drawQuad(m_BarrelQuadLeft
, m_BarrelMat
);
887 lensCenterX
= x
+ (w
- lensViewportShift
* 0.5f
) * 0.5f
;
888 screenCenterX
= x
+ w
* 0.5f
;
891 drvInternal
->setUniform2f(IDriver::PixelProgram
,
892 m_PixelProgram
->ovrIndices().LensCenter
,
893 lensCenterX
, lensCenterY
);
895 drvInternal
->setUniform2f(IDriver::PixelProgram
,
896 m_PixelProgram
->ovrIndices().ScreenCenter
,
897 screenCenterX
, screenCenterY
);
900 m_Driver
->drawQuad(m_BarrelQuadRight
, m_BarrelMat
);
902 drvInternal
->activePixelProgram(NULL
);
903 m_Driver
->enableFog(fogEnabled
);
905 // Recycle render target
906 m_Driver
->getRenderTargetManager().recycleRenderTarget(m_SceneTexture
);
907 m_SceneTexture
= NULL
;
915 NLMISC::CQuat
CStereoOVR::getOrientation() const
917 if (m_OrientationCached
)
918 return m_OrientationCache
;
920 OVR::Quatf quatovr
= m_DevicePtr
->SensorFusion
.GetPredictedOrientation();
921 NLMISC::CMatrix coordsys
;
923 1.0f
, 0.0f
, 0.0f
, 0.0f
,
924 0.0f
, 0.0f
, -1.0f
, 0.0f
,
925 0.0f
, 1.0f
, 0.0f
, 0.0f
,
926 0.0f
, 0.0f
, 0.0f
, 1.0f
,
929 NLMISC::CMatrix matovr
;
930 matovr
.setRot(NLMISC::CQuat(quatovr
.x
, quatovr
.y
, quatovr
.z
, quatovr
.w
));
931 NLMISC::CMatrix matr
;
932 matr
.rotateX(NLMISC::Pi
* 0.5f
); // fix this properly... :) (note: removing this allows you to use rift while lying down)
933 NLMISC::CMatrix matnel
= matr
* matovr
* coordsys
;
934 NLMISC::CQuat finalquat
= matnel
.getRot();
935 m_OrientationCache
= finalquat
;
936 m_OrientationCached
= true;
941 void CStereoOVR::getInterface2DShift(uint cid
, float &x
, float &y
, float distance
) const
945 // todo: take into account m_EyePosition
947 NLMISC::CVector vector
= CVector(0.f
, -distance
, 0.f
);
948 NLMISC::CQuat rot
= getOrientation();
952 //if (m_Stage % 2) mat.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * -0.5f, 0.f, 0.f));
953 //else mat.translate(CVector(m_DevicePtr->HMDInfo.InterpupillaryDistance * 0.5f, 0.f, 0.f));
954 mat
.translate(vector
);
955 CVector proj
= CStereoOVR::getCurrentFrustum(cid
).project(mat
.getPos());
958 if (m_Stage
% 2) ipd
= CVector(m_DevicePtr
->HMDInfo
.InterpupillaryDistance
* -0.5f
, 0.f
, 0.f
);
959 else ipd
= CVector(m_DevicePtr
->HMDInfo
.InterpupillaryDistance
* 0.5f
, 0.f
, 0.f
);
960 CVector projipd
= CStereoOVR::getCurrentFrustum(cid
).project(vector
+ ipd
);
961 CVector projvec
= CStereoOVR::getCurrentFrustum(cid
).project(vector
);
963 x
= (proj
.x
+ projipd
.x
- projvec
.x
- 0.5f
);
964 y
= (proj
.y
+ projipd
.y
- projvec
.y
- 0.5f
);
968 // Alternative method
969 // todo: take into account m_EyePosition
971 NLMISC::CVector vec
= CVector(0.f
, -distance
, 0.f
);
973 if (m_Stage
% 2) ipd
= CVector((m_DevicePtr
->HMDInfo
.InterpupillaryDistance
* m_Scale
) * -0.5f
, 0.f
, 0.f
);
974 else ipd
= CVector((m_DevicePtr
->HMDInfo
.InterpupillaryDistance
* m_Scale
) * 0.5f
, 0.f
, 0.f
);
977 NLMISC::CQuat rot
= getOrientation();
978 NLMISC::CQuat modrot
= NLMISC::CQuat(CVector(0.f
, 1.f
, 0.f
), NLMISC::Pi
);
980 float p
= NLMISC::Pi
+ atan2f(2.0f
* ((rot
.x
* rot
.y
) + (rot
.z
* rot
.w
)), 1.0f
- 2.0f
* ((rot
.y
* rot
.y
) + (rot
.w
* rot
.w
)));
981 if (p
> NLMISC::Pi
) p
-= NLMISC::Pi
* 2.0f
;
982 float t
= -atan2f(2.0f
* ((rot
.x
* rot
.w
) + (rot
.y
* rot
.z
)), 1.0f
- 2.0f
* ((rot
.z
* rot
.z
) + (rot
.w
* rot
.w
)));// // asinf(2.0f * ((rot.x * rot.z) - (rot.w * rot.y)));
984 CVector rotshift
= CVector(p
, 0.f
, t
) * -distance
;
986 CVector proj
= CStereoOVR::getCurrentFrustum(cid
).project(vec
+ ipd
+ rotshift
);
994 void CStereoOVR::setEyePosition(const NLMISC::CVector
&v
)
999 const NLMISC::CVector
&CStereoOVR::getEyePosition() const
1001 return m_EyePosition
;
1004 void CStereoOVR::setScale(float s
)
1006 m_EyePosition
= m_EyePosition
* (s
/ m_Scale
);
1010 void CStereoOVR::listDevices(std::vector
<CStereoDeviceInfo
> &devicesOut
)
1012 s_StereoOVRSystem
.Init();
1013 OVR::DeviceEnumerator
<OVR::HMDDevice
> devices
= s_DeviceManager
->EnumerateDevices
<OVR::HMDDevice
>();
1017 CStereoDeviceInfo deviceInfoOut
;
1018 OVR::DeviceInfo deviceInfo
;
1019 if (devices
.IsAvailable())
1021 devices
.GetDeviceInfo(&deviceInfo
);
1022 CStereoOVRDeviceHandle
*handle
= new CStereoOVRDeviceHandle();
1023 deviceInfoOut
.Factory
= static_cast<IStereoDeviceFactory
*>(handle
);
1024 handle
->DeviceHandle
= devices
;
1025 deviceInfoOut
.Class
= CStereoDeviceInfo::StereoHMD
; // 1; // OVR::HMDDevice
1026 deviceInfoOut
.Library
= CStereoDeviceInfo::OVR
; // "Oculus SDK";
1027 deviceInfoOut
.Manufacturer
= deviceInfo
.Manufacturer
;
1028 deviceInfoOut
.ProductName
= deviceInfo
.ProductName
;
1029 deviceInfoOut
.AllowAuto
= true;
1032 deviceInfoOut
.Serial
= ser
.str(); // can't get the real serial from the sdk...
1033 devicesOut
.push_back(deviceInfoOut
);
1037 } while (devices
.Next());
1040 bool CStereoOVR::isLibraryInUse()
1042 nlassert(s_DeviceCounter
>= 0);
1043 return s_DeviceCounter
> 0;
1046 void CStereoOVR::releaseLibrary()
1048 nlassert(s_DeviceCounter
== 0);
1049 s_StereoOVRSystem
.Release();
1052 bool CStereoOVR::isDeviceCreated()
1054 return m_DevicePtr
->HMDDevice
!= NULL
;
1057 } /* namespace NL3D */
1059 #endif /* HAVE_LIBOVR */