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