Thrakbad
15-11-2009 11:50:31
Is there any way to implement
raycasting to the polygon level, like you can in c++. I looked at the code and at some point it contains pointer-magic, so I figured this would be hard to do with Python? Has anyone implemented such a thing with pyOgre?
If you want to take a go at an initial conversion (basically make a complete demo -- as much as possible) I'll be happy to help complete it..
There isn't anything I see in the C++ code that stops it being converted to python -- just need to use some ctypes buffers etc - a bit like Demo_Bezier and Demo_Grass..
Regards
Andy
Thrakbad
15-11-2009 21:19:38
Ah I don't know that much about ctypes. But I'll have a look in the two Demos you mentioned and give it a try
Thrakbad
16-11-2009 15:33:47
I took a shot at a conversion of the code and it seems to work nicely. Except when I try to select a small object, that is fully placed within a larger object's BB. Dunno why that doesn't seem to work. Maybe you can have a look at it and see if I missed something.
def RayCast(self, ray):
# variable to hold resulting entity
closestEntity = None
# variable to hold point of hit
closestResult = None
# create a query object
raySceneQuery = self.sceneManager.createRayQuery(ray)
raySceneQuery.setSortByDistance(True)
# execute the query, returns a vector of hits
result = raySceneQuery.execute();
if len(result) == 0:
# raycast did not hit an objects bounding box
return None;
# at this point we have raycast to a series of different objects bounding boxes.
# we need to test these different objects to see which is the first polygon hit.
# there are some minor optimizations (distance based) that mean we wont have to
# check all of the objects most of the time, but the worst case scenario is that
# we need to test every triangle of every object.
closestDistance = -1.0
for item in result:
# stop checking if we have found a raycast hit that is closer
# than all remaining entities
if closestDistance >= 0.0 and closestDistance < item.distance:
print "stopped"
print item.distance
print closestDistance
break
# only check this result if its a hit against an entity
if item.movable and item.movable.getMovableType() == "Entity":
if item.movable.getName() == self.groundName:
continue
# get the entity to check
entity = self.sceneManager.getEntity(item.movable.getName())
# get the buffers from the mesh
buffers = self.GetMeshInformation(entity.getMesh())
vertexBuffer = buffers[0]
indexBuffer = buffers[1]
# get the world position, orientation (as a Matrix) and scale
position = entity.getParentNode()._getDerivedPosition()
orientation = entity.getParentNode()._getDerivedOrientation()
matOrient = ogre.Matrix3()
orientation.ToRotationMatrix(matOrient)
scale = entity.getParentNode()._getDerivedScale()
# Now test for hitting individual triangles on the mesh
newClosestFound = False
# get the positions of the vertices from the vertex buffer
# three vertices each form a triangle
triangle = []
i = 1
for vertexNum in indexBuffer:
startIndex = vertexNum * 8
pt = ogre.Vector3(vertexBuffer[startIndex], vertexBuffer[startIndex + 1], vertexBuffer[startIndex + 2])
# factor in the world position, orientation and scale
pt = (matOrient * (pt * scale)) + position
triangle.append(pt)
if i % 3 == 0:
# check for a hit against this triangle
hit = ogre.Math.intersects(ray, triangle[0], triangle[1], triangle[2], True, False)
# if it was a hit check if its the closest
if hit.first:
print "New closest hit at: "
print str(triangle[0].x) + " " + str(triangle[0].y) + " " + str(triangle[0].z)
print str(triangle[1].x) + " " + str(triangle[1].y) + " " + str(triangle[1].z)
print str(triangle[2].x) + " " + str(triangle[2].y) + " " + str(triangle[2].z)
if closestDistance < 0.0 or hit.second < closestDistance:
# this is the closest so far, save it off
closestDistance = hit.second
newClosestFound = True
# reset the triangle
triangle = []
i = i + 1
# if we found a new closest raycast for this object, update the
# closestResult and closestEntity before moving on to the next object.
if newClosestFound:
closestEntity = entity
closestResult = ray.getPoint(closestDistance)
#destroy the query
self.sceneManager.destroyQuery(raySceneQuery)
# return the result
return [closestEntity, closestResult]
# ***** def RayCast(self, ray)
def GetMeshInformation(self, mesh):
addedShared = False
current_offset = 0
shared_offset = 0
next_offset = 0
index_offset = 0
vertexCount = 0
indexCount = 0
# Calculate how many vertices and indices we're going to need
numSubMeshes = mesh.getNumSubMeshes()
for i in range(0, numSubMeshes):
submesh = mesh.getSubMesh(i)
# We only need to add the shared vertices once
if submesh.useSharedVertices:
if not addedShared:
vertexCount += mesh.sharedVertexData.vertexCount
addedShared = True
else:
vertexCount += submesh.vertexData.vertexCount
# Add the indices
indexCount += submesh.indexData.indexCount
addedShared = False;
# Run through the submeshes again, adding the data into the arrays
for i in range(0, numSubMeshes):
submesh = mesh.getSubMesh(i)
vertexData = None
if submesh.useSharedVertices:
vertexData = mesh.sharedVertexData
else:
vertexData = submesh.vertexData
if not submesh.useSharedVertices or (submesh.useSharedVertices and not addedShared):
if submesh.useSharedVertices:
addedShared = True
shared_offset = current_offset
# retrieve index buffer for this submesh
indexData = submesh.indexData;
numTris = indexData.indexCount / 3;
ibuf = indexData.indexBuffer;
pointer = ibuf.lock(ogre.HardwareBuffer.HBL_READ_ONLY)
indexBuffer = None;
if bool(ibuf.getType() == ogre.HardwareIndexBuffer.IT_32BIT):
indexBuffer = ogre.getUint32(pointer, indexCount)
else:
indexBuffer = ogre.getUint16(pointer, indexCount)
ibuf.unlock()
# retrieve vertex buffer for this submesh
posElem = vertexData.vertexDeclaration.findElementBySemantic(ogre.VES_POSITION)
vbuf = vertexData.vertexBufferBinding.getBuffer(posElem.getSource())
pointer = vbuf.lock(ogre.HardwareBuffer.HBL_READ_ONLY)
# There are 8 float entries for each vertex in the buffer
# 3 for position, 3 for normal, 2 for texture coordinate.
# We only need the position.
vertexBuffer = ogre.getFloat(pointer, vertexCount * 8)
vbuf.unlock()
return [vertexBuffer, indexBuffer]
# ***** def GetMeshInformation(self, mesh, position, rotation, scale)
EDIT: I updated the code so that there are only two iterations throught the vertex buffer, might make it a tiny bit faster.
Thrakbad
17-11-2009 14:15:35
I think there is something wrong with the orientation calculation. Rotated objects don't seem to register correct hits. In the original code the orientation was given as a Quaternion, but that didn't bring correct results either, so I changed it to a rotation matrix. Afaik about 3D-Math that formula should give me the right results. I'm a bit stuck here.
pt = (matOrient * (pt * scale)) + position