Line data Source code
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 :
58 829 : static void CALLBACK mesh_tess_begin(GLenum which) {
59 : assert(which==GL_TRIANGLES);
60 829 : }
61 829 : static void CALLBACK mesh_tess_end(void) {
62 829 : }
63 0 : static void CALLBACK mesh_tess_error(GLenum error_code)
64 : {
65 0 : if (error_code)
66 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Mesh] Tesselate error %s\n", gluErrorString(error_code)));
67 0 : }
68 :
69 : /*only needed to force GL_TRIANGLES*/
70 69558 : static void CALLBACK mesh_tess_edgeflag(GLenum flag) { }
71 :
72 173142 : static void CALLBACK mesh_tess_vertex(void *vertexData, void *user_data)
73 : {
74 : MeshTess *tess = (MeshTess *) user_data;
75 173142 : mesh_set_index(tess->mesh, *(u32*)vertexData);
76 173142 : }
77 :
78 3499 : 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 3499 : 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 3499 : if (tess->mesh->flags & MESH_IS_2D) {
107 : n.z = FIX_ONE;
108 : } else {
109 4 : for (i=0; i<4; i++) {
110 4 : if (weight[i]) {
111 : Fixed _weight = FLT2FIX(weight[i]);
112 : SFVec3f _n;
113 2 : idx = * (u32 *) vertex_data[i];
114 2 : MESH_GET_NORMAL(_n, tess->mesh->vertices[idx]);
115 2 : n.x += gf_mulfix(_weight, _n.x);
116 2 : n.y += gf_mulfix(_weight, _n.y);
117 2 : n.z += gf_mulfix(_weight, _n.z);
118 : }
119 : }
120 : }
121 : tx.x = tx.y = 0;
122 3499 : if (!(tess->mesh->flags & MESH_NO_TEXTURE)) {
123 5836 : for (i=0; i<4; i++) {
124 5836 : if (weight[i]) {
125 : Fixed _weight = FLT2FIX(weight[i]);
126 4046 : idx = * (u32 *) vertex_data[i];
127 4046 : tx.x += gf_mulfix(_weight, tess->mesh->vertices[idx].texcoords.x);
128 4046 : tx.y += gf_mulfix(_weight, tess->mesh->vertices[idx].texcoords.y);
129 : }
130 : }
131 : }
132 :
133 3499 : new_idx = (u32 *) gf_malloc(sizeof(u32));
134 3499 : gf_list_add(tess->vertex_index, new_idx);
135 3499 : *new_idx = tess->mesh->v_count;
136 3499 : 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 3499 : *out_data = new_idx;
138 3499 : }
139 :
140 820 : 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 820 : if (!mesh || !path || !path->n_contours) return;
149 820 : tess = gf_malloc(sizeof(MeshTess));
150 820 : if (!tess) return;
151 : memset(tess, 0, sizeof(MeshTess));
152 820 : tess->tess_obj = gluNewTess();
153 820 : if (!tess->tess_obj) {
154 0 : gf_free(tess);
155 0 : return;
156 : }
157 820 : tess->vertex_index = gf_list_new();
158 :
159 820 : mesh_reset(mesh);
160 820 : mesh->flags |= MESH_IS_2D;
161 820 : if (outline_style==1) mesh->flags |= MESH_NO_TEXTURE;
162 :
163 820 : tess->mesh = mesh;
164 820 : gluTessCallback(tess->tess_obj, GLU_TESS_VERTEX_DATA, (void (CALLBACK*)()) &mesh_tess_vertex);
165 820 : gluTessCallback(tess->tess_obj, GLU_TESS_BEGIN, (void (CALLBACK*)()) &mesh_tess_begin);
166 820 : gluTessCallback(tess->tess_obj, GLU_TESS_END, (void (CALLBACK*)()) &mesh_tess_end);
167 820 : gluTessCallback(tess->tess_obj, GLU_TESS_COMBINE_DATA, (void (CALLBACK*)()) &mesh_tess_combine);
168 820 : gluTessCallback(tess->tess_obj, GLU_TESS_ERROR, (void (CALLBACK*)()) &mesh_tess_error);
169 820 : gluTessCallback(tess->tess_obj, GLU_EDGE_FLAG,(void (CALLBACK*)()) &mesh_tess_edgeflag);
170 :
171 820 : if (path->flags & GF_PATH_FILL_ZERO_NONZERO) gluTessProperty(tess->tess_obj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
172 :
173 820 : gluTessBeginPolygon(tess->tess_obj, tess);
174 820 : gluTessNormal(tess->tess_obj, 0, 0, 1);
175 :
176 820 : gf_path_flatten(path);
177 820 : gf_path_get_bounds(path, &rc);
178 :
179 820 : w = rc.width;
180 820 : h = rc.height;
181 820 : min_y = rc.y - h;
182 820 : vertex[2] = 0;
183 : /*since we're not sure whether subpaths overlaps or not, tesselate everything*/
184 : cur = 0;
185 2064 : for (i=0; i<path->n_contours; i++) {
186 1244 : nb_pts = 1+path->contours[i]-cur;
187 :
188 1244 : gluTessBeginContour(tess->tess_obj);
189 :
190 47282 : for (j=0; j<nb_pts; j++) {
191 46038 : GF_Point2D pt = path->points[cur+j];
192 46038 : Fixed u = gf_divfix(pt.x - rc.x, w);
193 46038 : Fixed v = gf_divfix(pt.y - min_y, h);
194 :
195 46038 : idx = (u32 *) gf_malloc(sizeof(u32));
196 46038 : *idx = mesh->v_count;
197 46038 : gf_list_add(tess->vertex_index, idx);
198 46038 : mesh_set_vertex(mesh, pt.x, pt.y, 0, 0, 0, FIX_ONE, u, v);
199 :
200 46038 : vertex[0] = (Double) FIX2FLT(pt.x);
201 46038 : vertex[1] = (Double) FIX2FLT(pt.y);
202 46038 : gluTessVertex(tess->tess_obj, vertex, idx);
203 : }
204 1244 : gluTessEndContour(tess->tess_obj);
205 1244 : cur+=nb_pts;
206 : }
207 :
208 820 : gluTessEndPolygon(tess->tess_obj);
209 :
210 820 : gluDeleteTess(tess->tess_obj);
211 :
212 51176 : while (gf_list_count(tess->vertex_index)) {
213 49536 : u32 *idx = gf_list_get(tess->vertex_index, 0);
214 49536 : gf_list_rem(tess->vertex_index, 0);
215 49536 : gf_free(idx);
216 : }
217 820 : gf_list_del(tess->vertex_index);
218 820 : gf_free(tess);
219 :
220 820 : mesh->bounds.min_edge.x = rc.x;
221 820 : mesh->bounds.min_edge.y = rc.y-rc.height;
222 820 : mesh->bounds.max_edge.x = rc.x+rc.width;
223 820 : mesh->bounds.max_edge.y = rc.y;
224 820 : mesh->bounds.min_edge.z = mesh->bounds.max_edge.z = 0;
225 820 : gf_bbox_refresh(&mesh->bounds);
226 :
227 : #ifdef GPAC_ENABLE_COVERAGE
228 820 : if (gf_sys_is_cov_mode()) {
229 : mesh_tess_error(0);
230 : }
231 : #endif
232 :
233 : }
234 :
235 : #else
236 :
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 :
284 13872 : 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 13872 : if (len<3) return GF_POLYGON_CONVEX_LINE;
293 :
294 : pSecond.x = pSecond.y = 0;
295 : pThird = pSecond;
296 :
297 13872 : GetPoint2D(pThird, pts[0]);
298 : iread = 1;
299 13872 : ConvGetPointDelta(dprev, pThird, pSecond);
300 : pSaveSecond = pSecond;
301 : /*initial direction */
302 13872 : curDir = ConvCompare(dprev);
303 27995 : while ( iread < len) {
304 : /* Get different point, break if no more points */
305 14132 : ConvGetPointDelta(dcur, pSecond, pThird );
306 14132 : if ( (dcur.x == 0) && (dcur.y == 0) ) continue;
307 : /* Check current three points */
308 14132 : ConvCheckTriple;
309 : }
310 :
311 : /* Must check for direction changes from last vertex back to first */
312 : /* Prepare for 'ConvexCheckTriple' */
313 13863 : GetPoint2D(pThird, pts[0]);
314 13863 : dcur.x = pThird.x - pSecond.x;
315 13863 : dcur.y = pThird.y - pSecond.y;
316 13863 : if ( ConvCompare(dcur) ) {
317 13856 : ConvCheckTriple;
318 : }
319 : /* and check for direction changes back to second vertex */
320 13863 : dcur.x = pSaveSecond.x - pSecond.x;
321 13863 : dcur.y = pSaveSecond.y - pSecond.y;
322 : /* Don't care about 'pThird' now */
323 13863 : ConvCheckTriple;
324 :
325 : /* Decide on polygon type given accumulated status */
326 13863 : if ( dirChanges > 2 ) return GF_POLYGON_COMPLEX;
327 13863 : if ( angleSign > 0 ) return GF_POLYGON_CONVEX_CCW;
328 5290 : if ( angleSign < 0 ) return GF_POLYGON_CONVEX_CW;
329 0 : return GF_POLYGON_CONVEX_LINE;
330 : }
331 :
332 :
333 13872 : 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 13872 : if (orig->flags & MESH_IS_2D) {
346 32 : nor.x = nor.y = 0;
347 32 : nor.z = FIX_ONE;
348 : } else {
349 13840 : MESH_GET_NORMAL(nor, orig->vertices[0]);
350 : }
351 :
352 : /*select projection direction*/
353 : direction = 0;
354 13872 : max_nor_coord = ABS(nor.x);
355 13872 : c = ABS(nor.y);
356 13872 : if (c>max_nor_coord) {
357 : direction = 1;
358 : max_nor_coord = c;
359 : }
360 13872 : c = ABS(nor.z);
361 13872 : if (c>max_nor_coord) direction = 2;
362 :
363 : /*if this is a convex polygone don't triangulate*/
364 13872 : poly_type = polygon_check_convexity(orig->vertices, orig->v_count, direction);
365 13872 : switch (poly_type) {
366 13863 : 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 13863 : init_idx = dest->v_count;
371 : nb_pts = orig->v_count;
372 55682 : for (i=0; i<nb_pts; i++) {
373 41819 : mesh_set_vertex_vx(dest, &orig->vertices[i]);
374 : }
375 13863 : nb_pts -= 1;
376 27956 : for (i=1; i<nb_pts; i++) {
377 14093 : mesh_set_triangle(dest, init_idx, init_idx + i, init_idx + i+1);
378 : }
379 13863 : return;
380 : default:
381 : break;
382 : }
383 :
384 : #ifdef GPAC_HAS_GLU
385 :
386 : /*tesselate it*/
387 9 : tess = gf_malloc(sizeof(MeshTess));
388 9 : if (!tess) return;
389 : memset(tess, 0, sizeof(MeshTess));
390 9 : tess->tess_obj = gluNewTess();
391 9 : if (!tess->tess_obj) {
392 0 : gf_free(tess);
393 0 : return;
394 : }
395 9 : tess->vertex_index = gf_list_new();
396 :
397 9 : tess->mesh = dest;
398 9 : gluTessCallback(tess->tess_obj, GLU_TESS_VERTEX_DATA, (void (CALLBACK*)()) &mesh_tess_vertex);
399 9 : gluTessCallback(tess->tess_obj, GLU_TESS_BEGIN, (void (CALLBACK*)()) &mesh_tess_begin);
400 9 : gluTessCallback(tess->tess_obj, GLU_TESS_END, (void (CALLBACK*)()) &mesh_tess_end);
401 9 : gluTessCallback(tess->tess_obj, GLU_TESS_COMBINE_DATA, (void (CALLBACK*)()) &mesh_tess_combine);
402 9 : gluTessCallback(tess->tess_obj, GLU_TESS_ERROR, (void (CALLBACK*)()) &mesh_tess_error);
403 9 : gluTessCallback(tess->tess_obj, GLU_EDGE_FLAG,(void (CALLBACK*)()) &mesh_tess_edgeflag);
404 :
405 9 : gluTessBeginPolygon(tess->tess_obj, tess);
406 9 : gluTessBeginContour(tess->tess_obj);
407 :
408 :
409 94 : for (i=0; i<orig->v_count; i++) {
410 85 : idx = (u32 *) gf_malloc(sizeof(u32));
411 85 : *idx = dest->v_count;
412 85 : gf_list_add(tess->vertex_index, idx);
413 85 : mesh_set_vertex_vx(dest, &orig->vertices[i]);
414 :
415 85 : vertex[0] = (Double) FIX2FLT(orig->vertices[i].pos.x);
416 85 : vertex[1] = (Double) FIX2FLT(orig->vertices[i].pos.y);
417 85 : vertex[2] = (Double) FIX2FLT(orig->vertices[i].pos.z);
418 85 : gluTessVertex(tess->tess_obj, vertex, idx);
419 : }
420 :
421 9 : gluTessEndContour(tess->tess_obj);
422 9 : gluTessEndPolygon(tess->tess_obj);
423 9 : gluDeleteTess(tess->tess_obj);
424 :
425 104 : while (gf_list_count(tess->vertex_index)) {
426 86 : u32 *idx = gf_list_get(tess->vertex_index, 0);
427 86 : gf_list_rem(tess->vertex_index, 0);
428 86 : gf_free(idx);
429 : }
430 9 : gf_list_del(tess->vertex_index);
431 9 : gf_free(tess);
432 : #endif
433 : }
434 :
435 :
436 :
437 : #ifdef GPAC_HAS_GLU
438 :
439 0 : 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 0 : tess = gf_malloc(sizeof(MeshTess));
448 0 : if (!tess) return;
449 : memset(tess, 0, sizeof(MeshTess));
450 0 : tess->tess_obj = gluNewTess();
451 0 : if (!tess->tess_obj) {
452 0 : gf_free(tess);
453 0 : return;
454 : }
455 0 : tess->vertex_index = gf_list_new();
456 :
457 0 : tess->mesh = dest;
458 0 : gluTessCallback(tess->tess_obj, GLU_TESS_VERTEX_DATA, (void (CALLBACK*)()) &mesh_tess_vertex);
459 0 : gluTessCallback(tess->tess_obj, GLU_TESS_BEGIN, (void (CALLBACK*)()) &mesh_tess_begin);
460 0 : gluTessCallback(tess->tess_obj, GLU_TESS_END, (void (CALLBACK*)()) &mesh_tess_end);
461 0 : gluTessCallback(tess->tess_obj, GLU_TESS_COMBINE_DATA, (void (CALLBACK*)()) &mesh_tess_combine);
462 0 : gluTessCallback(tess->tess_obj, GLU_TESS_ERROR, (void (CALLBACK*)()) &mesh_tess_error);
463 0 : gluTessCallback(tess->tess_obj, GLU_EDGE_FLAG,(void (CALLBACK*)()) &mesh_tess_edgeflag);
464 :
465 0 : gluTessBeginPolygon(tess->tess_obj, tess);
466 0 : gluTessBeginContour(tess->tess_obj);
467 :
468 :
469 : cur_pt_faces = 0;
470 : cur_face = 0;
471 0 : for (i=0; i<orig->v_count; i++) {
472 :
473 0 : if (i>= cur_pt_faces + ptsPerFaces[cur_face]) {
474 : cur_pt_faces += ptsPerFaces[cur_face];
475 0 : cur_face++;
476 0 : if (cur_face>=nbFaces) break;
477 0 : gluTessEndContour(tess->tess_obj);
478 0 : gluTessBeginContour(tess->tess_obj);
479 : }
480 :
481 0 : idx = (u32 *) gf_malloc(sizeof(u32));
482 0 : *idx = dest->v_count;
483 0 : gf_list_add(tess->vertex_index, idx);
484 0 : mesh_set_vertex_vx(dest, &orig->vertices[i]);
485 :
486 0 : vertex[0] = (Double) FIX2FLT(orig->vertices[i].pos.x);
487 0 : vertex[1] = (Double) FIX2FLT(orig->vertices[i].pos.y);
488 0 : vertex[2] = (Double) FIX2FLT(orig->vertices[i].pos.z);
489 0 : gluTessVertex(tess->tess_obj, vertex, idx);
490 : }
491 :
492 0 : gluTessEndContour(tess->tess_obj);
493 0 : gluTessEndPolygon(tess->tess_obj);
494 0 : gluDeleteTess(tess->tess_obj);
495 :
496 0 : while (gf_list_count(tess->vertex_index)) {
497 0 : u32 *idx = gf_list_get(tess->vertex_index, 0);
498 0 : gf_list_rem(tess->vertex_index, 0);
499 0 : gf_free(idx);
500 : }
501 0 : gf_list_del(tess->vertex_index);
502 0 : gf_free(tess);
503 : }
504 : #endif
505 :
506 :
507 : #endif /*GPAC_DISABLE_3D*/
|