Spaces:
Build error
Build error
#!/usr/bin/env python | |
# encoding: utf-8 | |
# Copyright (c) 2012 Max Planck Society. All rights reserved. | |
# Created by Matthew Loper on 2012-05-11. | |
""" | |
Mesh visualization and related classes | |
-------------------------------------- | |
This module contains the core visualization tools for meshes. The backend used for visualization | |
is OpenGL. | |
The module itself can be run like the following | |
.. code:: | |
python -m psbody.mesh.meshviewer arguments | |
The following commands are used | |
* ``arguments=TEST_FOR_OPENGL`` a basic OpenGL support is run. This is usually performed | |
on a forked python process. In case OpenGL is not supported, a `DummyClass`` | |
mesh viewer is returned. | |
* ``arguments=title nb_x_axis nb_y_axis width height`` a new window is created | |
.. autosummary:: | |
MeshViewer | |
MeshViewers | |
MeshViewerLocal | |
test_for_opengl | |
""" | |
import sys | |
import os.path | |
import time | |
import copy | |
import numpy as np | |
import traceback | |
from multiprocessing import freeze_support | |
import zmq | |
import re | |
import subprocess | |
import tempfile | |
# this is way too verbose, organize imports better | |
from OpenGL.GL import glPixelStorei, glMatrixMode, glHint, glTexParameterf, glDisableClientState | |
from OpenGL.GL import glBindTexture, glEnableClientState, glPointSize, glEnable | |
from OpenGL.GL import glColor3f, glDisable, glBegin, glEnd, glClearColor, glClearDepth | |
from OpenGL.GL import glNormalPointer, glLineWidth, glTexCoord2f, glTexCoordPointer | |
from OpenGL.GL import glViewport, glLightModeli, glLoadIdentity, glTranslatef, glPushMatrix | |
from OpenGL.GL import glLoadMatrixf, glPopMatrix, glMultMatrixf, glDrawElementsui, glTexEnvf, glGetDoublev | |
from OpenGL.GL import glGenTextures, glTexImage2D, glFrustum, glGenerateMipmap | |
from OpenGL.GL import glBlendFunc, glVertex3f, glVertexPointer, glVertexPointerf, glColorPointerf, glColorPointer | |
from OpenGL.GL import glGetFloatv, glGetIntegerv, glReadPixels | |
from OpenGL.GL import glClear, glFlush, glDepthFunc, glShadeModel | |
from OpenGL.GL import GL_TRUE, GL_FLOAT, GL_POINTS, GL_COLOR_ARRAY, GL_NORMAL_ARRAY, GL_LINE_SMOOTH, GL_MODELVIEW_MATRIX | |
from OpenGL.GL import GL_BLEND, GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_TEXTURE_MAG_FILTER, GL_SMOOTH | |
from OpenGL.GL import GL_TEXTURE_MIN_FILTER, GL_VIEWPORT, GL_LINEAR, GL_COLOR_MATERIAL, GL_LIGHT0, GL_NORMALIZE | |
from OpenGL.GL import GL_PROJECTION, GL_MODELVIEW, GL_VERTEX_SHADER, GL_LIGHTING, GL_TEXTURE_COORD_ARRAY | |
from OpenGL.GL import GL_TEXTURE_ENV, GL_TRIANGLES, GL_LINEAR_MIPMAP_LINEAR, GL_COLOR_CLEAR_VALUE | |
from OpenGL.GL import GL_FRAGMENT_SHADER, GL_NEAREST, GL_UNPACK_ALIGNMENT, GL_RGB, GL_BGR, GL_DECAL, GL_MODULATE | |
from OpenGL.GL import GL_UNSIGNED_BYTE, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_QUADS, GL_POLYGON, GL_VERTEX_ARRAY, GL_DEPTH_COMPONENT | |
from OpenGL.GL import GL_LIGHT_MODEL_TWO_SIDE, GL_GENERATE_MIPMAP_HINT, GL_NICEST, GL_LINES, GL_PROJECTION_MATRIX | |
from OpenGL.GL import GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, GL_LEQUAL, GL_DEPTH_TEST, GL_PERSPECTIVE_CORRECTION_HINT | |
from OpenGL.GL import shaders | |
from OpenGL.GLUT import glutInit, glutDisplayFunc, glutInitDisplayMode, glutInitWindowSize, glutGet, GLUT_WINDOW_WIDTH, GLUT_WINDOW_HEIGHT | |
from OpenGL.GLUT import glutMainLoop, glutPostRedisplay, glutInitWindowPosition, glutCreateWindow, glutTimerFunc, glutSetWindowTitle | |
from OpenGL.GLUT import glutReshapeFunc, glutKeyboardFunc, glutMouseFunc, glutMotionFunc, glutSwapBuffers | |
from OpenGL.GLUT import GLUT_RGBA, GLUT_DOUBLE, GLUT_ALPHA, GLUT_DEPTH | |
from OpenGL.GLUT import GLUT_LEFT_BUTTON, GLUT_DOWN, GLUT_UP, GLUT_RIGHT_BUTTON, GLUT_MIDDLE_BUTTON | |
from OpenGL.GLU import gluPerspective, gluUnProject | |
import OpenGL.arrays.vbo | |
# if this file is processed/run as a python script/standalone, especially from the | |
# internal command | |
if __package__ is not None: | |
from .mesh import Mesh | |
from .geometry.tri_normals import TriNormals | |
from .arcball import ArcBallT, Matrix3fT, Matrix4fT, Point2fT, \ | |
Matrix3fMulMatrix3f, Matrix3fSetRotationFromQuat4f, Matrix4fSetRotationFromMatrix3f | |
from .fonts import get_textureid_with_text | |
# this block is below the previous one to make my linter happy | |
if __package__ is None: | |
print("this file cannot be executed as a standalone python module") | |
print("python -m psbody.mesh.%s arguments" % (os.path.splitext(os.path.basename(__file__))[0])) | |
sys.exit(-1) | |
def _run_self(args, stdin=None, stdout=None, stderr=None): | |
"""Executes this same script module with the given arguments (forking without subprocess dependencies)""" | |
return subprocess.Popen([sys.executable] + | |
['-m'] + ['%s.%s' % (__package__, os.path.splitext(os.path.basename(__file__))[0])] + | |
args, | |
stdin=stdin, | |
stdout=stdout, # if stdout is not None else subprocess.PIPE, | |
stderr=stderr) | |
def _test_for_opengl(): | |
try: | |
# from OpenGL.GLUT import glutInit | |
glutInit() | |
except Exception as e: | |
print(e, file=sys.stderr) | |
print('failure') | |
else: | |
print('success') | |
test_for_opengl_cached = None | |
def test_for_opengl(): | |
"""Tests if opengl is supported. | |
.. note:: the result of the test is cached | |
""" | |
global test_for_opengl_cached | |
if test_for_opengl_cached is None: | |
with open(os.devnull) as dev_null, \ | |
tempfile.TemporaryFile() as out, \ | |
tempfile.TemporaryFile() as err: | |
p = _run_self(["TEST_FOR_OPENGL"], | |
stdin=dev_null, | |
stdout=out, | |
stderr=err) | |
p.wait() | |
out.seek(0) | |
err.seek(0) | |
line = ''.join(out.read().decode()) | |
test_for_opengl_cached = 'success' in line | |
if not test_for_opengl_cached: | |
print('OpenGL test failed: ') | |
print('\tstdout:', line) | |
print('\tstderr:', '\n'.join(err.read().decode())) | |
return test_for_opengl_cached | |
class Dummy(object): | |
def __getattr__(self, name): | |
return Dummy() | |
def __call__(self, *args, **kwargs): | |
return Dummy() | |
def __getitem__(self, key): | |
return Dummy() | |
def __setitem__(self, key, value): | |
pass | |
def MeshViewer(titlebar='Mesh Viewer', | |
static_meshes=None, | |
static_lines=None, | |
uid=None, | |
autorecenter=True, | |
shape=(1, 1), | |
keepalive=False, | |
window_width=1280, | |
window_height=960, | |
snapshot_camera=None): | |
"""Allows visual inspection of geometric primitives. | |
Write-only Attributes: | |
:param titlebar: string printed in the window titlebar | |
:param static_meshes: list of Mesh objects to be displayed | |
:param static_lines: list of Lines objects to be displayed | |
.. note:: `static_meshes` is meant for Meshes that are updated infrequently, | |
`and dynamic_meshes` is for Meshes that are updated frequently | |
(same for `dynamic_lines` vs. `static_lines`). | |
They may be treated differently for performance reasons. | |
""" | |
if not test_for_opengl(): | |
return Dummy() | |
mv = MeshViewerLocal(shape=(1, 1), | |
uid=uid, | |
titlebar=titlebar, | |
keepalive=keepalive, | |
window_width=window_width, | |
window_height=window_height) | |
result = mv.get_subwindows()[0][0] | |
result.snapshot_camera = snapshot_camera | |
if static_meshes: | |
result.static_meshes = static_meshes | |
if static_lines: | |
result.static_lines = static_lines | |
result.autorecenter = autorecenter | |
return result | |
def MeshViewers(shape=(1, 1), | |
titlebar="Mesh Viewers", | |
keepalive=False, | |
window_width=1280, | |
window_height=960): | |
"""Allows subplot-style inspection of primitives in multiple subwindows. | |
:param shape: a tuple indicating the number of vertical and horizontal windows requested | |
:param titlebar: the title appearing on the created window | |
Returns: a list of lists of MeshViewer objects: one per window requested. | |
""" | |
if not test_for_opengl(): | |
return Dummy() | |
mv = MeshViewerLocal(shape=shape, | |
titlebar=titlebar, | |
uid=None, | |
keepalive=keepalive, | |
window_width=window_width, | |
window_height=window_height) | |
return mv.get_subwindows() | |
class MeshSubwindow(object): | |
def __init__(self, parent_window, which_window): | |
self.parent_window = parent_window | |
self.which_window = which_window | |
def set_dynamic_meshes(self, list_of_meshes, blocking=False): | |
self.parent_window.set_dynamic_meshes(list_of_meshes, blocking, self.which_window) | |
def set_static_meshes(self, list_of_meshes, blocking=False): | |
self.parent_window.set_static_meshes(list_of_meshes, blocking, self.which_window) | |
# list_of_model_names_and_parameters should be of form [{'name': scape_model_name, 'parameters': scape_model_parameters}] | |
# here scape_model_name is the filepath of the scape model. | |
def set_dynamic_models(self, list_of_model_names_and_parameters, blocking=False): | |
self.parent_window.set_dynamic_models(list_of_model_names_and_parameters, blocking, self.which_window) | |
def set_dynamic_lines(self, list_of_lines, blocking=False): | |
self.parent_window.set_dynamic_lines(list_of_lines, blocking, self.which_window) | |
def set_static_lines(self, list_of_lines, blocking=False): | |
self.parent_window.set_static_lines(list_of_lines, blocking=blocking, which_window=self.which_window) | |
def set_titlebar(self, titlebar, blocking=False): | |
self.parent_window.set_titlebar(titlebar, blocking, which_window=self.which_window) | |
def set_lighting_on(self, lighting_on, blocking=True): | |
self.parent_window.set_lighting_on(lighting_on, blocking=blocking, which_window=self.which_window) | |
def set_autorecenter(self, autorecenter, blocking=False): | |
self.parent_window.set_autorecenter(autorecenter, blocking=blocking, which_window=self.which_window) | |
def set_background_color(self, background_color, blocking=False): | |
self.parent_window.set_background_color(background_color, blocking=blocking, which_window=self.which_window) | |
def save_snapshot(self, path, blocking=False): | |
self.parent_window.save_snapshot(path, blocking=blocking, which_window=self.which_window) | |
def get_event(self): | |
return self.parent_window.get_event() | |
def get_keypress(self): | |
return self.parent_window.get_keypress()['key'] | |
def get_mouseclick(self): | |
return self.parent_window.get_mouseclick() | |
def close(self): | |
self.parent_window.p.terminate() | |
background_color = property(fset=set_background_color, doc="Background color, as 3-element numpy array where 0 <= color <= 1.0.") | |
dynamic_meshes = property(fset=set_dynamic_meshes, doc="List of meshes for dynamic display.") | |
static_meshes = property(fset=set_static_meshes, doc="List of meshes for static display.") | |
dynamic_models = property(fset=set_dynamic_models, doc="List of model names and parameters for dynamic display.") | |
dynamic_lines = property(fset=set_dynamic_lines, doc="List of Lines for dynamic display.") | |
static_lines = property(fset=set_static_lines, doc="List of Lines for static display.") | |
titlebar = property(fset=set_titlebar, doc="Titlebar string.") | |
lighting_on = property(fset=set_lighting_on, doc="Titlebar string.") | |
class MeshViewerLocal(object): | |
"""Proxy viewer instance for visual inspection of geometric primitives. | |
The lass forks another python process holding the display. It communicates | |
the commands with the remote instance seemlessly. | |
Write-only attributes: | |
:param titlebar: string printed in the window titlebar | |
:param dynamic_meshes: list of Mesh objects to be displayed | |
:param static_meshes: list of Mesh objects to be displayed | |
:param dynamic_lines: list of Lines objects to be displayed | |
:param static_lines: list of Lines objects to be displayed | |
.. note:: | |
`static_meshes` is meant for Meshes that are | |
updated infrequently, and dynamic_meshes is for Meshes | |
that are updated frequently (same for dynamic_lines vs | |
static_lines). They may be treated differently for | |
performance reasons. | |
""" | |
managed = {} | |
def __new__(cls, titlebar, uid, shape, keepalive, window_width, window_height): | |
assert(uid is None or isinstance(uid, str)) | |
if uid == 'stack': | |
uid = ''.join(traceback.format_list(traceback.extract_stack())) | |
if uid and uid in MeshViewer.managed.keys(): | |
return MeshViewer.managed[uid] | |
result = super(MeshViewerLocal, cls).__new__(cls) | |
result.client = zmq.Context.instance().socket(zmq.PUSH) | |
result.client.linger = 0 | |
with open(os.devnull) as dev_null, \ | |
tempfile.TemporaryFile() as err: | |
result.p = _run_self([titlebar, str(shape[0]), str(shape[1]), str(window_width), str(window_height)], | |
stdin=dev_null, | |
stdout=subprocess.PIPE, | |
stderr=err) | |
line = result.p.stdout.readline().decode() | |
result.p.stdout.close() | |
current_port = re.match('<PORT>(.*?)</PORT>', line) | |
if not current_port: | |
raise Exception("MeshViewer remote appears to have failed to launch") | |
current_port = int(current_port.group(1)) | |
result.client.connect('tcp://127.0.0.1:%d' % (current_port)) | |
if uid: | |
MeshViewerLocal.managed[uid] = result | |
result.shape = shape | |
result.keepalive = keepalive | |
return result | |
def get_subwindows(self): | |
return [[MeshSubwindow(parent_window=self, which_window=(r, c)) for c in range(self.shape[1])] for r in range(self.shape[0])] | |
def _sanitize_meshes(list_of_meshes): | |
lm = [] | |
# have to copy the meshes for now, because some contain CPython members, | |
# before pushing them on the queue | |
for m in list_of_meshes: | |
if hasattr(m, 'fc'): | |
lm.append(Mesh(v=m.v, f=m.f, fc=m.fc)) | |
elif hasattr(m, 'vc'): | |
lm.append(Mesh(v=m.v, f=m.f, vc=m.vc)) | |
else: | |
lm.append(Mesh(v=m.v, f=m.f if hasattr(m, 'f') else [])) | |
if hasattr(m, 'vn'): | |
lm[-1].vn = m.vn | |
if hasattr(m, 'fn'): | |
lm[-1].fn = m.fn | |
if hasattr(m, 'v_to_text'): | |
lm[-1].v_to_text = m.v_to_text | |
if hasattr(m, 'texture_filepath') and hasattr(m, 'vt') and hasattr(m, 'ft'): | |
lm[-1].texture_filepath = m.texture_filepath | |
lm[-1].vt = m.vt | |
lm[-1].ft = m.ft | |
return lm | |
def _send_pyobj(self, label, obj, blocking, which_window): | |
if blocking: | |
context = zmq.Context.instance() | |
server = context.socket(zmq.PULL) | |
server.linger = 0 | |
port = server.bind_to_random_port('tcp://127.0.0.1', | |
min_port=49152, | |
max_port=65535, | |
max_tries=100000) | |
# sending with blocking' | |
self.client.send_pyobj({'label': label, | |
'obj': obj, | |
'port': port, | |
'which_window': which_window}) | |
task_completion_time = server.recv_pyobj() | |
# task completion time was %.2fs in other process' % (task_completion_time,) | |
server.close() | |
else: | |
# sending nonblocking | |
self.client.send_pyobj({'label': label, | |
'obj': obj, | |
'which_window': which_window}) | |
def set_dynamic_meshes(self, list_of_meshes, blocking=False, which_window=(0, 0)): | |
self._send_pyobj('dynamic_meshes', self._sanitize_meshes(list_of_meshes), blocking, which_window) | |
def set_static_meshes(self, list_of_meshes, blocking=False, which_window=(0, 0)): | |
self._send_pyobj('static_meshes', self._sanitize_meshes(list_of_meshes), blocking, which_window) | |
# list_of_model_names_and_parameters should be of form [{'name': scape_model_name, 'parameters': scape_model_parameters}] | |
# here scape_model_name is the filepath of the scape model. | |
def set_dynamic_models(self, list_of_model_names_and_parameters, blocking=False, which_window=(0, 0)): | |
self._send_pyobj('dynamic_models', list_of_model_names_and_parameters, blocking, which_window) | |
def set_dynamic_lines(self, list_of_lines, blocking=False, which_window=(0, 0)): | |
self._send_pyobj('dynamic_lines', list_of_lines, blocking, which_window) | |
def set_static_lines(self, list_of_lines, blocking=False, which_window=(0, 0)): | |
self._send_pyobj('static_lines', list_of_lines, blocking, which_window) | |
def set_titlebar(self, titlebar, blocking=False, which_window=(0, 0)): | |
self._send_pyobj('titlebar', titlebar, blocking, which_window) | |
def set_lighting_on(self, lighting_on, blocking=False, which_window=(0, 0)): | |
self._send_pyobj('lighting_on', lighting_on, blocking, which_window) | |
def set_autorecenter(self, autorecenter, blocking=False, which_window=(0, 0)): | |
self._send_pyobj('autorecenter', autorecenter, blocking, which_window) | |
def set_background_color(self, background_color, blocking=False, which_window=(0, 0)): | |
assert(isinstance(background_color, np.ndarray)) | |
assert(background_color.size == 3) | |
self._send_pyobj('background_color', background_color.flatten(), blocking, which_window) | |
def get_keypress(self): | |
return self.get_ui_event('get_keypress') | |
def get_mouseclick(self): | |
"""Returns a mouse click event. | |
.. note:: | |
the call is blocking the caller until an event is received | |
""" | |
return self.get_ui_event('get_mouseclick') | |
def get_event(self): | |
return self.get_ui_event('get_event') | |
def get_ui_event(self, event_id): | |
context = zmq.Context.instance() | |
server = context.socket(zmq.PULL) | |
server.linger = 0 | |
port = server.bind_to_random_port('tcp://127.0.0.1', | |
min_port=49152, | |
max_port=65535, | |
max_tries=100000) | |
self._send_pyobj(event_id, port, blocking=True, which_window=(0, 0)) | |
result = server.recv_pyobj() | |
server.close() | |
return result | |
background_color = property(fset=set_background_color, | |
doc="Background color, as 3-element numpy array where 0 <= color <= 1.0.") | |
dynamic_meshes = property(fset=set_dynamic_meshes, | |
doc="List of meshes for dynamic display.") | |
static_meshes = property(fset=set_static_meshes, | |
doc="List of meshes for static display.") | |
dynamic_models = property(fset=set_dynamic_models, | |
doc="List of model names and parameters for dynamic display.") | |
dynamic_lines = property(fset=set_dynamic_lines, | |
doc="List of Lines for dynamic display.") | |
static_lines = property(fset=set_static_lines, | |
doc="List of Lines for static display.") | |
titlebar = property(fset=set_titlebar, | |
doc="Titlebar string.") | |
def save_snapshot(self, path, blocking=False, which_window=(0, 0)): | |
"""Saves a snapshot of the current window into the specified file | |
:param path: filename to which the current window content will be saved | |
""" | |
self._send_pyobj('save_snapshot', path, blocking, which_window) | |
def __del__(self): | |
if not self.keepalive: | |
self.p.terminate() | |
class MeshViewerSingle(object): | |
def __init__(self, x1_pct, y1_pct, width_pct, height_pct): | |
assert(width_pct <= 1) | |
assert(height_pct <= 1) | |
self.dynamic_meshes = [] | |
self.static_meshes = [] | |
self.dynamic_models = [] | |
self.dynamic_lines = [] | |
self.static_lines = [] | |
self.lighting_on = True | |
self.scape_models = {} | |
self.x1_pct = x1_pct | |
self.y1_pct = y1_pct | |
self.width_pct = width_pct | |
self.height_pct = height_pct | |
self.autorecenter = True | |
def get_dimensions(self): | |
d = {} | |
d['window_width'] = glutGet(GLUT_WINDOW_WIDTH) | |
d['window_height'] = glutGet(GLUT_WINDOW_HEIGHT) | |
d['subwindow_width'] = self.width_pct * d['window_width'] | |
d['subwindow_height'] = self.height_pct * d['window_height'] | |
d['subwindow_origin_x'] = self.x1_pct * d['window_width'] | |
d['subwindow_origin_y'] = self.y1_pct * d['window_height'] | |
return d | |
def on_draw(self, transform, want_camera=False): | |
d = self.get_dimensions() | |
glViewport( | |
int(d['subwindow_origin_x']), | |
int(d['subwindow_origin_y']), | |
int(d['subwindow_width']), | |
int(d['subwindow_height'])) | |
glMatrixMode(GL_PROJECTION) | |
glLoadIdentity() | |
fov_degrees = 45. | |
near = 1.0 | |
far = 100. | |
ratio = float(d['subwindow_width']) / float(d['subwindow_height']) | |
if d['subwindow_width'] < d['subwindow_height']: | |
xt = np.tan(fov_degrees * np.pi / 180. / 2.0) * near | |
yt = xt / ratio | |
glFrustum(-xt, xt, -yt, yt, near, far) | |
else: | |
gluPerspective(fov_degrees, ratio, near, far) | |
glMatrixMode(GL_MODELVIEW) | |
glLoadIdentity() | |
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE) | |
glTranslatef(0.0, 0.0, -6.0) | |
# glTranslatef(0.0,0.0,-3.5) | |
glPushMatrix() | |
glMultMatrixf(transform) | |
glColor3f(1.0, 0.75, 0.75) | |
if self.autorecenter: | |
camera = self.draw_primitives_recentered(want_camera=want_camera) | |
else: | |
if hasattr(self, 'current_center') and hasattr(self, 'current_scalefactor'): | |
camera = self.draw_primitives(scalefactor=self.current_scalefactor, center=self.current_center) | |
else: | |
camera = self.draw_primitives(want_camera=want_camera) | |
glPopMatrix() | |
if want_camera: | |
return camera | |
def draw_primitives_recentered(self, want_camera=False): | |
return self.draw_primitives(recenter=True, want_camera=want_camera) | |
def set_shaders(m): | |
VERTEX_SHADER = shaders.compileShader("""void main() { | |
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; | |
}""", GL_VERTEX_SHADER) | |
FRAGMENT_SHADER = shaders.compileShader("""void main() { | |
gl_FragColor = vec4( 0, 1, 0, 1 ); | |
}""", GL_FRAGMENT_SHADER) | |
m.shaders = shaders.compileProgram(VERTEX_SHADER, FRAGMENT_SHADER) | |
def set_texture(m): | |
texture_data = np.array(m.texture_image, dtype='int8') | |
m.textureID = glGenTextures(1) | |
glPixelStorei(GL_UNPACK_ALIGNMENT, 1) | |
glBindTexture(GL_TEXTURE_2D, m.textureID) | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture_data.shape[1], texture_data.shape[0], 0, GL_BGR, GL_UNSIGNED_BYTE, texture_data.flatten()) | |
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST) # must be GL_FASTEST, GL_NICEST or GL_DONT_CARE | |
glGenerateMipmap(GL_TEXTURE_2D) | |
def draw_mesh(m, lighting_on): | |
# Supply vertices | |
glEnableClientState(GL_VERTEX_ARRAY) | |
m.vbo['v'].bind() | |
glVertexPointer(3, GL_FLOAT, 0, m.vbo['v']) | |
m.vbo['v'].unbind() | |
# Supply normals | |
if 'vn' in m.vbo.keys(): | |
glEnableClientState(GL_NORMAL_ARRAY) | |
m.vbo['vn'].bind() | |
glNormalPointer(GL_FLOAT, 0, m.vbo['vn']) | |
m.vbo['vn'].unbind() | |
else: | |
glDisableClientState(GL_NORMAL_ARRAY) | |
# Supply colors | |
if 'vc' in m.vbo.keys(): | |
glEnableClientState(GL_COLOR_ARRAY) | |
m.vbo['vc'].bind() | |
glColorPointer(3, GL_FLOAT, 0, m.vbo['vc']) | |
m.vbo['vc'].unbind() | |
else: | |
glDisableClientState(GL_COLOR_ARRAY) | |
if ('vt' in m.vbo.keys()) and hasattr(m, 'textureID'): | |
glEnable(GL_TEXTURE_2D) | |
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) | |
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) | |
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE) | |
glBindTexture(GL_TEXTURE_2D, m.textureID) | |
glEnableClientState(GL_TEXTURE_COORD_ARRAY) | |
m.vbo['vt'].bind() | |
glTexCoordPointer(2, GL_FLOAT, 0, m.vbo['vt']) | |
m.vbo['vt'].unbind() | |
else: | |
glDisable(GL_TEXTURE_2D) | |
glDisableClientState(GL_TEXTURE_COORD_ARRAY) | |
# Draw | |
if len(m.f) > 0: | |
# ie if it is triangulated | |
if lighting_on: | |
glEnable(GL_LIGHTING) | |
else: | |
glDisable(GL_LIGHTING) | |
glDrawElementsui(GL_TRIANGLES, np.arange(m.f.size, dtype=np.uint32)) | |
else: | |
# not triangulated, so disable lighting | |
glDisable(GL_LIGHTING) | |
glPointSize(2) | |
glDrawElementsui(GL_POINTS, np.arange(len(m.v), dtype=np.uint32)) | |
if hasattr(m, 'v_to_text'): | |
glEnable(GL_TEXTURE_2D) | |
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) | |
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR) | |
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL) | |
# glEnable(GL_TEXTURE_GEN_S) | |
# glEnable(GL_TEXTURE_GEN_T) | |
# glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR) | |
# glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR) | |
bgcolor = np.array(glGetDoublev(GL_COLOR_CLEAR_VALUE)) | |
fgcolor = 1. - bgcolor | |
from .lines import Lines | |
sc = float(np.max(np.max(m.v, axis=0) - np.min(m.v, axis=0))) / 10. | |
cur_mtx = np.linalg.pinv(glGetFloatv(GL_MODELVIEW_MATRIX).T) | |
xdir = cur_mtx[:3, 0] | |
ydir = cur_mtx[:3, 1] | |
glEnable(GL_LINE_SMOOTH) | |
glEnable(GL_BLEND) | |
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) | |
for vidx, text in m.v_to_text.items(): | |
pos0 = m.v[vidx].copy() | |
pos1 = m.v[vidx].copy() | |
if hasattr(m, 'vn'): | |
pos1 += m.vn[vidx] * sc | |
glLineWidth(5.0) | |
ln = Lines(v=np.vstack((pos0, pos1)), e=np.array([[0, 1]])) | |
glEnable(GL_LIGHTING) | |
glColor3f(1. - 0.8, 1. - 0.8, 1. - 1.00) | |
MeshViewerSingle.draw_lines(ln) | |
glDisable(GL_LIGHTING) | |
texture_id = get_textureid_with_text(text, bgcolor, fgcolor) | |
glBindTexture(GL_TEXTURE_2D, texture_id) | |
glPushMatrix() | |
glTranslatef(pos1[0], pos1[1], pos1[2]) | |
dx = xdir * .10 | |
dy = ydir * .10 | |
if False: | |
glBegin(GL_QUADS) | |
glTexCoord2f(1., 0.) | |
glVertex3f(*(+dx + dy)) | |
glTexCoord2f(1., 1.) | |
glVertex3f(*(+dx - dy)) | |
glTexCoord2f(0., 1.) | |
glVertex3f(*(-dx - dy)) | |
glTexCoord2f(0., 0.) | |
glVertex3f(*(-dx + dy)) | |
# gluSphere(quadratic,0.05,32,32) | |
glEnd() | |
else: | |
glBegin(GL_POLYGON) | |
for r in np.arange(0, np.pi * 2., .01): | |
glTexCoord2f(np.cos(r) / 2. + .5, np.sin(r) / 2. + .5) | |
glVertex3f(*(dx * np.cos(r) + -dy * np.sin(r))) | |
glEnd() | |
glPopMatrix() | |
# | |
# glColor3f(bgcolor[0], bgcolor[1], bgcolor[2]) | |
# glRasterPos3f(pos1[0], pos1[1], pos1[2]) | |
# # print pos0 | |
# # print pos1 | |
# | |
# | |
# for t in text: | |
# GLUT.glutBitmapCharacter(GLUT.GLUT_BITMAP_HELVETICA_18, ord(t)) | |
def draw_lines(ls): | |
glDisableClientState(GL_NORMAL_ARRAY) | |
glEnableClientState(GL_VERTEX_ARRAY) | |
glLineWidth(3.0) | |
allpts = ls.v[ls.e.flatten()].astype(np.float32) | |
glVertexPointerf(allpts) | |
if hasattr(ls, 'vc') or hasattr(ls, 'ec'): | |
glEnableClientState(GL_COLOR_ARRAY) | |
if hasattr(ls, 'vc'): | |
glColorPointerf(ls.vc[ls.e.flatten()].astype(np.float32)) | |
else: | |
clrs = np.ones((ls.e.shape[0] * 2, 3)) * np.repeat(ls.ec, 2, axis=0) | |
glColorPointerf(clrs) | |
else: | |
glDisableClientState(GL_COLOR_ARRAY) | |
glDisable(GL_LIGHTING) | |
glDrawElementsui(GL_LINES, np.arange(len(allpts), dtype=np.uint32)) | |
def draw_primitives(self, | |
scalefactor=1.0, | |
center=[0.0, 0.0, 0.0], | |
recenter=False, | |
want_camera=False): | |
# measure the bounding box of all our primitives, so that we can | |
# recenter them in our field of view | |
if recenter: | |
all_meshes = self.static_meshes + self.dynamic_meshes | |
all_lines = self.static_lines + self.dynamic_lines | |
if (len(all_meshes) + len(all_lines)) == 0: | |
if want_camera: | |
return {'modelview_matrix': glGetDoublev(GL_MODELVIEW_MATRIX), | |
'projection_matrix': glGetDoublev(GL_PROJECTION_MATRIX), | |
'viewport': glGetIntegerv(GL_VIEWPORT) | |
} | |
else: | |
return None | |
for m in all_meshes: | |
m.v = m.v.reshape((-1, 3)) | |
all_verts = np.concatenate( | |
[m.v[m.f.flatten() if len(m.f) > 0 else np.arange(len(m.v))] for m in all_meshes] + | |
[l.v[l.e.flatten()] for l in all_lines], | |
axis=0) | |
maximum = np.max(all_verts, axis=0) | |
minimum = np.min(all_verts, axis=0) | |
center = (maximum + minimum) / 2. | |
scalefactor = (maximum - minimum) / 4. | |
scalefactor = np.max(scalefactor) | |
else: | |
center = np.array(center) | |
# for mesh in self.dynamic_meshes : | |
# if mesh.f : mesh.reset_normals() | |
all_meshes = self.static_meshes + self.dynamic_meshes | |
all_lines = self.static_lines + self.dynamic_lines | |
self.current_center = center | |
self.current_scalefactor = scalefactor | |
glMatrixMode(GL_MODELVIEW) | |
glPushMatrix() | |
# uncomment to add a default rotation (useful when automatically snapshoting kinect data | |
# glRotate(220, 0.0, 1.0, 0.0) | |
tf = np.identity(4, 'f') / scalefactor | |
tf[:3, 3] = -center / scalefactor | |
tf[3, 3] = 1 | |
cur_mtx = glGetFloatv(GL_MODELVIEW_MATRIX).T | |
glLoadMatrixf(cur_mtx.dot(tf).T) | |
if want_camera: | |
result = {'modelview_matrix': glGetDoublev(GL_MODELVIEW_MATRIX), | |
'projection_matrix': glGetDoublev(GL_PROJECTION_MATRIX), | |
'viewport': glGetIntegerv(GL_VIEWPORT) | |
} | |
else: | |
result = None | |
for m in all_meshes: | |
if not hasattr(m, 'vbo'): | |
# Precompute vertex vbo | |
fidxs = m.f.flatten() if len(m.f) > 0 else np.arange(len(m.v)) | |
allpts = m.v[fidxs].astype(np.float32).flatten() | |
vbo = OpenGL.arrays.vbo.VBO(allpts) | |
m.vbo = {'v': vbo} | |
# Precompute normals vbo | |
if hasattr(m, 'vn'): | |
ns = m.vn.astype(np.float32) | |
ns = ns[m.f.flatten(), :] | |
m.vbo['vn'] = OpenGL.arrays.vbo.VBO(ns.flatten()) | |
elif hasattr(m, 'f') and m.f.size > 0: | |
ns = TriNormals(m.v, m.f).reshape(-1, 3) | |
ns = np.tile(ns, (1, 3)).reshape(-1, 3).astype(np.float32) | |
m.vbo['vn'] = OpenGL.arrays.vbo.VBO(ns.flatten()) | |
# Precompute texture vbo | |
if hasattr(m, 'ft') and (m.ft.size > 0): | |
ftidxs = m.ft.flatten() | |
data = m.vt[ftidxs].astype(np.float32)[:, 0:2] | |
data[:, 1] = 1.0 - 1.0 * data[:, 1] | |
m.vbo['vt'] = OpenGL.arrays.vbo.VBO(data) | |
# Precompute color vbo | |
if hasattr(m, 'vc'): | |
data = m.vc[fidxs].astype(np.float32) | |
m.vbo['vc'] = OpenGL.arrays.vbo.VBO(data) | |
elif hasattr(m, 'fc'): | |
data = np.tile(m.fc, (1, 3)).reshape(-1, 3).astype(np.float32) | |
m.vbo['vc'] = OpenGL.arrays.vbo.VBO(data) | |
for e in all_lines: | |
self.draw_lines(e) | |
for m in all_meshes: | |
if hasattr(m, 'texture_image') and not hasattr(m, 'textureID'): | |
self.set_texture(m) | |
self.draw_mesh(m, self.lighting_on) | |
glMatrixMode(GL_MODELVIEW) | |
glPopMatrix() | |
return result | |
class MeshViewerRemote(object): | |
def __init__(self, | |
titlebar='Mesh Viewer', | |
subwins_vert=1, | |
subwins_horz=1, | |
width=100, | |
height=100): | |
context = zmq.Context.instance() | |
self.server = context.socket(zmq.PULL) | |
self.server.linger = 0 | |
# Find a port to use. The standard set of "private" ports is 49152 through 65535, as seen in... | |
# http://en.wikipedia.org/wiki/Port_(computer_networking) | |
port = self.server.bind_to_random_port('tcp://127.0.0.1', | |
min_port=49152, | |
max_port=65535, | |
max_tries=100000) | |
# Print out our port so that our client can connect to us with it. Flush stdout immediately; otherwise | |
# our client could wait forever. | |
print('<PORT>%d</PORT>\n' % (port,)) | |
sys.stdout.flush() | |
self.arcball = ArcBallT(width, height) | |
self.transform = Matrix4fT() | |
self.lastrot = Matrix3fT() | |
self.thisrot = Matrix3fT() | |
self.isdragging = False | |
self.need_redraw = True | |
self.mesh_viewers = [[MeshViewerSingle(float(c) / (subwins_horz), | |
float(r) / (subwins_vert), | |
1. / subwins_horz, | |
1. / subwins_vert) for c in range(subwins_horz)] for r in range(subwins_vert)] | |
self.tm_for_fps = 0. | |
self.titlebar = titlebar | |
self.activate(width, height) | |
def snapshot(self, path): | |
""" | |
Takes a snapshot of the meshviewer window and saves it to disc. | |
:param path: path to save the snapshot at. | |
.. note:: Requires the Pillow package to be installed. | |
""" | |
from PIL import Image | |
from OpenGL.GLU import GLubyte | |
self.on_draw() | |
x = 0 | |
y = 0 | |
width = glutGet(GLUT_WINDOW_WIDTH) | |
height = glutGet(GLUT_WINDOW_HEIGHT) | |
data = (GLubyte * (3 * width * height))(0) | |
glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, data) | |
image = Image.frombytes(mode="RGB", size=(width, height), data=data) | |
image = image.transpose(Image.FLIP_TOP_BOTTOM) | |
# Save image to disk | |
image.save(path) | |
def activate(self, width, height): | |
glutInit(['mesh_viewer']) | |
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH) | |
glutInitWindowSize(width, height) | |
glutInitWindowPosition(0, 0) | |
self.root_window_id = glutCreateWindow(self.titlebar) | |
glutDisplayFunc(self.on_draw) | |
glutTimerFunc(100, self.checkQueue, 0) | |
glutReshapeFunc(self.on_resize_window) | |
glutKeyboardFunc(self.on_keypress) | |
glutMouseFunc(self.on_click) | |
glutMotionFunc(self.on_drag) | |
# for r, lst in enumerate(self.mesh_viewers): | |
# for c, mv in enumerate(lst): | |
# mv.glut_window_id = glutCreateSubWindow(self.root_window_id, c*width/len(lst), r*height/len(self.mesh_viewers), width/len(lst), height/len(self.mesh_viewers)) | |
glutDisplayFunc(self.on_draw) | |
self.init_opengl() | |
glutMainLoop() # won't return until process is killed | |
def on_drag(self, cursor_x, cursor_y): | |
""" Mouse cursor is moving | |
Glut calls this function (when mouse button is down) | |
and pases the mouse cursor postion in window coords as the mouse moves. | |
""" | |
from .geometry.rodrigues import rodrigues | |
if (self.isdragging): | |
mouse_pt = Point2fT(cursor_x, cursor_y) | |
ThisQuat = self.arcball.drag(mouse_pt) # // Update End Vector And Get Rotation As Quaternion | |
self.thisrot = Matrix3fSetRotationFromQuat4f(ThisQuat) # // Convert Quaternion Into Matrix3fT | |
# Use correct Linear Algebra matrix multiplication C = A * B | |
self.thisrot = Matrix3fMulMatrix3f(self.lastrot, self.thisrot) # // Accumulate Last Rotation Into This One | |
# make sure it is a rotation | |
self.thisrot = rodrigues(rodrigues(self.thisrot)[0])[0] | |
self.transform = Matrix4fSetRotationFromMatrix3f(self.transform, self.thisrot) # // Set Our Final Transform's Rotation From This One | |
glutPostRedisplay() | |
return | |
# The function called whenever a key is pressed. Note the use of Python tuples to pass in: (key, x, y) | |
def on_keypress(self, *args): | |
key = args[0] | |
if hasattr(self, 'event_port'): | |
self.keypress_port = self.event_port | |
del self.event_port | |
if hasattr(self, 'keypress_port'): | |
client = zmq.Context.instance().socket(zmq.PUSH) | |
client.connect('tcp://127.0.0.1:%d' % (self.keypress_port)) | |
client.send_pyobj({'event_type': 'keyboard', 'key': key}) | |
del self.keypress_port | |
def on_click(self, button, button_state, cursor_x, cursor_y): | |
""" Mouse button clicked. | |
Glut calls this function when a mouse button is | |
clicked or released. | |
""" | |
self.isdragging = False | |
# if (button == GLUT_RIGHT_BUTTON and button_state == GLUT_UP): | |
# # Right button click | |
# self.lastrot = Matrix3fSetIdentity (); # // Reset Rotation | |
# self.thisrot = Matrix3fSetIdentity (); # // Reset Rotation | |
# self.transform = Matrix4fSetRotationFromMatrix3f (self.transform, self.thisrot); # // Reset Rotation | |
if (button == GLUT_LEFT_BUTTON and button_state == GLUT_UP): | |
# Left button released | |
self.lastrot = copy.copy(self.thisrot) # Set Last Static Rotation To Last Dynamic One | |
elif (button == GLUT_LEFT_BUTTON and button_state == GLUT_DOWN): | |
# Left button clicked down | |
self.lastrot = copy.copy(self.thisrot) # Set Last Static Rotation To Last Dynamic One | |
self.isdragging = True # // Prepare For Dragging | |
mouse_pt = Point2fT(cursor_x, cursor_y) | |
self.arcball.click(mouse_pt) # Update Start Vector And Prepare For Dragging | |
elif (button == GLUT_RIGHT_BUTTON and button_state == GLUT_DOWN): | |
# If a mouse click location was requested, return it to caller | |
if hasattr(self, 'event_port'): | |
self.mouseclick_port = self.event_port | |
del self.event_port | |
if hasattr(self, 'mouseclick_port'): | |
self.send_mouseclick_to_caller(cursor_x, cursor_y) | |
elif (button == GLUT_MIDDLE_BUTTON and button_state == GLUT_DOWN): | |
# If a mouse click location was requested, return it to caller | |
if hasattr(self, 'event_port'): | |
self.mouseclick_port = self.event_port | |
del self.event_port | |
if hasattr(self, 'mouseclick_port'): | |
self.send_mouseclick_to_caller(cursor_x, cursor_y, button='middle') | |
glutPostRedisplay() | |
def send_mouseclick_to_caller(self, | |
cursor_x, | |
cursor_y, | |
button='right'): | |
client = zmq.Context.instance().socket(zmq.PUSH) | |
client.connect('tcp://127.0.0.1:%d' % (self.mouseclick_port)) | |
cameras = self.on_draw(want_cameras=True) | |
window_height = glutGet(GLUT_WINDOW_HEIGHT) | |
depth_value = glReadPixels(cursor_x, window_height - cursor_y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT) | |
pyobj = { | |
'event_type': 'mouse_click_%sbutton' % button, | |
'u': None, 'v': None, | |
'x': None, 'y': None, 'z': None, | |
'subwindow_row': None, | |
'subwindow_col': None | |
} | |
for subwin_row, camera_list in enumerate(cameras): | |
for subwin_col, camera in enumerate(camera_list): | |
# test for out-of-bounds | |
if cursor_x < camera['viewport'][0]: | |
continue | |
if cursor_x > (camera['viewport'][0] + camera['viewport'][2]): | |
continue | |
if window_height - cursor_y < camera['viewport'][1]: | |
continue | |
if window_height - cursor_y > (camera['viewport'][1] + camera['viewport'][3]): | |
continue | |
xx, yy, zz = gluUnProject( | |
cursor_x, window_height - cursor_y, depth_value, | |
camera['modelview_matrix'], | |
camera['projection_matrix'], | |
camera['viewport']) | |
pyobj = { | |
'event_type': 'mouse_click_%sbutton' % button, | |
'u': cursor_x - camera['viewport'][0], 'v': window_height - cursor_y - camera['viewport'][1], | |
'x': xx, 'y': yy, 'z': zz, | |
'which_subwindow': (subwin_row, subwin_col) | |
} | |
client.send_pyobj(pyobj) | |
del self.mouseclick_port | |
def on_draw(self, want_cameras=False): | |
# sys.stderr.write('fps: %.2e\n' % (1. / (time.time() - self.tm_for_fps))) | |
self.tm_for_fps = time.time() | |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) | |
cameras = [] | |
for mvl in self.mesh_viewers: | |
cameras.append([]) | |
for mv in mvl: | |
cameras[-1].append(mv.on_draw(self.transform, want_cameras)) | |
glFlush() # Flush The GL Rendering Pipeline | |
glutSwapBuffers() | |
self.need_redraw = False | |
if want_cameras: | |
return cameras | |
def on_resize_window(self, Width, Height): | |
"""Reshape The Window When It's Moved Or Resized""" | |
self.arcball.setBounds(Width, Height) # //*NEW* Update mouse bounds for arcball | |
return | |
def handle_request(self, request): | |
label = request['label'] | |
obj = request['obj'] | |
w = request['which_window'] | |
mv = self.mesh_viewers[w[0]][w[1]] | |
# Handle each type of request. | |
# Some requests require a redraw, and | |
# some don't. | |
if label == 'dynamic_meshes': | |
mv.dynamic_meshes = obj | |
self.need_redraw = True | |
elif label == 'dynamic_models': | |
mv.dynamic_models = obj | |
self.need_redraw = True | |
elif label == 'static_meshes': | |
mv.static_meshes = obj | |
self.need_redraw = True | |
elif label == 'dynamic_lines': | |
mv.dynamic_lines = obj | |
self.need_redraw = True | |
elif label == 'static_lines': | |
mv.static_lines = obj | |
self.need_redraw = True | |
elif label == 'autorecenter': | |
mv.autorecenter = obj | |
self.need_redraw = True | |
elif label == 'titlebar': | |
assert(isinstance(obj, str)) | |
self.titlebar = obj | |
glutSetWindowTitle(obj) | |
elif label == 'lighting_on': | |
mv.lighting_on = obj | |
self.need_redraw = True | |
elif label == 'background_color': | |
glClearColor(obj[0], obj[1], obj[2], 1.0) | |
self.need_redraw = True | |
elif label == 'save_snapshot': # redraws for itself | |
assert(isinstance(obj, str)) | |
self.snapshot(obj) | |
elif label == 'get_keypress': | |
self.keypress_port = obj | |
elif label == 'get_mouseclick': | |
self.mouseclick_port = obj | |
elif label == 'get_event': | |
self.event_port = obj | |
else: | |
return False # can't handle this request string | |
return True # handled the request string | |
def checkQueue(self, unused_timer_id): | |
glutTimerFunc(20, self.checkQueue, 0) | |
# if True: # spinning | |
# w_whole_window = glutGet(GLUT_WINDOW_WIDTH) | |
# h_whole_window = glutGet(GLUT_WINDOW_HEIGHT) | |
# center_x = w_whole_window/2 | |
# center_y = h_whole_window/2 | |
# self.on_click(GLUT_LEFT_BUTTON, GLUT_DOWN, center_x, center_y) | |
# self.on_drag(center_x+2, center_y) | |
try: | |
request = self.server.recv_pyobj(zmq.NOBLOCK) | |
except zmq.ZMQError as e: | |
if e.errno != zmq.EAGAIN: | |
raise # something wrong besides empty queue | |
return # empty queue, no problem | |
if not request: | |
return | |
while (request): | |
task_completion_time = time.time() | |
if not self.handle_request(request): | |
raise Exception('Unknown command string: %s' % (request['label'])) | |
task_completion_time = time.time() - task_completion_time | |
if 'port' in request: # caller wants confirmation | |
port = request['port'] | |
client = zmq.Context.instance().socket(zmq.PUSH) | |
client.connect('tcp://127.0.0.1:%d' % (port)) | |
client.send_pyobj(task_completion_time) | |
try: | |
request = self.server.recv_pyobj(zmq.NOBLOCK) | |
except zmq.ZMQError as e: | |
if e.errno != zmq.EAGAIN: | |
raise | |
request = None | |
if self.need_redraw: | |
glutPostRedisplay() | |
def init_opengl(self): | |
"""A general OpenGL initialization function. Sets all of the initial parameters. | |
We call this right after our OpenGL window is created. | |
""" | |
glClearColor(0.0, 0.0, 0.0, 1.0) # This Will Clear The Background Color To Black | |
glClearDepth(1.0) # Enables Clearing Of The Depth Buffer | |
glDepthFunc(GL_LEQUAL) # The Type Of Depth Test To Do | |
glEnable(GL_DEPTH_TEST) # Enables Depth Testing | |
glShadeModel(GL_SMOOTH) | |
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) # Really Nice Perspective Calculations | |
glEnable(GL_LIGHT0) | |
glEnable(GL_LIGHTING) | |
glEnable(GL_COLOR_MATERIAL) | |
glEnable(GL_NORMALIZE) # important since we rescale the modelview matrix | |
return True | |
if __name__ == '__main__': | |
# Windows specific: see http://docs.python.org/2/library/multiprocessing.html#multiprocessing.freeze_support | |
freeze_support() | |
if len(sys.argv) == 2 and sys.argv[1] == 'TEST_FOR_OPENGL': | |
_test_for_opengl() | |
elif len(sys.argv) > 2: | |
m = MeshViewerRemote(titlebar=sys.argv[1], | |
subwins_vert=int(sys.argv[2]), | |
subwins_horz=int(sys.argv[3]), | |
width=int(sys.argv[4]), | |
height=int(sys.argv[5])) | |
else: | |
print("#" * 10) | |
print('Usage:') | |
print("python -m %s.%s arguments" % (__package__, os.path.splitext(os.path.basename(__file__))[0])) | |