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 ¢er, 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