Exemplo n.º 1
0
class Picker(object):
    '''
    A class for picking (Panda3d) objects.
    '''
    def __init__(self,
                 app,
                 render,
                 camera,
                 mouseWatcher,
                 pickKeyOn,
                 pickKeyOff,
                 collideMask,
                 pickableTag="pickable"):
        self.render = render
        self.mouseWatcher = mouseWatcher.node()
        self.camera = camera
        self.camLens = camera.node().get_lens()
        self.collideMask = collideMask
        self.pickableTag = pickableTag
        self.taskMgr = app.task_mgr
        # setup event callback for picking body
        self.pickKeyOn = pickKeyOn
        self.pickKeyOff = pickKeyOff
        app.accept(self.pickKeyOn, self._pickBody, [self.pickKeyOn])
        app.accept(self.pickKeyOff, self._pickBody, [self.pickKeyOff])
        # collision data
        self.collideMask = collideMask
        self.cTrav = CollisionTraverser()
        self.collisionHandler = CollisionHandlerQueue()
        self.pickerRay = CollisionRay()
        pickerNode = CollisionNode("Utilities.pickerNode")
        node = NodePath("PhysicsNode")
        node.reparentTo(render)
        anp = node.attachNewNode(pickerNode)
        base.physicsMgr.attachPhysicalNode(pickerNode)
        pickerNode.add_solid(self.pickerRay)
        pickerNode.set_from_collide_mask(self.collideMask)
        pickerNode.set_into_collide_mask(BitMask32.all_off())
        #pickerNode.node().getPhysicsObject().setMass(10)
        self.cTrav.add_collider(self.render.attach_new_node(pickerNode),
                                self.collisionHandler)
        # service data
        self.pickedBody = None
        self.oldPickingDist = 0.0
        self.deltaDist = 0.0
        self.dragging = False
        self.updateTask = None

    def _pickBody(self, event):
        # handle body picking
        if event == self.pickKeyOn:
            # check mouse position
            if self.mouseWatcher.has_mouse():
                # Get to and from pos in camera coordinates
                pMouse = self.mouseWatcher.get_mouse()
                #
                pFrom = LPoint3f()
                pTo = LPoint3f()
                if self.camLens.extrude(pMouse, pFrom, pTo):
                    # Transform to global coordinates
                    rayFromWorld = self.render.get_relative_point(
                        self.camera, pFrom)
                    rayToWorld = self.render.get_relative_point(
                        self.camera, pTo)
                    # cast a ray to detect a body
                    # traverse downward starting at rayOrigin
                    self.pickerRay.set_direction(rayToWorld - rayFromWorld)
                    self.pickerRay.set_origin(rayFromWorld)
                    self.cTrav.traverse(self.render)
                    if self.collisionHandler.get_num_entries() > 0:
                        self.collisionHandler.sort_entries()
                        entry0 = self.collisionHandler.get_entry(0)
                        hitPos = entry0.get_surface_point(self.render)
                        # get the first parent with name
                        pickedObject = entry0.get_into_node_path()
                        while not pickedObject.has_tag(self.pickableTag):
                            pickedObject = pickedObject.getParent()
                            if not pickedObject:
                                return
                            if pickedObject == self.render:
                                return
                        #
                        self.pickedBody = pickedObject

                        self.oldPickingDist = (hitPos - rayFromWorld).length()
                        self.deltaDist = (
                            self.pickedBody.get_pos(self.render) - hitPos)
                        print(self.pickedBody.get_name(), hitPos)
                        if not self.dragging:
                            self.dragging = True
                            # create the task for updating picked body motion
                            self.updateTask = self.taskMgr.add(
                                self._movePickedBody, "_movePickedBody")
                            # set sort/priority
                            self.updateTask.set_sort(0)
                            self.updateTask.set_priority(0)
        else:

            if self.dragging:
                # remove pick body motion update task
                self.taskMgr.remove("_movePickedBody")
                self.updateTask = None
                self.dragging = False
                self.pickedBody = None

    def _movePickedBody(self, task):
        # handle picked body if any
        if self.pickedBody and self.dragging:
            # check mouse position
            if self.mouseWatcher.has_mouse():
                # Get to and from pos in camera coordinates
                pMouse = self.mouseWatcher.get_mouse()
                #
                pFrom = LPoint3f()
                pTo = LPoint3f()
                if self.camLens.extrude(pMouse, pFrom, pTo):
                    # Transform to global coordinates
                    rayFromWorld = self.render.get_relative_point(
                        self.camera, pFrom)
                    rayToWorld = self.render.get_relative_point(
                        self.camera, pTo)
                    # keep it at the same picking distance
                    direction = (rayToWorld - rayFromWorld).normalized()
                    direction *= self.oldPickingDist
                    self.pickedBody.set_pos(
                        self.render, rayFromWorld + direction + self.deltaDist)
                    #self.pickedBody.reparentTo(np)
                    #self.pickedBody.setMass(10.0)

        #
        return task.cont
Exemplo n.º 2
0
class Raycaster(Entity):

    def __init__(self):
        super().__init__(
            name = 'raycaster',
            eternal = True
            )
        self._picker = CollisionTraverser()  # Make a traverser
        self._pq = CollisionHandlerQueue()  # Make a handler
        self._pickerNode = CollisionNode('raycaster')
        self._pickerNP = self.attach_new_node(self._pickerNode)
        self._collision_ray = CollisionRay()  # Make our ray
        self._pickerNode.addSolid(self._collision_ray)
        self._picker.addCollider(self._pickerNP, self._pq)
        self._pickerNP.show()


    def distance(self, a, b):
        return math.sqrt(sum( (a - b)**2 for a, b in zip(a, b)))


    def raycast(self, origin, direction=(0,0,1), dist=math.inf, traverse_target=scene, ignore=list(), debug=False):
        self.position = origin
        self.look_at(self.position + direction)
        # need to do this for it to work for some reason
        self._collision_ray.set_origin(Vec3(0,0,0))
        self._collision_ray.set_direction(Vec3(0,1,0))

        if debug:
            self._pickerNP.show()
        else:
            self._pickerNP.hide()

        self._picker.traverse(traverse_target)

        if self._pq.get_num_entries() == 0:
            self.hit = Hit(hit=False)
            return self.hit

        self._pq.sort_entries()
        self.entries = [        # filter out ignored entities
            e for e in self._pq.getEntries()
            if e.get_into_node_path().parent not in ignore
            ]

        if len(self.entries) == 0:
            self.hit = Hit(hit=False)
            return self.hit

        self.collision = self.entries[0]
        nP = self.collision.get_into_node_path().parent
        point = self.collision.get_surface_point(nP)
        point = Vec3(point[0], point[2], point[1])
        world_point = self.collision.get_surface_point(render)
        world_point = Vec3(world_point[0], world_point[2], world_point[1])
        hit_dist = self.distance(self.world_position, world_point)
        if hit_dist <= dist:
            if nP.name.endswith('.egg'):
                nP = nP.parent

            self.hit = Hit(hit=True)
            for e in scene.entities:
                if e == nP:
                    # print('cast nP to Entity')
                    self.hit.entity = e

            self.hit.point = point
            self.hit.world_point = world_point
            self.hit.distance = hit_dist
            normal = self.collision.get_surface_normal(self.collision.get_into_node_path().parent)
            self.hit.normal = (normal[0], normal[2], normal[1])
            normal = self.collision.get_surface_normal(render)
            self.hit.world_normal = (normal[0], normal[2], normal[1])
            return self.hit

        self.hit = Hit(hit=False)
        return self.hit