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