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
path: root/intern
diff options
context:
space:
mode:
Diffstat (limited to 'intern')
-rw-r--r--intern/cycles/blender/addon/__init__.py1
-rw-r--r--intern/cycles/blender/blender_curves.cpp49
-rw-r--r--intern/cycles/blender/blender_device.cpp8
-rw-r--r--intern/cycles/blender/blender_geometry.cpp39
-rw-r--r--intern/cycles/blender/blender_mesh.cpp133
-rw-r--r--intern/cycles/blender/blender_object.cpp10
-rw-r--r--intern/cycles/blender/blender_particles.cpp4
-rw-r--r--intern/cycles/blender/blender_python.cpp15
-rw-r--r--intern/cycles/blender/blender_session.cpp16
-rw-r--r--intern/cycles/blender/blender_shader.cpp159
-rw-r--r--intern/cycles/blender/blender_sync.cpp46
-rw-r--r--intern/cycles/blender/blender_sync.h1
-rw-r--r--intern/cycles/blender/blender_util.h16
-rw-r--r--intern/cycles/blender/blender_volume.cpp8
-rw-r--r--intern/cycles/device/device_memory.h69
-rw-r--r--intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h2
-rw-r--r--intern/cycles/kernel/osl/osl_globals.h2
-rw-r--r--intern/cycles/render/CMakeLists.txt14
-rw-r--r--intern/cycles/render/alembic.cpp1869
-rw-r--r--intern/cycles/render/alembic.h404
-rw-r--r--intern/cycles/render/attribute.cpp68
-rw-r--r--intern/cycles/render/attribute.h10
-rw-r--r--intern/cycles/render/background.cpp5
-rw-r--r--intern/cycles/render/bake.cpp20
-rw-r--r--intern/cycles/render/bake.h5
-rw-r--r--intern/cycles/render/film.cpp8
-rw-r--r--intern/cycles/render/geometry.cpp537
-rw-r--r--intern/cycles/render/geometry.h38
-rw-r--r--intern/cycles/render/hair.cpp33
-rw-r--r--intern/cycles/render/hair.h2
-rw-r--r--intern/cycles/render/image.cpp22
-rw-r--r--intern/cycles/render/image.h5
-rw-r--r--intern/cycles/render/integrator.cpp88
-rw-r--r--intern/cycles/render/integrator.h15
-rw-r--r--intern/cycles/render/light.cpp31
-rw-r--r--intern/cycles/render/light.h23
-rw-r--r--intern/cycles/render/mesh.cpp44
-rw-r--r--intern/cycles/render/mesh.h2
-rw-r--r--intern/cycles/render/object.cpp136
-rw-r--r--intern/cycles/render/object.h28
-rw-r--r--intern/cycles/render/osl.cpp6
-rw-r--r--intern/cycles/render/particles.cpp15
-rw-r--r--intern/cycles/render/particles.h6
-rw-r--r--intern/cycles/render/procedural.cpp89
-rw-r--r--intern/cycles/render/procedural.h73
-rw-r--r--intern/cycles/render/scene.cpp114
-rw-r--r--intern/cycles/render/scene.h13
-rw-r--r--intern/cycles/render/session.cpp8
-rw-r--r--intern/cycles/render/shader.cpp62
-rw-r--r--intern/cycles/render/shader.h23
-rw-r--r--intern/cycles/render/stats.cpp2
-rw-r--r--intern/cycles/render/stats.h1
-rw-r--r--intern/cycles/render/svm.cpp6
-rw-r--r--intern/cycles/render/tables.cpp15
-rw-r--r--intern/cycles/render/tables.h5
-rw-r--r--intern/cycles/util/util_array.h8
-rw-r--r--intern/ghost/intern/GHOST_DropTargetX11.h2
-rw-r--r--intern/guardedalloc/CMakeLists.txt6
-rw-r--r--intern/guardedalloc/MEM_guardedalloc.h6
-rw-r--r--intern/guardedalloc/intern/mmap_win.c294
-rw-r--r--intern/guardedalloc/mmap_win.h50
61 files changed, 3837 insertions, 952 deletions
diff --git a/intern/cycles/blender/addon/__init__.py b/intern/cycles/blender/addon/__init__.py
index 3ab352e52a2..b1deb5cb64e 100644
--- a/intern/cycles/blender/addon/__init__.py
+++ b/intern/cycles/blender/addon/__init__.py
@@ -59,6 +59,7 @@ class CyclesRender(bpy.types.RenderEngine):
bl_use_exclude_layers = True
bl_use_save_buffers = True
bl_use_spherical_stereo = True
+ bl_use_custom_freestyle = True
def __init__(self):
self.session = None
diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp
index 964241e9904..4fb5c7f57d1 100644
--- a/intern/cycles/blender/blender_curves.cpp
+++ b/intern/cycles/blender/blender_curves.cpp
@@ -67,11 +67,10 @@ static bool ObtainCacheParticleData(
Transform tfm = get_transform(b_ob->matrix_world());
Transform itfm = transform_quick_inverse(tfm);
- BL::Object::modifiers_iterator b_mod;
- for (b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
- if ((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) &&
- (background ? b_mod->show_render() : b_mod->show_viewport())) {
- BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
+ for (BL::Modifier &b_mod : b_ob->modifiers) {
+ if ((b_mod.type() == b_mod.type_PARTICLE_SYSTEM) &&
+ (background ? b_mod.show_render() : b_mod.show_viewport())) {
+ BL::ParticleSystemModifier psmd((const PointerRNA)b_mod.ptr);
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
@@ -163,11 +162,10 @@ static bool ObtainCacheParticleUV(Hair *hair,
CData->curve_uv.clear();
- BL::Object::modifiers_iterator b_mod;
- for (b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
- if ((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) &&
- (background ? b_mod->show_render() : b_mod->show_viewport())) {
- BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
+ for (BL::Modifier &b_mod : b_ob->modifiers) {
+ if ((b_mod.type() == b_mod.type_PARTICLE_SYSTEM) &&
+ (background ? b_mod.show_render() : b_mod.show_viewport())) {
+ BL::ParticleSystemModifier psmd((const PointerRNA)b_mod.ptr);
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
@@ -226,11 +224,10 @@ static bool ObtainCacheParticleVcol(Hair *hair,
CData->curve_vcol.clear();
- BL::Object::modifiers_iterator b_mod;
- for (b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
- if ((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) &&
- (background ? b_mod->show_render() : b_mod->show_viewport())) {
- BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
+ for (BL::Modifier &b_mod : b_ob->modifiers) {
+ if ((b_mod.type() == b_mod.type_PARTICLE_SYSTEM) &&
+ (background ? b_mod.show_render() : b_mod.show_viewport())) {
+ BL::ParticleSystemModifier psmd((const PointerRNA)b_mod.ptr);
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
@@ -510,11 +507,10 @@ static void ExportCurveSegmentsMotion(Hair *hair, ParticleCurveData *CData, int
bool BlenderSync::object_has_particle_hair(BL::Object b_ob)
{
/* Test if the object has a particle modifier with hair. */
- BL::Object::modifiers_iterator b_mod;
- for (b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) {
- if ((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) &&
- (preview ? b_mod->show_viewport() : b_mod->show_render())) {
- BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
+ for (BL::Modifier &b_mod : b_ob.modifiers) {
+ if ((b_mod.type() == b_mod.type_PARTICLE_SYSTEM) &&
+ (preview ? b_mod.show_viewport() : b_mod.show_render())) {
+ BL::ParticleSystemModifier psmd((const PointerRNA)b_mod.ptr);
BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
@@ -678,9 +674,7 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
/* Export curves and points. */
vector<float> points_length;
- BL::Hair::curves_iterator b_curve_iter;
- for (b_hair.curves.begin(b_curve_iter); b_curve_iter != b_hair.curves.end(); ++b_curve_iter) {
- BL::HairCurve b_curve = *b_curve_iter;
+ for (BL::HairCurve &b_curve : b_hair.curves) {
const int first_point_index = b_curve.first_point_index();
const int num_points = b_curve.num_points();
@@ -748,9 +742,7 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st
int num_motion_keys = 0;
int curve_index = 0;
- BL::Hair::curves_iterator b_curve_iter;
- for (b_hair.curves.begin(b_curve_iter); b_curve_iter != b_hair.curves.end(); ++b_curve_iter) {
- BL::HairCurve b_curve = *b_curve_iter;
+ for (BL::HairCurve &b_curve : b_hair.curves) {
const int first_point_index = b_curve.first_point_index();
const int num_points = b_curve.num_points();
@@ -855,10 +847,7 @@ void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BL::Object b_ob, Hair *ha
hair->set_value(socket, new_hair, socket);
}
- hair->attributes.clear();
- foreach (Attribute &attr, new_hair.attributes.attributes) {
- hair->attributes.attributes.push_back(std::move(attr));
- }
+ hair->attributes.update(std::move(new_hair.attributes));
/* tag update */
diff --git a/intern/cycles/blender/blender_device.cpp b/intern/cycles/blender/blender_device.cpp
index 3ec77719bea..d51b31de638 100644
--- a/intern/cycles/blender/blender_device.cpp
+++ b/intern/cycles/blender/blender_device.cpp
@@ -47,11 +47,9 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen
/* Find cycles preferences. */
PointerRNA cpreferences;
- BL::Preferences::addons_iterator b_addon_iter;
- for (b_preferences.addons.begin(b_addon_iter); b_addon_iter != b_preferences.addons.end();
- ++b_addon_iter) {
- if (b_addon_iter->module() == "cycles") {
- cpreferences = b_addon_iter->preferences().ptr;
+ for (BL::Addon &b_addon : b_preferences.addons) {
+ if (b_addon.module() == "cycles") {
+ cpreferences = b_addon.preferences().ptr;
break;
}
}
diff --git a/intern/cycles/blender/blender_geometry.cpp b/intern/cycles/blender/blender_geometry.cpp
index bd2f0731030..a009018f357 100644
--- a/intern/cycles/blender/blender_geometry.cpp
+++ b/intern/cycles/blender/blender_geometry.cpp
@@ -42,32 +42,20 @@ static Geometry::Type determine_geom_type(BL::Object &b_ob, bool use_particle_ha
return Geometry::MESH;
}
-Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
- BL::Object &b_ob,
- BL::Object &b_ob_instance,
- bool object_updated,
- bool use_particle_hair,
- TaskPool *task_pool)
+array<Node *> BlenderSync::find_used_shaders(BL::Object &b_ob)
{
- /* Test if we can instance or if the object is modified. */
- BL::ID b_ob_data = b_ob.data();
- BL::ID b_key_id = (BKE_object_is_modified(b_ob)) ? b_ob_instance : b_ob_data;
BL::Material material_override = view_layer.material_override;
Shader *default_shader = (b_ob.type() == BL::Object::type_VOLUME) ? scene->default_volume :
scene->default_surface;
- Geometry::Type geom_type = determine_geom_type(b_ob, use_particle_hair);
- GeometryKey key(b_key_id.ptr.data, geom_type);
- /* Find shader indices. */
array<Node *> used_shaders;
- BL::Object::material_slots_iterator slot;
- for (b_ob.material_slots.begin(slot); slot != b_ob.material_slots.end(); ++slot) {
+ for (BL::MaterialSlot &b_slot : b_ob.material_slots) {
if (material_override) {
find_shader(material_override, used_shaders, default_shader);
}
else {
- BL::ID b_material(slot->material());
+ BL::ID b_material(b_slot.material());
find_shader(b_material, used_shaders, default_shader);
}
}
@@ -79,6 +67,25 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
used_shaders.push_back_slow(default_shader);
}
+ return used_shaders;
+}
+
+Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
+ BL::Object &b_ob,
+ BL::Object &b_ob_instance,
+ bool object_updated,
+ bool use_particle_hair,
+ TaskPool *task_pool)
+{
+ /* Test if we can instance or if the object is modified. */
+ BL::ID b_ob_data = b_ob.data();
+ BL::ID b_key_id = (BKE_object_is_modified(b_ob)) ? b_ob_instance : b_ob_data;
+ Geometry::Type geom_type = determine_geom_type(b_ob, use_particle_hair);
+ GeometryKey key(b_key_id.ptr.data, geom_type);
+
+ /* Find shader indices. */
+ array<Node *> used_shaders = find_used_shaders(b_ob);
+
/* Ensure we only sync instanced geometry once. */
Geometry *geom = geometry_map.find(key);
if (geom) {
@@ -124,7 +131,7 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
foreach (Node *node, geom->get_used_shaders()) {
Shader *shader = static_cast<Shader *>(node);
- if (shader->need_update_geometry) {
+ if (shader->need_update_geometry()) {
attribute_recalc = true;
}
}
diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp
index 3420025f472..7edf797e096 100644
--- a/intern/cycles/blender/blender_mesh.cpp
+++ b/intern/cycles/blender/blender_mesh.cpp
@@ -285,12 +285,10 @@ static void attr_create_sculpt_vertex_color(Scene *scene,
BL::Mesh &b_mesh,
bool subdivision)
{
- BL::Mesh::sculpt_vertex_colors_iterator l;
-
- for (b_mesh.sculpt_vertex_colors.begin(l); l != b_mesh.sculpt_vertex_colors.end(); ++l) {
- const bool active_render = l->active_render();
+ for (BL::MeshVertColorLayer &l : b_mesh.sculpt_vertex_colors) {
+ const bool active_render = l.active_render();
AttributeStandard vcol_std = (active_render) ? ATTR_STD_VERTEX_COLOR : ATTR_STD_NONE;
- ustring vcol_name = ustring(l->name().c_str());
+ ustring vcol_name = ustring(l.name().c_str());
const bool need_vcol = mesh->need_attribute(scene, vcol_name) ||
mesh->need_attribute(scene, vcol_std);
@@ -307,7 +305,7 @@ static void attr_create_sculpt_vertex_color(Scene *scene,
int numverts = b_mesh.vertices.length();
for (int i = 0; i < numverts; i++) {
- *(cdata++) = get_float4(l->data[i].color());
+ *(cdata++) = get_float4(l.data[i].color());
}
}
}
@@ -315,12 +313,10 @@ static void attr_create_sculpt_vertex_color(Scene *scene,
/* Create vertex color attributes. */
static void attr_create_vertex_color(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, bool subdivision)
{
- BL::Mesh::vertex_colors_iterator l;
-
- for (b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l) {
- const bool active_render = l->active_render();
+ for (BL::MeshLoopColorLayer &l : b_mesh.vertex_colors) {
+ const bool active_render = l.active_render();
AttributeStandard vcol_std = (active_render) ? ATTR_STD_VERTEX_COLOR : ATTR_STD_NONE;
- ustring vcol_name = ustring(l->name().c_str());
+ ustring vcol_name = ustring(l.name().c_str());
const bool need_vcol = mesh->need_attribute(scene, vcol_name) ||
mesh->need_attribute(scene, vcol_std);
@@ -339,13 +335,12 @@ static void attr_create_vertex_color(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh,
vcol_attr = mesh->subd_attributes.add(vcol_name, TypeRGBA, ATTR_ELEMENT_CORNER_BYTE);
}
- BL::Mesh::polygons_iterator p;
uchar4 *cdata = vcol_attr->data_uchar4();
- for (b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) {
- int n = p->loop_total();
+ for (BL::MeshPolygon &p : b_mesh.polygons) {
+ int n = p.loop_total();
for (int i = 0; i < n; i++) {
- float4 color = get_float4(l->data[p->loop_start() + i].color());
+ float4 color = get_float4(l.data[p.loop_start() + i].color());
/* Compress/encode vertex color using the sRGB curve. */
*(cdata++) = color_float4_to_uchar4(color);
}
@@ -359,14 +354,13 @@ static void attr_create_vertex_color(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh,
vcol_attr = mesh->attributes.add(vcol_name, TypeRGBA, ATTR_ELEMENT_CORNER_BYTE);
}
- BL::Mesh::loop_triangles_iterator t;
uchar4 *cdata = vcol_attr->data_uchar4();
- for (b_mesh.loop_triangles.begin(t); t != b_mesh.loop_triangles.end(); ++t) {
- int3 li = get_int3(t->loops());
- float4 c1 = get_float4(l->data[li[0]].color());
- float4 c2 = get_float4(l->data[li[1]].color());
- float4 c3 = get_float4(l->data[li[2]].color());
+ for (BL::MeshLoopTriangle &t : b_mesh.loop_triangles) {
+ int3 li = get_int3(t.loops());
+ float4 c1 = get_float4(l.data[li[0]].color());
+ float4 c2 = get_float4(l.data[li[1]].color());
+ float4 c3 = get_float4(l.data[li[2]].color());
/* Compress/encode vertex color using the sRGB curve. */
cdata[0] = color_float4_to_uchar4(c1);
@@ -383,14 +377,12 @@ static void attr_create_vertex_color(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh,
static void attr_create_uv_map(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh)
{
if (b_mesh.uv_layers.length() != 0) {
- BL::Mesh::uv_layers_iterator l;
-
- for (b_mesh.uv_layers.begin(l); l != b_mesh.uv_layers.end(); ++l) {
- const bool active_render = l->active_render();
+ for (BL::MeshUVLoopLayer &l : b_mesh.uv_layers) {
+ const bool active_render = l.active_render();
AttributeStandard uv_std = (active_render) ? ATTR_STD_UV : ATTR_STD_NONE;
- ustring uv_name = ustring(l->name().c_str());
+ ustring uv_name = ustring(l.name().c_str());
AttributeStandard tangent_std = (active_render) ? ATTR_STD_UV_TANGENT : ATTR_STD_NONE;
- ustring tangent_name = ustring((string(l->name().c_str()) + ".tangent").c_str());
+ ustring tangent_name = ustring((string(l.name().c_str()) + ".tangent").c_str());
/* Denotes whether UV map was requested directly. */
const bool need_uv = mesh->need_attribute(scene, uv_name) ||
@@ -412,14 +404,13 @@ static void attr_create_uv_map(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh)
uv_attr = mesh->attributes.add(uv_name, TypeFloat2, ATTR_ELEMENT_CORNER);
}
- BL::Mesh::loop_triangles_iterator t;
float2 *fdata = uv_attr->data_float2();
- for (b_mesh.loop_triangles.begin(t); t != b_mesh.loop_triangles.end(); ++t) {
- int3 li = get_int3(t->loops());
- fdata[0] = get_float2(l->data[li[0]].uv());
- fdata[1] = get_float2(l->data[li[1]].uv());
- fdata[2] = get_float2(l->data[li[2]].uv());
+ for (BL::MeshLoopTriangle &t : b_mesh.loop_triangles) {
+ int3 li = get_int3(t.loops());
+ fdata[0] = get_float2(l.data[li[0]].uv());
+ fdata[1] = get_float2(l.data[li[1]].uv());
+ fdata[2] = get_float2(l.data[li[2]].uv());
fdata += 3;
}
}
@@ -427,10 +418,10 @@ static void attr_create_uv_map(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh)
/* UV tangent */
if (need_tangent) {
AttributeStandard sign_std = (active_render) ? ATTR_STD_UV_TANGENT_SIGN : ATTR_STD_NONE;
- ustring sign_name = ustring((string(l->name().c_str()) + ".tangent_sign").c_str());
+ ustring sign_name = ustring((string(l.name().c_str()) + ".tangent_sign").c_str());
bool need_sign = (mesh->need_attribute(scene, sign_name) ||
mesh->need_attribute(scene, sign_std));
- mikk_compute_tangents(b_mesh, l->name().c_str(), mesh, need_sign, active_render);
+ mikk_compute_tangents(b_mesh, l.name().c_str(), mesh, need_sign, active_render);
}
/* Remove temporarily created UV attribute. */
if (!need_uv && uv_attr != NULL) {
@@ -480,13 +471,12 @@ static void attr_create_subd_uv_map(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh,
uv_attr->flags |= ATTR_SUBDIVIDED;
}
- BL::Mesh::polygons_iterator p;
float2 *fdata = uv_attr->data_float2();
- for (b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) {
- int n = p->loop_total();
+ for (BL::MeshPolygon &p : b_mesh.polygons) {
+ int n = p.loop_total();
for (int j = 0; j < n; j++) {
- *(fdata++) = get_float2(l->data[p->loop_start() + j].uv());
+ *(fdata++) = get_float2(l->data[p.loop_start() + j].uv());
}
}
}
@@ -706,9 +696,8 @@ static void attr_create_random_per_island(Scene *scene,
DisjointSet vertices_sets(number_of_vertices);
- BL::Mesh::edges_iterator e;
- for (b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e) {
- vertices_sets.join(e->vertices()[0], e->vertices()[1]);
+ for (BL::MeshEdge &e : b_mesh.edges) {
+ vertices_sets.join(e.vertices()[0], e.vertices()[1]);
}
AttributeSet &attributes = (subdivision) ? mesh->subd_attributes : mesh->attributes;
@@ -716,15 +705,13 @@ static void attr_create_random_per_island(Scene *scene,
float *data = attribute->data_float();
if (!subdivision) {
- BL::Mesh::loop_triangles_iterator t;
- for (b_mesh.loop_triangles.begin(t); t != b_mesh.loop_triangles.end(); ++t) {
- data[t->index()] = hash_uint_to_float(vertices_sets.find(t->vertices()[0]));
+ for (BL::MeshLoopTriangle &t : b_mesh.loop_triangles) {
+ data[t.index()] = hash_uint_to_float(vertices_sets.find(t.vertices()[0]));
}
}
else {
- BL::Mesh::polygons_iterator p;
- for (b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) {
- data[p->index()] = hash_uint_to_float(vertices_sets.find(p->vertices()[0]));
+ for (BL::MeshPolygon &p : b_mesh.polygons) {
+ data[p.index()] = hash_uint_to_float(vertices_sets.find(p.vertices()[0]));
}
}
}
@@ -756,10 +743,9 @@ static void create_mesh(Scene *scene,
numtris = numfaces;
}
else {
- BL::Mesh::polygons_iterator p;
- for (b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) {
- numngons += (p->loop_total() == 4) ? 0 : 1;
- numcorners += p->loop_total();
+ for (BL::MeshPolygon &p : b_mesh.polygons) {
+ numngons += (p.loop_total() == 4) ? 0 : 1;
+ numcorners += p.loop_total();
}
}
@@ -803,17 +789,15 @@ static void create_mesh(Scene *scene,
/* create faces */
if (!subdivision) {
- BL::Mesh::loop_triangles_iterator t;
-
- for (b_mesh.loop_triangles.begin(t); t != b_mesh.loop_triangles.end(); ++t) {
- BL::MeshPolygon p = b_mesh.polygons[t->polygon_index()];
- int3 vi = get_int3(t->vertices());
+ for (BL::MeshLoopTriangle &t : b_mesh.loop_triangles) {
+ BL::MeshPolygon p = b_mesh.polygons[t.polygon_index()];
+ int3 vi = get_int3(t.vertices());
int shader = clamp(p.material_index(), 0, used_shaders.size() - 1);
bool smooth = p.use_smooth() || use_loop_normals;
if (use_loop_normals) {
- BL::Array<float, 9> loop_normals = t->split_normals();
+ BL::Array<float, 9> loop_normals = t.split_normals();
for (int i = 0; i < 3; i++) {
N[vi[i]] = make_float3(
loop_normals[i * 3], loop_normals[i * 3 + 1], loop_normals[i * 3 + 2]);
@@ -828,18 +812,17 @@ static void create_mesh(Scene *scene,
}
}
else {
- BL::Mesh::polygons_iterator p;
vector<int> vi;
- for (b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) {
- int n = p->loop_total();
- int shader = clamp(p->material_index(), 0, used_shaders.size() - 1);
- bool smooth = p->use_smooth() || use_loop_normals;
+ for (BL::MeshPolygon &p : b_mesh.polygons) {
+ int n = p.loop_total();
+ int shader = clamp(p.material_index(), 0, used_shaders.size() - 1);
+ bool smooth = p.use_smooth() || use_loop_normals;
vi.resize(n);
for (int i = 0; i < n; i++) {
/* NOTE: Autosmooth is already taken care about. */
- vi[i] = b_mesh.loops[p->loop_start() + i].vertex_index();
+ vi[i] = b_mesh.loops[p.loop_start() + i].vertex_index();
}
/* create subd faces */
@@ -891,19 +874,18 @@ static void create_subd_mesh(Scene *scene,
/* export creases */
size_t num_creases = 0;
- BL::Mesh::edges_iterator e;
- for (b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e) {
- if (e->crease() != 0.0f) {
+ for (BL::MeshEdge &e : b_mesh.edges) {
+ if (e.crease() != 0.0f) {
num_creases++;
}
}
mesh->reserve_subd_creases(num_creases);
- for (b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e) {
- if (e->crease() != 0.0f) {
- mesh->add_crease(e->vertices()[0], e->vertices()[1], e->crease());
+ for (BL::MeshEdge &e : b_mesh.edges) {
+ if (e.crease() != 0.0f) {
+ mesh->add_crease(e.vertices()[0], e.vertices()[1], e.crease());
}
}
@@ -1075,15 +1057,8 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BL::Object b_ob, Mesh *me
mesh->set_value(socket, new_mesh, socket);
}
- mesh->attributes.clear();
- foreach (Attribute &attr, new_mesh.attributes.attributes) {
- mesh->attributes.attributes.push_back(std::move(attr));
- }
-
- mesh->subd_attributes.clear();
- foreach (Attribute &attr, new_mesh.subd_attributes.attributes) {
- mesh->subd_attributes.attributes.push_back(std::move(attr));
- }
+ mesh->attributes.update(std::move(new_mesh.attributes));
+ mesh->subd_attributes.update(std::move(new_mesh.subd_attributes));
mesh->set_num_subd_faces(new_mesh.get_num_subd_faces());
diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp
index 5261fbcee07..f0460b129c2 100644
--- a/intern/cycles/blender/blender_object.cpp
+++ b/intern/cycles/blender/blender_object.cpp
@@ -51,10 +51,11 @@ bool BlenderSync::BKE_object_is_modified(BL::Object &b_ob)
}
else {
/* object level material links */
- BL::Object::material_slots_iterator slot;
- for (b_ob.material_slots.begin(slot); slot != b_ob.material_slots.end(); ++slot)
- if (slot->link() == BL::MaterialSlot::link_OBJECT)
+ for (BL::MaterialSlot &b_slot : b_ob.material_slots) {
+ if (b_slot.link() == BL::MaterialSlot::link_OBJECT) {
return true;
+ }
+ }
}
return false;
@@ -243,9 +244,6 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
/* holdout */
object->set_use_holdout(use_holdout);
- if (object->use_holdout_is_modified()) {
- scene->object_manager->tag_update(scene);
- }
object->set_visibility(visibility);
diff --git a/intern/cycles/blender/blender_particles.cpp b/intern/cycles/blender/blender_particles.cpp
index ca221b229b4..d5dd7215c47 100644
--- a/intern/cycles/blender/blender_particles.cpp
+++ b/intern/cycles/blender/blender_particles.cpp
@@ -57,7 +57,7 @@ bool BlenderSync::sync_dupli_particle(BL::Object &b_ob,
/* no update needed? */
if (!need_update && !object->get_geometry()->is_modified() &&
- !scene->object_manager->need_update)
+ !scene->object_manager->need_update())
return true;
/* first time used in this sync loop? clear and tag update */
@@ -85,7 +85,7 @@ bool BlenderSync::sync_dupli_particle(BL::Object &b_ob,
object->set_particle_index(psys->particles.size() - 1);
if (object->particle_index_is_modified())
- scene->object_manager->tag_update(scene);
+ scene->object_manager->tag_update(scene, ObjectManager::PARTICLE_MODIFIED);
/* return that this object has particle data */
return true;
diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp
index 525525e1047..5646bd5b2eb 100644
--- a/intern/cycles/blender/blender_python.cpp
+++ b/intern/cycles/blender/blender_python.cpp
@@ -597,22 +597,19 @@ static PyObject *osl_update_node_func(PyObject * /*self*/, PyObject *args)
bool removed;
do {
- BL::Node::inputs_iterator b_input;
- BL::Node::outputs_iterator b_output;
-
removed = false;
- for (b_node.inputs.begin(b_input); b_input != b_node.inputs.end(); ++b_input) {
- if (used_sockets.find(b_input->ptr.data) == used_sockets.end()) {
- b_node.inputs.remove(b_data, *b_input);
+ for (BL::NodeSocket &b_input : b_node.inputs) {
+ if (used_sockets.find(b_input.ptr.data) == used_sockets.end()) {
+ b_node.inputs.remove(b_data, b_input);
removed = true;
break;
}
}
- for (b_node.outputs.begin(b_output); b_output != b_node.outputs.end(); ++b_output) {
- if (used_sockets.find(b_output->ptr.data) == used_sockets.end()) {
- b_node.outputs.remove(b_data, *b_output);
+ for (BL::NodeSocket &b_output : b_node.outputs) {
+ if (used_sockets.find(b_output.ptr.data) == used_sockets.end()) {
+ b_node.outputs.remove(b_data, b_output);
removed = true;
break;
}
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
index 8566d0e7ed5..ae13310789e 100644
--- a/intern/cycles/blender/blender_session.cpp
+++ b/intern/cycles/blender/blender_session.cpp
@@ -358,11 +358,7 @@ void BlenderSession::do_write_update_render_tile(RenderTile &rtile,
if (do_read_only) {
/* copy each pass */
- BL::RenderLayer::passes_iterator b_iter;
-
- for (b_rlay.passes.begin(b_iter); b_iter != b_rlay.passes.end(); ++b_iter) {
- BL::RenderPass b_pass(*b_iter);
-
+ for (BL::RenderPass &b_pass : b_rlay.passes) {
/* find matching pass type */
PassType pass_type = BlenderSync::get_pass_type(b_pass);
int components = b_pass.channels();
@@ -552,7 +548,6 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
int seed = scene->integrator->get_seed();
seed += hash_uint2(seed, hash_uint2(view_index * 0xdeadbeef, 0));
scene->integrator->set_seed(seed);
- scene->integrator->tag_update(scene);
}
/* Update number of samples per layer. */
@@ -736,10 +731,7 @@ void BlenderSession::do_write_update_render_result(BL::RenderLayer &b_rlay,
if (!do_update_only) {
/* copy each pass */
- BL::RenderLayer::passes_iterator b_iter;
-
- for (b_rlay.passes.begin(b_iter); b_iter != b_rlay.passes.end(); ++b_iter) {
- BL::RenderPass b_pass(*b_iter);
+ for (BL::RenderPass &b_pass : b_rlay.passes) {
int components = b_pass.channels();
/* Copy pixels from regular render passes. */
@@ -1116,10 +1108,6 @@ void BlenderSession::update_resumable_tile_manager(int num_samples)
scene->integrator->set_start_sample(rounded_range_start_sample);
- if (scene->integrator->is_modified()) {
- scene->integrator->tag_update(scene);
- }
-
session->tile_manager.range_start_sample = rounded_range_start_sample;
session->tile_manager.range_num_samples = rounded_range_num_samples;
}
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
index ac86cf3345c..02a6638b083 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -148,15 +148,13 @@ BlenderAttributeType blender_attribute_name_split_type(ustring name, string *r_r
static BL::NodeSocket get_node_output(BL::Node &b_node, const string &name)
{
- BL::Node::outputs_iterator b_out;
-
- for (b_node.outputs.begin(b_out); b_out != b_node.outputs.end(); ++b_out)
- if (b_out->name() == name)
- return *b_out;
-
+ for (BL::NodeSocket &b_out : b_node.outputs) {
+ if (b_out.name() == name) {
+ return b_out;
+ }
+ }
assert(0);
-
- return *b_out;
+ return *b_node.outputs.begin();
}
static float3 get_node_output_rgba(BL::Node &b_node, const string &name)
@@ -723,9 +721,8 @@ static ShaderNode *add_node(Scene *scene,
image->set_alpha_type(get_image_alpha_type(b_image));
array<int> tiles;
- BL::Image::tiles_iterator b_iter;
- for (b_image.tiles.begin(b_iter); b_iter != b_image.tiles.end(); ++b_iter) {
- tiles.push_back_slow(b_iter->number());
+ for (BL::UDIMTile &b_tile : b_image.tiles) {
+ tiles.push_back_slow(b_tile.number());
}
image->set_tiles(tiles);
@@ -885,7 +882,7 @@ static ShaderNode *add_node(Scene *scene,
sky->set_sun_intensity(b_sky_node.sun_intensity());
sky->set_sun_elevation(b_sky_node.sun_elevation());
sky->set_sun_rotation(b_sky_node.sun_rotation());
- sky->set_altitude(1000.0f * b_sky_node.altitude());
+ sky->set_altitude(b_sky_node.altitude());
sky->set_air_density(b_sky_node.air_density());
sky->set_dust_density(b_sky_node.dust_density());
sky->set_ozone_density(b_sky_node.ozone_density());
@@ -1012,18 +1009,18 @@ static ShaderInput *node_find_input_by_name(ShaderNode *node,
string name = b_socket.name();
if (node_use_modified_socket_name(node)) {
- BL::Node::inputs_iterator b_input;
bool found = false;
int counter = 0, total = 0;
- for (b_node.inputs.begin(b_input); b_input != b_node.inputs.end(); ++b_input) {
- if (b_input->name() == name) {
- if (!found)
+ for (BL::NodeSocket &b_input : b_node.inputs) {
+ if (b_input.name() == name) {
+ if (!found) {
counter++;
+ }
total++;
}
- if (b_input->ptr.data == b_socket.ptr.data)
+ if (b_input.ptr.data == b_socket.ptr.data)
found = true;
}
@@ -1045,19 +1042,20 @@ static ShaderOutput *node_find_output_by_name(ShaderNode *node,
string name = b_socket.name();
if (node_use_modified_socket_name(node)) {
- BL::Node::outputs_iterator b_output;
bool found = false;
int counter = 0, total = 0;
- for (b_node.outputs.begin(b_output); b_output != b_node.outputs.end(); ++b_output) {
- if (b_output->name() == name) {
- if (!found)
+ for (BL::NodeSocket &b_output : b_node.outputs) {
+ if (b_output.name() == name) {
+ if (!found) {
counter++;
+ }
total++;
}
- if (b_output->ptr.data == b_socket.ptr.data)
+ if (b_output.ptr.data == b_socket.ptr.data) {
found = true;
+ }
}
/* rename if needed */
@@ -1082,25 +1080,19 @@ static void add_nodes(Scene *scene,
const ProxyMap &proxy_output_map)
{
/* add nodes */
- BL::ShaderNodeTree::nodes_iterator b_node;
PtrInputMap input_map;
PtrOutputMap output_map;
- BL::Node::inputs_iterator b_input;
- BL::Node::outputs_iterator b_output;
-
/* find the node to use for output if there are multiple */
BL::ShaderNode output_node = b_ntree.get_output_node(
BL::ShaderNodeOutputMaterial::target_CYCLES);
/* add nodes */
- for (b_ntree.nodes.begin(b_node); b_node != b_ntree.nodes.end(); ++b_node) {
- if (b_node->mute() || b_node->is_a(&RNA_NodeReroute)) {
+ for (BL::Node &b_node : b_ntree.nodes) {
+ if (b_node.mute() || b_node.is_a(&RNA_NodeReroute)) {
/* replace muted node with internal links */
- BL::Node::internal_links_iterator b_link;
- for (b_node->internal_links.begin(b_link); b_link != b_node->internal_links.end();
- ++b_link) {
- BL::NodeSocket to_socket(b_link->to_socket());
+ for (BL::NodeLink &b_link : b_node.internal_links) {
+ BL::NodeSocket to_socket(b_link.to_socket());
SocketType::Type to_socket_type = convert_socket_type(to_socket);
if (to_socket_type == SocketType::UNDEFINED) {
continue;
@@ -1108,22 +1100,22 @@ static void add_nodes(Scene *scene,
ConvertNode *proxy = graph->create_node<ConvertNode>(to_socket_type, to_socket_type, true);
- input_map[b_link->from_socket().ptr.data] = proxy->inputs[0];
- output_map[b_link->to_socket().ptr.data] = proxy->outputs[0];
+ input_map[b_link.from_socket().ptr.data] = proxy->inputs[0];
+ output_map[b_link.to_socket().ptr.data] = proxy->outputs[0];
graph->add(proxy);
}
}
- else if (b_node->is_a(&RNA_ShaderNodeGroup) || b_node->is_a(&RNA_NodeCustomGroup) ||
- b_node->is_a(&RNA_ShaderNodeCustomGroup)) {
+ else if (b_node.is_a(&RNA_ShaderNodeGroup) || b_node.is_a(&RNA_NodeCustomGroup) ||
+ b_node.is_a(&RNA_ShaderNodeCustomGroup)) {
BL::ShaderNodeTree b_group_ntree(PointerRNA_NULL);
- if (b_node->is_a(&RNA_ShaderNodeGroup))
- b_group_ntree = BL::ShaderNodeTree(((BL::NodeGroup)(*b_node)).node_tree());
- else if (b_node->is_a(&RNA_NodeCustomGroup))
- b_group_ntree = BL::ShaderNodeTree(((BL::NodeCustomGroup)(*b_node)).node_tree());
+ if (b_node.is_a(&RNA_ShaderNodeGroup))
+ b_group_ntree = BL::ShaderNodeTree(((BL::NodeGroup)(b_node)).node_tree());
+ else if (b_node.is_a(&RNA_NodeCustomGroup))
+ b_group_ntree = BL::ShaderNodeTree(((BL::NodeCustomGroup)(b_node)).node_tree());
else
- b_group_ntree = BL::ShaderNodeTree(((BL::ShaderNodeCustomGroup)(*b_node)).node_tree());
+ b_group_ntree = BL::ShaderNodeTree(((BL::ShaderNodeCustomGroup)(b_node)).node_tree());
ProxyMap group_proxy_input_map, group_proxy_output_map;
@@ -1131,8 +1123,8 @@ static void add_nodes(Scene *scene,
* Do this even if the node group has no internal tree,
* so that links have something to connect to and assert won't fail.
*/
- for (b_node->inputs.begin(b_input); b_input != b_node->inputs.end(); ++b_input) {
- SocketType::Type input_type = convert_socket_type(*b_input);
+ for (BL::NodeSocket &b_input : b_node.inputs) {
+ SocketType::Type input_type = convert_socket_type(b_input);
if (input_type == SocketType::UNDEFINED) {
continue;
}
@@ -1141,14 +1133,14 @@ static void add_nodes(Scene *scene,
graph->add(proxy);
/* register the proxy node for internal binding */
- group_proxy_input_map[b_input->identifier()] = proxy;
+ group_proxy_input_map[b_input.identifier()] = proxy;
- input_map[b_input->ptr.data] = proxy->inputs[0];
+ input_map[b_input.ptr.data] = proxy->inputs[0];
- set_default_value(proxy->inputs[0], *b_input, b_data, b_ntree);
+ set_default_value(proxy->inputs[0], b_input, b_data, b_ntree);
}
- for (b_node->outputs.begin(b_output); b_output != b_node->outputs.end(); ++b_output) {
- SocketType::Type output_type = convert_socket_type(*b_output);
+ for (BL::NodeSocket &b_output : b_node.outputs) {
+ SocketType::Type output_type = convert_socket_type(b_output);
if (output_type == SocketType::UNDEFINED) {
continue;
}
@@ -1157,9 +1149,9 @@ static void add_nodes(Scene *scene,
graph->add(proxy);
/* register the proxy node for internal binding */
- group_proxy_output_map[b_output->identifier()] = proxy;
+ group_proxy_output_map[b_output.identifier()] = proxy;
- output_map[b_output->ptr.data] = proxy->outputs[0];
+ output_map[b_output.ptr.data] = proxy->outputs[0];
}
if (b_group_ntree) {
@@ -1174,30 +1166,30 @@ static void add_nodes(Scene *scene,
group_proxy_output_map);
}
}
- else if (b_node->is_a(&RNA_NodeGroupInput)) {
+ else if (b_node.is_a(&RNA_NodeGroupInput)) {
/* map each socket to a proxy node */
- for (b_node->outputs.begin(b_output); b_output != b_node->outputs.end(); ++b_output) {
- ProxyMap::const_iterator proxy_it = proxy_input_map.find(b_output->identifier());
+ for (BL::NodeSocket &b_output : b_node.outputs) {
+ ProxyMap::const_iterator proxy_it = proxy_input_map.find(b_output.identifier());
if (proxy_it != proxy_input_map.end()) {
ConvertNode *proxy = proxy_it->second;
- output_map[b_output->ptr.data] = proxy->outputs[0];
+ output_map[b_output.ptr.data] = proxy->outputs[0];
}
}
}
- else if (b_node->is_a(&RNA_NodeGroupOutput)) {
- BL::NodeGroupOutput b_output_node(*b_node);
+ else if (b_node.is_a(&RNA_NodeGroupOutput)) {
+ BL::NodeGroupOutput b_output_node(b_node);
/* only the active group output is used */
if (b_output_node.is_active_output()) {
/* map each socket to a proxy node */
- for (b_node->inputs.begin(b_input); b_input != b_node->inputs.end(); ++b_input) {
- ProxyMap::const_iterator proxy_it = proxy_output_map.find(b_input->identifier());
+ for (BL::NodeSocket &b_input : b_node.inputs) {
+ ProxyMap::const_iterator proxy_it = proxy_output_map.find(b_input.identifier());
if (proxy_it != proxy_output_map.end()) {
ConvertNode *proxy = proxy_it->second;
- input_map[b_input->ptr.data] = proxy->inputs[0];
+ input_map[b_input.ptr.data] = proxy->inputs[0];
- set_default_value(proxy->inputs[0], *b_input, b_data, b_ntree);
+ set_default_value(proxy->inputs[0], b_input, b_data, b_ntree);
}
}
}
@@ -1205,52 +1197,49 @@ static void add_nodes(Scene *scene,
else {
ShaderNode *node = NULL;
- if (b_node->ptr.data == output_node.ptr.data) {
+ if (b_node.ptr.data == output_node.ptr.data) {
node = graph->output();
}
else {
- BL::ShaderNode b_shader_node(*b_node);
+ BL::ShaderNode b_shader_node(b_node);
node = add_node(
scene, b_engine, b_data, b_depsgraph, b_scene, graph, b_ntree, b_shader_node);
}
if (node) {
/* map node sockets for linking */
- for (b_node->inputs.begin(b_input); b_input != b_node->inputs.end(); ++b_input) {
- ShaderInput *input = node_find_input_by_name(node, *b_node, *b_input);
+ for (BL::NodeSocket &b_input : b_node.inputs) {
+ ShaderInput *input = node_find_input_by_name(node, b_node, b_input);
if (!input) {
/* XXX should not happen, report error? */
continue;
}
- input_map[b_input->ptr.data] = input;
+ input_map[b_input.ptr.data] = input;
- set_default_value(input, *b_input, b_data, b_ntree);
+ set_default_value(input, b_input, b_data, b_ntree);
}
- for (b_node->outputs.begin(b_output); b_output != b_node->outputs.end(); ++b_output) {
- ShaderOutput *output = node_find_output_by_name(node, *b_node, *b_output);
+ for (BL::NodeSocket &b_output : b_node.outputs) {
+ ShaderOutput *output = node_find_output_by_name(node, b_node, b_output);
if (!output) {
/* XXX should not happen, report error? */
continue;
}
- output_map[b_output->ptr.data] = output;
+ output_map[b_output.ptr.data] = output;
}
}
}
}
/* connect nodes */
- BL::NodeTree::links_iterator b_link;
-
- for (b_ntree.links.begin(b_link); b_link != b_ntree.links.end(); ++b_link) {
+ for (BL::NodeLink &b_link : b_ntree.links) {
/* Ignore invalid links to avoid unwanted cycles created in graph.
* Also ignore links with unavailable sockets. */
- if (!(b_link->is_valid() && b_link->from_socket().enabled() &&
- b_link->to_socket().enabled())) {
+ if (!(b_link.is_valid() && b_link.from_socket().enabled() && b_link.to_socket().enabled())) {
continue;
}
/* get blender link data */
- BL::NodeSocket b_from_sock = b_link->from_socket();
- BL::NodeSocket b_to_sock = b_link->to_socket();
+ BL::NodeSocket b_from_sock = b_link.from_socket();
+ BL::NodeSocket b_to_sock = b_link.to_socket();
ShaderOutput *output = 0;
ShaderInput *input = 0;
@@ -1298,13 +1287,12 @@ void BlenderSync::sync_materials(BL::Depsgraph &b_depsgraph, bool update_all)
TaskPool pool;
set<Shader *> updated_shaders;
- BL::Depsgraph::ids_iterator b_id;
- for (b_depsgraph.ids.begin(b_id); b_id != b_depsgraph.ids.end(); ++b_id) {
- if (!b_id->is_a(&RNA_Material)) {
+ for (BL::ID &b_id : b_depsgraph.ids) {
+ if (!b_id.is_a(&RNA_Material)) {
continue;
}
- BL::Material b_mat(*b_id);
+ BL::Material b_mat(b_id);
Shader *shader;
/* test if we need to sync */
@@ -1497,7 +1485,6 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d,
shader->set_graph(graph);
shader->tag_update(scene);
- background->tag_update(scene);
}
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
@@ -1517,8 +1504,7 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d,
viewport_parameters.custom_viewport_parameters());
background->set_use_ao(background->get_use_ao() && view_layer.use_background_ao);
- if (background->is_modified())
- background->tag_update(scene);
+ background->tag_update(scene);
}
/* Sync Lights */
@@ -1527,13 +1513,12 @@ void BlenderSync::sync_lights(BL::Depsgraph &b_depsgraph, bool update_all)
{
shader_map.set_default(scene->default_light);
- BL::Depsgraph::ids_iterator b_id;
- for (b_depsgraph.ids.begin(b_id); b_id != b_depsgraph.ids.end(); ++b_id) {
- if (!b_id->is_a(&RNA_Light)) {
+ for (BL::ID &b_id : b_depsgraph.ids) {
+ if (!b_id.is_a(&RNA_Light)) {
continue;
}
- BL::Light b_light(*b_id);
+ BL::Light b_light(b_id);
Shader *shader;
/* test if we need to sync */
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index e27daa2488d..4bcadab35a1 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -24,6 +24,7 @@
#include "render/mesh.h"
#include "render/nodes.h"
#include "render/object.h"
+#include "render/procedural.h"
#include "render/scene.h"
#include "render/shader.h"
@@ -131,9 +132,8 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d
}
/* Iterate over all IDs in this depsgraph. */
- BL::Depsgraph::updates_iterator b_update;
- for (b_depsgraph.updates.begin(b_update); b_update != b_depsgraph.updates.end(); ++b_update) {
- BL::ID b_id(b_update->id());
+ for (BL::DepsgraphUpdate &b_update : b_depsgraph.updates) {
+ BL::ID b_id(b_update.id());
/* Material */
if (b_id.is_a(&RNA_Material)) {
@@ -151,17 +151,17 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d
const bool is_geometry = object_is_geometry(b_ob);
const bool is_light = !is_geometry && object_is_light(b_ob);
- if (b_ob.is_instancer() && b_update->is_updated_shading()) {
+ if (b_ob.is_instancer() && b_update.is_updated_shading()) {
/* Needed for e.g. object color updates on instancer. */
object_map.set_recalc(b_ob);
}
if (is_geometry || is_light) {
- const bool updated_geometry = b_update->is_updated_geometry();
+ const bool updated_geometry = b_update.is_updated_geometry();
/* Geometry (mesh, hair, volume). */
if (is_geometry) {
- if (b_update->is_updated_transform() || b_update->is_updated_shading()) {
+ if (b_update.is_updated_transform() || b_update.is_updated_shading()) {
object_map.set_recalc(b_ob);
}
@@ -181,7 +181,7 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d
}
/* Light */
else if (is_light) {
- if (b_update->is_updated_transform() || b_update->is_updated_shading()) {
+ if (b_update.is_updated_transform() || b_update.is_updated_shading()) {
object_map.set_recalc(b_ob);
light_map.set_recalc(b_ob);
}
@@ -302,11 +302,6 @@ void BlenderSync::sync_integrator()
integrator->set_sample_clamp_direct(get_float(cscene, "sample_clamp_direct"));
integrator->set_sample_clamp_indirect(get_float(cscene, "sample_clamp_indirect"));
if (!preview) {
- if (integrator->get_motion_blur() != r.use_motion_blur()) {
- scene->object_manager->tag_update(scene);
- scene->camera->tag_modified();
- }
-
integrator->set_motion_blur(r.use_motion_blur());
}
@@ -375,8 +370,8 @@ void BlenderSync::sync_integrator()
integrator->set_ao_bounces(0);
}
- if (integrator->is_modified())
- integrator->tag_update(scene);
+ /* UPDATE_NONE as we don't want to tag the integrator as modified, just tag dependent things */
+ integrator->tag_update(scene, Integrator::UPDATE_NONE);
}
/* Film */
@@ -471,16 +466,15 @@ void BlenderSync::sync_images()
return;
}
/* Free buffers used by images which are not needed for render. */
- BL::BlendData::images_iterator b_image;
- for (b_data.images.begin(b_image); b_image != b_data.images.end(); ++b_image) {
+ for (BL::Image &b_image : b_data.images) {
/* TODO(sergey): Consider making it an utility function to check
* whether image is considered builtin.
*/
- const bool is_builtin = b_image->packed_file() ||
- b_image->source() == BL::Image::source_GENERATED ||
- b_image->source() == BL::Image::source_MOVIE || b_engine.is_preview();
+ const bool is_builtin = b_image.packed_file() ||
+ b_image.source() == BL::Image::source_GENERATED ||
+ b_image.source() == BL::Image::source_MOVIE || b_engine.is_preview();
if (is_builtin == false) {
- b_image->buffers_free();
+ b_image.buffers_free();
}
/* TODO(sergey): Free builtin images not used by any shader. */
}
@@ -581,10 +575,7 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
vector<Pass> passes;
/* loop over passes */
- BL::RenderLayer::passes_iterator b_pass_iter;
-
- for (b_rlay.passes.begin(b_pass_iter); b_pass_iter != b_rlay.passes.end(); ++b_pass_iter) {
- BL::RenderPass b_pass(*b_pass_iter);
+ for (BL::RenderPass &b_pass : b_rlay.passes) {
PassType pass_type = get_pass_type(b_pass);
if (pass_type == PASS_MOTION && scene->integrator->get_motion_blur())
@@ -729,7 +720,7 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
scene->film->set_pass_alpha_threshold(b_view_layer.pass_alpha_threshold());
scene->film->tag_passes_update(scene, passes);
- scene->integrator->tag_update(scene);
+ scene->integrator->tag_update(scene, Integrator::UPDATE_ALL);
return passes;
}
@@ -752,9 +743,8 @@ void BlenderSync::free_data_after_sync(BL::Depsgraph &b_depsgraph)
/* TODO(sergey): We can actually remove the whole dependency graph,
* but that will need some API support first.
*/
- BL::Depsgraph::objects_iterator b_ob;
- for (b_depsgraph.objects.begin(b_ob); b_ob != b_depsgraph.objects.end(); ++b_ob) {
- b_ob->cache_release();
+ for (BL::Object &b_ob : b_depsgraph.objects) {
+ b_ob.cache_release();
}
}
diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h
index ccf059d7704..1c43522a57e 100644
--- a/intern/cycles/blender/blender_sync.h
+++ b/intern/cycles/blender/blender_sync.h
@@ -134,6 +134,7 @@ class BlenderSync {
void sync_view();
/* Shader */
+ array<Node *> find_used_shaders(BL::Object &b_ob);
void sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, bool update_all);
void sync_shaders(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d);
void sync_nodes(Shader *shader, BL::ShaderNodeTree &b_ntree);
diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h
index 5185ebae789..43dbb4105df 100644
--- a/intern/cycles/blender/blender_util.h
+++ b/intern/cycles/blender/blender_util.h
@@ -538,11 +538,9 @@ static inline bool object_use_deform_motion(BL::Object &b_parent, BL::Object &b_
static inline BL::FluidDomainSettings object_fluid_liquid_domain_find(BL::Object &b_ob)
{
- BL::Object::modifiers_iterator b_mod;
-
- for (b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) {
- if (b_mod->is_a(&RNA_FluidModifier)) {
- BL::FluidModifier b_mmd(*b_mod);
+ for (BL::Modifier &b_mod : b_ob.modifiers) {
+ if (b_mod.is_a(&RNA_FluidModifier)) {
+ BL::FluidModifier b_mmd(b_mod);
if (b_mmd.fluid_type() == BL::FluidModifier::fluid_type_DOMAIN &&
b_mmd.domain_settings().domain_type() == BL::FluidDomainSettings::domain_type_LIQUID) {
@@ -556,11 +554,9 @@ static inline BL::FluidDomainSettings object_fluid_liquid_domain_find(BL::Object
static inline BL::FluidDomainSettings object_fluid_gas_domain_find(BL::Object &b_ob)
{
- BL::Object::modifiers_iterator b_mod;
-
- for (b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) {
- if (b_mod->is_a(&RNA_FluidModifier)) {
- BL::FluidModifier b_mmd(*b_mod);
+ for (BL::Modifier &b_mod : b_ob.modifiers) {
+ if (b_mod.is_a(&RNA_FluidModifier)) {
+ BL::FluidModifier b_mmd(b_mod);
if (b_mmd.fluid_type() == BL::FluidModifier::fluid_type_DOMAIN &&
b_mmd.domain_settings().domain_type() == BL::FluidDomainSettings::domain_type_GAS) {
diff --git a/intern/cycles/blender/blender_volume.cpp b/intern/cycles/blender/blender_volume.cpp
index 094d4bbc08c..410f7a72cf5 100644
--- a/intern/cycles/blender/blender_volume.cpp
+++ b/intern/cycles/blender/blender_volume.cpp
@@ -222,9 +222,7 @@ class BlenderVolumeLoader : public VDBImageLoader {
b_volume.grids.load(b_data.ptr.data);
#ifdef WITH_OPENVDB
- BL::Volume::grids_iterator b_grid_iter;
- for (b_volume.grids.begin(b_grid_iter); b_grid_iter != b_volume.grids.end(); ++b_grid_iter) {
- BL::VolumeGrid b_volume_grid(*b_grid_iter);
+ for (BL::VolumeGrid &b_volume_grid : b_volume.grids) {
if (b_volume_grid.name() == grid_name) {
const bool unload = !b_volume_grid.is_loaded();
@@ -260,9 +258,7 @@ static void sync_volume_object(BL::BlendData &b_data,
volume->set_object_space((b_render.space() == BL::VolumeRender::space_OBJECT));
/* Find grid with matching name. */
- BL::Volume::grids_iterator b_grid_iter;
- for (b_volume.grids.begin(b_grid_iter); b_grid_iter != b_volume.grids.end(); ++b_grid_iter) {
- BL::VolumeGrid b_grid = *b_grid_iter;
+ for (BL::VolumeGrid &b_grid : b_volume.grids) {
ustring name = ustring(b_grid.name());
AttributeStandard std = ATTR_STD_NONE;
diff --git a/intern/cycles/device/device_memory.h b/intern/cycles/device/device_memory.h
index 00b2aa864aa..1f63a152458 100644
--- a/intern/cycles/device/device_memory.h
+++ b/intern/cycles/device/device_memory.h
@@ -259,6 +259,8 @@ class device_memory {
device_ptr original_device_ptr;
size_t original_device_size;
Device *original_device;
+ bool need_realloc_;
+ bool modified;
};
/* Device Only Memory
@@ -329,6 +331,8 @@ template<typename T> class device_vector : public device_memory {
{
data_type = device_type_traits<T>::data_type;
data_elements = device_type_traits<T>::num_elements;
+ modified = true;
+ need_realloc_ = true;
assert(data_elements > 0);
}
@@ -347,6 +351,7 @@ template<typename T> class device_vector : public device_memory {
device_free();
host_free();
host_pointer = host_alloc(sizeof(T) * new_size);
+ modified = true;
assert(device_pointer == 0);
}
@@ -400,6 +405,19 @@ template<typename T> class device_vector : public device_memory {
assert(device_pointer == 0);
}
+ void give_data(array<T> &to)
+ {
+ device_free();
+
+ to.set_data((T *)host_pointer, data_size);
+ data_size = 0;
+ data_width = 0;
+ data_height = 0;
+ data_depth = 0;
+ host_pointer = 0;
+ assert(device_pointer == 0);
+ }
+
/* Free device and host memory. */
void free()
{
@@ -411,10 +429,40 @@ template<typename T> class device_vector : public device_memory {
data_height = 0;
data_depth = 0;
host_pointer = 0;
+ modified = true;
+ need_realloc_ = true;
assert(device_pointer == 0);
}
- size_t size()
+ void free_if_need_realloc(bool force_free)
+ {
+ if (need_realloc_ || force_free) {
+ free();
+ }
+ }
+
+ bool is_modified() const
+ {
+ return modified;
+ }
+
+ bool need_realloc()
+ {
+ return need_realloc_;
+ }
+
+ void tag_modified()
+ {
+ modified = true;
+ }
+
+ void tag_realloc()
+ {
+ need_realloc_ = true;
+ tag_modified();
+ }
+
+ size_t size() const
{
return data_size;
}
@@ -432,7 +480,24 @@ template<typename T> class device_vector : public device_memory {
void copy_to_device()
{
- device_copy_to();
+ if (data_size != 0) {
+ device_copy_to();
+ }
+ }
+
+ void copy_to_device_if_modified()
+ {
+ if (!modified) {
+ return;
+ }
+
+ copy_to_device();
+ }
+
+ void clear_modified()
+ {
+ modified = false;
+ need_realloc_ = false;
}
void copy_from_device()
diff --git a/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h b/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h
index d87e5f193ad..bb6b8a40e8e 100644
--- a/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h
+++ b/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h
@@ -18,7 +18,7 @@
/* Data type to replace `double` used in the NanoVDB headers. Cycles don't need doubles, and is
* safer and more portable to never use double datatype on GPU.
* Use a special structure, so that the following is true:
- * - No unnoticed implicit cast or mathermatical operations used on scalar 64bit type
+ * - No unnoticed implicit cast or mathematical operations used on scalar 64bit type
* (which rules out trick like using `uint64_t` as a drop-in replacement for double).
* - Padding rules are matching exactly `double`
* (which rules out array of `uint8_t`). */
diff --git a/intern/cycles/kernel/osl/osl_globals.h b/intern/cycles/kernel/osl/osl_globals.h
index caca3c28c8d..f1789f0d7eb 100644
--- a/intern/cycles/kernel/osl/osl_globals.h
+++ b/intern/cycles/kernel/osl/osl_globals.h
@@ -43,7 +43,7 @@ class ColorSpaceProcessor;
*
* Data needed by OSL render services, that is global to a rendering session.
* This includes all OSL shaders, name to attribute mapping and texture handles.
- * */
+ */
struct OSLGlobals {
OSLGlobals()
diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt
index 9663e25cd93..389f913b145 100644
--- a/intern/cycles/render/CMakeLists.txt
+++ b/intern/cycles/render/CMakeLists.txt
@@ -23,6 +23,7 @@ set(INC_SYS
)
set(SRC
+ alembic.cpp
attribute.cpp
background.cpp
bake.cpp
@@ -48,6 +49,7 @@ set(SRC
mesh_displace.cpp
mesh_subdivision.cpp
nodes.cpp
+ procedural.cpp
object.cpp
osl.cpp
particles.cpp
@@ -64,6 +66,7 @@ set(SRC
)
set(SRC_HEADERS
+ alembic.h
attribute.h
bake.h
background.h
@@ -90,6 +93,7 @@ set(SRC_HEADERS
object.h
osl.h
particles.h
+ procedural.h
curves.h
scene.h
session.h
@@ -144,6 +148,16 @@ if(WITH_OPENVDB)
)
endif()
+if(WITH_ALEMBIC)
+ add_definitions(-DWITH_ALEMBIC)
+ list(APPEND INC_SYS
+ ${ALEMBIC_INCLUDE_DIRS}
+ )
+ list(APPEND LIB
+ ${ALEMBIC_LIBRARIES}
+ )
+endif()
+
if(WITH_NANOVDB)
list(APPEND INC_SYS
${NANOVDB_INCLUDE_DIRS}
diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp
new file mode 100644
index 00000000000..e0e50d99f84
--- /dev/null
+++ b/intern/cycles/render/alembic.cpp
@@ -0,0 +1,1869 @@
+/*
+ * Copyright 2011-2018 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "render/alembic.h"
+
+#include "render/camera.h"
+#include "render/curves.h"
+#include "render/mesh.h"
+#include "render/object.h"
+#include "render/scene.h"
+#include "render/shader.h"
+
+#include "util/util_foreach.h"
+#include "util/util_progress.h"
+#include "util/util_transform.h"
+#include "util/util_vector.h"
+
+#ifdef WITH_ALEMBIC
+
+using namespace Alembic::AbcGeom;
+
+CCL_NAMESPACE_BEGIN
+
+/* TODO(@kevindietrich): motion blur support */
+
+void CachedData::clear()
+{
+ attributes.clear();
+ curve_first_key.clear();
+ curve_keys.clear();
+ curve_radius.clear();
+ curve_shader.clear();
+ num_ngons.clear();
+ shader.clear();
+ subd_creases_edge.clear();
+ subd_creases_weight.clear();
+ subd_face_corners.clear();
+ subd_num_corners.clear();
+ subd_ptex_offset.clear();
+ subd_smooth.clear();
+ subd_start_corner.clear();
+ transforms.clear();
+ triangles.clear();
+ triangles_loops.clear();
+ vertices.clear();
+
+ for (CachedAttribute &attr : attributes) {
+ attr.data.clear();
+ }
+
+ attributes.clear();
+}
+
+CachedData::CachedAttribute &CachedData::add_attribute(const ustring &name,
+ const TimeSampling &time_sampling)
+{
+ for (auto &attr : attributes) {
+ if (attr.name == name) {
+ return attr;
+ }
+ }
+
+ CachedAttribute &attr = attributes.emplace_back();
+ attr.name = name;
+ attr.data.set_time_sampling(time_sampling);
+ return attr;
+}
+
+bool CachedData::is_constant() const
+{
+# define CHECK_IF_CONSTANT(data) \
+ if (!data.is_constant()) { \
+ return false; \
+ }
+
+ CHECK_IF_CONSTANT(curve_first_key)
+ CHECK_IF_CONSTANT(curve_keys)
+ CHECK_IF_CONSTANT(curve_radius)
+ CHECK_IF_CONSTANT(curve_shader)
+ CHECK_IF_CONSTANT(num_ngons)
+ CHECK_IF_CONSTANT(shader)
+ CHECK_IF_CONSTANT(subd_creases_edge)
+ CHECK_IF_CONSTANT(subd_creases_weight)
+ CHECK_IF_CONSTANT(subd_face_corners)
+ CHECK_IF_CONSTANT(subd_num_corners)
+ CHECK_IF_CONSTANT(subd_ptex_offset)
+ CHECK_IF_CONSTANT(subd_smooth)
+ CHECK_IF_CONSTANT(subd_start_corner)
+ CHECK_IF_CONSTANT(transforms)
+ CHECK_IF_CONSTANT(triangles)
+ CHECK_IF_CONSTANT(triangles_loops)
+ CHECK_IF_CONSTANT(vertices)
+
+ for (const CachedAttribute &attr : attributes) {
+ if (!attr.data.is_constant()) {
+ return false;
+ }
+ }
+
+ return true;
+
+# undef CHECK_IF_CONSTANT
+}
+
+void CachedData::invalidate_last_loaded_time(bool attributes_only)
+{
+ if (attributes_only) {
+ for (CachedAttribute &attr : attributes) {
+ attr.data.invalidate_last_loaded_time();
+ }
+
+ return;
+ }
+
+ curve_first_key.invalidate_last_loaded_time();
+ curve_keys.invalidate_last_loaded_time();
+ curve_radius.invalidate_last_loaded_time();
+ curve_shader.invalidate_last_loaded_time();
+ num_ngons.invalidate_last_loaded_time();
+ shader.invalidate_last_loaded_time();
+ subd_creases_edge.invalidate_last_loaded_time();
+ subd_creases_weight.invalidate_last_loaded_time();
+ subd_face_corners.invalidate_last_loaded_time();
+ subd_num_corners.invalidate_last_loaded_time();
+ subd_ptex_offset.invalidate_last_loaded_time();
+ subd_smooth.invalidate_last_loaded_time();
+ subd_start_corner.invalidate_last_loaded_time();
+ transforms.invalidate_last_loaded_time();
+ triangles.invalidate_last_loaded_time();
+ triangles_loops.invalidate_last_loaded_time();
+ vertices.invalidate_last_loaded_time();
+}
+
+void CachedData::set_time_sampling(TimeSampling time_sampling)
+{
+ curve_first_key.set_time_sampling(time_sampling);
+ curve_keys.set_time_sampling(time_sampling);
+ curve_radius.set_time_sampling(time_sampling);
+ curve_shader.set_time_sampling(time_sampling);
+ num_ngons.set_time_sampling(time_sampling);
+ shader.set_time_sampling(time_sampling);
+ subd_creases_edge.set_time_sampling(time_sampling);
+ subd_creases_weight.set_time_sampling(time_sampling);
+ subd_face_corners.set_time_sampling(time_sampling);
+ subd_num_corners.set_time_sampling(time_sampling);
+ subd_ptex_offset.set_time_sampling(time_sampling);
+ subd_smooth.set_time_sampling(time_sampling);
+ subd_start_corner.set_time_sampling(time_sampling);
+ transforms.set_time_sampling(time_sampling);
+ triangles.set_time_sampling(time_sampling);
+ triangles_loops.set_time_sampling(time_sampling);
+ vertices.set_time_sampling(time_sampling);
+
+ for (CachedAttribute &attr : attributes) {
+ attr.data.set_time_sampling(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;
+ extractSHRT(mtx,
+ scale,
+ shear,
+ rotation,
+ translation,
+ true,
+ IMATH_INTERNAL_NAMESPACE::Euler<double>::XZY);
+
+ M44d rot_mat, scale_mat, trans_mat;
+ rot_mat.setEulerAngles(V3d(rotation.x, -rotation.z, rotation.y));
+ scale_mat.setScale(V3d(scale.x, scale.z, scale.y));
+ trans_mat.setTranslation(V3d(translation.x, -translation.z, translation.y));
+
+ M44d temp_mat = scale_mat * rot_mat * trans_mat;
+
+ scale_mat.setScale(static_cast<double>(scale_mult));
+
+ return temp_mat * scale_mat;
+}
+
+static void transform_decompose(
+ const M44d &mat, V3d &scale, V3d &shear, Quatd &rotation, V3d &translation)
+{
+ M44d mat_remainder(mat);
+
+ /* extract scale and shear */
+ Imath::extractAndRemoveScalingAndShear(mat_remainder, scale, shear);
+
+ /* extract translation */
+ translation.x = mat_remainder[3][0];
+ translation.y = mat_remainder[3][1];
+ translation.z = mat_remainder[3][2];
+
+ /* extract rotation */
+ rotation = extractQuat(mat_remainder);
+}
+
+static M44d transform_compose(const V3d &scale,
+ const V3d &shear,
+ const Quatd &rotation,
+ const V3d &translation)
+{
+ M44d scale_mat, shear_mat, rot_mat, trans_mat;
+
+ scale_mat.setScale(scale);
+ shear_mat.setShear(shear);
+ rot_mat = rotation.toMatrix44();
+ trans_mat.setTranslation(translation);
+
+ return scale_mat * shear_mat * rot_mat * trans_mat;
+}
+
+/* get the matrix for the specified time, or return the identity matrix if there is no exact match
+ */
+static M44d get_matrix_for_time(const MatrixSampleMap &samples, chrono_t time)
+{
+ MatrixSampleMap::const_iterator iter = samples.find(time);
+ if (iter != samples.end()) {
+ return iter->second;
+ }
+
+ return M44d();
+}
+
+/* get the matrix for the specified time, or interpolate between samples if there is no exact match
+ */
+static M44d get_interpolated_matrix_for_time(const MatrixSampleMap &samples, chrono_t time)
+{
+ if (samples.empty()) {
+ return M44d();
+ }
+
+ /* see if exact match */
+ MatrixSampleMap::const_iterator iter = samples.find(time);
+ if (iter != samples.end()) {
+ return iter->second;
+ }
+
+ if (samples.size() == 1) {
+ return samples.begin()->second;
+ }
+
+ if (time <= samples.begin()->first) {
+ return samples.begin()->second;
+ }
+
+ if (time >= samples.rbegin()->first) {
+ return samples.rbegin()->second;
+ }
+
+ /* find previous and next time sample to interpolate */
+ chrono_t prev_time = samples.begin()->first;
+ chrono_t next_time = samples.rbegin()->first;
+
+ for (MatrixSampleMap::const_iterator I = samples.begin(); I != samples.end(); ++I) {
+ chrono_t current_time = (*I).first;
+
+ if (current_time > prev_time && current_time <= time) {
+ prev_time = current_time;
+ }
+
+ if (current_time > next_time && current_time >= time) {
+ next_time = current_time;
+ }
+ }
+
+ const M44d prev_mat = get_matrix_for_time(samples, prev_time);
+ const M44d next_mat = get_matrix_for_time(samples, next_time);
+
+ V3d prev_scale, next_scale;
+ V3d prev_shear, next_shear;
+ V3d prev_translation, next_translation;
+ Quatd prev_rotation, next_rotation;
+
+ transform_decompose(prev_mat, prev_scale, prev_shear, prev_rotation, prev_translation);
+ transform_decompose(next_mat, next_scale, next_shear, next_rotation, next_translation);
+
+ chrono_t t = (time - prev_time) / (next_time - prev_time);
+
+ /* ensure rotation around the shortest angle */
+ if ((prev_rotation ^ next_rotation) < 0) {
+ next_rotation = -next_rotation;
+ }
+
+ return transform_compose(Imath::lerp(prev_scale, next_scale, t),
+ Imath::lerp(prev_shear, next_shear, t),
+ Imath::slerp(prev_rotation, next_rotation, t),
+ Imath::lerp(prev_translation, next_translation, t));
+}
+
+static void concatenate_xform_samples(const MatrixSampleMap &parent_samples,
+ const MatrixSampleMap &local_samples,
+ MatrixSampleMap &output_samples)
+{
+ set<chrono_t> union_of_samples;
+
+ for (const std::pair<chrono_t, M44d> pair : parent_samples) {
+ union_of_samples.insert(pair.first);
+ }
+
+ for (const std::pair<chrono_t, M44d> pair : local_samples) {
+ union_of_samples.insert(pair.first);
+ }
+
+ foreach (chrono_t time, union_of_samples) {
+ M44d parent_matrix = get_interpolated_matrix_for_time(parent_samples, time);
+ M44d local_matrix = get_interpolated_matrix_for_time(local_samples, time);
+
+ output_samples[time] = local_matrix * parent_matrix;
+ }
+}
+
+static Transform make_transform(const M44d &a, float scale)
+{
+ M44d m = convert_yup_zup(a, scale);
+ Transform trans;
+ for (int j = 0; j < 3; j++) {
+ for (int i = 0; i < 4; i++) {
+ trans[j][i] = static_cast<float>(m[i][j]);
+ }
+ }
+ return trans;
+}
+
+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());
+
+ foreach (chrono_t time, times) {
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ const ISampleSelector iss = ISampleSelector(time);
+ const IV2fGeomParam::Sample sample = uvs.getExpandedValue(iss);
+
+ const IV2fGeomParam::Sample uvsample = uvs.getIndexedValue(iss);
+
+ if (!uvsample.valid()) {
+ continue;
+ }
+
+ const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time);
+ const array<int3> *triangles_loops = cached_data.triangles_loops.data_for_time_no_check(time);
+
+ if (!triangles || !triangles_loops) {
+ continue;
+ }
+
+ array<char> data;
+ data.resize(triangles->size() * 3 * sizeof(float2));
+
+ float2 *data_float2 = reinterpret_cast<float2 *>(data.data());
+
+ 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);
+ }
+}
+
+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);
+
+ 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);
+
+ 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);
+
+ SOCKET_STRING(path, "Alembic Path", ustring());
+ SOCKET_NODE_ARRAY(used_shaders, "Used Shaders", &Shader::node_type);
+
+ SOCKET_INT(subd_max_level, "Max Subdivision Level", 1);
+ SOCKET_FLOAT(subd_dicing_rate, "Subdivision Dicing Rate", 1.0f);
+
+ SOCKET_FLOAT(radius_scale, "Radius Scale", 1.0f);
+
+ return type;
+}
+
+AlembicObject::AlembicObject() : Node(node_type)
+{
+}
+
+AlembicObject::~AlembicObject()
+{
+}
+
+void AlembicObject::set_object(Object *object_)
+{
+ object = object_;
+}
+
+Object *AlembicObject::get_object()
+{
+ return object;
+}
+
+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,
+ float scale,
+ Progress &progress)
+{
+ cached_data.clear();
+
+ 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());
+
+ /* 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.
+ *
+ * TODO(@kevindietrich): even for dynamic simulations, this is a waste of memory and
+ * processing time if only the positions are changing in a subsequence of frames but we
+ * cannot optimize in this current system if the attributes are changing over time as well,
+ * as we need valid data for each time point. This can be solved by using reference counting
+ * on the ccl::array and simply share the array across frames. */
+ if (schema.getTopologyVariance() != kHomogenousTopology || cached_data.triangles.size() == 0) {
+ /* 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);
+ }
+
+ if (normals.valid()) {
+ add_normals(sample.getFaceIndices(), normals, time, cached_data);
+ }
+ }
+
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ update_shader_attributes(schema.getArbGeomParams(), progress);
+
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ const IV2fGeomParam &uvs = schema.getUVsParam();
+
+ if (uvs.valid()) {
+ add_uvs(proc, uvs, cached_data, progress);
+ }
+
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ setup_transform_cache(scale);
+
+ data_loaded = true;
+}
+
+void AlembicObject::load_all_data(AlembicProcedural *proc,
+ ISubDSchema &schema,
+ float scale,
+ Progress &progress)
+{
+ cached_data.clear();
+
+ 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);
+ }
+ }
+
+ /* TODO(@kevindietrich) : attributes, need test files */
+
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ setup_transform_cache(scale);
+
+ data_loaded = true;
+}
+
+void AlembicObject::load_all_data(AlembicProcedural *proc,
+ const ICurvesSchema &schema,
+ float scale,
+ Progress &progress,
+ float default_radius)
+{
+ cached_data.clear();
+
+ 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;
+
+ 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);
+ }
+
+ 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);
+ cached_data.curve_first_key.add_data(curve_first_key, time);
+ cached_data.curve_shader.add_data(curve_shader, time);
+ }
+
+ // TODO(@kevindietrich): attributes, need example files
+
+ setup_transform_cache(scale);
+
+ data_loaded = true;
+}
+
+void AlembicObject::setup_transform_cache(float scale)
+{
+ cached_data.transforms.clear();
+ cached_data.transforms.invalidate_last_loaded_time();
+
+ if (xform_samples.size() == 0) {
+ Transform tfm = transform_scale(make_float3(scale));
+ cached_data.transforms.add_data(tfm, 0.0);
+ }
+ else {
+ /* It is possible for a leaf node of the hierarchy to have multiple samples for its transforms
+ * if a sibling has animated transforms. So check if we indeed have animated transformations.
+ */
+ M44d first_matrix = xform_samples.begin()->first;
+ bool has_animation = false;
+ for (const std::pair<chrono_t, M44d> pair : xform_samples) {
+ if (pair.second != first_matrix) {
+ has_animation = true;
+ break;
+ }
+ }
+
+ if (!has_animation) {
+ Transform tfm = make_transform(first_matrix, scale);
+ cached_data.transforms.add_data(tfm, 0.0);
+ }
+ else {
+ for (const std::pair<chrono_t, M44d> pair : xform_samples) {
+ Transform tfm = make_transform(pair.second, scale);
+ cached_data.transforms.add_data(tfm, pair.first);
+ }
+ }
+ }
+}
+
+AttributeRequestSet AlembicObject::get_requested_attributes()
+{
+ AttributeRequestSet requested_attributes;
+
+ Geometry *geometry = object->get_geometry();
+ assert(geometry);
+
+ foreach (Node *node, geometry->get_used_shaders()) {
+ Shader *shader = static_cast<Shader *>(node);
+
+ foreach (const AttributeRequest &attr, shader->attributes.requests) {
+ if (attr.name != "") {
+ requested_attributes.add(attr.name);
+ }
+ }
+ }
+
+ 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);
+ const array<int3> *triangles_loops = cached_data.triangles_loops.data_for_time_no_check(
+ time);
+
+ 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);
+
+ 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);
+
+ 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)
+{
+ set<Attribute *> cached_attributes;
+
+ for (CachedData::CachedAttribute &attribute : cached_data.attributes) {
+ const array<char> *attr_data = attribute.data.data_for_time(frame_time);
+
+ Attribute *attr = nullptr;
+ if (attribute.std != ATTR_STD_NONE) {
+ attr = attributes.add(attribute.std, attribute.name);
+ }
+ else {
+ attr = attributes.add(attribute.name, attribute.type_desc, attribute.element);
+ }
+ assert(attr);
+
+ cached_attributes.insert(attr);
+
+ if (!attr_data) {
+ /* no new data */
+ continue;
+ }
+
+ /* 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());
+ }
+
+ memcpy(attr->data(), attr_data->data(), attr_data->size());
+ }
+
+ /* remove any attributes not in cached_attributes */
+ list<Attribute>::iterator it;
+ for (it = attributes.attributes.begin(); it != attributes.attributes.end();) {
+ if (cached_attributes.find(&(*it)) == cached_attributes.end()) {
+ attributes.attributes.erase(it++);
+ continue;
+ }
+
+ it++;
+ }
+}
+
+NODE_DEFINE(AlembicProcedural)
+{
+ NodeType *type = NodeType::add("alembic", create);
+
+ SOCKET_STRING(filepath, "Filename", ustring());
+ SOCKET_FLOAT(frame, "Frame", 1.0f);
+ SOCKET_FLOAT(start_frame, "Start Frame", 1.0f);
+ SOCKET_FLOAT(end_frame, "End Frame", 1.0f);
+ SOCKET_FLOAT(frame_rate, "Frame Rate", 24.0f);
+ SOCKET_FLOAT(frame_offset, "Frame Offset", 0.0f);
+ SOCKET_FLOAT(default_radius, "Default Radius", 0.01f);
+ SOCKET_FLOAT(scale, "Scale", 1.0f);
+
+ SOCKET_NODE_ARRAY(objects, "Objects", &AlembicObject::node_type);
+
+ return type;
+}
+
+AlembicProcedural::AlembicProcedural() : Procedural(node_type)
+{
+ objects_loaded = false;
+ scene_ = nullptr;
+}
+
+AlembicProcedural::~AlembicProcedural()
+{
+ ccl::set<Geometry *> geometries_set;
+ ccl::set<Object *> objects_set;
+ ccl::set<AlembicObject *> abc_objects_set;
+
+ foreach (Node *node, objects) {
+ AlembicObject *abc_object = static_cast<AlembicObject *>(node);
+
+ objects_set.insert(abc_object->get_object());
+ geometries_set.insert(abc_object->get_object()->get_geometry());
+
+ delete_node(abc_object);
+ }
+
+ scene_->delete_nodes(geometries_set, this);
+ scene_->delete_nodes(objects_set, this);
+}
+
+void AlembicProcedural::generate(Scene *scene, Progress &progress)
+{
+ assert(scene_ == nullptr || scene_ == scene);
+ scene_ = scene;
+
+ bool need_shader_updates = false;
+
+ /* check for changes in shaders (newly requested atttributes) */
+ foreach (Node *object_node, objects) {
+ AlembicObject *object = static_cast<AlembicObject *>(object_node);
+
+ foreach (Node *shader_node, object->get_used_shaders()) {
+ Shader *shader = static_cast<Shader *>(shader_node);
+
+ if (shader->need_update_geometry()) {
+ object->need_shader_update = true;
+ need_shader_updates = true;
+ }
+ }
+ }
+
+ if (!is_modified() && !need_shader_updates) {
+ return;
+ }
+
+ if (!archive.valid()) {
+ Alembic::AbcCoreFactory::IFactory factory;
+ factory.setPolicy(Alembic::Abc::ErrorHandler::kQuietNoopPolicy);
+ archive = factory.getArchive(filepath.c_str());
+
+ if (!archive.valid()) {
+ /* avoid potential infinite update loops in viewport synchronization */
+ filepath.clear();
+ clear_modified();
+ return;
+ }
+ }
+
+ if (!objects_loaded || objects_is_modified()) {
+ load_objects(progress);
+ objects_loaded = true;
+ }
+
+ const chrono_t frame_time = (chrono_t)((frame - frame_offset) / frame_rate);
+
+ foreach (Node *node, objects) {
+ AlembicObject *object = static_cast<AlembicObject *>(node);
+
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ /* skip constant objects */
+ if (object->has_data_loaded() && object->is_constant() && !object->is_modified() &&
+ !object->need_shader_update && !scale_is_modified()) {
+ continue;
+ }
+
+ if (IPolyMesh::matches(object->iobject.getHeader())) {
+ read_mesh(scene, object, frame_time, progress);
+ }
+ else if (ICurves::matches(object->iobject.getHeader())) {
+ read_curves(scene, object, frame_time, progress);
+ }
+ else if (ISubD::matches(object->iobject.getHeader())) {
+ read_subd(scene, object, frame_time, progress);
+ }
+
+ object->clear_modified();
+ }
+
+ clear_modified();
+}
+
+void AlembicProcedural::add_object(AlembicObject *object)
+{
+ objects.push_back_slow(object);
+ tag_objects_modified();
+}
+
+void AlembicProcedural::tag_update(Scene *scene)
+{
+ scene->procedural_manager->tag_update();
+}
+
+AlembicObject *AlembicProcedural::get_or_create_object(const ustring &path)
+{
+ foreach (Node *node, objects) {
+ AlembicObject *object = static_cast<AlembicObject *>(node);
+
+ if (object->get_path() == path) {
+ return object;
+ }
+ }
+
+ AlembicObject *object = create_node<AlembicObject>();
+ object->set_path(path);
+
+ add_object(object);
+
+ return object;
+}
+
+void AlembicProcedural::load_objects(Progress &progress)
+{
+ unordered_map<string, AlembicObject *> object_map;
+
+ foreach (Node *node, objects) {
+ AlembicObject *object = static_cast<AlembicObject *>(node);
+
+ /* only consider newly added objects */
+ if (object->get_object() == nullptr) {
+ object_map.insert({object->get_path().c_str(), object});
+ }
+ }
+
+ IObject root = archive.getTop();
+
+ for (size_t i = 0; i < root.getNumChildren(); ++i) {
+ walk_hierarchy(root, root.getChildHeader(i), nullptr, object_map, progress);
+ }
+}
+
+void AlembicProcedural::read_mesh(Scene *scene,
+ AlembicObject *abc_object,
+ Abc::chrono_t frame_time,
+ Progress &progress)
+{
+ IPolyMesh polymesh(abc_object->iobject, Alembic::Abc::kWrapExisting);
+
+ Mesh *mesh = nullptr;
+
+ /* create a mesh node in the scene if not already done */
+ if (!abc_object->get_object()) {
+ mesh = scene->create_node<Mesh>();
+ mesh->set_owner(this);
+ mesh->name = abc_object->iobject.getName();
+
+ array<Node *> used_shaders = abc_object->get_used_shaders();
+ mesh->set_used_shaders(used_shaders);
+
+ /* create object*/
+ Object *object = scene->create_node<Object>();
+ object->set_owner(this);
+ object->set_geometry(mesh);
+ object->set_tfm(abc_object->xform);
+ object->name = abc_object->iobject.getName();
+
+ abc_object->set_object(object);
+ }
+ else {
+ mesh = static_cast<Mesh *>(abc_object->get_object()->get_geometry());
+ }
+
+ CachedData &cached_data = abc_object->get_cached_data();
+ IPolyMeshSchema schema = polymesh.getSchema();
+
+ if (!abc_object->has_data_loaded()) {
+ abc_object->load_all_data(this, schema, scale, progress);
+ }
+ else {
+ if (abc_object->need_shader_update) {
+ abc_object->update_shader_attributes(schema.getArbGeomParams(), progress);
+ }
+
+ if (scale_is_modified()) {
+ abc_object->setup_transform_cache(scale);
+ }
+ }
+
+ /* update sockets */
+
+ Object *object = abc_object->get_object();
+ cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket());
+
+ 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());
+
+ array<int3> *triangle_data = cached_data.triangles.data_for_time(frame_time);
+ if (triangle_data) {
+ array<int> triangles;
+ array<bool> smooth;
+
+ triangles.reserve(triangle_data->size() * 3);
+ smooth.reserve(triangle_data->size());
+
+ for (size_t i = 0; i < triangle_data->size(); ++i) {
+ int3 tri = (*triangle_data)[i];
+ triangles.push_back_reserved(tri.x);
+ triangles.push_back_reserved(tri.y);
+ triangles.push_back_reserved(tri.z);
+ smooth.push_back_reserved(1);
+ }
+
+ mesh->set_triangles(triangles);
+ mesh->set_smooth(smooth);
+ }
+
+ /* update attributes */
+
+ update_attributes(mesh->attributes, cached_data, frame_time);
+
+ /* we don't yet support arbitrary attributes, for now add vertex
+ * coordinates as generated coordinates if requested */
+ if (mesh->need_attribute(scene, ATTR_STD_GENERATED)) {
+ Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED);
+ memcpy(
+ attr->data_float3(), mesh->get_verts().data(), sizeof(float3) * mesh->get_verts().size());
+ }
+
+ if (mesh->is_modified()) {
+ bool need_rebuild = mesh->triangles_is_modified();
+ mesh->tag_update(scene, need_rebuild);
+ }
+}
+
+void AlembicProcedural::read_subd(Scene *scene,
+ AlembicObject *abc_object,
+ Abc::chrono_t frame_time,
+ Progress &progress)
+{
+ ISubD subd_mesh(abc_object->iobject, Alembic::Abc::kWrapExisting);
+ ISubDSchema schema = subd_mesh.getSchema();
+
+ Mesh *mesh = nullptr;
+
+ /* create a mesh node in the scene if not already done */
+ if (!abc_object->get_object()) {
+ mesh = scene->create_node<Mesh>();
+ mesh->set_owner(this);
+ mesh->name = abc_object->iobject.getName();
+
+ array<Node *> used_shaders = abc_object->get_used_shaders();
+ mesh->set_used_shaders(used_shaders);
+
+ /* Alembic is OpenSubDiv compliant, there is no option to set another subdivision type. */
+ mesh->set_subdivision_type(Mesh::SubdivisionType::SUBDIVISION_CATMULL_CLARK);
+
+ /* create object*/
+ Object *object = scene->create_node<Object>();
+ object->set_owner(this);
+ object->set_geometry(mesh);
+ object->set_tfm(abc_object->xform);
+ object->name = abc_object->iobject.getName();
+
+ abc_object->set_object(object);
+ }
+ else {
+ mesh = static_cast<Mesh *>(abc_object->get_object()->get_geometry());
+ }
+
+ if (!abc_object->has_data_loaded()) {
+ abc_object->load_all_data(this, schema, scale, progress);
+ }
+ else {
+ if (abc_object->need_shader_update) {
+ abc_object->update_shader_attributes(schema.getArbGeomParams(), progress);
+ }
+
+ if (scale_is_modified()) {
+ abc_object->setup_transform_cache(scale);
+ }
+ }
+
+ mesh->set_subd_max_level(abc_object->get_subd_max_level());
+ mesh->set_subd_dicing_rate(abc_object->get_subd_dicing_rate());
+
+ CachedData &cached_data = abc_object->get_cached_data();
+
+ if (abc_object->subd_max_level_is_modified() || abc_object->subd_dicing_rate_is_modified()) {
+ /* need to reset the current data is something changed */
+ cached_data.invalidate_last_loaded_time();
+ }
+
+ /* Cycles overwrites the original triangles when computing displacement, so we always have to
+ * repass the data if something is animated (vertices most likely) to avoid buffer overflows. */
+ if (!cached_data.is_constant()) {
+ cached_data.invalidate_last_loaded_time();
+
+ /* remove previous triangles, if any */
+ array<int> triangles;
+ mesh->set_triangles(triangles);
+ }
+
+ mesh->clear_non_sockets();
+
+ /* udpate sockets */
+
+ Object *object = abc_object->get_object();
+ cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket());
+
+ cached_data.vertices.copy_to_socket(frame_time, mesh, mesh->get_verts_socket());
+
+ /* cached_data.shader is also used for subd_shader */
+ cached_data.shader.copy_to_socket(frame_time, mesh, mesh->get_subd_shader_socket());
+
+ cached_data.subd_start_corner.copy_to_socket(
+ frame_time, mesh, mesh->get_subd_start_corner_socket());
+
+ cached_data.subd_num_corners.copy_to_socket(
+ frame_time, mesh, mesh->get_subd_num_corners_socket());
+
+ cached_data.subd_smooth.copy_to_socket(frame_time, mesh, mesh->get_subd_smooth_socket());
+
+ cached_data.subd_ptex_offset.copy_to_socket(
+ frame_time, mesh, mesh->get_subd_ptex_offset_socket());
+
+ cached_data.subd_face_corners.copy_to_socket(
+ frame_time, mesh, mesh->get_subd_face_corners_socket());
+
+ cached_data.num_ngons.copy_to_socket(frame_time, mesh, mesh->get_num_ngons_socket());
+
+ cached_data.subd_creases_edge.copy_to_socket(
+ frame_time, mesh, mesh->get_subd_creases_edge_socket());
+
+ cached_data.subd_creases_weight.copy_to_socket(
+ frame_time, mesh, mesh->get_subd_creases_weight_socket());
+
+ mesh->set_num_subd_faces(mesh->get_subd_shader().size());
+
+ /* udpate attributes */
+
+ update_attributes(mesh->subd_attributes, cached_data, frame_time);
+
+ /* we don't yet support arbitrary attributes, for now add vertex
+ * coordinates as generated coordinates if requested */
+ if (mesh->need_attribute(scene, ATTR_STD_GENERATED)) {
+ Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED);
+ memcpy(
+ attr->data_float3(), mesh->get_verts().data(), sizeof(float3) * mesh->get_verts().size());
+ }
+
+ if (mesh->is_modified()) {
+ bool need_rebuild = (mesh->triangles_is_modified()) ||
+ (mesh->subd_num_corners_is_modified()) ||
+ (mesh->subd_shader_is_modified()) || (mesh->subd_smooth_is_modified()) ||
+ (mesh->subd_ptex_offset_is_modified()) ||
+ (mesh->subd_start_corner_is_modified()) ||
+ (mesh->subd_face_corners_is_modified());
+
+ mesh->tag_update(scene, need_rebuild);
+ }
+}
+
+void AlembicProcedural::read_curves(Scene *scene,
+ AlembicObject *abc_object,
+ Abc::chrono_t frame_time,
+ Progress &progress)
+{
+ ICurves curves(abc_object->iobject, Alembic::Abc::kWrapExisting);
+ Hair *hair;
+
+ /* create a hair node in the scene if not already done */
+ if (!abc_object->get_object()) {
+ hair = scene->create_node<Hair>();
+ hair->set_owner(this);
+ hair->name = abc_object->iobject.getName();
+
+ array<Node *> used_shaders = abc_object->get_used_shaders();
+ hair->set_used_shaders(used_shaders);
+
+ /* create object*/
+ Object *object = scene->create_node<Object>();
+ object->set_owner(this);
+ object->set_geometry(hair);
+ object->set_tfm(abc_object->xform);
+ object->name = abc_object->iobject.getName();
+
+ abc_object->set_object(object);
+ }
+ else {
+ hair = static_cast<Hair *>(abc_object->get_object()->get_geometry());
+ }
+
+ ICurvesSchema schema = curves.getSchema();
+
+ if (!abc_object->has_data_loaded() || default_radius_is_modified() ||
+ abc_object->radius_scale_is_modified()) {
+ abc_object->load_all_data(this, schema, scale, progress, default_radius);
+ }
+ else {
+ if (scale_is_modified()) {
+ abc_object->setup_transform_cache(scale);
+ }
+ }
+
+ CachedData &cached_data = abc_object->get_cached_data();
+
+ /* update sockets */
+
+ Object *object = abc_object->get_object();
+ cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket());
+
+ cached_data.curve_keys.copy_to_socket(frame_time, hair, hair->get_curve_keys_socket());
+
+ cached_data.curve_radius.copy_to_socket(frame_time, hair, hair->get_curve_radius_socket());
+
+ cached_data.curve_shader.copy_to_socket(frame_time, hair, hair->get_curve_shader_socket());
+
+ cached_data.curve_first_key.copy_to_socket(frame_time, hair, hair->get_curve_first_key_socket());
+
+ /* update attributes */
+
+ update_attributes(hair->attributes, cached_data, frame_time);
+
+ /* we don't yet support arbitrary attributes, for now add first keys as generated coordinates if
+ * requested */
+ if (hair->need_attribute(scene, ATTR_STD_GENERATED)) {
+ Attribute *attr_generated = hair->attributes.add(ATTR_STD_GENERATED);
+ float3 *generated = attr_generated->data_float3();
+
+ for (size_t i = 0; i < hair->num_curves(); i++) {
+ generated[i] = hair->get_curve_keys()[hair->get_curve(i).first_key];
+ }
+ }
+
+ const bool rebuild = (hair->curve_keys_is_modified() || hair->curve_radius_is_modified());
+ hair->tag_update(scene, rebuild);
+}
+
+void AlembicProcedural::walk_hierarchy(
+ IObject parent,
+ const ObjectHeader &header,
+ MatrixSampleMap *xform_samples,
+ const unordered_map<std::string, AlembicObject *> &object_map,
+ Progress &progress)
+{
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ IObject next_object;
+
+ MatrixSampleMap concatenated_xform_samples;
+
+ if (IXform::matches(header)) {
+ IXform xform(parent, header.getName());
+
+ IXformSchema &xs = xform.getSchema();
+
+ if (xs.getNumOps() > 0) {
+ TimeSamplingPtr ts = xs.getTimeSampling();
+ MatrixSampleMap local_xform_samples;
+
+ MatrixSampleMap *temp_xform_samples = nullptr;
+ if (xform_samples == nullptr) {
+ /* If there is no parent transforms, fill the map directly. */
+ temp_xform_samples = &concatenated_xform_samples;
+ }
+ else {
+ /* use a temporary map */
+ temp_xform_samples = &local_xform_samples;
+ }
+
+ for (size_t i = 0; i < xs.getNumSamples(); ++i) {
+ chrono_t sample_time = ts->getSampleTime(index_t(i));
+ XformSample sample = xs.getValue(ISampleSelector(sample_time));
+ temp_xform_samples->insert({sample_time, sample.getMatrix()});
+ }
+
+ if (xform_samples != nullptr) {
+ concatenate_xform_samples(*xform_samples, local_xform_samples, concatenated_xform_samples);
+ }
+
+ xform_samples = &concatenated_xform_samples;
+ }
+
+ next_object = xform;
+ }
+ else if (ISubD::matches(header)) {
+ ISubD subd(parent, header.getName());
+
+ unordered_map<std::string, AlembicObject *>::const_iterator iter;
+ iter = object_map.find(subd.getFullName());
+
+ if (iter != object_map.end()) {
+ AlembicObject *abc_object = iter->second;
+ abc_object->iobject = subd;
+
+ if (xform_samples) {
+ abc_object->xform_samples = *xform_samples;
+ }
+ }
+
+ next_object = subd;
+ }
+ else if (IPolyMesh::matches(header)) {
+ IPolyMesh mesh(parent, header.getName());
+
+ unordered_map<std::string, AlembicObject *>::const_iterator iter;
+ iter = object_map.find(mesh.getFullName());
+
+ if (iter != object_map.end()) {
+ AlembicObject *abc_object = iter->second;
+ abc_object->iobject = mesh;
+
+ if (xform_samples) {
+ abc_object->xform_samples = *xform_samples;
+ }
+ }
+
+ next_object = mesh;
+ }
+ else if (ICurves::matches(header)) {
+ ICurves curves(parent, header.getName());
+
+ unordered_map<std::string, AlembicObject *>::const_iterator iter;
+ iter = object_map.find(curves.getFullName());
+
+ if (iter != object_map.end()) {
+ AlembicObject *abc_object = iter->second;
+ abc_object->iobject = curves;
+
+ if (xform_samples) {
+ abc_object->xform_samples = *xform_samples;
+ }
+ }
+
+ next_object = curves;
+ }
+ else if (IFaceSet::matches(header)) {
+ // ignore the face set, it will be read along with the data
+ }
+ else {
+ // unsupported type for now (Points, NuPatch)
+ next_object = parent.getChild(header.getName());
+ }
+
+ if (next_object.valid()) {
+ for (size_t i = 0; i < next_object.getNumChildren(); ++i) {
+ walk_hierarchy(
+ next_object, next_object.getChildHeader(i), xform_samples, object_map, progress);
+ }
+ }
+}
+
+CCL_NAMESPACE_END
+
+#endif
diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h
new file mode 100644
index 00000000000..77bdac09ba2
--- /dev/null
+++ b/intern/cycles/render/alembic.h
@@ -0,0 +1,404 @@
+/*
+ * Copyright 2011-2018 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "graph/node.h"
+#include "render/attribute.h"
+#include "render/procedural.h"
+#include "util/util_set.h"
+#include "util/util_transform.h"
+#include "util/util_vector.h"
+
+#ifdef WITH_ALEMBIC
+
+# include <Alembic/AbcCoreFactory/All.h>
+# include <Alembic/AbcGeom/All.h>
+
+CCL_NAMESPACE_BEGIN
+
+class AlembicProcedural;
+class Geometry;
+class Object;
+class Progress;
+class Shader;
+
+using MatrixSampleMap = std::map<Alembic::Abc::chrono_t, Alembic::Abc::M44d>;
+
+/* Helpers to detect if some type is a ccl::array. */
+template<typename> struct is_array : public std::false_type {
+};
+
+template<typename T> struct is_array<array<T>> : public std::true_type {
+};
+
+/* Store the data set for an animation at every time points, or at the beginning of the animation
+ * for constant data.
+ *
+ * The data is supposed to be stored in chronological order, and is looked up using the current
+ * animation time in seconds using the TimeSampling from the Alembic property. */
+template<typename T> class DataStore {
+ struct DataTimePair {
+ double time = 0;
+ T data{};
+ };
+
+ vector<DataTimePair> data{};
+ Alembic::AbcCoreAbstract::TimeSampling time_sampling{};
+
+ double last_loaded_time = std::numeric_limits<double>::max();
+
+ public:
+ void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling_)
+ {
+ time_sampling = time_sampling_;
+ }
+
+ Alembic::AbcCoreAbstract::TimeSampling get_time_sampling() const
+ {
+ return time_sampling;
+ }
+
+ /* Get the data for the specified time.
+ * Return nullptr if there is no data or if the data for this time was already loaded. */
+ T *data_for_time(double time)
+ {
+ if (size() == 0) {
+ return nullptr;
+ }
+
+ std::pair<size_t, Alembic::Abc::chrono_t> index_pair;
+ index_pair = time_sampling.getNearIndex(time, data.size());
+ DataTimePair &data_pair = data[index_pair.first];
+
+ if (last_loaded_time == data_pair.time) {
+ return nullptr;
+ }
+
+ last_loaded_time = data_pair.time;
+
+ return &data_pair.data;
+ }
+
+ /* get the data for the specified time, but do not check if the data was already loaded for this
+ * time return nullptr if there is no data */
+ T *data_for_time_no_check(double time)
+ {
+ if (size() == 0) {
+ return nullptr;
+ }
+
+ std::pair<size_t, Alembic::Abc::chrono_t> index_pair;
+ index_pair = time_sampling.getNearIndex(time, data.size());
+ DataTimePair &data_pair = data[index_pair.first];
+ return &data_pair.data;
+ }
+
+ void add_data(T &data_, double time)
+ {
+ if constexpr (is_array<T>::value) {
+ data.emplace_back();
+ data.back().data.steal_data(data_);
+ data.back().time = time;
+ return;
+ }
+
+ data.push_back({time, data_});
+ }
+
+ bool is_constant() const
+ {
+ return data.size() <= 1;
+ }
+
+ size_t size() const
+ {
+ return data.size();
+ }
+
+ void clear()
+ {
+ invalidate_last_loaded_time();
+ data.clear();
+ }
+
+ void invalidate_last_loaded_time()
+ {
+ last_loaded_time = std::numeric_limits<double>::max();
+ }
+
+ /* Copy the data for the specified time to the node's socket. If there is no
+ * data for this time or it was already loaded, do nothing. */
+ void copy_to_socket(double time, Node *node, const SocketType *socket)
+ {
+ T *data_ = data_for_time(time);
+
+ if (data_ == nullptr) {
+ return;
+ }
+
+ /* TODO(kevindietrich): arrays are emptied when passed to the sockets, so for now we copy the
+ * arrays to avoid reloading the data */
+ T value = *data_;
+ node->set(*socket, value);
+ }
+};
+
+/* Actual cache for the stored data.
+ * This caches the topological, transformation, and attribute data for a Mesh node or a Hair node
+ * inside of DataStores.
+ */
+struct CachedData {
+ DataStore<Transform> transforms{};
+
+ /* mesh data */
+ DataStore<array<float3>> vertices;
+ DataStore<array<int3>> triangles{};
+ /* triangle "loops" are the polygons' vertices indices used for indexing face varying attributes
+ * (like UVs) */
+ DataStore<array<int3>> triangles_loops{};
+ DataStore<array<int>> shader{};
+
+ /* subd data */
+ DataStore<array<int>> subd_start_corner;
+ DataStore<array<int>> subd_num_corners;
+ DataStore<array<bool>> subd_smooth;
+ DataStore<array<int>> subd_ptex_offset;
+ DataStore<array<int>> subd_face_corners;
+ DataStore<int> num_ngons;
+ DataStore<array<int>> subd_creases_edge;
+ DataStore<array<float>> subd_creases_weight;
+
+ /* hair data */
+ DataStore<array<float3>> curve_keys;
+ DataStore<array<float>> curve_radius;
+ DataStore<array<int>> curve_first_key;
+ DataStore<array<int>> curve_shader;
+
+ struct CachedAttribute {
+ AttributeStandard std;
+ AttributeElement element;
+ TypeDesc type_desc;
+ ustring name;
+ DataStore<array<char>> data{};
+ };
+
+ vector<CachedAttribute> attributes{};
+
+ void clear();
+
+ CachedAttribute &add_attribute(const ustring &name,
+ const Alembic::Abc::TimeSampling &time_sampling);
+
+ bool is_constant() const;
+
+ void invalidate_last_loaded_time(bool attributes_only = false);
+
+ void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling);
+};
+
+/* Representation of an Alembic object for the AlembicProcedural.
+ *
+ * The AlembicObject holds the path to the Alembic IObject inside of the archive that is desired
+ * for rendering, as well as the list of shaders that it is using.
+ *
+ * The names of the shaders should correspond to the names of the FaceSets inside of the Alembic
+ * archive for per-triangle shader association. If there is no FaceSets, or the names do not
+ * match, the first shader is used for rendering for all triangles.
+ */
+class AlembicObject : public Node {
+ public:
+ NODE_DECLARE
+
+ /* Path to the IObject inside of the archive. */
+ NODE_SOCKET_API(ustring, path)
+
+ /* Shaders used for rendering. */
+ NODE_SOCKET_API_ARRAY(array<Node *>, used_shaders)
+
+ /* Maximum number of subdivisions for ISubD objects. */
+ NODE_SOCKET_API(int, subd_max_level)
+
+ /* Finest level of detail (in pixels) for the subdivision. */
+ NODE_SOCKET_API(float, subd_dicing_rate)
+
+ /* Scale the radius of points and curves. */
+ NODE_SOCKET_API(float, radius_scale)
+
+ AlembicObject();
+ ~AlembicObject();
+
+ private:
+ friend class AlembicProcedural;
+
+ void set_object(Object *object);
+ Object *get_object();
+
+ void load_all_data(AlembicProcedural *proc,
+ Alembic::AbcGeom::IPolyMeshSchema &schema,
+ float scale,
+ Progress &progress);
+ void load_all_data(AlembicProcedural *proc,
+ Alembic::AbcGeom::ISubDSchema &schema,
+ float scale,
+ Progress &progress);
+ void load_all_data(AlembicProcedural *proc,
+ const Alembic::AbcGeom::ICurvesSchema &schema,
+ float scale,
+ Progress &progress,
+ float default_radius);
+
+ bool has_data_loaded() const;
+
+ bool need_shader_update = true;
+
+ MatrixSampleMap xform_samples;
+ Alembic::AbcGeom::IObject iobject;
+ Transform xform;
+
+ CachedData &get_cached_data()
+ {
+ return cached_data;
+ }
+
+ bool is_constant() const
+ {
+ return cached_data.is_constant();
+ }
+
+ Object *object = nullptr;
+
+ bool data_loaded = false;
+
+ CachedData cached_data;
+
+ void update_shader_attributes(const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,
+ Progress &progress);
+
+ void read_attribute(const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,
+ const ustring &attr_name,
+ Progress &progress);
+
+ template<typename SchemaType>
+ void read_face_sets(SchemaType &schema,
+ array<int> &polygon_to_shader,
+ Alembic::AbcGeom::ISampleSelector sample_sel);
+
+ void setup_transform_cache(float scale);
+
+ AttributeRequestSet get_requested_attributes();
+};
+
+/* Procedural to render objects from a single Alembic archive.
+ *
+ * Every object desired to be rendered should be passed as an AlembicObject through the objects
+ * socket.
+ *
+ * This procedural will load the data set for the entire animation in memory on the first frame,
+ * and directly set the data for the new frames on the created Nodes if needed. This allows for
+ * faster updates between frames as it avoids reseeking the data on disk.
+ */
+class AlembicProcedural : public Procedural {
+ Alembic::AbcGeom::IArchive archive;
+ bool objects_loaded;
+ Scene *scene_;
+
+ public:
+ NODE_DECLARE
+
+ /* The file path to the Alembic archive */
+ NODE_SOCKET_API(ustring, filepath)
+
+ /* The current frame to render. */
+ NODE_SOCKET_API(float, frame)
+
+ /* The first frame to load data for. */
+ NODE_SOCKET_API(float, start_frame)
+
+ /* The last frame to load data for. */
+ NODE_SOCKET_API(float, end_frame)
+
+ /* Subtracted to the current frame. */
+ NODE_SOCKET_API(float, frame_offset)
+
+ /* The frame rate used for rendering in units of frames per second. */
+ NODE_SOCKET_API(float, frame_rate)
+
+ /* List of AlembicObjects to render. */
+ NODE_SOCKET_API_ARRAY(array<Node *>, objects)
+
+ /* Set the default radius to use for curves when the Alembic Curves Schemas do not have radius
+ * information. */
+ NODE_SOCKET_API(float, default_radius)
+
+ /* Multiplier to account for differences in default units for measuring objects in various
+ * software. */
+ NODE_SOCKET_API(float, scale)
+
+ AlembicProcedural();
+ ~AlembicProcedural();
+
+ /* Populates the Cycles scene with Nodes for every contained AlembicObject on the first
+ * invocation, and updates the data on subsequent invocations if the frame changed. */
+ void generate(Scene *scene, Progress &progress);
+
+ /* Add an object to our list of objects, and tag the socket as modified. */
+ void add_object(AlembicObject *object);
+
+ /* Tag for an update only if something was modified. */
+ void tag_update(Scene *scene);
+
+ /* Returns a pointer to an exisiting or a newly created AlembicObject for the given path. */
+ AlembicObject *get_or_create_object(const ustring &path);
+
+ private:
+ /* Load the data for all the objects whose data has not yet been loaded. */
+ void load_objects(Progress &progress);
+
+ /* Traverse the Alembic hierarchy to lookup the IObjects for the AlembicObjects that were
+ * specified in our objects socket, and accumulate all of the transformations samples along the
+ * way for each IObject. */
+ void walk_hierarchy(Alembic::AbcGeom::IObject parent,
+ const Alembic::AbcGeom::ObjectHeader &ohead,
+ MatrixSampleMap *xform_samples,
+ const unordered_map<string, AlembicObject *> &object_map,
+ Progress &progress);
+
+ /* Read the data for an IPolyMesh at the specified frame_time. Creates corresponding Geometry and
+ * Object Nodes in the Cycles scene if none exist yet. */
+ void read_mesh(Scene *scene,
+ AlembicObject *abc_object,
+ Alembic::AbcGeom::Abc::chrono_t frame_time,
+ Progress &progress);
+
+ /* Read the data for an ICurves at the specified frame_time. Creates corresponding Geometry and
+ * Object Nodes in the Cycles scene if none exist yet. */
+ void read_curves(Scene *scene,
+ AlembicObject *abc_object,
+ Alembic::AbcGeom::Abc::chrono_t frame_time,
+ Progress &progress);
+
+ /* Read the data for an ISubD at the specified frame_time. Creates corresponding Geometry and
+ * Object Nodes in the Cycles scene if none exist yet. */
+ void read_subd(Scene *scene,
+ AlembicObject *abc_object,
+ Alembic::AbcGeom::Abc::chrono_t frame_time,
+ Progress &progress);
+};
+
+CCL_NAMESPACE_END
+
+#endif
diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp
index b478aae9ae2..ce4ae6e4295 100644
--- a/intern/cycles/render/attribute.cpp
+++ b/intern/cycles/render/attribute.cpp
@@ -28,7 +28,7 @@ CCL_NAMESPACE_BEGIN
Attribute::Attribute(
ustring name, TypeDesc type, AttributeElement element, Geometry *geom, AttributePrimitive prim)
- : name(name), std(ATTR_STD_NONE), type(type), element(element), flags(0)
+ : name(name), std(ATTR_STD_NONE), type(type), element(element), flags(0), modified(true)
{
/* string and matrix not supported! */
assert(type == TypeDesc::TypeFloat || type == TypeDesc::TypeColor ||
@@ -82,6 +82,8 @@ void Attribute::add(const float &f)
for (size_t i = 0; i < size; i++)
buffer.push_back(data[i]);
+
+ modified = true;
}
void Attribute::add(const uchar4 &f)
@@ -93,6 +95,8 @@ void Attribute::add(const uchar4 &f)
for (size_t i = 0; i < size; i++)
buffer.push_back(data[i]);
+
+ modified = true;
}
void Attribute::add(const float2 &f)
@@ -104,6 +108,8 @@ void Attribute::add(const float2 &f)
for (size_t i = 0; i < size; i++)
buffer.push_back(data[i]);
+
+ modified = true;
}
void Attribute::add(const float3 &f)
@@ -115,6 +121,8 @@ void Attribute::add(const float3 &f)
for (size_t i = 0; i < size; i++)
buffer.push_back(data[i]);
+
+ modified = true;
}
void Attribute::add(const Transform &f)
@@ -126,6 +134,8 @@ void Attribute::add(const Transform &f)
for (size_t i = 0; i < size; i++)
buffer.push_back(data[i]);
+
+ modified = true;
}
void Attribute::add(const char *data)
@@ -134,6 +144,26 @@ void Attribute::add(const char *data)
for (size_t i = 0; i < size; i++)
buffer.push_back(data[i]);
+
+ modified = true;
+}
+
+void Attribute::set_data_from(Attribute &&other)
+{
+ assert(other.std == std);
+ assert(other.type == type);
+ assert(other.element == element);
+
+ this->flags = other.flags;
+
+ if (this->buffer.size() != other.buffer.size()) {
+ this->buffer = std::move(other.buffer);
+ modified = true;
+ }
+ else if (memcmp(this->data(), other.data(), other.buffer.size()) != 0) {
+ this->buffer = std::move(other.buffer);
+ modified = true;
+ }
}
size_t Attribute::data_sizeof() const
@@ -627,6 +657,42 @@ void AttributeSet::clear(bool preserve_voxel_data)
}
}
+void AttributeSet::update(AttributeSet &&new_attributes)
+{
+ /* add or update old_attributes based on the new_attributes */
+ foreach (Attribute &attr, new_attributes.attributes) {
+ Attribute *nattr = add(attr.name, attr.type, attr.element);
+ nattr->std = attr.std;
+ nattr->set_data_from(std::move(attr));
+ }
+
+ /* remove any attributes not on new_attributes */
+ list<Attribute>::iterator it;
+ for (it = attributes.begin(); it != attributes.end();) {
+ if (it->std != ATTR_STD_NONE) {
+ if (new_attributes.find(it->std) == nullptr) {
+ attributes.erase(it++);
+ continue;
+ }
+ }
+ else if (it->name != "") {
+ if (new_attributes.find(it->name) == nullptr) {
+ attributes.erase(it++);
+ continue;
+ }
+ }
+
+ it++;
+ }
+}
+
+void AttributeSet::clear_modified()
+{
+ foreach (Attribute &attr, attributes) {
+ attr.modified = false;
+ }
+}
+
/* AttributeRequest */
AttributeRequest::AttributeRequest(ustring name_)
diff --git a/intern/cycles/render/attribute.h b/intern/cycles/render/attribute.h
index 9990a1325a7..f9997d3c422 100644
--- a/intern/cycles/render/attribute.h
+++ b/intern/cycles/render/attribute.h
@@ -54,6 +54,8 @@ class Attribute {
AttributeElement element;
uint flags; /* enum AttributeFlag */
+ bool modified;
+
Attribute(ustring name,
TypeDesc type,
AttributeElement element,
@@ -159,6 +161,8 @@ class Attribute {
void add(const Transform &tfm);
void add(const char *data);
+ void set_data_from(Attribute &&other);
+
static bool same_storage(TypeDesc a, TypeDesc b);
static const char *standard_name(AttributeStandard std);
static AttributeStandard name_standard(const char *name);
@@ -194,6 +198,12 @@ class AttributeSet {
void resize(bool reserve_only = false);
void clear(bool preserve_voxel_data = false);
+
+ /* Update the attributes in this AttributeSet with the ones from the new set,
+ * and remove any attribute not found on the new set from this. */
+ void update(AttributeSet &&new_attributes);
+
+ void clear_modified();
};
/* AttributeRequest
diff --git a/intern/cycles/render/background.cpp b/intern/cycles/render/background.cpp
index 7bdcb1578c3..1303f894912 100644
--- a/intern/cycles/render/background.cpp
+++ b/intern/cycles/render/background.cpp
@@ -130,8 +130,9 @@ void Background::device_free(Device * /*device*/, DeviceScene * /*dscene*/)
void Background::tag_update(Scene *scene)
{
- scene->integrator->tag_update(scene);
- tag_modified();
+ if (ao_factor_is_modified() || use_ao_is_modified()) {
+ scene->integrator->tag_update(scene, Integrator::BACKGROUND_AO_MODIFIED);
+ }
}
Shader *Background::get_shader(const Scene *scene)
diff --git a/intern/cycles/render/bake.cpp b/intern/cycles/render/bake.cpp
index 439ebdedb8e..317a3937cab 100644
--- a/intern/cycles/render/bake.cpp
+++ b/intern/cycles/render/bake.cpp
@@ -78,7 +78,7 @@ BakeManager::BakeManager()
type = SHADER_EVAL_BAKE;
pass_filter = 0;
- need_update = true;
+ need_update_ = true;
}
BakeManager::~BakeManager()
@@ -114,9 +114,9 @@ void BakeManager::set(Scene *scene,
/* create device and update scene */
scene->film->tag_modified();
- scene->integrator->tag_update(scene);
+ scene->integrator->tag_update(scene, Integrator::UPDATE_ALL);
- need_update = true;
+ need_update_ = true;
}
void BakeManager::device_update(Device * /*device*/,
@@ -124,7 +124,7 @@ void BakeManager::device_update(Device * /*device*/,
Scene *scene,
Progress & /* progress */)
{
- if (!need_update)
+ if (!need_update())
return;
scoped_callback_timer timer([scene](double time) {
@@ -152,11 +152,21 @@ void BakeManager::device_update(Device * /*device*/,
object_index++;
}
- need_update = false;
+ need_update_ = false;
}
void BakeManager::device_free(Device * /*device*/, DeviceScene * /*dscene*/)
{
}
+void BakeManager::tag_update()
+{
+ need_update_ = true;
+}
+
+bool BakeManager::need_update() const
+{
+ return need_update_;
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/bake.h b/intern/cycles/render/bake.h
index 93e664c2ab1..655b9b1cf7e 100644
--- a/intern/cycles/render/bake.h
+++ b/intern/cycles/render/bake.h
@@ -36,9 +36,12 @@ class BakeManager {
void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
void device_free(Device *device, DeviceScene *dscene);
- bool need_update;
+ void tag_update();
+
+ bool need_update() const;
private:
+ bool need_update_;
ShaderEvalType type;
int pass_filter;
std::string object_name;
diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp
index 5c3778f6ae5..9b7657802d6 100644
--- a/intern/cycles/render/film.cpp
+++ b/intern/cycles/render/film.cpp
@@ -688,16 +688,16 @@ void Film::device_free(Device * /*device*/, DeviceScene * /*dscene*/, Scene *sce
void Film::tag_passes_update(Scene *scene, const vector<Pass> &passes_, bool update_passes)
{
if (Pass::contains(scene->passes, PASS_UV) != Pass::contains(passes_, PASS_UV)) {
- scene->geometry_manager->tag_update(scene);
+ scene->geometry_manager->tag_update(scene, GeometryManager::UV_PASS_NEEDED);
foreach (Shader *shader, scene->shaders)
- shader->need_update_geometry = true;
+ shader->need_update_uvs = true;
}
else if (Pass::contains(scene->passes, PASS_MOTION) != Pass::contains(passes_, PASS_MOTION)) {
- scene->geometry_manager->tag_update(scene);
+ scene->geometry_manager->tag_update(scene, GeometryManager::MOTION_PASS_NEEDED);
}
else if (Pass::contains(scene->passes, PASS_AO) != Pass::contains(passes_, PASS_AO)) {
- scene->integrator->tag_update(scene);
+ scene->integrator->tag_update(scene, Integrator::AO_PASS_MODIFIED);
}
if (update_passes) {
diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp
index 6fc217f2d76..b6fe8a66b87 100644
--- a/intern/cycles/render/geometry.cpp
+++ b/intern/cycles/render/geometry.cpp
@@ -240,7 +240,6 @@ void Geometry::compute_bvh(
}
}
- clear_modified();
need_update_rebuild = false;
}
@@ -262,22 +261,21 @@ bool Geometry::has_voxel_attributes() const
void Geometry::tag_update(Scene *scene, bool rebuild)
{
- tag_modified();
-
if (rebuild) {
need_update_rebuild = true;
- scene->light_manager->need_update = true;
+ scene->light_manager->tag_update(scene, LightManager::MESH_NEED_REBUILD);
}
else {
foreach (Node *node, used_shaders) {
Shader *shader = static_cast<Shader *>(node);
- if (shader->has_surface_emission)
- scene->light_manager->need_update = true;
+ if (shader->has_surface_emission) {
+ scene->light_manager->tag_update(scene, LightManager::EMISSIVE_MESH_MODIFIED);
+ break;
+ }
}
}
- scene->geometry_manager->need_update = true;
- scene->object_manager->need_update = true;
+ scene->geometry_manager->tag_update(scene, GeometryManager::GEOMETRY_MODIFIED);
}
void Geometry::tag_bvh_update(bool rebuild)
@@ -293,7 +291,7 @@ void Geometry::tag_bvh_update(bool rebuild)
GeometryManager::GeometryManager()
{
- need_update = true;
+ update_flags = UPDATE_ALL;
need_flags_update = true;
}
@@ -494,6 +492,10 @@ void GeometryManager::update_svm_attributes(Device *,
if (attr_map_size == 0)
return;
+ if (!dscene->attributes_map.need_realloc()) {
+ return;
+ }
+
/* create attribute map */
uint4 *attr_map = dscene->attributes_map.alloc(attr_map_size);
memset(attr_map, 0, dscene->attributes_map.size() * sizeof(uint));
@@ -602,8 +604,10 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
offset = attr_uchar4_offset;
assert(attr_uchar4.size() >= offset + size);
- for (size_t k = 0; k < size; k++) {
- attr_uchar4[offset + k] = data[k];
+ if (mattr->modified) {
+ for (size_t k = 0; k < size; k++) {
+ attr_uchar4[offset + k] = data[k];
+ }
}
attr_uchar4_offset += size;
}
@@ -612,8 +616,10 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
offset = attr_float_offset;
assert(attr_float.size() >= offset + size);
- for (size_t k = 0; k < size; k++) {
- attr_float[offset + k] = data[k];
+ if (mattr->modified) {
+ for (size_t k = 0; k < size; k++) {
+ attr_float[offset + k] = data[k];
+ }
}
attr_float_offset += size;
}
@@ -622,8 +628,10 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
offset = attr_float2_offset;
assert(attr_float2.size() >= offset + size);
- for (size_t k = 0; k < size; k++) {
- attr_float2[offset + k] = data[k];
+ if (mattr->modified) {
+ for (size_t k = 0; k < size; k++) {
+ attr_float2[offset + k] = data[k];
+ }
}
attr_float2_offset += size;
}
@@ -632,8 +640,10 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
offset = attr_float3_offset;
assert(attr_float3.size() >= offset + size * 3);
- for (size_t k = 0; k < size * 3; k++) {
- attr_float3[offset + k] = (&tfm->x)[k];
+ if (mattr->modified) {
+ for (size_t k = 0; k < size * 3; k++) {
+ attr_float3[offset + k] = (&tfm->x)[k];
+ }
}
attr_float3_offset += size * 3;
}
@@ -642,8 +652,10 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom,
offset = attr_float3_offset;
assert(attr_float3.size() >= offset + size);
- for (size_t k = 0; k < size; k++) {
- attr_float3[offset + k] = data[k];
+ if (mattr->modified) {
+ for (size_t k = 0; k < size; k++) {
+ attr_float3[offset + k] = data[k];
+ }
}
attr_float3_offset += size;
}
@@ -808,6 +820,11 @@ 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();
+
size_t attr_float_offset = 0;
size_t attr_float2_offset = 0;
size_t attr_float3_offset = 0;
@@ -822,6 +839,12 @@ void GeometryManager::device_update_attributes(Device *device,
* they actually refer to the same mesh attributes, optimize */
foreach (AttributeRequest &req, attributes.requests) {
Attribute *attr = geom->attributes.find(req);
+
+ if (attr) {
+ /* force a copy if we need to reallocate all the data */
+ attr->modified |= copy_all_data;
+ }
+
update_attribute_element_offset(geom,
dscene->attributes_float,
attr_float_offset,
@@ -840,6 +863,11 @@ void GeometryManager::device_update_attributes(Device *device,
Mesh *mesh = static_cast<Mesh *>(geom);
Attribute *subd_attr = mesh->subd_attributes.find(req);
+ if (subd_attr) {
+ /* force a copy if we need to reallocate all the data */
+ subd_attr->modified |= copy_all_data;
+ }
+
update_attribute_element_offset(mesh,
dscene->attributes_float,
attr_float_offset,
@@ -903,18 +931,10 @@ void GeometryManager::device_update_attributes(Device *device,
/* copy to device */
progress.set_status("Updating Mesh", "Copying Attributes to device");
- if (dscene->attributes_float.size()) {
- dscene->attributes_float.copy_to_device();
- }
- if (dscene->attributes_float2.size()) {
- dscene->attributes_float2.copy_to_device();
- }
- if (dscene->attributes_float3.size()) {
- dscene->attributes_float3.copy_to_device();
- }
- if (dscene->attributes_uchar4.size()) {
- dscene->attributes_uchar4.copy_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();
if (progress.get_cancel())
return;
@@ -1066,17 +1086,34 @@ void GeometryManager::device_update_mesh(
uint *tri_patch = dscene->tri_patch.alloc(tri_size);
float2 *tri_patch_uv = dscene->tri_patch_uv.alloc(vert_size);
+ const bool copy_all_data = dscene->tri_shader.need_realloc() ||
+ dscene->tri_vindex.need_realloc() ||
+ dscene->tri_vnormal.need_realloc() ||
+ dscene->tri_patch.need_realloc() ||
+ dscene->tri_patch_uv.need_realloc();
+
foreach (Geometry *geom, scene->geometry) {
if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) {
Mesh *mesh = static_cast<Mesh *>(geom);
- mesh->pack_shaders(scene, &tri_shader[mesh->prim_offset]);
- mesh->pack_normals(&vnormal[mesh->vert_offset]);
- mesh->pack_verts(tri_prim_index,
- &tri_vindex[mesh->prim_offset],
- &tri_patch[mesh->prim_offset],
- &tri_patch_uv[mesh->vert_offset],
- mesh->vert_offset,
- mesh->prim_offset);
+
+ if (mesh->shader_is_modified() || mesh->smooth_is_modified() ||
+ mesh->triangles_is_modified() || copy_all_data) {
+ mesh->pack_shaders(scene, &tri_shader[mesh->prim_offset]);
+ }
+
+ if (mesh->verts_is_modified() || copy_all_data) {
+ mesh->pack_normals(&vnormal[mesh->vert_offset]);
+ }
+
+ if (mesh->triangles_is_modified() || mesh->vert_patch_uv_is_modified() || copy_all_data) {
+ mesh->pack_verts(tri_prim_index,
+ &tri_vindex[mesh->prim_offset],
+ &tri_patch[mesh->prim_offset],
+ &tri_patch_uv[mesh->vert_offset],
+ mesh->vert_offset,
+ mesh->prim_offset);
+ }
+
if (progress.get_cancel())
return;
}
@@ -1085,11 +1122,11 @@ void GeometryManager::device_update_mesh(
/* vertex coordinates */
progress.set_status("Updating Mesh", "Copying Mesh to device");
- dscene->tri_shader.copy_to_device();
- dscene->tri_vnormal.copy_to_device();
- dscene->tri_vindex.copy_to_device();
- dscene->tri_patch.copy_to_device();
- dscene->tri_patch_uv.copy_to_device();
+ dscene->tri_shader.copy_to_device_if_modified();
+ dscene->tri_vnormal.copy_to_device_if_modified();
+ dscene->tri_vindex.copy_to_device_if_modified();
+ dscene->tri_patch.copy_to_device_if_modified();
+ dscene->tri_patch_uv.copy_to_device_if_modified();
}
if (curve_size != 0) {
@@ -1098,9 +1135,21 @@ void GeometryManager::device_update_mesh(
float4 *curve_keys = dscene->curve_keys.alloc(curve_key_size);
float4 *curves = dscene->curves.alloc(curve_size);
+ const bool copy_all_data = dscene->curve_keys.need_realloc() || dscene->curves.need_realloc();
+
foreach (Geometry *geom, scene->geometry) {
if (geom->is_hair()) {
Hair *hair = static_cast<Hair *>(geom);
+
+ bool curve_keys_co_modified = hair->curve_radius_is_modified() ||
+ hair->curve_keys_is_modified();
+ bool curve_data_modified = hair->curve_shader_is_modified() ||
+ hair->curve_first_key_is_modified();
+
+ if (!curve_keys_co_modified && !curve_data_modified && !copy_all_data) {
+ continue;
+ }
+
hair->pack_curves(scene,
&curve_keys[hair->curvekey_offset],
&curves[hair->prim_offset],
@@ -1110,11 +1159,11 @@ void GeometryManager::device_update_mesh(
}
}
- dscene->curve_keys.copy_to_device();
- dscene->curves.copy_to_device();
+ dscene->curve_keys.copy_to_device_if_modified();
+ dscene->curves.copy_to_device_if_modified();
}
- if (patch_size != 0) {
+ if (patch_size != 0 && dscene->patches.need_realloc()) {
progress.set_status("Updating Mesh", "Copying Patches to device");
uint *patch_data = dscene->patches.alloc(patch_size);
@@ -1180,16 +1229,25 @@ void GeometryManager::device_update_bvh(Device *device,
VLOG(1) << "Using " << bvh_layout_name(bparams.bvh_layout) << " layout.";
- delete scene->bvh;
- BVH *bvh = scene->bvh = BVH::create(bparams, scene->geometry, scene->objects, device);
- device->build_bvh(bvh, progress, false);
+ const bool can_refit = scene->bvh != nullptr &&
+ (bparams.bvh_layout == BVHLayout::BVH_LAYOUT_OPTIX);
+ const bool pack_all = scene->bvh == nullptr;
+
+ BVH *bvh = scene->bvh;
+ if (!scene->bvh) {
+ bvh = scene->bvh = BVH::create(bparams, scene->geometry, scene->objects, device);
+ }
+
+ device->build_bvh(bvh, progress, can_refit);
if (progress.get_cancel()) {
return;
}
+ const bool has_bvh2_layout = (bparams.bvh_layout == BVH_LAYOUT_BVH2);
+
PackedBVH pack;
- if (bparams.bvh_layout == BVH_LAYOUT_BVH2) {
+ if (has_bvh2_layout) {
pack = std::move(static_cast<BVH2 *>(bvh)->pack);
}
else {
@@ -1210,12 +1268,22 @@ void GeometryManager::device_update_bvh(Device *device,
}
pack.root_index = -1;
- pack.prim_tri_index.reserve(num_prims);
- pack.prim_tri_verts.reserve(num_tri_verts);
- pack.prim_type.reserve(num_prims);
- pack.prim_index.reserve(num_prims);
- pack.prim_object.reserve(num_prims);
- pack.prim_visibility.reserve(num_prims);
+
+ if (!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);
+ }
+ else {
+ /* It is not strictly necessary to skip those resizes we if do not have to repack, as the OS
+ * will not allocate pages if we do not touch them, however it does help catching bugs. */
+ pack.prim_tri_index.resize(num_prims);
+ pack.prim_tri_verts.resize(num_tri_verts);
+ pack.prim_type.resize(num_prims);
+ pack.prim_index.resize(num_prims);
+ pack.prim_object.resize(num_prims);
+ pack.prim_visibility.resize(num_prims);
+ }
// Merge visibility flags of all objects and find object index for non-instanced geometry
unordered_map<const Geometry *, pair<int, uint>> geometry_to_object_info;
@@ -1229,17 +1297,27 @@ void GeometryManager::device_update_bvh(Device *device,
}
}
+ TaskPool pool;
// 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()) {
+ continue;
+ }
+
const pair<int, uint> &info = geometry_to_object_info[geom];
- geom->pack_primitives(pack, info.first, info.second);
+ pool.push(function_bind(
+ &Geometry::pack_primitives, geom, &pack, info.first, info.second, pack_all));
}
+ pool.wait_work();
}
/* copy to device */
progress.set_status("Updating Scene BVH", "Copying BVH to device");
+ /* When using BVH2, we always have to copy/update the data as its layout is dependent on the
+ * BVH's leaf nodes which may be different when the objects or vertices move. */
+
if (pack.nodes.size()) {
dscene->bvh_nodes.steal_data(pack.nodes);
dscene->bvh_nodes.copy_to_device();
@@ -1252,7 +1330,7 @@ void GeometryManager::device_update_bvh(Device *device,
dscene->object_node.steal_data(pack.object_node);
dscene->object_node.copy_to_device();
}
- if (pack.prim_tri_index.size()) {
+ if (pack.prim_tri_index.size() && (dscene->prim_tri_index.need_realloc() || has_bvh2_layout)) {
dscene->prim_tri_index.steal_data(pack.prim_tri_index);
dscene->prim_tri_index.copy_to_device();
}
@@ -1260,23 +1338,23 @@ void GeometryManager::device_update_bvh(Device *device,
dscene->prim_tri_verts.steal_data(pack.prim_tri_verts);
dscene->prim_tri_verts.copy_to_device();
}
- if (pack.prim_type.size()) {
+ if (pack.prim_type.size() && (dscene->prim_type.need_realloc() || has_bvh2_layout)) {
dscene->prim_type.steal_data(pack.prim_type);
dscene->prim_type.copy_to_device();
}
- if (pack.prim_visibility.size()) {
+ if (pack.prim_visibility.size() && (dscene->prim_visibility.need_realloc() || has_bvh2_layout)) {
dscene->prim_visibility.steal_data(pack.prim_visibility);
dscene->prim_visibility.copy_to_device();
}
- if (pack.prim_index.size()) {
+ if (pack.prim_index.size() && (dscene->prim_index.need_realloc() || has_bvh2_layout)) {
dscene->prim_index.steal_data(pack.prim_index);
dscene->prim_index.copy_to_device();
}
- if (pack.prim_object.size()) {
+ if (pack.prim_object.size() && (dscene->prim_object.need_realloc() || has_bvh2_layout)) {
dscene->prim_object.steal_data(pack.prim_object);
dscene->prim_object.copy_to_device();
}
- if (pack.prim_time.size()) {
+ if (pack.prim_time.size() && (dscene->prim_time.need_realloc() || has_bvh2_layout)) {
dscene->prim_time.steal_data(pack.prim_time);
dscene->prim_time.copy_to_device();
}
@@ -1289,12 +1367,65 @@ void GeometryManager::device_update_bvh(Device *device,
dscene->data.bvh.scene = NULL;
}
+/* Set of flags used to help determining what data has been modified or needs reallocation, so we
+ * can decide which device data to free or update. */
+enum {
+ DEVICE_CURVE_DATA_MODIFIED = (1 << 0),
+ DEVICE_MESH_DATA_MODIFIED = (1 << 1),
+
+ ATTR_FLOAT_MODIFIED = (1 << 2),
+ ATTR_FLOAT2_MODIFIED = (1 << 3),
+ ATTR_FLOAT3_MODIFIED = (1 << 4),
+ ATTR_UCHAR4_MODIFIED = (1 << 5),
+
+ CURVE_DATA_NEED_REALLOC = (1 << 6),
+ MESH_DATA_NEED_REALLOC = (1 << 7),
+
+ ATTR_FLOAT_NEEDS_REALLOC = (1 << 8),
+ ATTR_FLOAT2_NEEDS_REALLOC = (1 << 9),
+ ATTR_FLOAT3_NEEDS_REALLOC = (1 << 10),
+ ATTR_UCHAR4_NEEDS_REALLOC = (1 << 11),
+
+ ATTRS_NEED_REALLOC = (ATTR_FLOAT_NEEDS_REALLOC | ATTR_FLOAT2_NEEDS_REALLOC |
+ ATTR_FLOAT3_NEEDS_REALLOC | ATTR_UCHAR4_NEEDS_REALLOC),
+ DEVICE_MESH_DATA_NEEDS_REALLOC = (CURVE_DATA_NEED_REALLOC | ATTRS_NEED_REALLOC),
+ DEVICE_CURVE_DATA_NEEDS_REALLOC = (MESH_DATA_NEED_REALLOC | ATTRS_NEED_REALLOC),
+};
+
+static void update_device_flags_attribute(uint32_t &device_update_flags,
+ const AttributeSet &attributes)
+{
+ foreach (const Attribute &attr, attributes.attributes) {
+ if (!attr.modified) {
+ 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;
+ }
+ }
+}
+
void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Progress &progress)
{
- if (!need_update && !need_flags_update) {
+ if (!need_update() && !need_flags_update) {
return;
}
+ uint32_t device_update_flags = 0;
+
scoped_callback_timer timer([scene](double time) {
if (scene->update_stats) {
scene->update_stats->geometry.times.add_entry({"device_update_preprocess", time});
@@ -1314,9 +1445,54 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
if (shader->has_volume) {
geom->has_volume = true;
}
+
if (shader->has_surface_bssrdf) {
geom->has_surface_bssrdf = true;
}
+
+ if (shader->need_update_uvs) {
+ device_update_flags |= ATTR_FLOAT2_NEEDS_REALLOC;
+
+ /* Attributes might need to be tesselated if added. */
+ if (geom->is_mesh()) {
+ Mesh *mesh = static_cast<Mesh *>(geom);
+ if (mesh->need_tesselation()) {
+ mesh->tag_modified();
+ }
+ }
+ }
+
+ if (shader->need_update_attribute) {
+ device_update_flags |= ATTRS_NEED_REALLOC;
+
+ /* Attributes might need to be tesselated if added. */
+ if (geom->is_mesh()) {
+ Mesh *mesh = static_cast<Mesh *>(geom);
+ if (mesh->need_tesselation()) {
+ mesh->tag_modified();
+ }
+ }
+ }
+
+ if (shader->need_update_displacement) {
+ /* tag displacement related sockets as modified */
+ if (geom->is_mesh()) {
+ Mesh *mesh = static_cast<Mesh *>(geom);
+ mesh->tag_verts_modified();
+ mesh->tag_subd_dicing_rate_modified();
+ mesh->tag_subd_max_level_modified();
+ mesh->tag_subd_objecttoworld_modified();
+
+ device_update_flags |= ATTRS_NEED_REALLOC;
+ }
+ }
+ }
+
+ /* only check for modified attributes if we do not need to reallocate them already */
+ if ((device_update_flags & ATTRS_NEED_REALLOC) == 0) {
+ update_device_flags_attribute(device_update_flags, geom->attributes);
+ /* don't check for subd_attributes, as if they were modified, we would need to reallocate
+ * anyway */
}
/* Re-create volume mesh if we will rebuild or refit the BVH. Note we
@@ -1332,13 +1508,119 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
Volume *volume = static_cast<Volume *>(geom);
create_volume_mesh(volume, progress);
+
+ /* always reallocate when we have a volume, as we need to rebuild the BVH */
+ device_update_flags |= DEVICE_MESH_DATA_NEEDS_REALLOC;
}
if (geom->is_hair()) {
/* Set curve shape, still a global scene setting for now. */
Hair *hair = static_cast<Hair *>(geom);
hair->curve_shape = scene->params.hair_shape;
+
+ if (hair->need_update_rebuild) {
+ device_update_flags |= DEVICE_CURVE_DATA_NEEDS_REALLOC;
+ }
+ else if (hair->is_modified()) {
+ device_update_flags |= DEVICE_CURVE_DATA_MODIFIED;
+ }
}
+
+ if (geom->is_mesh()) {
+ Mesh *mesh = static_cast<Mesh *>(geom);
+
+ if (mesh->need_update_rebuild) {
+ device_update_flags |= DEVICE_MESH_DATA_NEEDS_REALLOC;
+ }
+ else if (mesh->is_modified()) {
+ device_update_flags |= DEVICE_MESH_DATA_MODIFIED;
+ }
+ }
+ }
+
+ if (update_flags & (MESH_ADDED | MESH_REMOVED)) {
+ device_update_flags |= DEVICE_MESH_DATA_NEEDS_REALLOC;
+ }
+
+ if (update_flags & (HAIR_ADDED | HAIR_REMOVED)) {
+ device_update_flags |= DEVICE_CURVE_DATA_NEEDS_REALLOC;
+ }
+
+ /* tag the device arrays for reallocation or modification */
+ DeviceScene *dscene = &scene->dscene;
+
+ if (device_update_flags & (DEVICE_MESH_DATA_NEEDS_REALLOC | DEVICE_CURVE_DATA_NEEDS_REALLOC)) {
+ delete scene->bvh;
+ scene->bvh = nullptr;
+
+ dscene->bvh_nodes.tag_realloc();
+ dscene->bvh_leaf_nodes.tag_realloc();
+ dscene->object_node.tag_realloc();
+ dscene->prim_tri_verts.tag_realloc();
+ dscene->prim_tri_index.tag_realloc();
+ dscene->prim_type.tag_realloc();
+ dscene->prim_visibility.tag_realloc();
+ dscene->prim_index.tag_realloc();
+ dscene->prim_object.tag_realloc();
+ dscene->prim_time.tag_realloc();
+
+ if (device_update_flags & DEVICE_MESH_DATA_NEEDS_REALLOC) {
+ 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->patches.tag_realloc();
+ }
+
+ if (device_update_flags & DEVICE_CURVE_DATA_NEEDS_REALLOC) {
+ dscene->curves.tag_realloc();
+ dscene->curve_keys.tag_realloc();
+ }
+ }
+
+ if (device_update_flags & ATTR_FLOAT_NEEDS_REALLOC) {
+ dscene->attributes_map.tag_realloc();
+ dscene->attributes_float.tag_realloc();
+ }
+ else if (device_update_flags & ATTR_FLOAT_MODIFIED) {
+ dscene->attributes_float.tag_modified();
+ }
+
+ if (device_update_flags & ATTR_FLOAT2_NEEDS_REALLOC) {
+ dscene->attributes_map.tag_realloc();
+ dscene->attributes_float2.tag_realloc();
+ }
+ else if (device_update_flags & ATTR_FLOAT_MODIFIED) {
+ dscene->attributes_float.tag_modified();
+ }
+
+ if (device_update_flags & ATTR_FLOAT3_NEEDS_REALLOC) {
+ dscene->attributes_map.tag_realloc();
+ dscene->attributes_float3.tag_realloc();
+ }
+ else if (device_update_flags & ATTR_FLOAT_MODIFIED) {
+ dscene->attributes_float.tag_modified();
+ }
+
+ if (device_update_flags & ATTR_UCHAR4_NEEDS_REALLOC) {
+ dscene->attributes_map.tag_realloc();
+ dscene->attributes_uchar4.tag_realloc();
+ }
+ else if (device_update_flags & ATTR_UCHAR4_MODIFIED) {
+ dscene->attributes_uchar4.tag_modified();
+ }
+
+ if (device_update_flags & DEVICE_MESH_DATA_MODIFIED) {
+ /* if anything else than vertices or shaders are modified, we would need to reallocate, so
+ * these are the only arrays that can be updated */
+ dscene->tri_vnormal.tag_modified();
+ dscene->tri_shader.tag_modified();
+ }
+
+ if (device_update_flags & DEVICE_CURVE_DATA_MODIFIED) {
+ dscene->curve_keys.tag_modified();
+ dscene->curves.tag_modified();
}
need_flags_update = false;
@@ -1423,7 +1705,7 @@ void GeometryManager::device_update(Device *device,
Scene *scene,
Progress &progress)
{
- if (!need_update)
+ if (!need_update())
return;
VLOG(1) << "Total " << scene->geometry.size() << " meshes.";
@@ -1439,12 +1721,6 @@ void GeometryManager::device_update(Device *device,
});
foreach (Geometry *geom, scene->geometry) {
- foreach (Node *node, geom->get_used_shaders()) {
- Shader *shader = static_cast<Shader *>(node);
- if (shader->need_update_geometry)
- geom->tag_modified();
- }
-
if (geom->is_modified() &&
(geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME)) {
Mesh *mesh = static_cast<Mesh *>(geom);
@@ -1541,7 +1817,7 @@ void GeometryManager::device_update(Device *device,
}
/* Device update. */
- device_free(device, dscene);
+ device_free(device, dscene, false);
const BVHLayout bvh_layout = BVHParams::best_bvh_layout(scene->params.bvh_layout,
device->get_bvh_layout_mask());
@@ -1614,7 +1890,7 @@ void GeometryManager::device_update(Device *device,
{"device_update (displacement: attributes)", time});
}
});
- device_free(device, dscene);
+ device_free(device, dscene, false);
device_update_attributes(device, dscene, scene, progress);
if (progress.get_cancel()) {
@@ -1622,6 +1898,9 @@ void GeometryManager::device_update(Device *device,
}
}
+ /* update the bvh even when there is no geometry so the kernel bvh data is still valid,
+ * especially when removing all of the objects during interactive renders */
+ bool need_update_scene_bvh = (scene->bvh == nullptr);
{
scoped_callback_timer timer([scene](double time) {
if (scene->update_stats) {
@@ -1633,6 +1912,7 @@ void GeometryManager::device_update(Device *device,
size_t i = 0;
foreach (Geometry *geom, scene->geometry) {
if (geom->is_modified()) {
+ need_update_scene_bvh = true;
pool.push(function_bind(
&Geometry::compute_bvh, geom, device, dscene, &scene->params, &progress, i, num_bvh));
if (geom->need_build_bvh(bvh_layout)) {
@@ -1647,7 +1927,9 @@ void GeometryManager::device_update(Device *device,
}
foreach (Shader *shader, scene->shaders) {
- shader->need_update_geometry = false;
+ shader->need_update_uvs = false;
+ shader->need_update_attribute = false;
+ shader->need_update_displacement = false;
}
Scene::MotionType need_motion = scene->need_motion();
@@ -1670,7 +1952,7 @@ void GeometryManager::device_update(Device *device,
return;
}
- {
+ if (need_update_scene_bvh) {
scoped_callback_timer timer([scene](double time) {
if (scene->update_stats) {
scene->update_stats->geometry.times.add_entry({"device_update (build scene BVH)", time});
@@ -1695,8 +1977,6 @@ void GeometryManager::device_update(Device *device,
}
}
- need_update = false;
-
if (true_displacement_used) {
/* Re-tag flags for update, so they're re-evaluated
* for meshes with correct bounding boxes.
@@ -1706,33 +1986,71 @@ void GeometryManager::device_update(Device *device,
*/
scene->object_manager->need_flags_update = old_need_object_flags_update;
}
+
+ /* unset flags */
+
+ foreach (Geometry *geom, scene->geometry) {
+ geom->clear_modified();
+ geom->attributes.clear_modified();
+
+ if (geom->is_mesh()) {
+ Mesh *mesh = static_cast<Mesh *>(geom);
+ mesh->subd_attributes.clear_modified();
+ }
+ }
+
+ update_flags = UPDATE_NONE;
+
+ dscene->bvh_nodes.clear_modified();
+ dscene->bvh_leaf_nodes.clear_modified();
+ dscene->object_node.clear_modified();
+ dscene->prim_tri_verts.clear_modified();
+ dscene->prim_tri_index.clear_modified();
+ dscene->prim_type.clear_modified();
+ dscene->prim_visibility.clear_modified();
+ dscene->prim_index.clear_modified();
+ dscene->prim_object.clear_modified();
+ dscene->prim_time.clear_modified();
+ dscene->tri_shader.clear_modified();
+ dscene->tri_vindex.clear_modified();
+ dscene->tri_patch.clear_modified();
+ dscene->tri_vnormal.clear_modified();
+ dscene->tri_patch_uv.clear_modified();
+ dscene->curves.clear_modified();
+ dscene->curve_keys.clear_modified();
+ dscene->patches.clear_modified();
+ dscene->attributes_map.clear_modified();
+ dscene->attributes_float.clear_modified();
+ dscene->attributes_float2.clear_modified();
+ dscene->attributes_float3.clear_modified();
+ dscene->attributes_uchar4.clear_modified();
}
-void GeometryManager::device_free(Device *device, DeviceScene *dscene)
+void GeometryManager::device_free(Device *device, DeviceScene *dscene, bool force_free)
{
- dscene->bvh_nodes.free();
- dscene->bvh_leaf_nodes.free();
- dscene->object_node.free();
- dscene->prim_tri_verts.free();
- dscene->prim_tri_index.free();
- dscene->prim_type.free();
- dscene->prim_visibility.free();
- dscene->prim_index.free();
- dscene->prim_object.free();
- dscene->prim_time.free();
- dscene->tri_shader.free();
- dscene->tri_vnormal.free();
- dscene->tri_vindex.free();
- dscene->tri_patch.free();
- dscene->tri_patch_uv.free();
- dscene->curves.free();
- dscene->curve_keys.free();
- dscene->patches.free();
- dscene->attributes_map.free();
- dscene->attributes_float.free();
- dscene->attributes_float2.free();
- dscene->attributes_float3.free();
- dscene->attributes_uchar4.free();
+ dscene->bvh_nodes.free_if_need_realloc(force_free);
+ dscene->bvh_leaf_nodes.free_if_need_realloc(force_free);
+ dscene->object_node.free_if_need_realloc(force_free);
+ dscene->prim_tri_verts.free_if_need_realloc(force_free);
+ dscene->prim_tri_index.free_if_need_realloc(force_free);
+ dscene->prim_type.free_if_need_realloc(force_free);
+ dscene->prim_visibility.free_if_need_realloc(force_free);
+ dscene->prim_index.free_if_need_realloc(force_free);
+ dscene->prim_object.free_if_need_realloc(force_free);
+ dscene->prim_time.free_if_need_realloc(force_free);
+ dscene->tri_shader.free_if_need_realloc(force_free);
+ dscene->tri_vnormal.free_if_need_realloc(force_free);
+ dscene->tri_vindex.free_if_need_realloc(force_free);
+ dscene->tri_patch.free_if_need_realloc(force_free);
+ dscene->tri_patch_uv.free_if_need_realloc(force_free);
+ dscene->curves.free_if_need_realloc(force_free);
+ dscene->curve_keys.free_if_need_realloc(force_free);
+ dscene->patches.free_if_need_realloc(force_free);
+ dscene->attributes_map.free_if_need_realloc(force_free);
+ dscene->attributes_float.free_if_need_realloc(force_free);
+ dscene->attributes_float2.free_if_need_realloc(force_free);
+ dscene->attributes_float3.free_if_need_realloc(force_free);
+ dscene->attributes_uchar4.free_if_need_realloc(force_free);
/* Signal for shaders like displacement not to do ray tracing. */
dscene->data.bvh.bvh_layout = BVH_LAYOUT_NONE;
@@ -1750,10 +2068,19 @@ void GeometryManager::device_free(Device *device, DeviceScene *dscene)
#endif
}
-void GeometryManager::tag_update(Scene *scene)
+void GeometryManager::tag_update(Scene *scene, uint32_t flag)
+{
+ update_flags |= flag;
+
+ /* do not tag the object manager for an update if it is the one who tagged us */
+ if ((flag & OBJECT_MANAGER) == 0) {
+ scene->object_manager->tag_update(scene, ObjectManager::GEOMETRY_MANAGER);
+ }
+}
+
+bool GeometryManager::need_update() const
{
- need_update = true;
- scene->object_manager->need_update = true;
+ return update_flags != UPDATE_NONE;
}
void GeometryManager::collect_statistics(const Scene *scene, RenderStats *stats)
diff --git a/intern/cycles/render/geometry.h b/intern/cycles/render/geometry.h
index b124e950ad2..88388f31a9b 100644
--- a/intern/cycles/render/geometry.h
+++ b/intern/cycles/render/geometry.h
@@ -125,7 +125,7 @@ class Geometry : public Node {
int n,
int total);
- virtual void pack_primitives(PackedBVH &pack, int object, uint visibility) = 0;
+ virtual void pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all) = 0;
/* Check whether the geometry should have own BVH built separately. Briefly,
* own BVH is needed for geometry, if:
@@ -155,6 +155,11 @@ class Geometry : public Node {
return geometry_type == HAIR;
}
+ bool is_volume() const
+ {
+ return geometry_type == VOLUME;
+ }
+
/* Updates */
void tag_update(Scene *scene, bool rebuild);
@@ -164,9 +169,32 @@ class Geometry : public Node {
/* Geometry Manager */
class GeometryManager {
+ uint32_t update_flags;
+
public:
+ enum : uint32_t {
+ UV_PASS_NEEDED = (1 << 0),
+ MOTION_PASS_NEEDED = (1 << 1),
+ GEOMETRY_MODIFIED = (1 << 2),
+ OBJECT_MANAGER = (1 << 3),
+ MESH_ADDED = (1 << 4),
+ MESH_REMOVED = (1 << 5),
+ HAIR_ADDED = (1 << 6),
+ HAIR_REMOVED = (1 << 7),
+
+ SHADER_ATTRIBUTE_MODIFIED = (1 << 8),
+ SHADER_DISPLACEMENT_MODIFIED = (1 << 9),
+
+ GEOMETRY_ADDED = MESH_ADDED | HAIR_ADDED,
+ GEOMETRY_REMOVED = MESH_REMOVED | HAIR_REMOVED,
+
+ /* tag everything in the manager for an update */
+ UPDATE_ALL = ~0u,
+
+ UPDATE_NONE = 0u,
+ };
+
/* Update Flags */
- bool need_update;
bool need_flags_update;
/* Constructor/Destructor */
@@ -176,10 +204,12 @@ class GeometryManager {
/* Device Updates */
void device_update_preprocess(Device *device, Scene *scene, Progress &progress);
void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
- void device_free(Device *device, DeviceScene *dscene);
+ void device_free(Device *device, DeviceScene *dscene, bool force_free);
/* Updates */
- void tag_update(Scene *scene);
+ void tag_update(Scene *scene, uint32_t flag);
+
+ bool need_update() const;
/* Statistics */
void collect_statistics(const Scene *scene, RenderStats *stats);
diff --git a/intern/cycles/render/hair.cpp b/intern/cycles/render/hair.cpp
index 896e798b6f9..e94cad6b32e 100644
--- a/intern/cycles/render/hair.cpp
+++ b/intern/cycles/render/hair.cpp
@@ -494,33 +494,38 @@ void Hair::pack_curves(Scene *scene,
}
}
-void Hair::pack_primitives(PackedBVH &pack, int object, uint visibility)
+void Hair::pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all)
{
if (curve_first_key.empty())
return;
- const size_t num_prims = num_segments();
- pack.prim_tri_index.reserve(pack.prim_tri_index.size() + num_prims);
- pack.prim_type.reserve(pack.prim_type.size() + num_prims);
- pack.prim_visibility.reserve(pack.prim_visibility.size() + num_prims);
- pack.prim_index.reserve(pack.prim_index.size() + num_prims);
- pack.prim_object.reserve(pack.prim_object.size() + num_prims);
- // 'pack.prim_time' is unused by Embree and OptiX
+ /* If the BVH does not have to be recreated, we can bail out. */
+ if (!pack_all) {
+ return;
+ }
+
+ 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) {
- pack.prim_tri_index.push_back_reserved(-1);
- pack.prim_type.push_back_reserved(PRIMITIVE_PACK_SEGMENT(type, k));
- pack.prim_visibility.push_back_reserved(visibility);
+ 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
- pack.prim_index.push_back_reserved(j + prim_offset);
- pack.prim_object.push_back_reserved(object);
+ 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 c7be08d679c..4b949f984e5 100644
--- a/intern/cycles/render/hair.h
+++ b/intern/cycles/render/hair.h
@@ -146,7 +146,7 @@ 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) override;
+ void pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all) override;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp
index 30858c4f68b..a09a680f93f 100644
--- a/intern/cycles/render/image.cpp
+++ b/intern/cycles/render/image.cpp
@@ -298,7 +298,7 @@ bool ImageLoader::is_vdb_loader() const
ImageManager::ImageManager(const DeviceInfo &info)
{
- need_update = true;
+ need_update_ = true;
osl_texture_system = NULL;
animation_frame = 0;
@@ -451,7 +451,7 @@ int ImageManager::add_image_slot(ImageLoader *loader,
images[slot] = img;
- need_update = true;
+ need_update_ = true;
return slot;
}
@@ -478,7 +478,7 @@ void ImageManager::remove_image_user(int slot)
* the reasons for this is that on shader changes we add and remove nodes
* that use them, but we do not want to reload the image all the time. */
if (image->users == 0)
- need_update = true;
+ need_update_ = true;
}
static bool image_associate_alpha(ImageManager::Image *img)
@@ -810,7 +810,7 @@ void ImageManager::device_free_image(Device *, int slot)
void ImageManager::device_update(Device *device, Scene *scene, Progress &progress)
{
- if (!need_update) {
+ if (!need_update()) {
return;
}
@@ -834,7 +834,7 @@ void ImageManager::device_update(Device *device, Scene *scene, Progress &progres
pool.wait_work();
- need_update = false;
+ need_update_ = false;
}
void ImageManager::device_update_slot(Device *device, Scene *scene, int slot, Progress *progress)
@@ -854,7 +854,7 @@ void ImageManager::device_load_builtin(Device *device, Scene *scene, Progress &p
{
/* Load only builtin images, Blender needs this to load evaluated
* scene data from depsgraph before it is freed. */
- if (!need_update) {
+ if (!need_update()) {
return;
}
@@ -896,4 +896,14 @@ void ImageManager::collect_statistics(RenderStats *stats)
}
}
+void ImageManager::tag_update()
+{
+ need_update_ = true;
+}
+
+bool ImageManager::need_update() const
+{
+ return need_update_;
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h
index 6ac1db9ed63..c802521db56 100644
--- a/intern/cycles/render/image.h
+++ b/intern/cycles/render/image.h
@@ -189,7 +189,9 @@ class ImageManager {
void collect_statistics(RenderStats *stats);
- bool need_update;
+ void tag_update();
+
+ bool need_update() const;
struct Image {
ImageParams params;
@@ -209,6 +211,7 @@ class ImageManager {
};
private:
+ bool need_update_;
bool has_half_images;
thread_mutex device_mutex;
diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp
index e5b9e6bfabf..d93856ceb61 100644
--- a/intern/cycles/render/integrator.cpp
+++ b/intern/cycles/render/integrator.cpp
@@ -17,9 +17,11 @@
#include "render/integrator.h"
#include "device/device.h"
#include "render/background.h"
+#include "render/camera.h"
#include "render/film.h"
#include "render/jitter.h"
#include "render/light.h"
+#include "render/object.h"
#include "render/scene.h"
#include "render/shader.h"
#include "render/sobol.h"
@@ -113,6 +115,18 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
}
});
+ const bool need_update_lut = ao_samples_is_modified() || diffuse_samples_is_modified() ||
+ glossy_samples_is_modified() || max_bounce_is_modified() ||
+ max_transmission_bounce_is_modified() ||
+ mesh_light_samples_is_modified() || method_is_modified() ||
+ sampling_pattern_is_modified() ||
+ subsurface_samples_is_modified() ||
+ transmission_samples_is_modified() || volume_samples_is_modified();
+
+ if (need_update_lut) {
+ dscene->sample_pattern_lut.tag_realloc();
+ }
+
device_free(device, dscene);
KernelIntegrator *kintegrator = &dscene->data.integrator;
@@ -242,45 +256,69 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
int dimensions = PRNG_BASE_NUM + max_samples * PRNG_BOUNCE_NUM;
dimensions = min(dimensions, SOBOL_MAX_DIMENSIONS);
- if (sampling_pattern == SAMPLING_PATTERN_SOBOL) {
- uint *directions = dscene->sample_pattern_lut.alloc(SOBOL_BITS * dimensions);
+ if (need_update_lut) {
+ if (sampling_pattern == SAMPLING_PATTERN_SOBOL) {
+ uint *directions = dscene->sample_pattern_lut.alloc(SOBOL_BITS * dimensions);
- sobol_generate_direction_vectors((uint(*)[SOBOL_BITS])directions, dimensions);
+ sobol_generate_direction_vectors((uint(*)[SOBOL_BITS])directions, dimensions);
- dscene->sample_pattern_lut.copy_to_device();
- }
- else {
- constexpr int sequence_size = NUM_PMJ_SAMPLES;
- constexpr int num_sequences = NUM_PMJ_PATTERNS;
- float2 *directions = (float2 *)dscene->sample_pattern_lut.alloc(sequence_size * num_sequences *
- 2);
- TaskPool pool;
- for (int j = 0; j < num_sequences; ++j) {
- float2 *sequence = directions + j * sequence_size;
- pool.push(
- function_bind(&progressive_multi_jitter_02_generate_2D, sequence, sequence_size, j));
+ dscene->sample_pattern_lut.copy_to_device();
+ }
+ else {
+ constexpr int sequence_size = NUM_PMJ_SAMPLES;
+ constexpr int num_sequences = NUM_PMJ_PATTERNS;
+ float2 *directions = (float2 *)dscene->sample_pattern_lut.alloc(sequence_size *
+ num_sequences * 2);
+ TaskPool pool;
+ for (int j = 0; j < num_sequences; ++j) {
+ float2 *sequence = directions + j * sequence_size;
+ pool.push(
+ function_bind(&progressive_multi_jitter_02_generate_2D, sequence, sequence_size, j));
+ }
+ pool.wait_work();
+ dscene->sample_pattern_lut.copy_to_device();
}
- pool.wait_work();
- dscene->sample_pattern_lut.copy_to_device();
}
+ dscene->sample_pattern_lut.clear_modified();
clear_modified();
}
-void Integrator::device_free(Device *, DeviceScene *dscene)
+void Integrator::device_free(Device *, DeviceScene *dscene, bool force_free)
{
- dscene->sample_pattern_lut.free();
+ dscene->sample_pattern_lut.free_if_need_realloc(force_free);
}
-void Integrator::tag_update(Scene *scene)
+void Integrator::tag_update(Scene *scene, uint32_t flag)
{
- foreach (Shader *shader, scene->shaders) {
- if (shader->has_integrator_dependency) {
- scene->shader_manager->need_update = true;
- break;
+ if (flag & UPDATE_ALL) {
+ tag_modified();
+ }
+
+ if (flag & (AO_PASS_MODIFIED | BACKGROUND_AO_MODIFIED)) {
+ /* tag only the ao_bounces socket as modified so we avoid updating sample_pattern_lut
+ * unnecessarily */
+ tag_ao_bounces_modified();
+ }
+
+ if ((flag & LIGHT_SAMPLES_MODIFIED) && (method == BRANCHED_PATH)) {
+ /* the number of light samples may affect the size of the sample_pattern_lut */
+ tag_sampling_pattern_modified();
+ }
+
+ if (filter_glossy_is_modified()) {
+ foreach (Shader *shader, scene->shaders) {
+ if (shader->has_integrator_dependency) {
+ scene->shader_manager->tag_update(scene, ShaderManager::INTEGRATOR_MODIFIED);
+ break;
+ }
}
}
- tag_modified();
+
+ if (motion_blur_is_modified()) {
+ scene->object_manager->tag_update(scene, ObjectManager::MOTION_BLUR_MODIFIED);
+ scene->camera->tag_modified();
+ }
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/integrator.h b/intern/cycles/render/integrator.h
index 9fe46ad591c..4eeeda92d41 100644
--- a/intern/cycles/render/integrator.h
+++ b/intern/cycles/render/integrator.h
@@ -89,13 +89,24 @@ class Integrator : public Node {
NODE_SOCKET_API(SamplingPattern, sampling_pattern)
+ enum : uint32_t {
+ AO_PASS_MODIFIED = (1 << 0),
+ BACKGROUND_AO_MODIFIED = (1 << 1),
+ LIGHT_SAMPLES_MODIFIED = (1 << 2),
+
+ /* tag everything in the manager for an update */
+ UPDATE_ALL = ~0u,
+
+ UPDATE_NONE = 0u,
+ };
+
Integrator();
~Integrator();
void device_update(Device *device, DeviceScene *dscene, Scene *scene);
- void device_free(Device *device, DeviceScene *dscene);
+ void device_free(Device *device, DeviceScene *dscene, bool force_free = false);
- void tag_update(Scene *scene);
+ void tag_update(Scene *scene, uint32_t flag);
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp
index 2bde3242b26..638ac376157 100644
--- a/intern/cycles/render/light.cpp
+++ b/intern/cycles/render/light.cpp
@@ -162,7 +162,13 @@ Light::Light() : Node(node_type)
void Light::tag_update(Scene *scene)
{
- scene->light_manager->need_update = is_modified();
+ if (is_modified()) {
+ scene->light_manager->tag_update(scene, LightManager::LIGHT_MODIFIED);
+
+ if (samples_is_modified()) {
+ scene->integrator->tag_update(scene, Integrator::LIGHT_SAMPLES_MODIFIED);
+ }
+ }
}
bool Light::has_contribution(Scene *scene)
@@ -183,7 +189,7 @@ bool Light::has_contribution(Scene *scene)
LightManager::LightManager()
{
- need_update = true;
+ update_flags = UPDATE_ALL;
need_update_background = true;
use_light_visibility = false;
last_background_enabled = false;
@@ -962,7 +968,7 @@ void LightManager::device_update(Device *device,
Scene *scene,
Progress &progress)
{
- if (!need_update)
+ if (!need_update())
return;
scoped_callback_timer timer([scene](double time) {
@@ -1000,7 +1006,7 @@ void LightManager::device_update(Device *device,
scene->film->set_use_light_visibility(use_light_visibility);
- need_update = false;
+ update_flags = UPDATE_NONE;
need_update_background = false;
}
@@ -1015,9 +1021,14 @@ void LightManager::device_free(Device *, DeviceScene *dscene, const bool free_ba
dscene->ies_lights.free();
}
-void LightManager::tag_update(Scene * /*scene*/)
+void LightManager::tag_update(Scene * /*scene*/, uint32_t flag)
+{
+ update_flags |= flag;
+}
+
+bool LightManager::need_update() const
{
- need_update = true;
+ return update_flags != UPDATE_NONE;
}
int LightManager::add_ies_from_file(const string &filename)
@@ -1063,7 +1074,7 @@ int LightManager::add_ies(const string &content)
ies_slots[slot]->users = 1;
ies_slots[slot]->hash = hash;
- need_update = true;
+ update_flags = UPDATE_ALL;
need_update_background = true;
return slot;
@@ -1082,8 +1093,10 @@ void LightManager::remove_ies(int slot)
ies_slots[slot]->users--;
/* If the slot has no more users, update the device to remove it. */
- need_update |= (ies_slots[slot]->users == 0);
- need_update_background |= need_update;
+ if (ies_slots[slot]->users == 0) {
+ update_flags |= UPDATE_ALL;
+ need_update_background = true;
+ }
}
void LightManager::device_update_ies(DeviceScene *dscene)
diff --git a/intern/cycles/render/light.h b/intern/cycles/render/light.h
index e590e13b489..39014b5d667 100644
--- a/intern/cycles/render/light.h
+++ b/intern/cycles/render/light.h
@@ -91,8 +91,23 @@ class Light : public Node {
class LightManager {
public:
+ enum : uint32_t {
+ MESH_NEED_REBUILD = (1 << 0),
+ EMISSIVE_MESH_MODIFIED = (1 << 1),
+ LIGHT_MODIFIED = (1 << 2),
+ LIGHT_ADDED = (1 << 3),
+ LIGHT_REMOVED = (1 << 4),
+ OBJECT_MANAGER = (1 << 5),
+ SHADER_COMPILED = (1 << 6),
+ SHADER_MODIFIED = (1 << 7),
+
+ /* tag everything in the manager for an update */
+ UPDATE_ALL = ~0u,
+
+ UPDATE_NONE = 0u,
+ };
+
bool use_light_visibility;
- bool need_update;
/* Need to update background (including multiple importance map) */
bool need_update_background;
@@ -108,7 +123,9 @@ class LightManager {
void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
void device_free(Device *device, DeviceScene *dscene, const bool free_background = true);
- void tag_update(Scene *scene);
+ void tag_update(Scene *scene, uint32_t flag);
+
+ bool need_update() const;
/* Check whether there is a background light. */
bool has_background_light(Scene *scene);
@@ -145,6 +162,8 @@ class LightManager {
bool last_background_enabled;
int last_background_resolution;
+
+ uint32_t update_flags;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp
index a0afdd3b841..5f62da8f18b 100644
--- a/intern/cycles/render/mesh.cpp
+++ b/intern/cycles/render/mesh.cpp
@@ -805,34 +805,42 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui
}
}
-void Mesh::pack_primitives(PackedBVH &pack, int object, uint visibility)
+void Mesh::pack_primitives(ccl::PackedBVH *pack, int object, uint visibility, bool pack_all)
{
if (triangles.empty())
return;
const size_t num_prims = num_triangles();
- pack.prim_tri_index.reserve(pack.prim_tri_index.size() + num_prims);
- pack.prim_tri_verts.reserve(pack.prim_tri_verts.size() + num_prims * 3);
- pack.prim_type.reserve(pack.prim_type.size() + num_prims);
- pack.prim_visibility.reserve(pack.prim_visibility.size() + num_prims);
- pack.prim_index.reserve(pack.prim_index.size() + num_prims);
- pack.prim_object.reserve(pack.prim_object.size() + num_prims);
- // 'pack.prim_time' is unused by Embree and OptiX
+
+ /* Use prim_offset for indexing as it is computed per geometry type, and prim_tri_verts does not
+ * contain data for Hair geometries. */
+ float4 *prim_tri_verts = &pack->prim_tri_verts[prim_offset * 3];
+ // 'pack->prim_time' is unused by Embree and OptiX
uint type = has_motion_blur() ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE;
- for (size_t k = 0; k < num_prims; ++k) {
- pack.prim_tri_index.push_back_reserved(pack.prim_tri_verts.size());
+ if (pack_all) {
+ /* 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;
+ }
+ }
+ for (size_t k = 0; k < num_prims; ++k) {
const Mesh::Triangle t = get_triangle(k);
- pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[0]]));
- pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[1]]));
- pack.prim_tri_verts.push_back_reserved(float3_to_float4(verts[t.v[2]]));
-
- pack.prim_type.push_back_reserved(type);
- pack.prim_visibility.push_back_reserved(visibility);
- pack.prim_index.push_back_reserved(k + prim_offset);
- pack.prim_object.push_back_reserved(object);
+ 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 b0a16fdfd8f..2b0ff92ab62 100644
--- a/intern/cycles/render/mesh.h
+++ b/intern/cycles/render/mesh.h
@@ -232,7 +232,7 @@ 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) override;
+ void pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all) override;
void tessellate(DiagSplit *split);
diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp
index 406982e3ef9..0f16a4fc12c 100644
--- a/intern/cycles/render/object.cpp
+++ b/intern/cycles/render/object.cpp
@@ -153,6 +153,10 @@ 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()) {
@@ -205,20 +209,39 @@ void Object::apply_transform(bool apply_to_motion)
void Object::tag_update(Scene *scene)
{
+ uint32_t flag = ObjectManager::UPDATE_NONE;
+
+ if (is_modified()) {
+ flag |= ObjectManager::OBJECT_MODIFIED;
+
+ if (use_holdout_is_modified()) {
+ flag |= ObjectManager::HOLDOUT_MODIFIED;
+ }
+ }
+
if (geometry) {
- if (geometry->transform_applied)
- geometry->tag_modified();
+ if (tfm_is_modified()) {
+ /* tag the geometry as modified so the BVH is updated, but do not tag everything as modified
+ */
+ if (geometry->is_mesh() || geometry->is_volume()) {
+ Mesh *mesh = static_cast<Mesh *>(geometry);
+ mesh->tag_verts_modified();
+ }
+ else if (geometry->is_hair()) {
+ Hair *hair = static_cast<Hair *>(geometry);
+ hair->tag_curve_keys_modified();
+ }
+ }
foreach (Node *node, geometry->get_used_shaders()) {
Shader *shader = static_cast<Shader *>(node);
if (shader->get_use_mis() && shader->has_surface_emission)
- scene->light_manager->need_update = true;
+ scene->light_manager->tag_update(scene, LightManager::EMISSIVE_MESH_MODIFIED);
}
}
scene->camera->need_flags_update = true;
- scene->geometry_manager->need_update = true;
- scene->object_manager->need_update = true;
+ scene->object_manager->tag_update(scene, flag);
}
bool Object::use_motion() const
@@ -361,7 +384,7 @@ int Object::get_device_index() const
ObjectManager::ObjectManager()
{
- need_update = true;
+ update_flags = UPDATE_ALL;
need_flags_update = true;
}
@@ -382,7 +405,9 @@ static float object_volume_density(const Transform &tfm, Geometry *geom)
return 1.0f;
}
-void ObjectManager::device_update_object_transform(UpdateObjectTransformState *state, Object *ob)
+void ObjectManager::device_update_object_transform(UpdateObjectTransformState *state,
+ Object *ob,
+ bool update_all)
{
KernelObject &kobject = state->objects[ob->index];
Transform *object_motion_pass = state->object_motion_pass;
@@ -456,8 +481,11 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
kobject.motion_offset = state->motion_offset[ob->index];
/* Decompose transforms for interpolation. */
- DecomposedTransform *decomp = state->object_motion + kobject.motion_offset;
- transform_motion_decompose(decomp, ob->motion.data(), ob->motion.size());
+ if (ob->tfm_is_modified() || update_all) {
+ DecomposedTransform *decomp = state->object_motion + kobject.motion_offset;
+ transform_motion_decompose(decomp, ob->motion.data(), ob->motion.size());
+ }
+
flag |= SD_OBJECT_MOTION;
state->have_motion = true;
}
@@ -480,10 +508,14 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
0;
kobject.patch_map_offset = 0;
kobject.attribute_map_offset = 0;
- uint32_t hash_name = util_murmur_hash3(ob->name.c_str(), ob->name.length(), 0);
- uint32_t hash_asset = util_murmur_hash3(ob->asset_name.c_str(), ob->asset_name.length(), 0);
- kobject.cryptomatte_object = util_hash_to_float(hash_name);
- kobject.cryptomatte_asset = util_hash_to_float(hash_asset);
+
+ if (ob->asset_name_is_modified() || update_all) {
+ uint32_t hash_name = util_murmur_hash3(ob->name.c_str(), ob->name.length(), 0);
+ uint32_t hash_asset = util_murmur_hash3(ob->asset_name.c_str(), ob->asset_name.length(), 0);
+ kobject.cryptomatte_object = util_hash_to_float(hash_name);
+ kobject.cryptomatte_asset = util_hash_to_float(hash_asset);
+ }
+
kobject.shadow_terminator_offset = 1.0f / (1.0f - 0.5f * ob->shadow_terminator_offset);
/* Object flag. */
@@ -544,6 +576,9 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, Scene *scene,
numparticles += psys->particles.size();
}
+ /* as all the arrays are the same size, checking only dscene.objects is sufficient */
+ const bool update_all = dscene->objects.need_realloc();
+
/* Parallel object update, with grain size to avoid too much threading overhead
* for individual objects. */
static const int OBJECTS_PER_TASK = 32;
@@ -551,7 +586,7 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, Scene *scene,
[&](const blocked_range<size_t> &r) {
for (size_t i = r.begin(); i != r.end(); i++) {
Object *ob = state.scene->objects[i];
- device_update_object_transform(&state, ob);
+ device_update_object_transform(&state, ob, update_all);
}
});
@@ -559,7 +594,7 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, Scene *scene,
return;
}
- dscene->objects.copy_to_device();
+ dscene->objects.copy_to_device_if_modified();
if (state.need_motion == Scene::MOTION_PASS) {
dscene->object_motion_pass.copy_to_device();
}
@@ -569,6 +604,10 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, Scene *scene,
dscene->data.bvh.have_motion = state.have_motion;
dscene->data.bvh.have_curves = state.have_curves;
+
+ dscene->objects.clear_modified();
+ dscene->object_motion_pass.clear_modified();
+ dscene->object_motion.clear_modified();
}
void ObjectManager::device_update(Device *device,
@@ -576,12 +615,28 @@ void ObjectManager::device_update(Device *device,
Scene *scene,
Progress &progress)
{
- if (!need_update)
+ if (!need_update())
return;
+ if (update_flags & (OBJECT_ADDED | OBJECT_REMOVED)) {
+ dscene->objects.tag_realloc();
+ dscene->object_motion_pass.tag_realloc();
+ dscene->object_motion.tag_realloc();
+ dscene->object_flag.tag_realloc();
+ dscene->object_volume_step.tag_realloc();
+ }
+
+ if (update_flags & HOLDOUT_MODIFIED) {
+ dscene->object_flag.tag_modified();
+ }
+
+ if (update_flags & PARTICLE_MODIFIED) {
+ dscene->objects.tag_modified();
+ }
+
VLOG(1) << "Total " << scene->objects.size() << " objects.";
- device_free(device, dscene);
+ device_free(device, dscene, false);
if (scene->objects.size() == 0)
return;
@@ -597,6 +652,16 @@ void ObjectManager::device_update(Device *device,
int index = 0;
foreach (Object *object, scene->objects) {
object->index = index++;
+
+ /* this is a bit too broad, however a bigger refactor might be needed to properly separate
+ * update each type of data (transform, flags, etc.) */
+ if (object->is_modified()) {
+ dscene->objects.tag_modified();
+ dscene->object_motion_pass.tag_modified();
+ dscene->object_motion.tag_modified();
+ dscene->object_flag.tag_modified();
+ dscene->object_volume_step.tag_modified();
+ }
}
}
@@ -638,7 +703,7 @@ void ObjectManager::device_update(Device *device,
void ObjectManager::device_update_flags(
Device *, DeviceScene *dscene, Scene *scene, Progress & /*progress*/, bool bounds_valid)
{
- if (!need_update && !need_flags_update)
+ if (!need_update() && !need_flags_update)
return;
scoped_callback_timer timer([scene](double time) {
@@ -647,7 +712,7 @@ void ObjectManager::device_update_flags(
}
});
- need_update = false;
+ update_flags = UPDATE_NONE;
need_flags_update = false;
if (scene->objects.size() == 0)
@@ -717,6 +782,9 @@ void ObjectManager::device_update_flags(
/* Copy object flag. */
dscene->object_flag.copy_to_device();
dscene->object_volume_step.copy_to_device();
+
+ dscene->object_flag.clear_modified();
+ dscene->object_volume_step.clear_modified();
}
void ObjectManager::device_update_mesh_offsets(Device *, DeviceScene *dscene, Scene *scene)
@@ -764,13 +832,13 @@ void ObjectManager::device_update_mesh_offsets(Device *, DeviceScene *dscene, Sc
}
}
-void ObjectManager::device_free(Device *, DeviceScene *dscene)
+void ObjectManager::device_free(Device *, DeviceScene *dscene, bool force_free)
{
- dscene->objects.free();
- dscene->object_motion_pass.free();
- dscene->object_motion.free();
- dscene->object_flag.free();
- dscene->object_volume_step.free();
+ dscene->objects.free_if_need_realloc(force_free);
+ dscene->object_motion_pass.free_if_need_realloc(force_free);
+ dscene->object_motion.free_if_need_realloc(force_free);
+ dscene->object_flag.free_if_need_realloc(force_free);
+ dscene->object_volume_step.free_if_need_realloc(force_free);
}
void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, Progress &progress)
@@ -841,11 +909,21 @@ void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, P
}
}
-void ObjectManager::tag_update(Scene *scene)
+void ObjectManager::tag_update(Scene *scene, uint32_t flag)
+{
+ update_flags |= flag;
+
+ /* avoid infinite loops if the geometry manager tagged us for an update */
+ if ((flag & GEOMETRY_MANAGER) == 0) {
+ scene->geometry_manager->tag_update(scene, GeometryManager::OBJECT_MANAGER);
+ }
+
+ scene->light_manager->tag_update(scene, LightManager::OBJECT_MANAGER);
+}
+
+bool ObjectManager::need_update() const
{
- need_update = true;
- scene->geometry_manager->need_update = true;
- scene->light_manager->need_update = true;
+ return update_flags != UPDATE_NONE;
}
string ObjectManager::get_cryptomatte_objects(Scene *scene)
diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h
index 76c9c30abb0..cf1b9ca510a 100644
--- a/intern/cycles/render/object.h
+++ b/intern/cycles/render/object.h
@@ -122,8 +122,24 @@ class Object : public Node {
/* Object Manager */
class ObjectManager {
+ uint32_t update_flags;
+
public:
- bool need_update;
+ enum : uint32_t {
+ PARTICLE_MODIFIED = (1 << 0),
+ GEOMETRY_MANAGER = (1 << 1),
+ MOTION_BLUR_MODIFIED = (1 << 2),
+ OBJECT_ADDED = (1 << 3),
+ OBJECT_REMOVED = (1 << 4),
+ OBJECT_MODIFIED = (1 << 5),
+ HOLDOUT_MODIFIED = (1 << 6),
+
+ /* tag everything in the manager for an update */
+ UPDATE_ALL = ~0u,
+
+ UPDATE_NONE = 0u,
+ };
+
bool need_flags_update;
ObjectManager();
@@ -139,9 +155,11 @@ class ObjectManager {
bool bounds_valid = true);
void device_update_mesh_offsets(Device *device, DeviceScene *dscene, Scene *scene);
- void device_free(Device *device, DeviceScene *dscene);
+ void device_free(Device *device, DeviceScene *dscene, bool force_free);
- void tag_update(Scene *scene);
+ void tag_update(Scene *scene, uint32_t flag);
+
+ bool need_update() const;
void apply_static_transforms(DeviceScene *dscene, Scene *scene, Progress &progress);
@@ -149,7 +167,9 @@ class ObjectManager {
string get_cryptomatte_assets(Scene *scene);
protected:
- void device_update_object_transform(UpdateObjectTransformState *state, Object *ob);
+ void device_update_object_transform(UpdateObjectTransformState *state,
+ Object *ob,
+ bool update_all);
void device_update_object_transform_task(UpdateObjectTransformState *state);
bool device_update_object_transform_pop_work(UpdateObjectTransformState *state,
int *start_index,
diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp
index 35b62746f1b..889636fe0f3 100644
--- a/intern/cycles/render/osl.cpp
+++ b/intern/cycles/render/osl.cpp
@@ -96,7 +96,7 @@ void OSLShaderManager::device_update(Device *device,
Scene *scene,
Progress &progress)
{
- if (!need_update)
+ if (!need_update())
return;
scoped_callback_timer timer([scene](double time) {
@@ -132,7 +132,7 @@ void OSLShaderManager::device_update(Device *device,
compiler.compile(og, shader);
if (shader->get_use_mis() && shader->has_surface_emission)
- scene->light_manager->need_update = true;
+ scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED);
}
/* setup shader engine */
@@ -147,7 +147,7 @@ void OSLShaderManager::device_update(Device *device,
foreach (Shader *shader, scene->shaders)
shader->clear_modified();
- need_update = false;
+ update_flags = UPDATE_NONE;
/* add special builtin texture types */
services->textures.insert(ustring("@ao"), new OSLTextureHandle(OSLTextureHandle::AO));
diff --git a/intern/cycles/render/particles.cpp b/intern/cycles/render/particles.cpp
index faad731d413..0e168050281 100644
--- a/intern/cycles/render/particles.cpp
+++ b/intern/cycles/render/particles.cpp
@@ -46,14 +46,14 @@ ParticleSystem::~ParticleSystem()
void ParticleSystem::tag_update(Scene *scene)
{
- scene->particle_system_manager->need_update = true;
+ scene->particle_system_manager->tag_update(scene);
}
/* Particle System Manager */
ParticleSystemManager::ParticleSystemManager()
{
- need_update = true;
+ need_update_ = true;
}
ParticleSystemManager::~ParticleSystemManager()
@@ -109,7 +109,7 @@ void ParticleSystemManager::device_update(Device *device,
Scene *scene,
Progress &progress)
{
- if (!need_update)
+ if (!need_update())
return;
scoped_callback_timer timer([scene](double time) {
@@ -128,7 +128,7 @@ void ParticleSystemManager::device_update(Device *device,
if (progress.get_cancel())
return;
- need_update = false;
+ need_update_ = false;
}
void ParticleSystemManager::device_free(Device *, DeviceScene *dscene)
@@ -138,7 +138,12 @@ void ParticleSystemManager::device_free(Device *, DeviceScene *dscene)
void ParticleSystemManager::tag_update(Scene * /*scene*/)
{
- need_update = true;
+ need_update_ = true;
+}
+
+bool ParticleSystemManager::need_update() const
+{
+ return need_update_;
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/particles.h b/intern/cycles/render/particles.h
index 0b0408184fe..8b59756f148 100644
--- a/intern/cycles/render/particles.h
+++ b/intern/cycles/render/particles.h
@@ -57,9 +57,9 @@ class ParticleSystem : public Node {
/* ParticleSystem Manager */
class ParticleSystemManager {
- public:
- bool need_update;
+ bool need_update_;
+ public:
ParticleSystemManager();
~ParticleSystemManager();
@@ -71,6 +71,8 @@ class ParticleSystemManager {
void device_free(Device *device, DeviceScene *dscene);
void tag_update(Scene *scene);
+
+ bool need_update() const;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/procedural.cpp b/intern/cycles/render/procedural.cpp
new file mode 100644
index 00000000000..1307a35dcf2
--- /dev/null
+++ b/intern/cycles/render/procedural.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2011-2018 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "procedural.h"
+
+#include "render/scene.h"
+#include "render/stats.h"
+
+#include "util/util_foreach.h"
+#include "util/util_progress.h"
+
+CCL_NAMESPACE_BEGIN
+
+NODE_ABSTRACT_DEFINE(Procedural)
+{
+ NodeType *type = NodeType::add("procedural_base", NULL);
+ return type;
+}
+
+Procedural::Procedural(const NodeType *type) : Node(type)
+{
+}
+
+Procedural::~Procedural()
+{
+}
+
+ProceduralManager::ProceduralManager()
+{
+ need_update_ = true;
+}
+
+ProceduralManager::~ProceduralManager()
+{
+}
+
+void ProceduralManager::update(Scene *scene, Progress &progress)
+{
+ if (!need_update()) {
+ return;
+ }
+
+ progress.set_status("Updating Procedurals");
+
+ scoped_callback_timer timer([scene](double time) {
+ if (scene->update_stats) {
+ scene->update_stats->procedurals.times.add_entry({"update", time});
+ }
+ });
+
+ foreach (Procedural *procedural, scene->procedurals) {
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ procedural->generate(scene, progress);
+ }
+
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ need_update_ = false;
+}
+
+void ProceduralManager::tag_update()
+{
+ need_update_ = true;
+}
+
+bool ProceduralManager::need_update() const
+{
+ return need_update_;
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/render/procedural.h b/intern/cycles/render/procedural.h
new file mode 100644
index 00000000000..985b8d69979
--- /dev/null
+++ b/intern/cycles/render/procedural.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2011-2018 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "graph/node.h"
+
+CCL_NAMESPACE_BEGIN
+
+class Progress;
+class Scene;
+
+/* A Procedural is a Node which can create other Nodes before rendering starts.
+ *
+ * The Procedural is supposed to be the owner of any nodes that it creates. It can also create
+ * Nodes directly in the Scene (through Scene.create_node), it should still be set as the owner of
+ * those Nodes.
+ */
+class Procedural : public Node, public NodeOwner {
+ public:
+ NODE_ABSTRACT_DECLARE
+
+ explicit Procedural(const NodeType *type);
+ virtual ~Procedural();
+
+ /* Called each time the ProceduralManager is tagged for an update, this function is the entry
+ * point for the data generated by this Procedural. */
+ virtual void generate(Scene *scene, Progress &progress) = 0;
+
+ /* Create a node and set this Procedural as the owner. */
+ template<typename T> T *create_node()
+ {
+ T *node = new T();
+ node->set_owner(this);
+ return node;
+ }
+
+ /* Delete a Node created and owned by this Procedural. */
+ template<typename T> void delete_node(T *node)
+ {
+ assert(node->get_owner() == this);
+ delete node;
+ }
+};
+
+class ProceduralManager {
+ bool need_update_;
+
+ public:
+ ProceduralManager();
+ ~ProceduralManager();
+
+ void update(Scene *scene, Progress &progress);
+
+ void tag_update();
+
+ bool need_update() const;
+};
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp
index b7720b7aa99..6bb25677965 100644
--- a/intern/cycles/render/scene.cpp
+++ b/intern/cycles/render/scene.cpp
@@ -18,6 +18,7 @@
#include "bvh/bvh.h"
#include "device/device.h"
+#include "render/alembic.h"
#include "render/background.h"
#include "render/bake.h"
#include "render/camera.h"
@@ -29,6 +30,7 @@
#include "render/object.h"
#include "render/osl.h"
#include "render/particles.h"
+#include "render/procedural.h"
#include "render/scene.h"
#include "render/session.h"
#include "render/shader.h"
@@ -114,6 +116,7 @@ Scene::Scene(const SceneParams &params_, Device *device)
image_manager = new ImageManager(device->info);
particle_system_manager = new ParticleSystemManager();
bake_manager = new BakeManager();
+ procedural_manager = new ProceduralManager();
kernels_loaded = false;
/* TODO(sergey): Check if it's indeed optimal value for the split kernel. */
@@ -142,6 +145,9 @@ void Scene::free_memory(bool final)
foreach (Shader *s, shaders)
delete s;
+ /* delete procedurals before other types as they may hold pointers to those types */
+ foreach (Procedural *p, procedurals)
+ delete p;
foreach (Geometry *g, geometry)
delete g;
foreach (Object *o, objects)
@@ -156,15 +162,16 @@ void Scene::free_memory(bool final)
objects.clear();
lights.clear();
particle_systems.clear();
+ procedurals.clear();
if (device) {
camera->device_free(device, &dscene, this);
film->device_free(device, &dscene, this);
background->device_free(device, &dscene);
- integrator->device_free(device, &dscene);
+ integrator->device_free(device, &dscene, true);
- object_manager->device_free(device, &dscene);
- geometry_manager->device_free(device, &dscene);
+ object_manager->device_free(device, &dscene, true);
+ geometry_manager->device_free(device, &dscene, true);
shader_manager->device_free(device, &dscene, this);
light_manager->device_free(device, &dscene);
@@ -195,6 +202,7 @@ void Scene::free_memory(bool final)
delete image_manager;
delete bake_manager;
delete update_stats;
+ delete procedural_manager;
}
}
@@ -236,6 +244,11 @@ void Scene::device_update(Device *device_, Progress &progress)
if (progress.get_cancel() || device->have_error())
return;
+ procedural_manager->update(this, progress);
+
+ if (progress.get_cancel())
+ return;
+
progress.set_status("Updating Background");
background->device_update(device, &dscene, this);
@@ -386,11 +399,12 @@ bool Scene::need_update()
bool Scene::need_data_update()
{
- return (background->is_modified() || image_manager->need_update || object_manager->need_update ||
- geometry_manager->need_update || light_manager->need_update ||
- lookup_tables->need_update || integrator->is_modified() || shader_manager->need_update ||
- particle_system_manager->need_update || bake_manager->need_update ||
- film->is_modified());
+ return (background->is_modified() || image_manager->need_update() ||
+ object_manager->need_update() || geometry_manager->need_update() ||
+ light_manager->need_update() || lookup_tables->need_update() ||
+ integrator->is_modified() || shader_manager->need_update() ||
+ particle_system_manager->need_update() || bake_manager->need_update() ||
+ film->is_modified() || procedural_manager->need_update());
}
bool Scene::need_reset()
@@ -407,12 +421,15 @@ void Scene::reset()
camera->tag_modified();
dicing_camera->tag_modified();
film->tag_modified();
+ background->tag_modified();
+
background->tag_update(this);
- integrator->tag_update(this);
- object_manager->tag_update(this);
- geometry_manager->tag_update(this);
- light_manager->tag_update(this);
+ integrator->tag_update(this, Integrator::UPDATE_ALL);
+ object_manager->tag_update(this, ObjectManager::UPDATE_ALL);
+ geometry_manager->tag_update(this, GeometryManager::UPDATE_ALL);
+ light_manager->tag_update(this, LightManager::UPDATE_ALL);
particle_system_manager->tag_update(this);
+ procedural_manager->tag_update();
}
void Scene::device_free()
@@ -596,7 +613,7 @@ template<> Light *Scene::create_node<Light>()
Light *node = new Light();
node->set_owner(this);
lights.push_back(node);
- light_manager->tag_update(this);
+ light_manager->tag_update(this, LightManager::LIGHT_ADDED);
return node;
}
@@ -605,7 +622,7 @@ template<> Mesh *Scene::create_node<Mesh>()
Mesh *node = new Mesh();
node->set_owner(this);
geometry.push_back(node);
- geometry_manager->tag_update(this);
+ geometry_manager->tag_update(this, GeometryManager::MESH_ADDED);
return node;
}
@@ -614,7 +631,7 @@ template<> Hair *Scene::create_node<Hair>()
Hair *node = new Hair();
node->set_owner(this);
geometry.push_back(node);
- geometry_manager->tag_update(this);
+ geometry_manager->tag_update(this, GeometryManager::HAIR_ADDED);
return node;
}
@@ -623,7 +640,7 @@ template<> Volume *Scene::create_node<Volume>()
Volume *node = new Volume();
node->set_owner(this);
geometry.push_back(node);
- geometry_manager->tag_update(this);
+ geometry_manager->tag_update(this, GeometryManager::MESH_ADDED);
return node;
}
@@ -632,7 +649,7 @@ template<> Object *Scene::create_node<Object>()
Object *node = new Object();
node->set_owner(this);
objects.push_back(node);
- object_manager->tag_update(this);
+ object_manager->tag_update(this, ObjectManager::OBJECT_ADDED);
return node;
}
@@ -650,10 +667,23 @@ template<> Shader *Scene::create_node<Shader>()
Shader *node = new Shader();
node->set_owner(this);
shaders.push_back(node);
- shader_manager->need_update = true;
+ shader_manager->tag_update(this, ShaderManager::SHADER_ADDED);
return node;
}
+template<> AlembicProcedural *Scene::create_node<AlembicProcedural>()
+{
+#ifdef WITH_ALEMBIC
+ AlembicProcedural *node = new AlembicProcedural();
+ node->set_owner(this);
+ procedurals.push_back(node);
+ procedural_manager->tag_update();
+ return node;
+#else
+ return nullptr;
+#endif
+}
+
template<typename T> void delete_node_from_array(vector<T> &nodes, T node)
{
for (size_t i = 0; i < nodes.size(); ++i) {
@@ -664,43 +694,52 @@ template<typename T> void delete_node_from_array(vector<T> &nodes, T node)
}
nodes.resize(nodes.size() - 1);
+
delete node;
}
template<> void Scene::delete_node_impl(Light *node)
{
delete_node_from_array(lights, node);
- light_manager->tag_update(this);
+ light_manager->tag_update(this, LightManager::LIGHT_REMOVED);
}
template<> void Scene::delete_node_impl(Mesh *node)
{
delete_node_from_array(geometry, static_cast<Geometry *>(node));
- geometry_manager->tag_update(this);
+ geometry_manager->tag_update(this, GeometryManager::MESH_REMOVED);
}
template<> void Scene::delete_node_impl(Hair *node)
{
delete_node_from_array(geometry, static_cast<Geometry *>(node));
- geometry_manager->tag_update(this);
+ geometry_manager->tag_update(this, GeometryManager::HAIR_REMOVED);
}
template<> void Scene::delete_node_impl(Volume *node)
{
delete_node_from_array(geometry, static_cast<Geometry *>(node));
- geometry_manager->tag_update(this);
+ geometry_manager->tag_update(this, GeometryManager::MESH_REMOVED);
}
template<> void Scene::delete_node_impl(Geometry *node)
{
+ uint flag;
+ if (node->is_hair()) {
+ flag = GeometryManager::HAIR_REMOVED;
+ }
+ else {
+ flag = GeometryManager::MESH_REMOVED;
+ }
+
delete_node_from_array(geometry, node);
- geometry_manager->tag_update(this);
+ geometry_manager->tag_update(this, flag);
}
template<> void Scene::delete_node_impl(Object *node)
{
delete_node_from_array(objects, node);
- object_manager->tag_update(this);
+ object_manager->tag_update(this, ObjectManager::OBJECT_REMOVED);
}
template<> void Scene::delete_node_impl(ParticleSystem *node)
@@ -714,6 +753,21 @@ template<> void Scene::delete_node_impl(Shader * /*node*/)
/* don't delete unused shaders, not supported */
}
+template<> void Scene::delete_node_impl(Procedural *node)
+{
+ delete_node_from_array(procedurals, node);
+ procedural_manager->tag_update();
+}
+
+template<> void Scene::delete_node_impl(AlembicProcedural *node)
+{
+#ifdef WITH_ALEMBIC
+ delete_node_impl(static_cast<Procedural *>(node));
+#else
+ (void)node;
+#endif
+}
+
template<typename T>
static void remove_nodes_in_set(const set<T *> &nodes_set,
vector<T *> &nodes_array,
@@ -742,19 +796,19 @@ static void remove_nodes_in_set(const set<T *> &nodes_set,
template<> void Scene::delete_nodes(const set<Light *> &nodes, const NodeOwner *owner)
{
remove_nodes_in_set(nodes, lights, owner);
- light_manager->tag_update(this);
+ light_manager->tag_update(this, LightManager::LIGHT_REMOVED);
}
template<> void Scene::delete_nodes(const set<Geometry *> &nodes, const NodeOwner *owner)
{
remove_nodes_in_set(nodes, geometry, owner);
- geometry_manager->tag_update(this);
+ geometry_manager->tag_update(this, GeometryManager::GEOMETRY_REMOVED);
}
template<> void Scene::delete_nodes(const set<Object *> &nodes, const NodeOwner *owner)
{
remove_nodes_in_set(nodes, objects, owner);
- object_manager->tag_update(this);
+ object_manager->tag_update(this, ObjectManager::OBJECT_REMOVED);
}
template<> void Scene::delete_nodes(const set<ParticleSystem *> &nodes, const NodeOwner *owner)
@@ -768,4 +822,10 @@ template<> void Scene::delete_nodes(const set<Shader *> & /*nodes*/, const NodeO
/* don't delete unused shaders, not supported */
}
+template<> void Scene::delete_nodes(const set<Procedural *> &nodes, const NodeOwner *owner)
+{
+ remove_nodes_in_set(nodes, procedurals, owner);
+ procedural_manager->tag_update();
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h
index 27e9a131bbd..7595817226c 100644
--- a/intern/cycles/render/scene.h
+++ b/intern/cycles/render/scene.h
@@ -36,6 +36,7 @@
CCL_NAMESPACE_BEGIN
+class AlembicProcedural;
class AttributeRequestSet;
class Background;
class BVH;
@@ -53,6 +54,8 @@ class Object;
class ObjectManager;
class ParticleSystemManager;
class ParticleSystem;
+class Procedural;
+class ProceduralManager;
class CurveSystemManager;
class Shader;
class ShaderManager;
@@ -236,6 +239,7 @@ class Scene : public NodeOwner {
vector<Light *> lights;
vector<ParticleSystem *> particle_systems;
vector<Pass> passes;
+ vector<Procedural *> procedurals;
/* data managers */
ImageManager *image_manager;
@@ -245,6 +249,7 @@ class Scene : public NodeOwner {
ObjectManager *object_manager;
ParticleSystemManager *particle_system_manager;
BakeManager *bake_manager;
+ ProceduralManager *procedural_manager;
/* default shaders */
Shader *default_surface;
@@ -379,6 +384,8 @@ template<> ParticleSystem *Scene::create_node<ParticleSystem>();
template<> Shader *Scene::create_node<Shader>();
+template<> AlembicProcedural *Scene::create_node<AlembicProcedural>();
+
template<> void Scene::delete_node_impl(Light *node);
template<> void Scene::delete_node_impl(Mesh *node);
@@ -395,6 +402,10 @@ template<> void Scene::delete_node_impl(ParticleSystem *node);
template<> void Scene::delete_node_impl(Shader *node);
+template<> void Scene::delete_node_impl(Procedural *node);
+
+template<> void Scene::delete_node_impl(AlembicProcedural *node);
+
template<> void Scene::delete_nodes(const set<Light *> &nodes, const NodeOwner *owner);
template<> void Scene::delete_nodes(const set<Geometry *> &nodes, const NodeOwner *owner);
@@ -405,6 +416,8 @@ template<> void Scene::delete_nodes(const set<ParticleSystem *> &nodes, const No
template<> void Scene::delete_nodes(const set<Shader *> &nodes, const NodeOwner *owner);
+template<> void Scene::delete_nodes(const set<Procedural *> &nodes, const NodeOwner *owner);
+
CCL_NAMESPACE_END
#endif /* __SCENE_H__ */
diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp
index 7a30ecb24f7..3c601e18126 100644
--- a/intern/cycles/render/session.cpp
+++ b/intern/cycles/render/session.cpp
@@ -1040,13 +1040,7 @@ bool Session::update_scene()
BakeManager *bake_manager = scene->bake_manager;
if (integrator->get_sampling_pattern() != SAMPLING_PATTERN_SOBOL || bake_manager->get_baking()) {
- int aa_samples = tile_manager.num_samples;
-
- integrator->set_aa_samples(aa_samples);
-
- if (integrator->is_modified()) {
- integrator->tag_update(scene);
- }
+ integrator->set_aa_samples(tile_manager.num_samples);
}
bool kernel_switch_needed = false;
diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp
index 7e06b427e4d..332599be708 100644
--- a/intern/cycles/render/shader.cpp
+++ b/intern/cycles/render/shader.cpp
@@ -16,6 +16,7 @@
#include "device/device.h"
+#include "render/alembic.h"
#include "render/background.h"
#include "render/camera.h"
#include "render/colorspace.h"
@@ -218,7 +219,9 @@ Shader::Shader() : Node(node_type)
id = -1;
used = false;
- need_update_geometry = true;
+ need_update_uvs = true;
+ need_update_attribute = true;
+ need_update_displacement = true;
}
Shader::~Shader()
@@ -291,7 +294,7 @@ void Shader::set_graph(ShaderGraph *graph_)
const char *new_hash = (graph_) ? graph_->displacement_hash.c_str() : "";
if (strcmp(old_hash, new_hash) != 0) {
- need_update_geometry = true;
+ need_update_displacement = true;
}
}
@@ -308,13 +311,14 @@ void Shader::tag_update(Scene *scene)
{
/* update tag */
tag_modified();
- scene->shader_manager->need_update = true;
+
+ scene->shader_manager->tag_update(scene, ShaderManager::SHADER_MODIFIED);
/* if the shader previously was emissive, update light distribution,
* if the new shader is emissive, a light manager update tag will be
* done in the shader manager device update. */
if (use_mis && has_surface_emission)
- scene->light_manager->need_update = true;
+ scene->light_manager->tag_update(scene, LightManager::SHADER_MODIFIED);
/* Special handle of background MIS light for now: for some reason it
* has use_mis set to false. We are quite close to release now, so
@@ -323,7 +327,7 @@ void Shader::tag_update(Scene *scene)
if (this == scene->background->get_shader(scene)) {
scene->light_manager->need_update_background = true;
if (scene->light_manager->has_background_light(scene)) {
- scene->light_manager->need_update = true;
+ scene->light_manager->tag_update(scene, LightManager::SHADER_MODIFIED);
}
}
@@ -352,17 +356,18 @@ void Shader::tag_update(Scene *scene)
attributes.add(ATTR_STD_POSITION_UNDISPLACED);
}
if (displacement_method_is_modified()) {
- need_update_geometry = true;
- scene->geometry_manager->need_update = true;
+ need_update_displacement = true;
+ scene->geometry_manager->tag_update(scene, GeometryManager::SHADER_DISPLACEMENT_MODIFIED);
scene->object_manager->need_flags_update = true;
}
}
/* compare if the attributes changed, mesh manager will check
- * need_update_geometry, update the relevant meshes and clear it. */
+ * need_update_attribute, update the relevant meshes and clear it. */
if (attributes.modified(prev_attributes)) {
- need_update_geometry = true;
- scene->geometry_manager->need_update = true;
+ need_update_attribute = true;
+ scene->geometry_manager->tag_update(scene, GeometryManager::SHADER_ATTRIBUTE_MODIFIED);
+ scene->procedural_manager->tag_update();
}
if (has_volume != prev_has_volume || volume_step_rate != prev_volume_step_rate) {
@@ -378,15 +383,20 @@ void Shader::tag_used(Scene *scene)
* recompiled because it was skipped for compilation before */
if (!used) {
tag_modified();
- scene->shader_manager->need_update = true;
+ scene->shader_manager->tag_update(scene, ShaderManager::SHADER_MODIFIED);
}
}
+bool Shader::need_update_geometry() const
+{
+ return need_update_uvs || need_update_attribute || need_update_displacement;
+}
+
/* Shader Manager */
ShaderManager::ShaderManager()
{
- need_update = true;
+ update_flags = UPDATE_ALL;
beckmann_table_offset = TABLE_OFFSET_INVALID;
xyz_to_r = make_float3(3.2404542f, -1.5371385f, -0.4985314f);
@@ -484,7 +494,7 @@ int ShaderManager::get_shader_id(Shader *shader, bool smooth)
void ShaderManager::update_shaders_used(Scene *scene)
{
- if (!need_update) {
+ if (!need_update()) {
return;
}
@@ -504,6 +514,21 @@ void ShaderManager::update_shaders_used(Scene *scene)
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);
@@ -793,4 +818,15 @@ string ShaderManager::get_cryptomatte_materials(Scene *scene)
return manifest;
}
+void ShaderManager::tag_update(Scene * /*scene*/, uint32_t /*flag*/)
+{
+ /* update everything for now */
+ update_flags = ShaderManager::UPDATE_ALL;
+}
+
+bool ShaderManager::need_update() const
+{
+ return update_flags != UPDATE_NONE;
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h
index de19048d8e1..4375ef9e978 100644
--- a/intern/cycles/render/shader.h
+++ b/intern/cycles/render/shader.h
@@ -100,7 +100,9 @@ class Shader : public Node {
float prev_volume_step_rate;
/* synchronization */
- bool need_update_geometry;
+ bool need_update_uvs;
+ bool need_update_attribute;
+ bool need_update_displacement;
/* If the shader has only volume components, the surface is assumed to
* be transparent.
@@ -152,6 +154,8 @@ class Shader : public Node {
void set_graph(ShaderGraph *graph);
void tag_update(Scene *scene);
void tag_used(Scene *scene);
+
+ bool need_update_geometry() const;
};
/* Shader Manager virtual base class
@@ -161,7 +165,16 @@ class Shader : public Node {
class ShaderManager {
public:
- bool need_update;
+ enum : uint32_t {
+ SHADER_ADDED = (1 << 0),
+ SHADER_MODIFIED = (1 << 2),
+ INTEGRATOR_MODIFIED = (1 << 3),
+
+ /* tag everything in the manager for an update */
+ UPDATE_ALL = ~0u,
+
+ UPDATE_NONE = 0u,
+ };
static ShaderManager *create(int shadingsystem);
virtual ~ShaderManager();
@@ -204,9 +217,15 @@ class ShaderManager {
string get_cryptomatte_materials(Scene *scene);
+ void tag_update(Scene *scene, uint32_t flag);
+
+ bool need_update() const;
+
protected:
ShaderManager();
+ uint32_t update_flags;
+
typedef unordered_map<ustring, uint, ustringHash> AttributeIDMap;
AttributeIDMap unique_attribute_id;
diff --git a/intern/cycles/render/stats.cpp b/intern/cycles/render/stats.cpp
index 1a840a906a5..2c6273842e2 100644
--- a/intern/cycles/render/stats.cpp
+++ b/intern/cycles/render/stats.cpp
@@ -375,6 +375,7 @@ string SceneUpdateStats::full_report()
result += "Particles:\n" + particles.full_report(1);
result += "SVM:\n" + svm.full_report(1);
result += "Tables:\n" + tables.full_report(1);
+ result += "Procedurals:\n" + procedurals.full_report(1);
return result;
}
@@ -394,6 +395,7 @@ void SceneUpdateStats::clear()
scene.times.clear();
svm.times.clear();
tables.times.clear();
+ procedurals.times.clear();
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/stats.h b/intern/cycles/render/stats.h
index a6be27db4c2..93d029bba61 100644
--- a/intern/cycles/render/stats.h
+++ b/intern/cycles/render/stats.h
@@ -219,6 +219,7 @@ class SceneUpdateStats {
UpdateTimeStats scene;
UpdateTimeStats svm;
UpdateTimeStats tables;
+ UpdateTimeStats procedurals;
string full_report();
diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp
index 6f5a03124f2..fce604234f1 100644
--- a/intern/cycles/render/svm.cpp
+++ b/intern/cycles/render/svm.cpp
@@ -74,7 +74,7 @@ void SVMShaderManager::device_update(Device *device,
Scene *scene,
Progress &progress)
{
- if (!need_update)
+ if (!need_update())
return;
scoped_callback_timer timer([scene](double time) {
@@ -125,7 +125,7 @@ void SVMShaderManager::device_update(Device *device,
shader->clear_modified();
if (shader->get_use_mis() && shader->has_surface_emission) {
- scene->light_manager->need_update = true;
+ scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED);
}
/* Update the global jump table.
@@ -159,7 +159,7 @@ void SVMShaderManager::device_update(Device *device,
device_update_common(device, dscene, scene, progress);
- need_update = false;
+ update_flags = UPDATE_NONE;
VLOG(1) << "Shader manager updated " << num_shaders << " shaders in " << time_dt() - start_time
<< " seconds.";
diff --git a/intern/cycles/render/tables.cpp b/intern/cycles/render/tables.cpp
index b581537c852..a0813400b1c 100644
--- a/intern/cycles/render/tables.cpp
+++ b/intern/cycles/render/tables.cpp
@@ -28,7 +28,7 @@ CCL_NAMESPACE_BEGIN
LookupTables::LookupTables()
{
- need_update = true;
+ need_update_ = true;
}
LookupTables::~LookupTables()
@@ -38,7 +38,7 @@ LookupTables::~LookupTables()
void LookupTables::device_update(Device *, DeviceScene *dscene, Scene *scene)
{
- if (!need_update)
+ if (!need_update())
return;
scoped_callback_timer timer([scene](double time) {
@@ -52,7 +52,7 @@ void LookupTables::device_update(Device *, DeviceScene *dscene, Scene *scene)
if (lookup_tables.size() > 0)
dscene->lookup_table.copy_to_device();
- need_update = false;
+ need_update_ = false;
}
void LookupTables::device_free(Device *, DeviceScene *dscene)
@@ -60,6 +60,11 @@ void LookupTables::device_free(Device *, DeviceScene *dscene)
dscene->lookup_table.free();
}
+bool LookupTables::need_update() const
+{
+ return need_update_;
+}
+
static size_t round_up_to_multiple(size_t size, size_t chunk)
{
return ((size + chunk - 1) / chunk) * chunk;
@@ -69,7 +74,7 @@ size_t LookupTables::add_table(DeviceScene *dscene, vector<float> &data)
{
assert(data.size() > 0);
- need_update = true;
+ need_update_ = true;
Table new_table;
new_table.offset = 0;
@@ -107,7 +112,7 @@ void LookupTables::remove_table(size_t *offset)
return;
}
- need_update = true;
+ need_update_ = true;
list<Table>::iterator table;
diff --git a/intern/cycles/render/tables.h b/intern/cycles/render/tables.h
index e912d9c01f4..de538e2af78 100644
--- a/intern/cycles/render/tables.h
+++ b/intern/cycles/render/tables.h
@@ -30,13 +30,14 @@ enum { TABLE_CHUNK_SIZE = 256 };
enum { TABLE_OFFSET_INVALID = -1 };
class LookupTables {
+ bool need_update_;
+
public:
struct Table {
size_t offset;
size_t size;
};
- bool need_update;
list<Table> lookup_tables;
LookupTables();
@@ -45,6 +46,8 @@ class LookupTables {
void device_update(Device *device, DeviceScene *dscene, Scene *scene);
void device_free(Device *device, DeviceScene *dscene);
+ bool need_update() const;
+
size_t add_table(DeviceScene *dscene, vector<float> &data);
void remove_table(size_t *offset);
};
diff --git a/intern/cycles/util/util_array.h b/intern/cycles/util/util_array.h
index ea481787018..73f7d6cf7f8 100644
--- a/intern/cycles/util/util_array.h
+++ b/intern/cycles/util/util_array.h
@@ -131,6 +131,14 @@ template<typename T, size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES> class arra
}
}
+ void set_data(T *ptr_, size_t datasize)
+ {
+ clear();
+ data_ = ptr_;
+ datasize_ = datasize;
+ capacity_ = datasize;
+ }
+
T *steal_pointer()
{
T *ptr = data_;
diff --git a/intern/ghost/intern/GHOST_DropTargetX11.h b/intern/ghost/intern/GHOST_DropTargetX11.h
index 69f6fec7ac1..7d777270317 100644
--- a/intern/ghost/intern/GHOST_DropTargetX11.h
+++ b/intern/ghost/intern/GHOST_DropTargetX11.h
@@ -109,7 +109,7 @@ class GHOST_DropTargetX11 {
/* class holding internal stiff of xdnd library */
static DndClass m_dndClass;
- /* list of supported types to eb draggeg into */
+ /* list of supported types to be dragged into */
static Atom *m_dndTypes;
/* list of supported dran'n'drop actions */
diff --git a/intern/guardedalloc/CMakeLists.txt b/intern/guardedalloc/CMakeLists.txt
index 0d46e81cd87..b47565b8ef9 100644
--- a/intern/guardedalloc/CMakeLists.txt
+++ b/intern/guardedalloc/CMakeLists.txt
@@ -49,12 +49,6 @@ set(LIB
)
if(WIN32 AND NOT UNIX)
- list(APPEND SRC
- intern/mmap_win.c
-
- mmap_win.h
- )
-
list(APPEND INC_SYS
${PTHREADS_INC}
)
diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h
index a0174e30aff..62f28c9e97e 100644
--- a/intern/guardedalloc/MEM_guardedalloc.h
+++ b/intern/guardedalloc/MEM_guardedalloc.h
@@ -122,7 +122,7 @@ extern void *(*MEM_calloc_arrayN)(size_t len,
/**
* Allocate a block of memory of size len, with tag name str. The
* name must be a static, because only a pointer to it is stored !
- * */
+ */
extern void *(*MEM_mallocN)(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
@@ -130,7 +130,7 @@ extern void *(*MEM_mallocN)(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_
* Allocate a block of memory of size (len * size), with tag name str,
* aborting in case of integer overflow to prevent vulnerabilities. The
* name must be a static, because only a pointer to it is stored !
- * */
+ */
extern void *(*MEM_malloc_arrayN)(size_t len,
size_t size,
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
@@ -139,7 +139,7 @@ extern void *(*MEM_malloc_arrayN)(size_t len,
/**
* Allocate an aligned block of memory of size len, with tag name str. The
* name must be a static, because only a pointer to it is stored !
- * */
+ */
extern void *(*MEM_mallocN_aligned)(size_t len,
size_t alignment,
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
diff --git a/intern/guardedalloc/intern/mmap_win.c b/intern/guardedalloc/intern/mmap_win.c
deleted file mode 100644
index a02a0f88fa9..00000000000
--- a/intern/guardedalloc/intern/mmap_win.c
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2008 Blender Foundation.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup MEM
- */
-
-#ifdef WIN32
-
-# include <errno.h>
-# include <io.h>
-# include <stdio.h>
-# include <sys/types.h>
-# include <windows.h>
-
-# include "mmap_win.h"
-
-# ifndef FILE_MAP_EXECUTE
-// not defined in earlier versions of the Platform SDK (before February 2003)
-# define FILE_MAP_EXECUTE 0x0020
-# endif
-
-/* copied from BLI_utildefines.h, ugh */
-# ifdef __GNUC__
-# define UNUSED(x) UNUSED_##x __attribute__((__unused__))
-# else
-# define UNUSED(x) x
-# endif
-
-/* --------------------------------------------------------------------- */
-/* local storage definitions */
-/* --------------------------------------------------------------------- */
-/* all memory mapped chunks are put in linked lists */
-typedef struct mmapLink {
- struct mmapLink *next, *prev;
-} mmapLink;
-
-typedef struct mmapListBase {
- void *first, *last;
-} mmapListBase;
-
-typedef struct MemMap {
- struct MemMap *next, *prev;
- void *mmap;
- HANDLE fhandle;
- HANDLE maphandle;
-} MemMap;
-
-/* --------------------------------------------------------------------- */
-/* local functions */
-/* --------------------------------------------------------------------- */
-
-static void mmap_addtail(volatile mmapListBase *listbase, void *vlink);
-static void mmap_remlink(volatile mmapListBase *listbase, void *vlink);
-static void *mmap_findlink(volatile mmapListBase *listbase, void *ptr);
-
-static int mmap_get_prot_flags(int flags);
-static int mmap_get_access_flags(int flags);
-
-/* --------------------------------------------------------------------- */
-/* vars */
-/* --------------------------------------------------------------------- */
-volatile static struct mmapListBase _mmapbase;
-volatile static struct mmapListBase *mmapbase = &_mmapbase;
-
-/* --------------------------------------------------------------------- */
-/* implementation */
-/* --------------------------------------------------------------------- */
-
-/* mmap for windows */
-void *mmap(void *UNUSED(start), size_t len, int prot, int flags, int fd, off_t offset)
-{
- HANDLE fhandle = INVALID_HANDLE_VALUE;
- HANDLE maphandle;
- int prot_flags = mmap_get_prot_flags(prot);
- int access_flags = mmap_get_access_flags(prot);
- MemMap *mm = NULL;
- void *ptr = NULL;
-
- if (flags & MAP_FIXED) {
- return MAP_FAILED;
- }
-
-# if 0
- if (fd == -1) {
- _set_errno(EBADF);
- return MAP_FAILED;
- }
-# endif
-
- if (fd != -1) {
- fhandle = (HANDLE)_get_osfhandle(fd);
- }
- if (fhandle == INVALID_HANDLE_VALUE) {
- if (!(flags & MAP_ANONYMOUS)) {
- errno = EBADF;
- return MAP_FAILED;
- }
- }
- else {
- if (!DuplicateHandle(GetCurrentProcess(),
- fhandle,
- GetCurrentProcess(),
- &fhandle,
- 0,
- FALSE,
- DUPLICATE_SAME_ACCESS)) {
- return MAP_FAILED;
- }
- }
-
- /* Split 64 bit size into low and high bits. */
- DWORD len_bits_high = len >> 32;
- DWORD len_bits_low = len & 0xFFFFFFFF;
-
- maphandle = CreateFileMapping(fhandle, NULL, prot_flags, len_bits_high, len_bits_low, NULL);
- if (maphandle == 0) {
- errno = EBADF;
- return MAP_FAILED;
- }
-
- ptr = MapViewOfFile(maphandle, access_flags, 0, offset, 0);
- if (ptr == NULL) {
- DWORD dwLastErr = GetLastError();
- if (dwLastErr == ERROR_MAPPED_ALIGNMENT) {
- errno = EINVAL;
- }
- else {
- errno = EACCES;
- }
- CloseHandle(maphandle);
- return MAP_FAILED;
- }
-
- mm = (MemMap *)malloc(sizeof(MemMap));
- if (!mm) {
- errno = ENOMEM;
- }
- mm->fhandle = fhandle;
- mm->maphandle = maphandle;
- mm->mmap = ptr;
- mmap_addtail(mmapbase, mm);
-
- return ptr;
-}
-
-/* munmap for windows */
-intptr_t munmap(void *ptr, size_t UNUSED(size))
-{
- MemMap *mm = mmap_findlink(mmapbase, ptr);
- if (!mm) {
- errno = EINVAL;
- return -1;
- }
- UnmapViewOfFile(mm->mmap);
- CloseHandle(mm->maphandle);
- CloseHandle(mm->fhandle);
- mmap_remlink(mmapbase, mm);
- free(mm);
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-/* local functions */
-/* --------------------------------------------------------------------- */
-
-static void mmap_addtail(volatile mmapListBase *listbase, void *vlink)
-{
- struct mmapLink *link = vlink;
-
- if (link == NULL) {
- return;
- }
- if (listbase == NULL) {
- return;
- }
-
- link->next = 0;
- link->prev = listbase->last;
-
- if (listbase->last) {
- ((struct mmapLink *)listbase->last)->next = link;
- }
- if (listbase->first == NULL) {
- listbase->first = link;
- }
- listbase->last = link;
-}
-
-static void mmap_remlink(volatile mmapListBase *listbase, void *vlink)
-{
- struct mmapLink *link = vlink;
-
- if (link == NULL) {
- return;
- }
- if (listbase == NULL) {
- return;
- }
- if (link->next) {
- link->next->prev = link->prev;
- }
- if (link->prev) {
- link->prev->next = link->next;
- }
-
- if (listbase->last == link) {
- listbase->last = link->prev;
- }
- if (listbase->first == link) {
- listbase->first = link->next;
- }
-}
-
-static void *mmap_findlink(volatile mmapListBase *listbase, void *ptr)
-{
- MemMap *mm;
-
- if (ptr == NULL) {
- return NULL;
- }
- if (listbase == NULL) {
- return NULL;
- }
-
- mm = (MemMap *)listbase->first;
- while (mm) {
- if (mm->mmap == ptr) {
- return mm;
- }
- mm = mm->next;
- }
- return NULL;
-}
-
-static int mmap_get_prot_flags(int flags)
-{
- int prot = PAGE_NOACCESS;
-
- if ((flags & PROT_READ) == PROT_READ) {
- if ((flags & PROT_WRITE) == PROT_WRITE) {
- prot = (flags & PROT_EXEC) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
- }
- else {
- prot = (flags & PROT_EXEC) ? PAGE_EXECUTE_READ : PAGE_READONLY;
- }
- }
- else if ((flags & PROT_WRITE) == PROT_WRITE) {
- prot = (flags & PROT_EXEC) ? PAGE_EXECUTE_READ : PAGE_WRITECOPY;
- }
- else if ((flags & PROT_EXEC) == PROT_EXEC) {
- prot = PAGE_EXECUTE_READ;
- }
- return prot;
-}
-
-static int mmap_get_access_flags(int flags)
-{
- int access = 0;
-
- if ((flags & PROT_READ) == PROT_READ) {
- if ((flags & PROT_WRITE) == PROT_WRITE) {
- access = FILE_MAP_WRITE;
- }
- else {
- access = (flags & PROT_EXEC) ? FILE_MAP_EXECUTE : FILE_MAP_READ;
- }
- }
- else if ((flags & PROT_WRITE) == PROT_WRITE) {
- access = FILE_MAP_COPY;
- }
- else if ((flags & PROT_EXEC) == PROT_EXEC) {
- access = FILE_MAP_EXECUTE;
- }
- return access;
-}
-
-#endif // WIN32
diff --git a/intern/guardedalloc/mmap_win.h b/intern/guardedalloc/mmap_win.h
deleted file mode 100644
index c0cbaa0e512..00000000000
--- a/intern/guardedalloc/mmap_win.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2008 Blender Foundation.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup MEM
- */
-
-#ifndef __MMAP_WIN_H__
-#define __MMAP_WIN_H__
-
-#define PROT_NONE 0
-#define PROT_READ 1
-#define PROT_WRITE 2
-#define PROT_EXEC 4
-
-#define MAP_FILE 0
-#define MAP_SHARED 1
-#define MAP_PRIVATE 2
-#define MAP_TYPE 0xF
-#define MAP_FIXED 0x10
-#define MAP_ANONYMOUS 0x20
-#define MAP_ANON MAP_ANONYMOUS
-
-#define MAP_FAILED ((void *)-1)
-
-/* needed for uintptr_t, exception, dont use BLI anywhere else in MEM_* */
-#include "../../source/blender/blenlib/BLI_sys_types.h"
-
-#include <sys/types.h>
-
-void *mmap(void *start, size_t len, int prot, int flags, int fd, off_t offset);
-intptr_t munmap(void *ptr, size_t size);
-
-#endif