diff options
author | Germano <germano.costa@ig.com.br> | 2017-09-10 02:11:38 +0300 |
---|---|---|
committer | Germano <germano.costa@ig.com.br> | 2017-09-10 02:11:38 +0300 |
commit | 136a8f839aab01c23a809eba13db014d654511c3 (patch) | |
tree | 6946ea9f162b65841c1c280219e5852fc49ad5b4 | |
parent | 1d9d466de8a56705edf8d274fe36157f4701bdb9 (diff) |
New `snap_context` module for `addons/module`
This module basically uses OpenGL to draw, in an offscreen, the vertices, edges and tris of specific objects and use colors as information for snap
-rw-r--r-- | modules/snap_context/__init__.py | 292 | ||||
-rw-r--r-- | modules/snap_context/bgl_ext.py | 140 | ||||
-rw-r--r-- | modules/snap_context/mesh_drawing.py | 505 | ||||
-rw-r--r-- | modules/snap_context/resources/3D_vert.glsl | 27 | ||||
-rw-r--r-- | modules/snap_context/resources/primitive_id_frag.glsl | 22 | ||||
-rw-r--r-- | modules/snap_context/utils_projection.py | 213 | ||||
-rw-r--r-- | modules/snap_context/utils_shader.py | 85 |
7 files changed, 1284 insertions, 0 deletions
diff --git a/modules/snap_context/__init__.py b/modules/snap_context/__init__.py new file mode 100644 index 00000000..4767862f --- /dev/null +++ b/modules/snap_context/__init__.py @@ -0,0 +1,292 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# ##### END GPL LICENSE BLOCK ##### + +import bgl +import gpu +from mathutils import Vector, Matrix +from mathutils.geometry import intersect_point_line, intersect_ray_tri + +from .bgl_ext import VoidBufValue, get_clip_planes +from .mesh_drawing import GPU_Indices_Mesh, gpu_Indices_enable_state, gpu_Indices_restore_state +from .utils_projection import ( + region_2d_to_orig_and_view_vector as _get_ray, + intersect_boundbox_threshold, + intersect_ray_segment_fac, + project_co_v3, + ) + +VERT = 1 +EDGE = 2 +FACE = 4 + +class _SnapObjectData(): + def __init__(self, data, omat): + self.data = data + self.mat = omat + + +class SnapContext(): + def __init__(self, region, space): + self.freed = False + self.snap_objects = [] + self.drawn_count = 0 + self._offset_cur = 1 # Starts with index 1 + self.region = region + self.rv3d = space.region_3d + self.proj_mat = Matrix.Identity(4) + self.depth_range = Vector((space.clip_start, space.clip_end)) + self.mval = Vector((0, 0)) + self._snap_mode = VERT | EDGE | FACE + + self.set_pixel_dist(12) + + self._offscreen = gpu.offscreen.new(self.region.width, self.region.height) + + self._texture = self._offscreen.color_texture + bgl.glBindTexture(bgl.GL_TEXTURE_2D, self._texture) + + NULL = VoidBufValue(0) + bgl.glTexImage2D(bgl.GL_TEXTURE_2D, 0, bgl.GL_R32UI, self.region.width, self.region.height, 0, bgl.GL_RED_INTEGER, bgl.GL_UNSIGNED_INT, NULL.buf) + del NULL + + bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_NEAREST) + bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_NEAREST) + bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0) + + self.winsize = Vector((self._offscreen.width, self._offscreen.height)) + + ## PRIVATE ## + + def _get_snap_obj_by_index(self, index): + for snap_obj in self.snap_objects[:self.drawn_count]: + data = snap_obj.data[1] + if index < data.first_index + data.get_tot_elems(): + return snap_obj + return None + + def _get_nearest_index(self): + loc = [self._dist_px, self._dist_px] + d = 1 + m = self.threshold + max = 2 * m - 1 + offset = 1 + last_snap_obj = None + r_value = 0 + while m < max: + for i in range(2): + while 2 * loc[i] * d < m: + value = int(self._snap_buffer[loc[0]][loc[1]]) + loc[i] += d + if value >= offset: + r_value = value + snap_obj = self._get_snap_obj_by_index(r_value) + + if self._snap_mode & FACE and self._snap_mode & (VERT | EDGE) and last_snap_obj != snap_obj: + data = snap_obj.data[1] + offset = data.first_index + data.num_tris + last_snap_obj = snap_obj + continue + return snap_obj, r_value + d = -d + m += 4 * self._dist_px * d + 1 + + return last_snap_obj, r_value + + def _get_loc(self, snap_obj, index): + index -= snap_obj.data[1].first_index + gpu_data = snap_obj.data[1] + + if gpu_data.draw_tris: + if index < snap_obj.data[1].num_tris: + tri_verts = gpu_data.get_tri_verts(index) + tri_co = [snap_obj.mat * Vector(v) for v in gpu_data.get_tri_co(index)] + return intersect_ray_tri(*tri_co, *self.last_ray, False), tri_verts + + index -= gpu_data.num_tris + + if gpu_data.draw_edges: + if index < snap_obj.data[1].num_edges: + edge_verts = gpu_data.get_edge_verts(index) + edge_co = [snap_obj.mat * Vector(v) for v in gpu_data.get_edge_co(index)] + fac = intersect_ray_segment_fac(*edge_co, *self.last_ray) + + if (self._snap_mode) & VERT and (fac < 0.25 or fac > 0.75): + co = edge_co[0] if fac < 0.5 else edge_co[1] + proj_co = project_co_v3(self, co) + dist = self.mval - proj_co + if abs(dist.x) < self._dist_px and abs(dist.y) < self._dist_px: + return co, (edge_verts[0] if fac < 0.5 else edge_verts[1],) + + if fac <= 0.0: + co = edge_co[0] + elif fac >= 1.0: + co = edge_co[1] + else: + co = edge_co[0] + fac * (edge_co[1] - edge_co[0]) + + return co, edge_verts + + index -= gpu_data.num_edges + + if gpu_data.draw_verts: + if index < snap_obj.data[1].num_verts: + return snap_obj.mat * Vector(gpu_data.get_loosevert_co(index)), (gpu_data.get_loosevert_index(index),) + + return None, None + + + def _get_snap_obj_by_obj(self, obj): + for snap_obj in self.snap_objects: + if obj == snap_obj.data[0]: + return snap_obj + + def __del__(self): + if not self.freed: + self._offscreen.free() + del self.snap_objects + + ## PUBLIC ## + + def update_all(self): + self.drawn_count = 0 + self._offset_cur = 1 + + bgl.glClearColor(0.0, 0.0, 0.0, 0.0) + bgl.glClear(bgl.GL_COLOR_BUFFER_BIT | bgl.GL_DEPTH_BUFFER_BIT) + + def update_drawn_snap_object(self, snap_obj): + if len(snap_obj.data) > 1: + del snap_obj.data[1:] + #self.update_all() + # Update on next snap_get call # + self.proj_mat = Matrix.Identity(4) + + def use_clip_planes(self, value): + planes = get_clip_planes(self.rv3d) + if planes: + self._store_current_shader_state() + GPU_Indices_Mesh.shader + bgl.glUseProgram(GPU_Indices_Mesh.shader.program) + bgl.glUniform1i(GPU_Indices_Mesh.unif_use_clip_planes, value) + + bgl.glUniform4fv(GPU_Indices_Mesh.unif_clip_plane, 4, planes) + + self._restore_shader_state() + + def set_pixel_dist(self, dist_px): + self._dist_px = int(dist_px) + self._dist_px_sq = self._dist_px ** 2 + self.threshold = 2 * self._dist_px + 1 + self._snap_buffer = bgl.Buffer(bgl.GL_FLOAT, (self.threshold, self.threshold)) + + def set_snap_mode(self, snap_to_vert, snap_to_edge, snap_to_face): + snap_mode = 0 + if snap_to_vert: + snap_mode |= VERT + if snap_to_edge: + snap_mode |= EDGE + if snap_to_face: + snap_mode |= FACE + + if snap_mode != self._snap_mode: + self._snap_mode = snap_mode + self.update_all() + + def add_obj(self, obj, matrix): + matrix = matrix.freeze() + snap_obj = self._get_snap_obj_by_obj(obj) + if not snap_obj: + self.snap_objects.append(_SnapObjectData([obj], matrix)) + else: + self.snap_objects.append(_SnapObjectData(snap_obj.data, matrix)) + + return self.snap_objects[-1] + + def get_ray(self, mval): + self.last_ray = _get_ray(self.region, self.rv3d, mval) + return self.last_ray + + def snap_get(self, mval): + ret = None, None + self.mval[:] = mval + snap_vert = self._snap_mode & VERT != 0 + snap_edge = self._snap_mode & EDGE != 0 + snap_face = self._snap_mode & FACE != 0 + + gpu_Indices_enable_state() + self._offscreen.bind() + + #bgl.glDisable(bgl.GL_DITHER) # dithering and AA break color coding, so disable # + #multisample_enabled = bgl.glIsEnabled(bgl.GL_MULTISAMPLE) + #bgl.glDisable(bgl.GL_MULTISAMPLE) + bgl.glEnable(bgl.GL_DEPTH_TEST) + + proj_mat = self.rv3d.perspective_matrix.copy() + if self.proj_mat != proj_mat: + self.proj_mat = proj_mat + GPU_Indices_Mesh.set_ProjectionMatrix(self.proj_mat) + self.update_all() + + ray_dir, ray_orig = self.get_ray(mval) + for i, snap_obj in enumerate(self.snap_objects[self.drawn_count:], self.drawn_count): + obj = snap_obj.data[0] + bbmin = Vector(obj.bound_box[0]) + bbmax = Vector(obj.bound_box[6]) + + if bbmin != bbmax: + MVP = proj_mat * snap_obj.mat + mat_inv = snap_obj.mat.inverted() + ray_orig_local = mat_inv * ray_orig + ray_dir_local = ray_dir * snap_obj.mat + in_threshold = intersect_boundbox_threshold(self, MVP, ray_orig_local, ray_dir_local, bbmin, bbmax) + else: + proj_co = project_co_v3(self, snap_obj.mat.translation) + dist = self.mval - proj_co + in_threshold = abs(dist.x) < self._dist_px and abs(dist.y) < self._dist_px + #snap_obj.data[1] = primitive_point + + if in_threshold: + if len(snap_obj.data) == 1: + snap_obj.data.append(GPU_Indices_Mesh(obj, snap_face, snap_edge, snap_vert)) + snap_obj.data[1].set_draw_mode(snap_face, snap_edge, snap_vert) + snap_obj.data[1].set_ModelViewMatrix(snap_obj.mat) + snap_obj.data[1].Draw(self._offset_cur) + self._offset_cur += snap_obj.data[1].get_tot_elems() + + self.snap_objects[self.drawn_count], self.snap_objects[i] = self.snap_objects[i], self.snap_objects[self.drawn_count] + self.drawn_count += 1 + + bgl.glReadBuffer(bgl.GL_COLOR_ATTACHMENT0) + bgl.glReadPixels( + int(self.mval[0]) - self._dist_px, int(self.mval[1]) - self._dist_px, + self.threshold, self.threshold, bgl.GL_RED_INTEGER, bgl.GL_UNSIGNED_INT, self._snap_buffer) + bgl.glReadBuffer(bgl.GL_BACK) + + snap_obj, index = self._get_nearest_index() + #print(index) + if snap_obj: + ret = self._get_loc(snap_obj, index) + + self._offscreen.unbind() + gpu_Indices_restore_state() + + return snap_obj, ret[0], ret[1] + + def free(self): + self.__del__() + self.freed = True + diff --git a/modules/snap_context/bgl_ext.py b/modules/snap_context/bgl_ext.py new file mode 100644 index 00000000..46ce3186 --- /dev/null +++ b/modules/snap_context/bgl_ext.py @@ -0,0 +1,140 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# ##### END GPL LICENSE BLOCK ##### + + +import bgl +import ctypes +import numpy as np + + +# figure out size of _Py_ssize_t +if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'): + _Py_ssize_t = ctypes.c_int64 +else: + _Py_ssize_t = ctypes.c_int + +class _PyObject(ctypes.Structure): + pass + +_PyObject._fields_ = [ + ('ob_refcnt', _Py_ssize_t), + ('ob_type', ctypes.POINTER(_PyObject)), +] + +if object.__basicsize__ != ctypes.sizeof(_PyObject): + # python with trace + class _PyObject(ctypes.Structure): + _fields_ = [ + ('_ob_next', ctypes.POINTER(_PyObject)), + ('_ob_prev', ctypes.POINTER(_PyObject)), + ('ob_refcnt', _Py_ssize_t), + ('ob_type', ctypes.POINTER(_PyObject)), + ] + +class _PyVarObject(_PyObject): + _fields_ = [ + ('ob_size', _Py_ssize_t), + ] + +class C_Buffer(_PyVarObject): + _fields_ = [ + ("parent", ctypes.py_object), + ("type", ctypes.c_int), + ("ndimensions", ctypes.c_int), + ("dimensions", ctypes.POINTER(ctypes.c_int)), + ("buf", ctypes.c_void_p), + ] + +assert ctypes.sizeof(C_Buffer) == bgl.Buffer.__basicsize__ + +class VoidBufValue(): + def __init__(self, value): + self.buf = bgl.Buffer(bgl.GL_BYTE, 1) + self.c_buf = C_Buffer.from_address(id(self.buf)) + self._allocated_buf = self.c_buf.buf + self.c_buf.buf = value + self.c_buf.type = 0 # allows repr + def __del__(self): + self.c_buf.buf = self._allocated_buf + #del self._allocated_buf + del self.buf + + +def np_array_as_bgl_Buffer(array): + type = array.dtype + if type == np.int8: + type = bgl.GL_BYTE + elif type == np.int16: + type = bgl.GL_SHORT + elif type == np.int32: + type = bgl.GL_INT + elif type == np.float32: + type = bgl.GL_FLOAT + elif type == np.float64: + type = bgl.GL_DOUBLE + else: + raise + + _decref = ctypes.pythonapi.Py_DecRef + _incref = ctypes.pythonapi.Py_IncRef + + _decref.argtypes = _incref.argtypes = [ctypes.py_object] + _decref.restype = _incref.restype = None + + buf = bgl.Buffer(bgl.GL_BYTE, (1, *array.shape))[0] + c_buf = C_Buffer.from_address(id(buf)) + + _decref(c_buf.parent) + _incref(array) + + c_buf.parent = array # Prevents MEM_freeN + c_buf.type = type + c_buf.buf = array.ctypes.data + + return buf + + +def bgl_Buffer_reshape(buf, shape): + assert np.prod(buf.dimensions) == np.prod(shape) + + c_buf = C_Buffer.from_address(id(buf)) + c_buf.ndimensions = len(shape) + + tmp_buf = bgl.Buffer(c_buf.type, (1,) * len(shape)) + c_tmp_buf = C_Buffer.from_address(id(tmp_buf)) + for i, v in enumerate(shape): + c_tmp_buf.dimensions[i] = v + + offset = C_Buffer.dimensions.offset + a = ctypes.pointer(ctypes.c_void_p.from_address(id(tmp_buf) + offset)) + b = ctypes.pointer(ctypes.c_void_p.from_address(id(buf) + offset)) + + a[0], b[0] = b[0], a[0] + + del c_buf + del c_tmp_buf + del tmp_buf + + +def get_clip_planes(rv3d): + #(int)(&((struct RegionView3D *)0)->rflag) == 842 + #(int)(&((struct RegionView3D *)0)->clip) == 464 + rv3d_ptr = rv3d.as_pointer() + rflag = ctypes.c_short.from_address(rv3d_ptr + 842).value + if rflag & 4: # RV3D_CLIPPING + clip = (6 * (4 * ctypes.c_float)).from_address(rv3d_ptr + 464) + return bgl.Buffer(bgl.GL_FLOAT, (6, 4), clip) diff --git a/modules/snap_context/mesh_drawing.py b/modules/snap_context/mesh_drawing.py new file mode 100644 index 00000000..f7087b16 --- /dev/null +++ b/modules/snap_context/mesh_drawing.py @@ -0,0 +1,505 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# ##### END GPL LICENSE BLOCK ##### + + +import bgl +import bmesh +import numpy as np +from mathutils import Matrix + +from .bgl_ext import VoidBufValue, np_array_as_bgl_Buffer, bgl_Buffer_reshape +from .utils_shader import Shader + + +def load_shader(shadername): + from os import path + with open(path.join(path.dirname(__file__), 'resources', shadername), 'r') as f: + return f.read() + + +def get_mesh_vert_co_array(me): + tot_vco = len(me.vertices) + if tot_vco: + verts_co = bgl.Buffer(bgl.GL_FLOAT, (tot_vco * 3)) + me.vertices.foreach_get("co", verts_co) + bgl_Buffer_reshape(verts_co, (tot_vco, 3)) + return verts_co + return None + + +def get_bmesh_vert_co_array(bm): + tot_vco = len(bm.verts) + if tot_vco: + return bgl.Buffer(bgl.GL_FLOAT, (tot_vco, 3), [v.co for v in bm.verts]) + return None + + +def get_mesh_tri_verts_array(me): + num_tris = len(me.loops) - 2 * len(me.polygons) + if num_tris: + bm = bmesh.new() + bm.from_mesh(me, face_normals=False) + ltris = bm.calc_tessface() + tris = bgl.Buffer(bgl.GL_INT, (num_tris, 3)) + for i, ltri in enumerate(ltris): + tris[i] = ltri[0].vert.index, ltri[1].vert.index, ltri[2].vert.index + bm.free() + return tris + return None + + +def get_mesh_tri_co_array(me, tri_verts): + num_tris = len(tri_verts) + if num_tris: + verts = me.vertices + tris = bgl.Buffer(bgl.GL_FLOAT, (num_tris, 3, 3)) + for i, tri in enumerate(tri_verts): + tris[i] = verts[tri[0]].co, verts[tri[1]].co, verts[tri[2]].co + return tris + return None + + +def get_bmesh_tri_verts_array(bm): + ltris = bm.calc_tessface() + tris = [[ltri[0].vert.index, ltri[1].vert.index, ltri[2].vert.index] for ltri in ltris if not ltri[0].face.hide] + if tris: + return bgl.Buffer(bgl.GL_INT, (len(tris), 3), tris) + return None + + +def get_bmesh_tri_co_array(bm, tri_verts): + num_tris = len(tri_verts) + if num_tris: + verts = bm.verts + tris = bgl.Buffer(bgl.GL_FLOAT, (num_tris, 3, 3)) + for i, tri in enumerate(tri_verts): + tris[i] = verts[tri[0]].co, verts[tri[1]].co, verts[tri[2]].co + return tris + return None + + +def get_mesh_edge_verts_array(me): + tot_edges = len(me.edges) + if tot_edges: + edge_verts = np.empty(tot_edges * 2, 'i4') + me.edges.foreach_get("vertices", edge_verts) + edge_verts.shape = tot_edges, 2 + return np_array_as_bgl_Buffer(edge_verts) + return None + + +def get_mesh_edge_co_array(me, edge_verts): + if edge_verts: + edges_co = bgl.Buffer(bgl.GL_FLOAT, (len(edge_verts), 2, 3)) + verts = me.vertices + for i, (v0, v1) in enumerate(edge_verts): + edges_co[i][0] = verts[v0].co + edges_co[i][1] = verts[v1].co + return edges_co + return None + + +def get_bmesh_edge_verts_array(bm): + bm.edges.ensure_lookup_table() + edges = [[e.verts[0].index, e.verts[1].index] for e in bm.edges if not e.hide] + if edges: + return bgl.Buffer(bgl.GL_INT, (len(edges), 2), edges) + return None + + +def get_bmesh_edge_co_array(bm, edge_verts): + if edge_verts: + edges_co = bgl.Buffer(bgl.GL_FLOAT, (len(edge_verts), 2, 3)) + verts = bm.verts + for i, (v0, v1) in enumerate(edge_verts): + edges_co[i][0] = verts[v0].co + edges_co[i][1] = verts[v1].co + return edges_co + return None + + +def get_mesh_loosevert_array(me, edges): + verts = np.arange(len(me.vertices)) + + mask = np.in1d(verts, edges, invert=True) + + verts = verts[mask] + if len(verts): + return bgl.Buffer(bgl.GL_INT, len(verts), verts) + return None + + +def get_bmesh_loosevert_array(bm): + looseverts = [v.index for v in bm.verts if not (v.link_edges or v.hide)] + if looseverts: + return bgl.Buffer(bgl.GL_INT, len(looseverts), looseverts) + return None + + +class _Mesh_Arrays(): + def __init__(self, obj, create_tris, create_edges, create_looseverts): + self.tri_verts = self.edge_verts = self.looseverts = None + self.tris_co = self.edges_co = self.looseverts_co = None + if obj.type == 'MESH': + me = obj.data + if me.is_editmode: + bm = bmesh.from_edit_mesh(me) + bm.verts.ensure_lookup_table() + + if False: #Blender 2.8 + self.verts = get_bmesh_vert_co_array(bm) + if create_tris: + self.tri_verts = get_bmesh_tri_verts_array(bm) + self.tris_co = get_bmesh_tri_co_array(bm, self.tri_verts) + if create_edges: + self.edge_verts = get_bmesh_edge_verts_array(bm) + self.edges_co = get_bmesh_edge_co_array(bm, self.edge_verts) + if create_looseverts: + self.looseverts = get_bmesh_loosevert_array(bm) + if self.looseverts: + self.looseverts_co = bgl.Buffer(bgl.GL_FLOAT, (len(self.looseverts), 3), [bm.verts[i].co for i in self.looseverts]) + else: + if False: #Blender 2.8 + self.verts = get_mesh_vert_co_array(me) + if create_tris: + self.tri_verts = get_mesh_tri_verts_array(me) + self.tris_co = get_mesh_tri_co_array(me, self.tri_verts) + if create_edges or create_looseverts: + self.edge_verts = get_mesh_edge_verts_array(me) + + if create_edges: + self.edges_co = get_mesh_edge_co_array(me, self.edge_verts) + if create_looseverts: + self.looseverts = get_mesh_loosevert_array(me, self.edge_verts) + if self.looseverts: + self.looseverts_co = bgl.Buffer(bgl.GL_FLOAT, (len(self.looseverts), 3), [me.vertices[i].co for i in self.looseverts]) + + else: #TODO + self.looseverts = bgl.Buffer(bgl.GL_INT, 1) + self.looseverts_co = bgl.Buffer(bgl.GL_FLOAT, (1, 3)) + + def __del__(self): + del self.tri_verts, self.edge_verts, self.looseverts + del self.tris_co, self.edges_co, self.looseverts_co + + +class GPU_Indices_Mesh(): + shader = Shader( + load_shader('3D_vert.glsl'), + None, + load_shader('primitive_id_frag.glsl'), + ) + + unif_use_clip_planes = bgl.glGetUniformLocation(shader.program, 'use_clip_planes') + unif_clip_plane = bgl.glGetUniformLocation(shader.program, 'clip_plane') + + unif_MVP = bgl.glGetUniformLocation(shader.program, 'MVP') + unif_MV = bgl.glGetUniformLocation(shader.program, 'MV') + unif_offset = bgl.glGetUniformLocation(shader.program, 'offset') + + attr_pos = bgl.glGetAttribLocation(shader.program, 'pos') + attr_primitive_id = bgl.glGetAttribLocation(shader.program, 'primitive_id') + + P = bgl.Buffer(bgl.GL_FLOAT, (4, 4)) + MV = bgl.Buffer(bgl.GL_FLOAT, (4, 4)) + + # returns of public API # + vert_index = bgl.Buffer(bgl.GL_INT, 1) + + tri_co = bgl.Buffer(bgl.GL_FLOAT, (3, 3)) + edge_co = bgl.Buffer(bgl.GL_FLOAT, (2, 3)) + vert_co = bgl.Buffer(bgl.GL_FLOAT, 3) + + def __init__(self, obj, draw_tris, draw_edges, draw_verts): + self._NULL = VoidBufValue(0) + + self.MVP = bgl.Buffer(bgl.GL_FLOAT, (4, 4)) + + self.obj = obj + self.draw_tris = draw_tris + self.draw_edges = draw_edges + self.draw_verts = draw_verts + + self.vbo = None + self.vbo_tris = None + self.vbo_edges = None + self.vbo_verts = None + + ## Create VAO ## + self.vao = bgl.Buffer(bgl.GL_INT, 1) + bgl.glGenVertexArrays(1, self.vao) + bgl.glBindVertexArray(self.vao[0]) + + ## Init Array ## + mesh_arrays = _Mesh_Arrays(obj, draw_tris, draw_edges, draw_verts) + + ## Create VBO for vertices ## + if False: # Blender 2.8 + if not mesh_arrays.verts: + self.draw_tris = False + self.draw_edges = False + self.draw_verts = False + return + + self.vbo_len = len(mesh_arrays.verts) + + self.vbo = bgl.Buffer(bgl.GL_INT, 1) + bgl.glGenBuffers(1, self.vbo) + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo[0]) + bgl.glBufferData(bgl.GL_ARRAY_BUFFER, self.vbo_len * 12, mesh_arrays.verts, bgl.GL_STATIC_DRAW) + + ## Create VBO for Tris ## + if mesh_arrays.tri_verts: + self.tri_verts = mesh_arrays.tri_verts + self.num_tris = len(self.tri_verts) + + self.vbo_tris = bgl.Buffer(bgl.GL_INT, 1) + bgl.glGenBuffers(1, self.vbo_tris) + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_tris[0]) + bgl.glBufferData(bgl.GL_ARRAY_BUFFER, self.num_tris * 36, mesh_arrays.tris_co, bgl.GL_STATIC_DRAW) + + tri_indices = np.repeat(np.arange(self.num_tris, dtype = 'f4'), 3) + self.vbo_tri_indices = bgl.Buffer(bgl.GL_INT, 1) + bgl.glGenBuffers(1, self.vbo_tri_indices) + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_tri_indices[0]) + bgl.glBufferData(bgl.GL_ARRAY_BUFFER, self.num_tris * 12, np_array_as_bgl_Buffer(tri_indices), bgl.GL_STATIC_DRAW) + del tri_indices + + else: + self.num_tris = 0 + self.draw_tris = False + + ## Create VBO for Edges ## + if mesh_arrays.edge_verts: + self.edge_verts = mesh_arrays.edge_verts + self.num_edges = len(self.edge_verts) + + self.vbo_edges = bgl.Buffer(bgl.GL_INT, 1) + bgl.glGenBuffers(1, self.vbo_edges) + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_edges[0]) + bgl.glBufferData(bgl.GL_ARRAY_BUFFER, self.num_edges * 24, mesh_arrays.edges_co, bgl.GL_STATIC_DRAW) + + edge_indices = np.repeat(np.arange(self.num_edges, dtype = 'f4'),2) + self.vbo_edge_indices = bgl.Buffer(bgl.GL_INT, 1) + bgl.glGenBuffers(1, self.vbo_edge_indices) + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_edge_indices[0]) + bgl.glBufferData(bgl.GL_ARRAY_BUFFER, self.num_edges * 8, np_array_as_bgl_Buffer(edge_indices), bgl.GL_STATIC_DRAW) + del edge_indices + else: + self.num_edges = 0 + self.draw_edges = False + + ## Create EBO for Loose Verts ## + if mesh_arrays.looseverts: + self.looseverts = mesh_arrays.looseverts + self.num_verts = len(mesh_arrays.looseverts) + + self.vbo_verts = bgl.Buffer(bgl.GL_INT, 1) + bgl.glGenBuffers(1, self.vbo_verts) + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_verts[0]) + bgl.glBufferData(bgl.GL_ARRAY_BUFFER, self.num_verts * 12, mesh_arrays.looseverts_co, bgl.GL_STATIC_DRAW) + + looseverts_indices = np.arange(self.num_verts, dtype = 'f4') + self.vbo_looseverts_indices = bgl.Buffer(bgl.GL_INT, 1) + bgl.glGenBuffers(1, self.vbo_looseverts_indices) + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_looseverts_indices[0]) + bgl.glBufferData(bgl.GL_ARRAY_BUFFER, self.num_verts * 4, np_array_as_bgl_Buffer(looseverts_indices), bgl.GL_STATIC_DRAW) + del looseverts_indices + else: + self.num_verts = 0 + self.draw_verts = False + + del mesh_arrays + + bgl.glBindVertexArray(0) + + + def get_tot_elems(self): + tot = 0 + + if self.draw_tris: + tot += self.num_tris + + if self.draw_edges: + tot += self.num_edges + + if self.draw_verts: + tot += self.num_verts + + return tot + + + def set_draw_mode(self, draw_tris, draw_edges, draw_verts): + self.draw_tris = draw_tris and self.vbo_tris + self.draw_edges = draw_edges and self.vbo_edges + self.draw_verts = draw_verts and self.vbo_verts + + + @classmethod + def set_ProjectionMatrix(cls, P): + cls.P[:] = P + + + def set_ModelViewMatrix(self, MV): + self.MV[:] = MV[:] + self.MVP[:] = Matrix(self.P) * MV + + + def Draw(self, index_offset): + self.first_index = index_offset + bgl.glUseProgram(self.shader.program) + bgl.glBindVertexArray(self.vao[0]) + + bgl.glUniformMatrix4fv(self.unif_MV, 1, bgl.GL_TRUE, self.MV) + bgl.glUniformMatrix4fv(self.unif_MVP, 1, bgl.GL_TRUE, self.MVP) + + if self.draw_tris: + bgl.glUniform1f(self.unif_offset, float(index_offset)) # bgl has no glUniform1ui :\ + + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_tris[0]) + bgl.glEnableVertexAttribArray(self.attr_pos) + bgl.glVertexAttribPointer(self.attr_pos, 3, bgl.GL_FLOAT, bgl.GL_FALSE, 0, self._NULL.buf) + + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_tri_indices[0]) + bgl.glEnableVertexAttribArray(self.attr_primitive_id) + bgl.glVertexAttribPointer(self.attr_primitive_id, 1, bgl.GL_FLOAT, bgl.GL_FALSE, 0, self._NULL.buf) + + bgl.glDrawArrays(bgl.GL_TRIANGLES, 0, self.num_tris * 3) + + index_offset += self.num_tris + bgl.glDepthRange(-0.00005, 0.99995) + + if self.draw_edges: + bgl.glUniform1f(self.unif_offset, float(index_offset)) #TODO: use glUniform1ui + + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_edges[0]) + bgl.glVertexAttribPointer(self.attr_pos, 3, bgl.GL_FLOAT, bgl.GL_FALSE, 0, self._NULL.buf) + bgl.glEnableVertexAttribArray(self.attr_pos) + + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_edge_indices[0]) + bgl.glVertexAttribPointer(self.attr_primitive_id, 1, bgl.GL_FLOAT, bgl.GL_FALSE, 0, self._NULL.buf) + bgl.glEnableVertexAttribArray(self.attr_primitive_id) + + bgl.glDrawArrays(bgl.GL_LINES, 0, self.num_edges * 2) + + index_offset += self.num_edges + + if self.draw_verts: + bgl.glUniform1f(self.unif_offset, float(index_offset)) #TODO: use glUniform1ui + + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_verts[0]) + bgl.glVertexAttribPointer(self.attr_pos, 3, bgl.GL_FLOAT, bgl.GL_FALSE, 0, self._NULL.buf) + bgl.glEnableVertexAttribArray(self.attr_pos) + + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_looseverts_indices[0]) + bgl.glVertexAttribPointer(self.attr_primitive_id, 1, bgl.GL_FLOAT, bgl.GL_FALSE, 0, self._NULL.buf) + bgl.glEnableVertexAttribArray(self.attr_primitive_id) + + bgl.glDrawArrays(bgl.GL_POINTS, 0, self.num_verts) + + bgl.glDepthRange(0.0, 1.0) + + + def get_tri_co(self, index): + bgl.glBindVertexArray(self.vao[0]) + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_tris[0]) + bgl.glGetBufferSubData(bgl.GL_ARRAY_BUFFER, index * 36, 36, self.tri_co) + bgl.glBindVertexArray(0) + return self.tri_co + + + def get_edge_co(self, index): + bgl.glBindVertexArray(self.vao[0]) + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_edges[0]) + bgl.glGetBufferSubData(bgl.GL_ARRAY_BUFFER, index * 24, 24, self.edge_co) + bgl.glBindVertexArray(0) + return self.edge_co + + + def get_loosevert_co(self, index): + bgl.glBindVertexArray(self.vao[0]) + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vbo_verts[0]) + bgl.glGetBufferSubData(bgl.GL_ARRAY_BUFFER, index * 12, 12, self.vert_co) + bgl.glBindVertexArray(0) + return self.vert_co + + + def get_tri_verts(self, index): + return self.tri_verts[index] + + + def get_edge_verts(self, index): + return self.edge_verts[index] + + + def get_loosevert_index(self, index): + return self.looseverts[index] + + + def __del__(self): + del self._NULL + + if self.vbo_tris: + bgl.glDeleteBuffers(1, self.vbo_tris) + bgl.glDeleteBuffers(1, self.vbo_tri_indices) + del self.tri_verts + + if self.vbo_edges: + bgl.glDeleteBuffers(1, self.vbo_edges) + bgl.glDeleteBuffers(1, self.vbo_edge_indices) + del self.edge_verts + + if self.vbo_verts: + bgl.glDeleteBuffers(1, self.vbo_verts) + bgl.glDeleteBuffers(1, self.vbo_looseverts_indices) + del self.looseverts + + bgl.glDeleteVertexArrays(1, self.vao) + #print('mesh_del', self.obj.name) + + +class PreviousGLState: + buf = bgl.Buffer(bgl.GL_INT, (4, 1)) + cur_program = buf[0] + cur_vao = buf[1] + cur_vbo = buf[2] + cur_ebo = buf[3] + + +def _store_current_shader_state(cls): + bgl.glGetIntegerv(bgl.GL_CURRENT_PROGRAM, cls.cur_program) + bgl.glGetIntegerv(bgl.GL_VERTEX_ARRAY_BINDING, cls.cur_vao) + bgl.glGetIntegerv(bgl.GL_ARRAY_BUFFER_BINDING, cls.cur_vbo) + bgl.glGetIntegerv(bgl.GL_ELEMENT_ARRAY_BUFFER_BINDING, cls.cur_ebo) + + +def _restore_shader_state(cls): + bgl.glUseProgram(cls.cur_program[0]) + bgl.glBindVertexArray(cls.cur_vao[0]) + bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, cls.cur_vbo[0]) + bgl.glBindBuffer(bgl.GL_ELEMENT_ARRAY_BUFFER, cls.cur_ebo[0]) + + +def gpu_Indices_enable_state(): + _store_current_shader_state(PreviousGLState) + + bgl.glUseProgram(GPU_Indices_Mesh.shader.program) + #bgl.glBindVertexArray(GPU_Indices_Mesh.vao[0]) + +def gpu_Indices_restore_state(): + bgl.glBindVertexArray(0) + _restore_shader_state(PreviousGLState) diff --git a/modules/snap_context/resources/3D_vert.glsl b/modules/snap_context/resources/3D_vert.glsl new file mode 100644 index 00000000..63891b93 --- /dev/null +++ b/modules/snap_context/resources/3D_vert.glsl @@ -0,0 +1,27 @@ +#version 120 + +uniform bool use_clip_planes; +uniform vec4 clip_plane[4]; +varying vec4 clip_distance; + +uniform mat4 MV; +uniform mat4 MVP; + +attribute vec3 pos; +attribute float primitive_id; +varying float primitive_id_var; + +void main() +{ + if (use_clip_planes) { + vec4 g_pos = MV * vec4(pos, 1.0); + + clip_distance[0] = dot(clip_plane[0], g_pos); + clip_distance[1] = dot(clip_plane[1], g_pos); + clip_distance[2] = dot(clip_plane[2], g_pos); + clip_distance[3] = dot(clip_plane[3], g_pos); + } + + primitive_id_var = primitive_id; + gl_Position = MVP * vec4(pos, 1.0); +} diff --git a/modules/snap_context/resources/primitive_id_frag.glsl b/modules/snap_context/resources/primitive_id_frag.glsl new file mode 100644 index 00000000..43801425 --- /dev/null +++ b/modules/snap_context/resources/primitive_id_frag.glsl @@ -0,0 +1,22 @@ +#version 120 + +uniform bool use_clip_planes; +varying vec4 clip_distance; + +uniform float offset; + +flat varying float primitive_id_var; + +void main() +{ + if (use_clip_planes && + ((clip_distance[0] < 0) || + (clip_distance[1] < 0) || + (clip_distance[2] < 0) || + (clip_distance[3] < 0))) + { + discard; + } + + gl_FragColor = vec4(offset + primitive_id_var, 0, 0, 0); +} diff --git a/modules/snap_context/utils_projection.py b/modules/snap_context/utils_projection.py new file mode 100644 index 00000000..70c110f4 --- /dev/null +++ b/modules/snap_context/utils_projection.py @@ -0,0 +1,213 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# ##### END GPL LICENSE BLOCK ##### + + +from mathutils import Vector +from mathutils.geometry import intersect_point_line + + +def depth_get(co, ray_start, ray_dir): + dvec = co - ray_start + return dvec.dot(ray_dir) + + +def region_2d_to_orig_and_view_vector(region, rv3d, coord): + viewinv = rv3d.view_matrix.inverted() + persinv = rv3d.perspective_matrix.inverted() + + dx = (2.0 * coord[0] / region.width) - 1.0 + dy = (2.0 * coord[1] / region.height) - 1.0 + + if rv3d.is_perspective: + origin_start = viewinv.translation.copy() + + out = Vector((dx, dy, -0.5)) + + w = out.dot(persinv[3].xyz) + persinv[3][3] + + view_vector = ((persinv * out) / w) - origin_start + else: + view_vector = -viewinv.col[2].xyz + + origin_start = ((persinv.col[0].xyz * dx) + + (persinv.col[1].xyz * dy) + + viewinv.translation) + + view_vector.normalize() + return view_vector, origin_start + + +def project_co_v3(sctx, co): + proj_co = sctx.proj_mat * co.to_4d() + proj_co.xy /= proj_co.w + + win_half = sctx.winsize * 0.5 + proj_co[0] = (proj_co[0] + 1.0) * win_half[0] + proj_co[1] = (proj_co[1] + 1.0) * win_half[1] + + return proj_co.xy + + + +def intersect_boundbox_threshold(sctx, MVP, ray_origin_local, ray_direction_local, bbmin, bbmax): + local_bvmin = Vector() + local_bvmax = Vector() + tmin = Vector() + tmax = Vector() + + if (ray_direction_local[0] < 0.0): + local_bvmin[0] = bbmax[0] + local_bvmax[0] = bbmin[0] + else: + local_bvmin[0] = bbmin[0] + local_bvmax[0] = bbmax[0] + + if (ray_direction_local[1] < 0.0): + local_bvmin[1] = bbmax[1] + local_bvmax[1] = bbmin[1] + else: + local_bvmin[1] = bbmin[1] + local_bvmax[1] = bbmax[1] + + if (ray_direction_local[2] < 0.0): + local_bvmin[2] = bbmax[2] + local_bvmax[2] = bbmin[2] + else: + local_bvmin[2] = bbmin[2] + local_bvmax[2] = bbmax[2] + + if (ray_direction_local[0]): + tmin[0] = (local_bvmin[0] - ray_origin_local[0]) / ray_direction_local[0] + tmax[0] = (local_bvmax[0] - ray_origin_local[0]) / ray_direction_local[0] + else: + tmin[0] = tmax[0] = sctx.depth_range[1] + + if (ray_direction_local[1]): + tmin[1] = (local_bvmin[1] - ray_origin_local[1]) / ray_direction_local[1] + tmax[1] = (local_bvmax[1] - ray_origin_local[1]) / ray_direction_local[1] + else: + tmin[1] = tmax[1] = sctx.depth_range[1] + + if (ray_direction_local[2]): + tmin[2] = (local_bvmin[2] - ray_origin_local[2]) / ray_direction_local[2] + tmax[2] = (local_bvmax[2] - ray_origin_local[2]) / ray_direction_local[2] + else: + tmin[2] = tmax[2] = sctx.depth_range[1] + + # `va` and `vb` are the coordinates of the AABB edge closest to the ray # + va = Vector() + vb = Vector() + # `rtmin` and `rtmax` are the minimum and maximum distances of the ray hits on the AABB # + + if ((tmax[0] <= tmax[1]) and (tmax[0] <= tmax[2])): + rtmax = tmax[0] + va[0] = vb[0] = local_bvmax[0] + main_axis = 3 + elif ((tmax[1] <= tmax[0]) and (tmax[1] <= tmax[2])): + rtmax = tmax[1] + va[1] = vb[1] = local_bvmax[1] + main_axis = 2 + else: + rtmax = tmax[2] + va[2] = vb[2] = local_bvmax[2] + main_axis = 1 + + if ((tmin[0] >= tmin[1]) and (tmin[0] >= tmin[2])): + rtmin = tmin[0] + va[0] = vb[0] = local_bvmin[0] + main_axis -= 3 + + elif ((tmin[1] >= tmin[0]) and (tmin[1] >= tmin[2])): + rtmin = tmin[1] + va[1] = vb[1] = local_bvmin[1] + main_axis -= 1 + + else: + rtmin = tmin[2] + va[2] = vb[2] = local_bvmin[2] + main_axis -= 2 + + if (main_axis < 0): + main_axis += 3 + +#ifdef IGNORE_BEHIND_RAY + depth_max = depth_get(local_bvmax, ray_origin_local, ray_direction_local) + if (depth_max < sctx.depth_range[0]): + return False +#endif + + if (rtmin <= rtmax): + # if rtmin < rtmax, ray intersect `AABB` # + return True + + if (ray_direction_local[main_axis] < 0.0): + va[main_axis] = local_bvmax[main_axis] + vb[main_axis] = local_bvmin[main_axis] + + else: + va[main_axis] = local_bvmin[main_axis] + vb[main_axis] = local_bvmax[main_axis] + + win_half = sctx.winsize * 0.5 + + scale = abs(local_bvmax[main_axis] - local_bvmin[main_axis]) + + va2d = Vector(( + (MVP[0].xyz.dot(va) + MVP[0][3]), + (MVP[1].xyz.dot(va) + MVP[1][3]), + )) + + vb2d = Vector(( + (va2d[0] + MVP[0][main_axis] * scale), + (va2d[1] + MVP[1][main_axis] * scale), + )) + + depth_a = MVP[3].xyz.dot(va) + MVP[3][3] + depth_b = depth_a + MVP[3][main_axis] * scale + + va2d /= depth_a + vb2d /= depth_b + + va2d[0] = (va2d[0] + 1.0) * win_half[0] + va2d[1] = (va2d[1] + 1.0) * win_half[1] + vb2d[0] = (vb2d[0] + 1.0) * win_half[0] + vb2d[1] = (vb2d[1] + 1.0) * win_half[1] + + p, fac = intersect_point_line(sctx.mval, va2d, vb2d) + if fac < 0.0: + return (sctx.mval - va2d).length_squared < sctx._dist_px_sq + elif fac > 1.0: + return (sctx.mval - vb2d).length_squared < sctx._dist_px_sq + else: + return (sctx.mval - p).length_squared < sctx._dist_px_sq + + +def intersect_ray_segment_fac(v0, v1, ray_direction, ray_origin): + a = v1 - v0 + t = v0 - ray_origin + n = a.cross(ray_direction) + nlen = n.length_squared + + # if (nlen == 0.0f) the lines are parallel, has no nearest point, only distance squared.*/ + if nlen == 0.0: + # Calculate the distance to the nearest point to origin then # + return a.dot(ray_direction) < 0 + else: + c = n - t + cray = c.cross(ray_direction) + return cray.dot(n) / nlen + diff --git a/modules/snap_context/utils_shader.py b/modules/snap_context/utils_shader.py new file mode 100644 index 00000000..7a664ad6 --- /dev/null +++ b/modules/snap_context/utils_shader.py @@ -0,0 +1,85 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# ##### END GPL LICENSE BLOCK ##### + + +import bgl + +def check_shaderError(shader, flag, isProgram, errorMessage): + success = bgl.Buffer(bgl.GL_INT, 1) + + if isProgram: + bgl.glGetProgramiv(shader, flag, success) + else: + bgl.glGetShaderiv(shader, flag, success) + + if success[0] == bgl.GL_FALSE: + import numpy as np + from .bgl_ext import VoidBufValue + + offset = VoidBufValue(None) + error = bgl.Buffer(bgl.GL_BYTE, 1024) + if isProgram: + bgl.glGetProgramInfoLog(shader, 1024, offset.buf, error) + print(errorMessage, np.bytes_(error).decode("utf-8")) + else: + bgl.glGetShaderInfoLog(shader, 1024, offset.buf, error) + print(errorMessage, np.bytes_(error).decode("utf-8")) + + del offset + raise #RuntimeError(errorMessage, bgl.glGetShaderInfoLog(shader)) + + +def create_shader(source, shaderType): + shader = bgl.glCreateShader(shaderType) + + if shader == 0: + raise RuntimeError("Error: Shader creation failed!") + + bgl.glShaderSource(shader, source) + bgl.glCompileShader(shader) + + check_shaderError(shader, bgl.GL_COMPILE_STATUS, False, "Error: Shader compilation failed:") + + return shader + + +class Shader(): + def __init__(self, vertexcode, geomcode, fragcode): + self.program = bgl.glCreateProgram() + self.shaders = [] + + if vertexcode: + self.shaders.append(create_shader(vertexcode, bgl.GL_VERTEX_SHADER)) + if geomcode: + self.shaders.append(create_shader(geomcode, bgl.GL_GEOMETRY_SHADER)) + if fragcode: + self.shaders.append(create_shader(fragcode, bgl.GL_FRAGMENT_SHADER)) + + for shad in self.shaders: + bgl.glAttachShader(self.program, shad) + + bgl.glLinkProgram(self.program) + check_shaderError(self.program, bgl.GL_LINK_STATUS, True, "Error: Program linking failed:") + bgl.glValidateProgram(self.program) + check_shaderError(self.program, bgl.GL_VALIDATE_STATUS, True, "Error: Program is invalid:") + + def __del__(self): + for shad in self.shaders: + bgl.glDetachShader(self.program, shad) + bgl.glDeleteShader(shad) + bgl.glDeleteProgram(self.program) + print('shader_del') |