Added missing files

This commit is contained in:
2025-07-09 15:01:16 +03:00
parent 912f3e5368
commit 99b711f42f
4 changed files with 1272 additions and 0 deletions

View File

@@ -0,0 +1,366 @@
#include <iostream>
#include <Ogre.h>
#include <OgreBullet.h>
#include "BulletCollision/CollisionDispatch/btGhostObject.h"
#include "LinearMath/btTransform.h"
#include "character.h"
#define RUN_SPEED 17 // character running speed in units per second
#define TURN_SPEED 500.0f // character turning in degrees per second
#define ANIM_FADE_SPEED \
7.5f // animation crossfade speed in % of full weight per second
Character::Character(Ogre::SceneManager *scnMgr, const Ogre::String &model,
Ogre::Bullet::DynamicsWorld *world)
: mModelName(model)
, mScnMgr(scnMgr)
, mPivotPitch(0)
, mVerticalVelocity(0)
, mAnimID(ANIM_NONE)
, mRunning(false)
, mCollisionShape(nullptr)
, mGhostObject(nullptr)
, mWorld(world)
, mGoalDirection(0, 0, 0)
{
setupBody();
setupAnimations();
}
Character::~Character()
{
}
bool Character::frameStarted(const Ogre::FrameEvent &evt)
{
return true;
}
bool Character::frameRenderingQueued(const Ogre::FrameEvent &evt)
{
updateBody(evt.timeSinceLastFrame);
updateAnimations(evt.timeSinceLastFrame);
if (evt.timeSinceLastFrame > 0)
updateRootMotion(evt.timeSinceLastFrame);
return true;
}
bool Character::frameEnded(const Ogre::FrameEvent &evt)
{
return true;
}
void Character::setupBody()
{
mBodyEnt = mScnMgr->createEntity(mModelName);
mBodyNode = mScnMgr->getRootSceneNode()->createChildSceneNode();
mBodyNode->attachObject(mBodyEnt);
mSkeleton = mBodyEnt->getSkeleton();
// mRigidBody = world->addCharacter(mBodyEnt, 0);
// mCollisionShape = static_cast<btCompoundShape *>(mRigidBody->getCollisionShape());
mGhostObject = new btPairCachingGhostObject();
mCollisionShape = new btCompoundShape;
mGhostObject->setCollisionShape(mCollisionShape);
{
btVector3 inertia(0, 0, 0);
// mCollisionShape = new btCompoundShape();
btScalar height = 1.0f;
btScalar radius = 0.3f;
btCapsuleShape *shape =
new btCapsuleShape(radius, 2 * height - 2 * radius);
btTransform transform;
transform.setIdentity();
transform.setOrigin(btVector3(0, 1, 0));
static_cast<btCompoundShape *>(mCollisionShape)
->addChildShape(transform, shape);
btScalar masses[1] = { 0 };
btTransform principal;
static_cast<btCompoundShape *>(mCollisionShape)
->calculatePrincipalAxisTransform(masses, principal,
inertia);
}
mGhostObject->setCollisionFlags(
btCollisionObject::CF_KINEMATIC_OBJECT |
btCollisionObject::CF_NO_CONTACT_RESPONSE);
mGhostObject->setActivationState(DISABLE_DEACTIVATION);
Ogre::Bullet::KinematicMotionSimple *controller =
new Ogre::Bullet::KinematicMotionSimple(mGhostObject,
mBodyNode);
mWorld->attachCollisionObject(mGhostObject, mBodyEnt,
btBroadphaseProxy::AllFilter,
btBroadphaseProxy::AllFilter);
mWorld->getBtWorld()->addAction(controller);
assert(mCollisionShape);
#if 0
if (mRigidBody->getMass() == 0) {
#if 0
mRigidBody->setCollisionFlags(mRigidBody->getCollisionFlags()
| btCollisionObject::CF_KINEMATIC_OBJECT
| btCollisionObject::CF_NO_CONTACT_RESPONSE
);
#endif
#if 0
mGhostObject->setWorldTransform(mRigidBody->getWorldTransform());
WorldData::get_singleton()->getBtWorld()
->getBroadphase()->getOverlappingPairCache()
->setInternalGhostPairCallback(new btGhostPairCallback());
#endif
}
#endif
#if 0
mRigidBody->setActivationState(DISABLE_DEACTIVATION);
#endif
#if 0
{
Ogre::Entity *e2 = mScnMgr->createEntity("normal-male.glb");
Ogre::SceneNode *e2node = mScnMgr->getRootSceneNode()->createChildSceneNode();
e2node->attachObject(e2);
mGhostObject = WorldData::get_singleton()->addGhostObject(e2, mCollisionShape);
mController = new btKinematicCharacterController(mGhostObject, mCollisionShape, 0.5f);
WorldData::get_singleton()->getBtWorld()->addAction(mController);
}
#endif
assert(mSkeleton->hasBone("Root"));
mRootBone = mSkeleton->getBone("Root");
assert(mRootBone);
}
void Character::setupAnimations()
{
int i, j;
mSkeleton->setBlendMode(Ogre::ANIMBLEND_CUMULATIVE);
Ogre::String animNames[NUM_ANIMS] = { "idle", "walking", "running" };
for (i = 0; i < NUM_ANIMS; i++) {
mAnims[i] = mBodyEnt->getAnimationState(animNames[i]);
mAnims[i]->setLoop(true);
mAnims[i]->setEnabled(true);
mAnims[i]->setWeight(0);
mFadingIn[i] = false;
mFadingOut[i] = false;
mSkelAnimations[i] = mSkeleton->getAnimation(animNames[i]);
for (const auto &it : mSkelAnimations[i]->_getNodeTrackList()) {
Ogre::NodeAnimationTrack *track = it.second;
Ogre::String trackName =
track->getAssociatedNode()->getName();
if (trackName == "mixamorig:Hips") {
mHipsTracks[i] = track;
} else if (trackName == "Root") {
mRootTracks[i] = track;
// mRootTracks[i]->removeAllKeyFrames();
}
}
Ogre::Vector3 delta = Ogre::Vector3::ZERO;
Ogre::Vector3 motion = Ogre::Vector3::ZERO;
for (j = 0; j < mRootTracks[i]->getNumKeyFrames(); j++) {
Ogre::Vector3 trans = mRootTracks[i]
->getNodeKeyFrame(j)
->getTranslate();
if (j == 0)
delta = trans;
else
delta = trans - motion;
mRootTracks[i]->getNodeKeyFrame(j)->setTranslate(delta);
motion = trans;
}
}
#if 0
for(i = 0; i < NUM_ANIMS - 1; i++) {
// need to cache
int j;
Ogre::String animName = mAnims[i]->getAnimationName();
Ogre::Animation *anim = mSkeleton->getAnimation(animName);
Ogre::NodeAnimationTrack *hips_track = nullptr, *root_track = nullptr;
Ogre::Node *root_node = nullptr;
for (const auto& it : anim->_getNodeTrackList()) {
Ogre::NodeAnimationTrack* track = it.second;
Ogre::String trackName = track->getAssociatedNode()->getName();
std::cout << animName << " track: " << trackName << "\n";
if (trackName == "mixamorig:Hips")
hips_track = track;
else if (trackName == "Root") {
root_track = track;
root_node = track->getAssociatedNode();
}
}
assert(false);
root_track->removeAllKeyFrames();
std::cout << hips_track << " " << root_track << "\n";
std::cout << hips_track->getNumKeyFrames() << " " << root_track->getNumKeyFrames() << "\n";
assert(hips_track && root_track);
Ogre::Vector3 delta = Ogre::Vector3::ZERO;
for(j = 0; j < hips_track->getNumKeyFrames(); j++) {
float timePos = hips_track->getNodeKeyFrame(j)->getTime();
Ogre::Vector3 trans = hips_track->getNodeKeyFrame(j)->getTranslate();
Ogre::Vector3 hips_trans(0, 0, 0);
Ogre::Vector3 root_trans(0, 0, 0);
hips_track->getNodeKeyFrame(j)->setTranslate(hips_trans);
Ogre::TransformKeyFrame *nk = root_track->createNodeKeyFrame(timePos);
nk->setTranslate(root_trans - delta);
nk->setScale(Ogre::Vector3(1, 1, 1));
nk->setRotation(Ogre::Quaternion());
std::cout << animName << " delta: " << j << " " << timePos << " " << root_trans - delta << "\n";
delta = root_trans;
}
for(j = 0; j < root_track->getNumKeyFrames(); j++) {
float timePos = hips_track->getNodeKeyFrame(j)->getTime();
Ogre::Vector3 root_trans = hips_track->getNodeKeyFrame(j)->getTranslate();
std::cout << animName << " delta: root: " << j << " " << timePos << " " << root_trans << "\n";
}
}
// assert(false);
#endif
setAnimation(ANIM_IDLE);
}
void Character::updateBody(Ogre::Real delta)
{
Ogre::Quaternion toGoal =
mBodyNode->getOrientation().zAxis().getRotationTo(
mGoalDirection);
// calculate how much the character has to turn to face goal direction
Ogre::Real yawToGoal = toGoal.getYaw().valueDegrees();
// this is how much the character CAN turn this frame
Ogre::Real yawAtSpeed =
yawToGoal / Ogre::Math::Abs(yawToGoal) * delta * TURN_SPEED;
// reduce "turnability" if we're in midair
// if (mBaseAnimID == ANIM_JUMP_LOOP) yawAtSpeed *= 0.2f;
if (yawToGoal < 0)
yawToGoal = std::min<Ogre::Real>(
0,
std::max<Ogre::Real>(
yawToGoal,
yawAtSpeed)); //yawToGoal = Math::Clamp<Real>(yawToGoal, yawAtSpeed, 0);
else if (yawToGoal > 0)
yawToGoal = std::max<Ogre::Real>(
0,
std::min<Ogre::Real>(
yawToGoal,
yawAtSpeed)); //yawToGoal = Math::Clamp<Real>(yawToGoal, 0, yawAtSpeed);
mBodyNode->yaw(Ogre::Degree(yawToGoal));
}
void Character::updateAnimations(Ogre::Real delta)
{
int i, j, k;
Ogre::Real animSpeed = 1;
mTimer += delta;
{
Ogre::Quaternion rot = mBodyNode->getOrientation();
OgreAssert(!Ogre::Math::isNaN(rot.x), "NaN");
OgreAssert(!Ogre::Math::isNaN(rot.y), "NaN");
OgreAssert(!Ogre::Math::isNaN(rot.z), "NaN");
}
if (mAnimID != ANIM_NONE) {
if (mAnimID == ANIM_WALK)
mAnims[mAnimID]->addTime(delta * 1.0f);
else
mAnims[mAnimID]->addTime(delta * animSpeed);
}
fadeAnimations(delta);
}
void Character::updateRootMotion(Ogre::Real delta)
{
Ogre::Vector3 boneMotion = mRootBone->getPosition();
OgreAssert(delta > 0.0f, "Zero delta");
Ogre::Vector3 motion = boneMotion - rootMotion;
if (motion.squaredLength() > 0.1f * 0.1f)
motion = Ogre::Vector3();
rootMotion = boneMotion;
#if 0
float mass = mRigidBody->getMass();
std::cout << "Root bone position: " << boneMotion << "\n";
std::cout << "body mass: " << mass << "\n";
#endif
/* Kinematic motion */
Ogre::Quaternion rot = mBodyNode->getOrientation();
// Ogre::Vector3 gravity(0, -9.8, 0);
Ogre::Vector3 gravity(0, 0, 0);
Ogre::Vector3 velocity = rot * boneMotion / delta;
velocity += gravity * delta;
Ogre::Vector3 rotMotion = velocity * delta;
btTransform from(convert(mBodyNode->getOrientation()),
convert(mBodyNode->getPosition()));
mBodyNode->setPosition(mBodyNode->getPosition() + rotMotion);
// WorldData::get_singleton()->getWorld()->testBodyMotion(mRigidBody, from, Ogre::Bullet::convert(rotMotion), true,
// nullptr, false, std::set<btCollisionObject *>());
}
void Character::fadeAnimations(Ogre::Real delta)
{
int i;
for (i = 0; i < NUM_ANIMS; i++) {
if (mFadingIn[i]) {
// slowly fade this animation in until it has full weight
Ogre::Real newWeight = mAnims[i]->getWeight() +
delta * ANIM_FADE_SPEED;
mAnims[i]->setWeight(
Ogre::Math::Clamp<Ogre::Real>(newWeight, 0, 1));
if (newWeight >= 1)
mFadingIn[i] = false;
} else if (mFadingOut[i]) {
// slowly fade this animation out until it has no weight, and then disable it
Ogre::Real newWeight = mAnims[i]->getWeight() -
delta * ANIM_FADE_SPEED;
mAnims[i]->setWeight(
Ogre::Math::Clamp<Ogre::Real>(newWeight, 0, 1));
if (newWeight <= 0) {
mAnims[i]->setEnabled(false);
mFadingOut[i] = false;
}
}
}
}
void Character::setAnimation(AnimID id, bool reset)
{
assert(id >= 0 && id < NUM_ANIMS);
if (mAnimID != ANIM_NONE) {
mFadingIn[mAnimID] = false;
mFadingOut[mAnimID] = true;
}
mAnimID = id;
if (id != ANIM_NONE) {
mAnims[id]->setEnabled(true);
mAnims[id]->setWeight(0);
mFadingOut[id] = false;
mFadingIn[id] = true;
if (reset)
mAnims[id]->setTimePosition(0);
}
}
bool Character::act_run()
{
if (mAnimID == ANIM_IDLE)
setAnimation(ANIM_RUN, true);
else if (mAnimID == ANIM_WALK)
setAnimation(ANIM_RUN);
return true;
}
bool Character::act_walk()
{
if (mAnimID == ANIM_IDLE)
setAnimation(ANIM_WALK, true);
else if (mAnimID == ANIM_RUN)
setAnimation(ANIM_WALK);
return true;
}
bool Character::act_idle()
{
setAnimation(ANIM_IDLE);
return true;
}
bool Character::isRunning()
{
return mAnimID == ANIM_RUN;
}
bool Character::isWalking()
{
return mAnimID == ANIM_WALK;
}
Ogre::Vector3 Character::getPosition()
{
return mBodyNode->_getDerivedPosition();
}

115
src/characters/character.h Normal file
View File

@@ -0,0 +1,115 @@
#include <Ogre.h>
#include <OgreFrameListener.h>
#include <OgreBullet.h>
class btCompoundShape;
class btPairCachingGhostObject;
class Character : public Ogre::FrameListener {
enum AnimID {
ANIM_IDLE = 0,
ANIM_WALK,
ANIM_RUN,
NUM_ANIMS,
ANIM_NONE = NUM_ANIMS
};
Ogre::String mModelName;
Ogre::Node *mRootBone;
Ogre::SceneManager *mScnMgr;
Ogre::SceneNode *mCameraPivot;
Ogre::SceneNode *mCameraGoal, *mBodyNode;
Ogre::Entity *mBodyEnt;
Ogre::Real mPivotPitch;
Ogre::Real mVerticalVelocity;
Ogre::Vector3 mGoalDirection; // actual intended direction in world-space
Ogre::AnimationState *mAnims[NUM_ANIMS]; // master animation list
Ogre::Animation *mSkelAnimations[NUM_ANIMS];
Ogre::NodeAnimationTrack *mHipsTracks[NUM_ANIMS];
Ogre::NodeAnimationTrack *mRootTracks[NUM_ANIMS];
AnimID mAnimID;
bool mFadingIn[NUM_ANIMS]; // which animations are fading in
bool mFadingOut[NUM_ANIMS]; // which animations are fading out
Ogre::Real
mTimer; // general timer to see how long animations have been playing
Ogre::Skeleton *mSkeleton;
bool mRunning;
Ogre::Vector3 rootMotion;
Ogre::Quaternion rootRotation;
// btRigidBody *mRigidBody;
btCompoundShape *mCollisionShape;
btPairCachingGhostObject *mGhostObject;
Ogre::Bullet::DynamicsWorld *mWorld;
public:
Character(Ogre::SceneManager *scnMgr, const Ogre::String &modelName,
Ogre::Bullet::DynamicsWorld *world);
~Character();
private:
void setupBody();
void setupAnimations();
public:
bool frameStarted(const Ogre::FrameEvent &evt) override;
bool frameEnded(const Ogre::FrameEvent &evt) override;
bool frameRenderingQueued(const Ogre::FrameEvent &evt) override;
private:
void updateBody(Ogre::Real deltaTime);
void updateAnimations(Ogre::Real deltaTime);
void updateRootMotion(Ogre::Real deltaTime);
void fadeAnimations(Ogre::Real deltaTime);
void setAnimation(AnimID id, bool reset = false);
inline btQuaternion convert(const Ogre::Quaternion &q)
{
return btQuaternion(q.x, q.y, q.z, q.w);
}
inline btVector3 convert(const Ogre::Vector3 &v)
{
return btVector3(v.x, v.y, v.z);
}
inline btTransform convert(const Ogre::Quaternion &q,
const Ogre::Vector3 &v)
{
btQuaternion mq = convert(q);
btVector3 mv = convert(v);
return btTransform(mq, mv);
}
inline Ogre::Quaternion convert(const btQuaternion &q)
{
return Ogre::Quaternion(q.w(), q.x(), q.y(), q.z());
}
inline Ogre::Vector3 convert(const btVector3 &v)
{
return Ogre::Vector3(v.x(), v.y(), v.z());
}
inline void convert(const btTransform &from, Ogre::Quaternion &q,
Ogre::Vector3 &v)
{
q = convert(from.getRotation());
v = convert(from.getOrigin());
}
public:
bool isIdle()
{
return mAnimID == ANIM_IDLE;
}
bool act_run();
bool act_walk();
bool act_idle();
bool isRunning();
bool isWalking();
bool isMoving()
{
return isRunning() || isWalking();
}
void setGoalDirection(const Ogre::Vector3 &goalDirection)
{
mGoalDirection = goalDirection;
}
Ogre::Vector3 getGoalDirection()
{
return mGoalDirection;
}
Ogre::Vector3 getPosition();
};

View File

@@ -0,0 +1,629 @@
#include <iostream>
#include "character.h"
#include "controller.h"
#if 0
#define CAM_HEIGHT 1.6f // height of camera above character's center of mass
CharacterController::CharacterController(Ogre::SceneNode *camNode,
Ogre::Camera *cam,
Ogre::SceneManager *scnMgr,
Character *character)
: mCameraNode(camNode)
, mCamera(cam)
, mScnMgr(scnMgr)
, mPivotPitch(0)
, mVerticalVelocity(0)
, mRunning(false)
, mCharacter(character)
, mCameraPivot(nullptr)
{
setupCamera();
}
CharacterController::~CharacterController()
{
}
void CharacterController::setupCamera()
{
// create a pivot at roughly the character's shoulder
mCameraPivot = mScnMgr->getRootSceneNode()->createChildSceneNode();
mCameraGoal =
mCameraPivot->createChildSceneNode(Ogre::Vector3(0, 2, 3));
mCameraNode->setPosition(mCameraPivot->getPosition() +
mCameraGoal->getPosition());
mCameraPivot->setFixedYawAxis(true);
mCameraGoal->setFixedYawAxis(true);
mCameraNode->setFixedYawAxis(true);
// our model is quite small, so reduce the clipping planes
mCamera->setNearClipDistance(0.1f);
mCamera->setFarClipDistance(700);
mPivotPitch = 0;
mKeyDirection = Ogre::Vector3::ZERO;
mVerticalVelocity = 0;
std::cout << "ORIGINAL: " << mCameraNode->getPosition() << "\n";
}
bool CharacterController::keyPressed(const OgreBites::KeyboardEvent &evt)
{
OgreBites::Keycode key = evt.keysym.sym;
if (key == 'q') {
/* ... */
} else if (key == 'e') {
} else if (key == 'w')
mKeyDirection.z = -1;
else if (key == 'a')
mKeyDirection.x = -1;
else if (key == 's')
mKeyDirection.z = 1;
else if (key == 'd')
mKeyDirection.x = 1;
if (key == OgreBites::SDLK_LSHIFT)
mRunning = true;
if (!mKeyDirection.isZeroLength() && mCharacter->isIdle()) {
if (mRunning)
mCharacter->act_run();
else
mCharacter->act_walk();
// std::cout << "Walking\n";
} else if (!mKeyDirection.isZeroLength() && mCharacter->isWalking() &&
mRunning)
mCharacter->act_run();
return true;
}
bool CharacterController::keyReleased(const OgreBites::KeyboardEvent &evt)
{
OgreBites::Keycode key = evt.keysym.sym;
if (key == 'w' && mKeyDirection.z == -1)
mKeyDirection.z = 0;
else if (key == 'a' && mKeyDirection.x == -1)
mKeyDirection.x = 0;
else if (key == 's' && mKeyDirection.z == 1)
mKeyDirection.z = 0;
else if (key == 'd' && mKeyDirection.x == 1)
mKeyDirection.x = 0;
if (key == OgreBites::SDLK_LSHIFT)
mRunning = false;
if (mKeyDirection.isZeroLength() && mCharacter->isMoving())
mCharacter->act_idle();
else if (!mKeyDirection.isZeroLength() && mCharacter->isRunning() &&
!mRunning)
mCharacter->act_walk();
return true;
}
bool CharacterController::mouseMoved(const OgreBites::MouseMotionEvent &evt)
{
// update camera goal based on mouse movement
updateCameraGoal(-0.18f * evt.xrel, -0.12f * evt.yrel, 0);
return true;
}
bool CharacterController::mouseWheelRolled(const OgreBites::MouseWheelEvent &evt)
{
// update camera goal based on mouse movement
updateCameraGoal(0, 0, -0.15f * evt.y);
return true;
}
bool CharacterController::mousePressed(const OgreBites::MouseButtonEvent &evt)
{
std::cout << "Mouse press\n";
return false;
}
void CharacterController::frameRendered(const Ogre::FrameEvent &evt)
{
Ogre::Vector3 goalDirection = Ogre::Vector3::ZERO;
updateCamera(evt.timeSinceLastFrame);
if (mKeyDirection != Ogre::Vector3::ZERO) {
// calculate actually goal direction in world based on player's key directions
goalDirection +=
mKeyDirection.z * mCameraNode->getOrientation().zAxis();
goalDirection +=
mKeyDirection.x * mCameraNode->getOrientation().xAxis();
goalDirection.y = 0;
goalDirection.normalise();
mCharacter->setGoalDirection(goalDirection);
}
}
bool CharacterController::frameStarted(const Ogre::FrameEvent &evt)
{
return true;
}
void CharacterController::updateCameraGoal(Ogre::Real deltaYaw,
Ogre::Real deltaPitch,
Ogre::Real deltaZoom)
{
mCameraPivot->yaw(Ogre::Degree(deltaYaw), Ogre::Node::TS_PARENT);
if (!(mPivotPitch + deltaPitch > 25 && deltaPitch > 0) &&
!(mPivotPitch + deltaPitch < -60 && deltaPitch < 0)) {
mCameraPivot->pitch(Ogre::Degree(deltaPitch),
Ogre::Node::TS_LOCAL);
mPivotPitch += deltaPitch;
}
Ogre::Real dist = mCameraGoal->_getDerivedPosition().distance(
mCameraPivot->_getDerivedPosition());
Ogre::Real distChange = deltaZoom * dist;
// bound the zoom
if (!(dist + distChange < 8 && distChange < 0) &&
!(dist + distChange > 25 && distChange > 0))
mCameraGoal->translate(0, 0, distChange, Ogre::Node::TS_LOCAL);
}
void CharacterController::updateCamera(Ogre::Real delta)
{
// static int count = 0;
// place the camera pivot roughly at the character's shoulder
Ogre::Vector3 pivotOffset =
mCharacter->getPosition() - mCameraPivot->_getDerivedPosition();
Ogre::Vector3 pivotPos = mCameraPivot->_getDerivedPosition();
mCameraPivot->setPosition(pivotPos + pivotOffset * 0.1f * delta +
Ogre::Vector3::UNIT_Y * CAM_HEIGHT);
// move the camera smoothly to the goal
Ogre::Vector3 goalOffset =
mCameraGoal->_getDerivedPosition() - mCameraNode->getPosition();
mCameraNode->translate(goalOffset * delta * 9.0f);
// always look at the pivot
mCameraNode->lookAt(mCameraPivot->_getDerivedPosition(),
Ogre::Node::TS_PARENT);
}
#endif
#define CAM_HEIGHT 1.6f // height of camera above character's center of mass
#define RUN_SPEED 17 // character running speed in units per second
#define TURN_SPEED 500.0f // character turning in degrees per second
#define ANIM_FADE_SPEED \
7.5f // animation crossfade speed in % of full weight per second
CharacterController::CharacterController(Ogre::SceneNode *camNode,
Ogre::Camera *cam,
Ogre::SceneManager *scnMgr,
Ogre::Bullet::DynamicsWorld *world)
: mCameraNode(camNode)
, mCamera(cam)
, mScnMgr(scnMgr)
, mPivotPitch(0)
, mVerticalVelocity(0)
, mAnimID(ANIM_NONE)
, mRunning(false)
, mWorld(world)
, mCollisionShape(nullptr)
, mGhostObject(nullptr)
{
setupBody();
setupCamera();
setupAnimations();
Ogre::Root::getSingleton().addFrameListener(this);
}
CharacterController::~CharacterController()
{
}
void CharacterController::setupBody()
{
mBodyEnt = mScnMgr->createEntity("normal-male.glb");
mBodyNode = mScnMgr->getRootSceneNode()->createChildSceneNode();
mBodyNode->attachObject(mBodyEnt);
mSkeleton = mBodyEnt->getSkeleton();
// mRigidBody = world->addCharacter(mBodyEnt, 0);
// mCollisionShape = static_cast<btCompoundShape *>(mRigidBody->getCollisionShape());
mGhostObject = new btPairCachingGhostObject();
mCollisionShape = new btCompoundShape;
mGhostObject->setCollisionShape(mCollisionShape);
{
btVector3 inertia(0, 0, 0);
// mCollisionShape = new btCompoundShape();
btScalar height = 1.0f;
btScalar radius = 0.3f;
btCapsuleShape *shape =
new btCapsuleShape(radius, 2 * height - 2 * radius);
btTransform transform;
transform.setIdentity();
transform.setOrigin(btVector3(0, 1, 0));
static_cast<btCompoundShape *>(mCollisionShape)
->addChildShape(transform, shape);
btScalar masses[1] = { 0 };
btTransform principal;
static_cast<btCompoundShape *>(mCollisionShape)
->calculatePrincipalAxisTransform(masses, principal,
inertia);
}
mGhostObject->setCollisionFlags(
btCollisionObject::CF_KINEMATIC_OBJECT |
btCollisionObject::CF_NO_CONTACT_RESPONSE);
mGhostObject->setActivationState(DISABLE_DEACTIVATION);
Ogre::Bullet::KinematicMotionSimple *controller =
new Ogre::Bullet::KinematicMotionSimple(mGhostObject,
mBodyNode);
mWorld->attachCollisionObject(mGhostObject, mBodyEnt,
btBroadphaseProxy::AllFilter,
btBroadphaseProxy::AllFilter);
mWorld->getBtWorld()->addAction(controller);
assert(mCollisionShape);
#if 0
if (mRigidBody->getMass() == 0) {
#if 0
mRigidBody->setCollisionFlags(mRigidBody->getCollisionFlags()
| btCollisionObject::CF_KINEMATIC_OBJECT
| btCollisionObject::CF_NO_CONTACT_RESPONSE
);
#endif
#if 0
mGhostObject->setWorldTransform(mRigidBody->getWorldTransform());
WorldData::get_singleton()->getBtWorld()
->getBroadphase()->getOverlappingPairCache()
->setInternalGhostPairCallback(new btGhostPairCallback());
#endif
}
#endif
#if 0
mRigidBody->setActivationState(DISABLE_DEACTIVATION);
#endif
#if 0
{
Ogre::Entity *e2 = mScnMgr->createEntity("normal-male.glb");
Ogre::SceneNode *e2node = mScnMgr->getRootSceneNode()->createChildSceneNode();
e2node->attachObject(e2);
mGhostObject = WorldData::get_singleton()->addGhostObject(e2, mCollisionShape);
mController = new btKinematicCharacterController(mGhostObject, mCollisionShape, 0.5f);
WorldData::get_singleton()->getBtWorld()->addAction(mController);
}
#endif
assert(mSkeleton->hasBone("Root"));
mRootBone = mSkeleton->getBone("Root");
assert(mRootBone);
}
void CharacterController::setupCamera()
{
// create a pivot at roughly the character's shoulder
mCameraPivot = mScnMgr->getRootSceneNode()->createChildSceneNode();
mCameraGoal =
mCameraPivot->createChildSceneNode(Ogre::Vector3(0, 2, 3));
mCameraNode->setPosition(mCameraPivot->getPosition() +
mCameraGoal->getPosition());
mCameraPivot->setFixedYawAxis(true);
mCameraGoal->setFixedYawAxis(true);
mCameraNode->setFixedYawAxis(true);
// our model is quite small, so reduce the clipping planes
mCamera->setNearClipDistance(0.1f);
mCamera->setFarClipDistance(700);
mPivotPitch = 0;
mKeyDirection = Ogre::Vector3::ZERO;
mVerticalVelocity = 0;
}
void CharacterController::setupAnimations()
{
int i, j;
mSkeleton->setBlendMode(Ogre::ANIMBLEND_CUMULATIVE);
Ogre::String animNames[NUM_ANIMS] = { "idle", "walking", "running" };
for (i = 0; i < NUM_ANIMS; i++) {
mAnims[i] = mBodyEnt->getAnimationState(animNames[i]);
mAnims[i]->setLoop(true);
mAnims[i]->setEnabled(true);
mAnims[i]->setWeight(0);
mFadingIn[i] = false;
mFadingOut[i] = false;
mSkelAnimations[i] = mSkeleton->getAnimation(animNames[i]);
for (const auto &it : mSkelAnimations[i]->_getNodeTrackList()) {
Ogre::NodeAnimationTrack *track = it.second;
Ogre::String trackName =
track->getAssociatedNode()->getName();
if (trackName == "mixamorig:Hips") {
mHipsTracks[i] = track;
} else if (trackName == "Root") {
mRootTracks[i] = track;
// mRootTracks[i]->removeAllKeyFrames();
}
}
Ogre::Vector3 delta = Ogre::Vector3::ZERO;
Ogre::Vector3 motion = Ogre::Vector3::ZERO;
for (j = 0; j < mRootTracks[i]->getNumKeyFrames(); j++) {
Ogre::Vector3 trans = mRootTracks[i]
->getNodeKeyFrame(j)
->getTranslate();
if (j == 0)
delta = trans;
else
delta = trans - motion;
mRootTracks[i]->getNodeKeyFrame(j)->setTranslate(delta);
motion = trans;
}
}
#if 0
for(i = 0; i < NUM_ANIMS - 1; i++) {
// need to cache
int j;
Ogre::String animName = mAnims[i]->getAnimationName();
Ogre::Animation *anim = mSkeleton->getAnimation(animName);
Ogre::NodeAnimationTrack *hips_track = nullptr, *root_track = nullptr;
Ogre::Node *root_node = nullptr;
for (const auto& it : anim->_getNodeTrackList()) {
Ogre::NodeAnimationTrack* track = it.second;
Ogre::String trackName = track->getAssociatedNode()->getName();
std::cout << animName << " track: " << trackName << "\n";
if (trackName == "mixamorig:Hips")
hips_track = track;
else if (trackName == "Root") {
root_track = track;
root_node = track->getAssociatedNode();
}
}
assert(false);
root_track->removeAllKeyFrames();
std::cout << hips_track << " " << root_track << "\n";
std::cout << hips_track->getNumKeyFrames() << " " << root_track->getNumKeyFrames() << "\n";
assert(hips_track && root_track);
Ogre::Vector3 delta = Ogre::Vector3::ZERO;
for(j = 0; j < hips_track->getNumKeyFrames(); j++) {
float timePos = hips_track->getNodeKeyFrame(j)->getTime();
Ogre::Vector3 trans = hips_track->getNodeKeyFrame(j)->getTranslate();
Ogre::Vector3 hips_trans(0, 0, 0);
Ogre::Vector3 root_trans(0, 0, 0);
hips_track->getNodeKeyFrame(j)->setTranslate(hips_trans);
Ogre::TransformKeyFrame *nk = root_track->createNodeKeyFrame(timePos);
nk->setTranslate(root_trans - delta);
nk->setScale(Ogre::Vector3(1, 1, 1));
nk->setRotation(Ogre::Quaternion());
std::cout << animName << " delta: " << j << " " << timePos << " " << root_trans - delta << "\n";
delta = root_trans;
}
for(j = 0; j < root_track->getNumKeyFrames(); j++) {
float timePos = hips_track->getNodeKeyFrame(j)->getTime();
Ogre::Vector3 root_trans = hips_track->getNodeKeyFrame(j)->getTranslate();
std::cout << animName << " delta: root: " << j << " " << timePos << " " << root_trans << "\n";
}
}
// assert(false);
#endif
setAnimation(ANIM_IDLE);
}
bool CharacterController::keyPressed(const OgreBites::KeyboardEvent &evt)
{
OgreBites::Keycode key = evt.keysym.sym;
if (key == 'q' && (mAnimID == ANIM_IDLE)) {
/* ... */
mTimer = 0;
} else if (key == 'e') {
} else if (key == 'w')
mKeyDirection.z = -1;
else if (key == 'a')
mKeyDirection.x = -1;
else if (key == 's')
mKeyDirection.z = 1;
else if (key == 'd')
mKeyDirection.x = 1;
if (key == OgreBites::SDLK_LSHIFT)
mRunning = true;
if (!mKeyDirection.isZeroLength() && mAnimID == ANIM_IDLE) {
if (mRunning)
setAnimation(ANIM_RUN, true);
else
setAnimation(ANIM_WALK, true);
// std::cout << "Walking\n";
} else if (!mKeyDirection.isZeroLength() && mAnimID == ANIM_WALK &&
mRunning)
setAnimation(ANIM_RUN);
return true;
}
bool CharacterController::keyReleased(const OgreBites::KeyboardEvent &evt)
{
OgreBites::Keycode key = evt.keysym.sym;
if (key == 'w' && mKeyDirection.z == -1)
mKeyDirection.z = 0;
else if (key == 'a' && mKeyDirection.x == -1)
mKeyDirection.x = 0;
else if (key == 's' && mKeyDirection.z == 1)
mKeyDirection.z = 0;
else if (key == 'd' && mKeyDirection.x == 1)
mKeyDirection.x = 0;
if (key == OgreBites::SDLK_LSHIFT)
mRunning = false;
if (mKeyDirection.isZeroLength() &&
(mAnimID == ANIM_WALK || mAnimID == ANIM_RUN))
setAnimation(ANIM_IDLE);
else if (!mKeyDirection.isZeroLength() && mAnimID == ANIM_RUN &&
!mRunning)
setAnimation(ANIM_WALK);
return true;
}
bool CharacterController::mouseMoved(const OgreBites::MouseMotionEvent &evt)
{
// update camera goal based on mouse movement
updateCameraGoal(-0.18f * evt.xrel, -0.12f * evt.yrel, 0);
return true;
}
bool CharacterController::mouseWheelRolled(const OgreBites::MouseWheelEvent &evt)
{
// update camera goal based on mouse movement
updateCameraGoal(0, 0, -0.15f * evt.y);
return true;
}
bool CharacterController::mousePressed(const OgreBites::MouseButtonEvent &evt)
{
std::cout << "Mouse press\n";
return false;
}
void CharacterController::frameRendered(const Ogre::FrameEvent &evt)
{
updateBody(evt.timeSinceLastFrame);
updateAnimations(evt.timeSinceLastFrame);
updateCamera(evt.timeSinceLastFrame);
if (evt.timeSinceLastFrame > 0)
updateRootMotion(evt.timeSinceLastFrame);
}
bool CharacterController::frameStarted(const Ogre::FrameEvent &evt)
{
return true;
}
void CharacterController::updateCameraGoal(Ogre::Real deltaYaw,
Ogre::Real deltaPitch,
Ogre::Real deltaZoom)
{
mCameraPivot->yaw(Ogre::Degree(deltaYaw), Ogre::Node::TS_PARENT);
if (!(mPivotPitch + deltaPitch > 25 && deltaPitch > 0) &&
!(mPivotPitch + deltaPitch < -60 && deltaPitch < 0)) {
mCameraPivot->pitch(Ogre::Degree(deltaPitch),
Ogre::Node::TS_LOCAL);
mPivotPitch += deltaPitch;
}
Ogre::Real dist = mCameraGoal->_getDerivedPosition().distance(
mCameraPivot->_getDerivedPosition());
Ogre::Real distChange = deltaZoom * dist;
// bound the zoom
if (!(dist + distChange < 8 && distChange < 0) &&
!(dist + distChange > 25 && distChange > 0))
mCameraGoal->translate(0, 0, distChange, Ogre::Node::TS_LOCAL);
}
void CharacterController::updateBody(Ogre::Real delta)
{
mGoalDirection = Ogre::Vector3::ZERO;
if (mKeyDirection != Ogre::Vector3::ZERO) {
// calculate actually goal direction in world based on player's key directions
mGoalDirection +=
mKeyDirection.z * mCameraNode->getOrientation().zAxis();
mGoalDirection +=
mKeyDirection.x * mCameraNode->getOrientation().xAxis();
mGoalDirection.y = 0;
mGoalDirection.normalise();
Ogre::Quaternion toGoal =
mBodyNode->getOrientation().zAxis().getRotationTo(
mGoalDirection);
// calculate how much the character has to turn to face goal direction
Ogre::Real yawToGoal = toGoal.getYaw().valueDegrees();
// this is how much the character CAN turn this frame
Ogre::Real yawAtSpeed = yawToGoal / Ogre::Math::Abs(yawToGoal) *
delta * TURN_SPEED;
// reduce "turnability" if we're in midair
// if (mBaseAnimID == ANIM_JUMP_LOOP) yawAtSpeed *= 0.2f;
if (yawToGoal < 0)
yawToGoal = std::min<Ogre::Real>(
0,
std::max<Ogre::Real>(
yawToGoal,
yawAtSpeed)); //yawToGoal = Math::Clamp<Real>(yawToGoal, yawAtSpeed, 0);
else if (yawToGoal > 0)
yawToGoal = std::max<Ogre::Real>(
0,
std::min<Ogre::Real>(
yawToGoal,
yawAtSpeed)); //yawToGoal = Math::Clamp<Real>(yawToGoal, 0, yawAtSpeed);
mBodyNode->yaw(Ogre::Degree(yawToGoal));
// move in current body direction (not the goal direction)
// mBodyNode->translate(0, 0, delta * RUN_SPEED * mAnims[mAnimID]->getWeight(),
// Ogre::Node::TS_LOCAL);
#if 0
if (mBaseAnimID == ANIM_JUMP_LOOP)
{
// if we're jumping, add a vertical offset too, and apply gravity
mBodyNode->translate(0, mVerticalVelocity * deltaTime, 0, Node::TS_LOCAL);
mVerticalVelocity -= GRAVITY * deltaTime;
Vector3 pos = mBodyNode->getPosition();
if (pos.y <= CHAR_HEIGHT)
{
// if we've hit the ground, change to landing state
pos.y = CHAR_HEIGHT;
mBodyNode->setPosition(pos);
setBaseAnimation(ANIM_JUMP_END, true);
mTimer = 0;
}
}
#endif
}
}
void CharacterController::updateAnimations(Ogre::Real delta)
{
int i, j, k;
Ogre::Real animSpeed = 1;
mTimer += delta;
if (mAnimID != ANIM_NONE) {
if (mAnimID == ANIM_WALK)
mAnims[mAnimID]->addTime(delta * 1.0f);
else
mAnims[mAnimID]->addTime(delta * animSpeed);
}
fadeAnimations(delta);
}
void CharacterController::updateRootMotion(Ogre::Real delta)
{
Ogre::Vector3 boneMotion = mRootBone->getPosition();
OgreAssert(delta > 0.0f, "Zero delta");
#if 0
Ogre::Vector3 motion = boneMotion - rootMotion;
if (motion.squaredLength() > 0.1f * 0.1f)
motion = Ogre::Vector3();
rootMotion = boneMotion;
#endif
#if 0
float mass = mRigidBody->getMass();
std::cout << "Root bone position: " << boneMotion << "\n";
std::cout << "body mass: " << mass << "\n";
#endif
/* Kinematic motion */
Ogre::Quaternion rot = mBodyNode->getOrientation();
Ogre::Vector3 gravity(0, -9.8, 0);
gravity.y = 0.5f;
Ogre::Vector3 velocity = rot * boneMotion / delta;
velocity += gravity * delta;
Ogre::Vector3 rotMotion = velocity * delta;
btTransform from(convert(mBodyNode->getOrientation()),
convert(mBodyNode->getPosition()));
mBodyNode->setPosition(mBodyNode->getPosition() + rotMotion);
// WorldData::get_singleton()->getWorld()->testBodyMotion(mRigidBody, from, Ogre::Bullet::convert(rotMotion), true,
// nullptr, false, std::set<btCollisionObject *>());
}
void CharacterController::fadeAnimations(Ogre::Real delta)
{
int i;
for (i = 0; i < NUM_ANIMS; i++) {
if (mFadingIn[i]) {
// slowly fade this animation in until it has full weight
Ogre::Real newWeight = mAnims[i]->getWeight() +
delta * ANIM_FADE_SPEED;
mAnims[i]->setWeight(
Ogre::Math::Clamp<Ogre::Real>(newWeight, 0, 1));
if (newWeight >= 1)
mFadingIn[i] = false;
} else if (mFadingOut[i]) {
// slowly fade this animation out until it has no weight, and then disable it
Ogre::Real newWeight = mAnims[i]->getWeight() -
delta * ANIM_FADE_SPEED;
mAnims[i]->setWeight(
Ogre::Math::Clamp<Ogre::Real>(newWeight, 0, 1));
if (newWeight <= 0) {
mAnims[i]->setEnabled(false);
mFadingOut[i] = false;
}
}
}
}
void CharacterController::updateCamera(Ogre::Real delta)
{
// place the camera pivot roughly at the character's shoulder
mCameraPivot->setPosition(mBodyNode->getPosition() +
Ogre::Vector3::UNIT_Y * CAM_HEIGHT);
// move the camera smoothly to the goal
Ogre::Vector3 goalOffset =
mCameraGoal->_getDerivedPosition() - mCameraNode->getPosition();
mCameraNode->translate(goalOffset * delta * 9.0f);
// always look at the pivot
mCameraNode->lookAt(mCameraPivot->_getDerivedPosition(),
Ogre::Node::TS_PARENT);
}
void CharacterController::setAnimation(AnimID id, bool reset)
{
assert(id >= 0 && id < NUM_ANIMS);
if (mAnimID != ANIM_NONE) {
mFadingIn[mAnimID] = false;
mFadingOut[mAnimID] = true;
}
mAnimID = id;
if (id != ANIM_NONE) {
mAnims[id]->setEnabled(true);
mAnims[id]->setWeight(0);
mFadingOut[id] = false;
mFadingIn[id] = true;
if (reset)
mAnims[id]->setTimePosition(0);
}
}

162
src/characters/controller.h Normal file
View File

@@ -0,0 +1,162 @@
#include <Ogre.h>
#include <OgreInput.h>
#include <OgreFrameListener.h>
#if 0
class Character;
class CharacterController : public OgreBites::InputListener,
Ogre::FrameListener {
Ogre::SceneNode *mCameraNode;
Ogre::Camera *mCamera;
Ogre::SceneManager *mScnMgr;
Ogre::SceneNode *mCameraPivot;
Ogre::SceneNode *mCameraGoal;
Ogre::Real mPivotPitch;
Ogre::Real mVerticalVelocity;
Ogre::Vector3
mKeyDirection; // player's local intended direction based on WASD keys
bool mRunning;
Character *mCharacter;
public:
CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam,
Ogre::SceneManager *scnMgr, Character *character);
~CharacterController();
private:
void setupCamera();
public:
bool keyPressed(const OgreBites::KeyboardEvent &evt) override;
bool keyReleased(const OgreBites::KeyboardEvent &evt) override;
bool mouseMoved(const OgreBites::MouseMotionEvent &evt) override;
bool mouseWheelRolled(const OgreBites::MouseWheelEvent &evt) override;
bool mousePressed(const OgreBites::MouseButtonEvent &evt) override;
bool frameStarted(const Ogre::FrameEvent &evt) override;
void frameRendered(const Ogre::FrameEvent &evt) override;
private:
void updateCamera(Ogre::Real deltaTime);
void updateCameraGoal(Ogre::Real deltaYaw, Ogre::Real deltaPitch,
Ogre::Real deltaZoom);
};
#endif
class CharacterController : public OgreBites::InputListener,
Ogre::FrameListener {
enum AnimID {
ANIM_IDLE = 0,
ANIM_WALK,
ANIM_RUN,
NUM_ANIMS,
ANIM_NONE = NUM_ANIMS
};
Ogre::Node *mRootBone;
Ogre::SceneNode *mCameraNode;
Ogre::Camera *mCamera;
Ogre::SceneManager *mScnMgr;
Ogre::SceneNode *mCameraPivot;
Ogre::SceneNode *mCameraGoal, *mBodyNode;
Ogre::Entity *mBodyEnt;
Ogre::Real mPivotPitch;
Ogre::Real mVerticalVelocity;
Ogre::Vector3
mKeyDirection; // player's local intended direction based on WASD keys
Ogre::Vector3 mGoalDirection; // actual intended direction in world-space
Ogre::AnimationState *mAnims[NUM_ANIMS]; // master animation list
Ogre::Animation *mSkelAnimations[NUM_ANIMS];
Ogre::NodeAnimationTrack *mHipsTracks[NUM_ANIMS];
Ogre::NodeAnimationTrack *mRootTracks[NUM_ANIMS];
AnimID mAnimID;
bool mFadingIn[NUM_ANIMS]; // which animations are fading in
bool mFadingOut[NUM_ANIMS]; // which animations are fading out
Ogre::Real
mTimer; // general timer to see how long animations have been playing
Ogre::Skeleton *mSkeleton;
bool mRunning;
Ogre::Bullet::DynamicsWorld *mWorld;
Ogre::Vector3 rootMotion;
Ogre::Quaternion rootRotation;
// btRigidBody *mRigidBody;
btCompoundShape *mCollisionShape;
btPairCachingGhostObject *mGhostObject;
public:
CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam,
Ogre::SceneManager *scnMgr,
Ogre::Bullet::DynamicsWorld *world);
~CharacterController();
private:
void setupBody();
void setupCamera();
void setupAnimations();
public:
bool keyPressed(const OgreBites::KeyboardEvent &evt) override;
bool keyReleased(const OgreBites::KeyboardEvent &evt) override;
bool mouseMoved(const OgreBites::MouseMotionEvent &evt) override;
bool mouseWheelRolled(const OgreBites::MouseWheelEvent &evt) override;
bool mousePressed(const OgreBites::MouseButtonEvent &evt) override;
bool frameStarted(const Ogre::FrameEvent &evt) override;
void frameRendered(const Ogre::FrameEvent &evt) override;
private:
void updateBody(Ogre::Real deltaTime);
void updateAnimations(Ogre::Real deltaTime);
void updateRootMotion(Ogre::Real deltaTime);
void fadeAnimations(Ogre::Real deltaTime);
void updateCamera(Ogre::Real deltaTime);
void updateCameraGoal(Ogre::Real deltaYaw, Ogre::Real deltaPitch,
Ogre::Real deltaZoom);
void setAnimation(AnimID id, bool reset = false);
#if 0
struct testMotionResult {
};
struct recoverResult {
};
bool bodyTestMotion(btRigidBody *body,
const btTransform &from,
const btVector3 &motion, bool infinite_inertia,
textMotionResult *result,
bool excludeRaycastShapes,
const std::set<btCollisionObject *> &exclude);
bool recoverFromPenetration(btRigidBody *body,
const btTransform &body_position,
btScalar recover_movement_scale,
bool infinite_inertia,
btVector3 &delta_recover_movement,
recoverResult *recover_result,
const std::set<btCollisionObject *> &exclude);
#endif
inline btQuaternion convert(const Ogre::Quaternion &q)
{
return btQuaternion(q.x, q.y, q.z, q.w);
}
inline btVector3 convert(const Ogre::Vector3 &v)
{
return btVector3(v.x, v.y, v.z);
}
inline btTransform convert(const Ogre::Quaternion &q,
const Ogre::Vector3 &v)
{
btQuaternion mq = convert(q);
btVector3 mv = convert(v);
return btTransform(mq, mv);
}
inline Ogre::Quaternion convert(const btQuaternion &q)
{
return Ogre::Quaternion(q.w(), q.x(), q.y(), q.z());
}
inline Ogre::Vector3 convert(const btVector3 &v)
{
return Ogre::Vector3(v.x(), v.y(), v.z());
}
inline void convert(const btTransform &from, Ogre::Quaternion &q,
Ogre::Vector3 &v)
{
q = convert(from.getRotation());
v = convert(from.getOrigin());
}
};