diff options
Diffstat (limited to 'intern/cycles/render/alembic.h')
-rw-r--r-- | intern/cycles/render/alembic.h | 568 |
1 files changed, 0 insertions, 568 deletions
diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h deleted file mode 100644 index 8e166a5ab04..00000000000 --- a/intern/cycles/render/alembic.h +++ /dev/null @@ -1,568 +0,0 @@ -/* - * Copyright 2011-2018 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "graph/node.h" -#include "render/attribute.h" -#include "render/procedural.h" -#include "util/util_set.h" -#include "util/util_transform.h" -#include "util/util_vector.h" - -#ifdef WITH_ALEMBIC - -# include <Alembic/AbcCoreFactory/All.h> -# include <Alembic/AbcGeom/All.h> - -CCL_NAMESPACE_BEGIN - -class AlembicProcedural; -class Geometry; -class Object; -class Progress; -class Shader; - -using MatrixSampleMap = std::map<Alembic::Abc::chrono_t, Alembic::Abc::M44d>; - -struct MatrixSamplesData { - MatrixSampleMap *samples = nullptr; - Alembic::AbcCoreAbstract::TimeSamplingPtr time_sampling; -}; - -/* Helpers to detect if some type is a `ccl::array`. */ -template<typename> struct is_array : public std::false_type { -}; - -template<typename T> struct is_array<array<T>> : public std::true_type { -}; - -/* Holds the data for a cache lookup at a given time, as well as information to - * help disambiguate successes or failures to get data from the cache. */ -template<typename T> class CacheLookupResult { - enum class State { - NEW_DATA, - ALREADY_LOADED, - NO_DATA_FOR_TIME, - }; - - T *data; - State state; - - protected: - /* Prevent default construction outside of the class: for a valid result, we - * should use the static functions below. */ - CacheLookupResult() = default; - - public: - static CacheLookupResult new_data(T *data_) - { - CacheLookupResult result; - result.data = data_; - result.state = State::NEW_DATA; - return result; - } - - static CacheLookupResult no_data_found_for_time() - { - CacheLookupResult result; - result.data = nullptr; - result.state = State::NO_DATA_FOR_TIME; - return result; - } - - static CacheLookupResult already_loaded() - { - CacheLookupResult result; - result.data = nullptr; - result.state = State::ALREADY_LOADED; - return result; - } - - /* This should only be call if new data is available. */ - const T &get_data() const - { - assert(state == State::NEW_DATA); - assert(data != nullptr); - return *data; - } - - T *get_data_or_null() const - { - // data_ should already be null if there is no new data so no need to check - return data; - } - - bool has_new_data() const - { - return state == State::NEW_DATA; - } - - bool has_already_loaded() const - { - return state == State::ALREADY_LOADED; - } - - bool has_no_data_for_time() const - { - return state == State::NO_DATA_FOR_TIME; - } -}; - -/* Store the data set for an animation at every time points, or at the beginning of the animation - * for constant data. - * - * The data is supposed to be stored in chronological order, and is looked up using the current - * animation time in seconds using the TimeSampling from the Alembic property. */ -template<typename T> class DataStore { - /* Holds information to map a cache entry for a given time to an index into the data array. */ - struct TimeIndexPair { - /* Frame time for this entry. */ - double time = 0; - /* Frame time for the data pointed to by `index`. */ - double source_time = 0; - /* Index into the data array. */ - size_t index = 0; - }; - - /* This is the actual data that is stored. We deduplicate data across frames to avoid storing - * values if they have not changed yet (e.g. the triangles for a building before fracturing, or a - * fluid simulation before a break or splash) */ - vector<T> data{}; - - /* This is used to map they entry for a given time to an index into the data array, multiple - * frames can point to the same index. */ - vector<TimeIndexPair> index_data_map{}; - - Alembic::AbcCoreAbstract::TimeSampling time_sampling{}; - - double last_loaded_time = std::numeric_limits<double>::max(); - - public: - /* Keys used to compare values. */ - Alembic::AbcCoreAbstract::ArraySample::Key key1; - Alembic::AbcCoreAbstract::ArraySample::Key key2; - - void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling_) - { - time_sampling = time_sampling_; - } - - Alembic::AbcCoreAbstract::TimeSampling get_time_sampling() const - { - return time_sampling; - } - - /* Get the data for the specified time. - * Return nullptr if there is no data or if the data for this time was already loaded. */ - CacheLookupResult<T> data_for_time(double time) - { - if (size() == 0) { - return CacheLookupResult<T>::no_data_found_for_time(); - } - - const TimeIndexPair &index = get_index_for_time(time); - - if (index.index == -1ul) { - return CacheLookupResult<T>::no_data_found_for_time(); - } - - if (last_loaded_time == index.time || last_loaded_time == index.source_time) { - return CacheLookupResult<T>::already_loaded(); - } - - last_loaded_time = index.source_time; - - assert(index.index < data.size()); - - return CacheLookupResult<T>::new_data(&data[index.index]); - } - - /* get the data for the specified time, but do not check if the data was already loaded for this - * time return nullptr if there is no data */ - CacheLookupResult<T> data_for_time_no_check(double time) - { - if (size() == 0) { - return CacheLookupResult<T>::no_data_found_for_time(); - } - - const TimeIndexPair &index = get_index_for_time(time); - - if (index.index == -1ul) { - return CacheLookupResult<T>::no_data_found_for_time(); - } - - assert(index.index < data.size()); - - return CacheLookupResult<T>::new_data(&data[index.index]); - } - - void add_data(T &data_, double time) - { - index_data_map.push_back({time, time, data.size()}); - - if constexpr (is_array<T>::value) { - data.emplace_back(); - data.back().steal_data(data_); - return; - } - - data.push_back(data_); - } - - void reuse_data_for_last_time(double time) - { - const TimeIndexPair &data_index = index_data_map.back(); - index_data_map.push_back({time, data_index.source_time, data_index.index}); - } - - void add_no_data(double time) - { - index_data_map.push_back({time, time, -1ul}); - } - - bool is_constant() const - { - return data.size() <= 1; - } - - size_t size() const - { - return data.size(); - } - - void clear() - { - invalidate_last_loaded_time(); - data.clear(); - index_data_map.clear(); - } - - void invalidate_last_loaded_time() - { - last_loaded_time = std::numeric_limits<double>::max(); - } - - /* Copy the data for the specified time to the node's socket. If there is no - * data for this time or it was already loaded, do nothing. */ - void copy_to_socket(double time, Node *node, const SocketType *socket) - { - CacheLookupResult<T> result = data_for_time(time); - - if (!result.has_new_data()) { - return; - } - - /* TODO(kevindietrich): arrays are emptied when passed to the sockets, so for now we copy the - * arrays to avoid reloading the data */ - T value = result.get_data(); - node->set(*socket, value); - } - - size_t memory_used() const - { - if constexpr (is_array<T>::value) { - size_t mem_used = 0; - - for (const T &array : data) { - mem_used += array.size() * sizeof(array[0]); - } - - return mem_used; - } - - return data.size() * sizeof(T); - } - - private: - const TimeIndexPair &get_index_for_time(double time) const - { - std::pair<size_t, Alembic::Abc::chrono_t> index_pair; - index_pair = time_sampling.getNearIndex(time, index_data_map.size()); - return index_data_map[index_pair.first]; - } -}; - -/* Actual cache for the stored data. - * This caches the topological, transformation, and attribute data for a Mesh node or a Hair node - * inside of DataStores. - */ -struct CachedData { - DataStore<Transform> transforms{}; - - /* mesh data */ - DataStore<array<float3>> vertices; - DataStore<array<int3>> triangles{}; - /* triangle "loops" are the polygons' vertices indices used for indexing face varying attributes - * (like UVs) */ - DataStore<array<int>> uv_loops{}; - DataStore<array<int>> shader{}; - - /* subd data */ - DataStore<array<int>> subd_start_corner; - DataStore<array<int>> subd_num_corners; - DataStore<array<bool>> subd_smooth; - DataStore<array<int>> subd_ptex_offset; - DataStore<array<int>> subd_face_corners; - DataStore<int> num_ngons; - DataStore<array<int>> subd_creases_edge; - DataStore<array<float>> subd_creases_weight; - - /* hair data */ - DataStore<array<float3>> curve_keys; - DataStore<array<float>> curve_radius; - DataStore<array<int>> curve_first_key; - DataStore<array<int>> curve_shader; - - struct CachedAttribute { - AttributeStandard std; - AttributeElement element; - TypeDesc type_desc; - ustring name; - DataStore<array<char>> data{}; - }; - - vector<CachedAttribute> attributes{}; - - void clear(); - - CachedAttribute &add_attribute(const ustring &name, - const Alembic::Abc::TimeSampling &time_sampling); - - bool is_constant() const; - - void invalidate_last_loaded_time(bool attributes_only = false); - - void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling); - - size_t memory_used() const; -}; - -/* Representation of an Alembic object for the AlembicProcedural. - * - * The AlembicObject holds the path to the Alembic IObject inside of the archive that is desired - * for rendering, as well as the list of shaders that it is using. - * - * The names of the shaders should correspond to the names of the FaceSets inside of the Alembic - * archive for per-triangle shader association. If there is no FaceSets, or the names do not - * match, the first shader is used for rendering for all triangles. - */ -class AlembicObject : public Node { - public: - NODE_DECLARE - - /* Path to the IObject inside of the archive. */ - NODE_SOCKET_API(ustring, path) - - /* Shaders used for rendering. */ - NODE_SOCKET_API_ARRAY(array<Node *>, used_shaders) - - /* Treat this subdivision object as a regular polygon mesh, so no subdivision will be performed. - */ - NODE_SOCKET_API(bool, ignore_subdivision) - - /* Maximum number of subdivisions for ISubD objects. */ - NODE_SOCKET_API(int, subd_max_level) - - /* Finest level of detail (in pixels) for the subdivision. */ - NODE_SOCKET_API(float, subd_dicing_rate) - - /* Scale the radius of points and curves. */ - NODE_SOCKET_API(float, radius_scale) - - AlembicObject(); - ~AlembicObject(); - - private: - friend class AlembicProcedural; - - void set_object(Object *object); - Object *get_object(); - - void load_data_in_cache(CachedData &cached_data, - AlembicProcedural *proc, - Alembic::AbcGeom::IPolyMeshSchema &schema, - Progress &progress); - void load_data_in_cache(CachedData &cached_data, - AlembicProcedural *proc, - Alembic::AbcGeom::ISubDSchema &schema, - Progress &progress); - void load_data_in_cache(CachedData &cached_data, - AlembicProcedural *proc, - const Alembic::AbcGeom::ICurvesSchema &schema, - Progress &progress); - - bool has_data_loaded() const; - - /* Enumeration used to speed up the discrimination of an IObject as IObject::matches() methods - * are too expensive and show up in profiles. */ - enum AbcSchemaType { - INVALID, - POLY_MESH, - SUBD, - CURVES, - }; - - bool need_shader_update = true; - - AlembicObject *instance_of = nullptr; - - Alembic::AbcCoreAbstract::TimeSamplingPtr xform_time_sampling; - MatrixSampleMap xform_samples; - Alembic::AbcGeom::IObject iobject; - - /* Set if the path points to a valid IObject whose type is supported. */ - AbcSchemaType schema_type; - - CachedData &get_cached_data() - { - return cached_data_; - } - - bool is_constant() const - { - return cached_data_.is_constant(); - } - - void clear_cache() - { - cached_data_.clear(); - } - - Object *object = nullptr; - - bool data_loaded = false; - - CachedData cached_data_; - - void setup_transform_cache(CachedData &cached_data, float scale); - - AttributeRequestSet get_requested_attributes(); -}; - -/* Procedural to render objects from a single Alembic archive. - * - * Every object desired to be rendered should be passed as an AlembicObject through the objects - * socket. - * - * This procedural will load the data set for the entire animation in memory on the first frame, - * and directly set the data for the new frames on the created Nodes if needed. This allows for - * faster updates between frames as it avoids reseeking the data on disk. - */ -class AlembicProcedural : public Procedural { - Alembic::AbcGeom::IArchive archive; - bool objects_loaded; - Scene *scene_; - - public: - NODE_DECLARE - - /* The file path to the Alembic archive */ - NODE_SOCKET_API(ustring, filepath) - - /* The current frame to render. */ - NODE_SOCKET_API(float, frame) - - /* The first frame to load data for. */ - NODE_SOCKET_API(float, start_frame) - - /* The last frame to load data for. */ - NODE_SOCKET_API(float, end_frame) - - /* Subtracted to the current frame. */ - NODE_SOCKET_API(float, frame_offset) - - /* The frame rate used for rendering in units of frames per second. */ - NODE_SOCKET_API(float, frame_rate) - - /* List of AlembicObjects to render. */ - NODE_SOCKET_API_ARRAY(array<Node *>, objects) - - /* Set the default radius to use for curves when the Alembic Curves Schemas do not have radius - * information. */ - NODE_SOCKET_API(float, default_radius) - - /* Multiplier to account for differences in default units for measuring objects in various - * software. */ - NODE_SOCKET_API(float, scale) - - /* Cache controls */ - NODE_SOCKET_API(bool, use_prefetch) - - /* Memory limit for the cache, if the data does not fit within this limit, rendering is aborted. - */ - NODE_SOCKET_API(int, prefetch_cache_size) - - AlembicProcedural(); - ~AlembicProcedural(); - - /* Populates the Cycles scene with Nodes for every contained AlembicObject on the first - * invocation, and updates the data on subsequent invocations if the frame changed. */ - void generate(Scene *scene, Progress &progress); - - /* Tag for an update only if something was modified. */ - void tag_update(Scene *scene); - - /* This should be called by scene exporters to request the rendering of an object located - * in the Alembic archive at the given path. - * - * Since we lazily load object, the function does not validate the existence of the object - * in the archive. If no objects with such path if found in the archive during the next call - * to `generate`, it will be ignored. - * - * Returns a pointer to an existing or a newly created AlembicObject for the given path. */ - AlembicObject *get_or_create_object(const ustring &path); - - private: - /* Add an object to our list of objects, and tag the socket as modified. */ - void add_object(AlembicObject *object); - - /* Load the data for all the objects whose data has not yet been loaded. */ - void load_objects(Progress &progress); - - /* Traverse the Alembic hierarchy to lookup the IObjects for the AlembicObjects that were - * specified in our objects socket, and accumulate all of the transformations samples along the - * way for each IObject. */ - void walk_hierarchy(Alembic::AbcGeom::IObject parent, - const Alembic::AbcGeom::ObjectHeader &ohead, - MatrixSamplesData matrix_samples_data, - const unordered_map<string, AlembicObject *> &object_map, - Progress &progress); - - /* Read the data for an IPolyMesh at the specified frame_time. Creates corresponding Geometry and - * Object Nodes in the Cycles scene if none exist yet. */ - void read_mesh(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time); - - /* Read the data for an ICurves at the specified frame_time. Creates corresponding Geometry and - * Object Nodes in the Cycles scene if none exist yet. */ - void read_curves(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time); - - /* Read the data for an ISubD at the specified frame_time. Creates corresponding Geometry and - * Object Nodes in the Cycles scene if none exist yet. */ - void read_subd(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time); - - void build_caches(Progress &progress); - - size_t get_prefetch_cache_size_in_bytes() const - { - /* prefetch_cache_size is in megabytes, so convert to bytes. */ - return static_cast<size_t>(prefetch_cache_size) * 1024 * 1024; - } -}; - -CCL_NAMESPACE_END - -#endif |