This function
In this section, we have created three key points in the scene to form a line. A villain will run from the first point to the last point, as shown in the figure:
The contents of this section are in the online disk, and the directory is the attachment / in the / osgChina webmaster's collection / file. If there are attachments, they will be stored in this directory according to the section number:
Please use the browser to open it. If you encounter problems or add groups, you can also add me on wechat: 13324598743:
Link: https://pan.baidu.com/s/13gwJLwo_LbRnN3Bl2NXXXw
Extraction code: xrf5
Key points of realization
Path generation
OSG uses the class osg::AnimationPath, which has a method called insert:
animationPath->insert(t, osg::AnimationPath::ControlPoint(keyPoint->at(i), rotate));
- The first parameter t represents the time when the path passes through the current point
- The second parameter ControlPoint contains two quantities, one is the position and the other is the orientation
When these two parameters are combined, it means that at a certain time, it passes through a certain point and faces in a certain direction. The time is easy to find. The distance between the two key points can be divided by the speed. We have realized a function here, which is called:
osg::AnimationPath* CreateAnimate(osg::Vec3Array* keyPoint, float speed)
Its input parameter osg::Vec3Array contains a series of points, and speed represents the speed of the path.
Now we should pay attention to two problems. One is how to calculate the orientation. The current scene ceep.ive is expanded along the XY plane, so the orientation calculation is mainly the angle of rotation along the Z axis. By default, our head is facing the Z axis and the positive direction of the Y axis, so we use a simple function to calculate this angle:
Quqt.makeRotate(osg::Y_AXIS, keyPoint->at(i+1) - keyPoint->at(i));
Because we know that the point in the keyPoint is expanded along the XY plane, the angle between its vector and the Y axis is its rotation angle.
The second problem is the interpolation of orientation,
As shown in the figure, the blue is the key point and orientation of the pressing, which is reasonable at first. The starting point faces the second point, and the second point faces the third point, but the value between them may not be reasonable. For the position, it is formed by interpolation. The position from the first point to the middle of the second point is interpolated according to time, but the orientation is also reasonable, such as the yellow arrow, It transitions from the orientation of the starting point to the orientation of the second point. This effect may not be what we want. Maybe we want him to turn when he runs to the front. This requires artificial interpolation of a key point at the turning point when calculating the path. This is what we often do.
Character loading
The animation control of this character is in Section 36.2 animation - control of bone animation , we take the 22nd action and play it to get the running person, but the default orientation of the person is not facing Z and y, and it is too large. We modify it. First scale it by 0.02 times, then rotate it 90 degrees along the X axis, then rotate it 180 degrees along the Z axis, and then raise it 4 meters in the positive direction of the Z axis, and then put the figure on the ground with its head facing the Z axis, Looking at the positive direction of the Y axis ensures that he is running along the path, not running backwards, head down or across.
These are all the key points.
Here are all the codes
#include <osgViewer/Viewer> #include <osgDB/ReadFile> #include <osg/NodeVisitor> #include <osg/Transform> #include <osg/AnimationPath> #include <osgGA/GUIEventHandler> #include <osgUtil/LineSegmentIntersector> #include <osg/ShapeDrawable> #include <osg/LineWidth> #include <osgAnimation/BasicAnimationManager> #include <osg/MatrixTransform> //Given the vertex and the speed of travel, create a path osg::AnimationPath* CreateAnimate(osg::Vec3Array* keyPoint, float speed) { //Only one point cannot form a path if (keyPoint->size() <= 1) { return NULL; } osg::AnimationPath* animationPath = new osg::AnimationPath; animationPath->setLoopMode(osg::AnimationPath::LOOP); //Time corresponding to key point float t = 0.0; //orientation osg::Quat rotate; for (int i = 0; i < keyPoint->size(); i++) { //Last point if ((keyPoint->size()-1) == i) { //The orientation is not calculated, so the orientation of the previous point is used animationPath->insert(t, osg::AnimationPath::ControlPoint(keyPoint->at(i), rotate)); //End of cycle break; } else { //Other points //If it is the first point, the time is t=0.0, and the orientation is determined by the orientation of the first point and the second point //Calculate the orientation of the current point. Because our orientation changes horizontally only on one axis, we can use a simple method //By default, the viewport is facing the Y axis, and we want to turn it to the vector from the first point to the second point rotate.makeRotate(osg::Y_AXIS, keyPoint->at(i+1) - keyPoint->at(i)); animationPath->insert(t, osg::AnimationPath::ControlPoint(keyPoint->at(i), rotate)); } //Distance between two points divided by speed t += ((keyPoint->at(i+1) - keyPoint->at(i)).length()/speed); } return animationPath; } //Create a red ball as a sign and give the center and radius of the circle osg::Node* CreateSphere(osg::Vec3 center, float r) { osg::Geode* gnode = new osg::Geode; osg::ShapeDrawable* sd = new osg::ShapeDrawable(new osg::Sphere(center, r)); sd->setColor(osg::Vec4(1.0, 0.0, 0.0, 1.0));//gules gnode->addDrawable(sd); return gnode; } //Create lines, given vertices and red width, osg::Node* CreateLine(osg::Vec3Array* vertex, float lineWidth) { osg::Geode* gnode = new osg::Geode; osg::Geometry* geom = new osg::Geometry; gnode->addDrawable(geom); //Set vertex geom->setVertexArray(vertex); //Set color osg::Vec4Array* color = new osg::Vec4Array(); color->push_back(osg::Vec4(1.0, 0.0, 0.0, 1.0)); geom->setColorArray(color, osg::Array::BIND_OVERALL); //Set lineweight osg::LineWidth* lw = new osg::LineWidth(lineWidth); geom->getOrCreateStateSet()->setAttribute(lw, osg::StateAttribute::ON | osg::StateAttribute::PROTECTED); //Turn off the light to make it brighter geom->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED); //Add settings as lines geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, vertex->size())); return gnode; } osg::Group* createMark(osg::Vec3Array* keyPoint) { osg::Group* markGroup = new osg::Group(); //Add red ball as key for (int i = 0; i < keyPoint->size(); i++) { markGroup->addChild(CreateSphere(keyPoint->at(i), 1.0)); } //Add line markGroup->addChild(CreateLine(keyPoint, 1.0)); return markGroup; } //Add a moving villain and move along the animation osg::Node* loadPersion(osg::AnimationPath* animation) { //Used to rotate the villain so that its default orientation is correct osg::MatrixTransform* mt = new osg::MatrixTransform; osg::Node* persion = osgDB::readNodeFile("Naruto.fbx"); //Because the orientation of the villain is uncertain during modeling, we need to look at the orientation loaded into the OSG, and then adjust it to the Y axis and the Z axis of the head, //When we build this path, we calculate the included angle based on the Z axis of the head and 0 degrees towards the Y axis //First reduce it by 0.02 times, then rotate the villain 90 degrees along the x axis, and then rotate the villain 180 degrees along the Z axis, so that its orientation is towards the positive direction of the Y axis, and then lift it for 4 meters along the positive direction of the Z axis mt->setMatrix(osg::Matrix::scale(0.02, 0.02, 0.02)*osg::Matrix::rotate(osg::inDegrees(90.0), osg::X_AXIS) * osg::Matrix::rotate(osg::inDegrees(180.0), osg::Z_AXIS)*osg::Matrix::translate(osg::Vec3(0.0, 0.0, 4.0))); mt->addChild(persion); //Play its 22nd action, which is running osgAnimation::BasicAnimationManager* apc = dynamic_cast<osgAnimation::BasicAnimationManager*>(persion->getUpdateCallback()); apc->playAnimation(apc->getAnimationList().at(22)); //Actual path control osg::MatrixTransform* persionMT = new osg::MatrixTransform; persionMT->setUpdateCallback(new osg::AnimationPathCallback(animation)); persionMT->addChild(mt); return persionMT; } int main() { osgViewer::Viewer viewer; //It stores the key points on the path osg::Vec3Array* keyPoint = new osg::Vec3Array; keyPoint->push_back(osg::Vec3(-28.22, -63.42, 1.0)); keyPoint->push_back(osg::Vec3(3.38, -32, 1.0)); keyPoint->push_back(osg::Vec3(40.34, -38.40, 1.0)); osg::Node* ceep = osgDB::readNodeFile("ceep.ive"); //Scenario ceep osg::Group* root = new osg::Group; root->addChild(ceep); //Marking of keys and paths root->addChild(createMark(keyPoint)); //Add sports villain root->addChild(loadPersion(CreateAnimate(keyPoint, 2.5))); viewer.setSceneData(root); return viewer.run(); }