1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
17 #pragma once
18 
19 /** \file
20  * \ingroup freestyle
21  * \brief Class to define a cell grid surrounding the projected image of a scene
22  */
23 
24 #define SPHERICAL_GRID_LOGGING 0
25 
26 // I would like to avoid using deque because including ViewMap.h and <deque> or <vector> separately
27 // results in redefinitions of identifiers. ViewMap.h already includes <vector> so it should be a
28 // safe fall-back.
29 //#include <vector>
30 //#include <deque>
31 
32 #include "GridDensityProvider.h"
33 #include "OccluderSource.h"
34 #include "ViewMap.h"
35 
36 #include "../geometry/BBox.h"
37 #include "../geometry/GridHelpers.h"
38 #include "../geometry/Polygon.h"
39 
40 #include "../system/PointerSequence.h"
41 
42 #include "../winged_edge/WEdge.h"
43 
44 #include "BKE_global.h"
45 
46 #ifdef WITH_CXX_GUARDEDALLOC
47 #  include "MEM_guardedalloc.h"
48 #endif
49 
50 namespace Freestyle {
51 
52 class SphericalGrid {
53  public:
54   // Helper classes
55   struct OccluderData {
56     explicit OccluderData(OccluderSource &source, Polygon3r &p);
57     Polygon3r poly;
58     Polygon3r cameraSpacePolygon;
59     real shallowest, deepest;
60     // N.B. We could, of course, store face in poly's userdata member, like the old ViewMapBuilder
61     // code does. However, code comments make it clear that userdata is deprecated, so we avoid the
62     // temptation to save 4 or 8 bytes.
63     WFace *face;
64 
65 #ifdef WITH_CXX_GUARDEDALLOC
66     MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:SphericalGrid:OccluderData")
67 #endif
68   };
69 
70  private:
71   struct Cell {
72     // Can't store Cell in a vector without copy and assign
73     // Cell(const Cell& other);
74     // Cell& operator=(const Cell& other);
75 
76     explicit Cell();
77     ~Cell();
78 
79     static bool compareOccludersByShallowestPoint(const OccluderData *a, const OccluderData *b);
80 
81     void setDimensions(real x, real y, real sizeX, real sizeY);
82     void checkAndInsert(OccluderSource &source, Polygon3r &poly, OccluderData *&occluder);
83     void indexPolygons();
84 
85     real boundary[4];
86     // deque<OccluderData*> faces;
87     vector<OccluderData *> faces;
88   };
89 
90  public:
91   /*! Iterator needs to allow the user to avoid full 3D comparison in two cases:
92    *
93    *  (1) Where (*current)->deepest < target[2], where the occluder is unambiguously in front of
94    * the target point.
95    *
96    *  (2) Where (*current)->shallowest > target[2], where the occluder is unambiguously in back of
97    * the target point.
98    *
99    *  In addition, when used by OptimizedFindOccludee, Iterator should stop iterating as soon as it
100    * has an occludee candidate and (*current)->shallowest > candidate[2], because at that point
101    * forward no new occluder could possibly be a better occludee.
102    */
103 
104   class Iterator {
105    public:
106     // epsilon is not used in this class, but other grids with the same interface may need an
107     // epsilon
108     explicit Iterator(SphericalGrid &grid, Vec3r &center, real epsilon = 1.0e-06);
109     ~Iterator();
110     void initBeforeTarget();
111     void initAfterTarget();
112     void nextOccluder();
113     void nextOccludee();
114     bool validBeforeTarget();
115     bool validAfterTarget();
116     WFace *getWFace() const;
117     Polygon3r *getCameraSpacePolygon();
118     void reportDepth(Vec3r origin, Vec3r u, real t);
119 
120    private:
121     bool testOccluder(bool wantOccludee);
122     void markCurrentOccludeeCandidate(real depth);
123 
124     Cell *_cell;
125     Vec3r _target;
126     bool _foundOccludee;
127     real _occludeeDepth;
128     // deque<OccluderData*>::iterator _current, _occludeeCandidate;
129     vector<OccluderData *>::iterator _current, _occludeeCandidate;
130 
131 #ifdef WITH_CXX_GUARDEDALLOC
132     MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:SphericalGrid:Iterator")
133 #endif
134   };
135 
136   class Transform : public GridHelpers::Transform {
137    public:
138     explicit Transform();
139     explicit Transform(Transform &other);
140     Vec3r operator()(const Vec3r &point) const;
141     static Vec3r sphericalProjection(const Vec3r &M);
142   };
143 
144  private:
145   // Prevent implicit copies and assignments.
146   SphericalGrid(const SphericalGrid &other);
147   SphericalGrid &operator=(const SphericalGrid &other);
148 
149  public:
150   explicit SphericalGrid(OccluderSource &source,
151                          GridDensityProvider &density,
152                          ViewMap *viewMap,
153                          Vec3r &viewpoint,
154                          bool enableQI);
155   virtual ~SphericalGrid();
156 
157   // Generate Cell structure
158   void assignCells(OccluderSource &source, GridDensityProvider &density, ViewMap *viewMap);
159   // Fill Cells
160   void distributePolygons(OccluderSource &source);
161   // Insert one polygon into each matching cell, return true if any cell consumes the polygon
162   bool insertOccluder(OccluderSource &source, OccluderData *&occluder);
163   // Sort occluders in each cell
164   void reorganizeCells();
165 
166   Cell *findCell(const Vec3r &point);
167 
168   // Accessors:
169   bool orthographicProjection() const;
170   const Vec3r &viewpoint() const;
171   bool enableQI() const;
172 
173  private:
174   void getCellCoordinates(const Vec3r &point, unsigned &x, unsigned &y);
175 
176   typedef PointerSequence<vector<Cell *>, Cell *> cellContainer;
177   // typedef PointerSequence<deque<OccluderData*>, OccluderData*> occluderContainer;
178   typedef PointerSequence<vector<OccluderData *>, OccluderData *> occluderContainer;
179   unsigned _cellsX, _cellsY;
180   float _cellSize;
181   float _cellOrigin[2];
182   cellContainer _cells;
183   occluderContainer _faces;
184   Vec3r _viewpoint;
185   bool _enableQI;
186 
187 #ifdef WITH_CXX_GUARDEDALLOC
188   MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:SphericalGrid")
189 #endif
190 };
191 
initBeforeTarget()192 inline void SphericalGrid::Iterator::initBeforeTarget()
193 {
194   _current = _cell->faces.begin();
195   while (_current != _cell->faces.end() && !testOccluder(false)) {
196     ++_current;
197   }
198 }
199 
initAfterTarget()200 inline void SphericalGrid::Iterator::initAfterTarget()
201 {
202   if (_foundOccludee) {
203 #if SPHERICAL_GRID_LOGGING
204     if (G.debug & G_DEBUG_FREESTYLE) {
205       std::cout << "\tStarting occludee search from occludeeCandidate at depth " << _occludeeDepth
206                 << std::endl;
207     }
208 #endif
209     _current = _occludeeCandidate;
210     return;
211   }
212 
213 #if SPHERICAL_GRID_LOGGING
214   if (G.debug & G_DEBUG_FREESTYLE) {
215     std::cout << "\tStarting occludee search from current position" << std::endl;
216   }
217 #endif
218 
219   while (_current != _cell->faces.end() && !testOccluder(true)) {
220     ++_current;
221   }
222 }
223 
testOccluder(bool wantOccludee)224 inline bool SphericalGrid::Iterator::testOccluder(bool wantOccludee)
225 {
226   // End-of-list is not even a valid iterator position
227   if (_current == _cell->faces.end()) {
228     // Returning true seems strange, but it will break us out of whatever loop is calling
229     // testOccluder, and _current=_cell->face.end() will make the calling routine give up.
230     return true;
231   }
232 #if SPHERICAL_GRID_LOGGING
233   if (G.debug & G_DEBUG_FREESTYLE) {
234     std::cout << "\tTesting occluder " << (*_current)->poly.getVertices()[0];
235     for (unsigned int i = 1; i < (*_current)->poly.getVertices().size(); ++i) {
236       std::cout << ", " << (*_current)->poly.getVertices()[i];
237     }
238     std::cout << " from shape " << (*_current)->face->GetVertex(0)->shape()->GetId() << std::endl;
239   }
240 #endif
241 
242   // If we have an occluder candidate and we are unambiguously after it, abort
243   if (_foundOccludee && (*_current)->shallowest > _occludeeDepth) {
244 #if SPHERICAL_GRID_LOGGING
245     if (G.debug & G_DEBUG_FREESTYLE) {
246       std::cout << "\t\tAborting: shallowest > occludeeCandidate->deepest" << std::endl;
247     }
248 #endif
249     _current = _cell->faces.end();
250 
251     // See note above
252     return true;
253   }
254 
255   // Specific continue or stop conditions when searching for each type
256   if (wantOccludee) {
257     if ((*_current)->deepest < _target[2]) {
258 #if SPHERICAL_GRID_LOGGING
259       if (G.debug & G_DEBUG_FREESTYLE) {
260         std::cout << "\t\tSkipping: shallower than target while looking for occludee" << std::endl;
261       }
262 #endif
263       return false;
264     }
265   }
266   else {
267     if ((*_current)->shallowest > _target[2]) {
268 #if SPHERICAL_GRID_LOGGING
269       if (G.debug & G_DEBUG_FREESTYLE) {
270         std::cout << "\t\tStopping: deeper than target while looking for occluder" << std::endl;
271       }
272 #endif
273       return true;
274     }
275   }
276 
277   // Depthwise, this is a valid occluder.
278 
279   // Check to see if target is in the 2D bounding box
280   Vec3r bbMin, bbMax;
281   (*_current)->poly.getBBox(bbMin, bbMax);
282   if (_target[0] < bbMin[0] || _target[0] > bbMax[0] || _target[1] < bbMin[1] ||
283       _target[1] > bbMax[1]) {
284 #if SPHERICAL_GRID_LOGGING
285     if (G.debug & G_DEBUG_FREESTYLE) {
286       std::cout << "\t\tSkipping: bounding box violation" << std::endl;
287     }
288 #endif
289     return false;
290   }
291 
292   // We've done all the corner cutting we can. Let the caller work out whether or not the geometry
293   // is correct.
294   return true;
295 }
296 
reportDepth(Vec3r origin,Vec3r u,real t)297 inline void SphericalGrid::Iterator::reportDepth(Vec3r origin, Vec3r u, real t)
298 {
299   // The reported depth is the length of a ray in camera space. We need to convert it into the
300   // distance from viewpoint If origin is the viewpoint, depth == t. A future optimization could
301   // allow the caller to tell us if origin is viewponit or target, at the cost of changing the
302   // OptimizedGrid API.
303   real depth = (origin + u * t).norm();
304 #if SPHERICAL_GRID_LOGGING
305   if (G.debug & G_DEBUG_FREESTYLE) {
306     std::cout << "\t\tReporting depth of occluder/ee: " << depth;
307   }
308 #endif
309   if (depth > _target[2]) {
310 #if SPHERICAL_GRID_LOGGING
311     if (G.debug & G_DEBUG_FREESTYLE) {
312       std::cout << " is deeper than target" << std::endl;
313     }
314 #endif
315     // If the current occluder is the best occludee so far, save it.
316     if (!_foundOccludee || _occludeeDepth > depth) {
317       markCurrentOccludeeCandidate(depth);
318     }
319   }
320   else {
321 #if SPHERICAL_GRID_LOGGING
322     if (G.debug & G_DEBUG_FREESTYLE) {
323       std::cout << std::endl;
324     }
325 #endif
326   }
327 }
328 
nextOccluder()329 inline void SphericalGrid::Iterator::nextOccluder()
330 {
331   if (_current != _cell->faces.end()) {
332     do {
333       ++_current;
334     } while (_current != _cell->faces.end() && !testOccluder(false));
335   }
336 }
337 
nextOccludee()338 inline void SphericalGrid::Iterator::nextOccludee()
339 {
340   if (_current != _cell->faces.end()) {
341     do {
342       ++_current;
343     } while (_current != _cell->faces.end() && !testOccluder(true));
344   }
345 }
346 
validBeforeTarget()347 inline bool SphericalGrid::Iterator::validBeforeTarget()
348 {
349   return _current != _cell->faces.end() && (*_current)->shallowest <= _target[2];
350 }
351 
validAfterTarget()352 inline bool SphericalGrid::Iterator::validAfterTarget()
353 {
354   return _current != _cell->faces.end();
355 }
356 
markCurrentOccludeeCandidate(real depth)357 inline void SphericalGrid::Iterator::markCurrentOccludeeCandidate(real depth)
358 {
359 #if SPHERICAL_GRID_LOGGING
360   if (G.debug & G_DEBUG_FREESTYLE) {
361     std::cout << "\t\tFound occludeeCandidate at depth " << depth << std::endl;
362   }
363 #endif
364   _occludeeCandidate = _current;
365   _occludeeDepth = depth;
366   _foundOccludee = true;
367 }
368 
getWFace()369 inline WFace *SphericalGrid::Iterator::getWFace() const
370 {
371   return (*_current)->face;
372 }
373 
getCameraSpacePolygon()374 inline Polygon3r *SphericalGrid::Iterator::getCameraSpacePolygon()
375 {
376   return &((*_current)->cameraSpacePolygon);
377 }
378 
OccluderData(OccluderSource & source,Polygon3r & p)379 inline SphericalGrid::OccluderData::OccluderData(OccluderSource &source, Polygon3r &p)
380     : poly(p), cameraSpacePolygon(source.getCameraSpacePolygon()), face(source.getWFace())
381 {
382   const Vec3r viewpoint(0, 0, 0);
383   // Get the point on the camera-space polygon that is closest to the viewpoint
384   // shallowest is the distance from the viewpoint to that point
385   shallowest = GridHelpers::distancePointToPolygon(viewpoint, cameraSpacePolygon);
386 
387   // Get the point on the camera-space polygon that is furthest from the viewpoint
388   // deepest is the distance from the viewpoint to that point
389   deepest = cameraSpacePolygon.getVertices()[2].norm();
390   for (unsigned int i = 0; i < 2; ++i) {
391     real t = cameraSpacePolygon.getVertices()[i].norm();
392     if (t > deepest) {
393       deepest = t;
394     }
395   }
396 }
397 
checkAndInsert(OccluderSource & source,Polygon3r & poly,OccluderData * & occluder)398 inline void SphericalGrid::Cell::checkAndInsert(OccluderSource &source,
399                                                 Polygon3r &poly,
400                                                 OccluderData *&occluder)
401 {
402   if (GridHelpers::insideProscenium(boundary, poly)) {
403     if (occluder == NULL) {
404       // Disposal of occluder will be handled in SphericalGrid::distributePolygons(),
405       // or automatically by SphericalGrid::_faces;
406       occluder = new OccluderData(source, poly);
407     }
408     faces.push_back(occluder);
409   }
410 }
411 
insertOccluder(OccluderSource & source,OccluderData * & occluder)412 inline bool SphericalGrid::insertOccluder(OccluderSource &source, OccluderData *&occluder)
413 {
414   Polygon3r &poly(source.getGridSpacePolygon());
415   occluder = NULL;
416 
417   Vec3r bbMin, bbMax;
418   poly.getBBox(bbMin, bbMax);
419   // Check overlapping cells
420   unsigned startX, startY, endX, endY;
421   getCellCoordinates(bbMin, startX, startY);
422   getCellCoordinates(bbMax, endX, endY);
423 
424   for (unsigned int i = startX; i <= endX; ++i) {
425     for (unsigned int j = startY; j <= endY; ++j) {
426       if (_cells[i * _cellsY + j] != NULL) {
427         _cells[i * _cellsY + j]->checkAndInsert(source, poly, occluder);
428       }
429     }
430   }
431 
432   return occluder != NULL;
433 }
434 
435 } /* namespace Freestyle */
436