Gamp v0.0.7-54-gccdc599
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
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_VERBOSE = 1 << 3;
55
56 struct Segment;
57 typedef std::vector<Segment> SegmentList;
58
59 struct Segment {
60 /// render implementation specific triangle type, i.e. triangle-strip, -fan or just triangles.
61 GLenum type;
62 /// index of first element in data-sink for this segment
63 GLint first;
64 /// number of elements in data-sink for this segment
65 GLsizei count;
66
67 inline static std::string toString(const std::string& pre, const SegmentList& segments) noexcept {
68 size_t i=0;
69 std::string r;
70 for(const Segment& s : segments ) {
71 r.append( jau::format_string_n(256, "%sSegment[%zu]: %s\n", pre.c_str(), i++, s.toString().c_str()) );
72 }
73 return r;
74 }
75
76 std::string toString() const {
77 std::string_view type_s;
78 switch( type ) {
79 case GL_TRIANGLE_STRIP:
80 type_s = "triangle_strip"; break;
81 case GL_TRIANGLE_FAN:
82 type_s = "triangle_fan"; break;
83 case GL_TRIANGLES:
84 type_s = "triangles"; break;
85 default:
86 type_s = "undefined";
87 }
88 return std::string("Segment[").append(type_s)
89 .append(", 1st ").append(std::to_string(first))
90 .append(", count ").append(std::to_string(count)).append("]");
91 }
92 };
93
94 private:
96 SegmentList m_segments;
97 std::vector<std::shared_ptr<Vertex>> m_vcache;
98 Vec3f m_normal;
99 uint32_t m_nextSegment = 0;
100 uint32_t m_curSegment = 0;
101 int m_flags;
102
103 public:
104 GLUtilTesselator(int flags, GLFloatArrayDataServer& array) noexcept
105 : m_array(array), m_flags(flags)
106 {
107 }
108
109 constexpr bool useNormal() const noexcept { return 0 != ( m_flags & FLAG_NORMAL ); }
110 constexpr bool useColor() const noexcept { return 0 != ( m_flags & FLAG_COLOR ); }
111 constexpr bool useTexture() const noexcept { return 0 != ( m_flags & FLAG_TEXTURE ); }
112 constexpr bool verbose() const noexcept { return 0 != ( m_flags & FLAG_VERBOSE ); }
113
114 /** Clears all internal data, not passed array or indices. */
115 void clear() {
116 m_segments.clear();
117 m_nextSegment = 0;
118 m_curSegment = 0;
119 m_vcache.clear();
120 }
121
122 /// Returns true if segments() is empty
123 bool empty() const noexcept { return m_segments.empty(); }
124
125 jau::nsize_t elementCount() const noexcept { return m_array.elemCount(); }
126
127 constexpr const std::vector<Segment>& segments() const noexcept { return m_segments; }
128
129 private:
130
131 static void cbBeginData( GLenum type, void *polygonData ) {
132 GLUtilTesselator* os = reinterpret_cast<GLUtilTesselator*>(polygonData);
133 os->m_curSegment = os->m_nextSegment++;
134 Segment s{.type=type, .first=castOrThrow<size_t, GLint>(os->m_array.elemCount()), .count=0 };
135 os->m_segments.push_back(s);
136 if( os->verbose() ) {
137 jau::INFO_PRINT("GLUtess begin %02d, type 0x%X, %s", os->m_curSegment, type, s.toString().c_str());
138 }
139 }
140 static void cbVertexData( void *data, void *polygonData ) {
141 GLUtilTesselator* os = reinterpret_cast<GLUtilTesselator*>(polygonData);
142 Vertex* v = reinterpret_cast<Vertex*>(data);
143 if( os->verbose() ) {
144 jau::INFO_PRINT("GLUtess vertex %02d, %s", os->m_curSegment, v->coord().toString().c_str());
145 }
146 os->m_array.put3f(v->coord());
147 if( os->useNormal() ) {
148 os->m_array.put3f(os->m_normal);
149 }
150 }
151 static void cbEndData( void *polygonData ) {
152 GLUtilTesselator* os = reinterpret_cast<GLUtilTesselator*>(polygonData);
153 Segment& s = os->m_segments.at(os->m_segments.size()-1);
154 s.count = castOrThrow<size_t, GLsizei>(os->m_array.elemCount() - s.first);
155 if( os->verbose() ) {
156 jau::INFO_PRINT("GLUtess end %02d, %s", os->m_curSegment, s.toString().c_str());
157 }
158 }
159 static void cbErrorData( GLenum errnum, void *polygonData ) {
160 GLUtilTesselator* os = reinterpret_cast<GLUtilTesselator*>(polygonData);
161 if( os->verbose() ) {
162 // jau::INFO_PRINT("GLUtess error %02d, errnum 0x%X, %s", os->m_curIndex, errnum, gluErrorString(errnum));
163 jau::INFO_PRINT("GLUtess error %02d, errnum 0x%X", os->m_curSegment, errnum);
164 }
165 }
166 static void cbCombineData( GLUTessFloat coords[3], void *data[4],
167 GLfloat weight[4], void **outData,
168 void *polygonData ) {
169 GLUtilTesselator* os = reinterpret_cast<GLUtilTesselator*>(polygonData);
170 if( os->verbose() ) {
171 jau::INFO_PRINT("GLUtess combine %02d, %f, %f, %f", os->m_curSegment, coords[0], coords[1], coords[2]);
172 // jau::INFO_PRINT("GLUtess combine %p, %p, %p, %p", data[0], data[1], data[2], data[3]);
173 }
174 std::shared_ptr<Vertex> v = std::make_shared<Vertex>((float)coords[0], (float)coords[1], (float)coords[2], true);
175 os->m_vcache.push_back(v);
176 for (int i = 0; i < 4; ++i) {
177 Vertex* s = (Vertex*) data[i];
178 if( !s ) { break; }
179 for (int j = 0; j < 3; ++j) {
180 // v->color()[j] += (float)( weight[i] * s->color()[j] );
181 v->texCoord()[j] += (float)( weight[i] * s->texCoord()[j] );
182 }
183 }
184 outData[0] = v.get();
185 }
186
187 public:
188 static SegmentList tesselate(int flags, GLFloatArrayDataServer& array, OutlineShape& outlines) {
189 GLUtilTesselator glutess(flags, array);
190 return glutess.tesselate(outlines);
191 }
193 const bool odirty = outlines.verticesDirty() || outlines.trianglesDirty();
194 size_t outlineCount = 0;
195 if( odirty || empty() ) {
196 clear();
197 GLUtesselator* tess = gluNewTess();
198 if( !tess ) { return m_segments; }
199 m_normal = outlines.normal();
200 if( verbose() ) {
201 jau::INFO_PRINT("GLUtess: normal: %s", m_normal.toString().c_str());
202 }
203 gluTessNormal(tess, m_normal.x, m_normal.y, m_normal.z);
204 gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (_GLUfuncptr)cbBeginData);
205 gluTessCallback(tess, GLU_TESS_END_DATA, (_GLUfuncptr)cbEndData);
206 gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (_GLUfuncptr)cbVertexData);
207 gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (_GLUfuncptr)cbCombineData);
208 gluTessCallback(tess, GLU_TESS_ERROR_DATA, (_GLUfuncptr)cbErrorData);
209 {
210 gluTessBeginPolygon(tess, this);
211 for(const Outline& o : outlines.outlines()) {
212 if( verbose() ) {
213 jau::INFO_PRINT("GLUtess: outline %zu: Vertices %zu, Winding %s", outlineCount, o.vertexCount(), to_string(o.getWinding()).c_str());
214 }
215 ++outlineCount;
216 gluTessBeginContour(tess);
217 if constexpr ( sizeof(GLfloat) == sizeof(GLUTessFloat) ) {
218 for(const Vertex& v : o.vertices()) {
219 gluTessVertex(tess, const_cast<GLfloat*>(v.coord().cbegin()), (void*)&v);
220 }
221 } else {
222 for(const Vertex& v : o.vertices()) {
223 GLUTessFloat coords[] = { v.coord().x, v.coord().y, v.coord().z };
224 gluTessVertex(tess, coords, (void*)&v);
225 }
226 }
227 gluTessEndContour(tess);
228 }
229 gluTessEndPolygon(tess);
230 }
231 gluDeleteTess(tess);
232 // FIXME outlines.markClean(OutlineShape::DirtyBits::vertices | OutlineShape::DirtyBits::triangles);
233 }
234 if( verbose() ) {
235 jau::INFO_PRINT("GLUtess: outlines: %zu", outlineCount);
236 jau::INFO_PRINT("GLUtess: segments: %zu", m_segments.size());
237 jau::INFO_PRINT("\n%s", Segment::toString("- ", m_segments).c_str() );
238 jau::INFO_PRINT("GLUtess: outline dirty: %d", odirty);
239 jau::INFO_PRINT("GLUtess: index next: %d", m_nextSegment);
240 jau::INFO_PRINT("GLUtess: vcache: %zu", m_vcache.size());
241 jau::INFO_PRINT("GLUtess: vertices: %s", m_array.toString().c_str());
242 }
243 return m_segments;
244 }
245 };
246
247
248 /**@}*/
249
250} // namespace gamp::graph::tess
251
252#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
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
constexpr bool useColor() const noexcept
const SegmentList & tesselate(OutlineShape &outlines)
constexpr const std::vector< Segment > & segments() const noexcept
constexpr bool useTexture() const noexcept
constexpr bool useNormal() const noexcept
std::string toString() const noexcept
Definition vec3f.hpp:236
GLArrayDataServer< float > GLFloatArrayDataServer
U castOrThrow(T has)
Definition GampTypes.hpp:70
uint_bytes_t< sizeof(unsigned long int)> nsize_t
Natural 'size_t' alternative using uint<XX>_t with xx = sizeof(unsigned long int)*8 as its natural si...
Definition int_types.hpp:85
Vector3F< float > Vec3f
Definition vec3f.hpp:422
std::string to_string(const math_error_t v) noexcept
Returns std::string representation of math_error_t.
constexpr std::string format_string_n(const std::size_t maxStrLen, const std::string_view &format, const Args &...args)
Safely returns a (potentially truncated) string according to snprintf() formatting rules and variable...
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:254
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.