Blender V4.3
draw_cache_impl_volume.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2017 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include <cstring>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_listbase.h"
16#include "BLI_math_base.h"
17#include "BLI_math_matrix.hh"
18#include "BLI_math_vector.h"
19#include "BLI_utildefines.h"
20
21#include "DNA_object_types.h"
22#include "DNA_volume_types.h"
23
24#include "BKE_global.hh"
25#include "BKE_volume.hh"
27#include "BKE_volume_render.hh"
28
29#include "GPU_batch.hh"
30#include "GPU_capabilities.hh"
31#include "GPU_texture.hh"
32
34
35#include "DRW_render.hh"
36
37#include "draw_cache.hh" /* own include */
38#include "draw_cache_impl.hh" /* own include */
39
40namespace blender::draw {
41
42static void volume_batch_cache_clear(Volume *volume);
43
44/* ---------------------------------------------------------------------- */
45/* Volume gpu::Batch Cache */
46
48 /* 3D textures */
50
51 /* Wireframe */
52 struct {
54 gpu::Batch *batch;
56
57 /* Surface for selection */
58 gpu::Batch *selection_surface;
59
60 /* settings to determine if cache is invalid */
62};
63
64/* gpu::Batch cache management. */
65
66static bool volume_batch_cache_valid(Volume *volume)
67{
68 VolumeBatchCache *cache = static_cast<VolumeBatchCache *>(volume->batch_cache);
69 return (cache && cache->is_dirty == false);
70}
71
72static void volume_batch_cache_init(Volume *volume)
73{
74 VolumeBatchCache *cache = static_cast<VolumeBatchCache *>(volume->batch_cache);
75
76 if (!cache) {
77 volume->batch_cache = cache = MEM_cnew<VolumeBatchCache>(__func__);
78 }
79 else {
80 memset(cache, 0, sizeof(*cache));
81 }
82
83 cache->is_dirty = false;
84}
85
87{
88 if (!volume_batch_cache_valid(volume)) {
91 }
92}
93
95{
97 return static_cast<VolumeBatchCache *>(volume->batch_cache);
98}
99
101{
102 VolumeBatchCache *cache = static_cast<VolumeBatchCache *>(volume->batch_cache);
103 if (cache == nullptr) {
104 return;
105 }
106 switch (mode) {
108 cache->is_dirty = true;
109 break;
110 default:
111 BLI_assert(0);
112 }
113}
114
116{
117 VolumeBatchCache *cache = static_cast<VolumeBatchCache *>(volume->batch_cache);
118 if (!cache) {
119 return;
120 }
121
122 LISTBASE_FOREACH (DRWVolumeGrid *, grid, &cache->grids) {
123 MEM_SAFE_FREE(grid->name);
124 DRW_TEXTURE_FREE_SAFE(grid->texture);
125 }
126 BLI_freelistN(&cache->grids);
127
131}
132
134{
136 MEM_SAFE_FREE(volume->batch_cache);
137}
142
144 void *userdata, const float (*verts)[3], const int (*edges)[2], int totvert, int totedge)
145{
146 VolumeWireframeUserData *data = static_cast<VolumeWireframeUserData *>(userdata);
147 Scene *scene = data->scene;
148 Volume *volume = data->volume;
149 VolumeBatchCache *cache = static_cast<VolumeBatchCache *>(volume->batch_cache);
150 const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 ||
152
153 /* Create vertex buffer. */
154 static GPUVertFormat format = {0};
155 static GPUVertFormat format_hq = {0};
156 static struct {
157 uint pos_id, nor_id;
158 uint pos_hq_id, nor_hq_id;
159 } attr_id;
160
161 if (format.attr_len == 0) {
165 attr_id.pos_id = GPU_vertformat_attr_add(&format_hq, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
167 &format_hq, "nor", GPU_COMP_I16, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
168 }
169
170 static float normal[3] = {1.0f, 0.0f, 0.0f};
171 GPUNormal packed_normal;
172 GPU_normal_convert_v3(&packed_normal, normal, do_hq_normals);
173 uint pos_id = do_hq_normals ? attr_id.pos_hq_id : attr_id.pos_id;
174 uint nor_id = do_hq_normals ? attr_id.nor_hq_id : attr_id.nor_id;
175
176 cache->face_wire.pos_nor_in_order = GPU_vertbuf_create_with_format(do_hq_normals ? format_hq :
177 format);
180 GPU_vertbuf_attr_fill_stride(cache->face_wire.pos_nor_in_order, nor_id, 0, &packed_normal);
181
182 /* Create wiredata. */
183 gpu::VertBuf *vbo_wiredata = GPU_vertbuf_calloc();
184 DRW_vertbuf_create_wiredata(vbo_wiredata, totvert);
185
186 if (volume->display.wireframe_type == VOLUME_WIREFRAME_POINTS) {
187 /* Create batch. */
190 }
191 else {
192 /* Create edge index buffer. */
194 GPU_indexbuf_init(&elb, GPU_PRIM_LINES, totedge, totvert);
195 for (int i = 0; i < totedge; i++) {
196 GPU_indexbuf_add_line_verts(&elb, edges[i][0], edges[i][1]);
197 }
199
200 /* Create batch. */
203 }
204
205 GPU_batch_vertbuf_add(cache->face_wire.batch, vbo_wiredata, true);
206}
207
209{
210 if (volume->display.wireframe_type == VOLUME_WIREFRAME_NONE) {
211 return nullptr;
212 }
213
215
216 if (cache->face_wire.batch == nullptr) {
217 const bke::VolumeGridData *volume_grid = BKE_volume_grid_active_get_for_read(volume);
218 if (volume_grid == nullptr) {
219 return nullptr;
220 }
221
222 /* Create wireframe from OpenVDB tree. */
223 const DRWContextState *draw_ctx = DRW_context_state_get();
225 userdata.volume = volume;
226 userdata.scene = draw_ctx->scene;
227 BKE_volume_grid_wireframe(volume, volume_grid, drw_volume_wireframe_cb, &userdata);
228 }
229
230 return cache->face_wire.batch;
231}
232
234 void *userdata, float (*verts)[3], int (*tris)[3], int totvert, int tottris)
235{
236 Volume *volume = static_cast<Volume *>(userdata);
237 VolumeBatchCache *cache = static_cast<VolumeBatchCache *>(volume->batch_cache);
238
239 static GPUVertFormat format = {0};
240 static uint pos_id;
241 if (format.attr_len == 0) {
243 }
244
245 /* Create vertex buffer. */
247 GPU_vertbuf_data_alloc(*vbo_surface, totvert);
248 GPU_vertbuf_attr_fill(vbo_surface, pos_id, verts);
249
250 /* Create index buffer. */
252 GPU_indexbuf_init(&elb, GPU_PRIM_TRIS, tottris, totvert);
253 for (int i = 0; i < tottris; i++) {
254 GPU_indexbuf_add_tri_verts(&elb, UNPACK3(tris[i]));
255 }
256 gpu::IndexBuf *ibo_surface = GPU_indexbuf_build(&elb);
257
259 GPU_PRIM_TRIS, vbo_surface, ibo_surface, GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX);
260}
261
263{
265 if (cache->selection_surface == nullptr) {
266 const bke::VolumeGridData *volume_grid = BKE_volume_grid_active_get_for_read(volume);
267 if (volume_grid == nullptr) {
268 return nullptr;
269 }
271 volume, volume_grid, drw_volume_selection_surface_cb, volume);
272 }
273 return cache->selection_surface;
274}
275
277 const bke::VolumeGridData *grid,
278 VolumeBatchCache *cache)
279{
280 const std::string name = bke::volume_grid::get_name(*grid);
281
282 /* Return cached grid. */
283 LISTBASE_FOREACH (DRWVolumeGrid *, cache_grid, &cache->grids) {
284 if (cache_grid->name == name) {
285 return cache_grid;
286 }
287 }
288
289 /* Allocate new grid. */
290 DRWVolumeGrid *cache_grid = MEM_cnew<DRWVolumeGrid>(__func__);
291 cache_grid->name = BLI_strdup(name.c_str());
292 BLI_addtail(&cache->grids, cache_grid);
293
294 /* TODO: can we load this earlier, avoid accessing the global and take
295 * advantage of dependency graph multi-threading? */
296 BKE_volume_load(volume, G.main);
297
298 /* Test if we support textures with the number of channels. */
300 if (!ELEM(channels, 1, 3)) {
301 return cache_grid;
302 }
303
304 DenseFloatVolumeGrid dense_grid;
305 if (BKE_volume_grid_dense_floats(volume, grid, &dense_grid)) {
306 cache_grid->texture_to_object = float4x4(dense_grid.texture_to_object);
307 cache_grid->object_to_texture = math::invert(cache_grid->texture_to_object);
308
309 /* Create GPU texture. */
310 eGPUTextureFormat format = (channels == 3) ? GPU_RGB16F : GPU_R16F;
311 cache_grid->texture = GPU_texture_create_3d("volume_grid",
312 UNPACK3(dense_grid.resolution),
313 1,
314 format,
316 dense_grid.voxels);
317 /* The texture can be null if the resolution along one axis is larger than
318 * GL_MAX_3D_TEXTURE_SIZE. */
319 if (cache_grid->texture != nullptr) {
320 GPU_texture_swizzle_set(cache_grid->texture, (channels == 3) ? "rgb1" : "rrr1");
323 }
324 else {
325 MEM_freeN(dense_grid.voxels);
326 printf("Error: Could not allocate 3D texture for volume.\n");
327 }
328 }
329
330 return cache_grid;
331}
332
334 const bke::VolumeGridData *volume_grid)
335{
337 DRWVolumeGrid *grid = volume_grid_cache_get(volume, volume_grid, cache);
338 return (grid->texture != nullptr) ? grid : nullptr;
339}
340
342{
343 return max_ii(1, volume->totcol);
344}
345
346} // namespace blender::draw
Volume data-block.
const blender::bke::VolumeGridData * BKE_volume_grid_active_get_for_read(const Volume *volume)
bool BKE_volume_load(const Volume *volume, const Main *bmain)
@ BKE_VOLUME_BATCH_DIRTY_ALL
Definition BKE_volume.hh:57
Volume data-block rendering and viewport drawing utilities.
void BKE_volume_dense_float_grid_clear(DenseFloatVolumeGrid *dense_grid)
void BKE_volume_grid_selection_surface(const Volume *volume, const blender::bke::VolumeGridData *volume_grid, BKE_volume_selection_surface_cb cb, void *cb_userdata)
bool BKE_volume_grid_dense_floats(const Volume *volume, const blender::bke::VolumeGridData *volume_grid, DenseFloatVolumeGrid *r_dense_grid)
void BKE_volume_grid_wireframe(const Volume *volume, const blender::bke::VolumeGridData *volume_grid, BKE_volume_wireframe_cb cb, void *cb_userdata)
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
MINLINE int max_ii(int a, int b)
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
unsigned int uint
#define UNPACK3(a)
#define ELEM(...)
Object is a sort of wrapper for general info.
@ SCE_PERF_HQ_NORMALS
@ VOLUME_WIREFRAME_NONE
@ VOLUME_WIREFRAME_POINTS
#define DRW_TEXTURE_FREE_SAFE(tex)
blender::gpu::Batch * GPU_batch_create_ex(GPUPrimType primitive_type, blender::gpu::VertBuf *vertex_buf, blender::gpu::IndexBuf *index_buf, eGPUBatchFlag owns_flag)
Definition gpu_batch.cc:56
#define GPU_batch_create(primitive_type, vertex_buf, index_buf)
Definition GPU_batch.hh:149
int GPU_batch_vertbuf_add(blender::gpu::Batch *batch, blender::gpu::VertBuf *vertex_buf, bool own_vbo)
#define GPU_BATCH_DISCARD_SAFE(batch)
Definition GPU_batch.hh:205
@ GPU_BATCH_OWNS_INDEX
Definition GPU_batch.hh:51
@ GPU_BATCH_OWNS_VBO
Definition GPU_batch.hh:42
bool GPU_use_hq_normals_workaround()
void GPU_indexbuf_init(GPUIndexBufBuilder *, GPUPrimType, uint prim_len, uint vertex_len)
blender::gpu::IndexBuf * GPU_indexbuf_build(GPUIndexBufBuilder *)
void GPU_indexbuf_add_line_verts(GPUIndexBufBuilder *, uint v1, uint v2)
void GPU_indexbuf_add_tri_verts(GPUIndexBufBuilder *, uint v1, uint v2, uint v3)
@ GPU_PRIM_LINES
@ GPU_PRIM_POINTS
@ GPU_PRIM_TRIS
void GPU_texture_extend_mode(GPUTexture *texture, GPUSamplerExtendMode extend_mode)
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER
GPUTexture * GPU_texture_create_3d(const char *name, int width, int height, int depth, int mip_len, eGPUTextureFormat format, eGPUTextureUsage usage, const void *data)
eGPUTextureFormat
@ GPU_RGB16F
void GPU_texture_swizzle_set(GPUTexture *texture, const char swizzle[4])
void GPU_vertbuf_attr_fill_stride(blender::gpu::VertBuf *, uint a_idx, uint stride, const void *data)
#define GPU_vertbuf_create_with_format(format)
void GPU_vertbuf_attr_fill(blender::gpu::VertBuf *, uint a_idx, const void *data)
#define GPU_VERTBUF_DISCARD_SAFE(verts)
blender::gpu::VertBuf * GPU_vertbuf_calloc()
void GPU_vertbuf_data_alloc(blender::gpu::VertBuf &verts, uint v_len)
@ GPU_FETCH_FLOAT
@ GPU_FETCH_INT_TO_FLOAT_UNIT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
BLI_INLINE void GPU_normal_convert_v3(GPUNormal *gpu_normal, const float data[3], const bool do_hq_normals)
@ GPU_COMP_I10
@ GPU_COMP_F32
@ GPU_COMP_I16
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define printf
const DRWContextState * DRW_context_state_get()
static float verts[][3]
struct @620::@623 attr_id
format
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
#define G(x, y, z)
std::string get_name(const VolumeGridData &grid)
int get_channels_num(VolumeGridType type)
VolumeGridType get_type(const VolumeGridData &grid)
blender::gpu::Batch * DRW_volume_batch_cache_get_selection_surface(Volume *volume)
static void volume_batch_cache_clear(Volume *volume)
static void volume_batch_cache_init(Volume *volume)
void DRW_volume_batch_cache_free(Volume *volume)
void DRW_volume_batch_cache_validate(Volume *volume)
void DRW_volume_batch_cache_dirty_tag(Volume *volume, int mode)
blender::gpu::Batch * DRW_volume_batch_cache_get_wireframes_face(Volume *volume)
static bool volume_batch_cache_valid(Volume *volume)
static DRWVolumeGrid * volume_grid_cache_get(const Volume *volume, const bke::VolumeGridData *grid, VolumeBatchCache *cache)
void DRW_vertbuf_create_wiredata(blender::gpu::VertBuf *vbo, const int vert_len)
static VolumeBatchCache * volume_batch_cache_get(Volume *volume)
static void drw_volume_selection_surface_cb(void *userdata, float(*verts)[3], int(*tris)[3], int totvert, int tottris)
DRWVolumeGrid * DRW_volume_batch_cache_get_grid(Volume *volume, const bke::VolumeGridData *volume_grid)
int DRW_volume_material_count_get(const Volume *volume)
static void drw_volume_wireframe_cb(void *userdata, const float(*verts)[3], const int(*edges)[2], int totvert, int totedge)
CartesianBasis invert(const CartesianBasis &basis)
MatBase< float, 4, 4 > float4x4
blender::float4x4 object_to_texture
GPUTexture * texture
blender::float4x4 texture_to_object
struct blender::draw::VolumeBatchCache::@265 face_wire