AnimatLab  2
Test
OsgCameraManipulator.cpp
1 #include "StdAfx.h"
2 
3 #include "OsgCameraManipulator.h"
4 #include "OsgMovableItem.h"
5 #include "OsgBody.h"
6 #include "OsgRigidBody.h"
7 #include "OsgJoint.h"
8 #include "OsgMouseSpring.h"
9 #include "OsgStructure.h"
10 #include "OsgLight.h"
11 #include "OsgUserData.h"
12 #include "OsgDragger.h"
13 #include "OsgSimulator.h"
14 
15 namespace OsgAnimatSim
16 {
17  namespace Visualization
18  {
19 
20 OsgCameraManipulator::OsgCameraManipulator(Simulator *lpSim, osgViewer::Viewer *osgViewer, osg::Viewport *osgViewport)
21 {
22  m_lpSim = lpSim;
23  m_lpOsgSim = dynamic_cast<OsgSimulator *>(lpSim);
24  m_osgViewer = osgViewer;
25  m_osgViewport = osgViewport;
26  m_lpPicked = NULL;
27 
28  m_bShiftDown = false;
29  m_bControlDown = false;
30  m_bInDrag = false;
31 
32  m_fltPrevX = 0;
33  m_fltPrevY = 0;
34 
35  m_v3Eye.set(0,0, 10);
36 
37  //Set this very small to prevent it from keeping you from zooming in as close as you need to for a part.
38  this->setMinimumDistance(0.00001);
39 }
40 
41 OsgCameraManipulator::~OsgCameraManipulator(void)
42 {
43  m_lpOsgSim->MouseSpring()->SetRigidBody(NULL);
44 }
45 
46 
47 bool OsgCameraManipulator::handle(const GUIEventAdapter& ea, GUIActionAdapter& aa)
48 {
49  bool bHandled = false;
50 
51  try
52  {
53  float x = ea.getX();
54  float y = ea.getY();
55 
56  //Take an action based on the event type
57  switch(ea.getEventType())
58  {
59  case(GUIEventAdapter::PUSH):
60  {
61  pick(ea, aa);
62  break;
63  }
64 
65  case(GUIEventAdapter::RELEASE):
66  {
67  m_lpOsgSim->MouseSpring()->Visible(false);
68 
69  if(!m_bInDrag)
70  {
71  if(m_lpPicked && m_lpSim && m_lpSim->VisualSelectionMode() != SIMULATION_SELECTION_MODE)
72  {
73  //Change the selected vertex of the selected part.
74  m_lpPicked->SelectedVertex(m_vSelectedVertex.x(), m_vSelectedVertex.y(), m_vSelectedVertex.z(), true, true);
75 
76  if(m_lpSim->AddBodiesMode())
77  m_lpPicked->AddBodyClicked(m_vPickPoint.x(), m_vPickPoint.y(), m_vPickPoint.z(),
78  m_vPickNormal.x(), m_vPickNormal.y(), m_vPickNormal.z());
79  else
80  m_lpPicked->Selected(true, m_bControlDown);
81  }
82  }
83 
84  m_bInDrag = false;
85  break;
86  }
87 
88  case(GUIEventAdapter::DRAG):
89  {
90 
91  m_bInDrag = true;
92  //if both left control and left shift are pressed then
93  //do the mouse spring
94  if(m_bShiftDown && m_bControlDown && CanDoMouseSpring())
95  bHandled = DoMouseSpring(ea, x, y);
96  //if just left shift is pressed then do pan
97  else if(m_bShiftDown)
98  {
99  DoPan(ea, x, y);
100  bHandled = true;
101  }
103  //else if(m_bLeftControl)
104  //{
105  // DoZoom(ea, x, y);
106  //}
108  //else
109  //{
110  // DoRotate(ea, x, y);
111  //}
112  break;
113  }
114 
115  case(GUIEventAdapter::KEYDOWN):
116  {
117  //std::string strText = "Key Down" + STR(ea.getKey());
118  //OutputDebugString(strText.c_str());
119  if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Shift_L)
120  {
121  m_bShiftDown = true;
122  //bHandled = true;
123  }
124 
125  if(ea.getKey() == osgGA::GUIEventAdapter::KEY_Control_L)
126  {
127  m_bControlDown = true;
128  //bHandled = true;
129  }
130 
131  if(m_bInDrag && m_bShiftDown && m_bControlDown)
132  {
133  bHandled = DoMouseSpring(ea, x, y);
134  //bHandled = true;
135  }
136  break;
137  }
138 
139  case(GUIEventAdapter::KEYUP):
140  {
141  //std::string strText = "Key Up" + STR(ea.getKey());
142  //OutputDebugString(strText.c_str());
143  if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Shift_L)
144  {
145  m_bShiftDown = false;
146  //bHandled = true;
147  }
148 
149  if(ea.getKey() == osgGA::GUIEventAdapter::KEY_Control_L)
150  {
151  m_bControlDown = false;
152  //bHandled = true;
153  }
154 
155  if(ea.getKey() == osgGA::GUIEventAdapter::KEY_F10 && m_lpSim)
156  m_lpSim->ResetSimulation();
157 
158  if(ea.getKey() == osgGA::GUIEventAdapter::KEY_F5 && m_lpSim)
159  m_lpSim->ToggleSimulation();
160 
161  break;
162  }
163  default:
164  bHandled = false;
165  }//end switch statement
166 
167  if(!bHandled)
168  bHandled = osgGA::TrackballManipulator::handle(ea, aa);
169 
170  m_fltPrevX = x;
171  m_fltPrevY = y;
172 
173  } //Eat any erorrs here.
174  catch(CStdErrorInfo oError)
175  {return false;}
176  catch(...)
177  {return false;}
178 
179  return bHandled;
180 }
181 
182 const osg::Camera *OsgCameraManipulator::GetCamera(float x, float y, float &local_x, float &local_y)
183 {
184  const osg::Camera* camera = m_osgViewer->getCameraContainingPosition(x, y, local_x, local_y);
185  if (!camera) camera = m_osgViewer->getCamera();
186  return camera;
187 }
188 
189 const osg::Camera *OsgCameraManipulator::GetCamera(float x, float y)
190 {
191  float local_x=0, local_y=0;
192  const osg::Camera* camera = m_osgViewer->getCameraContainingPosition(x, y, local_x, local_y);
193  if (!camera) camera = m_osgViewer->getCamera();
194  return camera;
195 }
196 
197 void OsgCameraManipulator::pick(const osgGA::GUIEventAdapter& ea, GUIActionAdapter& aa)
198 {
199  osgUtil::LineSegmentIntersector::Intersections intersections;
200 
201  m_lpPicked = NULL;
202 
203  //We have clicked something else so set the rigid body to null.
204  m_lpOsgSim->MouseSpring()->SetRigidBody(NULL);
205 
206  float x = ea.getX();
207  float y = ea.getY();
208 
209  float local_x, local_y = 0.0;
210  const osg::Camera* camera = GetCamera(x, y, local_x, local_y);
211 
212  osgUtil::LineSegmentIntersector::CoordinateFrame cf = camera->getViewport() ? osgUtil::Intersector::WINDOW : osgUtil::Intersector::PROJECTION;
213  osg::ref_ptr< osgUtil::LineSegmentIntersector > picker = new osgUtil::LineSegmentIntersector(cf, local_x, local_y);
214 
215  RigidBody *lpBody = NULL;
216  Joint *lpJoint = NULL;
217  Structure *lpStruct = NULL;
218  Light *lpLight = NULL;
219  if (m_osgViewer->computeIntersections(x,y,intersections))
220  {
221 
222 #pragma region LineSegmentIntersector
223 
224  for(osgUtil::LineSegmentIntersector::Intersections::iterator hitr = intersections.begin();
225  hitr != intersections.end();
226  ++hitr)
227  {
228  //Look for the first drawable object that has a defined user data.
229  if (hitr->drawable.valid() && hitr->drawable->getUserData())
230  {
231  //If the user data is a OsgUserData object then lets use it.
232  OsgUserData *osgData = dynamic_cast<OsgUserData *>(hitr->drawable->getUserData());
233 
234  if(osgData && m_lpSim)
235  {
236  lpBody = osgData->GetBody();
237  lpJoint = osgData->GetJoint();
238  lpStruct = osgData->GetStructure();
239  lpLight = osgData->GetLight();
240 
241  m_vPickPoint = hitr->getWorldIntersectPoint();
242  m_vPickNormal = hitr->getWorldIntersectNormal();
243  m_vSelectedVertex = FindSelectedVertex(hitr);
244 
245  switch (m_lpSim->VisualSelectionMode())
246  {
247  case GRAPHICS_SELECTION_MODE:
248  case COLLISION_SELECTION_MODE:
249  //if(lpBody)
250  //{ //For debugging.
251  // bool bManip = lpBody->AllowMouseManipulation();
252  // bool bVisible = lpBody->IsVisible();
253  // int iType = lpBody->VisualSelectionType();
254  // int iMode = m_lpSim->VisualSelectionMode();
255  // iMode=iType;
256  //}
257 
258  if(lpBody && lpBody->AllowMouseManipulation() && lpBody->IsVisible() && (lpBody->VisualSelectionType() & m_lpSim->VisualSelectionMode()) )
259  {
260  m_lpPicked = lpBody;
261  return;
262  }
263  if(lpStruct && lpStruct->IsVisible() )
264  {
265  m_lpPicked = lpStruct;
266  return;
267  }
268  if(lpLight && lpLight->IsVisible() )
269  {
270  m_lpPicked = lpLight;
271  return;
272  }
273  break;
274 
275  case JOINT_SELECTION_MODE:
276  if(lpJoint && lpJoint->AllowMouseManipulation() && lpJoint->IsVisible() && (lpJoint->VisualSelectionType() & m_lpSim->VisualSelectionMode()) )
277  {
278  m_lpPicked = lpJoint;
279  return;
280  }
281  break;
282 
283  case RECEPTIVE_FIELD_SELECTION_MODE:
284  if(lpBody && lpBody->AllowMouseManipulation() && lpBody->IsVisible() && (lpBody->VisualSelectionType() == COLLISION_SELECTION_MODE) )
285  {
286  m_lpPicked = lpBody;
287  return;
288  }
289  break;
290 
291  case SIMULATION_SELECTION_MODE:
292  if(lpBody && lpBody->AllowMouseManipulation() && lpBody->IsVisible() && (lpBody->VisualSelectionType() & m_lpSim->VisualSelectionMode()) )
293  {
294  m_lpOsgSim->MouseSpring()->SetRigidBody(osgData->GetBody());
295  m_lpOsgSim->MouseSpring()->SetGrabPosition(hitr->getLocalIntersectPoint());
296  m_lpPicked = osgData->GetBody();
297  return;
298  }
299  break;
300 
301  default:
302  break;
303  }
304  }
305  }
306  }
307 #pragma endregion
308 
309  }
310 
311  //If nothing was picked then set the mouse spring body to null
312  m_lpOsgSim->MouseSpring()->SetRigidBody(NULL);
313  m_lpPicked = NULL;
314 }
315 
316 osg::Vec3 OsgCameraManipulator::FindSelectedVertex(osgUtil::LineSegmentIntersector::Intersections::iterator &hitr)
317 {
318  //Get the selected vertex.
319  const osgUtil::LineSegmentIntersector::Intersection::IndexList& vil = hitr->indexList;
320  const osgUtil::LineSegmentIntersector::Intersection::RatioList& ral = hitr->ratioList;
321  osg::Geometry *osgGeom = dynamic_cast<osg::Geometry *>(hitr->drawable.get());
322  if(vil.size() > 0 && osgGeom)
323  {
324  //First get the vertex array.
325  osg::ref_ptr<osg::Vec3Array> aryVert = dynamic_cast<osg::Vec3Array*>(osgGeom->getVertexArray());
326 
327  //Then we need to find the vertex that was closest to the hit. We do this by first interpolating the
328  //hit position using the barycentric coords. Then find the vertex that is closest to that point.
329  if(vil.size() != ral.size())
330  THROW_ERROR(Osg_Err_lHitArrayMismatch, Osg_Err_strHitArrayMismatch);
331 
332  int iSize = vil.size();
333  osg::Vec3 osgHit(0, 0, 0);
334  for(int iIdx=0; iIdx<iSize; iIdx++)
335  {
336  osgHit += ((*aryVert)[vil[iIdx]])*ral[iIdx];
337  }
338 
339  double dblMin = 10000, dblDist = 0;
340  osg::Vec3 vSelVert;
341  for(int iIdx=0; iIdx<iSize; iIdx++)
342  {
343  osg::Vec3 osgVert = (*aryVert)[vil[iIdx]];
344  dblDist = Std_CalculateDistance(osgHit.x(), osgHit.y(), osgHit.z(), osgVert.x(), osgVert.y(), osgVert.z());
345  if(dblDist < dblMin)
346  {
347  dblMin = dblDist;
348  vSelVert = osgVert;
349  }
350  }
351 
352  return vSelVert;
353  }
354  else
355  return osg::Vec3(0, 0, 0);
356 
357 }
358 
359 /*
360 void OsgCameraManipulator::GetPickedFace(int iIndex, osg::Geometry *osgGeo)
361 {
362  osg::Vec3Array *aryVerts = (osg::Vec3Array *) geo->getVertexArray();
363  iSize = aryVerts->getNumElements();
364  if(iIndex >=0 && iIndex < iSize)
365  {
366  m_vPickedFace = (osg::Vec3) aryVerts->at(iIndex);
367 
368  }
369 }
370 */
371 
372 bool OsgCameraManipulator::CanDoMouseSpring()
373 {
374  if(m_lpOsgSim->MouseSpring()->GetRigidBody() && m_lpOsgSim->MouseSpring()->GetMovableItem())
375  return true;
376  else
377  return false;
378 }
379 
380 bool OsgCameraManipulator::DoMouseSpring(const GUIEventAdapter& ea, float x, float y)
381 {
382  m_lpOsgSim->MouseSpring()->Visible(false);
383 
384  OsgMovableItem *osgRBBody = m_lpOsgSim->MouseSpring()->GetMovableItem();
385  RigidBody *rbBody = m_lpOsgSim->MouseSpring()->GetRigidBody();
386 
387  if (!osgRBBody || !rbBody)
388  return false;
389 
390  m_lpOsgSim->MouseSpring()->Visible(true);
391 
392  //char strDest[150];
393 
394  //get the grab position on the body
395  osg::Vec3 vGrabPos = m_lpOsgSim->MouseSpring()->GetGrabPosition() *osgRBBody->GetCameraMatrixTransform()->getWorldMatrices().at(0);
396  m_lpOsgSim->MouseSpring()->SetStart(vGrabPos);
397 
398  //get the 3D mouse coords
399  osg::Vec3 v3End = ConvertMouseTo3D(ea, x, y, vGrabPos);
400  m_lpOsgSim->MouseSpring()->SetEnd(v3End);
401 
402  //Calculate the force from the spring
403  osg::Vec3 vSpringLength = ((v3End - vGrabPos));
404  osg::Vec3 vSpringForce = vSpringLength * m_lpSim->MouseSpringStiffness();
405 
406  //sprintf(strDest, "Spring Force: (%3.3f, %3.3f, %3.3f)\n", vSpringForce[0], vSpringForce[1], vSpringForce[2]);
407  //OutputDebugString(strDest);
408 
409  //And the force from damping of the spring.
410  CStdFPoint fpBodyVel = rbBody->GetVelocityAtPoint(vGrabPos.x(), vGrabPos.y(), vGrabPos.z());
411  osg::Vec3 v3BodyVel(fpBodyVel.x, fpBodyVel.y, fpBodyVel.z);
412  osg::Vec3 v3DampForce = (v3BodyVel) * m_lpSim->MouseSpringDamping();
413 
414  //sprintf(strDest, "Damping Force: (%3.3f, %3.3f, %3.3f)\n", v3DampForce[0], v3DampForce[1], v3DampForce[2]);
415  //OutputDebugString(strDest);
416 
417  //Then the total force = Spring force - damping force
418  osg::Vec3 vTotalForce = vSpringForce - v3DampForce;
419  rbBody->AddForceAtWorldPos(vGrabPos.x(), vGrabPos.y(), vGrabPos.z(), vTotalForce.x(), vTotalForce.y(), vTotalForce.z(), false);
420 
421  m_lpSim->MouseSpringForceMagnitude(vTotalForce.length(), true);
422  m_lpSim->MouseSpringDampingForceMagnitude(v3DampForce.length(), true);
423  m_lpSim->MouseSpringLengthMagnitude(vSpringLength.length(), true);
424 
425  //sprintf(strDest, "Length: (%3.3f, %3.3f, %3.3f) MS: (%3.1f, %3.1f, %3.1f)-(%3.1f, %3.1f, %3.1f) FORCE: (%3.1f, %3.1f, %3.1f)\n\n", vSpringLength[0], vSpringLength[1], vSpringLength[2], vGrabPos[0], vGrabPos[1], vGrabPos[2], v3End[0], v3End[1], v3End[2], vTotalForce[0], vTotalForce[1], vTotalForce[2]);
426  //OutputDebugString(strDest);
427 
428  return true;
429 }
430 
431 osg::Vec3 OsgCameraManipulator::ConvertMouseTo3D(const GUIEventAdapter& ea, int x, int y, osg::Vec3 vGrabPos)
432 {
433 
434  //get the camera of the view
435  const osg::Camera *camera = m_osgViewer->getCamera();
436 
437  //get the viewport of the camera
438  const osg::Viewport* viewport = camera->getViewport();
439 
440  if(!viewport)
441  viewport = m_osgViewport.get();
442 
443  //convert normalized mouse points to pixels
444  float mx = viewport->x() + (int)((float)viewport->width()*(ea.getXnormalized()*0.5f+0.5f));
445  float my = viewport->y() + (int)((float)viewport->height()*(ea.getYnormalized()*0.5f+0.5f));
446 
447  //compute the final window matrix
448  osg::Matrix matrix = camera->getViewMatrix() * camera->getProjectionMatrix() * viewport->computeWindowMatrix();
449 
450  //invert it so I can get world coords from the window matrix for the mouse
451  osg::Matrix inverseWM ;
452  inverseWM.invert(matrix);
453 
454  // x, y come from ea.getX() and ea.getY()
455  osg::Vec3 near_point = osg::Vec3(mx, my, 0.0) * inverseWM;
456  osg::Vec3 far_point = osg::Vec3(mx, my, 1.0) * inverseWM;
457  float mouseDistance = (near_point - vGrabPos).length();
458 
459  osg::Vec3 clickDir = far_point - near_point;
460  clickDir.normalize();
461 
462  osg::Vec3 desiredPosition = near_point + (clickDir * mouseDistance);
463 
464  //char strDest[150];
465  //sprintf(strDest, "XY: (%3.1f, %3.1f, %3.1f),(%3.1f, %3.1f, %3.1f)\n", vGrabPos[0], vGrabPos[1], vGrabPos[2], desiredPosition[0], desiredPosition[1], desiredPosition[2]);
466  //OutputDebugString(strDest);
467 
468 
469  return desiredPosition;
470 }
471 
472 void OsgCameraManipulator::DoPan(const GUIEventAdapter& ea, float x, float y)
473 {
474  float dx = x - m_fltPrevX;
475  float dy = y - m_fltPrevY;
476 
477  unsigned int buttonMask = ea.getButtonMask();
478  if(buttonMask==(GUIEventAdapter::LEFT_MOUSE_BUTTON))
479  {
480 
481  osg::Vec3 v3Eye(dx, 0, dy);
482  m_v3Eye = m_v3Eye - v3Eye;
483  }
484 }
485 
486 void OsgCameraManipulator::DoZoom(const GUIEventAdapter& ea, float x, float y)
487 {
488  float dy = y - m_fltPrevY;
489 
490  unsigned int buttonMask = ea.getButtonMask();
491  if(buttonMask==(GUIEventAdapter::LEFT_MOUSE_BUTTON))
492  {
493  osg::Vec3 v3Eye(0, 0, dy);
494  m_v3Eye = m_v3Eye + v3Eye;
495  }
496 }
497 
498 void OsgCameraManipulator::DoRotate(const GUIEventAdapter& ea, float x, float y)
499 {
500  float dx = x - m_fltPrevX;
501  float dy = y - m_fltPrevY;
502 
503  unsigned int buttonMask = ea.getButtonMask();
504  if(buttonMask==(GUIEventAdapter::LEFT_MOUSE_BUTTON))
505  {
506  osg::Matrix rotation_matrix(m_quatRotation);
507 
508  osg::Vec3 uv = osg::Vec3(0.0f,1.0f,0.0f)*rotation_matrix;
509  osg::Vec3 sv = osg::Vec3(1.0f,0.0f,0.0f)*rotation_matrix;
510  osg::Vec3 lv = osg::Vec3(0.0f,0.0f,-1.0f)*rotation_matrix;
511 
512  osg::Vec3 p2 = sv * x + uv * y - lv * tb_project_to_sphere(0.8f, x, y);
513  osg::Vec3 p1 = sv * m_fltPrevX + uv * m_fltPrevY - lv * tb_project_to_sphere(0.8f, m_fltPrevX, m_fltPrevY);
514 
515  osg::Vec3 axis;
516  axis = p2^p1;
517  axis.normalize();
518 
519  float t = (p2 - p1).length() / (2.0 * 0.8f);
520 
521  if (t > 1.0) t = 1.0;
522  if (t < -1.0) t = -1.0;
523 
524  float angle = osg::inRadians(asin(t));
525 
526  osg::Quat new_rotate;
527  new_rotate.makeRotate(angle,axis);
528 
529  m_quatRotation = m_quatRotation*new_rotate;
530 
531  /*osg::Matrixd matRot;
532  matRot = osg::Matrix::rotate(-dx, osg::Vec3(0,1,0)) *
533  osg::Matrix::rotate(dy, osg::Vec3(1,0,0));
534 
535  osg::Quat q = matRot.getRotate();
536  m_quatRotation = m_quatRotation * q;*/
537  }
538 }
539 
540 float OsgCameraManipulator::tb_project_to_sphere(float r, float x, float y)
541 {
542  float d, t, z;
543 
544  d = sqrt(x*x + y*y);
545  /* Inside sphere */
546  if (d < r * 0.70710678118654752440)
547  {
548  z = sqrt(r*r - d*d);
549  } /* On hyperbola */
550  else
551  {
552  t = r / 1.41421356237309504880;
553  z = t*t / d;
554  }
555  return z;
556 }
557 
558  }// end Visualization
559 }// end OsgAnimatSim
Declares the vortex Light class.
virtual osg::MatrixTransform * GetCameraMatrixTransform()
Gets the matrix transform used by the camera for the mouse spring.
Classes for implementing the cm-labs vortex physics engine for AnimatLab.
virtual bool handle(const GUIEventAdapter &ea, GUIActionAdapter &us)
Declares the vortex structure class.
double Std_CalculateDistance(CStdIPoint &ptA, CStdIPoint &ptB)
Calculates the distance between two points.