/* * 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 #ifndef FLT_MAX # include #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 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 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 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); }