setPosition strangeness

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
ElectroDruid
Posts: 23
Joined: Thu Oct 21, 2004 3:55 pm

setPosition strangeness

Post by ElectroDruid »

Hi,

I'm working on an entity factory which reads in information about entities from an XML file and sets them up in the world. So, I step through the XML file, find an entity, get its name as a string, and use its factory method to create a new entity of the right type and call an initialisation function on it (which sets up the scene node and some other stuff). Later on in the XML parsing, I grab a position vector for where this object should go, and set the scene node to that position.

Which doesn't work.

The entities all end up at the origin of the world, which happens to be inside a bit of collision. If I set their initial positions (in the initialise function when I'm setting up the scene node) to somewhere where there isn't any collision, and then let the parser update their position to where they're supposed to be, it seems to work. But it seems to be a pretty horrible work-around, and I'd rather find out why just setting their position once doesn't work.

Any ideas? Has anyone had something like this before? I can post the parsing and initialisation code if it helps.

Cheers
sgt_pinky
Posts: 149
Joined: Sat Oct 14, 2006 11:20 am
Location: Melbourne, Australia

Post by sgt_pinky »

Without seeing any code, it is impossible for anyone on this forum to help you.

Cheers,

Pinky
Intellectuals solve problems - geniuses prevent them. -- Einstein
#irrlicht on irc.freenode.net
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

Is it possible that the collision code detects that the node is in collision and then prevents the setPosition() call from taking effect?

I would imagine that the collision system would assume that if a node is at position A for tick t and position B at tick t+1, then the node has moved from A to B in the elapsed time. If, in the course of moving from A to B, the object collided with something, then the collision system would prevent the object from moving.

If that is the case then maybe you should disable collisions until all obect positions are final, or you should set the object to the correct position when it is created, but before the collision system has been notified of the new object.

Travis
Andreas
Posts: 166
Joined: Sun Oct 31, 2004 7:15 am
Location: Münster / Germany
Contact:

Post by Andreas »

I don't know if this is exactly what you mean, but maybe it helps:

I had to update the position of sceneNodes which had irrlicht collision detection with the level enabled (big square with Boxes on it).

If collision was active, the sceneNodes collided with the boxes and were dangling at the first box on their way.

The solution was:
1. Disable collision detection for this node
2. use setPosition to move the node
3. enable collision detection for this node.

Maybe it helps! :)
andreas
ElectroDruid
Posts: 23
Joined: Thu Oct 21, 2004 3:55 pm

Post by ElectroDruid »

Hi, thanks for the replies. I played about a bit, thinking about your suggestions, and I did indeed find that the problem seems to occur because I add the collision response animator when I set up the cube (i.e before I've had a chance to position it properly, meaning it's initialised to be at the origin, where it intersects with collision). But from looking through Irrlicht, I can't see how it could get stuck in collision - my code is happening at initialisation time, i.e. before entering the Irrlicht main update loop. There must be something in my setup code which is causing the entities to get stuck in collision even before I've had a chance to place them properly.

Here's my parsing code (with some error checking and irrelevant stuff taken out to make it easier to read):

Code: Select all

bool cEntityMgr::Load(const char* szFileName)
{
	io::IrrXMLReader* xml = io::createIrrXMLReader(szFileName);

	const char* szEntityType = NULL;
	Vec3f pos(0,0,0);
	Vec3f rot(0,0,0);
	cGameEntity* pNewEnt = NULL;

	while(xml->read())
	{
		io::EXML_NODE nodeType = xml->getNodeType();

		switch (nodeType)
		{
		case(io::EXN_ELEMENT):
			{
				irr::core::stringc nodeName = xml->getNodeName();

				if (nodeName == "Entity")
				{
					szEntityType = xml->getAttributeValue("Type");

					pNewEnt = CreateEntity(szEntityType);
					pNewEnt->Init(); // See below
				}
				else if (nodeName == "Position")
				{
					const char* szPosX = xml->getAttributeValue("x");
					const char* szPosY = xml->getAttributeValue("y");
					const char* szPosZ = xml->getAttributeValue("z");
					
					pNewEnt->mpSceneNode->setPosition(Vec3f(static_cast<float>(atof(szPosX)), 
								static_cast<float>(atof(szPosY)), 
								static_cast<float>(atof(szPosZ))));
				}
			}
			break;
		case(io::EXN_ELEMENT_END):
			{
				irr::core::stringc nodeName = xml->getNodeName();

				if (nodeName == "Entity")
				{
					// Register this entity with my entity manager
					Register(pNewEnt);

					pNewEnt = NULL;
				}
			}
		default:
			break;
		}
	}

	delete xml;

	return true;
}
Create entity just creates a new subclass of cGameEntity, depending on what string is passed into it from the XML file. I'm currently making a subclass call cEntityTestCube. The init function which gets called for this test class is as follows, again with some error checking taken out for clarity:

Code: Select all

void cEntityTestCube::Init()
{
	mpMesh = gpSceneManager->getMesh("models/cube.3ds");

	if (mpMesh)
	{
		mpSceneNode = gpSceneManager->addAnimatedMeshSceneNode(mpMesh);
		mpSceneNode->setMaterialTexture(0, gpVideoDriver->getTexture("textures/red.jpg"));

      // This is line I don't want to have to put in		
      mpSceneNode->setPosition(Vec3f(0.0f,50.0f,0.0f));

		mpSceneNode->setScale(Vec3f(1.0f,1.0f,1.0f));

		for (int i = 0; i < mpSceneNode->getMaterialCount(); ++i)
		{
			mpSceneNode->getMaterial(i).SpecularColor.set(255,255,255,255);
			mpSceneNode->getMaterial(i).AmbientColor.set(255,255,255,255);
			mpSceneNode->getMaterial(i).DiffuseColor.set(255,255,255,255);
			mpSceneNode->getMaterial(i).Shininess = 1.0f;
		}

		// Add this to my own collision manager, which just keeps track of things
		CollisionMgr()->AddToEntityCollision(mpMesh,mpSceneNode);
	}

	core::aabbox3d<f32> cubebox = mpSceneNode->getBoundingBox();
	Vec3f cuberadius = cubebox.MaxEdge - cubebox.getCenter();

	scene::ISceneNodeAnimator* pCubeAnim = gpSceneManager->createCollisionResponseAnimator(
		CollisionMgr()->GetLevelCollision(), mpSceneNode,
		cuberadius,
		Vec3f(0,-0.5,0),
		Vec3f(0,0,0), 100.0f);

	mpSceneNode->addAnimator(pCubeAnim);
	pCubeAnim->drop();
}
So, as I said, if I don't add the collision animator in the init function, but instead wait until after I've parsed and set the initial position from the XML, it works. But (A) It's a messy way of doing things, and (B) I'd like to know where in Irrlicht it stops me from changing the position of things before I've even hit the main loop (before I've even finished parsing the data files and creating objects, in fact). Because I don't like it when code does stuff (or won't let me do stuff) behind my back when I don't know how or why.

Any ideas?
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

I don't understand why you are not looking at the source code or at the very least setting a breakpoint in CSceneNodeAnimatorCollisionResponse::animateNode(). You would get the answer a lot quicker than asking on the forums, and you'd learn about how the Irrlicht collision system works.

When the collision response animator is created, the following lines are executed...

Code: Select all

if (Object)
    LastPosition = Object->getPosition();
This caches the object position at the time the animator is created. As you already know the value of LastPosition is going to be (0, 0, 0). Then in the animateNode() function, the following line is executed...

Code: Select all

core::vector3df pos = Object->getPosition();
core::vector3df vel = pos - LastPosition;
Here the animator figures out how far the object has travelled since the last tick. Since LastPosition was (0, 0, 0) and the object new position is somewhere else completely different, the vector is rather large. This vector is later used to do collision testing, and wow, guess what, your node collides with stuff.

Travis
ElectroDruid
Posts: 23
Joined: Thu Oct 21, 2004 3:55 pm

Post by ElectroDruid »

*sound of penny dropping*

Right, now it makes sense :D Thanks for illuminating me.

So, I will have to move the bit that creates the collision response animator out into another function, to be called only when everything else about the object has been set up. So, the Init() function sets up the scene node, mesh, default position etc, the XML dictates any optional over-rides to position, rotation, etc, and then when the tag closes and we know all the information is complete (ie, somewhere just before I call my Register() function), call a new method on my entity, called SetupCollisionAnimator() or whatever.

*shrug* it's not exactly how I wanted to do things, but I can see now that there's a good reason for doing things that way. Thanks again! :)
Post Reply