2023-06-14 16:52:36 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
2021-01-25 15:12:00 +01:00
|
|
|
|
2021-10-24 14:19:19 +02:00
|
|
|
#include "scene/alembic.h"
|
|
|
|
|
|
|
|
|
|
#include "scene/alembic_read.h"
|
|
|
|
|
#include "scene/camera.h"
|
|
|
|
|
#include "scene/curves.h"
|
2024-12-26 17:53:59 +01:00
|
|
|
#include "scene/hair.h"
|
2021-10-24 14:19:19 +02:00
|
|
|
#include "scene/mesh.h"
|
|
|
|
|
#include "scene/object.h"
|
2021-12-01 17:30:46 +01:00
|
|
|
#include "scene/pointcloud.h"
|
2021-10-24 14:19:19 +02:00
|
|
|
#include "scene/scene.h"
|
|
|
|
|
#include "scene/shader.h"
|
2021-01-25 15:12:00 +01:00
|
|
|
|
2021-10-24 14:19:19 +02:00
|
|
|
#include "util/log.h"
|
|
|
|
|
#include "util/progress.h"
|
2024-12-30 02:20:36 +01:00
|
|
|
#include "util/set.h"
|
2021-10-24 14:19:19 +02:00
|
|
|
#include "util/transform.h"
|
|
|
|
|
#include "util/vector.h"
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
#ifdef WITH_ALEMBIC
|
|
|
|
|
|
|
|
|
|
using namespace Alembic::AbcGeom;
|
|
|
|
|
|
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
|
|
2021-05-04 00:42:34 +10:00
|
|
|
/* TODO(kevindietrich): motion blur support. */
|
2021-01-25 15:12:00 +01:00
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
template<typename SchemaType>
|
|
|
|
|
static vector<FaceSetShaderIndexPair> parse_face_sets_for_shader_assignment(
|
|
|
|
|
SchemaType &schema, const array<Node *> &used_shaders)
|
|
|
|
|
{
|
|
|
|
|
vector<FaceSetShaderIndexPair> result;
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> face_set_names;
|
|
|
|
|
schema.getFaceSetNames(face_set_names);
|
|
|
|
|
|
|
|
|
|
if (face_set_names.empty()) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const std::string &face_set_name : face_set_names) {
|
|
|
|
|
int shader_index = 0;
|
|
|
|
|
|
|
|
|
|
for (Node *node : used_shaders) {
|
|
|
|
|
if (node->name == face_set_name) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++shader_index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shader_index >= used_shaders.size()) {
|
|
|
|
|
/* use the first shader instead if none was found */
|
|
|
|
|
shader_index = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Alembic::AbcGeom::IFaceSet face_set = schema.getFaceSet(face_set_name);
|
|
|
|
|
|
|
|
|
|
if (!face_set.valid()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.push_back({face_set, shader_index});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-25 15:12:00 +01:00
|
|
|
void CachedData::clear()
|
|
|
|
|
{
|
|
|
|
|
attributes.clear();
|
|
|
|
|
curve_first_key.clear();
|
|
|
|
|
curve_keys.clear();
|
|
|
|
|
curve_radius.clear();
|
|
|
|
|
curve_shader.clear();
|
|
|
|
|
num_ngons.clear();
|
|
|
|
|
shader.clear();
|
|
|
|
|
subd_creases_edge.clear();
|
|
|
|
|
subd_creases_weight.clear();
|
|
|
|
|
subd_face_corners.clear();
|
|
|
|
|
subd_num_corners.clear();
|
|
|
|
|
subd_ptex_offset.clear();
|
|
|
|
|
subd_smooth.clear();
|
|
|
|
|
subd_start_corner.clear();
|
|
|
|
|
transforms.clear();
|
|
|
|
|
triangles.clear();
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
uv_loops.clear();
|
2021-01-25 15:12:00 +01:00
|
|
|
vertices.clear();
|
2021-12-01 17:30:46 +01:00
|
|
|
points.clear();
|
|
|
|
|
radiuses.clear();
|
|
|
|
|
points_shader.clear();
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
for (CachedAttribute &attr : attributes) {
|
|
|
|
|
attr.data.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attributes.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CachedData::CachedAttribute &CachedData::add_attribute(const ustring &name,
|
|
|
|
|
const TimeSampling &time_sampling)
|
|
|
|
|
{
|
|
|
|
|
for (auto &attr : attributes) {
|
|
|
|
|
if (attr.name == name) {
|
|
|
|
|
return attr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CachedAttribute &attr = attributes.emplace_back();
|
|
|
|
|
attr.name = name;
|
|
|
|
|
attr.data.set_time_sampling(time_sampling);
|
|
|
|
|
return attr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CachedData::is_constant() const
|
|
|
|
|
{
|
|
|
|
|
# define CHECK_IF_CONSTANT(data) \
|
|
|
|
|
if (!data.is_constant()) { \
|
|
|
|
|
return false; \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK_IF_CONSTANT(curve_first_key)
|
|
|
|
|
CHECK_IF_CONSTANT(curve_keys)
|
|
|
|
|
CHECK_IF_CONSTANT(curve_radius)
|
|
|
|
|
CHECK_IF_CONSTANT(curve_shader)
|
|
|
|
|
CHECK_IF_CONSTANT(num_ngons)
|
|
|
|
|
CHECK_IF_CONSTANT(shader)
|
|
|
|
|
CHECK_IF_CONSTANT(subd_creases_edge)
|
|
|
|
|
CHECK_IF_CONSTANT(subd_creases_weight)
|
|
|
|
|
CHECK_IF_CONSTANT(subd_face_corners)
|
|
|
|
|
CHECK_IF_CONSTANT(subd_num_corners)
|
|
|
|
|
CHECK_IF_CONSTANT(subd_ptex_offset)
|
|
|
|
|
CHECK_IF_CONSTANT(subd_smooth)
|
|
|
|
|
CHECK_IF_CONSTANT(subd_start_corner)
|
|
|
|
|
CHECK_IF_CONSTANT(transforms)
|
|
|
|
|
CHECK_IF_CONSTANT(triangles)
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
CHECK_IF_CONSTANT(uv_loops)
|
2021-01-25 15:12:00 +01:00
|
|
|
CHECK_IF_CONSTANT(vertices)
|
2021-12-01 17:30:46 +01:00
|
|
|
CHECK_IF_CONSTANT(points)
|
|
|
|
|
CHECK_IF_CONSTANT(radiuses)
|
|
|
|
|
CHECK_IF_CONSTANT(points_shader)
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
for (const CachedAttribute &attr : attributes) {
|
|
|
|
|
if (!attr.data.is_constant()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
# undef CHECK_IF_CONSTANT
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CachedData::invalidate_last_loaded_time(bool attributes_only)
|
|
|
|
|
{
|
|
|
|
|
if (attributes_only) {
|
|
|
|
|
for (CachedAttribute &attr : attributes) {
|
|
|
|
|
attr.data.invalidate_last_loaded_time();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
curve_first_key.invalidate_last_loaded_time();
|
|
|
|
|
curve_keys.invalidate_last_loaded_time();
|
|
|
|
|
curve_radius.invalidate_last_loaded_time();
|
|
|
|
|
curve_shader.invalidate_last_loaded_time();
|
|
|
|
|
num_ngons.invalidate_last_loaded_time();
|
|
|
|
|
shader.invalidate_last_loaded_time();
|
|
|
|
|
subd_creases_edge.invalidate_last_loaded_time();
|
|
|
|
|
subd_creases_weight.invalidate_last_loaded_time();
|
|
|
|
|
subd_face_corners.invalidate_last_loaded_time();
|
|
|
|
|
subd_num_corners.invalidate_last_loaded_time();
|
|
|
|
|
subd_ptex_offset.invalidate_last_loaded_time();
|
|
|
|
|
subd_smooth.invalidate_last_loaded_time();
|
|
|
|
|
subd_start_corner.invalidate_last_loaded_time();
|
|
|
|
|
transforms.invalidate_last_loaded_time();
|
|
|
|
|
triangles.invalidate_last_loaded_time();
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
uv_loops.invalidate_last_loaded_time();
|
2021-01-25 15:12:00 +01:00
|
|
|
vertices.invalidate_last_loaded_time();
|
2021-12-01 17:30:46 +01:00
|
|
|
points.invalidate_last_loaded_time();
|
|
|
|
|
radiuses.invalidate_last_loaded_time();
|
|
|
|
|
points_shader.invalidate_last_loaded_time();
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CachedData::set_time_sampling(TimeSampling time_sampling)
|
|
|
|
|
{
|
|
|
|
|
curve_first_key.set_time_sampling(time_sampling);
|
|
|
|
|
curve_keys.set_time_sampling(time_sampling);
|
|
|
|
|
curve_radius.set_time_sampling(time_sampling);
|
|
|
|
|
curve_shader.set_time_sampling(time_sampling);
|
|
|
|
|
num_ngons.set_time_sampling(time_sampling);
|
|
|
|
|
shader.set_time_sampling(time_sampling);
|
|
|
|
|
subd_creases_edge.set_time_sampling(time_sampling);
|
|
|
|
|
subd_creases_weight.set_time_sampling(time_sampling);
|
|
|
|
|
subd_face_corners.set_time_sampling(time_sampling);
|
|
|
|
|
subd_num_corners.set_time_sampling(time_sampling);
|
|
|
|
|
subd_ptex_offset.set_time_sampling(time_sampling);
|
|
|
|
|
subd_smooth.set_time_sampling(time_sampling);
|
|
|
|
|
subd_start_corner.set_time_sampling(time_sampling);
|
|
|
|
|
transforms.set_time_sampling(time_sampling);
|
|
|
|
|
triangles.set_time_sampling(time_sampling);
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
uv_loops.set_time_sampling(time_sampling);
|
2021-01-25 15:12:00 +01:00
|
|
|
vertices.set_time_sampling(time_sampling);
|
2021-12-01 17:30:46 +01:00
|
|
|
points.set_time_sampling(time_sampling);
|
|
|
|
|
radiuses.set_time_sampling(time_sampling);
|
|
|
|
|
points_shader.set_time_sampling(time_sampling);
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
for (CachedAttribute &attr : attributes) {
|
|
|
|
|
attr.data.set_time_sampling(time_sampling);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-20 14:29:05 +02:00
|
|
|
size_t CachedData::memory_used() const
|
|
|
|
|
{
|
|
|
|
|
size_t mem_used = 0;
|
|
|
|
|
|
|
|
|
|
mem_used += curve_first_key.memory_used();
|
|
|
|
|
mem_used += curve_keys.memory_used();
|
|
|
|
|
mem_used += curve_radius.memory_used();
|
|
|
|
|
mem_used += curve_shader.memory_used();
|
|
|
|
|
mem_used += num_ngons.memory_used();
|
|
|
|
|
mem_used += shader.memory_used();
|
|
|
|
|
mem_used += subd_creases_edge.memory_used();
|
|
|
|
|
mem_used += subd_creases_weight.memory_used();
|
|
|
|
|
mem_used += subd_face_corners.memory_used();
|
|
|
|
|
mem_used += subd_num_corners.memory_used();
|
|
|
|
|
mem_used += subd_ptex_offset.memory_used();
|
|
|
|
|
mem_used += subd_smooth.memory_used();
|
|
|
|
|
mem_used += subd_start_corner.memory_used();
|
|
|
|
|
mem_used += transforms.memory_used();
|
|
|
|
|
mem_used += triangles.memory_used();
|
|
|
|
|
mem_used += uv_loops.memory_used();
|
|
|
|
|
mem_used += vertices.memory_used();
|
2021-12-01 17:30:46 +01:00
|
|
|
mem_used += points.memory_used();
|
|
|
|
|
mem_used += radiuses.memory_used();
|
|
|
|
|
mem_used += points_shader.memory_used();
|
2021-08-20 14:29:05 +02:00
|
|
|
|
|
|
|
|
for (const CachedAttribute &attr : attributes) {
|
|
|
|
|
mem_used += attr.data.memory_used();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mem_used;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
static M44d convert_yup_zup(const M44d &mtx, const float scale_mult)
|
2021-01-25 15:12:00 +01:00
|
|
|
{
|
2024-12-29 17:32:00 +01:00
|
|
|
V3d scale;
|
|
|
|
|
V3d shear;
|
|
|
|
|
V3d rotation;
|
|
|
|
|
V3d translation;
|
2023-04-04 20:00:56 +02:00
|
|
|
|
|
|
|
|
if (!extractSHRT(mtx,
|
|
|
|
|
scale,
|
|
|
|
|
shear,
|
|
|
|
|
rotation,
|
|
|
|
|
translation,
|
|
|
|
|
true,
|
|
|
|
|
IMATH_INTERNAL_NAMESPACE::Euler<double>::XZY))
|
|
|
|
|
{
|
|
|
|
|
return mtx;
|
|
|
|
|
}
|
2021-01-25 15:12:00 +01:00
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
M44d rot_mat;
|
|
|
|
|
M44d scale_mat;
|
|
|
|
|
M44d trans_mat;
|
2021-01-25 15:12:00 +01:00
|
|
|
rot_mat.setEulerAngles(V3d(rotation.x, -rotation.z, rotation.y));
|
|
|
|
|
scale_mat.setScale(V3d(scale.x, scale.z, scale.y));
|
|
|
|
|
trans_mat.setTranslation(V3d(translation.x, -translation.z, translation.y));
|
|
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
const M44d temp_mat = scale_mat * rot_mat * trans_mat;
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
scale_mat.setScale(static_cast<double>(scale_mult));
|
|
|
|
|
|
|
|
|
|
return temp_mat * scale_mat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void transform_decompose(
|
|
|
|
|
const M44d &mat, V3d &scale, V3d &shear, Quatd &rotation, V3d &translation)
|
|
|
|
|
{
|
|
|
|
|
M44d mat_remainder(mat);
|
|
|
|
|
|
|
|
|
|
/* extract scale and shear */
|
|
|
|
|
Imath::extractAndRemoveScalingAndShear(mat_remainder, scale, shear);
|
|
|
|
|
|
|
|
|
|
/* extract translation */
|
|
|
|
|
translation.x = mat_remainder[3][0];
|
|
|
|
|
translation.y = mat_remainder[3][1];
|
|
|
|
|
translation.z = mat_remainder[3][2];
|
|
|
|
|
|
|
|
|
|
/* extract rotation */
|
|
|
|
|
rotation = extractQuat(mat_remainder);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static M44d transform_compose(const V3d &scale,
|
|
|
|
|
const V3d &shear,
|
|
|
|
|
const Quatd &rotation,
|
|
|
|
|
const V3d &translation)
|
|
|
|
|
{
|
2024-12-29 17:32:00 +01:00
|
|
|
M44d scale_mat;
|
|
|
|
|
M44d shear_mat;
|
|
|
|
|
M44d rot_mat;
|
|
|
|
|
M44d trans_mat;
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
scale_mat.setScale(scale);
|
|
|
|
|
shear_mat.setShear(shear);
|
|
|
|
|
rot_mat = rotation.toMatrix44();
|
|
|
|
|
trans_mat.setTranslation(translation);
|
|
|
|
|
|
|
|
|
|
return scale_mat * shear_mat * rot_mat * trans_mat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get the matrix for the specified time, or return the identity matrix if there is no exact match
|
|
|
|
|
*/
|
|
|
|
|
static M44d get_matrix_for_time(const MatrixSampleMap &samples, chrono_t time)
|
|
|
|
|
{
|
2024-12-29 17:32:00 +01:00
|
|
|
const MatrixSampleMap::const_iterator iter = samples.find(time);
|
2021-01-25 15:12:00 +01:00
|
|
|
if (iter != samples.end()) {
|
|
|
|
|
return iter->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return M44d();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get the matrix for the specified time, or interpolate between samples if there is no exact match
|
|
|
|
|
*/
|
|
|
|
|
static M44d get_interpolated_matrix_for_time(const MatrixSampleMap &samples, chrono_t time)
|
|
|
|
|
{
|
|
|
|
|
if (samples.empty()) {
|
|
|
|
|
return M44d();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* see if exact match */
|
2024-12-29 17:32:00 +01:00
|
|
|
const MatrixSampleMap::const_iterator iter = samples.find(time);
|
2021-01-25 15:12:00 +01:00
|
|
|
if (iter != samples.end()) {
|
|
|
|
|
return iter->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (samples.size() == 1) {
|
|
|
|
|
return samples.begin()->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (time <= samples.begin()->first) {
|
|
|
|
|
return samples.begin()->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (time >= samples.rbegin()->first) {
|
|
|
|
|
return samples.rbegin()->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* find previous and next time sample to interpolate */
|
|
|
|
|
chrono_t prev_time = samples.begin()->first;
|
|
|
|
|
chrono_t next_time = samples.rbegin()->first;
|
|
|
|
|
|
|
|
|
|
for (MatrixSampleMap::const_iterator I = samples.begin(); I != samples.end(); ++I) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const chrono_t current_time = (*I).first;
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
if (current_time > prev_time && current_time <= time) {
|
|
|
|
|
prev_time = current_time;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (current_time > next_time && current_time >= time) {
|
|
|
|
|
next_time = current_time;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const M44d prev_mat = get_matrix_for_time(samples, prev_time);
|
|
|
|
|
const M44d next_mat = get_matrix_for_time(samples, next_time);
|
|
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
V3d prev_scale;
|
|
|
|
|
V3d next_scale;
|
|
|
|
|
V3d prev_shear;
|
|
|
|
|
V3d next_shear;
|
|
|
|
|
V3d prev_translation;
|
|
|
|
|
V3d next_translation;
|
|
|
|
|
Quatd prev_rotation;
|
|
|
|
|
Quatd next_rotation;
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
transform_decompose(prev_mat, prev_scale, prev_shear, prev_rotation, prev_translation);
|
|
|
|
|
transform_decompose(next_mat, next_scale, next_shear, next_rotation, next_translation);
|
|
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
const chrono_t t = (time - prev_time) / (next_time - prev_time);
|
2021-01-25 15:12:00 +01:00
|
|
|
|
2021-06-24 15:56:58 +10:00
|
|
|
/* Ensure rotation around the shortest angle. */
|
2021-01-25 15:12:00 +01:00
|
|
|
if ((prev_rotation ^ next_rotation) < 0) {
|
|
|
|
|
next_rotation = -next_rotation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return transform_compose(Imath::lerp(prev_scale, next_scale, t),
|
|
|
|
|
Imath::lerp(prev_shear, next_shear, t),
|
|
|
|
|
Imath::slerp(prev_rotation, next_rotation, t),
|
|
|
|
|
Imath::lerp(prev_translation, next_translation, t));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void concatenate_xform_samples(const MatrixSampleMap &parent_samples,
|
|
|
|
|
const MatrixSampleMap &local_samples,
|
|
|
|
|
MatrixSampleMap &output_samples)
|
|
|
|
|
{
|
|
|
|
|
set<chrono_t> union_of_samples;
|
|
|
|
|
|
|
|
|
|
for (const std::pair<chrono_t, M44d> pair : parent_samples) {
|
|
|
|
|
union_of_samples.insert(pair.first);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const std::pair<chrono_t, M44d> pair : local_samples) {
|
|
|
|
|
union_of_samples.insert(pair.first);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
for (const chrono_t time : union_of_samples) {
|
|
|
|
|
const M44d parent_matrix = get_interpolated_matrix_for_time(parent_samples, time);
|
|
|
|
|
const M44d local_matrix = get_interpolated_matrix_for_time(local_samples, time);
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
output_samples[time] = local_matrix * parent_matrix;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
static Transform make_transform(const M44d &a, const float scale)
|
2021-01-25 15:12:00 +01:00
|
|
|
{
|
|
|
|
|
M44d m = convert_yup_zup(a, scale);
|
|
|
|
|
Transform trans;
|
|
|
|
|
for (int j = 0; j < 3; j++) {
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
|
trans[j][i] = static_cast<float>(m[i][j]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return trans;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NODE_DEFINE(AlembicObject)
|
|
|
|
|
{
|
|
|
|
|
NodeType *type = NodeType::add("alembic_object", create);
|
|
|
|
|
|
|
|
|
|
SOCKET_STRING(path, "Alembic Path", ustring());
|
2021-03-15 16:11:12 +01:00
|
|
|
SOCKET_NODE_ARRAY(used_shaders, "Used Shaders", Shader::get_node_type());
|
2021-01-25 15:12:00 +01:00
|
|
|
|
2021-08-20 02:30:50 +02:00
|
|
|
SOCKET_BOOLEAN(ignore_subdivision, "Ignore Subdivision", true);
|
|
|
|
|
|
2021-01-25 15:12:00 +01:00
|
|
|
SOCKET_INT(subd_max_level, "Max Subdivision Level", 1);
|
|
|
|
|
SOCKET_FLOAT(subd_dicing_rate, "Subdivision Dicing Rate", 1.0f);
|
|
|
|
|
|
|
|
|
|
SOCKET_FLOAT(radius_scale, "Radius Scale", 1.0f);
|
|
|
|
|
|
|
|
|
|
return type;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-15 16:11:12 +01:00
|
|
|
AlembicObject::AlembicObject() : Node(get_node_type())
|
2021-01-25 15:12:00 +01:00
|
|
|
{
|
2021-02-23 05:31:34 +01:00
|
|
|
schema_type = INVALID;
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
|
2024-12-26 17:53:59 +01:00
|
|
|
AlembicObject::~AlembicObject() = default;
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
void AlembicObject::set_object(Object *object_)
|
|
|
|
|
{
|
|
|
|
|
object = object_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Object *AlembicObject::get_object()
|
|
|
|
|
{
|
|
|
|
|
return object;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AlembicObject::has_data_loaded() const
|
|
|
|
|
{
|
|
|
|
|
return data_loaded;
|
|
|
|
|
}
|
|
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
void AlembicObject::load_data_in_cache(CachedData &cached_data,
|
|
|
|
|
AlembicProcedural *proc,
|
|
|
|
|
IPolyMeshSchema &schema,
|
|
|
|
|
Progress &progress)
|
2021-01-25 15:12:00 +01:00
|
|
|
{
|
Alembic procedural: add support for instancing
Inside of the procedural, instances are AlembicObjects which point to
the AlembicObject that they instance.
In Alembic, an instance is an untyped Object pointing to the original
(instanced) one through its source path. During the archive traversal we
detect such instances and, only if the instanced object is asked to be
rendered, set the instance's AlembicObject to point to the original's
AlembicObject.
Cycles Object Nodes are created for each AlembicObject, but only for
non-instances are Geometries created, which are then shared between
Object Nodes. It is supposed, and expected, that all instances share the
same shaders, which will be set to be the ones found on the original
object.
As for caching, the data cache for an AlembicObject is only valid for
non-instances and should not be read to or from as it is implicitly
shared.
2021-03-12 01:30:12 +01:00
|
|
|
/* Only load data for the original Geometry. */
|
|
|
|
|
if (instance_of) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
cached_data.clear();
|
2021-03-24 14:18:05 +01:00
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
PolyMeshSchemaData data;
|
|
|
|
|
data.topology_variance = schema.getTopologyVariance();
|
|
|
|
|
data.time_sampling = schema.getTimeSampling();
|
|
|
|
|
data.positions = schema.getPositionsProperty();
|
|
|
|
|
data.face_counts = schema.getFaceCountsProperty();
|
|
|
|
|
data.face_indices = schema.getFaceIndicesProperty();
|
|
|
|
|
data.normals = schema.getNormalsParam();
|
|
|
|
|
data.num_samples = schema.getNumSamples();
|
|
|
|
|
data.shader_face_sets = parse_face_sets_for_shader_assignment(schema, get_used_shaders());
|
2021-01-25 15:12:00 +01:00
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
read_geometry_data(proc, cached_data, data, progress);
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
if (progress.get_cancel()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
/* Use the schema as the base compound property to also be able to look for top level properties.
|
|
|
|
|
*/
|
|
|
|
|
read_attributes(
|
|
|
|
|
proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress);
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
if (progress.get_cancel()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
cached_data.invalidate_last_loaded_time(true);
|
2021-01-25 15:12:00 +01:00
|
|
|
data_loaded = true;
|
|
|
|
|
}
|
|
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
void AlembicObject::load_data_in_cache(CachedData &cached_data,
|
|
|
|
|
AlembicProcedural *proc,
|
|
|
|
|
ISubDSchema &schema,
|
|
|
|
|
Progress &progress)
|
2021-01-25 15:12:00 +01:00
|
|
|
{
|
Alembic procedural: add support for instancing
Inside of the procedural, instances are AlembicObjects which point to
the AlembicObject that they instance.
In Alembic, an instance is an untyped Object pointing to the original
(instanced) one through its source path. During the archive traversal we
detect such instances and, only if the instanced object is asked to be
rendered, set the instance's AlembicObject to point to the original's
AlembicObject.
Cycles Object Nodes are created for each AlembicObject, but only for
non-instances are Geometries created, which are then shared between
Object Nodes. It is supposed, and expected, that all instances share the
same shaders, which will be set to be the ones found on the original
object.
As for caching, the data cache for an AlembicObject is only valid for
non-instances and should not be read to or from as it is implicitly
shared.
2021-03-12 01:30:12 +01:00
|
|
|
/* Only load data for the original Geometry. */
|
|
|
|
|
if (instance_of) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
cached_data.clear();
|
2021-01-25 15:12:00 +01:00
|
|
|
|
2021-08-20 02:30:50 +02:00
|
|
|
if (this->get_ignore_subdivision()) {
|
|
|
|
|
PolyMeshSchemaData data;
|
|
|
|
|
data.topology_variance = schema.getTopologyVariance();
|
|
|
|
|
data.time_sampling = schema.getTimeSampling();
|
|
|
|
|
data.positions = schema.getPositionsProperty();
|
|
|
|
|
data.face_counts = schema.getFaceCountsProperty();
|
|
|
|
|
data.face_indices = schema.getFaceIndicesProperty();
|
|
|
|
|
data.num_samples = schema.getNumSamples();
|
|
|
|
|
data.velocities = schema.getVelocitiesProperty();
|
|
|
|
|
data.shader_face_sets = parse_face_sets_for_shader_assignment(schema, get_used_shaders());
|
|
|
|
|
|
|
|
|
|
read_geometry_data(proc, cached_data, data, progress);
|
|
|
|
|
|
|
|
|
|
if (progress.get_cancel()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Use the schema as the base compound property to also be able to look for top level
|
|
|
|
|
* properties. */
|
|
|
|
|
read_attributes(
|
|
|
|
|
proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress);
|
|
|
|
|
|
|
|
|
|
cached_data.invalidate_last_loaded_time(true);
|
|
|
|
|
data_loaded = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
SubDSchemaData data;
|
|
|
|
|
data.time_sampling = schema.getTimeSampling();
|
|
|
|
|
data.num_samples = schema.getNumSamples();
|
|
|
|
|
data.topology_variance = schema.getTopologyVariance();
|
|
|
|
|
data.face_counts = schema.getFaceCountsProperty();
|
|
|
|
|
data.face_indices = schema.getFaceIndicesProperty();
|
|
|
|
|
data.positions = schema.getPositionsProperty();
|
|
|
|
|
data.face_varying_interpolate_boundary = schema.getFaceVaryingInterpolateBoundaryProperty();
|
|
|
|
|
data.face_varying_propagate_corners = schema.getFaceVaryingPropagateCornersProperty();
|
|
|
|
|
data.interpolate_boundary = schema.getInterpolateBoundaryProperty();
|
|
|
|
|
data.crease_indices = schema.getCreaseIndicesProperty();
|
|
|
|
|
data.crease_lengths = schema.getCreaseLengthsProperty();
|
|
|
|
|
data.crease_sharpnesses = schema.getCreaseSharpnessesProperty();
|
|
|
|
|
data.corner_indices = schema.getCornerIndicesProperty();
|
|
|
|
|
data.corner_sharpnesses = schema.getCornerSharpnessesProperty();
|
|
|
|
|
data.holes = schema.getHolesProperty();
|
|
|
|
|
data.subdivision_scheme = schema.getSubdivisionSchemeProperty();
|
|
|
|
|
data.velocities = schema.getVelocitiesProperty();
|
|
|
|
|
data.shader_face_sets = parse_face_sets_for_shader_assignment(schema, get_used_shaders());
|
|
|
|
|
|
|
|
|
|
read_geometry_data(proc, cached_data, data, progress);
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
if (progress.get_cancel()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
/* Use the schema as the base compound property to also be able to look for top level properties.
|
|
|
|
|
*/
|
2021-05-18 06:09:28 +02:00
|
|
|
read_attributes(
|
|
|
|
|
proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress);
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
|
|
|
|
|
cached_data.invalidate_last_loaded_time(true);
|
2021-01-25 15:12:00 +01:00
|
|
|
data_loaded = true;
|
|
|
|
|
}
|
|
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
void AlembicObject::load_data_in_cache(CachedData &cached_data,
|
|
|
|
|
AlembicProcedural *proc,
|
|
|
|
|
const ICurvesSchema &schema,
|
|
|
|
|
Progress &progress)
|
2021-01-25 15:12:00 +01:00
|
|
|
{
|
Alembic procedural: add support for instancing
Inside of the procedural, instances are AlembicObjects which point to
the AlembicObject that they instance.
In Alembic, an instance is an untyped Object pointing to the original
(instanced) one through its source path. During the archive traversal we
detect such instances and, only if the instanced object is asked to be
rendered, set the instance's AlembicObject to point to the original's
AlembicObject.
Cycles Object Nodes are created for each AlembicObject, but only for
non-instances are Geometries created, which are then shared between
Object Nodes. It is supposed, and expected, that all instances share the
same shaders, which will be set to be the ones found on the original
object.
As for caching, the data cache for an AlembicObject is only valid for
non-instances and should not be read to or from as it is implicitly
shared.
2021-03-12 01:30:12 +01:00
|
|
|
/* Only load data for the original Geometry. */
|
|
|
|
|
if (instance_of) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
cached_data.clear();
|
2021-01-25 15:12:00 +01:00
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
CurvesSchemaData data;
|
|
|
|
|
data.positions = schema.getPositionsProperty();
|
|
|
|
|
data.position_weights = schema.getPositionWeightsProperty();
|
|
|
|
|
data.normals = schema.getNormalsParam();
|
|
|
|
|
data.knots = schema.getKnotsProperty();
|
|
|
|
|
data.orders = schema.getOrdersProperty();
|
|
|
|
|
data.widths = schema.getWidthsParam();
|
|
|
|
|
data.velocities = schema.getVelocitiesProperty();
|
|
|
|
|
data.time_sampling = schema.getTimeSampling();
|
|
|
|
|
data.topology_variance = schema.getTopologyVariance();
|
|
|
|
|
data.num_samples = schema.getNumSamples();
|
|
|
|
|
data.num_vertices = schema.getNumVerticesProperty();
|
|
|
|
|
data.default_radius = proc->get_default_radius();
|
|
|
|
|
data.radius_scale = get_radius_scale();
|
|
|
|
|
|
|
|
|
|
read_geometry_data(proc, cached_data, data, progress);
|
2021-02-23 06:01:28 +01:00
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
if (progress.get_cancel()) {
|
|
|
|
|
return;
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
/* Use the schema as the base compound property to also be able to look for top level properties.
|
|
|
|
|
*/
|
|
|
|
|
read_attributes(
|
|
|
|
|
proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress);
|
2021-01-25 15:12:00 +01:00
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
cached_data.invalidate_last_loaded_time(true);
|
2021-01-25 15:12:00 +01:00
|
|
|
data_loaded = true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-15 03:38:16 +02:00
|
|
|
void AlembicObject::load_data_in_cache(CachedData &cached_data,
|
|
|
|
|
AlembicProcedural *proc,
|
|
|
|
|
const IPointsSchema &schema,
|
|
|
|
|
Progress &progress)
|
|
|
|
|
{
|
|
|
|
|
/* Only load data for the original Geometry. */
|
|
|
|
|
if (instance_of) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cached_data.clear();
|
|
|
|
|
|
|
|
|
|
PointsSchemaData data;
|
|
|
|
|
data.positions = schema.getPositionsProperty();
|
|
|
|
|
data.radiuses = schema.getWidthsParam();
|
|
|
|
|
data.velocities = schema.getVelocitiesProperty();
|
|
|
|
|
data.time_sampling = schema.getTimeSampling();
|
|
|
|
|
data.num_samples = schema.getNumSamples();
|
|
|
|
|
data.default_radius = proc->get_default_radius();
|
|
|
|
|
data.radius_scale = get_radius_scale();
|
|
|
|
|
|
|
|
|
|
read_geometry_data(proc, cached_data, data, progress);
|
|
|
|
|
|
|
|
|
|
if (progress.get_cancel()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Use the schema as the base compound property to also be able to look for top level properties.
|
|
|
|
|
*/
|
|
|
|
|
read_attributes(proc, cached_data, schema, {}, get_requested_attributes(), progress);
|
|
|
|
|
|
|
|
|
|
cached_data.invalidate_last_loaded_time(true);
|
|
|
|
|
data_loaded = true;
|
|
|
|
|
}
|
|
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
void AlembicObject::setup_transform_cache(CachedData &cached_data, float scale)
|
2021-01-25 15:12:00 +01:00
|
|
|
{
|
|
|
|
|
cached_data.transforms.clear();
|
|
|
|
|
cached_data.transforms.invalidate_last_loaded_time();
|
|
|
|
|
|
2021-03-11 23:45:42 +01:00
|
|
|
if (scale == 0.0f) {
|
|
|
|
|
scale = 1.0f;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-07 16:17:59 +01:00
|
|
|
if (xform_time_sampling) {
|
|
|
|
|
cached_data.transforms.set_time_sampling(*xform_time_sampling);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-26 17:53:59 +01:00
|
|
|
if (xform_samples.empty()) {
|
2021-01-25 15:12:00 +01:00
|
|
|
Transform tfm = transform_scale(make_float3(scale));
|
|
|
|
|
cached_data.transforms.add_data(tfm, 0.0);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* It is possible for a leaf node of the hierarchy to have multiple samples for its transforms
|
|
|
|
|
* if a sibling has animated transforms. So check if we indeed have animated transformations.
|
|
|
|
|
*/
|
2024-12-29 17:32:00 +01:00
|
|
|
const M44d first_matrix = xform_samples.begin()->first;
|
2021-01-25 15:12:00 +01:00
|
|
|
bool has_animation = false;
|
|
|
|
|
for (const std::pair<chrono_t, M44d> pair : xform_samples) {
|
|
|
|
|
if (pair.second != first_matrix) {
|
|
|
|
|
has_animation = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!has_animation) {
|
|
|
|
|
Transform tfm = make_transform(first_matrix, scale);
|
|
|
|
|
cached_data.transforms.add_data(tfm, 0.0);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (const std::pair<chrono_t, M44d> pair : xform_samples) {
|
|
|
|
|
Transform tfm = make_transform(pair.second, scale);
|
|
|
|
|
cached_data.transforms.add_data(tfm, pair.first);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AttributeRequestSet AlembicObject::get_requested_attributes()
|
|
|
|
|
{
|
|
|
|
|
AttributeRequestSet requested_attributes;
|
|
|
|
|
|
|
|
|
|
Geometry *geometry = object->get_geometry();
|
|
|
|
|
assert(geometry);
|
|
|
|
|
|
2024-12-26 19:41:25 +01:00
|
|
|
for (Node *node : geometry->get_used_shaders()) {
|
2021-01-25 15:12:00 +01:00
|
|
|
Shader *shader = static_cast<Shader *>(node);
|
|
|
|
|
|
2024-12-26 19:41:25 +01:00
|
|
|
for (const AttributeRequest &attr : shader->attributes.requests) {
|
2024-12-26 17:53:59 +01:00
|
|
|
if (!attr.name.empty()) {
|
2021-01-25 15:12:00 +01:00
|
|
|
requested_attributes.add(attr.name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return requested_attributes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update existing attributes and remove any attribute not in the cached_data, those attributes
|
|
|
|
|
* were added by Cycles (e.g. face normals) */
|
2025-01-01 18:15:54 +01:00
|
|
|
static void update_attributes(AttributeSet &attributes,
|
|
|
|
|
CachedData &cached_data,
|
|
|
|
|
const double frame_time)
|
2021-01-25 15:12:00 +01:00
|
|
|
{
|
|
|
|
|
set<Attribute *> cached_attributes;
|
|
|
|
|
|
|
|
|
|
for (CachedData::CachedAttribute &attribute : cached_data.attributes) {
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
const CacheLookupResult<array<char>> result = attribute.data.data_for_time(frame_time);
|
|
|
|
|
|
|
|
|
|
if (result.has_no_data_for_time()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
Attribute *attr = nullptr;
|
|
|
|
|
if (attribute.std != ATTR_STD_NONE) {
|
|
|
|
|
attr = attributes.add(attribute.std, attribute.name);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
attr = attributes.add(attribute.name, attribute.type_desc, attribute.element);
|
|
|
|
|
}
|
|
|
|
|
assert(attr);
|
|
|
|
|
|
|
|
|
|
cached_attributes.insert(attr);
|
|
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
if (!result.has_new_data()) {
|
2021-01-25 15:12:00 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-30 02:20:36 +01:00
|
|
|
const array<char> &attr_data = result.get_data();
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
|
2021-01-25 15:12:00 +01:00
|
|
|
/* weak way of detecting if the topology has changed
|
|
|
|
|
* todo: reuse code from device_update patch */
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
if (attr->buffer.size() != attr_data.size()) {
|
|
|
|
|
attr->buffer.resize(attr_data.size());
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
memcpy(attr->data(), attr_data.data(), attr_data.size());
|
2021-03-11 23:51:54 +01:00
|
|
|
attr->modified = true;
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* remove any attributes not in cached_attributes */
|
|
|
|
|
list<Attribute>::iterator it;
|
|
|
|
|
for (it = attributes.attributes.begin(); it != attributes.attributes.end();) {
|
|
|
|
|
if (cached_attributes.find(&(*it)) == cached_attributes.end()) {
|
Cycles: optimize attributes device updates
When an `AttributeSet` is tagged as modified, which happens after the addition or
removal of an `Attribute` from the set, during the following GeometryManager device
update, we update and repack the kernel data for all attribute types. However, if we
only add or remove a `float` attribute, `float2` or `float3` attributes should not
be repacked for efficiency.
This patch adds some mechanisms to detect which attribute types are modified from
the AttributeSet.
Firstly, this adds an `AttrKernelDataType` to map the data type of the Attribute to
the one used in the kernel as there is no one to one match between the two since e.g.
`Transform` or `float4` data are stored as `float3s` in the kernel.
Then, this replaces the `AttributeSet.modified` boolean with a set of flags to detect
which types have been modified. There is no specific flag type (e.g.
`enum ModifiedType`), rather the flags used derive simply from the
`AttrKernelDataType` enumeration, to keep things synchronized.
The logic to remove an `Attribute` from the `AttributeSet` and tag the latter as modified
is centralized in a new `AttributeSet.remove` method taking an iterator as input.
Lastly, as some attributes like standard normals are not stored in the various
kernel attribute arrays (`DeviceScene::attribute_*`), the modified flags are only
set if the associated standard corresponds to an attribute which will be stored
in the kernel's attribute arrays. This makes it so adding or removing such attributes
does not trigger an unnecessary update of other type-related attributes.
Reviewed By: brecht
Differential Revision: https://developer.blender.org/D11373
2021-05-18 13:09:29 +02:00
|
|
|
attributes.remove(it++);
|
2021-01-25 15:12:00 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
it++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NODE_DEFINE(AlembicProcedural)
|
|
|
|
|
{
|
|
|
|
|
NodeType *type = NodeType::add("alembic", create);
|
|
|
|
|
|
|
|
|
|
SOCKET_STRING(filepath, "Filename", ustring());
|
Alembic: add support for reading override layers
Override layers are a standard feature of Alembic, where archives can override
data from other archives, provided that the hierarchies match.
This is useful for modifying a UV map, updating an animation, or even creating
some sort of LOD system where low resolution meshes are swapped by high resolution
versions.
It is possible to add UV maps and vertex colors using this system, however, they
will only appear in the spreadsheet editor when viewing evaluated data, as the UV
map and Vertex color UI only show data present on the original mesh.
Implementation wise, this adds a `CacheFileLayer` data structure to the `CacheFile`
DNA, as well as some operators and UI to present and manage the layers. For both
the Alembic importer and the Cycles procedural, the main change is creating an
archive from a list of filepaths, instead of a single one.
After importing the base file through the regular import operator, layers can be added
to or removed from the `CacheFile` via the UI list under the `Override Layers` panel
located in the Mesh Sequence Cache modifier. Layers can also be moved around or
hidden.
See differential page for tests files and demos.
Reviewed by: brecht, sybren
Differential Revision: https://developer.blender.org/D13603
2022-01-17 14:50:47 +01:00
|
|
|
SOCKET_STRING_ARRAY(layers, "Layers", array<ustring>());
|
2021-01-25 15:12:00 +01:00
|
|
|
SOCKET_FLOAT(frame, "Frame", 1.0f);
|
|
|
|
|
SOCKET_FLOAT(start_frame, "Start Frame", 1.0f);
|
|
|
|
|
SOCKET_FLOAT(end_frame, "End Frame", 1.0f);
|
|
|
|
|
SOCKET_FLOAT(frame_rate, "Frame Rate", 24.0f);
|
|
|
|
|
SOCKET_FLOAT(frame_offset, "Frame Offset", 0.0f);
|
|
|
|
|
SOCKET_FLOAT(default_radius, "Default Radius", 0.01f);
|
|
|
|
|
SOCKET_FLOAT(scale, "Scale", 1.0f);
|
|
|
|
|
|
2021-08-20 14:29:05 +02:00
|
|
|
SOCKET_BOOLEAN(use_prefetch, "Use Prefetch", true);
|
|
|
|
|
SOCKET_INT(prefetch_cache_size, "Prefetch Cache Size", 4096);
|
|
|
|
|
|
2021-01-25 15:12:00 +01:00
|
|
|
return type;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-15 16:11:12 +01:00
|
|
|
AlembicProcedural::AlembicProcedural() : Procedural(get_node_type())
|
2021-01-25 15:12:00 +01:00
|
|
|
{
|
|
|
|
|
objects_loaded = false;
|
|
|
|
|
scene_ = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AlembicProcedural::~AlembicProcedural()
|
|
|
|
|
{
|
|
|
|
|
ccl::set<Geometry *> geometries_set;
|
|
|
|
|
ccl::set<Object *> objects_set;
|
2024-12-29 17:32:00 +01:00
|
|
|
const ccl::set<AlembicObject *> abc_objects_set;
|
2021-01-25 15:12:00 +01:00
|
|
|
|
2024-12-30 02:20:36 +01:00
|
|
|
for (Node *node : nodes) {
|
2021-01-25 15:12:00 +01:00
|
|
|
AlembicObject *abc_object = static_cast<AlembicObject *>(node);
|
|
|
|
|
|
2021-02-03 06:43:38 +01:00
|
|
|
if (abc_object->get_object()) {
|
|
|
|
|
objects_set.insert(abc_object->get_object());
|
|
|
|
|
|
|
|
|
|
if (abc_object->get_object()->get_geometry()) {
|
|
|
|
|
geometries_set.insert(abc_object->get_object()->get_geometry());
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
delete_node(abc_object);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-03 06:43:38 +01:00
|
|
|
/* We may delete a Procedural before rendering started, so scene_ can be null. */
|
|
|
|
|
if (!scene_) {
|
|
|
|
|
assert(geometries_set.empty());
|
|
|
|
|
assert(objects_set.empty());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-25 15:12:00 +01:00
|
|
|
scene_->delete_nodes(geometries_set, this);
|
|
|
|
|
scene_->delete_nodes(objects_set, this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AlembicProcedural::generate(Scene *scene, Progress &progress)
|
|
|
|
|
{
|
|
|
|
|
assert(scene_ == nullptr || scene_ == scene);
|
|
|
|
|
scene_ = scene;
|
|
|
|
|
|
2021-02-23 01:57:03 +01:00
|
|
|
if (frame < start_frame || frame > end_frame) {
|
|
|
|
|
clear_modified();
|
2024-12-30 02:20:36 +01:00
|
|
|
objects_modified = false;
|
2021-02-23 01:57:03 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-25 15:12:00 +01:00
|
|
|
bool need_shader_updates = false;
|
2021-03-07 15:39:01 +01:00
|
|
|
bool need_data_updates = false;
|
2021-01-25 15:12:00 +01:00
|
|
|
|
2024-12-30 02:20:36 +01:00
|
|
|
for (Node *object_node : nodes) {
|
2021-01-25 15:12:00 +01:00
|
|
|
AlembicObject *object = static_cast<AlembicObject *>(object_node);
|
|
|
|
|
|
2021-03-07 15:39:01 +01:00
|
|
|
if (object->is_modified()) {
|
|
|
|
|
need_data_updates = true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-31 17:17:54 +02:00
|
|
|
/* Check if the shaders were modified. */
|
|
|
|
|
if (object->used_shaders_is_modified() && object->get_object() &&
|
|
|
|
|
object->get_object()->get_geometry())
|
|
|
|
|
{
|
|
|
|
|
Geometry *geometry = object->get_object()->get_geometry();
|
|
|
|
|
array<Node *> used_shaders = object->get_used_shaders();
|
|
|
|
|
geometry->set_used_shaders(used_shaders);
|
|
|
|
|
need_shader_updates = true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-07 15:39:01 +01:00
|
|
|
/* Check for changes in shaders (e.g. newly requested attributes). */
|
2024-12-26 19:41:25 +01:00
|
|
|
for (Node *shader_node : object->get_used_shaders()) {
|
2021-01-25 15:12:00 +01:00
|
|
|
Shader *shader = static_cast<Shader *>(shader_node);
|
|
|
|
|
|
2021-01-25 16:15:35 +01:00
|
|
|
if (shader->need_update_geometry()) {
|
2021-01-25 15:12:00 +01:00
|
|
|
object->need_shader_update = true;
|
|
|
|
|
need_shader_updates = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-30 02:20:36 +01:00
|
|
|
if (!(is_modified() || objects_modified) && !need_shader_updates && !need_data_updates) {
|
2021-01-25 15:12:00 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Alembic: add support for reading override layers
Override layers are a standard feature of Alembic, where archives can override
data from other archives, provided that the hierarchies match.
This is useful for modifying a UV map, updating an animation, or even creating
some sort of LOD system where low resolution meshes are swapped by high resolution
versions.
It is possible to add UV maps and vertex colors using this system, however, they
will only appear in the spreadsheet editor when viewing evaluated data, as the UV
map and Vertex color UI only show data present on the original mesh.
Implementation wise, this adds a `CacheFileLayer` data structure to the `CacheFile`
DNA, as well as some operators and UI to present and manage the layers. For both
the Alembic importer and the Cycles procedural, the main change is creating an
archive from a list of filepaths, instead of a single one.
After importing the base file through the regular import operator, layers can be added
to or removed from the `CacheFile` via the UI list under the `Override Layers` panel
located in the Mesh Sequence Cache modifier. Layers can also be moved around or
hidden.
See differential page for tests files and demos.
Reviewed by: brecht, sybren
Differential Revision: https://developer.blender.org/D13603
2022-01-17 14:50:47 +01:00
|
|
|
if (!archive.valid() || filepath_is_modified() || layers_is_modified()) {
|
2021-01-25 15:12:00 +01:00
|
|
|
Alembic::AbcCoreFactory::IFactory factory;
|
|
|
|
|
factory.setPolicy(Alembic::Abc::ErrorHandler::kQuietNoopPolicy);
|
Alembic: add support for reading override layers
Override layers are a standard feature of Alembic, where archives can override
data from other archives, provided that the hierarchies match.
This is useful for modifying a UV map, updating an animation, or even creating
some sort of LOD system where low resolution meshes are swapped by high resolution
versions.
It is possible to add UV maps and vertex colors using this system, however, they
will only appear in the spreadsheet editor when viewing evaluated data, as the UV
map and Vertex color UI only show data present on the original mesh.
Implementation wise, this adds a `CacheFileLayer` data structure to the `CacheFile`
DNA, as well as some operators and UI to present and manage the layers. For both
the Alembic importer and the Cycles procedural, the main change is creating an
archive from a list of filepaths, instead of a single one.
After importing the base file through the regular import operator, layers can be added
to or removed from the `CacheFile` via the UI list under the `Override Layers` panel
located in the Mesh Sequence Cache modifier. Layers can also be moved around or
hidden.
See differential page for tests files and demos.
Reviewed by: brecht, sybren
Differential Revision: https://developer.blender.org/D13603
2022-01-17 14:50:47 +01:00
|
|
|
|
|
|
|
|
std::vector<std::string> filenames;
|
2024-12-26 17:53:59 +01:00
|
|
|
filenames.emplace_back(filepath.c_str());
|
Alembic: add support for reading override layers
Override layers are a standard feature of Alembic, where archives can override
data from other archives, provided that the hierarchies match.
This is useful for modifying a UV map, updating an animation, or even creating
some sort of LOD system where low resolution meshes are swapped by high resolution
versions.
It is possible to add UV maps and vertex colors using this system, however, they
will only appear in the spreadsheet editor when viewing evaluated data, as the UV
map and Vertex color UI only show data present on the original mesh.
Implementation wise, this adds a `CacheFileLayer` data structure to the `CacheFile`
DNA, as well as some operators and UI to present and manage the layers. For both
the Alembic importer and the Cycles procedural, the main change is creating an
archive from a list of filepaths, instead of a single one.
After importing the base file through the regular import operator, layers can be added
to or removed from the `CacheFile` via the UI list under the `Override Layers` panel
located in the Mesh Sequence Cache modifier. Layers can also be moved around or
hidden.
See differential page for tests files and demos.
Reviewed by: brecht, sybren
Differential Revision: https://developer.blender.org/D13603
2022-01-17 14:50:47 +01:00
|
|
|
|
|
|
|
|
for (const ustring &layer : layers) {
|
2024-12-26 17:53:59 +01:00
|
|
|
filenames.emplace_back(layer.c_str());
|
Alembic: add support for reading override layers
Override layers are a standard feature of Alembic, where archives can override
data from other archives, provided that the hierarchies match.
This is useful for modifying a UV map, updating an animation, or even creating
some sort of LOD system where low resolution meshes are swapped by high resolution
versions.
It is possible to add UV maps and vertex colors using this system, however, they
will only appear in the spreadsheet editor when viewing evaluated data, as the UV
map and Vertex color UI only show data present on the original mesh.
Implementation wise, this adds a `CacheFileLayer` data structure to the `CacheFile`
DNA, as well as some operators and UI to present and manage the layers. For both
the Alembic importer and the Cycles procedural, the main change is creating an
archive from a list of filepaths, instead of a single one.
After importing the base file through the regular import operator, layers can be added
to or removed from the `CacheFile` via the UI list under the `Override Layers` panel
located in the Mesh Sequence Cache modifier. Layers can also be moved around or
hidden.
See differential page for tests files and demos.
Reviewed by: brecht, sybren
Differential Revision: https://developer.blender.org/D13603
2022-01-17 14:50:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We need to reverse the order as overriding archives should come first. */
|
|
|
|
|
std::reverse(filenames.begin(), filenames.end());
|
|
|
|
|
|
|
|
|
|
archive = factory.getArchive(filenames);
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
if (!archive.valid()) {
|
|
|
|
|
/* avoid potential infinite update loops in viewport synchronization */
|
|
|
|
|
filepath.clear();
|
Alembic: add support for reading override layers
Override layers are a standard feature of Alembic, where archives can override
data from other archives, provided that the hierarchies match.
This is useful for modifying a UV map, updating an animation, or even creating
some sort of LOD system where low resolution meshes are swapped by high resolution
versions.
It is possible to add UV maps and vertex colors using this system, however, they
will only appear in the spreadsheet editor when viewing evaluated data, as the UV
map and Vertex color UI only show data present on the original mesh.
Implementation wise, this adds a `CacheFileLayer` data structure to the `CacheFile`
DNA, as well as some operators and UI to present and manage the layers. For both
the Alembic importer and the Cycles procedural, the main change is creating an
archive from a list of filepaths, instead of a single one.
After importing the base file through the regular import operator, layers can be added
to or removed from the `CacheFile` via the UI list under the `Override Layers` panel
located in the Mesh Sequence Cache modifier. Layers can also be moved around or
hidden.
See differential page for tests files and demos.
Reviewed by: brecht, sybren
Differential Revision: https://developer.blender.org/D13603
2022-01-17 14:50:47 +01:00
|
|
|
layers.clear();
|
2021-01-25 15:12:00 +01:00
|
|
|
clear_modified();
|
2024-12-30 02:20:36 +01:00
|
|
|
objects_modified = false;
|
2021-01-25 15:12:00 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-30 02:20:36 +01:00
|
|
|
if (!objects_loaded || objects_modified) {
|
2021-01-25 15:12:00 +01:00
|
|
|
load_objects(progress);
|
|
|
|
|
objects_loaded = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const chrono_t frame_time = (chrono_t)((frame - frame_offset) / frame_rate);
|
|
|
|
|
|
2021-08-20 02:30:50 +02:00
|
|
|
/* Clear the subdivision caches as the data is stored differently. */
|
2024-12-30 02:20:36 +01:00
|
|
|
for (Node *node : nodes) {
|
2021-08-20 02:30:50 +02:00
|
|
|
AlembicObject *object = static_cast<AlembicObject *>(node);
|
|
|
|
|
|
|
|
|
|
if (object->schema_type != AlembicObject::SUBD) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (object->ignore_subdivision_is_modified()) {
|
|
|
|
|
object->clear_cache();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-20 14:29:05 +02:00
|
|
|
if (use_prefetch_is_modified()) {
|
|
|
|
|
if (!use_prefetch) {
|
2024-12-30 02:20:36 +01:00
|
|
|
for (Node *node : nodes) {
|
2021-08-20 14:29:05 +02:00
|
|
|
AlembicObject *object = static_cast<AlembicObject *>(node);
|
|
|
|
|
object->clear_cache();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (prefetch_cache_size_is_modified()) {
|
|
|
|
|
/* Check whether the current memory usage fits in the new requested size,
|
|
|
|
|
* abort the render if it is any higher. */
|
|
|
|
|
size_t memory_used = 0ul;
|
2024-12-30 02:20:36 +01:00
|
|
|
for (Node *node : nodes) {
|
2021-08-20 14:29:05 +02:00
|
|
|
AlembicObject *object = static_cast<AlembicObject *>(node);
|
|
|
|
|
memory_used += object->get_cached_data().memory_used();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (memory_used > get_prefetch_cache_size_in_bytes()) {
|
|
|
|
|
progress.set_error("Error: Alembic Procedural memory limit reached");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-12 00:15:07 +01:00
|
|
|
build_caches(progress);
|
|
|
|
|
|
2024-12-30 02:20:36 +01:00
|
|
|
for (Node *node : nodes) {
|
2021-01-25 15:12:00 +01:00
|
|
|
AlembicObject *object = static_cast<AlembicObject *>(node);
|
|
|
|
|
|
|
|
|
|
if (progress.get_cancel()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* skip constant objects */
|
2021-03-12 00:15:07 +01:00
|
|
|
if (object->is_constant() && !object->is_modified() && !object->need_shader_update &&
|
|
|
|
|
!scale_is_modified())
|
|
|
|
|
{
|
2021-01-25 15:12:00 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-23 05:31:34 +01:00
|
|
|
if (object->schema_type == AlembicObject::POLY_MESH) {
|
2021-03-12 00:15:07 +01:00
|
|
|
read_mesh(object, frame_time);
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
2021-02-23 05:31:34 +01:00
|
|
|
else if (object->schema_type == AlembicObject::CURVES) {
|
2021-03-12 00:15:07 +01:00
|
|
|
read_curves(object, frame_time);
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
2021-12-01 17:30:46 +01:00
|
|
|
else if (object->schema_type == AlembicObject::POINTS) {
|
|
|
|
|
read_points(object, frame_time);
|
|
|
|
|
}
|
2021-02-23 05:31:34 +01:00
|
|
|
else if (object->schema_type == AlembicObject::SUBD) {
|
2021-03-12 00:15:07 +01:00
|
|
|
read_subd(object, frame_time);
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
object->need_shader_update = false;
|
2021-01-25 15:12:00 +01:00
|
|
|
object->clear_modified();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clear_modified();
|
2024-12-30 02:20:36 +01:00
|
|
|
objects_modified = false;
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AlembicProcedural::tag_update(Scene *scene)
|
|
|
|
|
{
|
|
|
|
|
scene->procedural_manager->tag_update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AlembicObject *AlembicProcedural::get_or_create_object(const ustring &path)
|
|
|
|
|
{
|
2024-12-30 02:20:36 +01:00
|
|
|
for (Node *node : nodes) {
|
2021-01-25 15:12:00 +01:00
|
|
|
AlembicObject *object = static_cast<AlembicObject *>(node);
|
|
|
|
|
|
|
|
|
|
if (object->get_path() == path) {
|
|
|
|
|
return object;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AlembicObject *object = create_node<AlembicObject>();
|
|
|
|
|
object->set_path(path);
|
2024-12-30 02:20:36 +01:00
|
|
|
objects_modified = true;
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
return object;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AlembicProcedural::load_objects(Progress &progress)
|
|
|
|
|
{
|
|
|
|
|
unordered_map<string, AlembicObject *> object_map;
|
|
|
|
|
|
2024-12-30 02:20:36 +01:00
|
|
|
for (Node *node : nodes) {
|
2021-01-25 15:12:00 +01:00
|
|
|
AlembicObject *object = static_cast<AlembicObject *>(node);
|
|
|
|
|
|
|
|
|
|
/* only consider newly added objects */
|
|
|
|
|
if (object->get_object() == nullptr) {
|
|
|
|
|
object_map.insert({object->get_path().c_str(), object});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
const IObject root = archive.getTop();
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
for (size_t i = 0; i < root.getNumChildren(); ++i) {
|
2021-03-07 16:17:59 +01:00
|
|
|
walk_hierarchy(root, root.getChildHeader(i), {}, object_map, progress);
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
|
2021-02-23 05:45:55 +01:00
|
|
|
/* Create nodes in the scene. */
|
2024-12-29 17:32:00 +01:00
|
|
|
for (const std::pair<string, AlembicObject *> pair : object_map) {
|
2021-02-23 05:45:55 +01:00
|
|
|
AlembicObject *abc_object = pair.second;
|
2021-01-25 15:12:00 +01:00
|
|
|
|
2021-02-23 05:45:55 +01:00
|
|
|
Geometry *geometry = nullptr;
|
2021-01-25 15:12:00 +01:00
|
|
|
|
Alembic procedural: add support for instancing
Inside of the procedural, instances are AlembicObjects which point to
the AlembicObject that they instance.
In Alembic, an instance is an untyped Object pointing to the original
(instanced) one through its source path. During the archive traversal we
detect such instances and, only if the instanced object is asked to be
rendered, set the instance's AlembicObject to point to the original's
AlembicObject.
Cycles Object Nodes are created for each AlembicObject, but only for
non-instances are Geometries created, which are then shared between
Object Nodes. It is supposed, and expected, that all instances share the
same shaders, which will be set to be the ones found on the original
object.
As for caching, the data cache for an AlembicObject is only valid for
non-instances and should not be read to or from as it is implicitly
shared.
2021-03-12 01:30:12 +01:00
|
|
|
if (!abc_object->instance_of) {
|
|
|
|
|
if (abc_object->schema_type == AlembicObject::CURVES) {
|
|
|
|
|
geometry = scene_->create_node<Hair>();
|
|
|
|
|
}
|
2021-12-01 17:30:46 +01:00
|
|
|
else if (abc_object->schema_type == AlembicObject::POINTS) {
|
|
|
|
|
geometry = scene_->create_node<PointCloud>();
|
|
|
|
|
}
|
Alembic procedural: add support for instancing
Inside of the procedural, instances are AlembicObjects which point to
the AlembicObject that they instance.
In Alembic, an instance is an untyped Object pointing to the original
(instanced) one through its source path. During the archive traversal we
detect such instances and, only if the instanced object is asked to be
rendered, set the instance's AlembicObject to point to the original's
AlembicObject.
Cycles Object Nodes are created for each AlembicObject, but only for
non-instances are Geometries created, which are then shared between
Object Nodes. It is supposed, and expected, that all instances share the
same shaders, which will be set to be the ones found on the original
object.
As for caching, the data cache for an AlembicObject is only valid for
non-instances and should not be read to or from as it is implicitly
shared.
2021-03-12 01:30:12 +01:00
|
|
|
else if (abc_object->schema_type == AlembicObject::POLY_MESH ||
|
|
|
|
|
abc_object->schema_type == AlembicObject::SUBD)
|
|
|
|
|
{
|
|
|
|
|
geometry = scene_->create_node<Mesh>();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-02-23 05:45:55 +01:00
|
|
|
|
Alembic procedural: add support for instancing
Inside of the procedural, instances are AlembicObjects which point to
the AlembicObject that they instance.
In Alembic, an instance is an untyped Object pointing to the original
(instanced) one through its source path. During the archive traversal we
detect such instances and, only if the instanced object is asked to be
rendered, set the instance's AlembicObject to point to the original's
AlembicObject.
Cycles Object Nodes are created for each AlembicObject, but only for
non-instances are Geometries created, which are then shared between
Object Nodes. It is supposed, and expected, that all instances share the
same shaders, which will be set to be the ones found on the original
object.
As for caching, the data cache for an AlembicObject is only valid for
non-instances and should not be read to or from as it is implicitly
shared.
2021-03-12 01:30:12 +01:00
|
|
|
geometry->set_owner(this);
|
|
|
|
|
geometry->name = abc_object->iobject.getName();
|
2021-01-25 15:12:00 +01:00
|
|
|
|
Alembic procedural: add support for instancing
Inside of the procedural, instances are AlembicObjects which point to
the AlembicObject that they instance.
In Alembic, an instance is an untyped Object pointing to the original
(instanced) one through its source path. During the archive traversal we
detect such instances and, only if the instanced object is asked to be
rendered, set the instance's AlembicObject to point to the original's
AlembicObject.
Cycles Object Nodes are created for each AlembicObject, but only for
non-instances are Geometries created, which are then shared between
Object Nodes. It is supposed, and expected, that all instances share the
same shaders, which will be set to be the ones found on the original
object.
As for caching, the data cache for an AlembicObject is only valid for
non-instances and should not be read to or from as it is implicitly
shared.
2021-03-12 01:30:12 +01:00
|
|
|
array<Node *> used_shaders = abc_object->get_used_shaders();
|
|
|
|
|
geometry->set_used_shaders(used_shaders);
|
|
|
|
|
}
|
2021-01-25 15:12:00 +01:00
|
|
|
|
2021-02-23 05:45:55 +01:00
|
|
|
Object *object = scene_->create_node<Object>();
|
2021-01-25 15:12:00 +01:00
|
|
|
object->set_owner(this);
|
2021-02-23 05:45:55 +01:00
|
|
|
object->set_geometry(geometry);
|
2021-01-25 15:12:00 +01:00
|
|
|
object->name = abc_object->iobject.getName();
|
|
|
|
|
|
|
|
|
|
abc_object->set_object(object);
|
|
|
|
|
}
|
Alembic procedural: add support for instancing
Inside of the procedural, instances are AlembicObjects which point to
the AlembicObject that they instance.
In Alembic, an instance is an untyped Object pointing to the original
(instanced) one through its source path. During the archive traversal we
detect such instances and, only if the instanced object is asked to be
rendered, set the instance's AlembicObject to point to the original's
AlembicObject.
Cycles Object Nodes are created for each AlembicObject, but only for
non-instances are Geometries created, which are then shared between
Object Nodes. It is supposed, and expected, that all instances share the
same shaders, which will be set to be the ones found on the original
object.
As for caching, the data cache for an AlembicObject is only valid for
non-instances and should not be read to or from as it is implicitly
shared.
2021-03-12 01:30:12 +01:00
|
|
|
|
|
|
|
|
/* Share geometries between instances. */
|
2024-12-30 02:20:36 +01:00
|
|
|
for (Node *node : nodes) {
|
Alembic procedural: add support for instancing
Inside of the procedural, instances are AlembicObjects which point to
the AlembicObject that they instance.
In Alembic, an instance is an untyped Object pointing to the original
(instanced) one through its source path. During the archive traversal we
detect such instances and, only if the instanced object is asked to be
rendered, set the instance's AlembicObject to point to the original's
AlembicObject.
Cycles Object Nodes are created for each AlembicObject, but only for
non-instances are Geometries created, which are then shared between
Object Nodes. It is supposed, and expected, that all instances share the
same shaders, which will be set to be the ones found on the original
object.
As for caching, the data cache for an AlembicObject is only valid for
non-instances and should not be read to or from as it is implicitly
shared.
2021-03-12 01:30:12 +01:00
|
|
|
AlembicObject *abc_object = static_cast<AlembicObject *>(node);
|
|
|
|
|
|
|
|
|
|
if (abc_object->instance_of) {
|
|
|
|
|
abc_object->get_object()->set_geometry(
|
|
|
|
|
abc_object->instance_of->get_object()->get_geometry());
|
|
|
|
|
abc_object->schema_type = abc_object->instance_of->schema_type;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-02-23 05:45:55 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-12 00:15:07 +01:00
|
|
|
void AlembicProcedural::read_mesh(AlembicObject *abc_object, Abc::chrono_t frame_time)
|
2021-02-23 05:45:55 +01:00
|
|
|
{
|
2021-01-25 15:12:00 +01:00
|
|
|
CachedData &cached_data = abc_object->get_cached_data();
|
|
|
|
|
|
|
|
|
|
/* update sockets */
|
|
|
|
|
|
|
|
|
|
Object *object = abc_object->get_object();
|
|
|
|
|
cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket());
|
|
|
|
|
|
2021-03-07 16:17:59 +01:00
|
|
|
if (object->is_modified()) {
|
|
|
|
|
object->tag_update(scene_);
|
|
|
|
|
}
|
|
|
|
|
|
Alembic procedural: add support for instancing
Inside of the procedural, instances are AlembicObjects which point to
the AlembicObject that they instance.
In Alembic, an instance is an untyped Object pointing to the original
(instanced) one through its source path. During the archive traversal we
detect such instances and, only if the instanced object is asked to be
rendered, set the instance's AlembicObject to point to the original's
AlembicObject.
Cycles Object Nodes are created for each AlembicObject, but only for
non-instances are Geometries created, which are then shared between
Object Nodes. It is supposed, and expected, that all instances share the
same shaders, which will be set to be the ones found on the original
object.
As for caching, the data cache for an AlembicObject is only valid for
non-instances and should not be read to or from as it is implicitly
shared.
2021-03-12 01:30:12 +01:00
|
|
|
/* Only update sockets for the original Geometry. */
|
|
|
|
|
if (abc_object->instance_of) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-12 00:15:07 +01:00
|
|
|
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
|
|
|
|
|
|
2021-03-31 17:17:54 +02:00
|
|
|
/* Make sure shader ids are also updated. */
|
|
|
|
|
if (mesh->used_shaders_is_modified()) {
|
|
|
|
|
mesh->tag_shader_modified();
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-25 15:12:00 +01:00
|
|
|
cached_data.vertices.copy_to_socket(frame_time, mesh, mesh->get_verts_socket());
|
|
|
|
|
|
|
|
|
|
cached_data.shader.copy_to_socket(frame_time, mesh, mesh->get_shader_socket());
|
|
|
|
|
|
2021-03-12 01:57:41 +01:00
|
|
|
array<int3> *triangle_data = cached_data.triangles.data_for_time(frame_time).get_data_or_null();
|
2021-01-25 15:12:00 +01:00
|
|
|
if (triangle_data) {
|
|
|
|
|
array<int> triangles;
|
|
|
|
|
array<bool> smooth;
|
|
|
|
|
|
|
|
|
|
triangles.reserve(triangle_data->size() * 3);
|
|
|
|
|
smooth.reserve(triangle_data->size());
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < triangle_data->size(); ++i) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const int3 tri = (*triangle_data)[i];
|
2021-01-25 15:12:00 +01:00
|
|
|
triangles.push_back_reserved(tri.x);
|
|
|
|
|
triangles.push_back_reserved(tri.y);
|
|
|
|
|
triangles.push_back_reserved(tri.z);
|
2024-12-26 17:53:59 +01:00
|
|
|
smooth.push_back_reserved(true);
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mesh->set_triangles(triangles);
|
|
|
|
|
mesh->set_smooth(smooth);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* update attributes */
|
|
|
|
|
|
|
|
|
|
update_attributes(mesh->attributes, cached_data, frame_time);
|
|
|
|
|
|
|
|
|
|
if (mesh->is_modified()) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const bool need_rebuild = mesh->triangles_is_modified();
|
2021-03-07 17:22:00 +01:00
|
|
|
mesh->tag_update(scene_, need_rebuild);
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-12 00:15:07 +01:00
|
|
|
void AlembicProcedural::read_subd(AlembicObject *abc_object, Abc::chrono_t frame_time)
|
2021-01-25 15:12:00 +01:00
|
|
|
{
|
2021-08-20 02:30:50 +02:00
|
|
|
if (abc_object->get_ignore_subdivision()) {
|
|
|
|
|
read_mesh(abc_object, frame_time);
|
|
|
|
|
return;
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
|
2021-08-20 02:30:50 +02:00
|
|
|
CachedData &cached_data = abc_object->get_cached_data();
|
|
|
|
|
|
2021-03-12 00:15:07 +01:00
|
|
|
/* Update sockets. */
|
|
|
|
|
|
|
|
|
|
Object *object = abc_object->get_object();
|
|
|
|
|
cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket());
|
|
|
|
|
|
|
|
|
|
if (object->is_modified()) {
|
|
|
|
|
object->tag_update(scene_);
|
|
|
|
|
}
|
|
|
|
|
|
Alembic procedural: add support for instancing
Inside of the procedural, instances are AlembicObjects which point to
the AlembicObject that they instance.
In Alembic, an instance is an untyped Object pointing to the original
(instanced) one through its source path. During the archive traversal we
detect such instances and, only if the instanced object is asked to be
rendered, set the instance's AlembicObject to point to the original's
AlembicObject.
Cycles Object Nodes are created for each AlembicObject, but only for
non-instances are Geometries created, which are then shared between
Object Nodes. It is supposed, and expected, that all instances share the
same shaders, which will be set to be the ones found on the original
object.
As for caching, the data cache for an AlembicObject is only valid for
non-instances and should not be read to or from as it is implicitly
shared.
2021-03-12 01:30:12 +01:00
|
|
|
/* Only update sockets for the original Geometry. */
|
|
|
|
|
if (abc_object->instance_of) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-20 02:30:50 +02:00
|
|
|
if (abc_object->subd_max_level_is_modified() || abc_object->subd_dicing_rate_is_modified()) {
|
|
|
|
|
/* need to reset the current data is something changed */
|
|
|
|
|
cached_data.invalidate_last_loaded_time();
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-12 00:15:07 +01:00
|
|
|
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
|
|
|
|
|
|
2021-03-31 17:17:54 +02:00
|
|
|
/* Make sure shader ids are also updated. */
|
|
|
|
|
if (mesh->used_shaders_is_modified()) {
|
|
|
|
|
mesh->tag_shader_modified();
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-25 15:12:00 +01:00
|
|
|
/* Cycles overwrites the original triangles when computing displacement, so we always have to
|
|
|
|
|
* repass the data if something is animated (vertices most likely) to avoid buffer overflows. */
|
|
|
|
|
if (!cached_data.is_constant()) {
|
|
|
|
|
cached_data.invalidate_last_loaded_time();
|
|
|
|
|
|
|
|
|
|
/* remove previous triangles, if any */
|
|
|
|
|
array<int> triangles;
|
|
|
|
|
mesh->set_triangles(triangles);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mesh->clear_non_sockets();
|
|
|
|
|
|
2021-03-12 00:15:07 +01:00
|
|
|
/* Alembic is OpenSubDiv compliant, there is no option to set another subdivision type. */
|
|
|
|
|
mesh->set_subdivision_type(Mesh::SubdivisionType::SUBDIVISION_CATMULL_CLARK);
|
|
|
|
|
mesh->set_subd_max_level(abc_object->get_subd_max_level());
|
|
|
|
|
mesh->set_subd_dicing_rate(abc_object->get_subd_dicing_rate());
|
2021-03-07 16:17:59 +01:00
|
|
|
|
2021-01-25 15:12:00 +01:00
|
|
|
cached_data.vertices.copy_to_socket(frame_time, mesh, mesh->get_verts_socket());
|
|
|
|
|
|
|
|
|
|
/* cached_data.shader is also used for subd_shader */
|
|
|
|
|
cached_data.shader.copy_to_socket(frame_time, mesh, mesh->get_subd_shader_socket());
|
|
|
|
|
|
|
|
|
|
cached_data.subd_start_corner.copy_to_socket(
|
|
|
|
|
frame_time, mesh, mesh->get_subd_start_corner_socket());
|
|
|
|
|
|
|
|
|
|
cached_data.subd_num_corners.copy_to_socket(
|
|
|
|
|
frame_time, mesh, mesh->get_subd_num_corners_socket());
|
|
|
|
|
|
|
|
|
|
cached_data.subd_smooth.copy_to_socket(frame_time, mesh, mesh->get_subd_smooth_socket());
|
|
|
|
|
|
|
|
|
|
cached_data.subd_ptex_offset.copy_to_socket(
|
|
|
|
|
frame_time, mesh, mesh->get_subd_ptex_offset_socket());
|
|
|
|
|
|
|
|
|
|
cached_data.subd_face_corners.copy_to_socket(
|
|
|
|
|
frame_time, mesh, mesh->get_subd_face_corners_socket());
|
|
|
|
|
|
|
|
|
|
cached_data.num_ngons.copy_to_socket(frame_time, mesh, mesh->get_num_ngons_socket());
|
|
|
|
|
|
|
|
|
|
cached_data.subd_creases_edge.copy_to_socket(
|
|
|
|
|
frame_time, mesh, mesh->get_subd_creases_edge_socket());
|
|
|
|
|
|
|
|
|
|
cached_data.subd_creases_weight.copy_to_socket(
|
|
|
|
|
frame_time, mesh, mesh->get_subd_creases_weight_socket());
|
|
|
|
|
|
Subdivision: add support for vertex creasing
This adds vertex creasing support for OpenSubDiv for modeling, rendering,
Alembic and USD I/O.
For modeling, vertex creasing follows the edge creasing implementation with an
operator accessible through the Vertex menu in Edit Mode, and some parameter in
the properties panel. The option in the Subsurf and Multires to use edge
creasing also affects vertex creasing.
The vertex crease data is stored as a CustomData layer, unlike edge creases
which for now are stored in `MEdge`, but will in the future also be moved to
a `CustomData` layer. See comments for details on the difference in behavior
for the `CD_CREASE` layer between egdes and vertices.
For Cycles this adds sockets on the Mesh node to hold data about which vertices
are creased (one socket for the indices, one for the weigths).
Viewport rendering of vertex creasing reuses the same color scheme as for edges
and creased vertices are drawn bigger than uncreased vertices.
For Alembic and USD, vertex crease support follows the edge crease
implementation, they are always read, but only exported if a `Subsurf` modifier
is present on the Mesh.
Reviewed By: brecht, fclem, sergey, sybren, campbellbarton
Differential Revision: https://developer.blender.org/D10145
2022-01-20 12:20:30 +01:00
|
|
|
cached_data.subd_vertex_crease_indices.copy_to_socket(
|
|
|
|
|
frame_time, mesh, mesh->get_subd_vert_creases_socket());
|
|
|
|
|
|
|
|
|
|
cached_data.subd_vertex_crease_weights.copy_to_socket(
|
|
|
|
|
frame_time, mesh, mesh->get_subd_vert_creases_weight_socket());
|
|
|
|
|
|
2021-01-25 15:12:00 +01:00
|
|
|
mesh->set_num_subd_faces(mesh->get_subd_shader().size());
|
|
|
|
|
|
2021-02-05 16:23:34 +11:00
|
|
|
/* Update attributes. */
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
update_attributes(mesh->subd_attributes, cached_data, frame_time);
|
|
|
|
|
|
|
|
|
|
if (mesh->is_modified()) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const bool need_rebuild = (mesh->triangles_is_modified()) ||
|
|
|
|
|
(mesh->subd_num_corners_is_modified()) ||
|
|
|
|
|
(mesh->subd_shader_is_modified()) ||
|
|
|
|
|
(mesh->subd_smooth_is_modified()) ||
|
|
|
|
|
(mesh->subd_ptex_offset_is_modified()) ||
|
|
|
|
|
(mesh->subd_start_corner_is_modified()) ||
|
|
|
|
|
(mesh->subd_face_corners_is_modified());
|
2021-01-25 15:12:00 +01:00
|
|
|
|
2021-03-07 17:22:00 +01:00
|
|
|
mesh->tag_update(scene_, need_rebuild);
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-12 00:15:07 +01:00
|
|
|
void AlembicProcedural::read_curves(AlembicObject *abc_object, Abc::chrono_t frame_time)
|
2021-01-25 15:12:00 +01:00
|
|
|
{
|
|
|
|
|
CachedData &cached_data = abc_object->get_cached_data();
|
|
|
|
|
|
|
|
|
|
/* update sockets */
|
|
|
|
|
|
|
|
|
|
Object *object = abc_object->get_object();
|
|
|
|
|
cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket());
|
|
|
|
|
|
2021-03-07 16:17:59 +01:00
|
|
|
if (object->is_modified()) {
|
|
|
|
|
object->tag_update(scene_);
|
|
|
|
|
}
|
|
|
|
|
|
Alembic procedural: add support for instancing
Inside of the procedural, instances are AlembicObjects which point to
the AlembicObject that they instance.
In Alembic, an instance is an untyped Object pointing to the original
(instanced) one through its source path. During the archive traversal we
detect such instances and, only if the instanced object is asked to be
rendered, set the instance's AlembicObject to point to the original's
AlembicObject.
Cycles Object Nodes are created for each AlembicObject, but only for
non-instances are Geometries created, which are then shared between
Object Nodes. It is supposed, and expected, that all instances share the
same shaders, which will be set to be the ones found on the original
object.
As for caching, the data cache for an AlembicObject is only valid for
non-instances and should not be read to or from as it is implicitly
shared.
2021-03-12 01:30:12 +01:00
|
|
|
/* Only update sockets for the original Geometry. */
|
|
|
|
|
if (abc_object->instance_of) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-12 00:15:07 +01:00
|
|
|
Hair *hair = static_cast<Hair *>(object->get_geometry());
|
|
|
|
|
|
2021-03-31 17:17:54 +02:00
|
|
|
/* Make sure shader ids are also updated. */
|
|
|
|
|
if (hair->used_shaders_is_modified()) {
|
|
|
|
|
hair->tag_curve_shader_modified();
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-25 15:12:00 +01:00
|
|
|
cached_data.curve_keys.copy_to_socket(frame_time, hair, hair->get_curve_keys_socket());
|
|
|
|
|
|
|
|
|
|
cached_data.curve_radius.copy_to_socket(frame_time, hair, hair->get_curve_radius_socket());
|
|
|
|
|
|
|
|
|
|
cached_data.curve_shader.copy_to_socket(frame_time, hair, hair->get_curve_shader_socket());
|
|
|
|
|
|
|
|
|
|
cached_data.curve_first_key.copy_to_socket(frame_time, hair, hair->get_curve_first_key_socket());
|
|
|
|
|
|
|
|
|
|
/* update attributes */
|
|
|
|
|
|
|
|
|
|
update_attributes(hair->attributes, cached_data, frame_time);
|
|
|
|
|
|
|
|
|
|
const bool rebuild = (hair->curve_keys_is_modified() || hair->curve_radius_is_modified());
|
2021-03-07 17:22:00 +01:00
|
|
|
hair->tag_update(scene_, rebuild);
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
|
2021-12-01 17:30:46 +01:00
|
|
|
void AlembicProcedural::read_points(AlembicObject *abc_object, Abc::chrono_t frame_time)
|
|
|
|
|
{
|
|
|
|
|
CachedData &cached_data = abc_object->get_cached_data();
|
|
|
|
|
|
|
|
|
|
/* update sockets */
|
|
|
|
|
|
|
|
|
|
Object *object = abc_object->get_object();
|
|
|
|
|
cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket());
|
|
|
|
|
|
|
|
|
|
if (object->is_modified()) {
|
|
|
|
|
object->tag_update(scene_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Only update sockets for the original Geometry. */
|
|
|
|
|
if (abc_object->instance_of) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PointCloud *point_cloud = static_cast<PointCloud *>(object->get_geometry());
|
|
|
|
|
|
|
|
|
|
/* Make sure shader ids are also updated. */
|
|
|
|
|
if (point_cloud->used_shaders_is_modified()) {
|
|
|
|
|
point_cloud->tag_shader_modified();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cached_data.points.copy_to_socket(frame_time, point_cloud, point_cloud->get_points_socket());
|
|
|
|
|
cached_data.radiuses.copy_to_socket(frame_time, point_cloud, point_cloud->get_radius_socket());
|
|
|
|
|
cached_data.points_shader.copy_to_socket(
|
|
|
|
|
frame_time, point_cloud, point_cloud->get_shader_socket());
|
|
|
|
|
|
|
|
|
|
/* update attributes */
|
|
|
|
|
|
|
|
|
|
update_attributes(point_cloud->attributes, cached_data, frame_time);
|
|
|
|
|
|
|
|
|
|
const bool rebuild = (point_cloud->points_is_modified() || point_cloud->radius_is_modified() ||
|
|
|
|
|
point_cloud->shader_is_modified());
|
|
|
|
|
point_cloud->tag_update(scene_, rebuild);
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-25 15:12:00 +01:00
|
|
|
void AlembicProcedural::walk_hierarchy(
|
|
|
|
|
IObject parent,
|
|
|
|
|
const ObjectHeader &header,
|
2021-03-07 16:17:59 +01:00
|
|
|
MatrixSamplesData matrix_samples_data,
|
2021-01-25 15:12:00 +01:00
|
|
|
const unordered_map<std::string, AlembicObject *> &object_map,
|
|
|
|
|
Progress &progress)
|
|
|
|
|
{
|
|
|
|
|
if (progress.get_cancel()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IObject next_object;
|
|
|
|
|
|
|
|
|
|
MatrixSampleMap concatenated_xform_samples;
|
|
|
|
|
|
|
|
|
|
if (IXform::matches(header)) {
|
|
|
|
|
IXform xform(parent, header.getName());
|
|
|
|
|
|
2024-12-29 17:32:00 +01:00
|
|
|
const IXformSchema &xs = xform.getSchema();
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
if (xs.getNumOps() > 0) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const TimeSamplingPtr ts = xs.getTimeSampling();
|
2021-01-25 15:12:00 +01:00
|
|
|
MatrixSampleMap local_xform_samples;
|
|
|
|
|
|
|
|
|
|
MatrixSampleMap *temp_xform_samples = nullptr;
|
2021-03-07 16:17:59 +01:00
|
|
|
if (matrix_samples_data.samples == nullptr) {
|
2021-01-25 15:12:00 +01:00
|
|
|
/* If there is no parent transforms, fill the map directly. */
|
|
|
|
|
temp_xform_samples = &concatenated_xform_samples;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* use a temporary map */
|
|
|
|
|
temp_xform_samples = &local_xform_samples;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < xs.getNumSamples(); ++i) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const chrono_t sample_time = ts->getSampleTime(index_t(i));
|
|
|
|
|
const XformSample sample = xs.getValue(ISampleSelector(sample_time));
|
2021-01-25 15:12:00 +01:00
|
|
|
temp_xform_samples->insert({sample_time, sample.getMatrix()});
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-07 16:17:59 +01:00
|
|
|
if (matrix_samples_data.samples != nullptr) {
|
|
|
|
|
concatenate_xform_samples(
|
|
|
|
|
*matrix_samples_data.samples, local_xform_samples, concatenated_xform_samples);
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-07 16:17:59 +01:00
|
|
|
matrix_samples_data.samples = &concatenated_xform_samples;
|
|
|
|
|
matrix_samples_data.time_sampling = ts;
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next_object = xform;
|
|
|
|
|
}
|
|
|
|
|
else if (ISubD::matches(header)) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const ISubD subd(parent, header.getName());
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
unordered_map<std::string, AlembicObject *>::const_iterator iter;
|
|
|
|
|
iter = object_map.find(subd.getFullName());
|
|
|
|
|
|
|
|
|
|
if (iter != object_map.end()) {
|
|
|
|
|
AlembicObject *abc_object = iter->second;
|
|
|
|
|
abc_object->iobject = subd;
|
2021-02-23 05:31:34 +01:00
|
|
|
abc_object->schema_type = AlembicObject::SUBD;
|
2021-01-25 15:12:00 +01:00
|
|
|
|
2021-03-07 16:17:59 +01:00
|
|
|
if (matrix_samples_data.samples) {
|
|
|
|
|
abc_object->xform_samples = *matrix_samples_data.samples;
|
|
|
|
|
abc_object->xform_time_sampling = matrix_samples_data.time_sampling;
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next_object = subd;
|
|
|
|
|
}
|
|
|
|
|
else if (IPolyMesh::matches(header)) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const IPolyMesh mesh(parent, header.getName());
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
unordered_map<std::string, AlembicObject *>::const_iterator iter;
|
|
|
|
|
iter = object_map.find(mesh.getFullName());
|
|
|
|
|
|
|
|
|
|
if (iter != object_map.end()) {
|
|
|
|
|
AlembicObject *abc_object = iter->second;
|
|
|
|
|
abc_object->iobject = mesh;
|
2021-02-23 05:31:34 +01:00
|
|
|
abc_object->schema_type = AlembicObject::POLY_MESH;
|
2021-01-25 15:12:00 +01:00
|
|
|
|
2021-03-07 16:17:59 +01:00
|
|
|
if (matrix_samples_data.samples) {
|
|
|
|
|
abc_object->xform_samples = *matrix_samples_data.samples;
|
|
|
|
|
abc_object->xform_time_sampling = matrix_samples_data.time_sampling;
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next_object = mesh;
|
|
|
|
|
}
|
|
|
|
|
else if (ICurves::matches(header)) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const ICurves curves(parent, header.getName());
|
2021-01-25 15:12:00 +01:00
|
|
|
|
|
|
|
|
unordered_map<std::string, AlembicObject *>::const_iterator iter;
|
|
|
|
|
iter = object_map.find(curves.getFullName());
|
|
|
|
|
|
|
|
|
|
if (iter != object_map.end()) {
|
|
|
|
|
AlembicObject *abc_object = iter->second;
|
|
|
|
|
abc_object->iobject = curves;
|
2021-02-23 05:31:34 +01:00
|
|
|
abc_object->schema_type = AlembicObject::CURVES;
|
2021-01-25 15:12:00 +01:00
|
|
|
|
2021-03-07 16:17:59 +01:00
|
|
|
if (matrix_samples_data.samples) {
|
|
|
|
|
abc_object->xform_samples = *matrix_samples_data.samples;
|
|
|
|
|
abc_object->xform_time_sampling = matrix_samples_data.time_sampling;
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next_object = curves;
|
|
|
|
|
}
|
|
|
|
|
else if (IFaceSet::matches(header)) {
|
|
|
|
|
// ignore the face set, it will be read along with the data
|
|
|
|
|
}
|
Alembic procedural: add support for instancing
Inside of the procedural, instances are AlembicObjects which point to
the AlembicObject that they instance.
In Alembic, an instance is an untyped Object pointing to the original
(instanced) one through its source path. During the archive traversal we
detect such instances and, only if the instanced object is asked to be
rendered, set the instance's AlembicObject to point to the original's
AlembicObject.
Cycles Object Nodes are created for each AlembicObject, but only for
non-instances are Geometries created, which are then shared between
Object Nodes. It is supposed, and expected, that all instances share the
same shaders, which will be set to be the ones found on the original
object.
As for caching, the data cache for an AlembicObject is only valid for
non-instances and should not be read to or from as it is implicitly
shared.
2021-03-12 01:30:12 +01:00
|
|
|
else if (IPoints::matches(header)) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const IPoints points(parent, header.getName());
|
2021-12-01 17:30:46 +01:00
|
|
|
|
|
|
|
|
unordered_map<std::string, AlembicObject *>::const_iterator iter;
|
|
|
|
|
iter = object_map.find(points.getFullName());
|
|
|
|
|
|
|
|
|
|
if (iter != object_map.end()) {
|
|
|
|
|
AlembicObject *abc_object = iter->second;
|
|
|
|
|
abc_object->iobject = points;
|
|
|
|
|
abc_object->schema_type = AlembicObject::POINTS;
|
|
|
|
|
|
|
|
|
|
if (matrix_samples_data.samples) {
|
|
|
|
|
abc_object->xform_samples = *matrix_samples_data.samples;
|
|
|
|
|
abc_object->xform_time_sampling = matrix_samples_data.time_sampling;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next_object = points;
|
Alembic procedural: add support for instancing
Inside of the procedural, instances are AlembicObjects which point to
the AlembicObject that they instance.
In Alembic, an instance is an untyped Object pointing to the original
(instanced) one through its source path. During the archive traversal we
detect such instances and, only if the instanced object is asked to be
rendered, set the instance's AlembicObject to point to the original's
AlembicObject.
Cycles Object Nodes are created for each AlembicObject, but only for
non-instances are Geometries created, which are then shared between
Object Nodes. It is supposed, and expected, that all instances share the
same shaders, which will be set to be the ones found on the original
object.
As for caching, the data cache for an AlembicObject is only valid for
non-instances and should not be read to or from as it is implicitly
shared.
2021-03-12 01:30:12 +01:00
|
|
|
}
|
|
|
|
|
else if (INuPatch::matches(header)) {
|
|
|
|
|
// unsupported for now
|
|
|
|
|
}
|
2021-01-25 15:12:00 +01:00
|
|
|
else {
|
|
|
|
|
next_object = parent.getChild(header.getName());
|
Alembic procedural: add support for instancing
Inside of the procedural, instances are AlembicObjects which point to
the AlembicObject that they instance.
In Alembic, an instance is an untyped Object pointing to the original
(instanced) one through its source path. During the archive traversal we
detect such instances and, only if the instanced object is asked to be
rendered, set the instance's AlembicObject to point to the original's
AlembicObject.
Cycles Object Nodes are created for each AlembicObject, but only for
non-instances are Geometries created, which are then shared between
Object Nodes. It is supposed, and expected, that all instances share the
same shaders, which will be set to be the ones found on the original
object.
As for caching, the data cache for an AlembicObject is only valid for
non-instances and should not be read to or from as it is implicitly
shared.
2021-03-12 01:30:12 +01:00
|
|
|
|
|
|
|
|
if (next_object.isInstanceRoot()) {
|
|
|
|
|
unordered_map<std::string, AlembicObject *>::const_iterator iter;
|
|
|
|
|
|
|
|
|
|
/* Was this object asked to be rendered? */
|
|
|
|
|
iter = object_map.find(next_object.getFullName());
|
|
|
|
|
|
|
|
|
|
if (iter != object_map.end()) {
|
|
|
|
|
AlembicObject *abc_object = iter->second;
|
|
|
|
|
|
|
|
|
|
/* Only try to render an instance if the original object is also rendered. */
|
|
|
|
|
iter = object_map.find(next_object.instanceSourcePath());
|
|
|
|
|
|
|
|
|
|
if (iter != object_map.end()) {
|
|
|
|
|
abc_object->iobject = next_object;
|
|
|
|
|
abc_object->instance_of = iter->second;
|
|
|
|
|
|
|
|
|
|
if (matrix_samples_data.samples) {
|
|
|
|
|
abc_object->xform_samples = *matrix_samples_data.samples;
|
|
|
|
|
abc_object->xform_time_sampling = matrix_samples_data.time_sampling;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (next_object.valid()) {
|
|
|
|
|
for (size_t i = 0; i < next_object.getNumChildren(); ++i) {
|
|
|
|
|
walk_hierarchy(
|
2021-03-07 16:17:59 +01:00
|
|
|
next_object, next_object.getChildHeader(i), matrix_samples_data, object_map, progress);
|
2021-01-25 15:12:00 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-12 00:15:07 +01:00
|
|
|
void AlembicProcedural::build_caches(Progress &progress)
|
|
|
|
|
{
|
2021-08-20 14:29:05 +02:00
|
|
|
size_t memory_used = 0;
|
|
|
|
|
|
2024-12-30 02:20:36 +01:00
|
|
|
for (Node *node : nodes) {
|
2021-03-12 00:15:07 +01:00
|
|
|
AlembicObject *object = static_cast<AlembicObject *>(node);
|
|
|
|
|
|
|
|
|
|
if (progress.get_cancel()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (object->schema_type == AlembicObject::POLY_MESH) {
|
|
|
|
|
if (!object->has_data_loaded()) {
|
|
|
|
|
IPolyMesh polymesh(object->iobject, Alembic::Abc::kWrapExisting);
|
|
|
|
|
IPolyMeshSchema schema = polymesh.getSchema();
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
object->load_data_in_cache(object->get_cached_data(), this, schema, progress);
|
2021-03-12 00:15:07 +01:00
|
|
|
}
|
|
|
|
|
else if (object->need_shader_update) {
|
|
|
|
|
IPolyMesh polymesh(object->iobject, Alembic::Abc::kWrapExisting);
|
2024-12-29 17:32:00 +01:00
|
|
|
const IPolyMeshSchema schema = polymesh.getSchema();
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
read_attributes(this,
|
|
|
|
|
object->get_cached_data(),
|
|
|
|
|
schema,
|
|
|
|
|
schema.getUVsParam(),
|
|
|
|
|
object->get_requested_attributes(),
|
|
|
|
|
progress);
|
2021-03-12 00:15:07 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (object->schema_type == AlembicObject::CURVES) {
|
|
|
|
|
if (!object->has_data_loaded() || default_radius_is_modified() ||
|
|
|
|
|
object->radius_scale_is_modified())
|
|
|
|
|
{
|
|
|
|
|
ICurves curves(object->iobject, Alembic::Abc::kWrapExisting);
|
2024-12-29 17:32:00 +01:00
|
|
|
const ICurvesSchema schema = curves.getSchema();
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
object->load_data_in_cache(object->get_cached_data(), this, schema, progress);
|
2021-03-12 00:15:07 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-12-01 17:30:46 +01:00
|
|
|
else if (object->schema_type == AlembicObject::POINTS) {
|
|
|
|
|
if (!object->has_data_loaded() || default_radius_is_modified() ||
|
|
|
|
|
object->radius_scale_is_modified())
|
|
|
|
|
{
|
|
|
|
|
IPoints points(object->iobject, Alembic::Abc::kWrapExisting);
|
2024-12-29 17:32:00 +01:00
|
|
|
const IPointsSchema schema = points.getSchema();
|
2021-12-01 17:30:46 +01:00
|
|
|
object->load_data_in_cache(object->get_cached_data(), this, schema, progress);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-12 00:15:07 +01:00
|
|
|
else if (object->schema_type == AlembicObject::SUBD) {
|
|
|
|
|
if (!object->has_data_loaded()) {
|
|
|
|
|
ISubD subd_mesh(object->iobject, Alembic::Abc::kWrapExisting);
|
|
|
|
|
ISubDSchema schema = subd_mesh.getSchema();
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
object->load_data_in_cache(object->get_cached_data(), this, schema, progress);
|
2021-03-12 00:15:07 +01:00
|
|
|
}
|
|
|
|
|
else if (object->need_shader_update) {
|
|
|
|
|
ISubD subd_mesh(object->iobject, Alembic::Abc::kWrapExisting);
|
2024-12-29 17:32:00 +01:00
|
|
|
const ISubDSchema schema = subd_mesh.getSchema();
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
read_attributes(this,
|
|
|
|
|
object->get_cached_data(),
|
|
|
|
|
schema,
|
|
|
|
|
schema.getUVsParam(),
|
|
|
|
|
object->get_requested_attributes(),
|
|
|
|
|
progress);
|
2021-03-12 00:15:07 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (scale_is_modified() || object->get_cached_data().transforms.size() == 0) {
|
Alembic Procedural: refactor data reading
This splits the data reading logic from the AlembicObject class and moves it to
separate files to better enforce a separation of concern. The goal was to simplify
and improve the logic to read data from an Alembic archive.
Since the procedural loads data for the entire animation, this requires looping
over the frame range and looking up data for each frame. Previously those loops
would be duplicated over the entire code causing divergences in how we might
skip or deduplicate data across frames (if only some data change over time and
not other on the same object, e.g. vertices and triangles might not have the
same animation times), and therefore, bugs.
Now, we only use a single function with callback to loop over the geometry data
for each requested frame, and another one to loop over attributes. Given how
attributes are accessed it is a bit tricky to simplify further and only use a
ingle function, however, this is left as a further improvement as it is not
impossible.
To read the data, we now use a set of structures to hold which data to read.
Those structures might seem redundant with the Alembic schemas as they are
somewhat a copy of the schemas' structures, however they will allow us in the
long run to treat the data of one object type as the data of another object
type (e.g. to ignore subdivision, or only loading the vertices as point clouds).
For attributes, this new system allows us to read arbitrary attributes, although
with some limitations still:
* only subdivision and polygon meshes are supported due to lack of examples for
curve data;
* some data types might be missing: we support float, float2, float3, booleans,
normals, uvs, rgb, and rbga at the moment, other types can be trivially added
* some attribute scopes (or domains) are not handled, again, due to lack of example
files
* color types are always interpreted as vertex colors
2021-05-03 07:00:58 +02:00
|
|
|
object->setup_transform_cache(object->get_cached_data(), scale);
|
2021-03-12 00:15:07 +01:00
|
|
|
}
|
2021-08-20 14:29:05 +02:00
|
|
|
|
|
|
|
|
memory_used += object->get_cached_data().memory_used();
|
|
|
|
|
|
|
|
|
|
if (use_prefetch) {
|
|
|
|
|
if (memory_used > get_prefetch_cache_size_in_bytes()) {
|
|
|
|
|
progress.set_error("Error: Alembic Procedural memory limit reached");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-12 00:15:07 +01:00
|
|
|
}
|
2021-08-20 14:29:05 +02:00
|
|
|
|
2022-06-16 19:39:13 +02:00
|
|
|
VLOG_WORK << "AlembicProcedural memory usage : " << string_human_readable_size(memory_used);
|
2021-03-12 00:15:07 +01:00
|
|
|
}
|
|
|
|
|
|
2021-01-25 15:12:00 +01:00
|
|
|
CCL_NAMESPACE_END
|
|
|
|
|
|
|
|
|
|
#endif
|