Change Encyclo button name and macros icon
[ryzomcore.git] / nel / src / 3d / stereo_ovr.cpp
blobd924d8145686f214e7d4f13b60d34dfc898442dc
1 /**
2 * \file stereo_ovr.cpp
3 * \brief CStereoOVR
4 * \date 2013-06-25 22:22GMT
5 * \author Jan Boon (Kaetemi)
6 * CStereoOVR
7 */
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/>.
24 //
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
28 // combination.
29 //
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.
41 #ifdef HAVE_LIBOVR_02
43 #include "std3d.h"
44 #include "nel/3d/stereo_ovr.h"
46 // STL includes
47 #include <sstream>
49 // External includes
50 #define OVR_NO_STDINT
51 #include <OVR.h>
53 // NeL includes
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"
63 // Project includes
65 using namespace std;
66 // using namespace NLMISC;
68 #ifdef DEBUG_NEW
69 #define new DEBUG_NEW
70 #endif
72 namespace NL3D {
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;
79 namespace {
81 class CStereoOVRLog : public OVR::Log
83 public:
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);
97 else
98 NLMISC::INelContext::getInstance().getInfoLog()->displayNL("OVR: %s", buffer);
103 CStereoOVRLog *s_StereoOVRLog = NULL;
104 OVR::Ptr<OVR::DeviceManager> s_DeviceManager;
106 class CStereoOVRSystem
108 public:
109 ~CStereoOVRSystem()
111 Release();
114 void Init()
116 if (!s_StereoOVRLog)
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();
127 void Release()
129 if (s_DeviceManager)
131 nldebug("Release OVR");
132 s_DeviceManager->Release();
134 s_DeviceManager.Clear();
135 if (OVR::System::IsInitialized())
136 OVR::System::Destroy();
137 if (s_StereoOVRLog)
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
152 public:
153 // fixme: virtual destructor???
154 OVR::DeviceEnumerator<OVR::HMDDevice> DeviceHandle;
155 IStereoDisplay *createDevice() const
157 CStereoOVR *stereo = new CStereoOVR(this);
158 if (stereo->isDeviceCreated())
159 return stereo;
160 delete stereo;
161 return NULL;
165 class CStereoOVRDevicePtr
167 public:
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)
176 ++s_DeviceCounter;
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;
222 m_Driver = 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();
231 delete m_DevicePtr;
232 m_DevicePtr = NULL;
233 --s_DeviceCounter;
236 class CPixelProgramOVR : public CPixelProgram
238 public:
239 struct COVRIndices
241 uint LensCenter;
242 uint ScreenCenter;
243 uint Scale;
244 uint ScaleIn;
245 uint HmdWarpParam;
248 CPixelProgramOVR()
251 CSource *source = new CSource();
252 source->Profile = glsl330f;
253 source->Features.MaterialFlags = CProgramFeatures::TextureStages;
254 source->setSourcePtr(g_StereoOVR_glsl330f);
255 addSource(source);
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;
267 addSource(source);
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;
279 addSource(source);
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;
291 addSource(source);
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; }
318 private:
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();
338 if (m_PixelProgram)
340 m_Driver = driver;
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);
387 else
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;
397 return true;
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
457 case 0:
458 m_Stage += 2;
459 m_SubStage = 0;
460 // stage 2:
461 // draw interface 2d (onto render target)
462 return true;
463 case 2:
464 ++m_Stage;
465 m_SubStage = 0;
466 // stage 3:
467 // (initBloom)
468 // clear buffer
469 // draw scene left
470 return true;
471 case 3:
472 ++m_Stage;
473 m_SubStage = 0;
474 // stage 4:
475 // draw scene right
476 return true;
477 case 4:
478 ++m_Stage;
479 m_SubStage = 0;
480 // stage 5:
481 // (endBloom)
482 // draw interface 3d left
483 return true;
484 case 5:
485 ++m_Stage;
486 m_SubStage = 0;
487 // stage 6:
488 // draw interface 3d right
489 return true;
490 /*case 6:
491 ++m_Stage;
492 m_SubStage = 0;
493 // stage 7:
494 // (endInterfacesDisplayBloom)
495 // draw interface 2d left
496 return true;
497 case 7:
498 ++m_Stage;
499 m_SubStage = 0;
500 // stage 8:
501 // draw interface 2d right
502 return true;*/
503 case 6:
504 m_Stage = 0;
505 m_SubStage = 0;
506 // present
507 m_OrientationCached = false;
508 return false;
511 else
513 switch (m_Stage)
515 case 0:
516 ++m_Stage;
517 m_SubStage = 0;
518 return true;
519 case 1:
520 m_Stage = 0;
521 m_SubStage = 0;
522 return false;
525 nlerror("Invalid stage");
526 m_Stage = 0;
527 m_SubStage = 0;
528 m_OrientationCached = false;
529 return 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
555 CMatrix translate;
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());
565 else
567 // camera->setTransformMode(NL3D::UTransformable::DirectMatrix);
568 camera->setMatrix(mat);
572 bool CStereoOVR::wantClear()
574 switch (m_Stage)
576 case 3:
577 m_SubStage = 1;
578 return true;
580 return m_Driver->getPolygonMode() != UDriver::Filled;
583 bool CStereoOVR::wantScene()
585 switch (m_Stage)
587 case 3:
588 case 4:
589 m_SubStage = 2;
590 return true;
592 return m_Driver->getPolygonMode() != UDriver::Filled;
595 bool CStereoOVR::wantInterface3D()
597 switch (m_Stage)
599 case 5:
600 case 6:
601 m_SubStage = 3;
602 return true;
604 return m_Driver->getPolygonMode() != UDriver::Filled;
607 bool CStereoOVR::wantInterface2D()
609 switch (m_Stage)
611 case 2:
612 m_SubStage = 4;
613 return true;
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));
633 return true;
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);
644 return true;
647 return false;
650 void CStereoOVR::setInterfaceMatrix(const NL3D::CMatrix &matrix)
652 m_InterfaceCameraMatrix = matrix;
655 void CStereoOVR::renderGUI()
658 /*CMatrix mat;
659 mat.translate(m_InterfaceCameraMatrix.getPos());
660 CVector dir = m_InterfaceCameraMatrix.getJ();
661 dir.z = 0;
662 dir.normalize();
663 if (dir.y < 0)
664 mat.rotateZ(float(NLMISC::Pi+asin(dir.x)));
665 else
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);
678 mat.setBlend(false);
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();
691 umat.initUnlit();
692 umat.setColor(NLMISC::CRGBA::White);
693 umat.setDoubleSided(true);
694 umat.setBlend(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());
704 // user options
705 float scale = 1.0f;
706 float distance = 1.5f;
707 float offcenter = 0.75f;
709 float height = scale * distance * 2.0f;
711 uint32 winw, winh;
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;
738 vb.lock(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)
744 uint vi0 = i * 2;
745 uint vi1 = vi0 + 1;
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;
763 ib.lock(iba);
764 for (uint i = 0; i < nbQuads; ++i)
766 uint ti0 = i * 2;
767 uint ti1 = ti0 + 1;
768 uint bl = ti0;
769 uint tl = ti0 + 1;
770 uint br = ti0 + 2;
771 uint tr = ti0 + 3;
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
809 renderGUI();
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
816 renderGUI();
818 // Recycle render target
819 m_Driver->getRenderTargetManager().recycleRenderTarget(m_GUITexture);
820 m_GUITexture = NULL;
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,
873 scaleX, scaleY);
875 drvInternal->setUniform2f(IDriver::PixelProgram,
876 m_PixelProgram->ovrIndices().ScaleIn,
877 scaleInX, scaleInY);
880 drvInternal->setUniform4fv(IDriver::PixelProgram,
881 m_PixelProgram->ovrIndices().HmdWarpParam,
882 1, m_DevicePtr->HMDInfo.DistortionK);
884 m_Driver->drawQuad(m_BarrelQuadLeft, m_BarrelMat);
886 x = w;
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;
909 return true;
912 return false;
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;
922 float csys[] = {
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,
928 coordsys.set(csys);
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;
937 return finalquat;
940 /// Get GUI shift
941 void CStereoOVR::getInterface2DShift(uint cid, float &x, float &y, float distance) const
943 #if 0
945 // todo: take into account m_EyePosition
947 NLMISC::CVector vector = CVector(0.f, -distance, 0.f);
948 NLMISC::CQuat rot = getOrientation();
949 rot.invert();
950 NLMISC::CMatrix mat;
951 mat.rotate(rot);
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());
957 NLMISC::CVector ipd;
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);
966 #elif 1
968 // Alternative method
969 // todo: take into account m_EyePosition
971 NLMISC::CVector vec = CVector(0.f, -distance, 0.f);
972 NLMISC::CVector ipd;
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);
979 rot = rot * modrot;
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);
988 x = (proj.x - 0.5f);
989 y = (proj.y - 0.5f);
991 #endif
994 void CStereoOVR::setEyePosition(const NLMISC::CVector &v)
996 m_EyePosition = 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);
1007 m_Scale = s;
1010 void CStereoOVR::listDevices(std::vector<CStereoDeviceInfo> &devicesOut)
1012 s_StereoOVRSystem.Init();
1013 OVR::DeviceEnumerator<OVR::HMDDevice> devices = s_DeviceManager->EnumerateDevices<OVR::HMDDevice>();
1014 uint id = 1;
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;
1030 stringstream ser;
1031 ser << id;
1032 deviceInfoOut.Serial = ser.str(); // can't get the real serial from the sdk...
1033 devicesOut.push_back(deviceInfoOut);
1034 ++id;
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 */
1061 /* end of file */