root/src/compositor/x3d_geometry.c

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

DEFINITIONS

This source file includes following definitions.
  1. disk2d_check_changes
  2. TraverseDisk2D
  3. compositor_init_disk2d
  4. arc2d_check_changes
  5. TraverseArc2D
  6. compositor_init_arc2d
  7. polyline2d_check_changes
  8. TraversePolyline2D
  9. compositor_init_polyline2d
  10. triangleset2d_check_changes
  11. TraverseTriangleSet2D
  12. compositor_init_triangle_set2d
  13. build_polypoint2d
  14. TraversePolypoint2D
  15. compositor_init_polypoint2d
  16. build_line_set
  17. TraverseLineSet
  18. compositor_init_lineset
  19. BuildTriangleSet
  20. build_triangle_set
  21. TraverseTriangleSet
  22. compositor_init_triangle_set
  23. build_indexed_triangle_set
  24. TraverseIndexedTriangleSet
  25. ITS_SetIndex
  26. compositor_init_indexed_triangle_set
  27. BuildTriangleStripSet
  28. build_triangle_strip_set
  29. TraverseTriangleStripSet
  30. compositor_init_triangle_strip_set
  31. build_indexed_triangle_strip_set
  32. TraverseIndexedTriangleStripSet
  33. ITSS_SetIndex
  34. compositor_init_indexed_triangle_strip_set
  35. BuildTriangleFanSet
  36. build_triangle_fan_set
  37. TraverseTriangleFanSet
  38. compositor_init_triangle_fan_set
  39. build_indexed_triangle_fan_set
  40. TraverseIndexedTriangleFanSet
  41. ITFS_SetIndex
  42. compositor_init_indexed_triangle_fan_set

/*
 *                      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 "nodes_stacks.h"
#include "visual_manager.h"
#include "drawable.h"

#ifndef GPAC_DISABLE_X3D

static void disk2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state)
{
        if (gf_node_dirty_get(node)) {
                Fixed a = ((X_Disk2D *) node)->outerRadius * 2;
                drawable_reset_path(stack);
                gf_path_add_ellipse(stack->path, 0, 0, a, a);
                a = ((X_Disk2D *) node)->innerRadius * 2;
                if (a) gf_path_add_ellipse(stack->path, 0, 0, a, a);
                gf_node_dirty_clear(node, 0);
                drawable_mark_modified(stack, tr_state);
        }
}

static void TraverseDisk2D(GF_Node *node, void *rs, Bool is_destroy)
{
        DrawableContext *ctx;
        Drawable *stack = (Drawable *)gf_node_get_private(node);
        GF_TraverseState *tr_state = (GF_TraverseState *)rs;

        if (is_destroy) {
                drawable_node_del(node);
                return;
        }

        disk2d_check_changes(node, stack, tr_state);

        switch (tr_state->traversing_mode) {
#ifndef GPAC_DISABLE_3D
        case TRAVERSE_DRAW_3D:
                if (!stack->mesh) {
                        stack->mesh = new_mesh();
                        /*FIXME - enable it with OpenGL-ES*/
                        mesh_from_path(stack->mesh, stack->path);
                }
                visual_3d_draw_2d(stack, tr_state);
                return;
#endif
        case TRAVERSE_GET_BOUNDS:
                gf_path_get_bounds(stack->path, &tr_state->bounds);
                return;
        case TRAVERSE_PICK:
                vrml_drawable_pick(stack, tr_state);
                return;
        case TRAVERSE_SORT:
#ifndef GPAC_DISABLE_3D
                if (tr_state->visual->type_3d) return;
#endif
                ctx = drawable_init_context_mpeg4(stack, tr_state);
                if (!ctx) return;
                drawable_finalize_sort(ctx, tr_state, NULL);
                return;
        }
}

void compositor_init_disk2d(GF_Compositor *compositor, GF_Node *node)
{
        drawable_stack_new(compositor, node);
        gf_node_set_callback_function(node, TraverseDisk2D);
}

static void arc2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state)
{
        if (gf_node_dirty_get(node)) {
                drawable_reset_path(stack);
                if (gf_node_get_tag(node)==TAG_X3D_Arc2D) {
                        X_Arc2D *a = (X_Arc2D *) node;
                        gf_path_add_arc(stack->path, a->radius, a->startAngle, a->endAngle, 0);
                } else {
                        X_ArcClose2D *a = (X_ArcClose2D *) node;
                        gf_path_add_arc(stack->path, a->radius, a->startAngle, a->endAngle, !stricmp(a->closureType.buffer, "PIE") ? 2 : 1);
                }
                gf_node_dirty_clear(node, 0);
                drawable_mark_modified(stack, tr_state);
        }
}

static void TraverseArc2D(GF_Node *node, void *rs, Bool is_destroy)
{
        DrawableContext *ctx;
        Drawable *stack = (Drawable *)gf_node_get_private(node);
        GF_TraverseState *tr_state = (GF_TraverseState *)rs;
        if (is_destroy) {
                drawable_node_del(node);
                return;
        }

        arc2d_check_changes(node, stack, tr_state);

        switch (tr_state->traversing_mode) {
#ifndef GPAC_DISABLE_3D
        case TRAVERSE_DRAW_3D:
                if (!stack->mesh) {
                        stack->mesh = new_mesh();
                        if (gf_node_get_tag(node)==TAG_X3D_Arc2D) {
                                mesh_get_outline(stack->mesh, stack->path);
                        } else {
                                mesh_from_path(stack->mesh, stack->path);
                        }
                }
                visual_3d_draw_2d(stack, tr_state);
                return;
#endif
        case TRAVERSE_GET_BOUNDS:
                gf_path_get_bounds(stack->path, &tr_state->bounds);
#ifndef GPAC_DISABLE_3D
                gf_bbox_from_rect(&tr_state->bbox, &tr_state->bounds);
#endif
                return;
        case TRAVERSE_PICK:
                vrml_drawable_pick(stack, tr_state);
                return;
        case TRAVERSE_SORT:
#ifndef GPAC_DISABLE_3D
                if (tr_state->visual->type_3d) return;
#endif
                ctx = drawable_init_context_mpeg4(stack, tr_state);
                if (!ctx) return;
                drawable_finalize_sort(ctx, tr_state, NULL);
                return;
        }
}

void compositor_init_arc2d(GF_Compositor *compositor, GF_Node *node)
{
        drawable_stack_new(compositor, node);
        gf_node_set_callback_function(node, TraverseArc2D);
}

static void polyline2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state)
{
        if (gf_node_dirty_get(node)) {
                u32 i;
                X_Polyline2D *a = (X_Polyline2D *) node;
                drawable_reset_path(stack);
                for (i=0; i<a->lineSegments.count; i++) {
                        if (i) {
                                gf_path_add_line_to(stack->path, a->lineSegments.vals[i].x, a->lineSegments.vals[i].y);
                        } else {
                                gf_path_add_move_to(stack->path, a->lineSegments.vals[i].x, a->lineSegments.vals[i].y);
                        }
                }
                gf_node_dirty_clear(node, 0);
                drawable_mark_modified(stack, tr_state);
        }
}

static void TraversePolyline2D(GF_Node *node, void *rs, Bool is_destroy)
{
        DrawableContext *ctx;
        Drawable *stack = (Drawable *)gf_node_get_private(node);
        GF_TraverseState *tr_state = (GF_TraverseState *)rs;
        if (is_destroy) {
                drawable_node_del(node);
                return;
        }

        polyline2d_check_changes(node, stack, tr_state);

        switch (tr_state->traversing_mode) {
#ifndef GPAC_DISABLE_3D
        case TRAVERSE_DRAW_3D:
                if (!stack->mesh) {
                        stack->mesh = new_mesh();
                        mesh_get_outline(stack->mesh, stack->path);
                }
                visual_3d_draw_2d(stack, tr_state);
                return;
#endif
        case TRAVERSE_GET_BOUNDS:
                gf_path_get_bounds(stack->path, &tr_state->bounds);
                return;
        case TRAVERSE_PICK:
                vrml_drawable_pick(stack, tr_state);
                return;
        case TRAVERSE_SORT:
#ifndef GPAC_DISABLE_3D
                if (tr_state->visual->type_3d) return;
#endif
                ctx = drawable_init_context_mpeg4(stack, tr_state);
                if (!ctx) return;
                drawable_finalize_sort(ctx, tr_state, NULL);
                return;
        }
}

void compositor_init_polyline2d(GF_Compositor *compositor, GF_Node *node)
{
        drawable_stack_new(compositor, node);
        gf_node_set_callback_function(node, TraversePolyline2D);
}

static void triangleset2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state)
{
        if (gf_node_dirty_get(node)) {
                u32 i, count;
                X_TriangleSet2D *p = (X_TriangleSet2D *)node;
                drawable_reset_path(stack);
                count = p->vertices.count;
                while (count%3) count--;
                for (i=0; i<count; i+=3) {
                        gf_path_add_move_to(stack->path, p->vertices.vals[i].x, p->vertices.vals[i].y);
                        gf_path_add_line_to(stack->path, p->vertices.vals[i+1].x, p->vertices.vals[i+1].y);
                        gf_path_add_line_to(stack->path, p->vertices.vals[i+2].x, p->vertices.vals[i+2].y);
                        gf_path_close(stack->path);
                }
                gf_node_dirty_clear(node, 0);
                drawable_mark_modified(stack, tr_state);
        }
}

static void TraverseTriangleSet2D(GF_Node *node, void *rs, Bool is_destroy)
{
        DrawableContext *ctx;
        Drawable *stack = (Drawable *)gf_node_get_private(node);
        GF_TraverseState *tr_state = (GF_TraverseState *)rs;

        if (is_destroy) {
                drawable_node_del(node);
                return;
        }

        triangleset2d_check_changes(node, stack, tr_state);

        switch (tr_state->traversing_mode) {
#ifndef GPAC_DISABLE_3D
        case TRAVERSE_DRAW_3D:
                if (!stack->mesh) {
                        SFColorRGBA col;
                        u32 i, count, idx;
                        GF_Vertex v1, v2, v3;
                        X_TriangleSet2D *p = (X_TriangleSet2D *)node;

                        stack->mesh = new_mesh();
                        stack->mesh->mesh_type = MESH_TRIANGLES;
                        col.red = col.green = col.blue = 0;
                        col.alpha = FIX_ONE;
                        v1.color = MESH_MAKE_COL(col);
                        v1.normal.x = v1.normal.y = 0;
                        v1.normal.z = MESH_NORMAL_UNIT;
                        v1.pos.z = 0;
                        v3 = v2 = v1;
                        count = p->vertices.count;
                        while (count%3) count--;
                        for (i=0; i<count; i+=3) {
                                idx = stack->mesh->v_count;
                                v1.pos.x = p->vertices.vals[i].x;
                                v1.pos.y = p->vertices.vals[i].y;
                                v2.pos.x = p->vertices.vals[i+1].x;
                                v2.pos.y = p->vertices.vals[i+1].y;
                                v3.pos.x = p->vertices.vals[i+2].x;
                                v3.pos.y = p->vertices.vals[i+2].y;
                                mesh_set_vertex_vx(stack->mesh, &v1);
                                mesh_set_vertex_vx(stack->mesh, &v2);
                                mesh_set_vertex_vx(stack->mesh, &v3);
                                gf_vec_diff(v2.pos, v2.pos, v1.pos);
                                gf_vec_diff(v3.pos, v3.pos, v1.pos);
                                v1.pos = gf_vec_cross(v2.pos, v3.pos);
                                if (v1.pos.z<0) {
                                        mesh_set_triangle(stack->mesh, idx, idx+2, idx+1);
                                } else {
                                        mesh_set_triangle(stack->mesh, idx, idx+1, idx+2);
                                }
                        }
                        stack->mesh->flags |= MESH_IS_2D;
                        mesh_update_bounds(stack->mesh);
                }
                visual_3d_draw_2d(stack, tr_state);
                return;
#endif
        case TRAVERSE_GET_BOUNDS:
                gf_path_get_bounds(stack->path, &tr_state->bounds);
                return;
        case TRAVERSE_PICK:
                vrml_drawable_pick(stack, tr_state);
                return;
        case TRAVERSE_SORT:
#ifndef GPAC_DISABLE_3D
                if (tr_state->visual->type_3d) return;
#endif
                ctx = drawable_init_context_mpeg4(stack, tr_state);
                if (!ctx) return;
                drawable_finalize_sort(ctx, tr_state, NULL);
                return;
        }
}

void compositor_init_triangle_set2d(GF_Compositor *compositor, GF_Node *node)
{
        drawable_stack_new(compositor, node);
        gf_node_set_callback_function(node, TraverseTriangleSet2D);
}

#ifndef GPAC_DISABLE_3D

static void build_polypoint2d(GF_Node *node, Drawable3D *stack, GF_TraverseState *tr_state)
{
        u32 i;
        SFColorRGBA col;
        X_Polypoint2D *p = (X_Polypoint2D *)node;

        stack->mesh->mesh_type = MESH_POINTSET;
        col.red = col.green = col.blue = 0;
        col.alpha = FIX_ONE;
        for (i=0; i<p->point.count; i++) {
                mesh_set_point(stack->mesh, p->point.vals[i].x, p->point.vals[i].y, 0, col);
                mesh_set_index(stack->mesh, stack->mesh->v_count-1);
        }
}

static void TraversePolypoint2D(GF_Node *node, void *rs, Bool is_destroy)
{
        drawable_3d_base_traverse(node, rs, is_destroy, build_polypoint2d);
}

void compositor_init_polypoint2d(GF_Compositor *compositor, GF_Node *node)
{
        drawable_3d_new(node);
        gf_node_set_callback_function(node, TraversePolypoint2D);
}

static void build_line_set(GF_Node *node, Drawable3D *stack, GF_TraverseState *tr_state)
{
        u32 i, j, c_idx;
        GenMFField *cols;
        GF_Vertex vx;
        Bool rgba_col;
        SFColorRGBA rgba;
        X_LineSet *p = (X_LineSet *)node;
        X_Coordinate *c = (X_Coordinate *) p->coord;

        stack->mesh->mesh_type = MESH_LINESET;

        cols = NULL;
        rgba_col = 0;
        if (p->color) {
                if (gf_node_get_tag(p->color)==TAG_X3D_ColorRGBA) {
                        rgba_col = 1;
                        cols = (GenMFField *) & ((X_ColorRGBA *) p->color)->color;
                } else {
                        cols = (GenMFField *) & ((M_Color *) p->color)->color;
                }
        }
        c_idx = 0;
        memset(&vx, 0, sizeof(GF_Vertex));
        for (i=0; i<p->vertexCount.count; i++) {
                if (p->vertexCount.vals[i]<2) continue;

                for (j=0; j<(u32) p->vertexCount.vals[i]; j++) {
                        vx.pos = c->point.vals[c_idx];
                        if (cols && (cols->count>c_idx)) {
                                if (rgba_col) {
                                        rgba = ((MFColorRGBA *)cols)->vals[c_idx];
                                } else {
                                        rgba = gf_sg_sfcolor_to_rgba( ((MFColor *)cols)->vals[c_idx]);
                                }
                                vx.color = MESH_MAKE_COL(rgba);
                        }
                        mesh_set_vertex_vx(stack->mesh, &vx);
                        if (j) {
                                mesh_set_index(stack->mesh, stack->mesh->v_count-2);
                                mesh_set_index(stack->mesh, stack->mesh->v_count-1);
                        }
                        c_idx++;
                        if (c_idx==c->point.count) break;
                }
        }
        if (cols) stack->mesh->flags |= MESH_HAS_COLOR;
        mesh_update_bounds(stack->mesh);
}

static void TraverseLineSet(GF_Node *node, void *rs, Bool is_destroy)
{
        drawable_3d_base_traverse(node, rs, is_destroy, build_line_set);
}


void compositor_init_lineset(GF_Compositor *compositor, GF_Node *node)
{
        drawable_3d_new(node);
        gf_node_set_callback_function(node, TraverseLineSet);
}

static void BuildTriangleSet(GF_Mesh *mesh, GF_Node *_coords, GF_Node *_color, GF_Node *_txcoords, GF_Node *_normal, MFInt32 *indices, Bool normalPerVertex, Bool ccw, Bool solid)
{
        u32 i, count, generate_tx;
        GF_Vertex vx;
        GenMFField *cols;
        MFVec3f *norms;
        MFVec2f *txcoords;
        Bool rgba_col;
        SFColorRGBA rgba;
        X_Coordinate *c = (X_Coordinate *) _coords;

        mesh_reset(mesh);

        cols = NULL;
        rgba_col = 0;
        if (_color) {
                if (gf_node_get_tag(_color)==TAG_X3D_ColorRGBA) {
                        rgba_col = 1;
                        cols = (GenMFField *) & ((X_ColorRGBA *) _color)->color;
                } else {
                        cols = (GenMFField *) & ((M_Color *) _color)->color;
                }
        }
        norms = NULL;
        if (_normal) norms = & ((M_Normal *)_normal)->vector;
        txcoords = NULL;
        generate_tx = 0;
        /*FIXME - this can't work with multitexturing*/
        if (_txcoords) {
                switch (gf_node_get_tag(_txcoords)) {
                case TAG_X3D_TextureCoordinate:
                case TAG_MPEG4_TextureCoordinate:
                        txcoords = & ((M_TextureCoordinate *)_txcoords)->point;
                        break;
                case TAG_X3D_TextureCoordinateGenerator:
                        generate_tx = 1;
                        break;
                }
        }

        if (indices) {
                count = indices->count;
        } else {
                count = c->point.count;
        }
        while (count%3) count--;
        memset(&vx, 0, sizeof(GF_Vertex));
        for (i=0; i<count; i++) {
                u32 idx;
                if (indices) {
                        if (indices->count<=i) return;
                        idx = indices->vals[i];
                } else {
                        idx = i;
                }
                vx.pos = c->point.vals[idx];
                if (cols && (cols->count>idx)) {
                        if (rgba_col) {
                                rgba = ((MFColorRGBA *)cols)->vals[idx];
                        } else {
                                rgba = gf_sg_sfcolor_to_rgba( ((MFColor *)cols)->vals[idx]);
                        }
                        vx.color = MESH_MAKE_COL(rgba);
                }
                if (norms && (norms->count>idx)) {
                        SFVec3f n = norms->vals[idx];
                        gf_vec_norm(&n);
                        MESH_SET_NORMAL(vx, n);
                }
                if (txcoords) {
                        if (txcoords->count>idx) vx.texcoords = txcoords->vals[idx];
                }
                /*X3D says nothing about default texture mapping here...*/
                else if (!generate_tx) {
                        switch (i%3) {
                        case 2:
                                vx.texcoords.x = FIX_ONE;
                                vx.texcoords.y = 0;
                                break;
                        case 1:
                                vx.texcoords.x = FIX_ONE/2;
                                vx.texcoords.y = FIX_ONE;
                                break;
                        case 0:
                                vx.texcoords.x = 0;
                                vx.texcoords.y = 0;
                                break;
                        }
                }
                mesh_set_vertex_vx(mesh, &vx);
        }
        for (i=0; i<mesh->v_count; i+=3) {
                mesh_set_triangle(mesh, i, i+1, i+2);
        }
        if (generate_tx) mesh_generate_tex_coords(mesh, _txcoords);

        if (!ccw) mesh->flags |= MESH_IS_CW;
        if (cols) mesh->flags |= MESH_HAS_COLOR;
        if (rgba_col) mesh->flags |= MESH_HAS_ALPHA;
        if (!_normal) mesh_recompute_normals(mesh);
        if (solid) mesh->flags |= MESH_IS_SOLID;
        mesh_update_bounds(mesh);
        gf_mesh_build_aabbtree(mesh);
}

static void build_triangle_set(GF_Node *node, Drawable3D *stack, GF_TraverseState *tr_state)
{
        X_TriangleSet *ts = (X_TriangleSet *)node;
        if (!ts->coord) return;
        BuildTriangleSet(stack->mesh, ts->coord, ts->color, ts->texCoord, ts->normal, NULL, ts->normalPerVertex, ts->ccw, ts->solid);
}

static void TraverseTriangleSet(GF_Node *node, void *rs, Bool is_destroy)
{
        drawable_3d_base_traverse(node, rs, is_destroy, build_triangle_set);
}


void compositor_init_triangle_set(GF_Compositor *compositor, GF_Node *node)
{
        drawable_3d_new(node);
        gf_node_set_callback_function(node, TraverseTriangleSet);
}


static void build_indexed_triangle_set(GF_Node *node, Drawable3D *stack, GF_TraverseState *tr_state)
{
        X_IndexedTriangleSet *its = (X_IndexedTriangleSet*)node;
        if (!its->coord) return;
        BuildTriangleSet(stack->mesh, its->coord, its->color, its->texCoord, its->normal, &its->index, its->normalPerVertex, its->ccw, its->solid);
}

static void TraverseIndexedTriangleSet(GF_Node *node, void *rs, Bool is_destroy)
{
        drawable_3d_base_traverse(node, rs, is_destroy, build_indexed_triangle_set);
}

static void ITS_SetIndex(GF_Node *node, GF_Route *route)
{
        X_IndexedTriangleSet *its = (X_IndexedTriangleSet*)node;
        gf_sg_vrml_field_copy(&its->index, &its->set_index, GF_SG_VRML_MFINT32);
        gf_sg_vrml_mf_reset(&its->set_index, GF_SG_VRML_MFINT32);
}

void compositor_init_indexed_triangle_set(GF_Compositor *compositor, GF_Node *node)
{
        X_IndexedTriangleSet *its = (X_IndexedTriangleSet*)node;
        drawable_3d_new(node);
        gf_node_set_callback_function(node, TraverseIndexedTriangleSet);
        its->on_set_index = ITS_SetIndex;
}


static void BuildTriangleStripSet(GF_Mesh *mesh, GF_Node *_coords, GF_Node *_color, GF_Node *_txcoords, GF_Node *_normal, MFInt32 *stripList, MFInt32 *indices, Bool normalPerVertex, Bool ccw, Bool solid)
{
        u32 strip, i, cur_idx, generate_tx;
        GF_Vertex vx;
        GenMFField *cols;
        MFVec3f *norms;
        MFVec2f *txcoords;
        Bool rgba_col;
        SFColorRGBA rgba;
        X_Coordinate *c = (X_Coordinate *) _coords;

        mesh_reset(mesh);

        cols = NULL;
        rgba_col = 0;
        if (_color) {
                if (gf_node_get_tag(_color)==TAG_X3D_ColorRGBA) {
                        rgba_col = 1;
                        cols = (GenMFField *) & ((X_ColorRGBA *) _color)->color;
                } else {
                        cols = (GenMFField *) & ((M_Color *) _color)->color;
                }
        }
        norms = NULL;
        if (_normal) norms = & ((M_Normal *)_normal)->vector;
        txcoords = NULL;
        generate_tx = 0;
        /*FIXME - this can't work with multitexturing*/
        if (_txcoords) {
                switch (gf_node_get_tag(_txcoords)) {
                case TAG_X3D_TextureCoordinate:
                case TAG_MPEG4_TextureCoordinate:
                        txcoords = & ((M_TextureCoordinate *)_txcoords)->point;
                        break;
                case TAG_X3D_TextureCoordinateGenerator:
                        generate_tx = 1;
                        break;
                }
        }
        memset(&vx, 0, sizeof(GF_Vertex));
        cur_idx = 0;
        for (strip= 0; strip<stripList->count; strip++) {
                u32 start_idx = mesh->v_count;
                if (stripList->vals[strip] < 3) continue;

                for (i=0; i<(u32) stripList->vals[strip]; i++) {
                        u32 idx;
                        if (indices) {
                                if (indices->count<=cur_idx) return;
                                if (indices->vals[cur_idx] == -1) {
                                        GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[X3D] bad formatted X3D triangle strip\n"));
                                        return;
                                }
                                idx = indices->vals[cur_idx];
                        } else {
                                idx = cur_idx;
                        }

                        vx.pos = c->point.vals[idx];

                        if (cols && (cols->count>idx)) {
                                if (rgba_col) {
                                        rgba = ((MFColorRGBA *)cols)->vals[idx];
                                } else {
                                        rgba = gf_sg_sfcolor_to_rgba( ((MFColor *)cols)->vals[idx]);
                                }
                                vx.color = MESH_MAKE_COL(rgba);
                        }
                        /*according to X3D spec, if normal field is set, it is ALWAYS as normal per vertex*/
                        if (norms && (norms->count>idx)) {
                                SFVec3f n = norms->vals[idx];
                                gf_vec_norm(&n);
                                MESH_SET_NORMAL(vx, n);
                        }
                        if (txcoords) {
                                if (txcoords->count>idx) vx.texcoords = txcoords->vals[idx];
                        }
                        /*X3D says nothing about default texture mapping here...*/
                        else if (!generate_tx) {
                                switch (idx%3) {
                                case 2:
                                        vx.texcoords.x = FIX_ONE;
                                        vx.texcoords.y = 0;
                                        break;
                                case 1:
                                        vx.texcoords.x = FIX_ONE/2;
                                        vx.texcoords.y = FIX_ONE;
                                        break;
                                case 0:
                                        vx.texcoords.x = 0;
                                        vx.texcoords.y = 0;
                                        break;
                                }
                        }
                        if (i>2) {
                                /*duplicate last 2 vertices (we really need independent vertices to handle normals per face)*/
                                mesh_set_vertex_vx(mesh, &mesh->vertices[mesh->v_count-2]);
                                mesh_set_vertex_vx(mesh, &mesh->vertices[mesh->v_count-2]);
                        }
                        mesh_set_vertex_vx(mesh, &vx);

                        cur_idx ++;
                        if (indices) {
                                if (cur_idx>=indices->count) break;
                        } else if (cur_idx==c->point.count) break;

                }
                for (i=start_idx; i<mesh->v_count; i+=3) {
                        mesh_set_triangle(mesh, i, i+1, i+2);
                }
                /*get rid of -1*/
                if (indices && (cur_idx<indices->count) && (indices->vals[cur_idx]==-1)) cur_idx++;
        }
        if (generate_tx) mesh_generate_tex_coords(mesh, _txcoords);

        if (cols) mesh->flags |= MESH_HAS_COLOR;
        if (rgba_col) mesh->flags |= MESH_HAS_ALPHA;
        if (_normal) {
                if (!ccw) mesh->flags |= MESH_IS_CW;
        }
        /*reorder everything to CCW*/
        else {
                u32 cur_face = 0;
                mesh_recompute_normals(mesh);
                for (i=0; i<mesh->i_count; i+= 3) {
                        if ((ccw && (cur_face%2)) || (!ccw && !(cur_face%2))) {
                                SFVec3f v;
                                u32 idx;
                                MESH_GET_NORMAL(v, mesh->vertices[mesh->indices[i]]);
                                v = gf_vec_scale(v,-1);
                                MESH_SET_NORMAL(mesh->vertices[mesh->indices[i]], v);
                                mesh->vertices[mesh->indices[i+1]].normal = mesh->vertices[mesh->indices[i]].normal;
                                mesh->vertices[mesh->indices[i+2]].normal = mesh->vertices[mesh->indices[i]].normal;
                                idx = mesh->indices[i+2];
                                mesh->indices[i+2] = mesh->indices[i+1];
                                mesh->indices[i+1] = idx;
                        }
                        cur_face++;
                }
                if (normalPerVertex) {
                        cur_face = 0;
                        for (strip=0; strip<stripList->count; strip++) {
                                SFVec3f n_0, n_1, n_2, n_avg;
                                u32 nb_face;
                                if (stripList->vals[strip] < 3) continue;
                                if (stripList->vals[strip] <= 3) {
                                        cur_face ++;
                                        continue;
                                }

                                /*first face normal*/
                                MESH_GET_NORMAL(n_0, mesh->vertices[mesh->indices[3*cur_face]]);
                                /*second face normal*/
                                MESH_GET_NORMAL(n_1, mesh->vertices[mesh->indices[3*(cur_face+1)]]);

                                gf_vec_add(n_avg, n_0, n_1);
                                gf_vec_norm(&n_avg);
                                /*assign to second point of first face and first of second face*/
                                MESH_SET_NORMAL(mesh->vertices[mesh->indices[3*cur_face+1]], n_avg);
                                MESH_SET_NORMAL(mesh->vertices[mesh->indices[3*(cur_face+1)]], n_avg);
                                nb_face = stripList->vals[strip] - 2;
                                cur_face++;
                                for (i=1; i<nb_face-1; i++) {
                                        /*get normal (use second pt of current face since first has been updated)*/
                                        MESH_GET_NORMAL(n_2, mesh->vertices[mesh->indices[3*cur_face + 1]]);
                                        gf_vec_add(n_avg, n_0, n_1);
                                        gf_vec_add(n_avg, n_avg, n_2);
                                        gf_vec_norm(&n_avg);
                                        /*last of prev face*/
                                        MESH_SET_NORMAL(mesh->vertices[mesh->indices[3*cur_face - 1]], n_avg);
                                        /*second of current face*/
                                        mesh->vertices[mesh->indices[3*cur_face + 1]].normal = mesh->vertices[mesh->indices[3*cur_face - 1]].normal;
                                        /*first of next face*/
                                        mesh->vertices[mesh->indices[3*cur_face + 3]].normal = mesh->vertices[mesh->indices[3*cur_face - 1]].normal;
                                        n_0 = n_1;
                                        n_1 = n_2;
                                        cur_face++;
                                }
                        }
                }
        }
        if (solid) mesh->flags |= MESH_IS_SOLID;
        mesh_update_bounds(mesh);
        gf_mesh_build_aabbtree(mesh);
}

static void build_triangle_strip_set(GF_Node *node, Drawable3D *stack, GF_TraverseState *tr_state)
{
        X_TriangleStripSet *tss = (X_TriangleStripSet *)node;
        if (!tss->coord) return;
        BuildTriangleStripSet(stack->mesh, tss->coord, tss->color, tss->texCoord, tss->normal, &tss->stripCount, NULL, tss->normalPerVertex, tss->ccw, tss->solid);
}

static void TraverseTriangleStripSet(GF_Node *node, void *rs, Bool is_destroy)
{
        drawable_3d_base_traverse(node, rs, is_destroy, build_triangle_strip_set);
}

void compositor_init_triangle_strip_set(GF_Compositor *compositor, GF_Node *node)
{
        drawable_3d_new(node);
        gf_node_set_callback_function(node, TraverseTriangleStripSet);
}

static void build_indexed_triangle_strip_set(GF_Node *node, Drawable3D *stack, GF_TraverseState *tr_state)
{
        MFInt32 stripList;
        u32 i, nb_strips;
        X_IndexedTriangleStripSet *itss = (X_IndexedTriangleStripSet *)node;

        if (!itss->coord) return;

        stripList.count = 0;
        stripList.vals = NULL;
        nb_strips = 0;
        for (i=0; i<itss->index.count; i++) {
                if (itss->index.vals[i]==-1) {
                        if (nb_strips>=3) {
                                u32 *out_nb;
                                gf_sg_vrml_mf_append(&stripList, GF_SG_VRML_MFINT32, (void **) &out_nb);
                                *out_nb = nb_strips;
                        }
                        nb_strips = 0;
                } else {
                        nb_strips++;
                }
        }
        if (nb_strips>=3) {
                u32 *out_nb;
                gf_sg_vrml_mf_append(&stripList, GF_SG_VRML_MFINT32, (void **) &out_nb);
                *out_nb = nb_strips;
        }
        BuildTriangleStripSet(stack->mesh, itss->coord, itss->color, itss->texCoord, itss->normal, &stripList, &itss->index, itss->normalPerVertex, itss->ccw, itss->solid);
        gf_sg_vrml_mf_reset(&stripList, GF_SG_VRML_MFINT32);
}

static void TraverseIndexedTriangleStripSet(GF_Node *node, void *rs, Bool is_destroy)
{
        drawable_3d_base_traverse(node, rs, is_destroy, build_indexed_triangle_strip_set);
}

static void ITSS_SetIndex(GF_Node *node, GF_Route *route)
{
        X_IndexedTriangleStripSet *itss = (X_IndexedTriangleStripSet*)node;
        gf_sg_vrml_field_copy(&itss->index, &itss->set_index, GF_SG_VRML_MFINT32);
        gf_sg_vrml_mf_reset(&itss->set_index, GF_SG_VRML_MFINT32);
}

void compositor_init_indexed_triangle_strip_set(GF_Compositor *compositor, GF_Node *node)
{
        X_IndexedTriangleStripSet *itss = (X_IndexedTriangleStripSet*)node;
        drawable_3d_new(node);
        gf_node_set_callback_function(node, TraverseIndexedTriangleStripSet);
        itss->on_set_index = ITSS_SetIndex;
}


static void BuildTriangleFanSet(GF_Mesh *mesh, GF_Node *_coords, GF_Node *_color, GF_Node *_txcoords, GF_Node *_normal, MFInt32 *fanList, MFInt32 *indices, Bool normalPerVertex, Bool ccw, Bool solid)
{
        u32 fan, i, cur_idx, generate_tx;
        GF_Vertex vx;
        GenMFField *cols;
        MFVec3f *norms;
        MFVec2f *txcoords;
        Bool rgba_col;
        SFColorRGBA rgba;
        X_Coordinate *c = (X_Coordinate *) _coords;
        mesh_reset(mesh);

        cols = NULL;
        rgba_col = 0;
        if (_color) {
                if (gf_node_get_tag(_color)==TAG_X3D_ColorRGBA) {
                        rgba_col = 1;
                        cols = (GenMFField *) & ((X_ColorRGBA *) _color)->color;
                } else {
                        cols = (GenMFField *) & ((M_Color *) _color)->color;
                }
        }
        norms = NULL;
        if (_normal) norms = & ((M_Normal *)_normal)->vector;
        txcoords = NULL;
        generate_tx = 0;
        /*FIXME - this can't work with multitexturing*/
        if (_txcoords) {
                switch (gf_node_get_tag(_txcoords)) {
                case TAG_X3D_TextureCoordinate:
                case TAG_MPEG4_TextureCoordinate:
                        txcoords = & ((M_TextureCoordinate *)_txcoords)->point;
                        break;
                case TAG_X3D_TextureCoordinateGenerator:
                        generate_tx = 1;
                        break;
                }
        }

        memset(&vx, 0, sizeof(GF_Vertex));
        cur_idx = 0;
        for (fan= 0; fan<fanList->count; fan++) {
                u32 start_idx = mesh->v_count;
                if (fanList->vals[fan] < 3) continue;

                for (i=0; i<(u32) fanList->vals[fan]; i++) {
                        u32 idx;
                        if (indices) {
                                if (indices->count<=cur_idx) return;
                                if (indices->vals[cur_idx] == -1) {
                                        GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[X3D] bad formatted X3D triangle set\n"));
                                        return;
                                }
                                idx = indices->vals[cur_idx];
                        } else {
                                idx = cur_idx;
                        }
                        vx.pos = c->point.vals[idx];

                        if (cols && (cols->count>idx)) {
                                if (rgba_col) {
                                        rgba = ((MFColorRGBA *)cols)->vals[idx];
                                } else {
                                        rgba = gf_sg_sfcolor_to_rgba( ((MFColor *)cols)->vals[idx]);
                                }
                                vx.color = MESH_MAKE_COL(rgba);
                        }
                        /*according to X3D spec, if normal field is set, it is ALWAYS as normal per vertex*/
                        if (norms && (norms->count>idx)) {
                                SFVec3f n = norms->vals[idx];
                                gf_vec_norm(&n);
                                MESH_SET_NORMAL(vx, n);
                        }
                        if (txcoords) {
                                if (txcoords->count>idx) vx.texcoords = txcoords->vals[idx];
                        }
                        /*X3D says nothing about default texture mapping here...*/
                        else if (!generate_tx) {
                                switch (idx%3) {
                                case 2:
                                        vx.texcoords.x = FIX_ONE;
                                        vx.texcoords.y = 0;
                                        break;
                                case 1:
                                        vx.texcoords.x = FIX_ONE/2;
                                        vx.texcoords.y = FIX_ONE;
                                        break;
                                case 0:
                                        vx.texcoords.x = 0;
                                        vx.texcoords.y = 0;
                                        break;
                                }
                        }
                        mesh_set_vertex_vx(mesh, &vx);

                        cur_idx ++;
                        if (indices) {
                                if (cur_idx>=indices->count) break;
                        } else if (cur_idx==c->point.count) break;

                        if (i>1) {
                                mesh_set_vertex_vx(mesh, &mesh->vertices[start_idx]);
                                mesh_set_vertex_vx(mesh, &vx);
                        }
                }
                for (i=start_idx; i<mesh->v_count; i+=3) {
                        mesh_set_triangle(mesh, i, i+1, i+2);
                }
                /*get rid of -1*/
                if (indices && (cur_idx<indices->count) && (indices->vals[cur_idx]==-1)) cur_idx++;
        }
        if (generate_tx) mesh_generate_tex_coords(mesh, _txcoords);

        if (cols) mesh->flags |= MESH_HAS_COLOR;
        if (rgba_col) mesh->flags |= MESH_HAS_ALPHA;
        if (!ccw) mesh->flags |= MESH_IS_CW;
        if (!_normal) {
                mesh_recompute_normals(mesh);
                if (normalPerVertex) {
                        u32 cur_face = 0;
                        for (fan=0; fan<fanList->count; fan++) {
                                SFVec3f n_0, n_1, n_avg, n_tot;
                                u32 nb_face, start_face;
                                if (fanList->vals[fan] < 3) continue;
                                if (fanList->vals[fan] == 3) {
                                        cur_face++;
                                        continue;
                                }

                                start_face = cur_face;

                                /*first face normal*/
                                MESH_GET_NORMAL(n_0, mesh->vertices[mesh->indices[3*cur_face]]);
                                n_tot = n_0;
                                cur_face++;
                                nb_face = fanList->vals[fan] - 2;
                                for (i=1; i<nb_face; i++) {
                                        MESH_GET_NORMAL(n_1, mesh->vertices[mesh->indices[3*cur_face + 1]]);
                                        gf_vec_add(n_avg, n_0, n_1);
                                        gf_vec_norm(&n_avg);
                                        MESH_SET_NORMAL(mesh->vertices[mesh->indices[3*cur_face + 1]], n_avg);
                                        gf_vec_add(n_tot, n_tot, n_1);
                                        n_0 = n_1;
                                        cur_face++;
                                }
                                /*and assign center normal*/
                                gf_vec_norm(&n_tot);
                                for (i=0; i<nb_face; i++) {
                                        MESH_SET_NORMAL(mesh->vertices[mesh->indices[3*(i+start_face)]], n_tot);
                                }
                        }
                }
        }
        if (solid) mesh->flags |= MESH_IS_SOLID;
        mesh_update_bounds(mesh);
        gf_mesh_build_aabbtree(mesh);
}

static void build_triangle_fan_set(GF_Node *node, Drawable3D *stack, GF_TraverseState *tr_state)
{
        X_TriangleFanSet *tfs = (X_TriangleFanSet *)node;
        if (!tfs->coord) return;
        BuildTriangleFanSet(stack->mesh, tfs->coord, tfs->color, tfs->texCoord, tfs->normal, &tfs->fanCount, NULL, tfs->normalPerVertex, tfs->ccw, tfs->solid);
}

static void TraverseTriangleFanSet(GF_Node *node, void *rs, Bool is_destroy)
{
        drawable_3d_base_traverse(node, rs, is_destroy, build_triangle_fan_set);
}

void compositor_init_triangle_fan_set(GF_Compositor *compositor, GF_Node *node)
{
        drawable_3d_new(node);
        gf_node_set_callback_function(node, TraverseTriangleFanSet);
}

static void build_indexed_triangle_fan_set(GF_Node *node, Drawable3D *stack, GF_TraverseState *tr_state)
{
        MFInt32 fanList;
        u32 i, nb_fans;
        X_IndexedTriangleFanSet *itfs = (X_IndexedTriangleFanSet *)node;
        gf_node_dirty_clear(node, 0);
        if (!itfs->coord) return;

        fanList.count = 0;
        fanList.vals = NULL;
        nb_fans = 0;
        for (i=0; i<itfs->index.count; i++) {
                if (itfs->index.vals[i]==-1) {
                        if (nb_fans>=3) {
                                u32 *out_nb;
                                gf_sg_vrml_mf_append(&fanList, GF_SG_VRML_MFINT32, (void **) &out_nb);
                                *out_nb = nb_fans;
                        }
                        nb_fans = 0;
                } else {
                        nb_fans++;
                }
        }
        if (nb_fans>=3) {
                u32 *out_nb;
                gf_sg_vrml_mf_append(&fanList, GF_SG_VRML_MFINT32, (void **) &out_nb);
                *out_nb = nb_fans;
        }
        BuildTriangleFanSet(stack->mesh, itfs->coord, itfs->color, itfs->texCoord, itfs->normal, &fanList, &itfs->index, itfs->normalPerVertex, itfs->ccw, itfs->solid);
        gf_sg_vrml_mf_reset(&fanList, GF_SG_VRML_MFINT32);
}

static void TraverseIndexedTriangleFanSet(GF_Node *node, void *rs, Bool is_destroy)
{
        drawable_3d_base_traverse(node, rs, is_destroy, build_indexed_triangle_fan_set);
}

static void ITFS_SetIndex(GF_Node *node, GF_Route *route)
{
        X_IndexedTriangleFanSet *itfs = (X_IndexedTriangleFanSet *)node;
        gf_sg_vrml_field_copy(&itfs->index, &itfs->set_index, GF_SG_VRML_MFINT32);
        gf_sg_vrml_mf_reset(&itfs->set_index, GF_SG_VRML_MFINT32);
}

void compositor_init_indexed_triangle_fan_set(GF_Compositor *compositor, GF_Node *node)
{
        X_IndexedTriangleFanSet *itfs = (X_IndexedTriangleFanSet *)node;
        drawable_3d_new(node);
        gf_node_set_callback_function(node, TraverseIndexedTriangleFanSet);
        itfs->on_set_index = ITFS_SetIndex;
}

#endif /*GPAC_DISABLE_3D*/

#endif /*GPAC_DISABLE_X3D*/

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