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
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#ifdef WITH_CXX_GUARDEDALLOC
35# include "MEM_guardedalloc.h"
36#endif
37
38namespace Freestyle {
39
40class SphericalGrid {
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:
91
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
163 typedef PointerSequence<vector<Cell *>, Cell *> cellContainer;
164 // typedef PointerSequence<deque<OccluderData*>, OccluderData*> occluderContainer;
165 typedef PointerSequence<vector<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
377 deepest = cameraSpacePolygon.getVertices()[2].norm();
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.
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
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:73
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
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:83
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)