]> git.tdb.fi Git - libs/vr.git/blob - source/openvr/openvrsystem.cpp
Fix memory leaks
[libs/vr.git] / source / openvr / openvrsystem.cpp
1 #include <openvr.h>
2 #include <msp/core/maputils.h>
3 #include <msp/gl/meshbuilder.h>
4 #include <msp/gl/programbuilder.h>
5 #include <msp/vr/stereoview.h>
6 #include "openvrcontroller_private.h"
7 #include "openvrsystem.h"
8
9 using namespace std;
10
11 namespace {
12
13 Msp::GL::Matrix convert_matrix(const vr::HmdMatrix34_t &m)
14 {
15         Msp::GL::Matrix result;
16         for(unsigned i=0; i<3; ++i)
17                 for(unsigned j=0; j<4; ++j)
18                         result(i, j) = m.m[i][j];
19         return result;
20 }
21
22 void set_texture_image(Msp::GL::Texture2D &texture, const vr::RenderModel_TextureMap_t &openvr_tex)
23 {
24         texture.storage(Msp::GL::SRGB8_ALPHA8, openvr_tex.unWidth, openvr_tex.unHeight);
25         texture.image(0, Msp::GL::RGBA, Msp::GL::UNSIGNED_BYTE, openvr_tex.rubTextureMapData);
26 }
27
28 }
29
30
31 namespace Msp {
32 namespace VR {
33
34 struct OpenVRSystem::RenderModel
35 {
36         GL::Mesh mesh;
37         GL::Technique tech;
38         GL::Object object;
39
40         RenderModel();
41
42         void convert(const vr::RenderModel_t &, OpenVRSystem &);
43 };
44
45
46 unsigned OpenVRSystem::n_instances = 0;
47
48 OpenVRSystem::OpenVRSystem():
49         n_tracked_devices(0)
50 {
51         vr::IVRSystem *vr_sys = 0;
52         if(!n_instances)
53         {
54                 vr::EVRInitError init_err;
55                 vr_sys = vr::VR_Init(&init_err, vr::VRApplication_Scene);
56                 if(init_err!=vr::VRInitError_None)
57                         throw runtime_error("OpenVR initialization failed");
58         }
59         ++n_instances;
60
61         vr::IVRCompositor *compositor = vr::VRCompositor();
62         if(!compositor)
63                 throw runtime_error("OpenVR compositor initialization failed");
64
65         vr::VRCompositor()->SetTrackingSpace(vr::TrackingUniverseSeated);
66
67         for(unsigned i=0; i<vr::k_unMaxTrackedDeviceCount; ++i)
68                 if(vr_sys->IsTrackedDeviceConnected(i))
69                 {
70                         n_tracked_devices = i+1;
71                         if(vr_sys->GetTrackedDeviceClass(i)==vr::TrackedDeviceClass_Controller)
72                                 unclaimed_controllers.push_back(i);
73                 }
74
75         tracking_matrices.resize(n_tracked_devices);
76
77         GL::ProgramBuilder::StandardFeatures features;
78         features.lighting = true;
79         features.material = true;
80         GL::ProgramBuilder(features).add_shaders(render_shprog);
81         render_shprog.link();
82
83         features.texture = true;
84         GL::ProgramBuilder(features).add_shaders(render_shprog_textured);
85         render_shprog_textured.link();
86
87         render_material.set_diffuse(GL::Color(1.0f));
88         render_material.set_ambient(GL::Color(1.0f));
89 }
90
91 OpenVRSystem::~OpenVRSystem()
92 {
93         if(!--n_instances)
94                 vr::VR_Shutdown();
95 }
96
97 bool OpenVRSystem::is_maybe_available()
98 {
99         return vr::VR_IsHmdPresent();
100 }
101
102 void OpenVRSystem::configure_view(StereoView &view) const
103 {
104         vr::IVRSystem *vr_sys = vr::VRSystem();
105
106 #if defined(__GNUC__) && defined(_WIN32)
107         /* Visual C++ passes the return value address as first stack parameter and
108         the this pointer in ecx.  MinGW does the other way around.  This trick
109         forces the function signature to match at machine code level. */
110         typedef void (vr::IVRSystem::*FuncPtr)(vr::HmdMatrix34_t *, vr::EVREye);
111         FuncPtr get_eye_to_head_transform = reinterpret_cast<FuncPtr>(&vr::IVRSystem::GetEyeToHeadTransform);
112
113         vr::HmdMatrix34_t left, right;
114         (vr_sys->*get_eye_to_head_transform)(&left, vr::Eye_Left);
115         (vr_sys->*get_eye_to_head_transform)(&right, vr::Eye_Right);
116 #else
117         vr::HmdMatrix34_t left = vr_sys->GetEyeToHeadTransform(vr::Eye_Left);
118         vr::HmdMatrix34_t right = vr_sys->GetEyeToHeadTransform(vr::Eye_Right);
119 #endif
120         
121         view.set_eye_matrices(convert_matrix(left), convert_matrix(right));
122 }
123
124 void OpenVRSystem::set_absolute_tracking(bool a)
125 {
126         vr::VRCompositor()->SetTrackingSpace(a ? vr::TrackingUniverseStanding : vr::TrackingUniverseSeated);
127 }
128
129 bool OpenVRSystem::get_absolute_tracking() const
130 {
131         return vr::VRCompositor()->GetTrackingSpace()==vr::TrackingUniverseStanding;
132 }
133
134 OpenVRCamera *OpenVRSystem::create_camera(const GL::Camera &bc)
135 {
136         return new OpenVRCamera(*this, bc);
137 }
138
139 OpenVRCombiner *OpenVRSystem::create_combiner(GL::View &v)
140 {
141         return new OpenVRCombiner(*this, v);
142 }
143
144 OpenVRController *OpenVRSystem::create_controller()
145 {
146         return new OpenVRController(*this);
147 }
148
149 void OpenVRSystem::tick()
150 {
151         vr::IVRSystem *vr_sys = vr::VRSystem();
152
153         OpenVRController::Event event;
154         while(vr_sys->PollNextEvent(&event, sizeof(event)))
155         {
156                 bool controller_matched = false;
157                 for(vector<OpenVRController *>::iterator i=controllers.begin(); i!=controllers.end(); ++i)
158                 {
159                         int cindex = (*i)->get_index();
160                         if(cindex>=0 && event.trackedDeviceIndex==static_cast<unsigned>(cindex))
161                         {
162                                 (*i)->event(event);
163                                 controller_matched = true;
164                         }
165                 }
166
167                 if(!controller_matched && event.eventType==vr::VREvent_TrackedDeviceActivated)
168                 {
169                         if(event.trackedDeviceIndex>=n_tracked_devices)
170                         {
171                                 n_tracked_devices = event.trackedDeviceIndex+1;
172                                 tracking_matrices.resize(n_tracked_devices);
173                         }
174
175                         vr::ETrackedDeviceClass dev_class = vr_sys->GetTrackedDeviceClass(event.trackedDeviceIndex);
176
177                         if(dev_class==vr::TrackedDeviceClass_Controller)
178                         {
179                                 bool assigned_to_controller = false;
180                                 for(vector<OpenVRController *>::iterator i=controllers.begin(); i!=controllers.end(); ++i)
181                                         if((*i)->get_index()<0)
182                                         {
183                                                 (*i)->event(event);
184                                                 assigned_to_controller = true;
185                                                 break;
186                                         }
187
188                                 if(!assigned_to_controller)
189                                         unclaimed_controllers.push_back(event.trackedDeviceIndex);
190                         }
191                 }
192         }
193
194         for(vector<OpenVRController *>::iterator i=controllers.begin(); i!=controllers.end(); ++i)
195                 (*i)->update_input_state();
196
197         for(list<string>::iterator i=loading_render_models.begin(); i!=loading_render_models.end(); )
198         {
199                 if(check_loading_render_model(*i))
200                         loading_render_models.erase(i++);
201                 else
202                         ++i;
203         }
204
205         for(list<unsigned>::iterator i=loading_textures.begin(); i!=loading_textures.end(); )
206         {
207                 if(check_loading_texture(*i))
208                         loading_textures.erase(i++);
209                 else
210                         ++i;
211         }
212 }
213
214 void OpenVRSystem::update_pose_matrices()
215 {
216         vector<vr::TrackedDevicePose_t> poses;
217         poses.resize(n_tracked_devices);
218         vr::VRCompositor()->WaitGetPoses(&poses[0], poses.size(), 0, 0);
219
220         for(unsigned i=0; i<n_tracked_devices; ++i)
221                 if(poses[i].bPoseIsValid)
222                         tracking_matrices[i] = convert_matrix(poses[i].mDeviceToAbsoluteTracking);
223 }
224
225 const GL::Matrix &OpenVRSystem::get_tracking_matrix(unsigned index) const
226 {
227         if(index>=tracking_matrices.size())
228                 throw out_of_range("OpenVRSystem::get_tracking_matrix");
229
230         return tracking_matrices[index];
231 }
232
233 const GL::Matrix &OpenVRSystem::get_hmd_matrix() const
234 {
235         return get_tracking_matrix(vr::k_unTrackedDeviceIndex_Hmd);
236 }
237
238 void OpenVRSystem::add_controller(OpenVRController &controller)
239 {
240         vector<OpenVRController *>::iterator i = find(controllers.begin(), controllers.end(), &controller);
241         if(i!=controllers.end())
242                 throw invalid_argument("already added");
243
244         controllers.push_back(&controller);
245
246         if(!unclaimed_controllers.empty())
247         {
248                 OpenVRController::Event event;
249                 event.eventType = vr::VREvent_TrackedDeviceActivated;
250                 event.trackedDeviceIndex = unclaimed_controllers.back();
251                 event.eventAgeSeconds = 0;
252                 controller.event(event);
253                 unclaimed_controllers.pop_back();
254         }
255 }
256
257 void OpenVRSystem::remove_controller(OpenVRController &controller)
258 {
259         vector<OpenVRController *>::iterator i = find(controllers.begin(), controllers.end(), &controller);
260         if(i==controllers.end())
261                 throw invalid_argument("not added");
262
263         int index = controller.get_index();
264         if(index>=0)
265                 unclaimed_controllers.push_back(index);
266         controllers.erase(i);
267 }
268
269 const GL::Object *OpenVRSystem::get_render_model(const string &name)
270 {
271         map<string, RenderModel *>::const_iterator i = render_models.find(name);
272         if(i!=render_models.end())
273                 return &i->second->object;
274
275         vr::IVRRenderModels *service = vr::VRRenderModels();
276         vr::RenderModel_t *openvr_model = 0;
277         vr::EVRRenderModelError err = service->LoadRenderModel_Async(name.c_str(), &openvr_model);
278         if(err!=vr::VRRenderModelError_None && err!=vr::VRRenderModelError_Loading)
279                 throw runtime_error(service->GetRenderModelErrorNameFromEnum(err));
280
281         if(err==vr::VRRenderModelError_None && !openvr_model)
282                 return 0;
283
284         RenderModel *model = new RenderModel;
285         render_models[name] = model;
286         if(err==vr::VRRenderModelError_Loading)
287                 loading_render_models.push_back(name);
288         else
289                 model->convert(*openvr_model, *this);
290
291         return &model->object;
292 }
293
294 bool OpenVRSystem::check_loading_render_model(const string &name)
295 {
296         RenderModel &model = *get_item(render_models, name);
297
298         vr::IVRRenderModels *service = vr::VRRenderModels();
299         vr::RenderModel_t *openvr_model = 0;
300         vr::EVRRenderModelError err = service->LoadRenderModel_Async(name.c_str(), &openvr_model);
301         if(err==vr::VRRenderModelError_Loading)
302                 return false;
303         else if(err!=vr::VRRenderModelError_None)
304                 throw runtime_error(service->GetRenderModelErrorNameFromEnum(err));
305
306         model.convert(*openvr_model, *this);
307         return true;
308 }
309
310 const GL::Texture2D *OpenVRSystem::get_texture(unsigned id)
311 {
312         map<unsigned, GL::Texture2D *>::const_iterator i = render_textures.find(id);
313         if(i!=render_textures.end())
314                 return i->second;
315
316         vr::IVRRenderModels *service = vr::VRRenderModels();
317         vr::RenderModel_TextureMap_t *openvr_texture = 0;
318         vr::EVRRenderModelError err = service->LoadTexture_Async(id, &openvr_texture);
319         if(err!=vr::VRRenderModelError_None && err!=vr::VRRenderModelError_Loading)
320                 throw runtime_error(service->GetRenderModelErrorNameFromEnum(err));
321
322         GL::Texture2D *texture = new GL::Texture2D;
323         render_textures[id] = texture;
324         texture->set_min_filter(GL::LINEAR_MIPMAP_LINEAR);
325         texture->set_generate_mipmap(true);
326         if(err==vr::VRRenderModelError_Loading)
327                 loading_textures.push_back(id);
328         else
329                 set_texture_image(*texture, *openvr_texture);
330
331         return texture;
332 }
333
334 bool OpenVRSystem::check_loading_texture(unsigned id)
335 {
336         GL::Texture2D &texture = *get_item(render_textures, id);
337
338         vr::IVRRenderModels *service = vr::VRRenderModels();
339         vr::RenderModel_TextureMap_t *openvr_texture = 0;
340         vr::EVRRenderModelError err = service->LoadTexture_Async(id, &openvr_texture);
341         if(err==vr::VRRenderModelError_Loading)
342                 return false;
343         else if(err!=vr::VRRenderModelError_None)
344                 throw runtime_error(service->GetRenderModelErrorNameFromEnum(err));
345
346         set_texture_image(texture, *openvr_texture);
347         return true;
348 }
349
350
351 OpenVRSystem::RenderModel::RenderModel():
352         mesh((GL::NORMAL3, GL::TEXCOORD2, GL::VERTEX3)),
353         object(&mesh, &tech)
354 { }
355
356 void OpenVRSystem::RenderModel::convert(const vr::RenderModel_t &model, OpenVRSystem &system)
357 {
358         GL::RenderPass &pass = tech.add_pass(0);
359         pass.set_material(&system.render_material);
360         if(model.diffuseTextureId>=0)
361         {
362                 pass.set_shader_program(&system.render_shprog_textured, 0);
363                 pass.set_texture(0, system.get_texture(model.diffuseTextureId));
364         }
365         else
366                 pass.set_shader_program(&system.render_shprog, 0);
367
368         mesh.clear();
369         Msp::GL::MeshBuilder bld(mesh);
370         for(unsigned i=0; i<model.unVertexCount; ++i)
371         {
372                 const vr::RenderModel_Vertex_t &v = model.rVertexData[i];
373                 bld.normal(Msp::GL::Vector3(v.vNormal.v));
374                 bld.texcoord(v.rfTextureCoord[0], v.rfTextureCoord[1]);
375                 bld.vertex(Msp::GL::Vector3(v.vPosition.v));
376         }
377
378         bld.begin(Msp::GL::TRIANGLES);
379         for(unsigned i=0; i<model.unTriangleCount; ++i)
380                 for(unsigned j=0; j<3; ++j)
381                         bld.element(model.rIndexData[i*3+j]);
382         bld.end();
383 }
384
385 } // namespace VR
386 } // namespace Msp