Blender V4.3
CulledOccluderSource.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11
13
14#include "BLI_sys_types.h"
15
16#include "BKE_global.hh"
17
18namespace Freestyle {
19
20CulledOccluderSource::CulledOccluderSource(const GridHelpers::Transform &t,
21 WingedEdge &we,
22 ViewMap &viewMap,
23 bool extensiveFEdgeSearch)
24 : OccluderSource(t, we), rejected(0), gridSpaceOccluderProsceniumInitialized(false)
25{
26 cullViewEdges(viewMap, extensiveFEdgeSearch);
27
28 // If we have not found any visible FEdges during our cull, then there is nothing to iterate
29 // over. Short-circuit everything.
30 valid = gridSpaceOccluderProsceniumInitialized;
31
32 if (valid && !testCurrent()) {
33 next();
34 }
35}
36
37bool CulledOccluderSource::testCurrent()
38{
39 if (valid) {
40 // The test for gridSpaceOccluderProsceniumInitialized should not be necessary
41 return gridSpaceOccluderProsceniumInitialized &&
42 GridHelpers::insideProscenium(gridSpaceOccluderProscenium, cachedPolygon);
43 }
44 return false;
45}
46
48{
49 while (OccluderSource::next()) {
50 if (testCurrent()) {
51 ++rejected;
52 return true;
53 }
54 }
55 if (G.debug & G_DEBUG_FREESTYLE) {
56 std::cout << "Finished generating occluders. Rejected " << rejected << " faces." << std::endl;
57 }
58 return false;
59}
60
62{
63 for (uint i = 0; i < 4; ++i) {
64 proscenium[i] = gridSpaceOccluderProscenium[i];
65 }
66}
67
68static inline real distance2D(const Vec3r &point, const real origin[2])
69{
70 return ::hypot((point[0] - origin[0]), (point[1] - origin[1]));
71}
72
73static inline bool crossesProscenium(real proscenium[4], FEdge *fe)
74{
75 Vec2r min(proscenium[0], proscenium[2]);
76 Vec2r max(proscenium[1], proscenium[3]);
79
80 return GeomUtils::intersect2dSeg2dArea(min, max, A, B);
81}
82
83static inline bool insideProscenium(const real proscenium[4], const Vec3r &point)
84{
85 return !(point[0] < proscenium[0] || point[0] > proscenium[1] || point[1] < proscenium[2] ||
86 point[1] > proscenium[3]);
87}
88
89void CulledOccluderSource::cullViewEdges(ViewMap &viewMap, bool extensiveFEdgeSearch)
90{
91 // Cull view edges by marking them as non-displayable.
92 // This avoids the complications of trying to delete edges from the ViewMap.
93
94 // Non-displayable view edges will be skipped over during visibility calculation.
95
96 // View edges will be culled according to their position w.r.t. the viewport proscenium (viewport
97 // + 5% border, or some such).
98
99 // Get proscenium boundary for culling
100 real viewProscenium[4];
102 real prosceniumOrigin[2];
103 prosceniumOrigin[0] = (viewProscenium[1] - viewProscenium[0]) / 2.0;
104 prosceniumOrigin[1] = (viewProscenium[3] - viewProscenium[2]) / 2.0;
105 if (G.debug & G_DEBUG_FREESTYLE) {
106 cout << "Proscenium culling:" << endl;
107 cout << "Proscenium: [" << viewProscenium[0] << ", " << viewProscenium[1] << ", "
108 << viewProscenium[2] << ", " << viewProscenium[3] << "]" << endl;
109 cout << "Origin: [" << prosceniumOrigin[0] << ", " << prosceniumOrigin[1] << "]" << endl;
110 }
111
112 // A separate occluder proscenium will also be maintained, starting out the same as the viewport
113 // proscenium, and expanding as necessary so that it encompasses the center point of at least one
114 // feature edge in each retained view edge. The occluder proscenium will be used later to cull
115 // occluding triangles before they are inserted into the Grid. The occluder proscenium starts out
116 // the same size as the view proscenium
117 GridHelpers::getDefaultViewProscenium(occluderProscenium);
118
119 // XXX Freestyle is inconsistent in its use of ViewMap::viewedges_container and
120 // vector<ViewEdge*>::iterator. Probably all occurrences of vector<ViewEdge*>::iterator should be
121 // replaced ViewMap::viewedges_container throughout the code. For each view edge
122 ViewMap::viewedges_container::iterator ve, veend;
123
124 for (ve = viewMap.ViewEdges().begin(), veend = viewMap.ViewEdges().end(); ve != veend; ve++) {
125 // Overview:
126 // Search for a visible feature edge
127 // If none: mark view edge as non-displayable
128 // Otherwise:
129 // Find a feature edge with center point inside occluder proscenium.
130 // If none exists, find the feature edge with center point closest to viewport origin.
131 // Expand occluder proscenium to enclose center point.
132
133 // For each feature edge, while bestOccluderTarget not found and view edge not visible
134 bool bestOccluderTargetFound = false;
135 FEdge *bestOccluderTarget = nullptr;
136 real bestOccluderDistance = 0.0;
137 FEdge *festart = (*ve)->fedgeA();
138 FEdge *fe = festart;
139 // All ViewEdges start culled
140 (*ve)->setIsInImage(false);
141
142 // For simple visibility calculation: mark a feature edge that is known to have a center point
143 // inside the occluder proscenium. Cull all other feature edges.
144 do {
145 // All FEdges start culled
146 fe->setIsInImage(false);
147
148 // Look for the visible edge that can most easily be included in the occluder proscenium.
149 if (!bestOccluderTargetFound) {
150 // If center point is inside occluder proscenium,
151 if (insideProscenium(occluderProscenium, fe->center2d())) {
152 // Use this feature edge for visibility deterimination
153 fe->setIsInImage(true);
154 expandGridSpaceOccluderProscenium(fe);
155 // Mark bestOccluderTarget as found
156 bestOccluderTargetFound = true;
157 bestOccluderTarget = fe;
158 }
159 else {
160 real d = distance2D(fe->center2d(), prosceniumOrigin);
161 // If center point is closer to viewport origin than current target
162 if (bestOccluderTarget == nullptr || d < bestOccluderDistance) {
163 // Then store as bestOccluderTarget
164 bestOccluderDistance = d;
165 bestOccluderTarget = fe;
166 }
167 }
168 }
169
170 // If feature edge crosses the view proscenium
171 if (!(*ve)->isInImage() && crossesProscenium(viewProscenium, fe)) {
172 // Then the view edge will be included in the image
173 (*ve)->setIsInImage(true);
174 }
175 fe = fe->nextEdge();
176 } while (fe != nullptr && fe != festart && !(bestOccluderTargetFound && (*ve)->isInImage()));
177
178 // Either we have run out of FEdges, or we already have the one edge we need to determine
179 // visibility Cull all remaining edges.
180 while (!ELEM(fe, nullptr, festart)) {
181 fe->setIsInImage(false);
182 fe = fe->nextEdge();
183 }
184
185 // If bestOccluderTarget was not found inside the occluder proscenium,
186 // we need to expand the occluder proscenium to include it.
187 if ((*ve)->isInImage() && bestOccluderTarget != nullptr && !bestOccluderTargetFound) {
188 // Expand occluder proscenium to enclose bestOccluderTarget
189 Vec3r point = bestOccluderTarget->center2d();
190 if (point[0] < occluderProscenium[0]) {
191 occluderProscenium[0] = point[0];
192 }
193 else if (point[0] > occluderProscenium[1]) {
194 occluderProscenium[1] = point[0];
195 }
196 if (point[1] < occluderProscenium[2]) {
197 occluderProscenium[2] = point[1];
198 }
199 else if (point[1] > occluderProscenium[3]) {
200 occluderProscenium[3] = point[1];
201 }
202 // Use bestOccluderTarget for visibility determination
203 bestOccluderTarget->setIsInImage(true);
204 }
205 }
206
207 // We are done calculating the occluder proscenium.
208 // Expand the occluder proscenium by an epsilon to avoid rounding errors.
209 const real epsilon = 1.0e-6;
210 occluderProscenium[0] -= epsilon;
211 occluderProscenium[1] += epsilon;
212 occluderProscenium[2] -= epsilon;
213 occluderProscenium[3] += epsilon;
214
215 // For "Normal" or "Fast" style visibility computation only:
216
217 // For more detailed visibility calculation, make a second pass through the view map, marking all
218 // feature edges with center points inside the final occluder proscenium. All of these feature
219 // edges can be considered during visibility calculation.
220
221 // So far we have only found one FEdge per ViewEdge. The "Normal" and "Fast" styles of visibility
222 // computation want to consider many FEdges for each ViewEdge. Here we re-scan the view map to
223 // find any usable FEdges that we skipped on the first pass, or that have become usable because
224 // the occluder proscenium has been expanded since the edge was visited on the first pass.
225 if (extensiveFEdgeSearch) {
226 // For each view edge,
227 for (ve = viewMap.ViewEdges().begin(), veend = viewMap.ViewEdges().end(); ve != veend; ve++) {
228 if (!(*ve)->isInImage()) {
229 continue;
230 }
231 // For each feature edge,
232 FEdge *festart = (*ve)->fedgeA();
233 FEdge *fe = festart;
234 do {
235 // If not (already) visible and center point inside occluder proscenium,
236 if (!fe->isInImage() && insideProscenium(occluderProscenium, fe->center2d())) {
237 // Use the feature edge for visibility determination
238 fe->setIsInImage(true);
239 expandGridSpaceOccluderProscenium(fe);
240 }
241 fe = fe->nextEdge();
242 } while (!ELEM(fe, nullptr, festart));
243 }
244 }
245
246 // Up until now, all calculations have been done in camera space.
247 // However, the occluder source's iteration and the grid that consumes the occluders both work in
248 // gridspace, so we need a version of the occluder proscenium in gridspace. Set the gridspace
249 // occlude proscenium
250}
251
252void CulledOccluderSource::expandGridSpaceOccluderProscenium(FEdge *fe)
253{
254 if (gridSpaceOccluderProsceniumInitialized) {
255 GridHelpers::expandProscenium(gridSpaceOccluderProscenium, transform(fe->center3d()));
256 }
257 else {
258 const Vec3r &point = transform(fe->center3d());
259 gridSpaceOccluderProscenium[0] = gridSpaceOccluderProscenium[1] = point[0];
260 gridSpaceOccluderProscenium[2] = gridSpaceOccluderProscenium[3] = point[1];
261 gridSpaceOccluderProsceniumInitialized = true;
262 }
263}
264
265} /* namespace Freestyle */
@ G_DEBUG_FREESTYLE
unsigned int uint
#define ELEM(...)
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.
#define A
void getOccluderProscenium(real proscenium[4])
void cullViewEdges(ViewMap &viewMap, bool extensiveFEdgeSearch)
FEdge * nextEdge()
Definition Silhouette.h:623
SVertex * vertexA()
Definition Silhouette.h:597
bool isInImage() const
Definition Silhouette.h:711
SVertex * vertexB()
Definition Silhouette.h:603
void setIsInImage(bool iFlag)
Definition Silhouette.h:811
const GridHelpers::Transform & transform
virtual real getProjectedY() const
Definition Silhouette.h:98
virtual real getProjectedX() const
Definition Silhouette.h:92
viewedges_container & ViewEdges()
Definition ViewMap.h:104
#define B
#define G(x, y, z)
bool intersect2dSeg2dArea(const Vec2r &min, const Vec2r &max, const Vec2r &A, const Vec2r &B)
Definition GeomUtils.cpp:19
VecMat::Vec3< real > Vec3r
Definition Geom.h:30
void getDefaultViewProscenium(real viewProscenium[4])
void expandProscenium(real proscenium[4], const Polygon3r &polygon)
bool insideProscenium(const real proscenium[4], const Polygon3r &polygon)
inherits from class Rep
Definition AppCanvas.cpp:20
static real distance2D(const Vec3r &point, const real origin[2])
static bool insideProscenium(const real proscenium[4], const Vec3r &point)
static bool crossesProscenium(real proscenium[4], FEdge *fe)
double real
Definition Precision.h:14
#define min(a, b)
Definition sort.c:32
float max