diff options
Diffstat (limited to 'interacticle/visual')
-rw-r--r-- | interacticle/visual/__init__.py | 5 | ||||
-rw-r--r-- | interacticle/visual/box.py | 67 | ||||
-rw-r--r-- | interacticle/visual/camera.py | 106 | ||||
-rw-r--r-- | interacticle/visual/links.py | 48 | ||||
-rw-r--r-- | interacticle/visual/plot.py | 248 | ||||
-rw-r--r-- | interacticle/visual/shader.py | 28 | ||||
-rw-r--r-- | interacticle/visual/view.py | 242 |
7 files changed, 744 insertions, 0 deletions
diff --git a/interacticle/visual/__init__.py b/interacticle/visual/__init__.py new file mode 100644 index 0000000..a9f9870 --- /dev/null +++ b/interacticle/visual/__init__.py @@ -0,0 +1,5 @@ +from .box import WireBox +from .view import View +from .links import MolecularLinks +from .plot import VelocityHistogram +from .plot import TemperaturePlot diff --git a/interacticle/visual/box.py b/interacticle/visual/box.py new file mode 100644 index 0000000..fb18d84 --- /dev/null +++ b/interacticle/visual/box.py @@ -0,0 +1,67 @@ +from OpenGL.GL import * + +class WireBox: + def __init__(self, size): + self.x0 = 0 + self.x1 = size + self.y0 = 0 + self.y1 = size + self.z0 = 0 + self.z1 = size + + def setup(self): + pass + + def update(self): + pass + + def shutdown(self): + pass + + def display_decoration(self, uniform): + glUniform3fv(uniform['color'], 1, [0.6,0.6,0.6]) + glBegin(GL_LINE_STRIP) + glVertex(self.x0, self.y0, self.z0) + glVertex(self.x0, self.y1, self.z0) + glVertex(self.x0, self.y1, self.z1) + glVertex(self.x0, self.y0, self.z1) + glVertex(self.x0, self.y0, self.z0) + glEnd() + glBegin(GL_LINE_STRIP) + glVertex(self.x1, self.y0, self.z0) + glVertex(self.x1, self.y1, self.z0) + glVertex(self.x1, self.y1, self.z1) + glVertex(self.x1, self.y0, self.z1) + glVertex(self.x1, self.y0, self.z0) + glEnd() + glBegin(GL_LINE_STRIP) + glVertex(self.x0, self.y0, self.z1) + glVertex(self.x1, self.y0, self.z1) + glVertex(self.x1, self.y1, self.z1) + glVertex(self.x0, self.y1, self.z1) + glVertex(self.x0, self.y0, self.z1) + glEnd() + glBegin(GL_LINE_STRIP) + glVertex(self.x0, self.y0, self.z0) + glVertex(self.x1, self.y0, self.z0) + glVertex(self.x1, self.y1, self.z0) + glVertex(self.x0, self.y1, self.z0) + glVertex(self.x0, self.y0, self.z0) + glEnd() + glBegin(GL_LINE_STRIP) + glVertex(self.x0, self.y0, self.z0) + glVertex(self.x1, self.y0, self.z0) + glVertex(self.x1, self.y0, self.z1) + glVertex(self.x0, self.y0, self.z1) + glVertex(self.x0, self.y0, self.z0) + glEnd() + glBegin(GL_LINE_STRIP) + glVertex(self.x0,self.y1,self.z0) + glVertex(self.x1,self.y1,self.z0) + glVertex(self.x1,self.y1,self.z1) + glVertex(self.x0,self.y1,self.z1) + glVertex(self.x0,self.y1,self.z0) + glEnd() + + def display_window(self, uniform): + pass diff --git a/interacticle/visual/camera.py b/interacticle/visual/camera.py new file mode 100644 index 0000000..b1e9650 --- /dev/null +++ b/interacticle/visual/camera.py @@ -0,0 +1,106 @@ +from OpenGL.GL import * +from OpenGL.GLUT import * + +from pyrr import matrix44, quaternion + +import numpy as np + +class Projection: + def __init__(self, distance): + self.distance = distance + self.ratio = 4./3. + self.x = 0 + self.y = 0 + self.update() + + def update(self): + projection = matrix44.create_perspective_projection(20.0, self.ratio, 0.1, 5000.0) + look = matrix44.create_look_at( + eye = [self.x, -self.distance, self.y], + target = [self.x, 0, self.y], + up = [ 0, 0,-1]) + + self.matrix = np.matmul(look, projection) + + def update_ratio(self, width, height, update_viewport = True): + if update_viewport: + glViewport(0,0,width,height) + + self.ratio = width/height + self.update() + + def update_distance(self, change): + self.distance += change + self.update() + + def shift(self, x, y): + self.x -= x + self.y -= y + self.update() + + def get(self): + return self.matrix + +class Rotation: + def __init__(self, shift, x = np.pi, y = np.pi): + self.matrix = matrix44.create_from_translation(shift), + self.rotation_x = quaternion.Quaternion() + self.update(x,y) + + def shift(self, x, y): + self.matrix = np.matmul( + self.matrix, + matrix44.create_from_translation([x,y,0]) + ) + self.inverse_matrix = np.linalg.inv(self.matrix) + + def update(self, x, y): + rotation_x = quaternion.Quaternion(quaternion.create_from_eulers([x,0,0])) + rotation_y = self.rotation_x.conjugate.cross( + quaternion.Quaternion(quaternion.create_from_eulers([0,y,0]))) + self.rotation_x = self.rotation_x.cross(rotation_x) + + self.matrix = np.matmul( + self.matrix, + matrix44.create_from_quaternion(rotation_y.cross(self.rotation_x)) + ) + self.inverse_matrix = np.linalg.inv(self.matrix) + + def get(self): + return self.matrix + + def get_inverse(self): + return self.inverse_matrix + +class MouseDragMonitor: + def __init__(self, button, callback): + self.button = button + self.active = False + self.callback = callback + + def on_mouse(self, button, state, x, y): + if button == self.button: + self.active = (state == GLUT_DOWN) + self.last_x = x + self.last_y = y + + def on_mouse_move(self, x, y): + if self.active: + delta_x = self.last_x - x + delta_y = y - self.last_y + self.last_x = x + self.last_y = y + self.callback(delta_x, delta_y) + +class MouseScrollMonitor: + def __init__(self, callback): + self.callback = callback + + def on_mouse(self, button, state, x, y): + if button == 3: + self.callback(-1.0) + elif button == 4: + self.callback(1.0) + + def on_mouse_move(self, x, y): + pass diff --git a/interacticle/visual/links.py b/interacticle/visual/links.py new file mode 100644 index 0000000..52e9d52 --- /dev/null +++ b/interacticle/visual/links.py @@ -0,0 +1,48 @@ +import numpy as np + +import pyopencl as cl +mf = cl.mem_flags + +import OpenGL.GL as gl +from OpenGL.arrays import vbo + +class MolecularLinks: + def __init__(self, simulation): + self.simulation = simulation + + def setup(self): + if self.simulation.n_bonds > 0: + self.np_view_link_indices = np.zeros((int(np.sum(self.simulation.np_bond_count)*2),1), dtype=np.uint32) + curr = 0 + for i in range(self.simulation.n_atoms): + for j in range(int(self.simulation.np_bond_count[i])): + self.np_view_link_indices[2*curr+0] = i + self.np_view_link_indices[2*curr+1] = self.simulation.np_bond_indices[self.simulation.max_bonds*i+j][0] + curr = curr + 1 + + self.cl_view_link_indices = cl.Buffer(self.simulation.context, mf.COPY_HOST_PTR, hostbuf=self.np_view_link_indices) + self.np_view_link_pos = np.zeros((self.np_view_link_indices.shape[0],4), dtype=np.float32) + self.gl_view_link_pos = vbo.VBO(data=self.np_view_link_pos, usage=gl.GL_DYNAMIC_DRAW, target=gl.GL_ARRAY_BUFFER) + self.gl_view_link_pos.bind() + self.cl_view_link_pos = cl.GLBuffer(self.simulation.context, mf.READ_WRITE, int(self.gl_view_link_pos)) + + def update(self): + pass + + def shutdown(self): + pass + + def display_decoration(self, uniform): + if self.simulation.n_bonds > 0: + cl.enqueue_acquire_gl_objects(self.simulation.queue, [self.cl_view_link_pos]) + self.simulation.program.update_view_links(self.simulation.queue, (int(self.np_view_link_indices.shape[0] / 2),), None, self.simulation.cl_position, self.cl_view_link_indices, self.cl_view_link_pos).wait() + + gl.glEnableClientState(gl.GL_VERTEX_ARRAY) + gl.glUniform3fv(uniform['color'], 1, [0.5,0.5,0.5]) + self.gl_view_link_pos.bind() + gl.glVertexPointer(4, gl.GL_FLOAT, 0, self.simulation.gl_position) + gl.glDrawArrays(gl.GL_LINES, 0, self.np_view_link_indices.shape[0]) + gl.glDisableClientState(gl.GL_VERTEX_ARRAY) + + def display_window(self, uniform): + pass diff --git a/interacticle/visual/plot.py b/interacticle/visual/plot.py new file mode 100644 index 0000000..60b4937 --- /dev/null +++ b/interacticle/visual/plot.py @@ -0,0 +1,248 @@ +from OpenGL.GL import * + +import numpy as np + +import scipy.stats as stats +import scipy.constants as const +from scipy.optimize import minimize + +import matplotlib +import matplotlib.pyplot as plt + +from concurrent.futures import Future, ProcessPoolExecutor + +def get_histogram(velocities): + upper = np.ceil(np.max(velocities) / 100) * 100 + maxwellian = stats.maxwell.fit(velocities) + + plt.style.use('dark_background') + + fig, ax = plt.subplots(1, figsize=(8,4)) + + #plt.ylim(0, 0.004) + plt.ylabel('Probability') + + plt.xlim(0, upper) + plt.xlabel('Velocity magnitude [m/s]') + + plt.hist(velocities, bins=50, density=True, alpha=0.5, label='Simulated velocities') + + xs = np.linspace(0, upper, 100) + ys = stats.maxwell.pdf(xs, *maxwellian) + plt.plot(xs, ys, label='Maxwell-Boltzmann distribution') + + plt.legend(loc='upper right') + + fig.canvas.draw() + buf = fig.canvas.tostring_rgb() + width, height = fig.canvas.get_width_height() + texture = np.frombuffer(buf, dtype=np.uint8).reshape(width, height, 3) + + plt.close(fig) + + return (texture, width, height) + + +class VelocityHistogram: + def __init__(self, gas, origin, extend): + self.gas = gas + self.origin = origin + self.extend = extend + self.steps = -1 + + self.pool = ProcessPoolExecutor(max_workers=1) + self.plotter = None + + self.tick = False + self.mixing = 0.0 + + self.vertices = np.array([ + self.origin[0] , self.origin[1] , 0., 1., + self.origin[0] + self.extend[0], self.origin[1] , 1., 1., + self.origin[0] , self.origin[1] + self.extend[1], 0., 0., + self.origin[0] + self.extend[0], self.origin[1] + self.extend[1], 1., 0. + ], dtype=np.float32) + + def setup(self): + self.vao = glGenVertexArrays(1) + self.vbo = glGenBuffers(1) + + glBindVertexArray(self.vao) + glBindBuffer(GL_ARRAY_BUFFER, self.vbo) + glBufferData(GL_ARRAY_BUFFER, self.vertices.tostring(), GL_STATIC_DRAW) + + glEnableVertexAttribArray(0) + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*np.dtype(np.float32).itemsize, ctypes.c_void_p(0)) + glEnableVertexAttribArray(1) + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*np.dtype(np.float32).itemsize, ctypes.c_void_p(2*np.dtype(np.float32).itemsize)) + + self.texture_id = glGenTextures(2) + + glBindTexture(GL_TEXTURE_2D, self.texture_id[0]) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glBindTexture(GL_TEXTURE_2D, self.texture_id[1]) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + def shutdown(self): + self.pool.shutdown(wait=True) + + def update(self): + self.steps = self.steps + 1 + + if self.steps % 50 == 0 and self.plotter == None: + self.plotter = self.pool.submit(get_histogram, self.gas.get_velocity_norms() * 1000) + + else: + if self.plotter != None and self.plotter.done(): + texture, width, height = self.plotter.result() + if self.tick: + glBindTexture(GL_TEXTURE_2D, self.texture_id[0]) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture) + self.tick = False + self.mixing = 1.0 + + else: + glBindTexture(GL_TEXTURE_2D, self.texture_id[1]) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture) + self.tick = True + self.mixing = 0.0 + + self.plotter = None + + def display_decoration(self, uniform): + pass + + def display_window(self, uniform): + if self.tick: + self.mixing = min(self.mixing+0.05, 1.0); + else: + self.mixing = max(self.mixing-0.05, 0.0); + + glBindTextures(self.texture_id[0], 2, self.texture_id) + glUniform1iv(uniform['picture'], len(self.texture_id), self.texture_id) + glUniform1f(uniform['mixing'], self.mixing) + + glBindVertexArray(self.vao); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4) + glBindVertexArray(0) + +def get_plot(data): + xs = data[0] + ys = data[1] + + plt.style.use('dark_background') + + fig, ax = plt.subplots(1, figsize=(8,4)) + + plt.ylim(0, 350) + plt.ylabel('Temperature [K]') + + plt.xlim(0, 200) + plt.xlabel('Time [ps]') + + plt.plot(xs, ys, label='Temperature') + + plt.legend(loc='upper right') + + fig.canvas.draw() + buf = fig.canvas.tostring_rgb() + width, height = fig.canvas.get_width_height() + texture = np.frombuffer(buf, dtype=np.uint8).reshape(width, height, 3) + + plt.close(fig) + + return (texture, width, height) + +class TemperaturePlot: + def __init__(self, gas, origin, extend): + self.gas = gas + self.origin = origin + self.extend = extend + self.steps = -1 + + self.pool = ProcessPoolExecutor(max_workers=1) + self.plotter = None + + self.tick = False + self.mixing = 0.0 + + self.xs = [] + self.ys = [] + + self.vertices = np.array([ + self.origin[0] , self.origin[1] , 0., 1., + self.origin[0] + self.extend[0], self.origin[1] , 1., 1., + self.origin[0] , self.origin[1] + self.extend[1], 0., 0., + self.origin[0] + self.extend[0], self.origin[1] + self.extend[1], 1., 0. + ], dtype=np.float32) + + def setup(self): + self.vao = glGenVertexArrays(1) + self.vbo = glGenBuffers(1) + + glBindVertexArray(self.vao) + glBindBuffer(GL_ARRAY_BUFFER, self.vbo) + glBufferData(GL_ARRAY_BUFFER, self.vertices.tostring(), GL_STATIC_DRAW) + + glEnableVertexAttribArray(0) + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*np.dtype(np.float32).itemsize, ctypes.c_void_p(0)) + glEnableVertexAttribArray(1) + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*np.dtype(np.float32).itemsize, ctypes.c_void_p(2*np.dtype(np.float32).itemsize)) + + self.texture_id = glGenTextures(2) + + glBindTexture(GL_TEXTURE_2D, self.texture_id[0]) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glBindTexture(GL_TEXTURE_2D, self.texture_id[1]) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + def shutdown(self): + self.pool.shutdown(wait=True) + + def update(self): + self.steps = self.steps + 1 + + if self.steps % 10 == 0 and self.plotter == None: + self.xs.append(self.gas.step * self.gas.tau) + self.ys.append(self.gas.get_temperature()) + self.plotter = self.pool.submit(get_plot, (self.xs, self.ys)) + + else: + if self.plotter != None and self.plotter.done(): + texture, width, height = self.plotter.result() + if self.tick: + glBindTexture(GL_TEXTURE_2D, self.texture_id[0]) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture) + self.tick = False + self.mixing = 1.0 + + else: + glBindTexture(GL_TEXTURE_2D, self.texture_id[1]) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture) + self.tick = True + self.mixing = 0.0 + + self.plotter = None + + def display_decoration(self, uniform): + pass + + def display_window(self, uniform): + if self.tick: + self.mixing = min(self.mixing+0.05, 1.0); + else: + self.mixing = max(self.mixing-0.05, 0.0); + + glBindTextures(self.texture_id[0], 2, self.texture_id) + glUniform1iv(uniform['picture'], len(self.texture_id), self.texture_id) + glUniform1f(uniform['mixing'], self.mixing) + + glBindVertexArray(self.vao); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4) + glBindVertexArray(0) diff --git a/interacticle/visual/shader.py b/interacticle/visual/shader.py new file mode 100644 index 0000000..c6c035b --- /dev/null +++ b/interacticle/visual/shader.py @@ -0,0 +1,28 @@ +from OpenGL.GL import GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER +from OpenGL.GL import shaders + +class Shader: + def __init__(self, fragment_src, vertex_src, uniform): + self.program = shaders.compileProgram( + shaders.compileShader(vertex_src, GL_VERTEX_SHADER), + shaders.compileShader(fragment_src, GL_FRAGMENT_SHADER)) + self.uniform = { } + for name in uniform: + self.uniform[name] = shaders.glGetUniformLocation(self.program, name) + + def use(self): + shaders.glUseProgram(self.program) + +class GeometryShader: + def __init__(self, fragment_src, geometry_src, vertex_src, uniform): + self.program = shaders.compileProgram( + shaders.compileShader(vertex_src, GL_VERTEX_SHADER), + shaders.compileShader(geometry_src, GL_GEOMETRY_SHADER), + shaders.compileShader(fragment_src, GL_FRAGMENT_SHADER)) + self.uniform = { } + for name in uniform: + self.uniform[name] = shaders.glGetUniformLocation(self.program, name) + + def use(self): + shaders.glUseProgram(self.program) + diff --git a/interacticle/visual/view.py b/interacticle/visual/view.py new file mode 100644 index 0000000..14ff487 --- /dev/null +++ b/interacticle/visual/view.py @@ -0,0 +1,242 @@ +from OpenGL.GL import * +from OpenGL.GLUT import * + +from pyrr import matrix44 + +import numpy as np + +from .shader import Shader, GeometryShader +from .camera import Projection, Rotation, MouseDragMonitor, MouseScrollMonitor + +particle_shader = ( + """ + #version 430 + + uniform vec4 camera_pos; + + in GS_OUT + { + vec3 particle_pos; + vec3 atom_color; + vec2 tex_coord; + } fs_in; + + void main(){ + if (length(fs_in.tex_coord - vec2(0.5)) > 0.5) { + discard; + } + + vec3 n = vec3(fs_in.tex_coord - vec2(0.5), 0.); + n.z = -sqrt(1 - length(n))*0.6; + n = normalize(n); + + vec3 dir = normalize(camera_pos.xyz - fs_in.particle_pos); + vec3 color = fs_in.atom_color * dot(dir, n); + + gl_FragColor = vec4(color.xyz, 1.0); + } + """, + """ + #version 430 + + layout (points) in; + layout (triangle_strip, max_vertices=4) out; + + out GS_OUT + { + vec3 particle_pos; + vec3 atom_color; + vec2 tex_coord; + } gs_out; + + uniform mat4 projection; + uniform mat4 rotation; + + uniform vec4 camera_pos; + + vec4 project(vec3 v) { + return projection * rotation * vec4(v, 1.); + } + + void emitSquareAt(vec3 position, float mass) { + mat4 m = projection * rotation; + vec3 camera_right = normalize(vec3(m[0][0], m[1][0], m[2][0])); + vec3 camera_up = normalize(vec3(m[0][1], m[1][1], m[2][1])); + + gs_out.particle_pos = project(position).xyz; + gs_out.atom_color = float(mass == 39.948) * vec3(1,0,1) * 0.7 + + float(mass == 20.18) * vec3(0.62,0.45,0.79) + + float(mass == 12.011) * vec3(0.5) + + float(mass == 15.9992) * vec3(1,0,0) * 0.6 + + float(mass == 1.008) * vec3(1) * 0.7 + + float(mass == 1.0079) * vec3(1) * 0.7; + + float r = 0.02 * pow(mass, 0.2); + + gl_Position = project(position + camera_right * -r + camera_up * -r); + gs_out.tex_coord = vec2(0.,0.); + EmitVertex(); + gl_Position = project(position + camera_right * r + camera_up * -r); + gs_out.tex_coord = vec2(1.,0.); + EmitVertex(); + gl_Position = project(position + camera_right * -r + camera_up * r); + gs_out.tex_coord = vec2(0.,1.); + EmitVertex(); + gl_Position = project(position + camera_right * r + camera_up * r); + gs_out.tex_coord = vec2(1.,1.); + EmitVertex(); + } + + void main() { + emitSquareAt(gl_in[0].gl_Position.xyz, gl_in[0].gl_Position.w); + EndPrimitive(); + } + """, + """ + #version 430 + + layout (location=0) in vec4 particle_pos; + + void main() { + gl_Position = particle_pos; + } + """, + ['projection', 'rotation', 'camera_pos'] +) + +decoration_shader = ( + """ + #version 430 + + uniform vec3 color; + + void main(){ + gl_FragColor = vec4(color, 1); + } + """, + """ + #version 430 + + layout (location=0) in vec3 vertex; + + uniform mat4 projection; + uniform mat4 rotation; + + void main() { + gl_Position = projection * rotation * vec4(vertex.xyz, 1.); + } + """, + ['projection', 'rotation', 'color'] +) + +texture_shader = ( + """ + #version 430 + + in vec2 tex_coord; + + uniform sampler2D picture[2]; + + uniform float mixing; + + void main() { + vec3 color = mix(texture(picture[0], tex_coord), texture(picture[1], tex_coord), mixing).xyz; + if (color == vec3(0.,0.,0.)) { + gl_FragColor = vec4(color, 0.5); + } else { + gl_FragColor = vec4(color, 1.0); + } + } + """, + """ + #version 430 + + layout (location=0) in vec2 screen_vertex; + layout (location=1) in vec2 texture_vertex; + + out vec2 tex_coord; + + uniform mat4 projection; + + void main() { + gl_Position = projection * vec4(screen_vertex, 0.0, 1.0); + tex_coord = texture_vertex; + } + """, + ['picture','projection','mixing'] +) + + +class View: + def __init__(self, simulation, instruments): + self.simulation = simulation + self.instruments = instruments + + self.texture_shader = Shader(*texture_shader) + self.particle_shader = GeometryShader(*particle_shader) + self.decoration_shader = Shader(*decoration_shader) + + self.camera_projection = Projection(distance = 5*simulation.domain_size) + self.camera_rotation = Rotation([-0.5*simulation.domain_size, -0.5*simulation.domain_size, -0.5*simulation.domain_size], np.pi/2, 0) + self.camera_pos = np.matmul([0,-self.camera_projection.distance,0,1], self.camera_rotation.get_inverse()) + + self.mouse_monitors = [ + MouseDragMonitor(GLUT_LEFT_BUTTON, lambda dx, dy: self.camera_rotation.update(0.005*dy, 0.005*dx)), + MouseDragMonitor(GLUT_RIGHT_BUTTON, lambda dx, dy: self.camera_projection.shift(0.005*dx, 0.005*dy)), + MouseScrollMonitor(lambda zoom: self.camera_projection.update_distance(0.1*zoom)) + ] + + self.show_windows = False + self.show_decorations = True + + def reshape(self, width, height): + glViewport(0,0,width,height) + + world_height = 1.4 + world_width = world_height / height * width + + projection = Projection(10) + + projection = matrix44.create_orthogonal_projection(-world_width/2, world_width/2, -world_height/2, world_height/2, -1, 1) + translation = matrix44.create_from_translation([-1.05, -1.0/2, 0]) + + self.camera_projection.update_ratio(width, height) + + self.pixels_per_unit = height / world_height + self.projection = np.matmul(translation, projection) + + + def display(self): + glClearColor(0,0,0,1.) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) + + glEnable(GL_DEPTH_TEST) + glDepthMask(GL_TRUE) + glDepthFunc(GL_LEQUAL) + + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + + self.particle_shader.use() + glUniformMatrix4fv(self.particle_shader.uniform['projection'], 1, False, np.ascontiguousarray(self.camera_projection.get())) + glUniformMatrix4fv(self.particle_shader.uniform['rotation'], 1, False, np.ascontiguousarray(self.camera_rotation.get())) + glUniform4fv(self.particle_shader.uniform['camera_pos'], 1, self.camera_pos) + + self.simulation.gl_draw_particles() + glBindVertexArray(0) + + if self.show_decorations: + self.decoration_shader.use() + glUniformMatrix4fv(self.decoration_shader.uniform['projection'], 1, False, np.ascontiguousarray(self.camera_projection.get())) + glUniformMatrix4fv(self.decoration_shader.uniform['rotation'], 1, False, np.ascontiguousarray(self.camera_rotation.get())) + glLineWidth(3) + for instrument in self.instruments: + instrument.display_decoration(self.decoration_shader.uniform) + + if self.show_windows: + self.texture_shader.use() + glUniformMatrix4fv(self.texture_shader.uniform['projection'], 1, False, np.asfortranarray(self.projection)) + for instrument in self.instruments: + instrument.display_window(self.texture_shader.uniform) + + glutSwapBuffers() |