81 lines
2.8 KiB
Python
81 lines
2.8 KiB
Python
import numpy as np
|
|
import numba
|
|
from ray_casting import light_cast
|
|
|
|
# left, top, right, bottom
|
|
offsets = np.array([[0.0, 0.5], [0.5, 0.0], [1.0, 0.5], [0.5, 1.0]])
|
|
normals = np.array([[-1, 0], [0, -1], [1, 0], [0, 1]], dtype=np.float64)
|
|
|
|
@numba.njit
|
|
def inverse_square_law(distance):
|
|
return 1 / ((distance + 1) ** 2)
|
|
|
|
@numba.njit
|
|
def process_light(map_arr, emissive_positions):
|
|
light_arr = np.zeros(map_arr.shape, dtype=np.float32)
|
|
|
|
# for each map block
|
|
for y in range(map_arr.shape[1]):
|
|
for x in range(map_arr.shape[0]):
|
|
# skip if block is not air
|
|
if map_arr[y, x] != 0:
|
|
continue
|
|
|
|
#print("===============")
|
|
#print(emissive_positions)
|
|
#print("===============")
|
|
|
|
# for each emissive block
|
|
for emissive_pos in emissive_positions:
|
|
|
|
#temp_emissive_pos = np.array(emissive_pos)
|
|
|
|
#print("===============")
|
|
# for each wall of emissive block
|
|
for i in range(4):
|
|
# calculate source position, normal axis and normal sign
|
|
#source = temp_emissive_pos + offsets[i]
|
|
source = emissive_pos + offsets[i]
|
|
#print(source)
|
|
|
|
# normal axis is alternating: x, y, x, y
|
|
source_normal_axis = i % 2
|
|
|
|
# normal sign is switching in the middle: -1, -1, 1, 1
|
|
source_normal_sign = -1 if i < 2 else 1
|
|
|
|
target = np.array([x + 0.5, y + 0.5])
|
|
|
|
# cast light
|
|
hit, dir, dir_3d = light_cast(map_arr, source, target, source_normal_axis, source_normal_sign)
|
|
#print(hit, dir)
|
|
|
|
# skip if hit (light was blocked by some wall)
|
|
if hit:
|
|
continue
|
|
|
|
# calculate distance
|
|
distance = np.linalg.norm(source - target)
|
|
|
|
|
|
## calculate light intensity
|
|
|
|
# area depending on distance
|
|
intensity = inverse_square_law(distance)
|
|
|
|
|
|
#intensity = distance
|
|
# multiply by dot product of normal and direction
|
|
#print(normals[i].dtype, dir.dtype)
|
|
|
|
# area depending on direction the emissive face is seen by the target
|
|
intensity *= np.dot(normals[i], dir)
|
|
|
|
# and the other way around (we assume target normal is always facing up in 3D in this scenario)
|
|
intensity *= np.dot(np.array([0, 0, -1], dtype=np.float64), dir_3d)
|
|
|
|
# add intensity to light array
|
|
light_arr[y, x] += intensity
|
|
|
|
|
|
return light_arr |