diff --git a/include/osg/AutoTransform b/include/osg/AutoTransform index b92435e82..efa0f65e5 100644 --- a/include/osg/AutoTransform +++ b/include/osg/AutoTransform @@ -72,13 +72,25 @@ class OSG_EXPORT AutoTransform : public Transform { NO_ROTATION, ROTATE_TO_SCREEN, - ROTATE_TO_CAMERA + ROTATE_TO_CAMERA, + ROTATE_TO_AXIS }; - void setAutoRotateMode(AutoRotateMode mode) { _autoRotateMode = mode; _firstTimeToInitEyePoint = true; } + void setAutoRotateMode(AutoRotateMode mode); AutoRotateMode getAutoRotateMode() const { return _autoRotateMode; } + /** Set the rotation axis for the AutoTransform's child nodes. + * Only utilized when _autoRotateMode==ROTATE_TO_AXIS. */ + void setAxis(const Vec3& axis); + /** Get the rotation axis. */ + inline const Vec3& getAxis() const { return _axis; } + + /** This normal defines child Nodes' front face direction when unrotated. */ + void setNormal(const Vec3& normal); + /** Get the front face direction normal. */ + inline const Vec3& getNormal() const { return _normal; } + void setAutoScaleToScreen(bool autoScaleToScreen) { _autoScaleToScreen = autoScaleToScreen; _matrixDirty=true; } bool getAutoScaleToScreen() const { return _autoScaleToScreen; } @@ -124,6 +136,24 @@ class OSG_EXPORT AutoTransform : public Transform mutable bool _matrixDirty; mutable osg::Matrixd _cachedMatrix; + + enum AxisAligned + { + AXIAL_ROT_X_AXIS=ROTATE_TO_AXIS+1, + AXIAL_ROT_Y_AXIS, + AXIAL_ROT_Z_AXIS, + CACHE_DIRTY + }; + + Vec3 _axis; + Vec3 _normal; + + // used internally as cache of which what _axis is aligned to help + // decide which method of rotation to use. + int _cachedMode; + Vec3 _side; + void updateCache(); + }; } diff --git a/src/osg/AutoTransform.cpp b/src/osg/AutoTransform.cpp index 4fde14a7e..acb3634f6 100644 --- a/src/osg/AutoTransform.cpp +++ b/src/osg/AutoTransform.cpp @@ -26,7 +26,11 @@ AutoTransform::AutoTransform(): _minimumScale(0.0), _maximumScale(DBL_MAX), _autoScaleTransitionWidthRatio(0.25), - _matrixDirty(true) + _matrixDirty(true), + _axis(0.0f,0.0f,1.0f), + _normal(0.0f,-1.0f,0.0f), + _cachedMode(NO_ROTATION), + _side(1.0f,0.0,0.0f) { // setNumChildrenRequiringUpdateTraversal(1); } @@ -44,11 +48,52 @@ AutoTransform::AutoTransform(const AutoTransform& pat,const CopyOp& copyop): _minimumScale(pat._minimumScale), _maximumScale(pat._maximumScale), _autoScaleTransitionWidthRatio(pat._autoScaleTransitionWidthRatio), - _matrixDirty(true) + _matrixDirty(true), + _axis(pat._axis), + _normal(pat._normal), + _cachedMode(pat._cachedMode), + _side(pat._side) { // setNumChildrenRequiringUpdateTraversal(getNumChildrenRequiringUpdateTraversal()+1); } +void AutoTransform::setAutoRotateMode(AutoRotateMode mode) +{ + _autoRotateMode = mode; + _firstTimeToInitEyePoint = true; + _cachedMode = CACHE_DIRTY; + updateCache(); +} + +void AutoTransform::setAxis(const Vec3& axis) +{ + _axis = axis; + _axis.normalize(); + updateCache(); +} + +void AutoTransform::setNormal(const Vec3& normal) +{ + _normal = normal; + _normal.normalize(); + updateCache(); +} + +void AutoTransform::updateCache() +{ + if (_autoRotateMode==ROTATE_TO_AXIS) + { + if (_axis==Vec3(1.0f,0.0,0.0f) && _normal==Vec3(0.0f,-1.0,0.0f)) _cachedMode = AXIAL_ROT_X_AXIS; + else if (_axis==Vec3(0.0f,1.0,0.0f) && _normal==Vec3(1.0f, 0.0,0.0f)) _cachedMode = AXIAL_ROT_Y_AXIS; + else if (_axis==Vec3(0.0f,0.0,1.0f) && _normal==Vec3(0.0f,-1.0,0.0f)) _cachedMode = AXIAL_ROT_Z_AXIS; + else _cachedMode = ROTATE_TO_AXIS; + } + else _cachedMode = _autoRotateMode; + + _side = _axis^_normal; + _side.normalize(); +} + void AutoTransform::setScale(const Vec3d& scale) { _scale = scale; @@ -240,6 +285,80 @@ void AutoTransform::accept(NodeVisitor& nv) q.set(osg::Matrix::inverse(lookto)); setRotation(q); } + else if (_autoRotateMode==ROTATE_TO_AXIS) + { + Matrix matrix; + Vec3 ev(eyePoint - _position); + + switch(_cachedMode) + { + case(AXIAL_ROT_Z_AXIS): + { + ev.z() = 0.0f; + float ev_length = ev.length(); + if (ev_length>0.0f) + { + //float rotation_zrotation_z = atan2f(ev.x(),ev.y()); + //mat.makeRotate(inRadians(rotation_z),0.0f,0.0f,1.0f); + float inv = 1.0f/ev_length; + float s = ev.x()*inv; + float c = -ev.y()*inv; + matrix(0,0) = c; + matrix(1,0) = -s; + matrix(0,1) = s; + matrix(1,1) = c; + } + break; + } + case(AXIAL_ROT_Y_AXIS): + { + ev.y() = 0.0f; + float ev_length = ev.length(); + if (ev_length>0.0f) + { + //float rotation_zrotation_z = atan2f(ev.x(),ev.y()); + //mat.makeRotate(inRadians(rotation_z),0.0f,0.0f,1.0f); + float inv = 1.0f/ev_length; + float s = -ev.z()*inv; + float c = ev.x()*inv; + matrix(0,0) = c; + matrix(2,0) = s; + matrix(0,2) = -s; + matrix(2,2) = c; + } + break; + } + case(AXIAL_ROT_X_AXIS): + { + ev.x() = 0.0f; + float ev_length = ev.length(); + if (ev_length>0.0f) + { + //float rotation_zrotation_z = atan2f(ev.x(),ev.y()); + //mat.makeRotate(inRadians(rotation_z),0.0f,0.0f,1.0f); + float inv = 1.0f/ev_length; + float s = -ev.z()*inv; + float c = -ev.y()*inv; + matrix(1,1) = c; + matrix(2,1) = -s; + matrix(1,2) = s; + matrix(2,2) = c; + } + break; + } + case(ROTATE_TO_AXIS): // need to implement + { + float ev_side = ev*_side; + float ev_normal = ev*_normal; + float rotation = atan2f(ev_side,ev_normal); + matrix.makeRotate(rotation,_axis); + break; + } + } + Quat q; + q.set(matrix); + setRotation(q); + } _previousEyePoint = eyePoint; _previousLocalUp = localUp;