107 lines
3.7 KiB
Python
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 |