/* This file is part of the VRender library. Copyright (C) 2005 Cyril Soler (Cyril.Soler@imag.fr) Version 1.0.0, released on June 27, 2005. http://artis.imag.fr/Members/Cyril.Soler/VRender VRender 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. VRender 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 VRender; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /**************************************************************************** Copyright (C) 2002-2006 Gilles Debunne (Gilles.Debunne@imag.fr) This file is part of the QGLViewer library. Version 2.2.4-1, released on December 12, 2006. http://artis.imag.fr/Members/Gilles.Debunne/QGLViewer libQGLViewer 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. libQGLViewer 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 libQGLViewer; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ #include #include "VRender.h" #include "Primitive.h" #include "PrimitivePositioning.h" #include "AxisAlignedBox.h" #include "SortMethod.h" #include "Vector2.h" using namespace std ; using namespace vrender ; // #define DEBUG_TS namespace vrender { class TopologicalSortUtils { public: static void buildPrecedenceGraph(vector& primitive_tab, vector< vector >& precedence_graph) ; static void recursFindNeighbors( const vector& primitive_tab, const vector& pindices, vector< vector >& precedence_graph, const AxisAlignedBox_xy&,int) ; static void checkAndAddEdgeToGraph(int a,int b,vector< vector >& precedence_graph) ; static void suppressPrecedence(int a,int b,vector< vector >& precedence_graph) ; static void recursTopologicalSort(vector< vector >& precedence_graph, vector& primitive_tab, vector& alread_rendered, vector& alread_visited, vector&,int,int&, VRenderParams& vparams, int info_cnt,int& nbrendered) ; static void recursTopologicalSort(vector< vector >& precedence_graph, vector& primitive_tab, vector& alread_rendered, vector& alread_visited, vector&,int, vector& ancestors, int&, int&, VRenderParams& vparams, int info_cnt,int& nbrendered) ; static void topologicalSort( vector< vector >& precedence_graph, vector& primitive_tab, VRenderParams&) ; static void topologicalSortBreakCycles(vector< vector >& precedence_graph, vector& primitive_tab, VRenderParams&) ; #ifdef DEBUG_TS static void printPrecedenceGraph(const vector< vector >& precedence_graph, const vector& primitive_tab) ; #endif }; TopologicalSortMethod::TopologicalSortMethod() { _break_cycles = false ; } void TopologicalSortMethod::sortPrimitives(vector& primitive_tab,VRenderParams& vparams) { // 1 - build a precedence graph #ifdef DEBUG_TS cout << "Computing precedence graph." << endl ; cout << "Old order: " ; for(unsigned int i=0;i > precedence_graph(primitive_tab.size()); TopologicalSortUtils::buildPrecedenceGraph(primitive_tab,precedence_graph) ; #ifdef DEBUG_TS TopologicalSortUtils::printPrecedenceGraph(precedence_graph,primitive_tab) ; #endif // 2 - perform a topological sorting of the graph #ifdef DEBUG_TS cout << "Sorting." << endl ; #endif if(_break_cycles) TopologicalSortUtils::topologicalSortBreakCycles(precedence_graph, primitive_tab,vparams) ; else TopologicalSortUtils::topologicalSort(precedence_graph, primitive_tab,vparams) ; #ifdef DEBUG_TS cout << "New order: " ; for(unsigned int i=0;i >& precedence_graph, const vector& primitive_tab) { for(unsigned int i=0;inbVertices() << ") : " ; for(unsigned int j=0;j& primitive_tab, vector< vector >& precedence_graph) { // The precedence graph is constructed by first conservatively determining which // primitives can possibly intersect using a quadtree. Candidate pairs of // primitives are then carefully checked to compute their exact relative positionning. // // Because of the conservativeness of the quadtree, some pairs of primitives may be checked // multiple times for intersection. Using a buffer of already computed results may proove // very efficient. // 0 - compute bounding box of the set of primitives. AxisAlignedBox_xy BBox ; for(unsigned int i=0;ibbox().mini().x(),primitive_tab[i]->bbox().mini().y())) ; BBox.include(Vector2(primitive_tab[i]->bbox().maxi().x(),primitive_tab[i]->bbox().maxi().y())) ; } // 1 - recursively find pairs. vector pindices(primitive_tab.size()) ; for(unsigned int j=0;j& primitive_tab, const vector& pindices, vector< vector >& precedence_graph, const AxisAlignedBox_xy& bbox, int depth) { static const unsigned int MAX_PRIMITIVES_IN_CELL = 5 ; // Refinment: first decide which sub-cell each primitive meets, then call // algorithm recursively. if(primitive_tab.size() > MAX_PRIMITIVES_IN_CELL) { vector p_indices_min_min ; vector p_indices_min_max ; vector p_indices_max_min ; vector p_indices_max_max ; double xmin = bbox.mini().x() ; double ymin = bbox.mini().y() ; double xmax = bbox.maxi().x() ; double ymax = bbox.maxi().y() ; double xMean = 0.5*(xmin+xmax) ; double yMean = 0.5*(ymin+ymax) ; for(unsigned int i=0;ibbox().mini().x() <= xMean ; bool right = primitive_tab[pindices[i]]->bbox().maxi().x() >= xMean ; bool down = primitive_tab[pindices[i]]->bbox().mini().y() <= yMean ; bool up = primitive_tab[pindices[i]]->bbox().maxi().y() >= yMean ; if(left && down) p_indices_min_min.push_back(pindices[i]) ; if(right && down) p_indices_max_min.push_back(pindices[i]) ; if(left && up ) p_indices_min_max.push_back(pindices[i]) ; if(right && up ) p_indices_max_max.push_back(pindices[i]) ; } // checks if refining is not too much stupid if(p_indices_min_min.size() < pindices.size() && p_indices_max_min.size() < pindices.size() && p_indices_min_max.size() < pindices.size() && p_indices_max_max.size() < pindices.size()) { recursFindNeighbors(primitive_tab,p_indices_min_min,precedence_graph,AxisAlignedBox_xy(Vector2(xmin,xMean),Vector2(ymin,yMean)),depth+1) ; recursFindNeighbors(primitive_tab,p_indices_min_max,precedence_graph,AxisAlignedBox_xy(Vector2(xmin,xMean),Vector2(yMean,ymax)),depth+1) ; recursFindNeighbors(primitive_tab,p_indices_max_min,precedence_graph,AxisAlignedBox_xy(Vector2(xMean,xmax),Vector2(ymin,yMean)),depth+1) ; recursFindNeighbors(primitive_tab,p_indices_max_max,precedence_graph,AxisAlignedBox_xy(Vector2(xMean,xmax),Vector2(yMean,ymax)),depth+1) ; return ; } } // No refinment either because it could not be possible, or because the number of primitives is below // the predefined limit. for(unsigned int i=0;i >& precedence_graph) { #ifdef DEBUG_TS cout << "Trying to add " << a << " -> " << b << " " ; #endif bool found = false ; for(unsigned int k=0;k >& precedence_graph) { vector prec_tab = vector(precedence_graph[a]) ; bool trouve = false ; for(unsigned int k=0;k >& precedence_graph, vector& primitive_tab, VRenderParams& vparams) { vector new_pr_tab ; vector already_visited(primitive_tab.size(),false) ; vector already_rendered(primitive_tab.size(),false) ; int nb_skews = 0 ; int info_cnt = primitive_tab.size()/200 + 1 ; int nbrendered = 0 ; // 1 - sorts primitives by rendering order for(unsigned int i=0;i 0) cout << nb_skews << " cycles found." << endl ; else cout << "No cycles found." << endl ; #endif primitive_tab = new_pr_tab ; } void TopologicalSortUtils::topologicalSortBreakCycles(vector< vector >& precedence_graph, vector& primitive_tab, VRenderParams& vparams) { vector new_pr_tab ; vector already_visited(primitive_tab.size(),false) ; vector already_rendered(primitive_tab.size(),false) ; vector ancestors ; int nb_skews = 0 ; int ancestors_backward_index ; int info_cnt = primitive_tab.size()/200 + 1 ; int nbrendered = 0 ; // 1 - sorts primitives by rendering order for(unsigned int i=0;i 0) cout << nb_skews << " cycles found." << endl ; else cout << "No cycles found." << endl ; #endif primitive_tab = new_pr_tab ; } void TopologicalSortUtils::recursTopologicalSort( vector< vector >& precedence_graph, vector& primitive_tab, vector& already_rendered, vector& already_visited, vector& new_pr_tab, int indx, int& nb_cycles, VRenderParams& vparams, int info_cnt,int& nbrendered) { // One must first render the primitives indicated by the precedence graph, // then render the current primitive. Skews are detected, but and treated. already_visited[indx] = true ; for(unsigned int j=0;j >& precedence_graph, vector& primitive_tab, vector& already_rendered, vector& already_visited, vector& new_pr_tab, int indx, vector& ancestors, int& ancestors_backward_index, int& nb_cycles, VRenderParams& vparams, int info_cnt,int& nbrendered) { // One must first render the primitives indicated by the precedence graph, // then render the current primitive. Skews are detected, but and treated. already_visited[indx] = true ; ancestors.push_back(indx) ; for(unsigned int j=0;j (unsigned int)(ancestors_backward_index+1)) { #ifdef DEBUG_TS cout << "Returning early" << endl ; #endif already_visited[indx] = false ; ancestors.pop_back() ; return; } if(ancestors_backward_index != INT_MAX) // we are returning from emergency. j must be re-tried --j ; } } else { // A cycle is detected. It must be broken. The algorithm is the following: primitives of the cycle // are successively split by a chosen splitting plane and the precendence graph is updated // at the same time by re-computing primitive precedence. As soon as the cycle is broken, // the algorithm stops and calls recursively calls on the new precedence graph. This necessarily // happens because of the BSP-node nature of the current set of primitives. // 0 - stats ++nb_cycles ; // 0.5 - determine cycle beginning int cycle_beginning_index = -1 ; for(int i=ancestors.size()-1; i >= 0 && cycle_beginning_index < 0;--i) if(ancestors[i] == precedence_graph[indx][j]) cycle_beginning_index = i ; #ifdef DEBUG_TS cout << "Unbreaking cycle : " ; for(unsigned int i=0;i= 0) ; #endif // 1 - determine splitting plane int split_prim_ancestor_indx = -1 ; int split_prim_indx = -1 ; // Go down ancestors tab, starting from the skewing primitive, and stopping at it. for(unsigned int i2=cycle_beginning_index;i2nbVertices() > 2) { split_prim_ancestor_indx = i2 ; split_prim_indx = ancestors[i2] ; } #ifdef DEBUG_TS cout << "Split primitive index = " << split_prim_ancestor_indx << "(primitive = " << split_prim_indx << ")" << endl ; #endif if(split_prim_indx < 0) // no need to unskew cycles between segments and points continue ; // 2 - split all necessary primitives const Polygone *P = dynamic_cast(primitive_tab[split_prim_indx]) ; const NVector3& normal = NVector3(P->normal()) ; double c(P->c()) ; ancestors.push_back(precedence_graph[indx][j]) ; // sentinel ancestors.push_back(ancestors[cycle_beginning_index+1]) ; // sentinel bool cycle_broken = false ; for(unsigned int i3=cycle_beginning_index+1;i3 prim_upper_prec ; vector prim_lower_prec ; vector old_prec = vector(precedence_graph[ancestors[i3]]) ; int upper_indx = precedence_graph.size() ; int lower_indx = ancestors[i3] ; // Updates the precedence graph downwards. for(unsigned int k=0;k " << endl ; #endif prim_upper_prec.push_back(old_prec[k]) ; if(old_prec[k] == ancestors[i3+1]) prim_upper_prec_contains_ip1 = true ; } #ifdef DEBUG_TS else cout << " I " << endl ; #endif int prp2 = PrimitivePositioning::computeRelativePosition(prim_lower,primitive_tab[old_prec[k]]) ; #ifdef DEBUG_TS cout << "Compariing " << lower_indx << " and " << old_prec[k] << ": " ; #endif if(prp2 & PrimitivePositioning::Lower) { #ifdef DEBUG_TS cout << " > " << endl ; #endif prim_lower_prec.push_back(old_prec[k]) ; if(old_prec[k] == ancestors[i3+1]) prim_lower_prec_contains_ip1 = true ; } #ifdef DEBUG_TS else cout << " I " << endl ; #endif } // We also have to update the primitives which are upper to the // current one, because some of them may not be upper anymore. // This would requires either a O(n^2) algorithm, or to store an // dual precedence graph. For now it's O(n^2). This test can not // be skipped because upper can still be lower to ancestors[i-1]. for(unsigned int l=0;l " << endl ; #endif if(l == (unsigned int)ancestors[i3-1]) // The index is the same => nothing to change. prim_lower_ante_contains_im1 = true ; } else { #ifdef DEBUG_TS cout << " I " << endl ; #endif // Not upper anymore. We have to suppress this entry from the tab. precedence_graph[l][k] = precedence_graph[l][precedence_graph[l].size()-1] ; precedence_graph[l].pop_back() ; --k ; } break ; // each entry is present only once. } // setup recorded new info primitive_tab.push_back(prim_upper) ; delete primitive_tab[lower_indx] ; primitive_tab[lower_indx] = prim_lower ; // Adds the info to the precedence graph precedence_graph.push_back(prim_upper_prec) ; precedence_graph[lower_indx] = prim_lower_prec ; // Adds new entries to the 'already_rendered' and 'already_visited' vectors already_visited.push_back(false) ; already_rendered.push_back(false) ; #ifdef DEBUG_TS cout << "New precedence graph: " << endl ; printPrecedenceGraph(precedence_graph,primitive_tab) ; #endif // Checks if the cycle is broken. Because the graph is only // updated downwards, we check wether lower (which still is // lower to ancestors[i-1]) is upper to ancestors[i+1], or // if upper is still . if(( !(prim_lower_ante_contains_im1 && prim_lower_prec_contains_ip1)) &&(!(prim_upper_ante_contains_im1 && prim_upper_prec_contains_ip1))) cycle_broken = true ; } ancestors.pop_back() ; ancestors.pop_back() ; // 3 - recurs call if(cycle_broken) { ancestors_backward_index = cycle_beginning_index ; #ifdef DEBUG_TS cout << "Cycle broken. Jumping back to rank " << ancestors_backward_index << endl ; #endif already_visited[indx] = false ; ancestors.pop_back() ; return; } #ifdef DEBUG_TS else cout << "Cycle could not be broken !!" << endl ; #endif } } if(!already_rendered[indx]) { #ifdef DEBUG_TS cout << "Returning ok. Rendered primitive " << indx << endl ; #endif new_pr_tab.push_back(primitive_tab[indx]) ; if((++nbrendered)%info_cnt==0) vparams.progress(nbrendered/(float)primitive_tab.size(),string("Advanced topological sort")) ; } already_rendered[indx] = true ; ancestors_backward_index = INT_MAX ; already_visited[indx] = false ; ancestors.pop_back() ; } } // namespace