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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/cycles/render')
-rw-r--r--intern/cycles/render/CMakeLists.txt2
-rw-r--r--intern/cycles/render/alembic.cpp997
-rw-r--r--intern/cycles/render/alembic.h69
-rw-r--r--intern/cycles/render/alembic_read.cpp1008
-rw-r--r--intern/cycles/render/alembic_read.h134
-rw-r--r--intern/cycles/render/attribute.cpp64
-rw-r--r--intern/cycles/render/attribute.h33
-rw-r--r--intern/cycles/render/background.cpp9
-rw-r--r--intern/cycles/render/geometry.cpp140
-rw-r--r--intern/cycles/render/geometry.h25
-rw-r--r--intern/cycles/render/hair.cpp61
-rw-r--r--intern/cycles/render/hair.h5
-rw-r--r--intern/cycles/render/image.cpp18
-rw-r--r--intern/cycles/render/image.h12
-rw-r--r--intern/cycles/render/image_oiio.cpp4
-rw-r--r--intern/cycles/render/image_oiio.h2
-rw-r--r--intern/cycles/render/image_sky.cpp2
-rw-r--r--intern/cycles/render/image_sky.h2
-rw-r--r--intern/cycles/render/image_vdb.cpp179
-rw-r--r--intern/cycles/render/image_vdb.h3
-rw-r--r--intern/cycles/render/light.cpp13
-rw-r--r--intern/cycles/render/light.h1
-rw-r--r--intern/cycles/render/mesh.cpp36
-rw-r--r--intern/cycles/render/mesh.h5
-rw-r--r--intern/cycles/render/nodes.cpp4
-rw-r--r--intern/cycles/render/nodes.h14
-rw-r--r--intern/cycles/render/object.cpp12
-rw-r--r--intern/cycles/render/object.h1
-rw-r--r--intern/cycles/render/osl.cpp14
-rw-r--r--intern/cycles/render/osl.h11
-rw-r--r--intern/cycles/render/scene.cpp66
-rw-r--r--intern/cycles/render/scene.h4
-rw-r--r--intern/cycles/render/session.cpp10
-rw-r--r--intern/cycles/render/shader.cpp62
-rw-r--r--intern/cycles/render/shader.h11
-rw-r--r--intern/cycles/render/svm.cpp10
-rw-r--r--intern/cycles/render/svm.h9
37 files changed, 1910 insertions, 1142 deletions
diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt
index c67919b375a..feead27c5ca 100644
--- a/intern/cycles/render/CMakeLists.txt
+++ b/intern/cycles/render/CMakeLists.txt
@@ -24,6 +24,7 @@ set(INC_SYS
set(SRC
alembic.cpp
+ alembic_read.cpp
attribute.cpp
background.cpp
bake.cpp
@@ -67,6 +68,7 @@ set(SRC
set(SRC_HEADERS
alembic.h
+ alembic_read.h
attribute.h
bake.h
background.h
diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp
index 1336f81896a..dcb456dc1ce 100644
--- a/intern/cycles/render/alembic.cpp
+++ b/intern/cycles/render/alembic.cpp
@@ -16,6 +16,7 @@
#include "render/alembic.h"
+#include "render/alembic_read.h"
#include "render/camera.h"
#include "render/curves.h"
#include "render/mesh.h"
@@ -34,7 +35,48 @@ using namespace Alembic::AbcGeom;
CCL_NAMESPACE_BEGIN
-/* TODO(@kevindietrich): motion blur support */
+/* TODO(kevindietrich): motion blur support. */
+
+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;
+}
void CachedData::clear()
{
@@ -54,7 +96,7 @@ void CachedData::clear()
subd_start_corner.clear();
transforms.clear();
triangles.clear();
- triangles_loops.clear();
+ uv_loops.clear();
vertices.clear();
for (CachedAttribute &attr : attributes) {
@@ -101,7 +143,7 @@ bool CachedData::is_constant() const
CHECK_IF_CONSTANT(subd_start_corner)
CHECK_IF_CONSTANT(transforms)
CHECK_IF_CONSTANT(triangles)
- CHECK_IF_CONSTANT(triangles_loops)
+ CHECK_IF_CONSTANT(uv_loops)
CHECK_IF_CONSTANT(vertices)
for (const CachedAttribute &attr : attributes) {
@@ -140,7 +182,7 @@ void CachedData::invalidate_last_loaded_time(bool attributes_only)
subd_start_corner.invalidate_last_loaded_time();
transforms.invalidate_last_loaded_time();
triangles.invalidate_last_loaded_time();
- triangles_loops.invalidate_last_loaded_time();
+ uv_loops.invalidate_last_loaded_time();
vertices.invalidate_last_loaded_time();
}
@@ -161,7 +203,7 @@ void CachedData::set_time_sampling(TimeSampling time_sampling)
subd_start_corner.set_time_sampling(time_sampling);
transforms.set_time_sampling(time_sampling);
triangles.set_time_sampling(time_sampling);
- triangles_loops.set_time_sampling(time_sampling);
+ uv_loops.set_time_sampling(time_sampling);
vertices.set_time_sampling(time_sampling);
for (CachedAttribute &attr : attributes) {
@@ -169,36 +211,6 @@ void CachedData::set_time_sampling(TimeSampling time_sampling)
}
}
-/* get the sample times to load data for the given the start and end frame of the procedural */
-static set<chrono_t> get_relevant_sample_times(AlembicProcedural *proc,
- const TimeSampling &time_sampling,
- size_t num_samples)
-{
- set<chrono_t> result;
-
- if (num_samples < 2) {
- result.insert(0.0);
- return result;
- }
-
- double start_frame = (double)(proc->get_start_frame() / proc->get_frame_rate());
- double end_frame = (double)((proc->get_end_frame() + 1) / proc->get_frame_rate());
-
- size_t start_index = time_sampling.getFloorIndex(start_frame, num_samples).first;
- size_t end_index = time_sampling.getCeilIndex(end_frame, num_samples).first;
-
- for (size_t i = start_index; i < end_index; ++i) {
- result.insert(time_sampling.getSampleTime(i));
- }
-
- return result;
-}
-
-static float3 make_float3_from_yup(const V3f &v)
-{
- return make_float3(v.x, -v.z, v.y);
-}
-
static M44d convert_yup_zup(const M44d &mtx, float scale_mult)
{
V3d scale, shear, rotation, translation;
@@ -366,249 +378,6 @@ static Transform make_transform(const M44d &a, float scale)
return trans;
}
-static void add_uvs(AlembicProcedural *proc,
- const IV2fGeomParam &uvs,
- CachedData &cached_data,
- Progress &progress)
-{
- if (uvs.getScope() != kFacevaryingScope) {
- return;
- }
-
- const TimeSamplingPtr time_sampling_ptr = uvs.getTimeSampling();
-
- TimeSampling time_sampling;
- if (time_sampling_ptr) {
- time_sampling = *time_sampling_ptr;
- }
-
- std::string name = Alembic::Abc::GetSourceName(uvs.getMetaData());
-
- /* According to the convention, primary UVs should have had their name
- * set using Alembic::Abc::SetSourceName, but you can't expect everyone
- * to follow it! :) */
- if (name.empty()) {
- name = uvs.getName();
- }
-
- CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(name), time_sampling);
- attr.std = ATTR_STD_UV;
-
- ccl::set<chrono_t> times = get_relevant_sample_times(proc, time_sampling, uvs.getNumSamples());
-
- /* Keys used to determine if the UVs do actually change over time. */
- ArraySample::Key previous_indices_key;
- ArraySample::Key previous_values_key;
-
- foreach (chrono_t time, times) {
- if (progress.get_cancel()) {
- return;
- }
-
- const ISampleSelector iss = ISampleSelector(time);
- const IV2fGeomParam::Sample uvsample = uvs.getIndexedValue(iss);
-
- if (!uvsample.valid()) {
- continue;
- }
-
- const array<int3> *triangles =
- cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
- const array<int3> *triangles_loops =
- cached_data.triangles_loops.data_for_time_no_check(time).get_data_or_null();
-
- if (!triangles || !triangles_loops) {
- continue;
- }
-
- array<char> data;
- data.resize(triangles->size() * 3 * sizeof(float2));
-
- float2 *data_float2 = reinterpret_cast<float2 *>(data.data());
-
- const ArraySample::Key indices_key = uvsample.getIndices()->getKey();
- const ArraySample::Key values_key = uvsample.getVals()->getKey();
-
- if (indices_key == previous_indices_key && values_key == previous_values_key) {
- attr.data.reuse_data_for_last_time(time);
- }
- else {
- const unsigned int *indices = uvsample.getIndices()->get();
- const V2f *values = uvsample.getVals()->get();
-
- for (const int3 &loop : *triangles_loops) {
- unsigned int v0 = indices[loop.x];
- unsigned int v1 = indices[loop.y];
- unsigned int v2 = indices[loop.z];
-
- data_float2[0] = make_float2(values[v0][0], values[v0][1]);
- data_float2[1] = make_float2(values[v1][0], values[v1][1]);
- data_float2[2] = make_float2(values[v2][0], values[v2][1]);
- data_float2 += 3;
- }
-
- attr.data.add_data(data, time);
- }
-
- previous_indices_key = indices_key;
- previous_values_key = values_key;
- }
-}
-
-static void add_normals(const Int32ArraySamplePtr face_indices,
- const IN3fGeomParam &normals,
- double time,
- CachedData &cached_data)
-{
- switch (normals.getScope()) {
- case kFacevaryingScope: {
- const ISampleSelector iss = ISampleSelector(time);
- const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss);
-
- if (!sample.valid()) {
- return;
- }
-
- CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()),
- *normals.getTimeSampling());
- attr.std = ATTR_STD_VERTEX_NORMAL;
-
- const array<float3> *vertices =
- cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
-
- if (!vertices) {
- return;
- }
-
- array<char> data;
- data.resize(vertices->size() * sizeof(float3));
-
- float3 *data_float3 = reinterpret_cast<float3 *>(data.data());
-
- const int *face_indices_array = face_indices->get();
- const N3fArraySamplePtr values = sample.getVals();
-
- for (size_t i = 0; i < face_indices->size(); ++i) {
- int point_index = face_indices_array[i];
- data_float3[point_index] = make_float3_from_yup(values->get()[i]);
- }
-
- attr.data.add_data(data, time);
- break;
- }
- case kVaryingScope:
- case kVertexScope: {
- const ISampleSelector iss = ISampleSelector(time);
- const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss);
-
- if (!sample.valid()) {
- return;
- }
-
- CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()),
- *normals.getTimeSampling());
- attr.std = ATTR_STD_VERTEX_NORMAL;
-
- const array<float3> *vertices =
- cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
-
- if (!vertices) {
- return;
- }
-
- array<char> data;
- data.resize(vertices->size() * sizeof(float3));
-
- float3 *data_float3 = reinterpret_cast<float3 *>(data.data());
-
- const Imath::V3f *values = sample.getVals()->get();
-
- for (size_t i = 0; i < vertices->size(); ++i) {
- data_float3[i] = make_float3_from_yup(values[i]);
- }
-
- attr.data.add_data(data, time);
-
- break;
- }
- default: {
- break;
- }
- }
-}
-
-static void add_positions(const P3fArraySamplePtr positions, double time, CachedData &cached_data)
-{
- if (!positions) {
- return;
- }
-
- array<float3> vertices;
- vertices.reserve(positions->size());
-
- for (size_t i = 0; i < positions->size(); i++) {
- V3f f = positions->get()[i];
- vertices.push_back_reserved(make_float3_from_yup(f));
- }
-
- cached_data.vertices.add_data(vertices, time);
-}
-
-static void add_triangles(const Int32ArraySamplePtr face_counts,
- const Int32ArraySamplePtr face_indices,
- double time,
- CachedData &cached_data,
- const array<int> &polygon_to_shader)
-{
- if (!face_counts || !face_indices) {
- return;
- }
-
- const size_t num_faces = face_counts->size();
- const int *face_counts_array = face_counts->get();
- const int *face_indices_array = face_indices->get();
-
- size_t num_triangles = 0;
- for (size_t i = 0; i < face_counts->size(); i++) {
- num_triangles += face_counts_array[i] - 2;
- }
-
- array<int> shader;
- array<int3> triangles;
- array<int3> triangles_loops;
- shader.reserve(num_triangles);
- triangles.reserve(num_triangles);
- triangles_loops.reserve(num_triangles);
- int index_offset = 0;
-
- for (size_t i = 0; i < num_faces; i++) {
- int current_shader = 0;
-
- if (!polygon_to_shader.empty()) {
- current_shader = polygon_to_shader[i];
- }
-
- for (int j = 0; j < face_counts_array[i] - 2; j++) {
- int v0 = face_indices_array[index_offset];
- int v1 = face_indices_array[index_offset + j + 1];
- int v2 = face_indices_array[index_offset + j + 2];
-
- shader.push_back_reserved(current_shader);
-
- /* Alembic orders the loops following the RenderMan convention, so need to go in reverse. */
- triangles.push_back_reserved(make_int3(v2, v1, v0));
- triangles_loops.push_back_reserved(
- make_int3(index_offset + j + 2, index_offset + j + 1, index_offset));
- }
-
- index_offset += face_counts_array[i];
- }
-
- cached_data.triangles.add_data(triangles, time);
- cached_data.triangles_loops.add_data(triangles_loops, time);
- cached_data.shader.add_data(shader, time);
-}
-
NODE_DEFINE(AlembicObject)
{
NodeType *type = NodeType::add("alembic_object", create);
@@ -648,398 +417,137 @@ bool AlembicObject::has_data_loaded() const
return data_loaded;
}
-void AlembicObject::update_shader_attributes(const ICompoundProperty &arb_geom_params,
- Progress &progress)
-{
- AttributeRequestSet requested_attributes = get_requested_attributes();
-
- foreach (const AttributeRequest &attr, requested_attributes.requests) {
- if (progress.get_cancel()) {
- return;
- }
-
- bool attr_exists = false;
- foreach (CachedData::CachedAttribute &cached_attr, cached_data.attributes) {
- if (cached_attr.name == attr.name) {
- attr_exists = true;
- break;
- }
- }
-
- if (attr_exists) {
- continue;
- }
-
- read_attribute(arb_geom_params, attr.name, progress);
- }
-
- cached_data.invalidate_last_loaded_time(true);
- need_shader_update = false;
-}
-
-template<typename SchemaType>
-void AlembicObject::read_face_sets(SchemaType &schema,
- array<int> &polygon_to_shader,
- ISampleSelector sample_sel)
-{
- std::vector<std::string> face_sets;
- schema.getFaceSetNames(face_sets);
-
- if (face_sets.empty()) {
- return;
- }
-
- const Int32ArraySamplePtr face_counts = schema.getFaceCountsProperty().getValue();
-
- polygon_to_shader.resize(face_counts->size());
-
- foreach (const std::string &face_set_name, face_sets) {
- int shader_index = 0;
-
- foreach (Node *node, get_used_shaders()) {
- if (node->name == face_set_name) {
- break;
- }
-
- ++shader_index;
- }
-
- if (shader_index >= get_used_shaders().size()) {
- /* use the first shader instead if none was found */
- shader_index = 0;
- }
-
- const IFaceSet face_set = schema.getFaceSet(face_set_name);
-
- if (!face_set.valid()) {
- continue;
- }
-
- const IFaceSetSchema face_schem = face_set.getSchema();
- const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel);
- const Int32ArraySamplePtr group_faces = face_sample.getFaces();
- const size_t num_group_faces = group_faces->size();
-
- for (size_t l = 0; l < num_group_faces; l++) {
- size_t pos = (*group_faces)[l];
-
- if (pos >= polygon_to_shader.size()) {
- continue;
- }
-
- polygon_to_shader[pos] = shader_index;
- }
- }
-}
-
-void AlembicObject::load_all_data(AlembicProcedural *proc,
- IPolyMeshSchema &schema,
- Progress &progress)
+void AlembicObject::load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ IPolyMeshSchema &schema,
+ Progress &progress)
{
- cached_data.clear();
-
/* Only load data for the original Geometry. */
if (instance_of) {
return;
}
- const TimeSamplingPtr time_sampling = schema.getTimeSampling();
- cached_data.set_time_sampling(*time_sampling);
-
- const IN3fGeomParam &normals = schema.getNormalsParam();
-
- ccl::set<chrono_t> times = get_relevant_sample_times(
- proc, *time_sampling, schema.getNumSamples());
-
- /* Key used to determine if the triangles change over time, if the key is the same as the
- * last one, we can avoid creating a new entry in the cache and simply point to the last
- * frame. */
- ArraySample::Key previous_key;
-
- /* read topology */
- foreach (chrono_t time, times) {
- if (progress.get_cancel()) {
- return;
- }
-
- const ISampleSelector iss = ISampleSelector(time);
- const IPolyMeshSchema::Sample sample = schema.getValue(iss);
-
- add_positions(sample.getPositions(), time, cached_data);
-
- /* Only copy triangles for other frames if the topology is changing over time as well. */
- if (schema.getTopologyVariance() != kHomogenousTopology || cached_data.triangles.size() == 0) {
- const ArraySample::Key key = sample.getFaceIndices()->getKey();
-
- if (key == previous_key) {
- cached_data.triangles.reuse_data_for_last_time(time);
- cached_data.triangles_loops.reuse_data_for_last_time(time);
- cached_data.shader.reuse_data_for_last_time(time);
- }
- else {
- /* start by reading the face sets (per face shader), as we directly split polygons to
- * triangles
- */
- array<int> polygon_to_shader;
- read_face_sets(schema, polygon_to_shader, iss);
-
- add_triangles(
- sample.getFaceCounts(), sample.getFaceIndices(), time, cached_data, polygon_to_shader);
- }
+ cached_data.clear();
- previous_key = key;
- }
+ 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());
- if (normals.valid()) {
- add_normals(sample.getFaceIndices(), normals, time, cached_data);
- }
- }
+ read_geometry_data(proc, cached_data, data, progress);
if (progress.get_cancel()) {
return;
}
- update_shader_attributes(schema.getArbGeomParams(), progress);
+ /* 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);
if (progress.get_cancel()) {
return;
}
- const IV2fGeomParam &uvs = schema.getUVsParam();
-
- if (uvs.valid()) {
- add_uvs(proc, uvs, cached_data, progress);
- }
-
+ cached_data.invalidate_last_loaded_time(true);
data_loaded = true;
}
-void AlembicObject::load_all_data(AlembicProcedural *proc, ISubDSchema &schema, Progress &progress)
+void AlembicObject::load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ ISubDSchema &schema,
+ Progress &progress)
{
- cached_data.clear();
-
/* Only load data for the original Geometry. */
if (instance_of) {
return;
}
- AttributeRequestSet requested_attributes = get_requested_attributes();
-
- const TimeSamplingPtr time_sampling = schema.getTimeSampling();
- cached_data.set_time_sampling(*time_sampling);
-
- ccl::set<chrono_t> times = get_relevant_sample_times(
- proc, *time_sampling, schema.getNumSamples());
-
- /* read topology */
- foreach (chrono_t time, times) {
- if (progress.get_cancel()) {
- return;
- }
-
- const ISampleSelector iss = ISampleSelector(time);
- const ISubDSchema::Sample sample = schema.getValue(iss);
-
- add_positions(sample.getPositions(), time, cached_data);
-
- const Int32ArraySamplePtr face_counts = sample.getFaceCounts();
- const Int32ArraySamplePtr face_indices = sample.getFaceIndices();
-
- /* start by reading the face sets (per face shader) */
- array<int> polygon_to_shader;
- read_face_sets(schema, polygon_to_shader, iss);
-
- /* read faces */
- array<int> subd_start_corner;
- array<int> shader;
- array<int> subd_num_corners;
- array<bool> subd_smooth;
- array<int> subd_ptex_offset;
- array<int> subd_face_corners;
-
- const size_t num_faces = face_counts->size();
- const int *face_counts_array = face_counts->get();
- const int *face_indices_array = face_indices->get();
-
- int num_ngons = 0;
- int num_corners = 0;
- for (size_t i = 0; i < face_counts->size(); i++) {
- num_ngons += (face_counts_array[i] == 4 ? 0 : 1);
- num_corners += face_counts_array[i];
- }
-
- subd_start_corner.reserve(num_faces);
- subd_num_corners.reserve(num_faces);
- subd_smooth.reserve(num_faces);
- subd_ptex_offset.reserve(num_faces);
- shader.reserve(num_faces);
- subd_face_corners.reserve(num_corners);
-
- int start_corner = 0;
- int current_shader = 0;
- int ptex_offset = 0;
-
- for (size_t i = 0; i < face_counts->size(); i++) {
- num_corners = face_counts_array[i];
-
- if (!polygon_to_shader.empty()) {
- current_shader = polygon_to_shader[i];
- }
-
- subd_start_corner.push_back_reserved(start_corner);
- subd_num_corners.push_back_reserved(num_corners);
-
- for (int j = 0; j < num_corners; ++j) {
- subd_face_corners.push_back_reserved(face_indices_array[start_corner + j]);
- }
-
- shader.push_back_reserved(current_shader);
- subd_smooth.push_back_reserved(1);
- subd_ptex_offset.push_back_reserved(ptex_offset);
-
- ptex_offset += (num_corners == 4 ? 1 : num_corners);
-
- start_corner += num_corners;
- }
-
- cached_data.shader.add_data(shader, time);
- cached_data.subd_start_corner.add_data(subd_start_corner, time);
- cached_data.subd_num_corners.add_data(subd_num_corners, time);
- cached_data.subd_smooth.add_data(subd_smooth, time);
- cached_data.subd_ptex_offset.add_data(subd_ptex_offset, time);
- cached_data.subd_face_corners.add_data(subd_face_corners, time);
- cached_data.num_ngons.add_data(num_ngons, time);
-
- /* read creases */
- Int32ArraySamplePtr creases_length = sample.getCreaseLengths();
- Int32ArraySamplePtr creases_indices = sample.getCreaseIndices();
- FloatArraySamplePtr creases_sharpnesses = sample.getCreaseSharpnesses();
-
- if (creases_length && creases_indices && creases_sharpnesses) {
- array<int> creases_edge;
- array<float> creases_weight;
-
- creases_edge.reserve(creases_sharpnesses->size() * 2);
- creases_weight.reserve(creases_sharpnesses->size());
-
- int length_offset = 0;
- int weight_offset = 0;
- for (size_t c = 0; c < creases_length->size(); ++c) {
- const int crease_length = creases_length->get()[c];
-
- for (size_t j = 0; j < crease_length - 1; ++j) {
- creases_edge.push_back_reserved(creases_indices->get()[length_offset + j]);
- creases_edge.push_back_reserved(creases_indices->get()[length_offset + j + 1]);
- creases_weight.push_back_reserved(creases_sharpnesses->get()[weight_offset++]);
- }
-
- length_offset += crease_length;
- }
-
- cached_data.subd_creases_edge.add_data(creases_edge, time);
- cached_data.subd_creases_weight.add_data(creases_weight, time);
- }
- }
+ cached_data.clear();
- /* TODO(@kevindietrich) : attributes, need test files */
+ 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);
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;
}
-void AlembicObject::load_all_data(AlembicProcedural *proc,
- const ICurvesSchema &schema,
- Progress &progress,
- float default_radius)
+void AlembicObject::load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ const ICurvesSchema &schema,
+ Progress &progress)
{
- cached_data.clear();
-
/* Only load data for the original Geometry. */
if (instance_of) {
return;
}
- const TimeSamplingPtr time_sampling = schema.getTimeSampling();
- cached_data.set_time_sampling(*time_sampling);
-
- ccl::set<chrono_t> times = get_relevant_sample_times(
- proc, *time_sampling, schema.getNumSamples());
-
- foreach (chrono_t time, times) {
- if (progress.get_cancel()) {
- return;
- }
-
- const ISampleSelector iss = ISampleSelector(time);
- const ICurvesSchema::Sample sample = schema.getValue(iss);
-
- const Int32ArraySamplePtr curves_num_vertices = sample.getCurvesNumVertices();
- const P3fArraySamplePtr position = sample.getPositions();
-
- const IFloatGeomParam widths_param = schema.getWidthsParam();
- FloatArraySamplePtr radiuses;
-
- if (widths_param.valid()) {
- IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(iss);
- radiuses = wsample.getVals();
- }
-
- const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1);
- float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : default_radius;
-
- array<float3> curve_keys;
- array<float> curve_radius;
- array<int> curve_first_key;
- array<int> curve_shader;
-
- const bool is_homogenous = schema.getTopologyVariance() == kHomogenousTopology;
-
- curve_keys.reserve(position->size());
- curve_radius.reserve(position->size());
- curve_first_key.reserve(curves_num_vertices->size());
- curve_shader.reserve(curves_num_vertices->size());
-
- int offset = 0;
- for (size_t i = 0; i < curves_num_vertices->size(); i++) {
- const int num_vertices = curves_num_vertices->get()[i];
-
- for (int j = 0; j < num_vertices; j++) {
- const V3f &f = position->get()[offset + j];
- curve_keys.push_back_reserved(make_float3_from_yup(f));
-
- if (do_radius) {
- radius = (*radiuses)[offset + j];
- }
-
- curve_radius.push_back_reserved(radius * radius_scale);
- }
-
- if (!is_homogenous || cached_data.curve_first_key.size() == 0) {
- curve_first_key.push_back_reserved(offset);
- curve_shader.push_back_reserved(0);
- }
-
- offset += num_vertices;
- }
+ cached_data.clear();
- cached_data.curve_keys.add_data(curve_keys, time);
- cached_data.curve_radius.add_data(curve_radius, time);
+ 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);
- if (!is_homogenous || cached_data.curve_first_key.size() == 0) {
- cached_data.curve_first_key.add_data(curve_first_key, time);
- cached_data.curve_shader.add_data(curve_shader, time);
- }
+ if (progress.get_cancel()) {
+ return;
}
- // TODO(@kevindietrich): attributes, need example files
+ /* 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;
}
-void AlembicObject::setup_transform_cache(float scale)
+void AlembicObject::setup_transform_cache(CachedData &cached_data, float scale)
{
cached_data.transforms.clear();
cached_data.transforms.invalidate_last_loaded_time();
@@ -1102,188 +610,6 @@ AttributeRequestSet AlembicObject::get_requested_attributes()
return requested_attributes;
}
-void AlembicObject::read_attribute(const ICompoundProperty &arb_geom_params,
- const ustring &attr_name,
- Progress &progress)
-{
- const PropertyHeader *prop = arb_geom_params.getPropertyHeader(attr_name.c_str());
-
- if (prop == nullptr) {
- return;
- }
-
- if (IV2fProperty::matches(prop->getMetaData()) && Alembic::AbcGeom::isUV(*prop)) {
- const IV2fGeomParam &param = IV2fGeomParam(arb_geom_params, prop->getName());
-
- CachedData::CachedAttribute &attribute = cached_data.add_attribute(attr_name,
- *param.getTimeSampling());
-
- for (size_t i = 0; i < param.getNumSamples(); ++i) {
- if (progress.get_cancel()) {
- return;
- }
-
- ISampleSelector iss = ISampleSelector(index_t(i));
-
- IV2fGeomParam::Sample sample;
- param.getIndexed(sample, iss);
-
- const chrono_t time = param.getTimeSampling()->getSampleTime(index_t(i));
-
- if (param.getScope() == kFacevaryingScope) {
- V2fArraySamplePtr values = sample.getVals();
- UInt32ArraySamplePtr indices = sample.getIndices();
-
- attribute.std = ATTR_STD_NONE;
- attribute.element = ATTR_ELEMENT_CORNER;
- attribute.type_desc = TypeFloat2;
-
- const array<int3> *triangles =
- cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
- const array<int3> *triangles_loops =
- cached_data.triangles_loops.data_for_time_no_check(time).get_data_or_null();
-
- if (!triangles || !triangles_loops) {
- return;
- }
-
- array<char> data;
- data.resize(triangles->size() * 3 * sizeof(float2));
-
- float2 *data_float2 = reinterpret_cast<float2 *>(data.data());
-
- for (const int3 &loop : *triangles_loops) {
- unsigned int v0 = (*indices)[loop.x];
- unsigned int v1 = (*indices)[loop.y];
- unsigned int v2 = (*indices)[loop.z];
-
- data_float2[0] = make_float2((*values)[v0][0], (*values)[v0][1]);
- data_float2[1] = make_float2((*values)[v1][0], (*values)[v1][1]);
- data_float2[2] = make_float2((*values)[v2][0], (*values)[v2][1]);
- data_float2 += 3;
- }
-
- attribute.data.set_time_sampling(*param.getTimeSampling());
- attribute.data.add_data(data, time);
- }
- }
- }
- else if (IC3fProperty::matches(prop->getMetaData())) {
- const IC3fGeomParam &param = IC3fGeomParam(arb_geom_params, prop->getName());
-
- CachedData::CachedAttribute &attribute = cached_data.add_attribute(attr_name,
- *param.getTimeSampling());
-
- for (size_t i = 0; i < param.getNumSamples(); ++i) {
- if (progress.get_cancel()) {
- return;
- }
-
- ISampleSelector iss = ISampleSelector(index_t(i));
-
- IC3fGeomParam::Sample sample;
- param.getIndexed(sample, iss);
-
- const chrono_t time = param.getTimeSampling()->getSampleTime(index_t(i));
-
- C3fArraySamplePtr values = sample.getVals();
-
- attribute.std = ATTR_STD_NONE;
-
- if (param.getScope() == kVaryingScope) {
- attribute.element = ATTR_ELEMENT_CORNER_BYTE;
- attribute.type_desc = TypeRGBA;
-
- const array<int3> *triangles =
- cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
-
- if (!triangles) {
- return;
- }
-
- array<char> data;
- data.resize(triangles->size() * 3 * sizeof(uchar4));
-
- uchar4 *data_uchar4 = reinterpret_cast<uchar4 *>(data.data());
-
- int offset = 0;
- for (const int3 &tri : *triangles) {
- Imath::C3f v = (*values)[tri.x];
- data_uchar4[offset + 0] = color_float_to_byte(make_float3(v.x, v.y, v.z));
-
- v = (*values)[tri.y];
- data_uchar4[offset + 1] = color_float_to_byte(make_float3(v.x, v.y, v.z));
-
- v = (*values)[tri.z];
- data_uchar4[offset + 2] = color_float_to_byte(make_float3(v.x, v.y, v.z));
-
- offset += 3;
- }
-
- attribute.data.set_time_sampling(*param.getTimeSampling());
- attribute.data.add_data(data, time);
- }
- }
- }
- else if (IC4fProperty::matches(prop->getMetaData())) {
- const IC4fGeomParam &param = IC4fGeomParam(arb_geom_params, prop->getName());
-
- CachedData::CachedAttribute &attribute = cached_data.add_attribute(attr_name,
- *param.getTimeSampling());
-
- for (size_t i = 0; i < param.getNumSamples(); ++i) {
- if (progress.get_cancel()) {
- return;
- }
-
- ISampleSelector iss = ISampleSelector(index_t(i));
-
- IC4fGeomParam::Sample sample;
- param.getIndexed(sample, iss);
-
- const chrono_t time = param.getTimeSampling()->getSampleTime(index_t(i));
-
- C4fArraySamplePtr values = sample.getVals();
-
- attribute.std = ATTR_STD_NONE;
-
- if (param.getScope() == kVaryingScope) {
- attribute.element = ATTR_ELEMENT_CORNER_BYTE;
- attribute.type_desc = TypeRGBA;
-
- const array<int3> *triangles =
- cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
-
- if (!triangles) {
- return;
- }
-
- array<char> data;
- data.resize(triangles->size() * 3 * sizeof(uchar4));
-
- uchar4 *data_uchar4 = reinterpret_cast<uchar4 *>(data.data());
-
- int offset = 0;
- for (const int3 &tri : *triangles) {
- Imath::C4f v = (*values)[tri.x];
- data_uchar4[offset + 0] = color_float4_to_uchar4(make_float4(v.r, v.g, v.b, v.a));
-
- v = (*values)[tri.y];
- data_uchar4[offset + 1] = color_float4_to_uchar4(make_float4(v.r, v.g, v.b, v.a));
-
- v = (*values)[tri.z];
- data_uchar4[offset + 2] = color_float4_to_uchar4(make_float4(v.r, v.g, v.b, v.a));
-
- offset += 3;
- }
-
- attribute.data.set_time_sampling(*param.getTimeSampling());
- attribute.data.add_data(data, time);
- }
- }
- }
-}
-
/* Update existing attributes and remove any attribute not in the cached_data, those attributes
* were added by Cycles (e.g. face normals) */
static void update_attributes(AttributeSet &attributes, CachedData &cached_data, double frame_time)
@@ -1291,7 +617,11 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data,
set<Attribute *> cached_attributes;
for (CachedData::CachedAttribute &attribute : cached_data.attributes) {
- const array<char> *attr_data = attribute.data.data_for_time(frame_time).get_data_or_null();
+ const CacheLookupResult<array<char>> result = attribute.data.data_for_time(frame_time);
+
+ if (result.has_no_data_for_time()) {
+ continue;
+ }
Attribute *attr = nullptr;
if (attribute.std != ATTR_STD_NONE) {
@@ -1304,18 +634,19 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data,
cached_attributes.insert(attr);
- if (!attr_data) {
- /* no new data */
+ if (!result.has_new_data()) {
continue;
}
+ const ccl::array<char> &attr_data = result.get_data();
+
/* weak way of detecting if the topology has changed
* todo: reuse code from device_update patch */
- if (attr->buffer.size() != attr_data->size()) {
- attr->buffer.resize(attr_data->size());
+ if (attr->buffer.size() != attr_data.size()) {
+ attr->buffer.resize(attr_data.size());
}
- memcpy(attr->data(), attr_data->data(), attr_data->size());
+ memcpy(attr->data(), attr_data.data(), attr_data.size());
attr->modified = true;
}
@@ -1323,8 +654,7 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data,
list<Attribute>::iterator it;
for (it = attributes.attributes.begin(); it != attributes.attributes.end();) {
if (cached_attributes.find(&(*it)) == cached_attributes.end()) {
- attributes.attributes.erase(it++);
- attributes.modified = true;
+ attributes.remove(it++);
continue;
}
@@ -1407,6 +737,15 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress)
need_data_updates = true;
}
+ /* 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;
+ }
+
/* Check for changes in shaders (e.g. newly requested attributes). */
foreach (Node *shader_node, object->get_used_shaders()) {
Shader *shader = static_cast<Shader *>(shader_node);
@@ -1467,6 +806,7 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress)
read_subd(object, frame_time);
}
+ object->need_shader_update = false;
object->clear_modified();
}
@@ -1586,6 +926,11 @@ void AlembicProcedural::read_mesh(AlembicObject *abc_object, Abc::chrono_t frame
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
+ /* Make sure shader ids are also updated. */
+ if (mesh->used_shaders_is_modified()) {
+ mesh->tag_shader_modified();
+ }
+
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());
@@ -1653,6 +998,11 @@ void AlembicProcedural::read_subd(AlembicObject *abc_object, Abc::chrono_t frame
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
+ /* Make sure shader ids are also updated. */
+ if (mesh->used_shaders_is_modified()) {
+ mesh->tag_shader_modified();
+ }
+
/* 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()) {
@@ -1743,6 +1093,11 @@ void AlembicProcedural::read_curves(AlembicObject *abc_object, Abc::chrono_t fra
Hair *hair = static_cast<Hair *>(object->get_geometry());
+ /* Make sure shader ids are also updated. */
+ if (hair->used_shaders_is_modified()) {
+ hair->tag_curve_shader_modified();
+ }
+
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());
@@ -1936,12 +1291,17 @@ void AlembicProcedural::build_caches(Progress &progress)
if (!object->has_data_loaded()) {
IPolyMesh polymesh(object->iobject, Alembic::Abc::kWrapExisting);
IPolyMeshSchema schema = polymesh.getSchema();
- object->load_all_data(this, schema, progress);
+ object->load_data_in_cache(object->get_cached_data(), this, schema, progress);
}
else if (object->need_shader_update) {
IPolyMesh polymesh(object->iobject, Alembic::Abc::kWrapExisting);
IPolyMeshSchema schema = polymesh.getSchema();
- object->update_shader_attributes(schema.getArbGeomParams(), progress);
+ read_attributes(this,
+ object->get_cached_data(),
+ schema,
+ schema.getUVsParam(),
+ object->get_requested_attributes(),
+ progress);
}
}
else if (object->schema_type == AlembicObject::CURVES) {
@@ -1949,24 +1309,29 @@ void AlembicProcedural::build_caches(Progress &progress)
object->radius_scale_is_modified()) {
ICurves curves(object->iobject, Alembic::Abc::kWrapExisting);
ICurvesSchema schema = curves.getSchema();
- object->load_all_data(this, schema, progress, default_radius);
+ object->load_data_in_cache(object->get_cached_data(), this, schema, progress);
}
}
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();
- object->load_all_data(this, schema, progress);
+ object->load_data_in_cache(object->get_cached_data(), this, schema, progress);
}
else if (object->need_shader_update) {
ISubD subd_mesh(object->iobject, Alembic::Abc::kWrapExisting);
ISubDSchema schema = subd_mesh.getSchema();
- object->update_shader_attributes(schema.getArbGeomParams(), progress);
+ read_attributes(this,
+ object->get_cached_data(),
+ schema,
+ schema.getUVsParam(),
+ object->get_requested_attributes(),
+ progress);
}
}
if (scale_is_modified() || object->get_cached_data().transforms.size() == 0) {
- object->setup_transform_cache(scale);
+ object->setup_transform_cache(object->get_cached_data(), scale);
}
}
}
diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h
index d0c5856a353..61c0e40fe4a 100644
--- a/intern/cycles/render/alembic.h
+++ b/intern/cycles/render/alembic.h
@@ -152,6 +152,10 @@ template<typename T> class DataStore {
double last_loaded_time = std::numeric_limits<double>::max();
public:
+ /* Keys used to compare values. */
+ Alembic::AbcCoreAbstract::ArraySample::Key key1;
+ Alembic::AbcCoreAbstract::ArraySample::Key key2;
+
void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling_)
{
time_sampling = time_sampling_;
@@ -225,6 +229,11 @@ template<typename T> class DataStore {
index_data_map.push_back({time, data_index.source_time, data_index.index});
}
+ void add_no_data(double time)
+ {
+ index_data_map.push_back({time, time, -1ul});
+ }
+
bool is_constant() const
{
return data.size() <= 1;
@@ -239,6 +248,7 @@ template<typename T> class DataStore {
{
invalidate_last_loaded_time();
data.clear();
+ index_data_map.clear();
}
void invalidate_last_loaded_time()
@@ -283,7 +293,7 @@ struct CachedData {
DataStore<array<int3>> triangles{};
/* triangle "loops" are the polygons' vertices indices used for indexing face varying attributes
* (like UVs) */
- DataStore<array<int3>> triangles_loops{};
+ DataStore<array<int>> uv_loops{};
DataStore<array<int>> shader{};
/* subd data */
@@ -361,16 +371,18 @@ class AlembicObject : public Node {
void set_object(Object *object);
Object *get_object();
- void load_all_data(AlembicProcedural *proc,
- Alembic::AbcGeom::IPolyMeshSchema &schema,
- Progress &progress);
- void load_all_data(AlembicProcedural *proc,
- Alembic::AbcGeom::ISubDSchema &schema,
- Progress &progress);
- void load_all_data(AlembicProcedural *proc,
- const Alembic::AbcGeom::ICurvesSchema &schema,
- Progress &progress,
- float default_radius);
+ void load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ Alembic::AbcGeom::IPolyMeshSchema &schema,
+ Progress &progress);
+ void load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ Alembic::AbcGeom::ISubDSchema &schema,
+ Progress &progress);
+ void load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ const Alembic::AbcGeom::ICurvesSchema &schema,
+ Progress &progress);
bool has_data_loaded() const;
@@ -396,33 +408,21 @@ class AlembicObject : public Node {
CachedData &get_cached_data()
{
- return cached_data;
+ return cached_data_;
}
bool is_constant() const
{
- return cached_data.is_constant();
+ return cached_data_.is_constant();
}
Object *object = nullptr;
bool data_loaded = false;
- CachedData cached_data;
-
- void update_shader_attributes(const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,
- Progress &progress);
-
- void read_attribute(const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,
- const ustring &attr_name,
- Progress &progress);
-
- template<typename SchemaType>
- void read_face_sets(SchemaType &schema,
- array<int> &polygon_to_shader,
- Alembic::AbcGeom::ISampleSelector sample_sel);
+ CachedData cached_data_;
- void setup_transform_cache(float scale);
+ void setup_transform_cache(CachedData &cached_data, float scale);
AttributeRequestSet get_requested_attributes();
};
@@ -480,16 +480,23 @@ class AlembicProcedural : public Procedural {
* invocation, and updates the data on subsequent invocations if the frame changed. */
void generate(Scene *scene, Progress &progress);
- /* Add an object to our list of objects, and tag the socket as modified. */
- void add_object(AlembicObject *object);
-
/* Tag for an update only if something was modified. */
void tag_update(Scene *scene);
- /* Returns a pointer to an existing or a newly created AlembicObject for the given path. */
+ /* This should be called by scene exporters to request the rendering of an object located
+ * in the Alembic archive at the given path.
+ *
+ * Since we lazily load object, the function does not validate the existence of the object
+ * in the archive. If no objects with such path if found in the archive during the next call
+ * to `generate`, it will be ignored.
+ *
+ * Returns a pointer to an existing or a newly created AlembicObject for the given path. */
AlembicObject *get_or_create_object(const ustring &path);
private:
+ /* Add an object to our list of objects, and tag the socket as modified. */
+ void add_object(AlembicObject *object);
+
/* Load the data for all the objects whose data has not yet been loaded. */
void load_objects(Progress &progress);
diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/render/alembic_read.cpp
new file mode 100644
index 00000000000..33ce3502879
--- /dev/null
+++ b/intern/cycles/render/alembic_read.cpp
@@ -0,0 +1,1008 @@
+/*
+ * Copyright 2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "render/alembic_read.h"
+
+#include "render/alembic.h"
+#include "render/mesh.h"
+
+#include "util/util_progress.h"
+
+#ifdef WITH_ALEMBIC
+
+using namespace Alembic::AbcGeom;
+
+CCL_NAMESPACE_BEGIN
+
+static float3 make_float3_from_yup(const V3f &v)
+{
+ return make_float3(v.x, -v.z, v.y);
+}
+
+/* get the sample times to load data for the given the start and end frame of the procedural */
+static set<chrono_t> get_relevant_sample_times(AlembicProcedural *proc,
+ const TimeSampling &time_sampling,
+ size_t num_samples)
+{
+ set<chrono_t> result;
+
+ if (num_samples < 2) {
+ result.insert(0.0);
+ return result;
+ }
+
+ // load the data for the entire animation
+ const double start_frame = static_cast<double>(proc->get_start_frame());
+ const double end_frame = static_cast<double>(proc->get_end_frame());
+
+ const double frame_rate = static_cast<double>(proc->get_frame_rate());
+ const double start_time = start_frame / frame_rate;
+ const double end_time = (end_frame + 1) / frame_rate;
+
+ const size_t start_index = time_sampling.getFloorIndex(start_time, num_samples).first;
+ const size_t end_index = time_sampling.getCeilIndex(end_time, num_samples).first;
+
+ for (size_t i = start_index; i < end_index; ++i) {
+ result.insert(time_sampling.getSampleTime(i));
+ }
+
+ return result;
+}
+
+/* Main function to read data, this will iterate over all the relevant sample times for the
+ * duration of the requested animation, and call the DataReadingFunc for each of those sample time.
+ */
+template<typename Params, typename DataReadingFunc>
+static void read_data_loop(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const Params &params,
+ DataReadingFunc &&func,
+ Progress &progress)
+{
+ const std::set<chrono_t> times = get_relevant_sample_times(
+ proc, *params.time_sampling, params.num_samples);
+
+ cached_data.set_time_sampling(*params.time_sampling);
+
+ for (chrono_t time : times) {
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ func(cached_data, params, time);
+ }
+}
+
+/* Polygon Mesh Geometries. */
+
+/* Compute the vertex normals in case none are present in the IPolyMeshSchema, this is mostly used
+ * to avoid computing them in the GeometryManager in order to speed up data updates. */
+static void compute_vertex_normals(CachedData &cache, double current_time)
+{
+ if (cache.vertices.size() == 0) {
+ return;
+ }
+
+ CachedData::CachedAttribute &attr_normal = cache.add_attribute(
+ ustring("N"), cache.vertices.get_time_sampling());
+ attr_normal.std = ATTR_STD_VERTEX_NORMAL;
+ attr_normal.element = ATTR_ELEMENT_VERTEX;
+ attr_normal.type_desc = TypeNormal;
+
+ const array<float3> *vertices =
+ cache.vertices.data_for_time_no_check(current_time).get_data_or_null();
+ const array<int3> *triangles =
+ cache.triangles.data_for_time_no_check(current_time).get_data_or_null();
+
+ if (!vertices || !triangles) {
+ attr_normal.data.add_no_data(current_time);
+ return;
+ }
+
+ array<char> attr_data(vertices->size() * sizeof(float3));
+ float3 *attr_ptr = reinterpret_cast<float3 *>(attr_data.data());
+ memset(attr_ptr, 0, vertices->size() * sizeof(float3));
+
+ for (size_t t = 0; t < triangles->size(); ++t) {
+ const int3 tri_int3 = triangles->data()[t];
+ Mesh::Triangle tri{};
+ tri.v[0] = tri_int3[0];
+ tri.v[1] = tri_int3[1];
+ tri.v[2] = tri_int3[2];
+
+ const float3 tri_N = tri.compute_normal(vertices->data());
+
+ for (int v = 0; v < 3; ++v) {
+ attr_ptr[tri_int3[v]] += tri_N;
+ }
+ }
+
+ for (size_t v = 0; v < vertices->size(); ++v) {
+ attr_ptr[v] = normalize(attr_ptr[v]);
+ }
+
+ attr_normal.data.add_data(attr_data, current_time);
+}
+
+static void add_normals(const Int32ArraySamplePtr face_indices,
+ const IN3fGeomParam &normals,
+ double time,
+ CachedData &cached_data)
+{
+ switch (normals.getScope()) {
+ case kFacevaryingScope: {
+ const ISampleSelector iss = ISampleSelector(time);
+ const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss);
+
+ if (!sample.valid()) {
+ return;
+ }
+
+ CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()),
+ *normals.getTimeSampling());
+ attr.std = ATTR_STD_VERTEX_NORMAL;
+
+ const array<float3> *vertices =
+ cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
+
+ if (!vertices) {
+ return;
+ }
+
+ array<char> data;
+ data.resize(vertices->size() * sizeof(float3));
+
+ float3 *data_float3 = reinterpret_cast<float3 *>(data.data());
+
+ const int *face_indices_array = face_indices->get();
+ const N3fArraySamplePtr values = sample.getVals();
+
+ for (size_t i = 0; i < face_indices->size(); ++i) {
+ int point_index = face_indices_array[i];
+ data_float3[point_index] = make_float3_from_yup(values->get()[i]);
+ }
+
+ attr.data.add_data(data, time);
+ break;
+ }
+ case kVaryingScope:
+ case kVertexScope: {
+ const ISampleSelector iss = ISampleSelector(time);
+ const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss);
+
+ if (!sample.valid()) {
+ return;
+ }
+
+ CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()),
+ *normals.getTimeSampling());
+ attr.std = ATTR_STD_VERTEX_NORMAL;
+
+ const array<float3> *vertices =
+ cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
+
+ if (!vertices) {
+ return;
+ }
+
+ array<char> data;
+ data.resize(vertices->size() * sizeof(float3));
+
+ float3 *data_float3 = reinterpret_cast<float3 *>(data.data());
+
+ const Imath::V3f *values = sample.getVals()->get();
+
+ for (size_t i = 0; i < vertices->size(); ++i) {
+ data_float3[i] = make_float3_from_yup(values[i]);
+ }
+
+ attr.data.add_data(data, time);
+
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+static void add_positions(const P3fArraySamplePtr positions, double time, CachedData &cached_data)
+{
+ if (!positions) {
+ return;
+ }
+
+ array<float3> vertices;
+ vertices.reserve(positions->size());
+
+ for (size_t i = 0; i < positions->size(); i++) {
+ V3f f = positions->get()[i];
+ vertices.push_back_reserved(make_float3_from_yup(f));
+ }
+
+ cached_data.vertices.add_data(vertices, time);
+}
+
+static void add_triangles(const Int32ArraySamplePtr face_counts,
+ const Int32ArraySamplePtr face_indices,
+ double time,
+ CachedData &cached_data,
+ const array<int> &polygon_to_shader)
+{
+ if (!face_counts || !face_indices) {
+ return;
+ }
+
+ const size_t num_faces = face_counts->size();
+ const int *face_counts_array = face_counts->get();
+ const int *face_indices_array = face_indices->get();
+
+ size_t num_triangles = 0;
+ for (size_t i = 0; i < face_counts->size(); i++) {
+ num_triangles += face_counts_array[i] - 2;
+ }
+
+ array<int> shader;
+ array<int3> triangles;
+ array<int> uv_loops;
+ shader.reserve(num_triangles);
+ triangles.reserve(num_triangles);
+ uv_loops.reserve(num_triangles * 3);
+ int index_offset = 0;
+
+ for (size_t i = 0; i < num_faces; i++) {
+ int current_shader = 0;
+
+ if (!polygon_to_shader.empty()) {
+ current_shader = polygon_to_shader[i];
+ }
+
+ for (int j = 0; j < face_counts_array[i] - 2; j++) {
+ int v0 = face_indices_array[index_offset];
+ int v1 = face_indices_array[index_offset + j + 1];
+ int v2 = face_indices_array[index_offset + j + 2];
+
+ shader.push_back_reserved(current_shader);
+
+ /* Alembic orders the loops following the RenderMan convention, so need to go in reverse. */
+ triangles.push_back_reserved(make_int3(v2, v1, v0));
+ uv_loops.push_back_reserved(index_offset + j + 2);
+ uv_loops.push_back_reserved(index_offset + j + 1);
+ uv_loops.push_back_reserved(index_offset);
+ }
+
+ index_offset += face_counts_array[i];
+ }
+
+ cached_data.triangles.add_data(triangles, time);
+ cached_data.uv_loops.add_data(uv_loops, time);
+ cached_data.shader.add_data(shader, time);
+}
+
+static array<int> compute_polygon_to_shader_map(
+ const Int32ArraySamplePtr &face_counts,
+ const vector<FaceSetShaderIndexPair> &face_set_shader_index,
+ ISampleSelector sample_sel)
+{
+ if (face_set_shader_index.empty()) {
+ return {};
+ }
+
+ if (!face_counts) {
+ return {};
+ }
+
+ if (face_counts->size() == 0) {
+ return {};
+ }
+
+ array<int> polygon_to_shader(face_counts->size());
+
+ for (const FaceSetShaderIndexPair &pair : face_set_shader_index) {
+ const IFaceSet &face_set = pair.face_set;
+ const IFaceSetSchema face_schem = face_set.getSchema();
+ const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel);
+ const Int32ArraySamplePtr group_faces = face_sample.getFaces();
+ const size_t num_group_faces = group_faces->size();
+
+ for (size_t l = 0; l < num_group_faces; l++) {
+ size_t pos = (*group_faces)[l];
+
+ if (pos >= polygon_to_shader.size()) {
+ continue;
+ }
+
+ polygon_to_shader[pos] = pair.shader_index;
+ }
+ }
+
+ return polygon_to_shader;
+}
+
+static void read_poly_mesh_geometry(CachedData &cached_data,
+ const PolyMeshSchemaData &data,
+ chrono_t time)
+{
+ const ISampleSelector iss = ISampleSelector(time);
+
+ add_positions(data.positions.getValue(iss), time, cached_data);
+
+ const Int32ArraySamplePtr face_counts = data.face_counts.getValue(iss);
+ const Int32ArraySamplePtr face_indices = data.face_indices.getValue(iss);
+
+ /* Only copy triangles for other frames if the topology is changing over time as well. */
+ if (data.topology_variance != kHomogeneousTopology || cached_data.triangles.size() == 0) {
+ bool do_triangles = true;
+
+ /* Compare key with last one to check whether the topology changed. */
+ if (cached_data.triangles.size() > 0) {
+ const ArraySample::Key key = face_indices->getKey();
+
+ if (key == cached_data.triangles.key1) {
+ do_triangles = false;
+ }
+
+ cached_data.triangles.key1 = key;
+ }
+
+ if (do_triangles) {
+ const array<int> polygon_to_shader = compute_polygon_to_shader_map(
+ face_counts, data.shader_face_sets, iss);
+ add_triangles(face_counts, face_indices, time, cached_data, polygon_to_shader);
+ }
+ else {
+ cached_data.triangles.reuse_data_for_last_time(time);
+ cached_data.uv_loops.reuse_data_for_last_time(time);
+ cached_data.shader.reuse_data_for_last_time(time);
+ }
+
+ /* Initialize the first key. */
+ if (data.topology_variance != kHomogeneousTopology && cached_data.triangles.size() == 1) {
+ cached_data.triangles.key1 = face_indices->getKey();
+ }
+ }
+
+ if (data.normals.valid()) {
+ add_normals(face_indices, data.normals, time, cached_data);
+ }
+ else {
+ compute_vertex_normals(cached_data, time);
+ }
+}
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const PolyMeshSchemaData &data,
+ Progress &progress)
+{
+ read_data_loop(proc, cached_data, data, read_poly_mesh_geometry, progress);
+}
+
+/* Subdivision Geometries */
+
+static void add_subd_polygons(CachedData &cached_data, const SubDSchemaData &data, chrono_t time)
+{
+ const ISampleSelector iss = ISampleSelector(time);
+
+ const Int32ArraySamplePtr face_counts = data.face_counts.getValue(iss);
+ const Int32ArraySamplePtr face_indices = data.face_indices.getValue(iss);
+
+ array<int> subd_start_corner;
+ array<int> shader;
+ array<int> subd_num_corners;
+ array<bool> subd_smooth;
+ array<int> subd_ptex_offset;
+ array<int> subd_face_corners;
+ array<int> uv_loops;
+
+ const size_t num_faces = face_counts->size();
+ const int *face_counts_array = face_counts->get();
+ const int *face_indices_array = face_indices->get();
+
+ int num_ngons = 0;
+ int num_corners = 0;
+ for (size_t i = 0; i < face_counts->size(); i++) {
+ num_ngons += (face_counts_array[i] == 4 ? 0 : 1);
+ num_corners += face_counts_array[i];
+ }
+
+ subd_start_corner.reserve(num_faces);
+ subd_num_corners.reserve(num_faces);
+ subd_smooth.reserve(num_faces);
+ subd_ptex_offset.reserve(num_faces);
+ shader.reserve(num_faces);
+ subd_face_corners.reserve(num_corners);
+ uv_loops.reserve(num_corners);
+
+ int start_corner = 0;
+ int current_shader = 0;
+ int ptex_offset = 0;
+
+ const array<int> polygon_to_shader = compute_polygon_to_shader_map(
+ face_counts, data.shader_face_sets, iss);
+
+ for (size_t i = 0; i < face_counts->size(); i++) {
+ num_corners = face_counts_array[i];
+
+ if (!polygon_to_shader.empty()) {
+ current_shader = polygon_to_shader[i];
+ }
+
+ subd_start_corner.push_back_reserved(start_corner);
+ subd_num_corners.push_back_reserved(num_corners);
+
+ for (int j = 0; j < num_corners; ++j) {
+ subd_face_corners.push_back_reserved(face_indices_array[start_corner + j]);
+ uv_loops.push_back_reserved(start_corner + j);
+ }
+
+ shader.push_back_reserved(current_shader);
+ subd_smooth.push_back_reserved(1);
+ subd_ptex_offset.push_back_reserved(ptex_offset);
+
+ ptex_offset += (num_corners == 4 ? 1 : num_corners);
+
+ start_corner += num_corners;
+ }
+
+ cached_data.shader.add_data(shader, time);
+ cached_data.subd_start_corner.add_data(subd_start_corner, time);
+ cached_data.subd_num_corners.add_data(subd_num_corners, time);
+ cached_data.subd_smooth.add_data(subd_smooth, time);
+ cached_data.subd_ptex_offset.add_data(subd_ptex_offset, time);
+ cached_data.subd_face_corners.add_data(subd_face_corners, time);
+ cached_data.num_ngons.add_data(num_ngons, time);
+ cached_data.uv_loops.add_data(uv_loops, time);
+}
+
+static void add_subd_creases(CachedData &cached_data, const SubDSchemaData &data, chrono_t time)
+{
+ if (!(data.crease_indices.valid() && data.crease_indices.valid() &&
+ data.crease_sharpnesses.valid())) {
+ return;
+ }
+
+ const ISampleSelector iss = ISampleSelector(time);
+
+ const Int32ArraySamplePtr creases_length = data.crease_lengths.getValue(iss);
+ const Int32ArraySamplePtr creases_indices = data.crease_indices.getValue(iss);
+ const FloatArraySamplePtr creases_sharpnesses = data.crease_sharpnesses.getValue(iss);
+
+ if (creases_length && creases_indices && creases_sharpnesses) {
+ array<int> creases_edge;
+ array<float> creases_weight;
+
+ creases_edge.reserve(creases_sharpnesses->size() * 2);
+ creases_weight.reserve(creases_sharpnesses->size());
+
+ int length_offset = 0;
+ int weight_offset = 0;
+ for (size_t c = 0; c < creases_length->size(); ++c) {
+ const int crease_length = creases_length->get()[c];
+
+ for (size_t j = 0; j < crease_length - 1; ++j) {
+ creases_edge.push_back_reserved(creases_indices->get()[length_offset + j]);
+ creases_edge.push_back_reserved(creases_indices->get()[length_offset + j + 1]);
+ creases_weight.push_back_reserved(creases_sharpnesses->get()[weight_offset++]);
+ }
+
+ length_offset += crease_length;
+ }
+
+ cached_data.subd_creases_edge.add_data(creases_edge, time);
+ cached_data.subd_creases_weight.add_data(creases_weight, time);
+ }
+}
+
+static void read_subd_geometry(CachedData &cached_data, const SubDSchemaData &data, chrono_t time)
+{
+ const ISampleSelector iss = ISampleSelector(time);
+
+ add_positions(data.positions.getValue(iss), time, cached_data);
+
+ if (data.topology_variance != kHomogenousTopology || cached_data.shader.size() == 0) {
+ add_subd_polygons(cached_data, data, time);
+ add_subd_creases(cached_data, data, time);
+ }
+}
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const SubDSchemaData &data,
+ Progress &progress)
+{
+ read_data_loop(proc, cached_data, data, read_subd_geometry, progress);
+}
+
+/* Curve Geometries. */
+
+static void read_curves_data(CachedData &cached_data, const CurvesSchemaData &data, chrono_t time)
+{
+ const ISampleSelector iss = ISampleSelector(time);
+
+ const Int32ArraySamplePtr curves_num_vertices = data.num_vertices.getValue(iss);
+ const P3fArraySamplePtr position = data.positions.getValue(iss);
+
+ FloatArraySamplePtr radiuses;
+
+ if (data.widths.valid()) {
+ IFloatGeomParam::Sample wsample = data.widths.getExpandedValue(iss);
+ radiuses = wsample.getVals();
+ }
+
+ const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1);
+ float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : data.default_radius;
+
+ array<float3> curve_keys;
+ array<float> curve_radius;
+ array<int> curve_first_key;
+ array<int> curve_shader;
+
+ const bool is_homogenous = data.topology_variance == kHomogenousTopology;
+
+ curve_keys.reserve(position->size());
+ curve_radius.reserve(position->size());
+ curve_first_key.reserve(curves_num_vertices->size());
+ curve_shader.reserve(curves_num_vertices->size());
+
+ int offset = 0;
+ for (size_t i = 0; i < curves_num_vertices->size(); i++) {
+ const int num_vertices = curves_num_vertices->get()[i];
+
+ for (int j = 0; j < num_vertices; j++) {
+ const V3f &f = position->get()[offset + j];
+ // todo(@kevindietrich): we are reading too much data?
+ curve_keys.push_back_slow(make_float3_from_yup(f));
+
+ if (do_radius) {
+ radius = (*radiuses)[offset + j];
+ }
+
+ curve_radius.push_back_slow(radius * data.radius_scale);
+ }
+
+ if (!is_homogenous || cached_data.curve_first_key.size() == 0) {
+ curve_first_key.push_back_reserved(offset);
+ curve_shader.push_back_reserved(0);
+ }
+
+ offset += num_vertices;
+ }
+
+ cached_data.curve_keys.add_data(curve_keys, time);
+ cached_data.curve_radius.add_data(curve_radius, time);
+
+ if (!is_homogenous || cached_data.curve_first_key.size() == 0) {
+ cached_data.curve_first_key.add_data(curve_first_key, time);
+ cached_data.curve_shader.add_data(curve_shader, time);
+ }
+}
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const CurvesSchemaData &data,
+ Progress &progress)
+{
+ read_data_loop(proc, cached_data, data, read_curves_data, progress);
+}
+
+/* Attributes conversions. */
+
+/* Type traits for converting between Alembic and Cycles types.
+ */
+
+template<typename T> struct value_type_converter {
+ using cycles_type = float;
+ /* Use `TypeDesc::FLOAT` instead of `TypeFloat` to work around a compiler bug in gcc 11. */
+ static constexpr TypeDesc type_desc = TypeDesc::FLOAT;
+ static constexpr const char *type_name = "float (default)";
+
+ static cycles_type convert_value(T value)
+ {
+ return static_cast<float>(value);
+ }
+};
+
+template<> struct value_type_converter<Imath::V2f> {
+ using cycles_type = float2;
+ static constexpr TypeDesc type_desc = TypeFloat2;
+ static constexpr const char *type_name = "float2";
+
+ static cycles_type convert_value(Imath::V2f value)
+ {
+ return make_float2(value.x, value.y);
+ }
+};
+
+template<> struct value_type_converter<Imath::V3f> {
+ using cycles_type = float3;
+ static constexpr TypeDesc type_desc = TypeVector;
+ static constexpr const char *type_name = "float3";
+
+ static cycles_type convert_value(Imath::V3f value)
+ {
+ return make_float3_from_yup(value);
+ }
+};
+
+template<> struct value_type_converter<Imath::C3f> {
+ using cycles_type = uchar4;
+ static constexpr TypeDesc type_desc = TypeRGBA;
+ static constexpr const char *type_name = "rgb";
+
+ static cycles_type convert_value(Imath::C3f value)
+ {
+ return color_float_to_byte(make_float3(value.x, value.y, value.z));
+ }
+};
+
+template<> struct value_type_converter<Imath::C4f> {
+ using cycles_type = uchar4;
+ static constexpr TypeDesc type_desc = TypeRGBA;
+ static constexpr const char *type_name = "rgba";
+
+ static cycles_type convert_value(Imath::C4f value)
+ {
+ return color_float4_to_uchar4(make_float4(value.r, value.g, value.b, value.a));
+ }
+};
+
+/* Main function used to read attributes of any type. */
+template<typename TRAIT>
+static void process_attribute(CachedData &cache,
+ CachedData::CachedAttribute &attribute,
+ GeometryScope scope,
+ const typename ITypedGeomParam<TRAIT>::Sample &sample,
+ double time)
+{
+ using abc_type = typename TRAIT::value_type;
+ using cycles_type = typename value_type_converter<abc_type>::cycles_type;
+
+ const TypedArraySample<TRAIT> &values = *sample.getVals();
+
+ switch (scope) {
+ case kConstantScope:
+ case kVertexScope: {
+ const array<float3> *vertices =
+ cache.vertices.data_for_time_no_check(time).get_data_or_null();
+
+ if (!vertices) {
+ attribute.data.add_no_data(time);
+ return;
+ }
+
+ if (vertices->size() != values.size()) {
+ attribute.data.add_no_data(time);
+ return;
+ }
+
+ array<char> data(vertices->size() * sizeof(cycles_type));
+
+ cycles_type *pod_typed_data = reinterpret_cast<cycles_type *>(data.data());
+
+ for (size_t i = 0; i < values.size(); ++i) {
+ *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[i]);
+ }
+
+ attribute.data.add_data(data, time);
+ break;
+ }
+ case kVaryingScope: {
+ const array<int3> *triangles =
+ cache.triangles.data_for_time_no_check(time).get_data_or_null();
+
+ if (!triangles) {
+ attribute.data.add_no_data(time);
+ return;
+ }
+
+ array<char> data(triangles->size() * 3 * sizeof(cycles_type));
+
+ cycles_type *pod_typed_data = reinterpret_cast<cycles_type *>(data.data());
+
+ for (const int3 &tri : *triangles) {
+ *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.x]);
+ *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.y]);
+ *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.z]);
+ }
+
+ attribute.data.add_data(data, time);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+/* UVs are processed separately as their indexing is based on loops, instead of vertices or
+ * corners. */
+static void process_uvs(CachedData &cache,
+ CachedData::CachedAttribute &attribute,
+ GeometryScope scope,
+ const IV2fGeomParam::Sample &sample,
+ double time)
+{
+ if (scope != kFacevaryingScope) {
+ return;
+ }
+
+ const array<int> *uv_loops = cache.uv_loops.data_for_time_no_check(time).get_data_or_null();
+
+ if (!uv_loops) {
+ return;
+ }
+
+ const array<int3> *triangles = cache.triangles.data_for_time_no_check(time).get_data_or_null();
+ const array<int> *corners =
+ cache.subd_face_corners.data_for_time_no_check(time).get_data_or_null();
+
+ array<char> data;
+ if (triangles) {
+ data.resize(triangles->size() * 3 * sizeof(float2));
+ }
+ else if (corners) {
+ data.resize(corners->size() * sizeof(float2));
+ }
+ else {
+ return;
+ }
+
+ float2 *data_float2 = reinterpret_cast<float2 *>(data.data());
+
+ const uint32_t *indices = sample.getIndices()->get();
+ const V2f *values = sample.getVals()->get();
+
+ for (const int uv_loop_index : *uv_loops) {
+ const uint32_t index = indices[uv_loop_index];
+ *data_float2++ = make_float2(values[index][0], values[index][1]);
+ }
+
+ attribute.data.add_data(data, time);
+}
+
+/* Type of the function used to parse one time worth of data, either process_uvs or
+ * process_attribute_generic. */
+template<typename TRAIT>
+using process_callback_type = void (*)(CachedData &,
+ CachedData::CachedAttribute &,
+ GeometryScope,
+ const typename ITypedGeomParam<TRAIT>::Sample &,
+ double);
+
+/* Main loop to process the attributes, this will look at the given param's TimeSampling and
+ * extract data based on which frame time is requested by the procedural and execute the callback
+ * for each of those requested time. */
+template<typename TRAIT>
+static void read_attribute_loop(AlembicProcedural *proc,
+ CachedData &cache,
+ const ITypedGeomParam<TRAIT> &param,
+ process_callback_type<TRAIT> callback,
+ Progress &progress,
+ AttributeStandard std = ATTR_STD_NONE)
+{
+ const std::set<chrono_t> times = get_relevant_sample_times(
+ proc, *param.getTimeSampling(), param.getNumSamples());
+
+ if (times.empty()) {
+ return;
+ }
+
+ std::string name = param.getName();
+
+ if (std == ATTR_STD_UV) {
+ std::string uv_source_name = Alembic::Abc::GetSourceName(param.getMetaData());
+
+ /* According to the convention, primary UVs should have had their name
+ * set using Alembic::Abc::SetSourceName, but you can't expect everyone
+ * to follow it! :) */
+ if (!uv_source_name.empty()) {
+ name = uv_source_name;
+ }
+ }
+
+ CachedData::CachedAttribute &attribute = cache.add_attribute(ustring(name),
+ *param.getTimeSampling());
+
+ using abc_type = typename TRAIT::value_type;
+
+ attribute.data.set_time_sampling(*param.getTimeSampling());
+ attribute.std = std;
+ attribute.type_desc = value_type_converter<abc_type>::type_desc;
+
+ if (attribute.type_desc == TypeRGBA) {
+ attribute.element = ATTR_ELEMENT_CORNER_BYTE;
+ }
+ else {
+ if (param.getScope() == kVaryingScope || param.getScope() == kFacevaryingScope) {
+ attribute.element = ATTR_ELEMENT_CORNER;
+ }
+ else {
+ attribute.element = ATTR_ELEMENT_VERTEX;
+ }
+ }
+
+ for (const chrono_t time : times) {
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ ISampleSelector iss = ISampleSelector(time);
+ typename ITypedGeomParam<TRAIT>::Sample sample;
+ param.getIndexed(sample, iss);
+
+ if (!sample.valid()) {
+ continue;
+ }
+
+ if (!sample.getVals()) {
+ attribute.data.add_no_data(time);
+ continue;
+ }
+
+ /* Check whether we already loaded constant data. */
+ if (attribute.data.size() != 0) {
+ if (param.isConstant()) {
+ return;
+ }
+
+ const ArraySample::Key indices_key = sample.getIndices()->getKey();
+ const ArraySample::Key values_key = sample.getVals()->getKey();
+
+ const bool is_same_as_last_time = (indices_key == attribute.data.key1 &&
+ values_key == attribute.data.key2);
+
+ attribute.data.key1 = indices_key;
+ attribute.data.key2 = values_key;
+
+ if (is_same_as_last_time) {
+ attribute.data.reuse_data_for_last_time(time);
+ continue;
+ }
+ }
+
+ callback(cache, attribute, param.getScope(), sample, time);
+ }
+}
+
+/* Attributes requests. */
+
+/* This structure is used to tell which ICoumpoundProperty the PropertyHeader comes from, as we
+ * need the parent when downcasting to the proper type. */
+struct PropHeaderAndParent {
+ const PropertyHeader *prop;
+ ICompoundProperty parent;
+};
+
+/* Parse the ICompoundProperty to look for properties whose names appear in the
+ * AttributeRequestSet. This also looks into any child ICompoundProperty of the given
+ * ICompoundProperty. If no property of the given name is found, let it be that way, Cycles will
+ * use a zero value for the missing attribute. */
+static void parse_requested_attributes_recursive(const AttributeRequestSet &requested_attributes,
+ const ICompoundProperty &arb_geom_params,
+ vector<PropHeaderAndParent> &requested_properties)
+{
+ if (!arb_geom_params.valid()) {
+ return;
+ }
+
+ for (const AttributeRequest &req : requested_attributes.requests) {
+ const PropertyHeader *property_header = arb_geom_params.getPropertyHeader(req.name.c_str());
+
+ if (!property_header) {
+ continue;
+ }
+
+ requested_properties.push_back({property_header, arb_geom_params});
+ }
+
+ /* Look into children compound properties. */
+ for (size_t i = 0; i < arb_geom_params.getNumProperties(); ++i) {
+ const PropertyHeader &property_header = arb_geom_params.getPropertyHeader(i);
+
+ if (property_header.isCompound()) {
+ ICompoundProperty compound_property = ICompoundProperty(arb_geom_params,
+ property_header.getName());
+ parse_requested_attributes_recursive(
+ requested_attributes, compound_property, requested_properties);
+ }
+ }
+}
+
+/* Main entry point for parsing requested attributes from an ICompoundProperty, this exists so that
+ * we can simply return the list of properties instead of allocating it on the stack and passing it
+ * as a parameter. */
+static vector<PropHeaderAndParent> parse_requested_attributes(
+ const AttributeRequestSet &requested_attributes, const ICompoundProperty &arb_geom_params)
+{
+ vector<PropHeaderAndParent> requested_properties;
+ parse_requested_attributes_recursive(
+ requested_attributes, arb_geom_params, requested_properties);
+ return requested_properties;
+}
+
+/* Read the attributes requested by the shaders from the archive. This will recursively find named
+ * attributes from the AttributeRequestSet in the ICompoundProperty and any of its compound child.
+ * The attributes are added to the CachedData's attribute list. For each attribute we will try to
+ * deduplicate data across consecutive frames. */
+void read_attributes(AlembicProcedural *proc,
+ CachedData &cache,
+ const ICompoundProperty &arb_geom_params,
+ const IV2fGeomParam &default_uvs_param,
+ const AttributeRequestSet &requested_attributes,
+ Progress &progress)
+{
+ if (default_uvs_param.valid()) {
+ /* Only the default UVs should be treated as the standard UV attribute. */
+ read_attribute_loop(proc, cache, default_uvs_param, process_uvs, progress, ATTR_STD_UV);
+ }
+
+ vector<PropHeaderAndParent> requested_properties = parse_requested_attributes(
+ requested_attributes, arb_geom_params);
+
+ for (const PropHeaderAndParent &prop_and_parent : requested_properties) {
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ const PropertyHeader *prop = prop_and_parent.prop;
+ const ICompoundProperty &parent = prop_and_parent.parent;
+
+ if (IBoolGeomParam::matches(*prop)) {
+ const IBoolGeomParam &param = IBoolGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<BooleanTPTraits>, progress);
+ }
+ else if (IInt32GeomParam::matches(*prop)) {
+ const IInt32GeomParam &param = IInt32GeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<Int32TPTraits>, progress);
+ }
+ else if (IFloatGeomParam::matches(*prop)) {
+ const IFloatGeomParam &param = IFloatGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<Float32TPTraits>, progress);
+ }
+ else if (IV2fGeomParam::matches(*prop)) {
+ const IV2fGeomParam &param = IV2fGeomParam(parent, prop->getName());
+ if (Alembic::AbcGeom::isUV(*prop)) {
+ read_attribute_loop(proc, cache, param, process_uvs, progress);
+ }
+ else {
+ read_attribute_loop(proc, cache, param, process_attribute<V2fTPTraits>, progress);
+ }
+ }
+ else if (IV3fGeomParam::matches(*prop)) {
+ const IV3fGeomParam &param = IV3fGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<V3fTPTraits>, progress);
+ }
+ else if (IN3fGeomParam::matches(*prop)) {
+ const IN3fGeomParam &param = IN3fGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<N3fTPTraits>, progress);
+ }
+ else if (IC3fGeomParam::matches(*prop)) {
+ const IC3fGeomParam &param = IC3fGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<C3fTPTraits>, progress);
+ }
+ else if (IC4fGeomParam::matches(*prop)) {
+ const IC4fGeomParam &param = IC4fGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<C4fTPTraits>, progress);
+ }
+ }
+
+ cache.invalidate_last_loaded_time(true);
+}
+
+CCL_NAMESPACE_END
+
+#endif
diff --git a/intern/cycles/render/alembic_read.h b/intern/cycles/render/alembic_read.h
new file mode 100644
index 00000000000..9cc8622a1ba
--- /dev/null
+++ b/intern/cycles/render/alembic_read.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2021 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifdef WITH_ALEMBIC
+
+# include <Alembic/AbcCoreFactory/All.h>
+# include <Alembic/AbcGeom/All.h>
+
+# include "util/util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+class AlembicProcedural;
+class AttributeRequestSet;
+class Progress;
+struct CachedData;
+
+/* Maps a FaceSet whose name matches that of a Shader to the index of said shader in the Geometry's
+ * used_shaders list. */
+struct FaceSetShaderIndexPair {
+ Alembic::AbcGeom::IFaceSet face_set;
+ int shader_index;
+};
+
+/* Data of an IPolyMeshSchema that we need to read. */
+struct PolyMeshSchemaData {
+ Alembic::AbcGeom::TimeSamplingPtr time_sampling;
+ size_t num_samples;
+ Alembic::AbcGeom::MeshTopologyVariance topology_variance;
+
+ Alembic::AbcGeom::IP3fArrayProperty positions;
+ Alembic::AbcGeom::IInt32ArrayProperty face_indices;
+ Alembic::AbcGeom::IInt32ArrayProperty face_counts;
+
+ Alembic::AbcGeom::IN3fGeomParam normals;
+
+ vector<FaceSetShaderIndexPair> shader_face_sets;
+
+ // Unsupported for now.
+ Alembic::AbcGeom::IV3fArrayProperty velocities;
+};
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const PolyMeshSchemaData &data,
+ Progress &progress);
+
+/* Data of an ISubDSchema that we need to read. */
+struct SubDSchemaData {
+ Alembic::AbcGeom::TimeSamplingPtr time_sampling;
+ size_t num_samples;
+ Alembic::AbcGeom::MeshTopologyVariance topology_variance;
+
+ Alembic::AbcGeom::IInt32ArrayProperty face_counts;
+ Alembic::AbcGeom::IInt32ArrayProperty face_indices;
+ Alembic::AbcGeom::IP3fArrayProperty positions;
+
+ Alembic::AbcGeom::IInt32ArrayProperty crease_indices;
+ Alembic::AbcGeom::IInt32ArrayProperty crease_lengths;
+ Alembic::AbcGeom::IFloatArrayProperty crease_sharpnesses;
+
+ vector<FaceSetShaderIndexPair> shader_face_sets;
+
+ // Those are unsupported for now.
+ Alembic::AbcGeom::IInt32ArrayProperty corner_indices;
+ Alembic::AbcGeom::IFloatArrayProperty corner_sharpnesses;
+ Alembic::AbcGeom::IInt32Property face_varying_interpolate_boundary;
+ Alembic::AbcGeom::IInt32Property face_varying_propagate_corners;
+ Alembic::AbcGeom::IInt32Property interpolate_boundary;
+ Alembic::AbcGeom::IInt32ArrayProperty holes;
+ Alembic::AbcGeom::IStringProperty subdivision_scheme;
+ Alembic::AbcGeom::IV3fArrayProperty velocities;
+};
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const SubDSchemaData &data,
+ Progress &progress);
+
+/* Data of a ICurvesSchema that we need to read. */
+struct CurvesSchemaData {
+ Alembic::AbcGeom::TimeSamplingPtr time_sampling;
+ size_t num_samples;
+ Alembic::AbcGeom::MeshTopologyVariance topology_variance;
+
+ Alembic::AbcGeom::IP3fArrayProperty positions;
+
+ Alembic::AbcGeom::IInt32ArrayProperty num_vertices;
+
+ float default_radius;
+ float radius_scale;
+
+ // Those are unsupported for now.
+ Alembic::AbcGeom::IV3fArrayProperty velocities;
+ // if this property is invalid then the weight for every point is 1
+ Alembic::AbcGeom::IFloatArrayProperty position_weights;
+ Alembic::AbcGeom::IN3fGeomParam normals;
+ Alembic::AbcGeom::IFloatGeomParam widths;
+ Alembic::AbcGeom::IUcharArrayProperty orders;
+ Alembic::AbcGeom::IFloatArrayProperty knots;
+
+ // TODO(@kevindietrich): type, basis, wrap
+};
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const CurvesSchemaData &data,
+ Progress &progress);
+
+void read_attributes(AlembicProcedural *proc,
+ CachedData &cache,
+ const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,
+ const Alembic::AbcGeom::IV2fGeomParam &default_uvs_param,
+ const AttributeRequestSet &requested_attributes,
+ Progress &progress);
+
+CCL_NAMESPACE_END
+
+#endif
diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp
index 331d30802f1..bf9d69cb47e 100644
--- a/intern/cycles/render/attribute.cpp
+++ b/intern/cycles/render/attribute.cpp
@@ -383,6 +383,23 @@ AttributeStandard Attribute::name_standard(const char *name)
return ATTR_STD_NONE;
}
+AttrKernelDataType Attribute::kernel_type(const Attribute &attr)
+{
+ if (attr.element == ATTR_ELEMENT_CORNER) {
+ return AttrKernelDataType::UCHAR4;
+ }
+
+ if (attr.type == TypeDesc::TypeFloat) {
+ return AttrKernelDataType::FLOAT;
+ }
+
+ if (attr.type == TypeFloat2) {
+ return AttrKernelDataType::FLOAT2;
+ }
+
+ return AttrKernelDataType::FLOAT3;
+}
+
void Attribute::get_uv_tiles(Geometry *geom,
AttributePrimitive prim,
unordered_set<int> &tiles) const
@@ -417,7 +434,7 @@ void Attribute::get_uv_tiles(Geometry *geom,
/* Attribute Set */
AttributeSet::AttributeSet(Geometry *geometry, AttributePrimitive prim)
- : geometry(geometry), prim(prim)
+ : modified_flag(~0u), geometry(geometry), prim(prim)
{
}
@@ -440,7 +457,7 @@ Attribute *AttributeSet::add(ustring name, TypeDesc type, AttributeElement eleme
Attribute new_attr(name, type, element, geometry, prim);
attributes.emplace_back(std::move(new_attr));
- modified = true;
+ tag_modified(attributes.back());
return &attributes.back();
}
@@ -462,8 +479,7 @@ void AttributeSet::remove(ustring name)
for (it = attributes.begin(); it != attributes.end(); it++) {
if (&*it == attr) {
- modified = true;
- attributes.erase(it);
+ remove(it);
return;
}
}
@@ -608,8 +624,7 @@ void AttributeSet::remove(AttributeStandard std)
for (it = attributes.begin(); it != attributes.end(); it++) {
if (&*it == attr) {
- modified = true;
- attributes.erase(it);
+ remove(it);
return;
}
}
@@ -634,6 +649,12 @@ void AttributeSet::remove(Attribute *attribute)
}
}
+void AttributeSet::remove(list<Attribute>::iterator it)
+{
+ tag_modified(*it);
+ attributes.erase(it);
+}
+
void AttributeSet::resize(bool reserve_only)
{
foreach (Attribute &attr, attributes) {
@@ -674,21 +695,22 @@ void AttributeSet::update(AttributeSet &&new_attributes)
for (it = attributes.begin(); it != attributes.end();) {
if (it->std != ATTR_STD_NONE) {
if (new_attributes.find(it->std) == nullptr) {
- modified = true;
- attributes.erase(it++);
+ remove(it++);
continue;
}
}
else if (it->name != "") {
if (new_attributes.find(it->name) == nullptr) {
- modified = true;
- attributes.erase(it++);
+ remove(it++);
continue;
}
}
it++;
}
+
+ /* If all attributes were replaced, transform is no longer applied. */
+ geometry->transform_applied = false;
}
void AttributeSet::clear_modified()
@@ -696,7 +718,27 @@ void AttributeSet::clear_modified()
foreach (Attribute &attr, attributes) {
attr.modified = false;
}
- modified = false;
+
+ modified_flag = 0;
+}
+
+void AttributeSet::tag_modified(const Attribute &attr)
+{
+ /* Some attributes are not stored in the various kernel attribute arrays
+ * (DeviceScene::attribute_*), so the modified flags are only set if the associated standard
+ * corresponds to an attribute which will be stored in the kernel's attribute arrays. */
+ const bool modifies_device_array = (attr.std != ATTR_STD_FACE_NORMAL &&
+ attr.std != ATTR_STD_VERTEX_NORMAL);
+
+ if (modifies_device_array) {
+ AttrKernelDataType kernel_type = Attribute::kernel_type(attr);
+ modified_flag |= (1u << kernel_type);
+ }
+}
+
+bool AttributeSet::modified(AttrKernelDataType kernel_type) const
+{
+ return (modified_flag & (1u << kernel_type)) != 0;
}
/* AttributeRequest */
diff --git a/intern/cycles/render/attribute.h b/intern/cycles/render/attribute.h
index 18c9e5ab83a..004c267cabc 100644
--- a/intern/cycles/render/attribute.h
+++ b/intern/cycles/render/attribute.h
@@ -39,6 +39,21 @@ class Hair;
class Mesh;
struct Transform;
+/* AttrKernelDataType.
+ *
+ * The data type of the device arrays storing the attribute's data. Those data types are different
+ * than the ones for attributes as some attribute types are stored in the same array, e.g. Point,
+ * Vector, and Transform are all stored as float3 in the kernel.
+ *
+ * The values of this enumeration are also used as flags to detect changes in AttributeSet. */
+
+enum AttrKernelDataType {
+ FLOAT = 0,
+ FLOAT2 = 1,
+ FLOAT3 = 2,
+ UCHAR4 = 3,
+};
+
/* Attribute
*
* Arbitrary data layers on meshes.
@@ -167,6 +182,8 @@ class Attribute {
static const char *standard_name(AttributeStandard std);
static AttributeStandard name_standard(const char *name);
+ static AttrKernelDataType kernel_type(const Attribute &attr);
+
void get_uv_tiles(Geometry *geom, AttributePrimitive prim, unordered_set<int> &tiles) const;
};
@@ -175,11 +192,12 @@ class Attribute {
* Set of attributes on a mesh. */
class AttributeSet {
+ uint32_t modified_flag;
+
public:
Geometry *geometry;
AttributePrimitive prim;
list<Attribute> attributes;
- bool modified = true;
AttributeSet(Geometry *geometry, AttributePrimitive prim);
AttributeSet(AttributeSet &&) = default;
@@ -197,6 +215,8 @@ class AttributeSet {
void remove(Attribute *attribute);
+ void remove(list<Attribute>::iterator it);
+
void resize(bool reserve_only = false);
void clear(bool preserve_voxel_data = false);
@@ -204,7 +224,18 @@ class AttributeSet {
* and remove any attribute not found on the new set from this. */
void update(AttributeSet &&new_attributes);
+ /* Return whether the attributes of the given kernel_type are modified, where "modified" means
+ * that some attributes of the given type were added or removed from this AttributeSet. This does
+ * not mean that the data of the remaining attributes in this AttributeSet were also modified. To
+ * check this, use Attribute.modified. */
+ bool modified(AttrKernelDataType kernel_type) const;
+
void clear_modified();
+
+ private:
+ /* Set the relevant modified flag for the attribute. Only attributes that are stored in device
+ * arrays will be considered for tagging this AttributeSet as modified. */
+ void tag_modified(const Attribute &attr);
};
/* AttributeRequest
diff --git a/intern/cycles/render/background.cpp b/intern/cycles/render/background.cpp
index b3d383afae4..b925e755434 100644
--- a/intern/cycles/render/background.cpp
+++ b/intern/cycles/render/background.cpp
@@ -59,6 +59,7 @@ Background::Background() : Node(get_node_type())
Background::~Background()
{
+ dereference_all_used_nodes();
}
void Background::device_update(Device *device, DeviceScene *dscene, Scene *scene)
@@ -130,6 +131,14 @@ void Background::device_free(Device * /*device*/, DeviceScene * /*dscene*/)
void Background::tag_update(Scene *scene)
{
+ Shader *bg_shader = get_shader(scene);
+ if (bg_shader && bg_shader->is_modified()) {
+ /* Tag as modified to update the KernelBackground visibility information.
+ * We only tag the use_shader socket as modified as it is related to the shader
+ * and to avoid doing unnecessary updates anywhere else. */
+ tag_use_shader_modified();
+ }
+
if (ao_factor_is_modified() || use_ao_is_modified()) {
scene->integrator->tag_update(scene, Integrator::BACKGROUND_AO_MODIFIED);
}
diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp
index 124a41db21e..ce76658acb6 100644
--- a/intern/cycles/render/geometry.cpp
+++ b/intern/cycles/render/geometry.cpp
@@ -46,6 +46,12 @@ CCL_NAMESPACE_BEGIN
/* Geometry */
+PackFlags operator|=(PackFlags &pack_flags, uint32_t value)
+{
+ pack_flags = (PackFlags)((uint32_t)pack_flags | value);
+ return pack_flags;
+}
+
NODE_ABSTRACT_DEFINE(Geometry)
{
NodeType *type = NodeType::add("geometry_base", NULL);
@@ -79,6 +85,7 @@ Geometry::Geometry(const NodeType *node_type, const Type type)
Geometry::~Geometry()
{
+ dereference_all_used_nodes();
delete bvh;
}
@@ -823,10 +830,13 @@ void GeometryManager::device_update_attributes(Device *device,
dscene->attributes_float3.alloc(attr_float3_size);
dscene->attributes_uchar4.alloc(attr_uchar4_size);
- const bool copy_all_data = dscene->attributes_float.need_realloc() ||
- dscene->attributes_float2.need_realloc() ||
- dscene->attributes_float3.need_realloc() ||
- dscene->attributes_uchar4.need_realloc();
+ /* The order of those flags needs to match that of AttrKernelDataType. */
+ const bool attributes_need_realloc[4] = {
+ dscene->attributes_float.need_realloc(),
+ dscene->attributes_float2.need_realloc(),
+ dscene->attributes_float3.need_realloc(),
+ dscene->attributes_uchar4.need_realloc(),
+ };
size_t attr_float_offset = 0;
size_t attr_float2_offset = 0;
@@ -845,7 +855,7 @@ void GeometryManager::device_update_attributes(Device *device,
if (attr) {
/* force a copy if we need to reallocate all the data */
- attr->modified |= copy_all_data;
+ attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)];
}
update_attribute_element_offset(geom,
@@ -868,7 +878,7 @@ void GeometryManager::device_update_attributes(Device *device,
if (subd_attr) {
/* force a copy if we need to reallocate all the data */
- subd_attr->modified |= copy_all_data;
+ subd_attr->modified |= attributes_need_realloc[Attribute::kernel_type(*subd_attr)];
}
update_attribute_element_offset(mesh,
@@ -899,6 +909,10 @@ void GeometryManager::device_update_attributes(Device *device,
foreach (AttributeRequest &req, attributes.requests) {
Attribute *attr = values.find(req);
+ if (attr) {
+ attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)];
+ }
+
update_attribute_element_offset(object->geometry,
dscene->attributes_float,
attr_float_offset,
@@ -934,10 +948,10 @@ void GeometryManager::device_update_attributes(Device *device,
/* copy to device */
progress.set_status("Updating Mesh", "Copying Attributes to device");
- dscene->attributes_float.copy_to_device();
- dscene->attributes_float2.copy_to_device();
- dscene->attributes_float3.copy_to_device();
- dscene->attributes_uchar4.copy_to_device();
+ dscene->attributes_float.copy_to_device_if_modified();
+ dscene->attributes_float2.copy_to_device_if_modified();
+ dscene->attributes_float3.copy_to_device_if_modified();
+ dscene->attributes_uchar4.copy_to_device_if_modified();
if (progress.get_cancel())
return;
@@ -1235,7 +1249,16 @@ void GeometryManager::device_update_bvh(Device *device,
const bool can_refit = scene->bvh != nullptr &&
(bparams.bvh_layout == BVHLayout::BVH_LAYOUT_OPTIX);
- const bool pack_all = scene->bvh == nullptr;
+
+ PackFlags pack_flags = PackFlags::PACK_NONE;
+
+ if (scene->bvh == nullptr) {
+ pack_flags |= PackFlags::PACK_ALL;
+ }
+
+ if (dscene->prim_visibility.is_modified()) {
+ pack_flags |= PackFlags::PACK_VISIBILITY;
+ }
BVH *bvh = scene->bvh;
if (!scene->bvh) {
@@ -1273,10 +1296,14 @@ void GeometryManager::device_update_bvh(Device *device,
pack.root_index = -1;
- if (!pack_all) {
+ if (pack_flags != PackFlags::PACK_ALL) {
/* if we do not need to recreate the BVH, then only the vertices are updated, so we can
* safely retake the memory */
dscene->prim_tri_verts.give_data(pack.prim_tri_verts);
+
+ if ((pack_flags & PackFlags::PACK_VISIBILITY) != 0) {
+ dscene->prim_visibility.give_data(pack.prim_visibility);
+ }
}
else {
/* It is not strictly necessary to skip those resizes we if do not have to repack, as the OS
@@ -1305,13 +1332,21 @@ void GeometryManager::device_update_bvh(Device *device,
// Iterate over scene mesh list instead of objects, since 'optix_prim_offset' was calculated
// based on that list, which may be ordered differently from the object list.
foreach (Geometry *geom, scene->geometry) {
- if (!pack_all && !geom->is_modified()) {
+ /* Make a copy of the pack_flags so the current geometry's flags do not pollute the others'.
+ */
+ PackFlags geom_pack_flags = pack_flags;
+
+ if (geom->is_modified()) {
+ geom_pack_flags |= PackFlags::PACK_VERTICES;
+ }
+
+ if (geom_pack_flags == PACK_NONE) {
continue;
}
const pair<int, uint> &info = geometry_to_object_info[geom];
pool.push(function_bind(
- &Geometry::pack_primitives, geom, &pack, info.first, info.second, pack_all));
+ &Geometry::pack_primitives, geom, &pack, info.first, info.second, geom_pack_flags));
}
pool.wait_work();
}
@@ -1346,7 +1381,7 @@ void GeometryManager::device_update_bvh(Device *device,
dscene->prim_type.steal_data(pack.prim_type);
dscene->prim_type.copy_to_device();
}
- if (pack.prim_visibility.size() && (dscene->prim_visibility.need_realloc() || has_bvh2_layout)) {
+ if (pack.prim_visibility.size() && (dscene->prim_visibility.is_modified() || has_bvh2_layout)) {
dscene->prim_visibility.steal_data(pack.prim_visibility);
dscene->prim_visibility.copy_to_device();
}
@@ -1364,7 +1399,6 @@ void GeometryManager::device_update_bvh(Device *device,
}
dscene->data.bvh.root = pack.root_index;
- dscene->data.bvh.bvh_layout = bparams.bvh_layout;
dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0);
dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions();
/* The scene handle is set in 'CPUDevice::const_copy_to' and 'OptiXDevice::const_copy_to' */
@@ -1404,24 +1438,46 @@ static void update_device_flags_attribute(uint32_t &device_update_flags,
continue;
}
- if (attr.element == ATTR_ELEMENT_CORNER) {
- device_update_flags |= ATTR_UCHAR4_MODIFIED;
- }
- else if (attr.type == TypeDesc::TypeFloat) {
- device_update_flags |= ATTR_FLOAT_MODIFIED;
- }
- else if (attr.type == TypeFloat2) {
- device_update_flags |= ATTR_FLOAT2_MODIFIED;
- }
- else if (attr.type == TypeDesc::TypeMatrix) {
- device_update_flags |= ATTR_FLOAT3_MODIFIED;
- }
- else if (attr.element != ATTR_ELEMENT_VOXEL) {
- device_update_flags |= ATTR_FLOAT3_MODIFIED;
+ AttrKernelDataType kernel_type = Attribute::kernel_type(attr);
+
+ switch (kernel_type) {
+ case AttrKernelDataType::FLOAT: {
+ device_update_flags |= ATTR_FLOAT_MODIFIED;
+ break;
+ }
+ case AttrKernelDataType::FLOAT2: {
+ device_update_flags |= ATTR_FLOAT2_MODIFIED;
+ break;
+ }
+ case AttrKernelDataType::FLOAT3: {
+ device_update_flags |= ATTR_FLOAT3_MODIFIED;
+ break;
+ }
+ case AttrKernelDataType::UCHAR4: {
+ device_update_flags |= ATTR_UCHAR4_MODIFIED;
+ break;
+ }
}
}
}
+static void update_attribute_realloc_flags(uint32_t &device_update_flags,
+ const AttributeSet &attributes)
+{
+ if (attributes.modified(AttrKernelDataType::FLOAT)) {
+ device_update_flags |= ATTR_FLOAT_NEEDS_REALLOC;
+ }
+ if (attributes.modified(AttrKernelDataType::FLOAT2)) {
+ device_update_flags |= ATTR_FLOAT2_NEEDS_REALLOC;
+ }
+ if (attributes.modified(AttrKernelDataType::FLOAT3)) {
+ device_update_flags |= ATTR_FLOAT3_NEEDS_REALLOC;
+ }
+ if (attributes.modified(AttrKernelDataType::UCHAR4)) {
+ device_update_flags |= ATTR_UCHAR4_NEEDS_REALLOC;
+ }
+}
+
void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Progress &progress)
{
if (!need_update() && !need_flags_update) {
@@ -1444,16 +1500,11 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
foreach (Geometry *geom, scene->geometry) {
geom->has_volume = false;
- if (geom->attributes.modified) {
- device_update_flags |= ATTRS_NEED_REALLOC;
- }
+ update_attribute_realloc_flags(device_update_flags, geom->attributes);
if (geom->is_mesh()) {
Mesh *mesh = static_cast<Mesh *>(geom);
-
- if (mesh->subd_attributes.modified) {
- device_update_flags |= ATTRS_NEED_REALLOC;
- }
+ update_attribute_realloc_flags(device_update_flags, mesh->subd_attributes);
}
foreach (Node *node, geom->get_used_shaders()) {
@@ -1584,7 +1635,6 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
dscene->tri_vnormal.tag_realloc();
dscene->tri_vindex.tag_realloc();
dscene->tri_patch.tag_realloc();
- dscene->tri_vnormal.tag_realloc();
dscene->tri_patch_uv.tag_realloc();
dscene->tri_shader.tag_realloc();
dscene->patches.tag_realloc();
@@ -1596,6 +1646,10 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
}
}
+ if ((update_flags & VISIBILITY_MODIFIED) != 0) {
+ dscene->prim_visibility.tag_modified();
+ }
+
if (device_update_flags & ATTR_FLOAT_NEEDS_REALLOC) {
dscene->attributes_map.tag_realloc();
dscene->attributes_float.tag_realloc();
@@ -1922,7 +1976,8 @@ void GeometryManager::device_update(Device *device,
* Also update the BVH if the transformations change, we cannot rely on tagging the Geometry
* as modified in this case, as we may accumulate displacement if the vertices do not also
* change. */
- bool need_update_scene_bvh = (scene->bvh == nullptr || (update_flags & TRANSFORM_MODIFIED) != 0);
+ bool need_update_scene_bvh = (scene->bvh == nullptr ||
+ (update_flags & (TRANSFORM_MODIFIED | VISIBILITY_MODIFIED)) != 0);
{
scoped_callback_timer timer([scene](double time) {
if (scene->update_stats) {
@@ -1985,6 +2040,11 @@ void GeometryManager::device_update(Device *device,
}
}
+ /* Always set BVH layout again after displacement where it was set to none,
+ * to avoid ray-tracing at that stage. */
+ dscene->data.bvh.bvh_layout = BVHParams::best_bvh_layout(scene->params.bvh_layout,
+ device->get_bvh_layout_mask());
+
{
scoped_callback_timer timer([scene](double time) {
if (scene->update_stats) {
@@ -2003,7 +2063,7 @@ void GeometryManager::device_update(Device *device,
* for meshes with correct bounding boxes.
*
* This wouldn't cause wrong results, just true
- * displacement might be less optimal ot calculate.
+ * displacement might be less optimal to calculate.
*/
scene->object_manager->need_flags_update = old_need_object_flags_update;
}
diff --git a/intern/cycles/render/geometry.h b/intern/cycles/render/geometry.h
index abdd851a089..7db122f69cb 100644
--- a/intern/cycles/render/geometry.h
+++ b/intern/cycles/render/geometry.h
@@ -43,6 +43,24 @@ class Shader;
class Volume;
struct PackedBVH;
+/* Flags used to determine which geometry data need to be packed. */
+enum PackFlags : uint32_t {
+ PACK_NONE = 0u,
+
+ /* Pack the geometry information (e.g. triangle or curve keys indices). */
+ PACK_GEOMETRY = (1u << 0),
+
+ /* Pack the vertices, for Meshes and Volumes' bounding meshes. */
+ PACK_VERTICES = (1u << 1),
+
+ /* Pack the visibility flags for each triangle or curve. */
+ PACK_VISIBILITY = (1u << 2),
+
+ PACK_ALL = (PACK_GEOMETRY | PACK_VERTICES | PACK_VISIBILITY),
+};
+
+PackFlags operator|=(PackFlags &pack_flags, uint32_t value);
+
/* Geometry
*
* Base class for geometric types like Mesh and Hair. */
@@ -126,7 +144,10 @@ class Geometry : public Node {
int n,
int total);
- virtual void pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all) = 0;
+ virtual void pack_primitives(PackedBVH *pack,
+ int object,
+ uint visibility,
+ PackFlags pack_flags) = 0;
/* Check whether the geometry should have own BVH built separately. Briefly,
* own BVH is needed for geometry, if:
@@ -191,6 +212,8 @@ class GeometryManager {
TRANSFORM_MODIFIED = (1 << 10),
+ VISIBILITY_MODIFIED = (1 << 11),
+
/* tag everything in the manager for an update */
UPDATE_ALL = ~0u,
diff --git a/intern/cycles/render/hair.cpp b/intern/cycles/render/hair.cpp
index dad235aa340..72fc612c0c0 100644
--- a/intern/cycles/render/hair.cpp
+++ b/intern/cycles/render/hair.cpp
@@ -494,38 +494,47 @@ void Hair::pack_curves(Scene *scene,
}
}
-void Hair::pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all)
+void Hair::pack_primitives(PackedBVH *pack, int object, uint visibility, PackFlags pack_flags)
{
if (curve_first_key.empty())
return;
- /* If the BVH does not have to be recreated, we can bail out. */
- if (!pack_all) {
- return;
+ /* Separate loop as other arrays are not initialized if their packing is not required. */
+ if ((pack_flags & PACK_VISIBILITY) != 0) {
+ unsigned int *prim_visibility = &pack->prim_visibility[optix_prim_offset];
+
+ size_t index = 0;
+ for (size_t j = 0; j < num_curves(); ++j) {
+ Curve curve = get_curve(j);
+ for (size_t k = 0; k < curve.num_segments(); ++k, ++index) {
+ prim_visibility[index] = visibility;
+ }
+ }
}
- unsigned int *prim_tri_index = &pack->prim_tri_index[optix_prim_offset];
- int *prim_type = &pack->prim_type[optix_prim_offset];
- unsigned int *prim_visibility = &pack->prim_visibility[optix_prim_offset];
- int *prim_index = &pack->prim_index[optix_prim_offset];
- int *prim_object = &pack->prim_object[optix_prim_offset];
- // 'pack->prim_time' is unused by Embree and OptiX
-
- uint type = has_motion_blur() ?
- ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
- PRIMITIVE_MOTION_CURVE_THICK) :
- ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK);
-
- size_t index = 0;
- for (size_t j = 0; j < num_curves(); ++j) {
- Curve curve = get_curve(j);
- for (size_t k = 0; k < curve.num_segments(); ++k, ++index) {
- prim_tri_index[index] = -1;
- prim_type[index] = PRIMITIVE_PACK_SEGMENT(type, k);
- prim_visibility[index] = visibility;
- // Each curve segment points back to its curve index
- prim_index[index] = j + prim_offset;
- prim_object[index] = object;
+ if ((pack_flags & PACK_GEOMETRY) != 0) {
+ unsigned int *prim_tri_index = &pack->prim_tri_index[optix_prim_offset];
+ int *prim_type = &pack->prim_type[optix_prim_offset];
+ int *prim_index = &pack->prim_index[optix_prim_offset];
+ int *prim_object = &pack->prim_object[optix_prim_offset];
+ // 'pack->prim_time' is unused by Embree and OptiX
+
+ uint type = has_motion_blur() ?
+ ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
+ PRIMITIVE_MOTION_CURVE_THICK) :
+ ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON :
+ PRIMITIVE_CURVE_THICK);
+
+ size_t index = 0;
+ for (size_t j = 0; j < num_curves(); ++j) {
+ Curve curve = get_curve(j);
+ for (size_t k = 0; k < curve.num_segments(); ++k, ++index) {
+ prim_tri_index[index] = -1;
+ prim_type[index] = PRIMITIVE_PACK_SEGMENT(type, k);
+ // Each curve segment points back to its curve index
+ prim_index[index] = j + prim_offset;
+ prim_object[index] = object;
+ }
}
}
}
diff --git a/intern/cycles/render/hair.h b/intern/cycles/render/hair.h
index 4b949f984e5..1a8f422e8c4 100644
--- a/intern/cycles/render/hair.h
+++ b/intern/cycles/render/hair.h
@@ -146,7 +146,10 @@ class Hair : public Geometry {
/* BVH */
void pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, size_t curvekey_offset);
- void pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all) override;
+ void pack_primitives(PackedBVH *pack,
+ int object,
+ uint visibility,
+ PackFlags pack_flags) override;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp
index 29a95beaf7e..27f9b7df1dd 100644
--- a/intern/cycles/render/image.cpp
+++ b/intern/cycles/render/image.cpp
@@ -303,7 +303,8 @@ ImageManager::ImageManager(const DeviceInfo &info)
animation_frame = 0;
/* Set image limits */
- has_half_images = info.has_half_images;
+ features.has_half_float = info.has_half_images;
+ features.has_nanovdb = info.has_nanovdb;
}
ImageManager::~ImageManager()
@@ -347,7 +348,7 @@ void ImageManager::load_image_metadata(Image *img)
metadata = ImageMetaData();
metadata.colorspace = img->params.colorspace;
- if (img->loader->load_metadata(metadata)) {
+ if (img->loader->load_metadata(features, metadata)) {
assert(metadata.type != IMAGE_DATA_NUM_TYPES);
}
else {
@@ -356,15 +357,10 @@ void ImageManager::load_image_metadata(Image *img)
metadata.detect_colorspace();
- /* No half textures on OpenCL, use full float instead. */
- if (!has_half_images) {
- if (metadata.type == IMAGE_DATA_TYPE_HALF4) {
- metadata.type = IMAGE_DATA_TYPE_FLOAT4;
- }
- else if (metadata.type == IMAGE_DATA_TYPE_HALF) {
- metadata.type = IMAGE_DATA_TYPE_FLOAT;
- }
- }
+ assert(features.has_half_float ||
+ (metadata.type != IMAGE_DATA_TYPE_HALF4 && metadata.type != IMAGE_DATA_TYPE_HALF));
+ assert(features.has_nanovdb || (metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT ||
+ metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3));
img->need_metadata = false;
}
diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h
index c802521db56..dede9513d5f 100644
--- a/intern/cycles/render/image.h
+++ b/intern/cycles/render/image.h
@@ -97,6 +97,13 @@ class ImageMetaData {
void detect_colorspace();
};
+/* Information about supported features that Image loaders can use. */
+class ImageDeviceFeatures {
+ public:
+ bool has_half_float;
+ bool has_nanovdb;
+};
+
/* Image loader base class, that can be subclassed to load image data
* from custom sources (file, memory, procedurally generated, etc). */
class ImageLoader {
@@ -105,7 +112,7 @@ class ImageLoader {
virtual ~ImageLoader(){};
/* Load metadata without actual image yet, should be fast. */
- virtual bool load_metadata(ImageMetaData &metadata) = 0;
+ virtual bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) = 0;
/* Load actual image contents. */
virtual bool load_pixels(const ImageMetaData &metadata,
@@ -212,7 +219,8 @@ class ImageManager {
private:
bool need_update_;
- bool has_half_images;
+
+ ImageDeviceFeatures features;
thread_mutex device_mutex;
thread_mutex images_mutex;
diff --git a/intern/cycles/render/image_oiio.cpp b/intern/cycles/render/image_oiio.cpp
index e9c87461822..4867efe6ac0 100644
--- a/intern/cycles/render/image_oiio.cpp
+++ b/intern/cycles/render/image_oiio.cpp
@@ -30,7 +30,7 @@ OIIOImageLoader::~OIIOImageLoader()
{
}
-bool OIIOImageLoader::load_metadata(ImageMetaData &metadata)
+bool OIIOImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata)
{
/* Perform preliminary checks, with meaningful logging. */
if (!path_exists(filepath.string())) {
@@ -76,7 +76,7 @@ bool OIIOImageLoader::load_metadata(ImageMetaData &metadata)
}
/* check if it's half float */
- if (spec.format == TypeDesc::HALF) {
+ if (spec.format == TypeDesc::HALF && features.has_half_float) {
is_half = true;
}
diff --git a/intern/cycles/render/image_oiio.h b/intern/cycles/render/image_oiio.h
index a234b968557..a6dbb168b65 100644
--- a/intern/cycles/render/image_oiio.h
+++ b/intern/cycles/render/image_oiio.h
@@ -26,7 +26,7 @@ class OIIOImageLoader : public ImageLoader {
OIIOImageLoader(const string &filepath);
~OIIOImageLoader();
- bool load_metadata(ImageMetaData &metadata) override;
+ bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override;
bool load_pixels(const ImageMetaData &metadata,
void *pixels,
diff --git a/intern/cycles/render/image_sky.cpp b/intern/cycles/render/image_sky.cpp
index 0560907c63e..7f9b85836f8 100644
--- a/intern/cycles/render/image_sky.cpp
+++ b/intern/cycles/render/image_sky.cpp
@@ -40,7 +40,7 @@ SkyLoader::SkyLoader(float sun_elevation,
SkyLoader::~SkyLoader(){};
-bool SkyLoader::load_metadata(ImageMetaData &metadata)
+bool SkyLoader::load_metadata(const ImageDeviceFeatures &, ImageMetaData &metadata)
{
metadata.width = 512;
metadata.height = 128;
diff --git a/intern/cycles/render/image_sky.h b/intern/cycles/render/image_sky.h
index 686f4e5b885..89ff586e7fd 100644
--- a/intern/cycles/render/image_sky.h
+++ b/intern/cycles/render/image_sky.h
@@ -34,7 +34,7 @@ class SkyLoader : public ImageLoader {
float ozone_density);
~SkyLoader();
- bool load_metadata(ImageMetaData &metadata) override;
+ bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override;
bool load_pixels(const ImageMetaData &metadata,
void *pixels,
diff --git a/intern/cycles/render/image_vdb.cpp b/intern/cycles/render/image_vdb.cpp
index 70b3de5a939..fb6394e8917 100644
--- a/intern/cycles/render/image_vdb.cpp
+++ b/intern/cycles/render/image_vdb.cpp
@@ -34,7 +34,7 @@ VDBImageLoader::~VDBImageLoader()
{
}
-bool VDBImageLoader::load_metadata(ImageMetaData &metadata)
+bool VDBImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata)
{
#ifdef WITH_OPENVDB
if (!grid) {
@@ -56,55 +56,71 @@ bool VDBImageLoader::load_metadata(ImageMetaData &metadata)
if (grid->isType<openvdb::FloatGrid>()) {
metadata.channels = 1;
# ifdef WITH_NANOVDB
- nanogrid = nanovdb::openToNanoVDB(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid));
+ if (features.has_nanovdb) {
+ nanogrid = nanovdb::openToNanoVDB(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid));
+ }
# endif
}
else if (grid->isType<openvdb::Vec3fGrid>()) {
metadata.channels = 3;
# ifdef WITH_NANOVDB
- nanogrid = nanovdb::openToNanoVDB(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid));
+ if (features.has_nanovdb) {
+ nanogrid = nanovdb::openToNanoVDB(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid));
+ }
# endif
}
else if (grid->isType<openvdb::BoolGrid>()) {
metadata.channels = 1;
# ifdef WITH_NANOVDB
- nanogrid = nanovdb::openToNanoVDB(
- openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid)));
+ if (features.has_nanovdb) {
+ nanogrid = nanovdb::openToNanoVDB(
+ openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid)));
+ }
# endif
}
else if (grid->isType<openvdb::DoubleGrid>()) {
metadata.channels = 1;
# ifdef WITH_NANOVDB
- nanogrid = nanovdb::openToNanoVDB(
- openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid)));
+ if (features.has_nanovdb) {
+ nanogrid = nanovdb::openToNanoVDB(
+ openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid)));
+ }
# endif
}
else if (grid->isType<openvdb::Int32Grid>()) {
metadata.channels = 1;
# ifdef WITH_NANOVDB
- nanogrid = nanovdb::openToNanoVDB(
- openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid)));
+ if (features.has_nanovdb) {
+ nanogrid = nanovdb::openToNanoVDB(
+ openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid)));
+ }
# endif
}
else if (grid->isType<openvdb::Int64Grid>()) {
metadata.channels = 1;
# ifdef WITH_NANOVDB
- nanogrid = nanovdb::openToNanoVDB(
- openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid)));
+ if (features.has_nanovdb) {
+ nanogrid = nanovdb::openToNanoVDB(
+ openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid)));
+ }
# endif
}
else if (grid->isType<openvdb::Vec3IGrid>()) {
metadata.channels = 3;
# ifdef WITH_NANOVDB
- nanogrid = nanovdb::openToNanoVDB(
- openvdb::Vec3fGrid(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid)));
+ if (features.has_nanovdb) {
+ nanogrid = nanovdb::openToNanoVDB(
+ openvdb::Vec3fGrid(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid)));
+ }
# endif
}
else if (grid->isType<openvdb::Vec3dGrid>()) {
metadata.channels = 3;
# ifdef WITH_NANOVDB
- nanogrid = nanovdb::openToNanoVDB(
- openvdb::Vec3fGrid(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid)));
+ if (features.has_nanovdb) {
+ nanogrid = nanovdb::openToNanoVDB(
+ openvdb::Vec3fGrid(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid)));
+ }
# endif
}
else if (grid->isType<openvdb::MaskGrid>()) {
@@ -118,21 +134,25 @@ bool VDBImageLoader::load_metadata(ImageMetaData &metadata)
}
# ifdef WITH_NANOVDB
- metadata.byte_size = nanogrid.size();
- if (metadata.channels == 1) {
- metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT;
- }
- else {
- metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT3;
- }
-# else
- if (metadata.channels == 1) {
- metadata.type = IMAGE_DATA_TYPE_FLOAT;
- }
- else {
- metadata.type = IMAGE_DATA_TYPE_FLOAT4;
+ if (nanogrid) {
+ metadata.byte_size = nanogrid.size();
+ if (metadata.channels == 1) {
+ metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT;
+ }
+ else {
+ metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT3;
+ }
}
+ else
# endif
+ {
+ if (metadata.channels == 1) {
+ metadata.type = IMAGE_DATA_TYPE_FLOAT;
+ }
+ else {
+ metadata.type = IMAGE_DATA_TYPE_FLOAT4;
+ }
+ }
/* Set transform from object space to voxel index. */
openvdb::math::Mat4f grid_matrix = grid->transform().baseMap()->getAffineMap()->getMat4();
@@ -143,20 +163,29 @@ bool VDBImageLoader::load_metadata(ImageMetaData &metadata)
}
}
+ Transform texture_to_index;
# ifdef WITH_NANOVDB
- Transform texture_to_index = transform_identity();
-# else
- openvdb::Coord min = bbox.min();
- Transform texture_to_index = transform_translate(min.x(), min.y(), min.z()) *
- transform_scale(dim.x(), dim.y(), dim.z());
+ if (nanogrid) {
+ texture_to_index = transform_identity();
+ }
+ else
# endif
+ {
+ openvdb::Coord min = bbox.min();
+ texture_to_index = transform_translate(min.x(), min.y(), min.z()) *
+ transform_scale(dim.x(), dim.y(), dim.z());
+ }
metadata.transform_3d = transform_inverse(index_to_object * texture_to_index);
metadata.use_transform_3d = true;
+# ifndef WITH_NANOVDB
+ (void)features;
+# endif
return true;
#else
(void)metadata;
+ (void)features;
return false;
#endif
}
@@ -165,48 +194,52 @@ bool VDBImageLoader::load_pixels(const ImageMetaData &, void *pixels, const size
{
#ifdef WITH_OPENVDB
# ifdef WITH_NANOVDB
- memcpy(pixels, nanogrid.data(), nanogrid.size());
-# else
- if (grid->isType<openvdb::FloatGrid>()) {
- openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid), dense);
- }
- else if (grid->isType<openvdb::Vec3fGrid>()) {
- openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
- bbox, (openvdb::Vec3f *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid), dense);
- }
- else if (grid->isType<openvdb::BoolGrid>()) {
- openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid), dense);
- }
- else if (grid->isType<openvdb::DoubleGrid>()) {
- openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid), dense);
- }
- else if (grid->isType<openvdb::Int32Grid>()) {
- openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid), dense);
- }
- else if (grid->isType<openvdb::Int64Grid>()) {
- openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid), dense);
- }
- else if (grid->isType<openvdb::Vec3IGrid>()) {
- openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
- bbox, (openvdb::Vec3f *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid), dense);
- }
- else if (grid->isType<openvdb::Vec3dGrid>()) {
- openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
- bbox, (openvdb::Vec3f *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid), dense);
- }
- else if (grid->isType<openvdb::MaskGrid>()) {
- openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid), dense);
+ if (nanogrid) {
+ memcpy(pixels, nanogrid.data(), nanogrid.size());
}
+ else
# endif
+ {
+ if (grid->isType<openvdb::FloatGrid>()) {
+ openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid), dense);
+ }
+ else if (grid->isType<openvdb::Vec3fGrid>()) {
+ openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
+ bbox, (openvdb::Vec3f *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid), dense);
+ }
+ else if (grid->isType<openvdb::BoolGrid>()) {
+ openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid), dense);
+ }
+ else if (grid->isType<openvdb::DoubleGrid>()) {
+ openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid), dense);
+ }
+ else if (grid->isType<openvdb::Int32Grid>()) {
+ openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid), dense);
+ }
+ else if (grid->isType<openvdb::Int64Grid>()) {
+ openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid), dense);
+ }
+ else if (grid->isType<openvdb::Vec3IGrid>()) {
+ openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
+ bbox, (openvdb::Vec3f *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid), dense);
+ }
+ else if (grid->isType<openvdb::Vec3dGrid>()) {
+ openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
+ bbox, (openvdb::Vec3f *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid), dense);
+ }
+ else if (grid->isType<openvdb::MaskGrid>()) {
+ openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid), dense);
+ }
+ }
return true;
#else
(void)pixels;
diff --git a/intern/cycles/render/image_vdb.h b/intern/cycles/render/image_vdb.h
index 71d10cc39f5..763196f2a15 100644
--- a/intern/cycles/render/image_vdb.h
+++ b/intern/cycles/render/image_vdb.h
@@ -33,7 +33,8 @@ class VDBImageLoader : public ImageLoader {
VDBImageLoader(const string &grid_name);
~VDBImageLoader();
- virtual bool load_metadata(ImageMetaData &metadata) override;
+ virtual bool load_metadata(const ImageDeviceFeatures &features,
+ ImageMetaData &metadata) override;
virtual bool load_pixels(const ImageMetaData &metadata,
void *pixels,
diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp
index 72450e2c546..5290d68e75a 100644
--- a/intern/cycles/render/light.cpp
+++ b/intern/cycles/render/light.cpp
@@ -129,6 +129,7 @@ NODE_DEFINE(Light)
SOCKET_VECTOR(axisv, "Axis V", zero_float3());
SOCKET_FLOAT(sizev, "Size V", 1.0f);
SOCKET_BOOLEAN(round, "Round", false);
+ SOCKET_FLOAT(spread, "Spread", M_PI_F);
SOCKET_INT(map_resolution, "Map Resolution", 0);
@@ -158,6 +159,7 @@ NODE_DEFINE(Light)
Light::Light() : Node(get_node_type())
{
+ dereference_all_used_nodes();
}
void Light::tag_update(Scene *scene)
@@ -858,6 +860,15 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc
float invarea = (area != 0.0f) ? 1.0f / area : 1.0f;
float3 dir = light->dir;
+ /* Convert from spread angle 0..180 to 90..0, clamping to a minimum
+ * angle to avoid excessive noise. */
+ const float min_spread_angle = 1.0f * M_PI_F / 180.0f;
+ const float spread_angle = 0.5f * (M_PI_F - max(light->spread, min_spread_angle));
+ /* Normalization computed using:
+ * integrate cos(x) * (1 - tan(x) * tan(a)) * sin(x) from x = 0 to pi/2 - a. */
+ const float tan_spread = tanf(spread_angle);
+ const float normalize_spread = 2.0f / (2.0f + (2.0f * spread_angle - M_PI_F) * tan_spread);
+
dir = safe_normalize(dir);
if (light->use_mis && area != 0.0f)
@@ -877,6 +888,8 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc
klights[light_index].area.dir[0] = dir.x;
klights[light_index].area.dir[1] = dir.y;
klights[light_index].area.dir[2] = dir.z;
+ klights[light_index].area.tan_spread = tan_spread;
+ klights[light_index].area.normalize_spread = normalize_spread;
}
else if (light->light_type == LIGHT_SPOT) {
shader_id &= ~SHADER_AREA_LIGHT;
diff --git a/intern/cycles/render/light.h b/intern/cycles/render/light.h
index 39014b5d667..fbd709125ff 100644
--- a/intern/cycles/render/light.h
+++ b/intern/cycles/render/light.h
@@ -58,6 +58,7 @@ class Light : public Node {
NODE_SOCKET_API(float3, axisv)
NODE_SOCKET_API(float, sizev)
NODE_SOCKET_API(bool, round)
+ NODE_SOCKET_API(float, spread)
NODE_SOCKET_API(Transform, tfm)
diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp
index d5e5b960665..fd9879dd5dd 100644
--- a/intern/cycles/render/mesh.cpp
+++ b/intern/cycles/render/mesh.cpp
@@ -805,7 +805,7 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui
}
}
-void Mesh::pack_primitives(ccl::PackedBVH *pack, int object, uint visibility, bool pack_all)
+void Mesh::pack_primitives(ccl::PackedBVH *pack, int object, uint visibility, PackFlags pack_flags)
{
if (triangles.empty())
return;
@@ -819,28 +819,38 @@ void Mesh::pack_primitives(ccl::PackedBVH *pack, int object, uint visibility, bo
uint type = has_motion_blur() ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE;
- if (pack_all) {
+ /* Separate loop as other arrays are not initialized if their packing is not required. */
+ if ((pack_flags & PackFlags::PACK_VISIBILITY) != 0) {
+ unsigned int *prim_visibility = &pack->prim_visibility[optix_prim_offset];
+ for (size_t k = 0; k < num_prims; ++k) {
+ prim_visibility[k] = visibility;
+ }
+ }
+
+ if ((pack_flags & PackFlags::PACK_GEOMETRY) != 0) {
/* Use optix_prim_offset for indexing as those arrays also contain data for Hair geometries. */
unsigned int *prim_tri_index = &pack->prim_tri_index[optix_prim_offset];
int *prim_type = &pack->prim_type[optix_prim_offset];
- unsigned int *prim_visibility = &pack->prim_visibility[optix_prim_offset];
int *prim_index = &pack->prim_index[optix_prim_offset];
int *prim_object = &pack->prim_object[optix_prim_offset];
for (size_t k = 0; k < num_prims; ++k) {
- prim_tri_index[k] = (prim_offset + k) * 3;
- prim_type[k] = type;
- prim_index[k] = prim_offset + k;
- prim_object[k] = object;
- prim_visibility[k] = visibility;
+ if ((pack_flags & PackFlags::PACK_GEOMETRY) != 0) {
+ prim_tri_index[k] = (prim_offset + k) * 3;
+ prim_type[k] = type;
+ prim_index[k] = prim_offset + k;
+ prim_object[k] = object;
+ }
}
}
- for (size_t k = 0; k < num_prims; ++k) {
- const Mesh::Triangle t = get_triangle(k);
- prim_tri_verts[k * 3] = float3_to_float4(verts[t.v[0]]);
- prim_tri_verts[k * 3 + 1] = float3_to_float4(verts[t.v[1]]);
- prim_tri_verts[k * 3 + 2] = float3_to_float4(verts[t.v[2]]);
+ if ((pack_flags & PackFlags::PACK_VERTICES) != 0) {
+ for (size_t k = 0; k < num_prims; ++k) {
+ const Mesh::Triangle t = get_triangle(k);
+ prim_tri_verts[k * 3] = float3_to_float4(verts[t.v[0]]);
+ prim_tri_verts[k * 3 + 1] = float3_to_float4(verts[t.v[1]]);
+ prim_tri_verts[k * 3 + 2] = float3_to_float4(verts[t.v[2]]);
+ }
}
}
diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h
index 2b0ff92ab62..e9e79f7f20d 100644
--- a/intern/cycles/render/mesh.h
+++ b/intern/cycles/render/mesh.h
@@ -232,7 +232,10 @@ class Mesh : public Geometry {
size_t tri_offset);
void pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_offset);
- void pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all) override;
+ void pack_primitives(PackedBVH *pack,
+ int object,
+ uint visibility,
+ PackFlags pack_flags) override;
void tessellate(DiagSplit *split);
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp
index f3d420c6fcb..5792d2458d1 100644
--- a/intern/cycles/render/nodes.cpp
+++ b/intern/cycles/render/nodes.cpp
@@ -6093,6 +6093,7 @@ NODE_DEFINE(VectorMathNode)
type_enum.insert("reflect", NODE_VECTOR_MATH_REFLECT);
type_enum.insert("refract", NODE_VECTOR_MATH_REFRACT);
type_enum.insert("faceforward", NODE_VECTOR_MATH_FACEFORWARD);
+ type_enum.insert("multiply_add", NODE_VECTOR_MATH_MULTIPLY_ADD);
type_enum.insert("dot_product", NODE_VECTOR_MATH_DOT_PRODUCT);
@@ -6165,7 +6166,8 @@ void VectorMathNode::compile(SVMCompiler &compiler)
int vector_stack_offset = compiler.stack_assign_if_linked(vector_out);
/* 3 Vector Operators */
- if (math_type == NODE_VECTOR_MATH_WRAP || math_type == NODE_VECTOR_MATH_FACEFORWARD) {
+ if (math_type == NODE_VECTOR_MATH_WRAP || math_type == NODE_VECTOR_MATH_FACEFORWARD ||
+ math_type == NODE_VECTOR_MATH_MULTIPLY_ADD) {
ShaderInput *vector3_in = input("Vector3");
int vector3_stack_offset = compiler.stack_assign(vector3_in);
compiler.add_node(
diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h
index fb9cf0c9836..99cb0b779b8 100644
--- a/intern/cycles/render/nodes.h
+++ b/intern/cycles/render/nodes.h
@@ -1600,11 +1600,23 @@ class SetNormalNode : public ShaderNode {
NODE_SOCKET_API(float3, direction)
};
-class OSLNode : public ShaderNode {
+class OSLNode final : public ShaderNode {
public:
static OSLNode *create(ShaderGraph *graph, size_t num_inputs, const OSLNode *from = NULL);
~OSLNode();
+ static void operator delete(void *ptr)
+ {
+ /* Override delete operator to silence new-delete-type-mismatch ASAN warnings
+ * regarding size mismatch in the destructor. This is intentional as we allocate
+ * extra space at the end of the node. */
+ ::operator delete(ptr);
+ }
+ static void operator delete(void *, void *)
+ {
+ /* Deliberately empty placement delete operator, to avoid MSVC warning C4291. */
+ }
+
ShaderNode *clone(ShaderGraph *graph) const;
char *input_default_value();
diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp
index 52f63685aeb..f65f8bc6e90 100644
--- a/intern/cycles/render/object.cpp
+++ b/intern/cycles/render/object.cpp
@@ -153,10 +153,6 @@ void Object::update_motion()
void Object::compute_bounds(bool motion_blur)
{
- if (!is_modified() && !geometry->is_modified()) {
- return;
- }
-
BoundBox mbounds = geometry->bounds;
if (motion_blur && use_motion()) {
@@ -224,6 +220,10 @@ void Object::tag_update(Scene *scene)
flag |= ObjectManager::TRANSFORM_MODIFIED;
}
+ if (visibility_is_modified()) {
+ flag |= ObjectManager::VISIBILITY_MODIFIED;
+ }
+
foreach (Node *node, geometry->get_used_shaders()) {
Shader *shader = static_cast<Shader *>(node);
if (shader->get_use_mis() && shader->has_surface_emission)
@@ -918,6 +918,10 @@ void ObjectManager::tag_update(Scene *scene, uint32_t flag)
geometry_flag |= GeometryManager::TRANSFORM_MODIFIED;
}
+ if ((flag & VISIBILITY_MODIFIED) != 0) {
+ geometry_flag |= GeometryManager::VISIBILITY_MODIFIED;
+ }
+
scene->geometry_manager->tag_update(scene, geometry_flag);
}
diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h
index 23682270fd1..e4bc7ac3d8e 100644
--- a/intern/cycles/render/object.h
+++ b/intern/cycles/render/object.h
@@ -134,6 +134,7 @@ class ObjectManager {
OBJECT_MODIFIED = (1 << 5),
HOLDOUT_MODIFIED = (1 << 6),
TRANSFORM_MODIFIED = (1 << 7),
+ VISIBILITY_MODIFIED = (1 << 8),
/* tag everything in the manager for an update */
UPDATE_ALL = ~0u,
diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp
index 53c67049571..9bd6e3a5e2d 100644
--- a/intern/cycles/render/osl.cpp
+++ b/intern/cycles/render/osl.cpp
@@ -91,10 +91,10 @@ void OSLShaderManager::reset(Scene * /*scene*/)
shading_system_init();
}
-void OSLShaderManager::device_update(Device *device,
- DeviceScene *dscene,
- Scene *scene,
- Progress &progress)
+void OSLShaderManager::device_update_specific(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress)
{
if (!need_update())
return;
@@ -1149,7 +1149,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
shader->has_integrator_dependency = false;
/* generate surface shader */
- if (shader->used && graph && output->input("Surface")->link) {
+ if (shader->reference_count() && graph && output->input("Surface")->link) {
shader->osl_surface_ref = compile_type(shader, shader->graph, SHADER_TYPE_SURFACE);
if (has_bump)
@@ -1165,7 +1165,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
}
/* generate volume shader */
- if (shader->used && graph && output->input("Volume")->link) {
+ if (shader->reference_count() && graph && output->input("Volume")->link) {
shader->osl_volume_ref = compile_type(shader, shader->graph, SHADER_TYPE_VOLUME);
shader->has_volume = true;
}
@@ -1173,7 +1173,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
shader->osl_volume_ref = OSL::ShaderGroupRef();
/* generate displacement shader */
- if (shader->used && graph && output->input("Displacement")->link) {
+ if (shader->reference_count() && graph && output->input("Displacement")->link) {
shader->osl_displacement_ref = compile_type(shader, shader->graph, SHADER_TYPE_DISPLACEMENT);
shader->has_displacement = true;
}
diff --git a/intern/cycles/render/osl.h b/intern/cycles/render/osl.h
index ea2d0ca492c..f6aa98d867a 100644
--- a/intern/cycles/render/osl.h
+++ b/intern/cycles/render/osl.h
@@ -72,15 +72,18 @@ class OSLShaderManager : public ShaderManager {
static void free_memory();
- void reset(Scene *scene);
+ void reset(Scene *scene) override;
- bool use_osl()
+ bool use_osl() override
{
return true;
}
- void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
- void device_free(Device *device, DeviceScene *dscene, Scene *scene);
+ void device_update_specific(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress) override;
+ void device_free(Device *device, DeviceScene *dscene, Scene *scene) override;
/* osl compile and query */
static bool osl_compile(const string &inputfile, const string &outputfile);
diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp
index d3dfe1c5be6..c4e7d2c79d6 100644
--- a/intern/cycles/render/scene.cpp
+++ b/intern/cycles/render/scene.cpp
@@ -143,21 +143,27 @@ void Scene::free_memory(bool final)
delete bvh;
bvh = NULL;
- foreach (Shader *s, shaders)
- delete s;
- /* delete procedurals before other types as they may hold pointers to those types */
+ /* The order of deletion is important to make sure data is freed based on possible dependencies
+ * as the Nodes' reference counts are decremented in the destructors:
+ *
+ * - Procedurals can create and hold pointers to any other types.
+ * - Objects can hold pointers to Geometries and ParticleSystems
+ * - Lights and Geometries can hold pointers to Shaders.
+ *
+ * Similarly, we first delete all nodes and their associated device data, and then the managers
+ * and their associated device data.
+ */
foreach (Procedural *p, procedurals)
delete p;
- foreach (Geometry *g, geometry)
- delete g;
foreach (Object *o, objects)
delete o;
- foreach (Light *l, lights)
- delete l;
+ foreach (Geometry *g, geometry)
+ delete g;
foreach (ParticleSystem *p, particle_systems)
delete p;
+ foreach (Light *l, lights)
+ delete l;
- shaders.clear();
geometry.clear();
objects.clear();
lights.clear();
@@ -169,7 +175,25 @@ void Scene::free_memory(bool final)
film->device_free(device, &dscene, this);
background->device_free(device, &dscene);
integrator->device_free(device, &dscene, true);
+ }
+ if (final) {
+ delete camera;
+ delete dicing_camera;
+ delete film;
+ delete background;
+ delete integrator;
+ }
+
+ /* Delete Shaders after every other nodes to ensure that we do not try to decrement the reference
+ * count on some dangling pointer. */
+ foreach (Shader *s, shaders)
+ delete s;
+
+ shaders.clear();
+
+ /* Now that all nodes have been deleted, we can safely delete managers and device data. */
+ if (device) {
object_manager->device_free(device, &dscene, true);
geometry_manager->device_free(device, &dscene, true);
shader_manager->device_free(device, &dscene, this);
@@ -179,7 +203,7 @@ void Scene::free_memory(bool final)
bake_manager->device_free(device, &dscene);
- if (!params.persistent_data || final)
+ if (final)
image_manager->device_free(device);
else
image_manager->device_free_builtin(device);
@@ -189,11 +213,6 @@ void Scene::free_memory(bool final)
if (final) {
delete lookup_tables;
- delete camera;
- delete dicing_camera;
- delete film;
- delete background;
- delete integrator;
delete object_manager;
delete geometry_manager;
delete shader_manager;
@@ -504,9 +523,6 @@ bool Scene::update(Progress &progress, bool &kernel_switch_needed)
{
/* update scene */
if (need_update()) {
- /* Updated used shader tag so we know which features are need for the kernel. */
- shader_manager->update_shaders_used(this);
-
/* Update max_closures. */
KernelIntegrator *kintegrator = &dscene.data.integrator;
if (params.background) {
@@ -526,9 +542,6 @@ bool Scene::update(Progress &progress, bool &kernel_switch_needed)
DeviceKernelStatus kernel_switch_status = device->get_active_kernel_switch_state();
kernel_switch_needed = kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE ||
kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_INVALID;
- if (kernel_switch_status == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) {
- progress.set_kernel_status("Compiling render kernels");
- }
if (new_kernels_needed || kernel_switch_needed) {
progress.set_kernel_status("Compiling render kernels");
device->wait_for_availability(loaded_kernel_features);
@@ -566,9 +579,6 @@ bool Scene::load_kernels(Progress &progress, bool lock_scene)
return false;
}
- progress.add_skip_time(timer, false);
- VLOG(1) << "Total time spent loading kernels: " << time_dt() - timer.get_start();
-
kernels_loaded = true;
loaded_kernel_features = requested_features;
return true;
@@ -587,7 +597,7 @@ int Scene::get_max_closure_count()
int max_closures = 0;
for (int i = 0; i < shaders.size(); i++) {
Shader *shader = shaders[i];
- if (shader->used) {
+ if (shader->reference_count()) {
int num_closures = shader->graph->get_num_closures();
max_closures = max(max_closures, num_closures);
}
@@ -748,9 +758,10 @@ template<> void Scene::delete_node_impl(ParticleSystem *node)
particle_system_manager->tag_update(this);
}
-template<> void Scene::delete_node_impl(Shader * /*node*/)
+template<> void Scene::delete_node_impl(Shader *shader)
{
/* don't delete unused shaders, not supported */
+ shader->clear_reference_count();
}
template<> void Scene::delete_node_impl(Procedural *node)
@@ -817,9 +828,12 @@ template<> void Scene::delete_nodes(const set<ParticleSystem *> &nodes, const No
particle_system_manager->tag_update(this);
}
-template<> void Scene::delete_nodes(const set<Shader *> & /*nodes*/, const NodeOwner * /*owner*/)
+template<> void Scene::delete_nodes(const set<Shader *> &nodes, const NodeOwner * /*owner*/)
{
/* don't delete unused shaders, not supported */
+ for (Shader *shader : nodes) {
+ shader->clear_reference_count();
+ }
}
template<> void Scene::delete_nodes(const set<Procedural *> &nodes, const NodeOwner *owner)
diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h
index 61c753e411c..a6dab06f8f2 100644
--- a/intern/cycles/render/scene.h
+++ b/intern/cycles/render/scene.h
@@ -178,7 +178,6 @@ class SceneParams {
int num_bvh_time_steps;
int hair_subdivisions;
CurveShapeType hair_shape;
- bool persistent_data;
int texture_limit;
bool background;
@@ -193,7 +192,6 @@ class SceneParams {
num_bvh_time_steps = 0;
hair_subdivisions = 3;
hair_shape = CURVE_RIBBON;
- persistent_data = false;
texture_limit = 0;
background = true;
}
@@ -206,7 +204,7 @@ class SceneParams {
use_bvh_unaligned_nodes == params.use_bvh_unaligned_nodes &&
num_bvh_time_steps == params.num_bvh_time_steps &&
hair_subdivisions == params.hair_subdivisions && hair_shape == params.hair_shape &&
- persistent_data == params.persistent_data && texture_limit == params.texture_limit);
+ texture_limit == params.texture_limit);
}
int curve_subdivisions()
diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp
index 3c601e18126..7830ca2293a 100644
--- a/intern/cycles/render/session.cpp
+++ b/intern/cycles/render/session.cpp
@@ -243,11 +243,6 @@ void Session::run_gpu()
}
}
- /* Don't go in pause mode when image was rendered with preview kernels
- * When feature kernels become available the session will be reset. */
- else if (no_tiles && kernel_state == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) {
- time_sleep(0.1);
- }
else if (no_tiles && kernel_state == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE) {
reset_gpu(tile_manager.params, params.samples);
}
@@ -762,11 +757,6 @@ void Session::run_cpu()
}
}
- /* Don't go in pause mode when preview kernels are used
- * When feature kernels become available the session will be reset. */
- else if (no_tiles && kernel_state == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) {
- time_sleep(0.1);
- }
else if (no_tiles && kernel_state == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE) {
reset_cpu(tile_manager.params, params.samples);
}
diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp
index 5ecbd92d96d..59b60904746 100644
--- a/intern/cycles/render/shader.cpp
+++ b/intern/cycles/render/shader.cpp
@@ -16,7 +16,6 @@
#include "device/device.h"
-#include "render/alembic.h"
#include "render/background.h"
#include "render/camera.h"
#include "render/colorspace.h"
@@ -27,6 +26,7 @@
#include "render/nodes.h"
#include "render/object.h"
#include "render/osl.h"
+#include "render/procedural.h"
#include "render/scene.h"
#include "render/shader.h"
#include "render/svm.h"
@@ -218,7 +218,6 @@ Shader::Shader() : Node(get_node_type())
displacement_method = DISPLACE_BUMP;
id = -1;
- used = false;
need_update_uvs = true;
need_update_attribute = true;
@@ -382,8 +381,9 @@ void Shader::tag_used(Scene *scene)
{
/* if an unused shader suddenly gets used somewhere, it needs to be
* recompiled because it was skipped for compilation before */
- if (!used) {
+ if (!reference_count()) {
tag_modified();
+ /* We do not reference here as the shader will be referenced when added to a socket. */
scene->shader_manager->tag_update(scene, ShaderManager::SHADER_MODIFIED);
}
}
@@ -461,52 +461,28 @@ int ShaderManager::get_shader_id(Shader *shader, bool smooth)
return id;
}
-void ShaderManager::update_shaders_used(Scene *scene)
+void ShaderManager::device_update(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress)
{
if (!need_update()) {
return;
}
- /* figure out which shaders are in use, so SVM/OSL can skip compiling them
- * for speed and avoid loading image textures into memory */
uint id = 0;
foreach (Shader *shader, scene->shaders) {
- shader->used = false;
shader->id = id++;
}
- scene->default_surface->used = true;
- scene->default_light->used = true;
- scene->default_background->used = true;
- scene->default_empty->used = true;
+ /* Those shaders should always be compiled as they are used as fallback if a shader cannot be
+ * found, e.g. bad shader index for the triangle shaders on a Mesh. */
+ assert(scene->default_surface->reference_count() != 0);
+ assert(scene->default_light->reference_count() != 0);
+ assert(scene->default_background->reference_count() != 0);
+ assert(scene->default_empty->reference_count() != 0);
- if (scene->background->get_shader())
- scene->background->get_shader()->used = true;
-
-#ifdef WITH_ALEMBIC
- foreach (Procedural *procedural, scene->procedurals) {
- AlembicProcedural *abc_proc = static_cast<AlembicProcedural *>(procedural);
-
- foreach (Node *abc_node, abc_proc->get_objects()) {
- AlembicObject *abc_object = static_cast<AlembicObject *>(abc_node);
-
- foreach (Node *node, abc_object->get_used_shaders()) {
- Shader *shader = static_cast<Shader *>(node);
- shader->used = true;
- }
- }
- }
-#endif
-
- foreach (Geometry *geom, scene->geometry)
- foreach (Node *node, geom->get_used_shaders()) {
- Shader *shader = static_cast<Shader *>(node);
- shader->used = true;
- }
-
- foreach (Light *light, scene->lights)
- if (light->get_shader())
- const_cast<Shader *>(light->get_shader())->used = true;
+ device_update_specific(device, dscene, scene, progress);
}
void ShaderManager::device_update_common(Device *device,
@@ -528,6 +504,8 @@ void ShaderManager::device_update_common(Device *device,
if (shader->get_use_mis())
flag |= SD_USE_MIS;
+ if (shader->has_surface_emission)
+ flag |= SD_HAS_EMISSION;
if (shader->has_surface_transparent && shader->get_use_transparent_shadow())
flag |= SD_HAS_TRANSPARENT_SHADOW;
if (shader->has_volume) {
@@ -637,6 +615,7 @@ void ShaderManager::add_default(Scene *scene)
Shader *shader = scene->create_node<Shader>();
shader->name = "default_surface";
shader->set_graph(graph);
+ shader->reference();
scene->default_surface = shader;
shader->tag_update(scene);
}
@@ -655,6 +634,8 @@ void ShaderManager::add_default(Scene *scene)
shader->set_graph(graph);
scene->default_volume = shader;
shader->tag_update(scene);
+ /* No default reference for the volume to avoid compiling volume kernels if there are no actual
+ * volumes in the scene */
}
/* default light */
@@ -671,6 +652,7 @@ void ShaderManager::add_default(Scene *scene)
Shader *shader = scene->create_node<Shader>();
shader->name = "default_light";
shader->set_graph(graph);
+ shader->reference();
scene->default_light = shader;
shader->tag_update(scene);
}
@@ -682,6 +664,7 @@ void ShaderManager::add_default(Scene *scene)
Shader *shader = scene->create_node<Shader>();
shader->name = "default_background";
shader->set_graph(graph);
+ shader->reference();
scene->default_background = shader;
shader->tag_update(scene);
}
@@ -693,6 +676,7 @@ void ShaderManager::add_default(Scene *scene)
Shader *shader = scene->create_node<Shader>();
shader->name = "default_empty";
shader->set_graph(graph);
+ shader->reference();
scene->default_empty = shader;
shader->tag_update(scene);
}
@@ -733,7 +717,7 @@ void ShaderManager::get_requested_features(Scene *scene,
requested_features->nodes_features = 0;
for (int i = 0; i < scene->shaders.size(); i++) {
Shader *shader = scene->shaders[i];
- if (!shader->used) {
+ if (!shader->reference_count()) {
continue;
}
diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h
index f47d64f346c..50c8bed4669 100644
--- a/intern/cycles/render/shader.h
+++ b/intern/cycles/render/shader.h
@@ -132,7 +132,6 @@ class Shader : public Node {
/* determined before compiling */
uint id;
- bool used;
#ifdef WITH_OSL
/* osl shading state references */
@@ -187,10 +186,11 @@ class ShaderManager {
}
/* device update */
- virtual void device_update(Device *device,
- DeviceScene *dscene,
- Scene *scene,
- Progress &progress) = 0;
+ void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
+ virtual void device_update_specific(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress) = 0;
virtual void device_free(Device *device, DeviceScene *dscene, Scene *scene) = 0;
void device_update_common(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
@@ -208,7 +208,6 @@ class ShaderManager {
static void add_default(Scene *scene);
/* Selective nodes compilation. */
- void update_shaders_used(Scene *scene);
void get_requested_features(Scene *scene, DeviceRequestedFeatures *requested_features);
static void free_memory();
diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp
index fce604234f1..5c793c5c016 100644
--- a/intern/cycles/render/svm.cpp
+++ b/intern/cycles/render/svm.cpp
@@ -69,10 +69,10 @@ void SVMShaderManager::device_update_shader(Scene *scene,
<< summary.full_report();
}
-void SVMShaderManager::device_update(Device *device,
- DeviceScene *dscene,
- Scene *scene,
- Progress &progress)
+void SVMShaderManager::device_update_specific(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress)
{
if (!need_update())
return;
@@ -776,7 +776,7 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
add_node(NODE_ENTER_BUMP_EVAL, bump_state_offset);
}
- if (shader->used) {
+ if (shader->reference_count()) {
CompilerState state(graph);
if (clin->link) {
bool generate = false;
diff --git a/intern/cycles/render/svm.h b/intern/cycles/render/svm.h
index a4ca68e1d8d..d23ff3e2a47 100644
--- a/intern/cycles/render/svm.h
+++ b/intern/cycles/render/svm.h
@@ -44,10 +44,13 @@ class SVMShaderManager : public ShaderManager {
SVMShaderManager();
~SVMShaderManager();
- void reset(Scene *scene);
+ void reset(Scene *scene) override;
- void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
- void device_free(Device *device, DeviceScene *dscene, Scene *scene);
+ void device_update_specific(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress) override;
+ void device_free(Device *device, DeviceScene *dscene, Scene *scene) override;
protected:
void device_update_shader(Scene *scene,