VoxelEngine/Objects/World.py

718 lines
32 KiB
Python

import time
from Lights.Lights import Light
from Objects.Objects import Object
from Objects.Renderable import Renderable
from Objects.Structure import Structure
from MatrixStuff.Transformations import translate
from OpenGL.GLU import *
from OpenGL.GL import *
import math
import numpy as np
import random
import sys
import ctypes
float_pointer = ctypes.POINTER(ctypes.c_float)
# Plate Types
SEA_PLATE = 0
CONTINENTAL_PLATE = 1
# Rock types
EMPTY = 0
SEA_PLATE_STONE = 1
MAGMATIC_STONE = 2
METAMORPH_STONE = 3
SEDIMENTAL_STONE = 4
SEDIMENT = 5
class WorldChunk(Structure):
def __init__(self, width: int, length: int, height: int, programs: dict):
assert width > 0, 'Width must be greater than 0'
assert length > 0, 'length must be greater than 0'
assert height > 0, 'height must be greater than 0'
super(WorldChunk, self).__init__()
self.visible = []
self.content = []
self.entities = []
self.lights = []
self.width = width
self.length = length
self.height = height
self.programs = programs
self.objects = {}
for x in range(width):
self.content.append([])
self.visible.append([])
for y in range(length):
self.content[x].append([])
self.visible[x].append([])
for z in range(height):
self.content[x][y].append(None)
self.visible[x][y].append(4)
def put_object(self, x: int, y: int, z: int, new_object: Object):
assert 0 <= x < self.width, 'Put out of bounds for x coordinate! Must be between 0 and %i' % self.width
assert 0 <= y < self.length, 'Put out of bounds for y coordinate! Must be between 0 and %i' % self.length
assert 0 <= z < self.height, 'Put out of bounds for z coordinate! Must be between 0 and %i' % self.height
no_visibility_changes = (self.content[x][y][z] is None) == (new_object is None)
old_object = self.content[x][y][z]
self.content[x][y][z] = new_object
new_object.translate(translate(x, y, z))
change = -1 if new_object is not None else 1
visible_carry_over = []
if not no_visibility_changes:
if x + 1 >= self.width:
visible_carry_over.append((1, 0, 0, change))
else:
self.visible[x + 1][y][z] += change
if x - 1 < 0:
visible_carry_over.append((-1, 0, 0, change))
else:
self.visible[x - 1][y][z] += change
if y + 1 >= self.length:
visible_carry_over.append((0, 1, 0, change))
else:
self.visible[x][y + 1][z] += change
if y - 1 < 0:
visible_carry_over.append((0, -1, 0, change))
else:
self.visible[x][y - 1][z] += change
if z + 1 >= self.height:
visible_carry_over.append((0, 0, 1, change))
else:
self.visible[x][y][z + 1] += change
if z - 1 < 0:
visible_carry_over.append((0, 0, -1, change))
else:
self.visible[x][y][z - 1] += change
# todo: add visibility check for object listing
added = False
if old_object is not None:
if new_object is not None and type(old_object) == type(new_object):
new_object.buffer_id = old_object.buffer_id
self.objects[self.programs[type(old_object)]][old_object.buffer_id] = new_object
added = True
else:
# todo: maybe replace the element with a placeholder that is skipped when rendering/ saving and have a
# cleanup task, since this could be exploited to lower update rates
leading = self.objects[self.programs[type(old_object)]][:old_object.buffer_id]
following = self.objects[self.programs[type(old_object)]][old_object.buffer_id + 1:]
for element in following:
element.buffer_id -= 1
self.objects[self.programs[type(old_object)]] = leading + following
if not added and new_object is not None:
if self.programs[type(new_object)] not in self.objects.keys():
self.objects[self.programs[type(new_object)]] = []
new_object.buffer_id = len(self.objects[self.programs[type(new_object)]])
self.objects[self.programs[type(new_object)]].append(new_object)
self.dirty = True
self.dirty_pos = True
self.dirty_color = True
self.dirty_size = True
return visible_carry_over
def get_object(self, x: int, y: int, z: int):
assert 0 <= x < self.width, 'Put out of bounds for x coordinate! Must be between 0 and %i' % self.width
assert 0 <= y < self.length, 'Put out of bounds for y coordinate! Must be between 0 and %i' % self.length
assert 0 <= z < self.height, 'Put out of bounds for z coordinate! Must be between 0 and %i' % self.height
return self.content[x][y][z]
def apply_visible_carry_over(self, x: int, y: int, z: int, change: int):
assert 0 <= x < self.width, 'Apply visible out of bounds for x coordinate! Must be between 0 and %i' % self.width
assert 0 <= y < self.length, 'Apply visible out of bounds for y coordinate! Must be between 0 and %i' % self.length
assert 0 <= z < self.height, 'Apply visible out of bounds for z coordinate! Must be between 0 and %i' % self.height
self.visible[x][y][z] += change
def set_visibility(self, x: int, y: int, z: int, visibility: int):
assert 0 <= x < self.width, 'Apply visible out of bounds for x coordinate! Must be between 0 and %i' % self.width
assert 0 <= y < self.length, 'Apply visible out of bounds for y coordinate! Must be between 0 and %i' % self.length
assert 0 <= z < self.height, 'Apply visible out of bounds for z coordinate! Must be between 0 and %i' % self.height
self.visible[x][y][z] = visibility
def buildvertexArrays(self):
if self.dirty:
# self.clearVertexArrays()
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
glEnableClientState(GL_NORMAL_ARRAY)
glEnableClientState(GL_COLOR_ARRAY)
for key, object_list in self.objects.items():
needs_new_buffers = key not in self.vais.keys()
if needs_new_buffers:
tvai = GLuint(0)
tpbi = GLuint(0)
tcbi = GLuint(0)
tsbi = GLuint(0)
glGenVertexArrays(1, tvai)
else:
tvai, tpbi, tcbi, tsbi, old_len = self.vais[key]
glBindVertexArray(tvai)
if self.dirty_pos:
if needs_new_buffers:
vid = glGetAttribLocation(key, "in_position")
glEnableVertexAttribArray(vid)
tpbi = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, tpbi)
positions = []
for index, o in enumerate(object_list):
o.buffer_id = index
positions.append(o.pos[0] + self.x_offset)
positions.append(o.pos[1] + self.y_offset)
positions.append(o.pos[2] + self.z_offset)
glBufferData(GL_ARRAY_BUFFER, np.array(positions, dtype=np.float32), GL_STATIC_DRAW)
if needs_new_buffers:
glVertexAttribPointer(vid, 3, GL_FLOAT, GL_FALSE, 0, None)
self.check_error("Could not create position buffer")
if self.dirty_color:
colors = []
for o in object_list:
colors.append(o.color[0])
colors.append(o.color[1])
colors.append(o.color[2])
if needs_new_buffers:
tcbi = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, tcbi)
if needs_new_buffers or old_len != len(object_list):
glBufferData(GL_ARRAY_BUFFER, np.array(colors, dtype=np.float32), GL_STATIC_DRAW)
else:
# todo: check if this improves anything. Timewise it seems to be the same
ptr = ctypes.cast(glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE), float_pointer)
for index, value in enumerate(colors):
ptr[index] = value
glUnmapBuffer(GL_ARRAY_BUFFER)
if needs_new_buffers:
vc = glGetAttribLocation(key, "MyInColor")
if vc != -1:
glEnableVertexAttribArray(vc)
glVertexAttribPointer(vc, 3, GL_FLOAT, GL_FALSE, 0, None)
self.check_error("Could not create color buffer")
if self.dirty_size:
if hasattr(object_list[0], 'size'):
sizes = []
for o in object_list:
sizes.append(o.size[0])
sizes.append(o.size[1])
sizes.append(o.size[2])
if needs_new_buffers:
tsbi = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, tsbi)
glBufferData(GL_ARRAY_BUFFER, np.array(sizes, dtype=np.float32), GL_STATIC_DRAW)
if needs_new_buffers:
vs = glGetAttribLocation(key, "MyInSize")
if vs != -1:
glEnableVertexAttribArray(vs)
glVertexAttribPointer(vs, 3, GL_FLOAT, GL_FALSE, 0, None)
self.check_error("Could not create size buffer")
glBindVertexArray(0)
self.vais[key] = (tvai, tpbi, tcbi, tsbi, len(object_list))
self.dirty = False
self.dirty_pos = False
self.dirty_color = False
self.dirty_size = False
def render(self, proj_matrix, geometry_rot_matrix, alternate_programs=None,
preselected_program=None, projection_pos=None, rot_pos=None):
super(WorldChunk, self).render(proj_matrix, geometry_rot_matrix, alternate_programs,
preselected_program, projection_pos, rot_pos)
for entity in self.entities:
entity.render(proj_matrix, geometry_rot_matrix, alternate_programs,
preselected_program, projection_pos, rot_pos)
def set_color(self, x: int, y: int, z: int, r: float, g: float, b: float):
assert 0 <= x < self.width, 'Put out of bounds for x coordinate! Must be between 0 and %i' % self.width
assert 0 <= y < self.length, 'Put out of bounds for y coordinate! Must be between 0 and %i' % self.length
assert 0 <= z < self.height, 'Put out of bounds for z coordinate! Must be between 0 and %i' % self.height
if self.content[x][y][z] is not None:
self.content[x][y][z].setColor(r, g, b)
self.dirty = True
self.dirty_color = True
def load(self):
for x in range(self.width):
for y in range(self.length):
for z in range(self.height):
if self.content[x][y][z] is not None: # and self.visible[x][y][z] > 0: TODO: check visibility...
if self.programs[type(self.content[x][y][z])] not in self.objects.keys():
self.objects[self.programs[type(self.content[x][y][z])]] = []
self.objects[self.programs[type(self.content[x][y][z])]].append(self.content[x][y][z])
class World(Renderable):
def __init__(self, chunk_size_x: int, chunk_size_y: int, chunk_size_z: int,
chunk_n_x: int, chunk_n_y: int, chunk_n_z: int, programs: dict):
super(World, self).__init__()
self.chunk_size_x = chunk_size_x
self.chunk_size_y = chunk_size_y
self.chunk_size_z = chunk_size_z
self.chunk_n_x = chunk_n_x
self.chunk_n_y = chunk_n_y
self.chunk_n_z = chunk_n_z
self.programs = programs
self.fault_nodes = []
self.fault_lines = []
self.plates = None
self.directions = None
self.num_plates = 0
self.stone = None
self.faults = None
self.chunks: [[[WorldChunk]]] = []
for x in range(chunk_n_x):
self.chunks.append([])
for y in range(chunk_n_y):
self.chunks[x].append([])
for z in range(chunk_n_z):
self.chunks[x][y].append(None)
def generate(self, seed: int=None, sea_plate_height: int = 50, continental_plate_height: int = 200):
if seed is None:
seed = random.randrange(2**32)
seed = 229805811
print('Generation seed is %i' % seed)
random.seed(seed)
np.random.seed(seed)
node_n = self.chunk_n_x + self.chunk_n_y
total_x = self.chunk_n_x * self.chunk_size_x
total_y = self.chunk_n_y * self.chunk_size_y
nodes = []
for _ in range(node_n):
nodes.append([random.randint(0, total_x - 1), random.randint(0, total_y - 1)])
# connections = np.random.randint(2, 5, len(nodes)) #np.zeros(len(nodes)) + 3
connections = (np.abs(np.random.normal(0, 5, len(nodes))) + 2).astype(np.int)
def calc_min_vector(start, end):
dx = end[0] - start[0]
wrapped_dx = dx % total_x
dy = end[1] - start[1]
wrapped_dy = dy % total_y
vector = np.array([dx, dy])
if wrapped_dx < abs(dx):
vector[0] = wrapped_dx
if wrapped_dy < abs(dy):
vector[1] = wrapped_dy
return vector
def is_intersecting_any(start, end, edges):
vec1 = calc_min_vector(start, end)
for (start2_index, end2_index) in edges:
start2 = nodes[start2_index]
end2 = nodes[end2_index]
vec2 = calc_min_vector(start2, end2)
norm1 = vec1 / np.sqrt(np.sum(np.square(vec1)))
norm2 = vec2 / np.sqrt(np.sum(np.square(vec2)))
# parrallel
parallel_threshold = 0.0001
if np.sqrt(np.sum(np.square(norm1 - norm2))) < parallel_threshold or np.sqrt(np.sum(np.square(norm1 + norm2))) < parallel_threshold:
t = (start[0] - start2[0]) / vec2[0]
t2 = (end[0] - start2[0]) / vec2[0]
s = (start2[0] - start[0]) / vec1[0]
s2 = (end2[0] - start[0]) / vec1[0]
if (start2[1] + t * vec2[1]) - start[1] < parallel_threshold:
if (0 <= t <= 1.0 and 0 <= t2 <= 1.0) or (0 <= s <= 1.0 and 0 <= s2 <= 1.0):
return True
else:
if start != start2 and end != end2 and end != start2 and start != end2:
t = (vec1[0] * start[1] + vec1[1] * start2[0] - vec1[1] * start[0] - start2[1] * vec1[0]) / (vec2[1] * vec1[0] - vec2[0] * vec1[1])
if 0 <= t <= 1.0:
intersection = np.array(start2) + vec2 * t
s = (intersection[0] - start[0]) / vec1[0]
if 0 <= s <= 1.0:
return True
return False
for index, node in enumerate(nodes):
distances = []
for other_index, other_node in enumerate(nodes):
if node != other_node and (index, other_index) not in self.fault_lines and\
(other_index, index) not in self.fault_lines:
if (not is_intersecting_any(node, other_node, self.fault_lines)) and (not is_intersecting_any(other_node, node, self.fault_lines)):
distances.append((other_index, np.sqrt(np.sum(np.square(calc_min_vector(node, other_node))))))
distances.sort(key=lambda element: element[1])
while connections[index] > 0 and len(distances) > 0:
self.fault_lines.append((index, distances[0][0]))
connections[distances[0][0]] -= 1
connections[index] -= 1
distances.pop(0)
self.fault_nodes = nodes
plates = np.zeros((total_x, total_y))
faults = np.zeros((total_x, total_y)) - 1
plate_bordering_fault = {}
# draw fault lines
for fault_index, fault_line in enumerate(self.fault_lines):
start = self.fault_nodes[fault_line[0]]
end = self.fault_nodes[fault_line[1]]
vector = calc_min_vector(start, end)
vector = vector / np.sqrt(np.sum(np.square(vector)))
point = np.array(start, dtype=np.float)
plate_bordering_fault[fault_index] = []
while np.sqrt(np.sum(np.square(point - np.array(end)))) > 0.5:
plates[int(point[0]), int(point[1])] = -1
if faults[int(point[0]), int(point[1])] == -1:
faults[int(point[0]), int(point[1])] = fault_index
elif faults[int(point[0]), int(point[1])] != fault_index:
faults[int(point[0]), int(point[1])] = -2
point += 0.5 * vector
point[0] %= total_x
point[1] %= total_y
self.faults = faults
plate = 1
while np.any(plates == 0):
start = np.where(plates == 0)
start = (start[0][0], start[1][0])
plates[start] = plate
work_list = [start]
while len(work_list) > 0:
work = work_list.pop()
up = (work[0], (work[1] + 1) % total_y)
down = (work[0], (work[1] - 1) % total_y)
left = ((work[0] - 1) % total_x, work[1])
right = ((work[0] + 1) % total_x, work[1])
if plates[up] == -1 and plates[down] == -1 and plates[left] == -1 and plates[right] == -1:
plates[work] = -1
continue
if plates[up] <= 0:
if plates[up] == 0:
work_list.append(up)
plates[up] = plate
if plates[down] <= 0:
if plates[down] == 0:
work_list.append(down)
plates[down] = plate
if plates[left] <= 0:
if plates[left] == 0:
work_list.append(left)
plates[left] = plate
if plates[right] <= 0:
if plates[right] == 0:
work_list.append(right)
plates[right] = plate
if faults[up] > -1:
if plate not in plate_bordering_fault[faults[up]]:
plate_bordering_fault[faults[up]].append(plate)
if faults[down] > -1:
if plate not in plate_bordering_fault[faults[down]]:
plate_bordering_fault[faults[down]].append(plate)
if faults[left] > -1:
if plate not in plate_bordering_fault[faults[left]]:
plate_bordering_fault[faults[left]].append(plate)
if faults[right] > -1:
if plate not in plate_bordering_fault[faults[right]]:
plate_bordering_fault[faults[right]].append(plate)
plate += 1
plate_num = plate
for plate in range(1, plate_num):
if np.sum(plates == plate) < 20:
plates[plates == plate] = -1
for key, item in plate_bordering_fault.items():
if plate in item:
item.remove(plate)
directions = np.zeros((total_x, total_y, 3))
coords = np.zeros((total_x, total_y, 2))
for x in range(total_x):
for y in range(total_y):
coords[x, y, 0] = x
coords[x, y, 1] = y
for fault_index, fault_line in enumerate(self.fault_lines):
start = self.fault_nodes[fault_line[0]]
end = self.fault_nodes[fault_line[1]]
vector = calc_min_vector(start, end)
vector = vector / np.sqrt(np.sum(np.square(vector)))
perpendicular = np.array([vector[1], -vector[0]])
if len(plate_bordering_fault[fault_index]) == 2:
for plate in plate_bordering_fault[fault_index]:
vecs = coords - np.array(start)
lengths = np.sqrt(np.sum(np.square(vecs), axis=2, keepdims=True))
norm_vecs = vecs / lengths
scalars = np.sum(norm_vecs * perpendicular, axis=2, keepdims=True)
scalars[lengths == 0] = 0
end_vecs = coords - np.array(end)
end_lengths = np.sqrt(np.sum(np.square(end_vecs), axis=2, keepdims=True))
end_min_length = np.min(end_lengths[np.logical_and(plates == plate, end_lengths[:, :, 0] > 0)])
end_min_length_scalar = scalars[np.logical_and(plates == plate, end_lengths[:, :, 0] == end_min_length)][0, 0]
min_length = np.min(lengths[np.logical_and(plates == plate, lengths[:, :, 0] > 0)])
min_length_scalar = scalars[np.logical_and(plates == plate, lengths[:, :, 0] == min_length)][0, 0]
mean_scalar = np.mean(scalars[plates == plate])
if (min_length_scalar / abs(min_length_scalar)) == (end_min_length_scalar / abs(end_min_length_scalar)):
scalar = min_length_scalar
else:
if (min_length_scalar / abs(min_length_scalar)) == (mean_scalar / abs(mean_scalar)):
scalar = min_length_scalar
else:
scalar = end_min_length_scalar
directions[plates == plate, :2] += perpendicular * (scalar / abs(scalar))
pass
for x in range(total_x):
for y in range(total_y):
if plates[x, y] == -1:
plate = np.max(plates[x - 1: x + 1, y - 1: y + 1])
plates[x, y] = plate
self.plates = plates
self.directions = directions
self.num_plates = plate_num
# max height will be three times the continental height
# sea level will be at one and a half time continental height
# with the continental plates top end ending there
# sea plates will be flush at the bottom end
max_height = 3 * continental_plate_height
sea_level = int(1.5 * continental_plate_height)
lower_level = sea_level - continental_plate_height
upper_sea_plate_level = lower_level + sea_plate_height
# stone kinds: 0: lava/air, 1: sea_plate, 2: magmatic_continental, 3: metamorph, 4: sedimental_rock, 5: sediment
self.stone = np.zeros((total_x, total_y, max_height), np.int)
plate_to_type = {}
for plate in range(1, plate_num):
if random.randint(1, 2) == 1:
self.stone[plates == plate, lower_level:upper_sea_plate_level] = SEA_PLATE_STONE
plate_to_type[plate] = SEA_PLATE
else:
self.stone[plates == plate, lower_level:sea_level] = MAGMATIC_STONE
plate_to_type[plate] = CONTINENTAL_PLATE
pass
def set_color(self, x: int, y: int, z: int, r: float, g: float, b: float):
x = x % (self.chunk_size_x * self.chunk_n_x)
y = y % (self.chunk_size_y * self.chunk_n_y)
z = z % (self.chunk_size_z * self.chunk_n_z)
chunk_x = int(x / self.chunk_size_x)
chunk_y = int(y / self.chunk_size_y)
chunk_z = int(z / self.chunk_size_z)
if self.chunks[chunk_x][chunk_y][chunk_z] is not None:
self.chunks[chunk_x][chunk_y][chunk_z].set_color(x % self.chunk_size_x,
y % self.chunk_size_y,
z % self.chunk_size_z,
r, g, b)
else:
print('Changing color of nonexistant element!')
def put_object(self, x: int, y: int, z: int, new_object: Object):
x = x % (self.chunk_size_x * self.chunk_n_x)
y = y % (self.chunk_size_y * self.chunk_n_y)
z = z % (self.chunk_size_z * self.chunk_n_z)
chunk_x = int(x / self.chunk_size_x)
chunk_y = int(y / self.chunk_size_y)
chunk_z = int(z / self.chunk_size_z)
if self.chunks[chunk_x][chunk_y][chunk_z] is None:
self.chunks[chunk_x][chunk_y][chunk_z] = WorldChunk(self.chunk_size_x, self.chunk_size_y, self.chunk_size_z, self.programs)
self.chunks[chunk_x][chunk_y][chunk_z].x_offset = chunk_x * self.chunk_size_x
self.chunks[chunk_x][chunk_y][chunk_z].y_offset = chunk_y * self.chunk_size_z
self.chunks[chunk_x][chunk_y][chunk_z].z_offset = chunk_z * self.chunk_size_y
carry_overs = self.chunks[chunk_x][chunk_y][chunk_z].put_object(x % self.chunk_size_x,
y % self.chunk_size_y,
z % self.chunk_size_z,
new_object)
for carry_over in carry_overs:
if self.chunks[(chunk_x + carry_over[0]) % self.chunk_n_x][(chunk_y + carry_over[1]) % self.chunk_n_y][(chunk_z + carry_over[2]) % self.chunk_n_z] is not None:
self.chunks[
(chunk_x + carry_over[0]) % self.chunk_n_x][
(chunk_y + carry_over[1]) % self.chunk_n_y][
(chunk_z + carry_over[2]) % self.chunk_n_z].apply_visible_carry_over(
(x + carry_over[0]) % self.chunk_size_x,
(y + carry_over[1]) % self.chunk_size_y,
(z + carry_over[2]) % self.chunk_size_z,
carry_over[3])
self.chunks[
(chunk_x + carry_over[0]) % self.chunk_n_x][
(chunk_y + carry_over[1]) % self.chunk_n_y][
(chunk_z + carry_over[2]) % self.chunk_n_z].dirty = True
visibility = 6
neighbour = self.get_object(x - 1, y, z)
if neighbour is not None:
visibility -= 1
neighbour = self.get_object(x + 1, y, z)
if neighbour is not None:
visibility -= 1
neighbour = self.get_object(x, y - 1, z)
if neighbour is not None:
visibility -= 1
neighbour = self.get_object(x, y + 1, z)
if neighbour is not None:
visibility -= 1
neighbour = self.get_object(x, y, z - 1)
if neighbour is not None:
visibility -= 1
neighbour = self.get_object(x, y, z + 1)
if neighbour is not None:
visibility -= 1
self.chunks[chunk_x][chunk_y][chunk_z].set_visibility(x % self.chunk_size_x,
y % self.chunk_size_y,
z % self.chunk_size_z,
visibility)
self.chunks[chunk_x][chunk_y][chunk_z].dirty = True
def get_object(self, x: int, y: int, z: int):
x = x % (self.chunk_size_x * self.chunk_n_x)
y = y % (self.chunk_size_y * self.chunk_n_y)
z = z % (self.chunk_size_z * self.chunk_n_z)
chunk_x = int(x / self.chunk_size_x)
chunk_y = int(y / self.chunk_size_y)
chunk_z = int(z / self.chunk_size_z)
if self.chunks[chunk_x][chunk_y][chunk_z] is None:
return None
return self.chunks[chunk_x][chunk_y][chunk_z].get_object(x % self.chunk_size_x,
y % self.chunk_size_y,
z % self.chunk_size_z)
def render(self, proj_matrix, geometry_rot_matrix, alternate_programs=None,
preselected_program=None, projection_pos=None, rot_pos=None):
if preselected_program is not None:
for x in range(self.chunk_n_x):
for y in range(self.chunk_n_y):
for z in range(self.chunk_n_z):
if self.chunks[x][y][z] is not None:
self.chunks[x][y][z].render(proj_matrix,
geometry_rot_matrix, alternate_programs,
preselected_program, projection_pos, rot_pos)
else:
for _, program_id in self.programs.items():
if alternate_programs == None:
used_program_id = program_id
else:
assert program_id in alternate_programs.keys()
used_program_id = alternate_programs[program_id]
glUseProgram(used_program_id)
self.check_error("Renderingprogram is not initialized!")
projection = glGetUniformLocation(used_program_id, 'projModelViewMatrix')
rot = glGetUniformLocation(used_program_id, 'rotMatrix')
glUniformMatrix3fv(rot, 1, GL_FALSE, np.array(geometry_rot_matrix))
glUniformMatrix4fv(projection, 1, GL_FALSE, np.array(proj_matrix))
for x in range(self.chunk_n_x):
for y in range(self.chunk_n_y):
for z in range(self.chunk_n_z):
if self.chunks[x][y][z] is not None:
self.chunks[x][y][z].render(proj_matrix,
geometry_rot_matrix, alternate_programs,
used_program_id, projection, rot)
def add_light(self, x: float, y: float, z: float, l: Light)-> Light:
x = x % (self.chunk_size_x * self.chunk_n_x)
y = y % (self.chunk_size_y * self.chunk_n_y)
z = z % (self.chunk_size_z * self.chunk_n_z)
chunk_x = int(x / self.chunk_size_x)
chunk_y = int(y / self.chunk_size_y)
chunk_z = int(z / self.chunk_size_z)
if self.chunks[chunk_x][chunk_y][chunk_z] is None:
self.chunks[chunk_x][chunk_y][chunk_z] = WorldChunk(self.chunk_size_x, self.chunk_size_y, self.chunk_size_z, self.programs)
self.chunks[chunk_x][chunk_y][chunk_z].x_offset = chunk_x * self.chunk_size_x
self.chunks[chunk_x][chunk_y][chunk_z].y_offset = chunk_y * self.chunk_size_z
self.chunks[chunk_x][chunk_y][chunk_z].z_offset = chunk_z * self.chunk_size_y
self.chunks[chunk_x][chunk_y][chunk_z].lights.append(l)
l.pos = [x, y, z]
return l
def remove_light(self, l: Light):
chunk_x = int(l.pos[0] / self.chunk_size_x)
chunk_y = int(l.pos[1] / self.chunk_size_y)
chunk_z = int(l.pos[2] / self.chunk_size_z)
if self.chunks[chunk_x][chunk_y][chunk_z] is None:
return False
if l in self.chunks[chunk_x][chunk_y][chunk_z].lights:
self.chunks[chunk_x][chunk_y][chunk_z].lights.remove(l)
return True
else:
return False
def move_light(self, l: Light, target_x: float, target_y: float, target_z: float):
self.remove_light(l)
self.add_light(target_x, target_y, target_z, l)
def get_lights_to_render(self, pos, distance):
distance_x = math.ceil(float(distance) / self.chunk_size_x)
distance_y = math.ceil(float(distance) / self.chunk_size_y)
distance_z = math.ceil(float(distance) / self.chunk_size_z)
pos_x = int(pos[0] / self.chunk_size_x)
pos_y = int(pos[1] / self.chunk_size_y)
pos_z = int(pos[2] / self.chunk_size_z)
lights = []
for x in range(distance_x):
for y in range(distance_y):
for z in range(distance_z):
chunk = self.chunks[(pos_x + x) % self.chunk_n_x][(pos_y + y) % self.chunk_n_y][(pos_z + z) % self.chunk_n_z]
if chunk is not None:
lights += chunk.lights
return lights