Blender V4.3
BoxGrid.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 BOX_GRID_LOGGING 0
13
14// I would like to avoid using deque because including ViewMap.h and <deque> or <vector>
15// separately results in redefinitions of identifiers. ViewMap.h already includes <vector>
16// so it should be a 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 BoxGrid {
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:BoxGrid: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:
78 /* Iterator needs to allow the user to avoid full 3D comparison in two cases:
79 *
80 * (1) Where (*current)->deepest < target[2], where the occluder is unambiguously in front of the
81 * target point.
82 *
83 * (2) Where (*current)->shallowest > target[2], where the occluder is unambiguously in back of
84 * the target point.
85 *
86 * In addition, when used by OptimizedFindOccludee, Iterator should stop iterating as soon as it
87 * has an occludee candidate and (*current)->shallowest > candidate[2], because at that point
88 * forward no new occluder could possibly be a better occludee.
89 */
90 class Iterator {
91 public:
92 // epsilon is not used in this class, but other grids with the same interface may need an
93 // epsilon
94 explicit Iterator(BoxGrid &grid, Vec3r &center, real epsilon = 1.0e-06);
95 void initBeforeTarget();
96 void initAfterTarget();
97 void nextOccluder();
98 void nextOccludee();
99 bool validBeforeTarget();
100 bool validAfterTarget();
101 WFace *getWFace() const;
103 void reportDepth(Vec3r origin, Vec3r u, real t);
104
105 private:
106 bool testOccluder(bool wantOccludee);
107 void markCurrentOccludeeCandidate(real depth);
108
109 Cell *_cell;
110 Vec3r _target;
111 bool _foundOccludee;
112 real _occludeeDepth;
113 // deque<OccluderData*>::iterator _current, _occludeeCandidate;
114 vector<OccluderData *>::iterator _current, _occludeeCandidate;
115
116#ifdef WITH_CXX_GUARDEDALLOC
117 MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BoxGrid:Iterator")
118#endif
119 };
120
122 public:
123 explicit Transform() = default;
124 explicit Transform(Transform &other);
125 Vec3r operator()(const Vec3r &point) const;
126 };
127
128 private:
129 // Prevent implicit copies and assignments.
130 BoxGrid(const BoxGrid &other);
131 BoxGrid &operator=(const BoxGrid &other);
132
133 public:
134 explicit BoxGrid(OccluderSource &source,
135 GridDensityProvider &density,
136 ViewMap *viewMap,
138 bool enableQI);
139 virtual ~BoxGrid();
140
141 // Generate Cell structure
142 void assignCells(OccluderSource &source, GridDensityProvider &density, ViewMap *viewMap);
143 // Fill Cells
145 // Insert one polygon into each matching cell, return true if any cell consumes the polygon
146 bool insertOccluder(OccluderSource &source, OccluderData *&occluder);
147 // Sort occluders in each cell
148 void reorganizeCells();
149
150 Cell *findCell(const Vec3r &point);
151
152 // Accessors:
153 bool orthographicProjection() const;
154 const Vec3r &viewpoint() const;
155 bool enableQI() const;
157
158 private:
159 void getCellCoordinates(const Vec3r &point, uint &x, uint &y);
160
161 typedef PointerSequence<vector<Cell *>, Cell *> cellContainer;
162 // typedef PointerSequence<deque<OccluderData*>, OccluderData*> occluderContainer;
163 typedef PointerSequence<vector<OccluderData *>, OccluderData *> occluderContainer;
164 uint _cellsX, _cellsY;
165 float _cellSize;
166 float _cellOrigin[2];
167 cellContainer _cells;
168 occluderContainer _faces;
169 Vec3r _viewpoint;
170 bool _enableQI;
171
172#ifdef WITH_CXX_GUARDEDALLOC
173 MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BoxGrid")
174#endif
175};
176
178{
179 _current = _cell->faces.begin();
180 while (_current != _cell->faces.end() && !testOccluder(false)) {
181 ++_current;
182 }
183}
184
186{
187 if (_foundOccludee) {
188#if BOX_GRID_LOGGING
189 if (G.debug & G_DEBUG_FREESTYLE) {
190 std::cout << "\tStarting occludee search from occludeeCandidate at depth " << _occludeeDepth
191 << std::endl;
192 }
193#endif
194 _current = _occludeeCandidate;
195 return;
196 }
197
198#if BOX_GRID_LOGGING
199 if (G.debug & G_DEBUG_FREESTYLE) {
200 std::cout << "\tStarting occludee search from current position" << std::endl;
201 }
202#endif
203
204 while (_current != _cell->faces.end() && !testOccluder(true)) {
205 ++_current;
206 }
207}
208
209inline bool BoxGrid::Iterator::testOccluder(bool wantOccludee)
210{
211 // End-of-list is not even a valid iterator position
212 if (_current == _cell->faces.end()) {
213 // Returning true seems strange, but it will break us out of whatever loop is calling
214 // testOccluder, and _current = _cell->face.end() will make the calling routine give up.
215 return true;
216 }
217#if BOX_GRID_LOGGING
218 if (G.debug & G_DEBUG_FREESTYLE) {
219 std::cout << "\tTesting occluder " << (*_current)->poly.getVertices()[0];
220 for (uint i = 1; i < (*_current)->poly.getVertices().size(); ++i) {
221 std::cout << ", " << (*_current)->poly.getVertices()[i];
222 }
223 std::cout << " from shape " << (*_current)->face->GetVertex(0)->shape()->GetId() << std::endl;
224 }
225#endif
226
227 // If we have an occluder candidate and we are unambiguously after it, abort
228 if (_foundOccludee && (*_current)->shallowest > _occludeeDepth) {
229#if BOX_GRID_LOGGING
230 if (G.debug & G_DEBUG_FREESTYLE) {
231 std::cout << "\t\tAborting: shallowest > occludeeCandidate->deepest" << std::endl;
232 }
233#endif
234 _current = _cell->faces.end();
235
236 // See note above
237 return true;
238 }
239
240 // Specific continue or stop conditions when searching for each type
241 if (wantOccludee) {
242 if ((*_current)->deepest < _target[2]) {
243#if BOX_GRID_LOGGING
244 if (G.debug & G_DEBUG_FREESTYLE) {
245 std::cout << "\t\tSkipping: shallower than target while looking for occludee" << std::endl;
246 }
247#endif
248 return false;
249 }
250 }
251 else {
252 if ((*_current)->shallowest > _target[2]) {
253#if BOX_GRID_LOGGING
254 if (G.debug & G_DEBUG_FREESTYLE) {
255 std::cout << "\t\tStopping: deeper than target while looking for occluder" << std::endl;
256 }
257#endif
258 return true;
259 }
260 }
261
262 // Depthwise, this is a valid occluder.
263
264 // Check to see if target is in the 2D bounding box
265 Vec3r bbMin, bbMax;
266 (*_current)->poly.getBBox(bbMin, bbMax);
267 if (_target[0] < bbMin[0] || _target[0] > bbMax[0] || _target[1] < bbMin[1] ||
268 _target[1] > bbMax[1])
269 {
270#if BOX_GRID_LOGGING
271 if (G.debug & G_DEBUG_FREESTYLE) {
272 std::cout << "\t\tSkipping: bounding box violation" << std::endl;
273 }
274#endif
275 return false;
276 }
277
278 // We've done all the corner cutting we can.
279 // Let the caller work out whether or not the geometry is correct.
280 return true;
281}
282
284{
285 // The reported depth is the length of a ray in camera space
286 // We need to convert it into a Z-value in grid space
287 real depth = -(origin + (u * t))[2];
288#if BOX_GRID_LOGGING
289 if (G.debug & G_DEBUG_FREESTYLE) {
290 std::cout << "\t\tReporting depth of occluder/ee: " << depth;
291 }
292#endif
293 if (depth > _target[2]) {
294#if BOX_GRID_LOGGING
295 if (G.debug & G_DEBUG_FREESTYLE) {
296 std::cout << " is deeper than target" << std::endl;
297 }
298#endif
299 // If the current occluder is the best occludee so far, save it.
300 if (!_foundOccludee || _occludeeDepth > depth) {
301 markCurrentOccludeeCandidate(depth);
302 }
303 }
304 else {
305#if BOX_GRID_LOGGING
306 if (G.debug & G_DEBUG_FREESTYLE) {
307 std::cout << std::endl;
308 }
309#endif
310 }
311}
312
314{
315 if (_current != _cell->faces.end()) {
316 do {
317 ++_current;
318 } while (_current != _cell->faces.end() && !testOccluder(false));
319 }
320}
321
323{
324 if (_current != _cell->faces.end()) {
325 do {
326 ++_current;
327 } while (_current != _cell->faces.end() && !testOccluder(true));
328 }
329}
330
332{
333 return _current != _cell->faces.end() && (*_current)->shallowest <= _target[2];
334}
335
337{
338 return _current != _cell->faces.end();
339}
340
341inline void BoxGrid::Iterator::markCurrentOccludeeCandidate(real depth)
342{
343#if BOX_GRID_LOGGING
344 if (G.debug & G_DEBUG_FREESTYLE) {
345 std::cout << "\t\tFound occludeeCandidate at depth " << depth << std::endl;
346 }
347#endif
348 _occludeeCandidate = _current;
349 _occludeeDepth = depth;
350 _foundOccludee = true;
351}
352
354{
355 return (*_current)->face;
356}
357
359{
360 return &((*_current)->cameraSpacePolygon);
361}
362
364 : poly(p), cameraSpacePolygon(source.getCameraSpacePolygon()), face(source.getWFace())
365{
366 // Set shallowest and deepest based on bbox
367 Vec3r min, max;
368 poly.getBBox(min, max);
369 shallowest = min[2];
370 deepest = max[2];
371}
372
373inline void BoxGrid::Cell::checkAndInsert(OccluderSource &source,
374 Polygon3r &poly,
375 OccluderData *&occluder)
376{
377 if (GridHelpers::insideProscenium(boundary, poly)) {
378 if (occluder == nullptr) {
379 // Disposal of occluder will be handled in BoxGrid::distributePolygons(),
380 // or automatically by BoxGrid::_faces;
381 occluder = new OccluderData(source, poly);
382 }
383 faces.push_back(occluder);
384 }
385}
386
388{
389 Polygon3r &poly(source.getGridSpacePolygon());
390 occluder = nullptr;
391
392 Vec3r bbMin, bbMax;
393 poly.getBBox(bbMin, bbMax);
394 // Check overlapping cells
395 uint startX, startY, endX, endY;
396 getCellCoordinates(bbMin, startX, startY);
397 getCellCoordinates(bbMax, endX, endY);
398
399 for (uint i = startX; i <= endX; ++i) {
400 for (uint j = startY; j <= endY; ++j) {
401 if (_cells[i * _cellsY + j] != nullptr) {
402 _cells[i * _cellsY + j]->checkAndInsert(source, poly, occluder);
403 }
404 }
405 }
406
407 return occluder != nullptr;
408}
409
410} /* 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
Polygon3r * getCameraSpacePolygon()
Definition BoxGrid.h:358
Iterator(BoxGrid &grid, Vec3r &center, real epsilon=1.0e-06)
Definition BoxGrid.cpp:55
void reportDepth(Vec3r origin, Vec3r u, real t)
Definition BoxGrid.h:283
WFace * getWFace() const
Definition BoxGrid.h:353
Vec3r operator()(const Vec3r &point) const
Definition BoxGrid.cpp:221
Transform(Transform &other)
Transform transform
Definition BoxGrid.h:156
void assignCells(OccluderSource &source, GridDensityProvider &density, ViewMap *viewMap)
Definition BoxGrid.cpp:107
const Vec3r & viewpoint() const
Definition BoxGrid.cpp:211
Cell * findCell(const Vec3r &point)
Definition BoxGrid.cpp:199
bool enableQI() const
Definition BoxGrid.cpp:216
void distributePolygons(OccluderSource &source)
Definition BoxGrid.cpp:154
bool insertOccluder(OccluderSource &source, OccluderData *&occluder)
Definition BoxGrid.h:387
bool orthographicProjection() const
Definition BoxGrid.cpp:206
void getBBox(Point &min, Point &max) const
Definition Polygon.h:73
local_group_size(16, 16) .push_constant(Type b
static char faces[256]
#define G(x, y, z)
VecMat::Vec3< real > Vec3r
Definition Geom.h:30
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
#define min(a, b)
Definition sort.c:32
OccluderData(OccluderSource &source, Polygon3r &p)
Definition BoxGrid.h:363
float max