/* * ***** 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/ViewEdgeXBuilder.cpp * \ingroup freestyle * \brief Class to build view edges and the underlying chains of feature edges... * \author Stephane Grabli * \date 27/10/2003 */ #include #include "SilhouetteGeomEngine.h" #include "ViewEdgeXBuilder.h" #include "ViewMap.h" #include "../winged_edge/WXEdge.h" using namespace std; namespace Freestyle { void ViewEdgeXBuilder::Init(ViewShape *oVShape) { if (NULL == oVShape) return; // for design conveniance, we store the current SShape. _pCurrentSShape = oVShape->sshape(); if (0 == _pCurrentSShape) return; _pCurrentVShape = oVShape; // Reset previous data //-------------------- if (!_SVertexMap.empty()) _SVertexMap.clear(); } void ViewEdgeXBuilder::BuildViewEdges(WXShape *iWShape, ViewShape *oVShape, vector& ioVEdges, vector& ioVVertices, vector& ioFEdges, vector& ioSVertices) { // Reinit structures Init(oVShape); /* ViewEdge *vedge; */ /* UNUSED */ // Let us build the smooth stuff //---------------------------------------- // We parse all faces to find the ones that contain smooth edges vector& wfaces = iWShape->GetFaceList(); vector::iterator wf, wfend; WXFace *wxf; for (wf = wfaces.begin(), wfend = wfaces.end(); wf != wfend; wf++) { wxf = dynamic_cast(*wf); if (false == ((wxf))->hasSmoothEdges()) // does it contain at least one smooth edge ? continue; // parse all smooth layers: vector& smoothLayers = wxf->getSmoothLayers(); for (vector::iterator sl = smoothLayers.begin(), slend = smoothLayers.end(); sl != slend; ++sl) { if (!(*sl)->hasSmoothEdge()) continue; if (stopSmoothViewEdge((*sl))) // has it been parsed already ? continue; // here we know that we're dealing with a face layer that has not been processed yet and that contains // a smooth edge. /* vedge =*/ /* UNUSED */ BuildSmoothViewEdge(OWXFaceLayer(*sl, true)); } } // Now let's build sharp view edges: //---------------------------------- // Reset all userdata for WXEdge structure //---------------------------------------- //iWShape->ResetUserData(); WXEdge *wxe; vector& wedges = iWShape->getEdgeList(); //------------------------------ for (vector::iterator we = wedges.begin(), weend = wedges.end(); we != weend; we++) { wxe = dynamic_cast(*we); if (Nature::NO_FEATURE == wxe->nature()) continue; if (!stopSharpViewEdge(wxe)) { bool b = true; if (wxe->order() == -1) b = false; BuildSharpViewEdge(OWXEdge(wxe, b)); } } // Reset all userdata for WXEdge structure //---------------------------------------- iWShape->ResetUserData(); // Add all these new edges to the scene's feature edges list: //----------------------------------------------------------- vector& newedges = _pCurrentSShape->getEdgeList(); vector& newVertices = _pCurrentSShape->getVertexList(); vector& newVVertices = _pCurrentVShape->vertices(); vector& newVEdges = _pCurrentVShape->edges(); // inserts in ioFEdges, at its end, all the edges of newedges ioFEdges.insert(ioFEdges.end(), newedges.begin(), newedges.end()); ioSVertices.insert(ioSVertices.end(), newVertices.begin(), newVertices.end()); ioVVertices.insert(ioVVertices.end(), newVVertices.begin(), newVVertices.end()); ioVEdges.insert(ioVEdges.end(), newVEdges.begin(), newVEdges.end()); } ViewEdge *ViewEdgeXBuilder::BuildSmoothViewEdge(const OWXFaceLayer& iFaceLayer) { // Find first edge: OWXFaceLayer first = iFaceLayer; OWXFaceLayer currentFace = first; // bidirectional chaining. // first direction list facesChain; unsigned size = 0; while (!stopSmoothViewEdge(currentFace.fl)) { facesChain.push_back(currentFace); ++size; currentFace.fl->userdata = (void *)1; // processed // Find the next edge! currentFace = FindNextFaceLayer(currentFace); } OWXFaceLayer end = facesChain.back(); // second direction currentFace = FindPreviousFaceLayer(first); while (!stopSmoothViewEdge(currentFace.fl)) { facesChain.push_front(currentFace); ++size; currentFace.fl->userdata = (void *)1; // processed // Find the previous edge! currentFace = FindPreviousFaceLayer(currentFace); } first = facesChain.front(); if (iFaceLayer.fl->nature() & Nature::RIDGE) { if (size < 4) { return 0; } } // Start a new chain edges ViewEdge *newVEdge = new ViewEdge; newVEdge->setId(_currentViewId); ++_currentViewId; _pCurrentVShape->AddEdge(newVEdge); // build FEdges FEdge *feprevious = NULL; FEdge *fefirst = NULL; FEdge *fe = NULL; for (list::iterator fl = facesChain.begin(), flend = facesChain.end(); fl != flend; ++fl) { fe = BuildSmoothFEdge(feprevious, (*fl)); if (feprevious && fe == feprevious) continue; fe->setViewEdge(newVEdge); if (!fefirst) fefirst = fe; feprevious = fe; } // Store the chain starting edge: _pCurrentSShape->AddChain(fefirst); newVEdge->setNature(iFaceLayer.fl->nature()); newVEdge->setFEdgeA(fefirst); newVEdge->setFEdgeB(fe); // is it a closed loop ? if ((first == end) && (size != 1)) { fefirst->setPreviousEdge(fe); fe->setNextEdge(fefirst); newVEdge->setA(0); newVEdge->setB(0); } else { ViewVertex *vva = MakeViewVertex(fefirst->vertexA()); ViewVertex *vvb = MakeViewVertex(fe->vertexB()); ((NonTVertex *)vva)->AddOutgoingViewEdge(newVEdge); ((NonTVertex *)vvb)->AddIncomingViewEdge(newVEdge); newVEdge->setA(vva); newVEdge->setB(vvb); } return newVEdge; } ViewEdge *ViewEdgeXBuilder::BuildSharpViewEdge(const OWXEdge& iWEdge) { // Start a new sharp chain edges ViewEdge *newVEdge = new ViewEdge; newVEdge->setId(_currentViewId); ++_currentViewId; unsigned size = 0; _pCurrentVShape->AddEdge(newVEdge); // Find first edge: OWXEdge firstWEdge = iWEdge; /* OWXEdge previousWEdge = firstWEdge; */ /* UNUSED */ OWXEdge currentWEdge = firstWEdge; list edgesChain; #if 0 /* TK 02-Sep-2012 Experimental fix for incorrect view edge visibility. */ // bidirectional chaining // first direction: while (!stopSharpViewEdge(currentWEdge.e)) { edgesChain.push_back(currentWEdge); ++size; currentWEdge.e->userdata = (void *)1; // processed // Find the next edge! currentWEdge = FindNextWEdge(currentWEdge); } OWXEdge endWEdge = edgesChain.back(); // second direction currentWEdge = FindPreviousWEdge(firstWEdge); while (!stopSharpViewEdge(currentWEdge.e)) { edgesChain.push_front(currentWEdge); ++size; currentWEdge.e->userdata = (void *)1; // processed // Find the previous edge! currentWEdge = FindPreviousWEdge(currentWEdge); } #else edgesChain.push_back(currentWEdge); ++size; currentWEdge.e->userdata = (void *)1; // processed OWXEdge endWEdge = edgesChain.back(); #endif firstWEdge = edgesChain.front(); // build FEdges FEdge *feprevious = NULL; FEdge *fefirst = NULL; FEdge *fe = NULL; for (list::iterator we = edgesChain.begin(), weend = edgesChain.end(); we != weend; ++we) { fe = BuildSharpFEdge(feprevious, (*we)); fe->setViewEdge(newVEdge); if (!fefirst) fefirst = fe; feprevious = fe; } // Store the chain starting edge: _pCurrentSShape->AddChain(fefirst); newVEdge->setNature(iWEdge.e->nature()); newVEdge->setFEdgeA(fefirst); newVEdge->setFEdgeB(fe); // is it a closed loop ? if ((firstWEdge == endWEdge) && (size != 1)) { fefirst->setPreviousEdge(fe); fe->setNextEdge(fefirst); newVEdge->setA(0); newVEdge->setB(0); } else { ViewVertex *vva = MakeViewVertex(fefirst->vertexA()); ViewVertex *vvb = MakeViewVertex(fe->vertexB()); ((NonTVertex *)vva)->AddOutgoingViewEdge(newVEdge); ((NonTVertex *)vvb)->AddIncomingViewEdge(newVEdge); newVEdge->setA(vva); newVEdge->setB(vvb); } return newVEdge; } OWXFaceLayer ViewEdgeXBuilder::FindNextFaceLayer(const OWXFaceLayer& iFaceLayer) { WXFace *nextFace = NULL; WOEdge *woeend; real tend; if (iFaceLayer.order) { woeend = iFaceLayer.fl->getSmoothEdge()->woeb(); tend = iFaceLayer.fl->getSmoothEdge()->tb(); } else { woeend = iFaceLayer.fl->getSmoothEdge()->woea(); tend = iFaceLayer.fl->getSmoothEdge()->ta(); } // special case of EDGE_VERTEX config: if ((tend == 0.0) || (tend == 1.0)) { WVertex *nextVertex; if (tend == 0.0) nextVertex = woeend->GetaVertex(); else nextVertex = woeend->GetbVertex(); if (nextVertex->isBoundary()) // if it's a non-manifold vertex -> ignore return OWXFaceLayer(0, true); bool found = false; WVertex::face_iterator f = nextVertex->faces_begin(); WVertex::face_iterator fend = nextVertex->faces_end(); while ((!found) && (f != fend)) { nextFace = dynamic_cast(*f); if ((0 != nextFace) && (nextFace != iFaceLayer.fl->getFace())) { vector sameNatureLayers; nextFace->retrieveSmoothEdgesLayers(iFaceLayer.fl->nature(), sameNatureLayers); // don't know... Maybe should test whether this face has also a vertex_edge configuration. if (sameNatureLayers.size() == 1) { WXFaceLayer *winner = sameNatureLayers[0]; // check face mark continuity if (winner->getFace()->GetMark() != iFaceLayer.fl->getFace()->GetMark()) return OWXFaceLayer(NULL, true); if (woeend == winner->getSmoothEdge()->woea()->twin()) return OWXFaceLayer(winner, true); else return OWXFaceLayer(winner, false); } } ++f; } } else { nextFace = dynamic_cast(iFaceLayer.fl->getFace()->GetBordingFace(woeend)); if (!nextFace) return OWXFaceLayer(NULL, true); // if the next face layer has either no smooth edge or no smooth edge of same nature, no next face if (!nextFace->hasSmoothEdges()) return OWXFaceLayer(NULL, true); vector sameNatureLayers; nextFace->retrieveSmoothEdgesLayers(iFaceLayer.fl->nature(), sameNatureLayers); // don't know how to deal with several edges of same nature on a single face if ((sameNatureLayers.empty()) || (sameNatureLayers.size() != 1)) { return OWXFaceLayer(NULL, true); } else { WXFaceLayer *winner = sameNatureLayers[0]; // check face mark continuity if (winner->getFace()->GetMark() != iFaceLayer.fl->getFace()->GetMark()) return OWXFaceLayer(NULL, true); if (woeend == winner->getSmoothEdge()->woea()->twin()) return OWXFaceLayer(winner, true); else return OWXFaceLayer(winner, false); } } return OWXFaceLayer(NULL, true); } OWXFaceLayer ViewEdgeXBuilder::FindPreviousFaceLayer(const OWXFaceLayer& iFaceLayer) { WXFace *previousFace = NULL; WOEdge *woebegin; real tend; if (iFaceLayer.order) { woebegin = iFaceLayer.fl->getSmoothEdge()->woea(); tend = iFaceLayer.fl->getSmoothEdge()->ta(); } else { woebegin = iFaceLayer.fl->getSmoothEdge()->woeb(); tend = iFaceLayer.fl->getSmoothEdge()->tb(); } // special case of EDGE_VERTEX config: if ((tend == 0.0) || (tend == 1.0)) { WVertex *previousVertex; if (tend == 0.0) previousVertex = woebegin->GetaVertex(); else previousVertex = woebegin->GetbVertex(); if (previousVertex->isBoundary()) // if it's a non-manifold vertex -> ignore return OWXFaceLayer(NULL, true); bool found = false; WVertex::face_iterator f = previousVertex->faces_begin(); WVertex::face_iterator fend = previousVertex->faces_end(); for (; (!found) && (f != fend); ++f) { previousFace = dynamic_cast(*f); if ((0 != previousFace) && (previousFace != iFaceLayer.fl->getFace())) { vector sameNatureLayers; previousFace->retrieveSmoothEdgesLayers(iFaceLayer.fl->nature(), sameNatureLayers); // don't know... Maybe should test whether this face has also a vertex_edge configuration if (sameNatureLayers.size() == 1) { WXFaceLayer *winner = sameNatureLayers[0]; // check face mark continuity if (winner->getFace()->GetMark() != iFaceLayer.fl->getFace()->GetMark()) return OWXFaceLayer(NULL, true); if (woebegin == winner->getSmoothEdge()->woeb()->twin()) return OWXFaceLayer(winner, true); else return OWXFaceLayer(winner, false); } } } } else { previousFace = dynamic_cast(iFaceLayer.fl->getFace()->GetBordingFace(woebegin)); if (0 == previousFace) return OWXFaceLayer(NULL, true); // if the next face layer has either no smooth edge or no smooth edge of same nature, no next face if (!previousFace->hasSmoothEdges()) return OWXFaceLayer(NULL, true); vector sameNatureLayers; previousFace->retrieveSmoothEdgesLayers(iFaceLayer.fl->nature(), sameNatureLayers); // don't know how to deal with several edges of same nature on a single face if ((sameNatureLayers.empty()) || (sameNatureLayers.size() != 1)) { return OWXFaceLayer(NULL, true); } else { WXFaceLayer *winner = sameNatureLayers[0]; // check face mark continuity if (winner->getFace()->GetMark() != iFaceLayer.fl->getFace()->GetMark()) return OWXFaceLayer(NULL, true); if (woebegin == winner->getSmoothEdge()->woeb()->twin()) return OWXFaceLayer(winner, true); else return OWXFaceLayer(winner, false); } } return OWXFaceLayer(NULL, true); } FEdge *ViewEdgeXBuilder::BuildSmoothFEdge(FEdge *feprevious, const OWXFaceLayer& ifl) { WOEdge *woea, *woeb; real ta, tb; SVertex *va, *vb; FEdgeSmooth *fe; // retrieve exact silhouette data WXSmoothEdge *se = ifl.fl->getSmoothEdge(); if (ifl.order) { woea = se->woea(); woeb = se->woeb(); ta = se->ta(); tb = se->tb(); } else { woea = se->woeb(); woeb = se->woea(); ta = se->tb(); tb = se->ta(); } Vec3r normal; // Make the 2 Svertices if (feprevious == 0) { // that means that we don't have any vertex already built for that face Vec3r A1(woea->GetaVertex()->GetVertex()); Vec3r A2(woea->GetbVertex()->GetVertex()); Vec3r A(A1 + ta * (A2 - A1)); va = MakeSVertex(A, false); // Set normal: Vec3r NA1(ifl.fl->getFace()->GetVertexNormal(woea->GetaVertex())); Vec3r NA2(ifl.fl->getFace()->GetVertexNormal(woea->GetbVertex())); Vec3r na((1 - ta) * NA1 + ta * NA2); na.normalize(); va->AddNormal(na); normal = na; // Set CurvatureInfo CurvatureInfo *curvature_info_a = new CurvatureInfo(*(dynamic_cast(woea->GetaVertex())->curvatures()), *(dynamic_cast(woea->GetbVertex())->curvatures()), ta); va->setCurvatureInfo(curvature_info_a); } else { va = feprevious->vertexB(); } Vec3r B1(woeb->GetaVertex()->GetVertex()); Vec3r B2(woeb->GetbVertex()->GetVertex()); Vec3r B(B1 + tb * (B2 - B1)); if (feprevious && (B - va->point3D()).norm() < 1.0e-6) return feprevious; vb = MakeSVertex(B, false); // Set normal: Vec3r NB1(ifl.fl->getFace()->GetVertexNormal(woeb->GetaVertex())); Vec3r NB2(ifl.fl->getFace()->GetVertexNormal(woeb->GetbVertex())); Vec3r nb((1 - tb) * NB1 + tb * NB2); nb.normalize(); normal += nb; vb->AddNormal(nb); // Set CurvatureInfo CurvatureInfo *curvature_info_b = new CurvatureInfo(*(dynamic_cast(woeb->GetaVertex())->curvatures()), *(dynamic_cast(woeb->GetbVertex())->curvatures()), tb); vb->setCurvatureInfo(curvature_info_b); // Creates the corresponding feature edge fe = new FEdgeSmooth(va, vb); fe->setNature(ifl.fl->nature()); fe->setId(_currentFId); fe->setFrsMaterialIndex(ifl.fl->getFace()->frs_materialIndex()); fe->setFace(ifl.fl->getFace()); fe->setFaceMark(ifl.fl->getFace()->GetMark()); if (feprevious == 0) normal.normalize(); fe->setNormal(normal); fe->setPreviousEdge(feprevious); if (feprevious) feprevious->setNextEdge(fe); _pCurrentSShape->AddEdge(fe); va->AddFEdge(fe); vb->AddFEdge(fe); ++_currentFId; ifl.fl->userdata = fe; return fe; } bool ViewEdgeXBuilder::stopSmoothViewEdge(WXFaceLayer *iFaceLayer) { if (NULL == iFaceLayer) return true; if (iFaceLayer->userdata == 0) return false; return true; } int ViewEdgeXBuilder::retrieveFaceMarks(WXEdge *iEdge) { WFace *aFace = iEdge->GetaFace(); WFace *bFace = iEdge->GetbFace(); int result = 0; if (aFace && aFace->GetMark()) result += 1; if (bFace && bFace->GetMark()) result += 2; return result; } OWXEdge ViewEdgeXBuilder::FindNextWEdge(const OWXEdge& iEdge) { if (Nature::NO_FEATURE == iEdge.e->nature()) return OWXEdge(NULL, true); WVertex *v; if (true == iEdge.order) v = iEdge.e->GetbVertex(); else v = iEdge.e->GetaVertex(); if (((WXVertex *)v)->isFeature()) return 0; /* XXX eeek? NULL? OWXEdge(NULL, true/false)?*/ int faceMarks = retrieveFaceMarks(iEdge.e); vector& vEdges = (v)->GetEdges(); for (vector::iterator ve = vEdges.begin(), veend = vEdges.end(); ve != veend; ve++) { WXEdge *wxe = dynamic_cast(*ve); if (wxe == iEdge.e) continue; // same edge as the one processed if (wxe->nature() != iEdge.e->nature()) continue; // check face mark continuity if (retrieveFaceMarks(wxe) != faceMarks) continue; if (wxe->GetaVertex() == v) { // That means that the face necesarily lies on the edge left. // So the vertex order is OK. return OWXEdge(wxe, true); } else { // That means that the face necesarily lies on the edge left. // So the vertex order is OK. return OWXEdge(wxe, false); } } // we did not find: return OWXEdge(NULL, true); } OWXEdge ViewEdgeXBuilder::FindPreviousWEdge(const OWXEdge& iEdge) { if (Nature::NO_FEATURE == iEdge.e->nature()) return OWXEdge(NULL, true); WVertex *v; if (true == iEdge.order) v = iEdge.e->GetaVertex(); else v = iEdge.e->GetbVertex(); if (((WXVertex *)v)->isFeature()) return 0; int faceMarks = retrieveFaceMarks(iEdge.e); vector& vEdges = (v)->GetEdges(); for (vector::iterator ve = vEdges.begin(), veend = vEdges.end(); ve != veend; ve++) { WXEdge *wxe = dynamic_cast(*ve); if (wxe == iEdge.e) continue; // same edge as the one processed if (wxe->nature() != iEdge.e->nature()) continue; // check face mark continuity if (retrieveFaceMarks(wxe) != faceMarks) continue; if (wxe->GetbVertex() == v) { return OWXEdge(wxe, true); } else { return OWXEdge(wxe, false); } } // we did not find: return OWXEdge(NULL, true); } FEdge *ViewEdgeXBuilder::BuildSharpFEdge(FEdge *feprevious, const OWXEdge& iwe) { SVertex *va, *vb; FEdgeSharp *fe; Vec3r vA, vB; if (iwe.order) { vA = iwe.e->GetaVertex()->GetVertex(); vB = iwe.e->GetbVertex()->GetVertex(); } else { vA = iwe.e->GetbVertex()->GetVertex(); vB = iwe.e->GetaVertex()->GetVertex(); } // Make the 2 SVertex va = MakeSVertex(vA, true); vb = MakeSVertex(vB, true); // get the faces normals and the material indices Vec3r normalA, normalB; unsigned matA(0), matB(0); bool faceMarkA = false, faceMarkB = false; if (iwe.order) { normalB = (iwe.e->GetbFace()->GetNormal()); matB = (iwe.e->GetbFace()->frs_materialIndex()); faceMarkB = (iwe.e->GetbFace()->GetMark()); if (!(iwe.e->nature() & Nature::BORDER)) { normalA = (iwe.e->GetaFace()->GetNormal()); matA = (iwe.e->GetaFace()->frs_materialIndex()); faceMarkA = (iwe.e->GetaFace()->GetMark()); } } else { normalA = (iwe.e->GetbFace()->GetNormal()); matA = (iwe.e->GetbFace()->frs_materialIndex()); faceMarkA = (iwe.e->GetbFace()->GetMark()); if (!(iwe.e->nature() & Nature::BORDER)) { normalB = (iwe.e->GetaFace()->GetNormal()); matB = (iwe.e->GetaFace()->frs_materialIndex()); faceMarkB = (iwe.e->GetaFace()->GetMark()); } } // Creates the corresponding feature edge fe = new FEdgeSharp(va, vb); fe->setNature(iwe.e->nature()); fe->setId(_currentFId); fe->setaFrsMaterialIndex(matA); fe->setbFrsMaterialIndex(matB); fe->setaFaceMark(faceMarkA); fe->setbFaceMark(faceMarkB); fe->setNormalA(normalA); fe->setNormalB(normalB); fe->setPreviousEdge(feprevious); if (feprevious) feprevious->setNextEdge(fe); _pCurrentSShape->AddEdge(fe); va->AddFEdge(fe); vb->AddFEdge(fe); //Add normals: va->AddNormal(normalA); va->AddNormal(normalB); vb->AddNormal(normalA); vb->AddNormal(normalB); ++_currentFId; iwe.e->userdata = fe; return fe; } bool ViewEdgeXBuilder::stopSharpViewEdge(WXEdge *iEdge) { if (NULL == iEdge) return true; if (iEdge->userdata == 0) return false; return true; } SVertex *ViewEdgeXBuilder::MakeSVertex(Vec3r& iPoint) { SVertex *va = new SVertex(iPoint, _currentSVertexId); SilhouetteGeomEngine::ProjectSilhouette(va); ++_currentSVertexId; // Add the svertex to the SShape svertex list: _pCurrentSShape->AddNewVertex(va); return va; } SVertex *ViewEdgeXBuilder::MakeSVertex(Vec3r& iPoint, bool shared) { SVertex *va; if (!shared) { va = MakeSVertex(iPoint); } else { // Check whether the iPoint is already in the table SVertexMap::const_iterator found = _SVertexMap.find(iPoint); if (shared && found != _SVertexMap.end()) { va = (*found).second; } else { va = MakeSVertex(iPoint); // Add the svertex into the table using iPoint as the key _SVertexMap[iPoint] = va; } } return va; } ViewVertex *ViewEdgeXBuilder::MakeViewVertex(SVertex *iSVertex) { ViewVertex *vva = iSVertex->viewvertex(); if (vva) return vva; vva = new NonTVertex(iSVertex); // Add the view vertex to the ViewShape svertex list: _pCurrentVShape->AddVertex(vva); return vva; } } /* namespace Freestyle */