Gamp v0.0.7-36-g24b1eb6
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
GLUTesselator.hpp
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#ifndef JAU_GAMP_GRAPH_TESS_GL_GLUTESSELATOR_HPP_
12#define JAU_GAMP_GRAPH_TESS_GL_GLUTESSELATOR_HPP_
13
14#include <string_view>
15#include <vector>
16
17#include <GL/glutess2.h>
18
19#include <jau/cpp_lang_util.hpp>
20#include <jau/debug.hpp>
21#include <jau/int_types.hpp>
22#include <jau/string_util.hpp>
23
24#include <gamp/GampTypes.hpp>
28
29namespace gamp::graph::tess {
30
31 using namespace jau::math;
32 using namespace jau::math::geom;
33 using namespace gamp::graph;
34 using namespace gamp::render::gl::data;
35
36 /** \addtogroup Gamp_Graph
37 *
38 * @{
39 */
40
41 /**
42 * GLUtilTesselator transform OutlineShapes to triangles using glutess2
43 * - The triangle's vertices are added to a given GLFloatArrayDataServer
44 * - If selected by flags, normals are added to the same GLFloatArrayDataServer (interleaved)
45 * - The triangle (strip, fan or plain) segments are captured via GLUtilTesselator::Segment
46 * and returned by the GLUtilTesselator::tesselate() command
47 * - Utilizes glutess2 in single precision float
48 */
50 public:
51 static constexpr int FLAG_NORMAL = 1 << 0;
52 static constexpr int FLAG_COLOR = 1 << 1;
53 static constexpr int FLAG_TEXTURE = 1 << 2;
54 static constexpr int FLAG_INDICES = 1 << 3;
55 static constexpr int FLAG_VERBOSE = 1 << 4;
56
57 struct Segment;
58 typedef std::vector<Segment> SegmentList;
59
60 struct Segment {
61 /// render implementation specific triangle type, i.e. triangle-strip, -fan or just triangles.
62 GLenum type;
63 /// index of first element in data-sink for this segment
64 GLint first;
65 /// number of elements in data-sink for this segment
66 GLsizei count;
67
68 inline static std::string toString(const std::string& pre, const SegmentList& segments) noexcept {
69 size_t i=0;
70 std::string r;
71 for(const Segment& s : segments ) {
72 r.append( jau::format_string_v(256, "%sSegment[%zu]: %s\n", pre.c_str(), i++, s.toString().c_str()) );
73 }
74 return r;
75 }
76
77 std::string toString() const {
78 std::string_view type_s;
79 switch( type ) {
80 case GL_TRIANGLE_STRIP:
81 type_s = "triangle_strip"; break;
82 case GL_TRIANGLE_FAN:
83 type_s = "triangle_fan"; break;
84 case GL_TRIANGLES:
85 type_s = "triangles"; break;
86 default:
87 type_s = "undefined";
88 }
89 return std::string("Segment[").append(type_s)
90 .append(", 1st ").append(std::to_string(first))
91 .append(", count ").append(std::to_string(count)).append("]");
92 }
93 };
94
95 private:
97 GLUIntArrayDataServer* m_indices;
98 SegmentList m_segments;
99 std::vector<std::shared_ptr<Vertex>> m_vcache;
100 Vec3f m_normal;
101 uint32_t m_nextIndex = 0;
102 uint32_t m_curIndex = 0;
103 int m_flags;
104
105 public:
106 GLUtilTesselator(int flags, GLFloatArrayDataServer& array) noexcept
107 : GLUtilTesselator(flags, array, nullptr) {}
108
110 : m_array(array), m_indices(indices),
111 m_flags(flags)
112 {
113 if( !m_indices ) {
114 m_flags &= ~FLAG_INDICES;
115 }
116 }
117
118 constexpr bool useNormal() const noexcept { return 0 != ( m_flags & FLAG_NORMAL ); }
119 constexpr bool useColor() const noexcept { return 0 != ( m_flags & FLAG_COLOR ); }
120 constexpr bool useTexture() const noexcept { return 0 != ( m_flags & FLAG_TEXTURE ); }
121 constexpr bool useIndices() const noexcept { return 0 != ( m_flags & FLAG_INDICES ); }
122 constexpr bool verbose() const noexcept { return 0 != ( m_flags & FLAG_VERBOSE ); }
123
124 /** Clears all internal data, not passed array or indices. */
125 void clear() {
126 m_segments.clear();
127 m_nextIndex = 0;
128 m_curIndex = 0;
129 m_vcache.clear();
130 }
131
132 /// Returns true if segments() is empty
133 bool empty() const noexcept { return m_segments.empty(); }
134
135 jau::nsize_t elementCount() const noexcept { return m_array.elemCount(); }
136
137 constexpr const std::vector<Segment>& segments() const noexcept { return m_segments; }
138
139 private:
140
141 static void cbBeginData( GLenum type, void *polygonData ) {
142 GLUtilTesselator* os = reinterpret_cast<GLUtilTesselator*>(polygonData);
143 os->m_curIndex = os->m_nextIndex++;
144 Segment s{.type=type, .first=castOrThrow<size_t, GLint>(os->m_array.elemCount()), .count=0 };
145 os->m_segments.push_back(s);
146 if( os->m_indices ) {
147 os->m_indices->putu32(os->m_curIndex);
148 }
149 if( os->verbose() ) {
150 jau::INFO_PRINT("GLUtess begin %02d, type 0x%X, %s", os->m_curIndex, type, s.toString().c_str());
151 }
152 }
153 static void cbVertexData( void *data, void *polygonData ) {
154 GLUtilTesselator* os = reinterpret_cast<GLUtilTesselator*>(polygonData);
155 Vertex* v = reinterpret_cast<Vertex*>(data);
156 if( os->verbose() ) {
157 jau::INFO_PRINT("GLUtess vertex %02d, %s", os->m_curIndex, v->coord().toString().c_str());
158 }
159 os->m_array.put3f(v->coord());
160 if( os->useNormal() ) {
161 os->m_array.put3f(os->m_normal);
162 }
163 }
164 static void cbEndData( void *polygonData ) {
165 GLUtilTesselator* os = reinterpret_cast<GLUtilTesselator*>(polygonData);
166 Segment& s = os->m_segments.at(os->m_segments.size()-1);
167 s.count = castOrThrow<size_t, GLsizei>(os->m_array.elemCount() - s.first);
168 if( os->verbose() ) {
169 jau::INFO_PRINT("GLUtess end %02d, %s", os->m_curIndex, s.toString().c_str());
170 }
171 }
172 static void cbErrorData( GLenum errnum, void *polygonData ) {
173 GLUtilTesselator* os = reinterpret_cast<GLUtilTesselator*>(polygonData);
174 if( os->verbose() ) {
175 // jau::INFO_PRINT("GLUtess error %02d, errnum 0x%X, %s", os->m_curIndex, errnum, gluErrorString(errnum));
176 jau::INFO_PRINT("GLUtess error %02d, errnum 0x%X", os->m_curIndex, errnum);
177 }
178 }
179 static void cbCombineData( GLUTessFloat coords[3], void *data[4],
180 GLfloat weight[4], void **outData,
181 void *polygonData ) {
182 GLUtilTesselator* os = reinterpret_cast<GLUtilTesselator*>(polygonData);
183 if( os->verbose() ) {
184 jau::INFO_PRINT("GLUtess combine %02d, %f, %f, %f", os->m_curIndex, coords[0], coords[1], coords[2]);
185 // jau::INFO_PRINT("GLUtess combine %p, %p, %p, %p", data[0], data[1], data[2], data[3]);
186 }
187 std::shared_ptr<Vertex> v = std::make_shared<Vertex>((float)coords[0], (float)coords[1], (float)coords[2], true);
188 os->m_vcache.push_back(v);
189 for (int i = 0; i < 4; ++i) {
190 Vertex* s = (Vertex*) data[i];
191 if( !s ) { break; }
192 for (int j = 0; j < 3; ++j) {
193 // v->color()[j] += (float)( weight[i] * s->color()[j] );
194 v->texCoord()[j] += (float)( weight[i] * s->texCoord()[j] );
195 }
196 }
197 outData[0] = v.get();
198 }
199
200 public:
201 static SegmentList tesselate(int flags, GLFloatArrayDataServer& array, OutlineShape& outlines) {
202 GLUtilTesselator glutess(flags, array);
203 return glutess.tesselate(outlines);
204 }
205 static SegmentList tesselate(int flags, GLFloatArrayDataServer& array, GLUIntArrayDataServer* indices, OutlineShape& outlines) {
206 GLUtilTesselator glutess(flags, array, indices);
207 return glutess.tesselate(outlines);
208 }
210 const bool odirty = outlines.verticesDirty() || outlines.trianglesDirty();
211 size_t outlineCount = 0;
212 if( odirty || empty() ) {
213 clear();
214 GLUtesselator* tess = gluNewTess();
215 if( !tess ) { return m_segments; }
216 m_normal = outlines.normal();
217 if( verbose() ) {
218 jau::INFO_PRINT("GLUtess: normal: %s", m_normal.toString().c_str());
219 }
220 gluTessNormal(tess, m_normal.x, m_normal.y, m_normal.z);
221 gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (_GLUfuncptr)cbBeginData);
222 gluTessCallback(tess, GLU_TESS_END_DATA, (_GLUfuncptr)cbEndData);
223 gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (_GLUfuncptr)cbVertexData);
224 gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (_GLUfuncptr)cbCombineData);
225 gluTessCallback(tess, GLU_TESS_ERROR_DATA, (_GLUfuncptr)cbErrorData);
226 {
227 gluTessBeginPolygon(tess, this);
228 for(const Outline& o : outlines.outlines()) {
229 if( verbose() ) {
230 jau::INFO_PRINT("GLUtess: outline %zu: Vertices %zu, Winding %s", outlineCount, o.vertexCount(), to_string(o.getWinding()).c_str());
231 }
232 ++outlineCount;
233 gluTessBeginContour(tess);
234 if constexpr ( sizeof(GLfloat) == sizeof(GLUTessFloat) ) {
235 for(const Vertex& v : o.vertices()) {
236 gluTessVertex(tess, const_cast<GLfloat*>(v.coord().cbegin()), (void*)&v);
237 }
238 } else {
239 for(const Vertex& v : o.vertices()) {
240 GLUTessFloat coords[] = { v.coord().x, v.coord().y, v.coord().z };
241 gluTessVertex(tess, coords, (void*)&v);
242 }
243 }
244 gluTessEndContour(tess);
245 }
246 gluTessEndPolygon(tess);
247 }
248 gluDeleteTess(tess);
249 // FIXME outlines.markClean(OutlineShape::DirtyBits::vertices | OutlineShape::DirtyBits::triangles);
250 }
251 if( verbose() ) {
252 jau::INFO_PRINT("GLUtess: outlines: %zu", outlineCount);
253 jau::INFO_PRINT("GLUtess: segments: %zu", m_segments.size());
254 jau::INFO_PRINT("\n%s", Segment::toString("- ", m_segments).c_str() );
255 jau::INFO_PRINT("GLUtess: outline dirty: %d", odirty);
256 jau::INFO_PRINT("GLUtess: index next: %d", m_nextIndex);
257 jau::INFO_PRINT("GLUtess: vcache: %zu", m_vcache.size());
258 jau::INFO_PRINT("GLUtess: vertices: %s", m_array.toString().c_str());
259 if( m_indices ) {
260 jau::INFO_PRINT("GLUtess: indices: %s", m_indices->toString().c_str());
261 }
262 }
263 return m_segments;
264 }
265 };
266
267
268 /**@}*/
269
270} // namespace gamp::graph::tess
271
272#endif /* JAU_GAMP_GRAPH_TESS_GL_GLUTESSELATOR_HPP_ */
A Generic shape objects which is defined by a list of Outlines.
const OutlineList & outlines() const noexcept
constexpr bool verticesDirty() const noexcept
constexpr const Vec3f & normal() const noexcept
Normal vector, optionally used by tesselator to add (interleaved) normals.
constexpr bool trianglesDirty() const noexcept
Define a single continuous stroke by control vertices.
Definition Outline.hpp:51
constexpr const Vec3f & texCoord() const noexcept
Definition PrimTypes.hpp:96
constexpr const Vec3f & coord() const noexcept
Definition PrimTypes.hpp:93
GLUtilTesselator transform OutlineShapes to triangles using glutess2.
GLUtilTesselator(int flags, GLFloatArrayDataServer &array) noexcept
constexpr bool useIndices() const noexcept
void clear()
Clears all internal data, not passed array or indices.
constexpr bool verbose() const noexcept
bool empty() const noexcept
Returns true if segments() is empty.
static SegmentList tesselate(int flags, GLFloatArrayDataServer &array, OutlineShape &outlines)
jau::nsize_t elementCount() const noexcept
GLUtilTesselator(int flags, GLFloatArrayDataServer &array, GLUIntArrayDataServer *indices) noexcept
constexpr bool useColor() const noexcept
const SegmentList & tesselate(OutlineShape &outlines)
constexpr const std::vector< Segment > & segments() const noexcept
static SegmentList tesselate(int flags, GLFloatArrayDataServer &array, GLUIntArrayDataServer *indices, OutlineShape &outlines)
constexpr bool useTexture() const noexcept
constexpr bool useNormal() const noexcept
std::string toString() const noexcept
Definition vec3f.hpp:239
GLArrayDataServer< float > GLFloatArrayDataServer
GLArrayDataServer< uint32_t > GLUIntArrayDataServer
U castOrThrow(T has)
Definition GampTypes.hpp:70
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition int_types.hpp:55
Vector3F< float > Vec3f
Definition vec3f.hpp:436
std::string to_string(const math_error_t v) noexcept
Returns std::string representation of math_error_t.
constexpr std::string format_string_v(const std::size_t maxStrLen, const std::string_view format, const Args &...args)
Safely returns a string according to printf() formatting rules and variable number of arguments follo...
Author: Sven Gothel sgothel@jausoft.com Copyright (c) 2024 Gothel Software e.K.
void INFO_PRINT(const char *format,...) noexcept
Use for unconditional informal messages, prefix '[elapsed_time] Info: '.
Definition debug.cpp:248
GLsizei count
number of elements in data-sink for this segment
static std::string toString(const std::string &pre, const SegmentList &segments) noexcept
GLint first
index of first element in data-sink for this segment
GLenum type
render implementation specific triangle type, i.e. triangle-strip, -fan or just triangles.