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