root/src/compositor/mesh.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. del_aabb_node
  2. mesh_reset
  3. mesh_free
  4. new_mesh
  5. mesh_fit_alloc
  6. mesh_update_bounds
  7. mesh_clone
  8. set_vertex
  9. mesh_set_vertex
  10. mesh_set_vertex_v
  11. mesh_set_vertex_vx
  12. mesh_set_point
  13. mesh_set_index
  14. mesh_set_triangle
  15. mesh_set_line
  16. mesh_recompute_normals
  17. mesh_generate_tex_coords
  18. mesh_new_box
  19. mesh_new_unit_bbox
  20. compute_cylinder
  21. mesh_new_cylinder
  22. mesh_new_cone
  23. compute_sphere
  24. mesh_new_sphere
  25. mesh_new_rectangle
  26. mesh_new_ellipse
  27. mesh_from_path_intern
  28. mesh_from_path
  29. mesh_get_outline
  30. mesh_new_ils
  31. mesh_new_ps
  32. register_point_in_face
  33. register_face_in_point
  34. smooth_face_normals
  35. mesh_new_ifs_intern
  36. mesh_new_ifs2d
  37. mesh_new_ifs
  38. mesh_new_elevation_grid
  39. mesh_extrude_path_intern
  40. mesh_extrude_path_ext
  41. mesh_extrude_path
  42. mesh_new_extrusion

/*
 *                      GPAC - Multimedia Framework C SDK
 *
 *                      Authors: Jean Le Feuvre
 *                      Copyright (c) Telecom ParisTech 2000-2012
 *                                      All rights reserved
 *
 *  This file is part of GPAC / Scene Compositor sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <gpac/internal/mesh.h>

#include <gpac/nodes_mpeg4.h>
#include <gpac/nodes_x3d.h>
#include <gpac/color.h>

#ifndef GPAC_DISABLE_3D


/*for GPAC_HAS_GLU and glDeleteBuffersARB */
#include "gl_inc.h"

/*when highspeeds, amount of subdivisions for bezier and ellipse will be devided by this factor*/
#define HIGH_SPEED_RATIO        2


/*size alloc for meshes doubles memory at each gf_realloc rather than using a fix-size increment
 (this really speeds up large meshes constructing). Final memory usage is adjusted when updating mesh bounds
*/
#define MESH_CHECK_VERTEX(m)            \
        if (m->v_count == m->v_alloc) { \
                m->v_alloc *= 2;        \
                m->vertices = (GF_Vertex *)gf_realloc(m->vertices, sizeof(GF_Vertex)*m->v_alloc);       \
        }       \
 
#define MESH_CHECK_IDX(m)               \
        if (m->i_count == m->i_alloc) { \
                m->i_alloc *= 2;        \
                m->indices = (IDX_TYPE*)gf_realloc(m->indices, sizeof(IDX_TYPE)*m->i_alloc);    \
        }       \
 


static void del_aabb_node(AABBNode *node)
{
        if (node->pos) del_aabb_node(node->pos);
        if (node->neg) del_aabb_node(node->neg);
        gf_free(node);
}

void mesh_reset(GF_Mesh *mesh)
{
        mesh->v_count = 0;
        mesh->i_count = 0;
        mesh->flags = 0;
        mesh->mesh_type = 0;
        memset(&mesh->bounds.min_edge, 0, sizeof(SFVec3f));
        memset(&mesh->bounds.max_edge, 0, sizeof(SFVec3f));
        if (mesh->aabb_root) del_aabb_node(mesh->aabb_root);
        mesh->aabb_root = NULL;
        if (mesh->aabb_indices) gf_free(mesh->aabb_indices);
        mesh->aabb_indices = NULL;


        if (mesh->vbo) {
                glDeleteBuffers(1, &mesh->vbo);
                mesh->vbo = 0;
        }
        if (mesh->vbo_idx) {
                glDeleteBuffers(1, &mesh->vbo_idx);
                mesh->vbo_idx = 0;
        }
}

void mesh_free(GF_Mesh *mesh)
{
        if (mesh->vertices) gf_free(mesh->vertices);
        if (mesh->indices) gf_free(mesh->indices);
        if (mesh->aabb_root) del_aabb_node(mesh->aabb_root);
        mesh->aabb_root = NULL;
        if (mesh->aabb_indices) gf_free(mesh->aabb_indices);
        gf_free(mesh);
}

GF_Mesh *new_mesh()
{
        GF_Mesh *mesh = (GF_Mesh *)gf_malloc(sizeof(GF_Mesh));
        if (mesh) {
                memset(mesh, 0, sizeof(GF_Mesh));
                mesh->v_alloc = 8;
                mesh->vertices = (GF_Vertex*)gf_malloc(sizeof(GF_Vertex)*mesh->v_alloc);
                mesh->i_alloc = 8;
                mesh->indices = (IDX_TYPE*)gf_malloc(sizeof(IDX_TYPE)*mesh->i_alloc);
        }
        return mesh;
}

static void mesh_fit_alloc(GF_Mesh *m)
{
        if (m->v_count && (m->v_count < m->v_alloc)) {
                m->v_alloc = m->v_count;
                m->vertices = (GF_Vertex *)gf_realloc(m->vertices, sizeof(GF_Vertex)*m->v_alloc);
        }
        if (m->i_count && (m->i_count  < m->i_alloc)) {
                m->i_alloc = m->i_count;
                m->indices = (IDX_TYPE*)gf_realloc(m->indices, sizeof(IDX_TYPE)*m->i_alloc);
        }
}


void mesh_update_bounds(GF_Mesh *mesh)
{
        u32 i;
        Fixed mx, my, mz, Mx, My, Mz;
        mx = my = mz = FIX_MAX;
        Mx = My = Mz = FIX_MIN;

        mesh_fit_alloc(mesh);

        for (i=0; i<mesh->v_count; i++) {
                SFVec3f *v = &mesh->vertices[i].pos;
                if (mx>v->x) mx=v->x;
                if (my>v->y) my=v->y;
                if (mz>v->z) mz=v->z;
                if (Mx<v->x) Mx=v->x;
                if (My<v->y) My=v->y;
                if (Mz<v->z) Mz=v->z;
        }
        mesh->bounds.min_edge.x = mx;
        mesh->bounds.min_edge.y = my;
        mesh->bounds.min_edge.z = mz;
        mesh->bounds.max_edge.x = Mx;
        mesh->bounds.max_edge.y = My;
        mesh->bounds.max_edge.z = Mz;
        gf_bbox_refresh(&mesh->bounds);
}

void mesh_clone(GF_Mesh *dest, GF_Mesh *orig)
{
        if (dest->v_alloc<orig->v_alloc) {
                dest->v_alloc = orig->v_alloc;
                dest->vertices = (GF_Vertex *)gf_realloc(dest->vertices, sizeof(GF_Vertex)*dest->v_alloc);
        }
        dest->v_count = orig->v_count;
        memcpy(dest->vertices, orig->vertices, sizeof(GF_Vertex)*dest->v_count);

        if (dest->i_alloc < orig->i_alloc) {
                dest->i_alloc = orig->i_alloc;
                dest->indices = (IDX_TYPE*)gf_realloc(dest->indices, sizeof(IDX_TYPE)*dest->i_alloc);
        }
        dest->i_count = orig->i_count;
        memcpy(dest->indices, orig->indices, sizeof(IDX_TYPE)*dest->i_count);

        dest->mesh_type = orig->mesh_type;
        dest->flags = orig->flags;
        dest->bounds = orig->bounds;
        /*and reset AABB*/
        if (dest->aabb_root) del_aabb_node(dest->aabb_root);
        dest->aabb_root = NULL;
        if (dest->aabb_indices) gf_free(dest->aabb_indices);
        dest->aabb_indices = NULL;
}


static GFINLINE GF_Vertex set_vertex(Fixed x, Fixed y, Fixed z, Fixed nx, Fixed ny, Fixed nz, Fixed u, Fixed v)
{
        SFVec3f nor;
        GF_Vertex res;
        res.pos.x = x;
        res.pos.y = y;
        res.pos.z = z;
        nor.x = nx;
        nor.y = ny;
        nor.z = nz;
        gf_vec_norm(&nor);
        MESH_SET_NORMAL(res, nor);

        res.texcoords.x = u;
        res.texcoords.y = v;
#ifdef MESH_USE_SFCOLOR
        res.color.blue = res.color.red = res.color.green = res.color.alpha = FIX_ONE;
#else
        res.color = 0xFFFFFFFF;
#endif
        return res;
}
void mesh_set_vertex(GF_Mesh *mesh, Fixed x, Fixed y, Fixed z, Fixed nx, Fixed ny, Fixed nz, Fixed u, Fixed v)
{
        MESH_CHECK_VERTEX(mesh);
        mesh->vertices[mesh->v_count] = set_vertex(x, y, z, nx, ny, nz, u, v);
        mesh->v_count++;
}

void mesh_set_vertex_v(GF_Mesh *mesh, SFVec3f pt, SFVec3f nor, SFVec2f tx, SFColorRGBA col)
{
        if (!mesh) return;
        MESH_CHECK_VERTEX(mesh);
        if (!mesh->vertices) return;
        mesh->vertices[mesh->v_count].pos = pt;
        mesh->vertices[mesh->v_count].texcoords = tx;
        mesh->vertices[mesh->v_count].color = MESH_MAKE_COL(col);
        gf_vec_norm(&nor);
        MESH_SET_NORMAL(mesh->vertices[mesh->v_count], nor);
        mesh->v_count++;
}

void mesh_set_vertex_vx(GF_Mesh *mesh, GF_Vertex *vx)
{
        MESH_CHECK_VERTEX(mesh);
        mesh->vertices[mesh->v_count] = *vx;
        mesh->v_count++;
}

void mesh_set_point(GF_Mesh *mesh, Fixed x, Fixed y, Fixed z, SFColorRGBA col)
{
        MESH_CHECK_VERTEX(mesh);
        mesh->vertices[mesh->v_count].pos.x = x;
        mesh->vertices[mesh->v_count].pos.y = y;
        mesh->vertices[mesh->v_count].pos.z = z;
        mesh->vertices[mesh->v_count].normal.x = mesh->vertices[mesh->v_count].normal.y = mesh->vertices[mesh->v_count].normal.z = 0;
        mesh->vertices[mesh->v_count].texcoords.x = mesh->vertices[mesh->v_count].texcoords.y = 0;
        mesh->vertices[mesh->v_count].color = MESH_MAKE_COL(col);
        mesh->v_count++;
}
void mesh_set_index(GF_Mesh *mesh, u32 idx)
{
        MESH_CHECK_IDX(mesh);
        mesh->indices[mesh->i_count] = (IDX_TYPE) idx;
        mesh->i_count++;
}
void mesh_set_triangle(GF_Mesh *mesh, u32 v1_idx, u32 v2_idx, u32 v3_idx)
{
        mesh_set_index(mesh, v1_idx);
        mesh_set_index(mesh, v2_idx);
        mesh_set_index(mesh, v3_idx);
}
void mesh_set_line(GF_Mesh *mesh, u32 v1_idx, u32 v2_idx)
{
        mesh_set_index(mesh, v1_idx);
        mesh_set_index(mesh, v2_idx);
}

void mesh_recompute_normals(GF_Mesh *mesh)
{
        u32 i;
        if (mesh->mesh_type) return;

        for (i=0; i<mesh->i_count; i+=3) {
                SFVec3f v1, v2, v3;
                gf_vec_diff(v1, mesh->vertices[mesh->indices[i+1]].pos, mesh->vertices[mesh->indices[i]].pos);
                gf_vec_diff(v2, mesh->vertices[mesh->indices[i+2]].pos, mesh->vertices[mesh->indices[i]].pos);
                v3 = gf_vec_cross(v1, v2);
                gf_vec_norm(&v3);
                MESH_SET_NORMAL(mesh->vertices[mesh->indices[i]], v3);
                MESH_SET_NORMAL(mesh->vertices[mesh->indices[i+1]], v3);
                MESH_SET_NORMAL(mesh->vertices[mesh->indices[i+2]], v3);
        }
}

#if !defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_X3D)
void mesh_generate_tex_coords(GF_Mesh *mesh, GF_Node *__texCoords)
{
        u32 i;
        X_TextureCoordinateGenerator *txgen = (X_TextureCoordinateGenerator *)__texCoords;

        if (!strcmp(txgen->mode.buffer, "SPHERE-LOCAL")) {
                for (i=0; i<mesh->v_count; i++) {
                        GF_Vertex *vx = &mesh->vertices[i];
                        vx->texcoords.x = (vx->normal.x+FIX_ONE) / 2;
                        vx->texcoords.y = (vx->normal.y+FIX_ONE) / 2;
                }
        }
        else if (!strcmp(txgen->mode.buffer, "COORD")) {
                for (i=0; i<mesh->v_count; i++) {
                        GF_Vertex *vx = &mesh->vertices[i];
                        vx->texcoords.x = vx->pos.x;
                        vx->texcoords.y = vx->pos.y;
                }
        }
}
#endif /*GPAC_DISABLE_VRML*/


void mesh_new_box(GF_Mesh *mesh, SFVec3f size)
{
        Fixed hx = size.x / 2;
        Fixed hy = size.y / 2;
        Fixed hz = size.z / 2;

        mesh_reset(mesh);
        /*back face (horiz flip of texcoords)*/
        mesh_set_vertex(mesh,  hx, -hy, -hz,  0,  0, -FIX_ONE, 0, 0);
        mesh_set_vertex(mesh, -hx, -hy, -hz,  0,  0, -FIX_ONE, FIX_ONE, 0);
        mesh_set_vertex(mesh, -hx,  hy, -hz,  0,  0, -FIX_ONE, FIX_ONE, FIX_ONE);
        mesh_set_vertex(mesh,  hx,  hy, -hz,  0,  0, -FIX_ONE, 0, FIX_ONE);
        mesh_set_triangle(mesh, 0, 1, 2);
        mesh_set_triangle(mesh, 0, 2, 3);
        /*top face*/
        mesh_set_vertex(mesh, -hx,  hy,  hz,  0,  FIX_ONE,  0, 0, 0);
        mesh_set_vertex(mesh,  hx,  hy,  hz,  0,  FIX_ONE,  0, FIX_ONE, 0);
        mesh_set_vertex(mesh,  hx,  hy, -hz,  0,  FIX_ONE,  0, FIX_ONE, FIX_ONE);
        mesh_set_vertex(mesh, -hx,  hy, -hz,  0,  FIX_ONE,  0, 0, FIX_ONE);
        mesh_set_triangle(mesh, 4, 5, 6);
        mesh_set_triangle(mesh, 4, 6, 7);
        /*front face*/
        mesh_set_vertex(mesh, -hx, -hy,  hz,  0,  0,  FIX_ONE, 0, 0);
        mesh_set_vertex(mesh,  hx, -hy,  hz,  0,  0,  FIX_ONE, FIX_ONE, 0);
        mesh_set_vertex(mesh,  hx,  hy,  hz,  0,  0,  FIX_ONE, FIX_ONE, FIX_ONE);
        mesh_set_vertex(mesh, -hx,  hy,  hz,  0,  0,  FIX_ONE, 0, FIX_ONE);
        mesh_set_triangle(mesh, 8, 9, 10);
        mesh_set_triangle(mesh, 8, 10, 11);
        /*left face*/
        mesh_set_vertex(mesh, -hx, -hy, -hz, -FIX_ONE,  0,  0, 0, 0);
        mesh_set_vertex(mesh, -hx, -hy,  hz, -FIX_ONE,  0,  0, FIX_ONE, 0);
        mesh_set_vertex(mesh, -hx,  hy,  hz, -FIX_ONE,  0,  0, FIX_ONE, FIX_ONE);
        mesh_set_vertex(mesh, -hx,  hy, -hz, -FIX_ONE,  0,  0, 0, FIX_ONE);
        mesh_set_triangle(mesh, 12, 13, 14);
        mesh_set_triangle(mesh, 12, 14, 15);
        /*bottom face*/
        mesh_set_vertex(mesh, -hx, -hy, -hz,  0, -FIX_ONE,  0, 0, 0);
        mesh_set_vertex(mesh,  hx, -hy, -hz,  0, -FIX_ONE,  0, FIX_ONE, 0);
        mesh_set_vertex(mesh,  hx, -hy,  hz,  0, -FIX_ONE,  0, FIX_ONE, FIX_ONE);
        mesh_set_vertex(mesh, -hx, -hy,  hz,  0, -FIX_ONE,  0, 0, FIX_ONE);
        mesh_set_triangle(mesh, 16, 17, 18);
        mesh_set_triangle(mesh, 16, 18, 19);
        /*right face*/
        mesh_set_vertex(mesh,  hx, -hy,  hz,  FIX_ONE,  0,  0, 0, 0);
        mesh_set_vertex(mesh,  hx, -hy, -hz,  FIX_ONE,  0,  0, FIX_ONE, 0);
        mesh_set_vertex(mesh,  hx,  hy, -hz,  FIX_ONE,  0,  0, FIX_ONE, FIX_ONE);
        mesh_set_vertex(mesh,  hx,  hy,  hz,  FIX_ONE,  0,  0, 0, FIX_ONE);
        mesh_set_triangle(mesh, 20, 21, 22);
        mesh_set_triangle(mesh, 20, 22, 23);


        mesh->flags |= MESH_IS_SOLID;
        mesh->bounds.min_edge.x = -hx;
        mesh->bounds.min_edge.y = -hy;
        mesh->bounds.min_edge.z = -hz;
        mesh->bounds.max_edge.x = hx;
        mesh->bounds.max_edge.y = hy;
        mesh->bounds.max_edge.z = hz;
        gf_bbox_refresh(&mesh->bounds);
        gf_mesh_build_aabbtree(mesh);
}

void mesh_new_unit_bbox(GF_Mesh *mesh)
{
        SFColorRGBA col;
        Fixed s = FIX_ONE/2;

        memset(&col, 0, sizeof(SFColor));
        col.alpha = 1;
        mesh_reset(mesh);
        mesh->mesh_type = MESH_LINESET;
        mesh_set_point(mesh, -s, -s, -s,  col);
        mesh_set_point(mesh, s, -s, -s,  col);
        mesh_set_point(mesh, s, s, -s,  col);
        mesh_set_point(mesh, -s, s, -s,  col);
        mesh_set_point(mesh, -s, -s, s,  col);
        mesh_set_point(mesh, s, -s, s,  col);
        mesh_set_point(mesh, s, s, s,  col);
        mesh_set_point(mesh, -s, s, s,  col);

        mesh_set_line(mesh, 0, 1);
        mesh_set_line(mesh, 1, 2);
        mesh_set_line(mesh, 2, 3);
        mesh_set_line(mesh, 3, 0);
        mesh_set_line(mesh, 4, 5);
        mesh_set_line(mesh, 5, 6);
        mesh_set_line(mesh, 6, 7);
        mesh_set_line(mesh, 7, 4);
        mesh_set_line(mesh, 0, 4);
        mesh_set_line(mesh, 1, 5);
        mesh_set_line(mesh, 2, 6);
        mesh_set_line(mesh, 3, 7);
        gf_bbox_refresh(&mesh->bounds);
}


static void compute_cylinder(Fixed height, Fixed radius, s32 numFacets, SFVec3f *coords, SFVec2f *texcoords)
{
        Fixed angle, t, u;
        s32 i;
        t = height / 2;
        for (i=0; i<numFacets; ++i) {
                angle = i*GF_2PI / numFacets - GF_PI2;
                coords[i].y = t;
                coords[i].x = gf_mulfix(radius, gf_cos(angle));
                coords[i].z = gf_mulfix(radius , gf_sin(angle));
                u = FIX_ONE - i*FIX_ONE/numFacets;
                texcoords[i].x = u;
                texcoords[i].y = FIX_ONE;
        }
}

#define CYLINDER_SUBDIV 24
void mesh_new_cylinder(GF_Mesh *mesh, Fixed height, Fixed radius, Bool bottom, Bool side, Bool top, Bool low_res)
{
        u32 nfacets, i, c_idx;
        SFVec3f *coords;
        SFVec2f *texcoords;

        mesh_reset(mesh);
        if (!bottom && !side && !top) return;

        nfacets = CYLINDER_SUBDIV;
        if (low_res) nfacets /= HIGH_SPEED_RATIO;
        coords = (SFVec3f*) gf_malloc(sizeof(SFVec3f) * nfacets);
        texcoords = (SFVec2f*)gf_malloc(sizeof(SFVec2f) * nfacets);

        compute_cylinder(height, radius, nfacets, coords, texcoords);

        if (side) {
                for (i=0; i<nfacets; ++i) {
                        /*top*/
                        mesh_set_vertex(mesh, coords[i].x, coords[i].y, coords[i].z,
                                        coords[i].x, 0, coords[i].z,
                                        texcoords[i].x, FIX_ONE);

                        /*bottom*/
                        mesh_set_vertex(mesh, coords[i].x, -1*coords[i].y, coords[i].z,
                                        coords[i].x, 0, coords[i].z,
                                        texcoords[i].x, 0);


                        /*top circle is counterclockwise, reverse coords*/
                        if (i) {
                                mesh_set_triangle(mesh, mesh->v_count-4, mesh->v_count-1, mesh->v_count-3);
                                mesh_set_triangle(mesh, mesh->v_count-4, mesh->v_count-2, mesh->v_count-1);
                        }
                }

                /*top*/
                mesh_set_vertex(mesh, coords[0].x, coords[0].y, coords[0].z,
                                coords[0].x, 0, coords[0].z,
                                texcoords[0].x - FIX_ONE, FIX_ONE);

                /*bottom*/
                mesh_set_vertex(mesh, coords[0].x, -1*coords[0].y, coords[0].z,
                                coords[0].x, 0, coords[0].z,
                                texcoords[0].x - FIX_ONE, 0);

                mesh_set_triangle(mesh, mesh->v_count-4, mesh->v_count-1, mesh->v_count-3);
                mesh_set_triangle(mesh, mesh->v_count-4, mesh->v_count-2, mesh->v_count-1);
        }

        if (bottom) {
                Fixed angle = 0;
                Fixed aincr = GF_2PI / nfacets;

                mesh_set_vertex(mesh, 0, -height/2, 0, 0, -FIX_ONE, 0, FIX_ONE/2, FIX_ONE/2);
                c_idx = mesh->v_count-1;
                for (i=0; i<nfacets; ++i, angle += aincr) {
                        mesh_set_vertex(mesh, coords[i].x, -1*coords[i].y, coords[i].z,
                                        0, -FIX_ONE, 0,
                                        (FIX_ONE + gf_sin(angle))/2, FIX_ONE - (FIX_ONE + gf_cos(angle))/2);
                        if (i) mesh_set_triangle(mesh, c_idx, mesh->v_count-2, mesh->v_count-1);
                }

                mesh_set_vertex(mesh, coords[0].x, -1*coords[0].y, coords[0].z,
                                0, -FIX_ONE, 0,
                                (FIX_ONE + gf_sin(angle))/2, FIX_ONE - (FIX_ONE + gf_cos(angle))/2);
                mesh_set_triangle(mesh, c_idx, mesh->v_count-2, mesh->v_count-1);
        }

        if (top) {
                Fixed aincr = GF_2PI / nfacets;
                Fixed angle = GF_PI + aincr;

                mesh_set_vertex(mesh, 0, height/2, 0, 0, FIX_ONE, 0, FIX_ONE/2, FIX_ONE/2);
                c_idx = mesh->v_count-1;
                for (i=nfacets; i>0; --i, angle += aincr) {

                        mesh_set_vertex(mesh, coords[i - 1].x, coords[i - 1].y, coords[i - 1].z,
                                        0, FIX_ONE, 0,
                                        (FIX_ONE + gf_sin(angle))/2, FIX_ONE - (FIX_ONE + gf_cos(angle))/2);
                        if (i) mesh_set_triangle(mesh, c_idx, mesh->v_count-2, mesh->v_count-1);
                }
                mesh_set_vertex(mesh, coords[nfacets - 1].x, coords[nfacets - 1].y, coords[nfacets - 1].z,
                                0, FIX_ONE, 0,
                                (FIX_ONE + gf_sin(angle))/2, FIX_ONE - (FIX_ONE + gf_cos(angle))/2);
                mesh_set_triangle(mesh, c_idx, mesh->v_count-2, mesh->v_count-1);
        }
        gf_free(texcoords);
        gf_free(coords);

        if (top && bottom && side) mesh->flags |= MESH_IS_SOLID;

        mesh->bounds.min_edge.x = mesh->bounds.min_edge.z = -radius;
        mesh->bounds.max_edge.x = mesh->bounds.max_edge.z = radius;
        mesh->bounds.max_edge.y = (side || (top && bottom)) ? height/2 : 0;
        mesh->bounds.min_edge.y = -mesh->bounds.max_edge.y;
        gf_bbox_refresh(&mesh->bounds);

        gf_mesh_build_aabbtree(mesh);
}

#define CONE_SUBDIV     24
void mesh_new_cone(GF_Mesh *mesh, Fixed height, Fixed radius, Bool bottom, Bool side, Bool low_res)
{
        u32 nfacets, i, c_idx;
        SFVec3f *coords;
        SFVec2f *texcoords;

        mesh_reset(mesh);
        if (!bottom && !side) return;

        nfacets = CONE_SUBDIV;
        if (low_res) nfacets /= HIGH_SPEED_RATIO;
        coords = (SFVec3f*)gf_malloc(sizeof(SFVec3f) * nfacets);
        texcoords = (SFVec2f*)gf_malloc(sizeof(SFVec2f) * nfacets);

        compute_cylinder(height, radius, nfacets, coords, texcoords);

        if (side) {
                Fixed Ny = gf_muldiv(radius, radius, height);

                for (i = 0; i < nfacets; ++i) {
                        /*top*/
                        mesh_set_vertex(mesh, 0, coords[i].y, 0,
                                        coords[i].x, Ny, coords[i].z,
                                        texcoords[i].x, FIX_ONE);

                        /*base*/
                        mesh_set_vertex(mesh, coords[i].x, -1*coords[i].y, coords[i].z,
                                        coords[i].x, Ny, coords[i].z,
                                        texcoords[i].x, 0);
                        if (i) {
                                mesh_set_triangle(mesh, mesh->v_count-4, mesh->v_count-1, mesh->v_count-3);
                        }
                }
                /*top*/
                mesh_set_vertex(mesh, 0, coords[0].y, 0, coords[0].x, Ny, coords[0].z, texcoords[0].x - FIX_ONE, FIX_ONE);
                /*base*/
                mesh_set_vertex(mesh, coords[0].x, -1*coords[0].y, coords[0].z,
                                coords[0].x, Ny, coords[0].z,
                                texcoords[0].x - FIX_ONE, 0);

                mesh_set_triangle(mesh, mesh->v_count-4, mesh->v_count-1, mesh->v_count-3);
        }

        if (bottom) {
                Fixed angle = 0;
                Fixed aincr = GF_2PI / nfacets;

                mesh_set_vertex(mesh, 0, -height/2, 0, 0, -FIX_ONE, 0, FIX_ONE/2, FIX_ONE/2);
                c_idx = mesh->v_count - 1;
                for (i=0; i<nfacets; ++i, angle += aincr) {
                        mesh_set_vertex(mesh, coords[i].x, -1*coords[i].y, coords[i].z,
                                        0, -FIX_ONE, 0,
                                        (FIX_ONE + gf_sin(angle))/2, FIX_ONE - (FIX_ONE + gf_cos(angle))/2);

                        if (i)
                                mesh_set_triangle(mesh, c_idx, mesh->v_count-2, mesh->v_count-1);
                }
                mesh_set_vertex(mesh, coords[0].x, -1*coords[0].y, coords[0].z,
                                0, -FIX_ONE, 0,
                                (FIX_ONE + gf_sin(angle))/2, FIX_ONE - (FIX_ONE + gf_cos(angle))/2);
                mesh_set_triangle(mesh, c_idx, mesh->v_count-2, mesh->v_count-1);
        }
        gf_free(texcoords);
        gf_free(coords);

        if (bottom && side) mesh->flags |= MESH_IS_SOLID;

        mesh->bounds.min_edge.x = mesh->bounds.min_edge.z = -radius;
        mesh->bounds.max_edge.x = mesh->bounds.max_edge.z = radius;
        mesh->bounds.max_edge.y = height/2;
        mesh->bounds.min_edge.y = -mesh->bounds.max_edge.y;
        gf_bbox_refresh(&mesh->bounds);

        gf_mesh_build_aabbtree(mesh);

}

void compute_sphere(Fixed radius, SFVec3f *coords, SFVec2f *texcoords, u32 num_steps, GF_MeshSphereAngles *sphere_angles)
{
        Fixed r, angle, x, y, z;
        u32 i, j;

        for (i=0; i<num_steps; i++) {
                if (!sphere_angles) {
                        angle = (i * GF_PI / (num_steps-1) ) - GF_PI2;
                } else {
                        angle = (i * (sphere_angles->max_phi - sphere_angles->min_phi) / (num_steps-1) ) + sphere_angles->min_phi;
                }
                y = gf_sin(angle);
                r = gf_sqrt(FIX_ONE - gf_mulfix(y, y));
                for (j = 0; j<num_steps; j++) {
                        if (!sphere_angles) {
                                angle = GF_2PI * j / num_steps - GF_PI2;
                        } else {
                                angle = (j * (sphere_angles->max_theta - sphere_angles->min_theta) / (num_steps-1) ) + sphere_angles->min_theta;
                        }
                        x = gf_mulfix(gf_cos(angle), r);
                        z = gf_mulfix(gf_sin(angle), r);
                        coords[i * num_steps + j].x = gf_mulfix(radius, x);
                        coords[i * num_steps + j].y = gf_mulfix(radius, y);
                        coords[i * num_steps + j].z = gf_mulfix(radius, z);
                        if (radius>0) {
                                if (!sphere_angles){
                                        texcoords[i * num_steps + j].x = FIX_ONE - j*FIX_ONE/num_steps;
                                        texcoords[i * num_steps + j].y = i*FIX_ONE/num_steps;
                                }else{
                                        texcoords[i * num_steps + j].x = j*FIX_ONE/(num_steps-1);
                                        texcoords[i * num_steps + j].y = FIX_ONE - i*FIX_ONE/(num_steps-1);

                                }
                        }else {
                                if (!sphere_angles){
                                        texcoords[i * num_steps + j].x = j*FIX_ONE/(num_steps);
                                        texcoords[i * num_steps + j].y = FIX_ONE - i*FIX_ONE/(num_steps);
                                }else{
                                        texcoords[i * num_steps + j].x = j*FIX_ONE/(num_steps-1);
                                        texcoords[i * num_steps + j].y = FIX_ONE - i*FIX_ONE/(num_steps-1);
                                }
                        }
                }
        }

}

void mesh_new_sphere(GF_Mesh *mesh, Fixed radius, Bool low_res, GF_MeshSphereAngles *sphere_angles)
{
        u32 i, j, num_steps, npts;
        SFVec3f *coords;
        SFVec2f *texcoords;

        num_steps = 48;
        //this is 360 VR, use a large number of subdivisions (1 seg for 5 degrees should be enough )
        if (radius<0) num_steps = 72;

        if (low_res) num_steps /= 2;
        npts = num_steps * num_steps;

        coords = (SFVec3f*)gf_malloc(sizeof(SFVec3f)*npts);
        texcoords = (SFVec2f*)gf_malloc(sizeof(SFVec2f)*npts);
        compute_sphere(radius, coords, texcoords, num_steps, sphere_angles);

        for (i=0; i<num_steps-1; i++) {
                u32 n = i * num_steps;
                Fixed last_tx_coord;
                for (j=0; j<num_steps; j++) {
                        mesh_set_vertex(mesh, coords[n + j + num_steps].x, coords[n + j + num_steps].y, coords[n + j + num_steps].z,
                                        coords[n + j + num_steps].x, coords[n + j + num_steps].y, coords[n + j + num_steps].z,
                                        texcoords[n + j + num_steps].x, texcoords[n + j + num_steps].y);

                        mesh_set_vertex(mesh, coords[n + j].x, coords[n + j].y, coords[n + j].z,
                                        coords[n + j].x, coords[n + j].y, coords[n + j].z,
                                        texcoords[n + j].x, texcoords[n + j].y);

                        if (j) {
                                mesh_set_triangle(mesh, mesh->v_count-3, mesh->v_count-4, mesh->v_count-2);
                                mesh_set_triangle(mesh, mesh->v_count-3, mesh->v_count-2, mesh->v_count-1);
                        }
                
                }
                if (!sphere_angles) {
                        last_tx_coord = (radius>0) ? 0 : FIX_ONE;
                        mesh_set_vertex(mesh, coords[n + num_steps].x, coords[n + num_steps].y, coords[n + num_steps].z,
                                        coords[n + num_steps].x, coords[n + num_steps].y, coords[n  + num_steps].z,
                                        last_tx_coord, texcoords[n + num_steps].y);
                        mesh_set_vertex(mesh, coords[n].x, coords[n].y, coords[n].z,
                                        coords[n].x, coords[n].y, coords[n].z,
                                        last_tx_coord, texcoords[n].y);

                        mesh_set_triangle(mesh, mesh->v_count-3, mesh->v_count-4, mesh->v_count-2);
                        mesh_set_triangle(mesh, mesh->v_count-3, mesh->v_count-2, mesh->v_count-1);
                }
        }

        gf_free(coords);
        gf_free(texcoords);
        if (!sphere_angles) {
                mesh->flags |= MESH_IS_SOLID;
        }

        mesh->bounds.min_edge.x = mesh->bounds.min_edge.y = mesh->bounds.min_edge.z = -radius;
        mesh->bounds.max_edge.x = mesh->bounds.max_edge.y = mesh->bounds.max_edge.z = radius;
        gf_bbox_refresh(&mesh->bounds);

        if (radius != FIX_ONE) gf_mesh_build_aabbtree(mesh);
}

void mesh_new_rectangle(GF_Mesh *mesh, SFVec2f size, SFVec2f *orig, Bool flip)
{
        Fixed tmin, tmax;
        Fixed x = - size.x / 2;
        Fixed y = size.y / 2;
        if (orig) {
                x = orig->x;
                y = orig->y;
        }
        mesh_reset(mesh);

        tmin = flip ? FIX_ONE : 0;
        tmax = flip ? 0 : FIX_ONE;

        mesh_set_vertex(mesh, x, y-size.y,  0,  0,  0,  FIX_ONE, 0, tmin);
        mesh_set_vertex(mesh, x+size.x, y-size.y,  0,  0,  0,  FIX_ONE, FIX_ONE, tmin);
        mesh_set_vertex(mesh, x+size.x, y,  0,  0,  0,  FIX_ONE, FIX_ONE, tmax);
        mesh_set_vertex(mesh, x,  y,  0,  0,  0,  FIX_ONE, 0, tmax);

        mesh_set_triangle(mesh, 0, 1, 2);
        mesh_set_triangle(mesh, 0, 2, 3);

        mesh->flags |= MESH_IS_2D;

        mesh->bounds.min_edge.x = x;
        mesh->bounds.min_edge.y = y-size.y;
        mesh->bounds.min_edge.z = 0;
        mesh->bounds.max_edge.x = x+size.x;
        mesh->bounds.max_edge.y = y;
        mesh->bounds.max_edge.z = 0;
        gf_bbox_refresh(&mesh->bounds);
}

#define ELLIPSE_SUBDIV          32
void mesh_new_ellipse(GF_Mesh *mesh, Fixed a_dia, Fixed b_dia, Bool low_res)
{
        Fixed step, cur, end, cosa, sina;
        a_dia /= 2;
        b_dia /= 2;

        /*no begin/end draw since we always use generic 2D node drawing methods*/
        end = GF_2PI;
        step = end / ELLIPSE_SUBDIV;
        if (low_res) step *= HIGH_SPEED_RATIO;

        mesh_reset(mesh);

        /*center*/
        mesh_set_vertex(mesh, 0, 0, 0, 0, 0, FIX_ONE, FIX_ONE/2, FIX_ONE/2);
        for (cur=0; cur<end; cur += step) {
                cosa = gf_cos(cur);
                sina = gf_sin(cur);

                mesh_set_vertex(mesh, gf_mulfix(a_dia, cosa), gf_mulfix(b_dia, sina), 0,
                                0, 0, FIX_ONE,
                                (FIX_ONE + cosa)/2, (FIX_ONE + sina)/2);

                if (cur) mesh_set_triangle(mesh, 0, mesh->v_count-2, mesh->v_count-1);
        }
        mesh_set_vertex(mesh, a_dia, 0, 0, 0, 0, FIX_ONE, FIX_ONE, FIX_ONE/2);
        mesh_set_triangle(mesh, 0, mesh->v_count-2, mesh->v_count-1);

        mesh->flags |= MESH_IS_2D;
        mesh->bounds.min_edge.x = -a_dia;
        mesh->bounds.min_edge.y = -b_dia;
        mesh->bounds.min_edge.z = 0;
        mesh->bounds.max_edge.x = a_dia;
        mesh->bounds.max_edge.y = b_dia;
        mesh->bounds.max_edge.z = 0;
        gf_bbox_refresh(&mesh->bounds);
}

void mesh_from_path_intern(GF_Mesh *mesh, GF_Path *path, Bool make_ccw)
{
        u32 i, nbPts;
        Fixed w, h;
        GF_Rect bounds;
        Bool isCW = 0;

        gf_path_flatten(path);
        gf_path_get_bounds(path, &bounds);

        mesh_reset(mesh);
        if (path->n_contours==1) {
                u32 type = gf_polygone2d_get_convexity(path->points, path->n_points);
                switch (type) {
                /*degenrated polygon - skip*/
                case GF_POLYGON_CONVEX_LINE:
                        return;
                case GF_POLYGON_CONVEX_CW:
                        isCW = make_ccw;
                case GF_POLYGON_CONVEX_CCW:
                        w = bounds.width;
                        h = bounds.height;

                        /*add all vertices*/
                        for (i=0; i<path->n_points-1; i++) {
                                GF_Point2D pt = path->points[i];
                                mesh_set_vertex(mesh, pt.x, pt.y, 0, 0, 0, FIX_ONE, gf_divfix(pt.x - bounds.x, w), gf_divfix(bounds.y - pt.y, h));
                        }
                        nbPts = path->n_points - 1;
                        /*take care of already closed path*/
                        if ( (path->points[i].x != path->points[0].x) || (path->points[i].y != path->points[0].y)) {
                                GF_Point2D pt = path->points[i];
                                mesh_set_vertex(mesh, pt.x, pt.y, 0, 0, 0, FIX_ONE, gf_divfix(pt.x - bounds.x, w), gf_divfix(bounds.y - pt.y, h));
                                nbPts = path->n_points;
                        }
                        /*make it CCW*/
                        for (i=1; i<nbPts-1; i++) {
                                if (isCW) {
                                        mesh_set_triangle(mesh, 0, nbPts-i, nbPts-i-1);
                                } else {
                                        mesh_set_triangle(mesh, 0, i, i+1);
                                }
                        }
                        mesh->bounds.min_edge.x = bounds.x;
                        mesh->bounds.min_edge.y = bounds.y-bounds.height;
                        mesh->bounds.min_edge.z = 0;
                        mesh->bounds.max_edge.x = bounds.x+bounds.width;
                        mesh->bounds.max_edge.y = bounds.y;
                        mesh->bounds.max_edge.z = 0;
                        gf_bbox_refresh(&mesh->bounds);
                        return;
                default:
                        break;
                }
        }
        /*we need to tesselate the path*/
#ifdef GPAC_HAS_GLU
        gf_mesh_tesselate_path(mesh, path, 0);
#endif
}

void mesh_from_path(GF_Mesh *mesh, GF_Path *path)
{
        mesh_from_path_intern(mesh, path, 1);
}


void mesh_get_outline(GF_Mesh *mesh, GF_Path *path)
{
        u32 i, j, cur, nb_pts;
        mesh_reset(mesh);

        mesh->mesh_type = MESH_LINESET;
        mesh->flags |= (MESH_IS_2D | MESH_NO_TEXTURE);

        gf_path_flatten(path);

        cur = 0;
        for (i=0; i<path->n_contours; i++) {
                nb_pts = 1+path->contours[i] - cur;
                for (j=0; j<nb_pts; j++) {
                        GF_Point2D pt = path->points[j+cur];
                        if (j) mesh_set_line(mesh, mesh->v_count-1, mesh->v_count);
                        mesh_set_vertex(mesh, pt.x, pt.y, 0, 0, 0, FIX_ONE, 0, 0);
                }
                cur += nb_pts;
        }
        mesh_update_bounds(mesh);
}


#ifndef GPAC_DISABLE_VRML


#define COL_TO_RGBA(res, col) { res.red = col.red; res.green = col.green; res.blue = col.blue; res.alpha = FIX_ONE; }


#define MESH_GET_COL(thecol, index)     {\
        if (colorRGB && ((u32) index < colorRGB->color.count) ) COL_TO_RGBA(thecol, colorRGB->color.vals[index])        \
        else if (colorRGBA && (u32) index < colorRGBA->color.count) thecol = colorRGBA->color.vals[index]; \
        } \
 
void mesh_new_ils(GF_Mesh *mesh, GF_Node *__coord, MFInt32 *coordIndex, GF_Node *__color, MFInt32 *colorIndex, Bool colorPerVertex, Bool do_close)
{
        u32 i, n, count, c_count, col_count;
        u32 index;
        u32 first_idx, last_idx;
        Bool move_to;
        SFVec3f pt;
        SFColorRGBA colRGBA;
        Bool has_color, has_coord;
        M_Coordinate2D *coord2D = (M_Coordinate2D*) __coord;
        M_Coordinate *coord = (M_Coordinate*) __coord;
        M_Color *colorRGB = (M_Color *) __color;
#ifndef GPAC_DISABLE_X3D
        X_ColorRGBA *colorRGBA = (X_ColorRGBA *) __color;
#endif

        if (__coord && (gf_node_get_tag(__coord) == TAG_MPEG4_Coordinate2D)) {
                coord = NULL;
        } else {
                coord2D = NULL;
        }

        colRGBA.red = colRGBA.green = colRGBA.blue = colRGBA.alpha = 0;

        if (!coord2D && !coord) return;
        c_count = coord2D ? coord2D->point.count : coord->point.count;
        if (!c_count) return;

        count = coordIndex->count;
        has_coord = count ? 1 : 0;
        if (!has_coord) count = c_count;

        if (!colorIndex->vals) colorIndex = coordIndex;
        col_count = colorIndex->count ? colorIndex->count : c_count;
        /*not enough color indices, use coord ones*/
        if (colorPerVertex && (col_count<count) ) {
                colorIndex = coordIndex;
                col_count = count;
        }
        has_color = 0;
        if (__color) {
#ifndef GPAC_DISABLE_X3D
                if (gf_node_get_tag(__color)==TAG_X3D_ColorRGBA) {
                        colorRGB = NULL;
                        has_color = (colorRGBA->color.count) ? 1 : 0;
                } else
#endif
                {
#ifndef GPAC_DISABLE_X3D
                        colorRGBA = NULL;
#endif
                        has_color = (colorRGB->color.count) ? 1 : 0;
                }
        }

        mesh_reset(mesh);
        mesh->mesh_type = MESH_LINESET;
        if (has_color) mesh->flags |= MESH_HAS_COLOR;

        n = 0;
        if (has_color && !colorPerVertex) {
                index = colorIndex->count ? colorIndex->vals[0] : 0;
#ifndef GPAC_DISABLE_X3D
                if ((u32) index < col_count) MESH_GET_COL(colRGBA, index);
#endif
        }
        move_to = 1;

        first_idx = last_idx = 0;
        for (i=0; i<count; i++) {
                if (has_coord && coordIndex->vals[i] == -1) {
                        /*close color per vertex*/
                        if (!move_to && do_close && !gf_vec_equal(mesh->vertices[first_idx].pos, mesh->vertices[last_idx].pos) ) {
                                mesh_set_line(mesh, last_idx, first_idx);
                        }
                        move_to = 1;
                        n++;
                        if (has_color && !colorPerVertex) {
                                if (n<colorIndex->count) index = colorIndex->vals[n];
                                else if (n<col_count) index = n;
                                else index = 0;
#ifndef GPAC_DISABLE_X3D
                                MESH_GET_COL(colRGBA, index);
#endif
                        }
                } else {
                        if (has_color && colorPerVertex) {
                                if (i<colorIndex->count) index = colorIndex->vals[i];
                                else if (i<col_count) index = i;
                                else index=0;
#ifndef GPAC_DISABLE_X3D
                                MESH_GET_COL(colRGBA, index);
#endif
                        }
                        if (has_coord) index = coordIndex->vals[i];
                        else index = i;
                        if (index < c_count) {
                                if (coord2D) {
                                        pt.x = coord2D->point.vals[index].x;
                                        pt.y = coord2D->point.vals[index].y;
                                        pt.z = 0;
                                } else {
                                        pt = coord->point.vals[index];
                                }
                                mesh_set_point(mesh, pt.x, pt.y, pt.z, colRGBA);
                                last_idx = mesh->v_count - 1;
                                if (move_to) {
                                        first_idx = last_idx;
                                        move_to = 0;
                                } else {
                                        mesh_set_line(mesh, last_idx-1, last_idx);
                                }
                        }
                }
        }
        /*close color per vertex*/
        if (do_close && !gf_vec_equal(mesh->vertices[first_idx].pos, mesh->vertices[last_idx].pos) ) {
                mesh_set_line(mesh, last_idx, first_idx);
        }
        if (coord2D) mesh->flags |= MESH_IS_2D;
#ifndef GPAC_DISABLE_X3D
        if (colorRGBA) mesh->flags |= MESH_HAS_ALPHA;
#endif
        mesh_update_bounds(mesh);
}

void mesh_new_ps(GF_Mesh *mesh, GF_Node *__coord, GF_Node *__color)
{
        u32 c_count, i;
        Bool has_color;
        SFVec3f pt;
        SFColorRGBA colRGBA;
        M_Coordinate2D *coord2D = (M_Coordinate2D*) __coord;
        M_Coordinate *coord = (M_Coordinate*) __coord;
        M_Color *colorRGB = (M_Color *) __color;
#ifndef GPAC_DISABLE_X3D
        X_ColorRGBA *colorRGBA = (X_ColorRGBA *) __color;
#endif

        if (__coord && (gf_node_get_tag(__coord) == TAG_MPEG4_Coordinate2D)) {
                coord = NULL;
        } else {
                coord2D = NULL;
        }

        if (!coord2D && !coord) return;
        c_count = coord2D ? coord2D->point.count : coord->point.count;
        if (!c_count) return;

        mesh_reset(mesh);
        mesh->mesh_type = MESH_POINTSET;

        has_color = 0;
        if (__color) {
#ifndef GPAC_DISABLE_X3D
                if (gf_node_get_tag(__color)==TAG_X3D_ColorRGBA) {
                        colorRGB = NULL;
                        has_color = (colorRGBA->color.count) ? 1 : 0;
                } else
#endif
                {
#ifndef GPAC_DISABLE_X3D
                        colorRGBA = NULL;
#endif
                        has_color = (colorRGB->color.count) ? 1 : 0;
                }
        }
        if (has_color) mesh->flags |= MESH_HAS_COLOR;

        colRGBA.red = colRGBA.green = colRGBA.blue = colRGBA.alpha = FIX_ONE;

        for (i=0; i<c_count; ++i) {
#ifndef GPAC_DISABLE_X3D
                if (has_color) MESH_GET_COL(colRGBA, i);
#endif
                if (coord2D) {
                        pt.x = coord2D->point.vals[i].x;
                        pt.y = coord2D->point.vals[i].y;
                        pt.z = 0;
                } else {
                        pt = coord->point.vals[i];
                }
                mesh_set_point(mesh, pt.x, pt.y, pt.z, colRGBA);
                mesh_set_index(mesh, mesh->v_count-1);
        }
        mesh_update_bounds(mesh);
}

/*structures used for normal smoothing*/
struct face_info
{
        /*face normal*/
        SFVec3f nor;
        u32 idx_alloc;
        /*nb pts in face*/
        u32 idx_count;
        /*indexes of face vertices in the pt_info structure*/
        u32 *idx;
};
/*for each pt in the mesh*/
struct pt_info
{
        u32 face_alloc;
        /*number of faces this point belongs to*/
        u32 face_count;
        /*idx of face in face_info structure*/
        u32 *faces;
};

void register_point_in_face(struct face_info *fi, u32 pt_index)
{
        if (fi->idx_count==fi->idx_alloc) {
                fi->idx_alloc += 10;
                fi->idx = (u32*)gf_realloc(fi->idx, sizeof(u32)*fi->idx_alloc);
        }
        fi->idx[fi->idx_count] = pt_index;
        fi->idx_count++;
}

void register_face_in_point(struct pt_info *pi, u32 face_index)
{
        if (pi->face_count==pi->face_alloc) {
                pi->face_alloc += 10;
                pi->faces = (u32*)gf_realloc(pi->faces, sizeof(u32)*pi->face_alloc);
        }
        pi->faces[pi->face_count] = face_index;
        pi->face_count++;
}

static GFINLINE SFVec3f smooth_face_normals(struct pt_info *pts, u32 nb_pts, struct face_info *faces, u32 nb_faces,
        u32 pt_idx_in_face, u32 face_idx, Fixed creaseAngleCos)
{
        u32 i=0;
        SFVec3f nor;
        struct face_info *fi = &faces[face_idx];
        struct pt_info *pi = &pts[fi->idx[pt_idx_in_face]];

        nor = fi->nor;
        /*for each face adjacent this point/face*/
        for (i=0; i<pi->face_count; i++) {
                /*current face, skip*/
                if (pi->faces[i]==face_idx) continue;
                if (gf_vec_dot(fi->nor, faces[pi->faces[i]].nor) > creaseAngleCos) {
                        gf_vec_add(nor, nor, faces[pi->faces[i]].nor);
                }

        }
        gf_vec_norm(&nor);
        return nor;
}


#define GET_IDX(idx, array) \
                if (idx<array->count && (array->vals[idx]>=0) ) index = array->vals[idx];       \
                else if (idx<c_count) index = idx;      \
                else index = 0; \
 
void mesh_new_ifs_intern(GF_Mesh *mesh, GF_Node *__coord, MFInt32 *coordIndex,
                         GF_Node *__color, MFInt32 *colorIndex, Bool colorPerVertex,
                         GF_Node *__normal, MFInt32 *normalIndex, Bool normalPerVertex,
                         GF_Node *__texCoords, MFInt32 *texCoordIndex,
                         Fixed creaseAngle)
{
        u32 i, n, count, c_count, nor_count;
        u32 index;
        Bool smooth_normals;
        SFVec2f tx;
        u32 s_axis, t_axis;
        SFVec3f pt, nor, bounds, center;
        SFColorRGBA colRGBA;
        Bool has_color, has_coord, has_normal, has_tex, gen_tex_coords;
        GF_Mesh **faces;
        struct face_info *faces_info;
        struct pt_info *pts_info;
        u32 face_count, cur_face;
        M_Coordinate2D *coord2D = (M_Coordinate2D*) __coord;
        M_Coordinate *coord = (M_Coordinate*) __coord;
        M_Color *colorRGB = (M_Color *) __color;
#ifndef GPAC_DISABLE_X3D
        X_ColorRGBA *colorRGBA = (X_ColorRGBA *) __color;
#endif
        M_Normal *normal = (M_Normal*) __normal;
        M_TextureCoordinate *txcoord = (M_TextureCoordinate*) __texCoords;

        nor.x = nor.y = nor.z = 0;
        center = pt = bounds = nor;
        tx.x = tx.y = 0;

        if (__coord && (gf_node_get_tag(__coord) == TAG_MPEG4_Coordinate2D)) {
                coord = NULL;
        } else {
                coord2D = NULL;
                if (!__coord)
                        return;
                /*not supported yet*/
#ifndef GPAC_DISABLE_X3D
                if (gf_node_get_tag(__coord) == TAG_X3D_CoordinateDouble)
                        return;
#endif
        }
        gen_tex_coords = 0;
#ifndef GPAC_DISABLE_X3D
        if (__texCoords && (gf_node_get_tag(__texCoords)==TAG_X3D_TextureCoordinateGenerator)) {
                gen_tex_coords = 1;
                txcoord = NULL;
        }
#endif

        if (!coord2D && !coord) return;
        c_count = coord2D ? coord2D->point.count : coord->point.count;
        if (!c_count) return;

        if (normal && normalIndex) {
                if (!normalIndex->vals) normalIndex = coordIndex;
                nor_count = normalIndex->count ? normalIndex->count : c_count;
                has_normal = normal->vector.count ? 1 : 0;
        } else {
                nor_count = 0;
                nor.x = nor.y = 0;
                nor.z = FIX_ONE;
                has_normal = 0;
        }

        has_tex = txcoord ? 1 : 0;
        if (has_tex && !texCoordIndex->vals) texCoordIndex = coordIndex;

        mesh_reset(mesh);
        memset(&colRGBA, 0, sizeof(SFColorRGBA));
        /*compute bounds - note we assume all points in the IFS coordinate are used*/
        s_axis = t_axis = 0;
        if (!has_tex) {
                for (i=0; i<c_count; i++) {
                        if (coord2D) mesh_set_point(mesh, coord2D->point.vals[i].x, coord2D->point.vals[i].y, 0, colRGBA);
                        else mesh_set_point(mesh, coord->point.vals[i].x, coord->point.vals[i].y, coord->point.vals[i].z, colRGBA);
                }
                mesh_update_bounds(mesh);
                gf_vec_diff(bounds, mesh->bounds.max_edge, mesh->bounds.min_edge);
                center = mesh->bounds.min_edge;
                if ( (bounds.x >= bounds.y) && (bounds.x >= bounds.z) ) {
                        s_axis = 0;
                        t_axis = (bounds.y >= bounds.z) ? 1 : 2;
                }
                else if ( (bounds.y >= bounds.x) && (bounds.y >= bounds.z) ) {
                        s_axis = 1;
                        t_axis = (bounds.x >= bounds.z) ? 0 : 2;
                }
                else {
                        s_axis = 2;
                        t_axis = (bounds.x >= bounds.y) ? 0 : 1;
                }
        }

        has_color = 0;
        if (__color) {
#ifndef GPAC_DISABLE_X3D
                if (gf_node_get_tag(__color)==TAG_X3D_ColorRGBA) {
                        colorRGB = NULL;
                        has_color = (colorRGBA->color.count) ? 1 : 0;
                } else
#endif
                {
#ifndef GPAC_DISABLE_X3D
                        colorRGBA = NULL;
#endif
                        has_color = (colorRGB->color.count) ? 1 : 0;
                }
        }
        n = 0;
        if (has_color) {
                if (!colorPerVertex) {
                        index = colorIndex->count ? colorIndex->vals[0] : 0;
#ifndef GPAC_DISABLE_X3D
                        MESH_GET_COL(colRGBA, index);
#endif
                } else {
                        if (!colorIndex->vals) colorIndex = coordIndex;
                }
        }

        if (has_normal && !normalPerVertex) {
                index = normalIndex->count ? normalIndex->vals[0] : 0;
                if (index < nor_count) nor = normal->vector.vals[index];
        }

        count = coordIndex->count;
        has_coord = count ? 1 : 0;
        if (!has_coord) count = c_count;

        smooth_normals = (!has_normal && coord && (creaseAngle > FIX_EPSILON)) ? 1 : 0;

        /*build face list*/
        if (!has_coord) {
                face_count = 1;
        } else {
                face_count = 0;
                for (i=0; i<count; i++) {
                        if (coordIndex->vals[i] == -1) face_count++;
                }
                /*don't forget last face*/
                if (coordIndex->vals[count-1] != -1) face_count++;
        }

        faces = (GF_Mesh**)gf_malloc(sizeof(GF_Mesh *)*face_count);
        for (i=0; i<face_count; i++) {
                faces[i] = new_mesh();
                if (coord2D) faces[i]->flags = MESH_IS_2D;
        }
        faces_info = NULL;
        pts_info = NULL;

        /*alloc face & normals tables*/
        if (smooth_normals) {
                faces_info = (struct face_info*)gf_malloc(sizeof(struct face_info)*face_count);
                memset(faces_info, 0, sizeof(struct face_info)*face_count);
                pts_info = (struct pt_info*)gf_malloc(sizeof(struct pt_info)*c_count);
                memset(pts_info, 0, sizeof(struct pt_info)*c_count);
        }

        cur_face = 0;
        for (i=0; i<count; i++) {
                if (has_coord && coordIndex->vals[i] == -1) {
                        n++;
                        if (has_color && !colorPerVertex) {
                                GET_IDX(n, colorIndex);
#ifndef GPAC_DISABLE_X3D
                                MESH_GET_COL(colRGBA, index);
#endif
                        }
                        if (has_normal && !normalPerVertex) {
                                GET_IDX(n, normalIndex);
                                if (index < nor_count) nor = normal->vector.vals[index];
                        }
                        if (faces[cur_face]->v_count<3) faces[cur_face]->v_count=0;
                        /*compute face normal - watchout for colinear vectors*/
                        else if (smooth_normals) {
                                SFVec3f v1, v2, fn;
                                u32 k=2;
                                gf_vec_diff(v1, faces[cur_face]->vertices[1].pos, faces[cur_face]->vertices[0].pos);
                                while (k<faces[cur_face]->v_count) {
                                        gf_vec_diff(v2, faces[cur_face]->vertices[k].pos, faces[cur_face]->vertices[0].pos);
                                        fn = gf_vec_cross(v1, v2);
                                        if (gf_vec_len(fn)) {
                                                gf_vec_norm(&fn);
                                                faces_info[cur_face].nor = fn;
                                                break;
                                        }
                                        k++;
                                }
                        }
                        cur_face++;
                } else {
                        if (has_color && colorPerVertex) {
                                GET_IDX(i, colorIndex);
#ifndef GPAC_DISABLE_X3D
                                MESH_GET_COL(colRGBA, index);
#endif
                        }
                        if (has_normal && normalPerVertex) {
                                GET_IDX(i, normalIndex);
                                if (index < normal->vector.count) {
                                        nor = normal->vector.vals[index];
                                }
                        }

                        if (has_coord) index = coordIndex->vals[i];
                        else index = i;
                        if (index < c_count) {
                                if (coord2D) {
                                        pt.x = coord2D->point.vals[index].x;
                                        pt.y = coord2D->point.vals[index].y;
                                        pt.z = 0;
                                } else {
                                        pt = coord->point.vals[index];
                                }
                                /*update face to point and point to face structures*/
                                if (smooth_normals) {
                                        register_point_in_face(&faces_info[cur_face], index);
                                        register_face_in_point(&pts_info[index], cur_face);
                                }
                        }

                        if (has_tex) {
                                GET_IDX(i, texCoordIndex);
                                if (index < txcoord->point.count) tx = txcoord->point.vals[index];
                        } else {
                                SFVec3f v;
                                gf_vec_diff(v, pt, center);
                                tx.x = tx.y = 0;
                                if (s_axis==0) tx.x = gf_divfix(v.x, bounds.x);
                                else if (s_axis==1) tx.x = gf_divfix(v.y, bounds.y);
                                else if (s_axis==2) tx.x = gf_divfix(v.z, bounds.z);
                                if (t_axis==0) tx.y = gf_divfix(v.x, bounds.x);
                                else if (t_axis==1) tx.y = gf_divfix(v.y, bounds.y);
                                else if (t_axis==2) tx.y = gf_divfix(v.z, bounds.z);
                        }

                        mesh_set_vertex_v(faces[cur_face], pt, nor, tx, colRGBA);
                }
        }

        /*generate normals*/
        if (!has_normal && coord) {
                u32 j;
                if (smooth_normals) {
                        Fixed cosCrease;
                        /*we only support 0->PI, whatever exceeds is smoothest*/
                        if (creaseAngle>GF_PI) cosCrease = -FIX_ONE;
                        else cosCrease = gf_cos(creaseAngle);

                        for (i=0; i<face_count; i++) {
                                for (j=0; j<faces[i]->v_count; j++) {
                                        SFVec3f n = smooth_face_normals(pts_info, c_count, faces_info, face_count, j, i, cosCrease);
                                        MESH_SET_NORMAL(faces[i]->vertices[j], n);
                                }
                        }

                        if (faces_info) {
                                for (i=0; i<face_count; i++) if (faces_info[i].idx) gf_free(faces_info[i].idx);
                                gf_free(faces_info);
                        }
                        if (pts_info) {
                                for (i=0; i<c_count; i++) if (pts_info[i].faces) gf_free(pts_info[i].faces);
                                gf_free(pts_info);
                        }
                        mesh->flags |= MESH_IS_SMOOTHED;
                } else {
                        for (i=0; i<face_count; i++) {
                                SFVec3f v1, v2, n;
                                if (! faces[i] || ! faces[i]->vertices) continue;
                                gf_vec_diff(v1, faces[i]->vertices[1].pos, faces[i]->vertices[0].pos);
                                gf_vec_diff(v2, faces[i]->vertices[2].pos, faces[i]->vertices[0].pos);
                                n = gf_vec_cross(v1, v2);
                                if (!n.x && !n.y && !n.z) n.z = FIX_ONE;
                                else gf_vec_norm(&n);
                                for (j=0; j<faces[i]->v_count; j++)
                                        MESH_SET_NORMAL(faces[i]->vertices[j], n);
                        }
                }
        }

        mesh_reset(mesh);
        mesh->mesh_type = MESH_TRIANGLES;
        if (has_color) mesh->flags |= MESH_HAS_COLOR;

        for (i=0; i<face_count; i++) {
                if (! faces[i]) continue;
                if (faces[i]->v_count) TesselateFaceMesh(mesh, faces[i]);
                mesh_free(faces[i]);
        }
        gf_free(faces);
        mesh_update_bounds(mesh);

        if (!coord2D) gf_mesh_build_aabbtree(mesh);

#ifndef GPAC_DISABLE_X3D
        if (colorRGBA) mesh->flags |= MESH_HAS_ALPHA;
        if (gen_tex_coords) mesh_generate_tex_coords(mesh, __texCoords);
#endif
}

void mesh_new_ifs2d(GF_Mesh *mesh, GF_Node *node)
{
        M_IndexedFaceSet2D *ifs2D = (M_IndexedFaceSet2D *)node;
        mesh_new_ifs_intern(mesh, ifs2D->coord, &ifs2D->coordIndex,
                            ifs2D->color, &ifs2D->colorIndex, ifs2D->colorPerVertex,
                            NULL, NULL, 0, ifs2D->texCoord, &ifs2D->texCoordIndex, 0);

        mesh->flags |= MESH_IS_2D;
}

void mesh_new_ifs(GF_Mesh *mesh, GF_Node *node)
{
        M_IndexedFaceSet *ifs = (M_IndexedFaceSet *)node;
        mesh_new_ifs_intern(mesh, ifs->coord, &ifs->coordIndex, ifs->color, &ifs->colorIndex, ifs->colorPerVertex,
                            ifs->normal, &ifs->normalIndex, ifs->normalPerVertex, ifs->texCoord, &ifs->texCoordIndex, ifs->creaseAngle);

        if (ifs->solid) mesh->flags |= MESH_IS_SOLID;
        if (!ifs->ccw) mesh->flags |= MESH_IS_CW;
}

void mesh_new_elevation_grid(GF_Mesh *mesh, GF_Node *node)
{
        u32 i, j, face_count, pt_count, zDimension, xDimension, cur_face, idx, pt_idx;
        Bool has_normal, has_txcoord, has_color, smooth_normals;
        GF_Mesh **faces;
        GF_Vertex vx;
        SFVec3f v1, v2, n;
        struct face_info *faces_info;
        struct pt_info *pts_info;
        M_ElevationGrid *eg = (M_ElevationGrid *) node;
        M_Normal *norm = (M_Normal *)eg->normal;
        M_Color *colorRGB = (M_Color *)eg->color;
#ifndef GPAC_DISABLE_X3D
        X_ColorRGBA *colorRGBA = (X_ColorRGBA *)eg->color;
#endif
        SFColorRGBA rgba;
        M_TextureCoordinate *txc = (M_TextureCoordinate *)eg->texCoord;

        mesh_reset(mesh);
        if (!eg->height.count || (eg->xDimension<2) || (eg->zDimension<2)) return;

        memset(&vx, 0, sizeof(GF_Vertex));
        memset(&rgba, 0, sizeof(SFColorRGBA));
        has_txcoord = txc ? txc->point.count : 0;
        has_normal = norm ? norm->vector.count : 0;
        has_color = 0;
        if (eg->color) {
#ifndef GPAC_DISABLE_X3D
                if (gf_node_get_tag(eg->color)==TAG_X3D_ColorRGBA) {
                        colorRGB = NULL;
                        has_color = colorRGBA->color.count ? 1 : 0;
                } else
#endif
                {
#ifndef GPAC_DISABLE_X3D
                        colorRGBA = NULL;
#endif
                        has_color = colorRGB->color.count ? 1 : 0;
                }
        }
        face_count = (eg->xDimension-1) * (eg->zDimension-1);
        pt_count = eg->xDimension * eg->zDimension;
        if (pt_count>eg->height.count) return;

        smooth_normals = (!has_normal && (eg->creaseAngle > FIX_EPSILON)) ? 1 : 0;

        faces = NULL;
        faces_info = NULL;
        pts_info = NULL;

        zDimension = (u32) eg->zDimension;
        xDimension = (u32) eg->xDimension;
        cur_face = 0;
        pt_idx = 0;

        /*basic case: nothing specified but the points*/
        if (!smooth_normals && !has_color && !has_normal && !has_txcoord) {
                for (j=0; j<zDimension; j++) {
                        for (i=0; i<xDimension; i++) {
                                vx.pos.z = eg->zSpacing * j;
                                vx.pos.y = eg->height.vals[i +j*xDimension];
                                vx.pos.x = eg->xSpacing * i;
                                vx.texcoords.x = INT2FIX(i) / (xDimension - 1);
                                vx.texcoords.y = INT2FIX(j) / (zDimension - 1);

                                mesh_set_vertex_vx(mesh, &vx);
                        }
                }
                for (j=0; j<zDimension-1; j++) {
                        u32 z0 = (j)*xDimension;
                        u32 z1 = (j+1)*xDimension;
                        for (i=0; i<xDimension-1; i++) {
                                mesh_set_triangle(mesh, i+z0, i+z1, i+z1+1);
                                mesh_set_triangle(mesh, i+z0, i+z1+1, i+z0+1);

                                gf_vec_diff(v1, mesh->vertices[i+z0].pos, mesh->vertices[i+z0+1].pos);
                                gf_vec_diff(v2, mesh->vertices[i+z1+1].pos, mesh->vertices[i+z0+1].pos);
                                n = gf_vec_cross(v1, v2);
                                gf_vec_norm(&n);
                                MESH_SET_NORMAL(mesh->vertices[i+z0], n);
                                MESH_SET_NORMAL(mesh->vertices[i+z0+1], n);
                                MESH_SET_NORMAL(mesh->vertices[i+z1], n);
                                MESH_SET_NORMAL(mesh->vertices[i+z1+1], n);
                        }
                }
                mesh->mesh_type = MESH_TRIANGLES;
                mesh_update_bounds(mesh);
                if (!eg->ccw) mesh->flags |= MESH_IS_CW;
                if (eg->solid) mesh->flags |= MESH_IS_SOLID;
                gf_mesh_build_aabbtree(mesh);
                return;
        }

        /*alloc face & normals tables*/
        if (smooth_normals) {
                faces = (GF_Mesh **)gf_malloc(sizeof(GF_Mesh *)*face_count);
                faces_info = (struct face_info*)gf_malloc(sizeof(struct face_info)*face_count);
                memset(faces_info, 0, sizeof(struct face_info)*face_count);
                pts_info = (struct pt_info*)gf_malloc(sizeof(struct pt_info)*pt_count);
                memset(pts_info, 0, sizeof(struct pt_info)*pt_count);
                faces[cur_face] = new_mesh();
        }

        for (j=0; j<zDimension-1; j++) {
                for (i = 0; i < xDimension-1; i++) {
                        u32 k, l;
                        /*get face color*/
                        if (has_color && !eg->colorPerVertex) {
                                idx = i + j * (xDimension-1);
#ifndef GPAC_DISABLE_X3D
                                MESH_GET_COL(rgba, idx);
#endif
                                vx.color = MESH_MAKE_COL(rgba);
                        }
                        /*get face normal*/
                        if (has_normal && !eg->normalPerVertex) {
                                idx = i + j * (xDimension-1);
                                if (idx<norm->vector.count) {
                                        SFVec3f n = norm->vector.vals[idx];
                                        gf_vec_norm(&n);
                                        MESH_SET_NORMAL(vx, n);
                                }
                        }

                        for (k=0; k<2; k++) {
                                vx.pos.z = eg->zSpacing * (j+k);
                                for (l=0; l<2; l++) {

                                        vx.pos.y = eg->height.vals[(i+l) +(j+k)*xDimension];
                                        vx.pos.x = eg->xSpacing * (i+l);

                                        /*get color per vertex*/
                                        if (has_color && eg->colorPerVertex) {
                                                idx = i+l + (j+k) * xDimension;
#ifndef GPAC_DISABLE_X3D
                                                MESH_GET_COL(rgba, idx);
#endif
                                                vx.color = MESH_MAKE_COL(rgba);
                                        }
                                        /*get tex coord*/
                                        if (!has_txcoord) {
                                                vx.texcoords.x = INT2FIX(i+l) / (xDimension - 1);
                                                vx.texcoords.y = INT2FIX(j+k) / (zDimension - 1);
                                        } else {
                                                idx = (i+l) +(j+k)*xDimension;
                                                if (idx<txc->point.count) vx.texcoords = txc->point.vals[idx];
                                        }
                                        /*get normal per vertex*/
                                        if (has_normal && eg->normalPerVertex) {
                                                idx = (i+l) + (j+k) * xDimension;
                                                if (idx<norm->vector.count) {
                                                        SFVec3f n = norm->vector.vals[idx];
                                                        gf_vec_norm(&n);
                                                        MESH_SET_NORMAL(vx, n);
                                                }
                                        }
                                        /*update face to point and point to face structures*/
                                        if (smooth_normals) {
                                                mesh_set_vertex_vx(faces[cur_face], &vx);
                                                register_point_in_face(&faces_info[cur_face], (i+l) + (j+k)*xDimension);
                                                register_face_in_point(&pts_info[(i+l) + (j+k)*xDimension], cur_face);
                                        } else {
                                                mesh_set_vertex_vx(mesh, &vx);
                                        }
                                }

                        }

                        /*compute face normal*/
                        if (smooth_normals) {
                                mesh_set_triangle(faces[cur_face], 0, 2, 3);
                                mesh_set_triangle(faces[cur_face], 0, 3, 1);
                                gf_vec_diff(v1, faces[cur_face]->vertices[0].pos, faces[cur_face]->vertices[1].pos);
                                gf_vec_diff(v2, faces[cur_face]->vertices[3].pos, faces[cur_face]->vertices[1].pos);
                                faces_info[cur_face].nor = gf_vec_cross(v1, v2);
                                gf_vec_norm(&faces_info[cur_face].nor);
                                /*done with face*/
                                cur_face++;
                                if (cur_face<face_count) faces[cur_face] = new_mesh();
                        } else {
                                mesh_set_triangle(mesh, pt_idx+0, pt_idx+2, pt_idx+3);
                                mesh_set_triangle(mesh, pt_idx+0, pt_idx+3, pt_idx+1);

                                if (!has_normal) {
                                        gf_vec_diff(v1, mesh->vertices[pt_idx+0].pos, mesh->vertices[pt_idx+1].pos);
                                        gf_vec_diff(v2, mesh->vertices[pt_idx+3].pos, mesh->vertices[pt_idx+1].pos);
                                        n = gf_vec_cross(v1, v2);
                                        gf_vec_norm(&n);
                                        MESH_SET_NORMAL(mesh->vertices[pt_idx+0], n);
                                        MESH_SET_NORMAL(mesh->vertices[pt_idx+1], n);
                                        MESH_SET_NORMAL(mesh->vertices[pt_idx+2], n);
                                        MESH_SET_NORMAL(mesh->vertices[pt_idx+3], n);
                                }
                                pt_idx+=4;
                        }
                }
        }

        /*generate normals*/
        if (smooth_normals) {
                Fixed cosCrease;
                /*we only support 0->PI, whatever exceeds is smoothest*/
                if (eg->creaseAngle>GF_PI) cosCrease = -FIX_ONE;
                else cosCrease = gf_cos(eg->creaseAngle);

                for (i=0; i<face_count; i++) {
                        for (j=0; j<faces[i]->v_count; j++) {
                                SFVec3f n = smooth_face_normals(pts_info, pt_count, faces_info, face_count, j, i, cosCrease);
                                MESH_SET_NORMAL(faces[i]->vertices[j], n);
                        }
                }

                if (faces_info) {
                        for (i=0; i<face_count; i++) if (faces_info[i].idx) gf_free(faces_info[i].idx);
                        gf_free(faces_info);
                }
                if (pts_info) {
                        for (i=0; i<pt_count; i++) if (pts_info[i].faces) gf_free(pts_info[i].faces);
                        gf_free(pts_info);
                }
                mesh->flags |= MESH_IS_SMOOTHED;


                for (i=0; i<face_count; i++) {
                        if (faces[i]->v_count) {
                                u32 init_idx;
                                GF_Mesh *face = faces[i];
                                init_idx = mesh->v_count;
                                /*quads only*/
                                mesh_set_vertex_vx(mesh, &face->vertices[0]);
                                mesh_set_vertex_vx(mesh, &face->vertices[1]);
                                mesh_set_vertex_vx(mesh, &face->vertices[2]);
                                mesh_set_vertex_vx(mesh, &face->vertices[3]);
                                mesh_set_triangle(mesh, init_idx, init_idx + 2, init_idx + 3);
                                mesh_set_triangle(mesh, init_idx, init_idx + 3, init_idx + 1);
                        }
                        mesh_free(faces[i]);
                }

                /*destroy faces*/
                gf_free(faces);
        }

        mesh->mesh_type = MESH_TRIANGLES;
        if (has_color) mesh->flags |= MESH_HAS_COLOR;
        mesh_update_bounds(mesh);
        if (!eg->ccw) mesh->flags |= MESH_IS_CW;
        if (eg->solid) mesh->flags |= MESH_IS_SOLID;
#ifndef GPAC_DISABLE_X3D
        if (colorRGBA) mesh->flags |= MESH_HAS_ALPHA;
#endif
        gf_mesh_build_aabbtree(mesh);
}


typedef struct
{
        SFVec3f yaxis, zaxis, xaxis;
} SCP;

typedef struct
{
        SFVec3f pt, yaxis, zaxis, xaxis;
        u32 max_idx;
} SCPInfo;

#define REGISTER_POINT_FACE(FACE_IDX) \
        {       u32 fidx;       \
                fidx = FACE_IDX; \
                mesh_set_vertex_vx(faces[fidx], &vx);   \
                if (smooth_normals) {   \
                        register_point_in_face(&faces_info[fidx], pidx);        \
                        register_face_in_point(&pts_info[pidx], fidx);  \
                } \
        } \
 

#define NEAR_ZERO(__x) (ABS(__x)<=FIX_EPSILON)

static void mesh_extrude_path_intern(GF_Mesh *mesh, GF_Path *path, MFVec3f *thespine, Fixed creaseAngle, Fixed min_cx, Fixed min_cy, Fixed width_cx, Fixed width_cy, Bool begin_cap, Bool end_cap, MFRotation *spine_ori, MFVec2f *spine_scale, Bool tx_along_spine)
{
        GF_Mesh **faces;
        GF_Vertex vx;
        struct face_info *faces_info;
        struct pt_info *pts_info;
        GF_Matrix mx;
        SCP *SCPs, SCPbegin, SCPend;
        SCPInfo *SCPi;
        Bool smooth_normals, spine_closed, check_first_spine_vec, do_close;
        u32 i, j, k, nb_scp, nb_spine, face_count, pt_count, faces_per_cross, begin_face, end_face, face_spines, pts_per_cross, cur_pts_in_cross,cur, nb_pts, convexity;
        SFVec3f *spine, v1, v2, n, spine_vec;
        Fixed cross_len, spine_len, cur_cross, cur_spine;
        SFRotation r;
        SFVec2f scale;

        if (!path->n_contours) return;
        if (path->n_points<2) return;
        if (thespine->count<2) return;

        spine_closed = 0;
        if (gf_vec_equal(thespine->vals[0], thespine->vals[thespine->count-1])) spine_closed = 1;
        if (spine_closed && (thespine->count==2)) return;

        gf_path_flatten(path);

        memset(&vx, 0, sizeof(GF_Vertex));
        pts_per_cross = 0;
        cross_len = 0;
        cur = 0;
        for (i=0; i<path->n_contours; i++) {
                nb_pts = 1 + path->contours[i] - cur;
                pts_per_cross += nb_pts;
                v1.z = 0;
                for (j=1; j<nb_pts; j++) {
                        v1.x = path->points[j+cur].x - path->points[j-1+cur].x;
                        v1.y = path->points[j+cur].y - path->points[j-1+cur].y;
                        cross_len += gf_vec_len(v1);
                }
        }

        faces_per_cross = pts_per_cross - path->n_contours;
        begin_face = end_face = 0;
        face_spines = face_count = (thespine->count-1)*faces_per_cross;
        if (begin_cap) {
                begin_face = face_count;
                face_count ++;
        }
        if (end_cap) {
                end_face = face_count;
                face_count ++;
        }

        pt_count = pts_per_cross * thespine->count;
        smooth_normals = NEAR_ZERO(creaseAngle) ? 0 : 1;

        faces = (GF_Mesh**)gf_malloc(sizeof(GF_Mesh *)*face_count);
        for (i=0; i<face_count; i++) faces[i] = new_mesh();
        faces_info = NULL;
        pts_info = NULL;

        /*alloc face & normals tables*/
        if (smooth_normals) {
                faces_info = (struct face_info*)gf_malloc(sizeof(struct face_info)*face_count);
                memset(faces_info, 0, sizeof(struct face_info)*face_count);
                pts_info = (struct pt_info*)gf_malloc(sizeof(struct pt_info)*pt_count);
                memset(pts_info, 0, sizeof(struct pt_info)*pt_count);
        }


        spine = thespine->vals;
        nb_spine = thespine->count;
        SCPs = (SCP *)gf_malloc(sizeof(SCP) * nb_spine);
        memset(SCPs, 0, sizeof(SCP) * nb_spine);
        SCPi = (SCPInfo *) gf_malloc(sizeof(SCPInfo) * nb_spine);
        memset(SCPi, 0, sizeof(SCPInfo) * nb_spine);

        /*collect all # SCPs:
        1- if a spine has identical consecutive points with # orientation, these points use the same SCPs
        2- if 2 segs of the spine are colinear, they also use the same SCP
        */
        SCPi[0].pt = spine[0];
        SCPi[0].max_idx = 0;
        nb_scp=1;
        spine_len = 0;
        check_first_spine_vec = 1;
        for (i=1; i<nb_spine; i++) {
                Fixed len;
                /*also get spine length for txcoord*/
                gf_vec_diff(v2, spine[i], spine[i-1]);
                len = gf_vec_len(v2);
                spine_len += len;
                if (check_first_spine_vec && len) {
                        check_first_spine_vec = 0;
                        spine_vec = v2;
                }

                /*case 1: same point, same SCP*/
                if (gf_vec_equal(SCPi[nb_scp-1].pt, spine[i])) {
                        SCPi[nb_scp-1].max_idx = i;
                        continue;
                }
                /*last point in spine*/
                if (i+1 == nb_spine) {
                        nb_scp++;
                        SCPi[nb_scp-1].pt = spine[i];
                        SCPi[nb_scp-1].max_idx = i;
                        break;
                }

                gf_vec_diff(v1, spine[i+1], spine[i]);
                gf_vec_diff(v2, SCPi[nb_scp-1].pt, spine[i]);
                n = gf_vec_cross(v1, v2);
                /*case 2: spine segs are colinear*/
                if (!n.x && !n.y && !n.z) {
                        SCPi[nb_scp-1].max_idx = i;
                }
                /*OK new SCP*/
                else {
                        nb_scp++;
                        SCPi[nb_scp-1].pt = spine[i];
                        SCPi[nb_scp-1].max_idx = i;
                }
        }

        /*all colinear!!*/
        if (nb_scp<=2) {
                SCPi[0].xaxis.x = FIX_ONE;
                SCPi[0].xaxis.y = 0;
                SCPi[0].xaxis.z = 0;
                SCPi[0].yaxis.x = 0;
                SCPi[0].yaxis.y = FIX_ONE;
                SCPi[0].yaxis.z = 0;
                SCPi[0].zaxis.x = 0;
                SCPi[0].zaxis.y = 0;
                SCPi[0].zaxis.z = FIX_ONE;
                /*compute rotation from (0, 1, 0) to spine_vec*/
                if (!check_first_spine_vec) {
                        Fixed alpha, gamma;
                        Fixed cos_a, sin_a, sin_g, cos_g;

                        gf_vec_norm(&spine_vec);
                        if (! NEAR_ZERO(spine_vec.x) ) {
                                if (spine_vec.x >= FIX_ONE-FIX_EPSILON) alpha = GF_PI2;
                                else if (spine_vec.x <= -FIX_ONE+FIX_EPSILON) alpha = -GF_PI2;
                                else alpha = gf_asin(spine_vec.x);
                                cos_a = gf_cos(alpha);
                                sin_a = spine_vec.x;

                                if (NEAR_ZERO(cos_a)) gamma = 0;
                                else {
                                        Fixed __abs;
                                        gamma = gf_acos(gf_divfix(spine_vec.y, cos_a));
                                        sin_g = gf_sin(gamma);
                                        __abs = gf_divfix(spine_vec.z, cos_a) + sin_g;
                                        if (ABS(__abs) > ABS(sin_g) ) gamma *= -1;
                                }
                                cos_g = gf_cos(gamma);
                                if (NEAR_ZERO(cos_g)) {
                                        cos_g = 0;
                                        sin_g = FIX_ONE;
                                } else {
                                        sin_g = gf_sin(gamma);
                                }
                                SCPi[0].yaxis.y = gf_mulfix(cos_a, sin_g);
                                SCPi[0].yaxis.z = gf_mulfix(cos_a, cos_g);
                                SCPi[0].yaxis.x = sin_a;
                                SCPi[0].zaxis.y = -gf_mulfix(sin_a, sin_g);
                                SCPi[0].zaxis.z = -gf_mulfix(sin_a, cos_g);
                                SCPi[0].zaxis.x = cos_a;
                        }
                        if (! NEAR_ZERO(spine_vec.z) ) {
                                if (spine_vec.z >= FIX_ONE-FIX_EPSILON) alpha = GF_PI2;
                                else if (spine_vec.z <= -FIX_ONE+FIX_EPSILON) alpha = -GF_PI2;
                                else alpha = gf_asin(spine_vec.z);
                                cos_a = gf_cos(alpha);
                                sin_a = spine_vec.z;

                                if (NEAR_ZERO(cos_a) ) gamma = 0;
                                else {
                                        Fixed __abs;
                                        gamma = gf_acos(gf_divfix(spine_vec.y, cos_a));
                                        sin_g = gf_sin(gamma);
                                        __abs = gf_divfix(spine_vec.x, cos_a) + sin_g;
                                        if (ABS(__abs) > ABS(sin_g) ) gamma *= -1;
                                }
                                cos_g = gf_cos(gamma);
                                sin_g = gf_sin(gamma);
                                SCPi[0].yaxis.y = gf_mulfix(cos_a, sin_g);
                                SCPi[0].yaxis.x = gf_mulfix(cos_a, cos_g);
                                SCPi[0].yaxis.z = sin_a;
                                SCPi[0].zaxis.y = -gf_mulfix(sin_a, sin_g);
                                SCPi[0].zaxis.x = -gf_mulfix(sin_a, cos_g);
                                SCPi[0].zaxis.z = cos_a;
                        }
                }
                for (i=0; i<nb_spine; i++) {
                        SCPs[i].xaxis = SCPi[0].xaxis;
                        SCPs[i].yaxis = SCPi[0].yaxis;
                        SCPs[i].zaxis = SCPi[0].zaxis;
                }
        }
        /*not colinear*/
        else {
                assert(nb_scp<=nb_spine);

                /*now non-cap SCPs axis*/
                for (i=1; i<nb_scp-1; i++) {
                        /*get Y axis*/
                        gf_vec_diff(SCPi[i].yaxis, SCPi[i+1].pt, SCPi[i-1].pt);
                        /*get Z axis*/
                        gf_vec_diff(v1, SCPi[i+1].pt, SCPi[i].pt);
                        gf_vec_diff(v2, SCPi[i-1].pt, SCPi[i].pt);
                        SCPi[i].zaxis = gf_vec_cross(v1, v2);
                }
                /*compute head and tail*/
                if (spine_closed) {
                        gf_vec_diff(SCPi[0].yaxis, SCPi[1].pt, SCPi[nb_scp-2].pt);
                        gf_vec_diff(v1, SCPi[1].pt, SCPi[0].pt);
                        gf_vec_diff(v2, SCPi[nb_scp-2].pt, SCPi[0].pt);
                        SCPi[0].zaxis = gf_vec_cross(v1, v2);
                        SCPi[nb_scp-1].yaxis = SCPi[0].yaxis;
                        SCPi[nb_scp-1].zaxis = SCPi[0].zaxis;
                } else {
                        gf_vec_diff(SCPi[0].yaxis, SCPi[1].pt, SCPi[0].pt);
                        SCPi[0].zaxis = SCPi[1].zaxis;
                        gf_vec_diff(SCPi[nb_scp-1].yaxis, SCPi[nb_scp-1].pt, SCPi[nb_scp-2].pt);
                        SCPi[nb_scp-1].zaxis = SCPi[nb_scp-2].zaxis;
                }
                /*check orientation*/
                for (i=1; i<nb_scp; i++) {
                        Fixed res = gf_vec_dot(SCPi[i].zaxis, SCPi[i-1].zaxis);
                        if (res<0) gf_vec_rev(SCPi[i].zaxis);
                }
                /*and assign SCPs*/
                j=0;
                for (i=0; i<nb_scp; i++) {
                        /*compute X, norm vectors*/
                        SCPi[i].xaxis = gf_vec_cross(SCPi[i].yaxis, SCPi[i].zaxis);
                        gf_vec_norm(&SCPi[i].xaxis);
                        gf_vec_norm(&SCPi[i].yaxis);
                        gf_vec_norm(&SCPi[i].zaxis);
                        /*assign SCPs*/
                        while (j<=SCPi[i].max_idx) {
                                SCPs[j].xaxis = SCPi[i].xaxis;
                                SCPs[j].yaxis = SCPi[i].yaxis;
                                SCPs[j].zaxis = SCPi[i].zaxis;
                                j++;
                        }
                }
        }
        gf_free(SCPi);

        r.x = r.q = r.z = 0;
        r.y = FIX_ONE;
        scale.x = scale.y = FIX_ONE;

        cur_spine = 0;
        /*insert all points of the extrusion*/
        for (i=0; i<nb_spine; i++) {
                u32 cur_face_in_cross;
                SCP *curSCP;

                if (spine_closed && (i+1==nb_spine)) {
                        curSCP = &SCPs[0];
                        do_close =  1;
                } else {
                        curSCP = &SCPs[i];
                        do_close =  0;

                        /*compute X*/
                        curSCP->xaxis = gf_vec_cross(curSCP->yaxis, curSCP->zaxis);
                        gf_vec_norm(&curSCP->xaxis);
                        gf_vec_norm(&curSCP->yaxis);
                        gf_vec_norm(&curSCP->zaxis);

                        if (spine_ori && (i<spine_ori->count)) r = spine_ori->vals[i];
                        if (spine_scale && (i<spine_scale->count)) scale = spine_scale->vals[i];

                        gf_mx_init(mx);
                        gf_mx_add_rotation(&mx, r.q, r.x, r.y, r.z);
                        gf_mx_apply_vec(&mx, &curSCP->xaxis);
                        gf_mx_apply_vec(&mx, &curSCP->yaxis);
                        gf_mx_apply_vec(&mx, &curSCP->zaxis);
                }

                vx.texcoords.y = gf_divfix(cur_spine, spine_len);

                cur_pts_in_cross = 0;
                cur_face_in_cross = 0;
                cur = 0;
                for (j=0; j<path->n_contours; j++) {
                        Bool subpath_closed;
                        nb_pts = 1+path->contours[j] - cur;
                        cur_cross = 0;
                        subpath_closed = 0;
                        if ((path->points[cur].x==path->points[path->contours[j]].x) && (path->points[cur].y==path->points[path->contours[j]].y))
                                subpath_closed = 1;

                        for (k=0; k<nb_pts; k++) {
                                u32 pidx = k + cur_pts_in_cross + i*pts_per_cross;
                                if (do_close) pidx = k + cur_pts_in_cross;

                                v1.x = path->points[k+cur].x;
                                v1.z = path->points[k+cur].y;

                                if (tx_along_spine) {
                                        vx.texcoords.x = gf_divfix(cur_cross, cross_len);
                                } else {
                                        vx.texcoords.x = gf_divfix(v1.x - min_cx, width_cx);
                                        vx.texcoords.y = gf_divfix(v1.z - min_cy, width_cy);
                                }

                                /*handle closed cross-section*/
                                if (subpath_closed && (k+1==nb_pts)) {
                                        pidx = cur_pts_in_cross + i*pts_per_cross;
                                        if (do_close) pidx = cur_pts_in_cross;

                                        v1.x = path->points[cur].x;
                                        v1.z = path->points[cur].y;
                                }
                                v1.x = gf_mulfix(v1.x, scale.x);
                                v1.z = gf_mulfix(v1.z, scale.y);
                                v1.y = 0;
                                vx.pos.x = gf_mulfix(v1.x, curSCP->xaxis.x) + gf_mulfix(v1.y, curSCP->yaxis.x) + gf_mulfix(v1.z, curSCP->zaxis.x) + spine[i].x;
                                vx.pos.y = gf_mulfix(v1.x, curSCP->xaxis.y) + gf_mulfix(v1.y, curSCP->yaxis.y) + gf_mulfix(v1.z, curSCP->zaxis.y) + spine[i].y;
                                vx.pos.z = gf_mulfix(v1.x, curSCP->xaxis.z) + gf_mulfix(v1.y, curSCP->yaxis.z) + gf_mulfix(v1.z, curSCP->zaxis.z) + spine[i].z;

                                /*in current spine*/
                                if (i+1<nb_spine) {
                                        /*current face*/
                                        if (k<nb_pts-1) REGISTER_POINT_FACE(k + cur_face_in_cross + i*faces_per_cross);
                                        /*previous face*/
                                        if (k) REGISTER_POINT_FACE(k-1 + cur_face_in_cross + i*faces_per_cross);
                                }
                                /*first spine*/
                                else if (smooth_normals && do_close) {
                                        if (k<nb_pts-1) {
                                                register_point_in_face(&faces_info[k + cur_face_in_cross], pidx);
                                                register_face_in_point(&pts_info[pidx], k + cur_face_in_cross);
                                        }
                                        /*previous face*/
                                        if (k) {
                                                register_point_in_face(&faces_info[k-1 + cur_face_in_cross], pidx);
                                                register_face_in_point(&pts_info[pidx], k-1 + cur_face_in_cross);
                                        }
                                }
                                /*in previous spine*/
                                if (i) {
                                        /*face "below" face*/
                                        if (k<nb_pts-1) REGISTER_POINT_FACE(k + cur_face_in_cross + (i-1)*faces_per_cross);
                                        /*previous face "below" face*/
                                        if (k) REGISTER_POINT_FACE(k-1 + cur_face_in_cross + (i-1)*faces_per_cross);
                                }

                                if (k+1<nb_pts) {
                                        v1.z = 0;
                                        v1.x = path->points[k+1+cur].x - path->points[k+cur].x;
                                        v1.y = path->points[k+1+cur].y - path->points[k+cur].y;
                                        cur_cross += gf_vec_len(v1);
                                }

                        }
                        cur_face_in_cross += nb_pts-1;
                        cur_pts_in_cross += nb_pts;
                        cur += nb_pts;
                }
                if (i+1<nb_spine) {
                        gf_vec_diff(v1, spine[i+1], spine[i]);
                        cur_spine += gf_vec_len(v1);
                }
        }

        /*generate triangles & normals*/
        for (i=0; i<face_spines; i++) {
                mesh_set_triangle(faces[i], 0, 1, 3);
                mesh_set_triangle(faces[i], 0, 3, 2);
                gf_vec_diff(v1, faces[i]->vertices[1].pos, faces[i]->vertices[0].pos);
                gf_vec_diff(v2, faces[i]->vertices[3].pos, faces[i]->vertices[0].pos);
                n = gf_vec_cross(v1, v2);
                gf_vec_norm(&n);
                for (j=0; j<faces[i]->v_count; j++) {
                        MESH_SET_NORMAL(faces[i]->vertices[j], n);
                        if (smooth_normals) faces_info[i].nor = n;
                }
        }


        /*generate begin cap*/
        if (begin_face) {
                SFVec3f n;
                scale.x = scale.y = FIX_ONE;
                if (spine_scale && spine_scale->count) scale = spine_scale->vals[0];

                /*get first SCP after rotation*/
                SCPbegin = SCPs[0];

                n = SCPbegin.yaxis;
                gf_vec_norm(&n);

                convexity = gf_polygone2d_get_convexity(path->points, path->n_points);
                switch (convexity) {
                case GF_POLYGON_CONVEX_CCW:
                case GF_POLYGON_COMPLEX_CCW:
                        break;
                default:
                        gf_vec_rev(n);
                        break;
                }

                MESH_SET_NORMAL(vx, n);


                if (smooth_normals) {
                        faces_info[begin_face].nor = n;
                        assert(gf_vec_len(n));
                }
                cur_pts_in_cross = 0;
                cur = 0;
                for (i=0; i<path->n_contours; i++) {
                        Bool subpath_closed = 0;
                        nb_pts = 1 + path->contours[i] - cur;
                        if ((path->points[cur].x==path->points[path->contours[i]].x) && (path->points[cur].y==path->points[path->contours[i]].y))
                                subpath_closed = 1;

                        for (j=0; j<nb_pts; j++) {
                                u32 pidx = j + (pts_per_cross-1-cur_pts_in_cross);
                                v1.x = path->points[j+cur].x;
                                v1.z = path->points[j+cur].y;
                                vx.texcoords.x = gf_divfix(v1.x - min_cx, width_cx);
                                vx.texcoords.y = gf_divfix(v1.z - min_cy, width_cy);
                                /*handle closed cross-section*/
                                if (subpath_closed  && (j+1==nb_pts)) {
                                        pidx = (pts_per_cross-1-cur_pts_in_cross);
                                        v1.x = path->points[cur].x;
                                        v1.z = path->points[cur].y;
                                }
                                v1.x = gf_mulfix(v1.x , scale.x);
                                v1.z = gf_mulfix(v1.z , scale.y);
                                v1.y = 0;
                                vx.pos.x = gf_mulfix(v1.x, SCPbegin.xaxis.x) + gf_mulfix(v1.y, SCPbegin.yaxis.x) + gf_mulfix(v1.z, SCPbegin.zaxis.x) + spine[0].x;
                                vx.pos.y = gf_mulfix(v1.x, SCPbegin.xaxis.y) + gf_mulfix(v1.y, SCPbegin.yaxis.y) + gf_mulfix(v1.z, SCPbegin.zaxis.y) + spine[0].y;
                                vx.pos.z = gf_mulfix(v1.x, SCPbegin.xaxis.z) + gf_mulfix(v1.y, SCPbegin.yaxis.z) + gf_mulfix(v1.z, SCPbegin.zaxis.z) + spine[0].z;
                                REGISTER_POINT_FACE(begin_face);
                        }
                        cur_pts_in_cross += nb_pts;
                        cur += nb_pts;
                }
        }

        /*generate end cap*/
        if (end_face) {
                SFVec3f n;
                scale.x = scale.y = FIX_ONE;
                if (spine_scale && (nb_spine-1<spine_scale->count)) scale = spine_scale->vals[nb_spine-1];
                /*get last SCP after rotation*/
                SCPend = SCPs[nb_spine-1];

                n = SCPend.yaxis;
                gf_vec_norm(&n);
                MESH_SET_NORMAL(vx, n);
                convexity = gf_polygone2d_get_convexity(path->points, path->n_points);
                switch (convexity) {
                case GF_POLYGON_CONVEX_CCW:
                case GF_POLYGON_COMPLEX_CCW:
                        gf_vec_rev(vx.normal);
                        gf_vec_rev(n);
                        break;
                }

                if (smooth_normals) {
                        faces_info[end_face].nor = n;
                        assert(gf_vec_len(n));
                }
                cur_pts_in_cross = 0;

                cur = 0;
                for (i=0; i<path->n_contours; i++) {
                        Bool subpath_closed = 0;
                        nb_pts = 1 + path->contours[i] - cur;
                        if ((path->points[cur].x==path->points[path->contours[i]].x) && (path->points[cur].y==path->points[path->contours[i]].y))
                                subpath_closed = 1;

                        for (j=0; j<nb_pts; j++) {
                                u32 pidx = j + cur_pts_in_cross + (nb_spine-1)*pts_per_cross;
                                v1.x = path->points[j+cur].x;
                                v1.z = path->points[j+cur].y;
                                vx.texcoords.x = gf_divfix(v1.x - min_cx, width_cx);
                                vx.texcoords.y = gf_divfix(v1.z - min_cy, width_cy);
                                /*handle closed cross-section*/
                                if (subpath_closed && (j+1==nb_pts)) {
                                        pidx = cur_pts_in_cross + (nb_spine-1)*pts_per_cross;
                                        v1.x = path->points[cur].x;
                                        v1.z = path->points[cur].y;
                                }
                                v1.x = gf_mulfix(v1.x, scale.x);
                                v1.z = gf_mulfix(v1.z, scale.y);
                                v1.y = 0;
                                vx.pos.x = gf_mulfix(v1.x, SCPend.xaxis.x) + gf_mulfix(v1.y, SCPend.yaxis.x) + gf_mulfix(v1.z, SCPend.zaxis.x) + spine[nb_spine-1].x;
                                vx.pos.y = gf_mulfix(v1.x, SCPend.xaxis.y) + gf_mulfix(v1.y, SCPend.yaxis.y) + gf_mulfix(v1.z, SCPend.zaxis.y) + spine[nb_spine-1].y;
                                vx.pos.z = gf_mulfix(v1.x, SCPend.xaxis.z) + gf_mulfix(v1.y, SCPend.yaxis.z) + gf_mulfix(v1.z, SCPend.zaxis.z) + spine[nb_spine-1].z;

                                REGISTER_POINT_FACE(end_face);
                        }
                        cur_pts_in_cross += nb_pts;
                        cur += nb_pts;
                }
        }


        if (smooth_normals) {
                Fixed cosCrease;
                /*we only support 0->PI, whatever exceeds is smoothest*/
                if (creaseAngle>GF_PI) cosCrease = -FIX_ONE;
                else cosCrease = gf_cos(creaseAngle);

                for (i=0; i<face_count; i++) {
                        for (j=0; j<faces[i]->v_count; j++) {
                                SFVec3f n = smooth_face_normals(pts_info, pt_count, faces_info, face_count, j, i, cosCrease);
                                MESH_SET_NORMAL(faces[i]->vertices[j], n);
                        }
                }

                if (faces_info) {
                        for (i=0; i<face_count; i++) if (faces_info[i].idx) gf_free(faces_info[i].idx);
                        gf_free(faces_info);
                }
                if (pts_info) {
                        for (i=0; i<pt_count; i++) if (pts_info[i].faces) gf_free(pts_info[i].faces);
                        gf_free(pts_info);
                }
                mesh->flags |= MESH_IS_SMOOTHED;
        }

        mesh->mesh_type = MESH_TRIANGLES;

        for (i=0; i<face_spines; i++) {
                if (faces[i]->v_count) {
                        u32 init_idx;
                        GF_Mesh *face = faces[i];
                        init_idx = mesh->v_count;
                        /*quads only*/
                        mesh_set_vertex_vx(mesh, &face->vertices[0]);
                        mesh_set_vertex_vx(mesh, &face->vertices[1]);
                        mesh_set_vertex_vx(mesh, &face->vertices[2]);
                        mesh_set_vertex_vx(mesh, &face->vertices[3]);
                        mesh_set_triangle(mesh, init_idx + 0, init_idx + 1, init_idx + 3);
                        mesh_set_triangle(mesh, init_idx + 0, init_idx + 3, init_idx + 2);
                }
                mesh_free(faces[i]);
        }
        if (begin_face) {
                if (path->n_contours>1) {
#ifdef GPAC_HAS_GLU
                        u32 *ptsPerFace = gf_malloc(sizeof(u32)*path->n_contours);
                        /*we reversed begin cap!!!*/
                        cur = 0;
                        for (i=0; i<path->n_contours; i++) {
                                nb_pts = 1+path->contours[i] - cur;
                                cur+=nb_pts;
                                ptsPerFace[i] = nb_pts;
                        }
                        TesselateFaceMeshComplex(mesh, faces[begin_face], path->n_contours, ptsPerFace);
                        gf_free(ptsPerFace);
#endif
                } else {
                        TesselateFaceMesh(mesh, faces[begin_face]);
                }
                mesh_free(faces[begin_face]);
        }
        if (end_face) {
                if (path->n_contours>1) {
#ifdef GPAC_HAS_GLU
                        u32 *ptsPerFace = gf_malloc(sizeof(u32)*path->n_contours);
                        cur = 0;
                        for (i=0; i<path->n_contours; i++) {
                                nb_pts = 1+path->contours[i] - cur;
                                cur+=nb_pts;
                                ptsPerFace[i] = nb_pts;
                        }
                        TesselateFaceMeshComplex(mesh, faces[end_face], path->n_contours, ptsPerFace);
                        gf_free(ptsPerFace);
#endif
                } else {
                        TesselateFaceMesh(mesh, faces[end_face]);
                }
                mesh_free(faces[end_face]);
        }
        gf_free(faces);
        gf_free(SCPs);

        /*FIXME: this is correct except we need to handle path cw/ccw - until then no possibility
        to get correct lighting*/
        /*
                if (path->subpathlen && path->subpath[0]->closed && ((begin_face && end_face) || spine_closed))
                        mesh->flags |= MESH_IS_SOLID;
                else
                        mesh->flags &= ~MESH_IS_SOLID;
        */
}

void mesh_extrude_path_ext(GF_Mesh *mesh, GF_Path *path, MFVec3f *thespine, Fixed creaseAngle, Fixed min_cx, Fixed min_cy, Fixed width_cx, Fixed width_cy, Bool begin_cap, Bool end_cap, MFRotation *spine_ori, MFVec2f *spine_scale, Bool tx_along_spine)
{
        mesh_extrude_path_intern(mesh, path, thespine, creaseAngle, min_cx, min_cy, width_cx, width_cy, begin_cap, end_cap, spine_ori, spine_scale, tx_along_spine);
}

void mesh_extrude_path(GF_Mesh *mesh, GF_Path *path, MFVec3f *thespine, Fixed creaseAngle, Bool begin_cap, Bool end_cap, MFRotation *spine_ori, MFVec2f *spine_scale, Bool tx_along_spine)
{
        GF_Rect rc;
        gf_path_get_bounds(path, &rc);
        mesh_extrude_path_intern(mesh, path, thespine, creaseAngle, rc.x, rc.y-rc.height, rc.width, rc.height, begin_cap, end_cap, spine_ori, spine_scale, tx_along_spine);
        mesh_update_bounds(mesh);
        gf_mesh_build_aabbtree(mesh);
}

void mesh_new_extrusion(GF_Mesh *mesh, GF_Node *node)
{
        GF_Path *path;
        u32 i;
        M_Extrusion *ext = (M_Extrusion *)node;

        mesh_reset(mesh);
        path = gf_path_new();
        gf_path_add_move_to(path, ext->crossSection.vals[0].x, ext->crossSection.vals[0].y);
        for (i=1; i<ext->crossSection.count; i++) {
                gf_path_add_line_to(path, ext->crossSection.vals[i].x, ext->crossSection.vals[i].y);
        }

        mesh_extrude_path(mesh, path, &ext->spine, ext->creaseAngle, ext->beginCap, ext->endCap, &ext->orientation, &ext->scale, 1);
        gf_path_del(path);

        mesh_update_bounds(mesh);
        if (!ext->ccw) mesh->flags |= MESH_IS_CW;
}


#endif  /* GPAC_DISABLE_3D*/

#endif /*GPAC_DISABLE_VRML*/

/* [<][>][^][v][top][bottom][index][help] */