Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/cycles/render/alembic.h')
-rw-r--r--intern/cycles/render/alembic.h404
1 files changed, 404 insertions, 0 deletions
diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h
new file mode 100644
index 00000000000..77bdac09ba2
--- /dev/null
+++ b/intern/cycles/render/alembic.h
@@ -0,0 +1,404 @@
+/*
+ * 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>;
+
+/* 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 {
+};
+
+/* 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 {
+ struct DataTimePair {
+ double time = 0;
+ T data{};
+ };
+
+ vector<DataTimePair> data{};
+ Alembic::AbcCoreAbstract::TimeSampling time_sampling{};
+
+ double last_loaded_time = std::numeric_limits<double>::max();
+
+ public:
+ 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. */
+ T *data_for_time(double time)
+ {
+ if (size() == 0) {
+ return nullptr;
+ }
+
+ std::pair<size_t, Alembic::Abc::chrono_t> index_pair;
+ index_pair = time_sampling.getNearIndex(time, data.size());
+ DataTimePair &data_pair = data[index_pair.first];
+
+ if (last_loaded_time == data_pair.time) {
+ return nullptr;
+ }
+
+ last_loaded_time = data_pair.time;
+
+ return &data_pair.data;
+ }
+
+ /* 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 */
+ T *data_for_time_no_check(double time)
+ {
+ if (size() == 0) {
+ return nullptr;
+ }
+
+ std::pair<size_t, Alembic::Abc::chrono_t> index_pair;
+ index_pair = time_sampling.getNearIndex(time, data.size());
+ DataTimePair &data_pair = data[index_pair.first];
+ return &data_pair.data;
+ }
+
+ void add_data(T &data_, double time)
+ {
+ if constexpr (is_array<T>::value) {
+ data.emplace_back();
+ data.back().data.steal_data(data_);
+ data.back().time = time;
+ return;
+ }
+
+ data.push_back({time, data_});
+ }
+
+ bool is_constant() const
+ {
+ return data.size() <= 1;
+ }
+
+ size_t size() const
+ {
+ return data.size();
+ }
+
+ void clear()
+ {
+ invalidate_last_loaded_time();
+ data.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)
+ {
+ T *data_ = data_for_time(time);
+
+ if (data_ == nullptr) {
+ 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 = *data_;
+ node->set(*socket, value);
+ }
+};
+
+/* 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<int3>> triangles_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);
+};
+
+/* 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)
+
+ /* 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_all_data(AlembicProcedural *proc,
+ Alembic::AbcGeom::IPolyMeshSchema &schema,
+ float scale,
+ Progress &progress);
+ void load_all_data(AlembicProcedural *proc,
+ Alembic::AbcGeom::ISubDSchema &schema,
+ float scale,
+ Progress &progress);
+ void load_all_data(AlembicProcedural *proc,
+ const Alembic::AbcGeom::ICurvesSchema &schema,
+ float scale,
+ Progress &progress,
+ float default_radius);
+
+ bool has_data_loaded() const;
+
+ bool need_shader_update = true;
+
+ MatrixSampleMap xform_samples;
+ Alembic::AbcGeom::IObject iobject;
+ Transform xform;
+
+ CachedData &get_cached_data()
+ {
+ return cached_data;
+ }
+
+ bool is_constant() const
+ {
+ return cached_data.is_constant();
+ }
+
+ Object *object = nullptr;
+
+ bool data_loaded = false;
+
+ CachedData cached_data;
+
+ void update_shader_attributes(const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,
+ Progress &progress);
+
+ void read_attribute(const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,
+ const ustring &attr_name,
+ Progress &progress);
+
+ template<typename SchemaType>
+ void read_face_sets(SchemaType &schema,
+ array<int> &polygon_to_shader,
+ Alembic::AbcGeom::ISampleSelector sample_sel);
+
+ void setup_transform_cache(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)
+
+ 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);
+
+ /* Add an object to our list of objects, and tag the socket as modified. */
+ void add_object(AlembicObject *object);
+
+ /* Tag for an update only if something was modified. */
+ void tag_update(Scene *scene);
+
+ /* Returns a pointer to an exisiting or a newly created AlembicObject for the given path. */
+ AlembicObject *get_or_create_object(const ustring &path);
+
+ private:
+ /* 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,
+ MatrixSampleMap *xform_samples,
+ 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(Scene *scene,
+ AlembicObject *abc_object,
+ Alembic::AbcGeom::Abc::chrono_t frame_time,
+ Progress &progress);
+
+ /* 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(Scene *scene,
+ AlembicObject *abc_object,
+ Alembic::AbcGeom::Abc::chrono_t frame_time,
+ Progress &progress);
+
+ /* 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(Scene *scene,
+ AlembicObject *abc_object,
+ Alembic::AbcGeom::Abc::chrono_t frame_time,
+ Progress &progress);
+};
+
+CCL_NAMESPACE_END
+
+#endif