Bullicht... er... Irrlet... Help?

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
onimatrix
Posts: 5
Joined: Wed Jul 23, 2008 1:31 am

Bullicht... er... Irrlet... Help?

Post by onimatrix »

Hello to all of you, please consider me the eternal newbie, sailing the dark waters of game programming since circa 1990 and still sea sickened. :lol:

I'm currently developing a sim for a brand of educational robots, nothing fancy nor sophisticated, but still... I'm feeling kind of lightheaded.

Being -relatively- new to both Irrlicht and Bullet, I'm having serious difficulties regarding the integration of both engines while learning from ear (Currently loving the Irrlicht docs, but o Bullet... where art thou?).

My questions, should anyone be so kind to give me a hand.

1) What's the format returned by TerrainSceneNode.getMesh()?
For an extra point: How can I make a collisionShape with this? I'm using a heightmap.

2) I got this code, right from the dead hands of the PAL server via Google's cache:

Code: Select all

class BindObject 
{
    public:
	irr::scene::ISceneNode *node;
    
	palBodyBase *pb;
	//update Irrlicht position from physics
	void Update() 
    {
		//get the location matrix from the physics system
		palMatrix4x4 matrix = pb->GetLocationMatrix();
		//copy it to a Irrlicht matrix
		core::matrix4 mat;
		memcpy(&mat[0], matrix._mat, sizeof(f32)*4*4);
		//set the node position from our Irrlicht matrix
		node->setPosition(mat.getTranslation());
		node->setRotation(mat.getRotationDegrees());
	}
};
Seems like a nice way to merge the nodes but, as you can see, it has references to the PAL library (PAL being Physics Abstraction Layer).
Any tips?

And yes, I have tried to use nomad's example of the implementation, only to confirm that it was meant to compile under older versions of the engines. Still, it's a tad complicated for what I want to do.


Thanks to all and beautiful engine,


Oni
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Re: Bullicht... er... Irrlet... Help?

Post by BlindSide »

onimatrix wrote: Bullicht... er... Irrlet...
Funny guy :P
onimatrix wrote: since circa 1990
:shock: Half the people on these forums weren't born by then. (And the other half have been alive for hundreds of years.)

Ok down to business:
onimatrix wrote: 1) What's the format returned by TerrainSceneNode.getMesh()?
Don't really understand what you mean by "format". A pointer to an IMesh is returned. You can go on to access the vertex positions using something like:

Code: Select all

IMeshBuffer* buff = Mesh->getMeshBuffer(0);
for(u32 i = 0;i < buff->getVertexCount();++i)
{
    ((video::S3DVertex2TCoords*)buff->getVertices())[i].Pos; // Do whatever you like with it here.
}
For an extra point: How can I make a collisionShape with this? I'm using a heightmap.
The following is straight out of Nomads tutorial:

Code: Select all

			irr::video::S3DVertex2TCoords* mb_vertices=(irr::video::S3DVertex2TCoords*)mb->getVertices();
			u16* mb_indices = mb->getIndices();
			s32 numVertices = mb->getVertexCount();
			for(j=0;j<mb->getIndexCount();j+=3){ //index into irrlicht data
				for (k=0;k<3;k++) {
					s32 index = mb_indices[j+k];
					if (index > numVertices) goto error;
					vertices[k] = btVector3(mb_vertices[index].Pos.X,mb_vertices[index].Pos.Y,mb_vertices[index].Pos.Z);		
				}
				pTriMesh->addTriangle(vertices[0], vertices[1], vertices[2]);
			}
You can see it's pretty simple, you just access the vertices using the indices provided, and the bullet mesh of type "btTriangleMesh" has a convenient "addTriangle" function. That's pretty much all there is to it, not so scary huh?

Notice that I chose to cast the vertices to "S3DVertex2TCoords" in the previous examples? This is the vertex format heightmaps use, to allow for display of detail maps etc. This is all very irrelevant when it comes to physics as you just want the position.
Any tips?
For positions, it seems bullet has the function "getCenterOfMassPosition();", so you can just update the position of the Irrlicht scene node every frame using this call. Rotation are a bit more tricky, referring back to Nomad's tutorial it seems you have to obtain a quaternion and convert it to eular angles. Another snippet:

Code: Select all

btVector3 btv;
btQuaternion btq=m_pRigidBody->getOrientation();
QuaternionToEulerXYZ(btq, btv);
vector3df v(btv.getX(), btv.getY(), btv.getZ());
v *= GRAD_PI;
Notice how on the last line he had to convert it from radians to degrees? I don't know where he got GRAD_PI from, but in Irrlicht you can use core::RADTODEG, which is just 360/2Pi for the trigonometry impaired.

Ok that's about all for now. Good luck with the robot thing.

Cheers
ShadowMapping for Irrlicht!: Get it here
Need help? Come on the IRC!: #irrlicht on irc://irc.freenode.net
onimatrix
Posts: 5
Joined: Wed Jul 23, 2008 1:31 am

Post by onimatrix »

BlindSide wrote:Funny guy :P
Helpful guy :lol:

I see... That goes there, this does that... Yeah baby!


Thanks for your educational post, my brain has actually swollen while reading it. :)
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Post by BlindSide »

Wait wtf... theres a goto statement in Nomad's snippet that I posted :shock:
ShadowMapping for Irrlicht!: Get it here
Need help? Come on the IRC!: #irrlicht on irc://irc.freenode.net
onimatrix
Posts: 5
Joined: Wed Jul 23, 2008 1:31 am

Post by onimatrix »

Yep, next thing we are going to see it's a wrapper to QBasic4.5 :lol:
onimatrix
Posts: 5
Joined: Wed Jul 23, 2008 1:31 am

Post by onimatrix »

Sorry to bother again, but I'm still having problems and I don't know where to begin.

When compiling this with that line commented, it doesn't check for collisions (Of course, as there is no terrain collision object).
Thing is that, when uncommenting it, I get an unhandled exception :S

Code: Select all

#pragma comment(lib, "Irrlicht.lib")
#pragma comment(lib, "libbulletdynamics.lib")
#pragma comment(lib, "libbulletcollision.lib")
#pragma comment(lib, "libbulletmath.lib")

#include <irrlicht.h>
#include <btBulletDynamicsCommon.h>
#include <iostream>

using namespace std;

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

class MyEventReceiver : public IEventReceiver
{
public:

	MyEventReceiver(ISceneNode* terrain)
	{
		Terrain = terrain;
	}

	bool OnEvent(const SEvent& event)
    {
        if (event.EventType == EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown)
		{
			switch (event.KeyInput.Key)
			{
			    case KEY_KEY_W:
				    Terrain->setMaterialFlag(   EMF_WIREFRAME, 
                                                !Terrain->getMaterial(0).Wireframe);
		        return true;
			}
		}

		return false;
	}

private:
	ISceneNode* Terrain;
};

void QuaternionToEulerXYZ(const btQuaternion &quat,btVector3 &euler)
{
	f32 w=quat.getW();	f32 x=quat.getX();	f32 y=quat.getY();	f32 z=quat.getZ();
	double sqw = w*w; double sqx = x*x; double sqy = y*y; double sqz = z*z; 
	euler.setZ((btScalar)((atan2(2.0 * (x*y + z*w),(sqx - sqy - sqz + sqw)))));
	euler.setX((btScalar)((atan2(2.0 * (y*z + x*w),(-sqx - sqy + sqz + sqw)))));
	euler.setY((btScalar)((asin(-2.0 * (x*z - y*w)))));
}

vector3df GetPosition(btRigidBody *m_pRigidBody)
{
	if (m_pRigidBody == NULL) 
        return vector3df(0,0,0);
    btPoint3 p = m_pRigidBody->getCenterOfMassPosition();
	return vector3df(p.getX(), p.getY(), p.getZ());
}

vector3df GetRotation(btRigidBody *m_pRigidBody)
{
	if (m_pRigidBody == NULL)
        return vector3df(0,0,0);
	btVector3 btv;
	btQuaternion btq=m_pRigidBody->getOrientation();
	QuaternionToEulerXYZ(btq, btv);
 	vector3df v(btv.getX(), btv.getY(), btv.getZ());
	v *= 57.29577951f;
	return v;
}

int main()
{
	IrrlichtDevice* device = createDevice(  EDT_OPENGL, 
                                            dimension2d<s32>(   640,
                                                                480));
	if (device == 0)
		return 1; 

	IVideoDriver* driver = device->getVideoDriver();
	ISceneManager* smgr = device->getSceneManager();
	IGUIEnvironment* env = device->getGUIEnvironment();

	btDefaultCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration();

	btCollisionDispatcher* dispatcher = new	btCollisionDispatcher(collisionConfiguration);

    btVector3 worldAabbMin(-15000,-15000,-15000);
	btVector3 worldAabbMax(15000,15000,15000);
	int	maxProxies = 1024;
	btAxisSweep3* overlappingPairCache = new btAxisSweep3(worldAabbMin,worldAabbMax,maxProxies);

    btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;

	btDiscreteDynamicsWorld* dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);

	dynamicsWorld->setGravity(btVector3(0, -9.81f, 0));

    driver->setTextureCreationFlag( ETCF_ALWAYS_32_BIT, 
                                    true);

    ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS( 0,
                                                            100.0f,
                                                            1200.f);

	camera->setPosition(vector3df(  0,
                                    1000,
                                    0));
	camera->setTarget(vector3df(    1500,
                                    0,
                                    1500));

    camera->setFarValue(12000.0f);

	device->getCursorControl()->setVisible(false);

        ITerrainSceneNode* terrain = smgr->addTerrainSceneNode(
        ".\\heightmap.bmp",
		0,										
		-1,										
		vector3df(  0.f, 
                    0.f,
                    0.f),			

		vector3df(  0.f,
                    0.f,
                    0.f),			
		vector3df(  40.f,
                    4.4f,
                    40.f),		
		SColor (    255,
                    255, 
                    255,
                    255 ),	
		5,										
		ETPS_17,							
		4										
		);

    IMesh *terrainMesh = terrain->getMesh();
    IMeshBuffer* mb = terrainMesh->getMeshBuffer(0);
    S3DVertex2TCoords* mb_vertices=(S3DVertex2TCoords*)mb->getVertices();
    u16* mb_indices = mb->getIndices();
    s32 numVertices = mb->getVertexCount();
    
    btVector3 vertices[3];
	
    btTriangleMesh *pTriMesh = new btTriangleMesh();

    for(u16 j = 0; j < mb->getIndexCount(); j += 3)
    { 
       for (s16 k = 0; k < 3; k++) 
       {
          s32 index = mb_indices[j+k];
          if (index > numVertices) 
            continue;      
          vertices[k] = btVector3(mb_vertices[index].Pos.X,mb_vertices[index].Pos.Y,mb_vertices[index].Pos.Z);      
       }
       pTriMesh->addTriangle(vertices[0], vertices[1], vertices[2]);
    } 

    btCollisionShape* terrainShape = new btBvhTriangleMeshShape(pTriMesh, true, false);

    btAlignedObjectArray<btCollisionShape*> collisionShapes;

   	collisionShapes.push_back(terrainShape);

	btTransform terrainTransform;
	terrainTransform.setIdentity();
	terrainTransform.setOrigin(btVector3(0, 0, 0));

    {
	    btScalar mass(0.);

	    bool isDynamic = (mass != 0.f);

	    btVector3 localInertia(0, 0, 0);
	    if (isDynamic)
		    terrainShape->calculateLocalInertia(mass,localInertia);

	    btDefaultMotionState* myMotionState = new btDefaultMotionState(terrainTransform);
	    btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,terrainShape,localInertia);
	    btRigidBody* body = new btRigidBody(rbInfo);

	    dynamicsWorld->addRigidBody(body);

        btCollisionObject * collisionTerrain = new btCollisionObject();
        collisionTerrain->setCollisionShape(terrainShape);
//        dynamicsWorld->addCollisionObject(collisionTerrain);
    }

    terrain->setMaterialTexture(0,
                                driver->getTexture(".\\grass.bmp"));
    
    terrain->scaleTexture(20);
   
    terrain->setMaterialType(EMT_SOLID);
    
    terrain->setMaterialFlag(   EMF_LIGHTING, 
                                false);

    ITriangleSelector* selector = smgr->createTerrainTriangleSelector(  terrain,
                                                                        0);
	terrain->setTriangleSelector(selector);

	ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
		selector, 
        camera, 
        vector3df(  60,
                    100,
                    60),
		vector3df(  0,
                    0,
                    0), 
		vector3df(  0,
                    50,
                    0));
	
    selector->drop();
	camera->addAnimator(anim);
	anim->drop();

	MyEventReceiver receiver(terrain);
	device->setEventReceiver(&receiver);

	int lastFPS = -1;

    btRigidBody* sphereBody; 

    btCollisionShape* colShape = new btSphereShape(btScalar(100.));
	collisionShapes.push_back(colShape);

	btTransform startTransform;
	startTransform.setIdentity();

	btScalar	mass(1.f);

	bool isDynamic = (mass != 0.f);

	btVector3 localInertia(0,0,0);

    if (isDynamic)
		colShape->calculateLocalInertia(mass,localInertia);

	startTransform.setOrigin(btVector3(2000, 1000, 2000));

	btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);
	btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,colShape,localInertia);
	sphereBody = new btRigidBody(rbInfo);
	dynamicsWorld->addRigidBody(sphereBody);
    
    btCollisionObject * collisionSphere = new btCollisionObject();
    collisionSphere->setCollisionShape(colShape);
    dynamicsWorld->addCollisionObject(collisionSphere, 1, 1);

    ISceneNode *sphere = smgr->addSphereSceneNode(100, 64, 0, -1, vector3df(0, 0, 0), vector3df(0, 0, 0)); 

    sphere->setMaterialTexture(0,
                                driver->getTexture(".\\grass.bmp"));
    
    sphere->setMaterialType(EMT_SPHERE_MAP);
    
    sphere->setMaterialFlag(   EMF_LIGHTING, 
                               false);

    while(device->run())
	    if (device->isWindowActive())
	    {
            dynamicsWorld->stepSimulation(.166666f, 2);
            sphere->setPosition(GetPosition(sphereBody));
            sphere->setRotation(GetRotation(sphereBody));

            driver->beginScene( true,
                                true,
                                0 );

		    smgr->drawAll();
		    env->drawAll();

		    driver->endScene();

		    int fps = driver->getFPS();
		    if (lastFPS != fps)
		    {
			    stringw str = L"Virtual Sarasa [";
			    str += driver->getName();
			    str += "] FPS:";
			    str += fps;
			    str += " Height: ";
			    str += terrain->getHeight(camera->getAbsolutePosition().X, camera->getAbsolutePosition().Z);

			    device->setWindowCaption(str.c_str());
			    lastFPS = fps;
		    }
	    }

    for (s8 i=dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--)
	{
		btCollisionObject* obj = dynamicsWorld->getCollisionObjectArray()[i];
		btRigidBody* body = btRigidBody::upcast(obj);
		if (body && body->getMotionState())
		{
			delete body->getMotionState();
		}
		dynamicsWorld->removeCollisionObject( obj );
		delete obj;
	}

	for (int j=0;j<collisionShapes.size();j++)
	{
		btCollisionShape* shape = collisionShapes[j];
		collisionShapes[j] = 0;
		delete shape;
	}

	delete dynamicsWorld;

	delete solver;

	delete overlappingPairCache;

	delete dispatcher;

	delete collisionConfiguration;

	collisionShapes.clear();

	device->drop();
	
	return 0;
}


Also, please don't mind my coding style as this is just a prototype xD


Hope to get some help, thanks to all of you =)
Last edited by onimatrix on Tue Jul 29, 2008 6:47 pm, edited 2 times in total.
onimatrix
Posts: 5
Joined: Wed Jul 23, 2008 1:31 am

Post by onimatrix »

- Why is nobody helping me?
If you want people to help you, you must put as much effort in to your question as you expect from an answer. Your question must be interesting and show that you are not being lazy but genuinely need help.
Explain what you have tried, what happens and how this is different from what you expect to happen. Also explain what you are trying to do, because you might be doing it the wrong way. Screenshots might also help, you can upload them to Image Shack or another one-click image host.
If you asked your question the smart way and still get no answers, then you can assume nobody knows the answer.
So this wasn't the smart way, right? :lol:
(Or this isn't a Bullet forum :P )

I'm sorry, I got anxious and internets, here in the third world, don't grow from trees :(

Let me ask this in a better way please:


When I create a terrainShape using btBvhTriangleMeshShape and then I make a btRigidBody out of it, I get an unhandled exception in btOptimizedBvh::reportAabbOverlappingNodex.

The code is up there, and I have been trying to figure out why is this happening with no success whatsoever.

I did as BlindSide told me, as you can see. terrainShape should be filled correctly, right?
Then I do steps that worked alright when making the sphere (It falls through the terrain!).

So... where am I screwing this? :?


Sorry again for my last post, I should be locked away in a mental institution or something...

Following, the piece of code from the creation of the SceneNode to that f***ing line:

Code: Select all

        ITerrainSceneNode* terrain = smgr->addTerrainSceneNode(
        ".\\heightmap.bmp",
		0,										
		-1,										
		vector3df(  0.f, 
                    0.f,
                    0.f),			

		vector3df(  0.f,
                    0.f,
                    0.f),			
		vector3df(  40.f,
                    4.4f,
                    40.f),		
		SColor (    255,
                    255, 
                    255,
                    255 ),	
		5,										
		ETPS_17,							
		4										
		);

    IMesh *terrainMesh = terrain->getMesh();
    IMeshBuffer* mb = terrainMesh->getMeshBuffer(0);
    S3DVertex2TCoords* mb_vertices=(S3DVertex2TCoords*)mb->getVertices();
    u16* mb_indices = mb->getIndices();
    s32 numVertices = mb->getVertexCount();
    
    btVector3 vertices[3];
	
    btTriangleMesh *pTriMesh = new btTriangleMesh();

    for(u16 j = 0; j < mb->getIndexCount(); j += 3)
    { 
       for (s16 k = 0; k < 3; k++) 
       {
          s32 index = mb_indices[j+k];
          if (index > numVertices) 
            continue;      
          vertices[k] = btVector3(mb_vertices[index].Pos.X,mb_vertices[index].Pos.Y,mb_vertices[index].Pos.Z);      
       }
       pTriMesh->addTriangle(vertices[0], vertices[1], vertices[2]);
    } 

    btCollisionShape* terrainShape = new btBvhTriangleMeshShape(pTriMesh, true, false);

    btAlignedObjectArray<btCollisionShape*> collisionShapes;

   	collisionShapes.push_back(terrainShape);

	btTransform terrainTransform;
	terrainTransform.setIdentity();
	terrainTransform.setOrigin(btVector3(0, 0, 0));

    {
	    btScalar mass(0.);

	    bool isDynamic = (mass != 0.f);

	    btVector3 localInertia(0, 0, 0);
	    if (isDynamic)
		    terrainShape->calculateLocalInertia(mass,localInertia);

	    btDefaultMotionState* myMotionState = new btDefaultMotionState(terrainTransform);
	    btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,terrainShape,localInertia);
	    btRigidBody* body = new btRigidBody(rbInfo);

	    dynamicsWorld->addRigidBody(body);

        btCollisionObject * collisionTerrain = new btCollisionObject();
        collisionTerrain->setCollisionShape(terrainShape);
//        dynamicsWorld->addCollisionObject(collisionTerrain);
    }
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Post by BlindSide »

Hmm, I do recall a problem with stuff falling through concave meshes.

From the context of Nomad's tutorial, omaremad suggested this fix:
Btw the bug mention by Blindside is due to a cast which causes a copy constructor being called rather than the constructor (im not sure), maybe this happened after an api change in bullet.

To correct it use this line instead

Code: Select all

//Terrain - optimized static mesh
CTerrain::CTerrain(const char* name)
{
   GET_SMGR;
   m_pMesh = smgr->getMesh(name); //returns a IAnimatedMesh*, not a IMesh* as docs say
   m_pNode = smgr->addOctTreeSceneNode(m_pMesh->getMesh(0));
   m_pNode->setMaterialFlag(video::EMF_LIGHTING, false);
   m_Mass = 0.0f;
   m_pCollisionObject=g_pCollisionObjectMgr->Get((void*)m_pMesh);
   if (!m_pCollisionObject) {
      btTriangleMesh* pMesh = GetTriangleMesh(m_pMesh->getMesh(0));
      btCollisionShape* pShape = new btBvhTriangleMeshShape((btStridingMeshInterface*)pMesh,true);
      m_pCollisionObject=g_pCollisionObjectMgr->Add(pShape, 0, (void*) m_pMesh);
   }
   InitPhysics(GetMaxExtent() / 2);
}
About your crash, I recall experiencing it myself using MSVC 2005 when I ran the application outside of the dev environment and dynamic triangle mesh object. (Static concave triangle meshes and cubes, spheres, etc worked fine however.). I never really found a solution though, is this the case for you?

I am going to be working on a project that uses Bullet soon, so if I find any solutions along the way (And produce a more usable source snippet), then I'll be sure to let you know.
ShadowMapping for Irrlicht!: Get it here
Need help? Come on the IRC!: #irrlicht on irc://irc.freenode.net
m506
Posts: 12
Joined: Wed Jun 18, 2008 5:34 am

Post by m506 »

have you found by chance a solution for this?

I am under a similar issue and would appreciate any tips.

Thanks
Post Reply