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