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