Blender V4.5
alembic.h
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#pragma once
6
7#include "graph/node.h"
8#include "scene/attribute.h"
9#include "scene/procedural.h"
10#include "util/transform.h"
11#include "util/vector.h"
12
13#ifdef WITH_ALEMBIC
14
15# include <Alembic/AbcCoreFactory/All.h>
16# include <Alembic/AbcGeom/All.h>
17
19
20class AlembicProcedural;
21class Geometry;
22class Object;
23class Progress;
24class Shader;
25
26using MatrixSampleMap = std::map<Alembic::Abc::chrono_t, Alembic::Abc::M44d>;
27
28struct MatrixSamplesData {
29 MatrixSampleMap *samples = nullptr;
30 Alembic::AbcCoreAbstract::TimeSamplingPtr time_sampling;
31};
32
33/* Helpers to detect if some type is a `ccl::array`. */
34template<typename> struct is_array : public std::false_type {};
35
36template<typename T> struct is_array<array<T>> : public std::true_type {};
37
38/* Holds the data for a cache lookup at a given time, as well as information to
39 * help disambiguate successes or failures to get data from the cache. */
40template<typename T> class CacheLookupResult {
41 enum class State {
42 NEW_DATA,
43 ALREADY_LOADED,
44 NO_DATA_FOR_TIME,
45 };
46
47 T *data;
48 State state;
49
50 protected:
51 /* Prevent default construction outside of the class: for a valid result, we
52 * should use the static functions below. */
53 CacheLookupResult() = default;
54
55 public:
56 static CacheLookupResult new_data(T *data_)
57 {
58 CacheLookupResult result;
59 result.data = data_;
60 result.state = State::NEW_DATA;
61 return result;
62 }
63
64 static CacheLookupResult no_data_found_for_time()
65 {
66 CacheLookupResult result;
67 result.data = nullptr;
68 result.state = State::NO_DATA_FOR_TIME;
69 return result;
70 }
71
72 static CacheLookupResult already_loaded()
73 {
74 CacheLookupResult result;
75 result.data = nullptr;
76 result.state = State::ALREADY_LOADED;
77 return result;
78 }
79
80 /* This should only be call if new data is available. */
81 const T &get_data() const
82 {
83 assert(state == State::NEW_DATA);
84 assert(data != nullptr);
85 return *data;
86 }
87
88 T *get_data_or_null() const
89 {
90 // data_ should already be null if there is no new data so no need to check
91 return data;
92 }
93
94 bool has_new_data() const
95 {
96 return state == State::NEW_DATA;
97 }
98
99 bool has_already_loaded() const
100 {
101 return state == State::ALREADY_LOADED;
102 }
103
104 bool has_no_data_for_time() const
105 {
106 return state == State::NO_DATA_FOR_TIME;
107 }
108};
109
110/* Store the data set for an animation at every time points, or at the beginning of the animation
111 * for constant data.
112 *
113 * The data is supposed to be stored in chronological order, and is looked up using the current
114 * animation time in seconds using the TimeSampling from the Alembic property. */
115template<typename T> class DataStore {
116 /* Holds information to map a cache entry for a given time to an index into the data array. */
117 struct TimeIndexPair {
118 /* Frame time for this entry. */
119 double time = 0;
120 /* Frame time for the data pointed to by `index`. */
121 double source_time = 0;
122 /* Index into the data array. */
123 size_t index = 0;
124 };
125
126 /* This is the actual data that is stored. We deduplicate data across frames to avoid storing
127 * values if they have not changed yet (e.g. the triangles for a building before fracturing, or a
128 * fluid simulation before a break or splash) */
129 vector<T> data{};
130
131 /* This is used to map they entry for a given time to an index into the data array, multiple
132 * frames can point to the same index. */
133 vector<TimeIndexPair> index_data_map{};
134
135 Alembic::AbcCoreAbstract::TimeSampling time_sampling{};
136
137 double last_loaded_time = std::numeric_limits<double>::max();
138
139 public:
140 /* Keys used to compare values. */
141 Alembic::AbcCoreAbstract::ArraySample::Key key1;
142 Alembic::AbcCoreAbstract::ArraySample::Key key2;
143
144 void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling_)
145 {
146 time_sampling = time_sampling_;
147 }
148
149 Alembic::AbcCoreAbstract::TimeSampling get_time_sampling() const
150 {
151 return time_sampling;
152 }
153
154 /* Get the data for the specified time.
155 * Return nullptr if there is no data or if the data for this time was already loaded. */
156 CacheLookupResult<T> data_for_time(const double time)
157 {
158 if (size() == 0) {
159 return CacheLookupResult<T>::no_data_found_for_time();
160 }
161
162 const TimeIndexPair &index = get_index_for_time(time);
163
164 if (index.index == -1ul) {
165 return CacheLookupResult<T>::no_data_found_for_time();
166 }
167
168 if (last_loaded_time == index.time || last_loaded_time == index.source_time) {
169 return CacheLookupResult<T>::already_loaded();
170 }
171
172 last_loaded_time = index.source_time;
173
174 assert(index.index < data.size());
175
176 return CacheLookupResult<T>::new_data(&data[index.index]);
177 }
178
179 /* get the data for the specified time, but do not check if the data was already loaded for this
180 * time return nullptr if there is no data */
181 CacheLookupResult<T> data_for_time_no_check(const double time)
182 {
183 if (size() == 0) {
184 return CacheLookupResult<T>::no_data_found_for_time();
185 }
186
187 const TimeIndexPair &index = get_index_for_time(time);
188
189 if (index.index == -1ul) {
190 return CacheLookupResult<T>::no_data_found_for_time();
191 }
192
193 assert(index.index < data.size());
194
195 return CacheLookupResult<T>::new_data(&data[index.index]);
196 }
197
198 void add_data(T &data_, double time)
199 {
200 index_data_map.push_back({time, time, data.size()});
201
202 if constexpr (is_array<T>::value) {
203 data.emplace_back();
204 data.back().steal_data(data_);
205 return;
206 }
207
208 data.push_back(data_);
209 }
210
211 void reuse_data_for_last_time(const double time)
212 {
213 const TimeIndexPair &data_index = index_data_map.back();
214 index_data_map.push_back({time, data_index.source_time, data_index.index});
215 }
216
217 void add_no_data(const double time)
218 {
219 index_data_map.push_back({time, time, -1ul});
220 }
221
222 bool is_constant() const
223 {
224 return data.size() <= 1;
225 }
226
227 size_t size() const
228 {
229 return data.size();
230 }
231
232 void clear()
233 {
234 invalidate_last_loaded_time();
235 data.clear();
236 index_data_map.clear();
237 }
238
239 void invalidate_last_loaded_time()
240 {
241 last_loaded_time = std::numeric_limits<double>::max();
242 }
243
244 /* Copy the data for the specified time to the node's socket. If there is no
245 * data for this time or it was already loaded, do nothing. */
246 void copy_to_socket(const double time, Node *node, const SocketType *socket)
247 {
248 CacheLookupResult<T> result = data_for_time(time);
249
250 if (!result.has_new_data()) {
251 return;
252 }
253
254 /* TODO(kevindietrich): arrays are emptied when passed to the sockets, so for now we copy the
255 * arrays to avoid reloading the data */
256 T value = result.get_data();
257 node->set(*socket, value);
258 }
259
260 size_t memory_used() const
261 {
262 if constexpr (is_array<T>::value) {
263 size_t mem_used = 0;
264
265 for (const T &array : data) {
266 mem_used += array.size() * sizeof(array[0]);
267 }
268
269 return mem_used;
270 }
271
272 return data.size() * sizeof(T);
273 }
274
275 private:
276 const TimeIndexPair &get_index_for_time(const double time) const
277 {
278 std::pair<size_t, Alembic::Abc::chrono_t> index_pair;
279 index_pair = time_sampling.getNearIndex(time, index_data_map.size());
280 return index_data_map[index_pair.first];
281 }
282};
283
284/* Actual cache for the stored data.
285 * This caches the topological, transformation, and attribute data for a Mesh node or a Hair node
286 * inside of DataStores.
287 */
288struct CachedData {
289 DataStore<Transform> transforms{};
290
291 /* mesh data */
292 DataStore<array<float3>> vertices;
293 DataStore<array<int3>> triangles{};
294 /* triangle "loops" are the polygons' vertices indices used for indexing face varying attributes
295 * (like UVs) */
296 DataStore<array<int>> uv_loops{};
297 DataStore<array<int>> shader{};
298
299 /* subd data */
300 DataStore<array<int>> subd_start_corner;
301 DataStore<array<int>> subd_num_corners;
302 DataStore<array<bool>> subd_smooth;
303 DataStore<array<int>> subd_ptex_offset;
304 DataStore<array<int>> subd_face_corners;
305 DataStore<array<int>> subd_creases_edge;
306 DataStore<array<float>> subd_creases_weight;
307 DataStore<array<int>> subd_vertex_crease_indices;
308 DataStore<array<float>> subd_vertex_crease_weights;
309
310 /* hair data */
311 DataStore<array<float3>> curve_keys;
312 DataStore<array<float>> curve_radius;
313 DataStore<array<int>> curve_first_key;
314 DataStore<array<int>> curve_shader;
315
316 /* point data */
317 DataStore<array<float3>> points;
318 DataStore<array<float>> radiuses;
319 DataStore<array<int>> points_shader;
320
321 struct CachedAttribute {
324 TypeDesc type_desc;
325 ustring name;
326 DataStore<array<char>> data{};
327 };
328
329 vector<CachedAttribute> attributes{};
330
331 void clear();
332
333 CachedAttribute &add_attribute(const ustring &name,
334 const Alembic::Abc::TimeSampling &time_sampling);
335
336 bool is_constant() const;
337
338 void invalidate_last_loaded_time(bool attributes_only = false);
339
340 void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling);
341
342 size_t memory_used() const;
343};
344
345/* Representation of an Alembic object for the AlembicProcedural.
346 *
347 * The AlembicObject holds the path to the Alembic IObject inside of the archive that is desired
348 * for rendering, as well as the list of shaders that it is using.
349 *
350 * The names of the shaders should correspond to the names of the FaceSets inside of the Alembic
351 * archive for per-triangle shader association. If there is no FaceSets, or the names do not
352 * match, the first shader is used for rendering for all triangles.
353 */
354class AlembicObject : public Node {
355 public:
357
358 /* Path to the IObject inside of the archive. */
359 NODE_SOCKET_API(ustring, path)
360
361 /* Shaders used for rendering. */
362 NODE_SOCKET_API_ARRAY(array<Node *>, used_shaders)
363
364 /* Treat this subdivision object as a regular polygon mesh, so no subdivision will be performed.
365 */
366 NODE_SOCKET_API(bool, ignore_subdivision)
367
368 /* Maximum number of subdivisions for ISubD objects. */
369 NODE_SOCKET_API(int, subd_max_level)
370
371 /* Finest level of detail (in pixels) for the subdivision. */
372 NODE_SOCKET_API(float, subd_dicing_rate)
373
374 /* Scale the radius of points and curves. */
375 NODE_SOCKET_API(float, radius_scale)
376
377 AlembicObject();
378 ~AlembicObject() override;
379
380 private:
381 friend class AlembicProcedural;
382
383 void set_object(Object *object);
384 Object *get_object();
385
386 void load_data_in_cache(CachedData &cached_data,
387 AlembicProcedural *proc,
388 Alembic::AbcGeom::IPolyMeshSchema &schema,
389 Progress &progress);
390 void load_data_in_cache(CachedData &cached_data,
391 AlembicProcedural *proc,
392 Alembic::AbcGeom::ISubDSchema &schema,
393 Progress &progress);
394 void load_data_in_cache(CachedData &cached_data,
395 AlembicProcedural *proc,
396 const Alembic::AbcGeom::ICurvesSchema &schema,
397 Progress &progress);
398 void load_data_in_cache(CachedData &cached_data,
399 AlembicProcedural *proc,
400 const Alembic::AbcGeom::IPointsSchema &schema,
401 Progress &progress);
402
403 bool has_data_loaded() const;
404
405 /* Enumeration used to speed up the discrimination of an IObject as IObject::matches() methods
406 * are too expensive and show up in profiles. */
407 enum AbcSchemaType {
408 INVALID,
409 POLY_MESH,
410 SUBD,
411 CURVES,
412 POINTS,
413 };
414
415 bool need_shader_update = true;
416
417 AlembicObject *instance_of = nullptr;
418
419 Alembic::AbcCoreAbstract::TimeSamplingPtr xform_time_sampling;
420 MatrixSampleMap xform_samples;
421 Alembic::AbcGeom::IObject iobject;
422
423 /* Set if the path points to a valid IObject whose type is supported. */
424 AbcSchemaType schema_type;
425
426 CachedData &get_cached_data()
427 {
428 return cached_data_;
429 }
430
431 bool is_constant() const
432 {
433 return cached_data_.is_constant();
434 }
435
436 void clear_cache()
437 {
438 cached_data_.clear();
439 }
440
441 Object *object = nullptr;
442
443 bool data_loaded = false;
444
445 CachedData cached_data_;
446
447 void setup_transform_cache(CachedData &cached_data, const float scale);
448
449 AttributeRequestSet get_requested_attributes();
450};
451
452/* Procedural to render objects from a single Alembic archive.
453 *
454 * Every object desired to be rendered should be passed as an AlembicObject through the objects
455 * socket.
456 *
457 * This procedural will load the data set for the entire animation in memory on the first frame,
458 * and directly set the data for the new frames on the created Nodes if needed. This allows for
459 * faster updates between frames as it avoids reseeking the data on disk.
460 */
461class AlembicProcedural : public Procedural {
462 Alembic::AbcGeom::IArchive archive;
463 bool objects_loaded = false;
464 bool objects_modified = false;
465 Scene *scene_ = nullptr;
466
467 public:
469
470 /* The file path to the Alembic archive */
471 NODE_SOCKET_API(ustring, filepath)
472
473 /* Layers for the Alembic archive. Layers are in the order in which they override data, with the
474 * latter elements overriding the former ones. */
475 NODE_SOCKET_API_ARRAY(array<ustring>, layers)
476
477 /* The current frame to render. */
478 NODE_SOCKET_API(float, frame)
479
480 /* The first frame to load data for. */
481 NODE_SOCKET_API(float, start_frame)
482
483 /* The last frame to load data for. */
484 NODE_SOCKET_API(float, end_frame)
485
486 /* Subtracted to the current frame. */
487 NODE_SOCKET_API(float, frame_offset)
488
489 /* The frame rate used for rendering in units of frames per second. */
490 NODE_SOCKET_API(float, frame_rate)
491
492 /* Set the default radius to use for curves when the Alembic Curves Schemas do not have radius
493 * information. */
494 NODE_SOCKET_API(float, default_radius)
495
496 /* Multiplier to account for differences in default units for measuring objects in various
497 * software. */
498 NODE_SOCKET_API(float, scale)
499
500 /* Cache controls */
501 NODE_SOCKET_API(bool, use_prefetch)
502
503 /* Memory limit for the cache, if the data does not fit within this limit, rendering is aborted.
504 */
505 NODE_SOCKET_API(int, prefetch_cache_size)
506
507 AlembicProcedural();
508 ~AlembicProcedural() override;
509
510 /* Populates the Cycles scene with Nodes for every contained AlembicObject on the first
511 * invocation, and updates the data on subsequent invocations if the frame changed. */
512 void generate(Scene *scene, Progress &progress) override;
513
514 /* Tag for an update only if something was modified. */
515 void tag_update(Scene *scene);
516
517 /* This should be called by scene exporters to request the rendering of an object located
518 * in the Alembic archive at the given path.
519 *
520 * Since we lazily load object, the function does not validate the existence of the object
521 * in the archive. If no objects with such path if found in the archive during the next call
522 * to `generate`, it will be ignored.
523 *
524 * Returns a pointer to an existing or a newly created AlembicObject for the given path. */
525 AlembicObject *get_or_create_object(const ustring &path);
526
527 private:
528 /* Load the data for all the objects whose data has not yet been loaded. */
529 void load_objects(Progress &progress);
530
531 /* Traverse the Alembic hierarchy to lookup the IObjects for the AlembicObjects that were
532 * specified in our objects socket, and accumulate all of the transformations samples along the
533 * way for each IObject. */
534 void walk_hierarchy(Alembic::AbcGeom::IObject parent,
535 const Alembic::AbcGeom::ObjectHeader &header,
536 MatrixSamplesData matrix_samples_data,
537 const unordered_map<string, AlembicObject *> &object_map,
538 Progress &progress);
539
540 /* Read the data for an IPolyMesh at the specified frame_time. Creates corresponding Geometry and
541 * Object Nodes in the Cycles scene if none exist yet. */
542 void read_mesh(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time);
543
544 /* Read the data for an ICurves at the specified frame_time. Creates corresponding Geometry and
545 * Object Nodes in the Cycles scene if none exist yet. */
546 void read_curves(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time);
547
548 /* Read the data for an IPoints at the specified frame_time. Creates corresponding Geometry and
549 * Object Nodes in the Cycles scene if none exist yet. */
550 void read_points(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time);
551
552 /* Read the data for an ISubD at the specified frame_time. Creates corresponding Geometry and
553 * Object Nodes in the Cycles scene if none exist yet. */
554 void read_subd(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time);
555
556 void build_caches(Progress &progress);
557
558 size_t get_prefetch_cache_size_in_bytes() const
559 {
560 /* prefetch_cache_size is in megabytes, so convert to bytes. */
561 return static_cast<size_t>(prefetch_cache_size) * 1024 * 1024;
562 }
563};
564
566
567#endif
struct Object Object
struct Scene Scene
float progress
Definition WM_types.hh:1019
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const void * element
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
virtual void generate(Scene *scene, Progress &progress)=0
#define CCL_NAMESPACE_END
#define assert(assertion)
#define NODE_SOCKET_API_ARRAY(type_, name)
Definition graph/node.h:63
#define NODE_SOCKET_API(type_, name)
Definition graph/node.h:55
AttributeStandard
AttributeElement
static ulong state[N]
#define T
static void clear(Message &msg)
Definition msgfmt.cc:213
#define NODE_DECLARE
Definition node_type.h:142
long long TypeDesc
void set(const SocketType &input, bool value)