1 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2  *
3  * This library is open source and may be redistributed and/or modified under
4  * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5  * (at your option) any later version.  The full license is in LICENSE file
6  * included with this distribution, and on the openscenegraph.org website.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * OpenSceneGraph Public License for more details.
12 */
13 
14 #include <osgGA/TerrainManipulator>
15 #include <osgUtil/LineSegmentIntersector>
16 #include <osg/io_utils>
17 
18 using namespace osg;
19 using namespace osgGA;
20 
21 
22 
23 /// Constructor.
24 TerrainManipulator::TerrainManipulator( int flags )
25     : inherited( flags )
26 {
27 }
28 
29 
30 /// Constructor.
31 TerrainManipulator::TerrainManipulator( const TerrainManipulator& tm, const CopyOp& copyOp )
32     : osg::Callback(tm, copyOp),
33       inherited( tm, copyOp ),
34       _previousUp( tm._previousUp )
35 {
do_diff3(svn_stream_t * ostream,const char * original,const char * modified,const char * latest,const char * conflict_original,const char * conflict_modified,const char * conflict_latest,svn_diff_conflict_display_style_t conflict_style,svn_boolean_t * has_changes,apr_pool_t * pool)36 }
37 
38 
39 /** Sets the manipulator rotation mode. RotationMode is now deprecated by
40     osgGA::StandardManipulator::setVerticalAxisFixed() functionality,
41     that is used across StandardManipulator derived classes.*/
42 void TerrainManipulator::setRotationMode( TerrainManipulator::RotationMode mode )
43 {
44     setVerticalAxisFixed( mode == ELEVATION_AZIM );
45 }
46 
47 
48 /** Returns the manipulator rotation mode.*/
49 TerrainManipulator::RotationMode TerrainManipulator::getRotationMode() const
50 {
51     return getVerticalAxisFixed() ? ELEVATION_AZIM : ELEVATION_AZIM_ROLL;
52 }
53 
54 
55 void TerrainManipulator::setNode( Node* node )
56 {
57     inherited::setNode( node );
58 
59     // update model size
60     if( _flags & UPDATE_MODEL_SIZE )
61     {
62         if( _node.valid() )
63         {
64             setMinimumDistance( clampBetween( _modelSize * 0.001, 0.00001, 1.0 ) );
65             OSG_INFO << "TerrainManipulator: setting _minimumDistance to "
66                      << _minimumDistance << std::endl;
main(int argc,const char * argv[])67         }
68     }
69 }
70 
71 
72 void TerrainManipulator::setByMatrix(const Matrixd& matrix)
73 {
74 
75     Vec3d lookVector(- matrix(2,0),-matrix(2,1),-matrix(2,2));
76     Vec3d eye(matrix(3,0),matrix(3,1),matrix(3,2));
77 
78     OSG_INFO<<"eye point "<<eye<<std::endl;
79     OSG_INFO<<"lookVector "<<lookVector<<std::endl;
80 
81     if (!_node)
82     {
83         _center = eye+ lookVector;
84         _distance = lookVector.length();
85         _rotation = matrix.getRotate();
86         return;
87     }
88 
89 
90     // need to reintersect with the terrain
91     const BoundingSphere& bs = _node->getBound();
92     float distance = (eye-bs.center()).length() + _node->getBound().radius();
93     Vec3d start_segment = eye;
94     Vec3d end_segment = eye + lookVector*distance;
95 
96     Vec3d ip;
97     bool hitFound = false;
98     if (intersect(start_segment, end_segment, ip))
99     {
100         OSG_INFO << "Hit terrain ok A"<< std::endl;
101         _center = ip;
102 
103         _distance = (eye-ip).length();
104 
105         Matrixd rotation_matrix = Matrixd::translate(0.0,0.0,-_distance)*
106                                   matrix*
107                                   Matrixd::translate(-_center);
108 
109         _rotation = rotation_matrix.getRotate();
110 
111         hitFound = true;
112     }
113 
114     if (!hitFound)
115     {
116         CoordinateFrame eyePointCoordFrame = getCoordinateFrame( eye );
117 
118         if (intersect(eye+getUpVector(eyePointCoordFrame)*distance,
119                       eye-getUpVector(eyePointCoordFrame)*distance,
120                       ip))
121         {
122             _center = ip;
123 
124             _distance = (eye-ip).length();
125 
126             _rotation.set(0,0,0,1);
127 
128             hitFound = true;
129         }
130     }
131 
132 
133     CoordinateFrame coordinateFrame = getCoordinateFrame(_center);
134     _previousUp = getUpVector(coordinateFrame);
135 
136     clampOrientation();
137 }
138 
139 
140 void TerrainManipulator::setTransformation( const osg::Vec3d& eye, const osg::Vec3d& center, const osg::Vec3d& up )
141 {
142     if (!_node) return;
143 
144     // compute rotation matrix
145     Vec3d lv(center-eye);
146     _distance = lv.length();
147     _center = center;
148 
149     OSG_INFO << "In compute"<< std::endl;
150 
151     if (_node.valid())
152     {
153         bool hitFound = false;
154 
155         double distance = lv.length();
156         double maxDistance = distance+2*(eye-_node->getBound().center()).length();
157         Vec3d farPosition = eye+lv*(maxDistance/distance);
158         Vec3d endPoint = center;
159         for(int i=0;
160             !hitFound && i<2;
161             ++i, endPoint = farPosition)
162         {
163             // compute the intersection with the scene.
164 
165             Vec3d ip;
166             if (intersect(eye, endPoint, ip))
167             {
168                 _center = ip;
169                 _distance = (ip-eye).length();
170 
171                 hitFound = true;
172             }
173         }
174     }
175 
176     // note LookAt = inv(CF)*inv(RM)*inv(T) which is equivalent to:
177     // inv(R) = CF*LookAt.
178 
179     Matrixd rotation_matrix = Matrixd::lookAt(eye,center,up);
180 
181     _rotation = rotation_matrix.getRotate().inverse();
182 
183     CoordinateFrame coordinateFrame = getCoordinateFrame(_center);
184     _previousUp = getUpVector(coordinateFrame);
185 
186     clampOrientation();
187 }
188 
189 
190 bool TerrainManipulator::intersect( const Vec3d& start, const Vec3d& end, Vec3d& intersection ) const
191 {
192     ref_ptr<osgUtil::LineSegmentIntersector> lsi = new osgUtil::LineSegmentIntersector(start,end);
193 
194     osgUtil::IntersectionVisitor iv(lsi.get());
195     iv.setTraversalMask(_intersectTraversalMask);
196 
197     _node->accept(iv);
198 
199     if (lsi->containsIntersections())
200     {
201         intersection = lsi->getIntersections().begin()->getWorldIntersectPoint();
202         return true;
203     }
204     return false;
205 }
206 
207 
208 bool TerrainManipulator::performMovementMiddleMouseButton( const double eventTimeDelta, const double dx, const double dy )
209 {
210     // pan model.
211     double scale = -0.3f * _distance * getThrowScale( eventTimeDelta );
212 
213     Matrixd rotation_matrix;
214     rotation_matrix.makeRotate(_rotation);
215 
216 
217     // compute look vector.
218     Vec3d sideVector = getSideVector(rotation_matrix);
219 
220     // CoordinateFrame coordinateFrame = getCoordinateFrame(_center);
221     // Vec3d localUp = getUpVector(coordinateFrame);
222     Vec3d localUp = _previousUp;
223 
224     Vec3d forwardVector =localUp^sideVector;
225     sideVector = forwardVector^localUp;
226 
227     forwardVector.normalize();
228     sideVector.normalize();
229 
230     Vec3d dv = forwardVector * (dy*scale) + sideVector * (dx*scale);
231 
232     _center += dv;
233 
234     // need to recompute the intersection point along the look vector.
235 
236     bool hitFound = false;
237 
238     if (_node.valid())
239     {
240         // now reorientate the coordinate frame to the frame coords.
241         CoordinateFrame coordinateFrame =  getCoordinateFrame(_center);
242 
243         // need to reintersect with the terrain
244         double distance = _node->getBound().radius()*0.25f;
245 
246         Vec3d ip1;
247         Vec3d ip2;
248         bool hit_ip1 = intersect(_center, _center + getUpVector(coordinateFrame) * distance, ip1);
249         bool hit_ip2 = intersect(_center, _center - getUpVector(coordinateFrame) * distance, ip2);
250         if (hit_ip1)
251         {
252             if (hit_ip2)
253             {
254                 _center = (_center-ip1).length2() < (_center-ip2).length2() ?
255                             ip1 :
256                             ip2;
257 
258                 hitFound = true;
259             }
260             else
261             {
262                 _center = ip1;
263                 hitFound = true;
264             }
265         }
266         else if (hit_ip2)
267         {
268             _center = ip2;
269             hitFound = true;
270         }
271 
272         if (!hitFound)
273         {
274             // ??
275             OSG_INFO<<"TerrainManipulator unable to intersect with terrain."<<std::endl;
276         }
277 
278         coordinateFrame = getCoordinateFrame(_center);
279         Vec3d new_localUp = getUpVector(coordinateFrame);
280 
281 
282         Quat pan_rotation;
283         pan_rotation.makeRotate(localUp,new_localUp);
284 
285         if (!pan_rotation.zeroRotation())
286         {
287             _rotation = _rotation * pan_rotation;
288             _previousUp = new_localUp;
289             //OSG_NOTICE<<"Rotating from "<<localUp<<" to "<<new_localUp<<"  angle = "<<acos(localUp*new_localUp/(localUp.length()*new_localUp.length()))<<std::endl;
290 
291             //clampOrientation();
292         }
293         else
294         {
295             OSG_INFO<<"New up orientation nearly inline - no need to rotate"<<std::endl;
296         }
297     }
298 
299     return true;
300 }
301 
302 
303 bool TerrainManipulator::performMovementRightMouseButton( const double eventTimeDelta, const double /*dx*/, const double dy )
304 {
305     // zoom model
306     zoomModel( dy * getThrowScale( eventTimeDelta ), false );
307     return true;
308 }
309 
310 
311 void TerrainManipulator::clampOrientation()
312 {
313     if (!getVerticalAxisFixed())
314     {
315         Matrixd rotation_matrix;
316         rotation_matrix.makeRotate(_rotation);
317 
318         Vec3d lookVector = -getUpVector(rotation_matrix);
319         Vec3d upVector = getFrontVector(rotation_matrix);
320 
321         CoordinateFrame coordinateFrame = getCoordinateFrame(_center);
322         Vec3d localUp = getUpVector(coordinateFrame);
323         //Vec3d localUp = _previousUp;
324 
325         Vec3d sideVector = lookVector ^ localUp;
326 
327         if (sideVector.length()<0.1)
328         {
329             OSG_INFO<<"Side vector short "<<sideVector.length()<<std::endl;
330 
331             sideVector = upVector^localUp;
332             sideVector.normalize();
333         }
334 
335         Vec3d newUpVector = sideVector^lookVector;
336         newUpVector.normalize();
337 
338         Quat rotate_roll;
339         rotate_roll.makeRotate(upVector,newUpVector);
340 
341         if (!rotate_roll.zeroRotation())
342         {
343             _rotation = _rotation * rotate_roll;
344         }
345     }
346 }
347