/*
 * Mesh.cpp
 *
 * Copyright (C) 1999 Stephen F. White
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file "COPYING" for details); if 
 * not, write to the Free Software Foundation, Inc., 675 Mass Ave, 
 * Cambridge, MA 02139, USA.
 */

// Mesh::generateTextureCoordinates() based on Polyrep.c of FreeWRL
/*******************************************************************
 Copyright (C) 1998 Tuomas J. Lukka
 Copyright (C) 2002 John Stewart, CRC Canada.
 DISTRIBUTED WITH NO WARRANTY, EXPRESS OR IMPLIED.
 See the GNU Library General Public License (file COPYING in the distribution)
 for conditions of use and redistribution.
*********************************************************************/

#include <stdlib.h>
#ifndef FLT_MAX 
# include <float.h>
#endif
#ifdef _WIN32
# include <search.h>
# include <stdlib.h>
#endif

#include "stdafx.h"
#include "swt.h"

#include "Mesh.h"
#include "Face.h"
#include "List.h"
#include "MFColor.h"
#include "MFColorRGBA.h"
#include "MFInt32.h"
#include "MFVec2f.h"
#include "MFVec3f.h"
#include "SFInt32.h"
#include "Matrix.h"
#include "DuneApp.h"
#include "Util.h"

Mesh::Mesh(MFVec3f *vertices, MFInt32 *coordIndex,
           MFVec3f *normals, MFInt32 *normalIndex,
           MFFloat *colors, MFInt32 *colorIndex,
           MFVec2f *texCoords, MFInt32 *texCoordIndex,
           float creaseAngle, int meshFlags, float transparency)
{
    _isTriangulated = false;
    _ccw = meshFlags & MESH_CCW;
    _solid = meshFlags & MESH_SOLID;
    _convex = meshFlags & MESH_CONVEX;
    _normalPerVertex = meshFlags & MESH_NORMAL_PER_VERTEX;
    _colorPerVertex = meshFlags & MESH_COLOR_PER_VERTEX;
    _colorRGBA = meshFlags & MESH_COLOR_RGBA;

    _creaseAngle = creaseAngle;
    _transparency = transparency;

    _vertices = vertices;
    if (_vertices)
        _vertices->ref();

    _normals = normals;
    if (_normals)
        if (_normals->getSize() == 0)
            _normals = NULL;
        else
            _normals->ref();

    _colors = colors;
    if (_colors)
        if (_colors->getSize() == 0)
            _colors = NULL;
        else
            _colors->ref();

    _coordIndex = coordIndex;
    _colorIndex = colorIndex;
    _normalIndex = normalIndex;
    _texCoordIndex = texCoordIndex;

    if (_coordIndex)
        _coordIndex->ref();
    else 
        _coordIndex = new MFInt32();

    _texCoords = texCoords;

    if (!_texCoords)
        _texCoords = ::generateTextureCoordinates(_vertices, _coordIndex);
    if (_texCoords)
        _texCoords->ref();
    
    if (_normalPerVertex && (normalIndex == NULL))
        _normalIndex = _coordIndex;
    if (_normalIndex)
        _normalIndex->ref();

    if (_colorPerVertex && (colorIndex == NULL))
        _colorIndex = _coordIndex;
    if (_colorIndex)
        _colorIndex->ref();

    if (_texCoordIndex == NULL)
        _texCoordIndex = _coordIndex;
    if (_texCoordIndex)
        _texCoordIndex->ref();

    _faces = NULL;
    _numFaces = 0;
    buildFaces();
    generateFaceNormals();
    if (!_normals) {
        smoothNormals();
    }
    _listIndex = 0;
    _drawCounter = 0;
    _currentFace = -1;
}

Mesh::~Mesh()
{
    _vertices->unref();
    if (_normals) _normals->unref();
    if (_colors) _colors->unref();
    if (_texCoords) _texCoords->unref();

    if (_coordIndex) _coordIndex->unref();
    if (_normalIndex) _normalIndex->unref();
    if (_colorIndex) _colorIndex->unref();
    if (_texCoordIndex) _texCoordIndex->unref();

    for (int i = 0; i < _numFaces; i++) {
        delete _faces[i];
    }
    delete [] _faces;
    if (_listIndex != 0)
        glDeleteLists(_listIndex, 1);

}

void
Mesh::buildFaces(void)
{
    int start = 0;
    int n = _coordIndex->getSize();
    const int *c = _coordIndex->getValues();
    int i, numFaces = 0;

    for (i = 0; i < n; i++) {
        if (c[i] < -1) {
            swDebugf("silently ignoring coordIndex < -1, assuming -1\n");
            _coordIndex->setSFValue(i,-1);
            c = _coordIndex->getValues();
        }
        if (c[i] == -1) {
            if (i - start > 0) {
                numFaces++;
            }
            start = i + 1;
        } 
    }
    if ((i != 0) && (i != 1))
        if ((c[i-1] != -1) && (c[i-2] != -1))
            numFaces++;
    for (i = 0; i < _numFaces; i++) {
        delete _faces[i];
    }
    delete [] _faces;
    if (numFaces != 0)
       _faces = new Face *[numFaces];
    else
       _faces = new Face *[1];
    _numFaces = numFaces;
    numFaces = 0;
    start = 0;
    for (i = 0; i < n; i++) {
        if (c[i] == -1) {
            if (i - start > 0) {
                _faces[numFaces] = new Face(i - start, start);
                numFaces++;
            }
            start = i + 1;
        }
    }
    // handle last face if coordIndex array do not end with -1
    if (numFaces < _numFaces) {
        _faces[_numFaces - 1] = new Face(n - start, start);
    }
}

#define NUMBER_OF_DRAWS_BEFORE_TRYING_GL_LIST 5

void
Mesh::draw(int meshFlags)
{
    if (_vertices == NULL)
        return;
    if (_vertices->getSize() == 0)
        return;       
    if (_coordIndex == NULL)
        return;
    if (_coordIndex->getSize() == 0)
        return;
   
    if (_listIndex > 0) {
        glCallList(_listIndex);
        return;
    } else
        if (_drawCounter >= 0) {
            if (_drawCounter == NUMBER_OF_DRAWS_BEFORE_TRYING_GL_LIST) {
                _listIndex = glGenLists(1);
                if (_listIndex == 0)
                    _drawCounter = -1; // disables further usage of display list
            } else
                _drawCounter++;
        }

    if (_listIndex > 0)
        glNewList(_listIndex, GL_COMPILE_AND_EXECUTE);

    const float *vertices = _vertices->getValues();
    const int *coordIndex = _coordIndex->getValues();
    const float *normals = _normals ? _normals->getValues() : NULL;
    const int *normalIndex = _normalIndex ? _normalIndex->getValues() : NULL;
    const float *texCoords = _texCoords ? _texCoords->getValues() : NULL;
    const int *texCoordIndex = _texCoordIndex ? _texCoordIndex->getValues() 
                                                 : NULL;
    const float *colors = _colors ? _colors->getValues() : NULL;
    const int *colorIndex = _colorIndex ? _colorIndex->getValues() : NULL;

    bool ccw = _ccw;
    bool solid = _solid;
    bool convex = _convex;
    if (meshFlags != -1) {
        ccw = meshFlags & MESH_CCW;
        solid = meshFlags & MESH_SOLID;
        convex = meshFlags & MESH_CONVEX;
    }

    if (!ccw) glFrontFace(GL_CW);
    if (solid) {
        glEnable(GL_CULL_FACE);
        glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
    } else {
        glDisable(GL_CULL_FACE);
        glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
    }
    if (_colors) {
        glEnable(GL_COLOR_MATERIAL);
        glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
    }
    int colorStride = _colorRGBA ? 4 : 3; 
    for (int i = 0; i < _numFaces; i++) {
        int offset = _faces[i]->getOffset();
        int numVertices = _faces[i]->getNumVertices();
        if (numVertices < 3) 
            continue;
        glBegin(GL_POLYGON);
        for (int j = offset; j < offset + numVertices; j++) {
            if (texCoords && texCoordIndex &&
                (j < _texCoordIndex->getSFSize()) &&
                (texCoordIndex[j] < _texCoords->getSFSize())) {
                glTexCoord2fv(texCoords + texCoordIndex[j] * 2);
            }
            if (colors) { 
                int index = -1; 
                if (_colorPerVertex) {
                    if (j < _colorIndex->getSize())
                        index = colorIndex[j] * colorStride;
                } else {
                    if (_colorIndex && (_colorIndex->getSize() > 0)) {
                        if (i < _colorIndex->getSize())
                               index = colorIndex[i] * colorStride;
                    } else
                        index = i * colorStride;
                }
                if ((index >= 0) && (index < _colors->getSize())) 
                    if (_colorRGBA) 
                        Util::myGlColor4f(colors[index],
                                          colors[index + 1],
                                          colors[index + 2],
                                          colors[index + 3]);
                    else
                        Util::myGlColor4f(colors[index],
                                          colors[index + 1],
                                          colors[index + 2],
                                          1 - _transparency);
            }
            if (normals) {
                int index = -1; 
                if (_normalPerVertex) {
                    if (j < _normalIndex->getSize())
                          index = normalIndex[j] * 3;
                } else {
                    if (_normalIndex && (_normalIndex->getSize() > 0)) {
                        if (i < _normalIndex->getSize())
                            index = normalIndex[i] * 3;
                    } else
                       index = i * 3;
                } 
                if ((index >= 0) && (index < _normals->getSize()))
                    glNormal3fv(normals + index);
            }
            glVertex3fv(vertices + coordIndex[j] * 3);            
        }
        glEnd();
    }

    if (_colors) {
        glDisable(GL_COLOR_MATERIAL);
    }

    if (solid) {
        glDisable(GL_CULL_FACE);
        glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
    } else {
        glEnable(GL_CULL_FACE);
        glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
    }
    if (!ccw) glFrontFace(GL_CCW);

    if (_listIndex > 0) {
        glEndList();
        unsigned int error = TheApp->printRenderErrors(false);
        if (error == GL_OUT_OF_MEMORY) {
            glDeleteLists(_listIndex, 1);
            _listIndex = 0;
            _drawCounter = -1; // disables further usage of display list
        } else if (error != GL_NO_ERROR)
            TheApp->printRenderErrors(error);
    }
}

void
Mesh::drawNormals(void) 
{
    if (_vertices == NULL)
        return;
    if (_vertices->getSize() == 0)
        return;       
    if (_coordIndex == NULL)
        return;
    if (_coordIndex->getSize() == 0)
        return;

    const float *vertices = _vertices->getValues();
    const int *coordIndex = _coordIndex->getValues();
    const float *normals = _normals ? _normals->getValues() : NULL;
    const int *normalIndex = _normalIndex ? _normalIndex->getValues() : 
                                            coordIndex;

    glDisable(GL_LIGHTING);
    glBegin(GL_LINES);
    for (int i = 0; i < _numFaces; i++) {
        int offset = _faces[i]->getOffset();
        int numVertices = _faces[i]->getNumVertices();
        for (int j = offset; j < offset + numVertices; j++) {
            Vec3f v1 = vertices + coordIndex[j] * 3;
            Vec3f v2 = v1 + Vec3f(normals + normalIndex[j] * 3) * 0.5f;
            glVertex3f(v1.x, v1.y, v1.z);
            glVertex3f(v2.x, v2.y, v2.z);
        }
    }
    glEnd();
    glEnable(GL_LIGHTING);
}


class Edge {
public:
                Edge(int p1, int p2, int f, bool s = false)
                   { 
                   pos1 = p1;
                   pos2 = p2; 
                   face = f; 
                   smooth = s; 
                   }
    int         pos1;
    int         pos2;
    int         face;
    bool        smooth;
};

typedef List<Edge *> EdgeList;

void
Mesh::generateFaceNormals()
{
    int i;
    int n = _coordIndex->getSize();
    const int *coordIndex = _coordIndex->getValues();
    const float *vertices = _vertices->getValues();
    int numVertices = _vertices->getSFSize();
    Face *const *face = _faces;
    int indFaces;

    indFaces = 0;
    if (_vertices->getSize() != 0) {
        for (i = 0; i < n; face++) {
            indFaces++;
            if (indFaces > _numFaces) 
                break;

            if ((coordIndex[i] >= numVertices) || (coordIndex[i] < 0)) {
                while ((coordIndex[i] < 0) && (i < n)) 
                    i++;
                continue;
            }
            Vec3f c1(vertices + coordIndex[i++] * 3);

            if ((coordIndex[i] >= numVertices) || (coordIndex[i] < 0)) {
                while ((coordIndex[i] < 0) && (i < n)) 
                    i++;
                continue;
            }
            Vec3f c2(vertices + coordIndex[i++] * 3);

            if ((coordIndex[i] >= numVertices) || (coordIndex[i] < 0)) {
                while ((coordIndex[i] < 0) && (i < n)) 
                    i++;
                continue;
            }
            Vec3f c3(vertices + coordIndex[i++] * 3);

            Vec3f v1 = c1 - c2;
            Vec3f v2 = c3 - c2;
            Vec3f normal = _ccw ? v2.cross(v1) : v1.cross(v2);
            normal.normalize();
            if (face)
                if (*face) 
                    (*face)->setNormal(normal);
            while (i < n && coordIndex[i] != -1) i++;
            while (i < n && coordIndex[i] == -1) i++;
        }
    }
}

static Edge *
findEdge(const int *coordIndex, EdgeList::Iterator *i, int vertex)
{
    for(; i != NULL; i = i->next()) {
        Edge *e = i->item();
        if (coordIndex[e->pos2] == vertex) 
            return e;
    }
    return NULL;
}

void
Mesh::smoothNormals(bool optimizeNormals)
{
    optimizeNormals = false;

    if ((_normalIndex != NULL) && (_normalIndex->getSize() > 0))
        _normalIndex->unref();

    if (_vertices->getSize()==0)
       return;       

    int i;
    EdgeList::Iterator *j;
    int nVerts = _vertices->getSize() / 3;
    Array<Vec3f> normals;
    // the created normals are normalPerVertex, so any vertex need a normal
    _normalPerVertex = true;
    float cosAngle = (float) cos(_creaseAngle);
    int numFaces = 0;
    EdgeList *edgeLists = new EdgeList[_vertices->getSize()];
    const int *coordIndex = _coordIndex->getValues();
    int nCoords = _coordIndex->getSize();
    int start = 0;
    Array<int> normalIndex;

    for (i = 0; i < nCoords; i++) {
        int v = coordIndex[i];
        if ((v == -1) || (v > nVerts)) {
            if (i - start > 0) {
                numFaces++;
            }
            start = i + 1;
        } else {
            int pos2;
            if (i == nCoords - 1 || coordIndex[i + 1] == -1) {
                pos2 = start;
            } else {
                pos2 = i + 1;
            }
            edgeLists[v].append(new Edge(i, pos2, numFaces));
        }
        normalIndex[i] = -1;
    }

    if (optimizeNormals) {
        for (i = 0; i < nVerts; i++) {
            for (j = edgeLists[i].first(); j != NULL; j = j->next()) {
                Edge *e = j->item();
                if (e->face >= _numFaces)
                    continue;
                const Vec3f &refNormal = _faces[e->face]->getNormal();
                int v2 = coordIndex[e->pos2];
                Edge *f = findEdge(coordIndex, edgeLists[v2].first(), i);
                if (f && (f->face < _numFaces)) {
                    const Vec3f &otherNormal = _faces[f->face]->getNormal();
                    float dotNormals = refNormal.dot(otherNormal);
                    if (dotNormals > cosAngle) {
                        // this edge is smooth
                        int i1 = normalIndex[e->pos1];
                        int i2 = normalIndex[f->pos2];
                        if (i1 == -1 && i2 == -1) {
                            // create a new normal
                            int index = normals.size();
                            normals[index] = refNormal + otherNormal;
                            normalIndex[e->pos1] = index;
                            normalIndex[f->pos2] = index;
                        } else if (i1 == -1 && i2 != -1) {
                            // use v2's normal
                            normals[i2] += refNormal;
                            normals[i2] /= 2.0f;
                            normalIndex[e->pos1] = i2;
                            f->smooth = true;
                        } else if (i1 != -1 && i2 == -1) {
                            // use v1's normal
                            normals[i1] += otherNormal;
                            normals[i1] /= 2.0f;
                            normalIndex[f->pos2] = i1;
                            e->smooth = true;
                        } else {
                            // they're both specified, so combine them in place
                            normals[i1] += normals[i2];
                            normals[i1] /= 2.0f;
                            normals[i2] = normals[i1];
                            if (dotNormals != 1.0f) {
                                e->smooth = true;
                                f->smooth = true;
                            }
                        }
                    }
                }
            }
        }
    }
    // vertices without normals get the face normal
    for (i = 0; i < nVerts; i++) {
        for (j = edgeLists[i].first(); j != NULL; j = j->next()) {
            Edge *e = j->item();
            if (normalIndex[e->pos1] == -1) {
                int index = normals.size();
                if ((e->face < _numFaces) && _faces[e->face]) {
                    normals[index] = _faces[e->face]->getNormal();
                    normalIndex[e->pos1] = index;
                }
            }
        }
    }
    if (!optimizeNormals) {
        for (i = 0; i < nVerts; i++) {
            for (j = edgeLists[i].first(); j != NULL; j = j->next()) {
                Edge *e = j->item();
                if (e->face >= _numFaces)
                    continue;
                const Vec3f &refNormal = _faces[e->face]->getNormal();
                int v2 = coordIndex[e->pos2];
                Edge *f = findEdge(coordIndex, edgeLists[v2].first(), i);
                if (f && (f->face < _numFaces)) {
                    const Vec3f &otherNormal = _faces[f->face]->getNormal();
                    float dotNormals = refNormal.dot(otherNormal);
                    if (dotNormals > cosAngle) {
                        // this edge is smooth
                        int i1 = normalIndex[e->pos1];
                        int i2 = normalIndex[f->pos2];
                        if (i1 == -1 && i2 == -1) {
                            // create a new normal
                            normals[i1] = (refNormal + otherNormal) / 2.0f;
                            normals[i2] = (refNormal + otherNormal) / 2.0f;
                        } else if (i1 == -1 && i2 != -1) {
                            // use v2's normal
                            normals[i2] += refNormal;
                            normals[i2] /= 2.0f;
                        } else if (i1 != -1 && i2 == -1) {
                            // use v1's normal
                            normals[i1] += otherNormal;
                            normals[i1] /= 2.0f;
                        } else {
                            // they're both specified, so combine them in place
                            normals[i1] += normals[i2];
                            normals[i1] /= 2.0f;
                            normals[i2] = normals[i1];
                            if (dotNormals != 1.0f) {
                                e->smooth = true;
                                f->smooth = true;
                            }
                        }
                    }
                }
            }
        }
    }
    // join all smoothed normals of a vertex
    Array <IntArray *> smoothNormalIndices;
    for (i = 0; (i < normalIndex.size()) || (i < nVerts); i++)
        smoothNormalIndices.append(new IntArray());
    for (i = 0; i < nVerts; i++) {
        // find all normals for a vertex
        for (j = edgeLists[i].first(); j != NULL; j = j->next()) {
            Edge *e = j->item();
            if (e->face >= _numFaces)
                continue;
            if (e->smooth)
                smoothNormalIndices[i]->append(e->pos1);
        }
    }
    for (i = 0; i < nVerts; i++) {
        Vec3f averageNormal(0, 0, 0);
        bool hasAverageNormal = false;
        for (int j = 0; j < smoothNormalIndices[i]->size(); j++) {
            hasAverageNormal = true;
            int indexNormal = smoothNormalIndices[i]->get(j);
            averageNormal += normals[normalIndex[indexNormal]];
        }
        if (hasAverageNormal) {
            averageNormal.normalize();
            for (int j = 0; j < smoothNormalIndices[i]->size(); j++) {
                int indexNormal = smoothNormalIndices[i]->get(j);
                normals[normalIndex[indexNormal]] = averageNormal;
            }
        }
    }
    // vertices with normal 0 0 0 get the face normal
    for (i = 0; i < nVerts; i++) {
        for (j = edgeLists[i].first(); j != NULL; j = j->next()) {
            Edge *e = j->item();
            if (normals[e->pos1].isZero())
                if ((e->face < _numFaces) && _faces[e->face])
                    normals[e->pos1] = _faces[e->face]->getNormal();
            if (normals[e->pos2].isZero())
                if ((e->face < _numFaces) && _faces[e->face])
                    normals[e->pos2] = _faces[e->face]->getNormal();           
        }
    }
    // cleanup
    for (i = 0; i < nVerts; i++) {
        for (j = edgeLists[i].first(); j != NULL; j = j->next()) {
            Edge *e = j->item();
            delete e;
        }
        edgeLists[i].removeAll();
    }
    delete [] edgeLists;
    for (i = 0; i < smoothNormalIndices.size(); i++) {
        delete smoothNormalIndices[i];
        smoothNormalIndices[i] = NULL;        
    }
    for (int k = 0; k < normals.size(); k++) {
        normals[k].normalize();
    }
    delete _normals;
    _normals = new MFVec3f((float *) normals.extractData(), normals.size() * 3);
    _normals = (MFVec3f *)_normals->copy();
    if (normalIndex.size() > 0) {
        int *n = new int[normalIndex.size() + 1];
        for (i = 0; i < normalIndex.size(); i++)
            n[i] = normalIndex[i];
        int nsize = normalIndex.size();
        if (n[nsize - 1] != -1) {
            nsize++;
            n[nsize - 1] = -1;
        }
        _normalIndex = new MFInt32(n, nsize);
    }
    _normals->ref();
    _normalIndex->ref();
}

static int
compareFaceFunction(const void *f1, const void *f2)
{
    float min1 = (*(Face **) f1)->getMinZ();
    float max1 = (*(Face **) f1)->getMaxZ();
    float min2 = (*(Face **) f2)->getMinZ();
    float max2 = (*(Face **) f2)->getMaxZ();

    if (max1 < min2) return -1;
    if (max2 < min1) return 1;
    return 0;
}

void
Mesh::sort()
{
#ifdef SORT_IN_MESH
    if (TheApp->GetRenderFasterWorse())
#endif
        return;
    const int  *coordIndex = _coordIndex->getValues();
    const float *vertices = _vertices->getValues();
    int i, j;
    Matrix matrix;

    if (_vertices->getSize()==0)
       return;       

    glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *) matrix);

    for (i = 0; i < _numFaces; i++) {
        int offset = _faces[i]->getOffset();
        int numVertices = _faces[i]->getNumVertices();
        Vec3f v(0.0f, 0.0f, 0.0f);
        float min = FLT_MAX;
        float max = 0.0f;
        for (j = 0; j < numVertices; j++) {
            if (offset + j >= _vertices->getSFSize())
                break;
            v = -(matrix * Vec3f(vertices + coordIndex[offset + j] * 3));
            if (v.z > max) max = v.z;
            if (v.z < min) min = v.z;
        }
        _faces[i]->setMinZ(min);
        _faces[i]->setMaxZ(max);
    }
    qsort(_faces, _numFaces, sizeof(Face *), compareFace);
}

MFVec2f* 
generateTextureCoordinates(MFVec3f *points, MFInt32* cindex)
{
    MFVec2f *texcoords = NULL;
    if (!points)
        return texcoords;
    int npoints = points->getSFSize(); 
    int i;

    if (npoints > 0) {
        float* tcoords = new float[npoints * 2];
        for (i = 0; i < npoints; i++) {
            tcoords[i * 2] = 0;
            tcoords[i * 2 + 1] = 0;
        }

        /* texture generation points... */

        float minVals[3];
        float maxVals[3];

        // find first valid point to initialise 
        bool initialised = false;
        for (i = 0; i < cindex->getSize(); i++) {
            int ind = cindex->getValue(i);
            if ((ind >= 0) && (ind < npoints)) {
                const float *point = points->getValue(ind);
                if (point) {
                    for (int j = 0; j < 3; j++) {
                        minVals[j] = point[j];
                        maxVals[j] = point[j];
                    }
                initialised = true;
                break;
                } 
            } 
        }

        if (initialised == false)
            return NULL;

        /* generate default texture mapping */
        for (i = 0; i < cindex->getSize(); i++) {
            int ind = cindex->getValue(i);
            if ((ind >= 0) && (ind < npoints)) {
                const float *point = points->getValue(ind);
                if (point) {
                    for (int j = 0; j < 3; j++) {
                        if (minVals[j] > point[j]) 
                            minVals[j] = point[j];
                        if (maxVals[j] < point[j]) 
                            maxVals[j] = point[j];
                    }
                } 
            } 
        }

        float Ssize = 0.0;
        float Tsize = 0.0;
        int Sindex = 0;
        int Tindex = 0;

        /* find the S,T mapping. */
        float Xsize = maxVals[0]-minVals[0]; 
        float Ysize = maxVals[1]-minVals[1];
        float Zsize = maxVals[2]-minVals[2];

        if ((Xsize >= Ysize) && (Xsize >= Zsize)) {
            /* X size largest */
            Ssize = Xsize;
            Sindex = 0;
            if (Ysize >= Zsize) {
                Tsize = Ysize;
                Tindex = 1;
            } else {
                Tsize = Zsize;
                Tindex = 2;
            }
        } else if ((Ysize >= Xsize) && (Ysize >= Zsize)) {
            /* Y size largest */
            Ssize = Ysize;
            Sindex = 1;
            if (Xsize >= Zsize) {
                Tsize = Xsize;
                Tindex = 0;
            } else {
                Tsize = Zsize;
                Tindex = 2;
            }
        } else {
            /* Z is the largest */
            Ssize = Zsize;
            Sindex = 2;
            if (Xsize >= Ysize) {
                Tsize = Xsize;
                Tindex = 0;
            } else {
                Tsize = Ysize;
                Tindex = 1;
            }
        }
        if (Ssize == 0) 
            return NULL;

        for( i = 0; i < cindex->getSize(); i++) {
            int ind = cindex->getValue(i);
            if ((ind >= 0) && (ind < npoints)) {
                const float *point = points->getValue(ind);
                if (point) {
                    /* temporary place for X,Y,Z */
                    float XYZ[3] = {0.0, 0.0, 0.0};

                    XYZ[0] = point[0]; 
                    XYZ[1] = point[1]; 
                    XYZ[2] = point[2];
                       
                    // default textures 
                    // we want the S values to range from 0..1, 
                    //     and the T values to range from 0..S/T
                    tcoords[ind*2]   = (XYZ[Sindex] - minVals[Sindex])/Ssize;
                    tcoords[ind*2+1] = (XYZ[Tindex] - minVals[Tindex])/Ssize;
                }
            }
        }
        texcoords = new MFVec2f(tcoords, 2 * npoints);
    }
    return texcoords;
}

MFVec2f* 
Mesh::generateTextureCoordinates()
{
    return ::generateTextureCoordinates(_vertices, _coordIndex); 
}

#define VEC3F(x,y) Vec3f((*x).getValue(y))

bool 
Mesh::onlyPlanarFaces(void)
{
    int vertexCounter = 0;
    for (int i = 0; i < _coordIndex->getSize(); i++) {
        if (_coordIndex->getValue(i) <= -1)
            vertexCounter = 0;
        else {
            Vec3f vec1(0, 0, 0);
            Vec3f vec2(0, 0, 0);
            Vec3f vec3(0, 0, 0);
            Vec3f cross12(0, 0, 0);
            if (vertexCounter == 1)
                vec1 = VEC3F(_vertices,1) - VEC3F(_vertices,0);
            else if (vertexCounter == 2) {
                vec2 = VEC3F(_vertices,2) - VEC3F(_vertices,0);
                cross12 = vec1.cross(vec2);
            } else if (vertexCounter > 2) {
                vec3 = VEC3F(_vertices,3) - VEC3F(_vertices,0);
                if (cross12.cross(vec3).length() > EPSILON)
                    return false;
            }
            vertexCounter++;
        }
   } 
   return true;
}

void
Mesh::setNormals(MFVec3f *normals) 
{
    if (_normals)
        _normals->unref();
    _normals = normals;
}

void
Mesh::optimize(void)
{
    _coordIndex = optimizeCoordIndex();
}

MFInt32 *
Mesh::optimizeCoordIndex(void)
{
    int i;
    const float *vertices = _vertices->getValues();
    MFInt32 *coordIndex = new MFInt32();
    for (i = 0; i < _coordIndex->getSize(); i++)
        coordIndex->appendSFValue(_coordIndex->getValue(i));

    // test if all coordIndex values are in range and 
    // delete them, which are not
    for (i = 0; i < coordIndex->getSize(); i++)
        if (coordIndex->getValue(i) >= _vertices->getSFSize())
            coordIndex->removeSFValue(i--);        

    // test if there are faces with identical vertices in a row
    // if such unnecessary repeated vertices are found, 
    // the matching coordIndices are deleted
    Array<int> coordIndexDeleteArray;
    for (i = 0; i < _numFaces; i++) {
        int offset = _faces[i]->getOffset();
        int numVertices = _faces[i]->getNumVertices();
        if (numVertices < 3)
            continue;
        Vec3f lastVertex(vertices + coordIndex->getValue(offset) * 3);
        for (int j = 1; j < numVertices; j++) {
            Vec3f vertex(vertices + coordIndex->getValue(offset + j) * 3);
            if (vertex == lastVertex)
                coordIndexDeleteArray.append(offset + j);
            lastVertex = vertex;
        }
    }
    for (i = coordIndexDeleteArray.size() - 1; i >= 0; i--)
        coordIndex->removeSFValue(coordIndexDeleteArray[i]);

    // search identical vertices with different coordIndex
    for (i = coordIndex->getSize() - 1; i >= 0; i--) {
        if (coordIndex->getValue(i) >= 0) {
            Vec3f vertex1(vertices + coordIndex->getValue(i) * 3);
            for (int j = i + 1; j < coordIndex->getSize(); j++) {
                if (coordIndex->getValue(j) >= 0) {
                    Vec3f vertex2(vertices + coordIndex->getValue(j) * 3);
                    // replace coordIndex with index of first identical vertex 
                    if (vertex1 == vertex2)
                        coordIndex->setSFValue(j, coordIndex->getValue(i));
                }
            }
        }
    }

    // test for repeated faces and delete them
    coordIndexDeleteArray.resize(0);
    int faceOffset1 = 0;
    for (i = 0; i < coordIndex->getSize(); i++)
        if (coordIndex->getValue(i) < 0) {
            int j = i + 1;
            int faceOffset2 = j;
            bool identicalFace = true;
            while (j < coordIndex->getSize()) {
                for (int coordIndex1 = faceOffset1; coordIndex1 < i; 
                     coordIndex1++) {
                    j++;
                    Vec3f vert1(vertices + 
                                coordIndex->getValue(coordIndex1) * 3);
                    int coordIndex2 = faceOffset2 + coordIndex1 - faceOffset1;
                    if (coordIndex2 < coordIndex->getSize()) {
                        // end of second face ?            
                        if (coordIndex->getValue(coordIndex2) < 0) {
                            if (identicalFace)
                                for (int k = faceOffset2; k < coordIndex2; k++)
                                     coordIndexDeleteArray.append(k);
                            faceOffset2 = coordIndex2 + 1;
                            break;                       
                        } else {
                            Vec3f vert2(vertices + 
                                        coordIndex->getValue(coordIndex2) * 3);
                            if (vert1 != vert2)
                                identicalFace = false;
                        }
                    } else {
                       if (identicalFace)
                           for (int k = faceOffset2; k < coordIndex2; k++)
                               coordIndexDeleteArray.append(k);
                        break;
                    }
                }
            }
            faceOffset1 = i + 1;
        }
    for (i = coordIndexDeleteArray.size() - 1; i >= 0; i--)
        coordIndex->removeSFValue(coordIndexDeleteArray[i]);

    // search for faces with less than 3 vertices and delete them
    int verticesPerFace = 0;
    for (i = 0; i < coordIndex->getSize(); i++)
        if (coordIndex->getValue(i) < 0) {
            if (verticesPerFace < 3) {
                coordIndex->removeSFValue(i--);
                for (int j = i; j >= 0; j--)
                    if (coordIndex->getValue(j) < 0)
                        break;
                    else {
                        coordIndex->removeSFValue(j);
                        i--;
                    }
            }
            verticesPerFace = 0;
        } else
            verticesPerFace++;       
    
    return coordIndex;
}

void
Mesh::optimizeVertices(MFVec3f *newCoord, MFInt32 *newCoordIndex)
{
    int i;
    const float *vertex = _vertices->getValues();
    Array<bool> vertexAlreadyHandled;
    for (i = 0; i < _vertices->getSize(); i++)
        vertexAlreadyHandled.append(false);

    const int *coordIndex = _coordIndex->getValues();
    Array<int> tmpCoordIndex;
    for (i = 0; i < _coordIndex->getSize(); i++)
        tmpCoordIndex.append(-1);

    int vertexCounter = 0;
    for (i = 0; i < _vertices->getSize(); i++) {
        bool increaseVertexCounter = false;
        for (int j = 0; j < _coordIndex->getSize(); j++) {
            if ((coordIndex[j] == i) && (!vertexAlreadyHandled[i])) {
                tmpCoordIndex[j] = vertexCounter;
                increaseVertexCounter = true;
                if (coordIndex[j] > -1) {
                    for (int k = j + 1; k < _coordIndex->getSize(); k++) {
                        if (coordIndex[k] < 0)
                            continue;
                        if (i == coordIndex[k])
                            continue;
                        int v1 = i * 3;
                        int v2 = coordIndex[k] * 3;
                        Vec3f vec1(vertex[v1], vertex[v1 + 1], vertex[v1 + 2]);
                        Vec3f vec2(vertex[v2], vertex[v2 + 1], vertex[v2 + 2]);
                        if (vec1 == vec2) {
                            vertexAlreadyHandled[coordIndex[k]] = true;
                            tmpCoordIndex[k] = vertexCounter;
                        }
                    }
                }
            }
        }
        if (increaseVertexCounter && (!vertexAlreadyHandled[i]))  {
            vertexCounter++;
            int v = i * 3;
            newCoord->appendSFValue(vertex[v], vertex[v + 1], vertex[v + 2]);
        }
    }

    for (i = 0; i < _coordIndex->getSize(); i++)
        newCoordIndex->appendSFValue(tmpCoordIndex[i]);
}

int                 
Mesh::getMaxNumberEdgesPerFace(void)
{
    int maxNumberEdges = 0;
    for (int i = 0; i < getNumFaces(); i++)
        if (_faces[i]->getNumVertices() > maxNumberEdges)
            maxNumberEdges = _faces[i]->getNumVertices();
    return maxNumberEdges;
}

#ifdef HAVE_GLUNEWTESS

void                
Mesh::startTesselation(GLenum type)
{
    _tesselationType = type;
    _tesselationStart = _triangulatedVertices.size() / 3;
    _evenTriangleStrip = false;
}

void 
Mesh::addNewVertex(VertexInfo* vertexInfo)
{
    int vertexNumber = _triangulatedVertices.size() / 3;
    if (_tesselationType == GL_TRIANGLE_FAN) {
        // repeat indices of first and last vertices of triangle fan
        if (vertexNumber > (_tesselationStart + 2)) {
            _triangulatedIndices.append(-1);
            _triangulatedIndices.append(_tesselationStart);
            _triangulatedIndices.append(vertexNumber - 1);
        }
    } else if (_tesselationType == GL_TRIANGLE_STRIP) {
        // repeat indices of the two last vertices of triangle fan
        if (vertexNumber > (_tesselationStart + 2)) {
            _triangulatedIndices.append(-1);
            if (_evenTriangleStrip) {
                _triangulatedIndices.append(vertexNumber - 2);
                _triangulatedIndices.append(vertexNumber - 1);
            } else {
                _triangulatedIndices.append(vertexNumber - 1);
                _triangulatedIndices.append(vertexNumber - 2);
            }
            _evenTriangleStrip = ! _evenTriangleStrip;
        }
    } else if (_tesselationType == GL_TRIANGLES) {
        if (vertexNumber > (_tesselationStart + 2)) {
            _triangulatedIndices.append(-1);
            _tesselationStart = vertexNumber;
        }
    }
   
    _triangulatedIndices.append(vertexNumber);

    _triangulatedVertices.append(vertexInfo->vertex[0]);
    _triangulatedVertices.append(vertexInfo->vertex[1]);
    _triangulatedVertices.append(vertexInfo->vertex[2]);

    _triangulatedNormals.append(vertexInfo->normal[0]);
    _triangulatedNormals.append(vertexInfo->normal[1]);
    _triangulatedNormals.append(vertexInfo->normal[2]);

    _triangulatedColors.append(vertexInfo->color[0]);
    _triangulatedColors.append(vertexInfo->color[1]);
    _triangulatedColors.append(vertexInfo->color[2]);
    _triangulatedColors.append(vertexInfo->color[3]);

    _triangulatedTexCoords.append(vertexInfo->texCoord[0]);
    _triangulatedTexCoords.append(vertexInfo->texCoord[1]);
}

void 
Mesh::endNewPolygon(void) 
{
    _triangulatedIndices.append(-1);
}

static void beginTriangleCallback(GLenum type, void *data)
{
    Mesh *self = (Mesh*)data;
    self->startTesselation(type);
}

void newVertexCallback(void *vertex_data, void *data)
{
    Mesh *self = (Mesh*)data;
    VertexInfo *vertexInfo = (VertexInfo *) vertex_data;

    self->addNewVertex(vertexInfo);    
}

void endTriangleCallback(void *data) {
    Mesh *self = (Mesh*)data;    
    self->endNewPolygon();
}

void Mesh::collectMemoryToFree(VertexInfo * vertexInfo)
{
    _newMemory4VerticesInfo.append(vertexInfo);
}


static void 
combineVertices(GLdouble coords[3], GLdouble *vertex_data[4],
                GLfloat weight[4], GLdouble **dataOut, void *data) {
    int i;

    Mesh *self = (Mesh*)data;
    VertexInfo *vertexInfo = new VertexInfo();
    self->collectMemoryToFree(vertexInfo);

    vertexInfo->vertex[0] = coords[0];
    vertexInfo->vertex[1] = coords[1];
    vertexInfo->vertex[2] = coords[2];

    VertexInfo *vertexData[4];
    for (i = 0; i < 4; i++)
        vertexData[i] = (VertexInfo *)vertex_data[i]; 

    for (i = 0; i < 3; i++) {
        vertexInfo->normal[i] = 0;
        for (int j = 0; j < 4; j++)
            if (vertexData[j] != NULL)
                vertexInfo->normal[i] += weight[j] * vertexData[j]->normal[i];
    }

    for (i = 0; i < 4; i++) {
        vertexInfo->color[i] = 0;
        for (int j = 0; j < 4; j++)
            if (vertexData[j] != NULL)
                vertexInfo->color[i] += weight[j] * vertexData[j]->color[i];
    }

    for (i = 0; i < 2; i++) {
        vertexInfo->texCoord[i] = 0;
        for (int j = 0; j < 4; j++)
            if (vertexData[j] != NULL)
                vertexInfo->texCoord[i] += weight[j] * 
                                           vertexData[j]->texCoord[i];
    }

    *dataOut = &vertexInfo->vertex[0];
}

static void tesselationError(GLenum error) 
{
    swDebugf("%s\n", gluErrorString(error));
}

void 
Mesh::initialiseTriangulation(GLUtesselator *tess)
{
    gluTessCallback(tess, GLU_TESS_BEGIN_DATA, 
                    (MY_GLU_CALLBACK_PTR)beginTriangleCallback);
    gluTessCallback(tess, GLU_TESS_VERTEX_DATA, 
                    (MY_GLU_CALLBACK_PTR)newVertexCallback);
    gluTessCallback(tess, GLU_TESS_END_DATA, 
                    (MY_GLU_CALLBACK_PTR)endTriangleCallback);
    gluTessCallback(tess, GLU_TESS_COMBINE_DATA, 
                    (MY_GLU_CALLBACK_PTR)combineVertices);
#ifdef _WIN32
    gluTessCallback(tess, GLU_TESS_ERROR, 
                    (MY_GLU_CALLBACK_PTR)tesselationError);
#endif
    _evenTriangleStrip = false;
}

Mesh*
Mesh::buildNewTriangulatedMesh(void)
{
    int i;

    MFVec3f *vertices = new MFVec3f(_triangulatedVertices.size() / 3);
    for (i = 0; i < _triangulatedVertices.size(); i++)
        vertices->setValue(i, _triangulatedVertices[i]); 

    MFInt32 *coordIndex = new MFInt32(_triangulatedIndices.size());
    for (i = 0; i < _triangulatedIndices.size(); i++)
        coordIndex->setSFValue(i, _triangulatedIndices[i]);

    MFVec3f *normals = NULL;
    if (_triangulatedHasNormals) {
        normals = new MFVec3f(_triangulatedNormals.size() / 3);
        for (i = 0; i < _triangulatedNormals.size(); i++)
            normals->setValue(i, _triangulatedNormals[i]); 
    }
    MFInt32 *normalIndex = NULL;

    MFColor *colors = NULL;
    if (_triangulatedHasColors) {
        colors = new MFColor(_triangulatedColors.size() / 4);
        for (i = 0; i < _triangulatedColors.size(); i++)
            colors->setValue(i, _triangulatedColors[i]); 
    }
    MFColorRGBA *colorsRGBA = NULL;
    if (_triangulatedHasColorsRGBA) {
        colorsRGBA = new MFColorRGBA(_triangulatedColors.size() / 4);
        for (i = 0; i < _triangulatedColors.size(); i++)
            colorsRGBA->setValue(i, _triangulatedColors[i]); 
    }    
    MFInt32 *colorIndex = NULL;

    MFVec2f *texCoords = NULL;
    if (_triangulatedHasTexCoords) {
        texCoords = new MFVec2f(_triangulatedTexCoords.size() / 2);
        for (i = 0; i < _triangulatedTexCoords.size(); i++)
            texCoords->setValue(i, _triangulatedTexCoords[i]); 
    }
    if (texCoords == NULL)
        texCoords = ::generateTextureCoordinates(vertices, coordIndex);
    MFInt32 *texCoordIndex = NULL;

    float creaseAngle = _creaseAngle;
    int meshFlags = 0;
    if (_ccw)
        meshFlags |= MESH_CCW;
    if (_solid)
        meshFlags |= MESH_SOLID;
    if (_convex)
        meshFlags |= MESH_CONVEX;
    if (_colorPerVertex)
        meshFlags |= MESH_COLOR_PER_VERTEX;
    if (_normalPerVertex)
        meshFlags |= MESH_NORMAL_PER_VERTEX;
    if (_triangulatedHasColorsRGBA)
        meshFlags |= MESH_COLOR_RGBA;
    float transparency = _transparency;

    return new Mesh(vertices, coordIndex, normals, normalIndex, colors, 
                    colorIndex, texCoords, texCoordIndex,
                    creaseAngle, meshFlags, transparency);
}

Mesh *
Mesh::triangulate(void)
{
    _triangulatedVertices.resize(0);
    _triangulatedIndices.resize(0);
    _triangulatedNormals.resize(0);
    _triangulatedColors.resize(0);
    _triangulatedTexCoords.resize(0);

    GLUtesselator *tess = gluNewTess();

    if (tess == NULL)
        return NULL;

    initialiseTriangulation(tess);

    const float *vertices = _vertices->getValues();
    const int  *coordIndex = _coordIndex->getValues();

    const float *normals = _normals ? _normals->getValues() : NULL;
    const int *normalIndex = _normalIndex ? _normalIndex->getValues() : NULL;

    const float *colors = _colors ? _colors->getValues() : NULL;
    const int *colorIndex = _colorIndex ? _colorIndex->getValues() : NULL;

    const float *texCoords = _texCoords ? _texCoords->getValues() : NULL;
    const int *texCoordIndex = _texCoordIndex ? _texCoordIndex->getValues() 
                                                : NULL;

    _triangulatedHasNormals = normals ? true : false;

    _triangulatedHasColors = colors ? true : false;
    _triangulatedHasColorsRGBA = _triangulatedHasColors && _colorRGBA;
    if (_colorRGBA)
        _triangulatedHasColors = false;

    _triangulatedHasTexCoords = (texCoords && texCoordIndex) ? true : false;
  
    for (int i = 0; i < _numFaces; i++) {
        _currentFace = i;
        int offset = _faces[i]->getOffset();
        int numVertices = _faces[i]->getNumVertices();
        if (numVertices < 3) 
            continue;

        int colorStride = _colorRGBA ? 4 : 3; 
        _verticesInfo.resize(0);
        int j;
        for (j = offset; j < offset + numVertices; j++) {
            if ((coordIndex[j] * 3) >= _vertices->getSize())
                continue;
            VertexInfo vertexInfo;
            const float *vertices = _vertices->getValue(coordIndex[j]);
            vertexInfo.vertex[0] = vertices[0];
            vertexInfo.vertex[1] = vertices[1];
            vertexInfo.vertex[2] = vertices[2];
            if (normals) {
                int index = -1; 
                if (_normalPerVertex) {
                    if (j < _normalIndex->getSize())
                          index = normalIndex[j] * 3;
                } else {
                    if (_normalIndex && (_normalIndex->getSize() > 0)) {
                        if (i < _normalIndex->getSize())
                            index = normalIndex[i] * 3;
                    } else
                       index = i * 3;
                } 
                if ((index >= 0) && (index < _normals->getSize())) {
                    vertexInfo.normal[0] = normals[index];
                    vertexInfo.normal[1] = normals[index + 1];
                    vertexInfo.normal[2] = normals[index + 2];
                } else
                    _triangulatedHasNormals = false; // invalid Normal node
            }
            if (colors) {
                int index = -1; 
                if (_colorPerVertex) {
                    if (j < _colorIndex->getSize())
                        index = colorIndex[j] * colorStride;
                } else {
                    if (_colorIndex && (_colorIndex->getSize() > 0)) {
                        if (i < _colorIndex->getSize())
                               index = colorIndex[i] * colorStride;
                    } else
                        index = i * colorStride;
                }
                if ((index >= 0) && (index < _colors->getSize())) {
                    vertexInfo.color[0] = colors[index];
                    vertexInfo.color[1] = colors[index + 1];
                    vertexInfo.color[2] = colors[index + 2];
                    if (_colorRGBA) 
                        vertexInfo.color[3] = colors[index + 3];
                    else
                        vertexInfo.color[3] = 1 - _transparency;
                } else {
                    // invalid Color node
                    _triangulatedHasColors = false;
                    _triangulatedHasColorsRGBA = false; 
                }
            }

            if (texCoords && texCoordIndex &&
                (j < _texCoordIndex->getSFSize()) &&
                (texCoordIndex[j] < _texCoords->getSFSize())) {
                 vertexInfo.texCoord[0] = texCoords[texCoordIndex[j] * 2];
                 vertexInfo.texCoord[1] = texCoords[texCoordIndex[j] * 2 + 1];
            }
            _verticesInfo.append(vertexInfo);
        }

        gluTessBeginPolygon(tess, this);
        gluTessBeginContour(tess);
        for (j = 0; j < numVertices; j++) {
            gluTessVertex(tess, (GLdouble*)
                                &(_verticesInfo[j].vertex), 
                                &(_verticesInfo[j].vertex));
        }
        gluTessEndContour(tess);
        gluTessEndPolygon(tess);
    }

    // destroy the tesselation object
    gluDeleteTess(tess);

    _verticesInfo.resize(0);
    for (List<VertexInfo *>::Iterator *it = _newMemory4VerticesInfo.first(); 
         it != NULL; it = it->next())
        delete it->item();

    _isTriangulated = true;
    return buildNewTriangulatedMesh();
}

#endif

#ifdef TEST
#include "Scene.h"
#include "NodeBox.h"

int main(int argc, char **argv)
{
    TheApp = new DuneApp();
    Scene* scene = new Scene();
    NodeBox* nodeBox = (NodeBox*)scene->createNode("Box"); 
    nodeBox->toTriangleSet();
    return 0;
}
#endif
