1 /*=========================================================================
2 
3 Program:   Visualization Toolkit
4 Module:    vtkVRControlsHelper.cxx
5 
6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7 All rights reserved.
8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10 This software is distributed WITHOUT ANY WARRANTY; without even
11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12 PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkVRControlsHelper.h"
16 
17 #include "vtkActor.h"
18 #include "vtkCallbackCommand.h"
19 #include "vtkCamera.h"
20 #include "vtkCellArray.h"
21 #include "vtkLineSource.h"
22 #include "vtkPoints.h"
23 #include "vtkPolyData.h"
24 #include "vtkPolyDataMapper.h"
25 #include "vtkProperty.h"
26 #include "vtkRenderWindow.h"
27 #include "vtkRenderWindowInteractor.h"
28 #include "vtkRenderer.h"
29 #include "vtkSmartPointer.h"
30 #include "vtkTextActor3D.h"
31 #include "vtkTextProperty.h"
32 #include "vtkTransform.h"
33 #include "vtkVRModel.h"
34 #include "vtkVRRenderWindow.h"
35 #include "vtkWindow.h"
36 
37 //------------------------------------------------------------------------------
vtkVRControlsHelper()38 vtkVRControlsHelper::vtkVRControlsHelper()
39 {
40   // The text
41   this->Text = vtkStdString("");
42   this->TextActor = vtkTextActor3D::New();
43   this->TextActor->GetTextProperty()->SetFontSize(30);
44   this->TextActor->SetInput(this->Text.c_str());
45 
46   vtkTextProperty* prop = this->TextActor->GetTextProperty();
47   this->TextActor->ForceOpaqueOn();
48 
49   prop->SetFontFamilyToTimes();
50   prop->SetFrame(1);
51   prop->SetFrameWidth(12);
52   prop->SetFrameColor(0.0, 0.0, 0.0);
53   prop->SetBackgroundOpacity(1.0);
54   prop->SetBackgroundColor(0.0, 0.0, 0.0);
55   prop->SetFontSize(20);
56 
57   // The line
58   this->LineSource = vtkLineSource::New();
59   this->LineSource->SetPoint1(0, 0, 0);
60   this->LineSource->SetPoint2(0, 0, -1);
61   this->LineMapper = vtkPolyDataMapper::New();
62   this->LineActor = vtkActor::New();
63   this->LineMapper->SetInputConnection(this->LineSource->GetOutputPort());
64   this->LineActor->SetMapper(this->LineMapper);
65 
66   // Tooltip default option
67   this->ComponentName = vtkStdString("trigger");
68   this->DrawSide = vtkVRControlsHelper::Right;
69   this->ButtonSide = vtkVRControlsHelper::Back;
70 
71   this->EnabledOff();
72   this->VisibilityOff();
73 
74   this->ControlPositionLC[0] = 0.;
75   this->ControlPositionLC[1] = 0.;
76   this->ControlPositionLC[2] = 0.;
77 
78   this->MoveCallbackCommand = vtkCallbackCommand::New();
79   this->MoveCallbackCommand->SetClientData(this);
80   this->MoveCallbackCommand->SetCallback(vtkVRControlsHelper::MoveEvent);
81   this->MoveCallbackCommand->SetPassiveObserver(1);
82 
83   this->Device = vtkEventDataDevice::Unknown;
84   this->Renderer = nullptr;
85 
86   this->NeedUpdate = false;
87   this->LabelVisible = false;
88 }
89 
90 //------------------------------------------------------------------------------
~vtkVRControlsHelper()91 vtkVRControlsHelper::~vtkVRControlsHelper()
92 {
93   this->SetRenderer(nullptr);
94 
95   this->TextActor->Delete();
96 
97   this->LineMapper->Delete();
98   this->LineActor->Delete();
99   this->LineSource->Delete();
100 
101   this->MoveCallbackCommand->Delete();
102 }
103 
104 //------------------------------------------------------------------------------
SetDevice(vtkEventDataDevice val)105 void vtkVRControlsHelper::SetDevice(vtkEventDataDevice val)
106 {
107   if (this->Device == val)
108   {
109     return;
110   }
111 
112   this->Device = val;
113   this->Modified();
114 }
115 
116 //------------------------------------------------------------------------------
MoveEvent(vtkObject *,unsigned long,void * clientdata,void * calldata)117 void vtkVRControlsHelper::MoveEvent(vtkObject*, unsigned long, void* clientdata, void* calldata)
118 {
119   vtkVRControlsHelper* self = static_cast<vtkVRControlsHelper*>(clientdata);
120 
121   vtkVRRenderWindow* renWin = static_cast<vtkVRRenderWindow*>(self->Renderer->GetRenderWindow());
122 
123   vtkEventData* ed = static_cast<vtkEventData*>(calldata);
124   vtkEventDataDevice3D* ed3 = ed->GetAsEventDataDevice3D();
125   if (ed3 && self->Enabled && ed3->GetType() == vtkCommand::Move3DEvent &&
126     ed3->GetDevice() == self->Device)
127   {
128     const double* controllerPositionWC = ed3->GetWorldPosition();
129     std::copy(controllerPositionWC, controllerPositionWC + 3, self->LastEventPosition);
130 
131     const double* wxyz = ed3->GetWorldOrientation();
132     std::copy(wxyz, wxyz + 4, self->LastEventOrientation);
133 
134     std::copy(renWin->GetPhysicalTranslation(), renWin->GetPhysicalTranslation() + 3,
135       self->LastPhysicalTranslation);
136 
137     self->NeedUpdate = true;
138   }
139 }
140 
141 //------------------------------------------------------------------------------
UpdateRepresentation()142 void vtkVRControlsHelper::UpdateRepresentation()
143 {
144   this->NeedUpdate = false;
145   if (!this->Enabled)
146   {
147     return;
148   }
149 
150   if (this->Renderer && this->Renderer->GetRenderWindow() &&
151     this->Renderer->GetRenderWindow()->GetInteractor())
152   {
153     vtkVRRenderWindow* renWin = static_cast<vtkVRRenderWindow*>(this->Renderer->GetRenderWindow());
154     if (!renWin)
155     {
156       return;
157     }
158 
159     // Update physical scale
160     double physicalScale = renWin->GetPhysicalScale();
161 
162     // Get the active controller device
163     vtkEventDataDevice controller = this->Device;
164 
165     // Hide controls tooltips if the controller is off
166     vtkVRModel* mod = renWin->GetTrackedDeviceModel(controller);
167     if (!mod)
168     {
169       this->LabelVisible = false;
170       return;
171     }
172 
173     // Get the controls offset position in the controller local coordinate system
174     if (this->ControlPositionLC[0] == 0. && this->ControlPositionLC[1] == 0. &&
175       this->ControlPositionLC[2] == 0.)
176     {
177       this->InitControlPosition();
178     }
179 
180     // Controller position and world orientation
181     double* ptrans = renWin->GetPhysicalTranslation();
182     this->LastEventPosition[0] += this->LastPhysicalTranslation[0] - ptrans[0];
183     this->LastEventPosition[1] += this->LastPhysicalTranslation[1] - ptrans[1];
184     this->LastEventPosition[2] += this->LastPhysicalTranslation[2] - ptrans[2];
185     const double* controllerPositionWC = this->LastEventPosition;
186     const double* wxyz = this->LastEventOrientation;
187 
188     // todo use ivar here
189     this->TempTransform->Identity();
190     this->TempTransform->RotateWXYZ(wxyz[0], wxyz[1], wxyz[2], wxyz[3]);
191 
192     double* frameForward = this->Renderer->GetActiveCamera()->GetDirectionOfProjection();
193     // Controller up direction in WC
194     double* controllerUpWC = this->TempTransform->TransformDoubleVector(0.0, 1.0, 0.0);
195 
196     // Compute scale factor. It reaches its max value when the control button
197     //  faces the camera. This results in tooltips popping from the controller.
198     double dotFactor = -vtkMath::Dot(controllerUpWC, frameForward);
199 
200     // minimize scaling of the TextActor. ~MultiSampling
201     double reductionFactor = 0.5;
202 
203     // Make the dot product always positive on the button side
204     dotFactor *= this->ButtonSide * reductionFactor * physicalScale;
205 
206     if (dotFactor > 0.0)
207     {
208       // We are looking on the right side, show tooltip
209       this->LabelVisible = true;
210 
211       double PPI = 450;                  // Screen resolution in pixels per inch
212       double FontSizeFactor = 1.0 / PPI; // Map font size to world coordinates
213       this->TextActor->SetScale(FontSizeFactor * dotFactor, FontSizeFactor * dotFactor, 1.0);
214     }
215     else
216     {
217       // We are looking on the wrong side, hide tooltip
218       this->LabelVisible = false;
219       return;
220     }
221 
222     // Control origin in world coordinates.
223     // It corresponds to the vector from the controller position to the
224     // position of the button in world coordinates.
225     double* controlOriginWC = this->TempTransform->TransformDoublePoint(this->ControlPositionLC);
226 
227     // Control position
228     double controlPositionWC[3] = { controllerPositionWC[0] + controlOriginWC[0] * physicalScale,
229       controllerPositionWC[1] + controlOriginWC[1] * physicalScale,
230       controllerPositionWC[2] + controlOriginWC[2] * physicalScale };
231 
232     // Frame main directions in WC
233     double* frameUp = this->Renderer->GetActiveCamera()->GetViewUp();
234     double frameRight[3];
235     vtkMath::Cross(frameForward, frameUp, frameRight);
236     vtkMath::Normalize(frameRight);
237 
238     // Define an offset along the frame right direction and the controller
239     // up direction.
240     double tooltipOffset = 0.15;
241 
242     // Set the origin to the bottom-left or bottom-right corner depending on
243     // the button draw side.
244     double frameOrigin[3] = { (1 - this->DrawSide) / 2. * this->FrameSize[0] * frameRight[0] *
245         dotFactor * this->DrawSide,
246       (1 - this->DrawSide) / 2. * this->FrameSize[0] * frameRight[1] * dotFactor * this->DrawSide,
247       (1 - this->DrawSide) / 2. * this->FrameSize[0] * frameRight[2] * dotFactor * this->DrawSide };
248     // Position of the frame
249     double framePosition[3] = { controlPositionWC[0] + frameOrigin[0],
250       controlPositionWC[1] + frameOrigin[1], controlPositionWC[2] + frameOrigin[2] };
251 
252     controllerUpWC = this->TempTransform->TransformDoubleVector(0.0, 1.0, 0.0);
253 
254     // Apply offset along the frame right axis
255     framePosition[0] += tooltipOffset * frameRight[0] * dotFactor * this->DrawSide;
256     framePosition[1] += tooltipOffset * frameRight[1] * dotFactor * this->DrawSide;
257     framePosition[2] += tooltipOffset * frameRight[2] * dotFactor * this->DrawSide;
258 
259     // Apply offset along the controller up axis
260     framePosition[0] += tooltipOffset * controllerUpWC[0] * dotFactor * this->ButtonSide;
261     framePosition[1] += tooltipOffset * controllerUpWC[1] * dotFactor * this->ButtonSide;
262     framePosition[2] += tooltipOffset * controllerUpWC[2] * dotFactor * this->ButtonSide;
263 
264     double* ori = this->Renderer->GetActiveCamera()->GetOrientationWXYZ();
265     this->TempTransform->Identity();
266     this->TempTransform->RotateWXYZ(-ori[0], ori[1], ori[2], ori[3]);
267 
268     // Update Text Actor
269     this->TextActor->SetPosition(framePosition);
270     this->TextActor->SetOrientation(this->TempTransform->GetOrientation());
271 
272     // Update Line Actor
273     // WARNING: Transforming the Actor is cheaper than setting the geometry
274     double lineAnchor[3] = { framePosition[0] - frameOrigin[0], framePosition[1] - frameOrigin[1],
275       framePosition[2] - frameOrigin[2] };
276 
277     double lineDirection[3] = { controlPositionWC[0] - lineAnchor[0],
278       controlPositionWC[1] - lineAnchor[1], controlPositionWC[2] - lineAnchor[2] };
279 
280     this->LineActor->SetPosition(controlPositionWC);
281     this->LineActor->SetScale(vtkMath::Norm(lineDirection));
282 
283     double z[3] = { 0, 0, 1 };
284     double w = vtkMath::AngleBetweenVectors(lineDirection, z);
285     double xyz[3];
286     vtkMath::Cross(lineDirection, z, xyz);
287     this->TempTransform->Identity();
288     this->TempTransform->RotateWXYZ(vtkMath::DegreesFromRadians(-w), xyz[0], xyz[1], xyz[2]);
289     this->LineActor->SetOrientation(this->TempTransform->GetOrientation());
290   }
291 }
292 
293 //------------------------------------------------------------------------------
ReleaseGraphicsResources(vtkWindow * w)294 void vtkVRControlsHelper::ReleaseGraphicsResources(vtkWindow* w)
295 {
296   this->TextActor->ReleaseGraphicsResources(w);
297   this->LineActor->ReleaseGraphicsResources(w);
298 }
299 
300 //------------------------------------------------------------------------------
RenderOpaqueGeometry(vtkViewport * v)301 int vtkVRControlsHelper::RenderOpaqueGeometry(vtkViewport* v)
302 {
303   if (this->NeedUpdate)
304   {
305     this->UpdateRepresentation();
306   }
307 
308   if (!this->LabelVisible)
309   {
310     return 0;
311   }
312 
313   int count = 0;
314 
315   count += this->TextActor->RenderOpaqueGeometry(v);
316   count += this->LineActor->RenderOpaqueGeometry(v);
317 
318   return count;
319 }
320 
321 //------------------------------------------------------------------------------
RenderTranslucentPolygonalGeometry(vtkViewport * v)322 int vtkVRControlsHelper::RenderTranslucentPolygonalGeometry(vtkViewport* v)
323 {
324   if (this->NeedUpdate)
325   {
326     this->UpdateRepresentation();
327   }
328 
329   if (!this->LabelVisible)
330   {
331     return 0;
332   }
333 
334   int count = 0;
335 
336   count += this->TextActor->RenderTranslucentPolygonalGeometry(v);
337 
338   return count;
339 }
340 
341 //------------------------------------------------------------------------------
HasTranslucentPolygonalGeometry()342 vtkTypeBool vtkVRControlsHelper::HasTranslucentPolygonalGeometry()
343 {
344   if (!this->GetVisibility())
345   {
346     return 0;
347   }
348 
349   int result = 0;
350 
351   result |= this->TextActor->HasTranslucentPolygonalGeometry();
352 
353   return result;
354 }
355 
356 //------------------------------------------------------------------------------
BuildRepresentation()357 void vtkVRControlsHelper::BuildRepresentation()
358 {
359   // Compute text size in world coordinates
360   int bbox[4] = { 0, 0, 0, 0 };
361   this->TextActor->GetBoundingBox(bbox);
362 
363   double PPI = 450;                  // Screen resolution in pixels per inch
364   double FontSizeFactor = 1.0 / PPI; // Map font size to world coordinates
365 
366   double textSize[2] = { static_cast<double>(bbox[1] - bbox[0]),
367     static_cast<double>(bbox[3] - bbox[2]) };
368 
369   double textFrameWidth = this->TextActor->GetTextProperty()->GetFrameWidth();
370 
371   this->FrameSize[0] = (textSize[0] - 2.0 * textFrameWidth) * FontSizeFactor;
372   this->FrameSize[1] = (textSize[1] - 2.0 * textFrameWidth) * FontSizeFactor;
373 }
374 
375 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)376 void vtkVRControlsHelper::PrintSelf(ostream& os, vtkIndent indent)
377 {
378   this->Superclass::PrintSelf(os, indent);
379 
380   os << indent << "FrameSize: (" << this->FrameSize[0] << ", " << this->FrameSize[1] << ")\n";
381   this->TextActor->PrintSelf(os, indent);
382   os << indent << "Text: " << this->Text << "\n";
383   this->LineSource->PrintSelf(os, indent);
384   this->LineMapper->PrintSelf(os, indent);
385   this->LineActor->PrintSelf(os, indent);
386   os << indent << "ComponentName: " << this->ComponentName << "\n";
387   os << indent << "DrawSide: " << this->DrawSide << "\n";
388   os << indent << "ButtonSide: " << this->ButtonSide << "\n";
389   os << indent << "Enabled: " << this->Enabled << "\n";
390   os << indent << "ControlPositionLC: (" << this->ControlPositionLC[0] << ", "
391      << this->ControlPositionLC[1] << "," << this->ControlPositionLC[2] << ")\n";
392   os << indent << "LastPhysicalTranslation: (" << this->LastPhysicalTranslation[0] << ", "
393      << this->LastPhysicalTranslation[1] << ")\n";
394   os << indent << "LastEventPosition: (" << this->LastEventPosition[0] << ", "
395      << this->LastEventPosition[1] << "," << this->LastEventPosition[2] << ")\n";
396   os << indent << "LastEventOrientation: (" << this->LastEventOrientation[0] << ", "
397      << this->LastEventOrientation[1] << ", " << this->LastEventOrientation[2] << ","
398      << this->LastEventOrientation[3] << ")\n";
399   os << indent << "NeedUpdate: " << this->NeedUpdate << "\n";
400   os << indent << "LabelVisible: " << this->LabelVisible << "\n";
401 }
402 
403 //------------------------------------------------------------------------------
SetText(const std::string & _text)404 void vtkVRControlsHelper::SetText(const std::string& _text)
405 {
406   if (this->Text == _text)
407   {
408     return;
409   }
410   this->Text = _text;
411 
412   this->TextActor->SetInput(this->Text.c_str());
413   this->Modified();
414 }
415 
416 //------------------------------------------------------------------------------
SetEnabled(bool val)417 void vtkVRControlsHelper::SetEnabled(bool val)
418 {
419   if (val == this->Enabled)
420   {
421     return;
422   }
423 
424   this->Enabled = val;
425   this->SetVisibility(this->Enabled);
426   this->Modified();
427 }
428 
429 //------------------------------------------------------------------------------
SetRenderer(vtkRenderer * ren)430 void vtkVRControlsHelper::SetRenderer(vtkRenderer* ren)
431 {
432   if (ren == this->Renderer)
433   {
434     return;
435   }
436 
437   if (this->Renderer)
438   {
439     vtkRenderWindowInteractor* i =
440       static_cast<vtkRenderWindow*>(this->Renderer->GetVTKWindow())->GetInteractor();
441     i->RemoveObserver(this->ObserverTag);
442   }
443 
444   this->Renderer = ren;
445   if (this->Renderer)
446   {
447     vtkRenderWindowInteractor* i =
448       static_cast<vtkRenderWindow*>(this->Renderer->GetVTKWindow())->GetInteractor();
449     this->ObserverTag = i->AddObserver(vtkCommand::Move3DEvent, this->MoveCallbackCommand, 10.0);
450   }
451 
452   this->Modified();
453 }
454 
455 //------------------------------------------------------------------------------
GetRenderer()456 vtkRenderer* vtkVRControlsHelper::GetRenderer()
457 {
458   return this->Renderer;
459 }
460