q3dviewer 1.2.2__py3-none-any.whl → 1.2.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,330 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
5
+ Distributed under MIT license. See LICENSE for more information.
6
+ """
7
+
8
+ import numpy as np
9
+ from q3dviewer.base_item import BaseItem
10
+ from OpenGL.GL import *
11
+ from OpenGL.GL import shaders
12
+ from q3dviewer.Qt.QtWidgets import QLabel, QCheckBox, QDoubleSpinBox, QSlider, QHBoxLayout, QLineEdit
13
+
14
+ import os
15
+ from q3dviewer.utils import set_uniform, text_to_rgba
16
+
17
+
18
+ class StaticMeshItem(BaseItem):
19
+ """
20
+ A OpenGL mesh item for rendering static 3D triangular meshes.
21
+ Optimized for static geometry with triangle-only rendering.
22
+ Data format: Nx9 numpy array (3 vertices per triangle, no good flag needed)
23
+
24
+ Attributes:
25
+ color (str or tuple): Accepts any valid matplotlib color (e.g., 'red', '#FF4500', (1.0, 0.5, 0.0)).
26
+ wireframe (bool): If True, renders the mesh in wireframe mode.
27
+ """
28
+ def __init__(self, color='lightblue', wireframe=False):
29
+ super(StaticMeshItem, self).__init__()
30
+ self.wireframe = wireframe
31
+ self.color = color
32
+ self.flat_rgb = text_to_rgba(color, flat=True)
33
+
34
+ # Static mesh buffer: N x 9 numpy array
35
+ # Each row: [v0.x, v0.y, v0.z, v1.x, v1.y, v1.z, v2.x, v2.y, v2.z]
36
+ self.vertices = None
37
+ self.num_triangles = 0
38
+
39
+ # OpenGL objects
40
+ self.vao = None
41
+ self.vbo = None
42
+ self.program = None
43
+
44
+ # Fixed rendering parameters
45
+ self.enable_lighting = True
46
+ self.line_width = 1.0
47
+ self.light_color = [1.0, 1.0, 1.0]
48
+ self.ambient_strength = 0.1
49
+ self.diffuse_strength = 1.2
50
+ self.specular_strength = 0.1
51
+ self.shininess = 32.0
52
+ self.alpha = 1.0
53
+
54
+ # Settings flag
55
+ self.need_update_setting = True
56
+ self.need_update_buffer = True
57
+ self.path = os.path.dirname(__file__)
58
+
59
+ def add_setting(self, layout):
60
+ """Add UI controls for mesh visualization"""
61
+ # Wireframe toggle
62
+ self.wireframe_box = QCheckBox("Wireframe Mode")
63
+ self.wireframe_box.setChecked(self.wireframe)
64
+ self.wireframe_box.toggled.connect(self.update_wireframe)
65
+ layout.addWidget(self.wireframe_box)
66
+
67
+ # Enable lighting toggle
68
+ self.lighting_box = QCheckBox("Enable Lighting")
69
+ self.lighting_box.setChecked(self.enable_lighting)
70
+ self.lighting_box.toggled.connect(self.update_enable_lighting)
71
+ layout.addWidget(self.lighting_box)
72
+
73
+ # Color setting
74
+ label_rgb = QLabel("Color:")
75
+ label_rgb.setToolTip("Use hex color, i.e. #FF4500, or named color, i.e. 'red'")
76
+ layout.addWidget(label_rgb)
77
+ self.edit_rgb = QLineEdit()
78
+ self.edit_rgb.setToolTip("Use hex color, i.e. #FF4500, or named color, i.e. 'red'")
79
+ self.edit_rgb.setText(self.color)
80
+ self.edit_rgb.textChanged.connect(self._on_color)
81
+ layout.addWidget(self.edit_rgb)
82
+
83
+ # Material property controls for Phong lighting
84
+ if self.enable_lighting:
85
+ # Ambient strength control
86
+ ambient_layout = QHBoxLayout()
87
+ ambient_label = QLabel("Ambient Strength:")
88
+ ambient_layout.addWidget(ambient_label)
89
+ self.ambient_slider = QSlider()
90
+ self.ambient_slider.setOrientation(1) # Qt.Horizontal
91
+ self.ambient_slider.setRange(0, 100)
92
+ self.ambient_slider.setValue(int(self.ambient_strength * 100))
93
+ self.ambient_slider.valueChanged.connect(lambda v: self.update_ambient_strength(v / 100.0))
94
+ ambient_layout.addWidget(self.ambient_slider)
95
+ layout.addLayout(ambient_layout)
96
+
97
+ # Diffuse strength control
98
+ diffuse_layout = QHBoxLayout()
99
+ diffuse_label = QLabel("Diffuse Strength:")
100
+ diffuse_layout.addWidget(diffuse_label)
101
+ self.diffuse_slider = QSlider()
102
+ self.diffuse_slider.setOrientation(1)
103
+ self.diffuse_slider.setRange(0, 200)
104
+ self.diffuse_slider.setValue(int(self.diffuse_strength * 100))
105
+ self.diffuse_slider.valueChanged.connect(lambda v: self.update_diffuse_strength(v / 100.0))
106
+ diffuse_layout.addWidget(self.diffuse_slider)
107
+ layout.addLayout(diffuse_layout)
108
+
109
+ # Specular strength control
110
+ specular_layout = QHBoxLayout()
111
+ specular_label = QLabel("Specular Strength:")
112
+ specular_layout.addWidget(specular_label)
113
+ self.specular_slider = QSlider()
114
+ self.specular_slider.setOrientation(1)
115
+ self.specular_slider.setRange(0, 100)
116
+ self.specular_slider.setValue(int(self.specular_strength * 100))
117
+ self.specular_slider.valueChanged.connect(lambda v: self.update_specular_strength(v / 100.0))
118
+ specular_layout.addWidget(self.specular_slider)
119
+ layout.addLayout(specular_layout)
120
+
121
+ # Shininess control
122
+ shininess_layout = QHBoxLayout()
123
+ shininess_label = QLabel("Shininess:")
124
+ shininess_layout.addWidget(shininess_label)
125
+ self.shininess_slider = QSlider()
126
+ self.shininess_slider.setOrientation(1)
127
+ self.shininess_slider.setRange(1, 256)
128
+ self.shininess_slider.setValue(int(self.shininess))
129
+ self.shininess_slider.valueChanged.connect(lambda v: self.update_shininess(float(v)))
130
+ shininess_layout.addWidget(self.shininess_slider)
131
+ layout.addLayout(shininess_layout)
132
+
133
+ def _on_color(self, color):
134
+ try:
135
+ self.color = color
136
+ self.flat_rgb = text_to_rgba(color, flat=True)
137
+ self.need_update_setting = True
138
+ except ValueError:
139
+ pass
140
+
141
+ def update_wireframe(self, value):
142
+ self.wireframe = value
143
+
144
+ def update_enable_lighting(self, value):
145
+ self.enable_lighting = value
146
+ self.need_update_setting = True
147
+
148
+ def update_line_width(self, value):
149
+ self.line_width = value
150
+ self.need_update_setting = True
151
+
152
+ def update_ambient_strength(self, value):
153
+ self.ambient_strength = value
154
+ self.need_update_setting = True
155
+
156
+ def update_diffuse_strength(self, value):
157
+ self.diffuse_strength = value
158
+ self.need_update_setting = True
159
+
160
+ def update_specular_strength(self, value):
161
+ self.specular_strength = value
162
+ self.need_update_setting = True
163
+
164
+ def update_shininess(self, value):
165
+ self.shininess = value
166
+ self.need_update_setting = True
167
+
168
+ def update_alpha(self, value):
169
+ """Update mesh alpha (opacity)"""
170
+ self.alpha = float(value)
171
+ self.need_update_setting = True
172
+
173
+ def set_data(self, data):
174
+ """
175
+ Set complete mesh data at once.
176
+
177
+ Args:
178
+ data: Nx9 numpy array where N is the number of triangles
179
+ Each row: [v0.x, v0.y, v0.z, v1.x, v1.y, v1.z, v2.x, v2.y, v2.z]
180
+ """
181
+ print("Setting static mesh data with {} triangles".format(len(data)))
182
+ if not isinstance(data, np.ndarray):
183
+ raise ValueError("Data must be a numpy array")
184
+
185
+ # Check shape
186
+ if data.ndim != 2 or data.shape[1] != 9:
187
+ # Try to reshape if it's Nx3 (vertex list)
188
+ if data.ndim == 2 and data.shape[1] == 3 and data.shape[0] % 3 == 0:
189
+ data = data.reshape(-1, 9)
190
+ else:
191
+ raise ValueError(f"Invalid data shape {data.shape}. Expected Nx9 or (N*3)x3")
192
+
193
+ self.vertices = data.astype(np.float32)
194
+ self.num_triangles = len(self.vertices)
195
+ self.need_update_buffer = True
196
+
197
+ def clear_mesh(self):
198
+ """Clear all mesh data"""
199
+ self.vertices = None
200
+ self.num_triangles = 0
201
+ self.need_update_buffer = True
202
+
203
+ def initialize_gl(self):
204
+ """OpenGL initialization - load triangle shader"""
205
+ frag_shader = open(self.path + '/../shaders/mesh_frag.glsl', 'r').read()
206
+
207
+ try:
208
+ vert_shader = open(self.path + '/../shaders/triangle_mesh_vert.glsl', 'r').read()
209
+ geom_shader = open(self.path + '/../shaders/triangle_mesh_geom.glsl', 'r').read()
210
+ self.program = shaders.compileProgram(
211
+ shaders.compileShader(vert_shader, GL_VERTEX_SHADER),
212
+ shaders.compileShader(geom_shader, GL_GEOMETRY_SHADER),
213
+ shaders.compileShader(frag_shader, GL_FRAGMENT_SHADER),
214
+ )
215
+ except Exception as e:
216
+ print(f"Error compiling static mesh shader: {e}")
217
+ raise
218
+
219
+ def update_render_buffer(self):
220
+ """
221
+ Update GPU buffer with triangle data.
222
+ Each triangle: 9 floats (3 vertices x 3 coordinates)
223
+ """
224
+ if self.num_triangles == 0 or self.vertices is None:
225
+ return
226
+
227
+ # Initialize buffers on first call
228
+ if self.vao is None:
229
+ self.vao = glGenVertexArrays(1)
230
+ self.vbo = glGenBuffers(1)
231
+
232
+ if not self.need_update_buffer:
233
+ return
234
+
235
+ glBindVertexArray(self.vao)
236
+ glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
237
+
238
+ # Upload all triangle data
239
+ glBufferData(GL_ARRAY_BUFFER,
240
+ self.vertices.nbytes,
241
+ self.vertices,
242
+ GL_STATIC_DRAW)
243
+
244
+ # Setup vertex attributes
245
+ # Triangle: [v0.x, v0.y, v0.z, v1.x, v1.y, v1.z, v2.x, v2.y, v2.z]
246
+ # 9 floats = 36 bytes stride
247
+ stride = 36
248
+
249
+ # v0 (location 1) - vec3
250
+ glEnableVertexAttribArray(1)
251
+ glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, stride, ctypes.c_void_p(0))
252
+ glVertexAttribDivisor(1, 1)
253
+
254
+ # v1 (location 2) - vec3
255
+ glEnableVertexAttribArray(2)
256
+ glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, stride, ctypes.c_void_p(12))
257
+ glVertexAttribDivisor(2, 1)
258
+
259
+ # v2 (location 3) - vec3
260
+ glEnableVertexAttribArray(3)
261
+ glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, stride, ctypes.c_void_p(24))
262
+ glVertexAttribDivisor(3, 1)
263
+
264
+ glBindVertexArray(0)
265
+ glBindBuffer(GL_ARRAY_BUFFER, 0)
266
+ self.need_update_buffer = False
267
+
268
+ def update_setting(self):
269
+ """Set rendering parameters"""
270
+ if not self.need_update_setting:
271
+ return
272
+
273
+ set_uniform(self.program, int(self.enable_lighting), 'if_light')
274
+ set_uniform(self.program, 1, 'two_sided')
275
+ set_uniform(self.program, np.array(self.light_color, dtype=np.float32), 'light_color')
276
+ set_uniform(self.program, float(self.ambient_strength), 'ambient_strength')
277
+ set_uniform(self.program, float(self.diffuse_strength), 'diffuse_strength')
278
+ set_uniform(self.program, float(self.specular_strength), 'specular_strength')
279
+ set_uniform(self.program, float(self.shininess), 'shininess')
280
+ set_uniform(self.program, float(self.alpha), 'alpha')
281
+ set_uniform(self.program, int(self.flat_rgb), 'flat_rgb')
282
+ self.need_update_setting = False
283
+
284
+ def paint(self):
285
+ """
286
+ Render the static mesh using instanced rendering with geometry shader.
287
+ Each triangle instance is rendered as a point, geometry shader generates 1 triangle.
288
+ """
289
+ if self.num_triangles == 0 or self.vertices is None:
290
+ return
291
+
292
+ glUseProgram(self.program)
293
+
294
+ self.update_render_buffer()
295
+ self.update_setting()
296
+
297
+ view_matrix = self.glwidget().view_matrix
298
+ set_uniform(self.program, view_matrix, 'view')
299
+ project_matrix = self.glwidget().projection_matrix
300
+ set_uniform(self.program, project_matrix, 'projection')
301
+ view_pos = self.glwidget().center
302
+ set_uniform(self.program, np.array(view_pos), 'view_pos')
303
+
304
+ # Enable blending and depth testing
305
+ glEnable(GL_BLEND)
306
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
307
+ glEnable(GL_DEPTH_TEST)
308
+ glDisable(GL_CULL_FACE) # two-sided rendering
309
+
310
+ # Set line width
311
+ glLineWidth(self.line_width)
312
+
313
+ # Bind VAO
314
+ glBindVertexArray(self.vao)
315
+
316
+ if self.wireframe:
317
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
318
+ else:
319
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
320
+
321
+ # Draw using instanced rendering
322
+ # Input: POINTS (one per triangle instance)
323
+ # Geometry shader generates 1 triangle (3 vertices) per point
324
+ glDrawArraysInstanced(GL_POINTS, 0, 1, self.num_triangles)
325
+
326
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
327
+ glBindVertexArray(0)
328
+ glDisable(GL_DEPTH_TEST)
329
+ glDisable(GL_BLEND)
330
+ glUseProgram(0)
q3dviewer/glwidget.py CHANGED
@@ -55,6 +55,7 @@ class GLWidget(BaseGLWidget):
55
55
  self.followable_item_name = None
56
56
  self.setting_window = SettingWindow()
57
57
  self.enable_show_center = True
58
+ self.old_center = None
58
59
  super(GLWidget, self).__init__()
59
60
 
60
61
  def keyPressEvent(self, ev: QKeyEvent):
@@ -63,14 +64,38 @@ class GLWidget(BaseGLWidget):
63
64
  self.open_setting_window()
64
65
  else:
65
66
  super().keyPressEvent(ev)
67
+ if ev.key() == QtCore.Qt.Key_F: # reset follow
68
+ if self.followable_item_name is None:
69
+ self.initial_followable()
70
+
71
+ if self.followed_name != 'none':
72
+ self.followed_name = 'none'
73
+ print("Reset follow.")
74
+ elif len(self.followable_item_name) > 1:
75
+ self.followed_name = self.followable_item_name[1]
76
+ print("Set follow to ", self.followed_name)
77
+ else:
78
+ pass # do nothing
66
79
 
67
80
  def on_followable_selection(self, index):
68
81
  self.followed_name = self.followable_item_name[index]
69
82
 
83
+ def mouseDoubleClickEvent(self, event):
84
+ """Double click to set center."""
85
+ p = self.get_point(event.x(), event.y())
86
+ if p is not None:
87
+ self.set_center(p)
88
+ super().mouseDoubleClickEvent(event)
89
+
70
90
  def update(self):
71
91
  if self.followed_name != 'none':
72
92
  new_center = self.named_items[self.followed_name].T[:3, 3]
73
- self.set_center(new_center)
93
+ if self.old_center is None:
94
+ self.old_center = self.center
95
+ return
96
+ delta = new_center - self.old_center
97
+ self.set_center(self.center + delta)
98
+ self.old_center = new_center
74
99
  super().update()
75
100
 
76
101
  def add_setting(self, layout):
@@ -66,7 +66,7 @@ void main()
66
66
  {
67
67
  uint intensity = value >> 24;
68
68
  float range = vmax - vmin;
69
- float value = 1.0 - (float(intensity) - vmin) / range;
69
+ float value = (float(intensity) - vmin) / range;
70
70
  c.z = value;
71
71
  c.y = value;
72
72
  c.x = value;
@@ -0,0 +1,83 @@
1
+ #version 430 core
2
+
3
+ /*
4
+ Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
5
+ Distributed under MIT license. See LICENSE for more information.
6
+ */
7
+
8
+ layout(points) in;
9
+ layout(triangle_strip, max_vertices = 6) out;
10
+
11
+ // Input from vertex shader
12
+ in VS_OUT {
13
+ vec3 v0, v1, v2, v3;
14
+ float good;
15
+ } gs_in[];
16
+
17
+ // Uniforms
18
+ uniform mat4 view;
19
+ uniform mat4 projection;
20
+
21
+ uniform int flat_rgb;
22
+
23
+
24
+ // Output to fragment shader
25
+ out vec3 FragPos;
26
+ out vec3 Normal;
27
+ out vec3 objectColor;
28
+
29
+ // Calculate normal from three vertices
30
+ vec3 calculateNormal(vec3 a, vec3 b, vec3 c) {
31
+ vec3 edge1 = b - a;
32
+ vec3 edge2 = c - a;
33
+ return normalize(cross(edge1, edge2));
34
+ }
35
+
36
+ void emitVertex(vec3 pos, vec3 normal, vec3 color) {
37
+ FragPos = pos;
38
+ Normal = normal;
39
+ objectColor = color;
40
+ gl_Position = projection * view * vec4(pos, 1.0);
41
+ EmitVertex();
42
+ }
43
+
44
+ void main()
45
+ {
46
+ // Discard if face is not good
47
+ if (gs_in[0].good != 1.0) {
48
+ return;
49
+ }
50
+
51
+ vec3 v0 = gs_in[0].v0;
52
+ vec3 v1 = gs_in[0].v1;
53
+ vec3 v2 = gs_in[0].v2;
54
+ vec3 v3 = gs_in[0].v3;
55
+
56
+ float eps = 0.0001;
57
+
58
+ // Use default light blue color
59
+ vec3 color = vec3(
60
+ float((uint(flat_rgb) & uint(0x00FF0000)) >> 16)/255.,
61
+ float((uint(flat_rgb) & uint(0x0000FF00)) >> 8)/255.,
62
+ float( uint(flat_rgb) & uint(0x000000FF))/255.
63
+ );
64
+ // Triangle 1: (v0, v1, v2)
65
+ vec3 normal1 = calculateNormal(v0, v1, v2);
66
+ // Skip degenerate triangles
67
+ if (length(normal1) > eps) {
68
+ emitVertex(v0, normal1, color);
69
+ emitVertex(v1, normal1, color);
70
+ emitVertex(v2, normal1, color);
71
+ EndPrimitive();
72
+ }
73
+
74
+ // Triangle 2: (v0, v1, v3)
75
+ vec3 normal2 = calculateNormal(v0, v1, v3);
76
+ // Skip degenerate triangles
77
+ if (length(normal2) > eps) {
78
+ emitVertex(v0, normal2, color);
79
+ emitVertex(v1, normal2, color);
80
+ emitVertex(v3, normal2, color);
81
+ EndPrimitive();
82
+ }
83
+ }
@@ -1,65 +1,37 @@
1
- #version 330 core
2
- layout (location = 0) in vec3 aPos;
3
- layout (location = 1) in vec3 aNormal;
4
- layout (location = 2) in uint aColor;
1
+ #version 430 core
5
2
 
6
- out vec3 FragPos;
7
- out vec3 Normal;
8
- out vec3 objectColor;
3
+ /*
4
+ Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
5
+ Distributed under MIT license. See LICENSE for more information.
6
+ */
9
7
 
8
+ // Face attributes (per-instance) - vertex positions embedded in each face
9
+ layout(location = 1) in vec3 v0;
10
+ layout(location = 2) in vec3 v1;
11
+ layout(location = 3) in vec3 v2;
12
+ layout(location = 4) in vec3 v3;
13
+ layout(location = 5) in float good;
14
+
15
+ // Uniforms
10
16
  uniform mat4 view;
11
17
  uniform mat4 projection;
12
- uniform int flat_rgb;
13
- uniform int color_mode; // 0: FLAT, 1: Intensity, 2: RGB
14
- uniform float vmin;
15
- uniform float vmax;
16
-
17
- vec3 getRainbowColor(uint value_raw) {
18
- float range = vmax - vmin;
19
- float value = 1.0 - (float(value_raw) - vmin) / range;
20
- value = clamp(value, 0.0, 1.0);
21
- float hue = value * 5.0 + 1.0;
22
- int i = int(floor(hue));
23
- float f = hue - float(i);
24
- if (mod(i, 2) == 0) f = 1.0 - f;
25
- float n = 1.0 - f;
26
18
 
27
- vec3 color;
28
- if (i <= 1) color = vec3(n, 0.0, 1.0);
29
- else if (i == 2) color = vec3(0.0, n, 1.0);
30
- else if (i == 3) color = vec3(0.0, 1.0, n);
31
- else if (i == 4) color = vec3(n, 1.0, 0.0);
32
- else color = vec3(1.0, n, 0.0);
33
- return color;
34
- }
19
+ // Outputs to fragment shader (via geometry shader)
20
+ out VS_OUT {
21
+ vec3 v0, v1, v2, v3;
22
+ float good;
23
+ } vs_out;
35
24
 
36
25
  void main()
37
26
  {
38
- FragPos = aPos;
39
- Normal = aNormal;
40
-
41
- vec3 c = vec3(1.0, 1.0, 1.0);
27
+ // Pass vertex positions directly (no SSBO lookup needed)
28
+ vs_out.v0 = v0;
29
+ vs_out.v1 = v1;
30
+ vs_out.v2 = v2;
31
+ vs_out.v3 = v3;
32
+ vs_out.good = good;
42
33
 
43
- if (color_mode == 0) {
44
- // FLAT: use uniform flat color
45
- c.z = float( uint(flat_rgb) & uint(0x000000FF))/255.;
46
- c.y = float((uint(flat_rgb) & uint(0x0000FF00)) >> 8)/255.;
47
- c.x = float((uint(flat_rgb) & uint(0x00FF0000)) >> 16)/255.;
48
- }
49
- else if (color_mode == 1) {
50
- // Intensity: use intensity channel (bits 24-31) for rainbow color
51
- uint intensity = aColor >> 24;
52
- c = getRainbowColor(intensity);
53
- }
54
- else if (color_mode == 2) {
55
- // RGB: use RGB channels (bits 0-23)
56
- c.z = float(aColor & uint(0x000000FF))/255.;
57
- c.y = float((aColor & uint(0x0000FF00)) >> 8)/255.;
58
- c.x = float((aColor & uint(0x00FF0000)) >> 16)/255.;
59
- }
60
-
61
- objectColor = c;
62
-
63
- // Final vertex position (apply view/projection)
64
- gl_Position = projection * view * vec4(aPos, 1.0);
65
- }
34
+ // Output dummy point (geometry shader will generate triangles)
35
+ // Note: This position is not used; geometry shader generates actual triangles
36
+ gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
37
+ }
@@ -0,0 +1,66 @@
1
+ #version 430 core
2
+
3
+ /*
4
+ Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
5
+ Distributed under MIT license. See LICENSE for more information.
6
+ */
7
+
8
+ layout(points) in;
9
+ layout(triangle_strip, max_vertices = 3) out;
10
+
11
+ // Input from vertex shader
12
+ in VS_OUT {
13
+ vec3 v0, v1, v2;
14
+ } gs_in[];
15
+
16
+ // Uniforms
17
+ uniform mat4 view;
18
+ uniform mat4 projection;
19
+
20
+ uniform int flat_rgb;
21
+
22
+ // Output to fragment shader
23
+ out vec3 FragPos;
24
+ out vec3 Normal;
25
+ out vec3 objectColor;
26
+
27
+ // Calculate normal from three vertices
28
+ vec3 calculateNormal(vec3 a, vec3 b, vec3 c) {
29
+ vec3 edge1 = b - a;
30
+ vec3 edge2 = c - a;
31
+ return normalize(cross(edge1, edge2));
32
+ }
33
+
34
+ void emitVertex(vec3 pos, vec3 normal, vec3 color) {
35
+ FragPos = pos;
36
+ Normal = normal;
37
+ objectColor = color;
38
+ gl_Position = projection * view * vec4(pos, 1.0);
39
+ EmitVertex();
40
+ }
41
+
42
+ void main()
43
+ {
44
+ vec3 v0 = gs_in[0].v0;
45
+ vec3 v1 = gs_in[0].v1;
46
+ vec3 v2 = gs_in[0].v2;
47
+
48
+ float eps = 0.0001;
49
+
50
+ // Use color from uniform
51
+ vec3 color = vec3(
52
+ float((uint(flat_rgb) & uint(0x00FF0000)) >> 16)/255.,
53
+ float((uint(flat_rgb) & uint(0x0000FF00)) >> 8)/255.,
54
+ float( uint(flat_rgb) & uint(0x000000FF))/255.
55
+ );
56
+
57
+ // Triangle: (v0, v1, v2)
58
+ vec3 normal = calculateNormal(v0, v1, v2);
59
+ // Skip degenerate triangles
60
+ if (length(normal) > eps) {
61
+ emitVertex(v0, normal, color);
62
+ emitVertex(v1, normal, color);
63
+ emitVertex(v2, normal, color);
64
+ EndPrimitive();
65
+ }
66
+ }
@@ -0,0 +1,31 @@
1
+ #version 430 core
2
+
3
+ /*
4
+ Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
5
+ Distributed under MIT license. See LICENSE for more information.
6
+ */
7
+
8
+ // Triangle face attributes (per-instance) - 3 vertex positions
9
+ layout(location = 1) in vec3 v0;
10
+ layout(location = 2) in vec3 v1;
11
+ layout(location = 3) in vec3 v2;
12
+
13
+ // Uniforms
14
+ uniform mat4 view;
15
+ uniform mat4 projection;
16
+
17
+ // Outputs to fragment shader (via geometry shader)
18
+ out VS_OUT {
19
+ vec3 v0, v1, v2;
20
+ } vs_out;
21
+
22
+ void main()
23
+ {
24
+ // Pass vertex positions directly (no SSBO lookup needed)
25
+ vs_out.v0 = v0;
26
+ vs_out.v1 = v1;
27
+ vs_out.v2 = v2;
28
+
29
+ // Output dummy point (geometry shader will generate triangles)
30
+ gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
31
+ }