Clicking on object

I have this code for a tilemap:

from panda3d.core import CollisionTraverser, CollisionNode
from panda3d.core import CollisionHandlerQueue, CollisionRay
from panda3d.core import CollisionBox, BitMask32, LineSegs

class TileMap():

    # -----------------------------------------------------
    def __init__(self):

        self.width = 8
        self.height = 8
        self.tile_width = 8
        self.tile_height = 8
        self.full_width = self.width * self.tile_width
        self.full_height = self.height * self.tile_height
        self.half_width = self.full_width / 2
        self.half_height = self.full_height / 2
        
        self.picker = CollisionTraverser()
        self.pq = CollisionHandlerQueue()
        self.pickerNode = CollisionNode('mouseRay')

        self.pickerNP = render.attachNewNode(self.pickerNode)

        self.pickerNode.setFromCollideMask(BitMask32.bit(1))
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.picker.addCollider(self.pickerNP, self.pq)

        self.tile_dict = {
            "0": ["grass", "assets/tiles/grass1.obj", "0"],
            "1": ["water", "assets/tiles/water1.obj", "0"],
            "2": ["hill", "assets/tiles/hill1.obj", "0"]
        }

        self.tiles = [
            ["1", "0", "0", "1", "1", "0", "0", "1"],
            ["0", "0", "0", "0", "0", "0", "0", "0"],
            ["0", "1", "1", "0", "0", "2", "0", "0"],
            ["1", "0", "0", "0", "0", "2", "0", "1"],
            ["0", "0", "0", "2", "2", "2", "0", "1"],
            ["0", "0", "0", "0", "0", "0", "0", "0"],
            ["0", "0", "0", "0", "0", "0", "0", "0"],
            ["0", "0", "1", "0", "1", "1", "0", "0"]
        ]

        # the dimensions for the collisionbox
        dx = self.tile_width / 2
        dy = 1
        dz = self.tile_height / 2

        # node for the tiles
        self.tilesRoot = render.attachNewNode("tilesRoot")


        for i in range(self.width - 1, -1, -1):
            for j in range(self.height - 1, -1, -1):
                x = i * self.tile_width
                z = j * self.tile_height

                t = self.tiles[j][i]
                m = loader.loadModel(self.tile_dict[t][1])

                m.setPos(x, 0, z)
                m.reparentTo(self.tilesRoot)

                # set the collisionbox
                box = CollisionBox((0, 0, 0), dx, dy, dz)
                mPath = m.attachNewNode(CollisionNode('cnode'))
                mPath.node().addSolid(box)
                mPath.node().setIntoCollideMask(BitMask32.bit(1))
                mPath.node().setTag("monu_i", str(i))
                mPath.node().setTag("monu_j", str(j))
                # mPath.show()
    
    # -----------------------------------------------------
    def getTile(self, x, y):

        # check ray collsion with mouse
        self.pickerRay.setFromLens(base.camNode, x, y)

        print(base.cam.getPos())
        
        # draw the ray
        seg = LineSegs("lines")
        seg.setColor(1, 1, 0, 1)
        seg.drawTo(self.pickerRay.getOrigin())
        seg.drawTo(self.pickerRay.getDirection())
        segnode = seg.create(False)
        render.attachNewNode(segnode)
      
        self.picker.traverse(self.tilesRoot)

        if self.pq.getNumEntries() > 0:
            self.pq.sortEntries()
            i = int(self.pq.getEntry(0).getIntoNode().getTag('monu_i'))
            j = int(self.pq.getEntry(0).getIntoNode().getTag('monu_j'))

If I click on the tiles, you can see that the ray is wrong, he shows always to the first tile at 0, 0, 0:

Can someone check, what’s my mistake is?

Well, one issue may lie in how you’re drawing your “LineSegs”-lines: As I understand it, “drawTo” draws from a previous position to the given position. However, I see two potential conflicts with this in your code:

  • First, you don’t seem to be setting a “previous position” before drawing; I imagine that it’s defaulting to (0, 0, 0).
    • I think that an initial call to the “moveTo” command might help here.
  • And second, “getDirection” produces not a point, but a vector–in a sense, a direction (and magnitude), not a position. But since “drawTo” expects a position, it’s presumably treating the result of “getDirection” as though it were a position, which may be producing unexpected results.
    • I think that the correct position would be given by the ray-origin plus the ray-direction.
      • That is, you would end up drawing from a first position at the ray-origin to a second position at the ray-origin plus the ray-direction.

My problem is not the drawing, my problem is that I got always the object a 0, 0, 0 as result. there is no other result, even if I click on tile 5, 0, 5.

If I print the value’s for rayOrigin I got this:

LPoint3f(0.00267951, 1, 0.0232223)
LPoint3f(-0.158983, 1, -0.117898)
LPoint3f(0.187564, 1, -0.164342)
LPoint3f(0.196496, 1, -0.0446582)
LPoint3f(-0.0276881, 1, 0.065201)
LPoint3f(-0.15809, 1, -0.0723463)
LPoint3f(-0.331364, 1, -0.0410855)
LPoint3f(-0.215253, 1, -0.0375129)
LPoint3f(0.353693, 1, -0.0580557)
LPoint3f(0.218825, 1, 0.0142906)
LPoint3f(0.31886, 1, -0.000893163)
LPoint3f(-0.0321539, 1, 0.246513)
LPoint3f(-0.109859, 1, 0.216146)
LPoint3f(-0.15809, 1, 0.0178633)
LPoint3f(-0.193817, 1, 0)
LPoint3f(-0.322432, 1, -0.0660941)
LPoint3f(-0.234009, 1, -0.065201)
LPoint3f(-0.156304, 1, -0.0205428)
LPoint3f(0.127722, 1, -0.0946754)
LPoint3f(0.17774, 1, -0.117004)
LPoint3f(0.225971, 1, -0.147372)
LPoint3f(0.238475, 1, -0.179526)
LPoint3f(0.139334, 1, -0.209)
LPoint3f(0.244727, 1, -0.229543)

It is always between -1 and 1, is this correct?

To address your last question, those appear to be screen coordinates which are given in (X,Y,Z) from -1 to 1. Y is the depth and I think in this case would always be 1. X/Z are vertical and horizontal screen coordinates where 0,0 is the center and -1,1 represent the minima and maxima of the window.

I was looking at this answer, and it might be of value to you.

Ah, fair enough.

Hmm… What values are you passing into “getTile”? Are they mouse-coordinates?

Hmm… I’m not sure.

Looking over some old code of mine, however, I do notice that I have my ray attached to “base.cam”, rather than to “render”. Perhaps “setFromLens” is assuming that the ray is attached to the camera to which the lens belongs, and thus is producing incorrect results when that’s not the case?

Yes, “getTile” are the mouse-coordinates.

Looking over some old code of mine, however, I do notice that I have my ray attached to “base.cam”, rather than to “render”. Perhaps “setFromLens” is assuming that the ray is attached to the camera to which the lens belongs, and thus is producing incorrect results when that’s not the case?

What do you mean with this?

Okay, that’s good.

In your code, you’re creating the NodePath “pickerNP” (which holds your ray) as a child of “render”–that is, it’s ending up “attached to” render.

I’m suggesting that you instead create it as a child of the camera, of “base.cam”.

And I’m suggesting this because, looking over some old code of mine that did something similar to what you’re working with, I believe that I had the NodePath that held my ray attached to “base.cam”. I’m thus speculating that the “setFromLens” method assumes that something like this is the case–and that it therefore produces unexpected results when that’s not the case.

Hey, you are the greatest…

It works…

Thank you very much…

2 Likes

It’s my pleasure! I’m glad that you got it working! :slight_smile: