Updated almost all stuff
This commit is contained in:
984
Procedural.cpp
Normal file
984
Procedural.cpp
Normal file
@@ -0,0 +1,984 @@
|
||||
// This file is part of the OGRE project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at https://www.ogre3d.org/licensing.
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
#include <iostream>
|
||||
|
||||
#include "Ogre.h"
|
||||
#include "OgreApplicationContext.h"
|
||||
#include "Bullet/OgreBullet.h"
|
||||
#include "BulletCollision/CollisionDispatch/btGhostObject.h"
|
||||
#include "btKinematicCharacterController.h"
|
||||
#include "LinearMath/btTransform.h"
|
||||
#include "OgrePageManager.h"
|
||||
#include "Procedural.h"
|
||||
|
||||
#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
|
||||
using Real = Ogre::Real;
|
||||
using Math = Ogre::Math;
|
||||
|
||||
class WorldData {
|
||||
std::unique_ptr<Ogre::Bullet::DynamicsWorld> mDynWorld;
|
||||
std::unique_ptr<Ogre::Bullet::DebugDrawer> mDbgDraw;
|
||||
std::unique_ptr<Ogre::Root> mRoot;
|
||||
std::unique_ptr<Ogre::SceneManager> mScnMgr;
|
||||
std::unique_ptr<btDynamicsWorld> mbtWorld;
|
||||
std::unique_ptr<Ogre::PageManager> mPageManager;
|
||||
Ogre::PagedWorld *mPagedWorld;
|
||||
|
||||
private:
|
||||
static WorldData *singleton;
|
||||
class DummyPageProvider : public Ogre::PageProvider {
|
||||
public:
|
||||
bool
|
||||
prepareProceduralPage(Ogre::Page *page,
|
||||
Ogre::PagedWorldSection *section) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool
|
||||
loadProceduralPage(Ogre::Page *page,
|
||||
Ogre::PagedWorldSection *section) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool
|
||||
unloadProceduralPage(Ogre::Page *page,
|
||||
Ogre::PagedWorldSection *section) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool unprepareProceduralPage(
|
||||
Ogre::Page *page,
|
||||
Ogre::PagedWorldSection *section) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
DummyPageProvider mDummyPageProvider;
|
||||
|
||||
WorldData(Ogre::Root *root, Ogre::SceneManager *scnMgr)
|
||||
: mDynWorld(new Ogre::Bullet::DynamicsWorld(
|
||||
Ogre::Vector3(0, -9.8, 0)))
|
||||
, mDbgDraw(new Ogre::Bullet::DebugDrawer(
|
||||
scnMgr->getRootSceneNode(), mDynWorld->getBtWorld()))
|
||||
, mRoot(root)
|
||||
, mScnMgr(scnMgr)
|
||||
, mbtWorld(mDynWorld->getBtWorld())
|
||||
, mPageManager(nullptr)
|
||||
, mPagedWorld(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
static void init(Ogre::Root *root, Ogre::SceneManager *scnMgr)
|
||||
{
|
||||
singleton = new WorldData(root, scnMgr);
|
||||
}
|
||||
static WorldData *get_singleton()
|
||||
{
|
||||
return singleton;
|
||||
}
|
||||
static void cleanup()
|
||||
{
|
||||
if (singleton)
|
||||
delete singleton;
|
||||
singleton = nullptr;
|
||||
}
|
||||
Ogre::SceneManager *getSceneManager()
|
||||
{
|
||||
return mScnMgr.get();
|
||||
}
|
||||
Ogre::Root *getRoot()
|
||||
{
|
||||
return mRoot.get();
|
||||
}
|
||||
void createTrimesh(Ogre::Entity *entity)
|
||||
{
|
||||
}
|
||||
btPairCachingGhostObject *addGhostObject(Ogre::Entity *ent,
|
||||
btCollisionShape *shape,
|
||||
int group = 1,
|
||||
int mask = 0xFFFF)
|
||||
{
|
||||
btDynamicsWorld *world = mDynWorld->getBtWorld();
|
||||
Ogre::SceneNode *node = ent->getParentSceneNode();
|
||||
btPairCachingGhostObject *ghost =
|
||||
new btPairCachingGhostObject();
|
||||
ghost->setCollisionShape(shape);
|
||||
ghost->setCollisionFlags(
|
||||
ghost->getCollisionFlags() |
|
||||
btCollisionObject::CF_NO_CONTACT_RESPONSE |
|
||||
btCollisionObject::CF_CHARACTER_OBJECT);
|
||||
getWorld()->attachCollisionObject(ghost, ent, group, mask);
|
||||
#if 0
|
||||
getBtWorld()
|
||||
->getBroadphase()->getOverlappingPairCache()
|
||||
->setInternalGhostPairCallback(new btGhostPairCallback());
|
||||
ghost->setUserPointer(new EntityCollisionListener{ent, nullptr});
|
||||
#endif
|
||||
return ghost;
|
||||
}
|
||||
btRigidBody *addRigidBody(float mass, Ogre::Entity *ent,
|
||||
Ogre::Bullet::ColliderType ct, int group = 1,
|
||||
int mask = 0xFFFF)
|
||||
{
|
||||
btDynamicsWorld *world = mDynWorld->getBtWorld();
|
||||
Ogre::SceneNode *node = ent->getParentSceneNode();
|
||||
Ogre::Bullet::RigidBodyState *state =
|
||||
new Ogre::Bullet::RigidBodyState(node);
|
||||
btCollisionShape *cs;
|
||||
btCollisionShape *shape;
|
||||
btVector3 inertia(0, 0, 0);
|
||||
switch (ct) {
|
||||
case Ogre::Bullet::CT_TRIMESH: {
|
||||
cs = Ogre::Bullet::createTrimeshCollider(ent);
|
||||
if (mass != 0)
|
||||
cs->calculateLocalInertia(mass, inertia);
|
||||
} break;
|
||||
case Ogre::Bullet::CT_CAPSULE: {
|
||||
cs = new btCompoundShape();
|
||||
btScalar height = 1.0f;
|
||||
btScalar radius = 0.3f;
|
||||
shape = new btCapsuleShape(radius,
|
||||
2 * height - 2 * radius);
|
||||
btTransform transform;
|
||||
transform.setIdentity();
|
||||
transform.setOrigin(btVector3(0, 1, 0));
|
||||
static_cast<btCompoundShape *>(cs)->addChildShape(
|
||||
transform, shape);
|
||||
btScalar masses[1] = { mass };
|
||||
btTransform principal;
|
||||
static_cast<btCompoundShape *>(cs)
|
||||
->calculatePrincipalAxisTransform(
|
||||
masses, principal, inertia);
|
||||
} break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
btRigidBody *body = new btRigidBody(mass, state, cs, inertia);
|
||||
getWorld()->attachRigidBody(body, ent, nullptr, group, mask);
|
||||
#if 0
|
||||
body->setUserPointer(new EntityCollisionListener{ent, nullptr});
|
||||
// btRigidBody *body = mDynWorld->addRigidBody(0, ent, Ogre::Bullet::CT_TRIMESH);
|
||||
#endif
|
||||
return body;
|
||||
}
|
||||
btRigidBody *addKinematicRigidBody(float mass, Ogre::Entity *ent,
|
||||
Ogre::Bullet::ColliderType ct,
|
||||
int group = 1, int mask = 0xFFFF)
|
||||
{
|
||||
return mDynWorld->addKinematicRigidBody(ent, ct, group, mask);
|
||||
}
|
||||
btDynamicsWorld *getBtWorld()
|
||||
{
|
||||
return mDynWorld->getBtWorld();
|
||||
}
|
||||
Ogre::Bullet::DynamicsWorld *getWorld()
|
||||
{
|
||||
return mDynWorld.get();
|
||||
}
|
||||
void update(float delta)
|
||||
{
|
||||
WorldData::get_singleton()->getBtWorld()->stepSimulation(delta,
|
||||
10);
|
||||
mDbgDraw->update();
|
||||
}
|
||||
void initPagedWorld(Ogre::Camera *camera)
|
||||
{
|
||||
mPageManager = std::make_unique<Ogre::PageManager>();
|
||||
mPageManager->setPageProvider(&mDummyPageProvider);
|
||||
mPageManager->addCamera(camera);
|
||||
mPageManager->setDebugDisplayLevel(0);
|
||||
mPagedWorld = mPageManager->createWorld();
|
||||
}
|
||||
};
|
||||
WorldData *WorldData::singleton = nullptr;
|
||||
|
||||
class MainWorld : public Ogre::FrameListener {
|
||||
btRigidBody *mFloorBody;
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
// mScnMgr = scnMgr;
|
||||
// mDynWorld.reset(new Ogre::Bullet::DynamicsWorld(Ogre::Vector3(0, -9.8, 0)));
|
||||
// mDbgDraw.reset(new Ogre::Bullet::DebugDrawer(mScnMgr->getRootSceneNode(), mDynWorld->getBtWorld()));
|
||||
Ogre::MeshManager::getSingleton().createPlane(
|
||||
"floor",
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
Ogre::Plane(Ogre::Vector3::UNIT_Y, 0), 100, 100, 10, 10,
|
||||
true, 1, 10, 10, Ogre::Vector3::UNIT_Z);
|
||||
|
||||
// create a floor entity, give it a material, and place it at the origin
|
||||
Ogre::SceneManager *scnMgr =
|
||||
WorldData::get_singleton()->getSceneManager();
|
||||
Ogre::Entity *floor = scnMgr->createEntity("Floor", "floor");
|
||||
scnMgr->getRootSceneNode()->attachObject(floor);
|
||||
mFloorBody = WorldData::get_singleton()->addRigidBody(
|
||||
0, floor, Ogre::Bullet::CT_TRIMESH);
|
||||
}
|
||||
btRigidBody *addCharacter(Ogre::Entity *ent, float mass)
|
||||
{
|
||||
return WorldData::get_singleton()->addKinematicRigidBody(
|
||||
mass, ent, Ogre::Bullet::CT_COMPOUND);
|
||||
}
|
||||
bool frameStarted(const Ogre::FrameEvent &evt) override;
|
||||
};
|
||||
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;
|
||||
Real mPivotPitch;
|
||||
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
|
||||
Real mTimer; // general timer to see how long animations have been playing
|
||||
Ogre::Skeleton *mSkeleton;
|
||||
bool mRunning;
|
||||
MainWorld *world;
|
||||
Ogre::Vector3 rootMotion;
|
||||
Ogre::Quaternion rootRotation;
|
||||
// btRigidBody *mRigidBody;
|
||||
btCompoundShape *mCollisionShape;
|
||||
btPairCachingGhostObject *mGhostObject;
|
||||
btKinematicCharacterController *mController;
|
||||
|
||||
public:
|
||||
CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam,
|
||||
Ogre::SceneManager *scnMgr, MainWorld *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(Real deltaTime);
|
||||
void updateRootMotion(Real deltaTime);
|
||||
void fadeAnimations(Real deltaTime);
|
||||
void updateCamera(Real deltaTime);
|
||||
void updateCameraGoal(Real deltaYaw, Real deltaPitch, 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());
|
||||
}
|
||||
};
|
||||
CharacterController::CharacterController(Ogre::SceneNode *camNode,
|
||||
Ogre::Camera *cam,
|
||||
Ogre::SceneManager *scnMgr,
|
||||
MainWorld *world)
|
||||
: mCameraNode(camNode)
|
||||
, mCamera(cam)
|
||||
, mScnMgr(scnMgr)
|
||||
, mPivotPitch(0)
|
||||
, mVerticalVelocity(0)
|
||||
, mAnimID(ANIM_NONE)
|
||||
, mRunning(false)
|
||||
, world(world)
|
||||
, mCollisionShape(nullptr)
|
||||
, mGhostObject(nullptr)
|
||||
, mController(nullptr)
|
||||
{
|
||||
setupBody();
|
||||
setupCamera();
|
||||
setupAnimations();
|
||||
}
|
||||
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);
|
||||
WorldData::get_singleton()->getWorld()->attachCollisionObject(
|
||||
mGhostObject, mBodyEnt, btBroadphaseProxy::AllFilter,
|
||||
btBroadphaseProxy::AllFilter);
|
||||
WorldData::get_singleton()->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(Real deltaYaw, Real deltaPitch,
|
||||
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;
|
||||
}
|
||||
Real dist = mCameraGoal->_getDerivedPosition().distance(
|
||||
mCameraPivot->_getDerivedPosition());
|
||||
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(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
|
||||
Real yawToGoal = toGoal.getYaw().valueDegrees();
|
||||
// this is how much the character CAN turn this frame
|
||||
Real yawAtSpeed =
|
||||
yawToGoal / 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<Real>(
|
||||
0,
|
||||
std::max<Real>(
|
||||
yawToGoal,
|
||||
yawAtSpeed)); //yawToGoal = Math::Clamp<Real>(yawToGoal, yawAtSpeed, 0);
|
||||
else if (yawToGoal > 0)
|
||||
yawToGoal = std::max<Real>(
|
||||
0,
|
||||
std::min<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(Real delta)
|
||||
{
|
||||
int i, j, k;
|
||||
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(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);
|
||||
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(Real delta)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < NUM_ANIMS; i++) {
|
||||
if (mFadingIn[i]) {
|
||||
// slowly fade this animation in until it has full weight
|
||||
Real newWeight = mAnims[i]->getWeight() +
|
||||
delta * ANIM_FADE_SPEED;
|
||||
mAnims[i]->setWeight(
|
||||
Math::Clamp<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
|
||||
Real newWeight = mAnims[i]->getWeight() -
|
||||
delta * ANIM_FADE_SPEED;
|
||||
mAnims[i]->setWeight(
|
||||
Math::Clamp<Real>(newWeight, 0, 1));
|
||||
if (newWeight <= 0) {
|
||||
mAnims[i]->setEnabled(false);
|
||||
mFadingOut[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void CharacterController::updateCamera(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);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool CharacterController::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)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CharacterController::bodyTestMotion(btRigidBody *body,
|
||||
const btTransform &from,
|
||||
const btVector3 &motion, bool infinite_inertia,
|
||||
textMotionResult *result,
|
||||
bool excludeRaycastShapes,
|
||||
std::set<btCollisionObject *> exclude)
|
||||
{
|
||||
int t;
|
||||
btTransform body_transform;
|
||||
btVector3 initial_recover_motion(0, 0, 0);
|
||||
// phase one - depenetration
|
||||
for (t = 0; t < RECOVERING_MOVEMENT_CYCLES; t++)
|
||||
if (!recoverFromPenetration(body,
|
||||
body_transform,
|
||||
RECOVERING_MOVEMENT_SCALE,
|
||||
infinite_inertia,
|
||||
initial_recover_motion,
|
||||
nullptr, exclude))
|
||||
break;
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
class KeyHandler : public OgreBites::InputListener {
|
||||
bool keyPressed(const OgreBites::KeyboardEvent &evt) override
|
||||
{
|
||||
if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) {
|
||||
Ogre::Root::getSingleton().queueEndRendering();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
bool MainWorld::frameStarted(const Ogre::FrameEvent &evt)
|
||||
{
|
||||
WorldData::get_singleton()->update(evt.timeSinceLastFrame);
|
||||
return true;
|
||||
}
|
||||
class App : public OgreBites::ApplicationContext {
|
||||
Ogre::SceneNode *mCameraNode;
|
||||
Ogre::SceneManager *mScnMgr;
|
||||
std::unique_ptr<CharacterController> mCharacter;
|
||||
KeyHandler mKeyHandler;
|
||||
MainWorld mWorld;
|
||||
|
||||
public:
|
||||
App();
|
||||
virtual ~App();
|
||||
|
||||
void setup();
|
||||
void locateResources();
|
||||
void initCamera();
|
||||
Ogre::SceneManager *getSceneManager()
|
||||
{
|
||||
return mScnMgr;
|
||||
}
|
||||
void createContent();
|
||||
void createCharacter();
|
||||
void setupWorld();
|
||||
};
|
||||
|
||||
App::App()
|
||||
: OgreBites::ApplicationContext("App")
|
||||
{
|
||||
}
|
||||
void App::setup()
|
||||
{
|
||||
OgreBites::ApplicationContext::setup();
|
||||
Ogre::Root *root = getRoot();
|
||||
Ogre::SceneManager *scnMgr = root->createSceneManager();
|
||||
mScnMgr = scnMgr;
|
||||
}
|
||||
void App::locateResources()
|
||||
{
|
||||
OgreBites::ApplicationContext::locateResources();
|
||||
}
|
||||
App::~App()
|
||||
{
|
||||
}
|
||||
void App::initCamera()
|
||||
{
|
||||
// also need to tell where we are
|
||||
mCameraNode = mScnMgr->getRootSceneNode()->createChildSceneNode();
|
||||
mCameraNode->setPosition(0, 2, 3);
|
||||
mCameraNode->lookAt(Ogre::Vector3(0, 1, -1), Ogre::Node::TS_PARENT);
|
||||
|
||||
// create the camera
|
||||
Ogre::Camera *cam = mScnMgr->createCamera("tps_camera");
|
||||
cam->setNearClipDistance(0.1f); // specific to this sample
|
||||
cam->setAutoAspectRatio(true);
|
||||
mCameraNode->attachObject(cam);
|
||||
|
||||
// and tell it to render into the main window
|
||||
getRenderWindow()->addViewport(cam);
|
||||
}
|
||||
void App::setupWorld()
|
||||
{
|
||||
addInputListener(&mKeyHandler);
|
||||
mWorld.setup();
|
||||
getRoot()->addFrameListener(&mWorld);
|
||||
}
|
||||
void App::createCharacter()
|
||||
{
|
||||
Ogre::Camera *cam = static_cast<Ogre::Camera *>(
|
||||
mCameraNode->getAttachedObject("tps_camera"));
|
||||
mCharacter = std::make_unique<CharacterController>(mCameraNode, cam,
|
||||
mScnMgr, &mWorld);
|
||||
// mInputListenerChain = TouchAgnosticInputListenerChain(getRenderWindow(), {&mKeyHandler, mCharacter.get()});
|
||||
addInputListener(mCharacter.get());
|
||||
WorldData::get_singleton()->initPagedWorld(cam);
|
||||
}
|
||||
void App::createContent()
|
||||
{
|
||||
// without light we would just get a black screen
|
||||
Ogre::Light *light = mScnMgr->createLight("MainLight");
|
||||
Ogre::SceneNode *lightNode =
|
||||
mScnMgr->getRootSceneNode()->createChildSceneNode();
|
||||
// lightNode->setPosition(0, 10, 15);
|
||||
lightNode->setDirection(
|
||||
Ogre::Vector3(0.55, -0.3, 0.75).normalisedCopy());
|
||||
lightNode->attachObject(light);
|
||||
light->setType(Ogre::Light::LT_DIRECTIONAL);
|
||||
light->setDiffuseColour(Ogre::ColourValue::White);
|
||||
light->setSpecularColour(Ogre::ColourValue(0.4, 0.4, 0.4));
|
||||
mScnMgr->setSkyBox(true, "Skybox", 490);
|
||||
// Two boxes in one batch
|
||||
Procedural::TriangleBuffer tb;
|
||||
Procedural::BoxGenerator b;
|
||||
b.setPosition(2, 0, 0).addToTriangleBuffer(tb);
|
||||
b.setPosition(-2, 0, 0).addToTriangleBuffer(tb);
|
||||
tb.transformToMesh("twoBoxes");
|
||||
Ogre::Entity *ent = mScnMgr->createEntity("twoBoxes");
|
||||
Ogre::SceneNode *boxes =
|
||||
mScnMgr->getRootSceneNode()->createChildSceneNode(
|
||||
"boxes", Ogre::Vector3(5, 0, -5));
|
||||
boxes->attachObject(ent);
|
||||
}
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
App ctx;
|
||||
ctx.initApp();
|
||||
// get a pointer to the already created root
|
||||
Ogre::Root *root = ctx.getRoot();
|
||||
Ogre::SceneManager *scnMgr = ctx.getSceneManager();
|
||||
|
||||
// register our scene with the RTSS
|
||||
Ogre::RTShader::ShaderGenerator *shadergen =
|
||||
Ogre::RTShader::ShaderGenerator::getSingletonPtr();
|
||||
shadergen->addSceneManager(scnMgr);
|
||||
WorldData::init(root, scnMgr);
|
||||
ctx.setWindowGrab(true);
|
||||
ctx.createContent();
|
||||
ctx.initCamera();
|
||||
ctx.setupWorld();
|
||||
ctx.createCharacter();
|
||||
// register for input events
|
||||
// KeyHandler keyHandler;
|
||||
// ctx.addInputListener(&keyHandler);
|
||||
|
||||
ctx.getRoot()->startRendering();
|
||||
ctx.setWindowGrab(false);
|
||||
ctx.closeApp();
|
||||
WorldData::cleanup();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user