This source file includes following definitions.
- mesh_tess_begin
- mesh_tess_end
- mesh_tess_error
- mesh_tess_edgeflag
- mesh_tess_vertex
- mesh_tess_combine
- gf_mesh_tesselate_path
- gf_mesh_tesselate_path
- polygon_check_convexity
- TesselateFaceMesh
- TesselateFaceMeshComplex
#include <gpac/internal/mesh.h>
#include <gpac/color.h>
#ifndef GPAC_DISABLE_3D
#include "gl_inc.h"
#ifndef CALLBACK
#define CALLBACK
#endif
#ifdef GPAC_HAS_GLU
#ifdef GPAC_IPHONE
#define GLdouble GLfloat
#endif
typedef struct
{
GLUtesselator *tess_obj;
GF_Mesh *mesh;
GF_List *vertex_index;
} MeshTess;
static void CALLBACK mesh_tess_begin(GLenum which) {
assert(which==GL_TRIANGLES);
}
static void CALLBACK mesh_tess_end(void) { }
static void CALLBACK mesh_tess_error(GLenum error_code) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Mesh] Tesselate error %s\n", gluErrorString(error_code)));
}
static void CALLBACK mesh_tess_edgeflag(GLenum flag) { }
static void CALLBACK mesh_tess_vertex(void *vertexData, void *user_data)
{
MeshTess *tess = (MeshTess *) user_data;
mesh_set_index(tess->mesh, *(u32*)vertexData);
}
static void CALLBACK mesh_tess_combine(GLdouble coords[3], void* vertex_data[4], GLfloat weight[4], void** out_data, void *user_data)
{
u32 i, idx;
u32 *new_idx;
SFVec3f n;
SFVec2f tx;
SFColor col;
MeshTess *tess = (MeshTess *) user_data;
col.red = col.green = col.blue = 0;
if (tess->mesh->flags & MESH_HAS_COLOR) {
for (i=0; i<4; i++) {
if (weight[i]) {
SFColorRGBA rgba;
Fixed _weight = FLT2FIX(weight[i]);
idx = * (u32 *) vertex_data[i];
MESH_GET_COLOR(rgba, tess->mesh->vertices[idx]);
col.red += gf_mulfix(_weight, rgba.red);
col.green += gf_mulfix(_weight, rgba.green);
col.blue += gf_mulfix(_weight, rgba.blue);
}
}
}
n.x = n.y = n.z = 0;
if (tess->mesh->flags & MESH_IS_2D) {
n.z = FIX_ONE;
} else {
for (i=0; i<4; i++) {
if (weight[i]) {
Fixed _weight = FLT2FIX(weight[i]);
SFVec3f _n;
idx = * (u32 *) vertex_data[i];
MESH_GET_NORMAL(_n, tess->mesh->vertices[idx]);
n.x += gf_mulfix(_weight, _n.x);
n.y += gf_mulfix(_weight, _n.y);
n.z += gf_mulfix(_weight, _n.z);
}
}
}
tx.x = tx.y = 0;
if (!(tess->mesh->flags & MESH_NO_TEXTURE)) {
for (i=0; i<4; i++) {
if (weight[i]) {
Fixed _weight = FLT2FIX(weight[i]);
idx = * (u32 *) vertex_data[i];
tx.x += gf_mulfix(_weight, tess->mesh->vertices[idx].texcoords.x);
tx.y += gf_mulfix(_weight, tess->mesh->vertices[idx].texcoords.y);
}
}
}
new_idx = (u32 *) gf_malloc(sizeof(u32));
gf_list_add(tess->vertex_index, new_idx);
*new_idx = tess->mesh->v_count;
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);
*out_data = new_idx;
}
void gf_mesh_tesselate_path(GF_Mesh *mesh, GF_Path *path, u32 outline_style)
{
u32 i, j, cur, nb_pts;
u32 *idx;
Fixed w, h, min_y;
GF_Rect rc;
GLdouble vertex[3];
MeshTess *tess;
if (!mesh || !path || !path->n_contours) return;
tess = gf_malloc(sizeof(MeshTess));
if (!tess) return;
memset(tess, 0, sizeof(MeshTess));
tess->tess_obj = gluNewTess();
if (!tess->tess_obj) {
gf_free(tess);
return;
}
tess->vertex_index = gf_list_new();
mesh_reset(mesh);
mesh->flags |= MESH_IS_2D;
if (outline_style==1) mesh->flags |= MESH_NO_TEXTURE;
tess->mesh = mesh;
gluTessCallback(tess->tess_obj, GLU_TESS_VERTEX_DATA, (void (CALLBACK*)()) &mesh_tess_vertex);
gluTessCallback(tess->tess_obj, GLU_TESS_BEGIN, (void (CALLBACK*)()) &mesh_tess_begin);
gluTessCallback(tess->tess_obj, GLU_TESS_END, (void (CALLBACK*)()) &mesh_tess_end);
gluTessCallback(tess->tess_obj, GLU_TESS_COMBINE_DATA, (void (CALLBACK*)()) &mesh_tess_combine);
gluTessCallback(tess->tess_obj, GLU_TESS_ERROR, (void (CALLBACK*)()) &mesh_tess_error);
gluTessCallback(tess->tess_obj, GLU_EDGE_FLAG,(void (CALLBACK*)()) &mesh_tess_edgeflag);
if (path->flags & GF_PATH_FILL_ZERO_NONZERO) gluTessProperty(tess->tess_obj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
gluTessBeginPolygon(tess->tess_obj, tess);
gluTessNormal(tess->tess_obj, 0, 0, 1);
gf_path_flatten(path);
gf_path_get_bounds(path, &rc);
w = rc.width;
h = rc.height;
min_y = rc.y - h;
vertex[2] = 0;
cur = 0;
for (i=0; i<path->n_contours; i++) {
nb_pts = 1+path->contours[i]-cur;
gluTessBeginContour(tess->tess_obj);
for (j=0; j<nb_pts; j++) {
GF_Point2D pt = path->points[cur+j];
Fixed u = gf_divfix(pt.x - rc.x, w);
Fixed v = gf_divfix(pt.y - min_y, h);
idx = (u32 *) gf_malloc(sizeof(u32));
*idx = mesh->v_count;
gf_list_add(tess->vertex_index, idx);
mesh_set_vertex(mesh, pt.x, pt.y, 0, 0, 0, FIX_ONE, u, v);
vertex[0] = (Double) FIX2FLT(pt.x);
vertex[1] = (Double) FIX2FLT(pt.y);
gluTessVertex(tess->tess_obj, vertex, idx);
}
gluTessEndContour(tess->tess_obj);
cur+=nb_pts;
}
gluTessEndPolygon(tess->tess_obj);
gluDeleteTess(tess->tess_obj);
while (gf_list_count(tess->vertex_index)) {
u32 *idx = gf_list_get(tess->vertex_index, 0);
gf_list_rem(tess->vertex_index, 0);
gf_free(idx);
}
gf_list_del(tess->vertex_index);
gf_free(tess);
mesh->bounds.min_edge.x = rc.x;
mesh->bounds.min_edge.y = rc.y-rc.height;
mesh->bounds.max_edge.x = rc.x+rc.width;
mesh->bounds.max_edge.y = rc.y;
mesh->bounds.min_edge.z = mesh->bounds.max_edge.z = 0;
gf_bbox_refresh(&mesh->bounds);
}
#else
void gf_mesh_tesselate_path(GF_Mesh *mesh, GF_Path *path, u32 outline_style) { }
#endif
#define GetPoint2D(pt, apt) \
if (!direction) { pt.x = - apt.pos.z; pt.y = apt.pos.y; } \
else if (direction==1) { pt.x = apt.pos.z; pt.y = apt.pos.x; } \
else if (direction==2) { pt.x = apt.pos.x; pt.y = apt.pos.y; } \
#define ConvCompare(delta) \
( (delta.x > 0) ? -1 : \
(delta.x < 0) ? 1 : \
(delta.y > 0) ? -1 : \
(delta.y < 0) ? 1 : \
0 )
#define ConvGetPointDelta(delta, pprev, pcur ) \
\
\
GetPoint2D(pcur, pts[iread]); iread++; \
delta.x = pcur.x - pprev.x; \
delta.y = pcur.y - pprev.y; \
#define ConvCross(p, q) gf_mulfix(p.x,q.y) - gf_mulfix(p.y,q.x);
#define ConvCheckTriple \
if ( (thisDir = ConvCompare(dcur)) == -curDir ) { \
++dirChanges; \
\
} \
curDir = thisDir; \
cross = ConvCross(dprev, dcur); \
if ( cross > 0 ) { \
if ( angleSign == -1 ) return GF_POLYGON_COMPLEX; \
angleSign = 1; \
} \
else if (cross < 0) { \
if (angleSign == 1) return GF_POLYGON_COMPLEX; \
angleSign = -1; \
} \
pSecond = pThird; \
dprev.x = dcur.x; \
dprev.y = dcur.y; \
u32 polygon_check_convexity(GF_Vertex *pts, u32 len, u32 direction)
{
s32 curDir, thisDir = 0, dirChanges = 0, angleSign = 0;
u32 iread;
Fixed cross;
GF_Point2D pSecond, pThird, pSaveSecond;
GF_Point2D dprev, dcur;
if (len<3) return GF_POLYGON_CONVEX_LINE;
pSecond.x = pSecond.y = 0;
pThird = pSecond;
GetPoint2D(pThird, pts[0]);
if (len < 3 ) return GF_POLYGON_CONVEX_LINE;
iread = 1;
ConvGetPointDelta(dprev, pThird, pSecond);
pSaveSecond = pSecond;
curDir = ConvCompare(dprev);
while ( iread < len) {
ConvGetPointDelta(dcur, pSecond, pThird );
if ( (dcur.x == 0) && (dcur.y == 0) ) continue;
ConvCheckTriple;
}
GetPoint2D(pThird, pts[0]);
dcur.x = pThird.x - pSecond.x;
dcur.y = pThird.y - pSecond.y;
if ( ConvCompare(dcur) ) ConvCheckTriple;
dcur.x = pSaveSecond.x - pSecond.x;
dcur.y = pSaveSecond.y - pSecond.y;
ConvCheckTriple;
if ( dirChanges > 2 ) return GF_POLYGON_COMPLEX;
if ( angleSign > 0 ) return GF_POLYGON_CONVEX_CCW;
if ( angleSign < 0 ) return GF_POLYGON_CONVEX_CW;
return GF_POLYGON_CONVEX_LINE;
}
void TesselateFaceMesh(GF_Mesh *dest, GF_Mesh *orig)
{
u32 poly_type, i, nb_pts, init_idx, direction;
Fixed max_nor_coord, c;
SFVec3f nor;
#ifdef GPAC_HAS_GLU
u32 *idx;
GLdouble vertex[3];
MeshTess *tess;
#endif
if (orig->flags & MESH_IS_2D) {
nor.x = nor.y = 0;
nor.z = FIX_ONE;
} else {
MESH_GET_NORMAL(nor, orig->vertices[0]);
}
direction = 0;
max_nor_coord = ABS(nor.x);
c = ABS(nor.y);
if (c>max_nor_coord) {
direction = 1;
max_nor_coord = c;
}
c = ABS(nor.z);
if (c>max_nor_coord) direction = 2;
poly_type = polygon_check_convexity(orig->vertices, orig->v_count, direction);
switch (poly_type) {
case GF_POLYGON_CONVEX_LINE:
case GF_POLYGON_CONVEX_CW:
case GF_POLYGON_CONVEX_CCW:
init_idx = dest->v_count;
nb_pts = orig->v_count;
for (i=0; i<nb_pts; i++) {
mesh_set_vertex_vx(dest, &orig->vertices[i]);
}
nb_pts -= 1;
for (i=1; i<nb_pts; i++) {
mesh_set_triangle(dest, init_idx, init_idx + i, init_idx + i+1);
}
return;
default:
break;
}
#ifdef GPAC_HAS_GLU
tess = gf_malloc(sizeof(MeshTess));
if (!tess) return;
memset(tess, 0, sizeof(MeshTess));
tess->tess_obj = gluNewTess();
if (!tess->tess_obj) {
gf_free(tess);
return;
}
tess->vertex_index = gf_list_new();
tess->mesh = dest;
gluTessCallback(tess->tess_obj, GLU_TESS_VERTEX_DATA, (void (CALLBACK*)()) &mesh_tess_vertex);
gluTessCallback(tess->tess_obj, GLU_TESS_BEGIN, (void (CALLBACK*)()) &mesh_tess_begin);
gluTessCallback(tess->tess_obj, GLU_TESS_END, (void (CALLBACK*)()) &mesh_tess_end);
gluTessCallback(tess->tess_obj, GLU_TESS_COMBINE_DATA, (void (CALLBACK*)()) &mesh_tess_combine);
gluTessCallback(tess->tess_obj, GLU_TESS_ERROR, (void (CALLBACK*)()) &mesh_tess_error);
gluTessCallback(tess->tess_obj, GLU_EDGE_FLAG,(void (CALLBACK*)()) &mesh_tess_edgeflag);
gluTessBeginPolygon(tess->tess_obj, tess);
gluTessBeginContour(tess->tess_obj);
for (i=0; i<orig->v_count; i++) {
idx = (u32 *) gf_malloc(sizeof(u32));
*idx = dest->v_count;
gf_list_add(tess->vertex_index, idx);
mesh_set_vertex_vx(dest, &orig->vertices[i]);
vertex[0] = (Double) FIX2FLT(orig->vertices[i].pos.x);
vertex[1] = (Double) FIX2FLT(orig->vertices[i].pos.y);
vertex[2] = (Double) FIX2FLT(orig->vertices[i].pos.z);
gluTessVertex(tess->tess_obj, vertex, idx);
}
gluTessEndContour(tess->tess_obj);
gluTessEndPolygon(tess->tess_obj);
gluDeleteTess(tess->tess_obj);
while (gf_list_count(tess->vertex_index)) {
u32 *idx = gf_list_get(tess->vertex_index, 0);
gf_list_rem(tess->vertex_index, 0);
gf_free(idx);
}
gf_list_del(tess->vertex_index);
gf_free(tess);
#endif
}
#ifdef GPAC_HAS_GLU
void TesselateFaceMeshComplex(GF_Mesh *dest, GF_Mesh *orig, u32 nbFaces, u32 *ptsPerFaces)
{
u32 i, cur_pt_faces, cur_face;
u32 *idx;
GLdouble vertex[3];
MeshTess *tess;
tess = gf_malloc(sizeof(MeshTess));
if (!tess) return;
memset(tess, 0, sizeof(MeshTess));
tess->tess_obj = gluNewTess();
if (!tess->tess_obj) {
gf_free(tess);
return;
}
tess->vertex_index = gf_list_new();
tess->mesh = dest;
gluTessCallback(tess->tess_obj, GLU_TESS_VERTEX_DATA, (void (CALLBACK*)()) &mesh_tess_vertex);
gluTessCallback(tess->tess_obj, GLU_TESS_BEGIN, (void (CALLBACK*)()) &mesh_tess_begin);
gluTessCallback(tess->tess_obj, GLU_TESS_END, (void (CALLBACK*)()) &mesh_tess_end);
gluTessCallback(tess->tess_obj, GLU_TESS_COMBINE_DATA, (void (CALLBACK*)()) &mesh_tess_combine);
gluTessCallback(tess->tess_obj, GLU_TESS_ERROR, (void (CALLBACK*)()) &mesh_tess_error);
gluTessCallback(tess->tess_obj, GLU_EDGE_FLAG,(void (CALLBACK*)()) &mesh_tess_edgeflag);
gluTessBeginPolygon(tess->tess_obj, tess);
gluTessBeginContour(tess->tess_obj);
cur_pt_faces = 0;
cur_face = 0;
for (i=0; i<orig->v_count; i++) {
if (i>= cur_pt_faces + ptsPerFaces[cur_face]) {
cur_pt_faces += ptsPerFaces[cur_face];
cur_face++;
if (cur_face>=nbFaces) break;
gluTessEndContour(tess->tess_obj);
gluTessBeginContour(tess->tess_obj);
}
idx = (u32 *) gf_malloc(sizeof(u32));
*idx = dest->v_count;
gf_list_add(tess->vertex_index, idx);
mesh_set_vertex_vx(dest, &orig->vertices[i]);
vertex[0] = (Double) FIX2FLT(orig->vertices[i].pos.x);
vertex[1] = (Double) FIX2FLT(orig->vertices[i].pos.y);
vertex[2] = (Double) FIX2FLT(orig->vertices[i].pos.z);
gluTessVertex(tess->tess_obj, vertex, idx);
}
gluTessEndContour(tess->tess_obj);
gluTessEndPolygon(tess->tess_obj);
gluDeleteTess(tess->tess_obj);
while (gf_list_count(tess->vertex_index)) {
u32 *idx = gf_list_get(tess->vertex_index, 0);
gf_list_rem(tess->vertex_index, 0);
gf_free(idx);
}
gf_list_del(tess->vertex_index);
gf_free(tess);
}
#endif
#endif