Blender V4.3
bake_items_serialize.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
5#include "BKE_bake_items.hh"
7#include "BKE_curves.hh"
8#include "BKE_customdata.hh"
10#include "BKE_instances.hh"
11#include "BKE_lib_id.hh"
12#include "BKE_mesh.hh"
13#include "BKE_pointcloud.hh"
14#include "BKE_volume.hh"
15
16#include "BLI_endian_defines.h"
17#include "BLI_endian_switch.h"
19#include "BLI_path_utils.hh"
20
21#include "DNA_material_types.h"
22#include "DNA_modifier_types.h"
23#include "DNA_volume_types.h"
24
25#include "RNA_access.hh"
26#include "RNA_enum_types.hh"
27
28#include <fmt/format.h>
29#include <sstream>
30#include <xxhash.h>
31
32#ifdef WITH_OPENVDB
33# include <openvdb/io/Stream.h>
34# include <openvdb/openvdb.h>
35
36# include "BKE_volume_grid.hh"
37#endif
38
39namespace blender::bke::bake {
40
41using namespace io::serialize;
42using DictionaryValuePtr = std::shared_ptr<DictionaryValue>;
43
44std::shared_ptr<DictionaryValue> BlobSlice::serialize() const
45{
46 auto io_slice = std::make_shared<DictionaryValue>();
47 io_slice->append_str("name", this->name);
48 io_slice->append_int("start", range.start());
49 io_slice->append_int("size", range.size());
50 return io_slice;
51}
52
53std::optional<BlobSlice> BlobSlice::deserialize(const DictionaryValue &io_slice)
54{
55 const std::optional<StringRefNull> name = io_slice.lookup_str("name");
56 const std::optional<int64_t> start = io_slice.lookup_int("start");
57 const std::optional<int64_t> size = io_slice.lookup_int("size");
58 if (!name || !start || !size) {
59 return std::nullopt;
60 }
61
62 return BlobSlice{*name, {*start, *size}};
63}
64
66 const FunctionRef<void(std::ostream &)> fn)
67{
68 std::ostringstream stream{std::ios::binary};
69 fn(stream);
70 std::string data = stream.rdbuf()->str();
71 return this->write(data.data(), data.size());
72}
73
74bool BlobReader::read_as_stream(const BlobSlice &slice, FunctionRef<bool(std::istream &)> fn) const
75{
76 const int64_t size = slice.range.size();
77 std::string buffer;
78 buffer.resize(size);
79 if (!this->read(slice, buffer.data())) {
80 return false;
81 }
82 std::istringstream stream{buffer, std::ios::binary};
83 if (!fn(stream)) {
84 return false;
85 }
86 return true;
87}
88
89DiskBlobReader::DiskBlobReader(std::string blobs_dir) : blobs_dir_(std::move(blobs_dir)) {}
90
91[[nodiscard]] bool DiskBlobReader::read(const BlobSlice &slice, void *r_data) const
92{
93 if (slice.range.is_empty()) {
94 return true;
95 }
96
97 char blob_path[FILE_MAX];
98 BLI_path_join(blob_path, sizeof(blob_path), blobs_dir_.c_str(), slice.name.c_str());
99
100 std::lock_guard lock{mutex_};
101 std::unique_ptr<fstream> &blob_file = open_input_streams_.lookup_or_add_cb_as(blob_path, [&]() {
102 return std::make_unique<fstream>(blob_path, std::ios::in | std::ios::binary);
103 });
104 blob_file->seekg(slice.range.start());
105 blob_file->read(static_cast<char *>(r_data), slice.range.size());
106 if (blob_file->gcount() != slice.range.size()) {
107 return false;
108 }
109 return true;
110}
111
112DiskBlobWriter::DiskBlobWriter(std::string blob_dir, std::string base_name)
113 : blob_dir_(std::move(blob_dir)), base_name_(std::move(base_name))
114{
115 blob_name_ = base_name_ + ".blob";
116}
117
118BlobSlice DiskBlobWriter::write(const void *data, const int64_t size)
119{
120 if (!blob_stream_.is_open()) {
121 char blob_path[FILE_MAX];
122 BLI_path_join(blob_path, sizeof(blob_path), blob_dir_.c_str(), blob_name_.c_str());
124 blob_stream_.open(blob_path, std::ios::out | std::ios::binary);
125 }
126
127 const int64_t old_offset = current_offset_;
128 blob_stream_.write(static_cast<const char *>(data), size);
129 current_offset_ += size;
131 return {blob_name_, {old_offset, size}};
132}
133
134static std::string make_independent_file_name(const StringRef base_name,
135 const int file_index,
136 const StringRef extension)
137{
138 return fmt::format("{}_file_{}{}", base_name, file_index, extension);
139}
140
142 const FunctionRef<void(std::ostream &)> fn)
143{
144 BLI_assert(file_extension.startswith("."));
145 independent_file_count_++;
146 const std::string file_name = make_independent_file_name(
147 base_name_, independent_file_count_, file_extension);
148
149 char path[FILE_MAX];
150 BLI_path_join(path, sizeof(path), blob_dir_.c_str(), file_name.c_str());
152 std::fstream stream{path, std::ios::out | std::ios::binary};
153 fn(stream);
154 const int64_t written_bytes_num = stream.tellg();
155 total_written_size_ += written_bytes_num;
156 return {file_name, {0, written_bytes_num}};
157}
158
160{
161 blob_by_name_.add(name, blob);
162}
163
164bool MemoryBlobReader::read(const BlobSlice &slice, void *r_data) const
165{
166 if (slice.range.is_empty()) {
167 return true;
168 }
169 const Span<std::byte> blob_data = blob_by_name_.lookup_default(slice.name, {});
170 if (!blob_data.index_range().contains(slice.range)) {
171 return false;
172 }
173 const void *copy_src = blob_data.slice(slice.range).data();
174 memcpy(r_data, copy_src, slice.range.size());
175 return true;
176}
177
178MemoryBlobWriter::MemoryBlobWriter(std::string base_name) : base_name_(std::move(base_name))
179{
180 blob_name_ = base_name_ + ".blob";
181 stream_by_name_.add(blob_name_, {std::make_unique<std::ostringstream>(std::ios::binary)});
182}
183
185{
186 OutputStream &stream = stream_by_name_.lookup(blob_name_);
187 const int64_t old_offset = stream.offset;
188 stream.stream->write(static_cast<const char *>(data), size);
189 stream.offset += size;
191 return {blob_name_, IndexRange::from_begin_size(old_offset, size)};
192}
193
195 const FunctionRef<void(std::ostream &)> fn)
196{
197 BLI_assert(file_extension.startswith("."));
198 independent_file_count_++;
199 const std::string name = make_independent_file_name(
200 base_name_, independent_file_count_, file_extension);
201 OutputStream stream{std::make_unique<std::ostringstream>(std::ios::binary)};
202 fn(*stream.stream);
203 const int64_t size = stream.stream->tellp();
204 stream_by_name_.add_new(name, std::move(stream));
206 return {base_name_, IndexRange(size)};
207}
208
210{
211 for (const ImplicitSharingInfo *sharing_info : stored_by_runtime_.keys()) {
212 sharing_info->remove_weak_user_and_delete_if_last();
213 }
214}
215
217{
218 for (const ImplicitSharingInfoAndData &value : runtime_by_stored_.values()) {
219 if (value.sharing_info) {
220 value.sharing_info->remove_user_and_delete_if_last();
221 }
222 }
223}
224
226 const ImplicitSharingInfo *sharing_info, FunctionRef<DictionaryValuePtr()> write_fn)
227{
228 if (sharing_info == nullptr) {
229 return write_fn();
230 }
231 return stored_by_runtime_.add_or_modify(
232 sharing_info,
233 /* Create new value. */
234 [&](StoredByRuntimeValue *value) {
235 new (value) StoredByRuntimeValue();
236 value->io_data = write_fn();
237 value->sharing_info_version = sharing_info->version();
238 sharing_info->add_weak_user();
239 return value->io_data;
240 },
241 /* Potentially modify existing value. */
242 [&](StoredByRuntimeValue *value) {
243 const int64_t new_version = sharing_info->version();
244 BLI_assert(value->sharing_info_version <= new_version);
245 if (value->sharing_info_version < new_version) {
246 value->io_data = write_fn();
247 value->sharing_info_version = new_version;
248 }
249 return value->io_data;
250 });
251}
252
253std::shared_ptr<io::serialize::DictionaryValue> BlobWriteSharing::write_deduplicated(
254 BlobWriter &writer, const void *data, const int64_t size_in_bytes)
255{
256 const uint64_t content_hash = XXH3_64bits(data, size_in_bytes);
257 const BlobSlice slice = slice_by_content_hash_.lookup_or_add_cb(
258 content_hash, [&]() { return writer.write(data, size_in_bytes); });
259 return slice.serialize();
260}
261
262std::optional<ImplicitSharingInfoAndData> BlobReadSharing::read_shared(
263 const DictionaryValue &io_data,
264 FunctionRef<std::optional<ImplicitSharingInfoAndData>()> read_fn) const
265{
266 std::lock_guard lock{mutex_};
267
269 std::stringstream ss;
270 formatter.serialize(ss, io_data);
271 const std::string key = ss.str();
272
273 if (const ImplicitSharingInfoAndData *shared_data = runtime_by_stored_.lookup_ptr(key)) {
274 shared_data->sharing_info->add_user();
275 return *shared_data;
276 }
277 std::optional<ImplicitSharingInfoAndData> data = read_fn();
278 if (!data) {
279 return std::nullopt;
280 }
281 if (data->sharing_info != nullptr) {
282 data->sharing_info->add_user();
283 runtime_by_stored_.add_new(key, *data);
284 }
285 return data;
286}
287
288static StringRefNull get_endian_io_name(const int endian)
289{
290 if (endian == L_ENDIAN) {
291 return "little";
292 }
293 BLI_assert(endian == B_ENDIAN);
294 return "big";
295}
296
298{
299 const char *io_name = "unknown";
301 return io_name;
302}
303
305{
306 const char *io_name = "unknown";
308 return io_name;
309}
310
311static std::optional<AttrDomain> get_domain_from_io_name(const StringRefNull io_name)
312{
313 int domain;
315 return std::nullopt;
316 }
317 return AttrDomain(domain);
318}
319
320static std::optional<eCustomDataType> get_data_type_from_io_name(const StringRefNull io_name)
321{
322 int domain;
324 return std::nullopt;
325 }
326 return eCustomDataType(domain);
327}
328
332static std::shared_ptr<DictionaryValue> write_blob_raw_data_with_endian(
333 BlobWriter &blob_writer,
334 BlobWriteSharing &blob_sharing,
335 const void *data,
336 const int64_t size_in_bytes)
337{
338 auto io_data = blob_sharing.write_deduplicated(blob_writer, data, size_in_bytes);
339 if (ENDIAN_ORDER == B_ENDIAN) {
340 io_data->append_str("endian", get_endian_io_name(ENDIAN_ORDER));
341 }
342 return io_data;
343}
344
348[[nodiscard]] static bool read_blob_raw_data_with_endian(const BlobReader &blob_reader,
349 const DictionaryValue &io_data,
350 const int64_t element_size,
351 const int64_t elements_num,
352 void *r_data)
353{
354 const std::optional<BlobSlice> slice = BlobSlice::deserialize(io_data);
355 if (!slice) {
356 return false;
357 }
358 if (slice->range.size() != element_size * elements_num) {
359 return false;
360 }
361 if (!blob_reader.read(*slice, r_data)) {
362 return false;
363 }
364 const StringRefNull stored_endian = io_data.lookup_str("endian").value_or("little");
365 const StringRefNull current_endian = get_endian_io_name(ENDIAN_ORDER);
366 const bool need_endian_switch = stored_endian != current_endian;
367 if (need_endian_switch) {
368 switch (element_size) {
369 case 1:
370 break;
371 case 2:
372 BLI_endian_switch_uint16_array(static_cast<uint16_t *>(r_data), elements_num);
373 break;
374 case 4:
375 BLI_endian_switch_uint32_array(static_cast<uint32_t *>(r_data), elements_num);
376 break;
377 case 8:
378 BLI_endian_switch_uint64_array(static_cast<uint64_t *>(r_data), elements_num);
379 break;
380 default:
381 return false;
382 }
383 }
384 return true;
385}
386
388static std::shared_ptr<DictionaryValue> write_blob_raw_bytes(BlobWriter &blob_writer,
389 BlobWriteSharing &blob_sharing,
390 const void *data,
391 const int64_t size_in_bytes)
392{
393 return blob_sharing.write_deduplicated(blob_writer, data, size_in_bytes);
394}
395
397[[nodiscard]] static bool read_blob_raw_bytes(const BlobReader &blob_reader,
398 const DictionaryValue &io_data,
399 const int64_t bytes_num,
400 void *r_data)
401{
402 const std::optional<BlobSlice> slice = BlobSlice::deserialize(io_data);
403 if (!slice) {
404 return false;
405 }
406 if (slice->range.size() != bytes_num) {
407 return false;
408 }
409 return blob_reader.read(*slice, r_data);
410}
411
412static std::shared_ptr<DictionaryValue> write_blob_simple_gspan(BlobWriter &blob_writer,
413 BlobWriteSharing &blob_sharing,
414 const GSpan data)
415{
416 const CPPType &type = data.type();
417 BLI_assert(type.is_trivial());
418 if (type.size() == 1 || type.is<ColorGeometry4b>()) {
419 return write_blob_raw_bytes(blob_writer, blob_sharing, data.data(), data.size_in_bytes());
420 }
422 blob_writer, blob_sharing, data.data(), data.size_in_bytes());
423}
424
425[[nodiscard]] static bool read_blob_simple_gspan(const BlobReader &blob_reader,
426 const DictionaryValue &io_data,
427 GMutableSpan r_data)
428{
429 const CPPType &type = r_data.type();
430 BLI_assert(type.is_trivial());
431 if (type.size() == 1 || type.is<ColorGeometry4b>()) {
432 return read_blob_raw_bytes(blob_reader, io_data, r_data.size_in_bytes(), r_data.data());
433 }
434 if (type.is_any<int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, float>()) {
436 blob_reader, io_data, type.size(), r_data.size(), r_data.data());
437 }
438 if (type.is_any<float2, int2>()) {
440 blob_reader, io_data, sizeof(int32_t), r_data.size() * 2, r_data.data());
441 }
442 if (type.is<float3>()) {
444 blob_reader, io_data, sizeof(float), r_data.size() * 3, r_data.data());
445 }
446 if (type.is<float4x4>()) {
448 blob_reader, io_data, sizeof(float), r_data.size() * 16, r_data.data());
449 }
450 if (type.is<ColorGeometry4f>()) {
452 blob_reader, io_data, sizeof(float), r_data.size() * 4, r_data.data());
453 }
454 if (type.is<math::Quaternion>()) {
456 blob_reader, io_data, sizeof(float), r_data.size() * 4, r_data.data());
457 }
458 return false;
459}
460
461static std::shared_ptr<DictionaryValue> write_blob_shared_simple_gspan(
462 BlobWriter &blob_writer,
463 BlobWriteSharing &blob_sharing,
464 const GSpan data,
465 const ImplicitSharingInfo *sharing_info)
466{
467 return blob_sharing.write_implicitly_shared(
468 sharing_info, [&]() { return write_blob_simple_gspan(blob_writer, blob_sharing, data); });
469}
470
471[[nodiscard]] static const void *read_blob_shared_simple_gspan(
472 const DictionaryValue &io_data,
473 const BlobReader &blob_reader,
474 const BlobReadSharing &blob_sharing,
475 const CPPType &cpp_type,
476 const int size,
477 const ImplicitSharingInfo **r_sharing_info)
478{
479 const char *func = __func__;
480 const std::optional<ImplicitSharingInfoAndData> sharing_info_and_data = blob_sharing.read_shared(
481 io_data, [&]() -> std::optional<ImplicitSharingInfoAndData> {
482 void *data_mem = MEM_mallocN_aligned(size * cpp_type.size(), cpp_type.alignment(), func);
483 if (!read_blob_simple_gspan(blob_reader, io_data, {cpp_type, data_mem, size})) {
484 MEM_freeN(data_mem);
485 return std::nullopt;
486 }
488 });
489 if (!sharing_info_and_data) {
490 *r_sharing_info = nullptr;
491 return nullptr;
492 }
493 *r_sharing_info = sharing_info_and_data->sharing_info;
494 return sharing_info_and_data->data;
495}
496
497template<typename T>
498[[nodiscard]] static bool read_blob_shared_simple_span(const DictionaryValue &io_data,
499 const BlobReader &blob_reader,
500 const BlobReadSharing &blob_sharing,
501 const int size,
502 T **r_data,
503 const ImplicitSharingInfo **r_sharing_info)
504{
505 *r_data = const_cast<T *>(static_cast<const T *>(read_blob_shared_simple_gspan(
506 io_data, blob_reader, blob_sharing, CPPType::get<T>(), size, r_sharing_info)));
507 return *r_data != nullptr;
508}
509
510[[nodiscard]] static bool load_materials(const io::serialize::ArrayValue &io_materials,
511 std::unique_ptr<BakeMaterialsList> &materials)
512{
513 if (io_materials.elements().is_empty()) {
514 return true;
515 }
516 materials = std::make_unique<BakeMaterialsList>();
517 for (const auto &io_material_value : io_materials.elements()) {
518 if (io_material_value->type() == io::serialize::eValueType::Null) {
519 materials->append(std::nullopt);
520 continue;
521 }
522 const auto *io_material = io_material_value->as_dictionary_value();
523 if (!io_material) {
524 return false;
525 }
526 std::optional<std::string> id_name = io_material->lookup_str("name");
527 if (!id_name) {
528 return false;
529 }
530 std::string lib_name = io_material->lookup_str("lib_name").value_or("");
531 materials->append(BakeDataBlockID(ID_MA, std::move(*id_name), std::move(lib_name)));
532 }
533 return true;
534}
535
536[[nodiscard]] static bool load_attributes(const io::serialize::ArrayValue &io_attributes,
537 MutableAttributeAccessor &attributes,
538 const BlobReader &blob_reader,
539 const BlobReadSharing &blob_sharing)
540{
541 for (const auto &io_attribute_value : io_attributes.elements()) {
542 const auto *io_attribute = io_attribute_value->as_dictionary_value();
543 if (!io_attribute) {
544 return false;
545 }
546 const std::optional<StringRefNull> name = io_attribute->lookup_str("name");
547 const std::optional<StringRefNull> domain_str = io_attribute->lookup_str("domain");
548 const std::optional<StringRefNull> type_str = io_attribute->lookup_str("type");
549 const auto *io_data = io_attribute->lookup_dict("data");
550 if (!name || !domain_str || !type_str || !io_data) {
551 return false;
552 }
553
554 const std::optional<AttrDomain> domain = get_domain_from_io_name(*domain_str);
555 const std::optional<eCustomDataType> data_type = get_data_type_from_io_name(*type_str);
556 if (!domain || !data_type) {
557 return false;
558 }
559 const CPPType *cpp_type = custom_data_type_to_cpp_type(*data_type);
560 if (!cpp_type) {
561 return false;
562 }
563 const int domain_size = attributes.domain_size(*domain);
564 const ImplicitSharingInfo *attribute_sharing_info;
565 const void *attribute_data = read_blob_shared_simple_gspan(
566 *io_data, blob_reader, blob_sharing, *cpp_type, domain_size, &attribute_sharing_info);
567 if (!attribute_data) {
568 return false;
569 }
570 BLI_SCOPED_DEFER([&]() { attribute_sharing_info->remove_user_and_delete_if_last(); });
571
572 if (attributes.contains(*name)) {
573 /* If the attribute exists already, copy the values over to the existing array. */
574 GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_only_span(
575 *name, *domain, *data_type);
576 if (!attribute) {
577 return false;
578 }
579 cpp_type->copy_assign_n(attribute_data, attribute.span.data(), domain_size);
580 attribute.finish();
581 }
582 else {
583 /* Add a new attribute that shares the data. */
584 if (!attributes.add(*name,
585 *domain,
586 *data_type,
587 AttributeInitShared(attribute_data, *attribute_sharing_info)))
588 {
589 return false;
590 }
591 }
592 }
593 return true;
594}
595
597 const BlobReader &blob_reader,
598 const BlobReadSharing &blob_sharing)
599{
600 const DictionaryValue *io_pointcloud = io_geometry.lookup_dict("pointcloud");
601 if (!io_pointcloud) {
602 return nullptr;
603 }
604 const io::serialize::ArrayValue *io_attributes = io_pointcloud->lookup_array("attributes");
605 if (!io_attributes) {
606 return nullptr;
607 }
608 PointCloud *pointcloud = BKE_pointcloud_new_nomain(0);
609 CustomData_free_layer_named(&pointcloud->pdata, "position", 0);
610 pointcloud->totpoint = io_pointcloud->lookup_int("num_points").value_or(0);
611
612 auto cancel = [&]() {
613 BKE_id_free(nullptr, pointcloud);
614 return nullptr;
615 };
616
617 MutableAttributeAccessor attributes = pointcloud->attributes_for_write();
618 if (!load_attributes(*io_attributes, attributes, blob_reader, blob_sharing)) {
619 return cancel();
620 }
621
622 if (const io::serialize::ArrayValue *io_materials = io_pointcloud->lookup_array("materials")) {
623 if (!load_materials(*io_materials, pointcloud->runtime->bake_materials)) {
624 return cancel();
625 }
626 }
627 return pointcloud;
628}
629
630static std::optional<CurvesGeometry> try_load_curves_geometry(const DictionaryValue &io_curves,
631 const BlobReader &blob_reader,
632 const BlobReadSharing &blob_sharing)
633{
634 const io::serialize::ArrayValue *io_attributes = io_curves.lookup_array("attributes");
635 if (!io_attributes) {
636 return std::nullopt;
637 }
638
639 CurvesGeometry curves;
640 CustomData_free_layer_named(&curves.point_data, "position", 0);
641 curves.point_num = io_curves.lookup_int("num_points").value_or(0);
642 curves.curve_num = io_curves.lookup_int("num_curves").value_or(0);
643
644 if (curves.curves_num() > 0) {
645 const auto *io_curve_offsets = io_curves.lookup_dict("curve_offsets");
646 if (!io_curve_offsets) {
647 return std::nullopt;
648 }
649 if (!read_blob_shared_simple_span(*io_curve_offsets,
650 blob_reader,
651 blob_sharing,
652 curves.curves_num() + 1,
653 &curves.curve_offsets,
654 &curves.runtime->curve_offsets_sharing_info))
655 {
656 return std::nullopt;
657 }
658 }
659
660 MutableAttributeAccessor attributes = curves.attributes_for_write();
661 if (!load_attributes(*io_attributes, attributes, blob_reader, blob_sharing)) {
662 return std::nullopt;
663 }
664
665 if (const io::serialize::ArrayValue *io_materials = io_curves.lookup_array("materials")) {
666 if (!load_materials(*io_materials, curves.runtime->bake_materials)) {
667 return std::nullopt;
668 }
669 }
670
671 curves.update_curve_types();
672 return curves;
673}
674
675static Curves *try_load_curves(const DictionaryValue &io_geometry,
676 const BlobReader &blob_reader,
677 const BlobReadSharing &blob_sharing)
678{
679 const DictionaryValue *io_curves = io_geometry.lookup_dict("curves");
680 if (!io_curves) {
681 return nullptr;
682 }
683
684 const io::serialize::ArrayValue *io_attributes = io_curves->lookup_array("attributes");
685 if (!io_attributes) {
686 return nullptr;
687 }
688
689 std::optional<CurvesGeometry> curves_opt = try_load_curves_geometry(
690 *io_curves, blob_reader, blob_sharing);
691 if (!curves_opt) {
692 return nullptr;
693 }
694
695 Curves *curves_id = curves_new_nomain(std::move(*curves_opt));
696 CurvesGeometry &curves = curves_id->geometry.wrap();
697
698 auto cancel = [&]() {
699 BKE_id_free(nullptr, curves_id);
700 return nullptr;
701 };
702
703 if (const io::serialize::ArrayValue *io_materials = io_curves->lookup_array("materials")) {
704 if (!load_materials(*io_materials, curves.runtime->bake_materials)) {
705 return cancel();
706 }
707 }
708
709 return curves_id;
710}
711
713 const BlobReader &blob_reader,
714 const BlobReadSharing &blob_sharing)
715{
716 const DictionaryValue *io_grease_pencil = io_geometry.lookup_dict("grease_pencil");
717 if (!io_grease_pencil) {
718 return nullptr;
719 }
720
721 const io::serialize::ArrayValue *io_layers = io_grease_pencil->lookup_array("layers");
722 if (!io_layers) {
723 return nullptr;
724 }
725
726 const io::serialize::ArrayValue *io_layer_attributes = io_grease_pencil->lookup_array(
727 "layer_attributes");
728 if (!io_layer_attributes) {
729 return nullptr;
730 }
731
733 auto cancel = [&]() {
734 BKE_id_free(nullptr, grease_pencil);
735 return nullptr;
736 };
737
738 const int layers_num = io_layers->elements().size();
739 grease_pencil->add_layers_with_empty_drawings_for_eval(layers_num);
740
741 for (const int layer_i : io_layers->elements().index_range()) {
742 const auto &io_layer_value = io_layers->elements()[layer_i];
743 const io::serialize::DictionaryValue *io_layer = io_layer_value->as_dictionary_value();
744 if (!io_layer) {
745 return cancel();
746 }
747 const io::serialize::DictionaryValue *io_strokes = io_layer->lookup_dict("strokes");
748 if (!io_strokes) {
749 return cancel();
750 }
751 const std::optional<std::string> layer_name = io_layer->lookup_str("name");
752 if (!layer_name) {
753 return cancel();
754 }
755 greasepencil::Layer &layer = grease_pencil->layer(layer_i);
756 layer.set_name(*layer_name);
757 std::optional<CurvesGeometry> curves_opt = try_load_curves_geometry(
758 *io_strokes, blob_reader, blob_sharing);
759 if (!curves_opt) {
760 return cancel();
761 }
762 greasepencil::Drawing &drawing = *grease_pencil->get_eval_drawing(layer);
763 drawing.strokes_for_write() = std::move(*curves_opt);
764 }
765
766 MutableAttributeAccessor attributes = grease_pencil->attributes_for_write();
767 if (!load_attributes(*io_layer_attributes, attributes, blob_reader, blob_sharing)) {
768 return cancel();
769 }
770
771 const DictionaryValue *io_layer_opacities = io_grease_pencil->lookup_dict("opacities");
772 Array<float> layer_opacities(layers_num);
773 if (!io_layer_opacities ||
774 !read_blob_simple_gspan(blob_reader, *io_layer_opacities, layer_opacities.as_mutable_span()))
775 {
776 return cancel();
777 }
778
779 const DictionaryValue *io_layer_blend_modes = io_grease_pencil->lookup_dict("blend_modes");
780 Array<int8_t> layer_blend_modes(layers_num);
781 if (!io_layer_opacities || !read_blob_simple_gspan(blob_reader,
782 *io_layer_blend_modes,
783 layer_blend_modes.as_mutable_span()))
784 {
785 return cancel();
786 }
787
788 const DictionaryValue *io_layer_transforms = io_grease_pencil->lookup_dict("transforms");
789 Array<float4x4> layer_transforms(layers_num);
790 if (!io_layer_transforms || !read_blob_simple_gspan(blob_reader,
791 *io_layer_transforms,
792 layer_transforms.as_mutable_span()))
793 {
794 return cancel();
795 }
796
797 for (const int layer_i : IndexRange(layers_num)) {
798 greasepencil::Layer &layer = grease_pencil->layer(layer_i);
799 layer.opacity = layer_opacities[layer_i];
800 layer.blend_mode = layer_blend_modes[layer_i];
801 layer.set_local_transform(layer_transforms[layer_i]);
802 }
803
804 if (const io::serialize::ArrayValue *io_materials = io_grease_pencil->lookup_array("materials"))
805 {
806 if (!load_materials(*io_materials, grease_pencil->runtime->bake_materials)) {
807 return cancel();
808 }
809 }
810
811 return grease_pencil;
812}
813
814static Mesh *try_load_mesh(const DictionaryValue &io_geometry,
815 const BlobReader &blob_reader,
816 const BlobReadSharing &blob_sharing)
817{
818 const DictionaryValue *io_mesh = io_geometry.lookup_dict("mesh");
819 if (!io_mesh) {
820 return nullptr;
821 }
822
823 const io::serialize::ArrayValue *io_attributes = io_mesh->lookup_array("attributes");
824 if (!io_attributes) {
825 return nullptr;
826 }
827
828 Mesh *mesh = BKE_mesh_new_nomain(0, 0, 0, 0);
829 CustomData_free_layer_named(&mesh->vert_data, "position", 0);
830 CustomData_free_layer_named(&mesh->edge_data, ".edge_verts", 0);
831 CustomData_free_layer_named(&mesh->corner_data, ".corner_vert", 0);
832 CustomData_free_layer_named(&mesh->corner_data, ".corner_edge", 0);
833 mesh->verts_num = io_mesh->lookup_int("num_vertices").value_or(0);
834 mesh->edges_num = io_mesh->lookup_int("num_edges").value_or(0);
835 mesh->faces_num = io_mesh->lookup_int("num_polygons").value_or(0);
836 mesh->corners_num = io_mesh->lookup_int("num_corners").value_or(0);
837
838 auto cancel = [&]() {
839 BKE_id_free(nullptr, mesh);
840 return nullptr;
841 };
842
843 if (mesh->faces_num > 0) {
844 const auto *io_poly_offsets = io_mesh->lookup_dict("poly_offsets");
845 if (!io_poly_offsets) {
846 return cancel();
847 }
848 if (!read_blob_shared_simple_span(*io_poly_offsets,
849 blob_reader,
850 blob_sharing,
851 mesh->faces_num + 1,
852 &mesh->face_offset_indices,
853 &mesh->runtime->face_offsets_sharing_info))
854 {
855 return cancel();
856 }
857 }
858
859 MutableAttributeAccessor attributes = mesh->attributes_for_write();
860 if (!load_attributes(*io_attributes, attributes, blob_reader, blob_sharing)) {
861 return cancel();
862 }
863
864 if (const io::serialize::ArrayValue *io_materials = io_mesh->lookup_array("materials")) {
865 if (!load_materials(*io_materials, mesh->runtime->bake_materials)) {
866 return cancel();
867 }
868 }
869
870 return mesh;
871}
872
873static GeometrySet load_geometry(const DictionaryValue &io_geometry,
874 const BlobReader &blob_reader,
875 const BlobReadSharing &blob_sharing);
876
877static std::unique_ptr<Instances> try_load_instances(const DictionaryValue &io_geometry,
878 const BlobReader &blob_reader,
879 const BlobReadSharing &blob_sharing)
880{
881 const DictionaryValue *io_instances = io_geometry.lookup_dict("instances");
882 if (!io_instances) {
883 return nullptr;
884 }
885 const int num_instances = io_instances->lookup_int("num_instances").value_or(0);
886 if (num_instances == 0) {
887 return nullptr;
888 }
889 const io::serialize::ArrayValue *io_attributes = io_instances->lookup_array("attributes");
890 if (!io_attributes) {
891 return nullptr;
892 }
893 const io::serialize::ArrayValue *io_references = io_instances->lookup_array("references");
894 if (!io_references) {
895 return nullptr;
896 }
897
898 std::unique_ptr<Instances> instances = std::make_unique<Instances>();
899 instances->resize(num_instances);
900
901 for (const auto &io_reference_value : io_references->elements()) {
902 const DictionaryValue *io_reference = io_reference_value->as_dictionary_value();
903 GeometrySet reference_geometry;
904 if (io_reference) {
905 reference_geometry = load_geometry(*io_reference, blob_reader, blob_sharing);
906 }
907 instances->add_new_reference(std::move(reference_geometry));
908 }
909
910 MutableAttributeAccessor attributes = instances->attributes_for_write();
911 if (!load_attributes(*io_attributes, attributes, blob_reader, blob_sharing)) {
912 return {};
913 }
914
915 if (!attributes.contains(".reference_index")) {
916 /* Try reading the reference index attribute from the old bake format from before it was an
917 * attribute. */
918 const auto *io_handles = io_instances->lookup_dict("handles");
919 if (!io_handles) {
920 return {};
921 }
923 blob_reader, *io_handles, instances->reference_handles_for_write()))
924 {
925 return {};
926 }
927 }
928
929 if (!attributes.contains("instance_transform")) {
930 /* Try reading the transform attribute from the old bake format from before it was an
931 * attribute. */
932 const auto *io_handles = io_instances->lookup_dict("transforms");
933 if (!io_handles) {
934 return {};
935 }
936 if (!read_blob_simple_gspan(blob_reader, *io_handles, instances->transforms_for_write())) {
937 return {};
938 }
939 }
940
941 return instances;
942}
943
944#ifdef WITH_OPENVDB
945static Volume *try_load_volume(const DictionaryValue &io_geometry, const BlobReader &blob_reader)
946{
947 const DictionaryValue *io_volume = io_geometry.lookup_dict("volume");
948 if (!io_volume) {
949 return nullptr;
950 }
951 const auto *io_vdb = io_volume->lookup_dict("vdb");
952 if (!io_vdb) {
953 return nullptr;
954 }
955 openvdb::GridPtrVecPtr vdb_grids;
956 if (std::optional<BlobSlice> vdb_slice = BlobSlice::deserialize(*io_vdb)) {
957 if (!blob_reader.read_as_stream(*vdb_slice, [&](std::istream &stream) {
958 try {
959 openvdb::io::Stream vdb_stream{stream};
960 vdb_grids = vdb_stream.getGrids();
961 return true;
962 }
963 catch (...) {
964 return false;
965 }
966 }))
967 {
968 return nullptr;
969 }
970 }
971 Volume *volume = reinterpret_cast<Volume *>(BKE_id_new_nomain(ID_VO, nullptr));
972 auto cancel = [&]() {
973 BKE_id_free(nullptr, volume);
974 return nullptr;
975 };
976
977 for (openvdb::GridBase::Ptr &vdb_grid : *vdb_grids) {
978 if (vdb_grid) {
979 bke::GVolumeGrid grid{std::move(vdb_grid)};
980 BKE_volume_grid_add(volume, *grid.release());
981 }
982 }
983 if (const io::serialize::ArrayValue *io_materials = io_volume->lookup_array("materials")) {
984 if (!load_materials(*io_materials, volume->runtime->bake_materials)) {
985 return cancel();
986 }
987 }
988 return volume;
989}
990#endif
991
992static GeometrySet load_geometry(const DictionaryValue &io_geometry,
993 const BlobReader &blob_reader,
994 const BlobReadSharing &blob_sharing)
995{
996 GeometrySet geometry;
997 geometry.replace_mesh(try_load_mesh(io_geometry, blob_reader, blob_sharing));
998 geometry.replace_pointcloud(try_load_pointcloud(io_geometry, blob_reader, blob_sharing));
999 geometry.replace_curves(try_load_curves(io_geometry, blob_reader, blob_sharing));
1000 geometry.replace_grease_pencil(try_load_grease_pencil(io_geometry, blob_reader, blob_sharing));
1001 geometry.replace_instances(try_load_instances(io_geometry, blob_reader, blob_sharing).release());
1002#ifdef WITH_OPENVDB
1003 geometry.replace_volume(try_load_volume(io_geometry, blob_reader));
1004#endif
1005 return geometry;
1006}
1007
1008static std::shared_ptr<io::serialize::ArrayValue> serialize_materials(
1009 const std::unique_ptr<BakeMaterialsList> &materials)
1010{
1011 auto io_materials = std::make_shared<io::serialize::ArrayValue>();
1012 if (!materials) {
1013 return io_materials;
1014 }
1015 for (const std::optional<BakeDataBlockID> &material : *materials) {
1016 if (material) {
1017 auto io_material = io_materials->append_dict();
1018 io_material->append_str("name", material->id_name);
1019 if (!material->lib_name.empty()) {
1020 io_material->append_str("lib_name", material->lib_name);
1021 }
1022 }
1023 else {
1024 io_materials->append_null();
1025 }
1026 }
1027 return io_materials;
1028}
1029
1030static std::shared_ptr<io::serialize::ArrayValue> serialize_attributes(
1031 const AttributeAccessor &attributes,
1032 BlobWriter &blob_writer,
1033 BlobWriteSharing &blob_sharing,
1034 const Set<std::string> &attributes_to_ignore)
1035{
1036 auto io_attributes = std::make_shared<io::serialize::ArrayValue>();
1037 attributes.foreach_attribute([&](const AttributeIter &iter) {
1039 if (attributes_to_ignore.contains_as(iter.name)) {
1040 return;
1041 }
1042
1043 auto io_attribute = io_attributes->append_dict();
1044
1045 io_attribute->append_str("name", iter.name);
1046
1047 const StringRefNull domain_name = get_domain_io_name(iter.domain);
1048 io_attribute->append_str("domain", domain_name);
1049
1050 const StringRefNull type_name = get_data_type_io_name(iter.data_type);
1051 io_attribute->append_str("type", type_name);
1052
1053 const GAttributeReader attribute = iter.get();
1054 const GVArraySpan attribute_span(attribute.varray);
1055 io_attribute->append("data",
1057 blob_writer,
1058 blob_sharing,
1059 attribute_span,
1060 attribute.varray.is_span() ? attribute.sharing_info : nullptr));
1061 });
1062 return io_attributes;
1063}
1064
1066 const CurvesGeometry &curves,
1067 BlobWriter &blob_writer,
1068 BlobWriteSharing &blob_sharing)
1069{
1070 io_curves.append_int("num_points", curves.point_num);
1071 io_curves.append_int("num_curves", curves.curve_num);
1072
1073 if (curves.curve_num > 0) {
1074 io_curves.append("curve_offsets",
1076 blob_sharing,
1077 curves.offsets(),
1078 curves.runtime->curve_offsets_sharing_info));
1079 }
1080
1081 auto io_attributes = serialize_attributes(curves.attributes(), blob_writer, blob_sharing, {});
1082 io_curves.append("attributes", io_attributes);
1083}
1084
1085static std::shared_ptr<DictionaryValue> serialize_geometry_set(const GeometrySet &geometry,
1086 BlobWriter &blob_writer,
1087 BlobWriteSharing &blob_sharing)
1088{
1089 auto io_geometry = std::make_shared<DictionaryValue>();
1090 if (geometry.has_mesh()) {
1091 const Mesh &mesh = *geometry.get_mesh();
1092 auto io_mesh = io_geometry->append_dict("mesh");
1093
1094 io_mesh->append_int("num_vertices", mesh.verts_num);
1095 io_mesh->append_int("num_edges", mesh.edges_num);
1096 io_mesh->append_int("num_polygons", mesh.faces_num);
1097 io_mesh->append_int("num_corners", mesh.corners_num);
1098
1099 if (mesh.faces_num > 0) {
1100 io_mesh->append("poly_offsets",
1102 blob_sharing,
1103 mesh.face_offsets(),
1104 mesh.runtime->face_offsets_sharing_info));
1105 }
1106
1107 auto io_materials = serialize_materials(mesh.runtime->bake_materials);
1108 io_mesh->append("materials", io_materials);
1109
1110 auto io_attributes = serialize_attributes(mesh.attributes(), blob_writer, blob_sharing, {});
1111 io_mesh->append("attributes", io_attributes);
1112 }
1113 if (geometry.has_pointcloud()) {
1114 const PointCloud &pointcloud = *geometry.get_pointcloud();
1115 auto io_pointcloud = io_geometry->append_dict("pointcloud");
1116
1117 io_pointcloud->append_int("num_points", pointcloud.totpoint);
1118
1119 auto io_materials = serialize_materials(pointcloud.runtime->bake_materials);
1120 io_pointcloud->append("materials", io_materials);
1121
1122 auto io_attributes = serialize_attributes(
1123 pointcloud.attributes(), blob_writer, blob_sharing, {});
1124 io_pointcloud->append("attributes", io_attributes);
1125 }
1126 if (geometry.has_curves()) {
1127 const Curves &curves_id = *geometry.get_curves();
1128 const CurvesGeometry &curves = curves_id.geometry.wrap();
1129
1130 auto io_curves = io_geometry->append_dict("curves");
1131
1132 serialize_curves_geometry(*io_curves, curves, blob_writer, blob_sharing);
1133
1134 auto io_materials = serialize_materials(curves.runtime->bake_materials);
1135 io_curves->append("materials", io_materials);
1136 }
1137 if (geometry.has_grease_pencil()) {
1138 const GreasePencil &grease_pencil = *geometry.get_grease_pencil();
1139 auto io_grease_pencil = io_geometry->append_dict("grease_pencil");
1140 auto io_layers = io_grease_pencil->append_array("layers");
1141
1142 Vector<float> layer_opacities;
1143 Vector<int8_t> layer_blend_modes;
1144 Vector<float4x4> layer_transforms;
1145 for (const greasepencil::Layer *layer : grease_pencil.layers()) {
1146 auto io_layer = io_layers->append_dict();
1147 io_layer->append_str("name", layer->name());
1148 auto io_strokes = io_layer->append_dict("strokes");
1149 const greasepencil::Drawing *drawing = grease_pencil.get_eval_drawing(*layer);
1150 if (drawing) {
1151 serialize_curves_geometry(*io_strokes, drawing->strokes(), blob_writer, blob_sharing);
1152 }
1153 else {
1154 serialize_curves_geometry(*io_strokes, CurvesGeometry(), blob_writer, blob_sharing);
1155 }
1156
1157 layer_opacities.append(layer->opacity);
1158 layer_blend_modes.append(layer->blend_mode);
1159 layer_transforms.append(layer->local_transform());
1160 }
1161
1162 io_grease_pencil->append(
1163 "opacities",
1164 write_blob_simple_gspan(blob_writer, blob_sharing, layer_opacities.as_span()));
1165 io_grease_pencil->append(
1166 "blend_modes",
1167 write_blob_simple_gspan(blob_writer, blob_sharing, layer_blend_modes.as_span()));
1168 io_grease_pencil->append(
1169 "transforms",
1170 write_blob_simple_gspan(blob_writer, blob_sharing, layer_transforms.as_span()));
1171
1172 auto io_layer_attributes = serialize_attributes(
1173 grease_pencil.attributes(), blob_writer, blob_sharing, {});
1174 io_grease_pencil->append("layer_attributes", io_layer_attributes);
1175
1176 auto io_materials = serialize_materials(grease_pencil.runtime->bake_materials);
1177 io_grease_pencil->append("materials", io_materials);
1178 }
1179#ifdef WITH_OPENVDB
1180 if (geometry.has_volume()) {
1181 const Volume &volume = *geometry.get_volume();
1182 const int grids_num = BKE_volume_num_grids(&volume);
1183
1184 auto io_volume = io_geometry->append_dict("volume");
1185 auto io_vdb = blob_writer
1186 .write_as_stream(".vdb",
1187 [&](std::ostream &stream) {
1188 openvdb::GridCPtrVec vdb_grids;
1190 for (const int i : IndexRange(grids_num)) {
1191 const bke::VolumeGridData *grid = BKE_volume_grid_get(
1192 &volume, i);
1193 tree_tokens.append_as();
1194 vdb_grids.push_back(grid->grid_ptr(tree_tokens.last()));
1195 }
1196
1197 openvdb::io::Stream vdb_stream(stream);
1198 vdb_stream.write(vdb_grids);
1199 })
1200 .serialize();
1201 io_volume->append("vdb", std::move(io_vdb));
1202
1203 auto io_materials = serialize_materials(volume.runtime->bake_materials);
1204 io_volume->append("materials", io_materials);
1205 }
1206#endif
1207 if (geometry.has_instances()) {
1208 const Instances &instances = *geometry.get_instances();
1209 auto io_instances = io_geometry->append_dict("instances");
1210
1211 io_instances->append_int("num_instances", instances.instances_num());
1212
1213 auto io_references = io_instances->append_array("references");
1214 for (const InstanceReference &reference : instances.references()) {
1215 if (reference.type() == InstanceReference::Type::GeometrySet) {
1216 const GeometrySet &geometry = reference.geometry_set();
1217 io_references->append(serialize_geometry_set(geometry, blob_writer, blob_sharing));
1218 }
1219 else {
1220 /* TODO: Support serializing object and collection references. */
1221 io_references->append(serialize_geometry_set({}, blob_writer, blob_sharing));
1222 }
1223 }
1224
1225 auto io_attributes = serialize_attributes(
1226 instances.attributes(), blob_writer, blob_sharing, {});
1227 io_instances->append("attributes", io_attributes);
1228 }
1229 return io_geometry;
1230}
1231
1232static std::shared_ptr<io::serialize::ArrayValue> serialize_float_array(const Span<float> values)
1233{
1234 auto io_value = std::make_shared<io::serialize::ArrayValue>();
1235 for (const float value : values) {
1236 io_value->append_double(value);
1237 }
1238 return io_value;
1239}
1240
1241static std::shared_ptr<io::serialize::ArrayValue> serialize_int_array(const Span<int> values)
1242{
1243 auto io_value = std::make_shared<io::serialize::ArrayValue>();
1244 for (const int value : values) {
1245 io_value->append_int(value);
1246 }
1247 return io_value;
1248}
1249
1250static std::shared_ptr<io::serialize::Value> serialize_primitive_value(
1251 const eCustomDataType data_type, const void *value_ptr)
1252{
1253 switch (data_type) {
1254 case CD_PROP_FLOAT: {
1255 const float value = *static_cast<const float *>(value_ptr);
1256 return std::make_shared<io::serialize::DoubleValue>(value);
1257 }
1258 case CD_PROP_FLOAT2: {
1259 const float2 value = *static_cast<const float2 *>(value_ptr);
1260 return serialize_float_array({&value.x, 2});
1261 }
1262 case CD_PROP_FLOAT3: {
1263 const float3 value = *static_cast<const float3 *>(value_ptr);
1264 return serialize_float_array({&value.x, 3});
1265 }
1266 case CD_PROP_BOOL: {
1267 const bool value = *static_cast<const bool *>(value_ptr);
1268 return std::make_shared<io::serialize::BooleanValue>(value);
1269 }
1270 case CD_PROP_INT32: {
1271 const int value = *static_cast<const int *>(value_ptr);
1272 return std::make_shared<io::serialize::IntValue>(value);
1273 }
1274 case CD_PROP_INT32_2D: {
1275 const int2 value = *static_cast<const int2 *>(value_ptr);
1276 return serialize_int_array({&value.x, 2});
1277 }
1278 case CD_PROP_BYTE_COLOR: {
1279 const ColorGeometry4b value = *static_cast<const ColorGeometry4b *>(value_ptr);
1280 const int4 value_int{&value.r};
1281 return serialize_int_array({&value_int.x, 4});
1282 }
1283 case CD_PROP_COLOR: {
1284 const ColorGeometry4f value = *static_cast<const ColorGeometry4f *>(value_ptr);
1285 return serialize_float_array({&value.r, 4});
1286 }
1287 case CD_PROP_QUATERNION: {
1288 const math::Quaternion value = *static_cast<const math::Quaternion *>(value_ptr);
1289 return serialize_float_array({&value.w, 4});
1290 }
1291 case CD_PROP_FLOAT4X4: {
1292 const float4x4 value = *static_cast<const float4x4 *>(value_ptr);
1293 return serialize_float_array({value.base_ptr(), value.col_len * value.row_len});
1294 }
1295 default:
1296 break;
1297 }
1299 return {};
1300}
1301
1302template<typename T>
1303[[nodiscard]] static bool deserialize_typed_array(
1304 const io::serialize::Value &io_value,
1305 FunctionRef<std::optional<T>(const io::serialize::Value &io_element)> fn,
1306 MutableSpan<T> r_values)
1307{
1308 const io::serialize::ArrayValue *io_array = io_value.as_array_value();
1309 if (!io_array) {
1310 return false;
1311 }
1312 if (io_array->elements().size() != r_values.size()) {
1313 return false;
1314 }
1315 for (const int i : r_values.index_range()) {
1316 const io::serialize::Value &io_element = *io_array->elements()[i];
1317 std::optional<T> element = fn(io_element);
1318 if (!element) {
1319 return false;
1320 }
1321 r_values[i] = std::move(*element);
1322 }
1323 return true;
1324}
1325
1326template<typename T> static std::optional<T> deserialize_int(const io::serialize::Value &io_value)
1327{
1328 const io::serialize::IntValue *io_int = io_value.as_int_value();
1329 if (!io_int) {
1330 return std::nullopt;
1331 }
1332 const int64_t value = io_int->value();
1333 if (value < std::numeric_limits<T>::lowest()) {
1334 return std::nullopt;
1335 }
1336 if (value > std::numeric_limits<T>::max()) {
1337 return std::nullopt;
1338 }
1339 return value;
1340}
1341
1342static std::optional<float> deserialize_float(const io::serialize::Value &io_value)
1343{
1344 if (const io::serialize::DoubleValue *io_double = io_value.as_double_value()) {
1345 return io_double->value();
1346 }
1347 if (const io::serialize::IntValue *io_int = io_value.as_int_value()) {
1348 return io_int->value();
1349 }
1350 return std::nullopt;
1351}
1352
1353[[nodiscard]] static bool deserialize_float_array(const io::serialize::Value &io_value,
1354 MutableSpan<float> r_values)
1355{
1356 return deserialize_typed_array<float>(io_value, deserialize_float, r_values);
1357}
1358
1359template<typename T>
1360[[nodiscard]] static bool deserialize_int_array(const io::serialize::Value &io_value,
1361 MutableSpan<T> r_values)
1362{
1363 static_assert(std::is_integral_v<T>);
1364 return deserialize_typed_array<T>(io_value, deserialize_int<T>, r_values);
1365}
1366
1367[[nodiscard]] static bool deserialize_primitive_value(const io::serialize::Value &io_value,
1368 const eCustomDataType type,
1369 void *r_value)
1370{
1371 switch (type) {
1372 case CD_PROP_FLOAT: {
1373 const std::optional<float> value = deserialize_float(io_value);
1374 if (!value) {
1375 return false;
1376 }
1377 *static_cast<float *>(r_value) = *value;
1378 return true;
1379 }
1380 case CD_PROP_FLOAT2: {
1381 return deserialize_float_array(io_value, {static_cast<float *>(r_value), 2});
1382 }
1383 case CD_PROP_FLOAT3: {
1384 return deserialize_float_array(io_value, {static_cast<float *>(r_value), 3});
1385 }
1386 case CD_PROP_BOOL: {
1387 if (const io::serialize::BooleanValue *io_value_boolean = io_value.as_boolean_value()) {
1388 *static_cast<bool *>(r_value) = io_value_boolean->value();
1389 return true;
1390 }
1391 return false;
1392 }
1393 case CD_PROP_INT32: {
1394 const std::optional<int> value = deserialize_int<int>(io_value);
1395 if (!value) {
1396 return false;
1397 }
1398 *static_cast<int *>(r_value) = *value;
1399 return true;
1400 }
1401 case CD_PROP_INT32_2D: {
1402 return deserialize_int_array<int>(io_value, {static_cast<int *>(r_value), 2});
1403 }
1404 case CD_PROP_BYTE_COLOR: {
1405 return deserialize_int_array<uint8_t>(io_value, {static_cast<uint8_t *>(r_value), 4});
1406 }
1407 case CD_PROP_COLOR: {
1408 return deserialize_float_array(io_value, {static_cast<float *>(r_value), 4});
1409 }
1410 case CD_PROP_QUATERNION: {
1411 return deserialize_float_array(io_value, {static_cast<float *>(r_value), 4});
1412 }
1413 case CD_PROP_FLOAT4X4: {
1414 return deserialize_float_array(io_value, {static_cast<float *>(r_value), 4 * 4});
1415 }
1416 default:
1417 break;
1418 }
1419 return false;
1420}
1421
1422static void serialize_bake_item(const BakeItem &item,
1423 BlobWriter &blob_writer,
1424 BlobWriteSharing &blob_sharing,
1425 DictionaryValue &r_io_item)
1426{
1427 if (!item.name.empty()) {
1428 r_io_item.append_str("name", item.name);
1429 }
1430 if (const auto *geometry_state_item = dynamic_cast<const GeometryBakeItem *>(&item)) {
1431 r_io_item.append_str("type", "GEOMETRY");
1432
1433 const GeometrySet &geometry = geometry_state_item->geometry;
1434 auto io_geometry = serialize_geometry_set(geometry, blob_writer, blob_sharing);
1435 r_io_item.append("data", io_geometry);
1436 }
1437 else if (const auto *attribute_state_item = dynamic_cast<const AttributeBakeItem *>(&item)) {
1438 r_io_item.append_str("type", "ATTRIBUTE");
1439 r_io_item.append_str("name", attribute_state_item->name());
1440 }
1441#ifdef WITH_OPENVDB
1442 else if (const auto *grid_state_item = dynamic_cast<const VolumeGridBakeItem *>(&item)) {
1443 r_io_item.append_str("type", "GRID");
1444 const GVolumeGrid &grid = *grid_state_item->grid;
1445 auto io_vdb = blob_writer
1446 .write_as_stream(".vdb",
1447 [&](std::ostream &stream) {
1448 openvdb::GridCPtrVec vdb_grids;
1449 bke::VolumeTreeAccessToken tree_token;
1450 vdb_grids.push_back(grid->grid_ptr(tree_token));
1451 openvdb::io::Stream vdb_stream(stream);
1452 vdb_stream.write(vdb_grids);
1453 })
1454 .serialize();
1455 r_io_item.append("vdb", std::move(io_vdb));
1456 }
1457#endif
1458 else if (const auto *string_state_item = dynamic_cast<const StringBakeItem *>(&item)) {
1459 r_io_item.append_str("type", "STRING");
1460 const StringRefNull str = string_state_item->value();
1461 /* Small strings are inlined, larger strings are stored separately. */
1462 const int64_t blob_threshold = 100;
1463 if (str.size() < blob_threshold) {
1464 r_io_item.append_str("data", string_state_item->value());
1465 }
1466 else {
1467 r_io_item.append("data",
1468 write_blob_raw_bytes(blob_writer, blob_sharing, str.data(), str.size()));
1469 }
1470 }
1471 else if (const auto *primitive_state_item = dynamic_cast<const PrimitiveBakeItem *>(&item)) {
1472 const eCustomDataType data_type = cpp_type_to_custom_data_type(primitive_state_item->type());
1473 r_io_item.append_str("type", get_data_type_io_name(data_type));
1474 auto io_data = serialize_primitive_value(data_type, primitive_state_item->value());
1475 r_io_item.append("data", std::move(io_data));
1476 }
1477}
1478
1479static std::unique_ptr<BakeItem> deserialize_bake_item(const DictionaryValue &io_item,
1480 const BlobReader &blob_reader,
1481 const BlobReadSharing &blob_sharing)
1482{
1483
1484 const std::optional<StringRefNull> state_item_type = io_item.lookup_str("type");
1485 if (!state_item_type) {
1486 return {};
1487 }
1488 if (*state_item_type == StringRef("GEOMETRY")) {
1489 const DictionaryValue *io_geometry = io_item.lookup_dict("data");
1490 if (!io_geometry) {
1491 return {};
1492 }
1493 GeometrySet geometry = load_geometry(*io_geometry, blob_reader, blob_sharing);
1494 return std::make_unique<GeometryBakeItem>(std::move(geometry));
1495 }
1496 if (*state_item_type == StringRef("ATTRIBUTE")) {
1497 const DictionaryValue *io_attribute = &io_item;
1498 if (!io_attribute) {
1499 return {};
1500 }
1501 std::optional<StringRefNull> name = io_attribute->lookup_str("name");
1502 if (!name) {
1503 return {};
1504 }
1505 return std::make_unique<AttributeBakeItem>(std::move(*name));
1506 }
1507#ifdef WITH_OPENVDB
1508 if (*state_item_type == StringRef("GRID")) {
1509 const DictionaryValue &io_grid = io_item;
1510 const auto *io_vdb = io_grid.lookup_dict("vdb");
1511 if (!io_vdb) {
1512 return {};
1513 }
1514 std::optional<BlobSlice> vdb_slice = BlobSlice::deserialize(*io_vdb);
1515 if (!vdb_slice) {
1516 return {};
1517 }
1518 openvdb::GridPtrVecPtr vdb_grids;
1519 if (!blob_reader.read_as_stream(*vdb_slice, [&](std::istream &stream) {
1520 try {
1521 openvdb::io::Stream vdb_stream{stream};
1522 vdb_grids = vdb_stream.getGrids();
1523 return true;
1524 }
1525 catch (...) {
1526 return false;
1527 }
1528 }))
1529 {
1530 return {};
1531 }
1532 if (vdb_grids->size() != 1) {
1533 return {};
1534 }
1535 std::shared_ptr<openvdb::GridBase> vdb_grid = std::move((*vdb_grids)[0]);
1536 GVolumeGrid grid{std::move(vdb_grid)};
1537 return std::make_unique<VolumeGridBakeItem>(std::make_unique<GVolumeGrid>(grid));
1538 }
1539#endif
1540 if (*state_item_type == StringRef("STRING")) {
1541 const std::shared_ptr<io::serialize::Value> *io_data = io_item.lookup("data");
1542 if (!io_data) {
1543 return {};
1544 }
1545 if (io_data->get()->type() == io::serialize::eValueType::String) {
1546 const io::serialize::StringValue &io_string = *io_data->get()->as_string_value();
1547 return std::make_unique<StringBakeItem>(io_string.value());
1548 }
1549 if (const io::serialize::DictionaryValue *io_string = io_data->get()->as_dictionary_value()) {
1550 const std::optional<int64_t> size = io_string->lookup_int("size");
1551 if (!size) {
1552 return {};
1553 }
1554 std::string str;
1555 str.resize(*size);
1556 if (!read_blob_raw_bytes(blob_reader, *io_string, *size, str.data())) {
1557 return {};
1558 }
1559 return std::make_unique<StringBakeItem>(std::move(str));
1560 }
1561 }
1562 const std::shared_ptr<io::serialize::Value> *io_data = io_item.lookup("data");
1563 if (!io_data) {
1564 return {};
1565 }
1566 const std::optional<eCustomDataType> data_type = get_data_type_from_io_name(*state_item_type);
1567 if (data_type) {
1568 const CPPType &cpp_type = *custom_data_type_to_cpp_type(*data_type);
1569 BUFFER_FOR_CPP_TYPE_VALUE(cpp_type, buffer);
1570 if (!deserialize_primitive_value(**io_data, *data_type, buffer)) {
1571 return {};
1572 }
1573 BLI_SCOPED_DEFER([&]() { cpp_type.destruct(buffer); });
1574 return std::make_unique<PrimitiveBakeItem>(cpp_type, buffer);
1575 }
1576 return {};
1577}
1578
1579static constexpr int bake_file_version = 3;
1580
1581void serialize_bake(const BakeState &bake_state,
1582 BlobWriter &blob_writer,
1583 BlobWriteSharing &blob_sharing,
1584 std::ostream &r_stream)
1585{
1587 io_root.append_int("version", bake_file_version);
1588 io::serialize::DictionaryValue &io_items = *io_root.append_dict("items");
1589 for (auto item : bake_state.items_by_id.items()) {
1590 io::serialize::DictionaryValue &io_item = *io_items.append_dict(std::to_string(item.key));
1591 serialize_bake_item(*item.value, blob_writer, blob_sharing, io_item);
1592 }
1593
1595 formatter.serialize(r_stream, io_root);
1596}
1597
1598std::optional<BakeState> deserialize_bake(std::istream &stream,
1599 const BlobReader &blob_reader,
1600 const BlobReadSharing &blob_sharing)
1601{
1602 JsonFormatter formatter;
1603 std::unique_ptr<io::serialize::Value> io_root_value;
1604 try {
1605 io_root_value = formatter.deserialize(stream);
1606 }
1607 catch (...) {
1608 return std::nullopt;
1609 }
1610 if (!io_root_value) {
1611 return std::nullopt;
1612 }
1613 const io::serialize::DictionaryValue *io_root = io_root_value->as_dictionary_value();
1614 if (!io_root) {
1615 return std::nullopt;
1616 }
1617 const std::optional<int> version = io_root->lookup_int("version");
1618 if (!version.has_value() || *version != bake_file_version) {
1619 return std::nullopt;
1620 }
1621 const io::serialize::DictionaryValue *io_items = io_root->lookup_dict("items");
1622 if (!io_items) {
1623 return std::nullopt;
1624 }
1625 BakeState bake_state;
1626 for (const auto &io_item_value : io_items->elements()) {
1627 const io::serialize::DictionaryValue *io_item = io_item_value.second->as_dictionary_value();
1628 if (!io_item) {
1629 return std::nullopt;
1630 }
1631 int id;
1632 try {
1633 id = std::stoi(io_item_value.first);
1634 }
1635 catch (...) {
1636 return std::nullopt;
1637 }
1638 if (bake_state.items_by_id.contains(id)) {
1639 return std::nullopt;
1640 }
1641 std::unique_ptr<BakeItem> bake_item = deserialize_bake_item(
1642 *io_item, blob_reader, blob_sharing);
1643 if (!bake_item) {
1644 return std::nullopt;
1645 }
1646 bake_state.items_by_id.add_new(id, std::move(bake_item));
1647 }
1648 return bake_state;
1649}
1650
1651} // namespace blender::bke::bake
Low-level operations for curves.
CustomData interface, see also DNA_customdata_types.h.
bool CustomData_free_layer_named(CustomData *data, blender::StringRef name, const int totelem)
Low-level operations for grease pencil.
GreasePencil * BKE_grease_pencil_new_nomain()
void BKE_id_free(Main *bmain, void *idv)
void * BKE_id_new_nomain(short type, const char *name)
Definition lib_id.cc:1487
Mesh * BKE_mesh_new_nomain(int verts_num, int edges_num, int faces_num, int corners_num)
General operations for point clouds.
PointCloud * BKE_pointcloud_new_nomain(int totpoint)
Volume data-block.
void BKE_volume_grid_add(Volume *volume, const blender::bke::VolumeGridData &grid)
int BKE_volume_num_grids(const Volume *volume)
const blender::bke::VolumeGridData * BKE_volume_grid_get(const Volume *volume, int grid_index)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
#define B_ENDIAN
#define L_ENDIAN
#define ENDIAN_ORDER
void BLI_endian_switch_uint16_array(unsigned short *val, int size) ATTR_NONNULL(1)
void BLI_endian_switch_uint64_array(uint64_t *val, int size) ATTR_NONNULL(1)
void BLI_endian_switch_uint32_array(unsigned int *val, int size) ATTR_NONNULL(1)
bool BLI_file_ensure_parent_dir_exists(const char *filepath) ATTR_NONNULL(1)
Definition fileops_c.cc:429
#define BLI_SCOPED_DEFER(function_to_defer)
#define FILE_MAX
#define BLI_path_join(...)
@ ID_VO
@ ID_MA
struct CurvesGeometry CurvesGeometry
@ CD_PROP_BYTE_COLOR
@ CD_PROP_FLOAT
@ CD_PROP_FLOAT3
@ CD_PROP_INT32_2D
@ CD_PROP_COLOR
@ CD_PROP_QUATERNION
@ CD_PROP_INT32
@ CD_PROP_FLOAT2
@ CD_PROP_FLOAT4X4
volatile int lock
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
virtual bool serialize(void *o_alignedDataBuffer, unsigned i_dataBufferSize, bool i_swapEndian) const
Data buffer MUST be 16 byte aligned.
AttributeSet attributes
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
static const CPPType & get()
void copy_assign_n(const void *src, void *dst, int64_t n) const
int64_t size() const
int64_t alignment() const
int64_t size_in_bytes() const
const CPPType & type() const
constexpr int64_t size() const
constexpr bool is_empty() const
static constexpr IndexRange from_begin_size(const int64_t begin, const int64_t size)
constexpr int64_t start() const
constexpr bool contains(int64_t value) const
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
bool contains_as(const ForwardKey &key) const
Definition BLI_set.hh:295
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
constexpr const T * data() const
Definition BLI_span.hh:216
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool startswith(StringRef prefix) const
constexpr const char * c_str() const
void append(const T &value)
const T & last(const int64_t n=0) const
Span< T > as_span() const
void append_as(ForwardValue &&...value)
GAttributeReader get() const
std::optional< ImplicitSharingInfoAndData > read_shared(const io::serialize::DictionaryValue &io_data, FunctionRef< std::optional< ImplicitSharingInfoAndData >()> read_fn) const
virtual bool read_as_stream(const BlobSlice &slice, FunctionRef< bool(std::istream &)> fn) const
virtual bool read(const BlobSlice &slice, void *r_data) const =0
std::shared_ptr< io::serialize::DictionaryValue > write_implicitly_shared(const ImplicitSharingInfo *sharing_info, FunctionRef< std::shared_ptr< io::serialize::DictionaryValue >()> write_fn)
std::shared_ptr< io::serialize::DictionaryValue > write_deduplicated(BlobWriter &writer, const void *data, int64_t size_in_bytes)
virtual BlobSlice write(const void *data, int64_t size)=0
virtual BlobSlice write_as_stream(StringRef file_extension, FunctionRef< void(std::ostream &)> fn)
bool read(const BlobSlice &slice, void *r_data) const override
DiskBlobWriter(std::string blob_dir, std::string base_name)
BlobSlice write_as_stream(StringRef file_extension, FunctionRef< void(std::ostream &)> fn) override
BlobSlice write(const void *data, int64_t size) override
void add(StringRef name, Span< std::byte > blob)
bool read(const BlobSlice &slice, void *r_data) const override
BlobSlice write(const void *data, int64_t size) override
BlobSlice write_as_stream(StringRef file_extension, FunctionRef< void(std::ostream &)> fn) override
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
Span< std::shared_ptr< Value > > elements() const
void append(std::string key, std::shared_ptr< Value > value)
Definition serialize.cc:317
const std::shared_ptr< Value > * lookup(const StringRef key) const
Definition serialize.cc:261
void append_str(std::string key, std::string value)
Definition serialize.cc:332
std::shared_ptr< DictionaryValue > append_dict(std::string key)
Definition serialize.cc:337
const DictionaryValue * lookup_dict(const StringRef key) const
Definition serialize.cc:301
void append_int(std::string key, int64_t value)
Definition serialize.cc:322
const ArrayValue * lookup_array(const StringRef key) const
Definition serialize.cc:309
std::optional< int64_t > lookup_int(const StringRef key) const
Definition serialize.cc:281
std::optional< StringRefNull > lookup_str(const StringRef key) const
Definition serialize.cc:271
std::unique_ptr< Value > deserialize(std::istream &is) override
Definition serialize.cc:363
void serialize(std::ostream &os, const Value &value) override
Definition serialize.cc:351
const BooleanValue * as_boolean_value() const
Definition serialize.cc:36
const ArrayValue * as_array_value() const
Definition serialize.cc:52
const IntValue * as_int_value() const
Definition serialize.cc:20
const DoubleValue * as_double_value() const
Definition serialize.cc:28
const DictionaryValue * as_dictionary_value() const
Definition serialize.cc:60
std::string id_name(void *id)
#define str(s)
void * MEM_mallocN_aligned(size_t len, size_t alignment, const char *str)
Definition mallocn.cc:110
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
static bool deserialize_typed_array(const io::serialize::Value &io_value, FunctionRef< std::optional< T >(const io::serialize::Value &io_element)> fn, MutableSpan< T > r_values)
static StringRefNull get_domain_io_name(const AttrDomain domain)
static constexpr int bake_file_version
static bool read_blob_shared_simple_span(const DictionaryValue &io_data, const BlobReader &blob_reader, const BlobReadSharing &blob_sharing, const int size, T **r_data, const ImplicitSharingInfo **r_sharing_info)
static std::optional< AttrDomain > get_domain_from_io_name(const StringRefNull io_name)
static std::optional< eCustomDataType > get_data_type_from_io_name(const StringRefNull io_name)
static bool deserialize_int_array(const io::serialize::Value &io_value, MutableSpan< T > r_values)
static bool read_blob_simple_gspan(const BlobReader &blob_reader, const DictionaryValue &io_data, GMutableSpan r_data)
static std::shared_ptr< io::serialize::ArrayValue > serialize_int_array(const Span< int > values)
std::shared_ptr< DictionaryValue > DictionaryValuePtr
Definition bake_items.cc:24
static bool read_blob_raw_bytes(const BlobReader &blob_reader, const DictionaryValue &io_data, const int64_t bytes_num, void *r_data)
static StringRefNull get_data_type_io_name(const eCustomDataType data_type)
static bool load_materials(const io::serialize::ArrayValue &io_materials, std::unique_ptr< BakeMaterialsList > &materials)
static StringRefNull get_endian_io_name(const int endian)
static std::optional< CurvesGeometry > try_load_curves_geometry(const DictionaryValue &io_curves, const BlobReader &blob_reader, const BlobReadSharing &blob_sharing)
static std::optional< float > deserialize_float(const io::serialize::Value &io_value)
static std::optional< T > deserialize_int(const io::serialize::Value &io_value)
static std::shared_ptr< io::serialize::Value > serialize_primitive_value(const eCustomDataType data_type, const void *value_ptr)
static bool load_attributes(const io::serialize::ArrayValue &io_attributes, MutableAttributeAccessor &attributes, const BlobReader &blob_reader, const BlobReadSharing &blob_sharing)
void serialize_bake(const BakeState &bake_state, BlobWriter &blob_writer, BlobWriteSharing &blob_sharing, std::ostream &r_stream)
static void serialize_curves_geometry(DictionaryValue &io_curves, const CurvesGeometry &curves, BlobWriter &blob_writer, BlobWriteSharing &blob_sharing)
static std::shared_ptr< DictionaryValue > write_blob_raw_bytes(BlobWriter &blob_writer, BlobWriteSharing &blob_sharing, const void *data, const int64_t size_in_bytes)
static std::shared_ptr< DictionaryValue > serialize_geometry_set(const GeometrySet &geometry, BlobWriter &blob_writer, BlobWriteSharing &blob_sharing)
static Curves * try_load_curves(const DictionaryValue &io_geometry, const BlobReader &blob_reader, const BlobReadSharing &blob_sharing)
static std::shared_ptr< DictionaryValue > write_blob_shared_simple_gspan(BlobWriter &blob_writer, BlobWriteSharing &blob_sharing, const GSpan data, const ImplicitSharingInfo *sharing_info)
static GeometrySet load_geometry(const DictionaryValue &io_geometry, const BlobReader &blob_reader, const BlobReadSharing &blob_sharing)
static void serialize_bake_item(const BakeItem &item, BlobWriter &blob_writer, BlobWriteSharing &blob_sharing, DictionaryValue &r_io_item)
static Mesh * try_load_mesh(const DictionaryValue &io_geometry, const BlobReader &blob_reader, const BlobReadSharing &blob_sharing)
static std::unique_ptr< BakeItem > deserialize_bake_item(const DictionaryValue &io_item, const BlobReader &blob_reader, const BlobReadSharing &blob_sharing)
static PointCloud * try_load_pointcloud(const DictionaryValue &io_geometry, const BlobReader &blob_reader, const BlobReadSharing &blob_sharing)
std::optional< BakeState > deserialize_bake(std::istream &stream, const BlobReader &blob_reader, const BlobReadSharing &blob_sharing)
static bool deserialize_primitive_value(const io::serialize::Value &io_value, const eCustomDataType type, void *r_value)
static std::shared_ptr< DictionaryValue > write_blob_simple_gspan(BlobWriter &blob_writer, BlobWriteSharing &blob_sharing, const GSpan data)
static GreasePencil * try_load_grease_pencil(const DictionaryValue &io_geometry, const BlobReader &blob_reader, const BlobReadSharing &blob_sharing)
static const void * read_blob_shared_simple_gspan(const DictionaryValue &io_data, const BlobReader &blob_reader, const BlobReadSharing &blob_sharing, const CPPType &cpp_type, const int size, const ImplicitSharingInfo **r_sharing_info)
static std::unique_ptr< Instances > try_load_instances(const DictionaryValue &io_geometry, const BlobReader &blob_reader, const BlobReadSharing &blob_sharing)
static bool deserialize_float_array(const io::serialize::Value &io_value, MutableSpan< float > r_values)
static std::shared_ptr< DictionaryValue > write_blob_raw_data_with_endian(BlobWriter &blob_writer, BlobWriteSharing &blob_sharing, const void *data, const int64_t size_in_bytes)
static std::string make_independent_file_name(const StringRef base_name, const int file_index, const StringRef extension)
static std::shared_ptr< io::serialize::ArrayValue > serialize_float_array(const Span< float > values)
static std::shared_ptr< io::serialize::ArrayValue > serialize_attributes(const AttributeAccessor &attributes, BlobWriter &blob_writer, BlobWriteSharing &blob_sharing, const Set< std::string > &attributes_to_ignore)
static std::shared_ptr< io::serialize::ArrayValue > serialize_materials(const std::unique_ptr< BakeMaterialsList > &materials)
static bool read_blob_raw_data_with_endian(const BlobReader &blob_reader, const DictionaryValue &io_data, const int64_t element_size, const int64_t elements_num, void *r_data)
bool attribute_name_is_anonymous(const StringRef name)
eCustomDataType cpp_type_to_custom_data_type(const CPPType &type)
const CPPType * custom_data_type_to_cpp_type(eCustomDataType type)
Curves * curves_new_nomain(int points_num, int curves_num)
const ImplicitSharingInfo * info_for_mem_free(void *data)
bool RNA_enum_id_from_value(const EnumPropertyItem *item, int value, const char **r_identifier)
bool RNA_enum_value_from_identifier(const EnumPropertyItem *item, const char *identifier, int *r_value)
const EnumPropertyItem rna_enum_attribute_domain_items[]
const EnumPropertyItem rna_enum_attribute_type_items[]
signed short int16_t
Definition stdint.h:76
unsigned short uint16_t
Definition stdint.h:79
unsigned int uint32_t
Definition stdint.h:80
__int64 int64_t
Definition stdint.h:89
signed int int32_t
Definition stdint.h:77
unsigned char uint8_t
Definition stdint.h:78
unsigned __int64 uint64_t
Definition stdint.h:90
CurvesGeometry geometry
GreasePencilRuntimeHandle * runtime
PointCloudRuntimeHandle * runtime
struct CustomData pdata
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
Map< int, std::unique_ptr< BakeItem > > items_by_id
std::shared_ptr< io::serialize::DictionaryValue > serialize() const
static std::optional< BlobSlice > deserialize(const io::serialize::DictionaryValue &io_slice)