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