1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkVRPanelRepresentation.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 "vtkVRPanelRepresentation.h"
16
17 #include "vtkCamera.h"
18 #include "vtkEventData.h"
19 #include "vtkObjectFactory.h"
20 #include "vtkQuaternion.h"
21 #include "vtkRenderer.h"
22 #include "vtkTextActor3D.h"
23 #include "vtkTextProperty.h"
24 #include "vtkTransform.h"
25 #include "vtkVRRenderWindow.h"
26 #include "vtkVectorOperators.h"
27
28 // TODO
29 // - add option to remove crop planes
30
31 vtkStandardNewMacro(vtkVRPanelRepresentation);
32
33 //------------------------------------------------------------------------------
vtkVRPanelRepresentation()34 vtkVRPanelRepresentation::vtkVRPanelRepresentation()
35 {
36 this->TextActor = vtkTextActor3D::New();
37 this->TextActor->GetTextProperty()->SetFontSize(17);
38 this->Text = "This is a Panel Widget";
39 this->TextActor->SetInput(this->Text.c_str());
40
41 vtkTextProperty* prop = this->TextActor->GetTextProperty();
42 this->TextActor->ForceOpaqueOn();
43 this->TextActor->SetUserMatrix(vtkMatrix4x4::New());
44 this->TextActor->GetUserMatrix()->Delete();
45 prop->SetFontFamilyToTimes();
46 prop->SetFrame(1);
47 prop->SetFrameWidth(12);
48 prop->SetFrameColor(0.0, 0.0, 0.0);
49 prop->SetBackgroundOpacity(0.5);
50 prop->SetBackgroundColor(0.0, 0.0, 0.0);
51 prop->SetFontSize(25);
52
53 this->InteractionState = vtkVRPanelRepresentation::Outside;
54 this->CoordinateSystem = World;
55 this->AllowAdjustment = true;
56 }
57
58 //------------------------------------------------------------------------------
~vtkVRPanelRepresentation()59 vtkVRPanelRepresentation::~vtkVRPanelRepresentation()
60 {
61 this->TextActor->Delete();
62 }
63
64 //------------------------------------------------------------------------------
SetCoordinateSystemToWorld()65 void vtkVRPanelRepresentation::SetCoordinateSystemToWorld()
66 {
67 if (this->CoordinateSystem == World)
68 {
69 return;
70 }
71 this->TextActor->GetUserMatrix()->Identity();
72 this->CoordinateSystem = World;
73 this->Modified();
74 }
75
76 //------------------------------------------------------------------------------
SetCoordinateSystemToHMD()77 void vtkVRPanelRepresentation::SetCoordinateSystemToHMD()
78 {
79 if (this->CoordinateSystem == HMD)
80 {
81 return;
82 }
83 this->CoordinateSystem = HMD;
84 this->Modified();
85 }
86
87 //------------------------------------------------------------------------------
SetCoordinateSystemToLeftController()88 void vtkVRPanelRepresentation::SetCoordinateSystemToLeftController()
89 {
90 if (this->CoordinateSystem == LeftController)
91 {
92 return;
93 }
94 this->CoordinateSystem = LeftController;
95 this->Modified();
96 }
97
98 //------------------------------------------------------------------------------
SetCoordinateSystemToRightController()99 void vtkVRPanelRepresentation::SetCoordinateSystemToRightController()
100 {
101 if (this->CoordinateSystem == RightController)
102 {
103 return;
104 }
105 this->CoordinateSystem = RightController;
106 this->Modified();
107 }
108
109 //------------------------------------------------------------------------------
ComputeComplexInteractionState(vtkRenderWindowInteractor *,vtkAbstractWidget *,unsigned long,void * calldata,int)110 int vtkVRPanelRepresentation::ComputeComplexInteractionState(
111 vtkRenderWindowInteractor*, vtkAbstractWidget*, unsigned long, void* calldata, int)
112 {
113 if (!this->AllowAdjustment || this->InteractionState == vtkVRPanelRepresentation::Moving)
114 {
115 return this->InteractionState;
116 }
117
118 vtkEventData* edata = static_cast<vtkEventData*>(calldata);
119 vtkEventDataDevice3D* edd = edata->GetAsEventDataDevice3D();
120 if (edd)
121 {
122 double pos[4];
123 edd->GetWorldPosition(pos);
124 pos[3] = 1.0;
125
126 const double* bds = this->TextActor->GetBounds();
127 double length = sqrt((bds[1] - bds[0]) * (bds[1] - bds[0]) +
128 (bds[3] - bds[2]) * (bds[3] - bds[2]) + (bds[5] - bds[4]) * (bds[5] - bds[4]));
129 double tolerance = length * 0.05;
130 if (pos[0] > bds[0] - tolerance && pos[0] < bds[1] + tolerance && pos[1] > bds[2] - tolerance &&
131 pos[1] < bds[3] + tolerance && pos[2] > bds[4] - tolerance && pos[2] < bds[5] + tolerance)
132 {
133 this->InteractionState = vtkVRPanelRepresentation::Moving;
134 }
135 else
136 {
137 this->InteractionState = vtkVRPanelRepresentation::Outside;
138 }
139 }
140
141 return this->InteractionState;
142 }
143
144 //------------------------------------------------------------------------------
StartComplexInteraction(vtkRenderWindowInteractor *,vtkAbstractWidget *,unsigned long,void * calldata)145 void vtkVRPanelRepresentation::StartComplexInteraction(
146 vtkRenderWindowInteractor*, vtkAbstractWidget*, unsigned long, void* calldata)
147 {
148 vtkEventData* edata = static_cast<vtkEventData*>(calldata);
149 vtkEventDataDevice3D* edd = edata->GetAsEventDataDevice3D();
150 if (edd)
151 {
152 edd->GetWorldPosition(this->StartEventPosition);
153 this->LastEventPosition[0] = this->StartEventPosition[0];
154 this->LastEventPosition[1] = this->StartEventPosition[1];
155 this->LastEventPosition[2] = this->StartEventPosition[2];
156 edd->GetWorldOrientation(this->StartEventOrientation);
157 std::copy(
158 this->StartEventOrientation, this->StartEventOrientation + 4, this->LastEventOrientation);
159 }
160 }
161
162 //------------------------------------------------------------------------------
ComplexInteraction(vtkRenderWindowInteractor *,vtkAbstractWidget *,unsigned long,void * calldata)163 void vtkVRPanelRepresentation::ComplexInteraction(
164 vtkRenderWindowInteractor*, vtkAbstractWidget*, unsigned long, void* calldata)
165 {
166 vtkEventData* edata = static_cast<vtkEventData*>(calldata);
167 vtkEventDataDevice3D* edd = edata->GetAsEventDataDevice3D();
168 if (edd)
169 {
170 double eventPos[3];
171 edd->GetWorldPosition(eventPos);
172 double eventDir[4];
173 edd->GetWorldOrientation(eventDir);
174
175 // Process the motion
176 if (this->InteractionState == vtkVRPanelRepresentation::Moving)
177 {
178 // this->TranslateOutline(this->LastEventPosition, eventPos);
179 this->UpdatePose(this->LastEventPosition, this->LastEventOrientation, eventPos, eventDir);
180 }
181
182 // Book keeping
183 this->LastEventPosition[0] = eventPos[0];
184 this->LastEventPosition[1] = eventPos[1];
185 this->LastEventPosition[2] = eventPos[2];
186 std::copy(eventDir, eventDir + 4, this->LastEventOrientation);
187 this->Modified();
188 }
189 }
190
191 //------------------------------------------------------------------------------
EndComplexInteraction(vtkRenderWindowInteractor *,vtkAbstractWidget *,unsigned long,void *)192 void vtkVRPanelRepresentation::EndComplexInteraction(
193 vtkRenderWindowInteractor*, vtkAbstractWidget*, unsigned long, void*)
194 {
195 this->InteractionState = vtkVRPanelRepresentation::Outside;
196 }
197
198 //------------------------------------------------------------------------------
199 // Loop through all points and translate and rotate them
UpdatePose(double * p1,double * orient1,double * p2,double * orient2)200 void vtkVRPanelRepresentation::UpdatePose(double* p1, double* orient1, double* p2, double* orient2)
201 {
202 if (this->CoordinateSystem == World)
203 {
204 this->UpdatePropPose(this->TextActor, p1, orient1, p2, orient2);
205 return;
206 }
207
208 if (this->CoordinateSystem == HMD)
209 {
210 vtkMatrix4x4* mat = this->TextActor->GetUserMatrix();
211 mat->Register(this);
212 this->TextActor->SetUserMatrix(nullptr);
213
214 this->TempMatrix->DeepCopy(mat);
215 this->TempMatrix->Invert();
216 double p14[4];
217 std::copy(p1, p1 + 3, p14);
218 p14[3] = 1.0;
219 double p24[4];
220 std::copy(p2, p2 + 3, p24);
221 p24[3] = 1.0;
222 this->TempMatrix->MultiplyPoint(p14, p14);
223 this->TempMatrix->MultiplyPoint(p24, p24);
224
225 double trans[3];
226 for (int i = 0; i < 3; i++)
227 {
228 trans[i] = p24[i] - p14[i];
229 }
230
231 vtkTransform* newTransform = this->TempTransform;
232
233 // changes in Z adjust the scale
234 double ratio = (0.5 + trans[2] / this->LastScale) / 0.5;
235 double* scale = this->TextActor->GetScale();
236 this->TextActor->SetScale(scale[0] * ratio, scale[1] * ratio, scale[2] * ratio);
237 this->TextActor->AddPosition(trans[0], trans[1], 0.0);
238
239 // compute the net rotation
240 vtkQuaternion<double> q1;
241 q1.SetRotationAngleAndAxis(
242 vtkMath::RadiansFromDegrees(orient1[0]), orient1[1], orient1[2], orient1[3]);
243 vtkQuaternion<double> q2;
244 q2.SetRotationAngleAndAxis(
245 vtkMath::RadiansFromDegrees(orient2[0]), orient2[1], orient2[2], orient2[3]);
246 q1.Conjugate();
247 q2 = q2 * q1;
248 double axis[4];
249 axis[0] = vtkMath::DegreesFromRadians(q2.GetRotationAngleAndAxis(axis + 1));
250
251 newTransform->Identity();
252 newTransform->PostMultiply();
253 newTransform->Concatenate(this->TempMatrix);
254 newTransform->TransformNormal(axis + 1, axis + 1);
255
256 vtkMatrix4x4* oldMatrix = this->TempMatrix;
257 this->TextActor->GetMatrix(oldMatrix);
258
259 newTransform->Identity();
260 newTransform->PostMultiply();
261 newTransform->Concatenate(oldMatrix);
262 newTransform->Translate(-(p14[0]), -(p14[1]), -(p14[2]));
263 newTransform->RotateWXYZ(axis[0], axis[1], axis[2], axis[3]);
264 newTransform->Translate(p14[0], p14[1], p14[2]);
265
266 this->TextActor->SetPosition(newTransform->GetPosition());
267 this->TextActor->SetOrientation(newTransform->GetOrientation());
268 this->TextActor->SetUserMatrix(mat);
269 mat->UnRegister(this);
270 }
271
272 if (this->CoordinateSystem == LeftController || this->CoordinateSystem == RightController)
273 {
274 vtkMatrix4x4* mat = this->TextActor->GetUserMatrix();
275 mat->Register(this);
276 this->TextActor->SetUserMatrix(nullptr);
277
278 this->TempMatrix->DeepCopy(mat);
279 this->TempMatrix->Invert();
280 double p14[4];
281 std::copy(p1, p1 + 3, p14);
282 p14[3] = 1.0;
283 double p24[4];
284 std::copy(p2, p2 + 3, p24);
285 p24[3] = 1.0;
286 this->TempMatrix->MultiplyPoint(p14, p14);
287 this->TempMatrix->MultiplyPoint(p24, p24);
288
289 double trans[3];
290 for (int i = 0; i < 3; i++)
291 {
292 trans[i] = p24[i] - p14[i];
293 }
294 this->TextActor->AddPosition(trans[0], trans[1], trans[2]);
295
296 vtkTransform* newTransform = this->TempTransform;
297
298 // compute the net rotation
299 vtkQuaternion<double> q1;
300 q1.SetRotationAngleAndAxis(
301 vtkMath::RadiansFromDegrees(orient1[0]), orient1[1], orient1[2], orient1[3]);
302 vtkQuaternion<double> q2;
303 q2.SetRotationAngleAndAxis(
304 vtkMath::RadiansFromDegrees(orient2[0]), orient2[1], orient2[2], orient2[3]);
305 q1.Conjugate();
306 q2 = q2 * q1;
307 double axis[4];
308 axis[0] = vtkMath::DegreesFromRadians(q2.GetRotationAngleAndAxis(axis + 1));
309
310 newTransform->Identity();
311 newTransform->PostMultiply();
312 newTransform->Concatenate(this->TempMatrix);
313 newTransform->TransformNormal(axis + 1, axis + 1);
314
315 vtkMatrix4x4* oldMatrix = this->TempMatrix;
316 this->TextActor->GetMatrix(oldMatrix);
317
318 newTransform->Identity();
319 newTransform->PostMultiply();
320 newTransform->Concatenate(oldMatrix);
321 newTransform->Translate(-(p14[0]), -(p14[1]), -(p14[2]));
322 newTransform->RotateWXYZ(axis[0], axis[1], axis[2], axis[3]);
323 newTransform->Translate(p14[0], p14[1], p14[2]);
324
325 this->TextActor->SetPosition(newTransform->GetPosition());
326 this->TextActor->SetOrientation(newTransform->GetOrientation());
327 this->TextActor->SetUserMatrix(mat);
328 mat->UnRegister(this);
329 }
330 }
331
332 //------------------------------------------------------------------------------
ReleaseGraphicsResources(vtkWindow * w)333 void vtkVRPanelRepresentation::ReleaseGraphicsResources(vtkWindow* w)
334 {
335 this->TextActor->ReleaseGraphicsResources(w);
336 }
337
338 //------------------------------------------------------------------------------
ComputeMatrix(vtkRenderer * ren)339 void vtkVRPanelRepresentation::ComputeMatrix(vtkRenderer* ren)
340 {
341 // check whether or not need to rebuild the matrix
342 // only rebuild on left eye otherwise we get two different
343 // poses for two eyes
344 vtkCamera* cam = ren->GetActiveCamera();
345 if (this->CoordinateSystem != World && cam->GetLeftEye())
346 {
347 vtkVRRenderWindow* rw = static_cast<vtkVRRenderWindow*>(ren->GetVTKWindow());
348
349 if (this->CoordinateSystem == HMD)
350 {
351 vtkTransform* vt = cam->GetViewTransformObject();
352 vt->GetInverse(this->TextActor->GetUserMatrix());
353
354 if (rw->GetPhysicalScale() != this->LastScale)
355 {
356 double ratio = rw->GetPhysicalScale() / this->LastScale;
357 double* scale = this->TextActor->GetScale();
358 this->TextActor->SetScale(scale[0] * ratio, scale[1] * ratio, scale[2] * ratio);
359 double* pos = this->TextActor->GetPosition();
360 this->TextActor->SetPosition(pos[0] * ratio, pos[1] * ratio, -0.5 * rw->GetPhysicalScale());
361 this->LastScale = rw->GetPhysicalScale();
362 }
363 else
364 {
365 double* pos = this->TextActor->GetPosition();
366 this->TextActor->SetPosition(pos[0], pos[1], -0.5 * rw->GetPhysicalScale());
367 }
368 }
369
370 vtkEventDataDevice device = this->CoordinateSystem == LeftController
371 ? vtkEventDataDevice::LeftController
372 : vtkEventDataDevice::RightController;
373
374 vtkNew<vtkMatrix4x4> poseMatrixWorld;
375 if (rw->GetPoseMatrixWorldFromDevice(device, poseMatrixWorld))
376 {
377 this->TextActor->GetUserMatrix()->DeepCopy(poseMatrixWorld);
378 }
379 }
380 }
381
382 //------------------------------------------------------------------------------
RenderOpaqueGeometry(vtkViewport * v)383 int vtkVRPanelRepresentation::RenderOpaqueGeometry(vtkViewport* v)
384 {
385 if (!this->GetVisibility())
386 {
387 return 0;
388 }
389
390 // make sure the device has the same matrix
391 if (true /* HMD coords */)
392 {
393 vtkRenderer* ren = static_cast<vtkRenderer*>(v);
394 this->ComputeMatrix(ren);
395 }
396
397 int count = this->TextActor->RenderOpaqueGeometry(v);
398 return count;
399 }
400
401 //------------------------------------------------------------------------------
RenderTranslucentPolygonalGeometry(vtkViewport * v)402 int vtkVRPanelRepresentation::RenderTranslucentPolygonalGeometry(vtkViewport* v)
403 {
404 if (!this->GetVisibility())
405 {
406 return 0;
407 }
408
409 int count = this->TextActor->RenderTranslucentPolygonalGeometry(v);
410
411 return count;
412 }
413
414 //------------------------------------------------------------------------------
HasTranslucentPolygonalGeometry()415 vtkTypeBool vtkVRPanelRepresentation::HasTranslucentPolygonalGeometry()
416 {
417 if (!this->GetVisibility())
418 {
419 return 0;
420 }
421
422 int result = 0;
423
424 result |= this->TextActor->HasTranslucentPolygonalGeometry();
425
426 return result;
427 }
428
429 //------------------------------------------------------------------------------
PlaceWidget(double bds[6])430 void vtkVRPanelRepresentation::PlaceWidget(double bds[6])
431 {
432 this->TextActor->GetUserMatrix()->Identity();
433 if (this->CoordinateSystem == World)
434 {
435 // center the planel
436 this->TextActor->SetPosition(
437 0.5 * (bds[0] + bds[1]), 0.5 * (bds[2] + bds[3]), 0.5 * (bds[4] + bds[5]));
438
439 double length = sqrt((bds[1] - bds[0]) * (bds[1] - bds[0]) +
440 (bds[3] - bds[2]) * (bds[3] - bds[2]) + (bds[5] - bds[4]) * (bds[5] - bds[4]));
441 this->TextActor->SetScale(length / 700.0, length / 700.0, length / 700.0);
442 this->LastScale = length;
443 }
444
445 if (this->CoordinateSystem != World)
446 {
447 double scale = sqrt((bds[1] - bds[0]) * (bds[1] - bds[0]) +
448 (bds[3] - bds[2]) * (bds[3] - bds[2]) + (bds[5] - bds[4]) * (bds[5] - bds[4]));
449 this->TextActor->SetScale(scale / 700.0, scale / 700.0, scale / 700.0);
450 this->LastScale = scale;
451 this->TextActor->SetPosition(0.0, 0.0, -0.5 * scale);
452 }
453 }
454
PlaceWidgetExtended(const double * bds,const double * normal,const double * upvec,double scale)455 void vtkVRPanelRepresentation::PlaceWidgetExtended(
456 const double* bds, const double* normal, const double* upvec, double scale)
457 {
458 this->TextActor->GetUserMatrix()->Identity();
459 this->TextActor->SetOrientation(0, 0, 0);
460 this->LastScale = scale;
461
462 // grab the bounding box of the text
463 // so we can position and scale to that
464 int tbounds[4];
465 this->TextActor->GetBoundingBox(tbounds);
466 int maxdim = tbounds[1] - tbounds[0];
467 if (tbounds[3] - tbounds[2] > maxdim)
468 {
469 maxdim = tbounds[3] - tbounds[2];
470 }
471 // should always be at least 50 pixels
472 // for any reasonable string.
473 maxdim = maxdim > 50 ? maxdim : 50.0;
474
475 // make the normal ortho to upvec
476 vtkVector3d nup(upvec);
477 nup.Normalize();
478 vtkVector3d nvpn(normal);
479 nvpn.Normalize();
480 vtkVector3d nvright = nup.Cross(nvpn);
481 nvright.Normalize();
482 nvpn = nvright.Cross(nup);
483
484 double basis[16];
485 basis[0] = nvright[0];
486 basis[4] = nvright[1];
487 basis[8] = nvright[2];
488 basis[12] = 0;
489 basis[1] = nup[0];
490 basis[5] = nup[1];
491 basis[9] = nup[2];
492 basis[13] = 0;
493 basis[2] = nvpn[0];
494 basis[6] = nvpn[1];
495 basis[10] = nvpn[2];
496 basis[14] = 0;
497 basis[3] = 0;
498 basis[7] = 0;
499 basis[11] = 0;
500 basis[15] = 1;
501
502 vtkNew<vtkTransform> basisT;
503 basisT->SetMatrix(basis);
504 this->TextActor->SetOrientation(basisT->GetOrientation());
505
506 if (this->CoordinateSystem == World)
507 {
508 // center the planel and default size to 400cm
509 this->TextActor->SetScale(0.4 * scale / maxdim, 0.4 * scale / maxdim, 0.4 * scale / maxdim);
510 this->TextActor->SetPosition(
511 0.5 * (bds[0] + bds[1]), 0.5 * (bds[2] + bds[3]), 0.5 * (bds[4] + bds[5]));
512 }
513
514 if (this->CoordinateSystem == LeftController || this->CoordinateSystem == RightController)
515 {
516 // position the planel and default size to 400cm
517 this->TextActor->SetScale(0.4 / maxdim, 0.4 / maxdim, 0.4 / maxdim);
518 this->TextActor->SetPosition(0.5 * (bds[0] + bds[1]) - 0.2 * (tbounds[1] - tbounds[0]) / maxdim,
519 0.5 * (bds[2] + bds[3]), 0.5 * (bds[4] + bds[5]));
520 }
521
522 if (this->CoordinateSystem == HMD)
523 {
524 // center the planel and default size to 400cm
525 this->TextActor->SetScale(0.4 * scale / maxdim, 0.4 * scale / maxdim, 0.4 * scale / maxdim);
526 this->TextActor->SetPosition(-0.2 * (tbounds[1] - tbounds[0]) * scale / maxdim,
527 -0.2 * (tbounds[3] - tbounds[2]) * scale / maxdim, -0.5 * scale);
528 }
529 }
530
531 //------------------------------------------------------------------------------
BuildRepresentation()532 void vtkVRPanelRepresentation::BuildRepresentation()
533 {
534 // if (this->GetMTime() > this->BuildTime)
535 // {
536 // this->TextActor3D->SetPosition(tpos);
537
538 // // scale should cover 10% of FOV
539 // double fov = ren->GetActiveCamera()->GetViewAngle();
540 // double tsize = 0.1*2.0*atan(fov*0.5); // 10% of fov
541 // tsize /= 200.0; // about 200 pixel texture map
542 // scale *= tsize;
543 // this->TextActor3D->SetScale(scale, scale, scale);
544 // this->TextActor3D->SetInput(text.c_str());
545
546 // this->BuildTime.Modified();
547 // }
548 }
549
550 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)551 void vtkVRPanelRepresentation::PrintSelf(ostream& os, vtkIndent indent)
552 {
553 this->Superclass::PrintSelf(os, indent);
554 }
555
556 //------------------------------------------------------------------------------
SetText(const char * text)557 void vtkVRPanelRepresentation::SetText(const char* text)
558 {
559 if (this->Text == text)
560 {
561 return;
562 }
563 this->Text = text;
564
565 this->TextActor->SetInput(this->Text.c_str());
566 this->Modified();
567 }
568