Adding objects to Roaming Ralph's world

Hi.

I want to add objects to the world of Roaming Ralph and I want to randomize the XY position of the objects.

How can I get the Z coordinate of any given XY point in the “world” ??

Best regards,
KK

You must place a temporary object with a CollisionRay at the given point and check it for collisions. Just exactly as in Ralph’s case.

i really sugest you change the terrain to a Geomipmap one, then you only have to use the built-in getElevation(x,y) function

Hi guys.

I’ve created the CollisionRay at the given xy point, attached it to my object and it points down to the terrain. How can I get the Z coordinate to move my object down to the ground?

I’m a total newby, so sorry for the silly questions :confused:

Create a collision handler, attach to traverser, traverse once and get the collision entry from the handler (unless you use a Floor handler, in which case it gets done automatically, I guess)
Roaming Ralph shows how to achieve this.

I implemented it creating a node with a collisionray pointing down, and a personal traverser; when i load the level, i place the collision node at random x, y position, call traverser.traverse(render) and get the x from the cEntry with a cHandlerQueue.
Then i place at that position the model I want.
This metod creates the nodes:

def createRayPutter(self):
        #this node serves to put elements at the right height
        self.cDummy=self.model.attachNewNode(CollisionNode("cDummy"))
        self.floorRay=CollisionRay()
        self.floorRay.setDirection(0,0,-1)
        self.floorRay.setOrigin(0, 0, 100)
        self.cDummy.node().addSolid(self.floorRay)
        self.cDummy.node().setIntoCollideMask(BitMask32.allOff())
        self.cDummy.node().setFromCollideMask(BitMask32.bit(0))
        self.cHandler=CollisionHandlerQueue()
        self.cTrav=CollisionTraverser()
        self.cTrav.addCollider(self.cDummy, self.cHandler)

Then I use another metod to do the rest:

def putBush(self):
        for x in range(1000):
            holder=loader.loadModel("Models/grassBlade")
            holder.reparentTo(self.flattenHolder)
            x=random.random()*300-150
            y=random.random()*300-150
            self.cDummy.setPos(x, y, 0)
            self.cTrav.traverse(self.model)
            if self.cHandler.getNumEntries()>0:
                self.cHandler.sortEntries()
                pos=self.cHandler.getEntry(0).getSurfacePoint(render)
                holder.setPos(pos)
                holder.setH(random.random()*120)
                self.cHandler.clearEntries()
        print "Nodes flattened (grassBlade): ",self.flattenHolder.flattenStrong()

These are inside my “level” class, and self.flattenHolder is a temporary node just to flatten static models.
At the end of the init i destroy the cHandler, the cDummy and the traverser, as i don’t need them anymore.

probably you won’t need it now, but here’s a way for finding the elevation of a terrain without need of collisions. Basically is a bilinear interpolation function wich find the elevation point given 4 vertices, so you first have to found the 4 vertices of the terrain wich are nearest to the character and then found the elevation.

The getElevation function:

def getElevation(self,x,y):
        """ returns the terrain elevation at the given xy location 
            Uses bilinear interpolation (thanks pro-soft for let me know about that)
            (1-a)(1-b)      a(1-b)    where a and b are the horizontal and vertical
            b(1-a)            ab      distances to the nearest top-left pixel.
        """
        
        v1,v2,v3,v4=self.getCellVertices(x,y)
        #get elevation: bilinear interpolation
        a=abs(x-v4[0])#x distance to top-left
        b=abs(y-v4[1])#y distance to top-right
        i1=v4[2]#top-left
        i2=v3[2]#top-right
        i3=v1[2]#bottom-left
        i4=v2[2]#bottom-right
        #bilinear interpolation equation
        z=(1-a)*(1-b)*i1 + (a)*(1-b)*i2 + (1-a)*(b)*i3 + (a)*(b)*i4

        return z

and the function for finding the 4 vertices:
(it was a grid based thing so always the char was in a given cell of an grid array wich had the height values of the vertices stored)

def getCellVertices(self,x,y,debug=False):
        """ calculates the location of the 4 vertices of the current cell at the given location """
        #vertex 1
        x1=int(x)
        y1=int(y)
        z1=self.data[x1][y1]['h']
        #vertex2
        x2=x1+1
        y2=y1
        z2=self.data[x2][y2]['h']
        #vertex3
        x3=x1+1
        y3=y1+1
        z3=self.data[x3][y3]['h']
        #vertex4
        x4=x1
        y4=y1+1
        z4=self.data[x4][y4]['h']
        
        return (x1,y1,z1),(x2,y2,z2),(x3,y3,z3),(x4,y4,z4)

As i said, probably you won’t need it now but i thing is an interesting approach wich was sugested to me by rpro-soft, and in fact is a lot more fast than collision detection…

Or, if you use the GeoMipTerrain, the builtin getElevation function already performs bilinear interpolation, if you supply non-whole numbers.

Well, is it possible to change the world in RoamingRalphs sample program to a GeoMipTerrain ? The sample program only comes with the file world.egg.pz and no grayscale image of the terrain.

yes of course its possible

-you have to change the code yourself and generate a geomipmap terrain from a given heightmap and texturize it with a given texture

-take a look at Yet Another Roaming Ralph entry in the forums, it has the geomipterrain already implemented

c

here:

https://discourse.panda3d.org/viewtopic.php?t=4559&highlight=another+roaming+ralph