PyODE and mesh data?

saluk

18-10-2005 22:56:41

Ak, so I am trying to get ode working in my test application. I have objects able to move around just fine with ODE forces. Now I am trying to add collision detection. Is there any nice way to get the mesh data in order to build TriMeshes to tell pyODE where the collisions are occuring? I can't seem to find any way to get at it.

Great work on the binding so far, it seems to have the good parts of both ogreland and pythonland.

Clay

18-10-2005 23:05:27

I'm not certain how to do this because I've never used ODE. Do you have an example of how to do it in C++ that I could try to translate into python?

saluk

19-10-2005 04:38:32

I don't know c++, but what pyODE wants is something like this:


myspace = ode.Space() #space to put collision objects
meshdata = ode.TriMeshData() #create the data buffer
meshdata.build(verts,faces) #Put vertex and face data into the buffer
mesh = ode.GeomTriMesh(meshdata,myspace) #create collide mesh


verts would be a list of points:

verts = [(0,0,1),(0,0,10),(-5,2.5,3.2)] #Each point has x,y,z

and faces would be a list of faces:

faces = [(0,1,2)] #each face has index of pt1, index pt2, index pt3


What I need is a way to get at those verts and faces which are stored somewhere in the ogre meshes.

Clay

19-10-2005 07:13:55

Ah ok. This is not yet possible in pyogre. I have to wrap all of this manually because Ogre likes to do this with void pointers for raw data. I'll have to set aside some time to figure out the best way of doing this.

saluk

19-10-2005 11:13:47

Ok, thanks for the response. I'll wait for now then, but if I get impatient I'll write it the other way (using the intermediate xml mesh data to construct collisions).

Actually that might not be a bad way to go anyway :) Except that keeping around two versions of meshdata is a lot of space.

Many decisions...

I'll let you all know if I get it working!

Clay

19-10-2005 18:32:15

Ah ok, let me know how that turns out. Even if you get it working that way it's still on my todo list.

saluk

20-10-2005 02:37:07

Just a little progress report in case anyone else is trying to do the same thing. I wrote up the importer to read in the vert/face information from the mesh.xml files that the blender2ogre mesh exporter creates. I have since discovered how easy it is to KILL the framerate when there is collision detection going on :O

I can only load about 20 objects out of the 140 in my scene before the framerate goes down to about 2fps. Next step - limit collision detection to the terrain only :)

Here is the mesh import code:


from elementtree import ElementTree

def get_verts_faces_from_xml(path):
"""Returns vert,face data from the xml model file"""
#print dir(ElementTree)
tree = ElementTree.parse(path)
root = tree.getroot()
submeshes = root.find("submeshes").getchildren()
facedata = []
vertdata = []
for submesh in submeshes:
faces = submesh.find("faces").getchildren()
vertexbuffers = submesh.find("geometry").getchildren()
for face in faces:
indices = [int(face.get(x)) for x in ["v1","v2","v3"]]
facedata.append(tuple(indices))
for vb in vertexbuffers:
if "positions" in vb.keys():
verts = vb.getchildren()
for v in verts:
pos = v.find("position")
xyz = [eval(pos.get(x)) for x in "xyz"]
vertdata.append(tuple(xyz))
return vertdata,facedata


needs elementtree to work.

I haven't actually tested that collision detection is even working, but it's definately trying hard :)

Clay

20-10-2005 04:34:43

You should only be doing collision detection a couple of times per second, not every frame. This should speed things up. Also if you are using the terrain scene manager there is an easier way to do collision detection (using rayscenequery shot downwards). I know there's a LOT of tricks involved to make physics not kill your framerate, but I don't know any of them. =)

saluk

20-10-2005 05:35:18

Yeah, I know there are nicer ways, I always like to get things working before I optomize. Everything seems to be set up right, but it doesnt want to register collisions. I have seen similar posts on the pyODE forums regarding trimeshes, so I'll keep at it.

I impored all of my assets from an existing game, so some things (like terrain) probably would be a better fit if remade or altered to better suit the graphics environment.

Thanks for the tip on staggering the physics across several frames. I'm sure it will come in handy if I ever get them to work right :)

I'll keep experimenting...

dermont

20-10-2005 09:05:47

Have you you tried out a simpler approach to testing collision detection such as creating primitives e.g. GeomBox's for your entities/bodies using the dimensions from the entity bounding box. That's what I did when evaluating collision detection for pyOde/pyOgre i.e.


box = mEntity.boundingBox
min = box.minimum
max = box.maximum
centre = box.getCenter()
size = ogre.Vector3(ogre.Math.Abs(max.x-min.x),ogre.Math.Abs(max.y-min.y),ogre.Math.Abs(max.z-min.z))

and showing the bounding boxes:

sceneManager.showBoundingBoxes=True


The bounding box dimensions are larger than that of the entity but at least you will be able to roughly test collision detection.

As far as I remember with ode, collision detection is performed between each GeomTriMeshes in the same space which may account for your frame rate problems. You may want to look at setCollideBits and/or combining all your static GeomTriMeshes into one TriMesh and using primitives such as boxes,spheres etc for dynamic bodies.

Do a search for OgreOde (ode wrapper for Ogre) on the main forum. You'll probably pick up some useful tips and info there.

esteban

30-10-2005 08:18:23

I have hacked together a quick and dirty solution to this specific problem. I hope others will find this useful while a proper python wrap of the needed Ogre functionality isn't ready.

I solved the immediate problem by adding a function to the pyogre.ogre module that takes a mesh and returns a tuple of (vertices, triangles), as described by saluk.


verts would be a list of points:
verts = [(0,0,1),(0,0,10),(-5,2.5,3.2)] #Each point has x,y,z

and faces would be a list of faces:

faces = [(0,1,2)] #each face has index of pt1, index pt2, index pt3


I started with method OgreOdeMeshInformer::addMesh:

http://cvs.sourceforge.net/viewcvs.py/ogre/ogreaddons/ogreode/src/OgreOdeMeshInformer.cpp?rev=1.1&view=markup

Rather than trying to expose all the Ogre API calls needed for this in a pythonic manner, I just repurposed this function to be PyOgre-friendly, then added it to my local copy of the PyOgre project. If you are interested and you need help doing this (or if you are as lazy as me and just want the ogre.py and _ogre.pyd :wink:) I'll be glad to help.

This is the revised function. It could maybe be faster if I created the list with the actual size to begin with, rather than appending the data, but I do mesh loading as a preprocess so it doesn't matter enough to me to try what's faster.


PyObject* verticesAndTrianglesFromMesh(Ogre::MeshPtr& mesh, const Ogre::Vector3& position, const Ogre::Quaternion& orient, const Ogre::Vector3& scale)
{
bool added_shared = false;

size_t current_offset = 0, shared_offset = 0, next_offset = 0;

PyObject* vertices = PyList_New(0);
PyObject* triangles = PyList_New(0);

// Run through the submeshes, adding the data into the PyList
for(int i = 0;i < mesh->getNumSubMeshes();i++)
{
Ogre::SubMesh* submesh = mesh->getSubMesh(i);

Ogre::VertexData* vertex_data = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData;
if((!submesh->useSharedVertices)||(submesh->useSharedVertices && !added_shared))
{
if(submesh->useSharedVertices)
{
added_shared = true;
shared_offset = current_offset;
}

const Ogre::VertexElement* posElem = vertex_data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);
Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(posElem->getSource());
unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
Ogre::Real* pReal;

for(size_t j = 0; j < vertex_data->vertexCount; ++j, vertex += vbuf->getVertexSize())
{
posElem->baseVertexPointerToElement(vertex, &pReal);

Ogre::Vector3 pt;

pt.x = (*pReal++);
pt.y = (*pReal++);
pt.z = (*pReal++);

pt = (orient * (pt * scale)) + position;

PyList_Append(vertices, Py_BuildValue("ddd", pt.x, pt.y, pt.z));
}
vbuf->unlock();
next_offset += vertex_data->vertexCount;
}

Ogre::IndexData* index_data = submesh->indexData;

size_t numTris = index_data->indexCount / 3;
unsigned short* pShort;
unsigned int* pInt;
Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer;
bool use32bitindexes = (ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT);
if (use32bitindexes) pInt = static_cast<unsigned int*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
else pShort = static_cast<unsigned short*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));

for(size_t k = 0; k < numTris; ++k)
{
size_t offset = (submesh->useSharedVertices)?shared_offset:current_offset;

int a, b, c; // triangle vertex indices
unsigned int vindex = use32bitindexes? *pInt++ : *pShort++;
a = vindex + offset;
vindex = use32bitindexes? *pInt++ : *pShort++;
b = vindex + offset;
vindex = use32bitindexes? *pInt++ : *pShort++;
c = vindex + offset;
PyList_Append(triangles, Py_BuildValue(use32bitindexes?"iii":"hhh", a, b, c));

}
ibuf->unlock();
current_offset = next_offset;
}
return Py_BuildValue("OO", vertices, triangles);
}


HTH,

Esteban.

Clay

30-10-2005 17:06:19

Very nice. I'll use this as a starting point when I try to solve it for the general case. Sorry work is piling up around here. I have to take care of a few projects before I can clear out a few more bugs.

esteban

30-10-2005 18:31:47

If you want to iterate, in Python, over the submeshes and get their vertex/triangle data separately, you can use this slightly modified version:


PyObject* verticesAndTrianglesFromSubMesh(Ogre::MeshPtr& mesh, Ogre::SubMesh* submesh, const Ogre::Vector3& position, const Ogre::Quaternion& orient, const Ogre::Vector3& scale)
{
PyObject* vertices = PyList_New(0);
PyObject* triangles = PyList_New(0);

Ogre::VertexData* vertex_data = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData;

const Ogre::VertexElement* posElem = vertex_data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);
Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(posElem->getSource());
unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
Ogre::Real* pReal;

for(size_t j = 0; j < vertex_data->vertexCount; ++j, vertex += vbuf->getVertexSize())
{
posElem->baseVertexPointerToElement(vertex, &pReal);

Ogre::Vector3 pt;

pt.x = (*pReal++);
pt.y = (*pReal++);
pt.z = (*pReal++);

pt = (orient * (pt * scale)) + position;

PyList_Append(vertices, Py_BuildValue("ddd", pt.x, pt.y, pt.z));
}
vbuf->unlock();

Ogre::IndexData* index_data = submesh->indexData;

size_t numTris = index_data->indexCount / 3;
unsigned short* pShort;
unsigned int* pInt;
Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer;
bool use32bitindexes = (ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT);
if (use32bitindexes) pInt = static_cast<unsigned int*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
else pShort = static_cast<unsigned short*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));

for(size_t k = 0; k < numTris; ++k)
{
unsigned int a = use32bitindexes? *pInt++ : *pShort++;
unsigned int b = use32bitindexes? *pInt++ : *pShort++;
unsigned int c = use32bitindexes? *pInt++ : *pShort++;
PyList_Append(triangles, Py_BuildValue(use32bitindexes?"iii":"hhh", a, b, c));

}
ibuf->unlock();
return Py_BuildValue("OO", vertices, triangles);
}


I haven't looked at what is missing to be able to do this from pure PyOgre (for one thing, SubMesh isn't wrapped in the official trunk as of yet). I'll take a look at this and if it looks like something I can reasonably expect to do I will try and submit a proper patch.

Regards,

Esteban.

P.S.: Thanks to you for your excellent work with PyOgre, Clay!

Clay

30-10-2005 23:02:27

One quick thing, the PyList_Append and Py_BuildValue do not consume references, so you should do this, with tmp is a PyObject *:

PyObject *tmp = Py_BuildValue("ddd", pt.x, pt.y, pt.z);
if (tmp)
{
PyList_Append(vertices, tmp);
Py_DECREF(tmp);
}


Also the return:

tmp = Py_BuildValue("OO", vertices, triangles);
Py_DECREF(verticies);
Py_DECREF(triangles);
return tmp;


The only things that I know of that steal a reference is PyList_SetItem and PyTuple_SetItem, everything else you must DECREF if you create it...with the exception of the return value. So, you called PyList_New(0) for verticies and triangles.... When you add this to the return value (with the Py_BuildValue) their reference count is increased to 2...so you have to decref it before exiting the function.

Clay

06-11-2005 05:10:58

Ok, I modified esteban's code slightly. Now there are two attributes and one function in submesh to get the vertices and indices. To obtain a list of vertices, use the vertices attribute:
print sub_mesh.vertices
[(5.6, 15.2, 5.6), (6.1, 16.1, 4.5), (4.0, 14.7, 6.4), (...)]


To obtain the index list use the indices attribute:
print sub_mesh.indices
[(1, 2, 3), (2, 3, 5), (...)]


If you want to obtain vertex information that is based on the actual location, size, and orientation of the getVertices function:
getVertices(const Vector3 &position=Ogre::Vector3::ZERO,
const Quaternion &orient=Ogre::Quaternion::IDENTITY,
const Vector3 &scale=Ogre::Vector3::UNIT_SCALE);


So, this would get you the vertices based on an Entity "ent" attached to a SceneNode "node":

verts = ent.mesh.subMesh(0).getVertices(node.position, node.orientation, node.scale)

Note that I have not named this vertexData and indexData. The next step is to wrap the VertexData, IndexData, and related classes, so I would like to leave those variables open.

Also, thank you esteban for doing all the hard work. I'd add you to the authors file, but you are already there. =)

esteban

07-11-2005 15:54:57

Good catch on the missing Py_DECREFs! I must admit am not too acquainted with reference managing in CPython.

And to be fair, monster did all the hard work with his OgreOde wrapper. I only did minor modifications to pythonize the function. Glad to help, anyway!

Thanks fo the great work with PyOgre, and keep it up! :D

Esteban.

esteban

08-11-2005 07:50:55

I can confirm this works except for one thing. submesh.indices should be a list of tuples of ints, but they are currently float values. This is trivial to fix; I've submitted a patch to the BerliOS project site.

Regards and thanks again,

Esteban.

Clay

08-11-2005 12:10:35

Oops, fixed in svn. Thanks.

mthorn

15-02-2006 19:47:30

Does someone have a full working example of this to post? In the mean time I'll work on figuring it out.

griminventions

15-02-2006 22:42:33

If you figure it out, please post. :) I'd like to know, as well.

dermont

17-02-2006 03:48:10

You can retrieve the vertices / indices from the sumeshes:

PyObject *getVertices(const Vector3 &position=Ogre::Vector3::ZERO,
const Quaternion &orient=Ogre::Quaternion::IDENTITY,
const Vector3 &scale=Ogre::Vector3::UNIT_SCALE);
}




and something like:

def createStaticTriangleMesh(self,ent,space):

vertdata=[]
facedata=[]
node = ent.parentNode
for i in range(ent.mesh.numSubMeshes):
#if not ent.mesh.getSubMesh(i).useSharedVertices:
for v in ( ent.mesh.getSubMesh(i).getVertices(node.position, node.orientation, node.scale)):
vertdata.append(v)
for f in (ent.mesh.getSubMesh(i).indices):
facedata.append(f)

data = ode.TriMeshData()
data.build(vertdata, facedata)
geom = ode.GeomTriMesh(data, space)
del vertdata
del facedata
return geom

.

track_node = sceneManager.rootSceneNode.createChildSceneNode("track")
track_entity = sceneManager.createEntity("track","RacingCircuit.mesh")
track_node.attachObject(track_entity)

.

geom=createStaticTriangleMesh(track_entity,space)

cradle

11-03-2007 11:59:01

How do I get the above working with Python-Ogre? (I think I'm using "0.80 Candidate", according to setup.py)

Hey all, just like to say you've got an awesome community here, I am really getting a lot out of it. I'm kinda stumped by this one though. I've been bashing my head trying to work out how I implement what is being written in this thread. Do I have to compile the C++ Python bindings myself? Or is it in the latest version of Python-Ogre (PyOgre)?

I think I understand what the code is all doing now, vertex buffers and pointers and whatnot, and I understand why I can't do what you're doing (converting meshes to trimeshes) without C++ code. I even tried to use the OgreODE tools (much to my chagrin, I like the fine grained control in PyODE) to get at the vertex list and indices information, but to no avail.

I've tried to upgrade my app to the newest PythonOgre (0.90a) but it keeps crashing


Traceback (most recent call last):
File "C:\Python25\pyogre\assaultvector\src\client.py", line 572, in <module>
world.go()
File "C:\Python25\pyogre\assaultvector\src\client.py", line 29, in go
if not self._setUp():
File "C:\Python25\pyogre\assaultvector\src\client.py", line 56, in _setUp
self._createScene()
File "C:\Python25\pyogre\assaultvector\src\client.py", line 407, in _createSc
ne
ogre.RENDER_QUEUE_OVERLAY, False, 3000, self.sceneManager)
RuntimeError: unidentifiable C++ exception


Are the updates in the new PythonOgre? Should I persevere with troubleshooting my upgrade to try and get 0.90a working? Or is there something else I have to do (that I may have missed)?

Thanks in advance for any help!

- Glenn

andy

11-03-2007 14:48:26

Could you send a test/minimal program to me that shows the problem and I look at fixing it..

Also you could try the snapshot that's on sourceforge -- 0.9a had fairly poor support for ODE (the OgreODE seems OK, the native ODE was bad) and I made improvements in the snapshot...

Send any test code to the Python-Ogre mailing list (python-ogre-developers@googlegroups.com)

Cheers

Andy

drizzith

05-04-2007 04:43:06

Andy, I has the same problem, when I chose to render with OpenGl, with all demos. I will take a print for you

drizzith

05-04-2007 05:07:28



In That exemple, I try to run the demo "demo_bezier.py".
And, I used the pyogre 0.9a.

andy

05-04-2007 10:07:38

Just to confirm -- all of the demos fail (in some way or another) if you select the GL driver, and they work (??) if you use DirectX ???

If this is the case then I'm guess that the problem is an incompatibility with your card/OpenGl driver and the Ogre GL driver -- if you have time could you download the Ogre 1.4 demos package (from the ogre3d site) and see if their C++ demos work and let me know the result

Cheers
Andy