471 lines
20 KiB
Python
471 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])
|