topical media & game development

talk show tell print

lib-game-delta3d-sdk-examples-testProceduralAnimation-proceduralanimationactor.cpp / cpp



  /*
  * Delta3D Open Source Game and Simulation Engine
  * Copyright (C) 2009 MOVES Institute
  *
  * This library is free software; you can redistribute it and/or modify it under
  * the terms of the GNU Lesser General Public License as published by the Free
  * Software Foundation; either version 2.1 of the License, or (at your option)
  * any later version.
  *
  * This library is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  * details.
  *
  * You should have received a copy of the GNU Lesser General Public License
  * along with this library; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  *
  * Michael Guerrero
  */
  
  include <proceduralanimationactor.h>
  
  include <dtCore/transform.h>
  include <dtDAL/enginepropertytypes.h>
  include <dtGame/basemessages.h>
  include <dtGame/gameactor.h>
  include <dtGame/messagetype.h>
  include <dtGame/gamemanager.h>
  include <dtUtil/mathdefines.h>
  
  include <dtAnim/animationhelper.h>
  include <dtAnim/cal3dmodelwrapper.h>
  include <dtAnim/posemeshdatabase.h>
  include <dtAnim/posemath.h>
  
  include <cassert>
  
  
////////////////////////////////////////////////////

// Proxy code
////////////////////////////////////////////////////

ProceduralAnimationActorProxy::ProceduralAnimationActorProxy() { SetClassName("ProceduralAnimationActor"); }
//////////////////////////////////////////////////////////////////////////

ProceduralAnimationActorProxy::~ProceduralAnimationActorProxy() { }
//////////////////////////////////////////////////////////////////////////

void ProceduralAnimationActorProxy::BuildInvokables() { dtAnim::AnimationGameActorProxy::BuildInvokables(); }
//////////////////////////////////////////////////////////////////////////

void ProceduralAnimationActorProxy::BuildPropertyMap() { dtAnim::AnimationGameActorProxy::BuildPropertyMap(); }
//////////////////////////////////////////////////////////////////////////

void ProceduralAnimationActorProxy::CreateActor() { ProceduralAnimationActor* pActor = new ProceduralAnimationActor(*this); SetActor(*pActor); }
////////////////////////////////////////////////////

// Actor code
////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////

ProceduralAnimationActor::ProceduralAnimationActor(ProceduralAnimationActorProxy& proxy) : AnimationGameActor(proxy) , mMode(MODE_AIM) , mPoseMeshDatabase(NULL) , mPoseMeshUtil(NULL) , mBlendTime(0.3f) , mCurrentTarget(NULL) { memset(mMarinePoseData.mPoseMeshes, 0, sizeof(dtAnim::PoseMesh*) * ProceduralAnimationData::PMP_TOTAL); // Initialize ik target data for (size_t partIndex = ProceduralAnimationData::PMP_FIRST; partIndex < ProceduralAnimationData::PMP_TOTAL; ++partIndex) { mMarinePoseData.mTargetTriangles[partIndex].mIsInside = false; mMarinePoseData.mTargetTriangles[partIndex].mTriangleID = -1; } }
//////////////////////////////////////////////////////////////////////////

ProceduralAnimationActor::~ProceduralAnimationActor() { }
//////////////////////////////////////////////////////////////////////////

void ProceduralAnimationActor::OnEnteredWorld() { AnimationGameActor::OnEnteredWorld(); // Make sure we receive the tick messages GetGameActorProxy().RegisterForMessages(dtGame::MessageType::TICK_LOCAL, dtGame::GameActorProxy::TICK_LOCAL_INVOKABLE); }
//////////////////////////////////////////////////////////////////////////

void ProceduralAnimationActor::SetPoseMeshDatabase(dtAnim::PoseMeshDatabase* poseMeshDatabase) { // Is there any reason to set this a second time? assert(!mPoseMeshDatabase); mPoseMeshDatabase = poseMeshDatabase; // Make sure we have a utility to use on the posemesh data if (!mPoseMeshUtil.valid()) { mPoseMeshUtil = new dtAnim::PoseMeshUtility; } // Get access to each individual part AssemblePoseData(); }
//////////////////////////////////////////////////////////////////////////

void ProceduralAnimationActor::SetTarget(const dtCore::Transformable* target, osg::Vec3* offset) { mCurrentTarget = target; if (offset != NULL) { mTargetOffset = *offset; } }
//////////////////////////////////////////////////////////////////////////

void ProceduralAnimationActor::SetBlendTime(float blendTime) { mBlendTime = blendTime; }
//////////////////////////////////////////////////////////////////////////

void ProceduralAnimationActor::AssemblePoseData() { mMarinePoseData.mPoseMeshes[ProceduralAnimationData::LEFT_EYE] = mPoseMeshDatabase->GetPoseMeshByName("Poses_LeftEye"); mMarinePoseData.mPoseMeshes[ProceduralAnimationData::RIGHT_EYE] = mPoseMeshDatabase->GetPoseMeshByName("Poses_RightEye"); mMarinePoseData.mPoseMeshes[ProceduralAnimationData::HEAD] = mPoseMeshDatabase->GetPoseMeshByName("Poses_Head"); mMarinePoseData.mPoseMeshes[ProceduralAnimationData::TORSO] = mPoseMeshDatabase->GetPoseMeshByName("Poses_Torso"); mMarinePoseData.mPoseMeshes[ProceduralAnimationData::GUN] = mPoseMeshDatabase->GetPoseMeshByName("Poses_Gun"); }
//////////////////////////////////////////////////////////////////////////

void ProceduralAnimationActor::OnTickLocal(const dtGame::TickMessage& tickMessage) { float dt = tickMessage.GetDeltaSimTime(); // inverse kinematics TickIK(dt); // canned animation update GetHelper()->GetModelWrapper()->Update(dt); }
//////////////////////////////////////////////////////////////////////////

void ProceduralAnimationActor::TickIK(float dt) { dtCore::Transform targetTransform; mCurrentTarget->GetTransform(targetTransform); // The 2 unit offset here is a crude approximation for this osg::Vec3 ownPosition = GetHeadPosition(); // We might want to get a point slightly offset from the base position so add it here osg::Vec3 targetPosition = targetTransform.GetTranslation() + mTargetOffset; // This is the direction from us to the target osg::Vec3 lookDirection = targetPosition - ownPosition; lookDirection.normalize(); osg::Vec3 actorForward = GetForwardDirection(); if (mMode == MODE_WATCH) { assert(mCurrentTarget); float remAzimuth = 0.0f; float remElevation = 0.0f; // Get the relative azimuth and elevation dtAnim::GetCelestialCoordinates(lookDirection, actorForward, remAzimuth, remElevation); // Get convenient short handles to all of our pose mesh data dtAnim::PoseMesh* eyeMesh1 = mMarinePoseData.mPoseMeshes[ProceduralAnimationData::LEFT_EYE]; dtAnim::PoseMesh* eyeMesh2 = mMarinePoseData.mPoseMeshes[ProceduralAnimationData::RIGHT_EYE]; dtAnim::PoseMesh* headMesh = mMarinePoseData.mPoseMeshes[ProceduralAnimationData::HEAD]; dtAnim::PoseMesh* torsoMesh = mMarinePoseData.mPoseMeshes[ProceduralAnimationData::TORSO]; dtAnim::PoseMesh::TargetTriangle& eyeTarget1 = mMarinePoseData.mTargetTriangles[ProceduralAnimationData::LEFT_EYE]; dtAnim::PoseMesh::TargetTriangle& eyeTarget2 = mMarinePoseData.mTargetTriangles[ProceduralAnimationData::RIGHT_EYE]; dtAnim::PoseMesh::TargetTriangle& headTarget = mMarinePoseData.mTargetTriangles[ProceduralAnimationData::HEAD]; dtAnim::PoseMesh::TargetTriangle& torsoTarget = mMarinePoseData.mTargetTriangles[ProceduralAnimationData::TORSO]; eyeMesh1->GetTargetTriangleData(remAzimuth, remElevation, eyeTarget1); eyeMesh2->GetTargetTriangleData(remAzimuth, remElevation, eyeTarget2); // The eyes have moved over, does the head need to move after this? remAzimuth -= eyeTarget1.mAzimuth; remElevation -= eyeTarget2.mElevation; // The head has moved over, does the torso need to move headMesh->GetTargetTriangleData(remAzimuth, remElevation, headTarget); remAzimuth -= headTarget.mAzimuth; remElevation -= headTarget.mElevation; torsoMesh->GetTargetTriangleData(remAzimuth, remElevation, torsoTarget); // Use the pose mesh util to apply the blends mPoseMeshUtil->BlendPoses(eyeMesh1, GetHelper()->GetModelWrapper(), eyeTarget1, mBlendTime); mPoseMeshUtil->BlendPoses(eyeMesh2, GetHelper()->GetModelWrapper(), eyeTarget2, mBlendTime); mPoseMeshUtil->BlendPoses(headMesh, GetHelper()->GetModelWrapper(), headTarget, mBlendTime); mPoseMeshUtil->BlendPoses(torsoMesh, GetHelper()->GetModelWrapper(), torsoTarget, mBlendTime); } else // we are aiming { float remAzimuth = 0.0f; float remElevation = 0.0f; dtAnim::GetCelestialCoordinates(lookDirection, actorForward, remAzimuth, remElevation); dtAnim::PoseMesh* gunMesh = mMarinePoseData.mPoseMeshes[ProceduralAnimationData::GUN]; dtAnim::PoseMesh::TargetTriangle& gunTarget = mMarinePoseData.mTargetTriangles[ProceduralAnimationData::GUN]; gunMesh->GetTargetTriangleData(remAzimuth, remElevation, gunTarget); mPoseMeshUtil->BlendPoses(gunMesh, GetHelper()->GetModelWrapper(), gunTarget, mBlendTime); // Only shoot when in sight and standing still if (gunTarget.mIsInside) { // do something here? } else { float rotationSign = 1.0f; // Determine which direction to rotate toward if ((lookDirection ^ actorForward).z() > 0.0f) { rotationSign = -1.0f; } dtCore::Transform currentTransform; GetTransform(currentTransform); osg::Vec3 hpr; currentTransform.GetRotation(hpr); // Start rotating closer to putting the target in reach hpr.x() += 100.0f * dt * rotationSign; currentTransform.SetRotation(hpr); SetTransform(currentTransform); } } }
//////////////////////////////////////////////////////////////////////////

void ProceduralAnimationActor::AddedToScene(dtCore::Scene* scene) { dtAnim::AnimationGameActor::AddedToScene(scene); }
//////////////////////////////////////////////////////////////////////////

osg::Vec3 ProceduralAnimationActor::GetPoseMeshEndEffectorDirection(const dtAnim::PoseMesh* poseMesh) const { const dtAnim::Cal3DModelWrapper* modelWrapper = GetHelper()->GetModelWrapper(); // If we have the ik system attached if (poseMesh != NULL) { osg::Quat boneRotation = modelWrapper->GetBoneAbsoluteRotation(poseMesh->GetEffectorID()); osg::Vec3 nativeBoneForward = poseMesh->GetEffectorForwardAxis(); dtCore::Transform transform; GetTransform(transform); osg::Matrix modelRotation; transform.GetRotation(modelRotation); // Get the direction the head is facing osg::Vec3 boneDirection = boneRotation * nativeBoneForward; boneDirection = boneDirection * modelRotation; osg::Quat rotationCorrection(osg::DegreesToRadians(180.0f), osg::Z_AXIS); return rotationCorrection * boneDirection; } return GetForwardDirection(); }
//////////////////////////////////////////////////////////////////////////

dtAnim::Cal3DModelWrapper* ProceduralAnimationActor::GetModelWrapper() { return GetHelper()->GetModelWrapper(); }
//////////////////////////////////////////////////////////////////////////

osg::Vec3 ProceduralAnimationActor::GetForwardDirection() const { dtCore::Transform currentTransform; GetTransform(currentTransform); osg::Matrix matrix; currentTransform.Get(matrix); // Many characters face backwards so we negate the forward direction return -osg::Vec3(matrix(1, 0), matrix(1, 1), matrix(1, 2)); }
//////////////////////////////////////////////////////////////////////////

osg::Vec3 ProceduralAnimationActor::GetGazeDirection() const { dtAnim::PoseMesh* headMesh = mMarinePoseData.mPoseMeshes[ProceduralAnimationData::HEAD]; if (headMesh) { return GetPoseMeshEndEffectorDirection(headMesh); } else { return GetForwardDirection(); } }
//////////////////////////////////////////////////////////////////////////

osg::Vec3 ProceduralAnimationActor::GetGunDirection() const { dtAnim::PoseMesh* gunMesh = mMarinePoseData.mPoseMeshes[ProceduralAnimationData::GUN]; return GetPoseMeshEndEffectorDirection(gunMesh); }
//////////////////////////////////////////////////////////////////////////

osg::Vec3 ProceduralAnimationActor::GetGunPosition() const { const dtAnim::Cal3DModelWrapper* modelWrapper = GetHelper()->GetModelWrapper(); dtAnim::PoseMesh* gunMesh = mMarinePoseData.mPoseMeshes[ProceduralAnimationData::GUN]; dtCore::Transform transform; GetTransform(transform); osg::Matrix rotation; osg::Vec3 translation; transform.GetRotation(rotation); transform.GetTranslation(translation); // Get the bone position relative to its root osg::Vec3 bonePosition = modelWrapper->GetBoneAbsoluteTranslation(gunMesh->GetEffectorID()); // Get the gun position in the world osg::Quat rotationCorrection(osg::DegreesToRadians(180.0f), osg::Z_AXIS); bonePosition = rotationCorrection * (bonePosition * rotation); bonePosition += translation; return bonePosition; }
//////////////////////////////////////////////////////////////////////////

// This could be more efficient osg::Vec3 ProceduralAnimationActor::GetWorldPosition() const { dtCore::Transform transform; GetTransform(transform); osg::Vec3 translation; transform.GetTranslation(translation); return translation; }
//////////////////////////////////////////////////////////////////////////

osg::Vec3 ProceduralAnimationActor::GetHeadPosition() const { const dtAnim::Cal3DModelWrapper* modelWrapper = GetHelper()->GetModelWrapper(); dtAnim::PoseMesh* headMesh = mMarinePoseData.mPoseMeshes[ProceduralAnimationData::HEAD]; dtCore::Transform transform; GetTransform(transform); osg::Matrix rotation; osg::Vec3 translation; transform.GetRotation(rotation); transform.GetTranslation(translation); // Get the bone position relative to its root osg::Vec3 bonePosition = modelWrapper->GetBoneAbsoluteTranslation(headMesh->GetEffectorID()); // Get the gun position in the world bonePosition = bonePosition * rotation; bonePosition += translation; return bonePosition; }
//////////////////////////////////////////////////////////////////////////



(C) Æliens 04/09/2009

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.