实验中,xquat是竖直的那个,yquat是与屏幕平行的那个,zquat是水平的那个。如果是vec2 = quatxquatyquatZ*vec1,则先转xquat,再转yquat,再转zquat,
问题来了,如果多个步骤,该怎么办?顺序的不同,会导致结果的不同
那就设定一个数组,
struct DataRange : public osg::Referenced
{
//四元数数组
std::vectorosg::Quat _quatVector = std::vectorosg::Quat();
//六个面的法线
std::vectorosg::Vec3d _normalVector = std::vectorosg::Vec3d();
//中心点初始坐标(可能没在原点上)
osg::Vec3d _center;
//当前旋转的dir,也就是哪个dragger在起作用
RotateCutDirection _dir = None;
};
在release时,把该四元数加入到数组中
bool MyRotateCylinderDragger::handle(
const osgManipulator::PointerInfo& pointer,
const osgGA::GUIEventAdapter& ea,
osgGA::GUIActionAdapter& aa)
{
// Check if the dragger node is in the nodepath.
if (!pointer.contains(this)) return false;
switch (ea.getEventType())
{
// Pick start.
case (osgGA::GUIEventAdapter::PUSH):
{
_bRelease = false;
_dataRange->_dir = _dir;
// Get the LocalToWorld matrix for this node and set it for the projector.
osg::NodePath nodePathToRoot;
computeNodePathToRoot(*this, nodePathToRoot);
osg::Matrix localToWorld = osg::computeLocalToWorld(nodePathToRoot);
_projector->setLocalToWorld(localToWorld);
_startLocalToWorld = _projector->getLocalToWorld();
_startWorldToLocal = _projector->getWorldToLocal();
if (_projector->isPointInFront(pointer, _startLocalToWorld))
_projector->setFront(true);
else
_projector->setFront(false);
osg::Vec3d projectedPoint;
if (_projector->project(pointer, projectedPoint))
{
// Generate the motion command.
osg::ref_ptr<osgManipulator::Rotate3DCommand> cmd = new osgManipulator::Rotate3DCommand();
cmd->setStage(osgManipulator::MotionCommand::START);
cmd->setLocalToWorldAndWorldToLocal(_startLocalToWorld, _startWorldToLocal);
// Dispatch command.
dispatch(*cmd);
// Set color to pick color.
setMaterialColor(_pickColor, *this);
_prevWorldProjPt = projectedPoint * _projector->getLocalToWorld();
_prevRotation = osg::Quat();
aa.requestRedraw();
}
return true;
}
// Pick move.
case (osgGA::GUIEventAdapter::DRAG):
{
_bRelease = false;
_dataRange->_dir = _dir;
// Get the LocalToWorld matrix for this node and set it for the projector.
osg::Matrix localToWorld = osg::Matrix(_prevRotation) * _startLocalToWorld;
_projector->setLocalToWorld(localToWorld);
osg::Vec3d projectedPoint;
if (_projector->project(pointer, projectedPoint))
{
osg::Vec3d prevProjectedPoint = _prevWorldProjPt * _projector->getWorldToLocal();
osg::Quat deltaRotation = _projector->getRotation(prevProjectedPoint,
projectedPoint);
osg::Quat rotation = deltaRotation * _prevRotation;
// Generate the motion command.
osg::ref_ptr<osgManipulator::Rotate3DCommand> cmd = new osgManipulator::Rotate3DCommand();
cmd->setStage(osgManipulator::MotionCommand::MOVE);
cmd->setLocalToWorldAndWorldToLocal(_startLocalToWorld, _startWorldToLocal);
cmd->setRotation(rotation);
// Dispatch command.
dispatch(*cmd);
_prevWorldProjPt = projectedPoint * _projector->getLocalToWorld();
_prevRotation = rotation;
aa.requestRedraw();
}
return true;
}
// Pick finish.
case (osgGA::GUIEventAdapter::RELEASE):
{
osg::ref_ptr<osgManipulator::Rotate3DCommand> cmd = new osgManipulator::Rotate3DCommand();
cmd->setStage(osgManipulator::MotionCommand::FINISH);
cmd->setLocalToWorldAndWorldToLocal(_startLocalToWorld, _startWorldToLocal);
// Dispatch command.
dispatch(*cmd);
// Reset color.
setMaterialColor(_color, *this);
aa.requestRedraw();
_bRelease = true;
_dataRange->_dir = _dir;
_dataRange->_quatVector.push_back(_prevRotation);
return true;
}
default:
return false;
}
}
其中每个dragger对应一个旋转轴,可以用枚举
MyCylinderPlaneProjector::MyCylinderPlaneProjector(RotateCutDirection dir)
:osgManipulator::CylinderPlaneProjector()
{
switch (dir)
{
case xAxis:
_cylinderAxis = osg::Vec3d(1.0, 0.0, 0.0);
break;
case yAxis:
_cylinderAxis = osg::Vec3d(0.0, 1.0, 0.0);
break;
case zAxis:
_cylinderAxis = osg::Vec3d(0.0, 0.0, 1.0);
break;
default:
break;
}
}
每帧返回当前四元数
osg::Quat MyRotateCylinderDragger::getRotation()
{
osg::Quat currentSegmentBeginQuat = osg::Quat();
for (int i = 0; i < _dataRange->_quatVector.size(); i++)
{
osg::Quat theQuat = _dataRange->_quatVector[i];
currentSegmentBeginQuat = currentSegmentBeginQuat * theQuat;
}
if (_bRelease)
{
return currentSegmentBeginQuat;
}
return currentSegmentBeginQuat * _prevRotation;
}
使用时通过回调函数每帧更改
osg::Quat theQuat = dragger->getRotation();
if (_lastQuat == theQuat)
{
return;
}
_lastQuat = theQuat;
创建到顶点的Geode
_lineGroup->removeChild(0, _lineGroup->getNumChildren());
for (int i = 0; i < _lineVec.size(); i++)
{
osg::Vec3d pos = _lineVec[i];
osg::Vec3d lineVec = theQuat* pos;
osg::ref_ptr<osg::Geode> linegeode = CreateRedLine(lineVec);
//_lineGroup->addChild(linegeode);
}
创建到法线点的Geode
osg::Vec3d center = theQuat * _dataRange->_center;
_normalGroup->removeChild(0, _normalGroup->getNumChildren());
for (int i = 0; i < _startNormalVec.size(); i++)
{
osg::Vec3d pos = _startNormalVec[i];
_dataRange->_normalVector[i] = theQuat * pos;
osg::ref_ptr<osg::Geode> normalgeode = CreateNormalLine(center,_dataRange->_normalVector[i]);
_normalGroup->addChild(normalgeode);
UpdateClipRender(_clipNode, _dataRange->_normalVector[i], center -_dataRange->_normalVector[i], i);
}
如下图所示
其实,进一步想,四元数代表旋转矩阵,用矩阵数组也可以,
好处在哪里呢?
1,这样可以考虑把平移也加到矩阵数组里,
2,从vec2 = quat1quat2quat3*vec1来看,总觉得不舒服,因为,最后的应该最后相乘。如果换成矩阵呢?
vec2 = vec1 * matrix1 * matrix2 * matrix3,这样符合矩阵结合律。顺序是自然的