Blender V4.3
pointcloud.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <optional>
10
11#include "MEM_guardedalloc.h"
12
13#include "DNA_defaults.h"
14#include "DNA_material_types.h"
15#include "DNA_object_types.h"
17
18#include "BLI_bounds.hh"
19#include "BLI_index_range.hh"
20#include "BLI_math_vector.hh"
21#include "BLI_rand.h"
22#include "BLI_span.hh"
23#include "BLI_task.hh"
24#include "BLI_utildefines.h"
25#include "BLI_vector.hh"
26
27#include "BKE_anim_data.hh"
29#include "BKE_customdata.hh"
30#include "BKE_geometry_set.hh"
31#include "BKE_idtype.hh"
32#include "BKE_lib_id.hh"
33#include "BKE_lib_query.hh"
34#include "BKE_modifier.hh"
35#include "BKE_object.hh"
36#include "BKE_object_types.hh"
37#include "BKE_pointcloud.hh"
38
39#include "BLT_translation.hh"
40
42
43#include "BLO_read_write.hh"
44
45using blender::float3;
48using blender::Span;
49using blender::Vector;
50
51/* PointCloud datablock */
52
53static void pointcloud_random(PointCloud *pointcloud);
54
55const char *POINTCLOUD_ATTR_POSITION = "position";
56const char *POINTCLOUD_ATTR_RADIUS = "radius";
57
58static void pointcloud_init_data(ID *id)
59{
60 PointCloud *pointcloud = (PointCloud *)id;
62
64
65 pointcloud->runtime = new blender::bke::PointCloudRuntime();
66
67 CustomData_reset(&pointcloud->pdata);
68 pointcloud->attributes_for_write().add<float3>(
70}
71
72static void pointcloud_copy_data(Main * /*bmain*/,
73 std::optional<Library *> /*owner_library*/,
74 ID *id_dst,
75 const ID *id_src,
76 const int /*flag*/)
77{
78 PointCloud *pointcloud_dst = (PointCloud *)id_dst;
79 const PointCloud *pointcloud_src = (const PointCloud *)id_src;
80 pointcloud_dst->mat = static_cast<Material **>(MEM_dupallocN(pointcloud_src->mat));
81
83 &pointcloud_src->pdata, &pointcloud_dst->pdata, CD_MASK_ALL, pointcloud_dst->totpoint);
84
85 pointcloud_dst->runtime = new blender::bke::PointCloudRuntime();
86 pointcloud_dst->runtime->bounds_cache = pointcloud_src->runtime->bounds_cache;
87 if (pointcloud_src->runtime->bake_materials) {
88 pointcloud_dst->runtime->bake_materials =
89 std::make_unique<blender::bke::bake::BakeMaterialsList>(
90 *pointcloud_src->runtime->bake_materials);
91 }
92
93 pointcloud_dst->batch_cache = nullptr;
94}
95
96static void pointcloud_free_data(ID *id)
97{
98 PointCloud *pointcloud = (PointCloud *)id;
99 BKE_animdata_free(&pointcloud->id, false);
101 CustomData_free(&pointcloud->pdata, pointcloud->totpoint);
102 MEM_SAFE_FREE(pointcloud->mat);
103 delete pointcloud->runtime;
104}
105
107{
108 PointCloud *pointcloud = (PointCloud *)id;
109 for (int i = 0; i < pointcloud->totcol; i++) {
111 }
112}
113
114static void pointcloud_blend_write(BlendWriter *writer, ID *id, const void *id_address)
115{
116 PointCloud *pointcloud = (PointCloud *)id;
117
118 Vector<CustomDataLayer, 16> point_layers;
119 CustomData_blend_write_prepare(pointcloud->pdata, point_layers);
120
121 /* Write LibData */
122 BLO_write_id_struct(writer, PointCloud, id_address, &pointcloud->id);
123 BKE_id_blend_write(writer, &pointcloud->id);
124
125 /* Direct data */
127 &pointcloud->pdata,
128 point_layers,
129 pointcloud->totpoint,
131 &pointcloud->id);
132
133 BLO_write_pointer_array(writer, pointcloud->totcol, pointcloud->mat);
134}
135
137{
138 PointCloud *pointcloud = (PointCloud *)id;
139
140 /* Geometry */
141 CustomData_blend_read(reader, &pointcloud->pdata, pointcloud->totpoint);
142
143 /* Materials */
144 BLO_read_pointer_array(reader, pointcloud->totcol, (void **)&pointcloud->mat);
145
146 pointcloud->runtime = new blender::bke::PointCloudRuntime();
147}
148
150 /*id_code*/ ID_PT,
151 /*id_filter*/ FILTER_ID_PT,
152 /*dependencies_id_types*/ FILTER_ID_MA,
153 /*main_listbase_index*/ INDEX_ID_PT,
154 /*struct_size*/ sizeof(PointCloud),
155 /*name*/ "PointCloud",
156 /*name_plural*/ N_("pointclouds"),
157 /*translation_context*/ BLT_I18NCONTEXT_ID_POINTCLOUD,
159 /*asset_type_info*/ nullptr,
160
161 /*init_data*/ pointcloud_init_data,
162 /*copy_data*/ pointcloud_copy_data,
163 /*free_data*/ pointcloud_free_data,
164 /*make_local*/ nullptr,
165 /*foreach_id*/ pointcloud_foreach_id,
166 /*foreach_cache*/ nullptr,
167 /*foreach_path*/ nullptr,
168 /*owner_pointer_get*/ nullptr,
169
170 /*blend_write*/ pointcloud_blend_write,
171 /*blend_read_data*/ pointcloud_blend_read_data,
172 /*blend_read_after_liblink*/ nullptr,
173
174 /*blend_read_undo_preserve*/ nullptr,
175
176 /*lib_override_apply_post*/ nullptr,
177};
178
179static void pointcloud_random(PointCloud *pointcloud)
180{
181 BLI_assert(pointcloud->totpoint == 0);
182 pointcloud->totpoint = 400;
183 CustomData_realloc(&pointcloud->pdata, 0, pointcloud->totpoint);
184
185 RNG *rng = BLI_rng_new(0);
186
187 blender::bke::MutableAttributeAccessor attributes = pointcloud->attributes_for_write();
188 blender::MutableSpan<float3> positions = pointcloud->positions_for_write();
190 attributes.lookup_or_add_for_write_only_span<float>(POINTCLOUD_ATTR_RADIUS,
192
193 for (const int i : positions.index_range()) {
194 positions[i] = float3(BLI_rng_get_float(rng), BLI_rng_get_float(rng), BLI_rng_get_float(rng)) *
195 2.0f -
196 1.0f;
197 radii.span[i] = 0.05f * BLI_rng_get_float(rng);
198 }
199
200 radii.finish();
201
202 BLI_rng_free(rng);
203}
204
205Span<float3> PointCloud::positions() const
206{
207 return {static_cast<const float3 *>(
208 CustomData_get_layer_named(&this->pdata, CD_PROP_FLOAT3, "position")),
209 this->totpoint};
210}
211
212MutableSpan<float3> PointCloud::positions_for_write()
213{
214 return {static_cast<float3 *>(CustomData_get_layer_named_for_write(
215 &this->pdata, CD_PROP_FLOAT3, "position", this->totpoint)),
216 this->totpoint};
217}
218
219void *BKE_pointcloud_add(Main *bmain, const char *name)
220{
221 PointCloud *pointcloud = static_cast<PointCloud *>(BKE_id_new(bmain, ID_PT, name));
222
223 return pointcloud;
224}
225
226void *BKE_pointcloud_add_default(Main *bmain, const char *name)
227{
228 PointCloud *pointcloud = static_cast<PointCloud *>(BKE_libblock_alloc(bmain, ID_PT, name, 0));
229
230 pointcloud_init_data(&pointcloud->id);
231 pointcloud_random(pointcloud);
232
233 return pointcloud;
234}
235
237{
238 PointCloud *pointcloud = static_cast<PointCloud *>(BKE_libblock_alloc(
240
241 pointcloud_init_data(&pointcloud->id);
242
243 CustomData_realloc(&pointcloud->pdata, 0, totpoint);
244 pointcloud->totpoint = totpoint;
245
246 return pointcloud;
247}
248
249void BKE_pointcloud_nomain_to_pointcloud(PointCloud *pointcloud_src, PointCloud *pointcloud_dst)
250{
251 BLI_assert(pointcloud_src->id.tag & ID_TAG_NO_MAIN);
252
253 CustomData_free(&pointcloud_dst->pdata, pointcloud_dst->totpoint);
254
255 const int totpoint = pointcloud_dst->totpoint = pointcloud_src->totpoint;
256 CustomData_init_from(&pointcloud_src->pdata, &pointcloud_dst->pdata, CD_MASK_ALL, totpoint);
257
258 BKE_id_free(nullptr, pointcloud_src);
259}
260
261std::optional<blender::Bounds<blender::float3>> PointCloud::bounds_min_max() const
262{
263 using namespace blender;
264 using namespace blender::bke;
265 if (this->totpoint == 0) {
266 return std::nullopt;
267 }
268 this->runtime->bounds_cache.ensure([&](Bounds<float3> &r_bounds) {
269 const AttributeAccessor attributes = this->attributes();
270 const Span<float3> positions = this->positions();
271 if (attributes.contains(POINTCLOUD_ATTR_RADIUS)) {
272 const VArraySpan radii = *attributes.lookup<float>(POINTCLOUD_ATTR_RADIUS);
273 r_bounds = *bounds::min_max_with_radii(positions, radii);
274 }
275 else {
276 r_bounds = *bounds::min_max(positions);
277 }
278 });
279 return this->runtime->bounds_cache.data();
280}
281
282void PointCloud::count_memory(blender::MemoryCounter &memory) const
283{
284 CustomData_count_memory(this->pdata, this->totpoint, memory);
285}
286
287bool BKE_pointcloud_attribute_required(const PointCloud * /*pointcloud*/, const char *name)
288{
289 return STREQ(name, POINTCLOUD_ATTR_POSITION);
290}
291
292/* Dependency Graph */
293
295{
296 return reinterpret_cast<PointCloud *>(
297 BKE_id_copy_ex(nullptr, &pointcloud_src->id, nullptr, LIB_ID_COPY_LOCALIZE));
298}
299
301 Scene *scene,
302 Object *object,
303 blender::bke::GeometrySet &geometry_set)
304{
305 /* Modifier evaluation modes. */
306 const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
307 const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
308 ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE;
309 const ModifierEvalContext mectx = {depsgraph, object, apply_flag};
310
312
313 /* Get effective list of modifiers to execute. Some effects like shape keys
314 * are added as virtual modifiers before the user created modifiers. */
315 VirtualModifierData virtual_modifier_data;
316 ModifierData *md = BKE_modifiers_get_virtual_modifierlist(object, &virtual_modifier_data);
317
318 /* Evaluate modifiers. */
319 for (; md; md = md->next) {
321
322 if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
323 continue;
324 }
325
326 blender::bke::ScopedModifierTimer modifier_timer{*md};
327
328 if (mti->modify_geometry_set) {
329 mti->modify_geometry_set(md, &mectx, &geometry_set);
330 }
331 }
332}
333
335 blender::bke::GeometrySet &geometry_set)
336{
337 if (!geometry_set.has<blender::bke::PointCloudComponent>()) {
338 return nullptr;
339 }
340 blender::bke::PointCloudComponent &pointcloud_component =
342 PointCloud *pointcloud = pointcloud_component.release();
343 if (pointcloud != nullptr) {
344 /* Add back, but as read-only non-owning component. */
345 pointcloud_component.replace(pointcloud, blender::bke::GeometryOwnershipType::ReadOnly);
346 }
347 else {
348 /* The component was empty, we can also remove it. */
350 }
351 return pointcloud;
352}
353
354void BKE_pointcloud_data_update(Depsgraph *depsgraph, Scene *scene, Object *object)
355{
356 /* Free any evaluated data and restore original data. */
358
359 /* Evaluate modifiers. */
360 PointCloud *pointcloud = static_cast<PointCloud *>(object->data);
363 pointcloud_evaluate_modifiers(depsgraph, scene, object, geometry_set);
364
365 PointCloud *pointcloud_eval = take_pointcloud_ownership_from_geometry_set(geometry_set);
366
367 /* If the geometry set did not contain a point cloud, we still create an empty one. */
368 if (pointcloud_eval == nullptr) {
369 pointcloud_eval = BKE_pointcloud_new_nomain(0);
370 }
371
372 /* Assign evaluated object. */
373 const bool eval_is_owned = pointcloud_eval != pointcloud;
374 BKE_object_eval_assign_data(object, &pointcloud_eval->id, eval_is_owned);
375 object->runtime->geometry_set_eval = new blender::bke::GeometrySet(std::move(geometry_set));
376}
377
378void PointCloud::tag_positions_changed()
379{
380 this->runtime->bounds_cache.tag_dirty();
381}
382
383void PointCloud::tag_radii_changed()
384{
385 this->runtime->bounds_cache.tag_dirty();
386}
387
388/* Draw Cache */
389
390void (*BKE_pointcloud_batch_cache_dirty_tag_cb)(PointCloud *pointcloud, int mode) = nullptr;
391void (*BKE_pointcloud_batch_cache_free_cb)(PointCloud *pointcloud) = nullptr;
392
394{
395 if (pointcloud->batch_cache) {
397 }
398}
399
401{
402 if (pointcloud->batch_cache) {
404 }
405}
406
407namespace blender::bke {
408
410{
411 PointCloud *pointcloud = BKE_pointcloud_new_nomain(0);
412 pointcloud->totpoint = totpoint;
413 CustomData_free_layer_named(&pointcloud->pdata, "position", 0);
414 return pointcloud;
415}
416
417} // namespace blender::bke
void BKE_animdata_free(ID *id, bool do_id_user)
Definition anim_data.cc:263
CustomData interface, see also DNA_customdata_types.h.
void CustomData_blend_write_prepare(CustomData &data, blender::Vector< CustomDataLayer, 16 > &layers_to_write, const blender::Set< std::string > &skip_names={})
void CustomData_count_memory(const CustomData &data, int totelem, blender::MemoryCounter &memory)
void CustomData_realloc(CustomData *data, int old_size, int new_size, eCDAllocType alloctype=CD_CONSTRUCT)
void * CustomData_get_layer_named_for_write(CustomData *data, eCustomDataType type, blender::StringRef name, int totelem)
bool CustomData_free_layer_named(CustomData *data, blender::StringRef name, const int totelem)
const void * CustomData_get_layer_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
void CustomData_reset(CustomData *data)
void CustomData_blend_write(BlendWriter *writer, CustomData *data, blender::Span< CustomDataLayer > layers_to_write, int count, eCustomDataMask cddata_mask, ID *id)
void CustomData_init_from(const CustomData *source, CustomData *dest, eCustomDataMask mask, int totelem)
void CustomData_free(CustomData *data, int totelem)
void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count)
@ IDTYPE_FLAGS_APPEND_IS_REUSABLE
Definition BKE_idtype.hh:39
const char * BKE_idtype_idcode_to_name(short idcode)
Definition idtype.cc:168
void * BKE_libblock_alloc(Main *bmain, short type, const char *name, int flag) ATTR_WARN_UNUSED_RESULT
Definition lib_id.cc:1415
@ LIB_ID_CREATE_LOCALIZE
@ LIB_ID_COPY_LOCALIZE
void BKE_id_free(Main *bmain, void *idv)
ID * BKE_id_copy_ex(Main *bmain, const ID *id, ID **new_id_p, int flag)
Definition lib_id.cc:760
void * BKE_id_new(Main *bmain, short type, const char *name)
Definition lib_id.cc:1482
void BKE_id_blend_write(BlendWriter *writer, ID *id)
Definition lib_id.cc:2560
#define BKE_LIB_FOREACHID_PROCESS_IDSUPER(data_, id_super_, cb_flag_)
@ IDWALK_CB_USER
void BKE_modifiers_clear_errors(Object *ob)
bool BKE_modifier_is_enabled(const Scene *scene, ModifierData *md, int required_mode)
const ModifierTypeInfo * BKE_modifier_get_info(ModifierType type)
ModifierData * BKE_modifiers_get_virtual_modifierlist(const Object *ob, VirtualModifierData *data)
ModifierApplyFlag
@ MOD_APPLY_USECACHE
@ MOD_APPLY_RENDER
General operations, lookup, etc. for blender objects.
void BKE_object_eval_assign_data(Object *object, ID *data, bool is_owned)
void BKE_object_free_derived_caches(Object *ob)
General operations for point clouds.
const char * POINTCLOUD_ATTR_RADIUS
Definition pointcloud.cc:56
PointCloud * BKE_pointcloud_new_nomain(int totpoint)
#define BLI_assert(a)
Definition BLI_assert.h:50
Random number functions.
struct RNG * BLI_rng_new(unsigned int seed)
Definition rand.cc:39
void BLI_rng_free(struct RNG *rng) ATTR_NONNULL(1)
Definition rand.cc:58
float BLI_rng_get_float(struct RNG *rng) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition rand.cc:93
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define MEMCPY_STRUCT_AFTER(struct_dst, struct_src, member)
#define STREQ(a, b)
#define BLO_write_id_struct(writer, struct_name, id_address, id)
void BLO_read_pointer_array(BlendDataReader *reader, int array_size, void **ptr_p)
Definition readfile.cc:5052
void BLO_write_pointer_array(BlendWriter *writer, uint num, const void *data_ptr)
#define BLT_I18NCONTEXT_ID_POINTCLOUD
@ DAG_EVAL_RENDER
eEvaluationMode DEG_get_mode(const Depsgraph *graph)
#define FILTER_ID_MA
Definition DNA_ID.h:1175
#define FILTER_ID_PT
Definition DNA_ID.h:1196
@ ID_TAG_NO_MAIN
Definition DNA_ID.h:945
@ INDEX_ID_PT
Definition DNA_ID.h:1297
@ ID_PT
#define CD_MASK_ALL
@ CD_PROP_FLOAT3
#define DNA_struct_default_get(struct_name)
@ eModifierMode_Render
@ eModifierMode_Realtime
Object is a sort of wrapper for general info.
struct PointCloud PointCloud
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
AttributeSet attributes
void replace(PointCloud *pointcloud, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
const Depsgraph * depsgraph
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
PointCloud * pointcloud_new_no_attributes(int totpoint)
static void pointcloud_init_data(ID *id)
Definition pointcloud.cc:58
PointCloud * BKE_pointcloud_copy_for_eval(const PointCloud *pointcloud_src)
void BKE_pointcloud_nomain_to_pointcloud(PointCloud *pointcloud_src, PointCloud *pointcloud_dst)
static void pointcloud_free_data(ID *id)
Definition pointcloud.cc:96
static void pointcloud_foreach_id(ID *id, LibraryForeachIDData *data)
const char * POINTCLOUD_ATTR_RADIUS
Definition pointcloud.cc:56
PointCloud * BKE_pointcloud_new_nomain(const int totpoint)
void BKE_pointcloud_batch_cache_dirty_tag(PointCloud *pointcloud, int mode)
void * BKE_pointcloud_add(Main *bmain, const char *name)
void BKE_pointcloud_batch_cache_free(PointCloud *pointcloud)
static PointCloud * take_pointcloud_ownership_from_geometry_set(blender::bke::GeometrySet &geometry_set)
IDTypeInfo IDType_ID_PT
void * BKE_pointcloud_add_default(Main *bmain, const char *name)
bool BKE_pointcloud_attribute_required(const PointCloud *, const char *name)
static void pointcloud_blend_write(BlendWriter *writer, ID *id, const void *id_address)
void BKE_pointcloud_data_update(Depsgraph *depsgraph, Scene *scene, Object *object)
static void pointcloud_blend_read_data(BlendDataReader *reader, ID *id)
const char * POINTCLOUD_ATTR_POSITION
Definition pointcloud.cc:55
static void pointcloud_copy_data(Main *, std::optional< Library * >, ID *id_dst, const ID *id_src, const int)
Definition pointcloud.cc:72
static void pointcloud_random(PointCloud *pointcloud)
void(* BKE_pointcloud_batch_cache_free_cb)(PointCloud *pointcloud)
static void pointcloud_evaluate_modifiers(Depsgraph *depsgraph, Scene *scene, Object *object, blender::bke::GeometrySet &geometry_set)
void(* BKE_pointcloud_batch_cache_dirty_tag_cb)(PointCloud *pointcloud, int mode)
Definition DNA_ID.h:413
int tag
Definition DNA_ID.h:434
struct ModifierData * next
void(* modify_geometry_set)(ModifierData *md, const ModifierEvalContext *ctx, blender::bke::GeometrySet *geometry_set)
struct Material ** mat
PointCloudRuntimeHandle * runtime
struct CustomData pdata
Definition rand.cc:33
GeometryComponent & get_component_for_write(GeometryComponent::Type component_type)
bool has(const GeometryComponent::Type component_type) const
static GeometrySet from_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
void remove(const GeometryComponent::Type component_type)
#define N_(msgid)