Blender V4.5
blender/geometry.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/curves.h"
6#include "scene/hair.h"
7#include "scene/light.h"
8#include "scene/mesh.h"
9#include "scene/object.h"
10#include "scene/pointcloud.h"
11#include "scene/volume.h"
12
13#include "blender/sync.h"
14#include "blender/util.h"
15
16#include "util/task.h"
17
19
20static Geometry::Type determine_geom_type(BObjectInfo &b_ob_info, bool use_particle_hair)
21{
22 if (b_ob_info.object_data.is_a(&RNA_Light)) {
23 return Geometry::LIGHT;
24 }
25
26 if (b_ob_info.object_data.is_a(&RNA_Curves) || use_particle_hair) {
27 return Geometry::HAIR;
28 }
29
30 if (b_ob_info.object_data.is_a(&RNA_PointCloud)) {
32 }
33
34 if (b_ob_info.object_data.is_a(&RNA_Volume) ||
35 (b_ob_info.object_data ==
38 {
39 return Geometry::VOLUME;
40 }
41
42 return Geometry::MESH;
43}
44
45array<Node *> BlenderSync::find_used_shaders(BL::Object &b_ob)
46{
47 array<Node *> used_shaders;
48
49 if (b_ob.type() == BL::Object::type_LIGHT) {
50 find_shader(b_ob.data(), used_shaders, scene->default_light);
51 return used_shaders;
52 }
53
54 BL::Material material_override = view_layer.material_override;
55 Shader *default_shader = (b_ob.type() == BL::Object::type_VOLUME) ? scene->default_volume :
56 scene->default_surface;
57
58 for (BL::MaterialSlot &b_slot : b_ob.material_slots) {
59 if (material_override) {
60 find_shader(material_override, used_shaders, default_shader);
61 }
62 else {
63 BL::ID b_material(b_slot.material());
64 find_shader(b_material, used_shaders, default_shader);
65 }
66 }
67
68 if (used_shaders.size() == 0) {
69 if (material_override) {
70 find_shader(material_override, used_shaders, default_shader);
71 }
72 else {
73 used_shaders.push_back_slow(default_shader);
74 }
75 }
76
77 return used_shaders;
78}
79
80Geometry *BlenderSync::sync_geometry(BObjectInfo &b_ob_info,
81 bool object_updated,
82 bool use_particle_hair,
84{
85 /* Test if we can instance or if the object is modified. */
86 const Geometry::Type geom_type = determine_geom_type(b_ob_info, use_particle_hair);
87 BL::ID const b_key_id = (b_ob_info.is_real_object_data() &&
88 BKE_object_is_modified(b_ob_info.real_object)) ?
89 b_ob_info.real_object :
90 b_ob_info.object_data;
91 const GeometryKey key(b_key_id.ptr.data, geom_type);
92
93 /* Find shader indices. */
94 array<Node *> used_shaders = find_used_shaders(b_ob_info.iter_object);
95
96 /* Ensure we only sync instanced geometry once. */
97 Geometry *geom = geometry_map.find(key);
98 if (geom) {
99 if (geometry_synced.find(geom) != geometry_synced.end()) {
100 return geom;
101 }
102 }
103
104 /* Test if we need to sync. */
105 bool sync = true;
106 if (geom == nullptr) {
107 /* Add new geometry if it did not exist yet. */
108 if (geom_type == Geometry::LIGHT) {
109 geom = scene->create_node<Light>();
110 }
111 else if (geom_type == Geometry::HAIR) {
112 geom = scene->create_node<Hair>();
113 }
114 else if (geom_type == Geometry::VOLUME) {
115 geom = scene->create_node<Volume>();
116 }
117 else if (geom_type == Geometry::POINTCLOUD) {
118 geom = scene->create_node<PointCloud>();
119 }
120 else {
121 geom = scene->create_node<Mesh>();
122 }
123 geometry_map.add(key, geom);
124 }
125 else {
126 /* Test if we need to update existing geometry. */
127 sync = geometry_map.update(geom, b_key_id);
128 }
129
130 if (!sync) {
131 /* Need to determine this every sync. */
132 if (geom->is_light() && static_cast<const Light *>(geom)->get_is_portal()) {
133 world_use_portal = true;
134 }
135
136 /* If transform was applied to geometry, need full update. */
137 if (object_updated && geom->transform_applied) {
138 ;
139 }
140 /* Test if shaders changed, these can be object level so geometry
141 * does not get tagged for recalc. */
142 else if (geom->get_used_shaders() != used_shaders) {
143 ;
144 }
145 else {
146 /* Even if not tagged for recalc, we may need to sync anyway
147 * because the shader needs different geometry attributes. */
148 bool attribute_recalc = false;
149
150 for (Node *node : geom->get_used_shaders()) {
151 Shader *shader = static_cast<Shader *>(node);
152 if (shader->need_update_geometry()) {
153 attribute_recalc = true;
154 }
155 }
156
157 if (!attribute_recalc) {
158 return geom;
159 }
160 }
161 }
162
163 geometry_synced.insert(geom);
164
165 geom->name = ustring(b_ob_info.object_data.name().c_str());
166
167 /* Store the shaders immediately for the object attribute code. */
168 geom->set_used_shaders(used_shaders);
169
170 auto sync_func = [this, geom_type, b_ob_info, geom]() mutable {
171 if (progress.get_cancel()) {
172 return;
173 }
174
175 progress.set_sync_status("Synchronizing object", b_ob_info.real_object.name());
176
177 if (geom_type == Geometry::LIGHT) {
178 Light *light = static_cast<Light *>(geom);
179 sync_light(b_ob_info, light);
180 }
181 else if (geom_type == Geometry::HAIR) {
182 Hair *hair = static_cast<Hair *>(geom);
183 sync_hair(b_ob_info, hair);
184 }
185 else if (geom_type == Geometry::VOLUME) {
186 Volume *volume = static_cast<Volume *>(geom);
187 sync_volume(b_ob_info, volume);
188 }
189 else if (geom_type == Geometry::POINTCLOUD) {
190 PointCloud *pointcloud = static_cast<PointCloud *>(geom);
191 sync_pointcloud(pointcloud, b_ob_info);
192 }
193 else {
194 Mesh *mesh = static_cast<Mesh *>(geom);
195 sync_mesh(b_ob_info, mesh);
196 }
197 };
198
199 /* Defer the actual geometry sync to the task_pool for multithreading */
200 if (task_pool) {
202 }
203 else {
204 sync_func();
205 }
206
207 return geom;
208}
209
210void BlenderSync::sync_geometry_motion(BObjectInfo &b_ob_info,
211 Object *object,
212 const float motion_time,
213 bool use_particle_hair,
215{
216 /* Ensure we only sync instanced geometry once. */
217 Geometry *geom = object->get_geometry();
218
219 if (geometry_motion_synced.find(geom) != geometry_motion_synced.end() ||
220 geometry_motion_attribute_synced.find(geom) != geometry_motion_attribute_synced.end())
221 {
222 return;
223 }
224
225 geometry_motion_synced.insert(geom);
226
227 /* Ensure we only motion sync geometry that also had geometry synced, to avoid
228 * unnecessary work and to ensure that its attributes were clear. */
229 if (geometry_synced.find(geom) == geometry_synced.end()) {
230 return;
231 }
232
233 /* Nothing to do for lights. */
234 if (geom->is_light()) {
235 return;
236 }
237
238 /* If the geometry already has motion blur from a velocity attribute, don't
239 * set the geometry motion steps again.
240 *
241 * Otherwise, setting geometry motion steps is done here to avoid concurrency issues.
242 * - It can't be done earlier in sync_object_motion_init because sync_geometry
243 * runs in parallel, and has_motion_blur would check attributes while
244 * sync_geometry is potentially creating the attribute from velocity.
245 * - It needs to happen before the parallel motion sync that happens right after
246 * this, because that can create the attribute from neighboring frames.
247 * Copying the motion steps from the object here solves this. */
248 if (!geom->has_motion_blur()) {
249 geom->set_motion_steps(object->get_motion().size());
250 }
251
252 /* Find time matching motion step required by geometry. */
253 const int motion_step = geom->motion_step(motion_time);
254 if (motion_step < 0) {
255 return;
256 }
257
258 auto sync_func = [this, b_ob_info, use_particle_hair, motion_step, geom]() mutable {
259 if (progress.get_cancel()) {
260 return;
261 }
262
263 if (b_ob_info.object_data.is_a(&RNA_Curves) || use_particle_hair) {
264 Hair *hair = static_cast<Hair *>(geom);
265 sync_hair_motion(b_ob_info, hair, motion_step);
266 }
267 else if (b_ob_info.object_data.is_a(&RNA_Volume) ||
269 {
270 /* No volume motion blur support yet. */
271 }
272 else if (b_ob_info.object_data.is_a(&RNA_PointCloud)) {
273 PointCloud *pointcloud = static_cast<PointCloud *>(geom);
274 sync_pointcloud_motion(pointcloud, b_ob_info, motion_step);
275 }
276 else {
277 Mesh *mesh = static_cast<Mesh *>(geom);
278 sync_mesh_motion(b_ob_info, mesh, motion_step);
279 }
280 };
281
282 /* Defer the actual geometry sync to the task_pool for multithreading */
283 if (task_pool) {
285 }
286 else {
287 sync_func();
288 }
289}
290
struct Light Light
struct Mesh Mesh
struct PointCloud PointCloud
struct Volume Volume
static CCL_NAMESPACE_BEGIN Geometry::Type determine_geom_type(BObjectInfo &b_ob_info, bool use_particle_hair)
int motion_step(const float time) const
bool transform_applied
bool is_light() const
virtual bool has_motion_blur() const
bool need_update_geometry() const
size_t size() const
void push_back_slow(const T &t)
static BL::FluidDomainSettings object_fluid_gas_domain_find(BL::Object &b_ob)
static CCL_NAMESPACE_BEGIN BL::ID object_get_data(const BL::Object &b_ob, const bool use_adaptive_subdivision)
#define CCL_NAMESPACE_END
TaskPool * task_pool
static PyObject * sync_func(PyObject *, PyObject *args)
Definition python.cpp:396
BL::Object real_object
BL::Object iter_object
bool is_real_object_data() const
ustring name
Definition graph/node.h:177
void push(TaskRunFunction &&task)
Definition task.cpp:21