/* * * Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved. * * This library 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.1 of the License, or (at your option) any later version. * * This library 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. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like. Any license provided herein, whether implied or * otherwise, applies only to this software file. Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA 94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ * */ // // Some convenient routines for doing stuff with faces. // #include #include #include "Faces.h" #include "Edges.h" #include #include // // Find a face's normal, assuming its vertices are in // counter-clockwise order. // void Face::findNormal(const SbVec3f *verts) { // Use Newell's method to find face normal. See Newman & Sproull, // pg. 499. This is better than the three-point cross-product // method. normal[0] = normal[1] = normal[2] = 0.0; for (int i = 0; i < nv; i++) { int iv1 = int(v[i]); int iv2 = int(v[(i+1)%nv]); if (iv1 == iv2) continue; SbVec3f v1 = verts[iv1]; SbVec3f v2 = verts[iv2]; normal[0] += (v1[1] - v2[1])*(v1[2] + v2[2]); normal[1] += (v1[2] - v2[2])*(v1[0] + v2[0]); normal[2] += (v1[0] - v2[0])*(v1[1] + v2[1]); } if (orientation == Face::CW) normal.negate(); if (normal.length() < 0.00001) { degenerate = 1; } else { normal.normalize(); degenerate = 0; } } // // Set this face's orientation relative to a given edge // void Face::orientFace(int v1, int v2) { int v1_i; // First, figure out which index is v1 for (v1_i = 0; v1_i < nv; v1_i++) { if (v[v1_i] == v1) { // Now just have to determine whether v2 is the next or previous // vertex. if (v[(v1_i+1)%nv] == v2) // Next { orientation = CCW; break; } else if (v[(v1_i+nv-1)%nv] == v2) // Previous { orientation = CW; break; } // Otherwise, keep on checking; we may be traversing a // degenerate edge or strange facet where vertices are repeated. } } assert(v1_i != nv); // Assertion: we found it. } FaceList::FaceList() { verts = NULL; faceSet = NULL; ed = NULL; vd = NULL; convex = TRUE; solid = TRUE; verbose = FALSE; } FaceList::FaceList(const SbVec3f *v, EdgeDict *e) { verts = v; ed = e; vd = NULL; faceSet = NULL; convex = TRUE; solid = TRUE; verbose = FALSE; } FaceList::FaceList(const SbVec3f *v, SoIndexedFaceSet *fs, SbBool vrb) { verts = v; faceSet = fs; fs->ref(); convex = TRUE; solid = TRUE; vd = NULL; verbose = vrb; ed = new EdgeDict(1000); SoMFInt32 *coord_indices = &faceSet->coordIndex; int ni = coord_indices->getNum(); int32_t *indices = coord_indices->startEditing(); int start_o_face = 0; // Now fill in face and edge structures int n_degenerate_faces = 0; int n_degenerate_edges = 0; for (int i = 0, ct=0; i < coord_indices->getNum(); i++) { if (indices[i] == SO_END_FACE_INDEX || i == coord_indices->getNum()-1) { Face *f = new Face; if (indices[i] == SO_END_FACE_INDEX) f->nv = i - start_o_face; else f->nv = i - start_o_face + 1; f->v = indices+start_o_face; f->vct = ct; // first vertex index count for this face. f->vidx = start_o_face; f->vn = NULL; f->orientation = Face::UNKNOWN; f->findNormal(verts); if (f->degenerate) { ++n_degenerate_faces; f->orientation = Face::CCW; } else for (int j = 0; j < f->nv; j++) { int i1 = (int)f->v[j]; int i2 = (int)f->v[(j+1)%f->nv]; if (i1 != i2) ed->Add(f, i1, i2); else ++n_degenerate_edges; } start_o_face = i+1; append(f); } else ct++; // "ct" counts the non-END entries, for use // as the index into a per_vertex normal set } if (n_degenerate_faces != 0) { if (verbose) fprintf(stderr, "Detected %d degenerate faces\n", n_degenerate_faces); } if (n_degenerate_edges != 0) { if (verbose) fprintf(stderr, "Detected %d degenerate edges\n", n_degenerate_edges); } } void FaceList::append(Face *f) { SbPList::append((void *)f); } // // This isn't accurate; we just use it to figure out if the volume is // positive or negative to try to figure out if the surface is // oriented correctly. // float FaceList::volume() { int i, j; int total_v = 0; SbVec3f average(0.0, 0.0, 0.0); for (j = 0; j < getLength(); j++) { Face *f = (*this)[j]; if (f->degenerate) continue; for (i = 0; i < f->nv; i++) { average += verts[f->v[i]]; ++total_v; } } average /= (float) total_v; float result = 0.0; for (j = 0; j < getLength(); j++) { Face *f = (*this)[j]; if (f->degenerate) continue; for (i = 1; i < f->nv-1; i++) { SbVec3f v1 = verts[f->v[0]] - average; SbVec3f v2 = verts[f->v[i]] - average; SbVec3f v3 = verts[f->v[i+1]] - average; float t = (v1.cross(v2)).dot(v3); if (f->orientation == Face::CCW) { result += t; } else if (f->orientation == Face::CW) { result -= t; } else { assert(0); } } } return result; } FaceList::~FaceList() { if (faceSet != NULL) { delete ed; faceSet->coordIndex.finishEditing(); faceSet->unref(); for (int i = 0; i < getLength(); i++) { delete (*this)[i]; } truncate(0); } if (vd) delete [] vd; } void FaceList::reverseOrientation() { for (int i = 0; i < getLength(); i++) { Face *f = (*this)[i]; if (f->orientation == Face::CW) { f->orientation = Face::CCW; } else if (f->orientation == Face::CCW) { f->orientation = Face::CW; } else { assert(0); } } } // // This routine works by starting with a 'seed' face and assumes that // its orientation is correct. It then visits all neighboring faces // and makes their orientations consistent with the seed face's. // void FaceList::findOrientation() { orientOutward(); correctOrientation(); } void FaceList::findFacetNormals(SoNormal *n) { assert(n != NULL && faceSet != NULL); for (int i = 0; i < getLength(); i++) { (*this)[i]->findNormal(verts); n->vector.set1Value(i, (*this)[i]->normal); } } // // Assuming that the correct orientation of a 'seed' face has been // discovered, this routine figures out the correct orientation for // all faces connected to that face. // void FaceList::recursivelyOrient(Face *seed) { int i, j; FaceList others; if (seed->degenerate) return; for (i = 0; i < seed->nv; i++) { j = (i+1)%seed->nv; // Find other faces attached to this edge ed->OtherFaces(seed, seed->v[i], seed->v[j], others); for (int f = 0; f < others.getLength(); f++) { if (others[f]->orientation == Face::UNKNOWN) { if (seed->orientation == Face::CW) others[f]->orientFace((int)seed->v[i], (int)seed->v[j]); else if (seed->orientation == Face::CCW) others[f]->orientFace((int)seed->v[j], (int)seed->v[i]); else assert(0); // Should never happen append(others[f]); recursivelyOrient(others[f]); } } } } // // This routine takes a collection of faces and trys to figure out // which way is out. // void FaceList::orientOutward() { // int num_fragments = 0; // // Loop through all the faces; if we find one whose orientation // hasn't been determined, will try to determine its orientation. // for (int i = 0; i < getLength(); i++) { Face *f = (*this)[i]; if (f->orientation != Face::UNKNOWN) continue; // First, take a wild guess... f->orientation = Face::CCW; // ++num_fragments; FaceList fragment(verts, ed); fragment.append(f); // Now recursively orient the faces connected to this face. fragment.recursivelyOrient(f); // Take a reasonable guess for whether or not that first face // is oriented correctly: float v = fragment.volume(); if (v*v < 0.00001*0.00001) // FLAT { // Do something... // fprintf(stderr, "Flat fragment found\n"); } else if (v < 0.0) fragment.reverseOrientation(); } // fprintf(stderr, "There were %d fragments\n", num_fragments); } void FaceList::correctOrientation() { for (int i = 0; i < getLength(); i++) { Face *f = (*this)[i]; if (f->orientation == Face::CW) { for (int j = 0; j < f->nv/2; j++) { int k = f->nv - j - 1; int32_t t = f->v[j]; f->v[j] = f->v[k]; f->v[k] = t; } f->orientation = Face::CCW; } } } void FaceList::findShapeInfo() { FaceList others; convex = TRUE; solid = TRUE; for (int k=0; k< getLength(); k++) { Face *f = (*this)[k]; if (f->degenerate) continue; // If a face has more than 3 vertices, assume it's convex. if (f->nv > 3) { convex = FALSE; } // run through the edges of the face for (int i = 0; i < f->nv; i++) { int j = (i+1)%f->nv; // Find other faces attached to this edge ed->OtherFaces(f, f->v[i], f->v[j], others); if (others.getLength() == 0) { solid = FALSE; break; } } // we can quit searching if we've found // out all there is to know. if (!convex && !solid) break; } } int FaceList::findBodies() { buildVertexDict(); // clear all faces int i; for (i=0; i< getLength(); i++) (*this)[i]->body = -1; int bodyN = 0; for (i=0; ibody == -1) { f->body = bodyN++; recursivelyMarkBody(f); } } return bodyN; } void FaceList::recursivelyMarkBody(Face *f) { int i; assert(f->body != -1); // only recurse on marked faces for (i = 0; i < f->nv; i++) { // get list of faces sharing this vertex const FaceList &faces = vd[f->v[i]]; for (int j=0; jbody == f->body) { ; // nothing to do if the other face is already part of this body } else if (faces[j]->body == -1) { // the other face is still unmarked, so mark it and recurse faces[j]->body = f->body; recursivelyMarkBody(faces[j]); } else { assert(0); // vertex dictionary isn't commutative?? } } } } void FaceList::extractBody(int b, int &bnf, int32_t *fNewFromOld, int32_t *fOldFromNew, int &bnv, int32_t *vNewFromOld, int32_t *vOldFromNew) { // build the face maps int f, bf; for (f=0, bf=0; f < getLength(); f++) { if ((*this)[f]->body == b) { // f is the old face index // bf is the new face index fNewFromOld[f] = bf; fOldFromNew[bf] = f; bf++; } } bnf = bf; // build the vertex maps int ni, i; for (ni=0, i=0; i < vdSize; i++) { const FaceList &faces = vd[i]; SbBool isInBody = FALSE; for (int j = 0; j < faces.getLength(); j++) if (faces[j]->body == b) { isInBody = TRUE; break; } if (isInBody) { // i is the old vertex index // ni is the new vertex index vNewFromOld[i] = ni; vOldFromNew[ni] = i; ni++; } else vNewFromOld[i] = -1; } bnv = ni; } void FaceList::buildVertexDict() { // already built? if (vd) return; int biggest_index = 0; int i; for (i = 0; i < getLength(); i++) { Face *f = (*this)[i]; for (int j = 0; j < f->nv; j++) { if (f->v[j] > biggest_index) biggest_index = f->v[j]; } } vd = new FaceList[biggest_index+1]; vdSize = biggest_index+1; // Build list of faces around each vertex for (i = 0; i < getLength(); i++) { Face *f = (*this)[i]; for (int j = 0; j < f->nv; j++) { vd[f->v[j]].append(f); } } } void FaceList::findVertexNormals(SoNormal *norm, SoIndexedFaceSet *ifs, float creaseAngle) { buildVertexDict(); // Initialize all faces int i; for (i = 0; i < getLength(); i++) { Face *f = (*this)[i]; f->findNormal(verts); f->vn = new int32_t[f->nv]; for (int j = 0; j < f->nv; j++) { f->vn[j] = -1; } } // Finally, create normals norm->vector.deleteValues(0); // get rid of default value int count = 0; for (i = 0; i < getLength(); i++) { Face *f = (*this)[i]; for (int j = 0; j < f->nv; j++) { if (f->degenerate) { f->vn[j] = getIdx(norm->vector, SbVec3f(0,0,0)); } else if (f->vn[j] == -1) { SbVec3f t; vd[f->v[j]].averageNormals(norm->vector, f->normal, creaseAngle, f->v[j]); } ifs->normalIndex.set1Value(count, f->vn[j]); ++count; } ifs->normalIndex.set1Value(count, SO_END_FACE_INDEX); ++count; } } // // get the index of a vector in a SFVec3f; // adds the vector if needed. // // use: private // int FaceList::getIdx(SoMFVec3f &mf, const SbVec3f &p) { int n = mf.getNum(); SbVec3f *v = mf.startEditing(); // search from the end, since recent vectors are likely to be reused for (int i=n-1; i>=0; i--) if (p == v[i]) { mf.finishEditing(); return i; } mf.finishEditing(); mf.set1Value(n, p); return n; } // // Average the normals around a vertex to get a vertex normal. Skip // faces that are too different (as defined by creaseAngle) // void FaceList::averageNormals(SoMFVec3f &norms, SbVec3f &reference, float creaseAngle, int whichV) { SbVec3f average; average.setValue(0.0, 0.0, 0.0); float ca = cos(creaseAngle); int num = 0; int max = getLength(); // first, loop through and compute the average int i; for (i = 0; i < max; i++) { Face *f = (*this)[i]; if (f->degenerate) continue; float dp = reference.dot(f->normal); if (dp >= ca) { average += f->normal; ++num; } } assert(num != 0); average /= (float)num; average.normalize(); // insert the average into the mfield int index = getIdx(norms, average); // loop through again and insert the average's index // into the face arrays for (i = 0; i < max; i++) { Face *f = (*this)[i]; if (f->degenerate) continue; float dp = reference.dot(f->normal); if (dp >= ca) { for (int j = 0; j < f->nv; j++) { if (f->v[j] == whichV) f->vn[j] = index; } } } assert(num != 0); average /= (float)num; average.normalize(); }