Logo Search packages:      
Sourcecode: whitedune version File versions  Download package

Mesh.cpp

/*
 * 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
#include "stdafx.h"

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

//make the drawing of normals to a interactive feature in a later version...
//#define DRAW_NORMALS 1

Mesh::Mesh(MFVec3f *vertices, MFInt32 *coordIndex,
         MFVec3f *normals, MFInt32 *normalIndex,
         MFColor *colors, MFInt32 *colorIndex,
         MFVec2f *texCoords, MFInt32 *texCoordIndex,
         float creaseAngle, int meshFlags)
{
    _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();

    _texCoords = texCoords;   

    _coordIndex = coordIndex; 
    if (_coordIndex)
        _coordIndex->ref();

    if (!_texCoords)
        _texCoords = ::generateTextureCoordinates(_vertices, coordIndex);
    if (_texCoords)
        _texCoords->ref();
    
    _normalIndex = normalIndex ? normalIndex : coordIndex;
    if (_normalIndex)
        _normalIndex->ref();

    _colorIndex = colorIndex ? colorIndex : coordIndex;
    if (_colorIndex)
        _colorIndex->ref();

    _texCoordIndex = texCoordIndex ? texCoordIndex : coordIndex;
    if (_texCoordIndex)
        _texCoordIndex->ref();

    _creaseAngle = creaseAngle;
    _ccw = meshFlags & MESH_CCW;
    _solid = meshFlags & MESH_SOLID;
    _convex = meshFlags & MESH_CONVEX ;
    _normalPerVertex = meshFlags & MESH_NORMAL_PER_VERTEX;
    _faces = NULL;
    _numFaces = 0;
    buildFaces();
    generateFaceNormals();
    if (!_normals) {
      smoothNormals();
    }
}

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

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

    for (int i = 0; i < _numFaces; i++) {
      delete _faces[i];
    }
    delete [] _faces;
}

void
Mesh::buildFaces()
{
    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) {
          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) {
          _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);
    }
}

void
Mesh::draw()
{
    if (_vertices == NULL)
        return;
    if (_vertices->getSize() == 0)
        return;       
    if (_coordIndex == NULL)
        return;
    if (_coordIndex->getSize() == 0)
        return;
   
    const int  *texCoordIndex = _texCoordIndex ? _texCoordIndex->getValues() 
                                                 : NULL;
    const int  *colorIndex = _colorIndex ? _colorIndex->getValues() : NULL;
    const int  *normalIndex = _normalIndex ? _normalIndex->getValues() : NULL;
    const int  *coordIndex = _coordIndex->getValues();
    const float *normals = _normals ? _normals->getValues() : NULL;
    const float *vertices = _vertices->getValues();
    const float *texCoords = _texCoords ? _texCoords->getValues() : NULL;
    const float *colors = _colors ? _colors->getValues() : NULL;
    int           i, j;

    if (!_ccw) glFrontFace(GL_CW);
    if (_solid) {
      glEnable(GL_CULL_FACE);
    } else {
      glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
    }
    if (_colors) {
      glEnable(GL_COLOR_MATERIAL);
      glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
    }
    for (i = 0; i < _numFaces; i++) {
      int         offset = _faces[i]->getOffset();
      int         numVertices = _faces[i]->getNumVertices();
      glBegin(GL_POLYGON);
      for (j = offset; j < offset + numVertices; j++) {
          if (texCoords && texCoordIndex) {
            glTexCoord2fv(texCoords + texCoordIndex[j] * 2);
          }
          if (colors) {
            Util::myGlColor3fv(colors + colorIndex[j] * 3);
          }
            if (normals) {
              glNormal3fv(normals + normalIndex[j] * 3);
            }
          glVertex3fv(vertices + coordIndex[j] * 3);
      }
      glEnd();
    }
#ifdef DRAW_NORMALS
    glDisable(GL_LIGHTING);
    glBegin(GL_LINES);
    for (i = 0; i < _numFaces; i++) {
      int         offset = _faces[i]->getOffset();
      int         numVertices = _faces[i]->getNumVertices();
      for (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);
#endif
    if (_colors) {
      glDisable(GL_COLOR_MATERIAL);
    }
    if (_solid) {
      glDisable(GL_CULL_FACE);
    } else {
      glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
    }
    if (!_ccw) glFrontFace(GL_CCW);
}

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

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))
                continue;
            Vec3f c1(vertices + coordIndex[i++] * 3);

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

            if ((coordIndex[i] >= numVertices) || (coordIndex[i] < 0))
                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()
{
    int                       i;
    EdgeList::Iterator         *j;
    Array<Vec3f>        normals;
    float               cosAngle = (float) cos(_creaseAngle);
    int                       numFaces = 0;
    EdgeList                   *edgeLists = new EdgeList[_vertices->getSize()];
    const int                  *coordIndex = _coordIndex->getValues();
    int                       nCoords = _coordIndex->getSize();
    int                       nVerts = _vertices->getSize() / 3;
    int                       start = 0;
    Array<int>                  normalIndex;

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

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

    for (i = 0; i < nCoords; i++) {
      int v = coordIndex[i];
      if (v == -1) {
          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;
    }

    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();
            if (refNormal.dot(otherNormal) > 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;
                  normalIndex[e->pos1] = i2;
                } else if (i1 != -1 && i2 == -1) {
                  // use v1's normal
                  normals[i1] += otherNormal;
                  normalIndex[f->pos2] = i1;
                } else {
                  // they're both specified, so combine them in place
                  normals[i1] += normals[i2];
                  normals[i2] = normals[i1];
                }
            }
          }
      }
    }
    // cleanup:  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;
                }
          }
          delete e;
      }
        edgeLists[i].removeAll();
    }
    delete [] edgeLists;
    for (int k = 0; k < normals.size(); k++) {
      normals[k].normalize();
    }
    delete _normals;
    _normals = new MFVec3f((float *) normals.extractData(), normals.size() * 3);
    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
compareFace(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()
{
    if (TheApp->GetRenderFasterWorse())
        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) {
        Vec2f* tcoords = new Vec2f[npoints];
        for (i = 0; i < npoints; i++) {
            tcoords[i].x = 0;
            tcoords[i].y = 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].x = (XYZ[Sindex] - minVals[Sindex])/Ssize;
                    tcoords[ind].y = (XYZ[Tindex] - minVals[Tindex])/Ssize;
                }
            }
        }
        texcoords = new MFVec2f((float *) tcoords, 2 * npoints);
    }
    return texcoords;
}

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


Generated by  Doxygen 1.6.0   Back to index