Blender V5.0
draw_view.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2022 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_math_geom.h"
10#include "BLI_math_matrix.h"
11#include "BLI_math_matrix.hh"
12
13#include "DRW_render.hh"
14#include "GPU_compute.hh"
15#include "GPU_debug.hh"
16
18#include "draw_shader.hh"
19#include "draw_view.hh"
20
21#include "draw_debug.hh"
22
23namespace blender::draw {
24
25std::atomic<uint32_t> View::global_sync_counter_ = 1;
26
27void View::sync(const float4x4 &view_mat, const float4x4 &win_mat, int view_id)
28{
29 data_[view_id].viewmat = view_mat;
30 data_[view_id].viewinv = math::invert(view_mat);
31 data_[view_id].winmat = win_mat;
32 data_[view_id].wininv = math::invert(win_mat);
33
34 is_inverted_ = (is_negative_m4(view_mat.ptr()) == is_negative_m4(win_mat.ptr()));
35
36 frustum_boundbox_calc(view_id);
39
40 dirty_ = true;
42 /* Add 2 to always have a non-null number even in case of overflow. */
43 sync_counter_ = (global_sync_counter_ += 2);
44}
45
47{
48 /* Extract the 8 corners from a Projection Matrix. */
49#if 0 /* Equivalent to this but it has accuracy problems. */
50 std::array<float3, 8> box = bounds::corners(Bounds<float3>(float3 (-1), float3 (1)));
51 for (int i = 0; i < 8; i++) {
52 mul_project_m4_v3(data_.wininv.ptr(), box[i]);
53 }
54#endif
55
56 MutableSpan<float4> corners = {culling_[view_id].frustum_corners.corners,
57 int64_t(ARRAY_SIZE(culling_[view_id].frustum_corners.corners))};
58
59 float left, right, bottom, top, near, far;
60 bool is_persp = data_[view_id].winmat[3][3] == 0.0f;
61
62 projmat_dimensions(data_[view_id].winmat.ptr(), &left, &right, &bottom, &top, &near, &far);
63
64 corners[0][2] = corners[3][2] = corners[7][2] = corners[4][2] = -near;
65 corners[0][0] = corners[3][0] = left;
66 corners[4][0] = corners[7][0] = right;
67 corners[0][1] = corners[4][1] = bottom;
68 corners[7][1] = corners[3][1] = top;
69
70 /* Get the coordinates of the far plane. */
71 if (is_persp) {
72 float sca_far = far / near;
73 left *= sca_far;
74 right *= sca_far;
75 bottom *= sca_far;
76 top *= sca_far;
77 }
78
79 corners[1][2] = corners[2][2] = corners[6][2] = corners[5][2] = -far;
80 corners[1][0] = corners[2][0] = left;
81 corners[6][0] = corners[5][0] = right;
82 corners[1][1] = corners[5][1] = bottom;
83 corners[2][1] = corners[6][1] = top;
84
85 const float4x4 &view_inv = data_[view_id].viewinv;
86 /* Transform into world space. */
87 for (float4 &corner : corners) {
88 corner = float4(math::transform_point(view_inv, float3(corner)), 1.0);
89 }
90}
91
93{
94 float4x4 persmat = data_[view_id].winmat * data_[view_id].viewmat;
96 culling_[view_id].frustum_planes.planes[0],
97 culling_[view_id].frustum_planes.planes[5],
98 culling_[view_id].frustum_planes.planes[1],
99 culling_[view_id].frustum_planes.planes[3],
100 culling_[view_id].frustum_planes.planes[4],
101 culling_[view_id].frustum_planes.planes[2]);
102 /* Normalize. */
103 for (float4 &plane : culling_[view_id].frustum_planes.planes) {
104 float len = math::length(plane.xyz());
105 if (len != 0.0f) {
106 plane /= len;
107 }
108 }
109}
110
112{
113 BoundSphere &bsphere = *reinterpret_cast<BoundSphere *>(&culling_[view_id].bound_sphere);
114 Span<float4> corners = {culling_[view_id].frustum_corners.corners,
115 int64_t(ARRAY_SIZE(culling_[view_id].frustum_corners.corners))};
116
117 /* Extract Bounding Sphere */
118 if (data_[view_id].winmat[3][3] != 0.0f) {
119 /* Orthographic */
120 /* The most extreme points on the near and far plane. (normalized device coords). */
121 const float *nearpoint = corners[0];
122 const float *farpoint = corners[6];
123
124 /* just use median point */
125 mid_v3_v3v3(bsphere.center, farpoint, nearpoint);
126 bsphere.radius = len_v3v3(bsphere.center, farpoint);
127 }
128 else if (data_[view_id].winmat[2][0] == 0.0f && data_[view_id].winmat[2][1] == 0.0f) {
129 /* Perspective with symmetrical frustum. */
130
131 /* We obtain the center and radius of the circumscribed circle of the
132 * isosceles trapezoid composed by the diagonals of the near and far clipping plane */
133
134 /* center of each clipping plane */
135 float mid_min[3], mid_max[3];
136 mid_v3_v3v3(mid_min, corners[3], corners[4]);
137 mid_v3_v3v3(mid_max, corners[2], corners[5]);
138
139 /* square length of the diagonals of each clipping plane */
140 float a_sq = len_squared_v3v3(corners[3], corners[4]);
141 float b_sq = len_squared_v3v3(corners[2], corners[5]);
142
143 /* distance squared between clipping planes */
144 float h_sq = len_squared_v3v3(mid_min, mid_max);
145
146 float fac = (4 * h_sq + b_sq - a_sq) / (8 * h_sq);
147
148 /* The goal is to get the smallest sphere,
149 * not the sphere that passes through each corner */
150 CLAMP(fac, 0.0f, 1.0f);
151
152 interp_v3_v3v3(bsphere.center, mid_min, mid_max, fac);
153
154 /* distance from the center to one of the points of the far plane (1, 2, 5, 6) */
155 bsphere.radius = len_v3v3(bsphere.center, corners[1]);
156 }
157 else {
158 /* Perspective with asymmetrical frustum. */
159
160 /* We put the sphere center on the line that goes from origin
161 * to the center of the far clipping plane. */
162
163 /* Detect which of the corner of the far clipping plane is the farthest to the origin */
164 float nfar[4]; /* most extreme far point in NDC space */
165 float farxy[2]; /* far-point projection onto the near plane */
166 float farpoint[3] = {0.0f}; /* most extreme far point in camera coordinate */
167 float nearpoint[3]; /* most extreme near point in camera coordinate */
168 float farcenter[3] = {0.0f}; /* center of far clipping plane in camera coordinate */
169 float F = -1.0f, N; /* square distance of far and near point to origin */
170 float f, n; /* distance of far and near point to z axis. f is always > 0 but n can be < 0 */
171 float e, s; /* far and near clipping distance (<0) */
172 float c; /* slope of center line = distance of far clipping center
173 * to z axis / far clipping distance. */
174 float z; /* projection of sphere center on z axis (<0) */
175
176 /* Find farthest corner and center of far clip plane. */
177 float corner[3] = {1.0f, 1.0f, 1.0f}; /* in clip space */
178 for (int i = 0; i < 4; i++) {
179 float point[3];
180 mul_v3_project_m4_v3(point, data_[view_id].wininv.ptr(), corner);
181 float len = len_squared_v3(point);
182 if (len > F) {
183 copy_v3_v3(nfar, corner);
184 copy_v3_v3(farpoint, point);
185 F = len;
186 }
187 add_v3_v3(farcenter, point);
188 /* rotate by 90 degree to walk through the 4 points of the far clip plane */
189 float tmp = corner[0];
190 corner[0] = -corner[1];
191 corner[1] = tmp;
192 }
193
194 /* the far center is the average of the far clipping points */
195 mul_v3_fl(farcenter, 0.25f);
196 /* the extreme near point is the opposite point on the near clipping plane */
197 copy_v3_fl3(nfar, -nfar[0], -nfar[1], -1.0f);
198 mul_v3_project_m4_v3(nearpoint, data_[view_id].wininv.ptr(), nfar);
199 /* this is a frustum projection */
200 N = len_squared_v3(nearpoint);
201 e = farpoint[2];
202 s = nearpoint[2];
203 /* distance to view Z axis */
204 f = len_v2(farpoint);
205 /* get corresponding point on the near plane */
206 mul_v2_v2fl(farxy, farpoint, s / e);
207 /* this formula preserve the sign of n */
208 sub_v2_v2(nearpoint, farxy);
209 n = f * s / e - len_v2(nearpoint);
210 c = len_v2(farcenter) / e;
211 /* the big formula, it simplifies to (F-N)/(2(e-s)) for the symmetric case */
212 z = (F - N) / (2.0f * (e - s + c * (f - n)));
213
214 bsphere.center[0] = farcenter[0] * z / e;
215 bsphere.center[1] = farcenter[1] * z / e;
216 bsphere.center[2] = z;
217
218 /* For XR, the view matrix may contain a scale factor. Then, transforming only the center
219 * into world space after calculating the radius will result in incorrect behavior. */
220 mul_m4_v3(data_[view_id].viewinv.ptr(), bsphere.center); /* Transform to world space. */
221 mul_m4_v3(data_[view_id].viewinv.ptr(), farpoint);
222 bsphere.radius = len_v3v3(bsphere.center, farpoint);
223 }
224}
225
227{
228 if (dirty_ && !procedural_) {
229 dirty_ = false;
230 data_.push_update();
231 culling_.push_update();
232 }
233
236}
237
239{
240 /* Sync happens on the GPU. This is called after each sync. */
242 /* Add 2 to always have a non-null number even in case of overflow. */
243 sync_counter_ = (global_sync_counter_ += 2);
244
245 GPU_debug_group_begin("View.compute_procedural_bounds");
246
248 GPU_shader_bind(shader);
251 GPU_compute_dispatch(shader, 1, 1, 1);
253
255}
256
258 ObjectInfosBuf & /*infos*/,
259 uint resource_len,
260 bool debug_freeze)
261{
262 if (debug_freeze && frozen_ == false) {
263 data_freeze_[0] = static_cast<ViewMatrices>(data_[0]);
264 data_freeze_.push_update();
265 culling_freeze_[0] = static_cast<ViewCullingData>(culling_[0]);
266 culling_freeze_.push_update();
267 }
268 if (debug_freeze) {
269 float4x4 persmat = data_freeze_[0].winmat * data_freeze_[0].viewmat;
271 }
272 frozen_ = debug_freeze;
273
274 GPU_debug_group_begin("View.compute_visibility");
275
276 /* TODO(fclem): Early out if visibility hasn't changed. */
277
278 uint word_per_draw = this->visibility_word_per_draw();
279 /* Switch between tightly packed and set of whole word per instance. */
280 uint words_len = (view_len_ == 1) ? divide_ceil_u(resource_len, 32) :
281 resource_len * word_per_draw;
282 words_len = ceil_to_multiple_u(max_ii(1, words_len), 4);
283 /* TODO(fclem): Resize to nearest pow2 to reduce fragmentation. */
284 visibility_buf_.resize(words_len);
285
286 const uint32_t data = 0xFFFFFFFFu;
288
289 if (do_visibility_) {
291 GPU_shader_bind(shader);
292 GPU_shader_uniform_1i(shader, "resource_len", resource_len);
293 GPU_shader_uniform_1i(shader, "view_len", view_len_);
294 GPU_shader_uniform_1i(shader, "visibility_word_per_draw", word_per_draw);
301 }
302
303 if (frozen_) {
304 /* Bind back the non frozen data. */
307 }
308
310}
311
316
321
322void View::default_set(const float4x4 &view_mat, const float4x4 &win_mat)
323{
324 drw_get().data->default_view->sync(view_mat, win_mat);
325}
326
327std::array<float4, 6> View::frustum_planes_get(int view_id)
328{
329 return {culling_[view_id].frustum_planes.planes[0],
330 culling_[view_id].frustum_planes.planes[1],
331 culling_[view_id].frustum_planes.planes[2],
332 culling_[view_id].frustum_planes.planes[3],
333 culling_[view_id].frustum_planes.planes[4],
334 culling_[view_id].frustum_planes.planes[5]};
335}
336
337std::array<float3, 8> View::frustum_corners_get(int view_id)
338{
339 return {culling_[view_id].frustum_corners.corners[0].xyz(),
340 culling_[view_id].frustum_corners.corners[1].xyz(),
341 culling_[view_id].frustum_corners.corners[2].xyz(),
342 culling_[view_id].frustum_corners.corners[3].xyz(),
343 culling_[view_id].frustum_corners.corners[4].xyz(),
344 culling_[view_id].frustum_corners.corners[5].xyz(),
345 culling_[view_id].frustum_corners.corners[6].xyz(),
346 culling_[view_id].frustum_corners.corners[7].xyz()};
347}
348
349} // namespace blender::draw
MINLINE uint ceil_to_multiple_u(uint a, uint b)
MINLINE uint divide_ceil_u(uint a, uint b)
MINLINE int max_ii(int a, int b)
void projmat_dimensions(const float winmat[4][4], float *r_left, float *r_right, float *r_bottom, float *r_top, float *r_near, float *r_far)
void planes_from_projmat(const float mat[4][4], float left[4], float right[4], float bottom[4], float top[4], float near[4], float far[4])
void mul_v3_project_m4_v3(float r[3], const float mat[4][4], const float vec[3])
void mul_project_m4_v3(const float mat[4][4], float vec[3])
void mul_m4_v3(const float M[4][4], float r[3])
bool is_negative_m4(const float mat[4][4])
MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v2_v2(float r[2], const float a[2])
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void copy_v3_fl3(float v[3], float x, float y, float z)
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v2_v2fl(float r[2], const float a[2], float f)
MINLINE void add_v3_v3(float r[3], const float a[3])
unsigned int uint
#define CLAMP(a, b, c)
#define ARRAY_SIZE(arr)
void GPU_compute_dispatch(blender::gpu::Shader *shader, uint groups_x_len, uint groups_y_len, uint groups_z_len, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_debug_group_end()
Definition gpu_debug.cc:33
void GPU_debug_group_begin(const char *name)
Definition gpu_debug.cc:22
void GPU_shader_bind(blender::gpu::Shader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
int GPU_shader_get_ssbo_binding(blender::gpu::Shader *shader, const char *name)
void GPU_shader_uniform_1i(blender::gpu::Shader *sh, const char *name, int value)
@ GPU_BARRIER_SHADER_STORAGE
Definition GPU_state.hh:48
@ GPU_BARRIER_UNIFORM
Definition GPU_state.hh:54
void GPU_memory_barrier(GPUBarrier barrier)
Definition gpu_state.cc:326
void GPU_storagebuf_clear(blender::gpu::StorageBuf *ssbo, uint32_t clear_value)
void GPU_storagebuf_bind(blender::gpu::StorageBuf *ssbo, int slot)
void GPU_uniformbuf_bind(blender::gpu::UniformBuf *ubo, int slot)
void GPU_uniformbuf_bind_as_ssbo(blender::gpu::UniformBuf *ubo, int slot)
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
long long int int64_t
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
UniformArrayBuffer< ViewCullingData, DRW_VIEW_MAX > culling_
Definition draw_view.hh:50
static void default_set(const float4x4 &view_mat, const float4x4 &win_mat)
Definition draw_view.cc:322
VisibilityBuf visibility_buf_
Definition draw_view.hh:55
bool is_persp(int view_id=0) const
Definition draw_view.hh:93
const float4x4 & winmat(int view_id=0) const
Definition draw_view.hh:148
const float4x4 persmat(int view_id=0) const
Definition draw_view.hh:161
void compute_procedural_bounds()
Definition draw_view.cc:238
UniformArrayBuffer< ViewMatrices, DRW_VIEW_MAX > data_freeze_
Definition draw_view.hh:52
virtual void compute_visibility(ObjectBoundsBuf &bounds, ObjectInfosBuf &infos, uint resource_len, bool debug_freeze)
Definition draw_view.cc:257
UniformArrayBuffer< ViewMatrices, DRW_VIEW_MAX > data_
Definition draw_view.hh:49
UniformArrayBuffer< ViewCullingData, DRW_VIEW_MAX > culling_freeze_
Definition draw_view.hh:53
static View & default_get()
Definition draw_view.cc:317
const float4x4 & viewinv(int view_id=0) const
Definition draw_view.hh:142
virtual VisibilityBuf & get_visibility_buffer()
Definition draw_view.cc:312
uint64_t manager_fingerprint_
Definition draw_view.hh:57
void frustum_culling_sphere_calc(int view_id)
Definition draw_view.cc:111
const float4x4 & wininv(int view_id=0) const
Definition draw_view.hh:154
std::array< float3, 8 > frustum_corners_get(int view_id=0)
Definition draw_view.cc:337
int visibility_word_per_draw() const
Definition draw_view.hh:167
std::array< float4, 6 > frustum_planes_get(int view_id=0)
Definition draw_view.cc:327
void frustum_boundbox_calc(int view_id)
Definition draw_view.cc:46
void sync(const float4x4 &view_mat, const float4x4 &win_mat, int view_id=0)
Definition draw_view.cc:27
void frustum_culling_planes_calc(int view_id)
Definition draw_view.cc:92
DRWContext & drw_get()
Simple API to draw debug shapes and log in the viewport.
#define DRW_VISIBILITY_GROUP_SIZE
#define DRW_VIEW_CULLING_UBO_SLOT
#define DRW_VIEW_UBO_SLOT
blender::gpu::Shader * DRW_shader_draw_view_finalize_get()
blender::gpu::Shader * DRW_shader_draw_visibility_compute_get()
uint top
static int left
#define N
#define F
std::array< VecBase< T, 3 >, 8 > corners(const Bounds< VecBase< T, 3 > > &bounds)
void drw_debug_matrix_as_bbox(const float4x4 &mat, const float4 color, const uint lifetime)
StorageArrayBuffer< ObjectBounds, 128 > ObjectBoundsBuf
Definition draw_view.hh:33
StorageArrayBuffer< ObjectInfos, 128 > ObjectInfosBuf
Definition draw_view.hh:34
StorageArrayBuffer< uint, 4, true > VisibilityBuf
Definition draw_view.hh:35
T length(const VecBase< T, Size > &a)
CartesianBasis invert(const CartesianBasis &basis)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< float, 3 > float3
float center[3]
Definition DRW_render.hh:66
DRWData * data
blender::draw::View * default_view
const c_style_mat & ptr() const
i
Definition text_draw.cc:230
uint len