Karakter-animáció fizikai alapon
Szécsi László
xmlParser.h upgrade
NxQuat readNxQuat(XMLCSTR name);
std::string readString(XMLCSTR name);
double readDouble(XMLCSTR name, double defaultValue=0.0 );
xmlParser.cpp upgradestd::string XMLNode::readString(XMLCSTR name){
XMLCSTR wideString = getAttribute(name);if(wideString == NULL)
return "Not specified in XML.";unsigned int nChars = 0;nChars = WideCharToMultiByte(CP_ACP, 0, wideString, -1, NULL, 0, false, false);char* mbString = new char[nChars];WideCharToMultiByte(CP_ACP, 0, wideString, -1, mbString, nChars, false, false);std::string& ret = std::string(mbString);delete [] mbString;return ret;
}
xmlParser.cpp upgradeNxQuat XMLNode::readNxQuat(XMLCSTR name){
NxQuat v;v.id();size_t nameLength = wcslen(name);XMLSTR nameWithChannel = new XMLCHAR[nameLength+3];wcscpy(nameWithChannel, name);wcscat(nameWithChannel, L".x");v.x = readDouble(nameWithChannel, v.x);nameWithChannel[nameLength] = L'\0';wcscat(nameWithChannel, L".y");v.y = readDouble(nameWithChannel, v.y);nameWithChannel[nameLength] = L'\0';wcscat(nameWithChannel, L".z");v.z = readDouble(nameWithChannel, v.z);nameWithChannel[nameLength] = L'\0';wcscat(nameWithChannel, L".w");v.w = readDouble(nameWithChannel, v.w);nameWithChannel[nameLength] = L'\0';delete nameWithChannel;v.normalize();return v;
}
Directoryclass CharacterModel;typedef std::map<const std::wstring,
CharacterModel*>CharacterModelDirectory;
class CharacterBone;typedef std::map<const std::wstring, CharacterBone*>
CharacterBoneDirectory;typedef std::vector<CharacterBone*>
CharacterBoneList;
typedef std::vector<NxActor*> PhysicsActorList;typedef std::vector<NxJoint*> PhysicsJointList;
Letöltendő média fileok
orc^CharacterModel.xml
orc^NXU.xml
orc.x
www.iit.bme.hu/~szecsi/GraphGame/*.*
www.iit.bme.hu/~szecsi/GraphGame/NXU.ZIP
NXU
kibontani a forráskönyvtárba
minden egy NXU nevű alkönyvtárban van
solution explorer: add new filter "NXU"
job klikk az NXU folderen: add existing item
összes hozzáadás
.cpp-k kiválasztása
jobb klikk, properties, not using precompiled headers
CharacterBone
letöltendő kód:
www.iit.bme.hu/~szecsi/GraphGame/
AnimationFiles.zip
add existing item:
CharacterBone.h
CharacterBone.cpp
CharacterModel
add existing item:
CharacterModel.h
CharacterModel.cpp
EngineCore
CharacterModelDirectory characterModelDirectory;
void loadCharacterModels(XMLNode& xMainNode);
EngineCore.cpp
#include "CharacterModel.h"
EngineCore.cppvoid EngineCore::loadCharacterModels(XMLNode& xMainNode){
int iCharacterModel = 0;XMLNode characterModelNode;while( !(characterModelNode =
xMainNode.getChildNode(L"CharacterModel",iCharacterModel)).isEmpty() ) {
const wchar_t* name = characterModelNode|L"name"; const wchar_t* file = characterModelNode|L"file"; if(file) {
XMLNode modelNode = XMLNode::openFileHelper(file); if(name && !modelNode.isEmpty())
characterModelDirectory[name] = new CharacterModel(modelNode, this);
} iCharacterModel++;}
}
EngineCore::loadLevel
loadPhysicsModels(xMainNode);
loadCharacterModels(xMainNode);
EngineCore::releaseManagedResources
{CharacterModelDirectory::iterator i =characterModelDirectory.begin();
while(i !=characterModelDirectory.end()){
delete i->second;i++;
}}
DualQuaternion
add existing item:
DualQuaternion.h
DualQuaternion.cpp
PhysicsCharacter
add existing item:
PhysicsCharacter.h
PhysicsCharacter.cpp
EngineCore
void loadPhysicsCharacters(XMLNode& groupNode, NodeGroup* group);
EngineCore.cpp
#include "PhysicsCharacter.h"
EngineCore::loadGroup
loadPhysicsEntities(groupNode, group);
loadPhysicsCharacters(groupNode, group);
EngineCore.cpp
void EngineCore::loadPhysicsCharacters(XMLNode& groupNode, NodeGroup* group)
{int iPhysicsCharacter = 0;XMLNode physicsCharacterNode;while( !(physicsCharacterNode = groupNode.getChildNode(L"PhysicsCharacter", iPhysicsCharacter)).isEmpty() ){
// ide jön a következő diaiPhysicsCharacter++;
}}
előző beleconst wchar_t* shadedMeshName =
physicsCharacterNode|L"shadedMesh";ShadedMeshDirectory::iterator iShadedMesh =
shadedMeshDirectory.find(shadedMeshName);const wchar_t* characterModelName =
physicsCharacterNode|L"characterModel";CharacterModelDirectory::iterator iCharacterModel =
characterModelDirectory.find(characterModelName);if(iShadedMesh != shadedMeshDirectory.end() &&
iCharacterModel != characterModelDirectory.end()) {
D3DXVECTOR3 position =physicsCharacterNode.readVector(L"position");
PhysicsCharacter* physicsCharacter =new PhysicsCharacter(
iShadedMesh->second, iCharacterModel->second,nxScene, position);
group->add(physicsCharacter);const wchar_t* entityName = physicsCharacterNode|L"name";if(entityName)
entityDirectory[entityName] = physicsCharacter;}
XML
<Mesh name="orc" xFileName="media\\orc.x" skinned="true" autoShadedMesh="false"/>
<ShadedMesh name="orc" mesh="orc" >
<Role name="basic">
<Material technique="skinning">
</Material>
</Role>
</ShadedMesh>
<CharacterModel name="orc" file="media\\orc^CharacterModel.xml" />
EngineCore::loadMeshes
if(xFileName != NULL){
HRESULT hr = E_FAIL;
if(meshNode|L"skinned")
hr = D3DCreateSkinnedMeshFromX(
device, xFileName, &mesh);
else
hr = D3DXLoadMeshFromX(
xFileName, D3DXMESH_MANAGED, device, NULL, &materialBuffer, NULL, (DWORD*)&nSubmeshes, &mesh);
SkinnedMesh
Add existing item:
SkinnedMesh.h
SkinnedMesh.cpp
EngineCore.h:
#include "SkinnedMesh.h"
jöhet a vertex blending
új file: skinning.fx
engineCore.fx:
#include "skinning.fx"
PhysicsCharacter:
csont trafók beállítása
PhysicsCharacter::renderDualQuaternion boneTransforms[128];for(int iBone=0; iBone<128; iBone++) {
NxMat34& dePose = characterModel->getRiggingBonePoseInverse(boneActors[iBone]);
NxMat34 actorPose = physicsActorList.at(boneActors[iBone])->getGlobalPose();
NxMat34 pose = actorPose * dePose ;NxVec3 p = pose.t;NxQuat q(pose.M);
boneTransforms[iBone].set(D3DXQUATERNION(q.w, q.x, q.y, q.z),D3DXVECTOR3(p.x, p.y, p.z));
}HRESULT hr = context.effect->SetValue("bones",
(void*)boneTransforms, sizeof(boneTransforms));
shadedMesh->render(context);}
skinning.fx
struct Bone
{
float4 orientation;
float4 dualTranslation;
};
Bone bones[100];
skinning.fx
struct SkinningInput
{
float4 pos : POSITION;
float3 normal : NORMAL;
float2 tex : TEXCOORD0;
float4 blendWeights : BLENDWEIGHT;
float4 blendIndices : BLENDINDICES;
};
skinning.fx
struct SkinningOutput
{
float4 pos : POSITION;
float3 normal : TEXCOORD2;
float2 tex : TEXCOORD0;
float4 worldPos : TEXCOORD1;
float4 color : TEXCOORD3;
};
vertex shader eleje
SkinningOutput
vsSkinning(SkinningInput input) {
SkinningOutput output =
(SkinningOutput)0;
float4 blendIndices = D3DCOLORtoUBYTE4(
input.blendIndices);
input.blendWeights.w = 1 –
dot(input.blendWeights.xyz,
float3(1, 1, 1));
vertex shader: bone trafók
float2x4 qe0 = float2x4(bones[blendIndices.x].orientation, bones[blendIndices.x].dualTranslation);
float2x4 qe1 = float2x4(bones[blendIndices.y].orientation, bones[blendIndices.y].dualTranslation);
float2x4 qe2 = float2x4(bones[blendIndices.z].orientation, bones[blendIndices.z].dualTranslation);
float2x4 qe3 = float2x4(bones[blendIndices.w].orientation, bones[blendIndices.w].dualTranslation);
vertex shader: podality
float3 podality = float3(
dot(qe0[0], qe1[0]),
dot(qe0[0], qe2[0]),
dot(qe0[0], qe3[0]));
input.blendWeights.yzw *=
(podality >= 0)?1:-1;
vertex shader: trafó keverés
float2x4 qe =
input.blendWeights.x * qe0;
qe += input.blendWeights.y * qe1;
qe += input.blendWeights.z * qe2;
qe += input.blendWeights.w * qe3;
float len = length(qe[0]);
qe /= len;
vertex shader: trafó végrehajtás
float3 blendedPos =
input.pos.xyz + 2 * cross(qe[0].yzw,
cross(qe[0].yzw, input.pos.xyz) +
qe[0].x * input.pos.xyz);
float3 trans =
2.0*(qe[0].x*qe[1].yzw - qe[1].x*qe[0].yzw + cross(qe[0].yzw, qe[1].yzw));
blendedPos += trans;
vertex shader vége
output.pos = mul(float4(blendedPos, 1), modelViewProjMatrix);
output.normal = input.normal.xyz + 2.0*cross(qe[0].yzw, cross(qe[0].yzw, input.normal.xyz) + qe[0].x*input.normal.xyz);
output.tex = input.tex;
output.color = blendIndices * 0.01;
return output;
}
pixel shader
float4 psSkinning(SkinningOutput input) : COLOR0
{
return abs(input.normal.y);
}
technique
technique skinning
{
pass ExamplePass
{
VertexShader = compile vs_3_0 vsSkinning();
PixelShader = compile ps_3_0 psSkinning();
}
}
próba
ragdoll ork, gerincénél felfüggesztve
orc^NXU.xml:
<NxActorDesc id="Actor_0" userProperties="" hasBody="true" name="CenterOfMassRigidBody">
… <NX_BF_KINEMATIC>false</NX_BF_KINEMATIC>
próba: összeeső ragdoll ork
animáció
orc^CharacterAnimation.xml
CharacterModel
class CharacterAnimation;
class CharacterModel{
CharacterAnimation*characterAnimation;
public:
unsigned int getBoneCount();
const std::wstring& getBoneNameByBoneIndex(unsigned int iBone);
CharacterAnimation*
getCharacterAnimation();
CharacterModel.cpp
const std::wstring& CharacterModel::
getBoneNameByBoneIndex(
unsigned int iBone)
{
return characterBoneList.at(iBone)
->getName();
}
CharacterModel.cpp
unsigned int CharacterModel::getParentIndexByBoneIndex(unsigned int boneIndex)
{if(boneIndex >=
characterBoneList.size())return 0;
return getBoneIndexByBoneName(characterBoneList.at(boneIndex)->getParent()->getName());
}
CharacterModel.cpp
CharacterAnimation* CharacterModel::getCharacterAnimation()
{
return characterAnimation;
}
unsigned int CharacterModel::getBoneCount()
{
return characterBoneList.size();
}
CharacterAnimation
add existing item:
CharacterAnimation.h
CharacterAnimation.cpp
Átszervezés
Rigging-pose-os dolgokat rakjuk át az animation-be
(már benne van csak a CharacterModel-ből szedjük ki)
törlés a CharacterModel-ből
bool initialized;
std::vector<NxMat34> riggingBonePoseInverses;
void setInitialized();
bool isInitialized();
NxMat34& getRiggingBonePoseInverse(unsigned int iBone);
void addRiggingBonePoseInverse(NxMat34& dePose);
CharacterModel::CharacterModel
Animáció betöltése#include "CharacterAnimation.h"CharacterModel::CharacterModel(XMLNode& modelNode){
actorsAndJointsCollection = NULL;loadSerializedScene(modelNode);
const wchar_t* animFile = modelNode|L"characterAnimation";if(animFile){
XMLNode& animNode =XMLNode::openFileHelper(animFile, L"CharacterAnimation");
characterAnimation = new CharacterAnimation(animNode, this);
}else
characterAnimation = NULL;}
CharacterAnimation (már megvan)
unsigned char boneActors[128];
unsigned char jointBones[128];
Directory.h
class Clip;
typedef std::map<
const std::wstring, Clip*, CompareStringsW> ClipDirectory;
Clip, Key
add existing item
Clip.h, Clip.cpp, Key.h, Key.cpp
Animation: Clip gyűjtemény
Clip: Key gyűjtemény
Key: bone orientációk gyűjteménye
Clip
void getKeyTimes(unsigned int iKey, double& keyTime, double& nextKeyTime);
void getNextKey(unsigned int& iKey, double& keyTime, double& nextKeyTime);
NxQuat slerpBoneOrientation(unsigned int iKey,
unsigned int iBone, double a);
Key
NxQuat getBoneOrientation(unsigned int iJoint);
AnimationState
add existing:
CharacterAnimationState.h
CharacterAnimationPlayer.h
CharacterAnimationPlayer.cpp
CharacterAnimationState
virtual void advance(double dt)=0;
virtual NxQuat getJointOrientation(unsigned int iJoint)=0;
CharacterAnimationPlayer
iterátor-szerűség egy animáció felett
hol tartunk épp most
egy bone orientációját lekérhetjük
CharacterAnimationPlayer
class CharacterAnimationPlayer :
public CharacterAnimationState {
ClipDirectory::iterator currentClip;
unsigned int iKey;
double keyTime;
double nextKeyTime;
double time;
PhysicsCharacter
na ezt most nagyon át kell írni
(vagy kivenni az új verziót a zip fileból)
ez nem kell (Animation-ben van):
unsigned char boneActors[128];
CharacterModel* characterModel;
helyett
CharacterAnimation* characterAnimation;
CharacterAnimationState* characterAnimationState;
ActorBinder módosítás
friend class ActorBinder;class ActorBinder : public NXU_userNotify{
PhysicsCharacter* owner;CharacterModel* characterModel;
public:void NXU_notifyActor (NxActor
*actor, const char *userProperties);void NXU_notifyJoint (NxJoint
*joint, const char *userProperties); ActorBinder(PhysicsCharacter* owner,
CharacterModel* characterModel);};
PhysicsCharacter::PhysicsCharacter
törölni:
this->characterModel =
characterModel;
for(int y=0; y<128; y++)
boneActors[y] = 0xff;
berakni:
characterAnimation = characterModel
->getCharacterAnimation();
characterModel->instantiate(nxScene, &pose, ActorBinder(this, characterModel));
PhysicsCharacter::ActorBinder:: ActorBinder(PhysicsCharacter* owner, CharacterModel* characterModel) {this->owner = owner;this->characterModel = characterModel;
}
Jointok betöltése
PhysicsCharacter-ben tároljuk
PhysicsJointList physicsJointList;
NXU_notifyActor eleje
void PhysicsCharacter::ActorBinder::
NXU_notifyActor (NxActor *actor,
const char *userProperties) {
if(actor->isDynamic() && !actor-> readBodyFlag(NX_BF_KINEMATIC)) {
NxVec3 zero;
zero.zero();
actor->setAngularVelocity(zero);
actor->setLinearVelocity(zero);
}
NXU_notifyActor végeowner->physicsActorList.push_back(actor);if(!owner->characterAnimation->isInitialized()) {
NxMat34 dePose;actor->getGlobalPose().getInverse(dePose);owner->characterAnimation
->addRiggingActorPoseInverse(dePose);const char* actorName = actor->getName();unsigned int nChars = 0;nChars = MultiByteToWideChar(CP_ACP, 0, actorName, -1, NULL, 0);wchar_t* wcString = new wchar_t[nChars];MultiByteToWideChar(CP_ACP, 0, actorName, -1, wcString, nChars);wcString[nChars-10] = L'\0'; // cut "RigidBody" postfixowner->characterAnimation->setBoneActor(
characterModel->getBoneIndexByBoneName(wcString), owner->physicsActorList.size() - 1);
delete wcString;}
}
NXU_notifyJoint eleje
void PhysicsCharacter::ActorBinder
::NXU_notifyJoint (
NxJoint *joint,
const char *userProperties)
{
NXU_notifyJoint folytif(!owner->characterAnimation->isInitialized()){
NxActor* actor0; NxActor* actor1;joint->getActors(&actor0, &actor1);const char* jointName = actor1->getName();unsigned int nChars = 0;nChars = MultiByteToWideChar(CP_ACP, 0, jointName, -1, NULL, 0);wchar_t* wcString = new wchar_t[nChars];
MultiByteToWideChar(CP_ACP, 0, jointName, -1, wcString, nChars);wcString[nChars-10] = L'\0'; // cut "RigidBody" postfix
unsigned int boneIndex = characterModel->getBoneIndexByBoneName(wcString);
owner->characterAnimation->setJointBone(owner->physicsJointList.size(), boneIndex);
delete wcString;}
NXU_notifyJoint folyt
NxD6Joint* j =
(NxD6Joint*)joint->isD6Joint();
if(j)
{
NxD6JointDesc desc;
j->saveToDesc(desc);
NXU_notifyJoint folytif(desc.swing1Motion == NX_D6JOINT_MOTION_LIMITED ||
desc.swing2Motion == NX_D6JOINT_MOTION_LIMITED ||desc.twistMotion == NX_D6JOINT_MOTION_LIMITED ||desc.swing1Motion == NX_D6JOINT_MOTION_FREE ||desc.swing2Motion == NX_D6JOINT_MOTION_FREE ||desc.twistMotion == NX_D6JOINT_MOTION_FREE){
desc.swing1Motion = NX_D6JOINT_MOTION_FREE;desc.swing2Motion = NX_D6JOINT_MOTION_FREE;desc.twistMotion = NX_D6JOINT_MOTION_FREE;
NXU_notifyJoint folyt
desc.swingDrive.driveType = NX_D6JOINT_DRIVE_POSITION;
desc.twistDrive.driveType = NX_D6JOINT_DRIVE_POSITION;
desc.swingDrive.spring = 10000.0;desc.swingDrive.forceLimit = FLT_MAX;desc.swingDrive.damping = 10.0f;desc.twistDrive.spring = 10000.0;desc.twistDrive.forceLimit = FLT_MAX;desc.twistDrive.damping = 10.0f;
NXU_notifyJoint vége
}
j->loadFromDesc(desc);
}
owner->
physicsJointList.push_back(joint);
}
PhysicsCharacter::PhysicsCharacter
törölni:for(int y=0; y<127; y++){
int referred = y;while(boneActors[referred] == 0xff)
referred = characterModel->getParentIndexByBoneIndex(referred);
boneActors[y] = boneActors[referred];}
berakni:characterAnimation
->setInitialized(characterModel);characterAnimationState = new CharacterAnimationPlayer(
characterAnimation);
~PhysicsCharacter
PhysicsCharacter::~PhysicsCharacter()
{
delete characterAnimationState;
}
render
for(int iBone=0; iBone<128; iBone++){
unsigned int iActor;
NxMat34& dePose = characterAnimation
->getRiggingActorPoseInverse(
iBone, iActor);
NxMat34 actorPose = physicsActorList.at(iActor)
->getGlobalPose();
NxMat34 pose = actorPose * dePose ;
PhysicsCharacter::animatevoid PhysicsCharacter::animate(double dt){
characterAnimationState->advance(dt);PhysicsJointList::iterator pi =
physicsJointList.begin();unsigned int iJoint=0;while(pi != physicsJointList.end()) {
NxQuat xq;NxD6Joint* j = (*pi)->isD6Joint();if(j) {
xq = characterAnimationState->getJointOrientation(iJoint);
j->setDriveOrientation( xq );}pi++;iJoint++;
}}
Próba
valami furcsa
gond:
a fizikai modell nem pont ugyanott van, mint a vertexek!
Hibakeresés
PhysicsCharacter::render-be
NxMat34 pose =
/* actorPose * */ dePose;
NxVec3 p = pose.t;
NxQuat q(pose.M);
Hibakeresés
skinning.fx-be
VS:
input.blendWeights = float4(1, 0, 0, 0); // merev skinning
output.color = length(blendedPos) / 4.0;
PS:
return input.color;
Próba
eredmény: kis helyre becsomagolva minden
és az origó nem a közepén van!
Gyorsjavítás
vsSkinning(SkinningInput input)
{
input.pos.z -= 2;
és csináljunk vissza mindent:
- actorPose * dePose;
- vertex blending
- pixel shader színezés
Top Related