osg示例解析之osganimationskinning(1)

  • 简介

osgAnimationSkinning这个示例主要使用了osgAnimation中的骨骼蒙皮动画,关于骨骼蒙皮动画的介绍可以参考Skinned Mesh原理解析和一个最简单的实现示例这篇CSDN的博客,里面用Glut实现了一个简单的蒙皮动画,整篇文章对骨骼动画和蒙皮进行了详尽的阐述,相信读完之后可以对骨骼动画有更深的理解。

  • 示例

源码中createAxis创建了一个坐标系,代码十分的简单,仅仅是画出三条坐标轴线而已。

osg::Geode* createAxis()
{
    osg::Geode* geode (new osg::Geode());
    osg::Geometry* geometry (new osg::Geometry());

    osg::Vec3Array* vertices (new osg::Vec3Array());
    vertices->push_back (osg::Vec3 ( 0.0, 0.0, 0.0));
    vertices->push_back (osg::Vec3 ( 1.0, 0.0, 0.0));
    vertices->push_back (osg::Vec3 ( 0.0, 0.0, 0.0));
    vertices->push_back (osg::Vec3 ( 0.0, 1.0, 0.0));
    vertices->push_back (osg::Vec3 ( 0.0, 0.0, 0.0));
    vertices->push_back (osg::Vec3 ( 0.0, 0.0, 1.0));
    geometry->setVertexArray (vertices);

    osg::Vec4Array* colors (new osg::Vec4Array());
    colors->push_back (osg::Vec4 (1.0f, 0.0f, 0.0f, 1.0f));
    colors->push_back (osg::Vec4 (1.0f, 0.0f, 0.0f, 1.0f));
    colors->push_back (osg::Vec4 (0.0f, 1.0f, 0.0f, 1.0f));
    colors->push_back (osg::Vec4 (0.0f, 1.0f, 0.0f, 1.0f));
    colors->push_back (osg::Vec4 (0.0f, 0.0f, 1.0f, 1.0f));
    colors->push_back (osg::Vec4 (0.0f, 0.0f, 1.0f, 1.0f));
    geometry->setColorArray (colors, osg::Array::BIND_PER_VERTEX);
    geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES,0,6));

    geode->addDrawable( geometry );
    return geode;
}
createTesselatedBox这个函数做的是创建了骨骼动画的蒙皮Mesh,相当于给骨骼做了一件“衣服”,我们看看它怎么创建的:

	float step = size / static_cast<float>(nsplit);
	float s = 0.5f/4.0f;
	for (int i = 0; i < nsplit; i++)
	{
		float x = -1.0f + static_cast<float>(i) * step;
		std::cout << x << std::endl;
		vertices->push_back (osg::Vec3 ( x, s, s));
		vertices->push_back (osg::Vec3 ( x, -s, s));
		vertices->push_back (osg::Vec3 ( x, -s, -s));
		vertices->push_back (osg::Vec3 ( x, s, -s));
		osg::Vec3 c (0.0f,0.0f,0.0f);
		c[i%3] = 1.0f;
		colors->push_back (c);
		colors->push_back (c);
		colors->push_back (c);
		colors->push_back (c);
	}

	osg::ref_ptr<osg::UIntArray> array = new osg::UIntArray;
	for (int i = 0; i < nsplit - 1; i++)
	{
		int base = i * 4;

		//Top
		array->push_back(base);
		array->push_back(base+1);
		array->push_back(base+4);
		array->push_back(base+1);
		array->push_back(base+5);
		array->push_back(base+4);

		//Back
		array->push_back(base+3);
		array->push_back(base);
		array->push_back(base+4);
		array->push_back(base+7);
		array->push_back(base+3);
		array->push_back(base+4);

		//Front
		array->push_back(base+5);
		array->push_back(base+1);
		array->push_back(base+2);
		array->push_back(base+2);
		array->push_back(base+6);
		array->push_back(base+5);

		//Bottom
		array->push_back(base+2);
		array->push_back(base+3);
		array->push_back(base+7);
		array->push_back(base+6);
		array->push_back(base+2);
		array->push_back(base+7);
	}

通过上面的注解可以知道,函数createTesselatedBox(int nsplit, float size)的两个参数,其中nsplit是把这个立方体柱分成了nsplit-1段,然后每段一个立方体的4个面,这段代码使用的是DrawElement的方式创建的,因此它传入的是索引值。

接下来就是Skin info的操作,给不同的顶点绑定到骨骼上,并且给这些顶点赋予权值,这个示例中设置权值的方式比较简单,所有X在-1到0之间的顶点只受到骨骼Bon0的影响,所有X在0到1之间的顶点只受Bone1的影响,所有X值大于1的顶点只受到Bone2的影响。

void initVertexMap(osgAnimation::Bone* b0,
                   osgAnimation::Bone* b1,
                   osgAnimation::Bone* b2,
                   osgAnimation::RigGeometry* geom,
                   osg::Vec3Array* array)
{
    //osgAnimation::VertexInfluenceSet vertexesInfluences;
    osgAnimation::VertexInfluenceMap* vim = new osgAnimation::VertexInfluenceMap;

    (*vim)[b0->getName()].setName(b0->getName());
    (*vim)[b1->getName()].setName(b1->getName());
    (*vim)[b2->getName()].setName(b2->getName());

    for (int i = 0; i < (int)array->size(); i++)
    {
        float val = (*array)[i][0];
        std::cout << val << std::endl;
        if (val >= -1.0f && val <= 0.0f)
            (*vim)[b0->getName()].push_back(osgAnimation::VertexIndexWeight(i,1.0f));
        else if ( val > 0.0f && val <= 1.0f)
            (*vim)[b1->getName()].push_back(osgAnimation::VertexIndexWeight(i,1.0f));
        else if ( val > 1.0f)
            (*vim)[b2->getName()].push_back(osgAnimation::VertexIndexWeight(i,1.0f));
    }

    geom->setInfluenceMap(vim);
}
主函数main里面主要创建了骨骼动画:

    osg::ref_ptr<osgAnimation::Skeleton> skelroot = new osgAnimation::Skeleton;
    skelroot->setDefaultUpdateCallback();
    osg::ref_ptr<osgAnimation::Bone> root = new osgAnimation::Bone;
    root->setInvBindMatrixInSkeletonSpace(osg::Matrix::inverse(osg::Matrix::translate(-1.0,0.0,0.0)));
    root->setName("root");
    osgAnimation::UpdateBone* pRootUpdate = new osgAnimation::UpdateBone("root");
    pRootUpdate->getStackedTransforms().push_back(new osgAnimation::StackedTranslateElement("translate",osg::Vec3(-1.0f,0.0f,0.0f)));
    root->setUpdateCallback(pRootUpdate);

    osg::ref_ptr<osgAnimation::Bone> right0 = new osgAnimation::Bone;
    right0->setInvBindMatrixInSkeletonSpace(osg::Matrix::inverse(osg::Matrix::translate(0.0,0.0,0.0)));
    right0->setName("right0");
    osgAnimation::UpdateBone* pRight0Update = new osgAnimation::UpdateBone("right0");
    pRight0Update->getStackedTransforms().push_back(new osgAnimation::StackedTranslateElement("translate", osg::Vec3(1.0f,0.0f,0.0f)));
    pRight0Update->getStackedTransforms().push_back(new osgAnimation::StackedRotateAxisElement("rotate", osg::Vec3(0.0f,0.0f,1.0f), 0.0));
    right0->setUpdateCallback(pRight0Update);

    osg::ref_ptr<osgAnimation::Bone> right1 = new osgAnimation::Bone;
    right1->setInvBindMatrixInSkeletonSpace(osg::Matrix::inverse(osg::Matrix::translate(1.0,0.0,0.0)));
    right1->setName("right1");
    osgAnimation::UpdateBone* pRight1Update = new osgAnimation::UpdateBone("right1");
    pRight1Update->getStackedTransforms().push_back(new osgAnimation::StackedTranslateElement("translate", osg::Vec3(1.0f,0.0f,0.0f)));
    pRight1Update->getStackedTransforms().push_back(new osgAnimation::StackedRotateAxisElement("rotate", osg::Vec3(0.0f,0.0f,1.0f), 0.0));
    right1->setUpdateCallback(pRight1Update);

    root->addChild(right0.get());
    right0->addChild(right1.get());
    skelroot->addChild(root.get());
以上代码创建了骨骼的框架,这个创建骨骼的模式还是比较固定的,需要注意的是UpdateBone的名称需要和下面Channel的SetTargetName名称一致,UpdateBone里面StackedTransformElement需要和Channel的Name一致

    osg::Group* scene = new osg::Group;
    osg::ref_ptr<osgAnimation::BasicAnimationManager> manager = new osgAnimation::BasicAnimationManager;
    scene->setUpdateCallback(manager.get());

    osgAnimation::Animation* anim = new osgAnimation::Animation;
    {
        osgAnimation::FloatKeyframeContainer* keys0 = new osgAnimation::FloatKeyframeContainer;
        keys0->push_back(osgAnimation::FloatKeyframe(0.0,0.0f));
        keys0->push_back(osgAnimation::FloatKeyframe(3.0,osg::PI_2));
        keys0->push_back(osgAnimation::FloatKeyframe(6.0,osg::PI_2));
        osgAnimation::FloatLinearSampler* sampler = new osgAnimation::FloatLinearSampler;
        sampler->setKeyframeContainer(keys0);
        osgAnimation::FloatLinearChannel* channel = new osgAnimation::FloatLinearChannel(sampler);
        channel->setName("rotate");
        channel->setTargetName("right0");
        anim->addChannel(channel);
    }

    {
        osgAnimation::FloatKeyframeContainer* keys1 = new osgAnimation::FloatKeyframeContainer;
        keys1->push_back(osgAnimation::FloatKeyframe(0.0,0.0f));
        keys1->push_back(osgAnimation::FloatKeyframe(3.0,0.0f));
        keys1->push_back(osgAnimation::FloatKeyframe(6.0,osg::PI_2));
        osgAnimation::FloatLinearSampler* sampler = new osgAnimation::FloatLinearSampler;
        sampler->setKeyframeContainer(keys1);
        osgAnimation::FloatLinearChannel* channel = new osgAnimation::FloatLinearChannel(sampler);
        channel->setName("rotate");
        channel->setTargetName("right1");
        anim->addChannel(channel);
    }
    manager->registerAnimation(anim);
    //manager->buildTargetReference();

    // let's start !
    manager->playAnimation(anim);
这段代码创建了骨骼动画运行的关键帧。

    osg::MatrixTransform* rootTransform = new osg::MatrixTransform;
    rootTransform->setMatrix(osg::Matrix::rotate(osg::PI_2,osg::Vec3(1.0f,0.0f,0.0f)));
    right0->addChild(createAxis());
    right0->setDataVariance(osg::Object::DYNAMIC);
    right1->addChild(createAxis());
    right1->setDataVariance(osg::Object::DYNAMIC);
    osg::MatrixTransform* trueroot = new osg::MatrixTransform;
    trueroot->setMatrix(osg::Matrix(root->getMatrixInBoneSpace().ptr()));
    trueroot->addChild(createAxis());
    trueroot->addChild(skelroot.get());
    trueroot->setDataVariance(osg::Object::DYNAMIC);
    rootTransform->addChild(trueroot);
    scene->addChild(rootTransform);
上面的这段代码把可绘制实体添加到骨骼动画之中,这样我们才能看见骨骼动画的实际效果。这些Geode将抽象的骨骼变换具体化,能让我们看见Geode上面绑定着的骨骼的运动效果。

最后一步当然是进行骨骼动画的蒙皮操作了,将蒙皮Mesh设置好与骨骼的绑定以及权值之后,将这个节点添加到Skeleton节点之下就可以了:

    osgAnimation::RigGeometry* geom = createTesselatedBox(4, 4.0f);
    osg::Geode* geode = new osg::Geode;
    geode->addDrawable(geom);
    skelroot->addChild(geode);
    osg::ref_ptr<osg::Vec3Array> src = dynamic_cast<osg::Vec3Array*>(geom->getSourceGeometry()->getVertexArray());
    geom->getOrCreateStateSet()->setMode(GL_LIGHTING, false);
    geom->setDataVariance(osg::Object::DYNAMIC);

    initVertexMap(root.get(), right0.get(), right1.get(), geom, src.get());
最后编译运行程序

osg示例解析之osganimationskinning(1)_第1张图片


你可能感兴趣的:(Examples,OSG,osgAnimation)