Help setting phantom's transform

Help setting phantom's transform

I've got a set of hkpCharacterProxies which each carry around a hkpSimpleShapePhantom'd capsule.  I need to mutate their position and orientation so it seemed like hkpShapePhantom->setTransform() would be the best way to accomplish this, however whenever I call hkpShapePhantom->setTransform I get an Access Violation Exception.  If anyone could offer some guidance on what I'm doing wrong, or a better/correct way to manipulate a phantom's orientation, I'd really appreciate it.  Also if it matters, this is C# application which communicates with Havok through a C++/CLI wrapper around an unmanaged C++ class.

My code looks something like this:

void NativeSimulation::MovePlayer(uint64_t id, hkVector4& position, hkQuaternion& rotation)
 {
auto proxy = GetProxy(id);
 auto phantom = proxy->getShapePhantom();
 auto transform = hkTransform(rotation, position);
 world->markForWrite();
phantom->setTransform(transform);
 world->unmarkForWrite();
 }
 

and the callstack:

Physics.dll!hkVector4f::dot<3>(const hkVector4f & a) Line 404 + 0x9 bytes C++
Physics.dll!hkVector4f::lengthSquared<3>() Line 585 + 0x10 bytes C++
Physics.dll!hkRotationf::isOrthonormal() + 0x4d bytes
Physics.dll!hkpShapePhantom::setTransform() + 0x7c bytes

10 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

Hi Shreek,

I don't think that the fact that your game is a C# application matters here, so we will ignore that for the moment. :)

Access violation errors are usually caused by reading the content of a NULL pointer, my guess here is that something you are passing in has actually an invalid value.
Judging from the callstack, it looks like the violation happens when accessing the content of the transform (so it doesn't seem to have anything to do with the phantom itself), could you make sure that the transform you are passing in setTransform() is really correct? It should be easy to verify this by setting a breakpoint in MovePlayer() and checking the value.

Regardless of what is causing the crash, if your intention is to move your character around with the character controller, hkpShapePhantom::setTransform() is not what you should be doing. In general the character proxy needs a hkpCharacterProxy::integrate() step to move around once the desired velocity has been set depending on user input. 
Check out Demo\Demos\Physics2012\UseCase\CharacterControl\CharacterProxy\CharacterController\CharacterControllerDemo.cpp to see a working example (and run the demo to see how it looks like).
Also have a look at section 2.5.1 of the docs for more info on generic character controllers.
If you want to "warp" the character and not move it normally, you should still use the functions in hkpCharacterProxy to do that and not manipulate the phantom directly.

Good luck!
Daniele

Try not to become a man of success, but rather try to become a man of value.

Daniele, thanks for the reply.

I'll look into my transform to make sure it's valid, but like you can see from the code, I'm just constructing it from an hkVec4 and an hkQuaternion.  I'm assuming for "warping" the character around the ideal entry point is proxy.SetPosition which works fine, however what I'm not seeing is the equivalent SetRotation/SetOrientation, so my thinking was that I could kill 2 birds with one stone via SetTransform.  If there is another way to manipulate a proxy's orientation I've been unable to find it.

EDIT:

I've managed to get SetTransform working by wrapping the Transform's input rotation in an hkQuaternion call.  It's a mystery to me as to why that's necessary, so if you've got any input there it would be appreciated, but making the following change seems to have fixed it:


	// old (doesn't work)

	auto transform = hkTransform(hkQuaternion::getIdentity(), position);

	


	// new (works)

	auto transform = hkTransform(hkQuaternion(hkQuaternion::getIdentity()), position);

	

Hi Shreek,

You're right, setting the position is not enough as you may want to set the position and the orientation of the character phantom in space.
A Transform in general represents a translation and a rotation, as you say.

Nevertheless, we have to think about what you're trying to do. The following should give you a good overview on the logic behind character controllers and rotation.

A character controller like the character proxy is used to move a character around depending on user inputs if the desired movement makes sense given the physics of the level. For instance, if the user wants to go forward, but the character is hitting a wall, the character controller will prevent the character from going inside the wall.
The way this works is the folliwng:
* The player says go forward
* We set a desired velocity inside the character proxy using setLinearVelocity().
* We call integrate() and this will result in a new position for the character (after solving the collision with the wall, so the character will not be inside the wall).

setPosition() in the character proxy is instead used for _actual teleportation_ of the character. If you're facing a wall and call setPosition() inside the wall, the character will teleport inside the wall. So this is not what you should do when the character is simply walking around.

If you think about a human character, the phantom shape used for that is usually a vertical capsule. In this condition the phantom _never_ rotates, because rotating the capsule just produces the same capsule. The actual character will rotate, but not its collision geometry (the capsule).

The only case where you actually want to rotate the phantom is when you have a character whose shape is not symmetrical to rotations (like a horse or a dog). Calling setTransform() on the phantom is not the right way to handle that though. 
If you call setTransform() or setRotation() on the phantom it will not check whether the character can rotate that way. We need to do something more clever.

Consider a horse that is standing 1 cm from wall on its left. If the horse was to rotate left 90 degrees its head would end up inside the wall (because the horse is more long than wide). This is solved in Demo\Demos\Physics2012\UseCase\CharacterControl\CharacterProxy\AsymetricCharacter\AsymetricCharacterDemo.cpp. The logic is in AsymetricCharacterDemo::reorientPhantom() and works as follows:
We rotate the phantom. We check whether the phantom is penetrating something too much (i.e. the horse head is in the wall), if it does we undo the rotation.

This is used to perform rotation of the character proxy phantom, but positioning of the character (even in the case of the horse) is always done using setLinearVelocity() and integrate().

The other type of character controller that we provide (hkpCharacterRigidBody) actually has a setAngularVelocity() to perform consistent rotation of the character even in the case of a horse or dog without too much special handling. You can check it out in: Demo\Demos\Physics2012\UseCase\CharacterControl\CharacterRigidBody\AsymetricCharacterRb\AsymetricCharacterRbDemo.cpp.

Now, concering the snippet you sent...
I have no clue why that change fixes the problem. :)
Specifically, I think that your change prevents the crash in this case but doesn't fix the problem in general, it just hides it.
When using static functions like hkQuaternion::getIdentity() and hkVector4::getZero() it should never crash, those are constants available in the static data section and should always be valid.
One thing to consider is that the position and rotation that you are receiving as input of your function are C++ references (which are basically pointers under the hood), so the access violation in your case makes me think that they are in reality NULL references. Or maybe the transform itself passed inside the phantom->setTransform() for some reason is NULL. Is the code in your first message accurate or is there stuff missing?

One way to generate NULL references is as follows:


	hkVector4& returnNull()

	{

	    hkVector4* null = HK_NULL;

	    return *null;

	}
...

	hkVector4& v = returnNull();

	auto transform = hkTransform(hkQuaternion::getIdentity(), v); // CRASH

	

Cheers,
Daniele

Try not to become a man of success, but rather try to become a man of value.

Right, I understand that setPosition() is warping people around and not using continuous motion/integration.  Right now we're in early stages of development on a networked game.  We allow the client to be somewhat authoritative on it's own position and just tell the server what it's current position is.  Before we had Havok the server simply had a  { playerid : { position, orientation } } map, now that we're integrating havok we want to replace that system.  So in effect we were warping players around on the server and doing smoothing on the client.  In the future we will of course be performing continuous integration using impulses/velocity, but right now we just want to replace the system we had.

We're using a capsule for the proxy, so it is symmetric, but we need to track facing (rotation around the up axis) for gameplay reasons and we didn't think it necessary to use a secondary system just for orientation.  If it's not recommended that we mutate orientation this way then we can track it externally to Havok, it just seemed like the best place.

On your code snippet, that's the really interesting part, I assumed I was having a similar problem (invalid memory), but the following code crashed (which uses the identity quaternion), while the code after it didn't:


	        void MovePlayer(uint64_t id, hkVector4Parameter position, hkQuaternionParameter rotation)

	        {

	            auto proxy = GetProxy(id);

	            auto phantom = proxy->getShapePhantom();

	            // passing in identity

	            auto transform = hkTransform(hkQuaternion::getIdentity(), position);

	            world->markForWrite();

	            // crashed on this line

	            phantom->setTransform(transform);

	            world->unmarkForWrite();

	        }
        void MovePlayer(uint64_t id, hkVector4Parameter position, hkQuaternionParameter rotation)

	        {

	            auto proxy = GetProxy(id);

	            auto phantom = proxy->getShapePhantom();

	            // passing in identity wrapped in hkQuaternion constructor

	            auto transform = hkTransform(hkQuaternion(hkQuaternion::getIdentity()), position);

	            world->markForWrite();

	            // worked just fine

	            phantom->setTransform(transform);

	            world->unmarkForWrite();

	        }

	

Thanks again for the help :)

Hi Shreek,
I see now, you're keeping track of the position and rotation of the character just by looking at the position and orientation of the phantom.
That's ok, I don't think it's the best way to do that because every time the phantom setPosition() or setTransform() is called on the phantom a cache of overlapping bodies AABBs is updated with a query on the physics world. This is fast and shouldn't be a problem, but if it can be avoided (when the character is rotating in place) then it would be best to do that!
It's ok for now anyway.

So, onto the actual access violation problem.
That is so amazingly weird! Is the callstack for the crash in the first case the same as before?

Physics.dll!hkVector4f::dot<3>(const hkVector4f & a) Line 404 + 0x9 bytes C++
Physics.dll!hkVector4f::lengthSquared<3>() Line 585 + 0x10 bytes C++
Physics.dll!hkRotationf::isOrthonormal() + 0x4d bytes 
Physics.dll!hkpShapePhantom::setTransform() + 0x7c bytes

Could you check in the first case what hkQuaternion::getIdentity() is returning?
Could you provide the _full_ callstack and the full access violation error with address etc (i.e. reading location.... )?

I know that it's hard to resist the beauty of C++11, but could I ask you to get rid of the autos for now to make sure that the code really does what it seems to be doing?

* proxy should be an hkpCharacterProxy*
* phantom should be an hkpShapePhantom*
* transform should be an hkTransform

I'm getting really curious about this.

Cheers,
Daniele

Try not to become a man of success, but rather try to become a man of value.

So I've tracked the culprit down:


	        void PhysicsEngine::MovePlayer(uint64_t id, hkVector4Parameter position, hkQuaternionParameter rotation)

	        {

	            std::unordered_map<uint64_t, hkpCharacterProxy*>::iterator iter = playerProxies.find(id);

	            if (iter == playerProxies.end()) return;

	            hkpCharacterProxy* proxy = iter->second;

	            hkpShapePhantom* phantom = proxy->getShapePhantom();

	            // This next line causes the crash, but it won't crash until I actually set the transform

	            // if I replace "auto" with "hkTransform" or "hkTransformf" everything works just fine.

	            // Intellisense identifies "transform" as an "hkTransformf", so I'm assuming the

	            // compiler is choking on something

	            auto transform = hkTransform(hkQuaternion::getIdentity(), hkVector4::getZero());

	            world->markForWrite(); //hkWorld*

	            phantom->setTransform(transform);

	            world->unmarkForWrite();

	        }

	

Callstack:
    ServerPhysics.dll!hkVector4f::dot<3>(const hkVector4f & a)  Line 404 + 0x9 bytes    C++
     ServerPhysics.dll!hkVector4f::lengthSquared<3>()  Line 585 + 0x10 bytes    C++
     ServerPhysics.dll!hkRotationf::isOrthonormal()  + 0x4d bytes    
     ServerPhysics.dll!hkpShapePhantom::setTransform()  + 0x7c bytes    
     ServerPhysics.dll!World::Unmanaged::PhysicsEngine::MovePlayer(unsigned __int64 id, const hkVector4f & position, const hkQuaternionf & rotation)  Line 385    C++
     ServerPhysics.dll!World::Unmanaged::PhysicsEngine::DispatchAction(World::Unmanaged::Actions::Action * action)  Line 269    C++
     ServerPhysics.dll!World::Unmanaged::PhysicsEngine::ProcessActions()  Line 236    C++
     ServerPhysics.dll!World::Unmanaged::PhysicsEngine::RunMt()  Line 186    C++
     [Managed to Native Transition]    
     ServerPhysics.dll!World::Managed::PhysicsEngine::RunMt() Line 37 + 0xb bytes    C++
     ServerMain.exe!WorldUpdater.Start.AnonymousMethod__0(object _) Line 203 + 0x16 bytes    C#

 

I've also attached the disassembly
Looking at the disassembly, the only noticeable difference appears to be with the auto keyword it doesn't align the stack coming into the method, so I'm going to assume it's probably related to this:
http://connect.microsoft.com/VisualStudio/feedback/details/775238/visual-c-2012-auto-keyword-ignores-structure-memory-alignment

Hi Shreek,
Nice!

That's why I was asking for the access violation address, I had a feeling that it could be something like this.

I can't see any attached disassembly, but your analysis seems to be accurate.

Havok math classes like hkRotationf, hkVector4f and hkTransformf are aligned to 16 bytes boundaries to perform efficient memory access and loading/unloading to/from sse SIMD registries. It looks like this is a VS2012 compiler bug that doesn't align the objects on the stack properly when using the auto keyword. The compiler intrinsic we use to access this memory (that should be aligned) only works on 16 bytes aligned memory and because it isn't it causes the access violation. This should be clear from the address of the access violation.

The compiler bug was also reported here: http://stackoverflow.com/questions/12255297/alignment-16-not-respected-w....

Great job tracking it down!
Daniele

Try not to become a man of success, but rather try to become a man of value.

Thanks again for all the help, and I'm going to try re-attaching the disassembly just for the sake of completeness.

One final question; once our usage of Havok is more mature and we're moving the proxy around with velocities, is there a better way to manipulate orientation other than using SetTransform on the phantom?  The asymmetric demo you mentioned was using the hkpCharacterRigidBody and angular velocity to rotate the character; I don't see an equivalent for proxies.  Obviously we could move to using hkpCharacterRigidBody but when I looked at the pros and cons in the documentation the proxy seemed like a better fit for us.  If settransform is going to be inefficient then clearly we need to re-evaluate our decision (or just track orientation separately).

Attachments: 

AttachmentSize
Download notworking.txt5.53 KB
Download working.txt5.88 KB

Hi Shreek,

I took a quick look at the assembly and it does look like in the case of auto a whole section making sure esp is 16 bytes aligned is missing.

My advice on orientating the proxy is that it should be done only when it would make a difference for character collision (e.g. asymmetric demo). There are ways to do this with both hkpCharacterRigidBody and with hkpCharacterProxy: 

See Demo\Demos\Physics2012\UseCase\CharacterControl\CharacterProxy\AsymetricCharacter\AsymetricCharacterDemo.cpp

In your case though when rotating the character you always know that it can rotate because the shape is symmetrical (capsule). So you don't have any of the problems encountered when rotating the asymmetric shape in the asymmetric demo. In that case you can go ahead and simply call setTransform as you're doing.

The only disadvantage of that is inefficiency as I explained before. You need to use the character proxy position to track the position of the character to make sure that it reacts to collisions accurately, but if you call setTransform() when the character is purely rotating in space you are paying the overhead of rebuilding the AABB overlap cache with the phantom.

This could be avoided if you were tracking orientation and position externally (in your own character structures) and only using the proxy to set velocities, integrate and get the updated position. This way if the character is purely rotating the proxy will have a desired linear velocity of zero, integrate would do nothing and the cost would be avoided. This would be the ideal solution.

Cheers!
Daniele

Try not to become a man of success, but rather try to become a man of value.

Login to leave a comment.