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
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
162 // typedef PointerSequence<deque<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.
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
local_group_size(16, 16) .push_constant(Type b
#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
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