VoxelEngine/Client/Client.py
2021-02-13 21:31:39 +01:00

472 lines
20 KiB
Python

from OpenGL.GL import *
import numpy as np
from OpenGL.GL.ARB.vertex_array_object import glDeleteVertexArrays
from OpenGL.GL.framebufferobjects import glBindRenderbuffer
from OpenGL.GLUT import *
import OpenGL.GLUT.freeglut
from OpenGL.GLU import *
from OpenGL.GL import *
from ctypes import sizeof, c_float, c_void_p, c_uint
from Lights.Spotlight.Spotlight import Spotlight
from WorldProvider.WorldProvider import WorldProvider
from MatrixStuff.Transformations import perspectiveMatrix, lookAt, translate, rotate
from Objects.Cube.Cube import Cube
from Objects.Cuboid.Cuboid import Cuboid
from Objects.World import World
import json
import random
import time
from scipy.signal import convolve
MAX_DISTANCE = 200.0
FRICTION_COEFFICENT = 1
EPSILON = 0.00001
def value_to_color(v, min_value, max_value):
r = g = b = 0.0
scope = max_value - min_value
normalized = (v - min_value) / (max_value - min_value)
if 0.5 * scope + min_value != 0:
b = max(0, 1.0 - abs(2.0 * normalized))
g = max(0, 1.0 - abs(2.0 * normalized - 1.0))
r = max(0, 1.0 - abs(2.0 * normalized - 2.0))
l = np.sqrt((r * r + b * b + g * g))
r /= l
g /= l
b /= l
return r, g, b
class Client:
def __init__(self, test=False, pos=[0, 0, 0]):
self.state = 0
with open('./config.json', 'r') as f:
self.config = json.load(f)
glutInit(sys.argv)
self.width = 1920
self.height = 1080
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH)
glutInitWindowSize(self.width, self.height)
glutCreateWindow(b'Voxelengine')
with open('passthroughvertex.glsl', 'r') as f:
vertex_shader_string = f.read()
self.passthrough_vertex_shader_id = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(self.passthrough_vertex_shader_id, vertex_shader_string)
glCompileShader(self.passthrough_vertex_shader_id)
if glGetShaderiv(self.passthrough_vertex_shader_id, GL_COMPILE_STATUS) != GL_TRUE:
raise RuntimeError(glGetShaderInfoLog(self.passthrough_vertex_shader_id))
with open('vertex.glsl', 'r') as f:
vertex_shader_string = f.read()
self.vertex_shader_id = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(self.vertex_shader_id, vertex_shader_string)
glCompileShader(self.vertex_shader_id)
if glGetShaderiv(self.vertex_shader_id, GL_COMPILE_STATUS) != GL_TRUE:
raise RuntimeError(glGetShaderInfoLog(self.vertex_shader_id))
with open('fragment.glsl', 'r') as f:
fragment_shader_string = f.read()
self.fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(self.fragment_shader_id, fragment_shader_string)
glCompileShader(self.fragment_shader_id)
if glGetShaderiv(self.fragment_shader_id, GL_COMPILE_STATUS) != GL_TRUE:
raise RuntimeError(glGetShaderInfoLog(self.fragment_shader_id))
Cube.initializeShader()
Cuboid.initializeShader()
self.geometry_shaders = {
Cube: Cube.GeometryShaderId,
Cuboid: Cuboid.GeometryShaderId
}
self.normal_program = {}
self.depth_program = {}
for key in self.geometry_shaders.keys():
self.normal_program[key] = glCreateProgram()
glAttachShader(self.normal_program[key], self.vertex_shader_id)
glAttachShader(self.normal_program[key], key.GeometryShaderId)
glAttachShader(self.normal_program[key], self.fragment_shader_id)
glLinkProgram(self.normal_program[key])
self.depth_program[self.normal_program[key]] = Spotlight.getDepthProgram(self.vertex_shader_id,
key.GeometryShaderId)
self.world_provider = WorldProvider(self.normal_program)
for x_pos in range(0, 100):
for y_pos in range(0, 100):
for z_pos in range(0, 1):
self.world_provider.world.put_object(x_pos, y_pos, z_pos, Cuboid().setColor(
random.randint(0, 100) / 100.0, random.randint(0, 100) / 100.0, random.randint(0, 100) / 100.0))
self.projMatrix = perspectiveMatrix(45.0, 400 / 400, 0.01, MAX_DISTANCE)
self.rx = self.cx = self.cy = 0
self.opening = 45
glutReshapeFunc(self.resize)
glutDisplayFunc(self.display)
glutKeyboardFunc(self.keyboardHandler)
glutSpecialFunc(self.funcKeydHandler)
self.pos = pos
self.time = time.time()
self.field = (100, 100, 1)
self.e_a = np.array([
# [0, 0, 0],
[1, 0, 0],
[1, 1, 0],
[0, 1, 0],
[-1, 1, 0],
[-1, 0, 0],
[-1, -1, 0],
[0, -1, 0],
[1, -1, 0],
])
self.relaxation_time = 0.55 # 0.55
self.w_a = [
4.0 / 9.0,
1.0 / 9.0,
1.0 / 36.0,
1.0 / 9.0,
1.0 / 36.0,
1.0 / 9.0,
1.0 / 36.0,
1.0 / 9.0,
1.0 / 36.0
]
self.n_a = np.zeros((len(self.e_a),) + self.field)
self.n_a_eq = np.zeros(self.n_a.shape)
self.n = np.zeros(self.field, np.int)
self.n[:, 50:, :] += 1
self.viscosity = np.reshape(self.n * 0.01, self.field + (1,))
self.gravity_applies = np.zeros(self.field)
# self.n /= np.sum(self.n)
self.n_a[0] = np.array(self.n)
self.u = np.zeros(self.field + (self.e_a.shape[1],))
self.f = np.zeros(self.field + (self.e_a.shape[1],))
self.true_pos = np.zeros(self.field + (self.e_a.shape[1],))
self.pos_indices = np.zeros(self.field + (self.e_a.shape[1],), dtype=np.int)
for x in range(self.field[0]):
for y in range(self.field[1]):
for z in range(self.field[2]):
self.pos_indices[x, y, z, :] = [x, y, z]
self.compressible = True
self.max_n = self.w_a[0]
self.test_pixel = [40, 50, 0]
if not test:
glutMainLoop()
else:
self.display()
self.resize(100, 100)
def display(self):
glClearColor(0, 0, 0, 0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
projMatrix = perspectiveMatrix(45, float(self.width) / float(self.height), 0.01, MAX_DISTANCE)
world: World = self.world_provider.world
lights = world.get_lights_to_render(self.pos, self.config['render_light_distance'])
for light in lights:
light.prepareForDepthMapping()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
light_mat = translate(light.pos[0], light.pos[1], light.pos[2]) * \
lookAt(0, 0, 0, -light.pos[0], -light.pos[1], -light.pos[2], 0, 1, 0) * \
perspectiveMatrix(90, float(light.map_size) / float(light.map_size), 0.01, MAX_DISTANCE)
for obj_type, program_id in self.depth_program.items():
glUseProgram(program_id)
widthid = glGetUniformLocation(program_id, 'width')
heightid = glGetUniformLocation(program_id, 'height')
nearid = glGetUniformLocation(program_id, 'near')
farid = glGetUniformLocation(program_id, 'far')
glUniform1f(nearid, 0.01)
glUniform1f(farid, 100)
glUniform1f(widthid, light.map_size)
glUniform1f(heightid, light.map_size)
world.render(light_mat, rotate(0, 0, 0), self.depth_program)
glFlush()
light.finishDepthMapping()
glClearColor(0, 0, 0, 0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glClearColor(0, 0, 0, 0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
for obj_type, program_id in self.normal_program.items():
glUseProgram(program_id)
widthid = glGetUniformLocation(program_id, 'width')
heightid = glGetUniformLocation(program_id, 'height')
nearid = glGetUniformLocation(program_id, 'near')
farid = glGetUniformLocation(program_id, 'far')
glUniform1f(nearid, 0.01)
glUniform1f(farid, 100)
glUniform1f(widthid, self.width)
glUniform1f(heightid, self.height)
world.render(translate(self.pos[0], self.pos[1], self.pos[2]) * lookAt(0, 0, 0, 0, 0, -self.pos[2], 0, 1,
0) * projMatrix, rotate(0, 0, 0))
glFlush()
glutSwapBuffers()
min_value = 0
max_value_n = np.max(self.n)
# max_value_n = 1.0
vel = np.sqrt(np.sum(np.square(self.u), axis=3)) * self.n
max_value_vel = np.max(vel)
# max_value_vel = np.sqrt(3)
print('round')
print('sum n: %f' % np.sum(self.n))
print('max n: %f' % np.max(self.n))
print('min n: %f' % np.min(self.n))
print('sum vel: %f' % np.sum(vel))
print('max vel: %f' % np.max(vel))
print('min vel: %f' % np.min(vel))
for x_pos in range(0, 100):
for y_pos in range(0, 100):
for z_pos in range(0, 1):
if self.state == 2:
r, g, b = value_to_color(int(self.gravity_applies[x_pos, y_pos, z_pos]), 0, 1)
if self.state == 1:
r, g, b = value_to_color(vel[x_pos, y_pos, z_pos], min_value, max_value_vel)
if self.state == 0:
r, g, b = value_to_color(self.n[x_pos, y_pos, z_pos], min_value, max_value_n)
self.world_provider.world.set_color(x_pos, y_pos, z_pos, r, g, b)
self.world_provider.world.set_color(int(round(self.test_pixel[0])),
int(round(self.test_pixel[1])),
int(round(self.test_pixel[2])), 1.0, 1.0, 1.0)
new_f = np.zeros(self.f.shape)
# viscosity
neighbours_filter = np.zeros((3, 3, 3), np.int) + 1
neighbours = convolve(np.pad(self.n, 1, constant_values=0), neighbours_filter, mode='valid') * self.n
neighbours = np.reshape(neighbours, self.field + (1,))
forces_to_share_per_neighbor = self.f * self.viscosity
length = np.sqrt(np.sum(np.square(forces_to_share_per_neighbor), axis=3, keepdims=True))
direction = forces_to_share_per_neighbor / length
direction[(length == 0)[:, :, :, 0]] = 0
############################## experimental
for a in range(len(self.e_a)):
unit = self.e_a[a] / np.sqrt(np.sum(np.square(self.e_a[a])))
scalar = np.sum(direction * unit, axis=3, keepdims=True)
altered_direction = direction + scalar * unit
altered_length = np.sqrt(np.sum(np.square(altered_direction), axis=3, keepdims=True))
altered_direction = altered_direction / altered_length
altered_direction[(altered_length == 0)[:, :, :, 0]] = 0
f_2_add = length * altered_direction
new_f[max(0, self.e_a[a][0]):min(new_f.shape[0], new_f.shape[0] + self.e_a[a][0]),
max(0, self.e_a[a][1]):min(new_f.shape[1], new_f.shape[1] + self.e_a[a][1]),
max(0, self.e_a[a][2]):min(new_f.shape[2], new_f.shape[2] + self.e_a[a][2])] += f_2_add[
max(0, -self.e_a[a][0]):min(
new_f.shape[0],
new_f.shape[0] -
self.e_a[a][0]),
max(0, -self.e_a[a][1]):min(
new_f.shape[1],
new_f.shape[1] -
self.e_a[a][1]),
max(0, -self.e_a[a][2]):min(
new_f.shape[2],
new_f.shape[2] -
self.e_a[a][2])]
new_f += self.f - forces_to_share_per_neighbor * neighbours
#########################################
new_f += self.f
# TODO movement generating things
# gravity
new_f[:, :, :, 1] -= 1.0 * self.n
# clean up
new_f[self.n == 0] = 0
self.f = new_f
# collision and movement
collision = True
iterations = 0
while collision:
f_length = np.sqrt(np.sum(np.square(self.f), axis=3, keepdims=True))
f_direction = self.f / f_length
f_direction[f_length[:, :, :, 0] == 0] = 0
velocity = f_direction * np.sqrt(f_length) / np.reshape(self.n, self.field + (1,)) # TODO replace self.n by mass
velocity[self.n == 0] = 0
timestep = min(1, 0.5 / np.max(np.sqrt(np.sum(np.square(velocity), axis=3))))
if iterations > 20:
print('Takes too long!')
timestep /= 10
new_pos = self.true_pos + velocity * timestep
new_pos_round = np.round(new_pos)
moved = np.logical_or.reduce(new_pos_round != [0, 0, 0], axis=3)
pos_change_targets = new_pos_round[moved] + self.pos_indices[moved]
# handle bordercases
bordercase = np.array(moved)
bordercase[moved] = np.logical_or(
np.logical_or(np.logical_or(0 > pos_change_targets[:, 0], pos_change_targets[:, 0] >= self.field[0]),
np.logical_or(0 > pos_change_targets[:, 1], pos_change_targets[:, 1] >= self.field[1])),
np.logical_or(0 > pos_change_targets[:, 2], pos_change_targets[:, 2] >= self.field[2]))
self.f[bordercase] *= -1
velocity[bordercase] *= -1
# recalculate targets
new_pos = self.true_pos + velocity * timestep
new_pos_round = np.round(new_pos)
new_pos_target = new_pos_round + self.pos_indices
contenders = np.zeros(self.field)
starts = self.pos_indices[self.n != 0]
targets = new_pos_target[self.n != 0].astype(np.int)
speeds = velocity[self.n != 0]
forces = new_f[self.n != 0]
max_speeds = np.zeros(self.field)
fast_pos = {}
is_stayer = []
for index in range(len(targets)):
target = targets[index]
speed = np.sqrt(np.sum(np.square(speeds[index])))
contenders[target[0], target[1], target[2]] += 1
# new max speed and we do not update stayers
if speed > max_speeds[target[0], target[1], target[2]] and tuple(target) not in is_stayer:
max_speeds[target[0], target[1], target[2]] = speed
fast_pos[tuple(target)] = index
# atoms that are already there are there (and stay there) are the fastest
start = starts[index]
if np.all(start == target):
fast_pos[tuple(target)] = index
is_stayer.append(tuple(target))
collision = np.max(contenders) > 1
# we only need collision if there is one
if collision:
# go through the movers again
for index in range(len(targets)):
target = targets[index]
# collision here?
if contenders[target[0], target[1], target[2]] > 1:
# the fastest one does not need to do anything
if index != fast_pos[tuple(target)]:
force = forces[index]
fastest = fast_pos[tuple(target)]
# TODO use relative weight?
forces[fastest] += 0.5*force
forces[index] *= 0.5
new_f[self.n != 0] = forces
self.f = new_f
iterations += 1
# final calculation
f_length = np.sqrt(np.sum(np.square(self.f), axis=3, keepdims=True))
f_direction = self.f / f_length
f_direction[f_length[:, :, :, 0] == 0] = 0
velocity = f_direction * np.sqrt(f_length) / np.reshape(self.n, self.field + (1,)) # TODO replace self.n by mass
velocity[self.n == 0] = 0
#timestep = min(1, 0.5 / np.max(np.sqrt(np.sum(np.square(velocity), axis=3))))
new_pos = self.true_pos + velocity * timestep
new_pos_round = np.round(new_pos)
moved = np.logical_or.reduce(new_pos_round[:, :, :] != [0, 0, 0], axis=3)
not_moved = np.logical_and.reduce(new_pos_round[:, :, :] == [0, 0, 0], axis=3)
pos_change_targets = (new_pos_round[moved] + self.pos_indices[moved]).astype(np.int)
movers = self.pos_indices[moved]
print('timestep: %f' % timestep)
update_n = np.zeros(self.n.shape, np.int)
update_f = np.zeros(self.f.shape)
update_u = np.zeros(self.u.shape)
update_true_pos = np.zeros(self.true_pos.shape)
update_n[not_moved] = self.n[not_moved]
update_f[not_moved, :] = self.f[not_moved, :]
update_u[not_moved, :] = velocity[not_moved, :]
update_true_pos[not_moved, :] = new_pos[not_moved, :]
for indice in range(len(movers)):
mover = movers[indice]
target = pos_change_targets[indice]
update_n[target[0], target[1], target[2]] = self.n[mover[0], mover[1], mover[2]]
update_f[target[0], target[1], target[2], :] = self.f[mover[0], mover[1], mover[2], :]
update_u[target[0], target[1], target[2], :] = velocity[mover[0], mover[1], mover[2], :]
update_true_pos[target[0], target[1], target[2], :] = new_pos[mover[0], mover[1], mover[2], :] - new_pos_round[mover[0], mover[1], mover[2], :]
self.n = update_n
self.f = update_f
self.u = update_u
self.true_pos = update_true_pos
print(1.0 / (time.time() - self.time))
self.time = time.time()
glutPostRedisplay()
def resize(self, w, h):
w = max(w, 1)
h = max(h, 1)
glViewport(0, 0, w, h)
self.projMatrix = perspectiveMatrix(45.0, float(w) / float(h), 0.01, MAX_DISTANCE)
self.width = w
self.height = h
def keyboardHandler(self, key: int, x: int, y: int):
if key == b'\x1b':
exit()
if key == b'+':
self.rx += 0.25
if key == b'-':
self.rx -= 0.25
if key == b'w':
self.cy += 0.25
if key == b's':
self.cy -= 0.25
if key == b'a':
self.cx -= 0.25
if key == b'd':
self.cx += 0.25
if key == b'q':
self.opening -= 0.25
if key == b'e':
self.opening += 0.25
if key == b'+':
self.state = (self.state + 1) % 3
if key == b'r':
print(self.cx, self.cy, self.opening)
# glutPostRedisplay()
# print(key,x,y)
def funcKeydHandler(self, key: int, x: int, y: int):
if key == 11:
glutFullScreenToggle()
# print(key)
if __name__ == '__main__':
client = Client(pos=[-50, -50, -200])