/* * ***** BEGIN GPL LICENSE BLOCK ***** * * 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; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/freestyle/intern/view_map/ViewMapBuilder.cpp * \ingroup freestyle * \brief Class to build silhouette edges from a Winged-Edge structure * \author Stephane Grabli * \date 25/03/2002 */ #include #include #include #include #include "FRS_freestyle.h" #include "BoxGrid.h" #include "CulledOccluderSource.h" #include "HeuristicGridDensityProviderFactory.h" #include "OccluderSource.h" #include "SphericalGrid.h" #include "ViewMapBuilder.h" #include "../geometry/GridHelpers.h" #include "../geometry/GeomUtils.h" #include "../winged_edge/WFillGrid.h" #include "BKE_global.h" namespace Freestyle { // XXX Grmll... G is used as template's typename parameter :/ static const Global &_global = G; #define LOGGING 0 using namespace std; template static void findOccludee(FEdge *fe, G& grid, I& occluders, real epsilon, WFace **oaWFace, Vec3r& u, Vec3r& A, Vec3r& origin, Vec3r& edge, vector& faceVertices) { WFace *face = NULL; if (fe->isSmooth()) { FEdgeSmooth *fes = dynamic_cast(fe); face = (WFace *)fes->face(); } WFace *oface; bool skipFace; WVertex::incoming_edge_iterator ie; *oaWFace = NULL; if (((fe)->getNature() & Nature::SILHOUETTE) || ((fe)->getNature() & Nature::BORDER)) { // we cast a ray from A in the same direction but looking behind Vec3r v(-u[0], -u[1], -u[2]); bool noIntersection = true; real mint = FLT_MAX; for (occluders.initAfterTarget(); occluders.validAfterTarget(); occluders.nextOccludee()) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tEvaluating intersection for occludee " << occluders.getWFace() << " and ray " << A << " * " << u << endl; } #endif oface = occluders.getWFace(); Polygon3r *p = occluders.getCameraSpacePolygon(); real d = -((p->getVertices())[0] * p->getNormal()); real t, t_u, t_v; if (0 != face) { skipFace = false; if (face == oface) continue; if (faceVertices.empty()) continue; for (vector::iterator fv = faceVertices.begin(), fvend = faceVertices.end(); fv != fvend; ++fv) { if ((*fv)->isBoundary()) continue; WVertex::incoming_edge_iterator iebegin = (*fv)->incoming_edges_begin(); WVertex::incoming_edge_iterator ieend = (*fv)->incoming_edges_end(); for (ie = iebegin; ie != ieend; ++ie) { if ((*ie) == 0) continue; WFace *sface = (*ie)->GetbFace(); if (sface == oface) { skipFace = true; break; } } if (skipFace) break; } if (skipFace) continue; } else { // check whether the edge and the polygon plane are coincident: //------------------------------------------------------------- //first let us compute the plane equation. if (GeomUtils::COINCIDENT == GeomUtils::intersectRayPlane(origin, edge, p->getNormal(), d, t, epsilon)) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tRejecting occluder for target coincidence." << endl; } #endif continue; } } if (p->rayIntersect(A, v, t, t_u, t_v)) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tRay " << A << " * " << v << " intersects at time " << t << endl; cout << "\t\t(v * normal) == " << (v * p->getNormal()) << " for normal " << p->getNormal() << endl; } #endif if (fabs(v * p->getNormal()) > 0.0001) { if ((t > 0.0)) { // && (t<1.0)) if (t < mint) { *oaWFace = oface; mint = t; noIntersection = false; fe->setOccludeeIntersection(Vec3r(A + t * v)); #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tIs occludee" << endl; } #endif } } } occluders.reportDepth(A, v, t); } } if (noIntersection) *oaWFace = NULL; } } template static void findOccludee(FEdge *fe, G& grid, real epsilon, ViewEdge *ve, WFace **oaFace) { Vec3r A; Vec3r edge; Vec3r origin; A = Vec3r(((fe)->vertexA()->point3D() + (fe)->vertexB()->point3D()) / 2.0); edge = Vec3r((fe)->vertexB()->point3D() - (fe)->vertexA()->point3D()); origin = Vec3r((fe)->vertexA()->point3D()); Vec3r u; if (grid.orthographicProjection()) { u = Vec3r(0.0, 0.0, grid.viewpoint().z() - A.z()); } else { u = Vec3r(grid.viewpoint() - A); } u.normalize(); vector faceVertices; WFace *face = NULL; if (fe->isSmooth()) { FEdgeSmooth *fes = dynamic_cast(fe); face = (WFace *)fes->face(); } if (face) { face->RetrieveVertexList(faceVertices); } I occluders(grid, A, epsilon); findOccludee(fe, grid, occluders, epsilon, oaFace, u, A, origin, edge, faceVertices); } // computeVisibility takes a pointer to foundOccluders, instead of using a reference, // so that computeVeryFastVisibility can skip the AddOccluders step with minimal overhead. template static int computeVisibility(ViewMap *viewMap, FEdge *fe, G& grid, real epsilon, ViewEdge *ve, WFace **oaWFace, set *foundOccluders) { int qi = 0; Vec3r center; Vec3r edge; Vec3r origin; center = fe->center3d(); edge = Vec3r(fe->vertexB()->point3D() - fe->vertexA()->point3D()); origin = Vec3r(fe->vertexA()->point3D()); Vec3r vp; if (grid.orthographicProjection()) { vp = Vec3r(center.x(), center.y(), grid.viewpoint().z()); } else { vp = Vec3r(grid.viewpoint()); } Vec3r u(vp - center); real raylength = u.norm(); u.normalize(); WFace *face = NULL; if (fe->isSmooth()) { FEdgeSmooth *fes = dynamic_cast(fe); face = (WFace *)fes->face(); } vector faceVertices; WVertex::incoming_edge_iterator ie; WFace *oface; bool skipFace; if (face) face->RetrieveVertexList(faceVertices); I occluders(grid, center, epsilon); for (occluders.initBeforeTarget(); occluders.validBeforeTarget(); occluders.nextOccluder()) { // If we're dealing with an exact silhouette, check whether we must take care of this occluder of not. // (Indeed, we don't consider the occluders that share at least one vertex with the face containing this edge). //----------- oface = occluders.getWFace(); Polygon3r *p = occluders.getCameraSpacePolygon(); real t, t_u, t_v; #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tEvaluating intersection for occluder " << (p->getVertices())[0] << (p->getVertices())[1] << (p->getVertices())[2] << endl << "\t\t\tand ray " << vp << " * " << u << " (center " << center << ")" << endl; } #endif #if LOGGING Vec3r v(vp - center); real rl = v.norm(); v.normalize(); vector points; // Iterate over vertices, storing projections in points for (vector::const_iterator woe = oface->getEdgeList().begin(), woend = oface->getEdgeList().end(); woe != woend; woe++) { points.push_back(Vec3r((*woe)->GetaVertex()->GetVertex())); } Polygon3r p1(points, oface->GetNormal()); Vec3r v1((p1.getVertices())[0]); real d = -(v1 * p->getNormal()); if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tp: " << (p->getVertices())[0] << (p->getVertices())[1] << (p->getVertices())[2] << ", norm: " << p->getNormal() << endl; cout << "\t\tp1: " << (p1.getVertices())[0] << (p1.getVertices())[1] << (p1.getVertices())[2] << ", norm: " << p1.getNormal() << endl; } #else real d = -((p->getVertices())[0] * p->getNormal()); #endif if (face) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tDetermining face adjacency..."; } #endif skipFace = false; if (face == oface) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << " Rejecting occluder for face concurrency." << endl; } #endif continue; } for (vector::iterator fv = faceVertices.begin(), fvend = faceVertices.end(); fv != fvend; ++fv) { if ((*fv)->isBoundary()) continue; WVertex::incoming_edge_iterator iebegin = (*fv)->incoming_edges_begin(); WVertex::incoming_edge_iterator ieend = (*fv)->incoming_edges_end(); for (ie = iebegin; ie != ieend; ++ie) { if ((*ie) == 0) continue; WFace *sface = (*ie)->GetbFace(); //WFace *sfacea = (*ie)->GetaFace(); //if ((sface == oface) || (sfacea == oface)) if (sface == oface) { skipFace = true; break; } } if (skipFace) break; } if (skipFace) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << " Rejecting occluder for face adjacency." << endl; } #endif continue; } } else { // check whether the edge and the polygon plane are coincident: //------------------------------------------------------------- //first let us compute the plane equation. if (GeomUtils::COINCIDENT == GeomUtils::intersectRayPlane(origin, edge, p->getNormal(), d, t, epsilon)) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tRejecting occluder for target coincidence." << endl; } #endif continue; } } #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { real x; if (p1.rayIntersect(center, v, x, t_u, t_v)) { cout << "\t\tRay should intersect at time " << (rl - x) << ". Center: " << center << ", V: " << v << ", RL: " << rl << ", T:" << x << endl; } else { cout << "\t\tRay should not intersect. Center: " << center << ", V: " << v << ", RL: " << rl << endl; } } #endif if (p->rayIntersect(center, u, t, t_u, t_v)) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tRay " << center << " * " << u << " intersects at time " << t << " (raylength is " << raylength << ")" << endl; cout << "\t\t(u * normal) == " << (u * p->getNormal()) << " for normal " << p->getNormal() << endl; } #endif if (fabs(u * p->getNormal()) > 0.0001) { if ((t > 0.0) && (t < raylength)) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tIs occluder" << endl; } #endif if ( foundOccluders != NULL ) { ViewShape *vshape = viewMap->viewShape(oface->GetVertex(0)->shape()->GetId()); foundOccluders->insert(vshape); } ++qi; if (! grid.enableQI()) break; } occluders.reportDepth(center, u, t); } } } // Find occludee findOccludee(fe, grid, occluders, epsilon, oaWFace, u, center, origin, edge, faceVertices); return qi; } // computeCumulativeVisibility returns the lowest x such that the majority of FEdges have QI <= x // // This was probably the original intention of the "normal" algorithm on which computeDetailedVisibility is based. // But because the "normal" algorithm chooses the most popular QI, without considering any other values, a ViewEdge // with FEdges having QIs of 0, 21, 22, 23, 24 and 25 will end up having a total QI of 0, even though most of the // FEdges are heavily occluded. computeCumulativeVisibility will treat this case as a QI of 22 because 3 out of // 6 occluders have QI <= 22. template static void computeCumulativeVisibility(ViewMap *ioViewMap, G& grid, real epsilon, RenderMonitor *iRenderMonitor) { vector& vedges = ioViewMap->ViewEdges(); FEdge *fe, *festart; int nSamples = 0; vector wFaces; WFace *wFace = NULL; unsigned cnt = 0; unsigned cntStep = (unsigned)ceil(0.01f * vedges.size()); unsigned tmpQI = 0; unsigned qiClasses[256]; unsigned maxIndex, maxCard; unsigned qiMajority; for (vector::iterator ve = vedges.begin(), veend = vedges.end(); ve != veend; ve++) { if (iRenderMonitor) { if (iRenderMonitor->testBreak()) break; if (cnt % cntStep == 0) { stringstream ss; ss << "Freestyle: Visibility computations " << (100 * cnt / vedges.size()) << "%"; iRenderMonitor->setInfo(ss.str()); iRenderMonitor->progress((float)cnt / vedges.size()); } cnt++; } #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "Processing ViewEdge " << (*ve)->getId() << endl; } #endif // Find an edge to test if (!(*ve)->isInImage()) { // This view edge has been proscenium culled (*ve)->setQI(255); (*ve)->setaShape(0); #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tCulled." << endl; } #endif continue; } // Test edge festart = (*ve)->fedgeA(); fe = (*ve)->fedgeA(); qiMajority = 0; do { if (fe != NULL && fe->isInImage()) { qiMajority++; } fe = fe->nextEdge(); } while (fe && fe != festart); if (qiMajority == 0) { // There are no occludable FEdges on this ViewEdge // This should be impossible. if (_global.debug & G_DEBUG_FREESTYLE) { cout << "View Edge in viewport without occludable FEdges: " << (*ve)->getId() << endl; } // We can recover from this error: // Treat this edge as fully visible with no occludee (*ve)->setQI(0); (*ve)->setaShape(0); continue; } else { ++qiMajority; qiMajority >>= 1; } #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tqiMajority: " << qiMajority << endl; } #endif tmpQI = 0; maxIndex = 0; maxCard = 0; nSamples = 0; memset(qiClasses, 0, 256 * sizeof(*qiClasses)); set foundOccluders; fe = (*ve)->fedgeA(); do { if (!fe || !fe->isInImage()) { fe = fe->nextEdge(); continue; } if ((maxCard < qiMajority)) { //ARB: change &wFace to wFace and use reference in called function tmpQI = computeVisibility(ioViewMap, fe, grid, epsilon, *ve, &wFace, &foundOccluders); #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tFEdge: visibility " << tmpQI << endl; } #endif //ARB: This is an error condition, not an alert condition. // Some sort of recovery or abort is necessary. if (tmpQI >= 256) { cerr << "Warning: too many occluding levels" << endl; //ARB: Wild guess: instead of aborting or corrupting memory, treat as tmpQI == 255 tmpQI = 255; } if (++qiClasses[tmpQI] > maxCard) { maxCard = qiClasses[tmpQI]; maxIndex = tmpQI; } } else { //ARB: FindOccludee is redundant if ComputeRayCastingVisibility has been called //ARB: change &wFace to wFace and use reference in called function findOccludee(fe, grid, epsilon, *ve, &wFace); #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tFEdge: occludee only (" << (wFace != NULL ? "found" : "not found") << ")" << endl; } #endif } // Store test results if (wFace) { vector vertices; for (int i = 0, numEdges = wFace->numberOfEdges(); i < numEdges; ++i) { vertices.push_back(Vec3r(wFace->GetVertex(i)->GetVertex())); } Polygon3r poly(vertices, wFace->GetNormal()); poly.userdata = (void *)wFace; fe->setaFace(poly); wFaces.push_back(wFace); fe->setOccludeeEmpty(false); #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tFound occludee" << endl; } #endif } else { fe->setOccludeeEmpty(true); } ++nSamples; fe = fe->nextEdge(); } while ((maxCard < qiMajority) && (fe) && (fe != festart)); #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tFinished with " << nSamples << " samples, maxCard = " << maxCard << endl; } #endif // ViewEdge // qi -- // Find the minimum value that is >= the majority of the QI for (unsigned count = 0, i = 0; i < 256; ++i) { count += qiClasses[i]; if (count >= qiMajority) { (*ve)->setQI(i); break; } } // occluders -- // I would rather not have to go through the effort of creating this set and then copying out its contents. // Is there a reason why ViewEdge::_Occluders cannot be converted to a set<>? for (set::iterator o = foundOccluders.begin(), oend = foundOccluders.end(); o != oend; ++o) { (*ve)->AddOccluder((*o)); } #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tConclusion: QI = " << maxIndex << ", " << (*ve)->occluders_size() << " occluders." << endl; } #else (void)maxIndex; #endif // occludee -- if (!wFaces.empty()) { if (wFaces.size() <= (float)nSamples / 2.0f) { (*ve)->setaShape(0); } else { ViewShape *vshape = ioViewMap->viewShape((*wFaces.begin())->GetVertex(0)->shape()->GetId()); (*ve)->setaShape(vshape); } } wFaces.clear(); } if (iRenderMonitor && vedges.size()) { stringstream ss; ss << "Freestyle: Visibility computations " << (100 * cnt / vedges.size()) << "%"; iRenderMonitor->setInfo(ss.str()); iRenderMonitor->progress((float)cnt / vedges.size()); } } template static void computeDetailedVisibility(ViewMap *ioViewMap, G& grid, real epsilon, RenderMonitor *iRenderMonitor) { vector& vedges = ioViewMap->ViewEdges(); FEdge *fe, *festart; int nSamples = 0; vector wFaces; WFace *wFace = NULL; unsigned tmpQI = 0; unsigned qiClasses[256]; unsigned maxIndex, maxCard; unsigned qiMajority; for (vector::iterator ve = vedges.begin(), veend = vedges.end(); ve != veend; ve++) { if (iRenderMonitor && iRenderMonitor->testBreak()) break; #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "Processing ViewEdge " << (*ve)->getId() << endl; } #endif // Find an edge to test if (!(*ve)->isInImage()) { // This view edge has been proscenium culled (*ve)->setQI(255); (*ve)->setaShape(0); #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tCulled." << endl; } #endif continue; } // Test edge festart = (*ve)->fedgeA(); fe = (*ve)->fedgeA(); qiMajority = 0; do { if (fe != NULL && fe->isInImage()) { qiMajority++; } fe = fe->nextEdge(); } while (fe && fe != festart); if (qiMajority == 0) { // There are no occludable FEdges on this ViewEdge // This should be impossible. if (_global.debug & G_DEBUG_FREESTYLE) { cout << "View Edge in viewport without occludable FEdges: " << (*ve)->getId() << endl; } // We can recover from this error: // Treat this edge as fully visible with no occludee (*ve)->setQI(0); (*ve)->setaShape(0); continue; } else { ++qiMajority; qiMajority >>= 1; } #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tqiMajority: " << qiMajority << endl; } #endif tmpQI = 0; maxIndex = 0; maxCard = 0; nSamples = 0; memset(qiClasses, 0, 256 * sizeof(*qiClasses)); set foundOccluders; fe = (*ve)->fedgeA(); do { if (fe == NULL || ! fe->isInImage()) { fe = fe->nextEdge(); continue; } if ((maxCard < qiMajority)) { //ARB: change &wFace to wFace and use reference in called function tmpQI = computeVisibility(ioViewMap, fe, grid, epsilon, *ve, &wFace, &foundOccluders); #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tFEdge: visibility " << tmpQI << endl; } #endif //ARB: This is an error condition, not an alert condition. // Some sort of recovery or abort is necessary. if (tmpQI >= 256) { cerr << "Warning: too many occluding levels" << endl; //ARB: Wild guess: instead of aborting or corrupting memory, treat as tmpQI == 255 tmpQI = 255; } if (++qiClasses[tmpQI] > maxCard) { maxCard = qiClasses[tmpQI]; maxIndex = tmpQI; } } else { //ARB: FindOccludee is redundant if ComputeRayCastingVisibility has been called //ARB: change &wFace to wFace and use reference in called function findOccludee(fe, grid, epsilon, *ve, &wFace); #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tFEdge: occludee only (" << (wFace != NULL ? "found" : "not found") << ")" << endl; } #endif } // Store test results if (wFace) { vector vertices; for (int i = 0, numEdges = wFace->numberOfEdges(); i < numEdges; ++i) { vertices.push_back(Vec3r(wFace->GetVertex(i)->GetVertex())); } Polygon3r poly(vertices, wFace->GetNormal()); poly.userdata = (void *)wFace; fe->setaFace(poly); wFaces.push_back(wFace); fe->setOccludeeEmpty(false); #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tFound occludee" << endl; } #endif } else { fe->setOccludeeEmpty(true); } ++nSamples; fe = fe->nextEdge(); } while ((maxCard < qiMajority) && (fe) && (fe != festart)); #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tFinished with " << nSamples << " samples, maxCard = " << maxCard << endl; } #endif // ViewEdge // qi -- (*ve)->setQI(maxIndex); // occluders -- // I would rather not have to go through the effort of creating this this set and then copying out its contents. // Is there a reason why ViewEdge::_Occluders cannot be converted to a set<>? for (set::iterator o = foundOccluders.begin(), oend = foundOccluders.end(); o != oend; ++o) { (*ve)->AddOccluder((*o)); } #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tConclusion: QI = " << maxIndex << ", " << (*ve)->occluders_size() << " occluders." << endl; } #endif // occludee -- if (!wFaces.empty()) { if (wFaces.size() <= (float)nSamples / 2.0f) { (*ve)->setaShape(0); } else { ViewShape *vshape = ioViewMap->viewShape((*wFaces.begin())->GetVertex(0)->shape()->GetId()); (*ve)->setaShape(vshape); } } wFaces.clear(); } } template static void computeFastVisibility(ViewMap *ioViewMap, G& grid, real epsilon) { vector& vedges = ioViewMap->ViewEdges(); FEdge *fe, *festart; unsigned nSamples = 0; vector wFaces; WFace *wFace = NULL; unsigned tmpQI = 0; unsigned qiClasses[256]; unsigned maxIndex, maxCard; unsigned qiMajority; bool even_test; for (vector::iterator ve = vedges.begin(), veend = vedges.end(); ve != veend; ve++) { // Find an edge to test if (!(*ve)->isInImage()) { // This view edge has been proscenium culled (*ve)->setQI(255); (*ve)->setaShape(0); continue; } // Test edge festart = (*ve)->fedgeA(); fe = (*ve)->fedgeA(); even_test = true; qiMajority = 0; do { if (even_test && fe && fe->isInImage()) { qiMajority++; even_test = !even_test; } fe = fe->nextEdge(); } while (fe && fe != festart); if (qiMajority == 0 ) { // There are no occludable FEdges on this ViewEdge // This should be impossible. if (_global.debug & G_DEBUG_FREESTYLE) { cout << "View Edge in viewport without occludable FEdges: " << (*ve)->getId() << endl; } // We can recover from this error: // Treat this edge as fully visible with no occludee (*ve)->setQI(0); (*ve)->setaShape(0); continue; } else { ++qiMajority; qiMajority >>= 1; } even_test = true; maxIndex = 0; maxCard = 0; nSamples = 0; memset(qiClasses, 0, 256 * sizeof(*qiClasses)); set foundOccluders; fe = (*ve)->fedgeA(); do { if (!fe || !fe->isInImage()) { fe = fe->nextEdge(); continue; } if (even_test) { if ((maxCard < qiMajority)) { //ARB: change &wFace to wFace and use reference in called function tmpQI = computeVisibility(ioViewMap, fe, grid, epsilon, *ve, &wFace, &foundOccluders); //ARB: This is an error condition, not an alert condition. // Some sort of recovery or abort is necessary. if (tmpQI >= 256) { cerr << "Warning: too many occluding levels" << endl; //ARB: Wild guess: instead of aborting or corrupting memory, treat as tmpQI == 255 tmpQI = 255; } if (++qiClasses[tmpQI] > maxCard) { maxCard = qiClasses[tmpQI]; maxIndex = tmpQI; } } else { //ARB: FindOccludee is redundant if ComputeRayCastingVisibility has been called //ARB: change &wFace to wFace and use reference in called function findOccludee(fe, grid, epsilon, *ve, &wFace); } if (wFace) { vector vertices; for (int i = 0, numEdges = wFace->numberOfEdges(); i < numEdges; ++i) { vertices.push_back(Vec3r(wFace->GetVertex(i)->GetVertex())); } Polygon3r poly(vertices, wFace->GetNormal()); poly.userdata = (void *)wFace; fe->setaFace(poly); wFaces.push_back(wFace); } ++nSamples; } even_test = ! even_test; fe = fe->nextEdge(); } while ((maxCard < qiMajority) && (fe) && (fe != festart)); // qi -- (*ve)->setQI(maxIndex); // occluders -- for (set::iterator o = foundOccluders.begin(), oend = foundOccluders.end(); o != oend; ++o) { (*ve)->AddOccluder((*o)); } // occludee -- if (!wFaces.empty()) { if (wFaces.size() < nSamples / 2) { (*ve)->setaShape(0); } else { ViewShape *vshape = ioViewMap->viewShape((*wFaces.begin())->GetVertex(0)->shape()->GetId()); (*ve)->setaShape(vshape); } } wFaces.clear(); } } template static void computeVeryFastVisibility(ViewMap *ioViewMap, G& grid, real epsilon) { vector& vedges = ioViewMap->ViewEdges(); FEdge *fe; unsigned qi = 0; WFace *wFace = 0; for (vector::iterator ve = vedges.begin(), veend = vedges.end(); ve != veend; ve++) { // Find an edge to test if (!(*ve)->isInImage()) { // This view edge has been proscenium culled (*ve)->setQI(255); (*ve)->setaShape(0); continue; } fe = (*ve)->fedgeA(); // Find a FEdge inside the occluder proscenium to test for visibility FEdge *festart = fe; while (fe && !fe->isInImage() && fe != festart) { fe = fe->nextEdge(); } // Test edge if (!fe || !fe->isInImage()) { // There are no occludable FEdges on this ViewEdge // This should be impossible. if (_global.debug & G_DEBUG_FREESTYLE) { cout << "View Edge in viewport without occludable FEdges: " << (*ve)->getId() << endl; } // We can recover from this error: // Treat this edge as fully visible with no occludee qi = 0; wFace = NULL; } else { qi = computeVisibility(ioViewMap, fe, grid, epsilon, *ve, &wFace, NULL); } // Store test results if (wFace) { vector vertices; for (int i = 0, numEdges = wFace->numberOfEdges(); i < numEdges; ++i) { vertices.push_back(Vec3r(wFace->GetVertex(i)->GetVertex())); } Polygon3r poly(vertices, wFace->GetNormal()); poly.userdata = (void *)wFace; fe->setaFace(poly); // This works because setaFace *copies* the polygon ViewShape *vshape = ioViewMap->viewShape(wFace->GetVertex(0)->shape()->GetId()); (*ve)->setaShape(vshape); } else { (*ve)->setaShape(0); } (*ve)->setQI(qi); } } void ViewMapBuilder::BuildGrid(WingedEdge& we, const BBox& bbox, unsigned int sceneNumFaces) { _Grid->clear(); Vec3r size; for (unsigned int i = 0; i < 3; i++) { size[i] = fabs(bbox.getMax()[i] - bbox.getMin()[i]); // let make the grid 1/10 bigger to avoid numerical errors while computing triangles/cells intersections. size[i] += size[i] / 10.0; if (size[i] == 0) { if (_global.debug & G_DEBUG_FREESTYLE) { cout << "Warning: the bbox size is 0 in dimension " << i << endl; } } } _Grid->configure(Vec3r(bbox.getMin() - size / 20.0), size, sceneNumFaces); // Fill in the grid: WFillGrid fillGridRenderer(_Grid, &we); fillGridRenderer.fillGrid(); // DEBUG _Grid->displayDebug(); } ViewMap *ViewMapBuilder::BuildViewMap(WingedEdge& we, visibility_algo iAlgo, real epsilon, const BBox& bbox, unsigned int sceneNumFaces) { _ViewMap = new ViewMap; _currentId = 1; _currentFId = 0; _currentSVertexId = 0; // Builds initial view edges computeInitialViewEdges(we); // Detects cusps computeCusps(_ViewMap); // Compute intersections ComputeIntersections(_ViewMap, sweep_line, epsilon); // Compute visibility ComputeEdgesVisibility(_ViewMap, we, bbox, sceneNumFaces, iAlgo, epsilon); return _ViewMap; } static inline real distance2D(const Vec3r & point, const real origin[2]) { return ::hypot((point[0] - origin[0]), (point[1] - origin[1])); } static inline bool crossesProscenium(real proscenium[4], FEdge *fe) { Vec2r min(proscenium[0], proscenium[2]); Vec2r max(proscenium[1], proscenium[3]); Vec2r A(fe->vertexA()->getProjectedX(), fe->vertexA()->getProjectedY()); Vec2r B(fe->vertexB()->getProjectedX(), fe->vertexB()->getProjectedY()); return GeomUtils::intersect2dSeg2dArea (min, max, A, B); } static inline bool insideProscenium(real proscenium[4], const Vec3r& point) { return !(point[0] < proscenium[0] || point[0] > proscenium[1] || point[1] < proscenium[2] || point[1] > proscenium[3]); } void ViewMapBuilder::CullViewEdges(ViewMap *ioViewMap, real viewProscenium[4], real occluderProscenium[4], bool extensiveFEdgeSearch) { // Cull view edges by marking them as non-displayable. // This avoids the complications of trying to delete edges from the ViewMap. // Non-displayable view edges will be skipped over during visibility calculation. // View edges will be culled according to their position w.r.t. the viewport proscenium (viewport + 5% border, // or some such). // Get proscenium boundary for culling GridHelpers::getDefaultViewProscenium(viewProscenium); real prosceniumOrigin[2]; prosceniumOrigin[0] = (viewProscenium[1] - viewProscenium[0]) / 2.0; prosceniumOrigin[1] = (viewProscenium[3] - viewProscenium[2]) / 2.0; if (_global.debug & G_DEBUG_FREESTYLE) { cout << "Proscenium culling:" << endl; cout << "Proscenium: [" << viewProscenium[0] << ", " << viewProscenium[1] << ", " << viewProscenium[2] << ", " << viewProscenium[3] << "]"<< endl; cout << "Origin: [" << prosceniumOrigin[0] << ", " << prosceniumOrigin[1] << "]"<< endl; } // A separate occluder proscenium will also be maintained, starting out the same as the viewport proscenium, and // expanding as necessary so that it encompasses the center point of at least one feature edge in each retained view // edge. // The occluder proscenium will be used later to cull occluding triangles before they are inserted into the Grid. // The occluder proscenium starts out the same size as the view proscenium GridHelpers::getDefaultViewProscenium(occluderProscenium); // N.B. Freestyle is inconsistent in its use of ViewMap::viewedges_container and vector::iterator. // Probably all occurences of vector::iterator should be replaced ViewMap::viewedges_container // throughout the code. // For each view edge ViewMap::viewedges_container::iterator ve, veend; for (ve = ioViewMap->ViewEdges().begin(), veend = ioViewMap->ViewEdges().end(); ve != veend; ve++) { // Overview: // Search for a visible feature edge // If none: mark view edge as non-displayable // Otherwise: // Find a feature edge with center point inside occluder proscenium. // If none exists, find the feature edge with center point closest to viewport origin. // Expand occluder proscenium to enclose center point. // For each feature edge, while bestOccluderTarget not found and view edge not visibile bool bestOccluderTargetFound = false; FEdge *bestOccluderTarget = NULL; real bestOccluderDistance = 0.0; FEdge *festart = (*ve)->fedgeA(); FEdge *fe = festart; // All ViewEdges start culled (*ve)->setIsInImage(false); // For simple visibility calculation: mark a feature edge that is known to have a center point inside the // occluder proscenium. Cull all other feature edges. do { // All FEdges start culled fe->setIsInImage(false); // Look for the visible edge that can most easily be included in the occluder proscenium. if (!bestOccluderTargetFound) { // If center point is inside occluder proscenium, if (insideProscenium(occluderProscenium, fe->center2d())) { // Use this feature edge for visibility deterimination fe->setIsInImage(true); // Mark bestOccluderTarget as found bestOccluderTargetFound = true; bestOccluderTarget = fe; } else { real d = distance2D(fe->center2d(), prosceniumOrigin); // If center point is closer to viewport origin than current target if (bestOccluderTarget == NULL || d < bestOccluderDistance) { // Then store as bestOccluderTarget bestOccluderDistance = d; bestOccluderTarget = fe; } } } // If feature edge crosses the view proscenium if (!(*ve)->isInImage() && crossesProscenium(viewProscenium, fe)) { // Then the view edge will be included in the image (*ve)->setIsInImage(true); } fe = fe->nextEdge(); } while (fe && fe != festart && !(bestOccluderTargetFound && (*ve)->isInImage())); // Either we have run out of FEdges, or we already have the one edge we need to determine visibility // Cull all remaining edges. while (fe && fe != festart) { fe->setIsInImage(false); fe = fe->nextEdge(); } // If bestOccluderTarget was not found inside the occluder proscenium, we need to expand the occluder // proscenium to include it. if ((*ve)->isInImage() && bestOccluderTarget != NULL && !bestOccluderTargetFound) { // Expand occluder proscenium to enclose bestOccluderTarget Vec3r point = bestOccluderTarget->center2d(); if (point[0] < occluderProscenium[0]) { occluderProscenium[0] = point[0]; } else if (point[0] > occluderProscenium[1]) { occluderProscenium[1] = point[0]; } if (point[1] < occluderProscenium[2]) { occluderProscenium[2] = point[1]; } else if (point[1] > occluderProscenium[3]) { occluderProscenium[3] = point[1]; } // Use bestOccluderTarget for visibility determination bestOccluderTarget->setIsInImage(true); } } // We are done calculating the occluder proscenium. // Expand the occluder proscenium by an epsilon to avoid rounding errors. const real epsilon = 1.0e-6; occluderProscenium[0] -= epsilon; occluderProscenium[1] += epsilon; occluderProscenium[2] -= epsilon; occluderProscenium[3] += epsilon; // For "Normal" or "Fast" style visibility computation only: // For more detailed visibility calculation, make a second pass through the view map, marking all feature edges // with center points inside the final occluder proscenium. All of these feature edges can be considered during // visibility calculation. // So far we have only found one FEdge per ViewEdge. The "Normal" and "Fast" styles of visibility computation // want to consider many FEdges for each ViewEdge. // Here we re-scan the view map to find any usable FEdges that we skipped on the first pass, or that have become // usable because the occluder proscenium has been expanded since the edge was visited on the first pass. if (extensiveFEdgeSearch) { // For each view edge, for (ve = ioViewMap->ViewEdges().begin(), veend = ioViewMap->ViewEdges().end(); ve != veend; ve++) { if (!(*ve)->isInImage()) { continue; } // For each feature edge, FEdge *festart = (*ve)->fedgeA(); FEdge *fe = festart; do { // If not (already) visible and center point inside occluder proscenium, if (!fe->isInImage() && insideProscenium(occluderProscenium, fe->center2d())) { // Use the feature edge for visibility determination fe->setIsInImage(true); } fe = fe->nextEdge(); } while (fe && fe != festart); } } } void ViewMapBuilder::computeInitialViewEdges(WingedEdge& we) { vector wshapes = we.getWShapes(); SShape *psShape; for (vector::const_iterator it = wshapes.begin(); it != wshapes.end(); it++) { if (_pRenderMonitor && _pRenderMonitor->testBreak()) break; // create the embedding psShape = new SShape; psShape->setId((*it)->GetId()); psShape->setName((*it)->getName()); psShape->setFrsMaterials((*it)->frs_materials()); // FIXME // create the view shape ViewShape *vshape = new ViewShape(psShape); // add this view shape to the view map: _ViewMap->AddViewShape(vshape); // we want to number the view edges in a unique way for the while scene. _pViewEdgeBuilder->setCurrentViewId(_currentId); // we want to number the feature edges in a unique way for the while scene. _pViewEdgeBuilder->setCurrentFId(_currentFId); // we want to number the SVertex in a unique way for the while scene. _pViewEdgeBuilder->setCurrentSVertexId(_currentFId); _pViewEdgeBuilder->BuildViewEdges(dynamic_cast(*it), vshape, _ViewMap->ViewEdges(), _ViewMap->ViewVertices(), _ViewMap->FEdges(), _ViewMap->SVertices()); _currentId = _pViewEdgeBuilder->currentViewId() + 1; _currentFId = _pViewEdgeBuilder->currentFId() + 1; _currentSVertexId = _pViewEdgeBuilder->currentSVertexId() + 1; psShape->ComputeBBox(); } } void ViewMapBuilder::computeCusps(ViewMap *ioViewMap) { vector newVVertices; vector newVEdges; ViewMap::viewedges_container& vedges = ioViewMap->ViewEdges(); ViewMap::viewedges_container::iterator ve = vedges.begin(), veend = vedges.end(); for (; ve != veend; ++ve) { if (_pRenderMonitor && _pRenderMonitor->testBreak()) break; if ((!((*ve)->getNature() & Nature::SILHOUETTE)) || (!((*ve)->fedgeA()->isSmooth()))) continue; FEdge *fe = (*ve)->fedgeA(); FEdge *fefirst = fe; bool first = true; bool positive = true; do { FEdgeSmooth *fes = dynamic_cast(fe); Vec3r A((fes)->vertexA()->point3d()); Vec3r B((fes)->vertexB()->point3d()); Vec3r AB(B - A); AB.normalize(); Vec3r m((A + B) / 2.0); Vec3r crossP(AB ^ (fes)->normal()); crossP.normalize(); Vec3r viewvector; if (_orthographicProjection) { viewvector = Vec3r(0.0, 0.0, m.z() - _viewpoint.z()); } else { viewvector = Vec3r(m - _viewpoint); } viewvector.normalize(); if (first) { if (((crossP) * (viewvector)) > 0) positive = true; else positive = false; first = false; } // If we're in a positive part, we need a stronger negative value to change NonTVertex *cusp = NULL; if (positive) { if (((crossP) * (viewvector)) < -0.1) { // state changes positive = false; // creates and insert cusp cusp = dynamic_cast(ioViewMap->InsertViewVertex(fes->vertexA(), newVEdges)); if (cusp) cusp->setNature(cusp->getNature() | Nature::CUSP); } } else { // If we're in a negative part, we need a stronger negative value to change if (((crossP) * (viewvector)) > 0.1) { positive = true; cusp = dynamic_cast(ioViewMap->InsertViewVertex(fes->vertexA(), newVEdges)); if (cusp) cusp->setNature(cusp->getNature() | Nature::CUSP); } } fe = fe->nextEdge(); } while (fe && fe != fefirst); } for (ve = newVEdges.begin(), veend = newVEdges.end(); ve != veend; ++ve) { (*ve)->viewShape()->AddEdge(*ve); vedges.push_back(*ve); } } void ViewMapBuilder::ComputeCumulativeVisibility(ViewMap *ioViewMap, WingedEdge& we, const BBox& bbox, real epsilon, bool cull, GridDensityProviderFactory& factory) { AutoPtr transform; AutoPtr source; if (_orthographicProjection) { transform.reset(new BoxGrid::Transform); } else { transform.reset(new SphericalGrid::Transform); } if (cull) { source.reset(new CulledOccluderSource(*transform, we, *ioViewMap, true)); } else { source.reset(new OccluderSource(*transform, we)); } AutoPtr density(factory.newGridDensityProvider(*source, bbox, *transform)); if (_orthographicProjection) { BoxGrid grid(*source, *density, ioViewMap, _viewpoint, _EnableQI); computeCumulativeVisibility(ioViewMap, grid, epsilon, _pRenderMonitor); } else { SphericalGrid grid(*source, *density, ioViewMap, _viewpoint, _EnableQI); computeCumulativeVisibility(ioViewMap, grid, epsilon, _pRenderMonitor); } } void ViewMapBuilder::ComputeDetailedVisibility(ViewMap *ioViewMap, WingedEdge& we, const BBox& bbox, real epsilon, bool cull, GridDensityProviderFactory& factory) { AutoPtr transform; AutoPtr source; if (_orthographicProjection) { transform.reset(new BoxGrid::Transform); } else { transform.reset(new SphericalGrid::Transform); } if (cull) { source.reset(new CulledOccluderSource(*transform, we, *ioViewMap, true)); } else { source.reset(new OccluderSource(*transform, we)); } AutoPtr density(factory.newGridDensityProvider(*source, bbox, *transform)); if (_orthographicProjection) { BoxGrid grid(*source, *density, ioViewMap, _viewpoint, _EnableQI); computeDetailedVisibility(ioViewMap, grid, epsilon, _pRenderMonitor); } else { SphericalGrid grid(*source, *density, ioViewMap, _viewpoint, _EnableQI); computeDetailedVisibility(ioViewMap, grid, epsilon, _pRenderMonitor); } } void ViewMapBuilder::ComputeEdgesVisibility(ViewMap *ioViewMap, WingedEdge& we, const BBox& bbox, unsigned int sceneNumFaces, visibility_algo iAlgo, real epsilon) { #if 0 iAlgo = ray_casting; // for testing algorithms equivalence #endif switch (iAlgo) { case ray_casting: if (_global.debug & G_DEBUG_FREESTYLE) { cout << "Using ordinary ray casting" << endl; } BuildGrid(we, bbox, sceneNumFaces); ComputeRayCastingVisibility(ioViewMap, epsilon); break; case ray_casting_fast: if (_global.debug & G_DEBUG_FREESTYLE) { cout << "Using fast ray casting" << endl; } BuildGrid(we, bbox, sceneNumFaces); ComputeFastRayCastingVisibility(ioViewMap, epsilon); break; case ray_casting_very_fast: if (_global.debug & G_DEBUG_FREESTYLE) { cout << "Using very fast ray casting" << endl; } BuildGrid(we, bbox, sceneNumFaces); ComputeVeryFastRayCastingVisibility(ioViewMap, epsilon); break; case ray_casting_culled_adaptive_traditional: if (_global.debug & G_DEBUG_FREESTYLE) { cout << "Using culled adaptive grid with heuristic density and traditional QI calculation" << endl; } try { HeuristicGridDensityProviderFactory factory(0.5f, sceneNumFaces); ComputeDetailedVisibility(ioViewMap, we, bbox, epsilon, true, factory); } catch (...) { // Last resort catch to make sure RAII semantics hold for OptimizedGrid. Can be replaced with // try...catch block around main() if the program as a whole is converted to RAII // This is the little-mentioned caveat of RAII: RAII does not work unless destructors are always // called, but destructors are only called if all exceptions are caught (or std::terminate() is // replaced). // We don't actually handle the exception here, so re-throw it now that our destructors have had a // chance to run. throw; } break; case ray_casting_adaptive_traditional: if (_global.debug & G_DEBUG_FREESTYLE) { cout << "Using unculled adaptive grid with heuristic density and traditional QI calculation" << endl; } try { HeuristicGridDensityProviderFactory factory(0.5f, sceneNumFaces); ComputeDetailedVisibility(ioViewMap, we, bbox, epsilon, false, factory); } catch (...) { throw; } break; case ray_casting_culled_adaptive_cumulative: if (_global.debug & G_DEBUG_FREESTYLE) { cout << "Using culled adaptive grid with heuristic density and cumulative QI calculation" << endl; } try { HeuristicGridDensityProviderFactory factory(0.5f, sceneNumFaces); ComputeCumulativeVisibility(ioViewMap, we, bbox, epsilon, true, factory); } catch (...) { throw; } break; case ray_casting_adaptive_cumulative: if (_global.debug & G_DEBUG_FREESTYLE) { cout << "Using unculled adaptive grid with heuristic density and cumulative QI calculation" << endl; } try { HeuristicGridDensityProviderFactory factory(0.5f, sceneNumFaces); ComputeCumulativeVisibility(ioViewMap, we, bbox, epsilon, false, factory); } catch (...) { throw; } break; default: break; } } static const unsigned gProgressBarMaxSteps = 10; static const unsigned gProgressBarMinSize = 2000; void ViewMapBuilder::ComputeRayCastingVisibility(ViewMap *ioViewMap, real epsilon) { vector& vedges = ioViewMap->ViewEdges(); bool progressBarDisplay = false; unsigned progressBarStep = 0; unsigned vEdgesSize = vedges.size(); unsigned fEdgesSize = ioViewMap->FEdges().size(); if (_pProgressBar != NULL && fEdgesSize > gProgressBarMinSize) { unsigned progressBarSteps = min(gProgressBarMaxSteps, vEdgesSize); progressBarStep = vEdgesSize / progressBarSteps; _pProgressBar->reset(); _pProgressBar->setLabelText("Computing Ray casting Visibility"); _pProgressBar->setTotalSteps(progressBarSteps); _pProgressBar->setProgress(0); progressBarDisplay = true; } unsigned counter = progressBarStep; FEdge *fe, *festart; int nSamples = 0; vector aFaces; Polygon3r *aFace = NULL; unsigned tmpQI = 0; unsigned qiClasses[256]; unsigned maxIndex, maxCard; unsigned qiMajority; static unsigned timestamp = 1; for (vector::iterator ve = vedges.begin(), veend = vedges.end(); ve != veend; ve++) { if (_pRenderMonitor && _pRenderMonitor->testBreak()) break; #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "Processing ViewEdge " << (*ve)->getId() << endl; } #endif festart = (*ve)->fedgeA(); fe = (*ve)->fedgeA(); qiMajority = 1; do { qiMajority++; fe = fe->nextEdge(); } while (fe && fe != festart); qiMajority >>= 1; #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tqiMajority: " << qiMajority << endl; } #endif tmpQI = 0; maxIndex = 0; maxCard = 0; nSamples = 0; fe = (*ve)->fedgeA(); memset(qiClasses, 0, 256 * sizeof(*qiClasses)); set occluders; do { if ((maxCard < qiMajority)) { tmpQI = ComputeRayCastingVisibility(fe, _Grid, epsilon, occluders, &aFace, timestamp++); #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tFEdge: visibility " << tmpQI << endl; } #endif //ARB: This is an error condition, not an alert condition. // Some sort of recovery or abort is necessary. if (tmpQI >= 256) { cerr << "Warning: too many occluding levels" << endl; //ARB: Wild guess: instead of aborting or corrupting memory, treat as tmpQI == 255 tmpQI = 255; } if (++qiClasses[tmpQI] > maxCard) { maxCard = qiClasses[tmpQI]; maxIndex = tmpQI; } } else { //ARB: FindOccludee is redundant if ComputeRayCastingVisibility has been called FindOccludee(fe, _Grid, epsilon, &aFace, timestamp++); #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tFEdge: occludee only (" << (aFace != NULL ? "found" : "not found") << ")" << endl; } #endif } if (aFace) { fe->setaFace(*aFace); aFaces.push_back(aFace); fe->setOccludeeEmpty(false); #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tFound occludee" << endl; } #endif } else { //ARB: We are arbitrarily using the last observed value for occludee (almost always the value observed // for the edge before festart). Is that meaningful? // ...in fact, _occludeeEmpty seems to be unused. fe->setOccludeeEmpty(true); } ++nSamples; fe = fe->nextEdge(); } while ((maxCard < qiMajority) && (fe) && (fe != festart)); #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tFinished with " << nSamples << " samples, maxCard = " << maxCard << endl; } #endif // ViewEdge // qi -- (*ve)->setQI(maxIndex); // occluders -- for (set::iterator o = occluders.begin(), oend = occluders.end(); o != oend; ++o) (*ve)->AddOccluder((*o)); #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\tConclusion: QI = " << maxIndex << ", " << (*ve)->occluders_size() << " occluders." << endl; } #endif // occludee -- if (!aFaces.empty()) { if (aFaces.size() <= (float)nSamples / 2.0f) { (*ve)->setaShape(0); } else { vector::iterator p = aFaces.begin(); WFace *wface = (WFace *)((*p)->userdata); ViewShape *vshape = ioViewMap->viewShape(wface->GetVertex(0)->shape()->GetId()); ++p; (*ve)->setaShape(vshape); } } if (progressBarDisplay) { counter--; if (counter <= 0) { counter = progressBarStep; _pProgressBar->setProgress(_pProgressBar->getProgress() + 1); } } aFaces.clear(); } } void ViewMapBuilder::ComputeFastRayCastingVisibility(ViewMap *ioViewMap, real epsilon) { vector& vedges = ioViewMap->ViewEdges(); bool progressBarDisplay = false; unsigned progressBarStep = 0; unsigned vEdgesSize = vedges.size(); unsigned fEdgesSize = ioViewMap->FEdges().size(); if (_pProgressBar != NULL && fEdgesSize > gProgressBarMinSize) { unsigned progressBarSteps = min(gProgressBarMaxSteps, vEdgesSize); progressBarStep = vEdgesSize / progressBarSteps; _pProgressBar->reset(); _pProgressBar->setLabelText("Computing Ray casting Visibility"); _pProgressBar->setTotalSteps(progressBarSteps); _pProgressBar->setProgress(0); progressBarDisplay = true; } unsigned counter = progressBarStep; FEdge *fe, *festart; unsigned nSamples = 0; vector aFaces; Polygon3r *aFace = NULL; unsigned tmpQI = 0; unsigned qiClasses[256]; unsigned maxIndex, maxCard; unsigned qiMajority; static unsigned timestamp = 1; bool even_test; for (vector::iterator ve = vedges.begin(), veend = vedges.end(); ve != veend; ve++) { if (_pRenderMonitor && _pRenderMonitor->testBreak()) break; festart = (*ve)->fedgeA(); fe = (*ve)->fedgeA(); qiMajority = 1; do { qiMajority++; fe = fe->nextEdge(); } while (fe && fe != festart); if (qiMajority >= 4) qiMajority >>= 2; else qiMajority = 1; set occluders; even_test = true; maxIndex = 0; maxCard = 0; nSamples = 0; memset(qiClasses, 0, 256 * sizeof(*qiClasses)); fe = (*ve)->fedgeA(); do { if (even_test) { if ((maxCard < qiMajority)) { tmpQI = ComputeRayCastingVisibility(fe, _Grid, epsilon, occluders, &aFace, timestamp++); //ARB: This is an error condition, not an alert condition. // Some sort of recovery or abort is necessary. if (tmpQI >= 256) { cerr << "Warning: too many occluding levels" << endl; //ARB: Wild guess: instead of aborting or corrupting memory, treat as tmpQI == 255 tmpQI = 255; } if (++qiClasses[tmpQI] > maxCard) { maxCard = qiClasses[tmpQI]; maxIndex = tmpQI; } } else { //ARB: FindOccludee is redundant if ComputeRayCastingVisibility has been called FindOccludee(fe, _Grid, epsilon, &aFace, timestamp++); } if (aFace) { fe->setaFace(*aFace); aFaces.push_back(aFace); } ++nSamples; even_test = false; } else { even_test = true; } fe = fe->nextEdge(); } while ((maxCard < qiMajority) && (fe) && (fe != festart)); (*ve)->setQI(maxIndex); if (!aFaces.empty()) { if (aFaces.size() < nSamples / 2) { (*ve)->setaShape(0); } else { vector::iterator p = aFaces.begin(); WFace *wface = (WFace *)((*p)->userdata); ViewShape *vshape = ioViewMap->viewShape(wface->GetVertex(0)->shape()->GetId()); ++p; #if 0 for (; p != pend; ++p) { WFace *f = (WFace*)((*p)->userdata); ViewShape *vs = ioViewMap->viewShape(f->GetVertex(0)->shape()->GetId()); if (vs != vshape) { sameShape = false; break; } } if (sameShape) #endif (*ve)->setaShape(vshape); } } //(*ve)->setaFace(aFace); if (progressBarDisplay) { counter--; if (counter <= 0) { counter = progressBarStep; _pProgressBar->setProgress(_pProgressBar->getProgress() + 1); } } aFaces.clear(); } } void ViewMapBuilder::ComputeVeryFastRayCastingVisibility(ViewMap *ioViewMap, real epsilon) { vector& vedges = ioViewMap->ViewEdges(); bool progressBarDisplay = false; unsigned progressBarStep = 0; unsigned vEdgesSize = vedges.size(); unsigned fEdgesSize = ioViewMap->FEdges().size(); if (_pProgressBar != NULL && fEdgesSize > gProgressBarMinSize) { unsigned progressBarSteps = min(gProgressBarMaxSteps, vEdgesSize); progressBarStep = vEdgesSize / progressBarSteps; _pProgressBar->reset(); _pProgressBar->setLabelText("Computing Ray casting Visibility"); _pProgressBar->setTotalSteps(progressBarSteps); _pProgressBar->setProgress(0); progressBarDisplay = true; } unsigned counter = progressBarStep; FEdge *fe; unsigned qi = 0; Polygon3r *aFace = NULL; static unsigned timestamp = 1; for (vector::iterator ve = vedges.begin(), veend = vedges.end(); ve != veend; ve++) { if (_pRenderMonitor && _pRenderMonitor->testBreak()) break; set occluders; fe = (*ve)->fedgeA(); qi = ComputeRayCastingVisibility(fe, _Grid, epsilon, occluders, &aFace, timestamp++); if (aFace) { fe->setaFace(*aFace); WFace *wface = (WFace *)(aFace->userdata); ViewShape *vshape = ioViewMap->viewShape(wface->GetVertex(0)->shape()->GetId()); (*ve)->setaShape(vshape); } else { (*ve)->setaShape(0); } (*ve)->setQI(qi); if (progressBarDisplay) { counter--; if (counter <= 0) { counter = progressBarStep; _pProgressBar->setProgress(_pProgressBar->getProgress() + 1); } } } } void ViewMapBuilder::FindOccludee(FEdge *fe, Grid *iGrid, real epsilon, Polygon3r **oaPolygon, unsigned timestamp, Vec3r& u, Vec3r& A, Vec3r& origin, Vec3r& edge, vector& faceVertices) { WFace *face = NULL; if (fe->isSmooth()) { FEdgeSmooth *fes = dynamic_cast(fe); face = (WFace *)fes->face(); } OccludersSet occluders; WFace *oface; bool skipFace; WVertex::incoming_edge_iterator ie; OccludersSet::iterator p, pend; *oaPolygon = NULL; if (((fe)->getNature() & Nature::SILHOUETTE) || ((fe)->getNature() & Nature::BORDER)) { occluders.clear(); // we cast a ray from A in the same direction but looking behind Vec3r v(-u[0], -u[1], -u[2]); iGrid->castInfiniteRay(A, v, occluders, timestamp); bool noIntersection = true; real mint = FLT_MAX; // we met some occluders, let us fill the aShape field with the first intersected occluder for (p = occluders.begin(), pend = occluders.end(); p != pend; p++) { // check whether the edge and the polygon plane are coincident: //------------------------------------------------------------- //first let us compute the plane equation. oface = (WFace *)(*p)->userdata; Vec3r v1(((*p)->getVertices())[0]); Vec3r normal((*p)->getNormal()); real d = -(v1 * normal); real t, t_u, t_v; if (face) { skipFace = false; if (face == oface) continue; if (faceVertices.empty()) continue; for (vector::iterator fv = faceVertices.begin(), fvend = faceVertices.end(); fv != fvend; ++fv) { if ((*fv)->isBoundary()) continue; WVertex::incoming_edge_iterator iebegin = (*fv)->incoming_edges_begin(); WVertex::incoming_edge_iterator ieend = (*fv)->incoming_edges_end(); for (ie = iebegin; ie != ieend; ++ie) { if ((*ie) == 0) continue; WFace *sface = (*ie)->GetbFace(); if (sface == oface) { skipFace = true; break; } } if (skipFace) break; } if (skipFace) continue; } else { if (GeomUtils::COINCIDENT == GeomUtils::intersectRayPlane(origin, edge, normal, d, t, epsilon)) continue; } if ((*p)->rayIntersect(A, v, t, t_u, t_v)) { if (fabs(v * normal) > 0.0001) { if (t > 0.0) { // && t < 1.0) { if (t < mint) { *oaPolygon = (*p); mint = t; noIntersection = false; fe->setOccludeeIntersection(Vec3r(A + t * v)); } } } } } if (noIntersection) *oaPolygon = NULL; } } void ViewMapBuilder::FindOccludee(FEdge *fe, Grid *iGrid, real epsilon, Polygon3r **oaPolygon, unsigned timestamp) { OccludersSet occluders; Vec3r A; Vec3r edge; Vec3r origin; A = Vec3r(((fe)->vertexA()->point3D() + (fe)->vertexB()->point3D()) / 2.0); edge = Vec3r((fe)->vertexB()->point3D() - (fe)->vertexA()->point3D()); origin = Vec3r((fe)->vertexA()->point3D()); Vec3r u; if (_orthographicProjection) { u = Vec3r(0.0, 0.0, _viewpoint.z() - A.z()); } else { u = Vec3r(_viewpoint - A); } u.normalize(); if (A < iGrid->getOrigin()) cerr << "Warning: point is out of the grid for fedge " << fe->getId().getFirst() << "-" << fe->getId().getSecond() << endl; vector faceVertices; WFace *face = NULL; if (fe->isSmooth()) { FEdgeSmooth *fes = dynamic_cast(fe); face = (WFace *)fes->face(); } if (face) face->RetrieveVertexList(faceVertices); return FindOccludee(fe, iGrid, epsilon, oaPolygon, timestamp, u, A, origin, edge, faceVertices); } int ViewMapBuilder::ComputeRayCastingVisibility(FEdge *fe, Grid *iGrid, real epsilon, set& oOccluders, Polygon3r **oaPolygon, unsigned timestamp) { OccludersSet occluders; int qi = 0; Vec3r center; Vec3r edge; Vec3r origin; center = fe->center3d(); edge = Vec3r(fe->vertexB()->point3D() - fe->vertexA()->point3D()); origin = Vec3r(fe->vertexA()->point3D()); // Is the edge outside the view frustum ? Vec3r gridOrigin(iGrid->getOrigin()); Vec3r gridExtremity(iGrid->getOrigin() + iGrid->gridSize()); if ((center.x() < gridOrigin.x()) || (center.y() < gridOrigin.y()) || (center.z() < gridOrigin.z()) || (center.x() > gridExtremity.x()) || (center.y() > gridExtremity.y()) || (center.z() > gridExtremity.z())) { cerr << "Warning: point is out of the grid for fedge " << fe->getId() << endl; //return 0; } #if 0 Vec3r A(fe->vertexA()->point2d()); Vec3r B(fe->vertexB()->point2d()); int viewport[4]; SilhouetteGeomEngine::retrieveViewport(viewport); if ((A.x() < viewport[0]) || (A.x() > viewport[2]) || (A.y() < viewport[1]) || (A.y() > viewport[3]) || (B.x() < viewport[0]) || (B.x() > viewport[2]) || (B.y() < viewport[1]) || (B.y() > viewport[3])) { cerr << "Warning: point is out of the grid for fedge " << fe->getId() << endl; //return 0; } #endif Vec3r vp; if (_orthographicProjection) { vp = Vec3r(center.x(), center.y(), _viewpoint.z()); } else { vp = Vec3r(_viewpoint); } Vec3r u(vp - center); real raylength = u.norm(); u.normalize(); #if 0 if (_global.debug & G_DEBUG_FREESTYLE) { cout << "grid origin " << iGrid->getOrigin().x() << "," << iGrid->getOrigin().y() << "," << iGrid->getOrigin().z() << endl; cout << "center " << center.x() << "," << center.y() << "," << center.z() << endl; } #endif iGrid->castRay(center, vp, occluders, timestamp); WFace *face = NULL; if (fe->isSmooth()) { FEdgeSmooth *fes = dynamic_cast(fe); face = (WFace *)fes->face(); } vector faceVertices; WVertex::incoming_edge_iterator ie; WFace *oface; bool skipFace; OccludersSet::iterator p, pend; if (face) face->RetrieveVertexList(faceVertices); for (p = occluders.begin(), pend = occluders.end(); p != pend; p++) { // If we're dealing with an exact silhouette, check whether we must take care of this occluder of not. // (Indeed, we don't consider the occluders that share at least one vertex with the face containing this edge). //----------- oface = (WFace *)(*p)->userdata; #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tEvaluating intersection for occluder " << ((*p)->getVertices())[0] << ((*p)->getVertices())[1] << ((*p)->getVertices())[2] << endl << "\t\t\tand ray " << vp << " * " << u << " (center " << center << ")" << endl; } #endif Vec3r v1(((*p)->getVertices())[0]); Vec3r normal((*p)->getNormal()); real d = -(v1 * normal); real t, t_u, t_v; #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tp: " << ((*p)->getVertices())[0] << ((*p)->getVertices())[1] << ((*p)->getVertices())[2] << ", norm: " << (*p)->getNormal() << endl; } #endif if (face) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tDetermining face adjacency..."; } #endif skipFace = false; if (face == oface) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << " Rejecting occluder for face concurrency." << endl; } #endif continue; } for (vector::iterator fv = faceVertices.begin(), fvend = faceVertices.end(); fv != fvend; ++fv) { if ((*fv)->isBoundary()) continue; WVertex::incoming_edge_iterator iebegin = (*fv)->incoming_edges_begin(); WVertex::incoming_edge_iterator ieend = (*fv)->incoming_edges_end(); for (ie = iebegin; ie != ieend; ++ie) { if ((*ie) == 0) continue; WFace *sface = (*ie)->GetbFace(); //WFace *sfacea = (*ie)->GetaFace(); //if ((sface == oface) || (sfacea == oface)) { if (sface == oface) { skipFace = true; break; } } if (skipFace) break; } if (skipFace) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << " Rejecting occluder for face adjacency." << endl; } #endif continue; } } else { // check whether the edge and the polygon plane are coincident: //------------------------------------------------------------- //first let us compute the plane equation. if (GeomUtils::COINCIDENT == GeomUtils::intersectRayPlane(origin, edge, normal, d, t, epsilon)) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tRejecting occluder for target coincidence." << endl; } #endif continue; } } if ((*p)->rayIntersect(center, u, t, t_u, t_v)) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tRay " << vp << " * " << u << " intersects at time " << t << " (raylength is " << raylength << ")" << endl; cout << "\t\t(u * normal) == " << (u * normal) << " for normal " << normal << endl; } #endif if (fabs(u * normal) > 0.0001) { if ((t>0.0) && (tuserdata); ViewShape *vshape = _ViewMap->viewShape(f->GetVertex(0)->shape()->GetId()); oOccluders.insert(vshape); ++qi; if (!_EnableQI) break; } } } } // Find occludee FindOccludee(fe, iGrid, epsilon, oaPolygon, timestamp, u, center, edge, origin, faceVertices); return qi; } void ViewMapBuilder::ComputeIntersections(ViewMap *ioViewMap, intersection_algo iAlgo, real epsilon) { switch (iAlgo) { case sweep_line: ComputeSweepLineIntersections(ioViewMap, epsilon); break; default: break; } ViewMap::viewvertices_container& vvertices = ioViewMap->ViewVertices(); for (ViewMap::viewvertices_container::iterator vv = vvertices.begin(), vvend = vvertices.end(); vv != vvend; ++vv) { if ((*vv)->getNature() == Nature::T_VERTEX) { TVertex *tvertex = (TVertex *)(*vv); if (_global.debug & G_DEBUG_FREESTYLE) { cout << "TVertex " << tvertex->getId() << " has :" << endl; cout << "FrontEdgeA: " << tvertex->frontEdgeA().first << endl; cout << "FrontEdgeB: " << tvertex->frontEdgeB().first << endl; cout << "BackEdgeA: " << tvertex->backEdgeA().first << endl; cout << "BackEdgeB: " << tvertex->backEdgeB().first << endl << endl; } } } } struct less_SVertex2D : public binary_function { real epsilon; less_SVertex2D(real eps) : binary_function() { epsilon = eps; } bool operator()(SVertex *x, SVertex *y) { Vec3r A = x->point2D(); Vec3r B = y->point2D(); for (unsigned int i = 0; i < 3; i++) { if ((fabs(A[i] - B[i])) < epsilon) continue; if (A[i] < B[i]) return true; if (A[i] > B[i]) return false; } return false; } }; typedef Segment segment; typedef Intersection intersection; struct less_Intersection : public binary_function { segment *edge; less_Intersection(segment *iEdge) : binary_function() { edge = iEdge; } bool operator()(intersection *x, intersection *y) { real tx = x->getParameter(edge); real ty = y->getParameter(edge); if (tx > ty) return true; return false; } }; struct silhouette_binary_rule : public binary_rule { silhouette_binary_rule() : binary_rule() {} virtual bool operator()(segment& s1, segment& s2) { FEdge *f1 = s1.edge(); FEdge *f2 = s2.edge(); if ((!(((f1)->getNature() & Nature::SILHOUETTE) || ((f1)->getNature() & Nature::BORDER))) && (!(((f2)->getNature() & Nature::SILHOUETTE) || ((f2)->getNature() & Nature::BORDER)))) { return false; } return true; } }; void ViewMapBuilder::ComputeSweepLineIntersections(ViewMap *ioViewMap, real epsilon) { vector& svertices = ioViewMap->SVertices(); bool progressBarDisplay = false; unsigned sVerticesSize = svertices.size(); unsigned fEdgesSize = ioViewMap->FEdges().size(); #if 0 if (_global.debug & G_DEBUG_FREESTYLE) { ViewMap::fedges_container& fedges = ioViewMap->FEdges(); for (ViewMap::fedges_container::const_iterator f = fedges.begin(), end = fedges.end(); f != end; ++f) { cout << (*f)->aMaterialIndex() << "-" << (*f)->bMaterialIndex() << endl; } } #endif unsigned progressBarStep = 0; if (_pProgressBar != NULL && fEdgesSize > gProgressBarMinSize) { unsigned progressBarSteps = min(gProgressBarMaxSteps, sVerticesSize); progressBarStep = sVerticesSize / progressBarSteps; _pProgressBar->reset(); _pProgressBar->setLabelText("Computing Sweep Line Intersections"); _pProgressBar->setTotalSteps(progressBarSteps); _pProgressBar->setProgress(0); progressBarDisplay = true; } unsigned counter = progressBarStep; sort(svertices.begin(), svertices.end(), less_SVertex2D(epsilon)); SweepLine SL; vector& ioEdges = ioViewMap->FEdges(); vector segments; vector::iterator fe, fend; for (fe = ioEdges.begin(), fend = ioEdges.end(); fe != fend; fe++) { segment *s = new segment((*fe), (*fe)->vertexA()->point2D(), (*fe)->vertexB()->point2D()); (*fe)->userdata = s; segments.push_back(s); } vector vsegments; for (vector::iterator sv = svertices.begin(), svend = svertices.end(); sv != svend; sv++) { if (_pRenderMonitor && _pRenderMonitor->testBreak()) break; const vector& vedges = (*sv)->fedges(); for (vector::const_iterator sve = vedges.begin(), sveend = vedges.end(); sve != sveend; sve++) { vsegments.push_back((segment *)((*sve)->userdata)); } Vec3r evt((*sv)->point2D()); silhouette_binary_rule sbr; SL.process(evt, vsegments, sbr, epsilon); if (progressBarDisplay) { counter--; if (counter <= 0) { counter = progressBarStep; _pProgressBar->setProgress(_pProgressBar->getProgress() + 1); } } vsegments.clear(); } if (_pRenderMonitor && _pRenderMonitor->testBreak()) { // delete segments if (!segments.empty()) { vector::iterator s, send; for (s = segments.begin(), send = segments.end(); s != send; s++) { delete *s; } } return; } // reset userdata: for (fe = ioEdges.begin(), fend = ioEdges.end(); fe != fend; fe++) (*fe)->userdata = NULL; // list containing the new edges resulting from splitting operations. vector newEdges; // retrieve the intersected edges: vector& iedges = SL.intersectedEdges(); // retrieve the intersections: vector& intersections = SL.intersections(); int id = 0; // create a view vertex for each intersection and linked this one with the intersection object vector::iterator i, iend; for (i = intersections.begin(), iend = intersections.end(); i != iend; i++) { FEdge *fA = (*i)->EdgeA->edge(); FEdge *fB = (*i)->EdgeB->edge(); Vec3r A1 = fA->vertexA()->point3D(); Vec3r A2 = fA->vertexB()->point3D(); Vec3r B1 = fB->vertexA()->point3D(); Vec3r B2 = fB->vertexB()->point3D(); Vec3r a1 = fA->vertexA()->point2D(); Vec3r a2 = fA->vertexB()->point2D(); Vec3r b1 = fB->vertexA()->point2D(); Vec3r b2 = fB->vertexB()->point2D(); real ta = (*i)->tA; real tb = (*i)->tB; if ((ta < -epsilon) || (ta > 1 + epsilon)) cerr << "Warning: 2D intersection out of range for edge " << fA->vertexA()->getId() << " - " << fA->vertexB()->getId() << endl; if ((tb < -epsilon) || (tb > 1 + epsilon)) cerr << "Warning: 2D intersection out of range for edge " << fB->vertexA()->getId() << " - " << fB->vertexB()->getId() << endl; real Ta = SilhouetteGeomEngine::ImageToWorldParameter(fA, ta); real Tb = SilhouetteGeomEngine::ImageToWorldParameter(fB, tb); if ((Ta < -epsilon) || (Ta > 1 + epsilon)) cerr << "Warning: 3D intersection out of range for edge " << fA->vertexA()->getId() << " - " << fA->vertexB()->getId() << endl; if ((Tb < -epsilon) || (Tb > 1 + epsilon)) cerr << "Warning: 3D intersection out of range for edge " << fB->vertexA()->getId() << " - " << fB->vertexB()->getId() << endl; #if 0 if (_global.debug & G_DEBUG_FREESTYLE) { if ((Ta < -epsilon) || (Ta > 1 + epsilon) || (Tb < -epsilon) || (Tb > 1 + epsilon)) { printf("ta %.12e\n", ta); printf("tb %.12e\n", tb); printf("a1 %e, %e -- a2 %e, %e\n", a1[0], a1[1], a2[0], a2[1]); printf("b1 %e, %e -- b2 %e, %e\n", b1[0], b1[1], b2[0], b2[1]); //printf("line([%e, %e], [%e, %e]);\n", a1[0], a2[0], a1[1], a2[1]); //printf("line([%e, %e], [%e, %e]);\n", b1[0], b2[0], b1[1], b2[1]); if ((Ta < -epsilon) || (Ta > 1 + epsilon)) printf("Ta %.12e\n", Ta); if ((Tb < -epsilon) || (Tb > 1 + epsilon)) printf("Tb %.12e\n", Tb); printf("A1 %e, %e, %e -- A2 %e, %e, %e\n", A1[0], A1[1], A1[2], A2[0], A2[1], A2[2]); printf("B1 %e, %e, %e -- B2 %e, %e, %e\n", B1[0], B1[1], B1[2], B2[0], B2[1], B2[2]); } } #endif TVertex *tvertex = ioViewMap->CreateTVertex(Vec3r(A1 + Ta * (A2 - A1)), Vec3r(a1 + ta * (a2 - a1)), fA, Vec3r(B1 + Tb * (B2 - B1)), Vec3r(b1 + tb * (b2 - b1)), fB, id); (*i)->userdata = tvertex; ++id; } progressBarStep = 0; if (progressBarDisplay) { unsigned iEdgesSize = iedges.size(); unsigned progressBarSteps = min(gProgressBarMaxSteps, iEdgesSize); progressBarStep = iEdgesSize / progressBarSteps; _pProgressBar->reset(); _pProgressBar->setLabelText("Splitting intersected edges"); _pProgressBar->setTotalSteps(progressBarSteps); _pProgressBar->setProgress(0); } counter = progressBarStep; vector edgeVVertices; vector newVEdges; vector::iterator s, send; for (s = iedges.begin(), send = iedges.end(); s != send; s++) { edgeVVertices.clear(); newEdges.clear(); newVEdges.clear(); FEdge *fedge = (*s)->edge(); ViewEdge *vEdge = fedge->viewedge(); ViewShape *shape = vEdge->viewShape(); vector& eIntersections = (*s)->intersections(); // we first need to sort these intersections from farther to closer to A sort(eIntersections.begin(), eIntersections.end(), less_Intersection(*s)); for (i = eIntersections.begin(), iend = eIntersections.end(); i != iend; i++) edgeVVertices.push_back((TVertex *)(*i)->userdata); shape->SplitEdge(fedge, edgeVVertices, ioViewMap->FEdges(), ioViewMap->ViewEdges()); if (progressBarDisplay) { counter--; if (counter <= 0) { counter = progressBarStep; _pProgressBar->setProgress(_pProgressBar->getProgress() + 1); } } } // reset userdata: for (fe = ioEdges.begin(), fend = ioEdges.end(); fe != fend; fe++) (*fe)->userdata = NULL; // delete segments if (!segments.empty()) { for (s = segments.begin(), send = segments.end(); s != send; s++) { delete *s; } } } } /* namespace Freestyle */