Exemple #1
0
    def raycast(self, origin, direction=(0,0,1), distance=inf, traverse_target=scene, ignore=list(), debug=False):
        self.position = origin
        self.look_at(self.position + direction)

        self._pickerNode.clearSolids()
        ray = CollisionRay()
        ray.setOrigin(Vec3(0,0,0))
        ray.setDirection(Vec3(0,0,1))

        self._pickerNode.addSolid(ray)

        if debug:
            temp = Entity(position=origin, model=Raycaster.line_model, scale=Vec3(1,1,min(distance,9999)), add_to_scene_entities=False)
            temp.look_at(self.position + direction)
            destroy(temp, 1/30)

        self._picker.traverse(traverse_target)

        if self._pq.get_num_entries() == 0:
            self.hit = HitInfo(hit=False, distance=distance)
            return self.hit

        ignore += tuple([e for e in scene.entities if not e.collision])

        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
            and self.distance(self.world_position, Vec3(*e.get_surface_point(render))) <= distance
            ]

        if len(self.entries) == 0:
            self.hit = HitInfo(hit=False, distance=distance)
            return self.hit

        self.collision = self.entries[0]
        nP = self.collision.get_into_node_path().parent
        point = Vec3(*self.collision.get_surface_point(nP))
        world_point = Vec3(*self.collision.get_surface_point(render))
        hit_dist = self.distance(self.world_position, world_point)


        self.hit = HitInfo(hit=True, distance=distance)
        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

        self.hit.normal = Vec3(*self.collision.get_surface_normal(self.collision.get_into_node_path().parent).normalized())
        self.hit.world_normal = Vec3(*self.collision.get_surface_normal(render).normalized())
        return self.hit

        self.hit = HitInfo(hit=False, distance=distance)
        return self.hit
Exemple #2
0
    def boxcast(self,
                origin,
                direction=(0, 0, 1),
                distance=math.inf,
                thickness=(1, 1),
                traverse_target=scene,
                ignore=list(),
                debug=False):
        if isinstance(thickness, (int, float, complex)):
            thickness = (thickness, thickness)
        resolution = 3
        rays = list()
        debugs = list()

        for y in range(3):
            for x in range(3):
                pos = origin + Vec3(
                    lerp(-(thickness[0] / 2), thickness[0] / 2, x / (3 - 1)),
                    lerp(-(thickness[1] / 2), thickness[1] / 2, y /
                         (3 - 1)), 0)
                ray = self.raycast(pos, direction, distance, traverse_target,
                                   ignore, False)
                rays.append(ray)

                if debug and ray.hit:
                    d = Entity(model='cube',
                               origin_z=-.5,
                               position=pos,
                               scale=(.02, .02, distance),
                               ignore=True)
                    d.look_at(pos + Vec3(direction))
                    debugs.append(d)
                    # print(pos, hit.point)
                    if ray.hit and ray.distance > 0:
                        d.scale_z = ray.distance
                        d.color = color.green

        from ursina import destroy
        # [destroy(e, 1/60) for e in debugs]

        rays.sort(key=lambda x: x.distance)
        closest = rays[0]

        return HitInfo(
            hit=sum([int(e.hit) for e in rays]) > 0,
            entity=closest.entity,
            point=closest.point,
            world_point=closest.world_point,
            distance=closest.distance,
            normal=closest.normal,
            world_normal=closest.world_normal,
            hits=[e.hit for e in rays],
            entities=list(set([e.entity
                               for e in rays])),  # get unique entities hit
        )
Exemple #3
0
    def find_collision(self):
        self.collisions = list()
        self.collision = None
        if not self.raycast or self._pq.get_num_entries() == 0:
            self.unhover_everything_not_hit()
            return False

        self._pq.sortEntries()

        for entry in self._pq.getEntries():
            for entity in scene.entities:
                if entry.getIntoNodePath(
                ).parent == entity and entity.collision:
                    if entity.collision:
                        hit = HitInfo(
                            hit=entry.collided(),
                            entity=entity,
                            distance=distance(entry.getSurfacePoint(scene),
                                              camera.getPos()),
                            point=entry.getSurfacePoint(entity),
                            world_point=entry.getSurfacePoint(scene),
                            normal=entry.getSurfaceNormal(entity),
                            world_normal=entry.getSurfaceNormal(scene),
                        )
                        self.collisions.append(hit)
                        break

        if self.collisions:
            self.collision = self.collisions[0]
            self.hovered_entity = self.collision.entity
            if not self.hovered_entity.hovered:
                self.hovered_entity.hovered = True
                if hasattr(self.hovered_entity, 'on_mouse_enter'):
                    self.hovered_entity.on_mouse_enter()
                for s in self.hovered_entity.scripts:
                    if hasattr(s, 'on_mouse_enter'):
                        s.on_mouse_enter()

        self.unhover_everything_not_hit()
Exemple #4
0
    def intersects(self, traverse_target=scene, ignore=(), debug=False):
        from ursina.hit_info import HitInfo

        if not self.collision or not self.collider:
            self.hit = HitInfo(hit=False)
            return self.hit

        from ursina import distance
        if not hasattr(self, '_picker'):
            from panda3d.core import CollisionTraverser, CollisionNode, CollisionHandlerQueue
            from panda3d.core import CollisionRay, CollisionSegment, CollisionBox

            self._picker = CollisionTraverser()  # Make a traverser
            self._pq = CollisionHandlerQueue()  # Make a handler
            self._pickerNode = CollisionNode('raycaster')
            self._pickerNode.set_into_collide_mask(0)
            self._pickerNP = self.attach_new_node(self._pickerNode)
            self._picker.addCollider(self._pickerNP, self._pq)
            self._pickerNP.show()
            self._pickerNode.addSolid(self._collider.shape)

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

        self._picker.traverse(traverse_target)

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

        ignore += (self, )
        ignore += tuple([e for e in scene.entities if not e.collision])

        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 = HitInfo(hit=False, distance=0)
            return self.hit

        collision = self.entries[0]
        nP = collision.get_into_node_path().parent
        point = collision.get_surface_point(nP)
        point = Vec3(*point)
        world_point = collision.get_surface_point(render)
        world_point = Vec3(*world_point)
        hit_dist = distance(self.world_position, world_point)

        self.hit = HitInfo(hit=True)
        self.hit.entity = next(e for e in scene.entities if e == nP)

        self.hit.point = point
        self.hit.world_point = world_point
        self.hit.distance = hit_dist

        normal = collision.get_surface_normal(collision.get_into_node_path().parent).normalized()
        self.hit.normal = Vec3(*normal)

        normal = collision.get_surface_normal(render).normalized()
        self.hit.world_normal = Vec3(*normal)

        self.hit.entities = []
        for collision in self.entries:
            self.hit.entities.append(next(e for e in scene.entities if e == collision.get_into_node_path().parent))

        return self.hit
Exemple #5
0
def terraincast(origin, terrain): # mainly used for getting the y position on a terrain. returns a HitInfo like raycast()
    

    
    origin = Vec3(*origin)
    height_values = terrain.model.height_values
    width = terrain.model.width
    depth = terrain.model.depth
    height_multiplier = terrain.world_scale_y

    if terrain.world_rotation[0] != 0 or terrain.world_rotation[2] != 0:
        print("terraincaster does not work when the terrain is rotated to not face upwards")
        return None

    #stores x,z to reduce unecessary calculations later
    point_x = origin[0]
    point_z = origin[2]


    #transformations processed for origin to align with height_values
    angle =  - radians(terrain.world_rotation_y)
    origin_vector = origin - terrain.world_position

    store = origin_vector[0]
    origin_vector[0] = origin_vector[0] * cos( - angle) - origin_vector[2] * sin( - angle)
    origin_vector[2] = store * sin( - angle) + origin_vector[2] * cos( - angle)
    origin = terrain.world_position + origin_vector
    
    
    origin = (origin - terrain.world_position + terrain.origin * terrain.world_scale)
    origin[0] = (origin[0] / terrain.world_scale_x + .5) * width
    origin[2] = (origin[2] / terrain.world_scale_z + .5) * width
    



    if origin[0] >=0 and origin[0] < len(height_values) and origin[2] >=0 and origin[2] < len(height_values[0]):
        if floor(origin[2]) == len(height_values[0]) - 1 and floor(origin[0]) == len(height_values) - 1:
            start = Vec3(floor(origin[0]),height_values[int(floor(origin[0]))][int(floor(origin[2]))],floor(origin[2]))
            normal = Vec3(0,1,0)
        else:
            if floor(origin[2]) == len(height_values[0]) - 1:
                start = Vec3(floor(origin[0]),height_values[int(floor(origin[0]))][int(floor(origin[2]))],floor(origin[2]))
                right = Vec3(floor(origin[0]),height_values[int(floor(origin[0]))][int(floor(origin[2]))],floor(origin[2] + 1))
                left = Vec3(floor(origin[0] + 1),height_values[int(floor(origin[0]) + 1)][int(floor(origin[2]))],floor(origin[2] + 1))
            elif floor(origin[0]) == len(height_values) - 1:
                start = Vec3(floor(origin[0]),height_values[int(floor(origin[0]))][int(floor(origin[2]))],floor(origin[2]))
                right = Vec3(floor(origin[0]),height_values[int(floor(origin[0]))][int(floor(origin[2]) + 1)],floor(origin[2]) + 1)
                left = Vec3(floor(origin[0] + 1),height_values[int(floor(origin[0]))][int(floor(origin[2]) + 1)],floor(origin[2] + 1))
                
                
            #determines which triangle of the current square the player is standing on and gets vectors for each corner
            elif origin[0] % 1 < origin[2] % 1:
                start = Vec3(floor(origin[0]),height_values[int(floor(origin[0]))][int(floor(origin[2]))],floor(origin[2]))
                right = Vec3(floor(origin[0]),height_values[int(floor(origin[0]))][int(floor(origin[2]) + 1)],floor(origin[2]) + 1)
                left = Vec3(floor(origin[0] + 1),height_values[int(floor(origin[0]) + 1)][int(floor(origin[2]) + 1)],floor(origin[2] + 1))
            else:
                start = Vec3(floor(origin[0]),height_values[int(floor(origin[0]))][int(floor(origin[2]))],floor(origin[2]))
                right = Vec3(floor(origin[0]) + 1,height_values[int(floor(origin[0]) + 1)][int(floor(origin[2]))],floor(origin[2]))
                left = Vec3(floor(origin[0] + 1),height_values[int(floor(origin[0]) + 1)][int(floor(origin[2]) + 1)],floor(origin[2] + 1))

            #get normal to face and make sure it's facing up and is 1 unit in length
            normal = cross(left - start,right - start)
            if normal[1] <0:
                normal =  - normal
            normal = normal / sqrt(normal[0] ** 2 + normal[1] ** 2 + normal[2] ** 2)

        hit = HitInfo(hit=True)                                                                                          
        #finds point where verticle line from origin and face plane intersect based on the calculated normal this uses the maths that is here for anyone interested https: /  / en.wikipedia.org / wiki / Line - plane_intersection
        hit.world_point = Vec3(point_x,
                             (dot(start,normal) - origin[0] * normal[0] - origin[2] * normal[2]) / normal[1] * terrain.world_scale_y + terrain.world_position[1] - terrain.origin[1] * terrain.world_scale_y,
                             point_z
        )

        #finishes up setting hit info object
        if hasattr(terrain.parent, 'position'):
            hit.point = hit.world_point - terrain.parent.world_position
        else:
            hit.point = hit.world_point
            #parented to scene or something so reverts to world coordinates
        hit.normal = Vec3(*normal)
        hit.world_normal = hit.normal
        hit.distance = origin[1] - hit.world_point[1]
        hit.entity = terrain
        hit.entities = [terrain, ]
        hit.hits = [True, ]
        return hit
    else:
        hit = HitInfo(hit=False, distance=inf)
        return hit
Exemple #6
0
    def raycast(self,
                origin,
                direction=(0, 0, 1),
                distance=inf,
                traverse_target=scene,
                ignore=list(),
                debug=False):
        self.position = origin
        self.look_at(self.position + direction)
        self._pickerNode.clearSolids()
        # if thickness == (0,0):
        if distance == inf:
            ray = CollisionRay()
            ray.setOrigin(Vec3(0, 0, 0))
            # ray.setDirection(Vec3(0,1,0))
            ray.setDirection(Vec3(0, 0, 1))
        else:
            # ray = CollisionSegment(Vec3(0,0,0), Vec3(0,distance,0))
            ray = CollisionSegment(Vec3(0, 0, 0), Vec3(0, 0, distance))

        self._pickerNode.addSolid(ray)

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

        self._picker.traverse(traverse_target)

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

        ignore += tuple([e for e in scene.entities if not e.collision])

        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 = HitInfo(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])
        point = Vec3(point[0], point[1], point[2])
        world_point = self.collision.get_surface_point(render)
        # world_point = Vec3(world_point[0], world_point[2], world_point[1])
        world_point = Vec3(world_point[0], world_point[1], world_point[2])
        hit_dist = self.distance(self.world_position, world_point)

        if nP.name.endswith('.egg'):
            nP = nP.parent

        self.hit = HitInfo(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

        self.hit.normal = Vec3(*self.collision.get_surface_normal(
            self.collision.get_into_node_path().parent).normalized())
        self.hit.world_normal = Vec3(
            *self.collision.get_surface_normal(render).normalized())
        return self.hit

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