1 /*=========================================================================
2
3 Program: Visualization Toolkit
4
5 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
6 All rights reserved.
7 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
8
9 This software is distributed WITHOUT ANY WARRANTY; without even
10 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 PURPOSE. See the above copyright notice for more information.
12
13 =========================================================================*/
14 #include "vtkOpenVROverlay.h"
15
16 #include "vtkCallbackCommand.h"
17 #include "vtkDataArray.h"
18 #include "vtkImageData.h"
19 #include "vtkInteractorStyle3D.h"
20 #include "vtkJPEGReader.h"
21 #include "vtkNew.h"
22 #include "vtkObjectFactory.h"
23 #include "vtkOpenVRCamera.h"
24 #include "vtkOpenVRRenderWindow.h"
25 #include "vtkOpenVRRenderWindowInteractor.h"
26 #include "vtkPointData.h"
27 #include "vtkRenderer.h"
28 #include "vtkRendererCollection.h"
29 #include "vtkTextureObject.h"
30 #include "vtkXMLDataElement.h"
31 #include "vtkXMLUtilities.h"
32 #include "vtksys/FStream.hxx"
33 #include "vtksys/SystemTools.hxx"
34
35 #include "vtkOpenVROverlayInternal.h"
36
37 #include "OpenVRDashboard.h"
38
39 #include <cmath>
40
41 vtkStandardNewMacro(vtkOpenVROverlay);
42
vtkOpenVROverlay()43 vtkOpenVROverlay::vtkOpenVROverlay()
44 {
45 this->OverlayHandle = 0;
46 this->OverlayThumbnailHandle = 0;
47 this->OriginalTextureData = nullptr;
48 this->CurrentTextureData = nullptr;
49 this->LastSpot = nullptr;
50 this->SessionName = "";
51 this->VRSystem = nullptr;
52 this->DashboardImageFileName = "OpenVRDashboard.jpg";
53 this->LastCameraPoseIndex = -1;
54 this->LastSpotIntensity = 0.3;
55 this->ActiveSpotIntensity = 0.3;
56 }
57
~vtkOpenVROverlay()58 vtkOpenVROverlay::~vtkOpenVROverlay()
59 {
60 if (this->OriginalTextureData)
61 {
62 delete[] this->OriginalTextureData;
63 this->OriginalTextureData = nullptr;
64 }
65 if (this->CurrentTextureData)
66 {
67 delete[] this->CurrentTextureData;
68 this->CurrentTextureData = nullptr;
69 }
70 }
71
GetSavedCameraPose(int i)72 vtkOpenVRCameraPose* vtkOpenVROverlay::GetSavedCameraPose(int i)
73 {
74 auto p = this->SavedCameraPoses.find(i);
75 if (p != this->SavedCameraPoses.end())
76 {
77 return &(p->second);
78 }
79 return nullptr;
80 }
81
WriteCameraPoses(ostream & os)82 void vtkOpenVROverlay::WriteCameraPoses(ostream& os)
83 {
84 vtkNew<vtkXMLDataElement> topel;
85 topel->SetName("CameraPoses");
86 for (auto p : this->SavedCameraPoses)
87 {
88 vtkOpenVRCameraPose& pose = p.second;
89 if (pose.Loaded)
90 {
91 vtkNew<vtkXMLDataElement> el;
92 el->SetName("CameraPose");
93 el->SetIntAttribute("PoseNumber", p.first);
94 el->SetVectorAttribute("Position", 3, pose.Position);
95 el->SetDoubleAttribute("Distance", pose.Distance);
96 el->SetDoubleAttribute("MotionFactor", pose.MotionFactor);
97 el->SetVectorAttribute("Translation", 3, pose.Translation);
98 el->SetVectorAttribute("InitialViewUp", 3, pose.PhysicalViewUp);
99 el->SetVectorAttribute("InitialViewDirection", 3, pose.PhysicalViewDirection);
100 el->SetVectorAttribute("ViewDirection", 3, pose.ViewDirection);
101 topel->AddNestedElement(el);
102 }
103 }
104
105 vtkXMLUtilities::FlattenElement(topel, os);
106 }
107
WriteCameraPoses()108 void vtkOpenVROverlay::WriteCameraPoses()
109 {
110 std::string fname = this->GetSessionName();
111 fname += "VTKOpenVRCameraPoses.vovrcp";
112 vtksys::ofstream os(fname.c_str(), ios::out);
113 this->WriteCameraPoses(os);
114
115 os.flush();
116 if (os.fail())
117 {
118 os.close();
119 unlink(fname.c_str());
120 }
121 }
122
ReadCameraPoses()123 void vtkOpenVROverlay::ReadCameraPoses()
124 {
125 std::string fname = this->GetSessionName();
126 fname += "VTKOpenVRCameraPoses.vovrcp";
127
128 if (!vtksys::SystemTools::FileExists(fname.c_str()))
129 {
130 return;
131 }
132
133 vtksys::ifstream is(fname.c_str());
134 this->ReadCameraPoses(is);
135 }
136
ReadCameraPoses(istream & is)137 void vtkOpenVROverlay::ReadCameraPoses(istream& is)
138 {
139 vtkXMLDataElement* topel = vtkXMLUtilities::ReadElementFromStream(is);
140
141 this->ReadCameraPoses(topel);
142 topel->Delete();
143 }
144
ReadCameraPoses(vtkXMLDataElement * topel)145 void vtkOpenVROverlay::ReadCameraPoses(vtkXMLDataElement* topel)
146 {
147 this->SavedCameraPoses.clear();
148 if (topel)
149 {
150 int numPoses = topel->GetNumberOfNestedElements();
151 for (size_t i = 0; i < numPoses; i++)
152 {
153 vtkXMLDataElement* el = topel->GetNestedElement(static_cast<int>(i));
154 int poseNum = 0;
155 el->GetScalarAttribute("PoseNumber", poseNum);
156 el->GetVectorAttribute("Position", 3, this->SavedCameraPoses[poseNum].Position);
157 el->GetVectorAttribute("InitialViewUp", 3, this->SavedCameraPoses[poseNum].PhysicalViewUp);
158 el->GetVectorAttribute(
159 "InitialViewDirection", 3, this->SavedCameraPoses[poseNum].PhysicalViewDirection);
160 el->GetVectorAttribute("ViewDirection", 3, this->SavedCameraPoses[poseNum].ViewDirection);
161 el->GetVectorAttribute("Translation", 3, this->SavedCameraPoses[poseNum].Translation);
162 el->GetScalarAttribute("Distance", this->SavedCameraPoses[poseNum].Distance);
163 el->GetScalarAttribute("MotionFactor", this->SavedCameraPoses[poseNum].MotionFactor);
164 this->SavedCameraPoses[poseNum].Loaded = true;
165 }
166 }
167 }
168
SetSavedCameraPose(int i,vtkOpenVRCameraPose * pose)169 void vtkOpenVROverlay::SetSavedCameraPose(int i, vtkOpenVRCameraPose* pose)
170 {
171 if (pose)
172 {
173 this->SavedCameraPoses[i] = *pose;
174 }
175 }
176
SaveCameraPose(int slot)177 void vtkOpenVROverlay::SaveCameraPose(int slot)
178 {
179 vtkOpenVRCameraPose* pose = &this->SavedCameraPoses[slot];
180 vtkRenderer* ren = static_cast<vtkRenderer*>(this->Window->GetRenderers()->GetItemAsObject(0));
181 pose->Set(static_cast<vtkOpenVRCamera*>(ren->GetActiveCamera()), this->Window);
182 this->InvokeEvent(vtkCommand::SaveStateEvent, reinterpret_cast<void*>(slot));
183 }
184
LoadCameraPose(int slot)185 void vtkOpenVROverlay::LoadCameraPose(int slot)
186 {
187 vtkOpenVRCameraPose* pose = this->GetSavedCameraPose(slot);
188 if (pose && pose->Loaded)
189 {
190 this->LastCameraPoseIndex = slot;
191 vtkRenderer* ren = static_cast<vtkRenderer*>(this->Window->GetRenderers()->GetItemAsObject(0));
192 pose->Apply(static_cast<vtkOpenVRCamera*>(ren->GetActiveCamera()), this->Window);
193 ren->ResetCameraClippingRange();
194 this->InvokeEvent(vtkCommand::LoadStateEvent, reinterpret_cast<void*>(slot));
195 }
196 }
197
LoadNextCameraPose()198 void vtkOpenVROverlay::LoadNextCameraPose()
199 {
200 if (this->SavedCameraPoses.empty())
201 {
202 return;
203 }
204
205 int nextValue = -1;
206 int firstValue = this->LastCameraPoseIndex;
207 // find the next pose index in the map
208 for (auto p : this->SavedCameraPoses)
209 {
210 if (p.first < firstValue)
211 {
212 firstValue = p.first;
213 }
214 if (p.first > this->LastCameraPoseIndex)
215 {
216 nextValue = p.first;
217 // is there anything lower than nextValue but still larger than current
218 for (auto p2 : this->SavedCameraPoses)
219 {
220 if (p2.first > this->LastCameraPoseIndex && p2.first < nextValue)
221 {
222 nextValue = p2.first;
223 }
224 }
225 break;
226 }
227 }
228
229 if (nextValue == -1)
230 {
231 nextValue = firstValue;
232 }
233
234 this->LoadCameraPose(nextValue);
235 }
236
Show()237 void vtkOpenVROverlay::Show()
238 {
239 vr::VROverlay()->ShowOverlay(this->OverlayHandle);
240 this->Render();
241 }
242
Hide()243 void vtkOpenVROverlay::Hide()
244 {
245 vr::VROverlay()->HideOverlay(this->OverlayHandle);
246 }
247
SetDashboardImageData(vtkJPEGReader * imgReader)248 void vtkOpenVROverlay::SetDashboardImageData(vtkJPEGReader* imgReader)
249 {
250 imgReader->SetMemoryBuffer(OpenVRDashboard);
251 imgReader->SetMemoryBufferLength(sizeof(OpenVRDashboard));
252 imgReader->Update();
253 }
254
Create(vtkOpenVRRenderWindow * win)255 void vtkOpenVROverlay::Create(vtkOpenVRRenderWindow* win)
256 {
257 if (!vr::VROverlay())
258 {
259 vtkErrorMacro("Error creating overlay");
260 return;
261 }
262
263 if (this->OverlayHandle)
264 {
265 return;
266 }
267
268 this->Window = win;
269
270 this->ReadCameraPoses();
271
272 std::string sKey = std::string("VTK OpenVR Settings");
273 vr::VROverlayError overlayError = vr::VROverlay()->CreateDashboardOverlay(
274 sKey.c_str(), "VTK", &this->OverlayHandle, &this->OverlayThumbnailHandle);
275 if (overlayError != vr::VROverlayError_None)
276 {
277 vtkErrorMacro("Error creating overlay");
278 return;
279 }
280
281 vr::VROverlay()->SetOverlayFlag(
282 this->OverlayHandle, vr::VROverlayFlags_SortWithNonSceneOverlays, true);
283 vr::VROverlay()->SetOverlayFlag(this->OverlayHandle, vr::VROverlayFlags_VisibleInDashboard, true);
284 vr::VROverlay()->SetOverlayWidthInMeters(this->OverlayHandle, 2.5f);
285 vr::VROverlay()->SetOverlayInputMethod(this->OverlayHandle, vr::VROverlayInputMethod_Mouse);
286
287 win->MakeCurrent();
288
289 this->OverlayTexture->SetContext(win);
290
291 // delete any old texture data
292 if (this->OriginalTextureData)
293 {
294 delete[] this->OriginalTextureData;
295 this->OriginalTextureData = nullptr;
296 }
297
298 // if dashboard image exists use it
299 vtkNew<vtkJPEGReader> imgReader;
300 if (!this->DashboardImageFileName.empty() &&
301 imgReader->CanReadFile(this->DashboardImageFileName.c_str()))
302 {
303 imgReader->SetFileName(this->DashboardImageFileName.c_str());
304 imgReader->Update();
305 }
306 else // use compiled in dashboard
307 {
308 this->SetDashboardImageData(imgReader);
309 }
310
311 vtkImageData* id = imgReader->GetOutput();
312 int dims[3];
313 id->GetDimensions(dims);
314 int numC = id->GetPointData()->GetScalars()->GetNumberOfComponents();
315
316 this->OriginalTextureData = new unsigned char[dims[0] * dims[1] * 4];
317 this->CurrentTextureData = new unsigned char[dims[0] * dims[1] * 4];
318 unsigned char* dataPtr = this->OriginalTextureData;
319 unsigned char* inPtr =
320 static_cast<unsigned char*>(id->GetPointData()->GetScalars()->GetVoidPointer(0));
321 for (int j = 0; j < dims[1]; j++)
322 {
323 for (int i = 0; i < dims[0]; i++)
324 {
325 *(dataPtr++) = *(inPtr++);
326 *(dataPtr++) = *(inPtr++);
327 *(dataPtr++) = *(inPtr++);
328 *(dataPtr++) = (numC == 4 ? *(inPtr++) : 255.0);
329 }
330 }
331 memcpy(this->CurrentTextureData, this->OriginalTextureData, dims[0] * dims[1] * 4);
332 this->OverlayTexture->Create2DFromRaw(dims[0], dims[1], 4, VTK_UNSIGNED_CHAR,
333 const_cast<void*>(static_cast<const void* const>(this->OriginalTextureData)));
334
335 this->SetupSpots();
336
337 int width = this->OverlayTexture->GetWidth();
338 int height = this->OverlayTexture->GetHeight();
339 vr::HmdVector2_t vecWindowSize = { static_cast<float>(width), static_cast<float>(height) };
340 vr::VROverlay()->SetOverlayMouseScale(this->OverlayHandle, &vecWindowSize);
341 }
342
Render()343 void vtkOpenVROverlay::Render()
344 {
345 // skip rendering if the overlay isn't visible
346 if (!vr::VROverlay() ||
347 (!vr::VROverlay()->IsOverlayVisible(this->OverlayHandle) &&
348 !vr::VROverlay()->IsOverlayVisible(this->OverlayThumbnailHandle)))
349 {
350 return;
351 }
352
353 this->Window->MakeCurrent();
354 int dims[2];
355 dims[0] = this->OverlayTexture->GetWidth();
356 dims[1] = this->OverlayTexture->GetHeight();
357 this->OverlayTexture->Create2DFromRaw(dims[0], dims[1], 4, VTK_UNSIGNED_CHAR,
358 const_cast<void*>(static_cast<const void* const>(this->CurrentTextureData)));
359 this->OverlayTexture->Bind();
360 GLuint unTexture = this->OverlayTexture->GetHandle();
361 if (unTexture != 0)
362 {
363 vr::Texture_t texture = { (void*)(uintptr_t)unTexture, vr::TextureType_OpenGL,
364 vr::ColorSpace_Auto };
365 vr::VROverlay()->SetOverlayTexture(this->OverlayHandle, &texture);
366 }
367 }
368
MouseMoved(int x,int y)369 void vtkOpenVROverlay::MouseMoved(int x, int y)
370 {
371 // did we leave the last active spot
372 bool leftSpot = false;
373 if (this->LastSpot &&
374 (x < this->LastSpot->xmin || x > this->LastSpot->xmax || y < this->LastSpot->ymin ||
375 y > this->LastSpot->ymax))
376 {
377 leftSpot = true;
378 vtkOpenVROverlaySpot* spot = this->LastSpot;
379 this->LastSpot = nullptr;
380 this->UpdateSpot(spot);
381 }
382
383 // if we are in a spot already and did not leave
384 // just return
385 if (this->LastSpot)
386 {
387 return;
388 }
389
390 // did we enter a new spot?
391 bool enteredSpot = false;
392
393 std::vector<vtkOpenVROverlaySpot>::iterator it = this->Spots.begin();
394 for (; !enteredSpot && it != this->Spots.end(); ++it)
395 {
396 if (x >= it->xmin && x <= it->xmax && y >= it->ymin && y <= it->ymax)
397 {
398 // if we are not already in this spot
399 if (this->LastSpot != &(*it))
400 {
401 this->LastSpot = &(*it);
402 enteredSpot = true;
403 this->UpdateSpot(this->LastSpot);
404 }
405 }
406 }
407
408 if (!leftSpot && !enteredSpot)
409 {
410 return;
411 }
412
413 this->Render();
414 }
415
UpdateSpot(vtkOpenVROverlaySpot * spot)416 void vtkOpenVROverlay::UpdateSpot(vtkOpenVROverlaySpot* spot)
417 {
418 int dims[2];
419 dims[0] = this->OverlayTexture->GetWidth();
420 dims[1] = this->OverlayTexture->GetHeight();
421 unsigned char* currPtr = this->CurrentTextureData;
422 unsigned char* origPtr = this->OriginalTextureData;
423
424 float shift = 0.0;
425 float scale = 1.0;
426 if (spot->Active)
427 {
428 shift = this->ActiveSpotIntensity * 255.0;
429 scale = 1.0 - this->ActiveSpotIntensity;
430 }
431 if (spot == this->LastSpot)
432 {
433 shift = this->LastSpotIntensity * 255.0;
434 scale = 1.0 - this->LastSpotIntensity;
435 }
436
437 for (int j = spot->ymin; j <= spot->ymax; j++)
438 {
439 unsigned char* dataPtr = currPtr + (j * dims[0] + spot->xmin) * 4;
440 unsigned char* inPtr = origPtr + (j * dims[0] + spot->xmin) * 4;
441 for (int i = spot->xmin; i <= spot->xmax; i++)
442 {
443 *(dataPtr++) = static_cast<unsigned char>(scale * *(inPtr++) + shift);
444 *(dataPtr++) = static_cast<unsigned char>(scale * *(inPtr++) + shift);
445 *(dataPtr++) = static_cast<unsigned char>(scale * *(inPtr++) + shift);
446 dataPtr++;
447 inPtr++;
448 }
449 }
450 }
451
MouseButtonPress(int x,int y)452 void vtkOpenVROverlay::MouseButtonPress(int x, int y)
453 {
454 this->MouseMoved(x, y);
455 if (this->LastSpot && this->LastSpot->Callback)
456 {
457 this->LastSpot->Callback->Execute(this, vtkCommand::LeftButtonPressEvent, this->Window);
458 }
459 }
460
MouseButtonRelease(int,int)461 void vtkOpenVROverlay::MouseButtonRelease(int, int)
462 {
463 if (this->LastSpot && this->LastSpot->Callback)
464 {
465 this->LastSpot->Callback->Execute(this, vtkCommand::LeftButtonReleaseEvent, this->Window);
466 }
467 }
468
PrintSelf(ostream & os,vtkIndent indent)469 void vtkOpenVROverlay::PrintSelf(ostream& os, vtkIndent indent)
470 {
471 this->Superclass::PrintSelf(os, indent);
472 }
473