osg--动画

本文详细介绍了osg中的动画实现,包括回调机制、traverse用法、避免更新冲突的方法,以及路径动画、渲染状态动画、图片动画、关键帧动画和人物动画的创建与示例。内容涵盖osg::Node、osg::Drawable、osg::StateAttribute等的更新回调,以及osg::AnimationPath、osgAnimation等关键组件的使用。

回调

  • osg::Node
    • setUpdateCallback()
      • 参数为osg::NodeCallback
        • operator()重载
    • addUpdateCallback()
    • setEventCallback()
      • 参数为osg::NodeCallback
        • operator()重载
    • setCullCallback()
      • 参数为osg::NodeCallback
        • operator()重载
  • osg::Drawable
    • setUpdateCallback()
      • osg::Drawable::UpdateCallback
        • update()重载
    • setEventCallback()
      • osg::Drawable::EventCallback
        • event()重载
    • setCullCallback()
      • osg::Drawable::CullCallback
        • cull()重载
  • osg::StateAttribute
    • setUpdateCallback()
      • osg:: StateAttributeCallback
        • operator()重载
    • setEventCallback()
      • osg:: StateAttributeCallback
        • operator()重载
  • osg::Uniform
    • setUpdateCallback()
      • osg::Uniform:: Callback
        • operator()重载
    • setEventCallback()
      • osg::Uniform:: Callback
        • operator()重载
  • osg::Camera
    • setPreDrawCallback()
      • osg::Camera:: DrawCallback
        • operator()重载
    • setPostDrawCallback()
      • osg::Camera:: DrawCallback
        • operator()重载

traverse用法

  • node和Nodevisitor中traverse用法

在这里插入图片描述

  • callback中traverse用法:
    • 调用Nodevisitor的traverse方法,如果忘记了,traversal会停止

示例

#include <osg/Switch>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>

class SwitchingCallback : public osg::NodeCallback
{
public:
	SwitchingCallback() : _count(0) {}
	virtual void operator()(osg::Node* node,
		osg::NodeVisitor* nv);

protected:
	unsigned int _count;
};
void SwitchingCallback::operator()(osg::Node* node,
	osg::NodeVisitor* nv)
{
	osg::Switch* switchNode = static_cast<osg::Switch*>(node);
	if (!((++_count) % 60) && switchNode)
	{
		switchNode->setValue(0, !switchNode->getValue(0));
		switchNode->setValue(1, !switchNode->getValue(1));
	}
	traverse(node, nv);
}
int main(int argc, char** argv)
{
	osg::ref_ptr<osg::Node> model1 = osgDB::readNodeFile(
		"cessna.osg");
	osg::ref_ptr<osg::Node> model2 = osgDB::readNodeFile("cessnafire.osg");
	osg::ref_ptr<osg::Switch> root = new osg::Switch;
	root->addChild(model1.get(), false);
	root->addChild(model2.get(), true);
	root->setUpdateCallback(new SwitchingCallback);

	osgViewer::Viewer viewer;
	viewer.setSceneData(root.get());
	return viewer.run();
}

避免冲突

osg::Object变量枚举值

  • UNSPECIFIED(default)

  • STATIC update和draw过程中不变

  • DYNAMIC update和cull完成后才能draw

      node->setDataVariance( osg::Object::DYNAMIC );
    

drawable回调中如果顶点和组元改变需调用

  • 更新显示列表中顶点和组元
    • setUseDisplayList(true)情况下
      • drawable->dirtyDisplayList()
    • setUseVertexBufferObjects(true)且setUseDisplayList(false)情况下
      • vertices->dirty();
  • 更新边界使cull正确
    • drawable->dirtyBound()

示例

#include <osg/Geometry>
#include <osg/Geode>
#include <osgViewer/Viewer>

osg::Geometry* createQuad()
{
	osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
	vertices->push_back(osg::Vec3(0.0f, 0.0f, 0.0f));
	vertices->push_back(osg::Vec3(1.0f, 0.0f, 0.0f));
	vertices->push_back(osg::Vec3(1.0f, 0.0f, 1.0f));
	vertices->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));


	osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;
	normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));


	osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;
	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, 0.0f, 1.0f, 1.0f));
	colors->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));


	osg::ref_ptr<osg::Geometry> quad = new osg::Geometry;
	quad->setVertexArray(vertices.get());
	quad->setNormalArray(normals.get());
	quad->setNormalBinding(osg::Geometry::BIND_OVERALL);
	quad->setColorArray(colors.get());
	quad->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
	quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4));
	return quad.release();
}
class DynamicQuadCallback : public osg::Drawable::UpdateCallback
{
public:
	virtual void update(osg::NodeVisitor*, osg::Drawable*
		drawable);
};
void DynamicQuadCallback::update(osg::NodeVisitor*,
	osg::Drawable* drawable)
{
	osg::Geometry* quad = static_cast<osg::Geometry*>(drawable);
	if (!quad) return;


	osg::Vec3Array* vertices = static_cast<osg::Vec3Array*>(
		quad->getVertexArray());
	if (!vertices) return;


	osg::Quat quat(osg::PI*0.01, osg::X_AXIS);
	vertices->back() = quat * vertices->back();

	quad->dirtyDisplayList();
	quad->dirtyBound();
}
int main(int argc, char** argv)
{
	osg::Geometry* quad = createQuad();
	quad->setDataVariance(osg::Object::DYNAMIC);
	quad->setUpdateCallback(new DynamicQuadCallback);

	osg::ref_ptr<osg::Geode> root = new osg::Geode;
	root->addDrawable(quad);
	osgViewer::Viewer viewer;
	viewer.setSceneData(root.get());
	return viewer.run();
}

运动方程

指定起点值和终点值,不同曲线方式下取值

在这里插入图片描述

  • update(dt)
  • getValue()

路径动画

  • osg::AnimationPath
    • insert(time in seconds,ControlPoint)
      • osg::AnimationPath::ControlPoint
    • setLoopMode()
      • LOOP
      • NO_LOOPING
      • SWING
  • osg::AnimationPathCallback
    • 只影响osg::MatrixTransform and osg::PositionAttitudeTransform
    • 继承osg::NodeCallback
    • 加入node的updatecallback
      • node->setUpdateCallback( animationPathCallback.get() );
    • 方法
      • setPause
      • reset
      • setTimeMultiplier
      • setTimeOffset

示例

#include <osg/AnimationPath>
#include <osg/MatrixTransform>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>

osg::AnimationPath* createAnimationPath(float radius, float time)
{
	osg::ref_ptr<osg::AnimationPath> path = new osg::AnimationPath;
	path->setLoopMode(osg::AnimationPath::LOOP);

	unsigned int numSamples = 32;
	float delta_yaw = 2.0f * osg::PI / ((float)numSamples - 1.0f);
	float delta_time = time / (float)numSamples;
	for (unsigned int i = 0; i<numSamples; ++i)
	{
		float yaw = delta_yaw * (float)i;
		osg::Vec3 pos(sinf(yaw)*radius, cosf(yaw)*radius, 0.0f);
		osg::Quat rot(-yaw, osg::Z_AXIS);
		path->insert(delta_time * (float)i,osg::AnimationPath::ControlPoint(pos, rot));
	}
	return path.release();
}
int main(int argc, char** argv)
{
	osg::ref_ptr<osg::Node> model =
		osgDB::readNodeFile("cessna.osg.0,0,90.rot");
	osg::ref_ptr<osg::MatrixTransform> root = new
		osg::MatrixTransform;
	root->addChild(model.get());

	osg::ref_ptr<osg::AnimationPathCallback> apcb = new
		osg::AnimationPathCallback;
	apcb->setAnimationPath(createAnimationPath(50.0f, 6.0f));
	root->setUpdateCallback(apcb.get());

	osgViewer::Viewer viewer;
	viewer.setSceneData(root.get());
	return viewer.run();
}

渲染状态动画

  • osg::StateAttributeCallback
    • 自定义osg::StateAttributeCallback并重载operator()
    • 加入属性的updatecallback
      • attribute->setUpdateCallback(attributeCallback);

示例

#include <osg/Geode>
#include <osg/Geometry>
#include <osg/BlendFunc>
#include <osg/Material>
#include <osgAnimation/EaseMotion>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>

class AlphaFadingCallback : public osg::StateAttributeCallback
{
public:
	AlphaFadingCallback()
	{
		_motion = new osgAnimation::InOutCubicMotion(0.0f, 1.0f);
	}


	virtual void operator()(osg::StateAttribute*, osg::NodeVisitor*);

protected:
	osg::ref_ptr<osgAnimation::InOutCubicMotion> _motion;
};

void AlphaFadingCallback::operator()(osg::StateAttribute* sa,osg::NodeVisitor* nv)
{
	osg::Material* material = static_cast<osg::Material*>(sa);
	if (material)
	{
		_motion->update(0.005);

		float alpha = _motion->getValue();
		material->setDiffuse(osg::Material::FRONT_AND_BACK,
			osg::Vec4(0.0f, 1.0f, 1.0f, alpha)
		);
	}
}

int main(int argc, char** argv)
{
	osg::ref_ptr<osg::Drawable> quad = osg::createTexturedQuadGeometry(
			osg::Vec3(-0.5f, 0.0f, -0.5f),
			osg::Vec3(1.0f, 0.0f, 0.0f), osg::Vec3(0.0f, 0.0f, 1.0f)
		);
	osg::ref_ptr<osg::Geode> geode = new osg::Geode;
	geode->addDrawable(quad.get());

	osg::ref_ptr<osg::Material> material = new osg::Material;
	material->setAmbient(osg::Material::FRONT_AND_BACK,
		osg::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
	material->setDiffuse(osg::Material::FRONT_AND_BACK,
		osg::Vec4(0.0f, 1.0f, 1.0f, 0.5f));
	material->setUpdateCallback(new AlphaFadingCallback);

	geode->getOrCreateStateSet()->setAttributeAndModes(
		material.get());
	geode->getOrCreateStateSet()->setAttributeAndModes(
		new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
	geode->getOrCreateStateSet()->setRenderingHint(
		osg::StateSet::TRANSPARENT_BIN);

	osg::ref_ptr<osg::Group> root = new osg::Group;
	root->addChild(geode.get());
	root->addChild(osgDB::readNodeFile("glider.osg"));

	osgViewer::Viewer viewer;
	viewer.setSceneData(root.get());
	return viewer.run();
}

图片动画

  • osg::ImageSequence 继承osg::Image
    • addImage()
    • setImage()
    • getImage()
    • getNumImages()
    • addImageFile()
    • setImageFile()
    • setLength()
    • setTimeMultiplier()
    • play()
    • pause()
    • rewind()
    • seek()
    • setMode
      • PRE_LOAD_ALL_IMAGES
      • PAGE_AND_RETAIN_IMAGES
      • PAGE_AND_DISCARD_USED_IMAGES

示例

#include <osg/ImageSequence>
#include <osg/Texture2D>
#include <osg/Geometry>
#include <osg/Geode>
#include <osgViewer/Viewer>

osg::Image* createSpotLight(const osg::Vec4& centerColor,
	const osg::Vec4& bgColor,
	unsigned int size, float power)
{
	osg::ref_ptr<osg::Image> image = new osg::Image;
	image->allocateImage(size, size, 1, GL_RGBA,
		GL_UNSIGNED_BYTE);


	float mid = (float(size) - 1) * 0.5f;
	float div = 2.0f / float(size);
	for (unsigned int r = 0; r<size; ++r)
	{
		unsigned char* ptr = image->data(0, r);
		for (unsigned int c = 0; c<size; ++c)
		{
			float dx = (float(c) - mid)*div;
			float dy = (float(r) - mid)*div;
			float r = powf(1.0f - sqrtf(dx*dx + dy*dy), power);
			if (r<0.0f) r = 0.0f; 
			osg::Vec4 color = centerColor*r + bgColor*(1.0f - r);
			*ptr++ = (unsigned char)((color[0]) * 255.0f);
			*ptr++ = (unsigned char)((color[1]) * 255.0f);
			*ptr++ = (unsigned char)((color[2]) * 255.0f);
			*ptr++ = (unsigned char)((color[3]) * 255.0f);
		}
	}
	return image.release();
}
int main(int argc, char** argv)
{
	osg::Vec4 centerColor(1.0f, 1.0f, 0.0f, 1.0f);
	osg::Vec4 bgColor(0.0f, 0.0f, 0.0f, 1.0f);
	osg::ref_ptr<osg::ImageSequence> sequence = new
		osg::ImageSequence;
	sequence->addImage(createSpotLight(centerColor, bgColor, 64,
		3.0f));
	sequence->addImage(createSpotLight(centerColor, bgColor, 64,
		3.5f));
	sequence->addImage(createSpotLight(centerColor, bgColor, 64,
		4.0f));
	sequence->addImage(createSpotLight(centerColor, bgColor, 64,
		3.5f));

	osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
	texture->setImage(sequence.get());

	osg::ref_ptr<osg::Geode> geode = new osg::Geode;
	geode->addDrawable(osg::createTexturedQuadGeometry(
		osg::Vec3(), osg::Vec3(1.0, 0.0, 0.0), osg::Vec3(0.0, 0.0, 1.0))
	);
	geode->getOrCreateStateSet()->setTextureAttributeAndModes(
		0, texture.get(), osg::StateAttribute::ON);

	sequence->setLength(0.5);
	sequence->play();

	osgViewer::Viewer viewer;
	viewer.setSceneData(geode.get());
	return viewer.run();
}
  • osg::ImageStream

关键帧动画

在这里插入图片描述

  • osgAnimation::TemplateKeyframe<>
  • osgAnimation::TemplateKeyframeContainer<>
  • osgAnimation::TemplateSampler<> 值求解器
    • getOrCreateKeyframeContainer
  • osgAnimation::TemplateChannel<>
    • getOrCreateSampler
    • setName
    • setTargetName通常为内建的更新回调
      • 比如osgAnimation::UpdateMatrixTransform,用channel的值每一帧更新osg::MatrixTransform节点的变换矩阵
        • getStackedTransforms
          • StackedTranslateElement
          • StackedScaleElement
          • StackedRotateAxisElement
          • StackedQuaternionElement
          • StackedMatrixElement
        • 加入osg::MatrixTransform节点的更新回调
          • transformnode->setDataVariance(osg::Object::DYNAMIC);
          • transformnode->setUpdateCallback(updater.get());
  • osg::Animation
    • setPlayMode
    • addChannel
    • getName
  • osgAnimation::BasicAnimationManager (root节点的更新回调)
    • registerAnimation
    • unregisterAnimation
    • getAnimationList
    • playAnimation
    • stopAnimation
    • isPlaying
    • 加入root节点的回调
      • root->setUpdateCallback(manager.get());

在这里插入图片描述

示例

#include <osg/MatrixTransform>
#include <osgAnimation/BasicAnimationManager>
#include <osgAnimation/UpdateMatrixTransform>
#include <osgAnimation/StackedTranslateElement>
#include <osgAnimation/StackedQuaternionElement>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>

void createAnimationPath(float radius, float time,
	osgAnimation::Vec3KeyframeContainer* container1,
	osgAnimation::QuatKeyframeContainer* container2)
{
	unsigned int numSamples = 32;
	float delta_yaw = 2.0f * osg::PI / ((float)numSamples - 1.0f);
	float delta_time = time / (float)numSamples;
	for (unsigned int i = 0; i<numSamples; ++i)
	{
		float yaw = delta_yaw * (float)i;
		osg::Vec3 pos(sinf(yaw)*radius, cosf(yaw)*radius, 0.0f);
		osg::Quat rot(-yaw, osg::Z_AXIS);
		container1->push_back(
			osgAnimation::Vec3Keyframe(delta_time * (float)i, pos)
		);
		container2->push_back(
			osgAnimation::QuatKeyframe(delta_time * (float)i, rot)
		);
	}
}
int main(int argc, char** argv)
{
	osg::ref_ptr<osgAnimation::Vec3LinearChannel> ch1 =
		new osgAnimation::Vec3LinearChannel;
	ch1->setName("position");
	ch1->setTargetName("PathCallback");

	osg::ref_ptr<osgAnimation::QuatSphericalLinearChannel> ch2 =
		new osgAnimation::QuatSphericalLinearChannel;
	ch2->setName("quat");
	ch2->setTargetName("PathCallback");

	createAnimationPath(50.0f, 6.0f,
		ch1->getOrCreateSampler()->getOrCreateKeyframeContainer(),
		ch2->getOrCreateSampler()->getOrCreateKeyframeContainer());

	osg::ref_ptr<osgAnimation::Animation> animation = new
		osgAnimation::Animation;
	animation->setPlayMode(osgAnimation::Animation::LOOP);
	animation->addChannel(ch1.get());
	animation->addChannel(ch2.get());

	osg::ref_ptr<osgAnimation::UpdateMatrixTransform> updater =
		new osgAnimation::UpdateMatrixTransform("PathCallback");
	updater->getStackedTransforms().push_back(
		new osgAnimation::StackedTranslateElement("position"));
	updater->getStackedTransforms().push_back(
		new osgAnimation::StackedQuaternionElement("quat"));

	osg::ref_ptr<osg::MatrixTransform> animRoot = new
		osg::MatrixTransform;
	animRoot->addChild(osgDB::readNodeFile("cessna.osg.0,0,90.rot"));
	animRoot->setDataVariance(osg::Object::DYNAMIC);
	animRoot->setUpdateCallback(updater.get());

	osg::ref_ptr<osgAnimation::BasicAnimationManager> manager =
		new osgAnimation::BasicAnimationManager;
	manager->registerAnimation(animation.get());

	osg::ref_ptr<osg::Group> root = new osg::Group;
	root->addChild(animRoot.get());
	root->setUpdateCallback(manager.get());

	manager->playAnimation(animation.get());

	osgViewer::Viewer viewer;
	viewer.setSceneData(root.get());
	return viewer.run();

}

人物动画

注意

节点必须为osgAnimation::Bone或者osgAnimation::Skeleton

制作

  • 工具
    • Autodesk 3dsmax
    • Autodesk Maya
    • Blender
    • 三方库
      • osgCal2
        • http://cal3d.sourceforge.net/
        • http://osgcal.sourceforge.net/
  • 格式
    • 输出FBX格式
    • 工具输出为OSG格式
      • http://hg.plopbyte.net/osgexport/

示例

#include <osgAnimation/BasicAnimationManager>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <iostream>


int main(int argc, char** argv)
{
	osg::ArgumentParser arguments(&argc, argv);
	bool listAll = false;
	std::string animationName;
	arguments.read("--animation", animationName);
	if (arguments.read("--listall")) listAll = true;
	osg::ref_ptr<osg::Node> model =
		osgDB::readNodeFile("bignathan.osg");
	if (!model) return 1;

	osgAnimation::BasicAnimationManager* manager =
		dynamic_cast<osgAnimation::BasicAnimationManager*>
		(model->getUpdateCallback());
	if (!manager) return 1;

	const osgAnimation::AnimationList& animations =
		manager->getAnimationList();
	if (listAll) std::cout << "**** Animations ****" << std::endl;
	for (unsigned int i = 0; i<animations.size(); ++i)
	{
		const std::string& name = animations[i]->getName();
		if (name == animationName)
			manager->playAnimation(animations[i].get());
		if (listAll) std::cout << name << std::endl;
	}
	if (listAll)
	{
		std::cout << "********************" << std::endl;
		return 0;
	}

	osgViewer::Viewer viewer;
	viewer.setSceneData(model.get());
	return viewer.run();
}
本人主要从事图形图象工作,空闲之余接触了一些游戏编程,特写一些编程心得,本文 适合没有接触过人物动画编程的初学者,希望游戏制作的大虾们指点,交流。 在以前还有没接触人物动画编程的时候,觉得通过编程让人物动起来一定是一件很麻烦 的事情,尤其是初学者,大都会摸不着头脑,碰到诸如骨骼动画之类,似乎无从下手。但是 当你了解了它们的格式,就会发现其实真正的人物动画的制作并不是在编程阶段,而是在模 型构建阶段,程序员主要做工作的是掌握模型文件的格式,将存储在人物模型中的各种信息, 如顶点,面片,材质,骨骼或顶点运动的关键帧序列等信息读入内存然后用你熟悉的 SDK 绘制出来,再根据时间采用线性或者球形插值对动作序列的关键帧进行插值,不断变换顶点 坐标,从而得到了一系列连续的人物动画,听起来确实不难吧!当然你也可以在程序里自己 控制人物每一帧每一个关节的运动,我想做游戏的很少有人这么做吧。下面我向大家介绍一 下自己是如何编写人物动画程序的。本人从事的图形图象开发主要是基于 OpenGL 和 OSG 因此范例程序将采用 OpenGL 或 OSG。先声明一下,本人的语言表达能力很差,请大家多 多谅解指正。 考虑到没有接触过人物模型的朋友,我首先从人物模型的结构讲起,游戏人物编程主要 采用的人物模型格式有 Quake 里的md2,md3,Half Life 里的 mdl,Doom里的 md5,还有 典型的骨骼动画模型 ms3d…,至于3dmax 的模型,本人觉得太麻烦!在此我说两个有代表 性的 Md3,和 ms3d,其它的模型都大同小异,只要你了解了它们的格式,程序实现都不难。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值