Files
PolyGun/demos/DDA light test/ray_casting.py
2024-06-02 15:20:04 +02:00

107 lines
3.7 KiB
Python

import numpy as np
import numba
# modified light_cast function from PolyGun
"""
target is NOT normalized position
light_cast ray should be cast from the center of the face of a node
source_normal_sign should be 1 if the normal is pointing towards positive direction, -1 otherwise
"""
@numba.njit
def light_cast(map_arr, source, target, source_normal_axis, source_normal_sign):
# floor
hit_node = np.floor(source).astype(np.int32)
# if front of the face is NOT visible
if (target[source_normal_axis] - source[source_normal_axis]) * source_normal_sign <= 0:
#hit = True
# if normal is positive, move hit_node backwards
#if source_normal_sign > 0:
# hit_node[source_normal_axis] -= 1
return True, None, None
# check for collision in starting place if normal is positive
if source_normal_sign > 0:
if map_arr[hit_node[1]][hit_node[0]] > 0:
#hit = True
return True, None, None
# and for next node if normal is negative
else:
temp_hit_node = hit_node.copy()
temp_hit_node[source_normal_axis] -= 1
if map_arr[temp_hit_node[1]][temp_hit_node[0]] > 0:
#hit = True
#hit_node = temp_hit_node
return True, None, None
# calculate direction
direction = (target - source) / np.linalg.norm(target - source)
ray_length = np.zeros(2).astype(np.float32)
unit_step_size = np.zeros(2).astype(np.float32)
step = np.zeros(2).astype(np.int32)
# calculate how long does the ray need to travel to go from the one to the other side of the node on each axis
#unit_step_size = np.sqrt(np.array([
# 1 + (direction[1] / direction[0]) ** 2,
# 1 + (direction[0] / direction[1]) ** 2
#]))
unit_step_size = np.sqrt(np.array([
np.inf if direction[0] == 0 else 1 + (direction[1] / direction[0]) ** 2,
np.inf if direction[1] == 0 else 1 + (direction[0] / direction[1]) ** 2
]))
# perform first step from current position to the node boundary
for i in range(2):
if direction[i] < 0:
step[i] = -1
#print("===============")
#print(ray_length)
ray_length[i] = (source[i] - hit_node[i]) * unit_step_size[i]
#print(ray_length)
else:
step[i] = 1
#print("===============")
#print(ray_length)
ray_length[i] = (hit_node[i] + 1 - source[i]) * unit_step_size[i]
#print(ray_length)
# floor
target_node = np.floor(target).astype(np.int32)
shortest_axis = 0
#hit = False
# perform next steps until there is a hit or the hit_node is the target_node
#while not hit and not np.array_equal(hit_node, target_node):
while not np.array_equal(hit_node, target_node):
# select axis with the shortest travel distance
shortest_axis = 0
shortest_dist = ray_length[0]
if ray_length[1] < shortest_dist:
shortest_dist = ray_length[1]
shortest_axis = 1
# move to the next node
hit_node[shortest_axis] += step[shortest_axis]
ray_length[shortest_axis] += unit_step_size[shortest_axis]
if map_arr[hit_node[1]][hit_node[0]] > 0:
#hit = True
return True, None, None
# calculate direction in 3D assuming source is always 0.5 of the ground
target_3d = np.append(target, 0.0)
source_3d = np.append(source, 0.5)
direction_3d = (target_3d - source_3d) / np.linalg.norm(target_3d - source_3d)
return False, direction, direction_3d