Camera/Character attachment

__bf_ddc__

22-10-2007 17:00:25

Hello--

I'm attaching a camera to a character in my scene (for collision), and it seems to be reversing where the camera starts. When I check it through remote debugger, the position of the character node is showing correct, but the camera is mirrored inside the next room's wall...

Here's a quick screeny/notes:



1: The current Character/attached cam
2: about where the camera actually is... through a wall and pointing the other way

Here's some code I'm doing:

Creation of scene/world/character/attaching camera:
m_pScene = m_pWorld->createScene("ModelPhysics", m_pSceneMgr, "gravity: no, floor: no, time-step-method: variable");
m_pCamBody = m_pScene->createCharacter("CamBody", Vector3(0, 0, 0), "dimensions: 1 2 1, type: box");
m_pCamBody->createNode();
m_pCamBody->getNode()->attachObject(m_pCamera);
m_pCharacterHitReport = new myHitReport();
m_pWorld->getCharacterController()->addHitReport(m_pCharacterHitReport);


When I load a certain room, I have a stored camera position...

m_pCamera->setOrientation(Quaternion::IDENTITY);
m_pCamera->setPosition(m_pCamPos[m_nRoomID].translate);
m_pCamera->yaw(Degree(m_pCamPos[m_nRoomID].yaw));
m_pCamera->pitch(Degree(m_pCamPos[m_nRoomID].pitch));
m_pCamera->setProjectionType(PT_PERSPECTIVE);
m_pCamera->setNearClipDistance(1);
m_pCamera->setFixedYawAxis(true);


Currently, I'm still just moving the camera, though I've tried changing all the camera movements to the character and it just stood in place or jittered all funky...

// Slerp for the new quaternion
Quaternion delta = Quaternion::Slerp(m_fRotateProgress, m_qOrientSrc, m_qOrientDest, true);
Vector3 X, Y, Z;
// Get the three vectors from the quaternion
delta.ToAxes(X, Y, Z);
// Take the negative cross product of vector Z with Y up
Vector3 newX = -Z.crossProduct(Vector3::UNIT_Y);
// Take the cross product of vector Z with new X vector
Vector3 newY = Z.crossProduct(newX);
// Normalise new X vector
newX.normalise();
// Normalise new Y vector
newY.normalise();
// Normalise Z vector
Z.normalise();
// Set the new delta quaternion by new X, new Y and Z
delta.FromAxes(newX, newY, Z);
// Update camera orientation with delta quaternion
m_pCamera->setOrientation(delta);


In my camera rotations I do a lot of slerp/rotations over time (buffered) to give a kind of smooth look, but in translation of movement it's just normal.

Is there something I'm doing wrong that the camera attached to the character node would always be pointing outward of the room?

I'm hoping to just be able to move my camera as normal, and the Character move along with it to check for collisions. I already have the camera structured really deep and don't want to have to change it all for the character instead -- can this be achieved?

__bf_ddc__

29-10-2007 20:20:36

Well, I keep having issues with this. I've had the program spit out the camera position/orientation tied to the physics character; and the character pos/dir:

PHYSICS POSITION>> [Room: 11] Position:[77.250366, 10.000000, -45.539822] Yaw:[0.000000] Pitch:[0.000000]
CAMERA POSITION>> [Room: 11] Position:[77.250366, 55.000000, -45.539822] Yaw:[49.853340] Pitch:[-5.601623]

It seems it always turns the yaw&pitch to 0 on the character. And with the yaw&pitch set to 0 it turns the camera far around into another entire room...

I've tried anything from setting its position directly to that of the camera and making own quaternions out of the stored yaw&pitch...

Through those efforts, I've gotten it to a point where the yaw works, but the pitch still reports 0:

PHYSICS POSITION>> [Room: 11] Position:[77.250366, 10.000000, -45.539822] Yaw:[49.853340] Pitch:[0.000000]
CAMERA POSITION>> [Room: 11] Position:[77.250366, 55.000000, -45.539822] Yaw:[49.853340] Pitch:[-5.601623]

Any ideas?

__bf_ddc__

30-10-2007 11:59:30

As per this topic: http://www.ogre3d.org/phpBB2addons/viewtopic.php?p=27984

Try the code I use

Quaternion q;
q.FromAngleAxis(
Radian(-mouse->getMouseState().X.rel * 0.13),
mCharacter->getGlobalOrientation() * Vector3::UNIT_Y
);
q = mCharacter->getGlobalOrientation() * q;

mCharacter->setDirection(q);


And if you have a Camera attached to the node:

mCamera->pitch(Radian(Degree(-ms.Y.rel * 0.13).valueRadians()));

To look up and down.


I do a similar camera->pitch and the next render call still bumps the character direction back to the pitch 0.0f...

m_pCamBody->setPosition(m_pCamPos[m_nRoomID].translate);
m_pCamBody->setDirection(Quaternion::IDENTITY);

Quaternion q;
q.FromAngleAxis(Radian(Degree(m_pCamPos[m_nRoomID].yaw)), m_pCamBody->getGlobalOrientation() * Vector3::UNIT_Y);
q = m_pCamBody->getGlobalOrientation() * q;
m_pCamBody->setDirection(q);

m_pCamera->pitch(Degree(m_pCamPos[m_nRoomID].pitch));

__bf_ddc__

30-10-2007 16:44:33

Getting further:

m_pCamera->setPosition(Vector3(0, 0, 0));
m_pCamera->setOrientation(Quaternion::IDENTITY);
m_pCamBody->setPosition(m_pCamPos[m_nRoomID].translate);
m_pCamBody->setDirection(Quaternion::IDENTITY);
m_pCamPos[m_nRoomID].direction.normalise();
m_pCamBody->setDirection(m_pCamPos[m_nRoomID].direction);
m_pCamera->pitch(Radian(m_pCamPos[m_nRoomID].direction.getPitch()));


The camera node that's attached to the character is now where it needs to be, as far as positioning goes -- the character is not oriented the correct direction, but the combination of the character direction + camera pitch is now showing like it should. But now when I try moving the character with the WASD keys, it is in the wrong direction and will start going the entirely wrong way...

Also I should note, when the first frame fires off, it shows the direction the character is originally facing but then the next frame reorients it fine...

I'm not sure why it's doing that -- but I think if the character is originally pointing the right direction it'll not do that + will operate right.

Anyone know what I can do?

betajaen

30-10-2007 16:54:45

I was working on the CharacterController last week on another Cake clone called WhiteBox. I had a similar problem, I think I just reversed the roles of WASD around.

I wish I had a copy of Whitebox handy, it's gone missing.

__bf_ddc__

30-10-2007 17:26:25

I see.

It seems a bit odd though --
I took a capture of the quaternion values I was supposed to have from an already made camera,

W: 0.904266
X: -0.068386
Y: 0.420258
Z: 0.031782

I call the setDirection on my character with this (normalised) quaternion, and I get these values (after it renders... it orients different):

Character:
W: 0.906848
X: 0.000000
Y: 0.421458
Z: 0.000000

Camera:
W: 0.998805
X: -0.048864
Y: 0.000000
Z: 0.000000

The camera view is pointing the correct way, but the character is set at a completely different direction (I think?) -- because when I try going [Forward - it goes Left], [Backward - it goes Right], [Left - it goes Forward], [Right - it goes Backward]...

So I'm either really mixing my quaternions up or what?

betajaen

30-10-2007 17:38:46

Exactly what I got with Whitebox. For now you could just swap the roles of the keys around like I did.

However, I'm planning a massive overhaul with the character system for 1.1

__bf_ddc__

30-10-2007 17:40:21

Exactly what I got with Whitebox. For now you could just swap the roles of the keys around like I did.

However, I'm planning a massive overhaul with the character system for 1.1

Okay, Thanks for your advice and patience ;)
I'll be looking forward to it.

__bf_ddc__

30-10-2007 19:46:34

This might actually be easier:

Is it possible to just orient the character direction to the direction the camera is currently pointing at?

I do a lot of slerp rotations that work REALLY well on my camera and they're a pain to have to convert over; so is it possible every rotation of the camera to just keep the character constantly looking at that direction?

Thanks

__bf_ddc__

31-10-2007 12:41:01

So I've completely detached my camera from my character because that was a complete and utter pain -- so now I'm just updating my character position with the camera's because that was working perfectly. Now I just have to figure out how to catch when the character collides with something and not move the camera...

I'm trying to check for collisions, but I noticed not moving the actual character anymore [through addMovement] will not net any custom hit report flags.... how can this be achieved? If at all?

betajaen

31-10-2007 13:27:25

You move the camera and you set the position of the character? That'll cause a lot of problems with clipping, tunnelling and so forth.

You'll have to do it via addMovement, and just make the camera follow the character around.

__bf_ddc__

31-10-2007 13:34:41

I tried that. But I had such problems with my slerp quaternion rotations that since only the character's direction could be set, it'd go wild and spin around.

Quaternion delta = Quaternion::Slerp(m_fRotateProgress, m_qOrientSrc, m_qOrientDest, true);
Vector3 X, Y, Z;
delta.ToAxes(X, Y, Z);
Vector3 newX = -Z.crossProduct(Vector3::UNIT_Y);
Vector3 newY = Z.crossProduct(newX);
newX.normalise();
newY.normalise();
Z.normalise();
delta.FromAxes(newX, newY, Z);


m_pCamBody->setDirection(m_pCamBody->getGlobalOrientation() * delta);
m_pCamera->pitch(Radian(delta.getPitch().valueRadians()));

I use these rotations to be smoothly rotating over time to the position, and I'd have to set the character->setDirection, then camera->pitch, it'd work okay with just regular mouse movements, but I have a certain function where when I select an object, it orients the camera to that position (so that position you click is in the middle of the screen), and when slerping that rotation, the camera goes wonky.

This is how I get my rotation to slerp from a mouse movement:
m_mMouseMat.FromEulerAnglesYXZ(Radian((-arg.state.X.rel / (float)arg.state.width)), Radian((-arg.state.Y.rel / (float)arg.state.height)), Radian(0));
m_qMouseQuat = Quaternion(m_mMouseMat);
m_qOrientSrc = m_pCamera->getOrientation();
m_qOrientDest = m_qOrientSrc * m_qMouseQuat;


And how I do the other function that doesn't properly work:
m_qOrientSrc = m_pCamera->getOrientation();
Vector3 hitPos = m_pMouseRaycast->getClosestRaycastHit().mWorldImpact;
m_pCamera->lookAt(hitPos);
m_qOrientDest = m_qOrientSrc * m_pCamera->getOrientation();

__bf_ddc__

31-10-2007 14:59:48

I suppose:

Is there anyway to do the following:

- Still keep my slerp quaternions which orient slowly to a final position.
- Orient the character (which I know only rotates on the Y axis, [X&Z set to 0 in setDirection function]) to the direction it needs to go.
- Make the camera go only up/down through pitch.

With the code in the above post as a reference...

__bf_ddc__

08-11-2007 19:47:43

I still don't have this working...

Perhaps someone can give insights:

I click an object in 3d space... I get the position of where it is in 3D space [Vector3]:
m_pMouseRaycast->getClosestRaycastHit().mWorldImpact

I then need to:
1.) Set the direction of my Character to look at the spot where that position in 3D space is
2.) Set my camera pitch to look at the spot

I also use a slerp function to slowly rotate towards this spot... so it's not just something as easy as a lookAt() function to update it where it needs to go.

If you have any ideas please help me out! :) thanks

__bf_ddc__

09-11-2007 02:14:32

I've gotten it to a point where :
[1] the Direction is correctly set on the character, but
[2] the Pitch is not being set on the camera correctly...

m_qOrientSrc = m_pCamBody->getGlobalOrientation();
Vector3 hitPos = m_pMouseRaycast->getClosestRaycastHit().mWorldImpact;
m_pCamera->lookAt(hitPos);
m_qOrientDest = m_pCamBody->getGlobalOrientation() * m_pCamera->getOrientation();


Then the slerp called each frame:

m_pCamBody->setDirection(delta);
m_pCamera->setOrientation(Quaternion::IDENTITY);
m_pCamera->pitch(delta.getPitch());


It also seems to flip my up/down when turning the camera around 180*, so when turning up it rotates down, and vice versa...

__bf_ddc__

09-11-2007 16:08:41

Im using the code betajaen has on the forums on how to move the character/camera with the mouse state... but when I turn the camera around back it inverts all the up|down calls, here's the code:

Quaternion q;
q.FromAngleAxis(Radian(-arg.state.X.rel * 0.001), m_pCamBody->getGlobalOrientation() * Vector3::UNIT_Y);
q = m_pCamBody->getGlobalOrientation() * q;
m_pCamBody->setDirection(q);
m_pCamera->pitch(Radian(Degree(-arg.state.Y.rel * 0.09).valueRadians()));


Also, as it turns toward the +X and -X axes, it rotates twice as fast...

I've had so much trouble with these quaternions; before they were working absolutely fine when just applying to a camera->setOrientation, but now that the character has to be directed correctly to translate around right, I've had to strip out all my smooth slerp rotations and replace with hard, ugly ones.

Please help :X

betajaen

09-11-2007 16:55:22

I'm going to track down my WhiteCakeBox code and post it especially for you.

betajaen

09-11-2007 16:59:17

Alright, I got it. I thought it was deleted.

That's based of the Cake framework, but that doesn't matter to much. The real code is below.

It features:
- Proper movement of the Character. Using WASD keys, but no jumping.
- Some early work on firing bullets and decals
- Some early work of picking up things.

I'm hoping it's the magic silver bullet for your problem but who knows. I should warn you, I think the roles of SteppingLeft/Right have to be switched with Forward/Backward when you check if the keys have been pressed. You'll notice when your character is moving to the side and your banging the W key down.

#include "WhiteCakeFrosting.h"

using namespace NxOgre;
using namespace Ogre;
using namespace std;

class Sponge_WhiteCake : public WhiteCake {

public:

World* mWorld;
Scene* mScene;
Character* mPlayer;
float mLastFireTime;
unsigned int nbBullet;

Actor* mItem;

float mEyeHeight;
float mMoveUpTime;

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

void createPhysics() {

mWorld = new World();
mScene = mWorld->createScene("Main", mSceneMgr, "gravity: yes, floor: yes");

mScene->createActor("FloorBase", new CubeShape(1024,0.5f,1024), Vector3(0,0.25f,0), "static: yes");
mScene->batchCreateBodies("Cubes", "cube.50cm.mesh", 8, new CubeShape(0.5), Vector3(0,4,0), Vector3(0.005,1,0.005), Scene::BT_NOW, "mass: 10");
mScene->batchCreateBodies("CubesA", "cube.50cm.mesh", 18, new CubeShape(0.5), Vector3(3,4,0), Vector3(0.005,1,0.005), Scene::BT_NOW, "mass: 10");

mPlayer = mScene->createCharacter("Player", Ogre::Vector3(6,2,6), "type: box, dimensions:0.5 2 0.5");
mPlayer->createNode();
mPlayer->getNode()->attachObject(mCamera);
mCamera->setPosition(0,1.8,0);
mCamera->setDirection(Ogre::Vector3::NEGATIVE_UNIT_X);

mLastFireTime = 0;
nbBullet = 0;
createDecal();
mItem = 0;
mEyeHeight = 0;
mMoveUpTime = 0;
}

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

void destroyPhysics() {
delete mWorld;
}

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

void turnCharacter() {

Quaternion q;

q.FromAngleAxis(
Radian(-mMouseState.X.rel * 0.013),
mPlayer->getGlobalOrientation() * Vector3::UNIT_Y
);

q = mPlayer->getGlobalOrientation() * q;
mPlayer->setDirection(q);

}

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

void createDecal() {

const float width = 0.04f;
const float height = 0.04f;

ManualObject mo("BulletDecal");
Vector3 vec(width/2, 0, 0);
Quaternion rot(1,0,0,0);
mo.begin("BulletDecal", RenderOperation::OT_TRIANGLE_LIST);

mo.position(-vec.x, height, -vec.z);
mo.textureCoord(0, 0);

mo.position(vec.x, height, vec.z);
mo.textureCoord(1, 0);

mo.position(-vec.x, 0, -vec.z);
mo.textureCoord(0, 1);

mo.position(vec.x, 0, vec.z);
mo.textureCoord(1, 1);

mo.triangle(1,3,0);
mo.triangle(3,2,0);

mo.end();
mo.convertToMesh("BulletDecalMesh");


}

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

void fireWeapon() {

NxOgre::Actor* a = 0;

mOgreRay = mCamera->getCameraToViewportRay(0.5f,0.5f);

mRay->setOrigin(mOgreRay.getOrigin());
mRay->setDirection(mOgreRay.getDirection());
mRay->setMaxDistance(1.5f);

if (mRay->castShape(NxOgre::RayCaster::AF_NONE))
a = mRay->getClosestActor();

if (a == 0)
return;

if (a->isDead() || !a->isDynamic())
return;

std::cout << mRay->getClosestRaycastHit().mWorldImpact << std::endl;

a->addForceAtPos(mOgreRay.getDirection(), mOgreRay.getOrigin());

mLastFireTime = 0.0f;

// Attach Bullet Decal.
if (!a->hasVisualisation())
return;

Body* body = static_cast<Body*>(a);


Ogre::String entName = "b" + Ogre::StringConverter::toString(nbBullet++);

SceneNode* n = mSceneMgr->createSceneNode();
n->attachObject(mSceneMgr->createEntity(entName, "BulletDecalMesh"));
n->setPosition(0,0,0);
body->getNode()->addChild(n);
//n->translate(mRay->getClosestRaycastHit().mWorldImpact, Ogre::Node::TS_WORLD);
Vector3 hit = mRay->getClosestRaycastHit().mWorldImpact;
Vector3 norm = mRay->getClosestRaycastHit().mWorldNormal;

Vector3 pos = hit - body->getNode()->getPosition() + (body->getNode()->getOrientation().Inverse() * norm);

//n->setDirection(mRay->getClosestRaycastHit().mWorldNormal);
//n->translate(hit, Ogre::Node::TS_WORLD);

// 1. Get the translation from C to the new N according to the world axes:
//Vector3 transWorld = hit - n->getDerivedPosition();
// 2. Adjust this translation so that it's relative to C's local axes:
//Vector3 transC = transWorld * n->getDerivedOrientation().Inverse();
// transC is the translation expressed as if C's axes were the 'world'

n->setPosition(pos);
n->setDirection(body->getNode()->getOrientation().Inverse() * norm);
}

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

void holdItem() {

mItem = 0;

mOgreRay = mCamera->getCameraToViewportRay(0.5f,0.5f);

mRay->setOrigin(mOgreRay.getOrigin());
mRay->setDirection(mOgreRay.getDirection());

if (mRay->castShape(NxOgre::RayCaster::AF_NONE))
mItem = mRay->getClosestActor();

if (mItem == 0)
return;

if (mItem->isDead() || !mItem->isDynamic())
return;

// mItem->raiseBodyFlag(NX_BF_KINEMATIC);

std::cout << "Holding " << mItem->getName() << std::endl;
mMoveUpTime = 0.0f;
moveItem();
}

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

void moveItem() {

if (mItem == 0)
return;

float height = 0.0f; //mPlayer->getGlobalPosition().y;

if (mMoveUpTime < 1.0f) {
height += mMoveUpTime;
mMoveUpTime += 0.05f;
}
else {
height = mCamera->getOrientation().getPitch().valueRadians();
if (height == 0) {
height = 1;
}
else {
height *= 0.1f;
height += 1.0f;
}
}

mOgreRay = mCamera->getCameraToViewportRay(0.5f,0.5f);

Vector3 targetPos = mOgreRay.getOrigin() + (mOgreRay.getDirection().normalisedCopy() * Vector3(4,4,4));
#if 0
std::cout << "Pitch" << mCamera->getOrientation().getPitch().valueDegrees() << ", Height =>" << height << std::endl;

Vector3 targetPos =
(
mPlayer->getGlobalPosition() + (mPlayer->getGlobalOrientation() * Vector3(-2,height,0))
/*-
mItem->getGlobalPosition()*/
);
#endif
// mItem->moveGlobalPosition(targetPos);

NxVec3 bs = mItem->getNxActor()->getGlobalPosition();
NxReal targetHeight = targetPos.y;
NxReal diff = bs.y - targetHeight;
NxReal mass = mItem->getMass();
NxReal gravVector = -mScene->getGravity().y;

if (diff < 0) {
mItem->setLinearDamping(1.0f);
NxReal displacement = -diff * 0.10f;
mItem->addForce(Vector3(0, gravVector * mass * displacement,0),NX_SMOOTH_IMPULSE);
}
else {
mItem->setLinearDamping(0.01f);
}

//Vector3 negGravity = (-mScene->getGravity() + Vector3(0,targetPos.y,0)) * mItem->getMass();
//mItem->addForce(negGravity, NX_IMPULSE);
//targetPos.y = 0;

//mItem->moveTowards(targetPos, mItem->getMass());

//std::cout << mItem->getGlobalPosition() << std::endl;

//if (mItem->getGlobalOrientation() != Ogre::Quaternion::IDENTITY)
// mItem->moveGlobalOrientation(Ogre::Quaternion::IDENTITY);


}

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

void dropItem() {

if (mItem == 0)
return;

std::cout << "Dropping " << mItem->getName() << std::endl;
// mItem->clearBodyFlag(NX_BF_KINEMATIC);
mItem = 0;
}

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

void onFrame(float deltaTime) {

if (mKeyboard->isKeyDown(mKeys[AC_SIDERIGHT]))
mPlayer->addMovement(Character::DR_StepLeft);

if (mKeyboard->isKeyDown(mKeys[AC_SIDELEFT]))
mPlayer->addMovement(Character::DR_StepRight);

if (mKeyboard->isKeyDown(mKeys[AC_FORWARD]))
mPlayer->addMovement(Character::DR_Forward);

if (mKeyboard->isKeyDown(mKeys[AC_BACKWARD]))
mPlayer->addMovement(Character::DR_Backward);

turnCharacter();
mCamera->pitch(Radian(Degree(-mMouseState.Y.rel * 0.26).valueRadians()));

mLastFireTime += deltaTime;

if (mLastFireTime > 0.2f && mMouseState.buttonDown(OIS::MB_Left))
fireWeapon();

if (mItem == 0 && mKeyboard->isKeyDown(mKeys[AC_CARRY])) {
holdItem();
}

if (mItem && mKeyboard->isKeyDown(mKeys[AC_DROP])) {
dropItem();
}
else if (mItem) {
moveItem();
}

}

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

BakeMyWhiteCake();

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

};

Hey_Cut_Me_Slice_Of(Sponge_WhiteCake)

__bf_ddc__

09-11-2007 17:33:20

Thanks for the snippets beta, unfortunately it's not gonna fix my problem. I can re-roll to a slightly older version where the mouse movements were doing fine, just the portion that automatically orients the character&camera to a certain position will have to stay commented... :/

__bf_ddc__

09-11-2007 18:05:44

Okay Now I feel rather dumb.

Basically realising that [1] mouse rotations -> quaternion rotation are RELATIVE [2] my instance with selecting a vector in 3D space, then making a quaternion slerp to rotate to that point over time is ABSOLUTE.

So I added a flag to determine whether its abs/rel, and if it was absolute, just set the character's direction to the delta quaternion, if it was relative, multiply the character's current direction * the delta, to get the new absolute quaternion to rotate to.

This works pretty good! There's a few bumps and hitches I have to tweak out, but I should also note that when orienting an absolute quaternion in this instance, to get the source and destination, multiply:

camera's orientation * character's direction = src
camera's new orientation (I grab it from a lookAt() func) * character's direction = dest

Then slowly slerp to that dest from the src over time.

It seems there's a few oddities, such as, when facing forward it does a bit of a 'jump' when orienting the camera's pitch, but when facing backward, it is completely smooth...