#๐Ÿ”’ Pygame ray casting

15 messages ยท Page 1 of 1 (latest)

ember inletBOT
#

@mild locust

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

mild locust
#
import pygame
import math

# Initialize Pygame
pygame.init()
width, height = 1000, 750
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()

# Define walls as rectangles with top-left and bottom-right points
walls = [((100, 100), (200, 200)), ((300, 100), (400, 300)), ((350, 250), (650, 400)), ((250, 300), (300, 350)), ((100, 100), (200, 200)), ((100, 100), (200, 200))]

# Light range (distance the light can reach)
light_range = 200


def draw_walls():
    for wall in walls:
        pygame.draw.polygon(screen, (255, 255, 255),
                            [wall[0], (wall[1][0], wall[0][1]), wall[1], (wall[0][0], wall[1][1])], 1)


def get_rect_edges(wall):
    """Given a wall's top-left and bottom-right corners, return all four edges."""
    tl, br = wall
    tr = (br[0], tl[1])  # top-right corner
    bl = (tl[0], br[1])  # bottom-left corner
    return [(tl, tr), (tr, br), (br, bl), (bl, tl)]


def find_intersection(ray_origin, ray_direction, edge):
    x1, y1 = edge[0]
    x2, y2 = edge[1]
    x3, y3 = ray_origin
    x4, y4 = ray_origin[0] + ray_direction[0] * 1000, ray_origin[1] + ray_direction[1] * 1000

    denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
    if denom == 0:
        return None  # Parallel lines

    t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom
    u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / denom

    if 0 <= t <= 1 and 0 <= u <= 1:
        intersection_x = x1 + t * (x2 - x1)
        intersection_y = y1 + t * (y2 - y1)
        return (intersection_x, intersection_y)
    else:
        return None
#
def is_occluded(source, target, walls):
    """Check if the target point is occluded by any walls from the source point."""
    direction = (target[0] - source[0], target[1] - source[1])
    for wall in walls:
        for edge in get_rect_edges(wall):
            intersection = find_intersection(source, direction, edge)
            if intersection and intersection != target:
                if 0 < math.dist(source, intersection) < math.dist(source, target):
                    return True
    return False

def point_inside_box(point, box):
    """Check if a point is inside a given box."""
    (box_tl, box_br) = box
    return box_tl[0] < point[0] < box_br[0] and box_tl[1] < point[1] < box_br[1]

def interpolate_points(start, end, num_points):
    """Generate points between start and end."""
    return [(start[0] + i*(end[0]-start[0])/num_points, start[1] + i*(end[1]-start[1])/num_points) for i in range(num_points)]

def cast_rays_from_light(mouse_pos):
    for wall in walls:
        edges = get_rect_edges(wall)
        for edge in edges:
            for corner in edge[:1]:  # Only check the first point of each edge to avoid duplicates
                if not is_occluded(mouse_pos, corner, walls) and math.dist(mouse_pos, corner) <= light_range:
                    corner_ray_length = math.dist(mouse_pos, corner)
                    shadow_ray_max_length = light_range - corner_ray_length

                    direction = (corner[0] - mouse_pos[0], corner[1] - mouse_pos[1])
                    norm = math.sqrt(direction[0] ** 2 + direction[1] ** 2)
                    normalized_direction = (direction[0] / norm, direction[1] / norm)
                    pygame.draw.line(screen, (255, 0, 0), mouse_pos, corner, 1)

                    shadow_direction = normalized_direction
                    shadow_ray_end = (corner[0] + shadow_direction[0] * shadow_ray_max_length,
                                      corner[1] + shadow_direction[1] * shadow_ray_max_length)


#
# Check multiple points along the shadow ray to see if it's inside any box
                    check_points = interpolate_points(corner, shadow_ray_end, 10)  # 10 can be adjusted as needed
                    shadow_ray_inside_box = any(point_inside_box(point, box) for point in check_points for box in walls)

                    shadow_intersection_point = None
                    min_distance = float('inf')
                    for check_wall in walls:
                        for check_edge in get_rect_edges(check_wall):
                            intersection = find_intersection(corner, shadow_direction, check_edge)
                            if intersection:
                                intersection_distance = math.dist(corner, intersection)
                                if 0 < intersection_distance < shadow_ray_max_length:
                                    shadow_intersection_point = intersection
                                    shadow_ray_max_length = intersection_distance  # Adjust the shadow ray's length

                    # Draw the shadow ray with the adjusted length
                    if shadow_intersection_point:
                        pygame.draw.line(screen, (0, 0, 255), corner, shadow_intersection_point, 1)
                    else:
                        pygame.draw.line(screen, (0, 0, 255), corner, shadow_ray_end, 1)


def main():
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        screen.fill((0, 0, 0))
        mouse_pos = pygame.mouse.get_pos()

        draw_walls()
        cast_rays_from_light(mouse_pos)

        pygame.display.flip()
        clock.tick(60)


if __name__ == '__main__':
    main()
#

The problem is the shadow rays (blue lines) go inside the boxes. It should do something like that

signal field
#

hm. why does it do that?

#

why do the rays go through the corners?

#

do you have any explanation for that behaviour?

#

also do you have an idea how to specify in which cases the ray should not pass a corner based on the coordinates of the mouse and the two edges?

mild locust
# signal field also do you have an idea how to specify in which cases the ray should **not** pa...

I'm trying to create an lighting system. The rays should be casted only when it is inside the light range and does see a corner of the box without begin blocked by any other walls (I call that corner ray). The blue rays are shadow rays. Shadow rays will be casted after the corner rays, they are cut when they hit another wall or the box itself. When it tries to go inside the box then just pass. And the ray length can not be over the light range so... light_range >= (corner_ray + shadow_ray).

signal field
#

Okay, but you don't want them to pass into the box.

#

So a corner that actually blocks the ray should be handled the same way as a solid edge. So it should be excluded from the ray casting.

ember inletBOT
#
Python help channel closed

This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.