Gamp v0.0.7-67-g7798ac4
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
GraphShapes02.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 <algorithm>
13#include <cstdio>
14#include <cmath>
15#include <gamp/graph/Graph.hpp>
19#include <memory>
20#include <string>
21#include <string_view>
22#include <vector>
23
24#include <jau/basic_types.hpp>
25#include <jau/cpp_lang_util.hpp>
26#include <jau/darray.hpp>
27#include <jau/debug.hpp>
28#include <jau/float_math.hpp>
29#include <jau/float_types.hpp>
30#include <jau/fraction_type.hpp>
31#include <jau/io/file_util.hpp>
33#include <jau/math/vec3f.hpp>
34#include <jau/math/vec4f.hpp>
35#include <jau/math/vec4f.hpp>
40
41#include <gamp/Gamp.hpp>
46
48
49#include "model_cobramk3.hpp"
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::render::gl::glsl;
63using namespace gamp::render::gl::data;
64
65class GraphRenderer {
66 public:
67 struct GraphRendererProps {
68 bool m_isTwoPass = false;
69 bool m_pass1 = true;
70 bool m_hasFrustumClipping = false;
71 bool m_hasNormalChannel = false;
72 bool m_hasLight0 = true;
73 bool m_hasColorChannel = false;
74 bool m_hasColorTexture = false;
75 bool m_hasDiscard = true;
76 };
77 private:
78 constexpr static PMVData mat_req = PMVData::inv_proj | PMVData::inv_mv | PMVData::inv_tps_mv;
79 constexpr static jau::math::Vec3f lightPos = jau::math::Vec3f(2.0f, 2.0f, 5.0f);
80 GraphRendererProps m_props;
81 ShaderState& m_st;
82 GLUniformSyncPMVMat4f m_pmvMat;
83 GLUniformVec3f m_light0Pos;
84 GLUniformVec4f m_staticColor;
85 bool m_initialized;
86
87 public:
88 constexpr bool usesNormal() const noexcept { return m_props.m_hasLight0 || m_props.m_hasNormalChannel; }
89 constexpr GLsizei arrayCompsPerElement() const noexcept { return usesNormal()? 3*3 : 2*3; }
90
91 static constexpr std::string_view GLSL_PARAM_COMMENT_START = "\n// Gamp Graph Parameter Start\n";
92 static constexpr std::string_view GLSL_PARAM_COMMENT_END = "// Gamp Graph Parameter End\n\n";
93 static constexpr std::string_view GLSL_USE_COLOR_CHANNEL = "#define USE_COLOR_CHANNEL 1\n";
94 static constexpr std::string_view GLSL_USE_NORMAL_CHANNEL = "#define USE_NORMAL_CHANNEL 1\n";
95 static constexpr std::string_view GLSL_USE_LIGHT0 = "#define USE_LIGHT0 1\n";
96 static constexpr std::string_view GLSL_USE_COLOR_TEXTURE = "#define USE_COLOR_TEXTURE 1\n";
97 static constexpr std::string_view GLSL_USE_FRUSTUM_CLIPPING = "#define USE_FRUSTUM_CLIPPING 1\n";
98 static constexpr std::string_view GLSL_USE_DISCARD = "#define USE_DISCARD 1\n";
99 static constexpr std::string_view GLSL_DEF_SAMPLE_COUNT = "#define SAMPLE_COUNT ";
100 static constexpr std::string_view GLSL_CONST_SAMPLE_COUNT = "const float sample_count = ";
101 static constexpr std::string_view GLSL_MAIN_BEGIN = "void main (void)\n{\n";
102 static constexpr std::string_view gcuTexture2D = "gcuTexture2D";
103 static constexpr std::string_view colTexLookupFuncName = "texture2D";
104 static constexpr std::string_view shader_basename = "curverenderer01";
105 static constexpr std::string_view source_dir = "impl/graph/glsl";
106 static constexpr std::string_view bin_dir = "impl/graph/glsl/bin";
107
108 public:
110 : m_st(st),
111 m_pmvMat("gcu_PMVMatrix", mat_req), // P, Mv, Mvi and Mvit
112 m_light0Pos("gcu_Light0Pos", lightPos),
113 m_staticColor("gcu_StaticColor", Vec4f(0, 0, 0, 1)),
114 m_initialized(false)
115 {
116 m_st.manage(m_pmvMat);
117 m_st.manage(m_light0Pos);
118 m_st.manage(m_staticColor);
119 }
120
121 constexpr bool initialized() const noexcept { return m_initialized; }
122
123 bool init(GL& gl, const jau::fraction_timespec& when) {
125 // ShaderState::VERBOSE_STATE = true;
126
127 std::string vertexShaderName, fragmentShaderName;
128 vertexShaderName.append(shader_basename);
129 if( m_props.m_isTwoPass ) {
130 vertexShaderName.append("-pass").append(m_props.m_pass1 ? "1":"2");
131 } else {
132 vertexShaderName.append("-single");
133 }
134 fragmentShaderName.append(shader_basename).append("-segment-head");
135
136 ShaderCodeSRef rsVp = ShaderCode::create(gl, GL_VERTEX_SHADER, source_dir, bin_dir, vertexShaderName);
137 ShaderCodeSRef rsFp = ShaderCode::create(gl, GL_FRAGMENT_SHADER, source_dir, bin_dir, fragmentShaderName);
138 if( !rsVp || !rsFp ) {
139 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
140 return false;
141 }
142 {
143 size_t posVp = rsVp->defaultShaderCustomization(gl);
144 size_t posFp = rsFp->defaultShaderCustomization(gl);
145 if( posVp == std::string::npos || posFp == std::string::npos ) {
146 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
147 return false;
148 }
149
150 // GLSL append from here on
151 posFp = -1;
152
153 posVp = rsVp->insertShaderSource(0, posVp, GLSL_PARAM_COMMENT_START);
154 posFp = rsFp->insertShaderSource(0, posFp, GLSL_PARAM_COMMENT_START);
155
156 // if( !gl.getContext().hasRendererQuirk(GLRendererQuirks.GLSLBuggyDiscard) ) {
157 if( m_props.m_hasDiscard ) {
158 posFp = rsFp->insertShaderSource(0, posFp, GLSL_USE_DISCARD);
159 }
160
161 if( m_props.m_hasFrustumClipping ) {
162 posVp = rsVp->insertShaderSource(0, posVp, GLSL_USE_FRUSTUM_CLIPPING);
163 posFp = rsFp->insertShaderSource(0, posFp, GLSL_USE_FRUSTUM_CLIPPING);
164 }
165
166 if( usesNormal() ) {
167 posVp = rsVp->insertShaderSource(0, posVp, GLSL_USE_NORMAL_CHANNEL);
168 posFp = rsFp->insertShaderSource(0, posFp, GLSL_USE_NORMAL_CHANNEL);
169 }
170 if( m_props.m_hasLight0 ) {
171 posVp = rsVp->insertShaderSource(0, posVp, GLSL_USE_LIGHT0);
172 posFp = rsFp->insertShaderSource(0, posFp, GLSL_USE_LIGHT0);
173 }
174 if( m_props.m_hasColorChannel ) {
175 posVp = rsVp->insertShaderSource(0, posVp, GLSL_USE_COLOR_CHANNEL);
176 posFp = rsFp->insertShaderSource(0, posFp, GLSL_USE_COLOR_CHANNEL);
177 }
178 if( m_props.m_hasColorTexture ) {
180 posFp = rsFp->insertShaderSource(0, posFp, GLSL_USE_COLOR_TEXTURE);
181 }
182 /*if( !pass1 ) {
183 posFp = rsFp->insertShaderSource(0, posFp, GLSL_DEF_SAMPLE_COUNT+sms.sampleCount+"\n");
184 posFp = rsFp->insertShaderSource(0, posFp, GLSL_CONST_SAMPLE_COUNT+sms.sampleCount+".0;\n");
185 } */
186
187 posVp = rsVp->insertShaderSource(0, posVp, GLSL_PARAM_COMMENT_END);
188 if( posVp == std::string::npos ) {
189 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
190 return false;
191 }
192
193 posFp = rsFp->insertShaderSource(0, posFp, GLSL_PARAM_COMMENT_END);
194
195 posFp = rsFp->insertShaderSourceFile(0, posFp, string_t(source_dir).append("/constants.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("/uniforms.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 posFp = rsFp->insertShaderSourceFile(0, posFp, string_t(source_dir).append("/varyings.glsl"));
206 if( posFp == std::string::npos ) {
207 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
208 return false;
209 }
210 if( m_props.m_hasColorTexture || m_props.m_hasFrustumClipping ) {
211 posFp = rsFp->insertShaderSourceFile(0, posFp, string_t(source_dir).append("/functions.glsl"));
212 if( posFp == std::string::npos ) {
213 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
214 return false;
215 }
216 }
217 /*if( hasColorTexture ) {
218 posFp = rsFp->insertShaderSource(0, posFp, "uniform "+colorTexSeq.getTextureSampler2DType()+" "+UniformNames.gcu_ColorTexUnit+";\n");
219 posFp = rsFp->insertShaderSource(0, posFp, colorTexSeq.getTextureLookupFragmentShaderImpl());
220 }*/
221
222 posFp = rsFp->insertShaderSource(0, posFp, GLSL_MAIN_BEGIN);
223
224 std::string passS = m_props.m_pass1 ? "-pass1-" : "-pass2-";
225 std::string shaderSegment = string_t(source_dir).append("/").append(shader_basename).append(passS).append("curve_simple").append(".glsl"); // sms.tech+sms.sub+".glsl";
227 jau_PLAIN_PRINT(true, "RegionRenderer.createShaderProgram.1: segment %s", shaderSegment.c_str());
228 }
229 posFp = rsFp->insertShaderSourceFile(0, posFp, shaderSegment);
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 posFp = rsFp->insertShaderSource(0, posFp, "}\n");
235 if( posFp == std::string::npos ) {
236 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
237 return false;
238 }
239
240 if( m_props.m_hasColorTexture ) {
241 rsFp->replaceInShaderSource(std::string(gcuTexture2D), std::string(colTexLookupFuncName));
242 }
243
244 }
246 if( !sp0->add(gl, rsVp, true) || !sp0->add(gl, rsFp, true) ) {
247 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
248 sp0->destroy(gl);
249 return false;
250 }
251 m_st.attachShaderProgram(gl, sp0, true);
252
253 PMVMat4f& pmv = m_pmvMat.pmv();
254 pmv.getP().loadIdentity();
255 pmv.getMv().loadIdentity();
256
257 m_st.send(gl, m_pmvMat);
258 m_st.send(gl, m_light0Pos);
259 m_st.send(gl, m_staticColor);
260
261 m_initialized = sp0->inUse();
262 if( !m_initialized ) {
263 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d\n", E_FILE_LINE);
264 m_st.destroy(gl);
265 }
266 return m_initialized;
267 }
268
269 void destroy(GL& gl) {
270 m_st.destroyShaderProgram(gl);
271 }
272
273 void useProgram(GL& gl, bool on) {
274 m_st.useProgram(gl, on);
275 }
276
278 m_st.send(gl, m_staticColor);
279 }
280 void updatePMV(GL& gl) {
281 m_st.send(gl, m_pmvMat);
282 }
283 void updateAll(GL& gl) {
284 m_st.send(gl, m_pmvMat);
285 m_st.send(gl, m_staticColor);
286 }
287 PMVMat4f& pmv() noexcept { return m_pmvMat.pmv(); }
288 const PMVMat4f& pmv() const noexcept { return m_pmvMat.pmv(); }
289 const Vec4f& color() const noexcept { return m_staticColor.vec4f(); }
290 void setColor(const Vec4f& c) noexcept { m_staticColor.vec4f()=c; }
291 ShaderState& st() noexcept { return m_st; }
292 const ShaderState& st() const noexcept { return m_st; }
293};
294
295class GraphRegion {
296 public:
298 private:
299 GraphRenderer& m_renderer;
300 bool m_initialized;
303 int m_num_vertices, m_num_indices;
304
305 public:
307 : m_renderer(renderer),
308 m_initialized(m_renderer.initialized()),
309 m_array(GLFloatArrayDataServer::createGLSLInterleaved(m_renderer.arrayCompsPerElement(), false, 256, GL_STATIC_DRAW)),
310 m_indices(GLUIntArrayDataServer::createData(3, 256, GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER)),
311 m_num_vertices(0), m_num_indices(0)
312 {
313 m_array->addGLSLSubArray("gca_Vertex", 3, GL_ARRAY_BUFFER);
314 m_array->addGLSLSubArray("gca_CurveParam", 3, GL_ARRAY_BUFFER);
315 if( m_renderer.usesNormal() ) {
316 m_array->addGLSLSubArray("gca_Normal", 3, GL_ARRAY_BUFFER);
317 }
318 m_renderer.st().manage(m_array);
319 // m_st.manage(m_indices);
320 }
321
322 constexpr bool initialized() const noexcept { return m_initialized; }
323
324 void destroy(GL& gl) {
325 m_renderer.st().destroyAllData(gl);
326 // m_array->destroy(gl); // owned by m_st
327 m_indices->destroy(gl);
328 m_num_vertices = 0;
329 m_num_indices = 0;
330 }
331
332 void seal(GL& gl, bool seal_) {
333 if( !m_initialized ) {
334 return;
335 }
336 m_array->seal(gl, seal_);
337 m_indices->seal(gl, seal_);
338 m_array->enableBuffer(gl, false);
339 m_indices->enableBuffer(gl, false);
340 }
341
342 private:
343 void pushVertex(const Vertex& v, const Vec3f& normal) {
344 // jau::PLAIN_PRINT(true, "pushVertex.0[%d]: v %s]", m_num_vertices, v.toString().c_str());
345 m_array->put3f(v.coord());
346 m_array->put3f(v.texCoord());
347 if( m_renderer.usesNormal() ) {
348 m_array->put3f(normal);
349 }
350 ++m_num_vertices;
351 }
352 void pushIndices(uint32_t i, uint32_t j, uint32_t k) {
353 // jau::PLAIN_PRINT(true, "pushIndices.0[%d]: %u, %u, %u]", m_num_indices, i, j, k);
354 m_indices->putN(i, j, k);
355 m_num_indices += 3;
356 }
357 void pushNewVerticesIdx(const Vertex& vertIn1, const Vertex& vertIn2, const Vertex& vertIn3, const Vec3f& normal) {
358 pushIndices(m_num_vertices, m_num_vertices+1, m_num_vertices+2);
359 pushVertex(vertIn1, normal);
360 pushVertex(vertIn2, normal);
361 pushVertex(vertIn3, normal);
362 }
363
364 public:
366 if( !m_initialized ) {
367 return;
368 }
369 if( Graph::DEBUG_MODE ) {
370 jau_PLAIN_PRINT(true, "add.0 num[vertices %d, indices %d]", m_num_vertices, m_num_indices);
371 jau_PLAIN_PRINT(true, "add.0 array: %s", m_array->toString().c_str());
372 jau_PLAIN_PRINT(true, "add.0 indices: %s", m_indices->toString().c_str());
373 }
374 const TriangleRefList& trisIn = shape.getTriangles();
375 const VertexList& vertsIn = shape.getVertices();
376 if( Graph::DEBUG_MODE ) {
377 jau_PLAIN_PRINT(true, "add.0 triangles %u, vertices %u", trisIn.size(), vertsIn.size());
378 }
379 {
380 glmemsize_t verticeCount = (glmemsize_t)vertsIn.size() + shape.addedVertexCount();
381 glmemsize_t indexCount = (glmemsize_t)trisIn.size() * 3;
382 m_array->growIfNeeded(verticeCount * m_array->compsPerElem());
383 m_indices->growIfNeeded(indexCount * m_indices->compsPerElem());
384 }
385 uint32_t idxOffset = m_num_vertices;
386 if( vertsIn.size() >= 3 ) {
387 //
388 // Processing Vertices
389 //
390 for(const Vertex& v : vertsIn) {
391 pushVertex(v, shape.normal());
392 }
393 constexpr static uint32_t max_index = std::numeric_limits<uint32_t>::max() / sizeof(uint32_t);
394 OutlineShape::size_type trisIn_sz = trisIn.size();
395 for(OutlineShape::size_type i=0; i < trisIn_sz; ++i) {
396 const TriangleRef& triIn = trisIn[i];
397 // triEx.addVertexIndicesOffset(idxOffset);
398 // triangles.add( triEx );
399 Triangle::trivert_t& triInVertices = triIn->vertices();
400 uint32_t tv0Idx = triInVertices[0].id();
401 if ( max_index - idxOffset > tv0Idx ) {
402 // valid 'known' idx - move by offset
403 pushIndices(tv0Idx+idxOffset,
404 triInVertices[1].id()+idxOffset,
405 triInVertices[2].id()+idxOffset);
406 } else {
407 // FIXME: If exceeding max_indices, we would need to generate a new buffer w/ indices
408 pushNewVerticesIdx(triInVertices[0], triInVertices[1], triInVertices[2], shape.normal());
409 }
410 }
411 }
412 if( Graph::DEBUG_MODE ) {
413 jau_PLAIN_PRINT(true, "add.x num[vertices %d, indices %d]", m_num_vertices, m_num_indices);
414 jau_PLAIN_PRINT(true, "add.x array: %s", m_array->toString().c_str());
415 jau_PLAIN_PRINT(true, "add.x indices: %s", m_indices->toString().c_str());
416 }
417 }
418
419 void draw(GL &gl) {
420 if( !m_initialized ) {
421 return;
422 }
423 m_renderer.useProgram(gl, true);
424
425 m_array->enableBuffer(gl, true);
426 m_indices->bindBuffer(gl, true); // keeps VBO binding
427
428 ::glEnable(GL_BLEND);
429 ::glBlendEquation(GL_FUNC_ADD); // default
430 ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
431
432 ::glDrawElements(GL_TRIANGLES, m_indices->elemCount() * m_indices->compsPerElem(), GL_UNSIGNED_INT, nullptr);
433
434 m_indices->bindBuffer(gl, false);
435 m_array->enableBuffer(gl, false);
436 // m_renderer.useProgram(gl, false);
437 }
438};
439
440class Shape;
441typedef std::shared_ptr<Shape> ShapeRef;
442
443class Shape {
444 private:
445 std::vector<OutlineShape> m_oshapes;
446
447 Vec3f m_position;
448 Quat4f m_rotation;
449 Vec3f m_rotPivot;
450 Vec3f m_scale = Vec3f(1, 1, 1);
451 float m_zOffset = 0.0f;
452 Vec4f m_color = Vec4f(0, 0, 0, 1);
453 GraphRenderer& m_renderer;
454 GraphRegion m_region;
455
456 Mat4f iMat;
457 Mat4f tmpMat;
458 bool iMatIdent = true;
459 bool iMatDirty = false;
460
461 float m_velo = 0; // m/s
462
463 struct Private{ explicit Private() = default; };
464
465 public:
466 Shape(Private, GraphRenderer &renderer)
467 : m_renderer(renderer), m_region(m_renderer)
468 {
469 std::cerr << "XXX ctor.x " << m_renderer.st() << "\n";
470 }
471
473 return std::make_shared<Shape>(Private(), renderer);
474 }
475
476 void destroy(GL& gl) {
477 m_region.destroy(gl);
478 }
479
480 constexpr const Vec3f& position() const noexcept { return m_position; }
481 constexpr Vec3f& position() noexcept { iMatDirty=true; return m_position; }
482 constexpr void set_position(Vec3f new_pos) noexcept { m_position = new_pos; }
483
484 constexpr const float& zOffset() const noexcept { return m_zOffset; }
485 constexpr float& zOffset() noexcept { iMatDirty=true; return m_zOffset; }
486
487 constexpr const Quat4f& rotation() const noexcept { return m_rotation; }
488 constexpr Quat4f& rotation() noexcept { iMatDirty=true; return m_rotation; }
489
490 constexpr const Vec3f& rotationPivot() const noexcept { return m_rotPivot; }
491 constexpr Vec3f& rotationPivot() noexcept { iMatDirty=true; return m_rotPivot; }
492
493 constexpr const Vec3f& scale() const noexcept { return m_scale; }
494 constexpr Vec3f& scale() noexcept { iMatDirty=true; return m_scale; }
495
496 constexpr const std::vector<OutlineShape>& outlineShapes() const noexcept { return m_oshapes; }
497 constexpr std::vector<OutlineShape>& outlineShapes() noexcept { return m_oshapes; }
498
499 const Vec4f& color() const noexcept { return m_color; }
500 void setColor(const Vec4f& c) noexcept { m_color = c; }
501
502 void update(GL& gl) {
503 for(OutlineShape& o : m_oshapes){
504 m_region.addOutlineShape(o);
505 }
506 m_region.seal(gl, true);
507 }
508
509 void draw(GL &gl) {
510 PMVMat4f& pmv = m_renderer.pmv();
511 pmv.pushMv();
512 applyMatToMv(pmv);
513
514 m_renderer.setColor(m_color);
515 m_renderer.updateAll(gl); // PMV + Color
516
517 m_region.draw(gl);
518 pmv.popMv();
519 }
520
521 /// Game ..
522 void tick(float dt) {
523 if( !jau::is_zero(m_velo) ) {
524 iMatDirty = true;
525 m_rotation.rotateByAngleZ( M_PI_2);
526 Vec3f dir = m_rotation.rotateVector(Vec3f(1, 0, 0));
527 m_rotation.rotateByAngleZ(-M_PI_2);
528 Vec3f d_p = dir * m_velo * dt;
529
530 m_position += d_p;
531 }
532 }
533 float& velo() noexcept { return m_velo; }
534
535 private:
536 /**
537 * Applies the internal {@link Matrix4f} to the given {@link PMVMatrix4f#getMv() modelview matrix},
538 * i.e. {@code pmv.mulMv( getMat() )}.
539 * <p>
540 * Calls {@link #updateMat()} if dirty.
541 * </p>
542 * In case {@link #isMatIdentity()} is {@code true}, implementation is a no-operation.
543 * </p>
544 * @param pmv the matrix
545 * @see #isMatIdentity()
546 * @see #updateMat()
547 * @see #getMat()
548 * @see PMVMatrix4f#mulMv(Matrix4f)
549 */
550 void applyMatToMv(PMVMat4f& pmvMat) noexcept {
551 if( iMatDirty ) {
552 updateMat();
553 }
554 if( !iMatIdent ) {
555 pmvMat.mulMv(iMat);
556 }
557 }
558 void updateMat() noexcept {
559 bool hasPos = !m_position.is_zero();
560 bool hasScale = m_scale != Vec3f::one;
561 bool hasRotate = !m_rotation.isIdentity();
562 bool hasRotPivot = false; // null != rotPivot;
563 for(OutlineShape& o : m_oshapes){
564 const Vec3f& ctr = o.bounds().center();
565 bool sameScaleRotatePivot = hasScale && hasRotate && ( !hasRotPivot || m_rotPivot == ctr );
566
567 if( sameScaleRotatePivot ) {
568 iMatIdent = false;
569 iMat.setToTranslation(m_position); // identity + translate, scaled
570 // Scale shape from its center position and rotate around its center
571 iMat.translate(Vec3f(ctr).mul(m_scale)); // add-back center, scaled
572 iMat.rotate(m_rotation);
573 iMat.scale(m_scale);
574 iMat.translate(-ctr); // move to center
575 } else if( hasRotate || hasScale ) {
576 iMatIdent = false;
577 iMat.setToTranslation(m_position); // identity + translate, scaled
578 if( hasRotate ) {
579 if( hasRotPivot ) {
580 // Rotate shape around its scaled pivot
581 iMat.translate(Vec3f(m_rotPivot).mul(m_scale)); // pivot back from rot-pivot, scaled
582 iMat.rotate(m_rotation);
583 iMat.translate(Vec3f(-m_rotPivot).mul(m_scale)); // pivot to rot-pivot, scaled
584 } else {
585 // Rotate shape around its scaled center
586 iMat.translate(Vec3f(ctr).mul(m_scale)); // pivot back from center-pivot, scaled
587 iMat.rotate(m_rotation);
588 iMat.translate(Vec3f(-ctr).mul(m_scale)); // pivot to center-pivot, scaled
589 }
590 }
591 if( hasScale ) {
592 // Scale shape from its center position
593 iMat.translate(Vec3f(ctr).mul(m_scale)); // add-back center, scaled
594 iMat.scale(m_scale);
595 iMat.translate(Vec3f(-ctr).mul(m_scale)); // move to center
596 }
597 } else if( hasPos ) {
598 iMatIdent = false;
599 iMat.setToTranslation(m_position); // identity + translate, scaled
600
601 } else {
602 iMatIdent = true;
603 iMat.loadIdentity();
604 }
605 iMatDirty = false;
606 }
607 }
608};
609
611 private:
612 constexpr static float zNear= 1.0f;
613 constexpr static float zFar =100.0f;
614
615 ShaderState m_st;
616 Recti m_viewport;
617 bool m_initialized;
618 bool m_animating = true;
619 bool m_oneframe = false;
621 GraphRenderer m_renderer;
622 std::vector<ShapeRef> m_shapes;
623 bool m_once = true;
624
625 public:
628 m_initialized(false),
629 m_renderer(m_st)
630 {
631 }
632
633 Recti& viewport() noexcept { return m_viewport; }
634 const Recti& viewport() const noexcept { return m_viewport; }
635 std::vector<ShapeRef>& shapes() noexcept { return m_shapes; }
636
637 bool animating() const noexcept { return m_animating; }
638 bool& animating() noexcept { return m_animating; }
639 void setOneFrame() noexcept { m_animating=false; m_oneframe=true; }
640
641 bool init(const WindowSRef& win, const jau::fraction_timespec& when) override {
642 jau::fprintf_td(when.to_ms(), stdout, "RL::init: %s\n", toString().c_str());
643 m_tlast = when;
644
645 GL& gl = GL::downcast(win->renderContext());
646
647 if( !m_renderer.init(gl, when) ) {
648 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d: %s\n", E_FILE_LINE, toString().c_str());
649 win->dispose(when);
650 return false;
651 }
652 ShapeRef cobraMkIII_Shape = Shape::createShared(m_renderer);
653 models::appendCobraMkIII(cobraMkIII_Shape->outlineShapes());
654 cobraMkIII_Shape->setColor(Vec4f(0.05f, 0.05f, 0.5f, 1.0f));
655 cobraMkIII_Shape->rotation().rotateByAngleX(-M_PI / 4.0f);
656 cobraMkIII_Shape->update(gl);
657 m_shapes.push_back(cobraMkIII_Shape);
658
659 //::glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
660 ::glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
661 ::glEnable(GL_DEPTH_TEST);
662 // ::glEnable(GL_CULL_FACE);
663 ::glDisable(GL_CULL_FACE);
664
665 m_initialized = true;
666 if( !m_initialized ) {
667 jau::fprintf_td(when.to_ms(), stdout, "ERROR %s:%d: %s\n", E_FILE_LINE, toString().c_str());
668 m_st.destroy(gl);
669 win->dispose(when);
670 }
671 return m_initialized;
672 }
673
674 void dispose(const WindowSRef& win, const jau::fraction_timespec& when) override {
675 GL& gl = GL::downcast(win->renderContext());
676 jau::fprintf_td(when.to_ms(), stdout, "RL::dispose: %s\n", toString().c_str());
677 for(ShapeRef& s : m_shapes) {
678 s->destroy(gl);
679 }
680 m_renderer.destroy(gl);
681 m_st.destroy(GL::downcast(win->renderContext()));
682 m_initialized = false;
683 }
684
685 void reshape(const WindowSRef& win, const jau::math::Recti& viewport, const jau::fraction_timespec& when) override {
686 GL& gl = GL::downcast(win->renderContext());
687 jau::fprintf_td(when.to_ms(), stdout, "RL::reshape: %s\n", toString().c_str());
688 m_viewport = viewport;
689
690 PMVMat4f& pmv = m_renderer.pmv();
691 pmv.getP().loadIdentity();
692 const float aspect = 1.0f;
693 const float fovy_deg=45.0f;
694 const float aspect2 = ( (float) m_viewport.width() / (float) m_viewport.height() ) / aspect;
695 pmv.perspectiveP(jau::adeg_to_rad(fovy_deg), aspect2, zNear, zFar);
696 pmv.getMv().loadIdentity();
697 pmv.translateMv(0, 0, -5);
698
699 m_st.useProgram(gl, true);
700 // m_st.useProgram(gl, false);
701 }
702
703 void display(const WindowSRef& win, const jau::fraction_timespec& when) override {
704 // jau::fprintf_td(when.to_ms(), stdout, "RL::display: %s, %s\n", toString().c_str(), win->toString().c_str());
705 if( !m_initialized ) {
706 return;
707 }
708 GL& gl = GL::downcast(win->renderContext());
709 ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
710
711 m_st.useProgram(gl, true);
712
713 const float dt = float( (when - m_tlast).to_double() );
714 for(const ShapeRef& s : m_shapes) {
715 if( (animating() || m_oneframe) && s != m_shapes[0]) {
716 constexpr float angle_per_sec = 30;
717 const float rad = dt * angle_per_sec;
718 s->rotation().rotateByAngleX(jau::adeg_to_rad( -rad ));
719 s->rotation().rotateByAngleY(jau::adeg_to_rad( rad ));
720 //s->rotation().rotateByAngleZ(jau::adeg_to_rad( rad ));
721 }
722 s->tick(dt);
723 s->draw(gl);
724 }
725 m_oneframe = false;
726
727 if( m_once ) {
728 m_once = false;
729 std::cerr << "XXX draw " << m_st << "\n";
730 }
731
732 // m_st.useProgram(gl, false);
733
734 m_tlast = when;
735 }
736
737 std::string toStringImpl() const noexcept override { return "GraphShapes01"; }
738};
739
740class Example : public GraphShapes02 {
741 private:
742 class MyKeyListener : public KeyListener {
743 private:
744 GraphShapes02& m_parent;
745 public:
746 MyKeyListener(GraphShapes02& p) : m_parent(p) {}
747
748 void keyPressed(KeyEvent& e, const KeyboardTracker& kt) override {
749 jau::fprintf_td(e.when().to_ms(), stdout, "KeyPressed: %s; keys %zu\n", e.toString().c_str(), kt.pressedKeyCodes().count());
750 std::vector<ShapeRef>& shapeList = m_parent.shapes();
751 if( e.keySym() == VKeyCode::VK_ESCAPE ) {
752 WindowSRef win = e.source().lock();
753 if( win ) {
754 win->dispose(e.when());
755 }
756 } else if( e.keySym() == VKeyCode::VK_PAUSE || e.keySym() == VKeyCode::VK_P ) {
757 m_parent.animating() = !m_parent.animating();
758 } else if( e.keySym() == VKeyCode::VK_PERIOD ) {
759 m_parent.setOneFrame();
760 } else if( e.keySym() == VKeyCode::VK_W ) {
761 WindowSRef win = e.source().lock();
762 jau::fprintf_td(e.when().to_ms(), stdout, "Source: %s\n", win ? win->toString().c_str() : "null");
763 } else if( e.keySym() == VKeyCode::VK_UP ) {
764 shapeList[0]->rotation().rotateByAngleX(-M_PI / 50);
765 } else if( e.keySym() == VKeyCode::VK_DOWN ) {
766 shapeList[0]->rotation().rotateByAngleX( M_PI / 50);
767 } else if( e.keySym() == VKeyCode::VK_SHIFT ) {
768 shapeList[0]->velo() += 0.1f;
769 } else if( e.keySym() == VKeyCode::VK_ENTER ) {
770 shapeList[0]->velo() = std::max(shapeList[0]->velo() - 0.1f, 0.0f);
771 } else if( e.keySym() == VKeyCode::VK_RIGHT) {
772 shapeList[0]->rotation().rotateByAngleY( M_PI / 50);
773 } else if( e.keySym() == VKeyCode::VK_LEFT ) {
774 shapeList[0]->rotation().rotateByAngleY(-M_PI / 50);
775 }
776 }
777 void keyReleased(KeyEvent& e, const KeyboardTracker& kt) override {
778 jau::fprintf_td(e.when().to_ms(), stdout, "KeyRelease: %s; keys %zu\n", e.toString().c_str(), kt.pressedKeyCodes().count());
779 }
780 };
781 typedef std::shared_ptr<MyKeyListener> MyKeyListenerRef;
782 MyKeyListenerRef m_kl;
783
784 public:
786 : GraphShapes02(),
787 m_kl(std::make_shared<MyKeyListener>(*this)) { }
788
789 bool init(const WindowSRef& win, const jau::fraction_timespec& when) override {
790 if( !GraphShapes02::init(win, when) ) {
791 return false;
792 }
793 win->addKeyListener(m_kl);
794 return true;
795 }
796 void dispose(const WindowSRef& win, const jau::fraction_timespec& when) override {
797 win->removeKeyListener(m_kl);
798 GraphShapes02::dispose(win, when);
799 }
800};
801
802int main(int argc, char *argv[]) // NOLINT(bugprone-exception-escape)
803{
804 return launch("GraphShapes02.cpp",
806 .contextFlags=gamp::render::RenderContextFlags::verbose}, // | gamp::render::RenderContextFlags::debug},
807 std::make_shared<Example>(), argc, argv);
808}
int launch(std::string_view sfile, GLLaunchProps props, const RenderListenerSRef &demo, int argc, char *argv[])
std::shared_ptr< Shape > ShapeRef
int main(int argc, char *argv[])
std::shared_ptr< Shape > ShapeRef
std::shared_ptr< Shape > ShapeRef
bool init(const WindowSRef &win, const jau::fraction_timespec &when) override
Called by the drawable immediately after the render context is initialized.
void dispose(const WindowSRef &win, const jau::fraction_timespec &when) override
Notifies the listener to perform the release of all renderer resources per context,...
void destroy(GL &gl)
constexpr bool initialized() const noexcept
void draw(GL &gl)
void addOutlineShape(OutlineShape &shape)
GraphRegion(GraphRenderer &renderer)
jau::darray< uint32_t, glmemsize_t > u32buffer_t
void seal(GL &gl, bool seal_)
void destroy(GL &gl)
static constexpr std::string_view GLSL_USE_LIGHT0
ShaderState & st() noexcept
bool init(GL &gl, const jau::fraction_timespec &when)
static constexpr std::string_view GLSL_CONST_SAMPLE_COUNT
static constexpr std::string_view GLSL_USE_COLOR_CHANNEL
static constexpr std::string_view GLSL_USE_DISCARD
void updatePMV(GL &gl)
const PMVMat4f & pmv() const noexcept
static constexpr std::string_view GLSL_USE_FRUSTUM_CLIPPING
static constexpr std::string_view source_dir
static constexpr std::string_view GLSL_PARAM_COMMENT_END
constexpr GLsizei arrayCompsPerElement() const noexcept
const Vec4f & color() const noexcept
constexpr bool initialized() const noexcept
static constexpr std::string_view shader_basename
static constexpr std::string_view bin_dir
static constexpr std::string_view gcuTexture2D
static constexpr std::string_view GLSL_MAIN_BEGIN
static constexpr std::string_view GLSL_USE_COLOR_TEXTURE
static constexpr std::string_view GLSL_USE_NORMAL_CHANNEL
constexpr bool usesNormal() const noexcept
const ShaderState & st() const noexcept
void updateAll(GL &gl)
void updateColor(GL &gl)
PMVMat4f & pmv() noexcept
static constexpr std::string_view colTexLookupFuncName
GraphRenderer(ShaderState &st)
void useProgram(GL &gl, bool on)
static constexpr std::string_view GLSL_PARAM_COMMENT_START
static constexpr std::string_view GLSL_DEF_SAMPLE_COUNT
void setColor(const Vec4f &c) noexcept
void dispose(const WindowSRef &win, const jau::fraction_timespec &when) override
Notifies the listener to perform the release of all renderer resources per context,...
bool init(const WindowSRef &win, const jau::fraction_timespec &when) override
Called by the drawable immediately after the render context is initialized.
void display(const WindowSRef &win, const jau::fraction_timespec &when) override
Called by the drawable to initiate rendering by the client.
void reshape(const WindowSRef &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.
bool & animating() noexcept
const Recti & viewport() const noexcept
bool animating() const noexcept
void setOneFrame() noexcept
std::vector< ShapeRef > & shapes() noexcept
std::string toStringImpl() const noexcept override
Recti & viewport() noexcept
constexpr const float & zOffset() const noexcept
void setColor(const Vec4f &c) noexcept
void tick(float dt)
Game ..
Shape(Private, GraphRenderer &renderer)
constexpr Vec3f & position() noexcept
constexpr std::vector< OutlineShape > & outlineShapes() noexcept
static ShapeRef createShared(ShaderState &st, GraphRenderer &renderer)
void draw(GL &gl)
void update(GL &gl)
float & velo() noexcept
constexpr const std::vector< OutlineShape > & outlineShapes() const noexcept
constexpr const Quat4f & rotation() const noexcept
constexpr void set_position(Vec3f new_pos) noexcept
void destroy(GL &gl)
constexpr const Vec3f & rotationPivot() const noexcept
constexpr float & zOffset() noexcept
constexpr const Vec3f & position() const noexcept
constexpr Vec3f & scale() noexcept
constexpr Vec3f & rotationPivot() noexcept
static ShapeRef createShared(GraphRenderer &renderer)
const Vec4f & color() const noexcept
constexpr const Vec3f & scale() 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...
const VertexList & getVertices()
Return list of concatenated vertices associated with all Outlines of this object.
constexpr const Vec3f & normal() const noexcept
Normal vector, optionally used by tesselator to add (interleaved) normals.
const TriangleRefList & getTriangles(VertexState destinationType=VertexState::quadratic_nurbs)
Triangulate the OutlineShape generating a list of triangles, while transformOutlines(VerticesState) b...
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:42
static constexpr std::string_view GLES2
The embedded OpenGL profile ES 2.x, with x >= 0.
Definition GLContext.hpp:65
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=true, bool addDefaultPrecision=true, bool addDefaultDefines=true)
Default customization of this shader source code.
static ShaderCodeSRef create(GLenum type, size_t count, const source_list_t &sources)
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.
static ShaderProgramSRef create() noexcept
void destroy(GL &gl) noexcept
Detaches all shader codes and deletes the program.
bool add(const ShaderCodeSRef &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
void addKeyListener(const KeyListenerSRef &l)
Definition Window.hpp:310
void dispose(const jau::fraction_timespec &when) noexcept override
Definition Window.hpp:355
size_t removeKeyListener(const KeyListenerSRef &l)
Definition Window.hpp:311
std::string toString() const noexcept
Definition gamp_wt.cpp:145
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_type count() const noexcept
Definition bitfield.hpp:368
Implementation of a dynamic linear array storage, aka vector, including relative positional access.
Definition darray.hpp:154
constexpr size_type size() const noexcept
Like std::vector::size().
Definition darray.hpp:1116
constexpr Matrix4 & loadIdentity() noexcept
Set this matrix to identity.
Definition mat4f.hpp:239
constexpr_cxx26 Quaternion & rotateByAngleX(const value_type angle) noexcept
Rotate this quaternion around X axis with the given angle in radians.
static constexpr const value_type one
Definition vec3f.hpp:64
constexpr Mat4 & getP() noexcept
Returns the projection matrix (P).
Definition pmvmat4.hpp:263
constexpr PMVMatrix4 & translateMv(float x, float y, float z) noexcept
Translate the modelview matrix.
Definition pmvmat4.hpp:641
constexpr_cxx20 PMVMatrix4 & popMv() noexcept
Pop the modelview matrix from its stack.
Definition pmvmat4.hpp:795
PMVMatrix4 & perspectiveP(const float fovy_rad, const float aspect, const float zNear, const float zFar)
Multiply the projection matrix with the perspective/frustum matrix.
Definition pmvmat4.hpp:885
constexpr Mat4 & getMv() noexcept
Returns the modelview matrix (Mv).
Definition pmvmat4.hpp:275
constexpr_cxx20 PMVMatrix4 & pushMv() noexcept
Push the modelview matrix to its stack, while preserving its values.
Definition pmvmat4.hpp:813
#define jau_PLAIN_PRINT(printPrefix, fmt,...)
Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true.
Definition debug.hpp:169
#define E_FILE_LINE
constexpr T adeg_to_rad(const T arc_degree) noexcept
Converts arc-degree to radians.
constexpr bool is_zero(const T &a, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if the given value is less than epsilon, w/ epsilon > 0.
GLArrayDataServer< float > GLFloatArrayDataServer
GLArrayDataServerSRef< float > GLFloatArrayDataServerSRef
GLsizeiptr glmemsize_t
Compatible with ssize_t.
Definition GLBuffers.hpp:26
GLArrayDataServer< uint32_t > GLUIntArrayDataServer
GLArrayDataServerSRef< uint32_t > GLUIntArrayDataServerSRef
std::shared_ptr< ShaderCode > ShaderCodeSRef
std::shared_ptr< ShaderProgram > ShaderProgramSRef
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 > WindowSRef
Definition Event.hpp:36
Matrix4< float > Mat4f
Definition mat4f.hpp:1968
Vector4F< float > Vec4f
Definition vec4f.hpp:360
PMVData
PMVMatrix4 derived matrices and values.
Definition pmvmat4.hpp:57
Quaternion< float > Quat4f
RectI< int > Recti
Definition recti.hpp:146
Vector3F< float > Vec3f
Definition vec3f.hpp:422
PMVMatrix4< float > PMVMat4f
Definition pmvmat4.hpp:1463
Gamp: Graphics, Audio, Multimedia and Processing Framework (Native C++, WebAssembly,...
Definition PTS.hpp:24
int fprintf_td(const uint64_t elapsed_ms, FILE *stream, std::string_view format, const Args &...args) noexcept
Convenient fprintf() invocation, prepending the given elapsed_ms timestamp.
Definition debug.hpp:184
void appendCobraMkIII(std::vector< OutlineShape > &oshapes, const float height=1.0f, const float width=2.0f, const float deep=0.3f)
STL namespace.
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.