Virtual Reality.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /******************************************************************************
  2. Rendering to VR works in a following way:
  3. -3D is rendered into RenderTexture
  4. -2D is drawn into GuiTexture (assumed to have pixel_aspect=1)
  5. -the textures are submitted to HMD ('ovr_SubmitFrame' OculusRift, 'Submit' OpenVR)
  6. -the textures are also drawn to the System Window (without any warp shaders):
  7. -RenderTexture is drawn with FIT_FILL for the Left Eye only (with correct scale and position so that the eye focus is at the window center)
  8. - GuiTexture is drawn with FIT_FULL
  9. Since VR GuiTexture can be set to a custom size, most likely it will be different than System Window size/resolution.
  10. Because of that, most display members are set based on VR GuiTexture, and not Window size, this includes:
  11. D.w, D.h, D.w2, D.h2, D.pixelToScreen, D.pixelToScreenSize, D.screenToPixel, D.screenToPixelSize, ..
  12. But DOES NOT INCLUDE:
  13. D.mode, D.res, D.resW, D.resH
  14. RenderTexture for simplicity, uses the same D.w and D.h values as GuiTexture.
  15. In both cases range (-D.w, -D.h) .. (D.w, D.h) covers the entire textures.
  16. However since the textures are not displayed with the same scale, visible screen position in one texture is not the same as in the other.
  17. To compensate for different texture sizes, 'D.viewFovTan' is calculated differently for RenderTexture/GuiTexture,
  18. to do correct mapping between 2D<->3D space.
  19. Just before doing drawing to the System Window, D.w, D.h are recalculated based on window size,
  20. to maintain correct aspect. Once window drawing is finished, D.w D.h are restored to the VR GuiTexture values.
  21. D.windowPixelToScreen is used to convert pixel in the System Window, to screen position on VR GuiTexture.
  22. This is used for converting mouse/touch pointer positions.
  23. To detect if we're rendering into VR, use:
  24. VR.active() - this remains true, as long as VR is connected
  25. Renderer._stereo - this is set at the start of rendering, based on viewport being full and VR Active
  26. /******************************************************************************/
  27. #include "stdafx.h"
  28. /******************************************************************************/
  29. namespace EE{
  30. /******************************************************************************/
  31. static struct VirtualRealityDummyApi : VirtualRealityApi
  32. {
  33. virtual Bool init()override
  34. {
  35. Set(VR._name, "Dummy VR");
  36. VR._res.set(1280, 720);
  37. VR._refresh =60;
  38. VR._eye_dist=0.064f;
  39. VR.setFOVTan(1.39f, 1.24f, 1.47f, 1.47f);
  40. _active=true; VR.connected(); // set active before calling connected
  41. return true;
  42. }
  43. virtual void shut()override
  44. {
  45. VR.delImages(); // !! need to call 'VR.delImages' and not 'T.delImages' !!
  46. _active=false; VR.disconnected(); // set active before calling connected
  47. }
  48. virtual Bool active ()C override {return _active;}
  49. virtual Matrix matrixCur ()C override {return MatrixIdentity;}
  50. virtual void recenter () override {}
  51. virtual void changedGuiDepth() override {}
  52. virtual void changedGuiSize () override {}
  53. virtual void update () override {}
  54. virtual void draw () override {}
  55. virtual void delImages()override {_render.del(); _gui.del();}
  56. virtual Bool createGuiImage ()override {return _gui .createTry(VR.guiRes().x, VR.guiRes().y, 1, IMAGE_R8G8B8A8, IMAGE_RT, 1);}
  57. virtual Bool createRenderImage ()override {return _render.createTry(1280, 720, 1, IMAGE_R8G8B8A8, IMAGE_RT, 1);}
  58. virtual ImageRC* getNewRender ()override {return _render.is() ? &_render : null;}
  59. virtual ImageRC* getNewGui ()override {return _gui .is() ? &_gui : null;}
  60. virtual ImageRC* getLastRender()override {return &_render;}
  61. virtual ImageRC* getLastGui ()override {return &_gui ;}
  62. private:
  63. Bool _active;
  64. ImageRC _render, _gui;
  65. }VirtualRealityDummy;
  66. /******************************************************************************/
  67. VirtualReality VR;
  68. static VirtualRealityApi VrNull;
  69. /******************************************************************************/
  70. static void ClampTexSize(Int &w, Int &h)
  71. {
  72. if(D.maxTexSize()>0)
  73. {
  74. if(w>D.maxTexSize())
  75. {
  76. h=RoundPos(Flt(D.maxTexSize())/w*h);
  77. w=D.maxTexSize();
  78. }
  79. if(h>D.maxTexSize())
  80. {
  81. w=RoundPos(Flt(D.maxTexSize())/h*w);
  82. h=D.maxTexSize();
  83. }
  84. }
  85. MAX(w, 1);
  86. MAX(h, 1);
  87. }
  88. /******************************************************************************/
  89. VirtualReality::VirtualReality()
  90. {
  91. draw_2d=true;
  92. _has_render=false;
  93. _name[0]='\0';
  94. _eye_dist=0.064f;
  95. _density=1;
  96. _refresh=60;
  97. _gui_depth=_gui_size=1;
  98. _res.zero();
  99. _gui_res.set(1024, 1024);
  100. _fov=DegToRad(70);
  101. _matrix.identity();
  102. _left .identity();
  103. _right .identity();
  104. _adapter_id=0;
  105. _api=&VrNull; // !! '_api' may never be null !!
  106. #if 0 // not needed at start, also 'VR' is global so it will always be set to zero
  107. _left_eye_tex_aspect=1;
  108. _left_eye_tex_rect .zero();
  109. #endif
  110. }
  111. void VirtualReality::DummyInit() {VR.init(VirtualRealityDummy);}
  112. Bool VirtualReality::init(VirtualRealityApi &api)
  113. {
  114. if(_api==&api)return true; // already using this API
  115. shut(); // need to fully shut down existing API first
  116. _api=&api; // need to set this first in case 'init' will call 'VR._api' methods
  117. if(api.init()) // if API initialized OK
  118. {
  119. // put to API what was set earlier
  120. api.changedGuiDepth();
  121. api.changedGuiSize ();
  122. return true;
  123. }
  124. _api=&VrNull; return false; // if failed then revert back to null
  125. }
  126. void VirtualReality::shut()
  127. {
  128. if(_api!=&VrNull)
  129. {
  130. _api->shut();
  131. _api=&VrNull; // !! '_api' may never be null !!
  132. _adapter_id=0; // disable forcing VR adapter
  133. }
  134. }
  135. Bool VirtualReality::connected()
  136. {
  137. if(createImages())
  138. {
  139. Ms.resetCursor();
  140. Frustum.set();
  141. #if GL // for OpenGL we need to adjust screen synchronization, DirectX can control sync real-time in the 'Present' function
  142. D.setSync();
  143. #endif
  144. return true;
  145. }
  146. return false;
  147. }
  148. void VirtualReality::disconnected()
  149. {
  150. if(D.created())
  151. {
  152. Renderer.setMain();
  153. D.aspectRatioEx();
  154. #if GL // for OpenGL we need to adjust screen synchronization, DirectX can control sync real-time in the 'Present' function
  155. D.setSync();
  156. #endif
  157. }
  158. Ms.clipUpdate(); // call 'clipUpdate' after 'aspectRatioEx', as display size affects mouse clip rect
  159. Ms.resetCursor();
  160. Frustum.set();
  161. }
  162. /******************************************************************************/
  163. Bool VirtualReality::active ()C {return _api->active ();}
  164. Matrix VirtualReality::matrixCur()C {return _api->matrixCur();}
  165. void VirtualReality::recenter () { _api->recenter ();}
  166. void VirtualReality::update () { _api->update ();}
  167. void VirtualReality::draw () { _api->draw ();}
  168. void VirtualReality::drawMain () // remember that we're rendering left eye, so it will be offsetted by eye distance
  169. {
  170. if(Image *render=getLastRender())
  171. {
  172. ALPHA_MODE alpha=D.alpha(ALPHA_NONE);
  173. #if DEBUG && 1
  174. if(Kb.b(KB_NPMUL)){D.clearCol(); render->drawFs(FIT_FULL, FILTER_LINEAR);}else
  175. #endif
  176. render->drawPart(Fit(_left_eye_tex_aspect, D.rect(), FIT_FILL), _left_eye_tex_rect);
  177. D.alpha(alpha);
  178. }else D.clearCol(); // clear because gui may not cover the entire window
  179. if(draw_2d)if(Image *image=getLastGui())
  180. {
  181. Rect screen=image->fit(D.rect(), FIT_FULL);
  182. image->draw(screen);
  183. }
  184. }
  185. /******************************************************************************/
  186. void VirtualReality::setFOVTan(Flt left, Flt right, Flt up, Flt down)
  187. {
  188. VR._fov.set(2*Atan(Avg(left, right)),
  189. 2*Atan(Avg(up , down )));
  190. Flt proj_center =left/(left+right),
  191. proj_center_offset=Lerp(-1.0f, 1.0f, proj_center);
  192. ProjMatrixEyeOffset[0]= proj_center_offset;
  193. ProjMatrixEyeOffset[1]=-proj_center_offset;
  194. VR._left_eye_tex_aspect=Min(left, right)/Min(up, down);
  195. VR._left_eye_tex_rect.setC(proj_center*0.5f, 0.5f, Min(proj_center, 1-proj_center), 1); // calculate width based on how much we can go to the left and right edge of the texture
  196. }
  197. /******************************************************************************/
  198. VirtualReality& VirtualReality::pixelDensity(Flt density)
  199. {
  200. Clamp(density, 0, 2); if(T._density!=density){T._density=density; createRenderImage();} return T;
  201. }
  202. VirtualReality& VirtualReality::guiRes(Int w, Int h)
  203. {
  204. ClampTexSize(w, h); if(_gui_res.x!=w || _gui_res.y!=h){_gui_res.set(w, h); createGuiImage();} return T;
  205. }
  206. VirtualReality& VirtualReality::guiDepth(Flt depth)
  207. {
  208. MAX(depth, 0); if(_gui_depth!=depth){_gui_depth=depth; _api->changedGuiDepth();} return T;
  209. }
  210. VirtualReality& VirtualReality::guiSize(Flt size)
  211. {
  212. MAX(size, 0); if(_gui_size!=size){_gui_size=size; _api->changedGuiSize(); D.setViewFovTan();} return T;
  213. }
  214. void VirtualReality::delImages()
  215. {
  216. _has_render=false; _api->delImages();
  217. }
  218. Bool VirtualReality::createGuiImage()
  219. {
  220. if(D.created() && active())
  221. {
  222. ClampTexSize(_gui_res.x, _gui_res.y);
  223. if(_api->createGuiImage())
  224. {
  225. SyncLockerEx locker(D._lock);
  226. Renderer.setMain();
  227. D.aspectRatioEx(); Ms.clipUpdate(); // call in this order, as display size affects mouse clip rect
  228. return true;
  229. }
  230. shut(); return false;
  231. }
  232. return true;
  233. }
  234. Bool VirtualReality::createRenderImage()
  235. {
  236. if(D.created() && active())
  237. {
  238. if(_api->createRenderImage())
  239. {
  240. _has_render=false;
  241. return true;
  242. }
  243. shut(); return false;
  244. }
  245. return true;
  246. }
  247. Bool VirtualReality::createImages()
  248. {
  249. return createRenderImage() && createGuiImage();
  250. }
  251. ImageRC* VirtualReality::getNewRender () { if(ImageRC *image=_api->getNewRender ()){_has_render=true; return image;} return null;}
  252. ImageRC* VirtualReality::getNewGui () {return _api->getNewGui () ;}
  253. ImageRC* VirtualReality::getLastRender() {return _has_render ? _api->getLastRender() : null;}
  254. ImageRC* VirtualReality::getLastGui () {return _api->getLastGui () ;}
  255. /******************************************************************************/
  256. }
  257. /******************************************************************************/