Gamp v0.0.7-36-g24b1eb6
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
GraphShapes01.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright Gothel Software e.K.
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * This Source Code Form is subject to the terms of the MIT License
8 * If a copy of the MIT was not distributed with this file,
9 * you can obtain one at https://opensource.org/license/mit/.
10 */
11
12#include <cstdio>
13#include <cmath>
14#include <gamp/graph/Graph.hpp>
17#include <memory>
18#include <string>
19#include <string_view>
20#include <vector>
21
22#include <jau/basic_types.hpp>
23#include <jau/cpp_lang_util.hpp>
24#include <jau/darray.hpp>
25#include <jau/debug.hpp>
26#include <jau/file_util.hpp>
27#include <jau/float_math.hpp>
28#include <jau/float_types.hpp>
29#include <jau/fraction_type.hpp>
31#include <jau/math/vec3f.hpp>
32#include <jau/math/vec4f.hpp>
33#include <jau/math/vec4f.hpp>
38
39#include <gamp/Gamp.hpp>
44
47
50
52
53using namespace jau::math;
54using namespace jau::math::util;
55using namespace jau::math::geom;
56
57using namespace gamp;
58using namespace gamp::wt;
59using namespace gamp::wt::event;
60
61using namespace gamp::graph;
62using namespace gamp::graph::tess;
63using namespace gamp::render::gl::glsl;
64using namespace gamp::render::gl::data;
65
69
71 : m( PMVMat4f::INVERSE_PROJECTION | PMVMat4f::INVERSE_MODELVIEW | PMVMat4f::INVERSE_TRANSPOSED_MODELVIEW ),
72 u( GLUniformSyncMatrices4f::create("gcu_PMVMatrix", m.getSyncPMvMviMvit()) ) // P, Mv, Mvi and Mvit
73 {}
74};
75
77 public:
79 bool m_isTwoPass = false;
80 bool m_pass1 = true;
82 bool m_hasNormalChannel = false;
83 bool m_hasLight0 = true;
84 bool m_hasColorChannel = false;
85 bool m_hasColorTexture = false;
86 };
87 private:
88 GraphRendererProps m_props;
89 ShaderState& m_st;
90 bool m_initialized;
91
92 public:
93 constexpr bool usesNormal() const noexcept { return m_props.m_hasLight0 || m_props.m_hasNormalChannel; }
94 constexpr GLsizei arrayCompsPerElement() const noexcept { return usesNormal()? 3*3 : 2*3; }
95
96 static constexpr bool DEBUG_MODE = true;
97 static constexpr std::string_view GLSL_PARAM_COMMENT_START = "\n// Gamp Graph Parameter Start\n";
98 static constexpr std::string_view GLSL_PARAM_COMMENT_END = "// Gamp Graph Parameter End\n\n";
99 static constexpr std::string_view GLSL_USE_COLOR_CHANNEL = "#define USE_COLOR_CHANNEL 1\n";
100 static constexpr std::string_view GLSL_USE_NORMAL_CHANNEL = "#define USE_NORMAL_CHANNEL 1\n";
101 static constexpr std::string_view GLSL_USE_LIGHT0 = "#define USE_LIGHT0 1\n";
102 static constexpr std::string_view GLSL_USE_COLOR_TEXTURE = "#define USE_COLOR_TEXTURE 1\n";
103 static constexpr std::string_view GLSL_USE_FRUSTUM_CLIPPING = "#define USE_FRUSTUM_CLIPPING 1\n";
104 static constexpr std::string_view GLSL_DEF_SAMPLE_COUNT = "#define SAMPLE_COUNT ";
105 static constexpr std::string_view GLSL_CONST_SAMPLE_COUNT = "const float sample_count = ";
106 static constexpr std::string_view GLSL_MAIN_BEGIN = "void main (void)\n{\n";
107 static constexpr std::string_view gcuTexture2D = "gcuTexture2D";
108 static constexpr std::string_view colTexLookupFuncName = "texture2D";
109 static constexpr std::string_view GLSL_USE_DISCARD = "#define USE_DISCARD 1\n";
110 static constexpr std::string_view shader_basename = "curverenderer01";
111 static constexpr std::string_view source_dir = "impl/graph/glsl";
112 static constexpr std::string_view bin_dir = "impl/graph/glsl/bin";
113
114 public:
116 : m_st(st),
117 m_initialized(false)
118 { }
119
120 constexpr bool initialized() const noexcept { return m_initialized; }
121
122 bool init(GL& gl, const jau::fraction_timespec& when) {
123 std::string vertexShaderName, fragmentShaderName;
124 vertexShaderName.append(shader_basename);
125 if( m_props.m_isTwoPass ) {
126 vertexShaderName.append("-pass").append(m_props.m_pass1 ? "1":"2");
127 } else {
128 vertexShaderName.append("-single");
129 }
130 fragmentShaderName.append(shader_basename).append("-segment-head");
131
132 ShaderCodeRef rsVp = ShaderCode::create(gl, GL_VERTEX_SHADER, source_dir, bin_dir, vertexShaderName);
133 ShaderCodeRef rsFp = ShaderCode::create(gl, GL_FRAGMENT_SHADER, source_dir, bin_dir, fragmentShaderName);
134 if( !rsVp || !rsFp ) {
135 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
136 return false;
137 }
138 {
139 size_t posVp = rsVp->defaultShaderCustomization(gl, true, true);
140 size_t posFp = rsFp->defaultShaderCustomization(gl, true, true);
141 if( posVp == std::string::npos || posFp == std::string::npos ) {
142 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
143 return false;
144 }
145
146 // GLSL append from here on
147 posFp = -1;
148
149 posVp = rsVp->insertShaderSource(0, posVp, GLSL_PARAM_COMMENT_START);
150 posFp = rsFp->insertShaderSource(0, posFp, GLSL_PARAM_COMMENT_START);
151
152 // if( !gl.getContext().hasRendererQuirk(GLRendererQuirks.GLSLBuggyDiscard) ) {
153 posFp = rsFp->insertShaderSource(0, posFp, GLSL_USE_DISCARD);
154 // }
155
156 if( m_props.m_hasFrustumClipping ) {
157 posVp = rsVp->insertShaderSource(0, posVp, GLSL_USE_FRUSTUM_CLIPPING);
158 posFp = rsFp->insertShaderSource(0, posFp, GLSL_USE_FRUSTUM_CLIPPING);
159 }
160
161 if( usesNormal() ) {
162 posVp = rsVp->insertShaderSource(0, posVp, GLSL_USE_NORMAL_CHANNEL);
163 posFp = rsFp->insertShaderSource(0, posFp, GLSL_USE_NORMAL_CHANNEL);
164 }
165 if( m_props.m_hasLight0 ) {
166 posVp = rsVp->insertShaderSource(0, posVp, GLSL_USE_LIGHT0);
167 posFp = rsFp->insertShaderSource(0, posFp, GLSL_USE_LIGHT0);
168 }
169 if( m_props.m_hasColorChannel ) {
170 posVp = rsVp->insertShaderSource(0, posVp, GLSL_USE_COLOR_CHANNEL);
171 posFp = rsFp->insertShaderSource(0, posFp, GLSL_USE_COLOR_CHANNEL);
172 }
173 if( m_props.m_hasColorTexture ) {
175 posFp = rsFp->insertShaderSource(0, posFp, GLSL_USE_COLOR_TEXTURE);
176 }
177 /*if( !pass1 ) {
178 posFp = rsFp->insertShaderSource(0, posFp, GLSL_DEF_SAMPLE_COUNT+sms.sampleCount+"\n");
179 posFp = rsFp->insertShaderSource(0, posFp, GLSL_CONST_SAMPLE_COUNT+sms.sampleCount+".0;\n");
180 } */
181
182 posVp = rsVp->insertShaderSource(0, posVp, GLSL_PARAM_COMMENT_END);
183 if( posVp == std::string::npos ) {
184 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
185 return false;
186 }
187
188 posFp = rsFp->insertShaderSource(0, posFp, GLSL_PARAM_COMMENT_END);
189
190 posFp = rsFp->insertShaderSourceFile(0, posFp, string_t(source_dir).append("/constants.glsl"));
191 if( posFp == std::string::npos ) {
192 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
193 return false;
194 }
195 posFp = rsFp->insertShaderSourceFile(0, posFp, string_t(source_dir).append("/uniforms.glsl"));
196 if( posFp == std::string::npos ) {
197 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
198 return false;
199 }
200 posFp = rsFp->insertShaderSourceFile(0, posFp, string_t(source_dir).append("/varyings.glsl"));
201 if( posFp == std::string::npos ) {
202 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
203 return false;
204 }
205 if( m_props.m_hasColorTexture || m_props.m_hasFrustumClipping ) {
206 posFp = rsFp->insertShaderSourceFile(0, posFp, string_t(source_dir).append("/functions.glsl"));
207 if( posFp == std::string::npos ) {
208 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
209 return false;
210 }
211 }
212 /*if( hasColorTexture ) {
213 posFp = rsFp->insertShaderSource(0, posFp, "uniform "+colorTexSeq.getTextureSampler2DType()+" "+UniformNames.gcu_ColorTexUnit+";\n");
214 posFp = rsFp->insertShaderSource(0, posFp, colorTexSeq.getTextureLookupFragmentShaderImpl());
215 }*/
216
217 posFp = rsFp->insertShaderSource(0, posFp, GLSL_MAIN_BEGIN);
218
219 std::string passS = m_props.m_pass1 ? "-pass1-" : "-pass2-";
220 std::string shaderSegment = string_t(source_dir).append("/").append(shader_basename).append(passS).append("curve_simple").append(".glsl"); // sms.tech+sms.sub+".glsl";
221 if(DEBUG_MODE) {
222 jau::PLAIN_PRINT(true, "RegionRenderer.createShaderProgram.1: segment %s", shaderSegment.c_str());
223 }
224 posFp = rsFp->insertShaderSourceFile(0, posFp, shaderSegment);
225 if( posFp == std::string::npos ) {
226 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
227 return false;
228 }
229 posFp = rsFp->insertShaderSource(0, posFp, "}\n");
230 if( posFp == std::string::npos ) {
231 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
232 return false;
233 }
234
235 if( m_props.m_hasColorTexture ) {
236 rsFp->replaceInShaderSource(std::string(gcuTexture2D), std::string(colTexLookupFuncName));
237 }
238
239 }
241 if( !sp0->add(gl, rsVp, true) || !sp0->add(gl, rsFp, true) ) {
242 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
243 sp0->destroy(gl);
244 return false;
245 }
246 m_st.attachShaderProgram(gl, sp0, true);
247
248 m_st.pushAllUniforms(gl);
249
250 m_initialized = sp0->inUse();
251 if( !m_initialized ) {
252 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
253 m_st.destroy(gl);
254 }
255 return m_initialized;
256 }
257
258 void useProgram(GL& gl, bool on) {
259 m_st.useProgram(gl, on);
260 }
261
262};
263
265 public:
267 private:
268 GraphRenderer& m_renderer;
269 ShaderState& m_st;
270 bool m_initialized;
272 GLUIntArrayDataServerRef m_indices;
273 int m_num_vertices, m_num_indices;
274
275 public:
277 : m_renderer(renderer), m_st(st),
278 m_initialized(m_renderer.initialized()),
279 m_array(GLFloatArrayDataServer::createGLSLInterleaved(m_renderer.arrayCompsPerElement(), false, 256, GL_STATIC_DRAW)),
280 m_indices(GLUIntArrayDataServer::createData(3, 256, GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER)),
281 m_num_vertices(0), m_num_indices(0)
282 {
283 m_array->addGLSLSubArray("gca_Vertex", 3, GL_ARRAY_BUFFER);
284 m_array->addGLSLSubArray("gca_CurveParam", 3, GL_ARRAY_BUFFER);
285 if( m_renderer.usesNormal() ) {
286 m_array->addGLSLSubArray("gca_Normal", 3, GL_ARRAY_BUFFER);
287 }
288 m_st.ownAttribute(m_array, true);
289 // m_st.ownAttribute(m_indices, true);
290 }
291
292 constexpr bool initialized() const noexcept { return m_initialized; }
293
294 void seal(GL& gl, bool seal_) {
295 if( !m_initialized ) {
296 return;
297 }
298 m_array->seal(gl, seal_);
299 m_indices->seal(gl, seal_);
300 m_array->enableBuffer(gl, false);
301 m_indices->enableBuffer(gl, false);
302 }
303
304 void pushVertex(const Vertex& v, const Vec3f& normal) {
305 // jau::PLAIN_PRINT(true, "pushVertex.0[%d]: v %s]", m_num_vertices, v.toString().c_str());
306 m_array->put3f(v.coord());
307 m_array->put3f(v.texCoord());
308 if( m_renderer.usesNormal() ) {
309 m_array->put3f(normal);
310 }
311 ++m_num_vertices;
312 }
313 void pushIndices(uint32_t i, uint32_t j, uint32_t k) {
314 // jau::PLAIN_PRINT(true, "pushIndices.0[%d]: %u, %u, %u]", m_num_indices, i, j, k);
315 m_indices->putN(i, j, k);
316 m_num_indices += 3;
317 }
318 void pushNewVerticesIdx(const Vertex& vertIn1, const Vertex& vertIn2, const Vertex& vertIn3, const Vec3f& normal) {
319 pushIndices(m_num_vertices, m_num_vertices+1, m_num_vertices+2);
320 pushVertex(vertIn1, normal);
321 pushVertex(vertIn2, normal);
322 pushVertex(vertIn3, normal);
323 }
324
326 if( Graph::DEBUG_MODE ) {
327 jau::PLAIN_PRINT(true, "add.0 num[vertices %d, indices %d]", m_num_vertices, m_num_indices);
328 jau::PLAIN_PRINT(true, "add.0 array: %s", m_array->toString().c_str());
329 jau::PLAIN_PRINT(true, "add.0 indices: %s", m_indices->toString().c_str());
330 }
331 const TriangleRefList& trisIn = shape.getTriangles();
332 const VertexList& vertsIn = shape.getVertices();
333 if( Graph::DEBUG_MODE ) {
334 jau::PLAIN_PRINT(true, "add.0 triangles %u, vertices %u", trisIn.size(), vertsIn.size());
335 }
336 {
337 glmemsize_t verticeCount = (glmemsize_t)vertsIn.size() + shape.addedVertexCount();
338 glmemsize_t indexCount = (glmemsize_t)trisIn.size() * 3;
339 m_array->growIfNeeded(verticeCount * m_array->compsPerElem());
340 m_indices->growIfNeeded(indexCount * m_indices->compsPerElem());
341 }
342 uint32_t idxOffset = m_num_vertices;
343 if( vertsIn.size() >= 3 ) {
344 //
345 // Processing Vertices
346 //
347 for(const Vertex& v : vertsIn) {
348 pushVertex(v, shape.normal());
349 }
350 constexpr static uint32_t max_index = std::numeric_limits<uint32_t>::max() / sizeof(uint32_t);
351 OutlineShape::size_type trisIn_sz = trisIn.size();
352 for(OutlineShape::size_type i=0; i < trisIn_sz; ++i) {
353 const TriangleRef& triIn = trisIn[i];
354 // triEx.addVertexIndicesOffset(idxOffset);
355 // triangles.add( triEx );
356 Triangle::trivert_t& triInVertices = triIn->vertices();
357 uint32_t tv0Idx = triInVertices[0].id();
358 if ( max_index - idxOffset > tv0Idx ) {
359 // valid 'known' idx - move by offset
360 pushIndices(tv0Idx+idxOffset,
361 triInVertices[1].id()+idxOffset,
362 triInVertices[2].id()+idxOffset);
363 } else {
364 // FIXME: If exceeding max_indices, we would need to generate a new buffer w/ indices
365 pushNewVerticesIdx(triInVertices[0], triInVertices[1], triInVertices[2], shape.normal());
366 }
367 }
368 }
369 if( Graph::DEBUG_MODE ) {
370 jau::PLAIN_PRINT(true, "add.x num[vertices %d, indices %d]", m_num_vertices, m_num_indices);
371 jau::PLAIN_PRINT(true, "add.x array: %s", m_array->toString().c_str());
372 jau::PLAIN_PRINT(true, "add.x indices: %s", m_indices->toString().c_str());
373 }
374 }
375
376 void draw(GL &gl) {
377 if( !m_initialized ) {
378 return;
379 }
380 m_renderer.useProgram(gl, true);
381
382 m_array->enableBuffer(gl, true);
383 m_indices->bindBuffer(gl, true); // keeps VBO binding
384
385 ::glEnable(GL_BLEND);
386 ::glBlendEquation(GL_FUNC_ADD); // default
387 ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
388
389 ::glDrawElements(GL_TRIANGLES, m_indices->elemCount() * m_indices->compsPerElem(), GL_UNSIGNED_INT, nullptr);
390
391
392 m_indices->bindBuffer(gl, false);
393 m_array->enableBuffer(gl, false);
394 // m_renderer.useProgram(gl, false);
395 }
396};
397
398class Shape;
399typedef std::shared_ptr<Shape> ShapeRef;
400
401class Shape {
402 private:
403 ShaderState& m_st;
404 PMVMat4fUniform& m_pmvMat;
405 OutlineShape m_oshape;
407
408 Vec3f m_position;
409 Quat4f m_rotation;
410 Vec3f m_rotPivot;
411 Vec3f m_scale = Vec3f(1, 1, 1);
412 float m_zOffset;
413 GraphRenderer& m_renderer;
414 GraphRegion m_region;
415 GLUniformVec4fRef m_uColor;
416
417 Mat4f iMat;
418 Mat4f tmpMat;
419 bool iMatIdent = true;
420 bool iMatDirty = false;
421
422 struct Private{ explicit Private() = default; };
423
424 public:
425 Shape(Private, ShaderState &st, PMVMat4fUniform& pmvMatU, GraphRenderer& renderer)
426 : m_st(st), m_pmvMat(pmvMatU), m_oshape(3, 16),
427 m_renderer(renderer), m_region(m_renderer, m_st)
428 {
429 m_uColor = GLUniformVec4f::create("gcu_StaticColor", Vec4f(0, 0, 0, 1));
430 m_st.ownUniform(m_uColor, true);
431 }
432
433 static ShapeRef create(ShaderState &st, PMVMat4fUniform& pmvMatU, GraphRenderer& renderer) {
434 return std::make_shared<Shape>(Private(), st, pmvMatU, renderer);
435 }
436
437 constexpr const Vec3f& position() const noexcept { return m_position; }
438 constexpr Vec3f& position() noexcept { iMatDirty=true; return m_position; }
439
440 constexpr const float& zOffset() const noexcept { return m_zOffset; }
441 constexpr float& zOffset() noexcept { iMatDirty=true; return m_zOffset; }
442
443 constexpr const Quat4f& rotation() const noexcept { return m_rotation; }
444 constexpr Quat4f& rotation() noexcept { iMatDirty=true; return m_rotation; }
445
446 constexpr const Vec3f& rotationPivot() const noexcept { return m_rotPivot; }
447 constexpr Vec3f& rotationPivot() noexcept { iMatDirty=true; return m_rotPivot; }
448
449 constexpr const Vec3f& scale() const noexcept { return m_scale; }
450 constexpr Vec3f& scale() noexcept { iMatDirty=true; return m_scale; }
451
452 constexpr const OutlineShape& outlines() const noexcept { return m_oshape; }
453 constexpr OutlineShape& outlines() noexcept { return m_oshape; }
454
455 const Vec4f& color() const noexcept { return m_uColor->vec4f(); }
456 void setColor(const Vec4f& c) noexcept { m_uColor->vec4f()=c; }
457
458 void update(GL& gl) {
459 m_region.addOutlineShape(m_oshape);
460 m_region.seal(gl, true);
461 }
462
463 void draw(GL &gl) {
464 m_pmvMat.m.pushMv();
465 applyMatToMv(m_pmvMat.m);
466 m_st.pushUniform(gl, m_pmvMat.u); // automatic sync + update of Mvi + Mvit
467
468 m_st.pushUniform(gl, m_uColor);
469 m_region.draw(gl);
470 m_pmvMat.m.popMv();
471 }
472
473 private:
474 /**
475 * Applies the internal {@link Matrix4f} to the given {@link PMVMatrix4f#getMv() modelview matrix},
476 * i.e. {@code pmv.mulMv( getMat() )}.
477 * <p>
478 * Calls {@link #updateMat()} if dirty.
479 * </p>
480 * In case {@link #isMatIdentity()} is {@code true}, implementation is a no-operation.
481 * </p>
482 * @param pmv the matrix
483 * @see #isMatIdentity()
484 * @see #updateMat()
485 * @see #getMat()
486 * @see PMVMatrix4f#mulMv(Matrix4f)
487 */
488 void applyMatToMv(PMVMat4f& pmvMat) noexcept {
489 if( iMatDirty ) {
490 updateMat();
491 }
492 if( !iMatIdent ) {
493 pmvMat.mulMv(iMat);
494 }
495 }
496 void updateMat() noexcept {
497 bool hasPos = !m_position.is_zero();
498 bool hasScale = m_scale != Vec3f::one;
499 bool hasRotate = !m_rotation.isIdentity();
500 bool hasRotPivot = false; // null != rotPivot;
501 const Vec3f& ctr = m_oshape.bounds().center();
502 bool sameScaleRotatePivot = hasScale && hasRotate && ( !hasRotPivot || m_rotPivot == ctr );
503
504 if( sameScaleRotatePivot ) {
505 iMatIdent = false;
506 iMat.setToTranslation(m_position); // identity + translate, scaled
507 // Scale shape from its center position and rotate around its center
508 iMat.translate(Vec3f(ctr).mul(m_scale)); // add-back center, scaled
509 iMat.rotate(m_rotation);
510 iMat.scale(m_scale);
511 iMat.translate(-ctr); // move to center
512 } else if( hasRotate || hasScale ) {
513 iMatIdent = false;
514 iMat.setToTranslation(m_position); // identity + translate, scaled
515 if( hasRotate ) {
516 if( hasRotPivot ) {
517 // Rotate shape around its scaled pivot
518 iMat.translate(Vec3f(m_rotPivot).mul(m_scale)); // pivot back from rot-pivot, scaled
519 iMat.rotate(m_rotation);
520 iMat.translate(Vec3f(-m_rotPivot).mul(m_scale)); // pivot to rot-pivot, scaled
521 } else {
522 // Rotate shape around its scaled center
523 iMat.translate(Vec3f(ctr).mul(m_scale)); // pivot back from center-pivot, scaled
524 iMat.rotate(m_rotation);
525 iMat.translate(Vec3f(-ctr).mul(m_scale)); // pivot to center-pivot, scaled
526 }
527 }
528 if( hasScale ) {
529 // Scale shape from its center position
530 iMat.translate(Vec3f(ctr).mul(m_scale)); // add-back center, scaled
531 iMat.scale(m_scale);
532 iMat.translate(Vec3f(-ctr).mul(m_scale)); // move to center
533 }
534 } else if( hasPos ) {
535 iMatIdent = false;
536 iMat.setToTranslation(m_position); // identity + translate, scaled
537
538 } else {
539 iMatIdent = true;
540 iMat.loadIdentity();
541 }
542 iMatDirty = false;
543 }
544};
545
547 private:
548 constexpr static jau::math::Vec3f lightPos = jau::math::Vec3f(0.0f, 5.0f, 10.0f);
549 constexpr static float zNear= 1.0f;
550 constexpr static float zFar =100.0f;
551
552 ShaderState m_st;
553 Recti m_viewport;
554 bool m_initialized;
555 bool m_animating = true;
556 bool m_oneframe = false;
558 PMVMat4fUniform m_pmvMat;
559 GraphRenderer m_renderer;
560 std::vector<ShapeRef> m_shapes;
561
562 public:
565 m_initialized(false), m_renderer(m_st)
566 {
567 }
568
569 Recti& viewport() noexcept { return m_viewport; }
570 const Recti& viewport() const noexcept { return m_viewport; }
571
572 PMVMat4f& pmv() noexcept { return m_pmvMat.m; }
573 const PMVMat4f& pmv() const noexcept { return m_pmvMat.m; }
574 bool animating() const noexcept { return m_animating; }
575 bool& animating() noexcept { return m_animating; }
576 void setOneFrame() noexcept { m_animating=false; m_oneframe=true; }
577
578 bool init(const WindowRef& win, const jau::fraction_timespec& when) override {
579 jau::fprintf_td(when.to_ms(), stdout, "RL::init: %s\n", toString().c_str());
580 m_tlast = when;
581
582 GL& gl = GL::downcast(win->renderContext());
583
584 // setup mgl_PMVMatrix
585 m_pmvMat.m.getP().loadIdentity();
586 m_pmvMat.m.getMv().loadIdentity();
587 m_st.ownUniform(m_pmvMat.u, true);
588
589 if( !m_renderer.init(gl, when) ) {
590 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d: %s\n", E_FILE_LINE, toString().c_str());
591 win->dispose(when);
592 return false;
593 }
594
595 GLUniformVec3fRef lightU = GLUniformVec3f::create("gcu_LightPos", lightPos);
596 m_st.ownUniform(lightU, true);
597 m_st.pushAllUniforms(gl);
598
599 const float lineWidth = 1/2.5f;
600 const float dz = 0.005f;
601 if( true ) {
602 // Cross / Plus
603 const float width = 1.5f;
604 const float height = 1.5f;
605
606 float lwh = lineWidth/2.0f;
607
608 float twh = width/2.0f;
609 float thh = height/2.0f;
610
611 float ctrX = 0, ctrY = 0, ctrZ = dz;
612 ShapeRef frontShape = Shape::create(m_st, m_pmvMat, m_renderer);
613 m_shapes.push_back(frontShape);
614 OutlineShape& oshape = frontShape->outlines();
615 // CCW
616 oshape.moveTo(ctrX-lwh, ctrY+thh, ctrZ); // vert: left-top
617 oshape.lineTo(ctrX-lwh, ctrY+lwh, ctrZ);
618 oshape.lineTo(ctrX-twh, ctrY+lwh, ctrZ); // horz: left-top
619 oshape.lineTo(ctrX-twh, ctrY-lwh, ctrZ); // horz: left-bottom
620 oshape.lineTo(ctrX-lwh, ctrY-lwh, ctrZ);
621 oshape.lineTo(ctrX-lwh, ctrY-thh, ctrZ); // vert: left-bottom
622 oshape.lineTo(ctrX+lwh, ctrY-thh, ctrZ); // vert: right-bottom
623 oshape.lineTo(ctrX+lwh, ctrY-lwh, ctrZ);
624 oshape.lineTo(ctrX+twh, ctrY-lwh, ctrZ); // horz: right-bottom
625 oshape.lineTo(ctrX+twh, ctrY+lwh, ctrZ); // horz: right-top
626 oshape.lineTo(ctrX+lwh, ctrY+lwh, ctrZ);
627 oshape.lineTo(ctrX+lwh, ctrY+thh, ctrZ); // vert: right-top
628 oshape.lineTo(ctrX-lwh, ctrY+thh, ctrZ); // vert: left-top
629 oshape.closePath();
630 // shape1->seal(gl, true);
631 frontShape->update(gl);
632 frontShape->setColor(Vec4f(0.5f, 0.05f, 0.05f, 1));
633 frontShape->position().x = -2.0f;
634
635 ShapeRef backShape = Shape::create(m_st, m_pmvMat, m_renderer);
636 m_shapes.push_back(backShape);
637 backShape->outlines() = oshape.flipFace(); // -dz);
638 backShape->outlines().clearCache();
639 backShape->update(gl);
640 backShape->setColor(Vec4f(0.2f, 0.5f, 0.2f, 1));
641 backShape->position().x = -2.0f;
642 }
643 if( true) {
644 ShapeRef frontShape = Shape::create(m_st, m_pmvMat, m_renderer);
645 m_shapes.push_back(frontShape);
646 OutlineShape& oshape = frontShape->outlines();
647 oshape.moveTo(0.0f,-10.0f, 0);
648 oshape.lineTo(15.0f,-10.0f, 0);
649 oshape.quadTo(10.0f,5.0f,0, 15.0f,10.0f,0);
650 oshape.cubicTo(6.0f,15.0f,0, 5.0f,8.0f,0, 0.0f,10.0f,0);
651 oshape.closePath();
652 oshape.moveTo(5.0f,-5.0f,0);
653 oshape.quadTo(10.0f,-5.0f,0, 10.0f,0.0f,0);
654 oshape.quadTo(5.0f,0.0f,0, 5.0f,-5.0f,0);
655 oshape.closePath();
656
657 frontShape->update(gl);
658 frontShape->setColor(Vec4f(0.4f, 0.4f, 0.1f, 1));
659 frontShape->position().x = -1.0f;
660 frontShape->scale().x *= 0.1f;
661 frontShape->scale().y *= 0.1f;
662
663 ShapeRef backShape = Shape::create(m_st, m_pmvMat, m_renderer);
664 m_shapes.push_back(backShape);
665 backShape->outlines() = oshape.flipFace(-dz);
666 backShape->outlines().clearCache();
667 backShape->update(gl);
668 backShape->setColor(Vec4f(0.2f, 0.2f, 0.5f, 1));
669 backShape->position().x = -1.0f;
670 backShape->scale().x *= 0.1f;
671 backShape->scale().y *= 0.1f;
672 }
673 if ( true ) {
674 ShapeRef frontShape = Shape::create(m_st, m_pmvMat, m_renderer);
675 m_shapes.push_back(frontShape);
676 OutlineShape& oshape = frontShape->outlines();
678 frontShape->update(gl);
679 frontShape->setColor(Vec4f(0.05f, 0.05f, 0.5f, 1));
680 frontShape->position().x = 1.5f;
681 frontShape->position().y = -1.0f;
682 frontShape->scale().x *= 2.0f;
683 frontShape->scale().y *= 2.0f;
684
685 ShapeRef backShape = Shape::create(m_st, m_pmvMat, m_renderer);
686 m_shapes.push_back(backShape);
687 backShape->outlines() = oshape.flipFace(-dz);
688 backShape->outlines().clearCache();
689 backShape->update(gl);
690 backShape->setColor(Vec4f(0.4f, 0.4f, 0.1f, 1));
691 backShape->position().x = 1.5f;
692 backShape->position().y = -1.0f;
693 backShape->scale().x *= 2.0f;
694 backShape->scale().y *= 2.0f;
695 }
696 if ( true ) {
697 ShapeRef frontShape = Shape::create(m_st, m_pmvMat, m_renderer);
698 m_shapes.push_back(frontShape);
699 OutlineShape& oshape = frontShape->outlines();
701 frontShape->update(gl);
702 frontShape->setColor(Vec4f(0.05f, 0.5f, 0.05f, 1));
703 frontShape->position().x = 1.5f;
704 frontShape->position().y = 0.5f;
705 frontShape->scale().x *= 2.0f;
706 frontShape->scale().y *= 2.0f;
707
708 ShapeRef backShape = Shape::create(m_st, m_pmvMat, m_renderer);
709 m_shapes.push_back(backShape);
710 backShape->outlines() = oshape.flipFace(-dz);
711 backShape->outlines().clearCache();
712 backShape->update(gl);
713 backShape->setColor(Vec4f(0.5f, 0.1f, 0.1f, 1));
714 backShape->position().x = 1.5f;
715 backShape->position().y = 0.5f;
716 backShape->scale().x *= 2.0f;
717 backShape->scale().y *= 2.0f;
718 }
719
720 ::glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
721 ::glEnable(GL_DEPTH_TEST);
722 // ::glEnable(GL_CULL_FACE);
723 ::glDisable(GL_CULL_FACE);
724
725 m_initialized = true;
726 if( !m_initialized ) {
727 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d: %s\n", E_FILE_LINE, toString().c_str());
728 m_st.destroy(gl);
729 win->dispose(when);
730 }
731 return m_initialized;
732 }
733
734 void dispose(const WindowRef& win, const jau::fraction_timespec& when) override {
735 jau::fprintf_td(when.to_ms(), stdout, "RL::dispose: %s\n", toString().c_str());
736 m_st.destroy(GL::downcast(win->renderContext()));
737 m_initialized = false;
738 }
739
740 void reshape(const WindowRef& win, const jau::math::Recti& viewport, const jau::fraction_timespec& when) override {
741 GL& gl = GL::downcast(win->renderContext());
742 jau::fprintf_td(when.to_ms(), stdout, "RL::reshape: %s\n", toString().c_str());
743 m_viewport = viewport;
744
745 m_pmvMat.m.getP().loadIdentity();
746 const float aspect = 1.0f;
747 const float fovy_deg=45.0f;
748 const float aspect2 = ( (float) m_viewport.width() / (float) m_viewport.height() ) / aspect;
749 m_pmvMat.m.perspectiveP(jau::adeg_to_rad(fovy_deg), aspect2, zNear, zFar);
750 m_st.useProgram(gl, true);
751 m_st.pushUniform(gl, m_pmvMat.u); // automatic sync + update of Mvi + Mvit
752 // m_st.useProgram(gl, false);
753 }
754
755 void display(const WindowRef& win, const jau::fraction_timespec& when) override {
756 // jau::fprintf_td(when.to_ms(), stdout, "RL::display: %s, %s\n", toString().c_str(), win->toString().c_str());
757 if( !m_initialized ) {
758 return;
759 }
760 GL& gl = GL::downcast(win->renderContext());
761 ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
762
763 m_st.useProgram(gl, true);
764 m_pmvMat.m.getMv().loadIdentity();
765 m_pmvMat.m.translateMv(0, 0, -5);
766
767 for(const ShapeRef& s : m_shapes) {
768 if( animating() || m_oneframe ) {
769 constexpr double angle_per_sec = 30;
770 const float rad = (float) ( (when - m_tlast).to_double() * angle_per_sec );
771 s->rotation().rotateByAngleY(jau::adeg_to_rad( rad ));
772 }
773 s->draw(gl);
774 }
775
776 m_oneframe = false;
777 // m_st.useProgram(gl, false);
778
779 m_tlast = when;
780 }
781
782 std::string toStringImpl() const noexcept override { return "GraphShapes01"; }
783};
784
785class Example : public GraphShapes01 {
786 private:
787 class MyKeyListener : public KeyListener {
788 private:
789 GraphShapes01& m_parent;
790 public:
791 MyKeyListener(GraphShapes01& p) : m_parent(p) {}
792
793 void keyPressed(KeyEvent& e, const KeyboardTracker& kt) override {
794 jau::fprintf_td(e.when().to_ms(), stdout, "KeyPressed: %s; keys %zu\n", e.toString().c_str(), kt.pressedKeyCodes().bitCount());
795 if( e.keySym() == VKeyCode::VK_ESCAPE ) {
796 WindowRef win = e.source().lock();
797 if( win ) {
798 win->dispose(e.when());
799 }
800 } else if( e.keySym() == VKeyCode::VK_PAUSE || e.keySym() == VKeyCode::VK_P ) {
801 m_parent.animating() = !m_parent.animating();
802 } else if( e.keySym() == VKeyCode::VK_PERIOD ) {
803 m_parent.setOneFrame();
804 } else if( e.keySym() == VKeyCode::VK_W ) {
805 WindowRef win = e.source().lock();
806 jau::fprintf_td(e.when().to_ms(), stdout, "Source: %s\n", win ? win->toString().c_str() : "null");
807 }
808 }
809 void keyReleased(KeyEvent& e, const KeyboardTracker& kt) override {
810 jau::fprintf_td(e.when().to_ms(), stdout, "KeyRelease: %s; keys %zu\n", e.toString().c_str(), kt.pressedKeyCodes().bitCount());
811 }
812 };
813 typedef std::shared_ptr<MyKeyListener> MyKeyListenerRef;
814 MyKeyListenerRef m_kl;
815
816 public:
818 : GraphShapes01(),
819 m_kl(std::make_shared<MyKeyListener>(*this)) { }
820
821 bool init(const WindowRef& win, const jau::fraction_timespec& when) override {
822 if( !GraphShapes01::init(win, when) ) {
823 return false;
824 }
825 win->addKeyListener(m_kl);
826 return true;
827 }
828 void dispose(const WindowRef& win, const jau::fraction_timespec& when) override {
829 win->removeKeyListener(m_kl);
830 GraphShapes01::dispose(win, when);
831 }
832};
833
834int main(int argc, char *argv[]) // NOLINT(bugprone-exception-escape)
835{
836 // ShaderCode::DEBUG_CODE = true;
837
838 return launch("GraphShapes01.cpp",
839 GLLaunchProps{GLProfile(GLProfile::GLES2), gamp::render::RenderContextFlags::verbose}, // | gamp::render::RenderContextFlags::debug},
840 std::make_shared<Example>(), argc, argv);
841}
int launch(std::string_view sfile, const GLLaunchProps &props, const RenderListenerRef &demo, int argc, char *argv[])
int main(int argc, char *argv[])
std::shared_ptr< Shape > ShapeRef
std::shared_ptr< Shape > ShapeRef
#define E_FILE_LINE
bool init(const WindowRef &win, const jau::fraction_timespec &when) override
Called by the drawable immediately after the render context is initialized.
void dispose(const WindowRef &win, const jau::fraction_timespec &when) override
Notifies the listener to perform the release of all renderer resources per context,...
static void addShapeToRegion(gamp::graph::OutlineShape &shape)
static void addShapeToRegion(gamp::graph::OutlineShape &shape)
constexpr bool initialized() const noexcept
void draw(GL &gl)
void addOutlineShape(OutlineShape &shape)
void pushVertex(const Vertex &v, const Vec3f &normal)
jau::darray< uint32_t, glmemsize_t > u32buffer_t
void pushNewVerticesIdx(const Vertex &vertIn1, const Vertex &vertIn2, const Vertex &vertIn3, const Vec3f &normal)
GraphRegion(GraphRenderer &renderer, ShaderState &st)
void pushIndices(uint32_t i, uint32_t j, uint32_t k)
void seal(GL &gl, bool seal_)
static constexpr std::string_view GLSL_USE_LIGHT0
bool init(GL &gl, const jau::fraction_timespec &when)
static constexpr std::string_view GLSL_MAIN_BEGIN
static constexpr std::string_view gcuTexture2D
static constexpr std::string_view GLSL_PARAM_COMMENT_START
static constexpr std::string_view GLSL_USE_DISCARD
static constexpr std::string_view colTexLookupFuncName
static constexpr std::string_view bin_dir
static constexpr std::string_view GLSL_PARAM_COMMENT_END
constexpr GLsizei arrayCompsPerElement() const noexcept
static constexpr bool DEBUG_MODE
constexpr bool initialized() const noexcept
static constexpr std::string_view GLSL_DEF_SAMPLE_COUNT
static constexpr std::string_view GLSL_USE_COLOR_TEXTURE
constexpr bool usesNormal() const noexcept
static constexpr std::string_view source_dir
static constexpr std::string_view GLSL_USE_FRUSTUM_CLIPPING
static constexpr std::string_view GLSL_USE_COLOR_CHANNEL
static constexpr std::string_view GLSL_CONST_SAMPLE_COUNT
GraphRenderer(ShaderState &st)
static constexpr std::string_view shader_basename
void useProgram(GL &gl, bool on)
static constexpr std::string_view GLSL_USE_NORMAL_CHANNEL
void dispose(const WindowRef &win, const jau::fraction_timespec &when) override
Notifies the listener to perform the release of all renderer resources per context,...
void setOneFrame() noexcept
bool animating() const noexcept
std::string toStringImpl() const noexcept override
void reshape(const WindowRef &win, const jau::math::Recti &viewport, const jau::fraction_timespec &when) override
Called by the drawable during the first repaint after the component has been resized.
const PMVMat4f & pmv() const noexcept
void display(const WindowRef &win, const jau::fraction_timespec &when) override
Called by the drawable to initiate rendering by the client.
PMVMat4f & pmv() noexcept
bool init(const WindowRef &win, const jau::fraction_timespec &when) override
Called by the drawable immediately after the render context is initialized.
bool & animating() noexcept
Recti & viewport() noexcept
const Recti & viewport() const noexcept
constexpr const float & zOffset() const noexcept
void setColor(const Vec4f &c) noexcept
Shape(Private, ShaderState &st, PMVMat4fUniform &pmvMatU, GraphRenderer &renderer)
constexpr Vec3f & position() noexcept
static ShapeRef create(ShaderState &st, PMVMat4fUniform &pmvMatU, GraphRenderer &renderer)
void draw(GL &gl)
void update(GL &gl)
constexpr OutlineShape & outlines() noexcept
constexpr const Quat4f & rotation() const noexcept
constexpr const Vec3f & rotationPivot() const noexcept
constexpr float & zOffset() noexcept
constexpr const Vec3f & position() const noexcept
constexpr Vec3f & scale() noexcept
constexpr Vec3f & rotationPivot() noexcept
const Vec4f & color() const noexcept
constexpr const Vec3f & scale() const noexcept
constexpr const OutlineShape & outlines() const noexcept
constexpr Quat4f & rotation() noexcept
static bool DEBUG_MODE
Definition Graph.hpp:24
A Generic shape objects which is defined by a list of Outlines.
constexpr size_type addedVertexCount() const noexcept
Return the number of newly added vertices during getTriangles(VerticesState) while transforming the o...
void clearCache() noexcept
Clears cached triangulated data, i.e.
const VertexList & getVertices()
Return list of concatenated vertices associated with all Outlines of this object.
void cubicTo(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3)
Add a cubic Bézier curve segment, intersecting the last point and the second given point x3/y3 (P3).
const TriangleRefList & getTriangles(VertexState destinationType=VertexState::quadratic_nurbs)
Triangulate the OutlineShape generating a list of triangles, while transformOutlines(VerticesState) b...
void quadTo(float x1, float y1, float z1, float x2, float y2, float z2)
Add a quadratic curve segment, intersecting the last point and the second given point x2/y2 (P2).
constexpr const Vec3f & normal() const noexcept
Normal vector, optionally used by tesselator to add (interleaved) normals.
void moveTo(float x, float y, float z)
Start a new position for the next line segment at given point x/y (P1).
void closePath()
Closes the current sub-path segment by drawing a straight line back to the coordinates of the last mo...
OutlineShape flipFace(float zoffset=0) const
Returns a copy of this instance with normal() and all outlines() vertices()'s z-axis sign-flipped,...
void lineTo(float x, float y, float z)
Add a line segment, intersecting the last point and the given point x/y (P1).
std::array< Vertex, 3 > trivert_t
constexpr const trivert_t & vertices() const noexcept
Returns array of 3 vertices, denominating the triangle.
constexpr const Vec3f & texCoord() const noexcept
Definition PrimTypes.hpp:96
constexpr const Vec3f & coord() const noexcept
Definition PrimTypes.hpp:93
static GLContext & downcast(RenderContext *rc)
Downcast dereferenced given RenderContext* to GLContext&, throws exception if signature doesn't match...
Specifies the OpenGL profile.
Definition GLContext.hpp:41
static constexpr std::string_view GLES2
The embedded OpenGL profile ES 2.x, with x >= 0.
Definition GLContext.hpp:64
static std::shared_ptr< GLUniformVec3f > create(const string_t &name, const jau::math::Vec3f &v)
static std::shared_ptr< GLUniformVec4f > create(const string_t &name, const jau::math::Vec4f &v)
size_t replaceInShaderSource(const string_t &oldName, const string_t &newName) noexcept
Replaces oldName with newName in all shader sources.
size_t defaultShaderCustomization(const GL &gl, bool preludeVersion, bool addDefaultPrecision)
Default customization of this shader source code.
static ShaderCodeRef create(GLenum type, size_t count, const source_list_t &sources) noexcept
size_t insertShaderSourceFile(size_t shaderIdx, size_t position, const string_t &path) noexcept
Adds shader source located in path, either relative to the location or absolute as-is at position in ...
size_t insertShaderSource(size_t shaderIdx, stringview_t tag, size_t fromIndex, stringview_t data) noexcept
Adds data after the line containing tag.
void destroy(GL &gl) noexcept
Detaches all shader codes and deletes the program.
static ShaderProgramRef create() noexcept
bool add(const ShaderCodeRef &shaderCode) noexcept
Adds a new shader to this program.
constexpr bool inUse() const noexcept
ShaderState allows to sharing data between shader programs, while updating the attribute and uniform ...
constexpr RenderListener(Private) noexcept
Private ctor for shared_ptr<RenderListener> instance method w/o public ctor.
Definition Window.hpp:60
std::string toString() const noexcept
Definition Window.hpp:112
const gamp::render::RenderContext * renderContext() const noexcept
Definition Surface.hpp:129
size_t removeKeyListener(const KeyListenerRef &l)
Definition Window.hpp:311
void dispose(const jau::fraction_timespec &when) noexcept override
Definition Window.hpp:355
std::string toString() const noexcept
Definition gamp_wt.cpp:145
void addKeyListener(const KeyListenerRef &l)
Definition Window.hpp:310
std::string toString() const noexcept
Definition KeyEvent.hpp:855
constexpr VKeyCode keySym() const noexcept
Returns the virtual key symbol reflecting the current keyboard layout.
Definition KeyEvent.hpp:798
virtual const PressedKeyCodes & pressedKeyCodes() const noexcept=0
constexpr const WindowWeakPtr & source() const noexcept
Definition Event.hpp:85
constexpr const jau::fraction_timespec & when() const noexcept
Definition Event.hpp:84
size_t bitCount() const noexcept
Definition bitfield.hpp:176
Implementation of a dynamic linear array storage, aka vector, including relative positional access.
Definition darray.hpp:153
constexpr size_type size() const noexcept
Like std::vector::size().
Definition darray.hpp:1069
value_type x
Definition vec3f.hpp:69
static constexpr const value_type one
Definition vec3f.hpp:67
value_type y
Definition vec3f.hpp:70
constexpr T adeg_to_rad(const T arc_degree) noexcept
Converts arc-degree to radians.
GLArrayDataServer< float > GLFloatArrayDataServer
GLsizeiptr glmemsize_t
Compatible with ssize_t.
Definition GLBuffers.hpp:26
GLArrayDataServerRef< float > GLFloatArrayDataServerRef
GLArrayDataServer< uint32_t > GLUIntArrayDataServer
std::shared_ptr< GLUniformData > GLUniformDataRef
std::shared_ptr< GLUniformVec4f > GLUniformVec4fRef
GLArrayDataServerRef< uint32_t > GLUIntArrayDataServerRef
std::shared_ptr< GLUniformVec3f > GLUniformVec3fRef
std::shared_ptr< ShaderProgram > ShaderProgramRef
std::shared_ptr< ShaderCode > ShaderCodeRef
jau::darray< Vertex, uint32_t > VertexList
std::shared_ptr< Triangle > TriangleRef
jau::darray< TriangleRef, uint32_t > TriangleRefList
@ verbose
Verbose operations (debugging).
std::shared_ptr< Window > WindowRef
Definition Event.hpp:36
Matrix4< float > Mat4f
Definition mat4f.hpp:1973
Vector4F< float > Vec4f
Definition vec4f.hpp:375
Quaternion< float > Quat4f
RectI< int > Recti
Definition recti.hpp:139
Vector3F< float > Vec3f
Definition vec3f.hpp:436
PMVMatrix4< float > PMVMat4f
Gamp: Graphics, Audio, Multimedia and Processing Framework (Native C++, WebAssembly,...
Definition Gamp.hpp:29
void PLAIN_PRINT(const bool printPrefix, const char *format,...) noexcept
Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true.
Definition debug.cpp:258
int fprintf_td(const uint64_t elapsed_ms, FILE *stream, const char *format,...) noexcept
Convenient fprintf() invocation, prepending the given elapsed_ms timestamp.
Definition debug.cpp:270
STL namespace.
GLUniformDataRef u
Timespec structure using int64_t for its components in analogy to struct timespec_t on 64-bit platfor...
constexpr uint64_t to_ms() const noexcept
Returns time in milliseconds.