Blender V5.0
blender/volume.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include "scene/volume.h"
6#include "scene/image.h"
7#include "scene/image_vdb.h"
8#include "scene/object.h"
9
10#include "blender/sync.h"
11#include "blender/util.h"
12
13#include "util/log.h"
14#include "util/vector.h"
15
16#include "BKE_volume_grid.hh"
17
19
20/* TODO: verify this is not loading unnecessary attributes. */
22 public:
24 : VDBImageLoader(Attribute::standard_name(attribute), clipping),
27 {
29 *static_cast<const ::Mesh *>(b_ob.data().ptr.data), texspace_loc, texspace_size);
30 }
31
32 void load_grid() override
33 {
34 if (!b_domain) {
35 return;
36 }
37
38 int channels;
41 {
42 channels = 1;
43 }
44 else if (attribute == ATTR_STD_VOLUME_COLOR) {
45 channels = 4;
46 }
48 channels = 3;
49 }
50 else {
51 return;
52 }
53
54 const int3 resolution = get_int3(b_domain.domain_resolution());
55 int amplify = (b_domain.use_noise()) ? b_domain.noise_scale() : 1;
56
57 /* Velocity and heat data is always low-resolution. */
59 amplify = 1;
60 }
61
62 const size_t width = resolution.x * amplify;
63 const size_t height = resolution.y * amplify;
64 const size_t depth = resolution.z * amplify;
65
66 /* Create a matrix to transform from object space to mesh texture space.
67 * This does not work with deformations but that can probably only be done
68 * well with a volume grid mapping of coordinates. */
70
71 vector<float> voxels;
72 if (!get_voxels(width, height, depth, channels, voxels)) {
73 return;
74 }
75
76 grid_from_dense_voxels(width, height, depth, channels, voxels.data(), transform_3d);
77 }
78
79 bool get_voxels(const size_t width,
80 const size_t height,
81 const size_t depth,
82 const int channels,
83 vector<float> &voxels)
84 {
85 if (!b_domain) {
86 return false;
87 }
88
89#ifdef WITH_FLUID
90 int length;
91 voxels.resize(width * height * depth * channels);
92
94 FluidDomainSettings_density_grid_get_length(&b_domain.ptr, &length);
95 if (length == voxels.size()) {
96 FluidDomainSettings_density_grid_get(&b_domain.ptr, voxels.data());
97 return true;
98 }
99 }
100 else if (attribute == ATTR_STD_VOLUME_FLAME) {
101 /* this is in range 0..1, and interpreted by the OpenGL smoke viewer
102 * as 1500..3000 K with the first part faded to zero density */
103 FluidDomainSettings_flame_grid_get_length(&b_domain.ptr, &length);
104 if (length == voxels.size()) {
105 FluidDomainSettings_flame_grid_get(&b_domain.ptr, voxels.data());
106 return true;
107 }
108 }
109 else if (attribute == ATTR_STD_VOLUME_COLOR) {
110 /* the RGB is "premultiplied" by density for better interpolation results */
111 FluidDomainSettings_color_grid_get_length(&b_domain.ptr, &length);
112 if (length == voxels.size()) {
113 FluidDomainSettings_color_grid_get(&b_domain.ptr, voxels.data());
114 return true;
115 }
116 }
118 FluidDomainSettings_velocity_grid_get_length(&b_domain.ptr, &length);
119 if (length == voxels.size()) {
120 FluidDomainSettings_velocity_grid_get(&b_domain.ptr, voxels.data());
121 return true;
122 }
123 }
124 else if (attribute == ATTR_STD_VOLUME_HEAT) {
125 FluidDomainSettings_heat_grid_get_length(&b_domain.ptr, &length);
126 if (length == voxels.size()) {
127 FluidDomainSettings_heat_grid_get(&b_domain.ptr, voxels.data());
128 return true;
129 }
130 }
132 FluidDomainSettings_temperature_grid_get_length(&b_domain.ptr, &length);
133 if (length == voxels.size()) {
134 FluidDomainSettings_temperature_grid_get(&b_domain.ptr, voxels.data());
135 return true;
136 }
137 }
138 else {
139 LOG_ERROR << "Unknown volume attribute " << Attribute::standard_name(attribute)
140 << "skipping ";
141 voxels[0] = 0.0f;
142 return false;
143 }
144 LOG_ERROR << "Unexpected smoke volume resolution, skipping";
145#else
146 (void)voxels;
147 (void)width;
148 (void)height;
149 (void)depth;
150 (void)channels;
151#endif
152 return false;
153 }
154
155 string name() const override
156 {
158 }
159
160 bool equals(const ImageLoader &other) const override
161 {
162 const BlenderSmokeLoader &other_loader = (const BlenderSmokeLoader &)other;
163 return b_domain == other_loader.b_domain && attribute == other_loader.attribute;
164 }
165
166 BL::FluidDomainSettings b_domain;
169};
170
172 BL::Scene &b_scene, Scene *scene, BObjectInfo &b_ob_info, Volume *volume, const float frame)
173{
174 if (!b_ob_info.is_real_object_data()) {
175 return;
176 }
177 BL::FluidDomainSettings b_domain = object_fluid_gas_domain_find(b_ob_info.real_object);
178 if (!b_domain) {
179 return;
180 }
181
182 float velocity_scale = b_domain.velocity_scale();
183 /* Motion blur attribute is relative to seconds, we need it relative to frames. */
184 const bool need_motion = object_need_motion_attribute(b_ob_info, scene);
185 const float motion_scale = (need_motion) ?
186 scene->motion_shutter_time() /
187 (b_scene.render().fps() / b_scene.render().fps_base()) :
188 0.0f;
189
190 velocity_scale *= motion_scale;
191
192 volume->set_velocity_scale(velocity_scale);
193
194 const AttributeStandard attributes[] = {ATTR_STD_VOLUME_DENSITY,
201
202 const Interval<int> frame_interval = {b_domain.cache_frame_start(), b_domain.cache_frame_end()};
203
204 for (int i = 0; attributes[i] != ATTR_STD_NONE; i++) {
205 const AttributeStandard std = attributes[i];
206 if (!volume->need_attribute(scene, std)) {
207 continue;
208 }
209
210 const float clipping = b_domain.clipping();
211
212 Attribute *attr = volume->attributes.add(std);
213
214 if (!frame_interval.contains(frame)) {
215 attr->data_voxel().clear();
216 continue;
217 }
218
219 unique_ptr<ImageLoader> loader = make_unique<BlenderSmokeLoader>(
220 b_ob_info.real_object, std, clipping);
222 params.frame = frame;
223
224 attr->data_voxel() = scene->image_manager->add_image(std::move(loader), params);
225 }
226}
227
229 public:
230 BlenderVolumeLoader(BL::BlendData &b_data,
231 BL::Volume &b_volume,
232 const string &grid_name,
233 BL::VolumeRender::precision_enum precision_,
234 const float clipping)
236 {
237 b_volume.grids.load(b_data.ptr.data);
238
239#ifdef WITH_OPENVDB
240 for (BL::VolumeGrid &b_volume_grid : b_volume.grids) {
241 if (b_volume_grid.name() == grid_name) {
242 const auto *grid_data = static_cast<const blender::bke::VolumeGridData *>(
243 b_volume_grid.ptr.data);
244 grid_data->add_user();
245 volume_grid = blender::bke::GVolumeGrid{grid_data};
246 grid = volume_grid->grid_ptr(tree_access_token);
247 break;
248 }
249 }
250#endif
251#ifdef WITH_NANOVDB
252 switch (precision_) {
253 case BL::VolumeRender::precision_FULL:
254 precision = 32;
255 break;
256 case BL::VolumeRender::precision_HALF:
257 precision = 16;
258 break;
259 default:
260 case BL::VolumeRender::precision_VARIABLE:
261 precision = 0;
262 break;
263 }
264#else
265 (void)precision_;
266#endif
267 }
268
269 BL::Volume b_volume;
270#ifdef WITH_OPENVDB
271 /* Store tree user so that the OPENVDB grid that is shared with Blender is not unloaded. */
272 blender::bke::GVolumeGrid volume_grid;
273 blender::bke::VolumeTreeAccessToken tree_access_token;
274#endif
275};
276
277static void sync_volume_object(BL::BlendData &b_data,
278 BL::Scene &b_scene,
279 BObjectInfo &b_ob_info,
280 Scene *scene,
281 Volume *volume)
282{
283 BL::Volume b_volume(b_ob_info.object_data);
284 b_volume.grids.load(b_data.ptr.data);
285
286 BL::VolumeRender b_render(b_volume.render());
287
288 volume->set_step_size(b_render.step_size());
289 volume->set_object_space((b_render.space() == BL::VolumeRender::space_OBJECT));
290
291 float velocity_scale = b_volume.velocity_scale();
292 if (b_volume.velocity_unit() == BL::Volume::velocity_unit_SECOND) {
293 /* Motion blur attribute is relative to seconds, we need it relative to frames. */
294 const bool need_motion = object_need_motion_attribute(b_ob_info, scene);
295 const float motion_scale = (need_motion) ?
296 scene->motion_shutter_time() /
297 (b_scene.render().fps() / b_scene.render().fps_base()) :
298 0.0f;
299
300 velocity_scale *= motion_scale;
301 }
302
303 volume->set_velocity_scale(velocity_scale);
304
305 /* Find grid with matching name. */
306 for (BL::VolumeGrid &b_grid : b_volume.grids) {
307 const ustring name = ustring(b_grid.name());
309
312 }
315 }
318 }
321 }
324 }
326 name == b_volume.velocity_grid())
327 {
329 }
331 name == b_volume.velocity_x_grid())
332 {
334 }
336 name == b_volume.velocity_y_grid())
337 {
339 }
341 name == b_volume.velocity_z_grid())
342 {
344 }
345
346 if ((std != ATTR_STD_NONE && volume->need_attribute(scene, std)) ||
347 volume->need_attribute(scene, name))
348 {
349 Attribute *attr = (std != ATTR_STD_NONE) ?
350 volume->attributes.add(std) :
351 volume->attributes.add(name, TypeFloat, ATTR_ELEMENT_VOXEL);
352
353 unique_ptr<ImageLoader> loader = make_unique<BlenderVolumeLoader>(
354 b_data, b_volume, name.string(), b_render.precision(), b_render.clipping());
356 params.frame = b_volume.grids.frame();
357
358 attr->data_voxel() = scene->image_manager->add_image(std::move(loader), params, false);
359 }
360 }
361}
362
363void BlenderSync::sync_volume(BObjectInfo &b_ob_info, Volume *volume)
364{
365 volume->clear(true);
366
367 if (view_layer.use_volumes) {
368 if (b_ob_info.object_data.is_a(&RNA_Volume)) {
369 /* Volume object. Create only attributes, bounding mesh will then
370 * be automatically generated later. */
371 sync_volume_object(b_data, b_scene, b_ob_info, scene, volume);
372 }
373 else {
374 /* Smoke domain. */
375 sync_smoke_volume(b_scene, scene, b_ob_info, volume, b_scene.frame_current());
376 }
377 }
378
379 volume->merge_grids(scene);
380
381 /* Tag update. */
382 volume->tag_update(scene, true);
383}
384
static void sync_smoke_volume(BL::Scene &b_scene, Scene *scene, BObjectInfo &b_ob_info, Volume *volume, const float frame)
static void sync_volume_object(BL::BlendData &b_data, BL::Scene &b_scene, BObjectInfo &b_ob_info, Scene *scene, Volume *volume)
Attribute * add(ustring name, const TypeDesc type, AttributeElement element)
void load_grid() override
string name() const override
bool get_voxels(const size_t width, const size_t height, const size_t depth, const int channels, vector< float > &voxels)
BlenderSmokeLoader(BL::Object &b_ob, AttributeStandard attribute, const float clipping)
BL::FluidDomainSettings b_domain
bool equals(const ImageLoader &other) const override
AttributeStandard attribute
BlenderVolumeLoader(BL::BlendData &b_data, BL::Volume &b_volume, const string &grid_name, BL::VolumeRender::precision_enum precision_, const float clipping)
void tag_update(Scene *scene, bool rebuild)
bool need_attribute(Scene *scene, AttributeStandard std)
AttributeSet attributes
string grid_name
Definition image_vdb.h:65
void grid_from_dense_voxels(const size_t width, const size_t height, const size_t depth, const int channels, const float *voxels, Transform transform_3d)
VDBImageLoader(const string &grid_name, const float clipping=0.001f)
Definition image_vdb.cpp:27
static void mesh_texture_space(const ::Mesh &b_mesh, float3 &loc, float3 &size)
static bool object_need_motion_attribute(BObjectInfo &b_ob_info, Scene *scene)
static int3 get_int3(const BL::Array< int, 3 > &array)
static BL::FluidDomainSettings object_fluid_gas_domain_find(BL::Object &b_ob)
#define CCL_NAMESPACE_END
float length(VecOp< float, D >) RET
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
AttributeStandard
@ ATTR_STD_VOLUME_VELOCITY_Y
@ ATTR_STD_VOLUME_TEMPERATURE
@ ATTR_STD_NONE
@ ATTR_STD_VOLUME_VELOCITY_Z
@ ATTR_STD_VOLUME_DENSITY
@ ATTR_STD_VOLUME_FLAME
@ ATTR_STD_VOLUME_VELOCITY
@ ATTR_STD_VOLUME_COLOR
@ ATTR_STD_VOLUME_HEAT
@ ATTR_STD_VOLUME_VELOCITY_X
@ ATTR_ELEMENT_VOXEL
#define LOG_ERROR
Definition log.h:101
const char * name
static const char * standard_name(AttributeStandard std)
ImageHandle & data_voxel()
BL::Object real_object
bool is_real_object_data() const
ccl_device_inline_method bool contains(T value) const
Definition math_base.h:880
unique_ptr< ImageManager > image_manager
Definition scene.h:145
float motion_shutter_time()
Definition scene.cpp:421
void merge_grids(const Scene *scene)
void clear(bool preserve_shaders=false) override
float velocity_scale
i
Definition text_draw.cc:230
ccl_device_inline Transform transform_scale(const float3 s)
Definition transform.h:280
ccl_device_inline Transform transform_translate(const float3 t)
Definition transform.h:270