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