1 /*
2 * GPAC - Multimedia Framework C SDK
3 *
4 * Authors: Jean Le Feuvre
5 * Copyright (c) Telecom ParisTech 2000-2012
6 * All rights reserved
7 *
8 * This file is part of GPAC / Scene Compositor sub-project
9 *
10 * GPAC is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
14 *
15 * GPAC is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; see the file COPYING. If not, write to
22 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25
26
27 #include <gpac/internal/mesh.h>
28 #include <gpac/color.h>
29
30 #ifndef GPAC_DISABLE_3D
31
32 /*for GPAC_HAS_GLU*/
33 #include "gl_inc.h"
34
35 #ifndef CALLBACK
36 #define CALLBACK
37 #endif
38
39
40 #ifdef GPAC_HAS_GLU
41
42 #ifdef GPAC_CONFIG_IOS
43 #define GLdouble GLfloat
44 #endif
45
46
47 typedef struct
48 {
49 /*for tesselation*/
50 GLUtesselator *tess_obj;
51 GF_Mesh *mesh;
52
53 /*vertex indices: we cannot use a static array because reallocating the array will likely change memory
54 address of indices, hence break triangulator*/
55 GF_List *vertex_index;
56 } MeshTess;
57
mesh_tess_begin(GLenum which)58 static void CALLBACK mesh_tess_begin(GLenum which) {
59 assert(which==GL_TRIANGLES);
60 }
mesh_tess_end(void)61 static void CALLBACK mesh_tess_end(void) {
62 }
mesh_tess_error(GLenum error_code)63 static void CALLBACK mesh_tess_error(GLenum error_code)
64 {
65 if (error_code)
66 GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Mesh] Tesselate error %s\n", gluErrorString(error_code)));
67 }
68
69 /*only needed to force GL_TRIANGLES*/
mesh_tess_edgeflag(GLenum flag)70 static void CALLBACK mesh_tess_edgeflag(GLenum flag) { }
71
mesh_tess_vertex(void * vertexData,void * user_data)72 static void CALLBACK mesh_tess_vertex(void *vertexData, void *user_data)
73 {
74 MeshTess *tess = (MeshTess *) user_data;
75 mesh_set_index(tess->mesh, *(u32*)vertexData);
76 }
77
mesh_tess_combine(GLdouble coords[3],void * vertex_data[4],GLfloat weight[4],void ** out_data,void * user_data)78 static void CALLBACK mesh_tess_combine(GLdouble coords[3], void* vertex_data[4], GLfloat weight[4], void** out_data, void *user_data)
79 {
80 u32 i, idx;
81 u32 *new_idx;
82 SFVec3f n;
83 SFVec2f tx;
84 SFColor col;
85
86 MeshTess *tess = (MeshTess *) user_data;
87
88 col.red = col.green = col.blue = 0;
89 if (tess->mesh->flags & MESH_HAS_COLOR) {
90 for (i=0; i<4; i++) {
91 if (weight[i]) {
92 SFColorRGBA rgba;
93 Fixed _weight = FLT2FIX(weight[i]);
94 idx = * (u32 *) vertex_data[i];
95
96 MESH_GET_COLOR(rgba, tess->mesh->vertices[idx]);
97
98 col.red += gf_mulfix(_weight, rgba.red);
99 col.green += gf_mulfix(_weight, rgba.green);
100 col.blue += gf_mulfix(_weight, rgba.blue);
101 }
102 }
103 }
104
105 n.x = n.y = n.z = 0;
106 if (tess->mesh->flags & MESH_IS_2D) {
107 n.z = FIX_ONE;
108 } else {
109 for (i=0; i<4; i++) {
110 if (weight[i]) {
111 Fixed _weight = FLT2FIX(weight[i]);
112 SFVec3f _n;
113 idx = * (u32 *) vertex_data[i];
114 MESH_GET_NORMAL(_n, tess->mesh->vertices[idx]);
115 n.x += gf_mulfix(_weight, _n.x);
116 n.y += gf_mulfix(_weight, _n.y);
117 n.z += gf_mulfix(_weight, _n.z);
118 }
119 }
120 }
121 tx.x = tx.y = 0;
122 if (!(tess->mesh->flags & MESH_NO_TEXTURE)) {
123 for (i=0; i<4; i++) {
124 if (weight[i]) {
125 Fixed _weight = FLT2FIX(weight[i]);
126 idx = * (u32 *) vertex_data[i];
127 tx.x += gf_mulfix(_weight, tess->mesh->vertices[idx].texcoords.x);
128 tx.y += gf_mulfix(_weight, tess->mesh->vertices[idx].texcoords.y);
129 }
130 }
131 }
132
133 new_idx = (u32 *) gf_malloc(sizeof(u32));
134 gf_list_add(tess->vertex_index, new_idx);
135 *new_idx = tess->mesh->v_count;
136 mesh_set_vertex(tess->mesh, FLT2FIX( (Float) coords[0]), FLT2FIX( (Float) coords[1]), FLT2FIX( (Float) coords[2]), n.x, n.y, n.z, tx.x, tx.y);
137 *out_data = new_idx;
138 }
139
gf_mesh_tesselate_path(GF_Mesh * mesh,GF_Path * path,u32 outline_style)140 void gf_mesh_tesselate_path(GF_Mesh *mesh, GF_Path *path, u32 outline_style)
141 {
142 u32 i, j, cur, nb_pts;
143 u32 *idx;
144 Fixed w, h, min_y;
145 GF_Rect rc;
146 GLdouble vertex[3];
147 MeshTess *tess;
148 if (!mesh || !path || !path->n_contours) return;
149 tess = gf_malloc(sizeof(MeshTess));
150 if (!tess) return;
151 memset(tess, 0, sizeof(MeshTess));
152 tess->tess_obj = gluNewTess();
153 if (!tess->tess_obj) {
154 gf_free(tess);
155 return;
156 }
157 tess->vertex_index = gf_list_new();
158
159 mesh_reset(mesh);
160 mesh->flags |= MESH_IS_2D;
161 if (outline_style==1) mesh->flags |= MESH_NO_TEXTURE;
162
163 tess->mesh = mesh;
164 gluTessCallback(tess->tess_obj, GLU_TESS_VERTEX_DATA, (void (CALLBACK*)()) &mesh_tess_vertex);
165 gluTessCallback(tess->tess_obj, GLU_TESS_BEGIN, (void (CALLBACK*)()) &mesh_tess_begin);
166 gluTessCallback(tess->tess_obj, GLU_TESS_END, (void (CALLBACK*)()) &mesh_tess_end);
167 gluTessCallback(tess->tess_obj, GLU_TESS_COMBINE_DATA, (void (CALLBACK*)()) &mesh_tess_combine);
168 gluTessCallback(tess->tess_obj, GLU_TESS_ERROR, (void (CALLBACK*)()) &mesh_tess_error);
169 gluTessCallback(tess->tess_obj, GLU_EDGE_FLAG,(void (CALLBACK*)()) &mesh_tess_edgeflag);
170
171 if (path->flags & GF_PATH_FILL_ZERO_NONZERO) gluTessProperty(tess->tess_obj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
172
173 gluTessBeginPolygon(tess->tess_obj, tess);
174 gluTessNormal(tess->tess_obj, 0, 0, 1);
175
176 gf_path_flatten(path);
177 gf_path_get_bounds(path, &rc);
178
179 w = rc.width;
180 h = rc.height;
181 min_y = rc.y - h;
182 vertex[2] = 0;
183 /*since we're not sure whether subpaths overlaps or not, tesselate everything*/
184 cur = 0;
185 for (i=0; i<path->n_contours; i++) {
186 nb_pts = 1+path->contours[i]-cur;
187
188 gluTessBeginContour(tess->tess_obj);
189
190 for (j=0; j<nb_pts; j++) {
191 GF_Point2D pt = path->points[cur+j];
192 Fixed u = gf_divfix(pt.x - rc.x, w);
193 Fixed v = gf_divfix(pt.y - min_y, h);
194
195 idx = (u32 *) gf_malloc(sizeof(u32));
196 *idx = mesh->v_count;
197 gf_list_add(tess->vertex_index, idx);
198 mesh_set_vertex(mesh, pt.x, pt.y, 0, 0, 0, FIX_ONE, u, v);
199
200 vertex[0] = (Double) FIX2FLT(pt.x);
201 vertex[1] = (Double) FIX2FLT(pt.y);
202 gluTessVertex(tess->tess_obj, vertex, idx);
203 }
204 gluTessEndContour(tess->tess_obj);
205 cur+=nb_pts;
206 }
207
208 gluTessEndPolygon(tess->tess_obj);
209
210 gluDeleteTess(tess->tess_obj);
211
212 while (gf_list_count(tess->vertex_index)) {
213 u32 *idx = gf_list_get(tess->vertex_index, 0);
214 gf_list_rem(tess->vertex_index, 0);
215 gf_free(idx);
216 }
217 gf_list_del(tess->vertex_index);
218 gf_free(tess);
219
220 mesh->bounds.min_edge.x = rc.x;
221 mesh->bounds.min_edge.y = rc.y-rc.height;
222 mesh->bounds.max_edge.x = rc.x+rc.width;
223 mesh->bounds.max_edge.y = rc.y;
224 mesh->bounds.min_edge.z = mesh->bounds.max_edge.z = 0;
225 gf_bbox_refresh(&mesh->bounds);
226
227 #ifdef GPAC_ENABLE_COVERAGE
228 if (gf_sys_is_cov_mode()) {
229 mesh_tess_error(0);
230 }
231 #endif
232
233 }
234
235 #else
236
gf_mesh_tesselate_path(GF_Mesh * mesh,GF_Path * path,u32 outline_style)237 void gf_mesh_tesselate_path(GF_Mesh *mesh, GF_Path *path, u32 outline_style) { }
238
239 #endif
240
241
242
243 #define GetPoint2D(pt, apt) \
244 if (!direction) { pt.x = - apt.pos.z; pt.y = apt.pos.y; } \
245 else if (direction==1) { pt.x = apt.pos.z; pt.y = apt.pos.x; } \
246 else if (direction==2) { pt.x = apt.pos.x; pt.y = apt.pos.y; } \
247
248
249 #define ConvCompare(delta) \
250 ( (delta.x > 0) ? -1 : \
251 (delta.x < 0) ? 1 : \
252 (delta.y > 0) ? -1 : \
253 (delta.y < 0) ? 1 : \
254 0 )
255
256 #define ConvGetPointDelta(delta, pprev, pcur ) \
257 /* Given a previous point 'pprev', read a new point into 'pcur' */ \
258 /* and return delta in 'delta'. */ \
259 GetPoint2D(pcur, pts[iread]); iread++; \
260 delta.x = pcur.x - pprev.x; \
261 delta.y = pcur.y - pprev.y; \
262
263 #define ConvCross(p, q) gf_mulfix(p.x,q.y) - gf_mulfix(p.y,q.x);
264
265 #define ConvCheckTriple \
266 if ( (thisDir = ConvCompare(dcur)) == -curDir ) { \
267 ++dirChanges; \
268 /* if ( dirChanges > 2 ) return NotConvex; */ \
269 } \
270 curDir = thisDir; \
271 cross = ConvCross(dprev, dcur); \
272 if ( cross > 0 ) { \
273 if ( angleSign == -1 ) return GF_POLYGON_COMPLEX; \
274 angleSign = 1; \
275 } \
276 else if (cross < 0) { \
277 if (angleSign == 1) return GF_POLYGON_COMPLEX; \
278 angleSign = -1; \
279 } \
280 pSecond = pThird; \
281 dprev.x = dcur.x; \
282 dprev.y = dcur.y; \
283
polygon_check_convexity(GF_Vertex * pts,u32 len,u32 direction)284 u32 polygon_check_convexity(GF_Vertex *pts, u32 len, u32 direction)
285 {
286 s32 curDir, thisDir = 0, dirChanges = 0, angleSign = 0;
287 u32 iread;
288 Fixed cross;
289 GF_Point2D pSecond, pThird, pSaveSecond;
290 GF_Point2D dprev, dcur;
291
292 if (len<3) return GF_POLYGON_CONVEX_LINE;
293
294 pSecond.x = pSecond.y = 0;
295 pThird = pSecond;
296
297 GetPoint2D(pThird, pts[0]);
298 iread = 1;
299 ConvGetPointDelta(dprev, pThird, pSecond);
300 pSaveSecond = pSecond;
301 /*initial direction */
302 curDir = ConvCompare(dprev);
303 while ( iread < len) {
304 /* Get different point, break if no more points */
305 ConvGetPointDelta(dcur, pSecond, pThird );
306 if ( (dcur.x == 0) && (dcur.y == 0) ) continue;
307 /* Check current three points */
308 ConvCheckTriple;
309 }
310
311 /* Must check for direction changes from last vertex back to first */
312 /* Prepare for 'ConvexCheckTriple' */
313 GetPoint2D(pThird, pts[0]);
314 dcur.x = pThird.x - pSecond.x;
315 dcur.y = pThird.y - pSecond.y;
316 if ( ConvCompare(dcur) ) {
317 ConvCheckTriple;
318 }
319 /* and check for direction changes back to second vertex */
320 dcur.x = pSaveSecond.x - pSecond.x;
321 dcur.y = pSaveSecond.y - pSecond.y;
322 /* Don't care about 'pThird' now */
323 ConvCheckTriple;
324
325 /* Decide on polygon type given accumulated status */
326 if ( dirChanges > 2 ) return GF_POLYGON_COMPLEX;
327 if ( angleSign > 0 ) return GF_POLYGON_CONVEX_CCW;
328 if ( angleSign < 0 ) return GF_POLYGON_CONVEX_CW;
329 return GF_POLYGON_CONVEX_LINE;
330 }
331
332
TesselateFaceMesh(GF_Mesh * dest,GF_Mesh * orig)333 void TesselateFaceMesh(GF_Mesh *dest, GF_Mesh *orig)
334 {
335 u32 poly_type, i, nb_pts, init_idx, direction;
336 Fixed max_nor_coord, c;
337 SFVec3f nor;
338 #ifdef GPAC_HAS_GLU
339 u32 *idx;
340 GLdouble vertex[3];
341 MeshTess *tess;
342 #endif
343
344 /*get normal*/
345 if (orig->flags & MESH_IS_2D) {
346 nor.x = nor.y = 0;
347 nor.z = FIX_ONE;
348 } else {
349 MESH_GET_NORMAL(nor, orig->vertices[0]);
350 }
351
352 /*select projection direction*/
353 direction = 0;
354 max_nor_coord = ABS(nor.x);
355 c = ABS(nor.y);
356 if (c>max_nor_coord) {
357 direction = 1;
358 max_nor_coord = c;
359 }
360 c = ABS(nor.z);
361 if (c>max_nor_coord) direction = 2;
362
363 /*if this is a convex polygone don't triangulate*/
364 poly_type = polygon_check_convexity(orig->vertices, orig->v_count, direction);
365 switch (poly_type) {
366 case GF_POLYGON_CONVEX_LINE:
367 /*do NOT try to make face CCW otherwise we loose front/back faces...*/
368 case GF_POLYGON_CONVEX_CW:
369 case GF_POLYGON_CONVEX_CCW:
370 init_idx = dest->v_count;
371 nb_pts = orig->v_count;
372 for (i=0; i<nb_pts; i++) {
373 mesh_set_vertex_vx(dest, &orig->vertices[i]);
374 }
375 nb_pts -= 1;
376 for (i=1; i<nb_pts; i++) {
377 mesh_set_triangle(dest, init_idx, init_idx + i, init_idx + i+1);
378 }
379 return;
380 default:
381 break;
382 }
383
384 #ifdef GPAC_HAS_GLU
385
386 /*tesselate it*/
387 tess = gf_malloc(sizeof(MeshTess));
388 if (!tess) return;
389 memset(tess, 0, sizeof(MeshTess));
390 tess->tess_obj = gluNewTess();
391 if (!tess->tess_obj) {
392 gf_free(tess);
393 return;
394 }
395 tess->vertex_index = gf_list_new();
396
397 tess->mesh = dest;
398 gluTessCallback(tess->tess_obj, GLU_TESS_VERTEX_DATA, (void (CALLBACK*)()) &mesh_tess_vertex);
399 gluTessCallback(tess->tess_obj, GLU_TESS_BEGIN, (void (CALLBACK*)()) &mesh_tess_begin);
400 gluTessCallback(tess->tess_obj, GLU_TESS_END, (void (CALLBACK*)()) &mesh_tess_end);
401 gluTessCallback(tess->tess_obj, GLU_TESS_COMBINE_DATA, (void (CALLBACK*)()) &mesh_tess_combine);
402 gluTessCallback(tess->tess_obj, GLU_TESS_ERROR, (void (CALLBACK*)()) &mesh_tess_error);
403 gluTessCallback(tess->tess_obj, GLU_EDGE_FLAG,(void (CALLBACK*)()) &mesh_tess_edgeflag);
404
405 gluTessBeginPolygon(tess->tess_obj, tess);
406 gluTessBeginContour(tess->tess_obj);
407
408
409 for (i=0; i<orig->v_count; i++) {
410 idx = (u32 *) gf_malloc(sizeof(u32));
411 *idx = dest->v_count;
412 gf_list_add(tess->vertex_index, idx);
413 mesh_set_vertex_vx(dest, &orig->vertices[i]);
414
415 vertex[0] = (Double) FIX2FLT(orig->vertices[i].pos.x);
416 vertex[1] = (Double) FIX2FLT(orig->vertices[i].pos.y);
417 vertex[2] = (Double) FIX2FLT(orig->vertices[i].pos.z);
418 gluTessVertex(tess->tess_obj, vertex, idx);
419 }
420
421 gluTessEndContour(tess->tess_obj);
422 gluTessEndPolygon(tess->tess_obj);
423 gluDeleteTess(tess->tess_obj);
424
425 while (gf_list_count(tess->vertex_index)) {
426 u32 *idx = gf_list_get(tess->vertex_index, 0);
427 gf_list_rem(tess->vertex_index, 0);
428 gf_free(idx);
429 }
430 gf_list_del(tess->vertex_index);
431 gf_free(tess);
432 #endif
433 }
434
435
436
437 #ifdef GPAC_HAS_GLU
438
TesselateFaceMeshComplex(GF_Mesh * dest,GF_Mesh * orig,u32 nbFaces,u32 * ptsPerFaces)439 void TesselateFaceMeshComplex(GF_Mesh *dest, GF_Mesh *orig, u32 nbFaces, u32 *ptsPerFaces)
440 {
441 u32 i, cur_pt_faces, cur_face;
442 u32 *idx;
443 GLdouble vertex[3];
444 MeshTess *tess;
445
446 /*tesselate it*/
447 tess = gf_malloc(sizeof(MeshTess));
448 if (!tess) return;
449 memset(tess, 0, sizeof(MeshTess));
450 tess->tess_obj = gluNewTess();
451 if (!tess->tess_obj) {
452 gf_free(tess);
453 return;
454 }
455 tess->vertex_index = gf_list_new();
456
457 tess->mesh = dest;
458 gluTessCallback(tess->tess_obj, GLU_TESS_VERTEX_DATA, (void (CALLBACK*)()) &mesh_tess_vertex);
459 gluTessCallback(tess->tess_obj, GLU_TESS_BEGIN, (void (CALLBACK*)()) &mesh_tess_begin);
460 gluTessCallback(tess->tess_obj, GLU_TESS_END, (void (CALLBACK*)()) &mesh_tess_end);
461 gluTessCallback(tess->tess_obj, GLU_TESS_COMBINE_DATA, (void (CALLBACK*)()) &mesh_tess_combine);
462 gluTessCallback(tess->tess_obj, GLU_TESS_ERROR, (void (CALLBACK*)()) &mesh_tess_error);
463 gluTessCallback(tess->tess_obj, GLU_EDGE_FLAG,(void (CALLBACK*)()) &mesh_tess_edgeflag);
464
465 gluTessBeginPolygon(tess->tess_obj, tess);
466 gluTessBeginContour(tess->tess_obj);
467
468
469 cur_pt_faces = 0;
470 cur_face = 0;
471 for (i=0; i<orig->v_count; i++) {
472
473 if (i>= cur_pt_faces + ptsPerFaces[cur_face]) {
474 cur_pt_faces += ptsPerFaces[cur_face];
475 cur_face++;
476 if (cur_face>=nbFaces) break;
477 gluTessEndContour(tess->tess_obj);
478 gluTessBeginContour(tess->tess_obj);
479 }
480
481 idx = (u32 *) gf_malloc(sizeof(u32));
482 *idx = dest->v_count;
483 gf_list_add(tess->vertex_index, idx);
484 mesh_set_vertex_vx(dest, &orig->vertices[i]);
485
486 vertex[0] = (Double) FIX2FLT(orig->vertices[i].pos.x);
487 vertex[1] = (Double) FIX2FLT(orig->vertices[i].pos.y);
488 vertex[2] = (Double) FIX2FLT(orig->vertices[i].pos.z);
489 gluTessVertex(tess->tess_obj, vertex, idx);
490 }
491
492 gluTessEndContour(tess->tess_obj);
493 gluTessEndPolygon(tess->tess_obj);
494 gluDeleteTess(tess->tess_obj);
495
496 while (gf_list_count(tess->vertex_index)) {
497 u32 *idx = gf_list_get(tess->vertex_index, 0);
498 gf_list_rem(tess->vertex_index, 0);
499 gf_free(idx);
500 }
501 gf_list_del(tess->vertex_index);
502 gf_free(tess);
503 }
504 #endif
505
506
507 #endif /*GPAC_DISABLE_3D*/
508