background:
in Panda3D 1.72 (not sure about newer version of Panda), the getElevation function doesn’t return accurate height.
collision detection may be the only way to get accurate value.
unlike collision detection, this algorithm can be called anytime/anywhere in a Python program.
before calling this function, some data must be prepared:
BS=int(blockSize) # GeoMipTerrain blockSize, must be int here
scaleX,scaleY,scaleZ=?? # terrain scale for gfx render, must be float, not int
minX,minY,maxX,maxY=?? # bound box , 4 values in world coordinate
HF=Terrain.heightfield(); tex=Texture(); tex.load(HF)
imgData=tex.getUncompressedRamImage().getData(); imgXSize=HF.getXSize()
this function assumes there is only one terrain object, and the terrain is not rotated:
def getGroundHeight(worldPos):
if minX <= worldPos.x < maxX and minY <= worldPos.y < maxY:
# for simplicity, if at max XY of a terrain, don't run
posX,posY=( worldPos.x - terrainPos.x ) / scaleX, ( worldPos.y - terrainPos.y ) / scaleY
# pos in img
posXint,posYint=int(posX), int(posY)
X,Y=posX-posXint, posY-posYint # pos in pixel
split=abs(bool( (posX-posXint/BS*BS)/BS > 0.5)-bool( (posY-posYint/BS*BS)/BS > 0.5 ))
# direction of splitting a square
rows=imgXSize * posYint
if split:
if X>1-Y: upper=1; c=posXint + rows + 1 ; b = c + imgXSize ; a=b-1
else: upper=0; a=posXint + rows ; b , c = a+1 , a + imgXSize
else:
if Y>X: upper=1; c=posXint + rows ; a = c + imgXSize ; b = a+1
else: upper=0; a=posXint + rows ; b= a+1 ; c= b + imgXSize
d=[ord(imgData[a]),ord(imgData[b]),ord(imgData[c])]; d+=[d[1]-d[0], d[2]-d[1-abs(split-upper)]]
return scaleZ * (d[3] * X + d[0] + d[4] * (upper - Y * (upper * 2 - 1))) / 255 + terrainPos.z
return 'no ground'