Blender V4.3
SphericalGrid.h
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#pragma once
6
12#define SPHERICAL_GRID_LOGGING 0
13
14// I would like to avoid using deque because including ViewMap.h and <deque> or <vector> separately
15// results in redefinitions of identifiers. ViewMap.h already includes <vector> so it should be a
16// safe fall-back.
17// #include <vector>
18// #include <deque>
19
20#include "GridDensityProvider.h"
21#include "OccluderSource.h"
22#include "ViewMap.h"
23
24#include "../geometry/BBox.h"
26#include "../geometry/Polygon.h"
27
29
31
32#include "BKE_global.hh"
33
34#ifdef WITH_CXX_GUARDEDALLOC
35# include "MEM_guardedalloc.h"
36#endif
37
38namespace Freestyle {
39
41 public:
42 // Helper classes
43 struct OccluderData {
44 explicit OccluderData(OccluderSource &source, Polygon3r &p);
48 // N.B. We could, of course, store face in poly's userdata member, like the old ViewMapBuilder
49 // code does. However, code comments make it clear that userdata is deprecated, so we avoid the
50 // temptation to save 4 or 8 bytes.
52
53#ifdef WITH_CXX_GUARDEDALLOC
54 MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:SphericalGrid:OccluderData")
55#endif
56 };
57
58 private:
59 struct Cell {
60 // Can't store Cell in a vector without copy and assign
61 // Cell(const Cell& other);
62 // Cell& operator=(const Cell& other);
63
64 explicit Cell() = default;
65
66 static bool compareOccludersByShallowestPoint(const OccluderData *a, const OccluderData *b);
67
68 void setDimensions(real x, real y, real sizeX, real sizeY);
69 void checkAndInsert(OccluderSource &source, Polygon3r &poly, OccluderData *&occluder);
70 void indexPolygons();
71
72 real boundary[4];
73 // deque<OccluderData*> faces;
74 vector<OccluderData *> faces;
75 };
76
77 public:
92 class Iterator {
93 public:
94 // epsilon is not used in this class, but other grids with the same interface may need an
95 // epsilon
96 explicit Iterator(SphericalGrid &grid, Vec3r &center, real epsilon = 1.0e-06);
97 void initBeforeTarget();
98 void initAfterTarget();
99 void nextOccluder();
100 void nextOccludee();
101 bool validBeforeTarget();
102 bool validAfterTarget();
103 WFace *getWFace() const;
105 void reportDepth(Vec3r origin, Vec3r u, real t);
106
107 private:
108 bool testOccluder(bool wantOccludee);
109 void markCurrentOccludeeCandidate(real depth);
110
111 Cell *_cell;
112 Vec3r _target;
113 bool _foundOccludee;
114 real _occludeeDepth;
115 // deque<OccluderData*>::iterator _current, _occludeeCandidate;
116 vector<OccluderData *>::iterator _current, _occludeeCandidate;
117
118#ifdef WITH_CXX_GUARDEDALLOC
119 MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:SphericalGrid:Iterator")
120#endif
121 };
122
124 public:
125 explicit Transform() = default;
126 explicit Transform(Transform &other);
127 Vec3r operator()(const Vec3r &point) const;
128 static Vec3r sphericalProjection(const Vec3r &M);
129 };
130
131 private:
132 // Prevent implicit copies and assignments.
133 SphericalGrid(const SphericalGrid &other);
134 SphericalGrid &operator=(const SphericalGrid &other);
135
136 public:
137 explicit SphericalGrid(OccluderSource &source,
138 GridDensityProvider &density,
139 ViewMap *viewMap,
141 bool enableQI);
142 virtual ~SphericalGrid();
143
144 // Generate Cell structure
145 void assignCells(OccluderSource &source, GridDensityProvider &density, ViewMap *viewMap);
146 // Fill Cells
148 // Insert one polygon into each matching cell, return true if any cell consumes the polygon
149 bool insertOccluder(OccluderSource &source, OccluderData *&occluder);
150 // Sort occluders in each cell
151 void reorganizeCells();
152
153 Cell *findCell(const Vec3r &point);
154
155 // Accessors:
156 bool orthographicProjection() const;
157 const Vec3r &viewpoint() const;
158 bool enableQI() const;
159
160 private:
161 void getCellCoordinates(const Vec3r &point, uint &x, uint &y);
162
164 // typedef PointerSequence<deque<OccluderData*>, OccluderData*> occluderContainer;
166 uint _cellsX, _cellsY;
167 float _cellSize;
168 float _cellOrigin[2];
169 cellContainer _cells;
170 occluderContainer _faces;
171 Vec3r _viewpoint;
172 bool _enableQI;
173
174#ifdef WITH_CXX_GUARDEDALLOC
175 MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:SphericalGrid")
176#endif
177};
178
180{
181 _current = _cell->faces.begin();
182 while (_current != _cell->faces.end() && !testOccluder(false)) {
183 ++_current;
184 }
185}
186
188{
189 if (_foundOccludee) {
190#if SPHERICAL_GRID_LOGGING
191 if (G.debug & G_DEBUG_FREESTYLE) {
192 std::cout << "\tStarting occludee search from occludeeCandidate at depth " << _occludeeDepth
193 << std::endl;
194 }
195#endif
196 _current = _occludeeCandidate;
197 return;
198 }
199
200#if SPHERICAL_GRID_LOGGING
201 if (G.debug & G_DEBUG_FREESTYLE) {
202 std::cout << "\tStarting occludee search from current position" << std::endl;
203 }
204#endif
205
206 while (_current != _cell->faces.end() && !testOccluder(true)) {
207 ++_current;
208 }
209}
210
211inline bool SphericalGrid::Iterator::testOccluder(bool wantOccludee)
212{
213 // End-of-list is not even a valid iterator position
214 if (_current == _cell->faces.end()) {
215 // Returning true seems strange, but it will break us out of whatever loop is calling
216 // testOccluder, and _current=_cell->face.end() will make the calling routine give up.
217 return true;
218 }
219#if SPHERICAL_GRID_LOGGING
220 if (G.debug & G_DEBUG_FREESTYLE) {
221 std::cout << "\tTesting occluder " << (*_current)->poly.getVertices()[0];
222 for (uint i = 1; i < (*_current)->poly.getVertices().size(); ++i) {
223 std::cout << ", " << (*_current)->poly.getVertices()[i];
224 }
225 std::cout << " from shape " << (*_current)->face->GetVertex(0)->shape()->GetId() << std::endl;
226 }
227#endif
228
229 // If we have an occluder candidate and we are unambiguously after it, abort
230 if (_foundOccludee && (*_current)->shallowest > _occludeeDepth) {
231#if SPHERICAL_GRID_LOGGING
232 if (G.debug & G_DEBUG_FREESTYLE) {
233 std::cout << "\t\tAborting: shallowest > occludeeCandidate->deepest" << std::endl;
234 }
235#endif
236 _current = _cell->faces.end();
237
238 // See note above
239 return true;
240 }
241
242 // Specific continue or stop conditions when searching for each type
243 if (wantOccludee) {
244 if ((*_current)->deepest < _target[2]) {
245#if SPHERICAL_GRID_LOGGING
246 if (G.debug & G_DEBUG_FREESTYLE) {
247 std::cout << "\t\tSkipping: shallower than target while looking for occludee" << std::endl;
248 }
249#endif
250 return false;
251 }
252 }
253 else {
254 if ((*_current)->shallowest > _target[2]) {
255#if SPHERICAL_GRID_LOGGING
256 if (G.debug & G_DEBUG_FREESTYLE) {
257 std::cout << "\t\tStopping: deeper than target while looking for occluder" << std::endl;
258 }
259#endif
260 return true;
261 }
262 }
263
264 // Depthwise, this is a valid occluder.
265
266 // Check to see if target is in the 2D bounding box
267 Vec3r bbMin, bbMax;
268 (*_current)->poly.getBBox(bbMin, bbMax);
269 if (_target[0] < bbMin[0] || _target[0] > bbMax[0] || _target[1] < bbMin[1] ||
270 _target[1] > bbMax[1])
271 {
272#if SPHERICAL_GRID_LOGGING
273 if (G.debug & G_DEBUG_FREESTYLE) {
274 std::cout << "\t\tSkipping: bounding box violation" << std::endl;
275 }
276#endif
277 return false;
278 }
279
280 // We've done all the corner cutting we can. Let the caller work out whether or not the geometry
281 // is correct.
282 return true;
283}
284
286{
287 // The reported depth is the length of a ray in camera space. We need to convert it into the
288 // distance from viewpoint If origin is the viewpoint, depth == t. A future optimization could
289 // allow the caller to tell us if origin is viewponit or target, at the cost of changing the
290 // OptimizedGrid API.
291 real depth = (origin + u * t).norm();
292#if SPHERICAL_GRID_LOGGING
293 if (G.debug & G_DEBUG_FREESTYLE) {
294 std::cout << "\t\tReporting depth of occluder/ee: " << depth;
295 }
296#endif
297 if (depth > _target[2]) {
298#if SPHERICAL_GRID_LOGGING
299 if (G.debug & G_DEBUG_FREESTYLE) {
300 std::cout << " is deeper than target" << std::endl;
301 }
302#endif
303 // If the current occluder is the best occludee so far, save it.
304 if (!_foundOccludee || _occludeeDepth > depth) {
305 markCurrentOccludeeCandidate(depth);
306 }
307 }
308 else {
309#if SPHERICAL_GRID_LOGGING
310 if (G.debug & G_DEBUG_FREESTYLE) {
311 std::cout << std::endl;
312 }
313#endif
314 }
315}
316
318{
319 if (_current != _cell->faces.end()) {
320 do {
321 ++_current;
322 } while (_current != _cell->faces.end() && !testOccluder(false));
323 }
324}
325
327{
328 if (_current != _cell->faces.end()) {
329 do {
330 ++_current;
331 } while (_current != _cell->faces.end() && !testOccluder(true));
332 }
333}
334
336{
337 return _current != _cell->faces.end() && (*_current)->shallowest <= _target[2];
338}
339
341{
342 return _current != _cell->faces.end();
343}
344
345inline void SphericalGrid::Iterator::markCurrentOccludeeCandidate(real depth)
346{
347#if SPHERICAL_GRID_LOGGING
348 if (G.debug & G_DEBUG_FREESTYLE) {
349 std::cout << "\t\tFound occludeeCandidate at depth " << depth << std::endl;
350 }
351#endif
352 _occludeeCandidate = _current;
353 _occludeeDepth = depth;
354 _foundOccludee = true;
355}
356
358{
359 return (*_current)->face;
360}
361
363{
364 return &((*_current)->cameraSpacePolygon);
365}
366
368 : poly(p), cameraSpacePolygon(source.getCameraSpacePolygon()), face(source.getWFace())
369{
370 const Vec3r viewpoint(0, 0, 0);
371 // Get the point on the camera-space polygon that is closest to the viewpoint
372 // shallowest is the distance from the viewpoint to that point
374
375 // Get the point on the camera-space polygon that is furthest from the viewpoint
376 // deepest is the distance from the viewpoint to that point
378 for (uint i = 0; i < 2; ++i) {
379 real t = cameraSpacePolygon.getVertices()[i].norm();
380 if (t > deepest) {
381 deepest = t;
382 }
383 }
384}
385
386inline void SphericalGrid::Cell::checkAndInsert(OccluderSource &source,
387 Polygon3r &poly,
388 OccluderData *&occluder)
389{
390 if (GridHelpers::insideProscenium(boundary, poly)) {
391 if (occluder == nullptr) {
392 // Disposal of occluder will be handled in SphericalGrid::distributePolygons(),
393 // or automatically by SphericalGrid::_faces;
394 occluder = new OccluderData(source, poly);
395 }
396 faces.push_back(occluder);
397 }
398}
399
401{
402 Polygon3r &poly(source.getGridSpacePolygon());
403 occluder = nullptr;
404
405 Vec3r bbMin, bbMax;
406 poly.getBBox(bbMin, bbMax);
407 // Check overlapping cells
408 uint startX, startY, endX, endY;
409 getCellCoordinates(bbMin, startX, startY);
410 getCellCoordinates(bbMax, endX, endY);
411
412 for (uint i = startX; i <= endX; ++i) {
413 for (uint j = startY; j <= endY; ++j) {
414 if (_cells[i * _cellsY + j] != nullptr) {
415 _cells[i * _cellsY + j]->checkAndInsert(source, poly, occluder);
416 }
417 }
418 }
419
420 return occluder != nullptr;
421}
422
423} /* namespace Freestyle */
A class to hold a bounding box.
@ G_DEBUG_FREESTYLE
unsigned int uint
Class to define a cell grid surrounding the projected image of a scene.
Class to define a cell grid surrounding the projected image of a scene.
Read Guarded memory(de)allocation.
Class to define a cell grid surrounding the projected image of a scene.
Simple RAII wrappers for std:: sequential containers.
Class to define a polygon.
Classes to define a View Map (ViewVertex, ViewEdge, etc.)
Classes to define a Winged Edge data structure.
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE btScalar norm() const
Return the norm (length) of the vector.
Definition btVector3.h:263
const vector< Point > & getVertices() const
Definition Polygon.h:68
void reportDepth(Vec3r origin, Vec3r u, real t)
Iterator(SphericalGrid &grid, Vec3r &center, real epsilon=1.0e-06)
static Vec3r sphericalProjection(const Vec3r &M)
Vec3r operator()(const Vec3r &point) const
void assignCells(OccluderSource &source, GridDensityProvider &density, ViewMap *viewMap)
const Vec3r & viewpoint() const
bool orthographicProjection() const
Cell * findCell(const Vec3r &point)
void distributePolygons(OccluderSource &source)
bool insertOccluder(OccluderSource &source, OccluderData *&occluder)
local_group_size(16, 16) .push_constant(Type b
#define M
#define G(x, y, z)
VecMat::Vec3< real > Vec3r
Definition Geom.h:30
real distancePointToPolygon(const Vec3r &point, const Polygon3r &poly)
Definition GridHelpers.h:83
bool insideProscenium(const real proscenium[4], const Polygon3r &polygon)
inherits from class Rep
Definition AppCanvas.cpp:20
double real
Definition Precision.h:14
OccluderData(OccluderSource &source, Polygon3r &p)