Blender V4.3
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/colorspace.h"
7#include "scene/image.h"
8#include "scene/image_vdb.h"
9#include "scene/object.h"
10
11#include "blender/sync.h"
12#include "blender/util.h"
13
14#include "BKE_volume.hh"
15#include "BKE_volume_grid.hh"
16
18
19/* TODO: verify this is not loading unnecessary attributes. */
21 public:
22 BlenderSmokeLoader(BL::Object &b_ob, AttributeStandard attribute)
23 : b_domain(object_fluid_gas_domain_find(b_ob)), attribute(attribute)
24 {
26 *static_cast<const ::Mesh *>(b_ob.data().ptr.data), texspace_loc, texspace_size);
27 }
28
29 bool load_metadata(const ImageDeviceFeatures &, ImageMetaData &metadata) override
30 {
31 if (!b_domain) {
32 return false;
33 }
34
35 if (attribute == ATTR_STD_VOLUME_DENSITY || attribute == ATTR_STD_VOLUME_FLAME ||
36 attribute == ATTR_STD_VOLUME_HEAT || attribute == ATTR_STD_VOLUME_TEMPERATURE)
37 {
38 metadata.type = IMAGE_DATA_TYPE_FLOAT;
39 metadata.channels = 1;
40 }
41 else if (attribute == ATTR_STD_VOLUME_COLOR) {
43 metadata.channels = 4;
44 }
45 else if (attribute == ATTR_STD_VOLUME_VELOCITY) {
47 metadata.channels = 3;
48 }
49 else {
50 return false;
51 }
52
53 int3 resolution = get_int3(b_domain.domain_resolution());
54 int amplify = (b_domain.use_noise()) ? b_domain.noise_scale() : 1;
55
56 /* Velocity and heat data is always low-resolution. */
57 if (attribute == ATTR_STD_VOLUME_VELOCITY || attribute == ATTR_STD_VOLUME_HEAT) {
58 amplify = 1;
59 }
60
61 metadata.width = resolution.x * amplify;
62 metadata.height = resolution.y * amplify;
63 metadata.depth = resolution.z * amplify;
64
65 /* Create a matrix to transform from object space to mesh texture space.
66 * This does not work with deformations but that can probably only be done
67 * well with a volume grid mapping of coordinates. */
69 metadata.use_transform_3d = true;
70
71 return true;
72 }
73
74 bool load_pixels(const ImageMetaData &, void *pixels, const size_t, const bool) override
75 {
76 if (!b_domain) {
77 return false;
78 }
79#ifdef WITH_FLUID
80 int3 resolution = get_int3(b_domain.domain_resolution());
81 int length, amplify = (b_domain.use_noise()) ? b_domain.noise_scale() : 1;
82
83 /* Velocity and heat data is always low-resolution. */
84 if (attribute == ATTR_STD_VOLUME_VELOCITY || attribute == ATTR_STD_VOLUME_HEAT) {
85 amplify = 1;
86 }
87
88 const int width = resolution.x * amplify;
89 const int height = resolution.y * amplify;
90 const int depth = resolution.z * amplify;
91 const size_t num_pixels = ((size_t)width) * height * depth;
92
93 float *fpixels = (float *)pixels;
94
95 if (attribute == ATTR_STD_VOLUME_DENSITY) {
96 FluidDomainSettings_density_grid_get_length(&b_domain.ptr, &length);
97 if (length == num_pixels) {
98 FluidDomainSettings_density_grid_get(&b_domain.ptr, fpixels);
99 return true;
100 }
101 }
102 else if (attribute == ATTR_STD_VOLUME_FLAME) {
103 /* this is in range 0..1, and interpreted by the OpenGL smoke viewer
104 * as 1500..3000 K with the first part faded to zero density */
105 FluidDomainSettings_flame_grid_get_length(&b_domain.ptr, &length);
106 if (length == num_pixels) {
107 FluidDomainSettings_flame_grid_get(&b_domain.ptr, fpixels);
108 return true;
109 }
110 }
111 else if (attribute == ATTR_STD_VOLUME_COLOR) {
112 /* the RGB is "premultiplied" by density for better interpolation results */
113 FluidDomainSettings_color_grid_get_length(&b_domain.ptr, &length);
114 if (length == num_pixels * 4) {
115 FluidDomainSettings_color_grid_get(&b_domain.ptr, fpixels);
116 return true;
117 }
118 }
119 else if (attribute == ATTR_STD_VOLUME_VELOCITY) {
120 FluidDomainSettings_velocity_grid_get_length(&b_domain.ptr, &length);
121 if (length == num_pixels * 3) {
122 FluidDomainSettings_velocity_grid_get(&b_domain.ptr, fpixels);
123 return true;
124 }
125 }
126 else if (attribute == ATTR_STD_VOLUME_HEAT) {
127 FluidDomainSettings_heat_grid_get_length(&b_domain.ptr, &length);
128 if (length == num_pixels) {
129 FluidDomainSettings_heat_grid_get(&b_domain.ptr, fpixels);
130 return true;
131 }
132 }
133 else if (attribute == ATTR_STD_VOLUME_TEMPERATURE) {
134 FluidDomainSettings_temperature_grid_get_length(&b_domain.ptr, &length);
135 if (length == num_pixels) {
136 FluidDomainSettings_temperature_grid_get(&b_domain.ptr, fpixels);
137 return true;
138 }
139 }
140 else {
141 fprintf(stderr,
142 "Cycles error: unknown volume attribute %s, skipping\n",
143 Attribute::standard_name(attribute));
144 fpixels[0] = 0.0f;
145 return false;
146 }
147#else
148 (void)pixels;
149#endif
150 fprintf(stderr, "Cycles error: unexpected smoke volume resolution, skipping\n");
151 return false;
152 }
153
154 string name() const override
155 {
156 return Attribute::standard_name(attribute);
157 }
158
159 bool equals(const ImageLoader &other) const override
160 {
161 const BlenderSmokeLoader &other_loader = (const BlenderSmokeLoader &)other;
162 return b_domain == other_loader.b_domain && attribute == other_loader.attribute;
163 }
164
165 BL::FluidDomainSettings b_domain;
168};
169
171 BL::Scene &b_scene, Scene *scene, BObjectInfo &b_ob_info, Volume *volume, float frame)
172{
173 if (!b_ob_info.is_real_object_data()) {
174 return;
175 }
176 BL::FluidDomainSettings b_domain = object_fluid_gas_domain_find(b_ob_info.real_object);
177 if (!b_domain) {
178 return;
179 }
180
181 float velocity_scale = b_domain.velocity_scale();
182 /* Motion blur attribute is relative to seconds, we need it relative to frames. */
183 const bool need_motion = object_need_motion_attribute(b_ob_info, scene);
184 const float motion_scale = (need_motion) ?
185 scene->motion_shutter_time() /
186 (b_scene.render().fps() / b_scene.render().fps_base()) :
187 0.0f;
188
189 velocity_scale *= motion_scale;
190
191 volume->set_velocity_scale(velocity_scale);
192
200
201 for (int i = 0; attributes[i] != ATTR_STD_NONE; i++) {
202 AttributeStandard std = attributes[i];
203 if (!volume->need_attribute(scene, std)) {
204 continue;
205 }
206
207 volume->set_clipping(b_domain.clipping());
208
209 Attribute *attr = volume->attributes.add(std);
210
211 ImageLoader *loader = new BlenderSmokeLoader(b_ob_info.real_object, std);
213 params.frame = frame;
214
215 attr->data_voxel() = scene->image_manager->add_image(loader, params);
216 }
217}
218
220 public:
221 BlenderVolumeLoader(BL::BlendData &b_data,
222 BL::Volume &b_volume,
223 const string &grid_name,
224 BL::VolumeRender::precision_enum precision_)
226 {
227 b_volume.grids.load(b_data.ptr.data);
228
229#ifdef WITH_OPENVDB
230 for (BL::VolumeGrid &b_volume_grid : b_volume.grids) {
231 if (b_volume_grid.name() == grid_name) {
232 const auto *grid_data = static_cast<const blender::bke::VolumeGridData *>(
233 b_volume_grid.ptr.data);
234 grid_data->add_user();
235 volume_grid = blender::bke::GVolumeGrid{grid_data};
236 grid = volume_grid->grid_ptr(tree_access_token);
237 break;
238 }
239 }
240#endif
241#ifdef WITH_NANOVDB
242 switch (precision_) {
243 case BL::VolumeRender::precision_FULL:
244 precision = 32;
245 break;
246 case BL::VolumeRender::precision_HALF:
247 precision = 16;
248 break;
249 default:
250 case BL::VolumeRender::precision_VARIABLE:
251 precision = 0;
252 break;
253 }
254#else
255 (void)precision_;
256#endif
257 }
258
259 BL::Volume b_volume;
260#ifdef WITH_OPENVDB
261 /* Store tree user so that the OPENVDB grid that is shared with Blender is not unloaded. */
262 blender::bke::GVolumeGrid volume_grid;
263 blender::bke::VolumeTreeAccessToken tree_access_token;
264#endif
265};
266
267static void sync_volume_object(BL::BlendData &b_data,
268 BL::Scene &b_scene,
269 BObjectInfo &b_ob_info,
270 Scene *scene,
271 Volume *volume)
272{
273 BL::Volume b_volume(b_ob_info.object_data);
274 b_volume.grids.load(b_data.ptr.data);
275
276 BL::VolumeRender b_render(b_volume.render());
277
278 volume->set_clipping(b_render.clipping());
279 volume->set_step_size(b_render.step_size());
280 volume->set_object_space((b_render.space() == BL::VolumeRender::space_OBJECT));
281
282 float velocity_scale = b_volume.velocity_scale();
283 if (b_volume.velocity_unit() == BL::Volume::velocity_unit_SECOND) {
284 /* Motion blur attribute is relative to seconds, we need it relative to frames. */
285 const bool need_motion = object_need_motion_attribute(b_ob_info, scene);
286 const float motion_scale = (need_motion) ?
287 scene->motion_shutter_time() /
288 (b_scene.render().fps() / b_scene.render().fps_base()) :
289 0.0f;
290
291 velocity_scale *= motion_scale;
292 }
293
294 volume->set_velocity_scale(velocity_scale);
295
296 /* Find grid with matching name. */
297 for (BL::VolumeGrid &b_grid : b_volume.grids) {
298 ustring name = ustring(b_grid.name());
300
303 }
306 }
309 }
312 }
315 }
317 name == b_volume.velocity_grid())
318 {
320 }
322 name == b_volume.velocity_x_grid())
323 {
325 }
327 name == b_volume.velocity_y_grid())
328 {
330 }
332 name == b_volume.velocity_z_grid())
333 {
335 }
336
337 if ((std != ATTR_STD_NONE && volume->need_attribute(scene, std)) ||
338 volume->need_attribute(scene, name))
339 {
340 Attribute *attr = (std != ATTR_STD_NONE) ?
341 volume->attributes.add(std) :
342 volume->attributes.add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VOXEL);
343
344 ImageLoader *loader = new BlenderVolumeLoader(
345 b_data, b_volume, name.string(), b_render.precision());
347 params.frame = b_volume.grids.frame();
348
349 attr->data_voxel() = scene->image_manager->add_image(loader, params, false);
350 }
351 }
352}
353
354void BlenderSync::sync_volume(BObjectInfo &b_ob_info, Volume *volume)
355{
356 volume->clear(true);
357
358 if (view_layer.use_volumes) {
359 if (b_ob_info.object_data.is_a(&RNA_Volume)) {
360 /* Volume object. Create only attributes, bounding mesh will then
361 * be automatically generated later. */
362 sync_volume_object(b_data, b_scene, b_ob_info, scene, volume);
363 }
364 else {
365 /* Smoke domain. */
366 sync_smoke_volume(b_scene, scene, b_ob_info, volume, b_scene.frame_current());
367 }
368 }
369
370 /* Tag update. */
371 volume->tag_update(scene, true);
372}
373
Volume data-block.
static void sync_smoke_volume(BL::Scene &b_scene, Scene *scene, BObjectInfo &b_ob_info, Volume *volume, float frame)
static void sync_volume_object(BL::BlendData &b_data, BL::Scene &b_scene, BObjectInfo &b_ob_info, Scene *scene, Volume *volume)
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
static const char * standard_name(AttributeStandard std)
ImageHandle & data_voxel()
void add(const float &f)
bool load_pixels(const ImageMetaData &, void *pixels, const size_t, const bool) override
string name() const override
bool load_metadata(const ImageDeviceFeatures &, ImageMetaData &metadata) override
BlenderSmokeLoader(BL::Object &b_ob, AttributeStandard attribute)
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_)
ImageDataType type
string grid_name
Definition image_vdb.h:48
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
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
BL::Object real_object
bool is_real_object_data() const
ccl_device_inline Transform transform_translate(float3 t)
Definition transform.h:244
ccl_device_inline Transform transform_scale(float3 s)
Definition transform.h:254
@ IMAGE_DATA_TYPE_FLOAT
@ IMAGE_DATA_TYPE_FLOAT4