diff options
426 files changed, 11030 insertions, 5384 deletions
diff --git a/build_files/cmake/Modules/FindClangTidy.cmake b/build_files/cmake/Modules/FindClangTidy.cmake index 04c5dfda448..4cf416e67d7 100644 --- a/build_files/cmake/Modules/FindClangTidy.cmake +++ b/build_files/cmake/Modules/FindClangTidy.cmake @@ -34,6 +34,8 @@ set(_clang_tidy_SEARCH_DIRS # TODO(sergey): Find more reliable way of finding the latest clang-tidy. find_program(CLANG_TIDY_EXECUTABLE NAMES + clang-tidy-12 + clang-tidy-11 clang-tidy-10 clang-tidy-9 clang-tidy-8 diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index 772fac56f63..f9055f02bcf 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = Blender # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "V2.92" +PROJECT_NUMBER = "V2.93" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a 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 ¶m = 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 ¶m = 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 ¶m = 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..c97a6549653 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,10 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene } }); + if (sampling_pattern_is_modified()) { + dscene->sample_pattern_lut.tag_realloc(); + } + device_free(device, dscene); KernelIntegrator *kintegrator = &dscene->data.integrator; @@ -242,45 +248,63 @@ 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 (sampling_pattern_is_modified()) { + 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(); } 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 (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..57dcaf95360 100644 --- a/intern/cycles/render/integrator.h +++ b/intern/cycles/render/integrator.h @@ -89,13 +89,23 @@ class Integrator : public Node { NODE_SOCKET_API(SamplingPattern, sampling_pattern) + enum : uint32_t { + AO_PASS_MODIFIED = (1 << 0), + BACKGROUND_AO_MODIFIED = (1 << 1), + + /* 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..6e9dea564c2 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -162,7 +162,9 @@ 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); + } } bool Light::has_contribution(Scene *scene) @@ -183,7 +185,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 +964,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 +1002,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 +1017,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 +1070,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 +1089,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 ¶ms_, 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 diff --git a/release/datafiles/splash.png b/release/datafiles/splash.png Binary files differindex 858709833fb..babb3e30c6d 100644 --- a/release/datafiles/splash.png +++ b/release/datafiles/splash.png diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index b448a230da8..2265e70df39 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -119,6 +119,7 @@ const UserDef U_default = { .gizmo_flag = USER_GIZMO_DRAW, .gizmo_size = 75, + .gizmo_size_navigate_v3d = 80, .edit_studio_light = 0, .lookdev_sphere_size = 150, .vbotimeout = 120, diff --git a/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py b/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py index ebc5370a7af..ef398d5e08f 100644 --- a/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py +++ b/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py @@ -74,8 +74,6 @@ def generate(context, space_type, use_fallback_keys=True, use_reset=True): if tap_reset_tool not in items_all_id: use_tap_reset = False - from bl_operators.wm import use_toolbar_release_hack - # Pie-menu style release to activate. use_release_confirm = use_reset @@ -437,7 +435,7 @@ def generate(context, space_type, use_fallback_keys=True, use_reset=True): kmi = keymap.keymap_items.new( "wm.tool_set_by_id", - value='PRESS' if use_toolbar_release_hack else 'DOUBLE_CLICK', + value='DOUBLE_CLICK', **kmi_toolbar_args_available, ) kmi.properties.name = tap_reset_tool @@ -451,15 +449,6 @@ def generate(context, space_type, use_fallback_keys=True, use_reset=True): ) kmi.properties.skip_depressed = True - if use_toolbar_release_hack: - # ... or pass through to let the toolbar know we're released. - # Let the operator know we're released. - kmi = keymap.keymap_items.new( - "wm.tool_set_by_id", - type=kmi_toolbar_type, - value='RELEASE', - any=True, - ) wm.keyconfigs.update() return keymap diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index ee46742fbd0..2ee20e08589 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -33,11 +33,6 @@ from bpy.props import ( ) from bpy.app.translations import pgettext_iface as iface_ -# FIXME, we need a way to detect key repeat events. -# unfortunately checking event previous values isn't reliable. -use_toolbar_release_hack = True - - rna_path_prop = StringProperty( name="Context Attributes", description="RNA context string", @@ -1698,18 +1693,6 @@ class WM_OT_tool_set_by_id(Operator): space_type: rna_space_type_prop - if use_toolbar_release_hack: - def invoke(self, context, event): - # Hack :S - if not self.properties.is_property_set("name"): - WM_OT_toolbar._key_held = False - return {'PASS_THROUGH'} - elif (WM_OT_toolbar._key_held == event.type) and (event.value != 'RELEASE'): - return {'PASS_THROUGH'} - WM_OT_toolbar._key_held = None - - return self.execute(context) - def execute(self, context): from bl_ui.space_toolsystem_common import ( activate_by_id, @@ -1801,13 +1784,6 @@ class WM_OT_toolbar(Operator): def poll(cls, context): return context.space_data is not None - if use_toolbar_release_hack: - _key_held = None - - def invoke(self, context, event): - WM_OT_toolbar._key_held = event.type - return self.execute(context) - @staticmethod def keymap_from_toolbar(context, space_type, use_fallback_keys=True, use_reset=True): from bl_ui.space_toolsystem_common import ToolSelectPanelHelper diff --git a/release/scripts/startup/bl_ui/properties_data_gpencil.py b/release/scripts/startup/bl_ui/properties_data_gpencil.py index 351d4928ddf..9db6eedb04c 100644 --- a/release/scripts/startup/bl_ui/properties_data_gpencil.py +++ b/release/scripts/startup/bl_ui/properties_data_gpencil.py @@ -23,6 +23,7 @@ from rna_prop_ui import PropertyPanel from bl_ui.properties_grease_pencil_common import ( GreasePencilLayerMasksPanel, + GreasePencilLayerTransformPanel, GreasePencilLayerAdjustmentsPanel, GreasePencilLayerRelationsPanel, GreasePencilLayerDisplayPanel, @@ -90,8 +91,10 @@ class GPENCIL_MT_layer_context_menu(Menu): layout = self.layout ob = context.object gpd = ob.data + gpl = gpd.layers.active - layout.operator("gpencil.layer_duplicate", icon='DUPLICATE') + layout.operator("gpencil.layer_duplicate", text="Duplicate", icon='DUPLICATE').mode='ALL' + layout.operator("gpencil.layer_duplicate", text="Duplicate Empty Keyframes").mode='EMPTY' layout.separator() @@ -103,6 +106,7 @@ class GPENCIL_MT_layer_context_menu(Menu): layout.operator("gpencil.lock_all", icon='LOCKED', text="Lock All") layout.operator("gpencil.unlock_all", icon='UNLOCKED', text="Unlock All") layout.prop(gpd, "use_autolock_layers", text="Autolock Inactive Layers") + layout.prop(gpl, "lock_material") layout.separator() @@ -188,6 +192,12 @@ class DATA_PT_gpencil_layer_masks(LayerDataButtonsPanel, GreasePencilLayerMasksP bl_options = {'DEFAULT_CLOSED'} +class DATA_PT_gpencil_layer_transform(LayerDataButtonsPanel, GreasePencilLayerTransformPanel, Panel): + bl_label = "Transform" + bl_parent_id = 'DATA_PT_gpencil_layers' + bl_options = {'DEFAULT_CLOSED'} + + class DATA_PT_gpencil_layer_adjustments(LayerDataButtonsPanel, GreasePencilLayerAdjustmentsPanel, Panel): bl_label = "Adjustments" bl_parent_id = 'DATA_PT_gpencil_layers' @@ -424,6 +434,7 @@ classes = ( DATA_PT_gpencil_onion_skinning_custom_colors, DATA_PT_gpencil_onion_skinning_display, DATA_PT_gpencil_layer_masks, + DATA_PT_gpencil_layer_transform, DATA_PT_gpencil_layer_adjustments, DATA_PT_gpencil_layer_relations, DATA_PT_gpencil_layer_display, diff --git a/release/scripts/startup/bl_ui/properties_data_volume.py b/release/scripts/startup/bl_ui/properties_data_volume.py index d4f108dcf24..e7bf9adb876 100644 --- a/release/scripts/startup/bl_ui/properties_data_volume.py +++ b/release/scripts/startup/bl_ui/properties_data_volume.py @@ -63,7 +63,7 @@ class DATA_PT_volume_file(DataButtonsPanel, Panel): layout.prop(volume, "filepath", text="") - if len(volume.filepath): + if volume.filepath: layout.use_property_split = True layout.use_property_decorate = False @@ -76,7 +76,7 @@ class DATA_PT_volume_file(DataButtonsPanel, Panel): col.prop(volume, "sequence_mode", text="Mode") error_msg = volume.grids.error_message - if len(error_msg): + if error_msg: layout.separator() col = layout.column(align=True) col.label(text="Failed to load volume:") diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 8b404c4a306..67905192fe8 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -727,12 +727,32 @@ class GreasePencilSimplifyPanel: col.prop(rd, "simplify_gpencil_antialiasing") +class GreasePencilLayerTransformPanel: + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + ob = context.object + gpd = ob.data + gpl = gpd.layers.active + layout.active = not gpl.lock + + row = layout.row(align=True) + row.prop(gpl, "location") + + row = layout.row(align=True) + row.prop(gpl, "rotation") + + row = layout.row(align=True) + row.prop(gpl, "scale") + + class GreasePencilLayerAdjustmentsPanel: def draw(self, context): layout = self.layout layout.use_property_split = True - scene = context.scene ob = context.object gpd = ob.data @@ -750,15 +770,6 @@ class GreasePencilLayerAdjustmentsPanel: col = layout.row(align=True) col.prop(gpl, "line_change", text="Stroke Thickness") - col = layout.row(align=True) - col.prop(gpl, "pass_index") - - col = layout.row(align=True) - col.prop_search(gpl, "viewlayer_render", scene, "view_layers", text="View Layer") - - col = layout.row(align=True) - col.prop(gpl, "lock_material") - class GPENCIL_UL_masks(UIList): def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): @@ -826,6 +837,7 @@ class GreasePencilLayerRelationsPanel: layout.use_property_split = True layout.use_property_decorate = False + scene = context.scene ob = context.object gpd = ob.data gpl = gpd.layers.active @@ -839,6 +851,14 @@ class GreasePencilLayerRelationsPanel: if parent and gpl.parent_type == 'BONE' and parent.type == 'ARMATURE': col.prop_search(gpl, "parent_bone", parent.data, "bones", text="Bone") + layout.separator() + + col = layout.row(align=True) + col.prop(gpl, "pass_index") + + col = layout.row(align=True) + col.prop_search(gpl, "viewlayer_render", scene, "view_layers", text="View Layer") + class GreasePencilLayerDisplayPanel: diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index 9d74a9c9fea..96920af1c7e 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -502,7 +502,9 @@ class CLIP_PT_tools_tracking(CLIP_PT_tracking_panel, Panel): col = layout.column(align=True) row = col.row(align=True) row.label(text="Merge:") - row.operator("clip.join_tracks", text="Join Tracks") + sub = row.column() + sub.operator("clip.join_tracks", text="Join Tracks") + sub.operator("clip.average_tracks", text="Average Tracks") class CLIP_PT_tools_plane_tracking(CLIP_PT_tracking_panel, Panel): @@ -1482,6 +1484,7 @@ class CLIP_MT_track(Menu): layout.separator() layout.operator("clip.join_tracks") + layout.operator("clip.average_tracks") layout.separator() @@ -1608,6 +1611,7 @@ class CLIP_MT_tracking_context_menu(Menu): layout.separator() layout.operator("clip.join_tracks") + layout.operator("clip.average_tracks") layout.separator() diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py index 0c222e8c023..aa98e4292f4 100644 --- a/release/scripts/startup/bl_ui/space_dopesheet.py +++ b/release/scripts/startup/bl_ui/space_dopesheet.py @@ -27,6 +27,7 @@ from bpy.types import ( from bl_ui.properties_grease_pencil_common import ( GreasePencilLayerMasksPanel, + GreasePencilLayerTransformPanel, GreasePencilLayerAdjustmentsPanel, GreasePencilLayerRelationsPanel, GreasePencilLayerDisplayPanel, @@ -726,6 +727,12 @@ class DOPESHEET_PT_gpencil_layer_masks(LayersDopeSheetPanel, GreasePencilLayerMa bl_options = {'DEFAULT_CLOSED'} +class DOPESHEET_PT_gpencil_layer_transform(LayersDopeSheetPanel, GreasePencilLayerTransformPanel, Panel): + bl_label = "Transform" + bl_parent_id = 'DOPESHEET_PT_gpencil_mode' + bl_options = {'DEFAULT_CLOSED'} + + class DOPESHEET_PT_gpencil_layer_adjustments(LayersDopeSheetPanel, GreasePencilLayerAdjustmentsPanel, Panel): bl_label = "Adjustments" bl_parent_id = 'DOPESHEET_PT_gpencil_mode' @@ -762,6 +769,7 @@ classes = ( DOPESHEET_PT_filters, DOPESHEET_PT_gpencil_mode, DOPESHEET_PT_gpencil_layer_masks, + DOPESHEET_PT_gpencil_layer_transform, DOPESHEET_PT_gpencil_layer_adjustments, DOPESHEET_PT_gpencil_layer_relations, DOPESHEET_PT_gpencil_layer_display, diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index e6af83b61f4..a9934850acd 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -678,16 +678,8 @@ class NODE_UL_interface_sockets(bpy.types.UIList): if self.layout_type in {'DEFAULT', 'COMPACT'}: row = layout.row(align=True) - # inputs get icon on the left - if not socket.is_output: - row.template_node_socket(color=color) - + row.template_node_socket(color=color) row.prop(socket, "name", text="", emboss=False, icon_value=icon) - - # outputs get icon on the right - if socket.is_output: - row.template_node_socket(color=color) - elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.template_node_socket(color=color) diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 4b30266a5fe..a0b5355e64d 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -691,6 +691,9 @@ class USERPREF_PT_viewport_display(ViewportPanel, CenterAlignMixIn, Panel): col.prop(view, "mini_axis_size", text="Size") col.prop(view, "mini_axis_brightness", text="Brightness") + if view.mini_axis_type == 'GIZMO': + col.prop(view, "gizmo_size_navigate_v3d", text="Size") + class USERPREF_PT_viewport_quality(ViewportPanel, CenterAlignMixIn, Panel): bl_label = "Quality" diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index f2fd04499f5..e653ead21bb 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2236,11 +2236,6 @@ class VIEW3D_MT_object_relations(Menu): layout.operator_menu_enum("object.make_local", "type", text="Make Local...") layout.menu("VIEW3D_MT_make_single_user") - layout.separator() - - layout.operator("object.data_transfer") - layout.operator("object.datalayout_transfer") - class VIEW3D_MT_object(Menu): bl_context = "objectmode" @@ -2274,7 +2269,7 @@ class VIEW3D_MT_object(Menu): layout.menu("VIEW3D_MT_object_relations") layout.menu("VIEW3D_MT_object_constraints") layout.menu("VIEW3D_MT_object_track") - layout.menu("VIEW3D_MT_make_links", text="Make Links") + layout.menu("VIEW3D_MT_make_links") layout.separator() @@ -2292,16 +2287,7 @@ class VIEW3D_MT_object(Menu): layout.separator() - ob = context.active_object - if ob and ob.type == 'GPENCIL' and context.gpencil_data: - layout.operator_menu_enum("gpencil.convert", "type", text="Convert To") - else: - layout.operator_menu_enum("object.convert", "target") - - # Potrace lib dependency - if bpy.app.build_options.potrace: - layout.separator() - layout.operator("gpencil.trace_image") + layout.menu("VIEW3D_MT_object_convert") layout.separator() @@ -2397,37 +2383,25 @@ class VIEW3D_MT_object_context_menu(Menu): ''' # If something is selected - if obj is not None and obj.type in {'MESH', 'CURVE', 'SURFACE'}: - layout.operator("object.shade_smooth", text="Shade Smooth") - layout.operator("object.shade_flat", text="Shade Flat") - - layout.separator() + # Individual object types. if obj is None: pass - elif obj.type == 'MESH': - layout.operator_context = 'INVOKE_REGION_WIN' - layout.operator_menu_enum("object.origin_set", text="Set Origin", property="type") - - layout.operator_context = 'INVOKE_DEFAULT' - # If more than one object is selected - if selected_objects_len > 1: - layout.operator("object.join") - - layout.separator() elif obj.type == 'CAMERA': layout.operator_context = 'INVOKE_REGION_WIN' + layout.operator("view3d.object_as_camera", text="Set Active Camera") + if obj.data.type == 'PERSP': - props = layout.operator("wm.context_modal_mouse", text="Camera Lens Angle") + props = layout.operator("wm.context_modal_mouse", text="Adjust Focal Length") props.data_path_iter = "selected_editable_objects" props.data_path_item = "data.lens" props.input_scale = 0.1 if obj.data.lens_unit == 'MILLIMETERS': - props.header_text = "Camera Lens Angle: %.1fmm" + props.header_text = "Camera Focal Length: %.1fmm" else: - props.header_text = "Camera Lens Angle: %.1f\u00B0" + props.header_text = "Camera Focal Length: %.1f\u00B0" else: props = layout.operator("wm.context_modal_mouse", text="Camera Lens Scale") @@ -2440,88 +2414,83 @@ class VIEW3D_MT_object_context_menu(Menu): if view and view.camera == obj and view.region_3d.view_perspective == 'CAMERA': props = layout.operator("ui.eyedropper_depth", text="DOF Distance (Pick)") else: - props = layout.operator("wm.context_modal_mouse", text="DOF Distance") + props = layout.operator("wm.context_modal_mouse", text="Adjust Focus Distance") props.data_path_iter = "selected_editable_objects" props.data_path_item = "data.dof.focus_distance" props.input_scale = 0.02 - props.header_text = "DOF Distance: %.3f" + props.header_text = "Focus Distance: %.3f" layout.separator() elif obj.type in {'CURVE', 'FONT'}: layout.operator_context = 'INVOKE_REGION_WIN' - props = layout.operator("wm.context_modal_mouse", text="Extrude Size") + props = layout.operator("wm.context_modal_mouse", text="Adjust Extrusion") props.data_path_iter = "selected_editable_objects" props.data_path_item = "data.extrude" props.input_scale = 0.01 - props.header_text = "Extrude Size: %.3f" + props.header_text = "Extrude: %.3f" - props = layout.operator("wm.context_modal_mouse", text="Width Size") + props = layout.operator("wm.context_modal_mouse", text="Adjust Offset") props.data_path_iter = "selected_editable_objects" props.data_path_item = "data.offset" props.input_scale = 0.01 - props.header_text = "Width Size: %.3f" - - layout.separator() - - layout.operator("object.convert", text="Convert to Mesh").target = 'MESH' - layout.operator("object.convert", text="Convert to Grease Pencil").target = 'GPENCIL' - layout.operator_menu_enum("object.origin_set", text="Set Origin", property="type") - - layout.separator() - - elif obj.type == 'GPENCIL': - layout.operator("gpencil.convert", text="Convert to Path").type = 'PATH' - layout.operator("gpencil.convert", text="Convert to Bezier Curve").type = 'CURVE' - layout.operator("gpencil.convert", text="Convert to Polygon Curve").type = 'POLY' - - layout.operator_menu_enum("object.origin_set", text="Set Origin", property="type") + props.header_text = "Offset: %.3f" layout.separator() elif obj.type == 'EMPTY': layout.operator_context = 'INVOKE_REGION_WIN' - props = layout.operator("wm.context_modal_mouse", text="Empty Draw Size") + props = layout.operator("wm.context_modal_mouse", text="Adjust Empty Display Size") props.data_path_iter = "selected_editable_objects" props.data_path_item = "empty_display_size" props.input_scale = 0.01 - props.header_text = "Empty Draw Size: %.3f" + props.header_text = "Empty Display Size: %.3f" layout.separator() + if obj.empty_display_type == 'IMAGE': + layout.operator("gpencil.trace_image") + + layout.separator() + elif obj.type == 'LIGHT': light = obj.data layout.operator_context = 'INVOKE_REGION_WIN' - props = layout.operator("wm.context_modal_mouse", text="Power") + props = layout.operator("wm.context_modal_mouse", text="Adjust Light Power") props.data_path_iter = "selected_editable_objects" props.data_path_item = "data.energy" props.input_scale = 1.0 props.header_text = "Light Power: %.3f" if light.type == 'AREA': - props = layout.operator("wm.context_modal_mouse", text="Size X") - props.data_path_iter = "selected_editable_objects" - props.data_path_item = "data.size" - props.header_text = "Light Size X: %.3f" - if light.shape in {'RECTANGLE', 'ELLIPSE'}: - props = layout.operator("wm.context_modal_mouse", text="Size Y") + props = layout.operator("wm.context_modal_mouse", text="Adjust Area Light X Size") + props.data_path_iter = "selected_editable_objects" + props.data_path_item = "data.size" + props.header_text = "Light Size X: %.3f" + + props = layout.operator("wm.context_modal_mouse", text="Adjust Area Light Y Size") props.data_path_iter = "selected_editable_objects" props.data_path_item = "data.size_y" props.header_text = "Light Size Y: %.3f" + else: + props = layout.operator("wm.context_modal_mouse", text="Adjust Area Light Size") + props.data_path_iter = "selected_editable_objects" + props.data_path_item = "data.size" + props.header_text = "Light Size: %.3f" elif light.type in {'SPOT', 'POINT'}: - props = layout.operator("wm.context_modal_mouse", text="Radius") + props = layout.operator("wm.context_modal_mouse", text="Adjust Light Radius") props.data_path_iter = "selected_editable_objects" props.data_path_item = "data.shadow_soft_size" props.header_text = "Light Radius: %.3f" elif light.type == 'SUN': - props = layout.operator("wm.context_modal_mouse", text="Angle") + props = layout.operator("wm.context_modal_mouse", text="Adjust Sun Light Angle") props.data_path_iter = "selected_editable_objects" props.data_path_item = "data.angle" props.header_text = "Light Angle: %.3f" @@ -2529,13 +2498,13 @@ class VIEW3D_MT_object_context_menu(Menu): if light.type == 'SPOT': layout.separator() - props = layout.operator("wm.context_modal_mouse", text="Spot Size") + props = layout.operator("wm.context_modal_mouse", text="Adjust Spot Light Size") props.data_path_iter = "selected_editable_objects" props.data_path_item = "data.spot_size" props.input_scale = 0.01 props.header_text = "Spot Size: %.2f" - props = layout.operator("wm.context_modal_mouse", text="Spot Blend") + props = layout.operator("wm.context_modal_mouse", text="Adjust Spot Light Blend") props.data_path_iter = "selected_editable_objects" props.data_path_item = "data.spot_blend" props.input_scale = -0.01 @@ -2543,6 +2512,35 @@ class VIEW3D_MT_object_context_menu(Menu): layout.separator() + # Shared among some object types. + if obj is not None: + if obj.type in {'MESH', 'CURVE', 'SURFACE'}: + layout.operator("object.shade_smooth", text="Shade Smooth") + layout.operator("object.shade_flat", text="Shade Flat") + + layout.separator() + + if obj.type in {'MESH', 'CURVE', 'SURFACE', 'ARMATURE', 'GPENCIL'}: + if selected_objects_len > 1: + layout.operator("object.join") + + if obj.type in {'MESH', 'CURVE', 'SURFACE', 'POINTCLOUD', 'META', 'FONT'}: + layout.operator_menu_enum("object.convert", "target") + + if obj.type == 'GPENCIL': + layout.operator_menu_enum("gpencil.convert", "type", text="Convert To") + + if ( + obj.type in {'MESH', 'CURVE', 'SURFACE', 'GPENCIL', 'LATTICE', 'ARMATURE', 'META'} or + (obj.type == 'EMPTY' and obj.instance_collection is not None) + ): + layout.operator_context = 'INVOKE_REGION_WIN' + layout.operator_menu_enum("object.origin_set", text="Set Origin", property="type") + layout.operator_context = 'INVOKE_DEFAULT' + + layout.separator() + + # Shared among all object types layout.operator("view3d.copybuffer", text="Copy Objects", icon='COPYDOWN') layout.operator("view3d.pastebuffer", text="Paste Objects", icon='PASTEDOWN') @@ -2787,8 +2785,25 @@ class VIEW3D_MT_make_single_user(Menu): props.object = props.obdata = props.material = False +class VIEW3D_MT_object_convert(Menu): + bl_label = "Convert" + + def draw(self, context): + layout = self.layout + ob = context.active_object + + if ob and ob.type == 'GPENCIL' and context.gpencil_data: + layout.operator_enum("gpencil.convert", "type") + else: + layout.operator_enum("object.convert", "target") + + # Potrace lib dependency. + if bpy.app.build_options.potrace: + layout.operator("gpencil.trace_image", icon='OUTLINER_OB_GREASEPENCIL') + + class VIEW3D_MT_make_links(Menu): - bl_label = "Make Links" + bl_label = "Link/Transfer Data" def draw(self, _context): layout = self.layout @@ -2796,10 +2811,10 @@ class VIEW3D_MT_make_links(Menu): if len(bpy.data.scenes) > 10: layout.operator_context = 'INVOKE_REGION_WIN' - layout.operator("object.make_links_scene", text="Objects to Scene...", icon='OUTLINER_OB_EMPTY') + layout.operator("object.make_links_scene", text="Link Objects to Scene...", icon='OUTLINER_OB_EMPTY') else: layout.operator_context = 'EXEC_REGION_WIN' - layout.operator_menu_enum("object.make_links_scene", "scene", text="Objects to Scene") + layout.operator_menu_enum("object.make_links_scene", "scene", text="Link Objects to Scene") layout.separator() @@ -2807,7 +2822,12 @@ class VIEW3D_MT_make_links(Menu): layout.operator_enum("object.make_links_data", "type") # inline - layout.operator("object.join_uvs") # stupid place to add this! + layout.operator("object.join_uvs", text="Copy UV Maps") + + layout.separator() + + layout.operator("object.data_transfer") + layout.operator("object.datalayout_transfer") class VIEW3D_MT_brush_paint_modes(Menu): @@ -7526,6 +7546,7 @@ classes = ( VIEW3D_MT_object_rigid_body, VIEW3D_MT_object_clear, VIEW3D_MT_object_context_menu, + VIEW3D_MT_object_convert, VIEW3D_MT_object_shading, VIEW3D_MT_object_apply, VIEW3D_MT_object_relations, diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 577f9678a62..7c718ee8155 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -352,10 +352,13 @@ class VIEW3D_PT_tools_particlemode(Panel, View3DPaintPanel): layout.row().prop(brush, "puff_mode", expand=True) layout.prop(brush, "use_puff_volume") elif tool == 'COMB': - layout.prop(settings, "use_emitter_deflect", text="Deflect Emitter") - col = layout.column() - col.active = settings.use_emitter_deflect - col.prop(settings, "emitter_distance", text="Distance") + col = layout.column(align=False, heading="Deflect Emitter") + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(settings, "use_emitter_deflect", text="") + sub = sub.row(align=True) + sub.active = settings.use_emitter_deflect + sub.prop(settings, "emitter_distance", text="") # TODO, move to space_view3d.py @@ -1231,14 +1234,20 @@ class VIEW3D_PT_tools_particlemode_options(View3DPanel, Panel): col = layout.column(align=True) col.active = pe.is_editable - col.prop(ob.data, "use_mirror_x") - if pe.tool == 'ADD': - col.prop(ob.data, "use_mirror_topology") - col.separator() - col.prop(pe, "use_preserve_length", text="Preserve Strand Lengths") - col.prop(pe, "use_preserve_root", text="Preserve Root Positions") + if not pe.is_hair: col.prop(pe, "use_auto_velocity", text="Auto-Velocity") + col.separator() + + sub = col.column(align=True, heading="Mirror") + sub.prop(ob.data, "use_mirror_x") + if pe.tool == 'ADD': + sub.prop(ob.data, "use_mirror_topology") + col.separator() + + sub = col.column(align=True, heading="Preserve") + sub.prop(pe, "use_preserve_length", text="Strand Lengths") + sub.prop(pe, "use_preserve_root", text="Root Positions") class VIEW3D_PT_tools_particlemode_options_shapecut(View3DPanel, Panel): @@ -1282,10 +1291,13 @@ class VIEW3D_PT_tools_particlemode_options_display(View3DPanel, Panel): else: if pe.type == 'PARTICLES': col.prop(pe, "show_particles", text="Particles") - col.prop(pe, "use_fade_time") - sub = col.row(align=True) + col = layout.column(align=False, heading="Fade Time") + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(pe, "use_fade_time", text="") + sub = sub.row(align=True) sub.active = pe.use_fade_time - sub.prop(pe, "fade_frames", slider=True) + sub.prop(pe, "fade_frames", slider=True, text="") # ********** grease pencil object tool panels **************** @@ -1435,7 +1447,7 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): row.prop(gp_settings, "fill_layer_mode", text="Layers") col.separator() - col.prop(gp_settings, "fill_factor", text="Resolution") + col.prop(gp_settings, "fill_factor") if gp_settings.fill_draw_mode != 'STROKE': col = layout.column(align=False, heading="Ignore Transparent") col.use_property_decorate = False diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 7abc62a69e3..56f0b5c0ba4 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -489,6 +489,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeAttributeMix"), NodeItem("GeometryNodeAttributeColorRamp"), NodeItem("GeometryNodeAttributeVectorMath"), + NodeItem("GeometryNodeAttributeSampleTexture"), ]), GeometryNodeCategory("GEO_COLOR", "Color", items=[ NodeItem("ShaderNodeValToRGB"), @@ -520,6 +521,9 @@ geometry_node_categories = [ NodeItem("GeometryNodeRotatePoints"), NodeItem("GeometryNodeAlignRotationToVector"), ]), + GeometryNodeCategory("GEO_VOLUME", "Volume", items=[ + NodeItem("GeometryNodePointsToVolume"), + ]), GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[ NodeItem("ShaderNodeMapRange"), NodeItem("ShaderNodeClamp"), diff --git a/release/windows/batch/blender_debug_gpu.cmd b/release/windows/batch/blender_debug_gpu.cmd index 46d126ab621..53d7863ec70 100644 --- a/release/windows/batch/blender_debug_gpu.cmd +++ b/release/windows/batch/blender_debug_gpu.cmd @@ -12,5 +12,5 @@ mkdir "%temp%\blender\debug_logs" > NUL 2>&1 echo. echo Starting blender and waiting for it to exit.... set PYTHONPATH= -"%~dp0\blender" --debug --debug-gpu --python-expr "import bpy; bpy.ops.wm.sysinfo(filepath=r'%temp%\blender\debug_logs\blender_system_info.txt')" > "%temp%\blender\debug_logs\blender_debug_output.txt" 2>&1 < %0 +"%~dp0\blender" --debug --debug-gpu --debug-cycles --python-expr "import bpy; bpy.ops.wm.sysinfo(filepath=r'%temp%\blender\debug_logs\blender_system_info.txt')" > "%temp%\blender\debug_logs\blender_debug_output.txt" 2>&1 < %0 explorer "%temp%\blender\debug_logs"
\ No newline at end of file diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h index 8d904bd6019..2fce4bfc5b8 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -212,8 +212,7 @@ struct NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context( struct ListBase *cache, struct PointerRNA *ptr, struct AnimData *adt, - const struct AnimationEvalContext *anim_eval_context, - const bool flush_to_original); + const struct AnimationEvalContext *anim_eval_context); bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context, struct PointerRNA *prop_ptr, struct PropertyRNA *prop, diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index abcf8ed1c54..bca96aac665 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -24,6 +24,7 @@ #include "BKE_attribute.h" #include "BLI_color.hh" +#include "BLI_float2.hh" #include "BLI_float3.hh" namespace blender::bke { @@ -301,11 +302,13 @@ template<typename T> class TypedWriteAttribute { using BooleanReadAttribute = TypedReadAttribute<bool>; using FloatReadAttribute = TypedReadAttribute<float>; +using Float2ReadAttribute = TypedReadAttribute<float2>; using Float3ReadAttribute = TypedReadAttribute<float3>; using Int32ReadAttribute = TypedReadAttribute<int>; using Color4fReadAttribute = TypedReadAttribute<Color4f>; using BooleanWriteAttribute = TypedWriteAttribute<bool>; using FloatWriteAttribute = TypedWriteAttribute<float>; +using Float2WriteAttribute = TypedWriteAttribute<float2>; using Float3WriteAttribute = TypedWriteAttribute<float3>; using Int32WriteAttribute = TypedWriteAttribute<int>; using Color4fWriteAttribute = TypedWriteAttribute<Color4f>; diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh new file mode 100644 index 00000000000..b8bb2048d9d --- /dev/null +++ b/source/blender/blenkernel/BKE_attribute_math.hh @@ -0,0 +1,94 @@ +/* + * 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. + */ + +#include "BLI_color.hh" +#include "BLI_float2.hh" +#include "BLI_float3.hh" +#include "DNA_customdata_types.h" + +namespace blender::attribute_math { + +/** + * Utility function that simplifies calling a templated function based on a custom data type. + */ +template<typename Func> +void convert_to_static_type(const CustomDataType data_type, const Func &func) +{ + switch (data_type) { + case CD_PROP_FLOAT: + func(float()); + break; + case CD_PROP_FLOAT2: + func(float2()); + break; + case CD_PROP_FLOAT3: + func(float3()); + break; + case CD_PROP_INT32: + func(int()); + break; + case CD_PROP_BOOL: + func(bool()); + break; + case CD_PROP_COLOR: + func(Color4f()); + break; + default: + BLI_assert(false); + break; + } +} + +/* Interpolate between three values. */ +template<typename T> T mix3(const float3 &weights, const T &v0, const T &v1, const T &v2); + +template<> inline bool mix3(const float3 &weights, const bool &v0, const bool &v1, const bool &v2) +{ + return (weights.x * v0 + weights.y * v1 + weights.z * v2) >= 0.5f; +} + +template<> inline int mix3(const float3 &weights, const int &v0, const int &v1, const int &v2) +{ + return static_cast<int>(weights.x * v0 + weights.y * v1 + weights.z * v2); +} + +template<> +inline float mix3(const float3 &weights, const float &v0, const float &v1, const float &v2) +{ + return weights.x * v0 + weights.y * v1 + weights.z * v2; +} + +template<> +inline float2 mix3(const float3 &weights, const float2 &v0, const float2 &v1, const float2 &v2) +{ + return weights.x * v0 + weights.y * v1 + weights.z * v2; +} + +template<> +inline float3 mix3(const float3 &weights, const float3 &v0, const float3 &v1, const float3 &v2) +{ + return weights.x * v0 + weights.y * v1 + weights.z * v2; +} + +template<> +inline Color4f mix3(const float3 &weights, const Color4f &v0, const Color4f &v1, const Color4f &v2) +{ + Color4f result; + interp_v4_v4v4v4(result, v0, v1, v2, weights); + return result; +} + +} // namespace blender::attribute_math diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 4ee96d1ab8d..6b76e69a7a6 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -31,15 +31,15 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 292 +#define BLENDER_VERSION 293 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ -#define BLENDER_VERSION_CYCLE beta +#define BLENDER_VERSION_CYCLE alpha /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 14 +#define BLENDER_FILE_SUBVERSION 5 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 3b093ae730a..51f7507bd6c 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -37,6 +37,7 @@ struct Collection; struct Mesh; struct Object; struct PointCloud; +struct Volume; /* Each geometry component has a specific type. The type determines what kind of data the component * stores. Functions modifying a geometry will usually just modify a subset of the component types. @@ -45,6 +46,7 @@ enum class GeometryComponentType { Mesh = 0, PointCloud = 1, Instances = 2, + Volume = 3, }; enum class GeometryOwnershipType { @@ -320,10 +322,13 @@ struct GeometrySet { bool has_mesh() const; bool has_pointcloud() const; bool has_instances() const; + bool has_volume() const; const Mesh *get_mesh_for_read() const; const PointCloud *get_pointcloud_for_read() const; + const Volume *get_volume_for_read() const; Mesh *get_mesh_for_write(); PointCloud *get_pointcloud_for_write(); + Volume *get_volume_for_write(); /* Utility methods for replacement. */ void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); @@ -447,3 +452,25 @@ class InstancesComponent : public GeometryComponent { static constexpr inline GeometryComponentType static_type = GeometryComponentType::Instances; }; + +/** A geometry component that stores volume grids. */ +class VolumeComponent : public GeometryComponent { + private: + Volume *volume_ = nullptr; + GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned; + + public: + VolumeComponent(); + ~VolumeComponent(); + GeometryComponent *copy() const override; + + void clear(); + bool has_volume() const; + void replace(Volume *volume, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + Volume *release(); + + const Volume *get_for_read() const; + Volume *get_for_write(); + + static constexpr inline GeometryComponentType static_type = GeometryComponentType::Volume; +}; diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index df5711f5120..b7fad9c891a 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -109,8 +109,11 @@ struct bGPDframe *BKE_gpencil_frame_addcopy(struct bGPDlayer *gpl, int cframe); struct bGPDlayer *BKE_gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive); struct bGPdata *BKE_gpencil_data_addnew(struct Main *bmain, const char name[]); -struct bGPDframe *BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src); -struct bGPDlayer *BKE_gpencil_layer_duplicate(const struct bGPDlayer *gpl_src); +struct bGPDframe *BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src, + const bool dup_strokes); +struct bGPDlayer *BKE_gpencil_layer_duplicate(const struct bGPDlayer *gpl_src, + const bool dup_frames, + const bool dup_strokes); void BKE_gpencil_frame_copy_strokes(struct bGPDframe *gpf_src, struct bGPDframe *gpf_dst); struct bGPDcurve *BKE_gpencil_stroke_curve_duplicate(struct bGPDcurve *gpc_src); struct bGPDstroke *BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src, @@ -280,12 +283,12 @@ void BKE_gpencil_frame_original_pointers_update(const struct bGPDframe *gpf_orig const struct bGPDframe *gpf_eval); void BKE_gpencil_update_orig_pointers(const struct Object *ob_orig, const struct Object *ob_eval); -void BKE_gpencil_parent_matrix_get(const struct Depsgraph *depsgraph, - struct Object *obact, - struct bGPDlayer *gpl, - float diff_mat[4][4]); +void BKE_gpencil_layer_transform_matrix_get(const struct Depsgraph *depsgraph, + struct Object *obact, + struct bGPDlayer *gpl, + float diff_mat[4][4]); -void BKE_gpencil_update_layer_parent(const struct Depsgraph *depsgraph, struct Object *ob); +void BKE_gpencil_update_layer_transforms(const struct Depsgraph *depsgraph, struct Object *ob); int BKE_gpencil_material_find_index_by_name_prefix(struct Object *ob, const char *name_prefix); diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index 1298e3c2bbf..edfc96f3059 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -106,6 +106,8 @@ typedef void (*IDTypeBlendReadUndoPreserve)(struct BlendLibReader *reader, struct ID *id_new, struct ID *id_old); +typedef void (*IDTypeLibOverrideApplyPost)(struct ID *id_dst, struct ID *id_src); + typedef struct IDTypeInfo { /* ********** General IDType data. ********** */ @@ -207,6 +209,13 @@ typedef struct IDTypeInfo { * \note Called from #setup_app_data when undoing or redoing a memfile step. */ IDTypeBlendReadUndoPreserve blend_read_undo_preserve; + + /** + * Called after library override operations have been applied. + * + * \note Currently needed for some update operation on point caches. + */ + IDTypeLibOverrideApplyPost lib_override_apply_post; } IDTypeInfo; /* ********** Declaration of each IDTypeInfo. ********** */ diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index c9a9c26e222..5fd451dc986 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -65,15 +65,6 @@ struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain, struct ID *reference_id, const bool do_tagged_remap); bool BKE_lib_override_library_create_from_tag(struct Main *bmain); -void BKE_lib_override_library_dependencies_tag(struct Main *bmain, - struct ID *id_root, - const uint tag, - const bool do_create_main_relashionships); -void BKE_lib_override_library_override_group_tag(struct Main *bmain, - struct ID *id_root, - const uint tag, - const uint missing_tag, - const bool do_create_main_relashionships); bool BKE_lib_override_library_create(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index 8106607572b..0768423fc5f 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -60,22 +60,52 @@ typedef struct BlendThumbnail { } BlendThumbnail; /* Structs caching relations between data-blocks in a given Main. */ +typedef struct MainIDRelationsEntryItem { + struct MainIDRelationsEntryItem *next; + + union { + /* For `from_ids` list, a user of the hashed ID. */ + struct ID *from; + /* For `to_ids` list, an ID used by the hashed ID. */ + struct ID **to; + } id_pointer; + /* Session uuid of the `id_pointer`. */ + uint session_uuid; + + int usage_flag; /* Using IDWALK_ enums, defined in BKE_lib_query.h */ +} MainIDRelationsEntryItem; + typedef struct MainIDRelationsEntry { - struct MainIDRelationsEntry *next; - /* WARNING! for user_to_used, - * that pointer is really an ID** one, but for used_to_user, it’s only an ID* one! */ - struct ID **id_pointer; - int usage_flag; /* Using IDWALK_ enums, in BKE_lib_query.h */ + /* Linked list of IDs using that ID. */ + struct MainIDRelationsEntryItem *from_ids; + /* Linked list of IDs used by that ID. */ + struct MainIDRelationsEntryItem *to_ids; + + /* Session uuid of the ID matching that entry. */ + uint session_uuid; + + /* Runtime tags, users should ensure those are reset after usage. */ + uint tags; } MainIDRelationsEntry; +/* MainIDRelationsEntry.tags */ +typedef enum MainIDRelationsEntryTags { + /* Generic tag marking the entry as to be processed. */ + MAINIDRELATIONS_ENTRY_TAGS_DOIT = 1 << 0, + /* Generic tag marking the entry as processed. */ + MAINIDRELATIONS_ENTRY_TAGS_PROCESSED = 1 << 1, +} MainIDRelationsEntryTags; + typedef struct MainIDRelations { - struct GHash *id_user_to_used; - struct GHash *id_used_to_user; + /* Mapping from an ID pointer to all of its parents (IDs using it) and children (IDs it uses). + * Values are `MainIDRelationsEntry` pointers. */ + struct GHash *relations_from_pointers; + /* Note: we could add more mappings when needed (e.g. from session uuid?). */ short flag; /* Private... */ - struct BLI_mempool *entry_pool; + struct BLI_mempool *entry_items_pool; } MainIDRelations; enum { @@ -172,7 +202,6 @@ void BKE_main_unlock(struct Main *bmain); void BKE_main_relations_create(struct Main *bmain, const short flag); void BKE_main_relations_free(struct Main *bmain); -void BKE_main_relations_ID_remove(struct Main *bmain, struct ID *id); struct GSet *BKE_main_gset_create(struct Main *bmain, struct GSet *gset); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 79bc00766fd..4912d634471 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -463,8 +463,8 @@ void ntreeSetOutput(struct bNodeTree *ntree); void ntreeFreeCache(struct bNodeTree *ntree); -bool ntreeNodeExists(struct bNodeTree *ntree, struct bNode *testnode); -bool ntreeOutputExists(struct bNode *node, struct bNodeSocket *testsock); +bool ntreeNodeExists(const struct bNodeTree *ntree, const struct bNode *testnode); +bool ntreeOutputExists(const struct bNode *node, const struct bNodeSocket *testsock); void ntreeNodeFlagSet(const bNodeTree *ntree, const int flag, const bool enable); struct bNodeTree *ntreeLocalize(struct bNodeTree *ntree); void ntreeLocalSync(struct bNodeTree *localtree, struct bNodeTree *ntree); @@ -501,7 +501,7 @@ struct bNodeSocket *ntreeInsertSocketInterfaceFromSocket(struct bNodeTree *ntree struct bNodeSocket *from_sock); void ntreeRemoveSocketInterface(struct bNodeTree *ntree, struct bNodeSocket *sock); -struct StructRNA *ntreeInterfaceTypeGet(struct bNodeTree *ntree, int create); +struct StructRNA *ntreeInterfaceTypeGet(struct bNodeTree *ntree, bool create); void ntreeInterfaceTypeFree(struct bNodeTree *ntree); void ntreeInterfaceTypeUpdate(struct bNodeTree *ntree); @@ -624,12 +624,12 @@ struct bNodeLink *nodeAddLink(struct bNodeTree *ntree, struct bNodeSocket *tosock); void nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link); void nodeRemSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock); -bool nodeLinkIsHidden(struct bNodeLink *link); +bool nodeLinkIsHidden(const struct bNodeLink *link); void nodeInternalRelink(struct bNodeTree *ntree, struct bNode *node); -void nodeToView(struct bNode *node, float x, float y, float *rx, float *ry); -void nodeFromView(struct bNode *node, float x, float y, float *rx, float *ry); -bool nodeAttachNodeCheck(struct bNode *node, struct bNode *parent); +void nodeToView(const struct bNode *node, float x, float y, float *rx, float *ry); +void nodeFromView(const struct bNode *node, float x, float y, float *rx, float *ry); +bool nodeAttachNodeCheck(const struct bNode *node, const struct bNode *parent); void nodeAttachNode(struct bNode *node, struct bNode *parent); void nodeDetachNode(struct bNode *node); @@ -661,9 +661,9 @@ void nodeChainIterBackwards(const bNodeTree *ntree, void nodeParentsIter(bNode *node, bool (*callback)(bNode *, void *), void *userdata); struct bNodeLink *nodeFindLink(struct bNodeTree *ntree, - struct bNodeSocket *from, - struct bNodeSocket *to); -int nodeCountSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock); + const struct bNodeSocket *from, + const struct bNodeSocket *to); +int nodeCountSocketLinks(const struct bNodeTree *ntree, const struct bNodeSocket *sock); void nodeSetSelected(struct bNode *node, bool select); void nodeSetActive(struct bNodeTree *ntree, struct bNode *node); @@ -678,14 +678,14 @@ void nodeUpdate(struct bNodeTree *ntree, struct bNode *node); bool nodeUpdateID(struct bNodeTree *ntree, struct ID *id); void nodeUpdateInternalLinks(struct bNodeTree *ntree, struct bNode *node); -int nodeSocketIsHidden(struct bNodeSocket *sock); +int nodeSocketIsHidden(const struct bNodeSocket *sock); void ntreeTagUsedSockets(struct bNodeTree *ntree); void nodeSetSocketAvailability(struct bNodeSocket *sock, bool is_available); -int nodeSocketLinkLimit(struct bNodeSocket *sock); +int nodeSocketLinkLimit(const struct bNodeSocket *sock); /* Node Clipboard */ -void BKE_node_clipboard_init(struct bNodeTree *ntree); +void BKE_node_clipboard_init(const struct bNodeTree *ntree); void BKE_node_clipboard_clear(void); void BKE_node_clipboard_free(void); bool BKE_node_clipboard_validate(void); @@ -706,8 +706,8 @@ extern const bNodeInstanceKey NODE_INSTANCE_KEY_BASE; extern const bNodeInstanceKey NODE_INSTANCE_KEY_NONE; bNodeInstanceKey BKE_node_instance_key(bNodeInstanceKey parent_key, - struct bNodeTree *ntree, - struct bNode *node); + const struct bNodeTree *ntree, + const struct bNode *node); bNodeInstanceHash *BKE_node_instance_hash_new(const char *info); void BKE_node_instance_hash_free(bNodeInstanceHash *hash, bNodeInstanceValueFP valfreefp); @@ -767,7 +767,7 @@ BLI_INLINE bool BKE_node_instance_hash_iterator_done(bNodeInstanceHashIterator * /* Node Previews */ -int BKE_node_preview_used(struct bNode *node); +bool BKE_node_preview_used(const struct bNode *node); bNodePreview *BKE_node_preview_verify( struct bNodeInstanceHash *previews, bNodeInstanceKey key, int xsize, int ysize, bool create); bNodePreview *BKE_node_preview_copy(struct bNodePreview *preview); @@ -1361,6 +1361,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_ALIGN_ROTATION_TO_VECTOR 1018 #define GEO_NODE_POINT_TRANSLATE 1019 #define GEO_NODE_POINT_SCALE 1020 +#define GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE 1021 +#define GEO_NODE_POINTS_TO_VOLUME 1022 /** \} */ diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 7b5df98d148..e9a141709b5 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -67,6 +67,13 @@ struct wmWindowManager; #define BKE_ST_MAXNAME 64 +typedef struct wmSpaceTypeListenerParams { + struct wmWindow *window; + struct ScrArea *area; + struct wmNotifier *notifier; + const struct Scene *scene; +} wmSpaceTypeListenerParams; + typedef struct SpaceType { struct SpaceType *next, *prev; @@ -85,10 +92,7 @@ typedef struct SpaceType { /* exit is called when the area is hidden or removed */ void (*exit)(struct wmWindowManager *wm, struct ScrArea *area); /* Listeners can react to bContext changes */ - void (*listener)(struct wmWindow *win, - struct ScrArea *area, - struct wmNotifier *wmn, - struct Scene *scene); + void (*listener)(const wmSpaceTypeListenerParams *params); /* called when the mouse moves out of the area */ void (*deactivate)(struct ScrArea *area); @@ -134,6 +138,23 @@ typedef struct SpaceType { /* region types are also defined using spacetypes_init, via a callback */ +typedef struct wmRegionListenerParams { + struct ScrArea *area; /* Can be NULL when the region is not part of an area. */ + struct ARegion *region; + struct wmNotifier *notifier; + const struct Scene *scene; +} wmRegionListenerParams; + +typedef struct wmRegionMessageSubscribeParams { + const struct bContext *context; + struct wmMsgBus *message_bus; + struct WorkSpace *workspace; + struct Scene *scene; + struct bScreen *screen; + struct ScrArea *area; + struct ARegion *region; +} wmRegionMessageSubscribeParams; + typedef struct ARegionType { struct ARegionType *next, *prev; @@ -158,19 +179,9 @@ typedef struct ARegionType { /* snap the size of the region (can be NULL for no snapping). */ int (*snap_size)(const struct ARegion *region, int size, int axis); /* contextual changes should be handled here */ - void (*listener)(struct wmWindow *win, - struct ScrArea *area, - struct ARegion *region, - struct wmNotifier *wmn, - const struct Scene *scene); + void (*listener)(const wmRegionListenerParams *params); /* Optional callback to generate subscriptions. */ - void (*message_subscribe)(const struct bContext *C, - struct WorkSpace *workspace, - struct Scene *scene, - struct bScreen *screen, - struct ScrArea *area, - struct ARegion *region, - struct wmMsgBus *mbus); + void (*message_subscribe)(const wmRegionMessageSubscribeParams *params); void (*free)(struct ARegion *); diff --git a/source/blender/blenkernel/BKE_tracking.h b/source/blender/blenkernel/BKE_tracking.h index 4dbc22ae18f..c2544c06514 100644 --- a/source/blender/blenkernel/BKE_tracking.h +++ b/source/blender/blenkernel/BKE_tracking.h @@ -89,6 +89,23 @@ struct MovieTrackingTrack *BKE_tracking_track_duplicate(struct MovieTrackingTrac void BKE_tracking_track_unique_name(struct ListBase *tracksbase, struct MovieTrackingTrack *track); void BKE_tracking_track_free(struct MovieTrackingTrack *track); +void BKE_tracking_track_first_last_frame_get(const struct MovieTrackingTrack *track, + int *r_first_frame, + int *r_last_frame); + +void BKE_tracking_tracks_first_last_frame_minmax(/*const*/ struct MovieTrackingTrack **tracks, + const int num_tracks, + int *r_first_frame, + int *r_last_frame); + +int BKE_tracking_count_selected_tracks_in_list(const struct ListBase *tracks_list); +int BKE_tracking_count_selected_tracks_in_active_object(/*const*/ struct MovieTracking *tracking); + +/* Get array of selected tracks from the current active object in the tracking structure. + * If nothing is selected then the result is nullptr and `r_num_tracks` is set to 0. */ +struct MovieTrackingTrack **BKE_tracking_selected_tracks_in_active_object( + struct MovieTracking *tracking, int *r_num_tracks); + void BKE_tracking_track_flag_set(struct MovieTrackingTrack *track, int area, int flag); void BKE_tracking_track_flag_clear(struct MovieTrackingTrack *track, int area, int flag); @@ -96,10 +113,15 @@ bool BKE_tracking_track_has_marker_at_frame(struct MovieTrackingTrack *track, in bool BKE_tracking_track_has_enabled_marker_at_frame(struct MovieTrackingTrack *track, int framenr); void BKE_tracking_track_path_clear(struct MovieTrackingTrack *track, int ref_frame, int action); + void BKE_tracking_tracks_join(struct MovieTracking *tracking, struct MovieTrackingTrack *dst_track, struct MovieTrackingTrack *src_track); +void BKE_tracking_tracks_average(struct MovieTrackingTrack *dst_track, + /*const*/ struct MovieTrackingTrack **src_tracks, + const int num_src_tracks); + struct MovieTrackingTrack *BKE_tracking_track_get_named(struct MovieTracking *tracking, struct MovieTrackingObject *object, const char *name); @@ -139,6 +161,17 @@ struct MovieTrackingMarker *BKE_tracking_marker_get_exact(struct MovieTrackingTr struct MovieTrackingMarker *BKE_tracking_marker_ensure(struct MovieTrackingTrack *track, int framenr); +/* Get marker position, possibly interpolating interpolating gap between keyframed/tracked markers. + * + * The result marker frame number is set to the requested frame number. Its flags are 0 if the + * marker is interpolated, and is set to original marker flag if there were no interpolation + * involved. + * + * Returns truth if the result is usable. */ +bool BKE_tracking_marker_get_interpolated(struct MovieTrackingTrack *track, + const int framenr, + struct MovieTrackingMarker *r_marker); + void BKE_tracking_marker_pattern_minmax(const struct MovieTrackingMarker *marker, float min[2], float max[2]); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 9aa2baef53c..20c57f4e15a 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -275,6 +275,7 @@ set(SRC BKE_asset.h BKE_attribute.h BKE_attribute_access.hh + BKE_attribute_math.hh BKE_autoexec.h BKE_blender.h BKE_blender_copybuffer.h diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index ccb077d6b82..db8fe75b6d1 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -298,6 +298,8 @@ IDTypeInfo IDType_ID_AC = { .blend_read_expand = action_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; /* ***************** Library data level operations on action ************** */ diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 20956d6eb18..da874539232 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -362,6 +362,20 @@ void BKE_keyingsets_blend_read_expand(BlendExpander *expander, ListBase *list) /* ***************************************** */ /* Evaluation Data-Setting Backend */ +static bool is_fcurve_evaluatable(FCurve *fcu) +{ + if (fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED)) { + return false; + } + if (fcu->grp != NULL && (fcu->grp->flag & AGRP_MUTED)) { + return false; + } + if (BKE_fcurve_is_empty(fcu)) { + return false; + } + return true; +} + bool BKE_animsys_store_rna_setting(PointerRNA *ptr, /* typically 'fcu->rna_path', 'fcu->array_index' */ const char *rna_path, @@ -594,18 +608,11 @@ static void animsys_evaluate_fcurves(PointerRNA *ptr, { /* Calculate then execute each curve. */ LISTBASE_FOREACH (FCurve *, fcu, list) { - /* Check if this F-Curve doesn't belong to a muted group. */ - if ((fcu->grp != NULL) && (fcu->grp->flag & AGRP_MUTED)) { - continue; - } - /* Check if this curve should be skipped. */ - if ((fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED))) { - continue; - } - /* Skip empty curves, as if muted. */ - if (BKE_fcurve_is_empty(fcu)) { + + if (!is_fcurve_evaluatable(fcu)) { continue; } + PathResolvedRNA anim_rna; if (BKE_animsys_store_rna_setting(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { const float curval = calculate_fcurve(&anim_rna, fcu, anim_eval_context); @@ -979,6 +986,19 @@ NlaEvalStrip *nlastrips_ctime_get_strip(ListBase *list, return nes; } +static NlaEvalStrip *nlastrips_ctime_get_strip_single( + ListBase *dst_list, + NlaStrip *single_strip, + const AnimationEvalContext *anim_eval_context, + const bool flush_to_original) +{ + ListBase single_tracks_list; + single_tracks_list.first = single_tracks_list.last = single_strip; + + return nlastrips_ctime_get_strip( + dst_list, &single_tracks_list, -1, anim_eval_context, flush_to_original); +} + /* ---------------------- */ /* Initialize a valid mask, allocating memory if necessary. */ @@ -1152,7 +1172,7 @@ static void nlaeval_snapshot_free_data(NlaEvalSnapshot *snapshot) /* Free memory owned by this evaluation channel. */ static void nlaevalchan_free_data(NlaEvalChannel *nec) { - nlavalidmask_free(&nec->valid); + nlavalidmask_free(&nec->domain); if (nec->blend_snapshot != NULL) { nlaevalchan_snapshot_free(nec->blend_snapshot); @@ -1353,7 +1373,7 @@ static NlaEvalChannel *nlaevalchan_verify_key(NlaEvalData *nlaeval, nec->mix_mode = nlaevalchan_detect_mix_mode(key, length); - nlavalidmask_init(&nec->valid, length); + nlavalidmask_init(&nec->domain, length); nec->base_snapshot.channel = nec; nec->base_snapshot.length = length; @@ -1682,14 +1702,6 @@ static bool nlaeval_blend_value(NlaBlendData *blend, return false; } - if (nec->mix_mode == NEC_MIX_QUATERNION) { - /* For quaternion properties, always output all sub-channels. */ - BLI_bitmap_set_all(nec->valid.ptr, true, 4); - } - else { - BLI_BITMAP_ENABLE(nec->valid.ptr, array_index); - } - NlaEvalChannelSnapshot *nec_snapshot = nlaeval_snapshot_ensure_channel(blend->snapshot, nec); float *p_value = &nec_snapshot->values[array_index]; @@ -1889,16 +1901,8 @@ static void nlastrip_evaluate_actionclip(PointerRNA *ptr, /* Evaluate all the F-Curves in the action, * saving the relevant pointers to data that will need to be used. */ for (fcu = strip->act->curves.first; fcu; fcu = fcu->next) { - float value = 0.0f; - /* check if this curve should be skipped */ - if (fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED)) { - continue; - } - if ((fcu->grp) && (fcu->grp->flag & AGRP_MUTED)) { - continue; - } - if (BKE_fcurve_is_empty(fcu)) { + if (!is_fcurve_evaluatable(fcu)) { continue; } @@ -1906,7 +1910,7 @@ static void nlastrip_evaluate_actionclip(PointerRNA *ptr, * NOTE: we use the modified time here, since strip's F-Curve Modifiers * are applied on top of this. */ - value = evaluate_fcurve(fcu, evaltime); + float value = evaluate_fcurve(fcu, evaltime); /* apply strip's F-Curve Modifiers on this value * NOTE: we apply the strip's original evaluation time not the modified one @@ -2099,12 +2103,21 @@ void nladata_flush_channels(PointerRNA *ptr, /* for each channel with accumulated values, write its value on the property it affects */ LISTBASE_FOREACH (NlaEvalChannel *, nec, &channels->channels) { + /** + * The bitmask is set for all channels touched by NLA due to the domain() function. + * Channels touched by current set of evaluated strips will have a snapshot channel directly + * from the evaluation snapshot. + * + * This function falls back to the default value if the snapshot channel doesn't exist. + * Thus channels, touched by NLA but not by the current set of evaluated strips, will be + * reset to default. If channel not touched by NLA then it's value is unchanged. + */ NlaEvalChannelSnapshot *nec_snapshot = nlaeval_snapshot_find_channel(snapshot, nec); PathResolvedRNA rna = {nec->key.ptr, nec->key.prop, -1}; for (int i = 0; i < nec_snapshot->length; i++) { - if (BLI_BITMAP_TEST(nec->valid.ptr, i)) { + if (BLI_BITMAP_TEST(nec->domain.ptr, i)) { float value = nec_snapshot->values[i]; if (nec->is_array) { rna.prop_index = i; @@ -2131,13 +2144,7 @@ static void nla_eval_domain_action(PointerRNA *ptr, LISTBASE_FOREACH (FCurve *, fcu, &act->curves) { /* check if this curve should be skipped */ - if (fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED)) { - continue; - } - if ((fcu->grp) && (fcu->grp->flag & AGRP_MUTED)) { - continue; - } - if (BKE_fcurve_is_empty(fcu)) { + if (!is_fcurve_evaluatable(fcu)) { continue; } @@ -2146,14 +2153,14 @@ static void nla_eval_domain_action(PointerRNA *ptr, if (nec != NULL) { /* For quaternion properties, enable all sub-channels. */ if (nec->mix_mode == NEC_MIX_QUATERNION) { - BLI_bitmap_set_all(nec->valid.ptr, true, 4); + BLI_bitmap_set_all(nec->domain.ptr, true, 4); continue; } int idx = nlaevalchan_validate_index(nec, fcu->array_index); if (idx >= 0) { - BLI_BITMAP_ENABLE(nec->valid.ptr, idx); + BLI_BITMAP_ENABLE(nec->domain.ptr, idx); } } } @@ -2212,190 +2219,345 @@ static void animsys_evaluate_nla_domain(PointerRNA *ptr, NlaEvalData *channels, /* ---------------------- */ +/** Tweaked strip is evaluated differently from other strips. Adjacent strips are ignored + * and includes a workaround for when user is not editing in place. */ +static void animsys_create_tweak_strip(const AnimData *adt, + const bool keyframing_to_strip, + NlaStrip *r_tweak_strip) + +{ + /* Copy active strip so we can modify how it evaluates without affecting user data. */ + memcpy(r_tweak_strip, adt->actstrip, sizeof(NlaStrip)); + r_tweak_strip->next = r_tweak_strip->prev = NULL; + + /* If tweaked strip is syncing action length, then evaluate using action length. */ + if (r_tweak_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH) { + BKE_nlastrip_recalculate_bounds_sync_action(r_tweak_strip); + } + + /* Strips with a user-defined time curve don't get properly remapped for editing + * at the moment, so mapping them just for display may be confusing. */ + const bool is_inplace_tweak = !(adt->flag & ADT_NLA_EDIT_NOMAP) && + !(adt->actstrip->flag & NLASTRIP_FLAG_USR_TIME); + + if (!is_inplace_tweak) { + /* Use Hold due to no proper remapping yet (the note above). */ + r_tweak_strip->extendmode = NLASTRIP_EXTEND_HOLD; + + /* Disable range. */ + r_tweak_strip->flag |= NLASTRIP_FLAG_NO_TIME_MAP; + } + + /** Controls whether able to keyframe outside range of tweaked strip. */ + if (keyframing_to_strip) { + r_tweak_strip->extendmode = (is_inplace_tweak && + !(r_tweak_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH)) ? + NLASTRIP_EXTEND_NOTHING : + NLASTRIP_EXTEND_HOLD; + } +} + +/** Action track and strip are associated with the non-pushed action. */ +static void animsys_create_action_track_strip(const AnimData *adt, + const bool keyframing_to_strip, + NlaStrip *r_action_strip) +{ + memset(r_action_strip, 0, sizeof(NlaStrip)); + + bAction *action = adt->action; + + if ((adt->flag & ADT_NLA_EDIT_ON)) { + action = adt->tmpact; + } + + /* Set settings of dummy NLA strip from AnimData settings. */ + r_action_strip->act = action; + + /* Action range is calculated taking F-Modifiers into account + * (which making new strips doesn't do due to the troublesome nature of that). */ + calc_action_range(r_action_strip->act, &r_action_strip->actstart, &r_action_strip->actend, 1); + r_action_strip->start = r_action_strip->actstart; + r_action_strip->end = (IS_EQF(r_action_strip->actstart, r_action_strip->actend)) ? + (r_action_strip->actstart + 1.0f) : + (r_action_strip->actend); + + r_action_strip->blendmode = adt->act_blendmode; + r_action_strip->extendmode = adt->act_extendmode; + r_action_strip->influence = adt->act_influence; + + /* NOTE: must set this, or else the default setting overrides, + * and this setting doesn't work. */ + r_action_strip->flag |= NLASTRIP_FLAG_USR_INFLUENCE; + + /* Unless extendmode is Nothing (might be useful for flattening NLA evaluation), disable range. + * Extendmode Nothing and Hold will behave as normal. Hold Forward will behave just like Hold. + */ + if (r_action_strip->extendmode != NLASTRIP_EXTEND_NOTHING) { + r_action_strip->flag |= NLASTRIP_FLAG_NO_TIME_MAP; + } + + const bool tweaking = (adt->flag & ADT_NLA_EDIT_ON) != 0; + const bool soloing = (adt->flag & ADT_NLA_SOLO_TRACK) != 0; + const bool actionstrip_evaluated = r_action_strip->act && !soloing && !tweaking; + if (!actionstrip_evaluated) { + r_action_strip->flag |= NLASTRIP_FLAG_MUTED; + } + + /** If we're keyframing, then we must allow keyframing outside fcurve bounds. */ + if (keyframing_to_strip) { + r_action_strip->extendmode = NLASTRIP_EXTEND_HOLD; + } +} + +static bool is_nlatrack_evaluatable(const AnimData *adt, const NlaTrack *nlt) +{ + /* Skip disabled tracks unless it contains the tweaked strip. */ + const bool contains_tweak_strip = (adt->flag & ADT_NLA_EDIT_ON) && + (nlt->index == adt->act_track->index); + if ((nlt->flag & NLATRACK_DISABLED) && !contains_tweak_strip) { + return false; + } + + /* Solo and muting are mutually exclusive. */ + if (adt->flag & ADT_NLA_SOLO_TRACK) { + /* Skip if there is a solo track, but this isn't it. */ + if ((nlt->flag & NLATRACK_SOLO) == 0) { + return false; + } + } + else { + /* Skip track if muted. */ + if (nlt->flag & NLATRACK_MUTED) { + return false; + } + } + + return true; +} + +/** Check for special case of non-pushed action being evaluated with no NLA influence (off and no + * strips evaluated) nor NLA interference (ensure NLA not soloing). */ +static bool is_action_track_evaluated_without_nla(const AnimData *adt, + const bool any_strip_evaluated) +{ + if (adt->action == NULL) { + return false; + } + + if (any_strip_evaluated) { + return false; + } + + /** NLA settings interference. */ + if ((adt->flag & (ADT_NLA_SOLO_TRACK | ADT_NLA_EDIT_ON)) == 0) { + return false; + } + + /** Allow action track to evaluate as if there isn't any NLA data. */ + return true; +} + /** - * NLA Evaluation function - values are calculated and stored in temporary "NlaEvalChannels" + * XXX(Wayde Moss): #BKE_nlatrack_find_tweaked() exists within nla.c, but it doesn't appear to + * work as expected. From #animsys_evaluate_nla_for_flush(), it returns NULL in tweak mode. I'm not + * sure why. Preferably, it would be as simple as checking for `(adt->act_Track == nlt)` but that + * doesn't work either, neither does comparing indices. * + * This function is a temporary work around. The first disabled track is always the tweaked track. + */ +static NlaTrack *nlatrack_find_tweaked(const AnimData *adt) +{ + NlaTrack *nlt; + + if (adt == NULL) { + return NULL; + } + + /* Since the track itself gets disabled, we want the first disabled. */ + for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) { + if (nlt->flag & NLATRACK_DISABLED) { + return nlt; + } + } + + return NULL; +} + +/** + * NLA Evaluation function - values are calculated and stored in temporary "NlaEvalChannels" * \param[out] echannels: Evaluation channels with calculated values - * \param[out] r_context: If not NULL, - * data about the currently edited strip is stored here and excluded from value calculation. - * \return false if NLA evaluation isn't actually applicable. */ -static bool animsys_evaluate_nla(NlaEvalData *echannels, - PointerRNA *ptr, - AnimData *adt, - const AnimationEvalContext *anim_eval_context, - const bool flush_to_original, - NlaKeyframingContext *r_context) +static bool animsys_evaluate_nla_for_flush(NlaEvalData *echannels, + PointerRNA *ptr, + const AnimData *adt, + const AnimationEvalContext *anim_eval_context, + const bool flush_to_original) { NlaTrack *nlt; short track_index = 0; bool has_strips = false; - ListBase estrips = {NULL, NULL}; NlaEvalStrip *nes; - NlaStrip dummy_strip_buf; - /* dummy strip for active action */ - NlaStrip *dummy_strip = r_context ? &r_context->strip : &dummy_strip_buf; + NlaStrip tweak_strip; - memset(dummy_strip, 0, sizeof(*dummy_strip)); + NlaTrack *tweaked_track = nlatrack_find_tweaked(adt); - /* 1. get the stack of strips to evaluate at current time (influence calculated here) */ + /* Get the stack of strips to evaluate at current time (influence calculated here). */ for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next, track_index++) { - /* stop here if tweaking is on and this strip is the tweaking track - * (it will be the first one that's 'disabled')... */ - if ((adt->flag & ADT_NLA_EDIT_ON) && (nlt->flag & NLATRACK_DISABLED)) { - break; + + if (!is_nlatrack_evaluatable(adt, nlt)) { + continue; } - /* solo and muting are mutually exclusive... */ - if (adt->flag & ADT_NLA_SOLO_TRACK) { - /* skip if there is a solo track, but this isn't it */ - if ((nlt->flag & NLATRACK_SOLO) == 0) { - continue; - } - /* else - mute doesn't matter */ + if (nlt->strips.first) { + has_strips = true; + } + + /** Append strip to evaluate for this track. */ + if (nlt == tweaked_track) { + /** Tweaked strip is evaluated differently. */ + animsys_create_tweak_strip(adt, false, &tweak_strip); + nes = nlastrips_ctime_get_strip_single( + &estrips, &tweak_strip, anim_eval_context, flush_to_original); } else { - /* no solo tracks - skip track if muted */ - if (nlt->flag & NLATRACK_MUTED) { - continue; - } + nes = nlastrips_ctime_get_strip( + &estrips, &nlt->strips, track_index, anim_eval_context, flush_to_original); + } + if (nes) { + nes->track = nlt; + } + } + + if (is_action_track_evaluated_without_nla(adt, has_strips)) { + BLI_freelistN(&estrips); + return false; + } + + NlaStrip action_strip = {0}; + animsys_create_action_track_strip(adt, false, &action_strip); + nlastrips_ctime_get_strip_single(&estrips, &action_strip, anim_eval_context, flush_to_original); + + /* Per strip, evaluate and accumulate on top of existing channels. */ + for (nes = estrips.first; nes; nes = nes->next) { + nlastrip_evaluate(ptr, + echannels, + NULL, + nes, + &echannels->eval_snapshot, + anim_eval_context, + flush_to_original); + } + + /* Free temporary evaluation data that's not used elsewhere. */ + BLI_freelistN(&estrips); + return true; +} + +/** Lower blended values are calculated and accumulated into r_context->lower_eval_data. */ +static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr, + const AnimData *adt, + const AnimationEvalContext *anim_eval_context, + NlaKeyframingContext *r_context) +{ + if (!r_context) { + return; + } + + /* Early out. If NLA track is soloing and tweaked action isn't it, then don't allow keyframe + * insertion. */ + if (adt->flag & ADT_NLA_SOLO_TRACK) { + if (!(adt->act_track && (adt->act_track->flag & NLATRACK_SOLO))) { + r_context->eval_strip = NULL; + return; + } + } + + NlaTrack *nlt; + short track_index = 0; + bool has_strips = false; + + ListBase lower_estrips = {NULL, NULL}; + NlaEvalStrip *nes; + + NlaTrack *tweaked_track = nlatrack_find_tweaked(adt); + + /* Get the lower stack of strips to evaluate at current time (influence calculated here). */ + for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next, track_index++) { + + if (!is_nlatrack_evaluatable(adt, nlt)) { + continue; + } + + /* Tweaked strip effect should not be stored in any snapshot. */ + if (nlt == tweaked_track) { + break; } - /* if this track has strips (but maybe they won't be suitable), set has_strips - * - used for mainly for still allowing normal action evaluation... - */ if (nlt->strips.first) { has_strips = true; } - /* otherwise, get strip to evaluate for this channel */ + /* Get strip to evaluate for this channel. */ nes = nlastrips_ctime_get_strip( - &estrips, &nlt->strips, track_index, anim_eval_context, flush_to_original); + &lower_estrips, &nlt->strips, track_index, anim_eval_context, false); if (nes) { nes->track = nlt; } } - /* add 'active' Action (may be tweaking track) as last strip to evaluate in NLA stack - * - only do this if we're not exclusively evaluating the 'solo' NLA-track - * - however, if the 'solo' track houses the current 'tweaking' strip, - * then we should allow this to play, otherwise nothing happens + /** Note: Although we early out, we can still keyframe to the non-pushed action since the + * keyframe remap function detects (r_context->strip.act == NULL) and will keyframe without + * remapping. */ - if ((adt->action) && ((adt->flag & ADT_NLA_SOLO_TRACK) == 0 || (adt->flag & ADT_NLA_EDIT_ON))) { - /* if there are strips, evaluate action as per NLA rules */ - if ((has_strips) || (adt->actstrip)) { - /* make dummy NLA strip, and add that to the stack */ - ListBase dummy_trackslist; - - dummy_trackslist.first = dummy_trackslist.last = dummy_strip; - - /* Strips with a user-defined time curve don't get properly remapped for editing - * at the moment, so mapping them just for display may be confusing. */ - bool is_inplace_tweak = (nlt) && !(adt->flag & ADT_NLA_EDIT_NOMAP) && - !(adt->actstrip->flag & NLASTRIP_FLAG_USR_TIME); - - if (is_inplace_tweak) { - /* edit active action in-place according to its active strip, so copy the data */ - memcpy(dummy_strip, adt->actstrip, sizeof(NlaStrip)); - /* Prevents nla eval from considering active strip's adj strips. - * For user, this means entering tweak mode on a strip ignores evaluating adjacent strips - * in the same track. */ - dummy_strip->next = dummy_strip->prev = NULL; - - /* If tweaked strip is syncing action length, then evaluate using action length. */ - if (dummy_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH) { - BKE_nlastrip_recalculate_bounds_sync_action(dummy_strip); - } - } - else { - /* set settings of dummy NLA strip from AnimData settings */ - dummy_strip->act = adt->action; - - /* action range is calculated taking F-Modifiers into account - * (which making new strips doesn't do due to the troublesome nature of that) */ - calc_action_range(dummy_strip->act, &dummy_strip->actstart, &dummy_strip->actend, 1); - dummy_strip->start = dummy_strip->actstart; - dummy_strip->end = (IS_EQF(dummy_strip->actstart, dummy_strip->actend)) ? - (dummy_strip->actstart + 1.0f) : - (dummy_strip->actend); - - /* Always use the blend mode of the strip in tweak mode, even if not in-place. */ - if (nlt && adt->actstrip) { - dummy_strip->blendmode = adt->actstrip->blendmode; - dummy_strip->extendmode = NLASTRIP_EXTEND_HOLD; - } - else { - dummy_strip->blendmode = adt->act_blendmode; - dummy_strip->extendmode = adt->act_extendmode; - } + if (is_action_track_evaluated_without_nla(adt, has_strips)) { + BLI_freelistN(&lower_estrips); + return; + } - /* Unless extend-mode is Nothing (might be useful for flattening NLA evaluation), - * disable range. */ - if (dummy_strip->extendmode != NLASTRIP_EXTEND_NOTHING) { - dummy_strip->flag |= NLASTRIP_FLAG_NO_TIME_MAP; - } + /* Write r_context->eval_strip. */ + if (adt->flag & ADT_NLA_EDIT_ON) { - dummy_strip->influence = adt->act_influence; + NlaStrip *tweak_strip = &r_context->strip; + animsys_create_tweak_strip(adt, true, tweak_strip); + r_context->eval_strip = nlastrips_ctime_get_strip_single( + NULL, tweak_strip, anim_eval_context, false); + } + else { - /* NOTE: must set this, or else the default setting overrides, - * and this setting doesn't work. */ - dummy_strip->flag |= NLASTRIP_FLAG_USR_INFLUENCE; - } + NlaStrip *action_strip = &r_context->strip; + animsys_create_action_track_strip(adt, true, action_strip); + r_context->eval_strip = nlastrips_ctime_get_strip_single( + NULL, action_strip, anim_eval_context, false); + } - /* add this to our list of evaluation strips */ - if (r_context == NULL) { - nlastrips_ctime_get_strip( - &estrips, &dummy_trackslist, -1, anim_eval_context, flush_to_original); - } - /* If computing the context for keyframing, store data there instead of the list. */ - else { - /* The extend mode here effectively controls - * whether it is possible to key-frame beyond the ends.*/ - dummy_strip->extendmode = (is_inplace_tweak && - !(dummy_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH)) ? - NLASTRIP_EXTEND_NOTHING : - NLASTRIP_EXTEND_HOLD; - - r_context->eval_strip = nes = nlastrips_ctime_get_strip( - NULL, &dummy_trackslist, -1, anim_eval_context, flush_to_original); - - /* These setting combinations require no data from strips below, so exit immediately. */ - if ((nes == NULL) || - (dummy_strip->blendmode == NLASTRIP_MODE_REPLACE && dummy_strip->influence == 1.0f)) { - BLI_freelistN(&estrips); - return true; - } - } - } - else { - /* special case - evaluate as if there isn't any NLA data */ - BLI_freelistN(&estrips); - return false; - } + /* If NULL, then keyframing will fail. No need to do any more processing. */ + if (!r_context->eval_strip) { + BLI_freelistN(&lower_estrips); + return; } - /* only continue if there are strips to evaluate */ - if (BLI_listbase_is_empty(&estrips)) { - return true; + /* If tweak strip is full REPLACE, then lower strips not needed. */ + if (r_context->strip.blendmode == NLASTRIP_MODE_REPLACE && + IS_EQF(r_context->strip.influence, 1.0f)) { + BLI_freelistN(&lower_estrips); + return; } - /* 2. for each strip, evaluate then accumulate on top of existing channels, - * but don't set values yet. */ - for (nes = estrips.first; nes; nes = nes->next) { + /* For each strip, evaluate then accumulate on top of existing channels. */ + for (nes = lower_estrips.first; nes; nes = nes->next) { nlastrip_evaluate(ptr, - echannels, + &r_context->lower_eval_data, NULL, nes, - &echannels->eval_snapshot, + &r_context->lower_eval_data.eval_snapshot, anim_eval_context, - flush_to_original); + false); } - /* 3. free temporary evaluation data that's not used elsewhere */ - BLI_freelistN(&estrips); - return true; + /* Free temporary evaluation data that's not used elsewhere. */ + BLI_freelistN(&lower_estrips); } /* NLA Evaluation function (mostly for use through do_animdata) @@ -2412,7 +2574,7 @@ static void animsys_calculate_nla(PointerRNA *ptr, nlaeval_init(&echannels); /* evaluate the NLA stack, obtaining a set of values to flush */ - if (animsys_evaluate_nla(&echannels, ptr, adt, anim_eval_context, flush_to_original, NULL)) { + if (animsys_evaluate_nla_for_flush(&echannels, ptr, adt, anim_eval_context, flush_to_original)) { /* reset any channels touched by currently inactive actions to default value */ animsys_evaluate_nla_domain(ptr, &echannels, adt); @@ -2448,8 +2610,7 @@ NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context( struct ListBase *cache, struct PointerRNA *ptr, struct AnimData *adt, - const AnimationEvalContext *anim_eval_context, - const bool flush_to_original) + const AnimationEvalContext *anim_eval_context) { /* No remapping needed if NLA is off or no action. */ if ((adt == NULL) || (adt->action == NULL) || (adt->nla_tracks.first == NULL) || @@ -2472,8 +2633,7 @@ NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context( ctx->adt = adt; nlaeval_init(&ctx->lower_eval_data); - animsys_evaluate_nla( - &ctx->lower_eval_data, ptr, adt, anim_eval_context, flush_to_original, ctx); + animsys_evaluate_nla_for_keyframing(ptr, adt, anim_eval_context, ctx); BLI_assert(ELEM(ctx->strip.act, NULL, adt->action)); BLI_addtail(cache, ctx); diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 4c9fb4b191a..b8a0ed087e0 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -331,6 +331,8 @@ IDTypeInfo IDType_ID_AR = { .blend_read_expand = armature_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; /** \} */ diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index e1e8d06b9ec..85dabe4490c 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -526,6 +526,10 @@ static ReadAttributePtr read_attribute_from_custom_data(const CustomData &custom case CD_PROP_BOOL: return std::make_unique<ArrayReadAttribute<bool>>( domain, Span(static_cast<bool *>(layer.data), size)); + case CD_MLOOPUV: + auto get_uv = [](const MLoopUV &uv) { return float2(uv.uv); }; + return std::make_unique<DerivedArrayReadAttribute<MLoopUV, float2, decltype(get_uv)>>( + domain, Span(static_cast<MLoopUV *>(layer.data), size), get_uv); } } } @@ -570,6 +574,12 @@ static WriteAttributePtr write_attribute_from_custom_data( case CD_PROP_BOOL: return std::make_unique<ArrayWriteAttribute<bool>>( domain, MutableSpan(static_cast<bool *>(layer.data), size)); + case CD_MLOOPUV: + auto get_uv = [](const MLoopUV &uv) { return float2(uv.uv); }; + auto set_uv = [](MLoopUV &uv, const float2 value) { copy_v2_v2(uv.uv, value); }; + return std::make_unique< + DerivedArrayWriteAttribute<MLoopUV, float2, decltype(get_uv), decltype(set_uv)>>( + domain, MutableSpan(static_cast<MLoopUV *>(layer.data), size), get_uv, set_uv); } } } @@ -597,8 +607,9 @@ static void get_custom_data_layer_attribute_names(const CustomData &custom_data, Set<std::string> &r_names) { for (const CustomDataLayer &layer : blender::Span(custom_data.layers, custom_data.totlayer)) { - if (component.attribute_domain_with_type_supported(domain, - static_cast<CustomDataType>(layer.type))) { + const CustomDataType data_type = static_cast<CustomDataType>(layer.type); + if (component.attribute_domain_with_type_supported(domain, data_type) || + ELEM(data_type, CD_MLOOPUV)) { r_names.add(layer.name); } } @@ -1327,7 +1338,7 @@ Set<std::string> MeshComponent::attribute_names() const for (StringRef name : vertex_group_names_.keys()) { names.add(name); } - get_custom_data_layer_attribute_names(mesh_->pdata, *this, ATTR_DOMAIN_CORNER, names); + get_custom_data_layer_attribute_names(mesh_->ldata, *this, ATTR_DOMAIN_CORNER, names); get_custom_data_layer_attribute_names(mesh_->vdata, *this, ATTR_DOMAIN_POINT, names); get_custom_data_layer_attribute_names(mesh_->edata, *this, ATTR_DOMAIN_EDGE, names); get_custom_data_layer_attribute_names(mesh_->pdata, *this, ATTR_DOMAIN_POLYGON, names); diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 0855db1a943..49475c014cd 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -647,6 +647,10 @@ UserDef *BKE_blendfile_userdef_from_defaults(void) BKE_studiolight_default(userdef->light_param, userdef->light_ambient); BKE_preferences_asset_library_default_add(userdef); + /* Enable asset browser features by default for alpha testing. + * BLO_sanitize_experimental_features_userpref_blend() will disable it again for non-alpha + * builds. */ + userdef->experimental.use_asset_browser = true; return userdef; } diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index 762ced7dc5f..2ad0ac950d0 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -23,7 +23,7 @@ * - passing output paths to the visitor?, like render out. * - passing sequence strips with many images. * - passing directory paths - visitors don't know which path is a dir or a file. - * */ + */ #include <sys/stat.h> diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 9a954a89cad..28c8ae3e416 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -410,6 +410,8 @@ IDTypeInfo IDType_ID_BR = { .blend_read_expand = brush_blend_read_expand, .blend_read_undo_preserve = brush_undo_preserve, + + .lib_override_apply_post = NULL, }; static RNG *brush_rng; @@ -978,7 +980,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_settings->fill_leak = 3; brush->gpencil_settings->fill_threshold = 0.1f; brush->gpencil_settings->fill_simplylvl = 1; - brush->gpencil_settings->fill_factor = 1; + brush->gpencil_settings->fill_factor = 1.0f; brush->gpencil_settings->draw_strength = 1.0f; brush->gpencil_settings->hardeness = 1.0f; diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index d6c31809a2e..1e2139522f1 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -142,6 +142,8 @@ IDTypeInfo IDType_ID_CF = { .blend_read_expand = NULL, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; /* TODO: make this per cache file to avoid global locks. */ diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 0ca22e34973..b0e3743add1 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -203,6 +203,8 @@ IDTypeInfo IDType_ID_CA = { .blend_read_expand = camera_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; /** \} */ diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 58ce7227398..601ee57fc89 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -362,6 +362,8 @@ IDTypeInfo IDType_ID_GR = { .blend_read_expand = collection_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; /** \} */ @@ -1941,9 +1943,6 @@ static void scene_collections_build_array(Collection *collection, void *data) static void scene_collections_array(Scene *scene, Collection ***collections_array, int *tot) { - Collection *collection; - Collection **array; - *collections_array = NULL; *tot = 0; @@ -1951,7 +1950,7 @@ static void scene_collections_array(Scene *scene, Collection ***collections_arra return; } - collection = scene->master_collection; + Collection *collection = scene->master_collection; BLI_assert(collection != NULL); scene_collection_callback(collection, scene_collections_count, tot); @@ -1959,7 +1958,8 @@ static void scene_collections_array(Scene *scene, Collection ***collections_arra return; } - *collections_array = array = MEM_mallocN(sizeof(Collection *) * (*tot), "CollectionArray"); + Collection **array = MEM_mallocN(sizeof(Collection *) * (*tot), "CollectionArray"); + *collections_array = array; scene_collection_callback(collection, scene_collections_build_array, &array); } diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index 65accc66084..6bc385ecd31 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -318,7 +318,7 @@ static eContextResult ctx_data_get(bContext *C, const char *member, bContextData * * Values in order of importance * (0, -1, 1) - Where 1 is highest priority - * */ + */ if (done != 1 && recursion < 1 && C->wm.store) { C->data.recursion = 1; diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index ebce28c4e23..5632ae28960 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -327,6 +327,8 @@ IDTypeInfo IDType_ID_CU = { .blend_read_expand = curve_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; static int cu_isectLL(const float v1[3], @@ -2373,7 +2375,7 @@ static void make_bevel_list_3D_minimum_twist(BevList *bl) * 0,1,2,3,4 --> 1,2,3,4,0 * * this is why we compare last with second last - * */ + */ float vec_1[3] = {0, 1, 0}, vec_2[3] = {0, 1, 0}, angle, ang_fac, cross_tmp[3]; BevPoint *bevp_first; diff --git a/source/blender/blenkernel/intern/curve_deform.c b/source/blender/blenkernel/intern/curve_deform.c index 4725be6d302..63da7c1dd11 100644 --- a/source/blender/blenkernel/intern/curve_deform.c +++ b/source/blender/blenkernel/intern/curve_deform.c @@ -233,7 +233,7 @@ static bool calc_curve_deform( * Now for Neg Up XYZ, the colors are all dark, and ordered clockwise - Campbell * * note: moved functions into quat_apply_track/vec_apply_track - * */ + */ copy_qt_qt(quat, new_quat); copy_v3_v3(cent, co); diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index b0994fb683a..37e9c810123 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -212,9 +212,9 @@ static void layerFree_mdeformvert(void *data, int count, int size) /* copy just zeros in this case */ static void layerCopy_bmesh_elem_py_ptr(const void *UNUSED(source), void *dest, int count) { - int i, size = sizeof(void *); + const int size = sizeof(void *); - for (i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { void **ptr = POINTER_OFFSET(dest, i * size); *ptr = NULL; } @@ -253,15 +253,14 @@ static void layerInterp_mdeformvert(const void **sources, MDeformVert *dvert = dest; struct MDeformWeight_Link *dest_dwlink = NULL; struct MDeformWeight_Link *node; - int i, j, totweight; /* build a list of unique def_nrs for dest */ - totweight = 0; - for (i = 0; i < count; i++) { + int totweight = 0; + for (int i = 0; i < count; i++) { const MDeformVert *source = sources[i]; float interp_weight = weights[i]; - for (j = 0; j < source->totweight; j++) { + for (int j = 0; j < source->totweight; j++) { MDeformWeight *dw = &source->dw[j]; float weight = dw->weight * interp_weight; @@ -311,7 +310,8 @@ static void layerInterp_mdeformvert(const void **sources, if (totweight) { dvert->totweight = totweight; - for (i = 0, node = dest_dwlink; node; node = node->next, i++) { + int i = 0; + for (node = dest_dwlink; node; node = node->next, i++) { if (node->dw.weight > 1.0f) { node->dw.weight = 1.0f; } @@ -416,18 +416,16 @@ static void layerInterp_tface( const void **sources, const float *weights, const float *sub_weights, int count, void *dest) { MTFace *tf = dest; - int i, j, k; float uv[4][2] = {{0.0f}}; - const float *sub_weight; - sub_weight = sub_weights; - for (i = 0; i < count; i++) { + const float *sub_weight = sub_weights; + for (int i = 0; i < count; i++) { const float interp_weight = weights[i]; const MTFace *src = sources[i]; - for (j = 0; j < 4; j++) { + for (int j = 0; j < 4; j++) { if (sub_weights) { - for (k = 0; k < 4; k++, sub_weight++) { + for (int k = 0; k < 4; k++, sub_weight++) { madd_v2_v2fl(uv[j], src->uv[k], (*sub_weight) * interp_weight); } } @@ -446,9 +444,8 @@ static void layerSwap_tface(void *data, const int *corner_indices) { MTFace *tf = data; float uv[4][2]; - int j; - for (j = 0; j < 4; j++) { + for (int j = 0; j < 4; j++) { const int source_index = corner_indices[j]; copy_v2_v2(uv[j], tf->uv[source_index]); } @@ -517,18 +514,16 @@ static void layerInterp_origspace_face( const void **sources, const float *weights, const float *sub_weights, int count, void *dest) { OrigSpaceFace *osf = dest; - int i, j, k; float uv[4][2] = {{0.0f}}; - const float *sub_weight; - sub_weight = sub_weights; - for (i = 0; i < count; i++) { + const float *sub_weight = sub_weights; + for (int i = 0; i < count; i++) { const float interp_weight = weights[i]; const OrigSpaceFace *src = sources[i]; - for (j = 0; j < 4; j++) { + for (int j = 0; j < 4; j++) { if (sub_weights) { - for (k = 0; k < 4; k++, sub_weight++) { + for (int k = 0; k < 4; k++, sub_weight++) { madd_v2_v2fl(uv[j], src->uv[k], (*sub_weight) * interp_weight); } } @@ -546,9 +541,8 @@ static void layerSwap_origspace_face(void *data, const int *corner_indices) { OrigSpaceFace *osf = data; float uv[4][2]; - int j; - for (j = 0; j < 4; j++) { + for (int j = 0; j < 4; j++) { copy_v2_v2(uv[j], osf->uv[corner_indices[j]]); } memcpy(osf->uv, uv, sizeof(osf->uv)); @@ -567,13 +561,11 @@ static void layerDefault_origspace_face(void *data, int count) static void layerSwap_mdisps(void *data, const int *ci) { MDisps *s = data; - float(*d)[3] = NULL; - int corners, cornersize, S; if (s->disps) { int nverts = (ci[1] == 3) ? 4 : 3; /* silly way to know vertex count of face */ - corners = multires_mdisp_corners(s); - cornersize = s->totdisp / corners; + int corners = multires_mdisp_corners(s); + int cornersize = s->totdisp / corners; if (corners != nverts) { /* happens when face changed vertex count in edit mode @@ -585,9 +577,9 @@ static void layerSwap_mdisps(void *data, const int *ci) return; } - d = MEM_calloc_arrayN(s->totdisp, sizeof(float[3]), "mdisps swap"); + float(*d)[3] = MEM_calloc_arrayN(s->totdisp, sizeof(float[3]), "mdisps swap"); - for (S = 0; S < corners; S++) { + for (int S = 0; S < corners; S++) { memcpy(d + cornersize * S, s->disps + cornersize * ci[S], sizeof(float[3]) * cornersize); } @@ -1128,9 +1120,8 @@ static void layerSwap_mcol(void *data, const int *corner_indices) { MCol *mcol = data; MCol col[4]; - int j; - for (j = 0; j < 4; j++) { + for (int j = 0; j < 4; j++) { col[j] = mcol[corner_indices[j]]; } @@ -2064,13 +2055,13 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, void CustomData_update_typemap(CustomData *data) { - int i, lasttype = -1; + int lasttype = -1; - for (i = 0; i < CD_NUMTYPES; i++) { + for (int i = 0; i < CD_NUMTYPES; i++) { data->typemap[i] = -1; } - for (i = 0; i < data->totlayer; i++) { + for (int i = 0; i < data->totlayer; i++) { const int type = data->layers[i].type; if (type != lasttype) { data->typemap[type] = i; @@ -2097,18 +2088,16 @@ bool CustomData_merge(const struct CustomData *source, { /*const LayerTypeInfo *typeInfo;*/ CustomDataLayer *layer, *newlayer; - void *data; - int i, type, lasttype = -1, lastactive = 0, lastrender = 0, lastclone = 0, lastmask = 0, - flag = 0; + int lasttype = -1, lastactive = 0, lastrender = 0, lastclone = 0, lastmask = 0; int number = 0, maxnumber = -1; bool changed = false; - for (i = 0; i < source->totlayer; i++) { + for (int i = 0; i < source->totlayer; i++) { layer = &source->layers[i]; /*typeInfo = layerType_getInfo(layer->type);*/ /*UNUSED*/ - type = layer->type; - flag = layer->flag; + int type = layer->type; + int flag = layer->flag; if (type != lasttype) { number = 0; @@ -2136,6 +2125,7 @@ bool CustomData_merge(const struct CustomData *source, continue; } + void *data; switch (alloctype) { case CD_ASSIGN: case CD_REFERENCE: @@ -2518,8 +2508,7 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, /* Passing a layer-data to copy from with an alloctype that won't copy is * most likely a bug */ - BLI_assert(!layerdata || (alloctype == CD_ASSIGN) || (alloctype == CD_DUPLICATE) || - (alloctype == CD_REFERENCE)); + BLI_assert(!layerdata || ELEM(alloctype, CD_ASSIGN, CD_DUPLICATE, CD_REFERENCE)); if (!typeInfo->defaultname && CustomData_has_layer(data, type)) { return &data->layers[CustomData_get_layer_index(data, type)]; @@ -2616,10 +2605,9 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, void *CustomData_add_layer( CustomData *data, int type, eCDAllocType alloctype, void *layerdata, int totelem) { - CustomDataLayer *layer; const LayerTypeInfo *typeInfo = layerType_getInfo(type); - layer = customData_add_layer__internal( + CustomDataLayer *layer = customData_add_layer__internal( data, type, alloctype, layerdata, totelem, typeInfo->defaultname); CustomData_update_typemap(data); @@ -2638,9 +2626,8 @@ void *CustomData_add_layer_named(CustomData *data, int totelem, const char *name) { - CustomDataLayer *layer; - - layer = customData_add_layer__internal(data, type, alloctype, layerdata, totelem, name); + CustomDataLayer *layer = customData_add_layer__internal( + data, type, alloctype, layerdata, totelem, name); CustomData_update_typemap(data); if (layer) { @@ -2828,12 +2815,10 @@ bool CustomData_is_referenced_layer(struct CustomData *data, int type) void CustomData_free_temporary(CustomData *data, int totelem) { - CustomDataLayer *layer; int i, j; bool changed = false; - for (i = 0, j = 0; i < data->totlayer; i++) { - layer = &data->layers[i]; + CustomDataLayer *layer = &data->layers[i]; if (i != j) { data->layers[j] = data->layers[i]; @@ -3681,10 +3666,8 @@ void CustomData_bmesh_free_block_data_exclude_by_type(CustomData *data, static void CustomData_bmesh_set_default_n(CustomData *data, void **block, int n) { - const LayerTypeInfo *typeInfo; int offset = data->layers[n].offset; - - typeInfo = layerType_getInfo(data->layers[n].type); + const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[n].type); if (typeInfo->set_default) { typeInfo->set_default(POINTER_OFFSET(*block, offset), 1); @@ -4050,7 +4033,6 @@ void CustomData_bmesh_interp(CustomData *data, return; } - int i, j; void *source_buf[SOURCE_BUF_SIZE]; const void **sources = (const void **)source_buf; @@ -4071,11 +4053,11 @@ void CustomData_bmesh_interp(CustomData *data, } /* interpolates a layer at a time */ - for (i = 0; i < data->totlayer; i++) { + for (int i = 0; i < data->totlayer; i++) { CustomDataLayer *layer = &data->layers[i]; const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); if (typeInfo->interp) { - for (j = 0; j < count; j++) { + for (int j = 0; j < count; j++) { sources[j] = POINTER_OFFSET(src_blocks[j], layer->offset); } CustomData_bmesh_interp_n( @@ -4453,7 +4435,6 @@ bool CustomData_layer_validate(CustomDataLayer *layer, const uint totitems, cons void CustomData_layers__print(CustomData *data) { - printf("{\n"); int i; @@ -4508,10 +4489,7 @@ void CustomData_external_read(CustomData *data, ID *id, CustomDataMask mask, int { CustomDataExternal *external = data->external; CustomDataLayer *layer; - CDataFile *cdf; - CDataFileLayer *blay; char filename[FILE_MAX]; - const LayerTypeInfo *typeInfo; int update = 0; if (!external) { @@ -4520,7 +4498,7 @@ void CustomData_external_read(CustomData *data, ID *id, CustomDataMask mask, int for (int i = 0; i < data->totlayer; i++) { layer = &data->layers[i]; - typeInfo = layerType_getInfo(layer->type); + const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); if (!(mask & CD_TYPE_AS_MASK(layer->type))) { /* pass */ @@ -4539,7 +4517,7 @@ void CustomData_external_read(CustomData *data, ID *id, CustomDataMask mask, int customdata_external_filename(filename, id, external); - cdf = cdf_create(CDF_TYPE_MESH); + CDataFile *cdf = cdf_create(CDF_TYPE_MESH); if (!cdf_read_open(cdf, filename)) { cdf_free(cdf); CLOG_ERROR(&LOG, "Failed to read %s layer from %s.", layerType_getName(layer->type), filename); @@ -4548,7 +4526,7 @@ void CustomData_external_read(CustomData *data, ID *id, CustomDataMask mask, int for (int i = 0; i < data->totlayer; i++) { layer = &data->layers[i]; - typeInfo = layerType_getInfo(layer->type); + const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); if (!(mask & CD_TYPE_AS_MASK(layer->type))) { /* pass */ @@ -4557,7 +4535,7 @@ void CustomData_external_read(CustomData *data, ID *id, CustomDataMask mask, int /* pass */ } else if ((layer->flag & CD_FLAG_EXTERNAL) && typeInfo->read) { - blay = cdf_layer_find(cdf, layer->type, layer->name); + CDataFileLayer *blay = cdf_layer_find(cdf, layer->type, layer->name); if (blay) { if (cdf_read_layer(cdf, blay)) { @@ -4584,10 +4562,6 @@ void CustomData_external_write( CustomData *data, ID *id, CustomDataMask mask, int totelem, int free) { CustomDataExternal *external = data->external; - CustomDataLayer *layer; - CDataFile *cdf; - CDataFileLayer *blay; - const LayerTypeInfo *typeInfo; int update = 0; char filename[FILE_MAX]; @@ -4597,8 +4571,8 @@ void CustomData_external_write( /* test if there is anything to write */ for (int i = 0; i < data->totlayer; i++) { - layer = &data->layers[i]; - typeInfo = layerType_getInfo(layer->type); + CustomDataLayer *layer = &data->layers[i]; + const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); if (!(mask & CD_TYPE_AS_MASK(layer->type))) { /* pass */ @@ -4616,11 +4590,11 @@ void CustomData_external_write( CustomData_external_read(data, id, mask, totelem); customdata_external_filename(filename, id, external); - cdf = cdf_create(CDF_TYPE_MESH); + CDataFile *cdf = cdf_create(CDF_TYPE_MESH); for (int i = 0; i < data->totlayer; i++) { - layer = &data->layers[i]; - typeInfo = layerType_getInfo(layer->type); + CustomDataLayer *layer = &data->layers[i]; + const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); if ((layer->flag & CD_FLAG_EXTERNAL) && typeInfo->filesize) { if (layer->flag & CD_FLAG_IN_MEMORY) { @@ -4642,11 +4616,11 @@ void CustomData_external_write( int i; for (i = 0; i < data->totlayer; i++) { - layer = &data->layers[i]; - typeInfo = layerType_getInfo(layer->type); + CustomDataLayer *layer = &data->layers[i]; + const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); if ((layer->flag & CD_FLAG_EXTERNAL) && typeInfo->write) { - blay = cdf_layer_find(cdf, layer->type, layer->name); + CDataFileLayer *blay = cdf_layer_find(cdf, layer->type, layer->name); if (cdf_write_layer(cdf, blay)) { if (typeInfo->write(cdf, layer->data, totelem)) { @@ -4670,8 +4644,8 @@ void CustomData_external_write( } for (i = 0; i < data->totlayer; i++) { - layer = &data->layers[i]; - typeInfo = layerType_getInfo(layer->type); + CustomDataLayer *layer = &data->layers[i]; + const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); if ((layer->flag & CD_FLAG_EXTERNAL) && typeInfo->write) { if (free) { @@ -4691,15 +4665,13 @@ void CustomData_external_add( CustomData *data, ID *UNUSED(id), int type, int UNUSED(totelem), const char *filename) { CustomDataExternal *external = data->external; - CustomDataLayer *layer; - int layer_index; - layer_index = CustomData_get_active_layer_index(data, type); + int layer_index = CustomData_get_active_layer_index(data, type); if (layer_index == -1) { return; } - layer = &data->layers[layer_index]; + CustomDataLayer *layer = &data->layers[layer_index]; if (layer->flag & CD_FLAG_EXTERNAL) { return; @@ -4823,8 +4795,6 @@ static void customdata_data_transfer_interp_generic(const CustomDataTransferLaye cd_interp interp_cd = NULL; cd_copy copy_cd = NULL; - void *tmp_dst; - if (!sources) { /* Not supported here, abort. */ return; @@ -4841,7 +4811,7 @@ static void customdata_data_transfer_interp_generic(const CustomDataTransferLaye copy_cd = type_info->copy; } - tmp_dst = MEM_mallocN(data_size, __func__); + void *tmp_dst = MEM_mallocN(data_size, __func__); if (count > 1 && !interp_cd) { if (data_flag) { diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.c index 375792a02c2..58c050493c9 100644 --- a/source/blender/blenkernel/intern/displist.c +++ b/source/blender/blenkernel/intern/displist.c @@ -488,6 +488,7 @@ void BKE_displist_fill(ListBase *dispbase, while (cont) { int dl_flag_accum = 0; + int dl_rt_accum = 0; cont = 0; totvert = 0; nextcol = 0; @@ -535,6 +536,7 @@ void BKE_displist_fill(ListBase *dispbase, } } dl_flag_accum |= dl->flag; + dl_rt_accum |= dl->rt; } } @@ -544,6 +546,7 @@ void BKE_displist_fill(ListBase *dispbase, dlnew = MEM_callocN(sizeof(DispList), "filldisplist"); dlnew->type = DL_INDEX3; dlnew->flag = (dl_flag_accum & (DL_BACK_CURVE | DL_FRONT_CURVE)); + dlnew->rt = (dl_rt_accum & CU_SMOOTH); dlnew->col = colnr; dlnew->nr = totvert; dlnew->parts = tot; @@ -1129,7 +1132,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, * * The right solution would be to COW the Curve data block at the input of the modifier * stack just like what the mesh modifier does. - * */ + */ modified = BKE_mesh_new_nomain_from_curve_displist(ob, dispbase); } diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index df1dbaa905f..25a0259abe3 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -176,6 +176,8 @@ IDTypeInfo IDType_ID_VF = { .blend_read_expand = NULL, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; /***************************** VFont *******************************/ diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index bb315bc0289..833e1dd3719 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -19,6 +19,7 @@ #include "BKE_mesh.h" #include "BKE_mesh_wrapper.h" #include "BKE_pointcloud.h" +#include "BKE_volume.h" #include "DNA_object_types.h" @@ -52,6 +53,8 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ return new PointCloudComponent(); case GeometryComponentType::Instances: return new InstancesComponent(); + case GeometryComponentType::Volume: + return new VolumeComponent(); } BLI_assert(false); return nullptr; @@ -202,6 +205,13 @@ const PointCloud *GeometrySet::get_pointcloud_for_read() const return (component == nullptr) ? nullptr : component->get_for_read(); } +/* Returns a read-only volume or null. */ +const Volume *GeometrySet::get_volume_for_read() const +{ + const VolumeComponent *component = this->get_component_for_read<VolumeComponent>(); + return (component == nullptr) ? nullptr : component->get_for_read(); +} + /* Returns true when the geometry set has a point cloud component that has a point cloud. */ bool GeometrySet::has_pointcloud() const { @@ -216,6 +226,13 @@ bool GeometrySet::has_instances() const return component != nullptr && component->instances_amount() >= 1; } +/* Returns true when the geometry set has a volume component that has a volume. */ +bool GeometrySet::has_volume() const +{ + const VolumeComponent *component = this->get_component_for_read<VolumeComponent>(); + return component != nullptr && component->has_volume(); +} + /* Create a new geometry set that only contains the given mesh. */ GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership) { @@ -263,6 +280,13 @@ PointCloud *GeometrySet::get_pointcloud_for_write() return component.get_for_write(); } +/* Returns a mutable volume or null. No ownership is transferred. */ +Volume *GeometrySet::get_volume_for_write() +{ + VolumeComponent &component = this->get_component_for_write<VolumeComponent>(); + return component.get_for_write(); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -528,6 +552,85 @@ bool InstancesComponent::is_empty() const /** \} */ /* -------------------------------------------------------------------- */ +/** \name Volume Component + * \{ */ + +VolumeComponent::VolumeComponent() : GeometryComponent(GeometryComponentType::Volume) +{ +} + +VolumeComponent::~VolumeComponent() +{ + this->clear(); +} + +GeometryComponent *VolumeComponent::copy() const +{ + VolumeComponent *new_component = new VolumeComponent(); + if (volume_ != nullptr) { + new_component->volume_ = BKE_volume_copy_for_eval(volume_, false); + new_component->ownership_ = GeometryOwnershipType::Owned; + } + return new_component; +} + +void VolumeComponent::clear() +{ + BLI_assert(this->is_mutable()); + if (volume_ != nullptr) { + if (ownership_ == GeometryOwnershipType::Owned) { + BKE_id_free(nullptr, volume_); + } + volume_ = nullptr; + } +} + +bool VolumeComponent::has_volume() const +{ + return volume_ != nullptr; +} + +/* Clear the component and replace it with the new volume. */ +void VolumeComponent::replace(Volume *volume, GeometryOwnershipType ownership) +{ + BLI_assert(this->is_mutable()); + this->clear(); + volume_ = volume; + ownership_ = ownership; +} + +/* Return the volume and clear the component. The caller takes over responsibility for freeing the + * volume (if the component was responsible before). */ +Volume *VolumeComponent::release() +{ + BLI_assert(this->is_mutable()); + Volume *volume = volume_; + volume_ = nullptr; + return volume; +} + +/* Get the volume from this component. This method can be used by multiple threads at the same + * time. Therefore, the returned volume should not be modified. No ownership is transferred. */ +const Volume *VolumeComponent::get_for_read() const +{ + return volume_; +} + +/* Get the volume from this component. This method can only be used when the component is mutable, + * i.e. it is not shared. The returned volume can be modified. No ownership is transferred. */ +Volume *VolumeComponent::get_for_write() +{ + BLI_assert(this->is_mutable()); + if (ownership_ == GeometryOwnershipType::ReadOnly) { + volume_ = BKE_volume_copy_for_eval(volume_, false); + ownership_ = GeometryOwnershipType::Owned; + } + return volume_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name C API * \{ */ diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index f68a390db64..0c813c170ad 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -93,7 +93,33 @@ static void greasepencil_copy_data(Main *UNUSED(bmain), /* make a copy of source layer and its data */ /* TODO here too could add unused flags... */ - bGPDlayer *gpl_dst = BKE_gpencil_layer_duplicate(gpl_src); + bGPDlayer *gpl_dst = BKE_gpencil_layer_duplicate(gpl_src, true, true); + + /* Apply local layer transform to all frames. Calc the active frame is not enough + * because onion skin can use more frames. This is more slow but required here. */ + if (gpl_dst->actframe != NULL) { + bool transfomed = ((!is_zero_v3(gpl_dst->location)) || (!is_zero_v3(gpl_dst->rotation)) || + (!is_one_v3(gpl_dst->scale))); + if (transfomed) { + loc_eul_size_to_mat4( + gpl_dst->layer_mat, gpl_dst->location, gpl_dst->rotation, gpl_dst->scale); + bool do_onion = ((gpl_dst->onion_flag & GP_LAYER_ONIONSKIN) != 0); + bGPDframe *init_gpf = (do_onion) ? gpl_dst->frames.first : gpl_dst->actframe; + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + bGPDspoint *pt; + int i; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + mul_m4_v3(gpl_dst->layer_mat, &pt->x); + } + } + /* if not onion, exit loop. */ + if (!do_onion) { + break; + } + } + } + } BLI_addtail(&gpd_dst->layers, gpl_dst); } @@ -303,6 +329,8 @@ IDTypeInfo IDType_ID_GD = { .blend_read_expand = greasepencil_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; /* ************************************************** */ @@ -581,7 +609,7 @@ bGPDframe *BKE_gpencil_frame_addcopy(bGPDlayer *gpl, int cframe) } /* Create a copy of the frame */ - new_frame = BKE_gpencil_frame_duplicate(gpl->actframe); + new_frame = BKE_gpencil_frame_duplicate(gpl->actframe, true); /* Find frame to insert it before */ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { @@ -686,6 +714,14 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setacti /* Enable always affected by scene lights. */ gpl->flag |= GP_LAYER_USE_LIGHTS; + + /* Init transform. */ + zero_v3(gpl->location); + zero_v3(gpl->rotation); + copy_v3_fl(gpl->scale, 1.0f); + loc_eul_size_to_mat4(gpl->layer_mat, gpl->location, gpl->rotation, gpl->scale); + invert_m4_m4(gpl->layer_invmat, gpl->layer_mat); + /* make this one the active one */ if (setactive) { BKE_gpencil_layer_active_set(gpd, gpl); @@ -957,7 +993,7 @@ bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src, * \param gpf_src: Source grease pencil frame * \return Pointer to new frame */ -bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src) +bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src, const bool dup_strokes) { bGPDstroke *gps_dst = NULL; bGPDframe *gpf_dst; @@ -971,12 +1007,14 @@ bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src) gpf_dst = MEM_dupallocN(gpf_src); gpf_dst->prev = gpf_dst->next = NULL; - /* copy strokes */ + /* Copy strokes. */ BLI_listbase_clear(&gpf_dst->strokes); - LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) { - /* make copy of source stroke */ - gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true); - BLI_addtail(&gpf_dst->strokes, gps_dst); + if (dup_strokes) { + LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) { + /* make copy of source stroke */ + gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true); + BLI_addtail(&gpf_dst->strokes, gps_dst); + } } /* return new frame */ @@ -1010,7 +1048,9 @@ void BKE_gpencil_frame_copy_strokes(bGPDframe *gpf_src, struct bGPDframe *gpf_ds * \param gpl_src: Source grease pencil layer * \return Pointer to new layer */ -bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) +bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src, + const bool dup_frames, + const bool dup_strokes) { const bGPDframe *gpf_src; bGPDframe *gpf_dst; @@ -1035,14 +1075,16 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) /* copy frames */ BLI_listbase_clear(&gpl_dst->frames); - for (gpf_src = gpl_src->frames.first; gpf_src; gpf_src = gpf_src->next) { - /* make a copy of source frame */ - gpf_dst = BKE_gpencil_frame_duplicate(gpf_src); - BLI_addtail(&gpl_dst->frames, gpf_dst); - - /* if source frame was the current layer's 'active' frame, reassign that too */ - if (gpf_src == gpl_dst->actframe) { - gpl_dst->actframe = gpf_dst; + if (dup_frames) { + for (gpf_src = gpl_src->frames.first; gpf_src; gpf_src = gpf_src->next) { + /* make a copy of source frame */ + gpf_dst = BKE_gpencil_frame_duplicate(gpf_src, dup_strokes); + BLI_addtail(&gpl_dst->frames, gpf_dst); + + /* if source frame was the current layer's 'active' frame, reassign that too */ + if (gpf_src == gpl_dst->actframe) { + gpl_dst->actframe = gpf_dst; + } } } @@ -2519,7 +2561,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer, int cfra) { bGPdata *gpd = (bGPdata *)ob->data; - const bool is_multiedit = GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const bool is_multiedit = ((GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) && (!GPENCIL_PLAY_ON(gpd))); const bool is_onion = do_onion && ((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0); const bool is_drawing = (gpd->runtime.sbuffer_used > 0); @@ -2541,6 +2583,11 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer, continue; } + /* If scale to 0 the layer must be invisible. */ + if (is_zero_v3(gpl->scale)) { + continue; + } + /* Hide the layer if it's defined a view layer filter. This is used to * generate renders, putting only selected GP layers for each View Layer. * This is used only in final render and never in Viewport. */ @@ -2759,10 +2806,10 @@ void BKE_gpencil_update_orig_pointers(const Object *ob_orig, const Object *ob_ev * \param gpl: Grease pencil layer * \param diff_mat: Result parent matrix */ -void BKE_gpencil_parent_matrix_get(const Depsgraph *depsgraph, - Object *obact, - bGPDlayer *gpl, - float diff_mat[4][4]) +void BKE_gpencil_layer_transform_matrix_get(const Depsgraph *depsgraph, + Object *obact, + bGPDlayer *gpl, + float diff_mat[4][4]) { Object *ob_eval = depsgraph != NULL ? DEG_get_evaluated_object(depsgraph, obact) : obact; Object *obparent = gpl->parent; @@ -2771,11 +2818,10 @@ void BKE_gpencil_parent_matrix_get(const Depsgraph *depsgraph, /* if not layer parented, try with object parented */ if (obparent_eval == NULL) { - if (ob_eval != NULL) { - if (ob_eval->type == OB_GPENCIL) { - copy_m4_m4(diff_mat, ob_eval->obmat); - return; - } + if ((ob_eval != NULL) && (ob_eval->type == OB_GPENCIL)) { + copy_m4_m4(diff_mat, ob_eval->obmat); + mul_m4_m4m4(diff_mat, diff_mat, gpl->layer_mat); + return; } /* not gpencil object */ unit_m4(diff_mat); @@ -2785,6 +2831,7 @@ void BKE_gpencil_parent_matrix_get(const Depsgraph *depsgraph, if (ELEM(gpl->partype, PAROBJECT, PARSKEL)) { mul_m4_m4m4(diff_mat, obparent_eval->obmat, gpl->inverse); add_v3_v3(diff_mat[3], ob_eval->obmat[3]); + mul_m4_m4m4(diff_mat, diff_mat, gpl->layer_mat); return; } if (gpl->partype == PARBONE) { @@ -2800,6 +2847,7 @@ void BKE_gpencil_parent_matrix_get(const Depsgraph *depsgraph, mul_m4_m4m4(diff_mat, obparent_eval->obmat, gpl->inverse); add_v3_v3(diff_mat[3], ob_eval->obmat[3]); } + mul_m4_m4m4(diff_mat, diff_mat, gpl->layer_mat); return; } @@ -2807,11 +2855,11 @@ void BKE_gpencil_parent_matrix_get(const Depsgraph *depsgraph, } /** - * Update parent matrix. + * Update parent matrix and local transforms. * \param depsgraph: Depsgraph * \param ob: Grease pencil object */ -void BKE_gpencil_update_layer_parent(const Depsgraph *depsgraph, Object *ob) +void BKE_gpencil_update_layer_transforms(const Depsgraph *depsgraph, Object *ob) { if (ob->type != OB_GPENCIL) { return; @@ -2820,31 +2868,50 @@ void BKE_gpencil_update_layer_parent(const Depsgraph *depsgraph, Object *ob) bGPdata *gpd = (bGPdata *)ob->data; float cur_mat[4][4]; + bool changed = false; LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { - if ((gpl->parent != NULL) && (gpl->actframe != NULL)) { - Object *ob_parent = DEG_get_evaluated_object(depsgraph, gpl->parent); - /* calculate new matrix */ - if (ELEM(gpl->partype, PAROBJECT, PARSKEL)) { - copy_m4_m4(cur_mat, ob_parent->obmat); - } - else if (gpl->partype == PARBONE) { - bPoseChannel *pchan = BKE_pose_channel_find_name(ob_parent->pose, gpl->parsubstr); - if (pchan != NULL) { - copy_m4_m4(cur_mat, ob->imat); - mul_m4_m4m4(cur_mat, ob_parent->obmat, pchan->pose_mat); + unit_m4(cur_mat); + if (gpl->actframe != NULL) { + if (gpl->parent != NULL) { + Object *ob_parent = DEG_get_evaluated_object(depsgraph, gpl->parent); + /* calculate new matrix */ + if (ELEM(gpl->partype, PAROBJECT, PARSKEL)) { + copy_m4_m4(cur_mat, ob_parent->obmat); } - else { - unit_m4(cur_mat); + else if (gpl->partype == PARBONE) { + bPoseChannel *pchan = BKE_pose_channel_find_name(ob_parent->pose, gpl->parsubstr); + if (pchan != NULL) { + copy_m4_m4(cur_mat, ob->imat); + mul_m4_m4m4(cur_mat, ob_parent->obmat, pchan->pose_mat); + } + else { + unit_m4(cur_mat); + } } + changed = !equals_m4m4(gpl->inverse, cur_mat); + } + + /* Calc local layer transform. */ + bool transfomed = ((!is_zero_v3(gpl->location)) || (!is_zero_v3(gpl->rotation)) || + (!is_one_v3(gpl->scale))); + if (transfomed) { + loc_eul_size_to_mat4(gpl->layer_mat, gpl->location, gpl->rotation, gpl->scale); } + /* only redo if any change */ - if (!equals_m4m4(gpl->inverse, cur_mat)) { + if (changed || transfomed) { LISTBASE_FOREACH (bGPDstroke *, gps, &gpl->actframe->strokes) { bGPDspoint *pt; int i; for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - mul_m4_v3(gpl->inverse, &pt->x); - mul_m4_v3(cur_mat, &pt->x); + if (changed) { + mul_m4_v3(gpl->inverse, &pt->x); + mul_m4_v3(cur_mat, &pt->x); + } + + if (transfomed) { + mul_m4_v3(gpl->layer_mat, &pt->x); + } } } } diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index 1be2cba31b5..8b12e1b5fca 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -701,13 +701,18 @@ void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *o Object *ob_orig = (Object *)DEG_get_original_id(&ob->id); bGPdata *gpd_orig = (bGPdata *)ob_orig->data; - /* Need check if some layer is parented. */ + /* Need check if some layer is parented or transformed. */ bool do_parent = false; + bool do_transform = false; LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd_orig->layers) { if (gpl->parent != NULL) { do_parent = true; break; } + if ((!is_zero_v3(gpl->location)) || (!is_zero_v3(gpl->rotation)) || (!is_one_v3(gpl->scale))) { + do_transform = true; + break; + } } const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_eval); @@ -715,7 +720,7 @@ void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *o const bool do_modifiers = (bool)((!is_multiedit) && (!is_curve_edit) && (ob->greasepencil_modifiers.first != NULL) && (!GPENCIL_SIMPLIFY_MODIF(scene))); - if ((!do_modifiers) && (!do_parent)) { + if ((!do_modifiers) && (!do_parent) && (!do_transform)) { return; } DEG_debug_print_eval(depsgraph, __func__, gpd_eval->id.name, gpd_eval); diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c index a44b054e366..f76e5a73478 100644 --- a/source/blender/blenkernel/intern/hair.c +++ b/source/blender/blenkernel/intern/hair.c @@ -197,6 +197,8 @@ IDTypeInfo IDType_ID_HA = { .blend_read_expand = hair_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; static void hair_random(Hair *hair) diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index b0991f1d343..6b164e6bc50 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -92,11 +92,9 @@ IDProperty *IDP_NewIDPArray(const char *name) IDProperty *IDP_CopyIDPArray(const IDProperty *array, const int flag) { /* don't use MEM_dupallocN because this may be part of an array */ - IDProperty *narray, *tmp; - BLI_assert(array->type == IDP_IDPARRAY); - narray = MEM_mallocN(sizeof(IDProperty), __func__); + IDProperty *narray = MEM_mallocN(sizeof(IDProperty), __func__); *narray = *array; narray->data.pointer = MEM_dupallocN(array->data.pointer); @@ -107,7 +105,7 @@ IDProperty *IDP_CopyIDPArray(const IDProperty *array, const int flag) * then free it. this makes for more maintainable * code than simply re-implementing the copy functions * in this loop.*/ - tmp = IDP_CopyProperty_ex(GETPROP(narray, i), flag); + IDProperty *tmp = IDP_CopyProperty_ex(GETPROP(narray, i), flag); memcpy(GETPROP(narray, i), tmp, sizeof(IDProperty)); MEM_freeN(tmp); } @@ -131,15 +129,13 @@ static void IDP_FreeIDPArray(IDProperty *prop, const bool do_id_user) /* shallow copies item */ void IDP_SetIndexArray(IDProperty *prop, int index, IDProperty *item) { - IDProperty *old; - BLI_assert(prop->type == IDP_IDPARRAY); if (index >= prop->len || index < 0) { return; } - old = GETPROP(prop, index); + IDProperty *old = GETPROP(prop, index); if (item != old) { IDP_FreePropertyContent(old); @@ -164,8 +160,6 @@ void IDP_AppendArray(IDProperty *prop, IDProperty *item) void IDP_ResizeIDPArray(IDProperty *prop, int newlen) { - int newsize; - BLI_assert(prop->type == IDP_IDPARRAY); /* first check if the array buffer size has room */ @@ -200,7 +194,7 @@ void IDP_ResizeIDPArray(IDProperty *prop, int newlen) * system realloc(). * The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ... */ - newsize = newlen; + int newsize = newlen; newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize; prop->data.pointer = MEM_recallocN(prop->data.pointer, sizeof(IDProperty) * (size_t)newsize); prop->len = newlen; @@ -218,9 +212,8 @@ static void idp_resize_group_array(IDProperty *prop, int newlen, void *newarr) /* bigger */ IDProperty **array = newarr; IDPropertyTemplate val; - int a; - for (a = prop->len; a < newlen; a++) { + for (int a = prop->len; a < newlen; a++) { val.i = 0; /* silence MSVC warning about uninitialized var when debugging */ array[a] = IDP_New(IDP_GROUP, &val, "IDP_ResizeArray group"); } @@ -228,9 +221,8 @@ static void idp_resize_group_array(IDProperty *prop, int newlen, void *newarr) else { /* smaller */ IDProperty **array = prop->data.pointer; - int a; - for (a = newlen; a < prop->len; a++) { + for (int a = newlen; a < prop->len; a++) { IDP_FreeProperty(array[a]); } } @@ -239,7 +231,6 @@ static void idp_resize_group_array(IDProperty *prop, int newlen, void *newarr) /*this function works for strings too!*/ void IDP_ResizeArray(IDProperty *prop, int newlen) { - int newsize; const bool is_grow = newlen >= prop->len; /* first check if the array buffer size has room */ @@ -257,7 +248,7 @@ void IDP_ResizeArray(IDProperty *prop, int newlen) * system realloc(). * The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ... */ - newsize = newlen; + int newsize = newlen; newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize; if (is_grow == false) { @@ -362,10 +353,8 @@ IDProperty *IDP_NewString(const char *st, const char *name, int maxlen) static IDProperty *IDP_CopyString(const IDProperty *prop, const int flag) { - IDProperty *newp; - BLI_assert(prop->type == IDP_STRING); - newp = idp_generic_copy(prop, flag); + IDProperty *newp = idp_generic_copy(prop, flag); if (prop->data.pointer) { newp->data.pointer = MEM_dupallocN(prop->data.pointer); @@ -379,10 +368,8 @@ static IDProperty *IDP_CopyString(const IDProperty *prop, const int flag) void IDP_AssignString(IDProperty *prop, const char *st, int maxlen) { - int stlen; - BLI_assert(prop->type == IDP_STRING); - stlen = (int)strlen(st); + int stlen = (int)strlen(st); if (maxlen > 0 && maxlen < stlen) { stlen = maxlen; } @@ -400,11 +387,9 @@ void IDP_AssignString(IDProperty *prop, const char *st, int maxlen) void IDP_ConcatStringC(IDProperty *prop, const char *st) { - int newlen; - BLI_assert(prop->type == IDP_STRING); - newlen = prop->len + (int)strlen(st); + int newlen = prop->len + (int)strlen(st); /* we have to remember that prop->len includes the null byte for strings. * so there's no need to add +1 to the resize function.*/ IDP_ResizeArray(prop, newlen); @@ -413,13 +398,11 @@ void IDP_ConcatStringC(IDProperty *prop, const char *st) void IDP_ConcatString(IDProperty *str1, IDProperty *append) { - int newlen; - BLI_assert(append->type == IDP_STRING); /* since ->len for strings includes the NULL byte, we have to subtract one or * we'll get an extra null byte after each concatenation operation.*/ - newlen = str1->len + append->len - 1; + int newlen = str1->len + append->len - 1; IDP_ResizeArray(str1, newlen); strcat(str1->data.pointer, append->data.pointer); } @@ -440,10 +423,8 @@ void IDP_FreeString(IDProperty *prop) static IDProperty *IDP_CopyID(const IDProperty *prop, const int flag) { - IDProperty *newp; - BLI_assert(prop->type == IDP_ID); - newp = idp_generic_copy(prop, flag); + IDProperty *newp = idp_generic_copy(prop, flag); newp->data.pointer = prop->data.pointer; if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { @@ -479,14 +460,12 @@ void IDP_AssignID(IDProperty *prop, ID *id, const int flag) */ static IDProperty *IDP_CopyGroup(const IDProperty *prop, const int flag) { - IDProperty *newp, *link; - BLI_assert(prop->type == IDP_GROUP); - newp = idp_generic_copy(prop, flag); + IDProperty *newp = idp_generic_copy(prop, flag); newp->len = prop->len; newp->subtype = prop->subtype; - for (link = prop->data.group.first; link; link = link->next) { + LISTBASE_FOREACH (IDProperty *, link, &prop->data.group) { BLI_addtail(&newp->data.group, IDP_CopyProperty_ex(link, flag)); } @@ -497,13 +476,11 @@ static IDProperty *IDP_CopyGroup(const IDProperty *prop, const int flag) * When values name and types match, copy the values, else ignore */ void IDP_SyncGroupValues(IDProperty *dest, const IDProperty *src) { - IDProperty *other, *prop; - BLI_assert(dest->type == IDP_GROUP); BLI_assert(src->type == IDP_GROUP); - for (prop = src->data.group.first; prop; prop = prop->next) { - other = BLI_findstring(&dest->data.group, prop->name, offsetof(IDProperty, name)); + LISTBASE_FOREACH (IDProperty *, prop, &src->data.group) { + IDProperty *other = BLI_findstring(&dest->data.group, prop->name, offsetof(IDProperty, name)); if (other && prop->type == other->type) { switch (prop->type) { case IDP_INT: @@ -526,12 +503,9 @@ void IDP_SyncGroupValues(IDProperty *dest, const IDProperty *src) void IDP_SyncGroupTypes(IDProperty *dest, const IDProperty *src, const bool do_arraylen) { - IDProperty *prop_dst, *prop_dst_next; - const IDProperty *prop_src; - - for (prop_dst = dest->data.group.first; prop_dst; prop_dst = prop_dst_next) { - prop_dst_next = prop_dst->next; - if ((prop_src = IDP_GetPropertyFromGroup((IDProperty *)src, prop_dst->name))) { + LISTBASE_FOREACH_MUTABLE (IDProperty *, prop_dst, &src->data.group) { + const IDProperty *prop_src = IDP_GetPropertyFromGroup((IDProperty *)src, prop_dst->name); + if (prop_src != NULL) { /* check of we should replace? */ if ((prop_dst->type != prop_src->type || prop_dst->subtype != prop_src->subtype) || (do_arraylen && ELEM(prop_dst->type, IDP_ARRAY, IDP_IDPARRAY) && @@ -554,12 +528,11 @@ void IDP_SyncGroupTypes(IDProperty *dest, const IDProperty *src, const bool do_a */ void IDP_ReplaceGroupInGroup(IDProperty *dest, const IDProperty *src) { - IDProperty *loop, *prop; - BLI_assert(dest->type == IDP_GROUP); BLI_assert(src->type == IDP_GROUP); - for (prop = src->data.group.first; prop; prop = prop->next) { + LISTBASE_FOREACH (IDProperty *, prop, &src->data.group) { + IDProperty *loop; for (loop = dest->data.group.first; loop; loop = loop->next) { if (STREQ(loop->name, prop->name)) { BLI_insertlinkreplace(&dest->data.group, loop, IDP_CopyProperty(prop)); @@ -612,13 +585,11 @@ void IDP_MergeGroup_ex(IDProperty *dest, const bool do_overwrite, const int flag) { - IDProperty *prop; - BLI_assert(dest->type == IDP_GROUP); BLI_assert(src->type == IDP_GROUP); if (do_overwrite) { - for (prop = src->data.group.first; prop; prop = prop->next) { + LISTBASE_FOREACH (IDProperty *, prop, &src->data.group) { if (prop->type == IDP_GROUP) { IDProperty *prop_exist = IDP_GetPropertyFromGroup(dest, prop->name); @@ -633,7 +604,7 @@ void IDP_MergeGroup_ex(IDProperty *dest, } } else { - for (prop = src->data.group.first; prop; prop = prop->next) { + LISTBASE_FOREACH (IDProperty *, prop, &src->data.group) { IDProperty *prop_exist = IDP_GetPropertyFromGroup(dest, prop->name); if (prop_exist != NULL) { if (prop->type == IDP_GROUP) { @@ -741,10 +712,9 @@ IDProperty *IDP_GetPropertyTypeFromGroup(const IDProperty *prop, const char *nam * direct data. */ static void IDP_FreeGroup(IDProperty *prop, const bool do_id_user) { - IDProperty *loop; - BLI_assert(prop->type == IDP_GROUP); - for (loop = prop->data.group.first; loop; loop = loop->next) { + + LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) { IDP_FreePropertyContent_ex(loop, do_id_user); } BLI_freelistN(&prop->data.group); @@ -863,14 +833,12 @@ bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is } return false; case IDP_GROUP: { - IDProperty *link1, *link2; - if (is_strict && prop1->len != prop2->len) { return false; } - for (link1 = prop1->data.group.first; link1; link1 = link1->next) { - link2 = IDP_GetPropertyFromGroup(prop2, link1->name); + LISTBASE_FOREACH (IDProperty *, link1, &prop1->data.group) { + IDProperty *link2 = IDP_GetPropertyFromGroup(prop2, link1->name); if (!IDP_EqualsProperties_ex(link1, link2, is_strict)) { return false; @@ -1158,11 +1126,10 @@ static void IDP_WriteIDPArray(const IDProperty *prop, BlendWriter *writer) /*REMEMBER to set totalen to len in the linking code!!*/ if (prop->data.pointer) { const IDProperty *array = prop->data.pointer; - int a; BLO_write_struct_array(writer, IDProperty, prop->len, array); - for (a = 0; a < prop->len; a++) { + for (int a = 0; a < prop->len; a++) { IDP_WriteProperty_OnlyData(&array[a], writer); } } @@ -1176,9 +1143,7 @@ static void IDP_WriteString(const IDProperty *prop, BlendWriter *writer) static void IDP_WriteGroup(const IDProperty *prop, BlendWriter *writer) { - IDProperty *loop; - - for (loop = prop->data.group.first; loop; loop = loop->next) { + LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) { IDP_BlendWrite(writer, loop); } } @@ -1212,13 +1177,11 @@ static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader); static void IDP_DirectLinkIDPArray(IDProperty *prop, BlendDataReader *reader) { - IDProperty *array; - /* since we didn't save the extra buffer, set totallen to len */ prop->totallen = prop->len; BLO_read_data_address(reader, &prop->data.pointer); - array = (IDProperty *)prop->data.pointer; + IDProperty *array = (IDProperty *)prop->data.pointer; /* note!, idp-arrays didn't exist in 2.4x, so the pointer will be cleared * there's not really anything we can do to correct this, at least don't crash */ @@ -1234,14 +1197,12 @@ static void IDP_DirectLinkIDPArray(IDProperty *prop, BlendDataReader *reader) static void IDP_DirectLinkArray(IDProperty *prop, BlendDataReader *reader) { - IDProperty **array; - /* since we didn't save the extra buffer, set totallen to len */ prop->totallen = prop->len; if (prop->subtype == IDP_GROUP) { BLO_read_pointer_array(reader, &prop->data.pointer); - array = prop->data.pointer; + IDProperty **array = prop->data.pointer; for (int i = 0; i < prop->len; i++) { IDP_DirectLinkProperty(array[i], reader); @@ -1266,12 +1227,11 @@ static void IDP_DirectLinkString(IDProperty *prop, BlendDataReader *reader) static void IDP_DirectLinkGroup(IDProperty *prop, BlendDataReader *reader) { ListBase *lb = &prop->data.group; - IDProperty *loop; BLO_read_list(reader, lb); /*Link child id properties now*/ - for (loop = prop->data.group.first; loop; loop = loop->next) { + LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) { IDP_DirectLinkProperty(loop, reader); } } diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index ab57d14d2cf..10f15519ea4 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -338,6 +338,8 @@ IDTypeInfo IDType_ID_IM = { .blend_read_expand = NULL, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; /* prototypes */ diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 1ab6e61e20e..d43a0cb3813 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -200,6 +200,8 @@ IDTypeInfo IDType_ID_IP = { .blend_read_expand = ipo_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; /* *************************************************** */ diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 433d64a5927..540337b84b3 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -223,6 +223,8 @@ IDTypeInfo IDType_ID_KE = { .blend_read_expand = shapekey_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; #define KEY_MODE_DUMMY 0 /* use where mode isn't checked for */ diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index 74f78106be5..3d3ade1a529 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -205,6 +205,8 @@ IDTypeInfo IDType_ID_LT = { .blend_read_expand = lattice_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; int BKE_lattice_index_from_uvw(Lattice *lt, const int u, const int v, const int w) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 13f76b46570..54c2f5f5565 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -112,6 +112,8 @@ IDTypeInfo IDType_ID_LINK_PLACEHOLDER = { .blend_read_expand = NULL, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; /* GS reads the memory pointed at in a specific ordering. @@ -1793,32 +1795,31 @@ static void library_make_local_copying_check(ID *id, return; /* Already checked, nothing else to do. */ } - MainIDRelationsEntry *entry = BLI_ghash_lookup(id_relations->id_used_to_user, id); + MainIDRelationsEntry *entry = BLI_ghash_lookup(id_relations->relations_from_pointers, id); BLI_gset_insert(loop_tags, id); - for (; entry != NULL; entry = entry->next) { - - /* Used_to_user stores ID pointer, not pointer to ID pointer. */ - ID *par_id = (ID *)entry->id_pointer; - + for (MainIDRelationsEntryItem *from_id_entry = entry->from_ids; from_id_entry != NULL; + from_id_entry = from_id_entry->next) { /* Our oh-so-beloved 'from' pointers... Those should always be ignored here, since the actual * relation we want to check is in the other way around. */ - if (entry->usage_flag & IDWALK_CB_LOOPBACK) { + if (from_id_entry->usage_flag & IDWALK_CB_LOOPBACK) { continue; } + ID *from_id = from_id_entry->id_pointer.from; + /* Shape-keys are considered 'private' to their owner ID here, and never tagged * (since they cannot be linked), so we have to switch effective parent to their owner. */ - if (GS(par_id->name) == ID_KE) { - par_id = ((Key *)par_id)->from; + if (GS(from_id->name) == ID_KE) { + from_id = ((Key *)from_id)->from; } - if (par_id->lib == NULL) { + if (from_id->lib == NULL) { /* Local user, early out to avoid some gset querying... */ continue; } - if (!BLI_gset_haskey(done_ids, par_id)) { - if (BLI_gset_haskey(loop_tags, par_id)) { + if (!BLI_gset_haskey(done_ids, from_id)) { + if (BLI_gset_haskey(loop_tags, from_id)) { /* We are in a 'dependency loop' of IDs, this does not say us anything, skip it. * Note that this is the situation that can lead to archipelagoes of linked data-blocks * (since all of them have non-local users, they would all be duplicated, @@ -1827,10 +1828,10 @@ static void library_make_local_copying_check(ID *id, continue; } /* Else, recursively check that user ID. */ - library_make_local_copying_check(par_id, loop_tags, id_relations, done_ids); + library_make_local_copying_check(from_id, loop_tags, id_relations, done_ids); } - if (par_id->tag & LIB_TAG_DOIT) { + if (from_id->tag & LIB_TAG_DOIT) { /* This user will be fully local in future, so far so good, * nothing to do here but check next user. */ } diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index e4094c48368..7df0e08d405 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -368,23 +368,50 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain) return success; } -static bool lib_override_hierarchy_recursive_tag(Main *bmain, - ID *id, - const uint tag, - const uint missing_tag, - Library *override_group_lib_reference) +typedef struct LibOverrideGroupTagData { + ID *id_root; + uint tag; + uint missing_tag; +} LibOverrideGroupTagData; + +static int lib_override_linked_group_tag_cb(LibraryIDLinkCallbackData *cb_data) { - void **entry_vp = BLI_ghash_lookup_p(bmain->relations->id_user_to_used, id); - if (entry_vp == NULL) { - /* Already processed. */ - return (id->tag & tag) != 0; + if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK)) { + return IDWALK_RET_STOP_RECURSION; + } + + LibOverrideGroupTagData *data = cb_data->user_data; + const uint tag = data->tag; + const uint missing_tag = data->missing_tag; + + ID *id_root = data->id_root; + Library *library_root = id_root->lib; + ID *id = *cb_data->id_pointer; + ID *id_owner = cb_data->id_owner; + + BLI_assert(id_owner == cb_data->id_self); + + if (ELEM(id, NULL, id_owner)) { + return IDWALK_RET_NOP; + } + + BLI_assert(id_owner->lib == library_root); + + if (*(uint *)&id->tag & (tag | missing_tag)) { + /* Already processed and tagged, nothing else to do here. */ + return IDWALK_RET_STOP_RECURSION; + } + + if (id->lib != library_root) { + /* We do not override data-blocks from other libraries, nor do we process them. */ + return IDWALK_RET_STOP_RECURSION; } - /* Note: in case some reference ID is missing from linked data (and therefore its override uses - * a placeholder as reference), use `missing_tag` instead of `tag` for this override. */ - if (override_group_lib_reference != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(id) && - id->override_library->reference->lib == override_group_lib_reference) { - if (id->override_library->reference->tag & LIB_TAG_MISSING) { + /* We tag all collections and objects for override. And we also tag all other data-blocks which + * would use one of those. + * Note: missing IDs (aka placeholders) are never overridden. */ + if (ELEM(GS(id->name), ID_OB, ID_GR)) { + if ((id->tag & LIB_TAG_MISSING)) { id->tag |= missing_tag; } else { @@ -392,94 +419,101 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, } } + return IDWALK_RET_NOP; +} + +/* Tag all IDs in dependency relationships within an override hierarchy/group. + * + * Note: this is typically called to complete `lib_override_linked_group_tag()`. + * Note: BMain's relations mapping won't be valid anymore after that call. + */ +static bool lib_override_hierarchy_dependencies_recursive_tag(Main *bmain, + ID *id, + const uint tag, + const uint missing_tag) +{ + MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); + BLI_assert(entry != NULL); + + if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) { + /* This ID has already been processed. */ + return (*(uint *)&id->tag & tag) != 0; + } /* This way we won't process again that ID, should we encounter it again through another - * relationship hierarchy. - * Note that this does not free any memory from relations, so we can still use the entries. - */ - BKE_main_relations_ID_remove(bmain, id); + * relationship hierarchy. */ + entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED; - for (MainIDRelationsEntry *entry = *entry_vp; entry != NULL; entry = entry->next) { - if ((entry->usage_flag & IDWALK_CB_LOOPBACK) != 0) { + for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL; + to_id_entry = to_id_entry->next) { + if ((to_id_entry->usage_flag & IDWALK_CB_LOOPBACK) != 0) { /* Never consider 'loop back' relationships ('from', 'parents', 'owner' etc. pointers) as * actual dependencies. */ continue; } /* We only consider IDs from the same library. */ - if (entry->id_pointer != NULL && (*entry->id_pointer)->lib == id->lib) { - if (lib_override_hierarchy_recursive_tag( - bmain, *entry->id_pointer, tag, missing_tag, override_group_lib_reference) && - override_group_lib_reference == NULL) { + ID *to_id = *to_id_entry->id_pointer.to; + if (to_id != NULL && to_id->lib == id->lib) { + if (lib_override_hierarchy_dependencies_recursive_tag(bmain, to_id, tag, missing_tag)) { id->tag |= tag; } } } - return (id->tag & tag) != 0; + return (*(uint *)&id->tag & tag) != 0; } -/** - * Tag all IDs in given \a bmain that are being used by given \a id_root ID or its dependencies, - * recursively. - * It detects and tag only chains of dependencies marked at both ends by given tag. +/* This will tag at least all 'boundary' linked IDs for a potential override group. * - * This will include all local IDs, and all IDs from the same library as the \a id_root. + * Note that you will then need to call #lib_override_hierarchy_dependencies_recursive_tag to + * complete tagging of all dependencies within the override group. * - * \param id_root: The root of the hierarchy of dependencies to be tagged. - * \param do_create_main_relashionships: Whether main relations needs to be created or already - * exist (in any case, they will be freed by this function). + * We currently only consider Collections and Objects (that are not used as bone shapes) as valid + * boundary IDs to define an override group. */ -void BKE_lib_override_library_dependencies_tag(Main *bmain, - ID *id_root, - const uint tag, - const bool do_create_main_relashionships) +static void lib_override_linked_group_tag(Main *bmain, + ID *id, + const uint tag, + const uint missing_tag) { - if (do_create_main_relashionships) { - BKE_main_relations_create(bmain, 0); - } - - /* We tag all intermediary data-blocks in-between two overridden ones (e.g. if a shape-key - * has a driver using an armature object's bone, we need to override the shape-key/obdata, - * the objects using them, etc.) */ - lib_override_hierarchy_recursive_tag(bmain, id_root, tag, 0, NULL); + BKE_main_relations_create(bmain, 0); - BKE_main_relations_free(bmain); -} + if (ELEM(GS(id->name), ID_OB, ID_GR)) { + LibOverrideGroupTagData data = {.id_root = id, .tag = tag, .missing_tag = missing_tag}; + /* Tag all collections and objects. */ + BKE_library_foreach_ID_link( + bmain, id, lib_override_linked_group_tag_cb, &data, IDWALK_READONLY | IDWALK_RECURSE); -/** - * Tag all IDs in given \a bmain that are part of the same \a id_root liboverride ID group. - * That is, all other liboverride IDs (in)directly used by \a is_root one, and sharing the same - * library for their reference IDs. - * - * \param id_root: The root of the hierarchy of liboverride dependencies to be tagged. - * \param do_create_main_relashionships: Whether main relations needs to be created or already - * exist (in any case, they will be freed by this function). - */ -void BKE_lib_override_library_override_group_tag(Main *bmain, - ID *id_root, - const uint tag, - const uint missing_tag, - const bool do_create_main_relashionships) -{ - if (do_create_main_relashionships) { - BKE_main_relations_create(bmain, 0); + /* Then, we remove (untag) bone shape objects, you shall never want to directly/explicitly + * override those. */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->type == OB_ARMATURE && ob->pose != NULL && (ob->id.tag & tag)) { + for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan != NULL; pchan = pchan->next) { + if (pchan->custom != NULL) { + pchan->custom->id.tag &= ~(tag | missing_tag); + } + } + } + } } - /* We tag all liboverride data-blocks from the same library as reference one, - * being used by the root ID. */ - lib_override_hierarchy_recursive_tag( - bmain, id_root, tag, missing_tag, id_root->override_library->reference->lib); + lib_override_hierarchy_dependencies_recursive_tag(bmain, id, tag, missing_tag); BKE_main_relations_free(bmain); } -static int lib_override_library_make_tag_ids_cb(LibraryIDLinkCallbackData *cb_data) +static int lib_override_local_group_tag_cb(LibraryIDLinkCallbackData *cb_data) { - if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK)) { + if (cb_data->cb_flag & + (IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { return IDWALK_RET_STOP_RECURSION; } - ID *id_root = cb_data->user_data; - Library *library_root = id_root->lib; + LibOverrideGroupTagData *data = cb_data->user_data; + const uint tag = data->tag; + const uint missing_tag = data->missing_tag; + + ID *id_root = data->id_root; + Library *library_reference_root = id_root->override_library->reference->lib; ID *id = *cb_data->id_pointer; ID *id_owner = cb_data->id_owner; @@ -489,62 +523,67 @@ static int lib_override_library_make_tag_ids_cb(LibraryIDLinkCallbackData *cb_da return IDWALK_RET_NOP; } - BLI_assert(id->lib != NULL); - BLI_assert(id_owner->lib == library_root); - - if (id->tag & LIB_TAG_DOIT) { + if (*(uint *)&id->tag & (tag | missing_tag)) { /* Already processed and tagged, nothing else to do here. */ return IDWALK_RET_STOP_RECURSION; } - if (id->lib != library_root) { + if (!ID_IS_OVERRIDE_LIBRARY(id) || ID_IS_LINKED(id)) { + /* Fully local, or linked ID, those are never part of a local override group. */ + return IDWALK_RET_STOP_RECURSION; + } + + /* NOTE: Since we rejected embedded data too at the beginning of this function, id should only be + * a real override now. + * + * However, our usual trouble maker, Key, is not considered as an embedded ID currently, yet it + * is never a real override either. Enjoy. */ + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + return IDWALK_RET_NOP; + } + + if (id->override_library->reference->lib != library_reference_root) { /* We do not override data-blocks from other libraries, nor do we process them. */ return IDWALK_RET_STOP_RECURSION; } - /* We tag all collections and objects for override. And we also tag all other data-blocks which - * would use one of those. - * Note: missing IDs (aka placeholders) are never overridden. */ - if (ELEM(GS(id->name), ID_OB, ID_GR) && !(id->tag & LIB_TAG_MISSING)) { - id->tag |= LIB_TAG_DOIT; + if (id->override_library->reference->tag & LIB_TAG_MISSING) { + id->tag |= missing_tag; + } + else { + id->tag |= tag; } return IDWALK_RET_NOP; } +/* This will tag at least all 'boundary' linked IDs for a potential override group. + * + * Note that you will then need to call #lib_override_hierarchy_dependencies_recursive_tag to + * complete tagging of all dependencies within the override group. + * + * We currently only consider Collections and Objects (that are not used as bone shapes) as valid + * boundary IDs to define an override group. + */ +static void lib_override_local_group_tag(Main *bmain, + ID *id, + const uint tag, + const uint missing_tag) +{ + LibOverrideGroupTagData data = {.id_root = id, .tag = tag, .missing_tag = missing_tag}; + /* Tag all local overrides in id_root's group. */ + BKE_library_foreach_ID_link( + bmain, id, lib_override_local_group_tag_cb, &data, IDWALK_READONLY | IDWALK_RECURSE); +} + static bool lib_override_library_create_do(Main *bmain, ID *id_root) { id_root->tag |= LIB_TAG_DOIT; BKE_main_relations_create(bmain, 0); - if (ELEM(GS(id_root->name), ID_OB, ID_GR)) { - /* Tag all collections and objects. */ - BKE_library_foreach_ID_link(bmain, - id_root, - lib_override_library_make_tag_ids_cb, - id_root, - IDWALK_READONLY | IDWALK_RECURSE); - - /* Then, we remove (untag) bone shape objects, you shall never want to override those - * (hopefully). */ - LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - if (ob->type == OB_ARMATURE && ob->pose != NULL && (ob->id.tag & LIB_TAG_DOIT)) { - for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan != NULL; pchan = pchan->next) { - if (pchan->custom != NULL) { - pchan->custom->id.tag &= ~LIB_TAG_DOIT; - } - } - } - } - } - - /* Now tag all non-object/collection IDs 'in-between' two tagged ones, as those are part of an - * override chain and therefore also need to be overridden. - * One very common cases are e.g. drivers on geometry or materials of an overridden object, that - * are using another overridden object as parameter. */ - /* Note that this call will also free the main relations data we created above. */ - BKE_lib_override_library_dependencies_tag(bmain, id_root, LIB_TAG_DOIT, false); + lib_override_linked_group_tag(bmain, id_root, LIB_TAG_DOIT, LIB_TAG_MISSING); + lib_override_hierarchy_dependencies_recursive_tag(bmain, id_root, LIB_TAG_DOIT, LIB_TAG_MISSING); return BKE_lib_override_library_create_from_tag(bmain); } @@ -737,14 +776,14 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_ { BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); - /* Tag all collections and objects, as well as other IDs using them. */ id_root->tag |= LIB_TAG_DOIT; ID *id_root_reference = id_root->override_library->reference; - /* Make a mapping 'linked reference IDs' -> 'Local override IDs' of existing overrides, and tag - * linked reference ones to be overridden again. */ - BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, LIB_TAG_MISSING, true); + lib_override_local_group_tag(bmain, id_root, LIB_TAG_DOIT, LIB_TAG_MISSING); + + lib_override_linked_group_tag(bmain, id_root_reference, LIB_TAG_DOIT, LIB_TAG_MISSING); + /* Make a mapping 'linked reference IDs' -> 'Local override IDs' of existing overrides. */ GHash *linkedref_to_old_override = BLI_ghash_new( BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); ID *id; @@ -755,7 +794,7 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_ * same linked ID in a same hierarchy. */ if (!BLI_ghash_haskey(linkedref_to_old_override, id->override_library->reference)) { BLI_ghash_insert(linkedref_to_old_override, id->override_library->reference, id); - id->override_library->reference->tag |= LIB_TAG_DOIT; + BLI_assert(id->override_library->reference->tag & LIB_TAG_DOIT); } } } @@ -765,7 +804,7 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_ /* Note that this call also remaps all pointers of tagged IDs from old override IDs to new * override IDs (including within the old overrides themselves, since those are tagged too * above). */ - const bool success = lib_override_library_create_do(bmain, id_root_reference); + const bool success = BKE_lib_override_library_create_from_tag(bmain); if (!success) { return success; @@ -828,6 +867,23 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_ RNA_id_pointer_create(id_override_old, &rnaptr_src); RNA_id_pointer_create(id_override_new, &rnaptr_dst); + /* We remove any operation tagged with `IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE`, + * that way the potentially new pointer will be properly kept, when old one is still valid + * too (typical case: assigning new ID to some usage, while old one remains used elsewhere + * in the override hierarchy). */ + LISTBASE_FOREACH_MUTABLE ( + IDOverrideLibraryProperty *, op, &id_override_new->override_library->properties) { + LISTBASE_FOREACH_MUTABLE (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { + if (opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) { + lib_override_library_property_operation_clear(opop); + BLI_freelinkN(&op->operations, opop); + } + } + if (BLI_listbase_is_empty(&op->operations)) { + BKE_lib_override_library_property_delete(id_override_new->override_library, op); + } + } + RNA_struct_override_apply( bmain, &rnaptr_dst, &rnaptr_src, NULL, id_override_new->override_library); } @@ -900,7 +956,7 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root) id_root->tag |= LIB_TAG_DOIT; /* Tag all library overrides in the chains of dependencies from the given root one. */ - BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, LIB_TAG_DOIT, true); + lib_override_local_group_tag(bmain, id_root, LIB_TAG_DOIT, LIB_TAG_DOIT); ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { @@ -1619,31 +1675,37 @@ static void lib_override_library_id_hierarchy_recursive_reset(Main *bmain, ID *i return; } - void **entry_pp = BLI_ghash_lookup(bmain->relations->id_user_to_used, id_root); - if (entry_pp == NULL) { - /* Already processed. */ + void **entry_vp = BLI_ghash_lookup_p(bmain->relations->relations_from_pointers, id_root); + if (entry_vp == NULL) { + /* This ID is not used by nor using any other ID. */ + lib_override_library_id_reset_do(bmain, id_root); + return; + } + + MainIDRelationsEntry *entry = *entry_vp; + if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) { + /* This ID has already been processed. */ return; } lib_override_library_id_reset_do(bmain, id_root); /* This way we won't process again that ID, should we encounter it again through another - * relationship hierarchy. - * Note that this does not free any memory from relations, so we can still use the entries. - */ - BKE_main_relations_ID_remove(bmain, id_root); + * relationship hierarchy. */ + entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED; - for (MainIDRelationsEntry *entry = *entry_pp; entry != NULL; entry = entry->next) { - if ((entry->usage_flag & IDWALK_CB_LOOPBACK) != 0) { + for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL; + to_id_entry = to_id_entry->next) { + if ((to_id_entry->usage_flag & IDWALK_CB_LOOPBACK) != 0) { /* Never consider 'loop back' relationships ('from', 'parents', 'owner' etc. pointers) as * actual dependencies. */ continue; } /* We only consider IDs from the same library. */ - if (entry->id_pointer != NULL) { - ID *id_entry = *entry->id_pointer; - if (id_entry->override_library != NULL) { - lib_override_library_id_hierarchy_recursive_reset(bmain, id_entry); + if (*to_id_entry->id_pointer.to != NULL) { + ID *to_id = *to_id_entry->id_pointer.to; + if (to_id->override_library != NULL) { + lib_override_library_id_hierarchy_recursive_reset(bmain, to_id); } } } @@ -1922,7 +1984,9 @@ ID *BKE_lib_override_library_operations_store_start(Main *bmain, ID *local) { if (ID_IS_OVERRIDE_LIBRARY_TEMPLATE(local) || ID_IS_OVERRIDE_LIBRARY_VIRTUAL(local)) { - /* This is actually purely local data with an override template, nothing to do here! */ + /* This is actually purely local data with an override template, or one of those embedded IDs + * (root node trees, master collections or shapekeys) that cannot have their own override. + * Nothing to do here! */ return NULL; } diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index e687e94073d..8be26fc8547 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -237,9 +237,12 @@ static void library_foreach_ID_link(Main *bmain, * but we might as well use it (Main->relations is always assumed valid, * it's responsibility of code creating it to free it, * especially if/when it starts modifying Main database). */ - MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->id_user_to_used, id); - for (; entry != NULL; entry = entry->next) { - BKE_lib_query_foreachid_process(&data, entry->id_pointer, entry->usage_flag); + MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, + id); + for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL; + to_id_entry = to_id_entry->next) { + BKE_lib_query_foreachid_process( + &data, to_id_entry->id_pointer.to, to_id_entry->usage_flag); } continue; } diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 6a560d51362..d2f1196d804 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -84,6 +84,8 @@ IDTypeInfo IDType_ID_LI = { .blend_read_expand = NULL, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath) diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c index a47a0c043ff..4a2afb7f5e6 100644 --- a/source/blender/blenkernel/intern/light.c +++ b/source/blender/blenkernel/intern/light.c @@ -209,6 +209,8 @@ IDTypeInfo IDType_ID_LA = { .blend_read_expand = light_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; Light *BKE_light_add(Main *bmain, const char *name) diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c index 0553c070fdf..4ef3b8c3237 100644 --- a/source/blender/blenkernel/intern/lightprobe.c +++ b/source/blender/blenkernel/intern/lightprobe.c @@ -107,6 +107,8 @@ IDTypeInfo IDType_ID_LP = { .blend_read_expand = NULL, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; void BKE_lightprobe_type_set(LightProbe *probe, const short lightprobe_type) diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index 8542959d4b0..283e2a94732 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -767,6 +767,8 @@ IDTypeInfo IDType_ID_LS = { .blend_read_expand = linestyle_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; static const char *modifier_name[LS_MODIFIER_NUM] = { diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index 4b577ccec2c..4a7636926e6 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -211,35 +211,51 @@ void BKE_main_unlock(struct Main *bmain) static int main_relations_create_idlink_cb(LibraryIDLinkCallbackData *cb_data) { - MainIDRelations *rel = cb_data->user_data; + MainIDRelations *bmain_relations = cb_data->user_data; ID *id_self = cb_data->id_self; ID **id_pointer = cb_data->id_pointer; const int cb_flag = cb_data->cb_flag; if (*id_pointer) { - MainIDRelationsEntry *entry, **entry_p; - - entry = BLI_mempool_alloc(rel->entry_pool); - if (BLI_ghash_ensure_p(rel->id_user_to_used, id_self, (void ***)&entry_p)) { - entry->next = *entry_p; - } - else { - entry->next = NULL; + MainIDRelationsEntry **entry_p; + + /* Add `id_pointer` as child of `id_self`. */ + { + if (!BLI_ghash_ensure_p( + bmain_relations->relations_from_pointers, id_self, (void ***)&entry_p)) { + *entry_p = MEM_callocN(sizeof(**entry_p), __func__); + (*entry_p)->session_uuid = id_self->session_uuid; + } + else { + BLI_assert((*entry_p)->session_uuid == id_self->session_uuid); + } + MainIDRelationsEntryItem *to_id_entry = BLI_mempool_alloc(bmain_relations->entry_items_pool); + to_id_entry->next = (*entry_p)->to_ids; + to_id_entry->id_pointer.to = id_pointer; + to_id_entry->session_uuid = (*id_pointer != NULL) ? (*id_pointer)->session_uuid : + MAIN_ID_SESSION_UUID_UNSET; + to_id_entry->usage_flag = cb_flag; + (*entry_p)->to_ids = to_id_entry; } - entry->id_pointer = id_pointer; - entry->usage_flag = cb_flag; - *entry_p = entry; - entry = BLI_mempool_alloc(rel->entry_pool); - if (BLI_ghash_ensure_p(rel->id_used_to_user, *id_pointer, (void ***)&entry_p)) { - entry->next = *entry_p; - } - else { - entry->next = NULL; + /* Add `id_self` as parent of `id_pointer`. */ + if (*id_pointer != NULL) { + if (!BLI_ghash_ensure_p( + bmain_relations->relations_from_pointers, *id_pointer, (void ***)&entry_p)) { + *entry_p = MEM_callocN(sizeof(**entry_p), __func__); + (*entry_p)->session_uuid = (*id_pointer)->session_uuid; + } + else { + BLI_assert((*entry_p)->session_uuid == (*id_pointer)->session_uuid); + } + MainIDRelationsEntryItem *from_id_entry = BLI_mempool_alloc( + bmain_relations->entry_items_pool); + from_id_entry->next = (*entry_p)->from_ids; + from_id_entry->id_pointer.from = id_self; + from_id_entry->session_uuid = id_self->session_uuid; + from_id_entry->usage_flag = cb_flag; + (*entry_p)->from_ids = from_id_entry; } - entry->id_pointer = (ID **)id_self; - entry->usage_flag = cb_flag; - *entry_p = entry; } return IDWALK_RET_NOP; @@ -253,64 +269,47 @@ void BKE_main_relations_create(Main *bmain, const short flag) } bmain->relations = MEM_mallocN(sizeof(*bmain->relations), __func__); - bmain->relations->id_used_to_user = BLI_ghash_new( - BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); - bmain->relations->id_user_to_used = BLI_ghash_new( + bmain->relations->relations_from_pointers = BLI_ghash_new( BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); - bmain->relations->entry_pool = BLI_mempool_create( - sizeof(MainIDRelationsEntry), 128, 128, BLI_MEMPOOL_NOP); + bmain->relations->entry_items_pool = BLI_mempool_create( + sizeof(MainIDRelationsEntryItem), 128, 128, BLI_MEMPOOL_NOP); + + bmain->relations->flag = flag; ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { const int idwalk_flag = IDWALK_READONLY | ((flag & MAINIDRELATIONS_INCLUDE_UI) != 0 ? IDWALK_INCLUDE_UI : 0); + + /* Ensure all IDs do have an entry, even if they are not connected to any other. */ + MainIDRelationsEntry **entry_p; + if (!BLI_ghash_ensure_p(bmain->relations->relations_from_pointers, id, (void ***)&entry_p)) { + *entry_p = MEM_callocN(sizeof(**entry_p), __func__); + (*entry_p)->session_uuid = id->session_uuid; + } + else { + BLI_assert((*entry_p)->session_uuid == id->session_uuid); + } + BKE_library_foreach_ID_link( NULL, id, main_relations_create_idlink_cb, bmain->relations, idwalk_flag); } FOREACH_MAIN_ID_END; - - bmain->relations->flag = flag; } void BKE_main_relations_free(Main *bmain) { - if (bmain->relations) { - if (bmain->relations->id_used_to_user) { - BLI_ghash_free(bmain->relations->id_used_to_user, NULL, NULL); - } - if (bmain->relations->id_user_to_used) { - BLI_ghash_free(bmain->relations->id_user_to_used, NULL, NULL); + if (bmain->relations != NULL) { + if (bmain->relations->relations_from_pointers != NULL) { + BLI_ghash_free(bmain->relations->relations_from_pointers, NULL, MEM_freeN); } - BLI_mempool_destroy(bmain->relations->entry_pool); + BLI_mempool_destroy(bmain->relations->entry_items_pool); MEM_freeN(bmain->relations); bmain->relations = NULL; } } /** - * Remove an ID from the relations (the two entries for that ID, not the ID from entries in other - * IDs' relationships). - * - * Does not free any allocated memory. - * Allows to use those relations as a way to mark an ID as already processed, without requiring any - * additional tagging or GSet. - * Obviously, relations should be freed after use then, since this will make them fully invalid. - */ -void BKE_main_relations_ID_remove(Main *bmain, ID *id) -{ - if (bmain->relations) { - /* Note: we do not free the entries from the mempool, those will be dealt with when finally - * freeing the whole relations. */ - if (bmain->relations->id_used_to_user) { - BLI_ghash_remove(bmain->relations->id_used_to_user, id, NULL, NULL); - } - if (bmain->relations->id_user_to_used) { - BLI_ghash_remove(bmain->relations->id_user_to_used, id, NULL, NULL); - } - } -} - -/** * Create a GSet storing all IDs present in given \a bmain, by their pointers. * * \param gset: If not NULL, given GSet will be extended with IDs from given \a bmain, diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index 04fec1e57c4..83d9449934c 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -270,6 +270,8 @@ IDTypeInfo IDType_ID_MSK = { .blend_read_expand = mask_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; static struct { diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index e892a3f4d53..70906065347 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -274,6 +274,8 @@ IDTypeInfo IDType_ID_MA = { .blend_read_expand = material_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; void BKE_gpencil_material_attr_init(Material *ma) diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index 65ec91c57cf..849c7ef57fb 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -204,6 +204,8 @@ IDTypeInfo IDType_ID_MB = { .blend_read_expand = metaball_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; /* Functions */ diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c index 051c7e56ef9..1550401cc9c 100644 --- a/source/blender/blenkernel/intern/mball_tessellate.c +++ b/source/blender/blenkernel/intern/mball_tessellate.c @@ -263,7 +263,7 @@ static void build_bvh_spatial(PROCESS *process, * BASED AT CODE (but mostly rewritten) : * C code from the article * "An Implicit Surface Polygonizer" - * by Jules Bloomenthal, jbloom@beauty.gmu.edu + * by Jules Bloomenthal <jbloom@beauty.gmu.edu> * in "Graphics Gems IV", Academic Press, 1994 * * Authored by Jules Bloomenthal, Xerox PARC. diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 950885d2114..02588bf144e 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -364,6 +364,8 @@ IDTypeInfo IDType_ID_ME = { .blend_read_expand = mesh_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; enum { diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index b872c91cc29..175caf85b49 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -362,6 +362,8 @@ IDTypeInfo IDType_ID_MC = { .blend_read_expand = NULL, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; /*********************** movieclip buffer loaders *************************/ diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index 441da8b134a..45ac20ef154 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -781,7 +781,7 @@ static DerivedMesh *subsurf_dm_create_local(Scene *scene, smd.levels = smd.renderLevels = lvl; smd.quality = 3; if (!is_plain_uv) { - smd.uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_CORNERS; + smd.uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES; } else { smd.uv_smooth = SUBSURF_UV_SMOOTH_NONE; diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 76885eadaae..247bfafc099 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -888,6 +888,8 @@ IDTypeInfo IDType_ID_NT = { .blend_read_expand = ntree_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType *ntype) @@ -2149,7 +2151,7 @@ void nodeRemSocketLinks(bNodeTree *ntree, bNodeSocket *sock) ntree->update |= NTREE_UPDATE_LINKS; } -bool nodeLinkIsHidden(bNodeLink *link) +bool nodeLinkIsHidden(const bNodeLink *link) { return nodeSocketIsHidden(link->fromsock) || nodeSocketIsHidden(link->tosock); } @@ -2200,7 +2202,7 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node) } } -void nodeToView(bNode *node, float x, float y, float *rx, float *ry) +void nodeToView(const bNode *node, float x, float y, float *rx, float *ry) { if (node->parent) { nodeToView(node->parent, x + node->locx, y + node->locy, rx, ry); @@ -2211,7 +2213,7 @@ void nodeToView(bNode *node, float x, float y, float *rx, float *ry) } } -void nodeFromView(bNode *node, float x, float y, float *rx, float *ry) +void nodeFromView(const bNode *node, float x, float y, float *rx, float *ry) { if (node->parent) { nodeFromView(node->parent, x, y, rx, ry); @@ -2224,10 +2226,10 @@ void nodeFromView(bNode *node, float x, float y, float *rx, float *ry) } } -bool nodeAttachNodeCheck(bNode *node, bNode *parent) +bool nodeAttachNodeCheck(const bNode *node, const bNode *parent) { - for (bNode *parent_recurse = node; parent_recurse; parent_recurse = parent_recurse->parent) { - if (parent_recurse == parent) { + for (const bNode *parent_iter = node; parent_iter; parent_iter = parent_iter->parent) { + if (parent_iter == parent) { return true; } } @@ -2317,8 +2319,6 @@ void nodePositionPropagate(bNode *node) bNodeTree *ntreeAddTree(Main *bmain, const char *name, const char *idname) { - bNodeTree *ntree; - /* trees are created as local trees for compositor, material or texture nodes, * node groups and other tree types are created as library data. */ @@ -2327,7 +2327,7 @@ bNodeTree *ntreeAddTree(Main *bmain, const char *name, const char *idname) if (is_embedded) { flag |= LIB_ID_CREATE_NO_MAIN; } - ntree = BKE_libblock_alloc(bmain, ID_NT, name, flag); + bNodeTree *ntree = BKE_libblock_alloc(bmain, ID_NT, name, flag); if (is_embedded) { ntree->id.flag |= LIB_EMBEDDED_DATA; } @@ -2362,7 +2362,7 @@ bNodeTree *ntreeCopyTree(Main *bmain, const bNodeTree *ntree) * using BKE_node_preview_init_tree to set up previews for a whole node tree in advance. * This should be left more to the individual node tree implementations. */ -int BKE_node_preview_used(bNode *node) +bool BKE_node_preview_used(const bNode *node) { /* XXX check for closed nodes? */ return (node->typeinfo->flag & NODE_PREVIEW) != 0; @@ -2959,9 +2959,9 @@ ID *BKE_node_tree_find_owner_ID(Main *bmain, struct bNodeTree *ntree) return NULL; } -bool ntreeNodeExists(bNodeTree *ntree, bNode *testnode) +bool ntreeNodeExists(const bNodeTree *ntree, const bNode *testnode) { - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + LISTBASE_FOREACH (const bNode *, node, &ntree->nodes) { if (node == testnode) { return true; } @@ -2969,9 +2969,9 @@ bool ntreeNodeExists(bNodeTree *ntree, bNode *testnode) return false; } -bool ntreeOutputExists(bNode *node, bNodeSocket *testsock) +bool ntreeOutputExists(const bNode *node, const bNodeSocket *testsock) { - LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { + LISTBASE_FOREACH (const bNodeSocket *, sock, &node->outputs) { if (sock == testsock) { return true; } @@ -3246,7 +3246,7 @@ static void ntree_interface_type_create(bNodeTree *ntree) } } -StructRNA *ntreeInterfaceTypeGet(bNodeTree *ntree, int create) +StructRNA *ntreeInterfaceTypeGet(bNodeTree *ntree, bool create) { if (ntree->interface_type) { /* strings are generated from base string + ID name, sizes are sufficient */ @@ -3333,7 +3333,7 @@ bool ntreeHasTree(const bNodeTree *ntree, const bNodeTree *lookup) return false; } -bNodeLink *nodeFindLink(bNodeTree *ntree, bNodeSocket *from, bNodeSocket *to) +bNodeLink *nodeFindLink(bNodeTree *ntree, const bNodeSocket *from, const bNodeSocket *to) { LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { if (link->fromsock == from && link->tosock == to) { @@ -3346,10 +3346,10 @@ bNodeLink *nodeFindLink(bNodeTree *ntree, bNodeSocket *from, bNodeSocket *to) return NULL; } -int nodeCountSocketLinks(bNodeTree *ntree, bNodeSocket *sock) +int nodeCountSocketLinks(const bNodeTree *ntree, const bNodeSocket *sock) { int tot = 0; - LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + LISTBASE_FOREACH (const bNodeLink *, link, &ntree->links) { if (link->fromsock == sock || link->tosock == sock) { tot++; } @@ -3515,7 +3515,7 @@ void nodeSetActive(bNodeTree *ntree, bNode *node) } } -int nodeSocketIsHidden(bNodeSocket *sock) +int nodeSocketIsHidden(const bNodeSocket *sock) { return ((sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) != 0); } @@ -3530,7 +3530,7 @@ void nodeSetSocketAvailability(bNodeSocket *sock, bool is_available) } } -int nodeSocketLinkLimit(struct bNodeSocket *sock) +int nodeSocketLinkLimit(const bNodeSocket *sock) { bNodeSocketType *stype = sock->typeinfo; if (stype != NULL && stype->use_link_limits_of_type) { @@ -3574,7 +3574,7 @@ typedef struct bNodeClipboard { static bNodeClipboard node_clipboard = {{NULL}}; -void BKE_node_clipboard_init(struct bNodeTree *ntree) +void BKE_node_clipboard_init(const struct bNodeTree *ntree) { node_clipboard.type = ntree->type; } @@ -3718,11 +3718,11 @@ static bNodeInstanceKey node_hash_int_str(bNodeInstanceKey hash, const char *str return hash; } -bNodeInstanceKey BKE_node_instance_key(bNodeInstanceKey parent_key, bNodeTree *ntree, bNode *node) +bNodeInstanceKey BKE_node_instance_key(bNodeInstanceKey parent_key, + const bNodeTree *ntree, + const bNode *node) { - bNodeInstanceKey key; - - key = node_hash_int_str(parent_key, ntree->id.name + 2); + bNodeInstanceKey key = node_hash_int_str(parent_key, ntree->id.name + 2); if (node) { key = node_hash_int_str(key, node->name); @@ -4749,6 +4749,8 @@ static void registerGeometryNodes(void) register_node_type_geo_attribute_color_ramp(); register_node_type_geo_point_rotate(); register_node_type_geo_align_rotation_to_vector(); + register_node_type_geo_sample_texture(); + register_node_type_geo_points_to_volume(); } static void registerFunctionNodes(void) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 155508e9caa..381823754e9 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1118,6 +1118,20 @@ static void object_blend_read_expand(BlendExpander *expander, ID *id) } } +static void object_lib_override_apply_post(ID *id_dst, ID *UNUSED(id_src)) +{ + Object *object = (Object *)id_dst; + + ListBase pidlist; + BKE_ptcache_ids_from_object(&pidlist, object, NULL, 0); + LISTBASE_FOREACH (PTCacheID *, pid, &pidlist) { + LISTBASE_FOREACH (PointCache *, point_cache, pid->ptcaches) { + point_cache->flag |= PTCACHE_FLAG_INFO_DIRTY; + } + } + BLI_freelistN(&pidlist); +} + IDTypeInfo IDType_ID_OB = { .id_code = ID_OB, .id_filter = FILTER_ID_OB, @@ -1141,6 +1155,8 @@ IDTypeInfo IDType_ID_OB = { .blend_read_expand = object_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = object_lib_override_apply_post, }; void BKE_object_workob_clear(Object *workob) @@ -1506,11 +1522,11 @@ bool BKE_object_copy_gpencil_modifier(struct Object *ob_dst, GpencilModifierData /** * Copy the whole stack of modifiers from one object into another. * - * \warning **Does not** clear modifier stack and related data (particle systems, softbody, + * \warning **Does not** clear modifier stack and related data (particle systems, soft-body, * etc.) in `ob_dst`, if needed calling code must do it. * - * @param do_copy_all If true, even modifiers that should not suport copying (like Hook one) will - * be duplicated. + * \param do_copy_all: If true, even modifiers that should not support copying (like Hook one) + * will be duplicated. */ bool BKE_object_modifier_stack_copy(Object *ob_dst, const Object *ob_src, @@ -3527,9 +3543,6 @@ static void solve_parenting( } } -/** - * \note scene is the active scene while actual_scene is the scene the object resides in. - */ static void object_where_is_calc_ex(Depsgraph *depsgraph, Scene *scene, Object *ob, diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 1d79f871fa2..69442b7646c 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -225,7 +225,7 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o case OB_GPENCIL: { BKE_gpencil_prepare_eval_data(depsgraph, scene, ob); BKE_gpencil_modifiers_calc(depsgraph, scene, ob); - BKE_gpencil_update_layer_parent(depsgraph, ob); + BKE_gpencil_update_layer_transforms(depsgraph, ob); break; } case OB_HAIR: diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 1d62a1cce2a..d2f4d0702ed 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -140,7 +140,7 @@ static void compute_eigenstuff(struct OceanResult *ocr, float jxx, float jzz, fl * instead of Complex.h * in fftw.h "fftw_complex" typedefed as double[2] * below you can see functions are needed to work with such complex numbers. - * */ + */ static void init_complex(fftw_complex cmpl, float real, float image) { cmpl[0] = real; diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 4eecf3a3a87..92dea0b4601 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -158,6 +158,8 @@ IDTypeInfo IDType_ID_PAL = { .blend_read_expand = NULL, .blend_read_undo_preserve = palette_undo_preserve, + + .lib_override_apply_post = NULL, }; static void paint_curve_copy_data(Main *UNUSED(bmain), @@ -221,6 +223,8 @@ IDTypeInfo IDType_ID_PC = { .blend_read_expand = NULL, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; const char PAINT_CURSOR_SCULPT[3] = {255, 100, 100}; diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 335913c9b8e..ec3b6198d7c 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -511,6 +511,8 @@ IDTypeInfo IDType_ID_PA = { .blend_read_expand = particle_settings_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; unsigned int PSYS_FRAND_SEED_OFFSET[PSYS_FRAND_COUNT]; diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index 7bd14e80333..b9ff2a1179d 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -190,6 +190,8 @@ IDTypeInfo IDType_ID_PT = { /* blend_read_expand */ pointcloud_blend_read_expand, /* blend_read_undo_preserve */ nullptr, + + /* lib_override_apply_post */ nullptr, }; static void pointcloud_random(PointCloud *pointcloud) diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 11cdf67cb82..239e2dc47a0 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -1687,6 +1687,19 @@ static void scene_undo_preserve(BlendLibReader *reader, ID *id_new, ID *id_old) } } +static void scene_lib_override_apply_post(ID *id_dst, ID *UNUSED(id_src)) +{ + Scene *scene = (Scene *)id_dst; + + if (scene->rigidbody_world != NULL) { + PTCacheID pid; + BKE_ptcache_id_from_rigidbody(&pid, NULL, scene->rigidbody_world); + LISTBASE_FOREACH (PointCache *, point_cache, pid.ptcaches) { + point_cache->flag |= PTCACHE_FLAG_INFO_DIRTY; + } + } +} + IDTypeInfo IDType_ID_SCE = { .id_code = ID_SCE, .id_filter = FILTER_ID_SCE, @@ -1712,6 +1725,8 @@ IDTypeInfo IDType_ID_SCE = { .blend_read_expand = scene_blend_read_expand, .blend_read_undo_preserve = scene_undo_preserve, + + .lib_override_apply_post = scene_lib_override_apply_post, }; const char *RE_engine_id_BLENDER_EEVEE = "BLENDER_EEVEE"; diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 52c41c9fd05..80a83aecea8 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -315,6 +315,8 @@ IDTypeInfo IDType_ID_SCR = { .blend_read_expand = NULL, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; /* ************ Spacetype/regiontype handling ************** */ @@ -1599,8 +1601,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) BLO_read_list(reader, &snode->treepath); snode->edittree = NULL; - snode->iofsd = NULL; - BLI_listbase_clear(&snode->linkdrag); + snode->runtime = NULL; } else if (sl->spacetype == SPACE_TEXT) { SpaceText *st = (SpaceText *)sl; diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 14e6ce63023..4e0a08455e6 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -173,6 +173,8 @@ IDTypeInfo IDType_ID_SIM = { /* blend_read_expand */ simulation_blend_read_expand, /* blend_read_undo_preserve */ nullptr, + + /* lib_override_apply_post */ nullptr, }; void *BKE_simulation_add(Main *bmain, const char *name) diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index 78729fb9261..0c917434bd1 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -221,6 +221,8 @@ IDTypeInfo IDType_ID_SO = { .blend_read_expand = sound_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; #ifdef WITH_AUDASPACE diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c index fabf0bb8971..9caeaf05e6a 100644 --- a/source/blender/blenkernel/intern/speaker.c +++ b/source/blender/blenkernel/intern/speaker.c @@ -114,6 +114,8 @@ IDTypeInfo IDType_ID_SPK = { .blend_read_expand = speaker_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; void *BKE_speaker_add(Main *bmain, const char *name) diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 9ef2e818293..9bf215515f7 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -258,6 +258,8 @@ IDTypeInfo IDType_ID_TXT = { .blend_read_expand = NULL, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; /** \} */ diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 4c2e4a82acb..ce84bfcd95a 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -226,6 +226,8 @@ IDTypeInfo IDType_ID_TE = { .blend_read_expand = texture_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; /* Utils for all IDs using those texture slots. */ diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index e5f9d59270e..d124922acd1 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -587,15 +587,15 @@ MovieTrackingTrack *BKE_tracking_track_add(MovieTracking *tracking, { const MovieTrackingSettings *settings = &tracking->settings; + MovieTrackingTrack *track = BKE_tracking_track_add_empty(tracking, tracksbase); + MovieTrackingMarker marker; + const float half_pattern_px = settings->default_pattern_size / 2.0f; const float half_search_px = settings->default_search_size / 2.0f; const float pattern_size[2] = {half_pattern_px / width, half_pattern_px / height}; const float search_size[2] = {half_search_px / width, half_search_px / height}; - MovieTrackingTrack *track = BKE_tracking_track_add_empty(tracking, tracksbase); - - MovieTrackingMarker marker; memset(&marker, 0, sizeof(marker)); marker.pos[0] = x; marker.pos[1] = y; @@ -665,6 +665,86 @@ void BKE_tracking_track_free(MovieTrackingTrack *track) } } +/* Get frame numbers of the very first and last markers. + * There is no check on whether the marker is enabled or not. */ +void BKE_tracking_track_first_last_frame_get(const MovieTrackingTrack *track, + int *r_first_frame, + int *r_last_frame) +{ + BLI_assert(track->markersnr > 0); + const int last_marker_index = track->markersnr - 1; + *r_first_frame = track->markers[0].framenr; + *r_last_frame = track->markers[last_marker_index].framenr; +} + +/* Find the minimum starting frame and maximum ending frame within given set of + * tracks. + */ +void BKE_tracking_tracks_first_last_frame_minmax(/*const*/ MovieTrackingTrack **tracks, + const int num_tracks, + int *r_first_frame, + int *r_last_frame) +{ + *r_first_frame = INT_MAX; + *r_last_frame = INT_MIN; + for (int i = 0; i < num_tracks; ++i) { + const struct MovieTrackingTrack *track = tracks[i]; + int track_first_frame, track_last_frame; + BKE_tracking_track_first_last_frame_get(track, &track_first_frame, &track_last_frame); + *r_first_frame = min_ii(*r_first_frame, track_first_frame); + *r_last_frame = max_ii(*r_last_frame, track_last_frame); + } +} + +int BKE_tracking_count_selected_tracks_in_list(const ListBase *tracks_list) +{ + int num_selected_tracks = 0; + LISTBASE_FOREACH (const MovieTrackingTrack *, track, tracks_list) { + if (TRACK_SELECTED(track)) { + ++num_selected_tracks; + } + } + return num_selected_tracks; +} + +int BKE_tracking_count_selected_tracks_in_active_object(/*const*/ MovieTracking *tracking) +{ + ListBase *tracks_list = BKE_tracking_get_active_tracks(tracking); + return BKE_tracking_count_selected_tracks_in_list(tracks_list); +} + +MovieTrackingTrack **BKE_tracking_selected_tracks_in_active_object(MovieTracking *tracking, + int *r_num_tracks) +{ + *r_num_tracks = 0; + + ListBase *tracks_list = BKE_tracking_get_active_tracks(tracking); + if (tracks_list == NULL) { + return NULL; + } + + /* Initialize input. */ + const int num_selected_tracks = BKE_tracking_count_selected_tracks_in_active_object(tracking); + if (num_selected_tracks == 0) { + return NULL; + } + + MovieTrackingTrack **source_tracks = MEM_malloc_arrayN( + num_selected_tracks, sizeof(MovieTrackingTrack *), "selected tracks array"); + int source_track_index = 0; + LISTBASE_FOREACH (MovieTrackingTrack *, track, tracks_list) { + if (!TRACK_SELECTED(track)) { + continue; + } + source_tracks[source_track_index] = track; + ++source_track_index; + } + + *r_num_tracks = num_selected_tracks; + + return source_tracks; +} + /* Set flag for all specified track's areas. * * area - which part of marker should be selected. see TRACK_AREA_* constants. @@ -918,6 +998,124 @@ void BKE_tracking_tracks_join(MovieTracking *tracking, BKE_tracking_dopesheet_tag_update(tracking); } +static void accumulate_marker(MovieTrackingMarker *dst_marker, + const MovieTrackingMarker *src_marker) +{ + BLI_assert(dst_marker->framenr == src_marker->framenr); + + if (src_marker->flag & MARKER_DISABLED) { + return; + } + + add_v2_v2(dst_marker->pos, src_marker->pos); + for (int corner = 0; corner < 4; ++corner) { + add_v2_v2(dst_marker->pattern_corners[corner], src_marker->pattern_corners[corner]); + } + add_v2_v2(dst_marker->search_min, src_marker->search_min); + add_v2_v2(dst_marker->search_max, src_marker->search_max); + + BLI_assert(is_finite_v2(src_marker->search_min)); + BLI_assert(is_finite_v2(src_marker->search_max)); + + dst_marker->flag &= ~MARKER_DISABLED; + if ((src_marker->flag & MARKER_TRACKED) == 0) { + dst_marker->flag &= ~MARKER_TRACKED; + } +} + +static void multiply_marker(MovieTrackingMarker *marker, const float multiplier) +{ + mul_v2_fl(marker->pos, multiplier); + for (int corner = 0; corner < 4; ++corner) { + mul_v2_fl(marker->pattern_corners[corner], multiplier); + } + mul_v2_fl(marker->search_min, multiplier); + mul_v2_fl(marker->search_max, multiplier); +} + +/* Helper function for BKE_tracking_tracks_average which takes care of averaging fields of + * markers (position, patterns, ...). */ +static void tracking_average_markers(MovieTrackingTrack *dst_track, + /*const*/ MovieTrackingTrack **src_tracks, + const int num_src_tracks) +{ + /* Get global range of frames within which averaging would happen. */ + int first_frame, last_frame; + BKE_tracking_tracks_first_last_frame_minmax( + src_tracks, num_src_tracks, &first_frame, &last_frame); + if (last_frame < first_frame) { + return; + } + const int num_frames = last_frame - first_frame + 1; + + /* Allocate temporary array where averaging will happen into. */ + MovieTrackingMarker *accumulator = MEM_calloc_arrayN( + num_frames, sizeof(MovieTrackingMarker), "tracks average accumulator"); + int *counters = MEM_calloc_arrayN(num_frames, sizeof(int), "tracks accumulator counters"); + for (int frame = first_frame; frame <= last_frame; ++frame) { + const int frame_index = frame - first_frame; + accumulator[frame_index].framenr = frame; + accumulator[frame_index].flag |= (MARKER_DISABLED | MARKER_TRACKED); + } + + /* Accumulate track markers. */ + for (int track_index = 0; track_index < num_src_tracks; ++track_index) { + /*const*/ MovieTrackingTrack *track = src_tracks[track_index]; + for (int frame = first_frame; frame <= last_frame; ++frame) { + MovieTrackingMarker interpolated_marker; + if (!BKE_tracking_marker_get_interpolated(track, frame, &interpolated_marker)) { + continue; + } + const int frame_index = frame - first_frame; + accumulate_marker(&accumulator[frame_index], &interpolated_marker); + ++counters[frame_index]; + } + } + + /* Average and store the result. */ + for (int frame = first_frame; frame <= last_frame; ++frame) { + /* Average. */ + const int frame_index = frame - first_frame; + if (!counters[frame_index]) { + continue; + } + const float multiplier = 1.0f / (float)counters[frame_index]; + multiply_marker(&accumulator[frame_index], multiplier); + /* Store the result. */ + BKE_tracking_marker_insert(dst_track, &accumulator[frame_index]); + } + + /* Free memory. */ + MEM_freeN(accumulator); + MEM_freeN(counters); +} + +/* Helper function for BKE_tracking_tracks_average which takes care of averaging fields of + * tracks (track for example, offset). */ +static void tracking_average_tracks(MovieTrackingTrack *dst_track, + /*const*/ MovieTrackingTrack **src_tracks, + const int num_src_tracks) +{ + /* TODO(sergey): Consider averaging weight, stabilization weight, maybe even bundle position. */ + zero_v2(dst_track->offset); + for (int track_index = 0; track_index < num_src_tracks; track_index++) { + add_v2_v2(dst_track->offset, src_tracks[track_index]->offset); + } + mul_v2_fl(dst_track->offset, 1.0f / num_src_tracks); +} + +void BKE_tracking_tracks_average(MovieTrackingTrack *dst_track, + /*const*/ MovieTrackingTrack **src_tracks, + const int num_src_tracks) +{ + if (num_src_tracks == 0) { + return; + } + + tracking_average_markers(dst_track, src_tracks, num_src_tracks); + tracking_average_tracks(dst_track, src_tracks, num_src_tracks); +} + MovieTrackingTrack *BKE_tracking_track_get_named(MovieTracking *tracking, MovieTrackingObject *object, const char *name) @@ -1224,8 +1422,6 @@ MovieTrackingMarker *BKE_tracking_marker_insert(MovieTrackingTrack *track, /* put new marker */ track->markers[a + 1] = *marker; - track->last_marker = a + 1; - return &track->markers[a + 1]; } @@ -1314,51 +1510,47 @@ void BKE_tracking_marker_clamp(MovieTrackingMarker *marker, int event) } } +/** + * Get marker closest to the given frame number. + * + * If there is maker with exact frame number it returned. + * Otherwise, marker with highest frame number but lower than the requested + * frame is returned if such marker exists. Otherwise, the marker with lowest + * frame number greater than the requested frame number is returned. + * + * This function has complexity of `O(log number_of_markers)`. + */ MovieTrackingMarker *BKE_tracking_marker_get(MovieTrackingTrack *track, int framenr) { - int a = track->markersnr - 1; + const int num_markers = track->markersnr; - if (!track->markersnr) { + if (num_markers == 0) { + BLI_assert(!"Detected degenerated track, should never happen."); return NULL; } - /* approximate pre-first framenr marker with first marker */ - if (framenr < track->markers[0].framenr) { - return &track->markers[0]; - } + int left_boundary = 0; + int right_boundary = num_markers; + while (left_boundary < right_boundary) { + const int median_index = (left_boundary + right_boundary) / 2; + MovieTrackingMarker *marker = &track->markers[median_index]; - if (track->last_marker < track->markersnr) { - a = track->last_marker; - } - - if (track->markers[a].framenr <= framenr) { - while (a < track->markersnr && track->markers[a].framenr <= framenr) { - if (track->markers[a].framenr == framenr) { - track->last_marker = a; - - return &track->markers[a]; - } - a++; + if (marker->framenr == framenr) { + return marker; } - /* if there's no marker for exact position, use nearest marker from left side */ - return &track->markers[a - 1]; - } - - while (a >= 0 && track->markers[a].framenr >= framenr) { - if (track->markers[a].framenr == framenr) { - track->last_marker = a; - - return &track->markers[a]; + if (marker->framenr < framenr) { + left_boundary = median_index + 1; + } + else { + BLI_assert(marker->framenr > framenr); + right_boundary = median_index - 1; } - - a--; } - /* if there's no marker for exact position, use nearest marker from left side */ - return &track->markers[a]; + const int closest_index = clamp_i(right_boundary, 0, num_markers - 1); - return NULL; + return &track->markers[closest_index]; } MovieTrackingMarker *BKE_tracking_marker_get_exact(MovieTrackingTrack *track, int framenr) @@ -1389,6 +1581,84 @@ MovieTrackingMarker *BKE_tracking_marker_ensure(MovieTrackingTrack *track, int f return marker; } +static const MovieTrackingMarker *get_usable_marker_for_interpolation( + struct MovieTrackingTrack *track, + const MovieTrackingMarker *anchor_marker, + const int direction) +{ + BLI_assert(direction == -1 || direction == 1); + + const MovieTrackingMarker *last_marker = track->markers + track->markersnr - 1; + const MovieTrackingMarker *current_marker = anchor_marker; + + while (current_marker >= track->markers && current_marker <= last_marker) { + if ((current_marker->flag & MARKER_DISABLED) == 0) { + return current_marker; + } + current_marker += direction; + } + + return NULL; +} + +bool BKE_tracking_marker_get_interpolated(struct MovieTrackingTrack *track, + const int framenr, + struct MovieTrackingMarker *r_marker) +{ + const MovieTrackingMarker *closest_marker = BKE_tracking_marker_get(track, framenr); + if (closest_marker == NULL) { + return false; + } + if (closest_marker->framenr == framenr && (closest_marker->flag & MARKER_DISABLED) == 0) { + *r_marker = *closest_marker; + return true; + } + + const MovieTrackingMarker *left_marker = get_usable_marker_for_interpolation( + track, closest_marker, -1); + if (left_marker == NULL) { + return false; + } + + const MovieTrackingMarker *right_marker = get_usable_marker_for_interpolation( + track, closest_marker + 1, 1); + if (right_marker == NULL) { + return false; + } + + if (left_marker == right_marker) { + *r_marker = *left_marker; + return true; + } + + const float factor = (float)(framenr - left_marker->framenr) / + (right_marker->framenr - left_marker->framenr); + + interp_v2_v2v2(r_marker->pos, left_marker->pos, right_marker->pos, factor); + + for (int i = 0; i < 4; i++) { + interp_v2_v2v2(r_marker->pattern_corners[i], + left_marker->pattern_corners[i], + right_marker->pattern_corners[i], + factor); + } + + interp_v2_v2v2(r_marker->search_min, left_marker->search_min, right_marker->search_min, factor); + interp_v2_v2v2(r_marker->search_max, left_marker->search_max, right_marker->search_max, factor); + + r_marker->framenr = framenr; + r_marker->flag = 0; + + if (framenr == left_marker->framenr) { + r_marker->flag = left_marker->flag; + } + else if (framenr == right_marker->framenr) { + r_marker->flag = right_marker->flag; + } + + return true; +} + void BKE_tracking_marker_pattern_minmax(const MovieTrackingMarker *marker, float min[2], float max[2]) @@ -1683,7 +1953,6 @@ MovieTrackingPlaneMarker *BKE_tracking_plane_marker_insert(MovieTrackingPlaneTra /* Put new marker to an array. */ plane_track->markers[a + 1] = *plane_marker; - plane_track->last_marker = a + 1; return &plane_track->markers[a + 1]; } diff --git a/source/blender/blenkernel/intern/tracking_stabilize.c b/source/blender/blenkernel/intern/tracking_stabilize.c index 6f58416924f..46589a578a8 100644 --- a/source/blender/blenkernel/intern/tracking_stabilize.c +++ b/source/blender/blenkernel/intern/tracking_stabilize.c @@ -311,11 +311,7 @@ static void retrieve_next_lower_usable_frame( * translation stabilization, which has an enabled tracking marker at this very * frame. We search both for the next lower and next higher position, to allow * the caller to interpolate gaps and to extrapolate at the ends of the - * definition range. - * - * NOTE: Regarding performance note that the individual tracks will cache the - * last search position. - */ + * definition range. */ static void find_next_working_frames(StabContext *ctx, int framenr, int *next_lower, diff --git a/source/blender/blenkernel/intern/tracking_test.cc b/source/blender/blenkernel/intern/tracking_test.cc index 6afcf6872eb..2877d8db358 100644 --- a/source/blender/blenkernel/intern/tracking_test.cc +++ b/source/blender/blenkernel/intern/tracking_test.cc @@ -5,15 +5,23 @@ #include "DNA_tracking_types.h" #include "BKE_tracking.h" +#include "BLI_float2.hh" + +namespace blender { namespace { class TrackingTest : public ::testing::Test { protected: - MovieTrackingMarker *addMarkerToTrack(MovieTrackingTrack *track, int frame_number) + MovieTrackingMarker *addMarkerToTrack(MovieTrackingTrack *track, + int frame_number, + const float2 &position = float2(0.0f, 0.0f), + int flag = 0) { MovieTrackingMarker marker = {{0.0f}}; + copy_v2_v2(marker.pos, position); marker.framenr = frame_number; + marker.flag = flag; return BKE_tracking_marker_insert(track, &marker); } }; @@ -22,24 +30,58 @@ class TrackingTest : public ::testing::Test { TEST_F(TrackingTest, BKE_tracking_marker_get) { - MovieTrackingTrack track = {nullptr}; + { + MovieTrackingTrack track = {nullptr}; - addMarkerToTrack(&track, 1); - addMarkerToTrack(&track, 10); + addMarkerToTrack(&track, 10); - { - const MovieTrackingMarker *marker = BKE_tracking_marker_get(&track, 1); - EXPECT_NE(marker, nullptr); - EXPECT_EQ(marker->framenr, 1); + EXPECT_EQ(BKE_tracking_marker_get(&track, 0), &track.markers[0]); + EXPECT_EQ(BKE_tracking_marker_get(&track, 10), &track.markers[0]); + EXPECT_EQ(BKE_tracking_marker_get(&track, 20), &track.markers[0]); + + BKE_tracking_track_free(&track); } { - const MovieTrackingMarker *marker = BKE_tracking_marker_get(&track, 5); - EXPECT_NE(marker, nullptr); - EXPECT_EQ(marker->framenr, 1); + MovieTrackingTrack track = {nullptr}; + + addMarkerToTrack(&track, 1); + addMarkerToTrack(&track, 10); + + { + const MovieTrackingMarker *marker = BKE_tracking_marker_get(&track, 1); + EXPECT_NE(marker, nullptr); + EXPECT_EQ(marker->framenr, 1); + } + + { + const MovieTrackingMarker *marker = BKE_tracking_marker_get(&track, 5); + EXPECT_NE(marker, nullptr); + EXPECT_EQ(marker->framenr, 1); + } + + BKE_tracking_track_free(&track); } - BKE_tracking_track_free(&track); + { + { + MovieTrackingTrack track = {nullptr}; + + addMarkerToTrack(&track, 1); + addMarkerToTrack(&track, 2); + addMarkerToTrack(&track, 10); + + EXPECT_EQ(BKE_tracking_marker_get(&track, 0), &track.markers[0]); + EXPECT_EQ(BKE_tracking_marker_get(&track, 1), &track.markers[0]); + EXPECT_EQ(BKE_tracking_marker_get(&track, 2), &track.markers[1]); + EXPECT_EQ(BKE_tracking_marker_get(&track, 3), &track.markers[1]); + EXPECT_EQ(BKE_tracking_marker_get(&track, 9), &track.markers[1]); + EXPECT_EQ(BKE_tracking_marker_get(&track, 10), &track.markers[2]); + EXPECT_EQ(BKE_tracking_marker_get(&track, 11), &track.markers[2]); + + BKE_tracking_track_free(&track); + } + } } TEST_F(TrackingTest, BKE_tracking_marker_get_exact) @@ -62,3 +104,107 @@ TEST_F(TrackingTest, BKE_tracking_marker_get_exact) BKE_tracking_track_free(&track); } + +TEST_F(TrackingTest, BKE_tracking_marker_get_interpolated) +{ + /* Simple case, no disabled markers in a way. */ + { + MovieTrackingTrack track = {nullptr}; + + addMarkerToTrack(&track, 1, float2(1.0f, 5.0f)); + addMarkerToTrack(&track, 10, float2(2.0f, 1.0f)); + + { + MovieTrackingMarker interpolated_marker; + EXPECT_TRUE(BKE_tracking_marker_get_interpolated(&track, 1, &interpolated_marker)); + EXPECT_EQ(interpolated_marker.framenr, 1); + EXPECT_V2_NEAR(interpolated_marker.pos, float2(1.0f, 5.0f), 1e-6f); + } + + { + MovieTrackingMarker interpolated_marker; + EXPECT_TRUE(BKE_tracking_marker_get_interpolated(&track, 10, &interpolated_marker)); + EXPECT_EQ(interpolated_marker.framenr, 10); + EXPECT_V2_NEAR(interpolated_marker.pos, float2(2.0f, 1.0f), 1e-6f); + } + + { + MovieTrackingMarker interpolated_marker; + EXPECT_TRUE(BKE_tracking_marker_get_interpolated(&track, 4, &interpolated_marker)); + EXPECT_EQ(interpolated_marker.framenr, 4); + EXPECT_V2_NEAR(interpolated_marker.pos, float2(1.3333333f, 3.6666666f), 1e-6f); + } + + BKE_tracking_track_free(&track); + } + + /* More comprehensive test, which resembles real life trackign scenario better. */ + { + MovieTrackingTrack track = {nullptr}; + + addMarkerToTrack(&track, 1, float2(1.0f, 5.0f)); + addMarkerToTrack(&track, 2, float2(0.0f, 0.0f), MARKER_DISABLED); + addMarkerToTrack(&track, 9, float2(0.0f, 0.0f), MARKER_DISABLED); + addMarkerToTrack(&track, 10, float2(2.0f, 1.0f)); + + { + MovieTrackingMarker interpolated_marker; + EXPECT_TRUE(BKE_tracking_marker_get_interpolated(&track, 1, &interpolated_marker)); + EXPECT_EQ(interpolated_marker.framenr, 1); + EXPECT_V2_NEAR(interpolated_marker.pos, float2(1.0f, 5.0f), 1e-6f); + } + + { + MovieTrackingMarker interpolated_marker; + EXPECT_TRUE(BKE_tracking_marker_get_interpolated(&track, 10, &interpolated_marker)); + EXPECT_EQ(interpolated_marker.framenr, 10); + EXPECT_V2_NEAR(interpolated_marker.pos, float2(2.0f, 1.0f), 1e-6f); + } + + { + MovieTrackingMarker interpolated_marker; + EXPECT_TRUE(BKE_tracking_marker_get_interpolated(&track, 4, &interpolated_marker)); + EXPECT_EQ(interpolated_marker.framenr, 4); + EXPECT_V2_NEAR(interpolated_marker.pos, float2(1.3333333f, 3.6666666f), 1e-6f); + } + + { + MovieTrackingMarker interpolated_marker; + EXPECT_TRUE(BKE_tracking_marker_get_interpolated(&track, 9, &interpolated_marker)); + EXPECT_EQ(interpolated_marker.framenr, 9); + EXPECT_V2_NEAR(interpolated_marker.pos, float2(1.888888f, 1.4444444f), 1e-6f); + } + + BKE_tracking_track_free(&track); + } + + /* Tracked/keyframed flag check. */ + { + MovieTrackingTrack track = {nullptr}; + + addMarkerToTrack(&track, 1, float2(1.0f, 5.0f), MARKER_TRACKED); + addMarkerToTrack(&track, 10, float2(2.0f, 1.0f), MARKER_TRACKED); + + { + MovieTrackingMarker interpolated_marker; + EXPECT_TRUE(BKE_tracking_marker_get_interpolated(&track, 1, &interpolated_marker)); + EXPECT_EQ(interpolated_marker.flag, MARKER_TRACKED); + } + + { + MovieTrackingMarker interpolated_marker; + EXPECT_TRUE(BKE_tracking_marker_get_interpolated(&track, 10, &interpolated_marker)); + EXPECT_EQ(interpolated_marker.flag, MARKER_TRACKED); + } + + { + MovieTrackingMarker interpolated_marker; + EXPECT_TRUE(BKE_tracking_marker_get_interpolated(&track, 4, &interpolated_marker)); + EXPECT_EQ(interpolated_marker.flag, 0); + } + + BKE_tracking_track_free(&track); + } +} + +} // namespace blender diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c index e78576206de..5f99f629f42 100644 --- a/source/blender/blenkernel/intern/undo_system.c +++ b/source/blender/blenkernel/intern/undo_system.c @@ -586,7 +586,7 @@ UndoPushReturn BKE_undosys_step_push_with_type(UndoStack *ustack, BLI_strncpy(us->name, name, sizeof(us->name)); } us->type = ut; - /* True by default, code needs to explicitely set it to false if necessary. */ + /* True by default, code needs to explicitly set it to false if necessary. */ us->use_old_bmain_data = true; /* Initialized, not added yet. */ diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c index b55f80c6473..e98fae9d92a 100644 --- a/source/blender/blenkernel/intern/unit.c +++ b/source/blender/blenkernel/intern/unit.c @@ -1167,8 +1167,7 @@ bool BKE_unit_replace_string( /* Replace # with add sign when there is no operator between it and the next number. * * "1*1# 3*100# * 3" -> "1*1+ 3*100 * 3" - * - * */ + */ { char *str_found = str; const char *ch = str; diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 9e7a3736141..d2722adc831 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -523,6 +523,8 @@ static void volume_copy_data(Main *UNUSED(bmain), volume_dst->runtime.grids = OBJECT_GUARDED_NEW(VolumeGridVector, grids_src); } #endif + + volume_dst->batch_cache = nullptr; } static void volume_free_data(ID *id) @@ -642,6 +644,8 @@ IDTypeInfo IDType_ID_VO = { /* blend_read_expand */ volume_blend_read_expand, /* blend_read_undo_preserve */ nullptr, + + /* lib_override_apply_post */ nullptr, }; void BKE_volume_init_grids(Volume *volume) diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index 291116556c3..5a101cf009b 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -192,6 +192,8 @@ IDTypeInfo IDType_ID_WS = { .blend_read_expand = workspace_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index 8fe7653fc25..a2ce37a5d90 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -206,6 +206,8 @@ IDTypeInfo IDType_ID_WO = { .blend_read_expand = world_blend_read_expand, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; World *BKE_world_add(Main *bmain, const char *name) diff --git a/source/blender/blenkernel/nla_private.h b/source/blender/blenkernel/nla_private.h index 7257be66b79..034f3f39fd3 100644 --- a/source/blender/blenkernel/nla_private.h +++ b/source/blender/blenkernel/nla_private.h @@ -112,8 +112,8 @@ typedef struct NlaEvalChannel { struct NlaEvalChannel *next_blend; NlaEvalChannelSnapshot *blend_snapshot; - /* Mask of array items controlled by NLA. */ - NlaValidMask valid; + /* Associated with the RNA property's value(s), marks which elements are affected by NLA. */ + NlaValidMask domain; /* Base set of values. */ NlaEvalChannelSnapshot base_snapshot; diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 74a491898b6..9f144a0ddaa 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -20,7 +20,7 @@ /** \file * \ingroup bli * \brief File and directory operations. - * */ + */ #pragma once diff --git a/source/blender/blenlib/BLI_float2.hh b/source/blender/blenlib/BLI_float2.hh index 2a5320e4c35..84dd0e358a2 100644 --- a/source/blender/blenlib/BLI_float2.hh +++ b/source/blender/blenlib/BLI_float2.hh @@ -29,6 +29,14 @@ struct float2 { { } + explicit float2(float value) : x(value), y(value) + { + } + + explicit float2(int value) : x(value), y(value) + { + } + float2(float x, float y) : x(x), y(y) { } @@ -52,6 +60,11 @@ struct float2 { return len_v2(*this); } + float length_squared() const + { + return len_squared_v2(*this); + } + float2 &operator+=(const float2 &other) { x += other.x; diff --git a/source/blender/blenlib/BLI_fnmatch.h b/source/blender/blenlib/BLI_fnmatch.h index d09a14621d8..2d0c13bc423 100644 --- a/source/blender/blenlib/BLI_fnmatch.h +++ b/source/blender/blenlib/BLI_fnmatch.h @@ -16,7 +16,7 @@ * Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. * * NOTE: The canonical source of this file is maintained with the GNU C Library. - * Bugs can be reported to bug-glibc@prep.ai.mit.edu. + * Bugs can be reported to <bug-glibc@prep.ai.mit.edu>. */ #pragma once diff --git a/source/blender/blenlib/BLI_math.h b/source/blender/blenlib/BLI_math.h index 51833d081d0..f6075367ac5 100644 --- a/source/blender/blenlib/BLI_math.h +++ b/source/blender/blenlib/BLI_math.h @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ #pragma once diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h index 0ec65999006..c862290b262 100644 --- a/source/blender/blenlib/BLI_math_base.h +++ b/source/blender/blenlib/BLI_math_base.h @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ #pragma once diff --git a/source/blender/blenlib/BLI_math_bits.h b/source/blender/blenlib/BLI_math_bits.h index b007dd7cfed..e881f1a0e4e 100644 --- a/source/blender/blenlib/BLI_math_bits.h +++ b/source/blender/blenlib/BLI_math_bits.h @@ -12,8 +12,7 @@ * 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. - * - * */ + */ #pragma once diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h index 9e613270538..26d2f1fcb29 100644 --- a/source/blender/blenlib/BLI_math_color.h +++ b/source/blender/blenlib/BLI_math_color.h @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ #pragma once diff --git a/source/blender/blenlib/BLI_math_color_blend.h b/source/blender/blenlib/BLI_math_color_blend.h index d5e4eedb1a6..2aff629def8 100644 --- a/source/blender/blenlib/BLI_math_color_blend.h +++ b/source/blender/blenlib/BLI_math_color_blend.h @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ #pragma once diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index c0a9ea91e75..d767c2924d1 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ #pragma once @@ -826,6 +825,11 @@ MINLINE float shell_v2v2_mid_normalized_to_dist(const float a[2], const float b[ float cubic_tangent_factor_circle_v3(const float tan_l[3], const float tan_r[3]); +/********************************** Geodesics *********************************/ + +float geodesic_distance_propagate_across_triangle( + const float v0[3], const float v1[3], const float v2[3], const float dist1, const float dist2); + /**************************** Inline Definitions ******************************/ #if BLI_MATH_DO_INLINE diff --git a/source/blender/blenlib/BLI_math_inline.h b/source/blender/blenlib/BLI_math_inline.h index 506386f8d25..70f56d15164 100644 --- a/source/blender/blenlib/BLI_math_inline.h +++ b/source/blender/blenlib/BLI_math_inline.h @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ #pragma once diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index d971f48c4cf..eac7f25f11a 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -17,7 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * */ + */ #pragma once diff --git a/source/blender/blenlib/BLI_math_rotation.h b/source/blender/blenlib/BLI_math_rotation.h index 15d4dc21f8d..f45a4d6fdef 100644 --- a/source/blender/blenlib/BLI_math_rotation.h +++ b/source/blender/blenlib/BLI_math_rotation.h @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ #pragma once diff --git a/source/blender/blenlib/BLI_math_solvers.h b/source/blender/blenlib/BLI_math_solvers.h index 9b2d1eedec7..13481e27e2a 100644 --- a/source/blender/blenlib/BLI_math_solvers.h +++ b/source/blender/blenlib/BLI_math_solvers.h @@ -15,7 +15,7 @@ * * The Original Code is Copyright (C) 2015 by Blender Foundation * All rights reserved. - * */ + */ #pragma once diff --git a/source/blender/blenlib/BLI_math_statistics.h b/source/blender/blenlib/BLI_math_statistics.h index ad6cb1a3c1e..6e818f5c8df 100644 --- a/source/blender/blenlib/BLI_math_statistics.h +++ b/source/blender/blenlib/BLI_math_statistics.h @@ -15,7 +15,7 @@ * * The Original Code is Copyright (C) 2015 by Blender Foundation * All rights reserved. - * */ + */ #pragma once diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index 0bddff9101e..1df22f7c2c9 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ #pragma once diff --git a/source/blender/blenlib/BLI_mmap.h b/source/blender/blenlib/BLI_mmap.h new file mode 100644 index 00000000000..4920152c9d1 --- /dev/null +++ b/source/blender/blenlib/BLI_mmap.h @@ -0,0 +1,56 @@ +/* + * 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) 2020 Blender Foundation. + * All rights reserved. + */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#include "BLI_compiler_attrs.h" +#include "BLI_utildefines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Memory-mapped file IO that implements all the OS-specific details and error handling. */ + +struct BLI_mmap_file; + +typedef struct BLI_mmap_file BLI_mmap_file; + +/* Prepares an opened file for memory-mapped IO. + * May return NULL if the operation fails. + * Note that this seeks to the end of the file to determine its length. */ +BLI_mmap_file *BLI_mmap_open(int fd) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT; + +/* Reads length bytes from file at the given offset into dest. + * Returns whether the operation was successful (may fail when reading beyond the file + * end or when IO errors occur). */ +bool BLI_mmap_read(BLI_mmap_file *file, void *dest, size_t offset, size_t length) + ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); + +void *BLI_mmap_get_pointer(BLI_mmap_file *file) ATTR_WARN_UNUSED_RESULT; + +void BLI_mmap_free(BLI_mmap_file *file) ATTR_NONNULL(1); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 45a1054fb53..363d3003b3c 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -54,6 +54,7 @@ set(SRC intern/BLI_memblock.c intern/BLI_memiter.c intern/BLI_mempool.c + intern/BLI_mmap.c intern/BLI_timer.c intern/DLRB_tree.c intern/array_store.c @@ -243,6 +244,7 @@ set(SRC BLI_mempool.h BLI_mesh_boolean.hh BLI_mesh_intersect.hh + BLI_mmap.h BLI_mpq2.hh BLI_mpq3.hh BLI_multi_value_map.hh diff --git a/source/blender/blenlib/intern/BLI_mmap.c b/source/blender/blenlib/intern/BLI_mmap.c new file mode 100644 index 00000000000..210312456d9 --- /dev/null +++ b/source/blender/blenlib/intern/BLI_mmap.c @@ -0,0 +1,233 @@ +/* + * 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) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#include "BLI_mmap.h" +#include "BLI_fileops.h" +#include "BLI_listbase.h" +#include "MEM_guardedalloc.h" + +#include <string.h> + +#ifndef WIN32 +# include <signal.h> +# include <stdlib.h> +# include <sys/mman.h> // for mmap +# include <unistd.h> // for read close +#else +# include "BLI_winstuff.h" +# include <io.h> // for open close read +#endif + +struct BLI_mmap_file { + /* The address to which the file was mapped. */ + char *memory; + + /* The length of the file (and therefore the mapped region). */ + size_t length; + + /* Platform-specific handle for the mapping. */ + void *handle; + + /* Flag to indicate IO errors. Needs to be volatile since it's being set from + * within the signal handler, which is not part of the normal execution flow. */ + volatile bool io_error; +}; + +#ifndef WIN32 +/* When using memory-mapped files, any IO errors will result in a SIGBUS signal. + * Therefore, we need to catch that signal and stop reading the file in question. + * To do so, we keep a list of all current FileDatas that use memory-mapped files, + * and if a SIGBUS is caught, we check if the failed address is inside one of the + * mapped regions. + * If it is, we set a flag to indicate a failed read and remap the memory in + * question to a zero-backed region in order to avoid additional signals. + * The code that actually reads the memory area has to check whether the flag was + * set after it's done reading. + * If the error occurred outside of a memory-mapped region, we call the previous + * handler if one was configured and abort the process otherwise. + */ + +struct error_handler_data { + ListBase open_mmaps; + char configured; + void (*next_handler)(int, siginfo_t *, void *); +} error_handler = {0}; + +static void sigbus_handler(int sig, siginfo_t *siginfo, void *ptr) +{ + /* We only handle SIGBUS here for now. */ + BLI_assert(sig == SIGBUS); + + char *error_addr = (char *)siginfo->si_addr; + /* Find the file that this error belongs to. */ + LISTBASE_FOREACH (LinkData *, link, &error_handler.open_mmaps) { + BLI_mmap_file *file = link->data; + + /* Is the address where the error occurred in this file's mapped range? */ + if (error_addr >= file->memory && error_addr < file->memory + file->length) { + file->io_error = true; + + /* Replace the mapped memory with zeroes. */ + mmap(file->memory, file->length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + + return; + } + } + + /* Fall back to other handler if there was one. */ + if (error_handler.next_handler) { + error_handler.next_handler(sig, siginfo, ptr); + } + else { + fprintf(stderr, "Unhandled SIGBUS caught\n"); + abort(); + } +} + +/* Ensures that the error handler is set up and ready. */ +static bool sigbus_handler_setup(void) +{ + if (!error_handler.configured) { + struct sigaction newact = {0}, oldact = {0}; + + newact.sa_sigaction = sigbus_handler; + newact.sa_flags = SA_SIGINFO; + + if (sigaction(SIGBUS, &newact, &oldact)) { + return false; + } + + /* Remember the previously configured handler to fall back to it if the error + * does not belong to any of the mapped files. */ + error_handler.next_handler = oldact.sa_sigaction; + error_handler.configured = 1; + } + + return true; +} + +/* Adds a file to the list that the error handler checks. */ +static void sigbus_handler_add(BLI_mmap_file *file) +{ + BLI_addtail(&error_handler.open_mmaps, BLI_genericNodeN(file)); +} + +/* Removes a file from the list that the error handler checks. */ +static void sigbus_handler_remove(BLI_mmap_file *file) +{ + LinkData *link = BLI_findptr(&error_handler.open_mmaps, file, offsetof(LinkData, data)); + BLI_freelinkN(&error_handler.open_mmaps, link); +} +#endif + +BLI_mmap_file *BLI_mmap_open(int fd) +{ + void *memory, *handle = NULL; + size_t length = BLI_lseek(fd, 0, SEEK_END); + +#ifndef WIN32 + /* Ensure that the SIGBUS handler is configured. */ + if (!sigbus_handler_setup()) { + return NULL; + } + + /* Map the given file to memory. */ + memory = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0); + if (memory == MAP_FAILED) { + return NULL; + } +#else + /* Convert the POSIX-style file descriptor to a Windows handle. */ + void *file_handle = (void *)_get_osfhandle(fd); + /* Memory mapping on Windows is a two-step process - first we create a mapping, + * then we create a view into that mapping. + * In our case, one view that spans the entire file is enough. */ + handle = CreateFileMapping(file_handle, NULL, PAGE_READONLY, 0, 0, NULL); + if (handle == NULL) { + return NULL; + } + memory = MapViewOfFile(handle, FILE_MAP_READ, 0, 0, 0); + if (memory == NULL) { + CloseHandle(handle); + return NULL; + } +#endif + + /* Now that the mapping was successful, allocate memory and set up the BLI_mmap_file. */ + BLI_mmap_file *file = MEM_callocN(sizeof(BLI_mmap_file), __func__); + file->memory = memory; + file->handle = handle; + file->length = length; + +#ifndef WIN32 + /* Register the file with the error handler. */ + sigbus_handler_add(file); +#endif + + return file; +} + +bool BLI_mmap_read(BLI_mmap_file *file, void *dest, size_t offset, size_t length) +{ + /* If a previous read has already failed or we try to read past the end, + * don't even attempt to read any further. */ + if (file->io_error || (offset + length > file->length)) { + return false; + } + +#ifndef WIN32 + /* If an error occurs in this call, sigbus_handler will be called and will set + * file->io_error to true. */ + memcpy(dest, file->memory + offset, length); +#else + /* On Windows, we use exception handling to be notified of errors. */ + __try { + memcpy(dest, file->memory + offset, length); + } + __except (GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR ? EXCEPTION_EXECUTE_HANDLER : + EXCEPTION_CONTINUE_SEARCH) { + file->io_error = true; + return false; + } +#endif + + return !file->io_error; +} + +void *BLI_mmap_get_pointer(BLI_mmap_file *file) +{ + return file->memory; +} + +void BLI_mmap_free(BLI_mmap_file *file) +{ +#ifndef WIN32 + munmap((void *)file->memory, file->length); + sigbus_handler_remove(file); +#else + UnmapViewOfFile(file->memory); + CloseHandle(file->handle); +#endif + + MEM_freeN(file); +} diff --git a/source/blender/blenlib/intern/bitmap_draw_2d.c b/source/blender/blenlib/intern/bitmap_draw_2d.c index 33250105c79..9d3b66d72d7 100644 --- a/source/blender/blenlib/intern/bitmap_draw_2d.c +++ b/source/blender/blenlib/intern/bitmap_draw_2d.c @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ /** \file * \ingroup bli diff --git a/source/blender/blenlib/intern/boxpack_2d.c b/source/blender/blenlib/intern/boxpack_2d.c index 9840dc77148..3d010011a79 100644 --- a/source/blender/blenlib/intern/boxpack_2d.c +++ b/source/blender/blenlib/intern/boxpack_2d.c @@ -414,8 +414,7 @@ void BLI_box_pack_2d(BoxPack *boxarray, const uint len, float *r_tot_x, float *r /* This vert has a free quadrant * Test if we can place the box here - * vert->free & quad_flags[j] - Checks - * */ + * `vert->free & quad_flags[j]` - Checks. */ for (j = 0; (j < 4) && isect; j++) { if (vert->free & quad_flag(j)) { diff --git a/source/blender/blenlib/intern/list_sort_impl.h b/source/blender/blenlib/intern/list_sort_impl.h index 46738803ee8..8f979ba5b0b 100644 --- a/source/blender/blenlib/intern/list_sort_impl.h +++ b/source/blender/blenlib/intern/list_sort_impl.h @@ -205,7 +205,7 @@ BLI_INLINE list_node *sweep_up(struct SortInfo *si, list_node *list, unsigned in * The 'ranks' array essentially captures the recursion stack of a mergesort. * The merge tree is built in a bottom-up manner. The control loop for * updating the 'ranks' array is analogous to incrementing a binary integer, - * and the `O(n)` time for counting upto n translates to `O(n)` merges when + * and the `O(n)` time for counting `upto` n translates to `O(n)` merges when * inserting `rank-0` lists. * When we plug in the sizes of the lists involved in those merges, * we get the `O(n log n)` time for the sort. diff --git a/source/blender/blenlib/intern/math_base.c b/source/blender/blenlib/intern/math_base.c index 2bf06371740..1137c4114a5 100644 --- a/source/blender/blenlib/intern/math_base.c +++ b/source/blender/blenlib/intern/math_base.c @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ /** \file * \ingroup bli diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index 1dc7c21f1d4..b332a0b2cb9 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ /** \file * \ingroup bli diff --git a/source/blender/blenlib/intern/math_bits_inline.c b/source/blender/blenlib/intern/math_bits_inline.c index ba18bb73850..1b381dccbfd 100644 --- a/source/blender/blenlib/intern/math_bits_inline.c +++ b/source/blender/blenlib/intern/math_bits_inline.c @@ -12,8 +12,7 @@ * 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. - * - * */ + */ /** \file * \ingroup bli diff --git a/source/blender/blenlib/intern/math_boolean.cc b/source/blender/blenlib/intern/math_boolean.cc index a345bc1d0af..6d4806a3fbc 100644 --- a/source/blender/blenlib/intern/math_boolean.cc +++ b/source/blender/blenlib/intern/math_boolean.cc @@ -160,7 +160,7 @@ static RobustInitCaller init_caller; * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, Pennsylvania 15213-3891 - * jrs@cs.cmu.edu + * <jrs@cs.cmu.edu> * * This file contains C implementation of algorithms for exact addition * and multiplication of floating-point numbers, and predicates for diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c index 31ee81de40e..8fd2802a547 100644 --- a/source/blender/blenlib/intern/math_color.c +++ b/source/blender/blenlib/intern/math_color.c @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ /** \file * \ingroup bli diff --git a/source/blender/blenlib/intern/math_color_blend_inline.c b/source/blender/blenlib/intern/math_color_blend_inline.c index 7ad4f0d9585..53257cc9285 100644 --- a/source/blender/blenlib/intern/math_color_blend_inline.c +++ b/source/blender/blenlib/intern/math_color_blend_inline.c @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ /** \file * \ingroup bli diff --git a/source/blender/blenlib/intern/math_color_inline.c b/source/blender/blenlib/intern/math_color_inline.c index 1264620cf36..a1caf2fe02d 100644 --- a/source/blender/blenlib/intern/math_color_inline.c +++ b/source/blender/blenlib/intern/math_color_inline.c @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ /** \file * \ingroup bli diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 3cc4d03d547..0873dece864 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ /** \file * \ingroup bli @@ -1439,12 +1438,12 @@ int isect_line_sphere_v3(const float l1[3], /* adapted for use in blender by Campbell Barton - 2011 * * atelier iebele abel - 2001 - * atelier@iebele.nl + * <atelier@iebele.nl> * http://www.iebele.nl * * sphere_line_intersection function adapted from: * http://astronomy.swin.edu.au/pbourke/geometry/sphereline - * Paul Bourke pbourke@swin.edu.au + * Paul Bourke <pbourke@swin.edu.au> */ const float ldir[3] = { @@ -3887,7 +3886,7 @@ void interp_weights_quad_v3(float w[4], * - 0 if the point is outside of triangle. * - 1 if the point is inside triangle. * - 2 if it's on the edge. - * */ + */ int barycentric_inside_triangle_v2(const float w[3]) { if (IN_RANGE(w[0], 0.0f, 1.0f) && IN_RANGE(w[1], 0.0f, 1.0f) && IN_RANGE(w[2], 0.0f, 1.0f)) { @@ -6246,3 +6245,56 @@ float cubic_tangent_factor_circle_v3(const float tan_l[3], const float tan_r[3]) const float angle_cos = cosf(angle); return ((1.0f - angle_cos) / (angle_sin * 2.0f)) / angle_sin; } + +/** + * Utility for computing approximate geodesic distances on triangle meshes. + * + * Given triangle with vertex coordinates v0, v1, v2, and known geodesic distances + * dist1 and dist2 at v1 and v2, estimate a geodesic distance at vertex v0. + * + * From "Dart Throwing on Surfaces", EGSR 2009. Section 7, Geodesic Dart Throwing. + */ +float geodesic_distance_propagate_across_triangle( + const float v0[3], const float v1[3], const float v2[3], const float dist1, const float dist2) +{ + /* Vectors along triangle edges. */ + float v10[3], v12[3]; + sub_v3_v3v3(v10, v0, v1); + sub_v3_v3v3(v12, v2, v1); + + if (dist1 != 0.0f && dist2 != 0.0f) { + /* Local coordinate system in the triangle plane. */ + float u[3], v[3], n[3]; + const float d12 = normalize_v3_v3(u, v12); + + if (d12 * d12 > 0.0f) { + cross_v3_v3v3(n, v12, v10); + normalize_v3(n); + cross_v3_v3v3(v, n, u); + + /* v0 in local coordinates */ + const float v0_[2] = {dot_v3v3(v10, u), fabsf(dot_v3v3(v10, v))}; + + /* Compute virtual source point in local coordinates, that we estimate the geodesic + * distance is being computed from. See figure 9 in the paper for the derivation. */ + const float a = 0.5f * (1.0f + (dist1 * dist1 - dist2 * dist2) / (d12 * d12)); + const float hh = dist1 * dist1 - a * a * d12 * d12; + + if (hh > 0.0f) { + const float h = sqrtf(hh); + const float S_[2] = {a * d12, -h}; + + /* Only valid if the line between the source point and v0 crosses + * the edge between v1 and v2. */ + const float x_intercept = S_[0] + h * (v0_[0] - S_[0]) / (v0_[1] + h); + if (x_intercept >= 0.0f && x_intercept <= d12) { + return len_v2v2(S_, v0_); + } + } + } + } + + /* Fall back to Dijsktra approximation in trivial case, or if no valid source + * point found that connects to v0 across the triangle. */ + return min_ff(dist1 + len_v3(v10), dist2 + len_v3v3(v0, v2)); +} diff --git a/source/blender/blenlib/intern/math_geom_inline.c b/source/blender/blenlib/intern/math_geom_inline.c index db317f5e81d..23c351026f2 100644 --- a/source/blender/blenlib/intern/math_geom_inline.c +++ b/source/blender/blenlib/intern/math_geom_inline.c @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ /** \file * \ingroup bli diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c index 19828e69638..f0d8bea52b9 100644 --- a/source/blender/blenlib/intern/math_rotation.c +++ b/source/blender/blenlib/intern/math_rotation.c @@ -1581,7 +1581,7 @@ void quat_to_compatible_eul(float eul[3], const float oldrot[3], const float qua * was adapted from * ANSI C code from the article * "Euler Angle Conversion" - * by Ken Shoemake, shoemake@graphics.cis.upenn.edu + * by Ken Shoemake <shoemake@graphics.cis.upenn.edu> * in "Graphics Gems IV", Academic Press, 1994 * for use in Blender */ diff --git a/source/blender/blenlib/intern/math_solvers.c b/source/blender/blenlib/intern/math_solvers.c index e366d834fc4..85900dc32a6 100644 --- a/source/blender/blenlib/intern/math_solvers.c +++ b/source/blender/blenlib/intern/math_solvers.c @@ -15,7 +15,7 @@ * * The Original Code is Copyright (C) 2015 by Blender Foundation. * All rights reserved. - * */ + */ /** \file * \ingroup bli diff --git a/source/blender/blenlib/intern/math_statistics.c b/source/blender/blenlib/intern/math_statistics.c index 18affbed708..b90ac99dbfe 100644 --- a/source/blender/blenlib/intern/math_statistics.c +++ b/source/blender/blenlib/intern/math_statistics.c @@ -15,7 +15,7 @@ * * The Original Code is Copyright (C) 2015 by Blender Foundation. * All rights reserved. - * */ + */ /** \file * \ingroup bli diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c index 5f3297134c6..000468b3715 100644 --- a/source/blender/blenlib/intern/math_vector.c +++ b/source/blender/blenlib/intern/math_vector.c @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ /** \file * \ingroup bli diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index f17b7535258..ead354c2d87 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -17,8 +17,7 @@ * All rights reserved. * * The Original Code is: some of this file. - * - * */ + */ /** \file * \ingroup bli diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 5636ffafb6a..7cd7991b8d8 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1650,7 +1650,7 @@ bool BLI_path_filename_ensure(char *filepath, size_t maxlen, const char *filenam * - Wont create any directories. * - Doesn't use CWD, or deal with relative paths. * - Only fill's in \a dir and \a file when they are non NULL. - * */ + */ void BLI_split_dirfile( const char *string, char *dir, char *file, const size_t dirlen, const size_t filelen) { diff --git a/source/blender/blenlib/intern/system_win32.c b/source/blender/blenlib/intern/system_win32.c index be2195d679d..7572936592e 100644 --- a/source/blender/blenlib/intern/system_win32.c +++ b/source/blender/blenlib/intern/system_win32.c @@ -386,7 +386,6 @@ void BLI_system_backtrace(FILE *fp) bli_windows_system_backtrace_threads(fp); } bli_windows_system_backtrace_modules(fp); - fputc(0, fp); /* Give our selves a nice zero terminator for later on */ } void BLI_windows_handle_exception(EXCEPTION_POINTERS *exception) diff --git a/source/blender/blenlib/intern/threads.cc b/source/blender/blenlib/intern/threads.cc index 0b88cf53442..b42c50803e0 100644 --- a/source/blender/blenlib/intern/threads.cc +++ b/source/blender/blenlib/intern/threads.cc @@ -289,9 +289,8 @@ void BLI_threadpool_clear(ListBase *threadbase) void BLI_threadpool_end(ListBase *threadbase) { - /* only needed if there's actually some stuff to end - * this way we don't end up decrementing thread_levels on an empty threadbase - * */ + /* Only needed if there's actually some stuff to end + * this way we don't end up decrementing thread_levels on an empty `threadbase`. */ if (threadbase == nullptr || BLI_listbase_is_empty(threadbase)) { return; } diff --git a/source/blender/blenlib/intern/voxel.c b/source/blender/blenlib/intern/voxel.c index eac01a0e2aa..c0c895654e3 100644 --- a/source/blender/blenlib/intern/voxel.c +++ b/source/blender/blenlib/intern/voxel.c @@ -54,12 +54,14 @@ BLI_INLINE int FLOORI(float x) return ((x >= 0.0f) || (float)r == x) ? r : (r - 1); } -/* clamp function, cannot use the CLAMPIS macro, +/** + * clamp function, cannot use the CLAMPIS macro, * it sometimes returns unwanted results apparently related to - * gcc optimization flag -fstrict-overflow which is enabled at -O2 + * gcc optimization flag `-fstrict-overflow` which is enabled at `-O2` * * this causes the test (x + 2) < 0 with int x == 2147483647 to return false (x being an integer, - * x + 2 should wrap around to -2147483647 so the test < 0 should return true, which it doesn't) */ + * x + 2 should wrap around to -2147483647 so the test < 0 should return true, which it doesn't). + */ BLI_INLINE int64_t _clamp(int a, int b, int c) { return (a < b) ? b : ((a > c) ? c : a); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index b61abd4ed06..2192e9a378f 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -68,6 +68,7 @@ #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_mempool.h" +#include "BLI_mmap.h" #include "BLI_threads.h" #include "BLT_translation.h" @@ -1179,6 +1180,53 @@ static ssize_t fd_read_from_memory(FileData *filedata, return readsize; } +/* Memory-mapped file reading. + * By using mmap(), we can map a file so that it can be treated like normal memory, + * meaning that we can just read from it with memcpy() etc. + * This avoids system call overhead and can significantly speed up file loading. + */ + +static ssize_t fd_read_from_mmap(FileData *filedata, + void *buffer, + size_t size, + bool *UNUSED(r_is_memchunck_identical)) +{ + /* don't read more bytes than there are available in the buffer */ + size_t readsize = MIN2(size, (size_t)(filedata->buffersize - filedata->file_offset)); + + if (!BLI_mmap_read(filedata->mmap_file, buffer, filedata->file_offset, readsize)) { + return 0; + } + + filedata->file_offset += readsize; + + return readsize; +} + +static off64_t fd_seek_from_mmap(FileData *filedata, off64_t offset, int whence) +{ + off64_t new_pos; + if (whence == SEEK_CUR) { + new_pos = filedata->file_offset + offset; + } + else if (whence == SEEK_SET) { + new_pos = offset; + } + else if (whence == SEEK_END) { + new_pos = filedata->buffersize + offset; + } + else { + return -1; + } + + if (new_pos < 0 || new_pos > filedata->buffersize) { + return -1; + } + + filedata->file_offset = new_pos; + return filedata->file_offset; +} + /* MemFile reading. */ static ssize_t fd_read_from_memfile(FileData *filedata, @@ -1306,6 +1354,8 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath, { FileDataReadFn *read_fn = NULL; FileDataSeekFn *seek_fn = NULL; /* Optional. */ + size_t buffersize = 0; + BLI_mmap_file *mmap_file = NULL; gzFile gzfile = (gzFile)Z_NULL; @@ -1322,14 +1372,21 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath, return NULL; } - BLI_lseek(file, 0, SEEK_SET); - /* Regular file. */ if (memcmp(header, "BLENDER", sizeof(header)) == 0) { read_fn = fd_read_data_from_file; seek_fn = fd_seek_data_from_file; + + mmap_file = BLI_mmap_open(file); + if (mmap_file != NULL) { + read_fn = fd_read_from_mmap; + seek_fn = fd_seek_from_mmap; + buffersize = BLI_lseek(file, 0, SEEK_END); + } } + BLI_lseek(file, 0, SEEK_SET); + /* Gzip file. */ errno = 0; if ((read_fn == NULL) && @@ -1363,6 +1420,8 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath, fd->read = read_fn; fd->seek = seek_fn; + fd->mmap_file = mmap_file; + fd->buffersize = buffersize; return fd; } @@ -1531,6 +1590,11 @@ void blo_filedata_free(FileData *fd) fd->buffer = NULL; } + if (fd->mmap_file) { + BLI_mmap_free(fd->mmap_file); + fd->mmap_file = NULL; + } + /* Free all BHeadN data blocks */ #ifndef NDEBUG BLI_freelistN(&fd->bhead_list); diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index c724cc32051..86f05eda7b7 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -41,6 +41,7 @@ struct Object; struct OldNewMap; struct ReportList; struct UserDef; +struct BLI_mmap_file; typedef struct IDNameLib_Map IDNameLib_Map; @@ -83,8 +84,9 @@ typedef struct FileData { /** Regular file reading. */ int filedes; - /** Variables needed for reading from memory / stream. */ + /** Variables needed for reading from memory / stream / memory-mapped files. */ const char *buffer; + struct BLI_mmap_file *mmap_file; /** Variables needed for reading from memfile (undo). */ struct MemFile *memfile; /** Whether we are undoing (< 0) or redoing (> 0), used to choose which 'unchanged' flag to use diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index bb477745849..2b630782806 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -475,7 +475,7 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports)) LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { bGPDframe *gpf = gpl->frames.first; if (gpf && gpf->framenum > scene->r.sfra) { - bGPDframe *gpf_dup = BKE_gpencil_frame_duplicate(gpf); + bGPDframe *gpf_dup = BKE_gpencil_frame_duplicate(gpf, true); gpf_dup->framenum = scene->r.sfra; BLI_addhead(&gpl->frames, gpf_dup); } @@ -499,7 +499,7 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports)) /** * Make sure Emission Alpha fcurve and drivers is properly mapped after the Emission Strength * got introduced. - * */ + */ /** * Effectively we are replacing the (animation of) node socket input 18 with 19. @@ -510,7 +510,7 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports)) * * The for loop for the input ids is at the top level otherwise we lose the animation * keyframe data. - * */ + */ for (int input_id = 21; input_id >= 18; input_id--) { FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_SHADER) { @@ -1548,7 +1548,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) FOREACH_NODETREE_END; } - if (!MAIN_VERSION_ATLEAST(bmain, 292, 13)) { + if (!MAIN_VERSION_ATLEAST(bmain, 293, 1)) { FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { version_node_socket_name(ntree, GEO_NODE_BOOLEAN, "Geometry A", "Geometry 1"); @@ -1573,7 +1573,8 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - if (!MAIN_VERSION_ATLEAST(bmain, 292, 14)) { + if ((!MAIN_VERSION_ATLEAST(bmain, 292, 14)) || + ((bmain->versionfile == 293) && (!MAIN_VERSION_ATLEAST(bmain, 293, 1)))) { FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type != NTREE_GEOMETRY) { continue; @@ -1590,6 +1591,75 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) FOREACH_NODETREE_END; } + if (!MAIN_VERSION_ATLEAST(bmain, 293, 1)) { + /* Grease pencil layer transform matrix. */ + if (!DNA_struct_elem_find(fd->filesdna, "bGPDlayer", "float", "location[0]")) { + LISTBASE_FOREACH (bGPdata *, gpd, &bmain->gpencils) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + zero_v3(gpl->location); + zero_v3(gpl->rotation); + copy_v3_fl(gpl->scale, 1.0f); + loc_eul_size_to_mat4(gpl->layer_mat, gpl->location, gpl->rotation, gpl->scale); + invert_m4_m4(gpl->layer_invmat, gpl->layer_mat); + } + } + } + /* Fix Fill factor for grease pencil fill brushes. */ + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + if ((brush->gpencil_settings) && (brush->gpencil_settings->fill_factor == 0.0f)) { + brush->gpencil_settings->fill_factor = 1.0f; + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 293, 3)) { + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type != NTREE_GEOMETRY) { + continue; + } + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == GEO_NODE_POINT_INSTANCE && node->storage == NULL) { + NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN( + sizeof(NodeGeometryPointInstance), __func__); + data->instance_type = node->custom1; + data->flag = (node->custom2 ? 0 : GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION); + node->storage = data; + } + } + } + FOREACH_NODETREE_END; + } + + if (!MAIN_VERSION_ATLEAST(bmain, 293, 4)) { + /* Add support for all operations to the "Attribute Math" node. */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_GEOMETRY) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == GEO_NODE_ATTRIBUTE_MATH) { + NodeAttributeMath *data = (NodeAttributeMath *)node->storage; + data->input_type_c = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; + } + } + } + } + FOREACH_NODETREE_END; + } + + if (!MAIN_VERSION_ATLEAST(bmain, 293, 5)) { + /* Change Nishita sky model Altitude unit. */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_SHADER) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == SH_NODE_TEX_SKY && node->storage) { + NodeTexSky *tex = (NodeTexSky *)node->storage; + tex->altitude *= 1000.0f; + } + } + } + } + FOREACH_NODETREE_END; + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 0bc1cc4a05c..2275084d53b 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -826,6 +826,13 @@ void blo_do_versions_userdef(UserDef *userdef) } } + if (!USER_VERSION_ATLEAST(293, 2)) { + /* Enable asset browser features by default for alpha testing. + * BLO_sanitize_experimental_features_userpref_blend() will disable it again for non-alpha + * builds. */ + userdef->experimental.use_asset_browser = true; + } + /** * Versioning code until next subversion bump goes here. * @@ -837,6 +844,9 @@ void blo_do_versions_userdef(UserDef *userdef) */ { /* Keep this block, even when empty. */ + if (userdef->gizmo_size_navigate_v3d == 0) { + userdef->gizmo_size_navigate_v3d = 80; + } } LISTBASE_FOREACH (bTheme *, btheme, &userdef->themes) { diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 9497466723a..44d146bc155 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -7211,14 +7211,14 @@ static void set_profile_spacing(BevelParams *bp, ProfileSpacing *pro_spacing, bo * * where edges are A, B, and C, following a face around vertices a, b, c, d. * th1 is angle abc and th2 is angle bcd; - * and the argument EdgeHalf eb is B, going from b to c. + * and the argument `EdgeHalf eb` is B, going from b to c. * In general case, edge offset specs for A, B, C have * the form ka*t, kb*t, kc*t where ka, kb, kc are some factors * (may be 0) and t is the current bp->offset. * We want to calculate t at which the clone of B parallel * to it collapses. This can be calculated using trig. * Another case of geometry collision that can happen is - * When B slides along A because A is unbeveled. + * When B slides along A because A is un-beveled. * Then it might collide with a. Similarly for B sliding along C. */ static float geometry_collide_offset(BevelParams *bp, EdgeHalf *eb) diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c index 60c49587387..6ae889fcee5 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c +++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c @@ -294,8 +294,7 @@ static void bm_decim_build_edge_cost_single(BMEdge *e, /* subtract existing cost to further differentiate edges from one another * * keep topology cost below 0.0 so their values don't interfere with quadric cost, - * (and they get handled first). - * */ + * (and they get handled first). */ if (vweights == NULL) { cost = bm_decim_build_edge_cost_single_squared__topology(e) - cost; } diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.h b/source/blender/compositor/operations/COM_BokehImageOperation.h index 01ffa026152..2d775bdf738 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.h +++ b/source/blender/compositor/operations/COM_BokehImageOperation.h @@ -55,7 +55,7 @@ class BokehImageOperation : public NodeOperation { NodeBokehImage *m_data; /** - * \brief precalced center of the image + * \brief precalculate center of the image */ float m_center[2]; @@ -87,7 +87,7 @@ class BokehImageOperation : public NodeOperation { /** * \brief determine the coordinate of a flap corner. * - * \param r: result in bokehimage space are stored [x,y] + * \param r: result in bokeh-image space are stored [x,y] * \param flapNumber: the flap number to calculate * \param distance: the lens distance is used to simulate lens shifts */ diff --git a/source/blender/compositor/operations/COM_DotproductOperation.cpp b/source/blender/compositor/operations/COM_DotproductOperation.cpp index 750e4308d11..5914be21453 100644 --- a/source/blender/compositor/operations/COM_DotproductOperation.cpp +++ b/source/blender/compositor/operations/COM_DotproductOperation.cpp @@ -39,7 +39,7 @@ void DotproductOperation::deinitExecution() this->m_input2Operation = nullptr; } -/** \todo: current implementation is the inverse of a dotproduct. not 'logically' correct +/** \todo current implementation is the inverse of a dot-product. not 'logically' correct */ void DotproductOperation::executePixelSampled(float output[4], float x, diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 5f591b4aee0..dff2f427f27 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -1197,7 +1197,7 @@ void DepsgraphRelationBuilder::build_constraints(ID *id, // TODO: loc vs rot vs scale? if (&ct->tar->id == id) { /* Constraint targeting own object: - * - This case is fine IFF we're dealing with a bone + * - This case is fine IF we're dealing with a bone * constraint pointing to its own armature. In that * case, it's just transform -> bone. * - If however it is a real self targeting case, just @@ -1768,7 +1768,7 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) RELATION_FLAG_GODMODE); } - /* Final transform is whetever solver gave to us. */ + /* Final transform is whatever the solver gave to us. */ if (object->rigidbody_object->type == RBO_TYPE_ACTIVE) { /* We do not have to update the objects final transform after the simulation if it is * passive or controlled by the animation system in blender. diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc index e472d82f2ee..c9780b9b129 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc @@ -193,6 +193,30 @@ bool deg_iterator_components_step(BLI_Iterator *iter) } } + /* The volume component. */ + if (data->geometry_component_id == 2) { + data->geometry_component_id++; + + /* Don't use a temporary object for this component, when the owner is a volume object. */ + if (data->geometry_component_owner->type == OB_VOLUME) { + iter->current = data->geometry_component_owner; + return true; + } + + const VolumeComponent *component = geometry_set->get_component_for_read<VolumeComponent>(); + if (component != nullptr) { + const Volume *volume = component->get_for_read(); + + Object *temp_object = &data->temp_geometry_component_object; + *temp_object = *data->geometry_component_owner; + temp_object->type = OB_VOLUME; + temp_object->data = (void *)volume; + temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id; + iter->current = temp_object; + return true; + } + } + data->geometry_component_owner = nullptr; return false; } diff --git a/source/blender/depsgraph/intern/node/deg_node_id.h b/source/blender/depsgraph/intern/node/deg_node_id.h index c4d36685bb8..e2d3b3fc36f 100644 --- a/source/blender/depsgraph/intern/node/deg_node_id.h +++ b/source/blender/depsgraph/intern/node/deg_node_id.h @@ -88,7 +88,7 @@ struct IDNode : public Node { * which could be "stale" pointer. */ uint id_orig_session_uuid; - /* Evaluated datablock. + /* Evaluated data-block. * Will be covered by the copy-on-write system if the ID Type needs it. */ ID *id_cow; @@ -107,7 +107,7 @@ struct IDNode : public Node { eDepsNode_LinkedState_Type linked_state; - /* Indicates the datablock is visible in the evaluated scene. */ + /* Indicates the data-block is visible in the evaluated scene. */ bool is_directly_visible; /* For the collection type of ID, denotes whether collection was fully diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c index 90948d67895..31e54e371b1 100644 --- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c +++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c @@ -363,7 +363,7 @@ static void eevee_cryptomatte_download_buffer(EEVEE_Data *vedata, GPUFrameBuffer download_buffer); /* Integrate download buffer into the accum buffer. - * The download buffer contains upto 3 floats per pixel (one float per cryptomatte layer. + * The download buffer contains up to 3 floats per pixel (one float per cryptomatte layer. * * NOTE: here we deviate from the cryptomatte standard. During integration the standard always * sort the samples by its weight to make sure that samples with the lowest weight diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index 20044fbe3ce..b6f0817adb3 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -214,6 +214,7 @@ void GPENCIL_cache_init(void *ved) NULL : false; pd->do_onion = show_onion && !hide_overlay && !playing; + pd->playing = playing; /* Save simplify flags (can change while drawing, so it's better to save). */ Scene *scene = draw_ctx->scene; pd->simplify_fill = GPENCIL_SIMPLIFY_FILL(scene, playing); @@ -241,6 +242,7 @@ void GPENCIL_cache_init(void *ved) pd->simplify_fill = false; pd->simplify_fx = false; pd->fade_layer_opacity = -1.0f; + pd->playing = false; } { @@ -617,7 +619,7 @@ void GPENCIL_cache_populate(void *ved, Object *ob) /* Special case for rendering onion skin. */ bGPdata *gpd = (bGPdata *)ob->data; bool do_onion = (!pd->is_render) ? pd->do_onion : (gpd->onion_flag & GP_ONION_GHOST_ALWAYS); - + gpd->runtime.playing = (short)pd->playing; BKE_gpencil_visible_stroke_iter(is_final_render ? pd->view_layer : NULL, ob, gpencil_layer_cache_populate, diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index d0bd56b42dd..2b07ea53187 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -342,6 +342,8 @@ typedef struct GPENCIL_PrivateData { /* Display onion skinning */ bool do_onion; + /* Playing animation */ + bool playing; /* simplify settings */ bool simplify_fill; bool simplify_fx; diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c index cb65fbd6ae7..f4af5ebff99 100644 --- a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c +++ b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c @@ -235,7 +235,7 @@ static void gpencil_vfx_rim(RimShaderFxData *fx, Object *ob, gpIterVfxData *iter DRW_shgroup_call_procedural_triangles(grp, NULL, 1); if (fx->mode == eShaderFxRimMode_Overlay) { - /* We cannot do custom blending on MultiTarget framebuffers. + /* We cannot do custom blending on multi-target frame-buffers. * Workaround by doing 2 passes. */ grp = DRW_shgroup_create_sub(grp); DRW_shgroup_state_disable(grp, DRW_STATE_BLEND_MUL); @@ -363,7 +363,7 @@ static void gpencil_vfx_shadow(ShadowShaderFxData *fx, Object *ob, gpIterVfxData copy_v2_v2(wave_ofs, wave_dir); SWAP(float, wave_ofs[0], wave_ofs[1]); wave_ofs[1] *= -1.0f; - /* Keep world space scalling and aspect ratio. */ + /* Keep world space scaling and aspect ratio. */ mul_v2_fl(wave_dir, 1.0f / (max_ff(1e-8f, fx->period) * distance_factor)); mul_v2_v2(wave_dir, vp_size); mul_v2_fl(wave_ofs, fx->amplitude * distance_factor); @@ -515,7 +515,7 @@ static void gpencil_vfx_wave(WaveShaderFxData *fx, Object *ob, gpIterVfxData *it copy_v2_v2(wave_ofs, wave_dir); SWAP(float, wave_ofs[0], wave_ofs[1]); wave_ofs[1] *= -1.0f; - /* Keep world space scalling and aspect ratio. */ + /* Keep world space scaling and aspect ratio. */ mul_v2_fl(wave_dir, 1.0f / (max_ff(1e-8f, fx->period) * distance_factor)); mul_v2_v2(wave_dir, vp_size); mul_v2_fl(wave_ofs, fx->amplitude * distance_factor); @@ -647,7 +647,7 @@ void gpencil_vfx_cache_populate(GPENCIL_Data *vedata, Object *ob, GPENCIL_tObjec DRW_shgroup_uniform_int_copy(grp, "isFirstPass", true); DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - /* We cannot do custom blending on MultiTarget framebuffers. + /* We cannot do custom blending on multi-target frame-buffers. * Workaround by doing 2 passes. */ grp = DRW_shgroup_create_sub(grp); DRW_shgroup_state_disable(grp, DRW_STATE_BLEND_MUL); diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.c index 7f9290a6c3a..19af3ae7e5d 100644 --- a/source/blender/draw/engines/overlay/overlay_gpencil.c +++ b/source/blender/draw/engines/overlay/overlay_gpencil.c @@ -258,6 +258,16 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) copy_m4_m4(mat, ob->obmat); + /* Rotate and scale except align to cursor. */ + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); + if (gpl != NULL) { + if (ts->gp_sculpt.lock_axis != GP_LOCKAXIS_CURSOR) { + float matrot[3][3]; + copy_m3_m4(matrot, gpl->layer_mat); + mul_m4_m4m3(mat, mat, matrot); + } + } + float viewinv[4][4]; /* Set the grid in the selected axis */ switch (ts->gp_sculpt.lock_axis) { @@ -294,6 +304,11 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) mul_v2_v2fl(size, gpd->grid.scale, 2.0f * ED_scene_grid_scale(scene, &grid_unit)); rescale_m4(mat, (float[3]){size[0], size[1], 0.0f}); + /* Apply layer loc transform, except cursor mode. */ + if ((gpl != NULL) && (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) == 0) { + add_v3_v3(mat[3], gpl->layer_mat[3]); + } + const int gridlines = (gpd->grid.lines <= 0) ? 1 : gpd->grid.lines; int line_ct = gridlines * 4 + 2; diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c index e1523db36c7..8feacf8b026 100644 --- a/source/blender/draw/intern/draw_cache_impl_gpencil.c +++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c @@ -800,20 +800,20 @@ static void gpencil_edit_curve_stroke_iter_cb(bGPDlayer *gpl, }; /* First segment. */ - copy_v3_v3(vert_ptr->pos, bezt->vec[0]); + mul_v3_m4v3(vert_ptr->pos, gpl->layer_mat, bezt->vec[0]); vert_ptr->data = vflag[0]; vert_ptr++; - copy_v3_v3(vert_ptr->pos, bezt->vec[1]); + mul_v3_m4v3(vert_ptr->pos, gpl->layer_mat, bezt->vec[1]); vert_ptr->data = vflag[1]; vert_ptr++; /* Second segment. */ - copy_v3_v3(vert_ptr->pos, bezt->vec[1]); + mul_v3_m4v3(vert_ptr->pos, gpl->layer_mat, bezt->vec[1]); vert_ptr->data = vflag[1]; vert_ptr++; - copy_v3_v3(vert_ptr->pos, bezt->vec[2]); + mul_v3_m4v3(vert_ptr->pos, gpl->layer_mat, bezt->vec[2]); vert_ptr->data = vflag[2]; vert_ptr++; } diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 5f7aa28c3c9..67778584d25 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -344,7 +344,7 @@ void DRW_state_reset(void) /* Should stay constant during the whole rendering. */ GPU_point_size(5); GPU_line_smooth(false); - /* Bypass U.pixelsize factor by using a factor of 0.0f. Will be clamped to 1.0f. */ + /* Bypass #U.pixelsize factor by using a factor of 0.0f. Will be clamped to 1.0f. */ GPU_line_width(0.0f); } diff --git a/source/blender/draw/intern/shaders/common_fxaa_lib.glsl b/source/blender/draw/intern/shaders/common_fxaa_lib.glsl index a87b4558227..0ecfc397e95 100644 --- a/source/blender/draw/intern/shaders/common_fxaa_lib.glsl +++ b/source/blender/draw/intern/shaders/common_fxaa_lib.glsl @@ -1,7 +1,7 @@ /* --------------------------------------------------------------------------------- * File: es3-kepler\FXAA/FXAA3_11.h * SDK Version: v3.00 - * Email: gameworks@nvidia.com + * Email: <gameworks@nvidia.com> * Site: http://developer.nvidia.com/ * * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 05cbb3ef48f..64fa93773b9 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -133,7 +133,16 @@ static void acf_generic_root_backdrop(bAnimContext *ac, UI_draw_roundbox_corner_set((expanded) ? UI_CNR_TOP_LEFT : (UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT)); UI_draw_roundbox_3fv_alpha( - true, offset, yminc, v2d->cur.xmax + EXTRA_SCROLL_PAD, ymaxc, 8, color, 1.0f); + &(const rctf){ + .xmin = offset, + .xmax = v2d->cur.xmax + EXTRA_SCROLL_PAD, + .ymin = yminc, + .ymax = ymaxc, + }, + true, + 8, + color, + 1.0f); } /* get backdrop color for data expanders under top-level Scene/Object */ @@ -464,7 +473,16 @@ static void acf_summary_backdrop(bAnimContext *ac, bAnimListElem *ale, float ymi */ UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT); UI_draw_roundbox_3fv_alpha( - true, 0, yminc - 2, v2d->cur.xmax + EXTRA_SCROLL_PAD, ymaxc, 8, color, 1.0f); + &(const rctf){ + .xmin = 0, + .xmax = v2d->cur.xmax + EXTRA_SCROLL_PAD, + .ymin = yminc - 2, + .ymax = ymaxc, + }, + true, + 8, + color, + 1.0f); } /* name for summary entries */ @@ -875,7 +893,16 @@ static void acf_group_backdrop(bAnimContext *ac, bAnimListElem *ale, float yminc /* rounded corners on LHS only - top only when expanded, but bottom too when collapsed */ UI_draw_roundbox_corner_set(expanded ? UI_CNR_TOP_LEFT : (UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT)); UI_draw_roundbox_3fv_alpha( - true, offset, yminc, v2d->cur.xmax + EXTRA_SCROLL_PAD, ymaxc, 8, color, 1.0f); + &(const rctf){ + .xmin = offset, + .xmax = v2d->cur.xmax + EXTRA_SCROLL_PAD, + .ymin = yminc, + .ymax = ymaxc, + }, + true, + 8, + color, + 1.0f); } /* name for group entries */ @@ -1149,7 +1176,16 @@ static void acf_nla_controls_backdrop(bAnimContext *ac, /* rounded corners on LHS only - top only when expanded, but bottom too when collapsed */ UI_draw_roundbox_corner_set(expanded ? UI_CNR_TOP_LEFT : (UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT)); UI_draw_roundbox_3fv_alpha( - true, offset, yminc, v2d->cur.xmax + EXTRA_SCROLL_PAD, ymaxc, 5, color, 1.0f); + &(const rctf){ + .xmin = offset, + .xmax = v2d->cur.xmax + EXTRA_SCROLL_PAD, + .ymin = yminc, + .ymax = ymaxc, + }, + true, + 5, + color, + 1.0f); } /* name for nla controls expander entries */ @@ -3936,13 +3972,16 @@ static void acf_nlaaction_backdrop(bAnimContext *ac, bAnimListElem *ale, float y /* draw slightly shifted up vertically to look like it has more separation from other channels, * but we then need to slightly shorten it so that it doesn't look like it overlaps */ - UI_draw_roundbox_4fv(true, - offset, - yminc + NLACHANNEL_SKIP, - (float)v2d->cur.xmax, - ymaxc + NLACHANNEL_SKIP - 1, - 8, - color); + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = offset, + .xmax = (float)v2d->cur.xmax, + .ymin = yminc + NLACHANNEL_SKIP, + .ymax = ymaxc + NLACHANNEL_SKIP - 1, + }, + true, + 8, + color); } /* name for nla action entries */ @@ -4710,7 +4749,7 @@ static void achannel_setting_slider_cb(bContext *C, void *id_poin, void *fcu_poi const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, (float)CFRA); NlaKeyframingContext *nla_context = BKE_animsys_get_nla_keyframing_context( - &nla_cache, &id_ptr, adt, &anim_eval_context, false); + &nla_cache, &id_ptr, adt, &anim_eval_context); /* get current frame and apply NLA-mapping to it (if applicable) */ cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); @@ -4766,7 +4805,7 @@ static void achannel_setting_slider_shapekey_cb(bContext *C, void *key_poin, voi const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, (float)CFRA); NlaKeyframingContext *nla_context = BKE_animsys_get_nla_keyframing_context( - &nla_cache, &id_ptr, key->adt, &anim_eval_context, false); + &nla_cache, &id_ptr, key->adt, &anim_eval_context); /* get current frame and apply NLA-mapping to it (if applicable) */ const float remapped_frame = BKE_nla_tweakedit_remap( diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index a6e96a4d919..7afb03b833d 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -393,7 +393,11 @@ static void anim_channels_select_set(bAnimContext *ac, FCurve *fcu = (FCurve *)ale->data; ACHANNEL_SET_FLAG(fcu, sel, FCURVE_SELECTED); - fcu->flag &= ~FCURVE_ACTIVE; + if ((fcu->flag & FCURVE_SELECTED) == 0) { + /* Only erase the ACTIVE flag when deselecting. This ensures that "select all curves" + * retains the currently active curve. */ + fcu->flag &= ~FCURVE_ACTIVE; + } break; } case ANIMTYPE_SHAPEKEY: { @@ -1197,7 +1201,7 @@ static void rearrange_nla_channels(bAnimContext *ac, AnimData *adt, eRearrangeAn rearrange_animchannel_islands( &adt->nla_tracks, rearrange_func, mode, ANIMTYPE_NLATRACK, &anim_data_visible); - /* Add back non-local NLA tracks at the begining of the animation data's list. */ + /* Add back non-local NLA tracks at the beginning of the animation data's list. */ if (!BLI_listbase_is_empty(&extracted_nonlocal_nla_tracks)) { BLI_assert(is_liboverride); ((NlaTrack *)extracted_nonlocal_nla_tracks.last)->next = adt->nla_tracks.first; diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index e8146ca960a..db3a1b99135 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -1384,7 +1384,7 @@ static AnimationEvalContext nla_time_remap(const AnimationEvalContext *anim_eval if (adt && adt->action == act) { /* Get NLA context for value remapping. */ *r_nla_context = BKE_animsys_get_nla_keyframing_context( - nla_cache, id_ptr, adt, anim_eval_context, false); + nla_cache, id_ptr, adt, anim_eval_context); /* Apply NLA-mapping to frame. */ const float remapped_frame = BKE_nla_tweakedit_remap( diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c index 98b4d93fbf1..034378399b9 100644 --- a/source/blender/editors/animation/time_scrub_ui.c +++ b/source/blender/editors/animation/time_scrub_ui.c @@ -124,23 +124,22 @@ static void draw_current_frame(const Scene *scene, UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_3fv_alpha(true, - frame_x - box_width / 2 + U.pixelsize / 2, - scrub_region_rect->ymin + box_padding, - frame_x + box_width / 2 + U.pixelsize / 2, - scrub_region_rect->ymax - box_padding, - 4 * UI_DPI_FAC, - bg_color, - 1.0f); - - UI_GetThemeColorShade4fv(TH_CFRAME, 5, bg_color); - UI_draw_roundbox_aa(false, - frame_x - box_width / 2 + U.pixelsize / 2, - scrub_region_rect->ymin + box_padding, - frame_x + box_width / 2 + U.pixelsize / 2, - scrub_region_rect->ymax - box_padding, - 4 * UI_DPI_FAC, - bg_color); + float outline_color[4]; + UI_GetThemeColorShade4fv(TH_CFRAME, 5, outline_color); + + UI_draw_roundbox_4fv_ex( + &(const rctf){ + .xmin = frame_x - box_width / 2 + U.pixelsize / 2, + .xmax = frame_x + box_width / 2 + U.pixelsize / 2, + .ymin = scrub_region_rect->ymin + box_padding, + .ymax = scrub_region_rect->ymax - box_padding, + }, + bg_color, + NULL, + 1.0f, + outline_color, + U.pixelsize, + 4 * UI_DPI_FAC); uchar text_color[4]; UI_GetThemeColor4ubv(TH_HEADER_TEXT_HI, text_color); diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index a0b8bf862d7..e538c20df6c 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -306,8 +306,7 @@ static EditBone *get_named_editbone(ListBase *edbo, const char *name) return NULL; } -/* Call this before doing any duplications - * */ +/* Call this before doing any duplications. */ void preEditBoneDuplicate(ListBase *editbones) { /* clear temp */ diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 4b646bb26e2..0b3c3855059 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -1246,15 +1246,15 @@ static void pose_slide_opdef_properties(wmOperatorType *ot) { PropertyRNA *prop; - prop = RNA_def_float_percentage(ot->srna, - "percentage", - 0.5f, - 0.0f, - 1.0f, - "Percentage", - "Weighting factor for which keyframe is favored more", - 0.0, - 1.0); + prop = RNA_def_float_factor(ot->srna, + "factor", + 0.5f, + 0.0f, + 1.0f, + "Factor", + "Weighting factor for which keyframe is favored more", + 0.0, + 1.0); RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_int(ot->srna, diff --git a/source/blender/editors/curve/editcurve_select.c b/source/blender/editors/curve/editcurve_select.c index 4097275a2b9..82f7b456284 100644 --- a/source/blender/editors/curve/editcurve_select.c +++ b/source/blender/editors/curve/editcurve_select.c @@ -1242,7 +1242,7 @@ static void curve_select_random(ListBase *editnurb, float randfac, int seed, boo static int curve_select_random_exec(bContext *C, wmOperator *op) { const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT); - const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f; + const float randfac = RNA_float_get(op->ptr, "ratio"); const int seed = WM_operator_properties_select_random_seed_increment_get(op); ViewLayer *view_layer = CTX_data_view_layer(C); diff --git a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c index eb40500d011..cbca230da7e 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c @@ -320,6 +320,16 @@ static int gizmo_button2d_cursor_get(wmGizmo *gz) return WM_CURSOR_DEFAULT; } +static void gizmo_button2d_bounds(bContext *C, wmGizmo *gz, rcti *r_bounding_box) +{ + ScrArea *area = CTX_wm_area(C); + float rad = CIRCLE_RESOLUTION * U.dpi_fac / 2.0f; + r_bounding_box->xmin = gz->matrix_basis[3][0] + area->totrct.xmin - rad; + r_bounding_box->ymin = gz->matrix_basis[3][1] + area->totrct.ymin - rad; + r_bounding_box->xmax = r_bounding_box->xmin + rad; + r_bounding_box->ymax = r_bounding_box->ymin + rad; +} + static void gizmo_button2d_free(wmGizmo *gz) { ButtonGizmo2D *shape = (ButtonGizmo2D *)gz; @@ -346,6 +356,7 @@ static void GIZMO_GT_button_2d(wmGizmoType *gzt) gzt->draw_select = gizmo_button2d_draw_select; gzt->test_select = gizmo_button2d_test_select; gzt->cursor_get = gizmo_button2d_cursor_get; + gzt->screen_bounds_get = gizmo_button2d_bounds; gzt->free = gizmo_button2d_free; gzt->struct_size = sizeof(ButtonGizmo2D); diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c index aae31b11025..166111c582c 100644 --- a/source/blender/editors/gpencil/editaction_gpencil.c +++ b/source/blender/editors/gpencil/editaction_gpencil.c @@ -276,7 +276,7 @@ void ED_gpencil_layer_frames_duplicate(bGPDlayer *gpl) bGPDframe *gpfd; /* duplicate frame, and deselect self */ - gpfd = BKE_gpencil_frame_duplicate(gpf); + gpfd = BKE_gpencil_frame_duplicate(gpf, true); gpf->flag &= ~GP_FRAME_SELECT; BLI_insertlinkafter(&gpl->frames, gpf, gpfd); @@ -361,7 +361,7 @@ bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac) /* if frame is selected, make duplicate it and its strokes */ if (gpf->flag & GP_FRAME_SELECT) { /* make a copy of this frame */ - bGPDframe *new_frame = BKE_gpencil_frame_duplicate(gpf); + bGPDframe *new_frame = BKE_gpencil_frame_duplicate(gpf, true); BLI_addtail(&copied_frames, new_frame); /* extend extents for keyframes encountered */ diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index 09b57029350..22a628de8b1 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -178,7 +178,7 @@ static void gpencil_strokepoint_convertcoords(bContext *C, /* apply parent transform */ float fpt[3]; - BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); mul_v3_m4v3(fpt, diff_mat, &source_pt->x); copy_v3_v3(&pt->x, fpt); diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index ad9b72b713e..e297a806895 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -449,20 +449,25 @@ void GPENCIL_OT_layer_annotation_move(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", ""); } /* ********************* Duplicate Layer ************************** */ +enum { + GP_LAYER_DUPLICATE_ALL = 0, + GP_LAYER_DUPLICATE_EMPTY = 1, +}; -static int gpencil_layer_copy_exec(bContext *C, wmOperator *UNUSED(op)) +static int gpencil_layer_copy_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); bGPDlayer *new_layer; - + const int mode = RNA_enum_get(op->ptr, "mode"); + const bool dup_strokes = (bool)(mode == GP_LAYER_DUPLICATE_ALL); /* sanity checks */ if (ELEM(NULL, gpd, gpl)) { return OPERATOR_CANCELLED; } /* make copy of layer, and add it immediately after the existing layer */ - new_layer = BKE_gpencil_layer_duplicate(gpl); + new_layer = BKE_gpencil_layer_duplicate(gpl, true, dup_strokes); BLI_insertlinkafter(&gpd->layers, gpl, new_layer); /* ensure new layer has a unique name, and is now the active layer */ @@ -484,6 +489,12 @@ static int gpencil_layer_copy_exec(bContext *C, wmOperator *UNUSED(op)) void GPENCIL_OT_layer_duplicate(wmOperatorType *ot) { + static const EnumPropertyItem copy_mode[] = { + {GP_LAYER_DUPLICATE_ALL, "ALL", 0, "All Data", ""}, + {GP_LAYER_DUPLICATE_EMPTY, "EMPTY", 0, "Empty Keyframes", ""}, + {0, NULL, 0, NULL, NULL}, + }; + /* identifiers */ ot->name = "Duplicate Layer"; ot->idname = "GPENCIL_OT_layer_duplicate"; @@ -495,6 +506,8 @@ void GPENCIL_OT_layer_duplicate(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_DUPLICATE_ALL, "Mode", ""); } /* ********************* Duplicate Layer in a new object ************************** */ @@ -1560,7 +1573,7 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { continue; } /* some stroke is already at front*/ @@ -1725,7 +1738,7 @@ static int gpencil_stroke_change_color_exec(bContext *C, wmOperator *op) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { continue; } @@ -2849,12 +2862,12 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) mul_v3_m3v3(offset_local, imat, offset_global); LISTBASE_FOREACH (bGPDlayer *, gpl_src, &gpd_src->layers) { - bGPDlayer *gpl_new = BKE_gpencil_layer_duplicate(gpl_src); + bGPDlayer *gpl_new = BKE_gpencil_layer_duplicate(gpl_src, true, true); float diff_mat[4][4]; float inverse_diff_mat[4][4]; /* recalculate all stroke points */ - BKE_gpencil_parent_matrix_get(depsgraph, ob_iter, gpl_src, diff_mat); + BKE_gpencil_layer_transform_matrix_get(depsgraph, ob_iter, gpl_src, diff_mat); invert_m4_m4_safe_ortho(inverse_diff_mat, diff_mat); Material *ma_src = NULL; @@ -3388,7 +3401,7 @@ static int gpencil_material_select_exec(bContext *C, wmOperator *op) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { continue; } diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index d1c8eca1be0..b5269bbfacf 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -1867,7 +1867,7 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op) } /* Check if the color is editable. */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { continue; } @@ -2610,7 +2610,7 @@ static int gpencil_delete_selected_points(bContext *C) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { continue; } @@ -2800,7 +2800,7 @@ static int gpencil_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) float diff_mat[4][4]; /* calculate difference matrix object */ - BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* skip strokes that are invalid for current view */ @@ -2808,7 +2808,7 @@ static int gpencil_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) { continue; } @@ -2935,7 +2935,7 @@ static int gpencil_snap_to_cursor(bContext *C, wmOperator *op) float diff_mat[4][4]; /* calculate difference matrix */ - BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { bGPDspoint *pt; @@ -2946,7 +2946,7 @@ static int gpencil_snap_to_cursor(bContext *C, wmOperator *op) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) { continue; } /* only continue if this stroke is selected (editable doesn't guarantee this)... */ @@ -3039,7 +3039,7 @@ static bool gpencil_stroke_points_centroid(Depsgraph *depsgraph, float diff_mat[4][4]; /* calculate difference matrix */ - BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { bGPDspoint *pt; @@ -3050,7 +3050,7 @@ static bool gpencil_stroke_points_centroid(Depsgraph *depsgraph, continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) { continue; } /* only continue if this stroke is selected (editable doesn't guarantee this)... */ @@ -3565,7 +3565,7 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op) continue; } /* check if the color is editable. */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { continue; } elem = &strokes_list[tot_strokes]; @@ -3697,7 +3697,7 @@ static int gpencil_stroke_flip_exec(bContext *C, wmOperator *op) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { continue; } @@ -4516,7 +4516,7 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { continue; } /* Separate selected strokes. */ @@ -4717,7 +4717,7 @@ static int gpencil_stroke_split_exec(bContext *C, wmOperator *op) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { continue; } /* Split selected strokes. */ diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 3c16a6fb028..8e17816dcc1 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -81,6 +81,7 @@ #define LEAK_HORZ 0 #define LEAK_VERT 1 +#define MIN_WINDOW_SIZE 128 /* Temporary fill operation data (op->customdata) */ typedef struct tGPDfill { @@ -137,7 +138,7 @@ typedef struct tGPDfill { /** boundary limits drawing mode */ int fill_draw_mode; /* scaling factor */ - short fill_factor; + float fill_factor; /* Frame to use. */ int active_cfra; @@ -262,14 +263,14 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4]) BLI_assert(gpl_active_index >= 0); LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { - /* calculate parent position */ - BKE_gpencil_parent_matrix_get(tgpw.depsgraph, ob, gpl, tgpw.diff_mat); - /* do not draw layer if hidden */ if (gpl->flag & GP_LAYER_HIDE) { continue; } + /* calculate parent position */ + BKE_gpencil_layer_transform_matrix_get(tgpw.depsgraph, ob, gpl, tgpw.diff_mat); + /* Decide if the strokes of layers are included or not depending on the layer mode. * Cannot skip the layer because it can use boundary strokes and must be used. */ bool skip = false; @@ -398,8 +399,8 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf) /* resize region */ tgpf->region->winrct.xmin = 0; tgpf->region->winrct.ymin = 0; - tgpf->region->winrct.xmax = (int)tgpf->region->winx * tgpf->fill_factor; - tgpf->region->winrct.ymax = (int)tgpf->region->winy * tgpf->fill_factor; + tgpf->region->winrct.xmax = max_ii((int)tgpf->region->winx * tgpf->fill_factor, MIN_WINDOW_SIZE); + tgpf->region->winrct.ymax = max_ii((int)tgpf->region->winy * tgpf->fill_factor, MIN_WINDOW_SIZE); tgpf->region->winx = (short)abs(tgpf->region->winrct.xmax - tgpf->region->winrct.xmin); tgpf->region->winy = (short)abs(tgpf->region->winrct.ymax - tgpf->region->winrct.ymin); @@ -456,7 +457,7 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf) } GPU_matrix_push_projection(); - GPU_matrix_identity_set(); + GPU_matrix_identity_projection_set(); GPU_matrix_push(); GPU_matrix_identity_set(); @@ -1274,7 +1275,7 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) float origin[3]; ED_gpencil_drawing_reference_get(tgpf->scene, tgpf->ob, ts->gpencil_v3d_align, origin); ED_gpencil_project_stroke_to_plane( - tgpf->scene, tgpf->ob, tgpf->rv3d, gps, origin, tgpf->lock_axis - 1); + tgpf->scene, tgpf->ob, tgpf->rv3d, tgpf->gpl, gps, origin, tgpf->lock_axis - 1); } /* if parented change position relative to parent object */ @@ -1394,11 +1395,12 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *UNUSED(op)) Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); tgpf->brush = brush; tgpf->flag = brush->gpencil_settings->flag; - tgpf->fill_leak = brush->gpencil_settings->fill_leak; tgpf->fill_threshold = brush->gpencil_settings->fill_threshold; tgpf->fill_simplylvl = brush->gpencil_settings->fill_simplylvl; tgpf->fill_draw_mode = brush->gpencil_settings->fill_draw_mode; - tgpf->fill_factor = (short)max_ii(1, min_ii((int)brush->gpencil_settings->fill_factor, 8)); + tgpf->fill_factor = max_ff(GPENCIL_MIN_FILL_FAC, + min_ff(brush->gpencil_settings->fill_factor, 8.0f)); + tgpf->fill_leak = (int)ceil((float)brush->gpencil_settings->fill_leak * tgpf->fill_factor); int totcol = tgpf->ob->totcol; diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 4a908eff92e..5fea46626d5 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -667,7 +667,8 @@ struct GP_EditableStrokes_Iter { bGPDframe *init_gpf_ = (is_multiedit_) ? gpl->frames.first : gpl->actframe; \ for (bGPDframe *gpf_ = init_gpf_; gpf_; gpf_ = gpf_->next) { \ if ((gpf_ == gpl->actframe) || ((gpf_->flag & GP_FRAME_SELECT) && is_multiedit_)) { \ - BKE_gpencil_parent_matrix_get(depsgraph_, obact_, gpl, gpstroke_iter.diff_mat); \ + BKE_gpencil_layer_transform_matrix_get( \ + depsgraph_, obact_, gpl, gpstroke_iter.diff_mat); \ invert_m4_m4(gpstroke_iter.inverse_diff_mat, gpstroke_iter.diff_mat); \ /* loop over strokes */ \ bGPDstroke *gpsn_; \ @@ -678,7 +679,7 @@ struct GP_EditableStrokes_Iter { continue; \ } \ /* check if the color is editable */ \ - if (ED_gpencil_stroke_color_use(obact_, gpl, gps) == false) { \ + if (ED_gpencil_stroke_material_editable(obact_, gpl, gps) == false) { \ continue; \ } \ /* ... Do Stuff With Strokes ... */ @@ -718,7 +719,8 @@ struct GP_EditableStrokes_Iter { bGPDframe *init_gpf_ = (is_multiedit_) ? gpl->frames.first : gpl->actframe; \ for (bGPDframe *gpf_ = init_gpf_; gpf_; gpf_ = gpf_->next) { \ if ((gpf_ == gpl->actframe) || ((gpf_->flag & GP_FRAME_SELECT) && is_multiedit_)) { \ - BKE_gpencil_parent_matrix_get(depsgraph_, obact_, gpl, gpstroke_iter.diff_mat); \ + BKE_gpencil_layer_transform_matrix_get( \ + depsgraph_, obact_, gpl, gpstroke_iter.diff_mat); \ invert_m4_m4(gpstroke_iter.inverse_diff_mat, gpstroke_iter.diff_mat); \ /* loop over strokes */ \ bGPDstroke *gpsn_; \ @@ -767,8 +769,10 @@ struct GP_EditableStrokes_Iter { bGPDframe *init_gpf_ = (is_multiedit_) ? gpl->frames.first : gpl->actframe; \ for (bGPDframe *gpf_ = init_gpf_; gpf_; gpf_ = gpf_->next) { \ if ((gpf_ == gpl->actframe) || ((gpf_->flag & GP_FRAME_SELECT) && is_multiedit_)) { \ - BKE_gpencil_parent_matrix_get(depsgraph_, obact_, gpl, gpstroke_iter.diff_mat); \ - invert_m4_m4(gpstroke_iter.inverse_diff_mat, gpstroke_iter.diff_mat); \ + BKE_gpencil_layer_transform_matrix_get( \ + depsgraph_, obact_, gpl, gpstroke_iter.diff_mat); \ + /* Undo layer transform. */ \ + mul_m4_m4m4(gpstroke_iter.diff_mat, gpstroke_iter.diff_mat, gpl->layer_invmat); \ /* loop over strokes */ \ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf_->strokes) { \ /* skip strokes that are invalid for current view */ \ @@ -776,7 +780,7 @@ struct GP_EditableStrokes_Iter { continue; \ } \ /* check if the color is editable */ \ - if (ED_gpencil_stroke_color_use(obact_, gpl, gps) == false) { \ + if (ED_gpencil_stroke_material_editable(obact_, gpl, gps) == false) { \ continue; \ } \ /* ... Do Stuff With Strokes ... */ diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index c666fcb67b7..2e756cf9c8d 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -229,7 +229,7 @@ static bool gpencil_interpolate_check_todo(bContext *C, bGPdata *gpd) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps_from) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps_from) == false) { continue; } @@ -283,8 +283,8 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer"); tgpil->gpl = gpl; - tgpil->prevFrame = BKE_gpencil_frame_duplicate(gpl->actframe); - tgpil->nextFrame = BKE_gpencil_frame_duplicate(gpl->actframe->next); + tgpil->prevFrame = BKE_gpencil_frame_duplicate(gpl->actframe, true); + tgpil->nextFrame = BKE_gpencil_frame_duplicate(gpl->actframe->next, true); BLI_addtail(&tgpi->ilayers, tgpil); @@ -315,7 +315,7 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, tgpil->gpl, gps_from) == false) { + if (ED_gpencil_stroke_material_editable(ob, tgpil->gpl, gps_from) == false) { valid = false; } @@ -734,7 +734,7 @@ void GPENCIL_OT_interpolate(wmOperatorType *ot) ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; /* properties */ - RNA_def_float_percentage( + RNA_def_float_factor( ot->srna, "shift", 0.0f, @@ -1008,8 +1008,8 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) } /* store extremes */ - prevFrame = BKE_gpencil_frame_duplicate(gpl->actframe); - nextFrame = BKE_gpencil_frame_duplicate(gpl->actframe->next); + prevFrame = BKE_gpencil_frame_duplicate(gpl->actframe, true); + nextFrame = BKE_gpencil_frame_duplicate(gpl->actframe->next, true); /* Loop over intermediary frames and create the interpolation */ for (cframe = prevFrame->framenum + step; cframe < nextFrame->framenum; cframe += step) { @@ -1048,7 +1048,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps_from) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps_from) == false) { continue; } diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index fcdada1e673..b55e47d74f3 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -426,7 +426,7 @@ static void gpencil_reproject_toplane(tGPsdata *p, bGPDstroke *gps) /* get drawing origin */ gpencil_get_3d_reference(p, origin); - ED_gpencil_project_stroke_to_plane(p->scene, obact, rv3d, gps, origin, p->lock_axis - 1); + ED_gpencil_project_stroke_to_plane(p->scene, obact, rv3d, p->gpl, gps, origin, p->lock_axis - 1); } /* convert screen-coordinates to buffer-coordinates */ @@ -887,11 +887,13 @@ static short gpencil_stroke_addpoint(tGPsdata *p, gpencil_get_3d_reference(p, origin); /* reproject current */ ED_gpencil_tpoint_to_point(p->region, origin, pt, &spt); - ED_gpencil_project_point_to_plane(p->scene, obact, rv3d, origin, p->lock_axis - 1, &spt); + ED_gpencil_project_point_to_plane( + p->scene, obact, p->gpl, rv3d, origin, p->lock_axis - 1, &spt); /* reproject previous */ ED_gpencil_tpoint_to_point(p->region, origin, ptb, &spt2); - ED_gpencil_project_point_to_plane(p->scene, obact, rv3d, origin, p->lock_axis - 1, &spt2); + ED_gpencil_project_point_to_plane( + p->scene, obact, p->gpl, rv3d, origin, p->lock_axis - 1, &spt2); p->totpixlen += len_v3v3(&spt.x, &spt2.x); pt->uv_fac = p->totpixlen; } @@ -1349,7 +1351,7 @@ static bool gpencil_stroke_eraser_is_occluded(tGPsdata *p, float diff_mat[4][4]; /* calculate difference matrix if parent object */ - BKE_gpencil_parent_matrix_get(p->depsgraph, obact, gpl, diff_mat); + BKE_gpencil_layer_transform_matrix_get(p->depsgraph, obact, gpl, diff_mat); if (ED_view3d_autodist_simple(p->region, mval_i, mval_3d, 0, NULL)) { const float depth_mval = view3d_point_depth(rv3d, mval_3d); @@ -1731,12 +1733,12 @@ static void gpencil_stroke_doeraser(tGPsdata *p) continue; } /* calculate difference matrix */ - BKE_gpencil_parent_matrix_get(p->depsgraph, p->ob, gpl, p->diff_mat); + BKE_gpencil_layer_transform_matrix_get(p->depsgraph, p->ob, gpl, p->diff_mat); /* loop over strokes, checking segments for intersections */ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(p->ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(p->ob, gpl, gps) == false) { continue; } @@ -2103,6 +2105,11 @@ static void gpencil_paint_initstroke(tGPsdata *p, copy_v3_v3(p->gpl->color, p->custom_color); } } + + /* Recalculate layer transform matrix to avoid problems if props are animated. */ + loc_eul_size_to_mat4(p->gpl->layer_mat, p->gpl->location, p->gpl->rotation, p->gpl->scale); + invert_m4_m4(p->gpl->layer_invmat, p->gpl->layer_mat); + if ((paintmode != GP_PAINTMODE_ERASER) && (p->gpl->flag & GP_LAYER_LOCKED)) { p->status = GP_STATUS_ERROR; if (G.debug & G_DEBUG) { diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index a742d149fce..3a0e1ce3d15 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -317,6 +317,10 @@ static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) } tgpi->gpl = gpl; + /* Recalculate layer transform matrix to avoid problems if props are animated. */ + loc_eul_size_to_mat4(gpl->layer_mat, gpl->location, gpl->rotation, gpl->scale); + invert_m4_m4(gpl->layer_invmat, gpl->layer_mat); + /* create a new temporary frame */ tgpi->gpf = MEM_callocN(sizeof(bGPDframe), "Temp bGPDframe"); tgpi->gpf->framenum = tgpi->cframe = cfra; @@ -1004,12 +1008,12 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) /* reproject current */ ED_gpencil_tpoint_to_point(tgpi->region, origin, tpt, &spt); ED_gpencil_project_point_to_plane( - tgpi->scene, tgpi->ob, tgpi->rv3d, origin, tgpi->lock_axis - 1, &spt); + tgpi->scene, tgpi->ob, tgpi->gpl, tgpi->rv3d, origin, tgpi->lock_axis - 1, &spt); /* reproject previous */ ED_gpencil_tpoint_to_point(tgpi->region, origin, tptb, &spt2); ED_gpencil_project_point_to_plane( - tgpi->scene, tgpi->ob, tgpi->rv3d, origin, tgpi->lock_axis - 1, &spt2); + tgpi->scene, tgpi->ob, tgpi->gpl, tgpi->rv3d, origin, tgpi->lock_axis - 1, &spt2); tgpi->totpixlen += len_v3v3(&spt.x, &spt2.x); tpt->uv_fac = tgpi->totpixlen; } @@ -1068,7 +1072,7 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) float origin[3]; ED_gpencil_drawing_reference_get(tgpi->scene, tgpi->ob, ts->gpencil_v3d_align, origin); ED_gpencil_project_stroke_to_plane( - tgpi->scene, tgpi->ob, tgpi->rv3d, gps, origin, ts->gp_sculpt.lock_axis - 1); + tgpi->scene, tgpi->ob, tgpi->rv3d, tgpi->gpl, gps, origin, ts->gp_sculpt.lock_axis - 1); } /* if parented change position relative to parent object */ diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index bb9dd8cac5d..0d3ab9011d6 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -1441,11 +1441,6 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso, bool changed = false; float rot_eval = 0.0f; - /* Check if the stroke collide with brush. */ - if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, diff_mat)) { - return false; - } - if (gps->totpoints == 1) { bGPDspoint pt_temp; pt = &gps->points[0]; @@ -1577,6 +1572,13 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C, Object *ob = gso->object; bGPdata *gpd = ob->data; char tool = gso->brush->gpencil_sculpt_tool; + GP_SpaceConversion *gsc = &gso->gsc; + Brush *brush = gso->brush; + const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure : + gso->brush->size; + /* Calc bound box matrix. */ + float bound_mat[4][4]; + BKE_gpencil_layer_transform_matrix_get(gso->depsgraph, gso->object, gpl, bound_mat); LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* skip strokes that are invalid for current view */ @@ -1584,7 +1586,12 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C, continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { + continue; + } + + /* Check if the stroke collide with brush. */ + if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) { continue; } @@ -1742,7 +1749,8 @@ static bool gpencil_sculpt_brush_apply_standard(bContext *C, tGP_BrushEditData * /* calculate difference matrix */ float diff_mat[4][4]; - BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); + mul_m4_m4m4(diff_mat, diff_mat, gpl->layer_invmat); /* Active Frame or MultiFrame? */ if (gso->is_multiframe) { diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index 281ab8c5adc..a00d21bf88a 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -2473,7 +2473,7 @@ static void gpencil_selected_hue_table(bContext *C, if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; } - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { continue; } if ((gps->flag & GP_STROKE_SELECT) == 0) { diff --git a/source/blender/editors/gpencil/gpencil_trace_ops.c b/source/blender/editors/gpencil/gpencil_trace_ops.c index 0be9d74278e..8d3ae9567cf 100644 --- a/source/blender/editors/gpencil/gpencil_trace_ops.c +++ b/source/blender/editors/gpencil/gpencil_trace_ops.c @@ -87,6 +87,7 @@ typedef struct TraceJob { bGPDlayer *gpl; bool was_ob_created; + bool use_current_frame; int32_t frame_target; float threshold; @@ -228,15 +229,17 @@ static void trace_start_job(void *customdata, short *stop, short *do_update, flo trace_job->do_update = do_update; trace_job->progress = progress; trace_job->was_canceled = false; + const int init_frame = max_ii((trace_job->use_current_frame) ? trace_job->frame_target : 0, 0); G.is_break = false; /* Single Image. */ - if ((trace_job->image->source == IMA_SRC_FILE) || (trace_job->mode == GPENCIL_TRACE_MODE_SINGLE)) { void *lock; - ImBuf *ibuf = BKE_image_acquire_ibuf(trace_job->image, NULL, &lock); + ImageUser *iuser = trace_job->ob_active->iuser; + iuser->framenr = init_frame; + ImBuf *ibuf = BKE_image_acquire_ibuf(trace_job->image, iuser, &lock); if (ibuf) { /* Create frame. */ bGPDframe *gpf = BKE_gpencil_layer_frame_get( @@ -249,7 +252,7 @@ static void trace_start_job(void *customdata, short *stop, short *do_update, flo /* Image sequence. */ else if (trace_job->image->type == IMA_TYPE_IMAGE) { ImageUser *iuser = trace_job->ob_active->iuser; - for (int i = 0; i < iuser->frames; i++) { + for (int i = init_frame; i < iuser->frames; i++) { if (G.is_break) { trace_job->was_canceled = true; break; @@ -320,6 +323,7 @@ static int gpencil_trace_image_exec(bContext *C, wmOperator *op) job->ob_active = job->base_active->object; job->image = (Image *)job->ob_active->data; job->frame_target = CFRA; + job->use_current_frame = RNA_boolean_get(op->ptr, "use_current_frame"); /* Create a new grease pencil object or reuse selected. */ eGP_TargetObjectMode target = RNA_enum_get(op->ptr, "target"); @@ -493,4 +497,9 @@ void GPENCIL_OT_trace_image(wmOperatorType *ot) GPENCIL_TRACE_MODE_SINGLE, "Mode", "Determines if trace simple image or full sequence"); + RNA_def_boolean(ot->srna, + "use_current_frame", + true, + "Start At Current Frame", + "Trace Image starting in current image frame"); } diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c index c2504ce329e..e545a3365e9 100644 --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -61,28 +61,22 @@ int ED_gpencil_session_active(void) return (BLI_listbase_is_empty(&undo_nodes) == false); } -int ED_undo_gpencil_step(bContext *C, int step, const char *name) +int ED_undo_gpencil_step(bContext *C, const int step) { bGPdata **gpd_ptr = NULL, *new_gpd = NULL; gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); - if (step == 1) { /* undo */ - // printf("\t\tGP - undo step\n"); + if (step == -1) { /* undo */ if (cur_node->prev) { - if (!name || STREQ(cur_node->name, name)) { - cur_node = cur_node->prev; - new_gpd = cur_node->gpd; - } + cur_node = cur_node->prev; + new_gpd = cur_node->gpd; } } - else if (step == -1) { - // printf("\t\tGP - redo step\n"); + else if (step == 1) { if (cur_node->next) { - if (!name || STREQ(cur_node->name, name)) { - cur_node = cur_node->next; - new_gpd = cur_node->gpd; - } + cur_node = cur_node->next; + new_gpd = cur_node->gpd; } } @@ -99,7 +93,7 @@ int ED_undo_gpencil_step(bContext *C, int step, const char *name) LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* make a copy of source layer and its data */ - gpld = BKE_gpencil_layer_duplicate(gpl); + gpld = BKE_gpencil_layer_duplicate(gpl, true, true); BLI_addtail(&gpd->layers, gpld); } } diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 7c796f7b7a1..42e3055ef65 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -579,7 +579,7 @@ bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps) } /* Check whether given stroke can be edited for the current color */ -bool ED_gpencil_stroke_color_use(Object *ob, const bGPDlayer *gpl, const bGPDstroke *gps) +bool ED_gpencil_stroke_material_editable(Object *ob, const bGPDlayer *gpl, const bGPDstroke *gps) { /* check if the color is editable */ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); @@ -674,7 +674,7 @@ void gpencil_apply_parent(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl, b float inverse_diff_mat[4][4]; float fpt[3]; - BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); invert_m4_m4(inverse_diff_mat, diff_mat); for (i = 0; i < gps->totpoints; i++) { @@ -697,10 +697,11 @@ void gpencil_apply_parent_point(Depsgraph *depsgraph, float inverse_diff_mat[4][4]; float fpt[3]; - BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); invert_m4_m4(inverse_diff_mat, diff_mat); mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); + copy_v3_v3(&pt->x, fpt); } @@ -997,6 +998,12 @@ void ED_gpencil_drawing_reference_get(const Scene *scene, else { /* use object location */ copy_v3_v3(r_vec, ob->obmat[3]); + /* Apply layer offset. */ + bGPdata *gpd = ob->data; + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); + if (gpl != NULL) { + add_v3_v3(r_vec, gpl->layer_mat[3]); + } } } } @@ -1021,7 +1028,7 @@ void ED_gpencil_project_stroke_to_view(bContext *C, bGPDlayer *gpl, bGPDstroke * /* init space conversion stuff */ gpencil_point_conversion_init(C, &gsc); - BKE_gpencil_parent_matrix_get(depsgraph, ob, gpl, diff_mat); + BKE_gpencil_layer_transform_matrix_get(depsgraph, ob, gpl, diff_mat); invert_m4_m4(inverse_diff_mat, diff_mat); /* Adjust each point */ @@ -1046,6 +1053,7 @@ void ED_gpencil_project_stroke_to_view(bContext *C, bGPDlayer *gpl, bGPDstroke * void ED_gpencil_project_stroke_to_plane(const Scene *scene, const Object *ob, const RegionView3D *rv3d, + bGPDlayer *gpl, bGPDstroke *gps, const float origin[3], const int axis) @@ -1058,6 +1066,10 @@ void ED_gpencil_project_stroke_to_plane(const Scene *scene, float ray[3]; float rpoint[3]; + /* Recalculate layer transform matrix. */ + loc_eul_size_to_mat4(gpl->layer_mat, gpl->location, gpl->rotation, gpl->scale); + invert_m4_m4(gpl->layer_invmat, gpl->layer_mat); + /* normal vector for a plane locked to axis */ zero_v3(plane_normal); if (axis < 0) { @@ -1074,24 +1086,27 @@ void ED_gpencil_project_stroke_to_plane(const Scene *scene, copy_m4_m4(mat, ob->obmat); /* move origin to cursor */ + if ((ts->gpencil_v3d_align & GP_PROJECT_CURSOR) == 0) { + if (gpl != NULL) { + add_v3_v3(mat[3], gpl->location); + } + } if (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) { copy_v3_v3(mat[3], cursor->location); } mul_mat3_m4_v3(mat, plane_normal); } + + if ((gpl != NULL) && (ts->gp_sculpt.lock_axis != GP_LOCKAXIS_CURSOR)) { + mul_mat3_m4_v3(gpl->layer_mat, plane_normal); + } } else { const float scale[3] = {1.0f, 1.0f, 1.0f}; plane_normal[2] = 1.0f; float mat[4][4]; loc_eul_size_to_mat4(mat, cursor->location, cursor->rotation_euler, scale); - - /* move origin to object */ - if ((ts->gpencil_v3d_align & GP_PROJECT_CURSOR) == 0) { - copy_v3_v3(mat[3], ob->obmat[3]); - } - mul_mat3_m4_v3(mat, plane_normal); } @@ -1127,8 +1142,12 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph, ARegion *region = gsc->region; RegionView3D *rv3d = region->regiondata; + /* Recalculate layer transform matrix. */ + loc_eul_size_to_mat4(gpl->layer_mat, gpl->location, gpl->rotation, gpl->scale); + invert_m4_m4(gpl->layer_invmat, gpl->layer_mat); + float diff_mat[4][4], inverse_diff_mat[4][4]; - BKE_gpencil_parent_matrix_get(depsgraph, gsc->ob, gpl, diff_mat); + BKE_gpencil_layer_transform_matrix_get(depsgraph, gsc->ob, gpl, diff_mat); invert_m4_m4(inverse_diff_mat, diff_mat); float origin[3]; @@ -1194,7 +1213,7 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph, } } - ED_gpencil_project_point_to_plane(gsc->scene, gsc->ob, rv3d, origin, axis, &pt2); + ED_gpencil_project_point_to_plane(gsc->scene, gsc->ob, gpl, rv3d, origin, axis, &pt2); copy_v3_v3(&pt->x, &pt2.x); @@ -1250,6 +1269,7 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph, */ void ED_gpencil_project_point_to_plane(const Scene *scene, const Object *ob, + bGPDlayer *gpl, const RegionView3D *rv3d, const float origin[3], const int axis, @@ -1277,6 +1297,11 @@ void ED_gpencil_project_point_to_plane(const Scene *scene, if (ob && (ob->type == OB_GPENCIL)) { float mat[4][4]; copy_m4_m4(mat, ob->obmat); + if ((ts->gpencil_v3d_align & GP_PROJECT_CURSOR) == 0) { + if (gpl != NULL) { + add_v3_v3(mat[3], gpl->location); + } + } /* move origin to cursor */ if (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) { @@ -1284,6 +1309,10 @@ void ED_gpencil_project_point_to_plane(const Scene *scene, } mul_mat3_m4_v3(mat, plane_normal); + /* Apply layer rotation (local transform). */ + if ((gpl != NULL) && (ts->gp_sculpt.lock_axis != GP_LOCKAXIS_CURSOR)) { + mul_mat3_m4_v3(gpl->layer_mat, plane_normal); + } } } else { @@ -1449,7 +1478,7 @@ void ED_gpencil_reset_layers_parent(Depsgraph *depsgraph, Object *obact, bGPdata /* only redo if any change */ if (!equals_m4m4(gpl->inverse, cur_mat)) { /* first apply current transformation to all strokes */ - BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); /* undo local object */ sub_v3_v3(diff_mat[3], gpl_loc); @@ -2144,7 +2173,7 @@ void ED_gpencil_update_color_uv(Main *bmain, Material *mat) LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* check if it is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { continue; } gps_ma = BKE_gpencil_material(ob, gps->mat_nr + 1); @@ -3126,7 +3155,7 @@ bGPDstroke *ED_gpencil_stroke_nearest_to_ends(bContext *C, /* calculate difference matrix object */ float diff_mat[4][4]; - BKE_gpencil_parent_matrix_get(depsgraph, ob, gpl, diff_mat); + BKE_gpencil_layer_transform_matrix_get(depsgraph, ob, gpl, diff_mat); /* Calculate the extremes of the stroke in 2D. */ bGPDspoint pt_parent; @@ -3144,15 +3173,13 @@ bGPDstroke *ED_gpencil_stroke_nearest_to_ends(bContext *C, float dist_min = FLT_MAX; LISTBASE_FOREACH (bGPDstroke *, gps_target, &gpf->strokes) { /* Check if the color is editable. */ - if ((gps_target == gps) || (ED_gpencil_stroke_color_use(ob, gpl, gps) == false)) { + if ((gps_target == gps) || (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false)) { continue; } /* Check if one of the ends is inside target stroke bounding box. */ - if (!ED_gpencil_stroke_check_collision(gsc, gps, pt2d_start, radius, diff_mat)) { - continue; - } - if (!ED_gpencil_stroke_check_collision(gsc, gps, pt2d_end, radius, diff_mat)) { + if ((!ED_gpencil_stroke_check_collision(gsc, gps_target, pt2d_start, radius, diff_mat)) && + (!ED_gpencil_stroke_check_collision(gsc, gps_target, pt2d_end, radius, diff_mat))) { continue; } /* Check the distance of the ends with the ends of target stroke to avoid middle contact. diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c index b212872b607..49f45acdb11 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_ops.c +++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c @@ -674,7 +674,7 @@ static bool gpencil_extract_palette_from_vertex(bContext *C, if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; } - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { continue; } MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); @@ -859,7 +859,7 @@ static int gpencil_material_to_vertex_exec(bContext *C, wmOperator *op) if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; } - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { continue; } diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c index e2c81d53fba..ae94836af03 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_paint.c +++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c @@ -825,7 +825,8 @@ static void gpencil_save_selected_point(tGP_BrushVertexpaintData *gso, static bool gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso, bGPDstroke *gps, const char tool, - const float diff_mat[4][4]) + const float diff_mat[4][4], + const float bound_mat[4][4]) { GP_SpaceConversion *gsc = &gso->gsc; rcti *rect = &gso->brush_rect; @@ -853,7 +854,7 @@ static bool gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso, } /* Check if the stroke collide with brush. */ - if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, diff_mat)) { + if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) { return false; } @@ -998,7 +999,8 @@ static bool gpencil_vertexpaint_brush_do_frame(bContext *C, tGP_BrushVertexpaintData *gso, bGPDlayer *gpl, bGPDframe *gpf, - const float diff_mat[4][4]) + const float diff_mat[4][4], + const float bound_mat[4][4]) { Object *ob = CTX_data_active_object(C); const char tool = ob->mode == OB_MODE_VERTEX_GPENCIL ? gso->brush->gpencil_vertex_tool : @@ -1020,12 +1022,12 @@ static bool gpencil_vertexpaint_brush_do_frame(bContext *C, continue; } /* Check if the color is editable. */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { continue; } /* Check points below the brush. */ - bool hit = gpencil_vertexpaint_select_stroke(gso, gps, tool, diff_mat); + bool hit = gpencil_vertexpaint_select_stroke(gso, gps, tool, diff_mat, bound_mat); /* If stroke was hit and has an editcurve the curve needs an update. */ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps; @@ -1130,9 +1132,11 @@ static bool gpencil_vertexpaint_brush_apply_to_layers(bContext *C, tGP_BrushVert continue; } - /* calculate difference matrix */ - float diff_mat[4][4]; - BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + /* Calculate transform matrix. */ + float diff_mat[4][4], bound_mat[4][4]; + BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); + copy_m4_m4(bound_mat, diff_mat); + mul_m4_m4m4(diff_mat, diff_mat, gpl->layer_invmat); /* Active Frame or MultiFrame? */ if (gso->is_multiframe) { @@ -1159,7 +1163,7 @@ static bool gpencil_vertexpaint_brush_apply_to_layers(bContext *C, tGP_BrushVert } /* affect strokes in this frame */ - changed |= gpencil_vertexpaint_brush_do_frame(C, gso, gpl, gpf, diff_mat); + changed |= gpencil_vertexpaint_brush_do_frame(C, gso, gpl, gpf, diff_mat, bound_mat); } } } @@ -1167,7 +1171,8 @@ static bool gpencil_vertexpaint_brush_apply_to_layers(bContext *C, tGP_BrushVert /* Apply to active frame's strokes */ if (gpl->actframe != NULL) { gso->mf_falloff = 1.0f; - changed |= gpencil_vertexpaint_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat); + changed |= gpencil_vertexpaint_brush_do_frame( + C, gso, gpl, gpl->actframe, diff_mat, bound_mat); } } } diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c index 7fa71fcce3c..a3e5ece5862 100644 --- a/source/blender/editors/gpencil/gpencil_weight_paint.c +++ b/source/blender/editors/gpencil/gpencil_weight_paint.c @@ -383,7 +383,8 @@ static void gpencil_save_selected_point(tGP_BrushWeightpaintData *gso, /* Select points in this stroke and add to an array to be used later. */ static void gpencil_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso, bGPDstroke *gps, - const float diff_mat[4][4]) + const float diff_mat[4][4], + const float bound_mat[4][4]) { GP_SpaceConversion *gsc = &gso->gsc; rcti *rect = &gso->brush_rect; @@ -402,7 +403,7 @@ static void gpencil_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso, bool include_last = false; /* Check if the stroke collide with brush. */ - if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, diff_mat)) { + if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) { return; } @@ -505,7 +506,8 @@ static bool gpencil_weightpaint_brush_do_frame(bContext *C, tGP_BrushWeightpaintData *gso, bGPDlayer *gpl, bGPDframe *gpf, - const float diff_mat[4][4]) + const float diff_mat[4][4], + const float bound_mat[4][4]) { Object *ob = CTX_data_active_object(C); char tool = gso->brush->gpencil_weight_tool; @@ -526,12 +528,12 @@ static bool gpencil_weightpaint_brush_do_frame(bContext *C, continue; } /* Check if the color is editable. */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { continue; } /* Check points below the brush. */ - gpencil_weightpaint_select_stroke(gso, gps, diff_mat); + gpencil_weightpaint_select_stroke(gso, gps, diff_mat, bound_mat); } /*--------------------------------------------------------------------- @@ -578,9 +580,11 @@ static bool gpencil_weightpaint_brush_apply_to_layers(bContext *C, tGP_BrushWeig continue; } - /* calculate difference matrix */ - float diff_mat[4][4]; - BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + /* Calculate transform matrix. */ + float diff_mat[4][4], bound_mat[4][4]; + BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); + copy_m4_m4(bound_mat, diff_mat); + mul_m4_m4m4(diff_mat, diff_mat, gpl->layer_invmat); /* Active Frame or MultiFrame? */ if (gso->is_multiframe) { @@ -608,7 +612,7 @@ static bool gpencil_weightpaint_brush_apply_to_layers(bContext *C, tGP_BrushWeig } /* affect strokes in this frame */ - changed |= gpencil_weightpaint_brush_do_frame(C, gso, gpl, gpf, diff_mat); + changed |= gpencil_weightpaint_brush_do_frame(C, gso, gpl, gpf, diff_mat, bound_mat); } } } @@ -616,7 +620,8 @@ static bool gpencil_weightpaint_brush_apply_to_layers(bContext *C, tGP_BrushWeig if (gpl->actframe != NULL) { /* Apply to active frame's strokes */ gso->mf_falloff = 1.0f; - changed |= gpencil_weightpaint_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat); + changed |= gpencil_weightpaint_brush_do_frame( + C, gso, gpl, gpl->actframe, diff_mat, bound_mat); } } } diff --git a/source/blender/editors/include/ED_clip.h b/source/blender/editors/include/ED_clip.h index 0a66c439f79..56494f87bfe 100644 --- a/source/blender/editors/include/ED_clip.h +++ b/source/blender/editors/include/ED_clip.h @@ -99,6 +99,30 @@ void ED_space_clip_set_clip(struct bContext *C, struct Mask *ED_space_clip_get_mask(struct SpaceClip *sc); void ED_space_clip_set_mask(struct bContext *C, struct SpaceClip *sc, struct Mask *mask); +/* Locked state is used to preserve current clip editor viewport upon changes. Example usage: + * + * ... + * + * ClipViewLockState lock_state; + * ED_clip_view_lock_state_store(C, &lock_state); + * + * <change selection> + * + * ED_clip_view_lock_state_restore_no_jump(C, &lock_state); + * + * These function are to be used from space clip editor context only. Otherwise debug builds will + * assert, release builds will crash. */ + +typedef struct ClipViewLockState { + float offset_x, offset_y; + float lock_offset_x, lock_offset_y; + float zoom; +} ClipViewLockState; + +void ED_clip_view_lock_state_store(const struct bContext *C, ClipViewLockState *state); +void ED_clip_view_lock_state_restore_no_jump(const struct bContext *C, + const ClipViewLockState *state); + /* ** clip_ops.c ** */ void ED_operatormacros_clip(void); diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index 1b7caf27ecf..acaa6495ba9 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -147,9 +147,9 @@ bool ED_gpencil_has_keyframe_v3d(struct Scene *scene, struct Object *ob, int cfr bool ED_gpencil_stroke_can_use_direct(const struct ScrArea *area, const struct bGPDstroke *gps); bool ED_gpencil_stroke_can_use(const struct bContext *C, const struct bGPDstroke *gps); -bool ED_gpencil_stroke_color_use(struct Object *ob, - const struct bGPDlayer *gpl, - const struct bGPDstroke *gps); +bool ED_gpencil_stroke_material_editable(struct Object *ob, + const struct bGPDlayer *gpl, + const struct bGPDstroke *gps); /* ----------- Grease Pencil Operators ----------------- */ @@ -213,7 +213,7 @@ bool ED_gpencil_anim_copybuf_paste(struct bAnimContext *ac, const short copy_mod /* ------------ Grease-Pencil Undo System ------------------ */ int ED_gpencil_session_active(void); -int ED_undo_gpencil_step(struct bContext *C, int step, const char *name); +int ED_undo_gpencil_step(struct bContext *C, const int step); /* ------------ Grease-Pencil Armature ------------------ */ bool ED_gpencil_add_armature(const struct bContext *C, @@ -263,11 +263,13 @@ bool ED_object_gpencil_exit(struct Main *bmain, struct Object *ob); void ED_gpencil_project_stroke_to_plane(const struct Scene *scene, const struct Object *ob, const struct RegionView3D *rv3d, + struct bGPDlayer *gpl, struct bGPDstroke *gps, const float origin[3], const int axis); void ED_gpencil_project_point_to_plane(const struct Scene *scene, const struct Object *ob, + struct bGPDlayer *gpl, const struct RegionView3D *rv3d, const float origin[3], const int axis, diff --git a/source/blender/editors/include/ED_mask.h b/source/blender/editors/include/ED_mask.h index bcf52da3f69..b20dae694fc 100644 --- a/source/blender/editors/include/ED_mask.h +++ b/source/blender/editors/include/ED_mask.h @@ -63,7 +63,10 @@ void ED_mask_point_pos__reverse( struct ScrArea *area, struct ARegion *region, float x, float y, float *xr, float *yr); void ED_mask_cursor_location_get(struct ScrArea *area, float cursor[2]); -bool ED_mask_selected_minmax(const struct bContext *C, float min[2], float max[2]); +bool ED_mask_selected_minmax(const struct bContext *C, + float min[2], + float max[2], + bool include_handles); /* mask_draw.c */ void ED_mask_draw(const struct bContext *C, const char draw_flag, const char draw_type); diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index 417cae800ea..78f354a300d 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -51,6 +51,10 @@ typedef enum { #define NODE_GRID_STEPS 5 /* space_node.c */ + +void ED_node_cursor_location_get(const struct SpaceNode *snode, float value[2]); +void ED_node_cursor_location_set(struct SpaceNode *snode, const float value[2]); + int ED_node_tree_path_length(struct SpaceNode *snode); void ED_node_tree_path_get(struct SpaceNode *snode, char *value); void ED_node_tree_path_get_fixedbuf(struct SpaceNode *snode, char *value, int max_length); diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index bd1a4a0c63f..73326a2d5f2 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -159,7 +159,7 @@ extern struct EnumPropertyItem prop_clear_parent_types[]; extern struct EnumPropertyItem prop_make_parent_types[]; #endif -/* Set the object's parent, return true iff successful. */ +/* Set the object's parent, return true if successful. */ bool ED_object_parent_set(struct ReportList *reports, const struct bContext *C, struct Scene *scene, diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 20417634020..deb6b7502c7 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -57,15 +57,14 @@ struct wmMsgSubscribeKey; struct wmMsgSubscribeValue; struct wmNotifier; struct wmOperatorType; +struct wmRegionListenerParams; +struct wmRegionMessageSubscribeParams; +struct wmSpaceTypeListenerParams; struct wmWindow; struct wmWindowManager; /* regions */ -void ED_region_do_listen(struct wmWindow *win, - struct ScrArea *area, - struct ARegion *region, - struct wmNotifier *note, - const Scene *scene); +void ED_region_do_listen(struct wmRegionListenerParams *params); void ED_region_do_layout(struct bContext *C, struct ARegion *region); void ED_region_do_draw(struct bContext *C, struct ARegion *region); void ED_region_exit(struct bContext *C, struct ARegion *region); @@ -144,29 +143,11 @@ void ED_area_do_msg_notify_tag_refresh(struct bContext *C, struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValue *msg_val); -void ED_area_do_mgs_subscribe_for_tool_header(const struct bContext *C, - struct WorkSpace *workspace, - struct Scene *scene, - struct bScreen *screen, - struct ScrArea *area, - struct ARegion *region, - struct wmMsgBus *mbus); -void ED_area_do_mgs_subscribe_for_tool_ui(const struct bContext *C, - struct WorkSpace *workspace, - struct Scene *scene, - struct bScreen *screen, - struct ScrArea *area, - struct ARegion *region, - struct wmMsgBus *mbus); +void ED_area_do_mgs_subscribe_for_tool_header(const struct wmRegionMessageSubscribeParams *params); +void ED_area_do_mgs_subscribe_for_tool_ui(const struct wmRegionMessageSubscribeParams *params); /* message bus */ -void ED_region_message_subscribe(struct bContext *C, - struct WorkSpace *workspace, - struct Scene *scene, - struct bScreen *screen, - struct ScrArea *area, - struct ARegion *region, - struct wmMsgBus *mbus); +void ED_region_message_subscribe(struct wmRegionMessageSubscribeParams *params); /* spaces */ void ED_spacetypes_keymap(struct wmKeyConfig *keyconf); @@ -178,7 +159,7 @@ void ED_area_exit(struct bContext *C, struct ScrArea *area); int ED_screen_area_active(const struct bContext *C); void ED_screen_global_areas_refresh(struct wmWindow *win); void ED_screen_global_areas_sync(struct wmWindow *win); -void ED_area_do_listen(struct wmWindow *win, ScrArea *area, struct wmNotifier *note, Scene *scene); +void ED_area_do_listen(struct wmSpaceTypeListenerParams *params); void ED_area_tag_redraw(ScrArea *area); void ED_area_tag_redraw_no_rebuild(ScrArea *area); void ED_area_tag_redraw_regiontype(ScrArea *area, int type); @@ -427,13 +408,8 @@ void ED_region_cache_draw_cached_segments(struct ARegion *region, const int efra); /* area_utils.c */ -void ED_region_generic_tools_region_message_subscribe(const struct bContext *C, - struct WorkSpace *workspace, - struct Scene *scene, - struct bScreen *screen, - struct ScrArea *area, - struct ARegion *region, - struct wmMsgBus *mbus); +void ED_region_generic_tools_region_message_subscribe( + const struct wmRegionMessageSubscribeParams *params); int ED_region_generic_tools_region_snap_size(const struct ARegion *region, int size, int axis); /* area_query.c */ diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index ca3e351a052..2ab062a718c 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -98,8 +98,7 @@ enum TfmMode { /* Standalone call to get the transformation center corresponding to the current situation * returns 1 if successful, 0 otherwise (usually means there's no selection) - * (if 0 is returns, *vec is unmodified) - * */ + * (if false is returns, `cent3d` is unmodified). */ bool calculateTransformCenter(struct bContext *C, int centerMode, float cent3d[3], diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 7bbb7225f14..bc053f60ca3 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -56,6 +56,7 @@ struct bNode; struct bNodeSocket; struct bNodeTree; struct bScreen; +struct rctf; struct rcti; struct uiButSearch; struct uiFontStyle; @@ -414,57 +415,38 @@ void UI_draw_anti_tria( void UI_draw_anti_fan(float tri_array[][2], unsigned int length, const float color[4]); void UI_draw_roundbox_corner_set(int type); -void UI_draw_roundbox_aa( - bool filled, float minx, float miny, float maxx, float maxy, float rad, const float color[4]); -void UI_draw_roundbox_4fv( - bool filled, float minx, float miny, float maxx, float maxy, float rad, const float col[4]); -void UI_draw_roundbox_3ub_alpha(bool filled, - float minx, - float miny, - float maxx, - float maxy, +void UI_draw_roundbox_aa(const struct rctf *rect, bool filled, float rad, const float color[4]); +void UI_draw_roundbox_4fv(const struct rctf *rect, bool filled, float rad, const float col[4]); +void UI_draw_roundbox_3ub_alpha(const struct rctf *rect, + bool filled, float rad, const unsigned char col[3], unsigned char alpha); -void UI_draw_roundbox_3fv_alpha(bool filled, - float minx, - float miny, - float maxx, - float maxy, - float rad, - const float col[3], - float alpha); -void UI_draw_roundbox_shade_x(bool filled, - float minx, - float miny, - float maxx, - float maxy, +void UI_draw_roundbox_3fv_alpha( + const struct rctf *rect, bool filled, float rad, const float col[3], float alpha); +void UI_draw_roundbox_shade_x(const struct rctf *rect, + bool filled, float rad, float shadetop, float shadedown, const float col[4]); +void UI_draw_roundbox_4fv_ex(const struct rctf *rect, + const float inner1[4], + const float inner2[4], + float shade_dir, + const float outline[4], + float outline_width, + float rad); #if 0 /* unused */ int UI_draw_roundbox_corner_get(void); -void UI_draw_roundbox_shade_y(bool filled, - float minx, - float miny, - float maxx, - float maxy, - float rad, - float shadeleft, - float shaderight, - const float col[4]); #endif -void UI_draw_box_shadow(unsigned char alpha, float minx, float miny, float maxx, float maxy); +void UI_draw_box_shadow(const struct rctf *rect, unsigned char alpha); void UI_draw_text_underline(int pos_x, int pos_y, int len, int height, const float color[4]); void UI_draw_safe_areas(uint pos, - float x1, - float x2, - float y1, - float y2, + const struct rctf *rect, const float title_aspect[2], const float action_aspect[2]); @@ -526,6 +508,7 @@ typedef bool (*uiButSearchContextMenuFn)(struct bContext *C, const struct wmEvent *event); typedef struct ARegion *(*uiButSearchTooltipFn)(struct bContext *C, struct ARegion *region, + const struct rcti *item_rect, void *arg, void *active); @@ -664,8 +647,7 @@ bool UI_popup_block_name_exists(const struct bScreen *screen, const char *name); * Begin/Define Buttons/End/Draw is the typical order in which these * function should be called, though for popup blocks Draw is left out. * Freeing blocks is done by the screen/ module automatically. - * - * */ + */ uiBlock *UI_block_begin(const struct bContext *C, struct ARegion *region, @@ -2499,11 +2481,12 @@ void UI_context_active_but_prop_get_templateID(struct bContext *C, struct PropertyRNA **r_prop); struct ID *UI_context_active_but_get_tab_ID(struct bContext *C); -uiBut *UI_region_active_but_get(struct ARegion *region); +uiBut *UI_region_active_but_get(const struct ARegion *region); uiBut *UI_region_but_find_rect_over(const struct ARegion *region, const struct rcti *rect_px); uiBlock *UI_region_block_find_mouse_over(const struct ARegion *region, const int xy[2], bool only_clip); +struct ARegion *UI_region_searchbox_region_get(const struct ARegion *button_region); /* uiFontStyle.align */ typedef enum eFontStyle_Align { @@ -2583,6 +2566,21 @@ struct ARegion *UI_tooltip_create_from_button(struct bContext *C, struct ARegion *UI_tooltip_create_from_gizmo(struct bContext *C, struct wmGizmo *gz); void UI_tooltip_free(struct bContext *C, struct bScreen *screen, struct ARegion *region); +typedef struct { + /** A description for the item, e.g. what happens when selecting it. */ + char description[UI_MAX_DRAW_STR]; + /* The full name of the item, without prefixes or suffixes (e.g. hint with UI_SEP_CHARP). */ + const char *name; + /** Additional info about the item (e.g. library name of a linked data-block). */ + char hint[UI_MAX_DRAW_STR]; +} uiSearchItemTooltipData; + +struct ARegion *UI_tooltip_create_from_search_item_generic( + struct bContext *C, + const struct ARegion *searchbox_region, + const struct rcti *item_rect, + const uiSearchItemTooltipData *item_tooltip_data); + /* How long before a tool-tip shows. */ #define UI_TOOLTIP_DELAY 0.5 #define UI_TOOLTIP_DELAY_LABEL 0.2 diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index 8df29e5b520..64f881052a1 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -231,7 +231,7 @@ struct View2D *UI_view2d_fromcontext(const struct bContext *C); struct View2D *UI_view2d_fromcontext_rwin(const struct bContext *C); void UI_view2d_scroller_size_get(const struct View2D *v2d, float *r_x, float *r_y); -void UI_view2d_scale_get(struct View2D *v2d, float *r_x, float *r_y); +void UI_view2d_scale_get(const struct View2D *v2d, float *r_x, float *r_y); float UI_view2d_scale_get_x(const struct View2D *v2d); float UI_view2d_scale_get_y(const struct View2D *v2d); void UI_view2d_scale_get_inverse(const struct View2D *v2d, float *r_x, float *r_y); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 7340a373573..1ef14f549e4 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -1284,12 +1284,10 @@ static bool ui_but_event_property_operator_string(const bContext *C, char *buf, const size_t buf_len) { - /* context toggle operator names to check... */ + /* Context toggle operator names to check. */ /* This function could use a refactor to generalize button type to operator relationship - * as well as which operators use properties. - * - Campbell - * */ + * as well as which operators use properties. - Campbell */ const char *ctx_toggle_opnames[] = { "WM_OT_context_toggle", "WM_OT_context_toggle_enum", diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 5bb6b0f21e7..0bae57f14d3 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -81,544 +81,116 @@ int UI_draw_roundbox_corner_get(void) } #endif -void UI_draw_roundbox_3ub_alpha(bool filled, - float minx, - float miny, - float maxx, - float maxy, - float rad, - const uchar col[3], - uchar alpha) +void UI_draw_roundbox_4fv_ex(const rctf *rect, + const float inner1[4], + const float inner2[4], + float shade_dir, + const float outline[4], + float outline_width, + float rad) { - float colv[4]; - colv[0] = ((float)col[0]) / 255; - colv[1] = ((float)col[1]) / 255; - colv[2] = ((float)col[2]) / 255; - colv[3] = ((float)alpha) / 255; - UI_draw_roundbox_4fv(filled, minx, miny, maxx, maxy, rad, colv); -} - -void UI_draw_roundbox_3fv_alpha(bool filled, - float minx, - float miny, - float maxx, - float maxy, - float rad, - const float col[3], - float alpha) -{ - float colv[4]; - colv[0] = col[0]; - colv[1] = col[1]; - colv[2] = col[2]; - colv[3] = alpha; - UI_draw_roundbox_4fv(filled, minx, miny, maxx, maxy, rad, colv); -} - -void UI_draw_roundbox_aa( - bool filled, float minx, float miny, float maxx, float maxy, float rad, const float color[4]) -{ - uiWidgetBaseParameters widget_params = { - .recti.xmin = minx + U.pixelsize, - .recti.ymin = miny + U.pixelsize, - .recti.xmax = maxx - U.pixelsize, - .recti.ymax = maxy - U.pixelsize, - .rect.xmin = minx, - .rect.ymin = miny, - .rect.xmax = maxx, - .rect.ymax = maxy, - .radi = rad, - .rad = rad, - .round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f, - .round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f, - .round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f, - .round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f, - .color_inner1[0] = filled ? color[0] : 0.0f, - .color_inner1[1] = filled ? color[1] : 0.0f, - .color_inner1[2] = filled ? color[2] : 0.0f, - .color_inner1[3] = filled ? color[3] : 0.0f, - .color_inner2[0] = filled ? color[0] : 0.0f, - .color_inner2[1] = filled ? color[1] : 0.0f, - .color_inner2[2] = filled ? color[2] : 0.0f, - .color_inner2[3] = filled ? color[3] : 0.0f, - .color_outline[0] = color[0], - .color_outline[1] = color[1], - .color_outline[2] = color[2], - .color_outline[3] = color[3], - .alpha_discard = 1.0f, - }; - - /* XXX this is to emulate previous behavior of semitransparent fills but that's was a side effect - * of the previous AA method. Better fix the callers. */ - if (filled) { - widget_params.color_inner1[3] *= 0.65f; - widget_params.color_inner2[3] *= 0.65f; - widget_params.color_outline[3] *= 0.65f; - } - /* WATCH: This is assuming the ModelViewProjectionMatrix is area pixel space. * If it has been scaled, then it's no longer valid. */ - - GPUBatch *batch = ui_batch_roundbox_widget_get(); - GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); - GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float(*)[4]) & widget_params); - - GPU_blend(GPU_BLEND_ALPHA); - - GPU_batch_draw(batch); - - GPU_blend(GPU_BLEND_NONE); -} - -void UI_draw_roundbox_4fv( - bool filled, float minx, float miny, float maxx, float maxy, float rad, const float col[4]) -{ -#if 0 - float vec[7][2] = { - {0.195, 0.02}, - {0.383, 0.067}, - {0.55, 0.169}, - {0.707, 0.293}, - {0.831, 0.45}, - {0.924, 0.617}, - {0.98, 0.805}, - }; - int a; - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - /* mult */ - for (a = 0; a < 7; a++) { - mul_v2_fl(vec[a], rad); - } - - uint vert_len = 0; - vert_len += (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 9 : 1; - vert_len += (roundboxtype & UI_CNR_TOP_RIGHT) ? 9 : 1; - vert_len += (roundboxtype & UI_CNR_TOP_LEFT) ? 9 : 1; - vert_len += (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 9 : 1; - - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformColor4fv(col); - - immBegin(filled ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_LOOP, vert_len); - /* start with corner right-bottom */ - if (roundboxtype & UI_CNR_BOTTOM_RIGHT) { - immVertex2f(pos, maxx - rad, miny); - for (a = 0; a < 7; a++) { - immVertex2f(pos, maxx - rad + vec[a][0], miny + vec[a][1]); - } - immVertex2f(pos, maxx, miny + rad); - } - else { - immVertex2f(pos, maxx, miny); - } - - /* corner right-top */ - if (roundboxtype & UI_CNR_TOP_RIGHT) { - immVertex2f(pos, maxx, maxy - rad); - for (a = 0; a < 7; a++) { - immVertex2f(pos, maxx - vec[a][1], maxy - rad + vec[a][0]); - } - immVertex2f(pos, maxx - rad, maxy); - } - else { - immVertex2f(pos, maxx, maxy); - } - - /* corner left-top */ - if (roundboxtype & UI_CNR_TOP_LEFT) { - immVertex2f(pos, minx + rad, maxy); - for (a = 0; a < 7; a++) { - immVertex2f(pos, minx + rad - vec[a][0], maxy - vec[a][1]); - } - immVertex2f(pos, minx, maxy - rad); - } - else { - immVertex2f(pos, minx, maxy); - } - - /* corner left-bottom */ - if (roundboxtype & UI_CNR_BOTTOM_LEFT) { - immVertex2f(pos, minx, miny + rad); - for (a = 0; a < 7; a++) { - immVertex2f(pos, minx + vec[a][1], miny + rad - vec[a][0]); - } - immVertex2f(pos, minx + rad, miny); - } - else { - immVertex2f(pos, minx, miny); - } - - immEnd(); - immUnbindProgram(); -#endif uiWidgetBaseParameters widget_params = { - .recti.xmin = minx + U.pixelsize, - .recti.ymin = miny + U.pixelsize, - .recti.xmax = maxx - U.pixelsize, - .recti.ymax = maxy - U.pixelsize, - .rect.xmin = minx, - .rect.ymin = miny, - .rect.xmax = maxx, - .rect.ymax = maxy, + .recti.xmin = rect->xmin + outline_width, + .recti.ymin = rect->ymin + outline_width, + .recti.xmax = rect->xmax - outline_width, + .recti.ymax = rect->ymax - outline_width, + .rect = *rect, .radi = rad, .rad = rad, .round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f, .round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f, .round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f, .round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f, - .color_inner1[0] = filled ? col[0] : 0.0f, - .color_inner1[1] = filled ? col[1] : 0.0f, - .color_inner1[2] = filled ? col[2] : 0.0f, - .color_inner1[3] = filled ? col[3] : 0.0f, - .color_inner2[0] = filled ? col[0] : 0.0f, - .color_inner2[1] = filled ? col[1] : 0.0f, - .color_inner2[2] = filled ? col[2] : 0.0f, - .color_inner2[3] = filled ? col[3] : 0.0f, - .color_outline[0] = col[0], - .color_outline[1] = col[1], - .color_outline[2] = col[2], - .color_outline[3] = col[3], + .color_inner1[0] = inner1 ? inner1[0] : 0.0f, + .color_inner1[1] = inner1 ? inner1[1] : 0.0f, + .color_inner1[2] = inner1 ? inner1[2] : 0.0f, + .color_inner1[3] = inner1 ? inner1[3] : 0.0f, + .color_inner2[0] = inner2 ? inner2[0] : inner1 ? inner1[0] : 0.0f, + .color_inner2[1] = inner2 ? inner2[1] : inner1 ? inner1[1] : 0.0f, + .color_inner2[2] = inner2 ? inner2[2] : inner1 ? inner1[2] : 0.0f, + .color_inner2[3] = inner2 ? inner2[3] : inner1 ? inner1[3] : 0.0f, + .color_outline[0] = outline ? outline[0] : inner1 ? inner1[0] : 0.0f, + .color_outline[1] = outline ? outline[1] : inner1 ? inner1[1] : 0.0f, + .color_outline[2] = outline ? outline[2] : inner1 ? inner1[2] : 0.0f, + .color_outline[3] = outline ? outline[3] : inner1 ? inner1[3] : 0.0f, + .shade_dir = shade_dir, .alpha_discard = 1.0f, }; - /* Exactly the same as UI_draw_roundbox_aa but does not do the legacy transparency. */ - - /* WATCH: This is assuming the ModelViewProjectionMatrix is area pixel space. - * If it has been scaled, then it's no longer valid. */ - GPUBatch *batch = ui_batch_roundbox_widget_get(); GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float(*)[4]) & widget_params); - GPU_blend(GPU_BLEND_ALPHA); - GPU_batch_draw(batch); - GPU_blend(GPU_BLEND_NONE); } -#if 0 -static void round_box_shade_col(uint attr, - const float col1[3], - float const col2[3], - const float fac) +void UI_draw_roundbox_3ub_alpha( + const rctf *rect, bool filled, float rad, const uchar col[3], uchar alpha) { - float col[4] = { - fac * col1[0] + (1.0f - fac) * col2[0], - fac * col1[1] + (1.0f - fac) * col2[1], - fac * col1[2] + (1.0f - fac) * col2[2], - 1.0f, + float colv[4] = { + ((float)col[0]) / 255, + ((float)col[1]) / 255, + ((float)col[2]) / 255, + ((float)alpha) / 255, }; - immAttr4fv(attr, col); + UI_draw_roundbox_4fv_ex(rect, (filled) ? colv : NULL, NULL, 1.0f, colv, U.pixelsize, rad); } -#endif -/* linear horizontal shade within button or in outline */ -/* view2d scrollers use it */ -void UI_draw_roundbox_shade_x(bool filled, - float minx, - float miny, - float maxx, - float maxy, - float rad, - float shadetop, - float shadedown, - const float col[4]) +void UI_draw_roundbox_3fv_alpha( + const rctf *rect, bool filled, float rad, const float col[3], float alpha) { -#if 0 - float vec[7][2] = { - {0.195, 0.02}, - {0.383, 0.067}, - {0.55, 0.169}, - {0.707, 0.293}, - {0.831, 0.45}, - {0.924, 0.617}, - {0.98, 0.805}, - }; - const float div = maxy - miny; - const float idiv = 1.0f / div; - float coltop[3], coldown[3]; - int vert_count = 0; - int a; - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); - - /* mult */ - for (a = 0; a < 7; a++) { - mul_v2_fl(vec[a], rad); - } - - /* 'shade' defines strength of shading */ - coltop[0] = min_ff(1.0f, col[0] + shadetop); - coltop[1] = min_ff(1.0f, col[1] + shadetop); - coltop[2] = min_ff(1.0f, col[2] + shadetop); - coldown[0] = max_ff(0.0f, col[0] + shadedown); - coldown[1] = max_ff(0.0f, col[1] + shadedown); - coldown[2] = max_ff(0.0f, col[2] + shadedown); - - vert_count += (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 9 : 1; - vert_count += (roundboxtype & UI_CNR_TOP_RIGHT) ? 9 : 1; - vert_count += (roundboxtype & UI_CNR_TOP_LEFT) ? 9 : 1; - vert_count += (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 9 : 1; - - immBegin(filled ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_LOOP, vert_count); - - /* start with corner right-bottom */ - if (roundboxtype & UI_CNR_BOTTOM_RIGHT) { - - round_box_shade_col(color, coltop, coldown, 0.0); - immVertex2f(pos, maxx - rad, miny); - - for (a = 0; a < 7; a++) { - round_box_shade_col(color, coltop, coldown, vec[a][1] * idiv); - immVertex2f(pos, maxx - rad + vec[a][0], miny + vec[a][1]); - } - - round_box_shade_col(color, coltop, coldown, rad * idiv); - immVertex2f(pos, maxx, miny + rad); - } - else { - round_box_shade_col(color, coltop, coldown, 0.0); - immVertex2f(pos, maxx, miny); - } - - /* corner right-top */ - if (roundboxtype & UI_CNR_TOP_RIGHT) { - - round_box_shade_col(color, coltop, coldown, (div - rad) * idiv); - immVertex2f(pos, maxx, maxy - rad); - - for (a = 0; a < 7; a++) { - round_box_shade_col(color, coltop, coldown, (div - rad + vec[a][1]) * idiv); - immVertex2f(pos, maxx - vec[a][1], maxy - rad + vec[a][0]); - } - round_box_shade_col(color, coltop, coldown, 1.0); - immVertex2f(pos, maxx - rad, maxy); - } - else { - round_box_shade_col(color, coltop, coldown, 1.0); - immVertex2f(pos, maxx, maxy); - } - - /* corner left-top */ - if (roundboxtype & UI_CNR_TOP_LEFT) { - - round_box_shade_col(color, coltop, coldown, 1.0); - immVertex2f(pos, minx + rad, maxy); - - for (a = 0; a < 7; a++) { - round_box_shade_col(color, coltop, coldown, (div - vec[a][1]) * idiv); - immVertex2f(pos, minx + rad - vec[a][0], maxy - vec[a][1]); - } - - round_box_shade_col(color, coltop, coldown, (div - rad) * idiv); - immVertex2f(pos, minx, maxy - rad); - } - else { - round_box_shade_col(color, coltop, coldown, 1.0); - immVertex2f(pos, minx, maxy); - } - - /* corner left-bottom */ - if (roundboxtype & UI_CNR_BOTTOM_LEFT) { - - round_box_shade_col(color, coltop, coldown, rad * idiv); - immVertex2f(pos, minx, miny + rad); - - for (a = 0; a < 7; a++) { - round_box_shade_col(color, coltop, coldown, (rad - vec[a][1]) * idiv); - immVertex2f(pos, minx + vec[a][1], miny + rad - vec[a][0]); - } + float colv[4] = {col[0], col[1], col[2], alpha}; + UI_draw_roundbox_4fv_ex(rect, (filled) ? colv : NULL, NULL, 1.0f, colv, U.pixelsize, rad); +} - round_box_shade_col(color, coltop, coldown, 0.0); - immVertex2f(pos, minx + rad, miny); - } - else { - round_box_shade_col(color, coltop, coldown, 0.0); - immVertex2f(pos, minx, miny); +void UI_draw_roundbox_aa(const rctf *rect, bool filled, float rad, const float color[4]) +{ + /* XXX this is to emulate previous behavior of semitransparent fills but that's was a side effect + * of the previous AA method. Better fix the callers. */ + float colv[4] = {color[0], color[1], color[2], color[3]}; + if (filled) { + colv[3] *= 0.65f; } - immEnd(); - immUnbindProgram(); -#endif - uiWidgetBaseParameters widget_params = { - .recti.xmin = minx + U.pixelsize, - .recti.ymin = miny + U.pixelsize, - .recti.xmax = maxx - U.pixelsize, - .recti.ymax = maxy - U.pixelsize, - .rect.xmin = minx, - .rect.ymin = miny, - .rect.xmax = maxx, - .rect.ymax = maxy, - .radi = rad, - .rad = rad, - .round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f, - .round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f, - .round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f, - .round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f, - .color_inner1[0] = !filled ? 0.0f : min_ff(1.0f, col[0] + shadetop), - .color_inner1[1] = !filled ? 0.0f : min_ff(1.0f, col[1] + shadetop), - .color_inner1[2] = !filled ? 0.0f : min_ff(1.0f, col[2] + shadetop), - .color_inner1[3] = !filled ? 0.0f : 1.0f, - .color_inner2[0] = !filled ? 0.0f : max_ff(0.0f, col[0] + shadedown), - .color_inner2[1] = !filled ? 0.0f : max_ff(0.0f, col[1] + shadedown), - .color_inner2[2] = !filled ? 0.0f : max_ff(0.0f, col[2] + shadedown), - .color_inner2[3] = !filled ? 0.0f : 1.0f, - /* TODO: non-filled box don't have gradients. Just use middle color. */ - .color_outline[0] = clamp_f(col[0] + shadetop + shadedown, 0.0f, 1.0f), - .color_outline[1] = clamp_f(col[1] + shadetop + shadedown, 0.0f, 1.0f), - .color_outline[2] = clamp_f(col[2] + shadetop + shadedown, 0.0f, 1.0f), - .color_outline[3] = clamp_f(col[3] + shadetop + shadedown, 0.0f, 1.0f), - .shade_dir = 1.0f, - .alpha_discard = 1.0f, - }; - - GPU_blend(GPU_BLEND_ALPHA); - - GPUBatch *batch = ui_batch_roundbox_widget_get(); - GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); - GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float(*)[4]) & widget_params); - GPU_batch_draw(batch); + UI_draw_roundbox_4fv_ex(rect, (filled) ? colv : NULL, NULL, 1.0f, colv, U.pixelsize, rad); +} - GPU_blend(GPU_BLEND_NONE); +void UI_draw_roundbox_4fv(const rctf *rect, bool filled, float rad, const float col[4]) +{ + /* Exactly the same as UI_draw_roundbox_aa but does not do the legacy transparency. */ + UI_draw_roundbox_4fv_ex(rect, (filled) ? col : NULL, NULL, 1.0f, col, U.pixelsize, rad); } -#if 0 /* unused */ -/* linear vertical shade within button or in outline */ +/* linear horizontal shade within button or in outline */ /* view2d scrollers use it */ -void UI_draw_roundbox_shade_y(bool filled, - float minx, - float miny, - float maxx, - float maxy, - float rad, - float shadeleft, - float shaderight, - const float col[4]) +void UI_draw_roundbox_shade_x( + const rctf *rect, bool filled, float rad, float shadetop, float shadedown, const float col[4]) { - float vec[7][2] = { - {0.195, 0.02}, - {0.383, 0.067}, - {0.55, 0.169}, - {0.707, 0.293}, - {0.831, 0.45}, - {0.924, 0.617}, - {0.98, 0.805}, - }; - const float div = maxx - minx; - const float idiv = 1.0f / div; - float colLeft[3], colRight[3]; - int vert_count = 0; - int a; - - /* mult */ - for (a = 0; a < 7; a++) { - mul_v2_fl(vec[a], rad); - } - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); - - /* 'shade' defines strength of shading */ - colLeft[0] = min_ff(1.0f, col[0] + shadeleft); - colLeft[1] = min_ff(1.0f, col[1] + shadeleft); - colLeft[2] = min_ff(1.0f, col[2] + shadeleft); - colRight[0] = max_ff(0.0f, col[0] + shaderight); - colRight[1] = max_ff(0.0f, col[1] + shaderight); - colRight[2] = max_ff(0.0f, col[2] + shaderight); - - vert_count += (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 9 : 1; - vert_count += (roundboxtype & UI_CNR_TOP_RIGHT) ? 9 : 1; - vert_count += (roundboxtype & UI_CNR_TOP_LEFT) ? 9 : 1; - vert_count += (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 9 : 1; - - immBegin(filled ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_LOOP, vert_count); + float inner1[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float inner2[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float outline[4]; - /* start with corner right-bottom */ - if (roundboxtype & UI_CNR_BOTTOM_RIGHT) { - round_box_shade_col(color, colLeft, colRight, 0.0); - immVertex2f(pos, maxx - rad, miny); - - for (a = 0; a < 7; a++) { - round_box_shade_col(color, colLeft, colRight, vec[a][0] * idiv); - immVertex2f(pos, maxx - rad + vec[a][0], miny + vec[a][1]); - } - - round_box_shade_col(color, colLeft, colRight, rad * idiv); - immVertex2f(pos, maxx, miny + rad); - } - else { - round_box_shade_col(color, colLeft, colRight, 0.0); - immVertex2f(pos, maxx, miny); - } - - /* corner right-top */ - if (roundboxtype & UI_CNR_TOP_RIGHT) { - round_box_shade_col(color, colLeft, colRight, 0.0); - immVertex2f(pos, maxx, maxy - rad); - - for (a = 0; a < 7; a++) { - - round_box_shade_col(color, colLeft, colRight, (div - rad - vec[a][0]) * idiv); - immVertex2f(pos, maxx - vec[a][1], maxy - rad + vec[a][0]); - } - round_box_shade_col(color, colLeft, colRight, (div - rad) * idiv); - immVertex2f(pos, maxx - rad, maxy); - } - else { - round_box_shade_col(color, colLeft, colRight, 0.0); - immVertex2f(pos, maxx, maxy); - } - - /* corner left-top */ - if (roundboxtype & UI_CNR_TOP_LEFT) { - round_box_shade_col(color, colLeft, colRight, (div - rad) * idiv); - immVertex2f(pos, minx + rad, maxy); - - for (a = 0; a < 7; a++) { - round_box_shade_col(color, colLeft, colRight, (div - rad + vec[a][0]) * idiv); - immVertex2f(pos, minx + rad - vec[a][0], maxy - vec[a][1]); - } - - round_box_shade_col(color, colLeft, colRight, 1.0); - immVertex2f(pos, minx, maxy - rad); - } - else { - round_box_shade_col(color, colLeft, colRight, 1.0); - immVertex2f(pos, minx, maxy); - } - - /* corner left-bottom */ - if (roundboxtype & UI_CNR_BOTTOM_LEFT) { - round_box_shade_col(color, colLeft, colRight, 1.0); - immVertex2f(pos, minx, miny + rad); - - for (a = 0; a < 7; a++) { - round_box_shade_col(color, colLeft, colRight, (vec[a][0]) * idiv); - immVertex2f(pos, minx + vec[a][1], miny + rad - vec[a][0]); - } - - round_box_shade_col(color, colLeft, colRight, 1.0); - immVertex2f(pos, minx + rad, miny); - } - else { - round_box_shade_col(color, colLeft, colRight, 1.0); - immVertex2f(pos, minx, miny); - } - - immEnd(); - immUnbindProgram(); + if (filled) { + inner1[0] = min_ff(1.0f, col[0] + shadetop); + inner1[1] = min_ff(1.0f, col[1] + shadetop); + inner1[2] = min_ff(1.0f, col[2] + shadetop); + inner1[3] = 1.0f; + inner2[0] = max_ff(0.0f, col[0] + shadedown); + inner2[1] = max_ff(0.0f, col[1] + shadedown); + inner2[2] = max_ff(0.0f, col[2] + shadedown); + inner2[3] = 1.0f; + } + + /* TODO: non-filled box don't have gradients. Just use middle color. */ + outline[0] = clamp_f(col[0] + shadetop + shadedown, 0.0f, 1.0f); + outline[1] = clamp_f(col[1] + shadetop + shadedown, 0.0f, 1.0f); + outline[2] = clamp_f(col[2] + shadetop + shadedown, 0.0f, 1.0f); + outline[3] = clamp_f(col[3] + shadetop + shadedown, 0.0f, 1.0f); + + UI_draw_roundbox_4fv_ex(rect, inner1, inner2, 1.0f, outline, U.pixelsize, rad); } -#endif /* unused */ void UI_draw_text_underline(int pos_x, int pos_y, int len, int height, const float color[4]) { @@ -799,15 +371,12 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(region), * \param x1, x2, y1, y2: The offsets for the view, not the zones. */ void UI_draw_safe_areas(uint pos, - float x1, - float x2, - float y1, - float y2, + const rctf *rect, const float title_aspect[2], const float action_aspect[2]) { - const float size_x_half = (x2 - x1) * 0.5f; - const float size_y_half = (y2 - y1) * 0.5f; + const float size_x_half = (rect->xmax - rect->xmin) * 0.5f; + const float size_y_half = (rect->ymax - rect->ymin) * 0.5f; const float *safe_areas[] = {title_aspect, action_aspect}; const int safe_len = ARRAY_SIZE(safe_areas); @@ -817,10 +386,10 @@ void UI_draw_safe_areas(uint pos, const float margin_x = safe_areas[i][0] * size_x_half; const float margin_y = safe_areas[i][1] * size_y_half; - const float minx = x1 + margin_x; - const float miny = y1 + margin_y; - const float maxx = x2 - margin_x; - const float maxy = y2 - margin_y; + const float minx = rect->xmin + margin_x; + const float miny = rect->ymin + margin_y; + const float maxx = rect->xmax - margin_x; + const float maxy = rect->ymax - margin_y; imm_draw_box_wire_2d(pos, minx, miny, maxx, maxy); } @@ -835,7 +404,15 @@ static void draw_scope_end(const rctf *rect) UI_draw_roundbox_corner_set(UI_CNR_ALL); const float color[4] = {0.0f, 0.0f, 0.0f, 0.5f}; UI_draw_roundbox_4fv( - false, rect->xmin - 1, rect->ymin, rect->xmax + 1, rect->ymax + 1, 3.0f, color); + &(const rctf){ + .xmin = rect->xmin - 1, + .xmax = rect->xmax + 1, + .ymin = rect->ymin, + .ymax = rect->ymax + 1, + }, + false, + 3.0f, + color); } static void histogram_draw_one(float r, @@ -928,7 +505,15 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region), UI_GetThemeColor4fv(TH_PREVIEW_BACK, color); UI_draw_roundbox_corner_set(UI_CNR_ALL); UI_draw_roundbox_4fv( - true, rect.xmin - 1, rect.ymin - 1, rect.xmax + 1, rect.ymax + 1, 3.0f, color); + &(const rctf){ + .xmin = rect.xmin - 1, + .xmax = rect.xmax + 1, + .ymin = rect.ymin - 1, + .ymax = rect.ymax + 1, + }, + true, + 3.0f, + color); /* need scissor test, histogram can draw outside of boundary */ int scissor[4]; @@ -1070,7 +655,15 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region), UI_GetThemeColor4fv(TH_PREVIEW_BACK, color); UI_draw_roundbox_corner_set(UI_CNR_ALL); UI_draw_roundbox_4fv( - true, rect.xmin - 1, rect.ymin - 1, rect.xmax + 1, rect.ymax + 1, 3.0f, color); + &(const rctf){ + .xmin = rect.xmin - 1, + .xmax = rect.xmax + 1, + .ymin = rect.ymin - 1, + .ymax = rect.ymax + 1, + }, + true, + 3.0f, + color); /* need scissor test, waveform can draw outside of boundary */ GPU_scissor_get(scissor); @@ -1399,7 +992,15 @@ void ui_draw_but_VECTORSCOPE(ARegion *UNUSED(region), UI_GetThemeColor4fv(TH_PREVIEW_BACK, color); UI_draw_roundbox_corner_set(UI_CNR_ALL); UI_draw_roundbox_4fv( - true, rect.xmin - 1, rect.ymin - 1, rect.xmax + 1, rect.ymax + 1, 3.0f, color); + &(const rctf){ + .xmin = rect.xmin - 1, + .xmax = rect.xmax + 1, + .ymin = rect.ymin - 1, + .ymax = rect.ymax + 1, + }, + true, + 3.0f, + color); /* need scissor test, hvectorscope can draw outside of boundary */ int scissor[4]; @@ -1774,7 +1375,16 @@ void ui_draw_but_UNITVEC(uiBut *but, const uiWidgetColors *wcol, const rcti *rec /* backdrop */ UI_draw_roundbox_corner_set(UI_CNR_ALL); UI_draw_roundbox_3ub_alpha( - true, rect->xmin, rect->ymin, rect->xmax, rect->ymax, 5.0f, wcol->inner, 255); + &(const rctf){ + .xmin = rect->xmin, + .xmax = rect->xmax, + .ymin = rect->ymin, + .ymax = rect->ymax, + }, + true, + 5.0f, + wcol->inner, + 255); GPU_face_culling(GPU_CULL_BACK); @@ -2465,7 +2075,15 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region), const float color[4] = {0.7f, 0.3f, 0.3f, 0.3f}; UI_draw_roundbox_corner_set(UI_CNR_ALL); UI_draw_roundbox_4fv( - true, rect.xmin - 1, rect.ymin, rect.xmax + 1, rect.ymax + 1, 3.0f, color); + &(const rctf){ + .xmin = rect.xmin - 1, + .xmax = rect.xmax + 1, + .ymin = rect.ymin, + .ymax = rect.ymax + 1, + }, + true, + 3.0f, + color); ok = true; } @@ -2514,7 +2132,15 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region), const float color[4] = {0.0f, 0.0f, 0.0f, 0.3f}; UI_draw_roundbox_corner_set(UI_CNR_ALL); UI_draw_roundbox_4fv( - true, rect.xmin - 1, rect.ymin, rect.xmax + 1, rect.ymax + 1, 3.0f, color); + &(const rctf){ + .xmin = rect.xmin - 1, + .xmax = rect.xmax + 1, + .ymin = rect.ymin, + .ymax = rect.ymax + 1, + }, + true, + 3.0f, + color); } IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); @@ -2577,7 +2203,15 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region), const float color[4] = {0.0f, 0.0f, 0.0f, 0.3f}; UI_draw_roundbox_corner_set(UI_CNR_ALL); UI_draw_roundbox_4fv( - true, rect.xmin - 1, rect.ymin, rect.xmax + 1, rect.ymax + 1, 3.0f, color); + &(const rctf){ + .xmin = rect.xmin - 1, + .xmax = rect.xmax + 1, + .ymin = rect.ymin, + .ymax = rect.ymax + 1, + }, + true, + 3.0f, + color); } /* Restore scissor test. */ @@ -2594,14 +2228,7 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region), * would replace / modify the following 3 functions - merwin */ -static void ui_shadowbox(uint pos, - uint color, - float minx, - float miny, - float maxx, - float maxy, - float shadsize, - uchar alpha) +static void ui_shadowbox(const rctf *rect, uint pos, uint color, float shadsize, uchar alpha) { /** * <pre> @@ -2616,16 +2243,16 @@ static void ui_shadowbox(uint pos, * v8______v6_- * </pre> */ - const float v1[2] = {maxx, maxy - 0.3f * shadsize}; - const float v2[2] = {maxx + shadsize, maxy - 0.75f * shadsize}; - const float v3[2] = {maxx, miny}; - const float v4[2] = {maxx + shadsize, miny}; + const float v1[2] = {rect->xmax, rect->ymax - 0.3f * shadsize}; + const float v2[2] = {rect->xmax + shadsize, rect->ymax - 0.75f * shadsize}; + const float v3[2] = {rect->xmax, rect->ymin}; + const float v4[2] = {rect->xmax + shadsize, rect->ymin}; - const float v5[2] = {maxx + 0.7f * shadsize, miny - 0.7f * shadsize}; + const float v5[2] = {rect->xmax + 0.7f * shadsize, rect->ymin - 0.7f * shadsize}; - const float v6[2] = {maxx, miny - shadsize}; - const float v7[2] = {minx + 0.3f * shadsize, miny}; - const float v8[2] = {minx + 0.5f * shadsize, miny - shadsize}; + const float v6[2] = {rect->xmax, rect->ymin - shadsize}; + const float v7[2] = {rect->xmin + 0.3f * shadsize, rect->ymin}; + const float v8[2] = {rect->xmin + 0.5f * shadsize, rect->ymin - shadsize}; /* right quad */ immAttr4ub(color, 0, 0, 0, alpha); @@ -2664,7 +2291,7 @@ static void ui_shadowbox(uint pos, immVertex2fv(pos, v3); } -void UI_draw_box_shadow(uchar alpha, float minx, float miny, float maxx, float maxy) +void UI_draw_box_shadow(const rctf *rect, uchar alpha) { GPU_blend(GPU_BLEND_ALPHA); @@ -2678,9 +2305,9 @@ void UI_draw_box_shadow(uchar alpha, float minx, float miny, float maxx, float m immBegin(GPU_PRIM_TRIS, 54); /* accumulated outline boxes to make shade not linear, is more pleasant */ - ui_shadowbox(pos, color, minx, miny, maxx, maxy, 11.0, (20 * alpha) >> 8); - ui_shadowbox(pos, color, minx, miny, maxx, maxy, 7.0, (40 * alpha) >> 8); - ui_shadowbox(pos, color, minx, miny, maxx, maxy, 5.0, (80 * alpha) >> 8); + ui_shadowbox(rect, pos, color, 11.0, (20 * alpha) >> 8); + ui_shadowbox(rect, pos, color, 7.0, (40 * alpha) >> 8); + ui_shadowbox(rect, pos, color, 5.0, (80 * alpha) >> 8); immEnd(); @@ -2755,13 +2382,16 @@ void ui_draw_dropshadow( /* outline emphasis */ const float color[4] = {0.0f, 0.0f, 0.0f, 0.4f}; - UI_draw_roundbox_4fv(false, - rct->xmin - 0.5f, - rct->ymin - 0.5f, - rct->xmax + 0.5f, - rct->ymax + 0.5f, - radius + 0.5f, - color); + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = rct->xmin - 0.5f, + .xmax = rct->xmax + 0.5f, + .ymin = rct->ymin - 0.5f, + .ymax = rct->ymax + 0.5f, + }, + false, + radius + 0.5f, + color); GPU_blend(GPU_BLEND_NONE); } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 50c5c27b5c1..1d2393ece8d 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -8317,7 +8317,7 @@ void ui_but_active_free(const bContext *C, uiBut *but) } /* returns the active button with an optional checking function */ -static uiBut *ui_context_button_active(ARegion *region, bool (*but_check_cb)(uiBut *)) +static uiBut *ui_context_button_active(const ARegion *region, bool (*but_check_cb)(const uiBut *)) { uiBut *but_found = NULL; @@ -8358,7 +8358,7 @@ static uiBut *ui_context_button_active(ARegion *region, bool (*but_check_cb)(uiB return but_found; } -static bool ui_context_rna_button_active_test(uiBut *but) +static bool ui_context_rna_button_active_test(const uiBut *but) { return (but->rnapoin.data != NULL); } @@ -8383,7 +8383,7 @@ uiBut *UI_context_active_but_get_respect_menu(const bContext *C) return ui_context_button_active(region_menu ? region_menu : CTX_wm_region(C), NULL); } -uiBut *UI_region_active_but_get(ARegion *region) +uiBut *UI_region_active_but_get(const ARegion *region) { return ui_context_button_active(region, NULL); } @@ -8481,6 +8481,15 @@ wmOperator *UI_context_active_operator_get(const struct bContext *C) return NULL; } +/** + * Try to find a search-box region opened from a button in \a button_region. + */ +ARegion *UI_region_searchbox_region_get(const ARegion *button_region) +{ + uiBut *but = UI_region_active_but_get(button_region); + return (but != NULL) ? but->active->searchbox : NULL; +} + /* helper function for insert keyframe, reset to default, etc operators */ void UI_context_update_anim_flag(const bContext *C) { diff --git a/source/blender/editors/interface/interface_icons_event.c b/source/blender/editors/interface/interface_icons_event.c index 223fcbfd45b..3962ff6a702 100644 --- a/source/blender/editors/interface/interface_icons_event.c +++ b/source/blender/editors/interface/interface_icons_event.c @@ -118,7 +118,15 @@ void icon_draw_rect_input(float x, UI_GetThemeColor4fv(TH_TEXT, color); UI_draw_roundbox_corner_set(UI_CNR_ALL); UI_draw_roundbox_aa( - false, (int)x - U.pixelsize, (int)y, (int)(x + w), (int)(y + h), 3.0f * U.pixelsize, color); + &(const rctf){ + .xmin = (int)x - U.pixelsize, + .xmax = (int)(x + w), + .ymin = (int)y, + .ymax = (int)(y + h), + }, + false, + 3.0f * U.pixelsize, + color); const enum { UNIX, diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 12862363c5b..ac29a1c7f9e 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -1122,13 +1122,16 @@ static void panel_draw_highlight_border(const Panel *panel, /* Abuse the property search theme color for now. */ float color[4]; UI_GetThemeColor4fv(TH_MATCH, color); - UI_draw_roundbox_aa(false, - rect->xmin, - UI_panel_is_closed(panel) ? header_rect->ymin : rect->ymin, - rect->xmax, - header_rect->ymax, - radius, - color); + UI_draw_roundbox_aa( + &(const rctf){ + .xmin = rect->xmin, + .xmax = rect->xmax, + .ymin = UI_panel_is_closed(panel) ? header_rect->ymin : rect->ymin, + .ymax = header_rect->ymax, + }, + false, + radius, + color); } static void panel_draw_aligned_widgets(const uiStyle *style, @@ -1254,13 +1257,16 @@ static void panel_draw_aligned_backdrop(const Panel *panel, float color[4]; UI_GetThemeColor4fv(TH_PANEL_SUB_BACK, color); /* Change the width a little bit to line up with sides. */ - UI_draw_roundbox_aa(true, - rect->xmin + U.pixelsize, - rect->ymin + U.pixelsize, - rect->xmax - U.pixelsize, - rect->ymax, - box_wcol->roundness * U.widget_unit, - color); + UI_draw_roundbox_aa( + &(const rctf){ + .xmin = rect->xmin + U.pixelsize, + .xmax = rect->xmax - U.pixelsize, + .ymin = rect->ymin + U.pixelsize, + .ymax = rect->ymax, + }, + true, + box_wcol->roundness * U.widget_unit, + color); } else { immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -1545,20 +1551,26 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) { /* Draw filled rectangle and outline for tab. */ UI_draw_roundbox_corner_set(roundboxtype); - UI_draw_roundbox_4fv(true, - rct->xmin, - rct->ymin, - rct->xmax, - rct->ymax, - tab_curve_radius, - is_active ? theme_col_tab_active : theme_col_tab_inactive); - UI_draw_roundbox_4fv(false, - rct->xmin, - rct->ymin, - rct->xmax, - rct->ymax, - tab_curve_radius, - theme_col_tab_outline); + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = rct->xmin, + .xmax = rct->xmax, + .ymin = rct->ymin, + .ymax = rct->ymax, + }, + true, + tab_curve_radius, + is_active ? theme_col_tab_active : theme_col_tab_inactive); + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = rct->xmin, + .xmax = rct->xmax, + .ymin = rct->ymin, + .ymax = rct->ymax, + }, + false, + tab_curve_radius, + theme_col_tab_outline); /* Disguise the outline on one side to join the tab to the panel. */ pos = GPU_vertformat_attr_add( diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c index 93e3dbb2cc8..62b8ce9d3eb 100644 --- a/source/blender/editors/interface/interface_region_popup.c +++ b/source/blender/editors/interface/interface_region_popup.c @@ -413,12 +413,11 @@ static void ui_block_region_draw(const bContext *C, ARegion *region) /** * Use to refresh centered popups on screen resizing (for splash). */ -static void ui_block_region_popup_window_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void ui_block_region_popup_window_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + switch (wmn->category) { case NC_WINDOW: { switch (wmn->action) { diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index 816162e9aa2..7f51eb90fe8 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -347,9 +347,20 @@ static struct ARegion *wm_searchbox_tooltip_init(struct bContext *C, } uiButSearch *search_but = (uiButSearch *)but; - if (search_but->item_tooltip_fn) { - return search_but->item_tooltip_fn(C, region, search_but->arg, search_but->item_active); + if (!search_but->item_tooltip_fn) { + continue; } + + ARegion *searchbox_region = UI_region_searchbox_region_get(region); + uiSearchboxData *data = searchbox_region->regiondata; + + BLI_assert(data->items.pointers[data->active] == search_but->item_active); + + rcti rect; + ui_searchbox_butrect(&rect, data, data->active); + + return search_but->item_tooltip_fn( + C, region, &rect, search_but->arg, search_but->item_active); } } return NULL; diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 2bf63955855..050a14cf574 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -1456,15 +1456,91 @@ ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz) { wmWindow *win = CTX_wm_window(C); const float aspect = 1.0f; - float init_position[2]; + float init_position[2] = {win->eventstate->x, win->eventstate->y}; uiTooltipData *data = ui_tooltip_data_from_gizmo(C, gz); if (data == NULL) { return NULL; } + /* TODO(harley): + * Julian preferred that the gizmo callback return the 3D bounding box + * which we then project to 2D here. Would make a nice improvement. + */ + if (gz->type->screen_bounds_get) { + rcti bounds; + gz->type->screen_bounds_get(C, gz, &bounds); + init_position[0] = bounds.xmin; + init_position[1] = bounds.ymin; + } + + return ui_tooltip_create_with_data(C, data, init_position, NULL, aspect); +} + +static uiTooltipData *ui_tooltip_data_from_search_item_tooltip_data( + const uiSearchItemTooltipData *item_tooltip_data) +{ + uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData"); + + if (item_tooltip_data->description[0]) { + uiTooltipField *field = text_field_add(data, + &(uiTooltipFormat){ + .style = UI_TIP_STYLE_HEADER, + .color_id = UI_TIP_LC_NORMAL, + .is_pad = true, + }); + field->text = BLI_strdup(item_tooltip_data->description); + } + + if (item_tooltip_data->name && item_tooltip_data->name[0]) { + uiTooltipField *field = text_field_add(data, + &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_VALUE, + .is_pad = true, + }); + field->text = BLI_strdup(item_tooltip_data->name); + } + if (item_tooltip_data->hint[0]) { + uiTooltipField *field = text_field_add(data, + &(uiTooltipFormat){ + .style = UI_TIP_STYLE_NORMAL, + .color_id = UI_TIP_LC_NORMAL, + .is_pad = true, + }); + field->text = BLI_strdup(item_tooltip_data->hint); + } + + if (data->fields_len == 0) { + MEM_freeN(data); + return NULL; + } + return data; +} + +/** + * Create a tooltip from search-item tooltip data \a item_tooltip data. + * To be called from a callback set with #UI_but_func_search_set_tooltip(). + * + * \param item_rect: Rectangle of the search item in search region space (#ui_searchbox_butrect()) + * which is passed to the tooltip callback. + */ +ARegion *UI_tooltip_create_from_search_item_generic( + bContext *C, + const ARegion *searchbox_region, + const rcti *item_rect, + const uiSearchItemTooltipData *item_tooltip_data) +{ + uiTooltipData *data = ui_tooltip_data_from_search_item_tooltip_data(item_tooltip_data); + if (data == NULL) { + return NULL; + } + + const float aspect = 1.0f; + const wmWindow *win = CTX_wm_window(C); + float init_position[2]; init_position[0] = win->eventstate->x; - init_position[1] = win->eventstate->y; + init_position[1] = item_rect->ymin + searchbox_region->winrct.ymin - (UI_POPUP_MARGIN / 2); return ui_tooltip_create_with_data(C, data, init_position, NULL, aspect); } diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index a37fb0dfde1..eaefc2c3736 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -341,13 +341,16 @@ void UI_fontstyle_draw_simple_backdrop(const uiFontStyle *fs, const float color[4] = {col_bg[0], col_bg[1], col_bg[2], 0.5f}; UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(true, - x - margin, - (y + decent) - margin, - x + width + margin, - (y + decent) + height + margin, - margin, - color); + UI_draw_roundbox_aa( + &(const rctf){ + .xmin = x - margin, + .xmax = x + width + margin, + .ymin = (y + decent) - margin, + .ymax = (y + decent) + height + margin, + }, + true, + margin, + color); } BLF_position(fs->uifont_id, x, y, 0.0f); diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c index 9f4cd32588d..25cf2e12377 100644 --- a/source/blender/editors/interface/interface_template_search_menu.c +++ b/source/blender/editors/interface/interface_template_search_menu.c @@ -1069,6 +1069,7 @@ static bool ui_search_menu_create_context_menu(struct bContext *C, static struct ARegion *ui_search_menu_create_tooltip(struct bContext *C, struct ARegion *region, + const rcti *UNUSED(item_rect), void *arg, void *active) { diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 003bb110baf..8127a32b271 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -208,6 +208,7 @@ static uiBlock *template_common_search_menu(const bContext *C, void *search_arg, uiButHandleFunc search_exec_fn, void *active_item, + uiButSearchTooltipFn item_tooltip_fn, const int preview_rows, const int preview_cols, float scale) @@ -284,6 +285,7 @@ static uiBlock *template_common_search_menu(const bContext *C, NULL, search_exec_fn, active_item); + UI_but_func_search_set_tooltip(but, item_tooltip_fn); UI_block_bounds_set_normal(block, 0.3f * U.widget_unit); UI_block_direction_set(block, UI_DIR_DOWN); @@ -485,6 +487,31 @@ static void id_search_cb_objects_from_scene(const bContext *C, id_search_cb_tagged(C, arg_template, str, items); } +static ARegion *template_ID_search_menu_item_tooltip( + bContext *C, ARegion *region, const rcti *item_rect, void *arg, void *active) +{ + TemplateID *template_ui = arg; + ID *active_id = active; + StructRNA *type = RNA_property_pointer_type(&template_ui->ptr, template_ui->prop); + + uiSearchItemTooltipData tooltip_data = {0}; + + tooltip_data.name = active_id->name + 2; + BLI_snprintf(tooltip_data.description, + sizeof(tooltip_data.description), + TIP_("Choose %s data-block to be assigned to this user"), + RNA_struct_ui_name(type)); + if (ID_IS_LINKED(active_id)) { + BLI_snprintf(tooltip_data.hint, + sizeof(tooltip_data.hint), + TIP_("Source library: %s\n%s"), + active_id->lib->id.name + 2, + active_id->lib->filepath); + } + + return UI_tooltip_create_from_search_item_generic(C, region, item_rect, &tooltip_data); +} + /* ID Search browse menu, open */ static uiBlock *id_search_menu(bContext *C, ARegion *region, void *arg_litem) { @@ -512,6 +539,7 @@ static uiBlock *id_search_menu(bContext *C, ARegion *region, void *arg_litem) &template_ui, template_ID_set_property_exec_fn, active_item_ptr.data, + template_ID_search_menu_item_tooltip, template_ui.prv_rows, template_ui.prv_cols, template_ui.scale); @@ -1632,6 +1660,7 @@ static uiBlock *template_search_menu(bContext *C, ARegion *region, void *arg_tem &template_search, template_search_exec_fn, active_ptr.data, + NULL, template_search.preview_rows, template_search.preview_cols, 1.0f); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index c0c34b0a93d..89720cbf17e 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -1533,25 +1533,22 @@ static void ui_text_clip_right_ex(const uiFontStyle *fstyle, { BLI_assert(str[0]); - /* If the trailing ellipsis takes more than 20% of all available width, just cut the string - * (as using the ellipsis would remove even more useful chars, and we cannot show much - * already!). - */ - if (sep_strwidth / okwidth > 0.2f) { - float tmp; - const int l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, okwidth, &tmp); - str[l_end] = '\0'; + /* How many BYTES (not characters) of this utf-8 string can fit, along with appended ellipsis. */ + int l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, okwidth - sep_strwidth, NULL); + + if (l_end > 0) { + /* At least one character, so clip and add the ellipsis. */ + memcpy(str + l_end, sep, sep_len + 1); /* +1 for trailing '\0'. */ if (r_final_len) { - *r_final_len = (size_t)l_end; + *r_final_len = (size_t)(l_end) + sep_len; } } else { - float tmp; - const int l_end = BLF_width_to_strlen( - fstyle->uifont_id, str, max_len, okwidth - sep_strwidth, &tmp); - memcpy(str + l_end, sep, sep_len + 1); /* +1 for trailing '\0'. */ + /* Otherwise fit as much as we can without adding an ellipsis. */ + l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, okwidth, NULL); + str[l_end] = '\0'; if (r_final_len) { - *r_final_len = (size_t)(l_end) + sep_len; + *r_final_len = (size_t)l_end; } } } @@ -5211,8 +5208,7 @@ void ui_draw_tooltip_background(const uiStyle *UNUSED(style), uiBlock *UNUSED(bl * * \param state: The state of the button, * typically #UI_ACTIVE, #UI_BUT_DISABLED, #UI_BUT_INACTIVE. - * \param use_sep: When true, characters after the last #UI_SEP_CHAR are right aligned, - * use for displaying key shortcuts. + * \param separator_type: The kind of separator which controls if and how the string is clipped. * \param r_xmax: The right hand position of the text, this takes into the icon, * padding and text clipping when there is not enough room to display the full text. */ @@ -5260,7 +5256,7 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, rect->xmax -= BLF_width(fstyle->uifont_id, cpoin + 1, INT_MAX) + UI_DPI_ICON_SIZE; } else if (separator_type == UI_MENU_ITEM_SEPARATOR_HINT) { - /* Deterimine max-width for the hint string to leave the name string un-clipped (if there's + /* Determine max-width for the hint string to leave the name string un-clipped (if there's * enough space to display it). */ const int available_width = BLI_rcti_size_x(rect) - padding; diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index f115618c13b..59aee0fde29 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -1895,7 +1895,7 @@ void UI_view2d_scroller_size_get(const View2D *v2d, float *r_x, float *r_y) * * \param r_x, r_y: scale on each axis */ -void UI_view2d_scale_get(View2D *v2d, float *r_x, float *r_y) +void UI_view2d_scale_get(const View2D *v2d, float *r_x, float *r_y) { if (r_x) { *r_x = UI_view2d_scale_get_x(v2d); diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 222f03ee1d8..4e642137d2a 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -1106,11 +1106,6 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op) float dx = RNA_float_get(op->ptr, "deltax") / U.dpi_fac; float dy = RNA_float_get(op->ptr, "deltay") / U.dpi_fac; - if (U.uiflag & USER_ZOOM_INVERT) { - dx *= -1; - dy *= -1; - } - /* Check if the 'timer' is initialized, as zooming with the trackpad * never uses the "Continuous" zoom method, and the 'timer' is not initialized. */ if ((U.viewzoom == USER_ZOOM_CONT) && vzd->timer) { /* XXX store this setting as RNA prop? */ @@ -1232,26 +1227,53 @@ static int view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *even vzd->lastx = event->prevx; vzd->lasty = event->prevy; - /* As we have only 1D information (magnify value), feed both axes - * with magnify information that is stored in x axis - */ - float fac = 0.01f * (event->prevx - event->x); - float dx = fac * BLI_rctf_size_x(&v2d->cur) / 10.0f; + float facx, facy; + float zoomfac = 0.01f; + + /* Some view2d's (graph) don't have min/max zoom, or extreme ones. */ + if (v2d->maxzoom > 0.0f) { + zoomfac = clamp_f(0.001f * v2d->maxzoom, 0.001f, 0.01f); + } + if (event->type == MOUSEPAN) { - fac = 0.01f * (event->prevy - event->y); + facx = zoomfac * WM_event_absolute_delta_x(event); + facy = zoomfac * WM_event_absolute_delta_y(event); + + if (U.uiflag & USER_ZOOM_INVERT) { + facx *= -1.0f; + facy *= -1.0f; + } + } + else { /* MOUSEZOOM */ + facx = facy = zoomfac * WM_event_absolute_delta_x(event); + } + + /* Only respect user setting zoom axis if the view does not have any zoom restrictions + * any will be scaled uniformly. */ + if (((v2d->keepzoom & (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y)) == 0) && + (v2d->keepzoom & V2D_KEEPASPECT)) { + if (U.uiflag & USER_ZOOM_HORIZ) { + facy = 0.0f; + } + else { + facx = 0.0f; + } } - float dy = fac * BLI_rctf_size_y(&v2d->cur) / 10.0f; /* support trackpad zoom to always zoom entirely - the v2d code uses portrait or * landscape exceptions */ if (v2d->keepzoom & V2D_KEEPASPECT) { - if (fabsf(dx) > fabsf(dy)) { - dy = dx; + if (fabsf(facx) > fabsf(facy)) { + facy = facx; } else { - dx = dy; + facx = facy; } } + + const float dx = facx * BLI_rctf_size_x(&v2d->cur); + const float dy = facy * BLI_rctf_size_y(&v2d->cur); + RNA_float_set(op->ptr, "deltax", dx); RNA_float_set(op->ptr, "deltay", dy); @@ -1320,19 +1342,13 @@ static int view_zoomdrag_modal(bContext *C, wmOperator *op, const wmEvent *event /* x-axis transform */ dist = BLI_rcti_size_x(&v2d->mask) / 2.0f; - len_old[0] = fabsf(vzd->lastx - vzd->region->winrct.xmin - dist); - len_new[0] = fabsf(event->x - vzd->region->winrct.xmin - dist); - - len_old[0] *= zoomfac * BLI_rctf_size_x(&v2d->cur); - len_new[0] *= zoomfac * BLI_rctf_size_x(&v2d->cur); + len_old[0] = zoomfac * fabsf(vzd->lastx - vzd->region->winrct.xmin - dist); + len_new[0] = zoomfac * fabsf(event->x - vzd->region->winrct.xmin - dist); /* y-axis transform */ dist = BLI_rcti_size_y(&v2d->mask) / 2.0f; - len_old[1] = fabsf(vzd->lasty - vzd->region->winrct.ymin - dist); - len_new[1] = fabsf(event->y - vzd->region->winrct.ymin - dist); - - len_old[1] *= zoomfac * BLI_rctf_size_y(&v2d->cur); - len_new[1] *= zoomfac * BLI_rctf_size_y(&v2d->cur); + len_old[1] = zoomfac * fabsf(vzd->lasty - vzd->region->winrct.ymin - dist); + len_new[1] = zoomfac * fabsf(event->y - vzd->region->winrct.ymin - dist); /* Calculate distance */ if (v2d->keepzoom & V2D_KEEPASPECT) { @@ -1343,40 +1359,44 @@ static int view_zoomdrag_modal(bContext *C, wmOperator *op, const wmEvent *event dx = len_new[0] - len_old[0]; dy = len_new[1] - len_old[1]; } - } - else { - /* 'continuous' or 'dolly' */ - float fac; - /* x-axis transform */ - fac = zoomfac * (event->x - vzd->lastx); - dx = fac * BLI_rctf_size_x(&v2d->cur); - /* y-axis transform */ - fac = zoomfac * (event->y - vzd->lasty); - dy = fac * BLI_rctf_size_y(&v2d->cur); + dx *= BLI_rctf_size_x(&v2d->cur); + dy *= BLI_rctf_size_y(&v2d->cur); + } + else { /* USER_ZOOM_CONT or USER_ZOOM_DOLLY */ + float facx = zoomfac * (event->x - vzd->lastx); + float facy = zoomfac * (event->y - vzd->lasty); /* Only respect user setting zoom axis if the view does not have any zoom restrictions * any will be scaled uniformly */ if ((v2d->keepzoom & V2D_LOCKZOOM_X) == 0 && (v2d->keepzoom & V2D_LOCKZOOM_Y) == 0 && (v2d->keepzoom & V2D_KEEPASPECT)) { if (U.uiflag & USER_ZOOM_HORIZ) { - dy = 0; + facy = 0.0f; } else { - dx = 0; + facx = 0.0f; } } - } - /* support zoom to always zoom entirely - the v2d code uses portrait or - * landscape exceptions */ - if (v2d->keepzoom & V2D_KEEPASPECT) { - if (fabsf(dx) > fabsf(dy)) { - dy = dx; - } - else { - dx = dy; + /* support zoom to always zoom entirely - the v2d code uses portrait or + * landscape exceptions */ + if (v2d->keepzoom & V2D_KEEPASPECT) { + if (fabsf(facx) > fabsf(facy)) { + facy = facx; + } + else { + facx = facy; + } } + + dx = facx * BLI_rctf_size_x(&v2d->cur); + dy = facy * BLI_rctf_size_y(&v2d->cur); + } + + if (U.uiflag & USER_ZOOM_INVERT) { + dx *= -1.0f; + dy *= -1.0f; } /* set transform amount, and add current deltas to stored total delta (for redo) */ diff --git a/source/blender/editors/lattice/editlattice_select.c b/source/blender/editors/lattice/editlattice_select.c index 4497ca1a379..cb3f9a89e62 100644 --- a/source/blender/editors/lattice/editlattice_select.c +++ b/source/blender/editors/lattice/editlattice_select.c @@ -108,7 +108,7 @@ bool ED_lattice_deselect_all_multi(struct bContext *C) static int lattice_select_random_exec(bContext *C, wmOperator *op) { - const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f; + const float randfac = RNA_float_get(op->ptr, "ratio"); const int seed = WM_operator_properties_select_random_seed_increment_get(op); const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT); diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c index d7b3d74bc7e..1226cc57359 100644 --- a/source/blender/editors/mask/mask_add.c +++ b/source/blender/editors/mask/mask_add.c @@ -507,6 +507,9 @@ static int add_vertex_handle_cyclic( static int add_vertex_exec(bContext *C, wmOperator *op) { + MaskViewLockState lock_state; + ED_mask_view_lock_state_store(C, &lock_state); + Mask *mask = CTX_data_edit_mask(C); if (mask == NULL) { /* if there's no active mask, create one */ @@ -548,6 +551,8 @@ static int add_vertex_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&mask->id, ID_RECALC_GEOMETRY); + ED_mask_view_lock_state_restore_no_jump(C, &lock_state); + return OPERATOR_FINISHED; } @@ -690,6 +695,9 @@ void MASK_OT_add_feather_vertex(wmOperatorType *ot) static int create_primitive_from_points( bContext *C, wmOperator *op, const float (*points)[2], int num_points, char handle_type) { + MaskViewLockState lock_state; + ED_mask_view_lock_state_store(C, &lock_state); + ScrArea *area = CTX_wm_area(C); int size = RNA_float_get(op->ptr, "size"); @@ -752,6 +760,8 @@ static int create_primitive_from_points( DEG_id_tag_update(&mask->id, ID_RECALC_GEOMETRY); + ED_mask_view_lock_state_restore_no_jump(C, &lock_state); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/mask/mask_edit.c b/source/blender/editors/mask/mask_edit.c index 663ae0097ad..f1041d062a8 100644 --- a/source/blender/editors/mask/mask_edit.c +++ b/source/blender/editors/mask/mask_edit.c @@ -184,3 +184,39 @@ void ED_operatormacros_mask(void) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Lock-to-selection viewport preservation + * \{ */ + +void ED_mask_view_lock_state_store(const bContext *C, MaskViewLockState *state) +{ + SpaceClip *space_clip = CTX_wm_space_clip(C); + if (space_clip != NULL) { + ED_clip_view_lock_state_store(C, &state->space_clip_state); + } +} + +void ED_mask_view_lock_state_restore_no_jump(const bContext *C, const MaskViewLockState *state) +{ + SpaceClip *space_clip = CTX_wm_space_clip(C); + if (space_clip != NULL) { + if ((space_clip->flag & SC_LOCK_SELECTION) == 0) { + /* Early output if the editor is not locked to selection. + * Avoids forced dependency graph evaluation here. */ + return; + } + + /* Mask's lock-to-selection requires deformed splines to be evaluated to calculate bounds of + * points after animation has been evaluated. The restore-no-jump type of function does + * calculation of new offset for the view for an updated state of mask to cancel the offset out + * by modifying locked offset. In order to do such calculation mask needs to be evaluated after + * modification by an operator. */ + struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + (void)depsgraph; + + ED_clip_view_lock_state_restore_no_jump(C, &state->space_clip_state); + } +} + +/** \} */ diff --git a/source/blender/editors/mask/mask_intern.h b/source/blender/editors/mask/mask_intern.h index f6990583383..ee1784011ea 100644 --- a/source/blender/editors/mask/mask_intern.h +++ b/source/blender/editors/mask/mask_intern.h @@ -23,6 +23,8 @@ #pragma once +#include "ED_clip.h" + struct Mask; struct bContext; struct wmOperatorType; @@ -92,6 +94,19 @@ void ED_mask_select_flush_all(struct Mask *mask); bool ED_maskedit_poll(struct bContext *C); bool ED_maskedit_mask_poll(struct bContext *C); +/* Generalized solution for preserving editor viewport when making changes while lock-to-selection + * is enabled. + * Any mask operator can use this API, without worrying that some editors do not have an idea of + * lock-to-selection. */ + +typedef struct MaskViewLockState { + ClipViewLockState space_clip_state; +} MaskViewLockState; + +void ED_mask_view_lock_state_store(const struct bContext *C, MaskViewLockState *state); +void ED_mask_view_lock_state_restore_no_jump(const struct bContext *C, + const MaskViewLockState *state); + /* mask_query.c */ bool ED_mask_find_nearest_diff_point(const struct bContext *C, struct Mask *mask, diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c index 25cc39bf9a0..98509e82f48 100644 --- a/source/blender/editors/mask/mask_ops.c +++ b/source/blender/editors/mask/mask_ops.c @@ -226,6 +226,12 @@ typedef struct SlidePointData { int width, height; float prev_mouse_coord[2]; + + /* Previous clip coordinate which was resolved from mouse position (0, 0). + * Is used to compensate for view offset moving in-between of mouse events when + * lock-to-selection is enabled. */ + float prev_zero_coord[2]; + float no[2]; bool is_curvature_only, is_accurate, is_initial_feather, is_overall_feather; @@ -431,6 +437,9 @@ static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent * const float threshold = 19; eMaskWhichHandle which_handle; + MaskViewLockState lock_state; + ED_mask_view_lock_state_store(C, &lock_state); + ED_mask_mouse_pos(area, region, event->mval, co); ED_mask_get_size(area, &width, &height); @@ -530,7 +539,15 @@ static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent * } customdata->which_handle = which_handle; + { + WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); + DEG_id_tag_update(&mask->id, 0); + + ED_mask_view_lock_state_restore_no_jump(C, &lock_state); + } + ED_mask_mouse_pos(area, region, event->mval, customdata->prev_mouse_coord); + ED_mask_mouse_pos(area, region, (int[2]){0, 0}, customdata->prev_zero_coord); } return customdata; @@ -655,10 +672,24 @@ static int slide_point_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_mask_mouse_pos(area, region, event->mval, co); sub_v2_v2v2(delta, co, data->prev_mouse_coord); + copy_v2_v2(data->prev_mouse_coord, co); + + /* Compensate for possibly moved view offset since the last event. + * The idea is to see how mapping of a fixed and known position did change. */ + { + float zero_coord[2]; + ED_mask_mouse_pos(area, region, (int[2]){0, 0}, zero_coord); + + float zero_delta[2]; + sub_v2_v2v2(zero_delta, zero_coord, data->prev_zero_coord); + sub_v2_v2(delta, zero_delta); + + copy_v2_v2(data->prev_zero_coord, zero_coord); + } + if (data->is_accurate) { mul_v2_fl(delta, 0.2f); } - copy_v2_v2(data->prev_mouse_coord, co); if (data->action == SLIDE_ACTION_HANDLE) { float new_handle[2]; @@ -966,6 +997,9 @@ static SlideSplineCurvatureData *slide_spline_curvature_customdata(bContext *C, float u, co[2]; BezTriple *next_bezt; + MaskViewLockState lock_state; + ED_mask_view_lock_state_store(C, &lock_state); + ED_mask_mouse_pos(CTX_wm_area(C), CTX_wm_region(C), event->mval, co); if (!ED_mask_find_nearest_diff_point(C, @@ -1047,6 +1081,9 @@ static SlideSplineCurvatureData *slide_spline_curvature_customdata(bContext *C, mask_layer->act_point = point; ED_mask_select_flush_all(mask); + DEG_id_tag_update(&mask->id, 0); + ED_mask_view_lock_state_restore_no_jump(C, &lock_state); + return slide_data; } diff --git a/source/blender/editors/mask/mask_query.c b/source/blender/editors/mask/mask_query.c index cfd57ca3477..eace146dbe9 100644 --- a/source/blender/editors/mask/mask_query.c +++ b/source/blender/editors/mask/mask_query.c @@ -604,22 +604,22 @@ void ED_mask_point_pos__reverse( *yr = co[1]; } -bool ED_mask_selected_minmax(const bContext *C, float min[2], float max[2]) +bool ED_mask_selected_minmax(const bContext *C, float min[2], float max[2], bool include_handles) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Mask *mask = CTX_data_edit_mask(C); - /* Use evaluated mask to take animation into account. - * The animation of splies is not "flushed" back to original, so need to explicitly - * sue evaluated datablock here. */ - Mask *mask_eval = (Mask *)DEG_get_evaluated_id(depsgraph, &mask->id); - bool ok = false; if (mask == NULL) { return ok; } + /* Use evaluated mask to take animation into account. + * The animation of splies is not "flushed" back to original, so need to explicitly + * sue evaluated datablock here. */ + Mask *mask_eval = (Mask *)DEG_get_evaluated_id(depsgraph, &mask->id); + INIT_MINMAX2(min, max); for (MaskLayer *mask_layer = mask_eval->masklayers.first; mask_layer != NULL; mask_layer = mask_layer->next) { @@ -638,22 +638,29 @@ bool ED_mask_selected_minmax(const bContext *C, float min[2], float max[2]) } if (bezt->f2 & SELECT) { minmax_v2v2_v2(min, max, deform_point->bezt.vec[1]); + ok = true; + } + + if (!include_handles) { + /* Ignore handles. */ } - if (BKE_mask_point_handles_mode_get(point) == MASK_HANDLE_MODE_STICK) { + else if (BKE_mask_point_handles_mode_get(point) == MASK_HANDLE_MODE_STICK) { BKE_mask_point_handle(deform_point, MASK_WHICH_HANDLE_STICK, handle); minmax_v2v2_v2(min, max, handle); + ok = true; } else { if ((bezt->f1 & SELECT) && (bezt->h1 != HD_VECT)) { BKE_mask_point_handle(deform_point, MASK_WHICH_HANDLE_LEFT, handle); minmax_v2v2_v2(min, max, handle); + ok = true; } if ((bezt->f3 & SELECT) && (bezt->h2 != HD_VECT)) { BKE_mask_point_handle(deform_point, MASK_WHICH_HANDLE_RIGHT, handle); minmax_v2v2_v2(min, max, handle); + ok = true; } } - ok = true; } } } diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c index cdc6ece1e84..5c369afc4cd 100644 --- a/source/blender/editors/mask/mask_select.c +++ b/source/blender/editors/mask/mask_select.c @@ -214,12 +214,17 @@ static int select_all_exec(bContext *C, wmOperator *op) Mask *mask = CTX_data_edit_mask(C); int action = RNA_enum_get(op->ptr, "action"); + MaskViewLockState lock_state; + ED_mask_view_lock_state_store(C, &lock_state); + ED_mask_select_toggle_all(mask, action); ED_mask_select_flush_all(mask); DEG_id_tag_update(&mask->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); + ED_mask_view_lock_state_restore_no_jump(C, &lock_state); + return OPERATOR_FINISHED; } @@ -261,6 +266,9 @@ static int select_exec(bContext *C, wmOperator *op) eMaskWhichHandle which_handle; const float threshold = 19; + MaskViewLockState lock_state; + ED_mask_view_lock_state_store(C, &lock_state); + RNA_float_get_array(op->ptr, "location", co); point = ED_mask_point_find_nearest( @@ -324,6 +332,8 @@ static int select_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&mask->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); + ED_mask_view_lock_state_restore_no_jump(C, &lock_state); + return OPERATOR_FINISHED; } @@ -364,12 +374,15 @@ static int select_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&mask->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); + ED_mask_view_lock_state_restore_no_jump(C, &lock_state); + return OPERATOR_FINISHED; } if (deselect_all) { /* For clip editor tracks, leave deselect all to clip editor. */ if (!ED_clip_can_select(C)) { ED_mask_deselect_all(C); + ED_mask_view_lock_state_restore_no_jump(C, &lock_state); return OPERATOR_FINISHED; } } diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index f45f48e0e32..379140a821a 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -2752,8 +2752,6 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) } } - view3d_operator_needs_opengl(C); - /* alloc new customdata */ kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), __func__); @@ -2831,7 +2829,6 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) em_setup_viewcontext(C, &kcd->vc); kcd->region = kcd->vc.region; - view3d_operator_needs_opengl(C); ED_view3d_init_mats_rv3d(obedit, kcd->vc.rv3d); /* needed to initialize clipping */ if (kcd->mode == MODE_PANNING) { @@ -3067,8 +3064,6 @@ void EDBM_mesh_knife(bContext *C, LinkNode *polys, bool use_tag, bool cut_throug { KnifeTool_OpData *kcd; - view3d_operator_needs_opengl(C); - /* init */ { const bool only_select = false; diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index b5b4802aa78..0c68bb8f82b 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -4573,7 +4573,7 @@ void MESH_OT_select_non_manifold(wmOperatorType *ot) static int edbm_select_random_exec(bContext *C, wmOperator *op) { const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT); - const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f; + const float randfac = RNA_float_get(op->ptr, "ratio"); const int seed = WM_operator_properties_select_random_seed_increment_get(op); ViewLayer *view_layer = CTX_data_view_layer(C); diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c index 094011ebef1..cf453bf0c32 100644 --- a/source/blender/editors/metaball/mball_edit.c +++ b/source/blender/editors/metaball/mball_edit.c @@ -483,7 +483,7 @@ void MBALL_OT_select_similar(wmOperatorType *ot) static int select_random_metaelems_exec(bContext *C, wmOperator *op) { const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT); - const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f; + const float randfac = RNA_float_get(op->ptr, "ratio"); const int seed = WM_operator_properties_select_random_seed_increment_get(op); ViewLayer *view_layer = CTX_data_view_layer(C); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index c6d6b8b16b9..9ab5eafa84d 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -607,7 +607,6 @@ Object *ED_object_add_type_with_obdata(bContext *C, Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - /* For as long scene has editmode... */ { Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); if (obedit != NULL) { @@ -3364,6 +3363,9 @@ static int object_add_named_exec(bContext *C, wmOperator *op) ED_view3d_cursor3d_position(C, mval, false, basen->object->loc); } + /* object_add_duplicate_internal() doesn't deselect other objects, unlike object_add_common() or + * BKE_view_layer_base_deselect_all(). */ + ED_object_base_deselect_all(view_layer, NULL, BA_DESELECT); ED_object_base_select(basen, BA_SELECT); ED_object_base_activate(C, basen); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index acad1b43cbb..e4d06dca8cf 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1790,20 +1790,33 @@ void OBJECT_OT_make_links_scene(wmOperatorType *ot) void OBJECT_OT_make_links_data(wmOperatorType *ot) { static const EnumPropertyItem make_links_items[] = { - {MAKE_LINKS_OBDATA, "OBDATA", 0, "Object Data", ""}, - {MAKE_LINKS_MATERIALS, "MATERIAL", 0, "Materials", ""}, - {MAKE_LINKS_ANIMDATA, "ANIMATION", 0, "Animation Data", ""}, - {MAKE_LINKS_GROUP, "GROUPS", 0, "Collection", ""}, - {MAKE_LINKS_DUPLICOLLECTION, "DUPLICOLLECTION", 0, "Instance Collection", ""}, - {MAKE_LINKS_MODIFIERS, "MODIFIERS", 0, "Modifiers", ""}, - {MAKE_LINKS_FONTS, "FONTS", 0, "Fonts", ""}, - {MAKE_LINKS_SHADERFX, "EFFECTS", 0, "Effects", ""}, + {MAKE_LINKS_OBDATA, "OBDATA", 0, "Link Object Data", "Replace assigned Object Data"}, + {MAKE_LINKS_MATERIALS, "MATERIAL", 0, "Link Materials", "Replace assigned Materials"}, + {MAKE_LINKS_ANIMDATA, + "ANIMATION", + 0, + "Link Animation Data", + "Replace assigned Animation Data"}, + {MAKE_LINKS_GROUP, "GROUPS", 0, "Link Collections", "Replace assigned Collections"}, + {MAKE_LINKS_DUPLICOLLECTION, + "DUPLICOLLECTION", + 0, + "Link Instance Collection", + "Replace assigned Collection Instance"}, + {MAKE_LINKS_FONTS, "FONTS", 0, "Link Fonts to Text", "Replace Text object Fonts"}, + {0, "", 0, NULL, NULL}, + {MAKE_LINKS_MODIFIERS, "MODIFIERS", 0, "Copy Modifiers", "Replace Modifiers"}, + {MAKE_LINKS_SHADERFX, + "EFFECTS", + 0, + "Copy Grease Pencil Effects", + "Replace Grease Pencil Effects"}, {0, NULL, 0, NULL, NULL}, }; /* identifiers */ - ot->name = "Link Data"; - ot->description = "Apply active object links to other selected objects"; + ot->name = "Link/Transfer Data"; + ot->description = "Transfer data from active object to selected objects"; ot->idname = "OBJECT_OT_make_links_data"; /* api callbacks */ diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c index b36d89dc37a..50dac12ab57 100644 --- a/source/blender/editors/object/object_select.c +++ b/source/blender/editors/object/object_select.c @@ -1453,7 +1453,7 @@ void OBJECT_OT_select_less(wmOperatorType *ot) static int object_select_random_exec(bContext *C, wmOperator *op) { - const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f; + const float randfac = RNA_float_get(op->ptr, "ratio"); const int seed = WM_operator_properties_select_random_seed_increment_get(op); const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT); diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index 0528d64dca9..13c0740bce6 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -1394,7 +1394,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) * (all layers are considered without evaluating lock attributes) */ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* calculate difference matrix */ - BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); /* undo matrix */ invert_m4_m4(inverse_diff_mat, diff_mat); LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 8ab25fa74b8..3a6201fd418 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -1999,7 +1999,7 @@ static int select_random_exec(bContext *C, wmOperator *op) int p; int k; - const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f; + const float randfac = RNA_float_get(op->ptr, "ratio"); const int seed = WM_operator_properties_select_random_seed_increment_get(op); const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT); RNG *rng; @@ -3750,8 +3750,7 @@ static void brush_puff(PEData *data, int point_index, float mouse_distance) } } else { - /* compute position as if hair was standing up straight. - * */ + /* Compute position as if hair was standing up straight. */ float length; copy_v3_v3(co_prev, co); copy_v3_v3(co, key->co); diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 581169d823e..2c71345699f 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -141,13 +141,15 @@ void ED_region_pixelspace(ARegion *region) } /* only exported for WM */ -void ED_region_do_listen( - wmWindow *win, ScrArea *area, ARegion *region, wmNotifier *note, const Scene *scene) +void ED_region_do_listen(wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *notifier = params->notifier; + /* generic notes first */ - switch (note->category) { + switch (notifier->category) { case NC_WM: - if (note->data == ND_FILEREAD) { + if (notifier->data == ND_FILEREAD) { ED_region_tag_redraw(region); } break; @@ -157,16 +159,16 @@ void ED_region_do_listen( } if (region->type && region->type->listener) { - region->type->listener(win, area, region, note, scene); + region->type->listener(params); } } /* only exported for WM */ -void ED_area_do_listen(wmWindow *win, ScrArea *area, wmNotifier *note, Scene *scene) +void ED_area_do_listen(wmSpaceTypeListenerParams *params) { /* no generic notes? */ - if (area->type && area->type->listener) { - area->type->listener(win, area, note, scene); + if (params->area->type && params->area->type->listener) { + params->area->type->listener(params); } } @@ -289,7 +291,15 @@ static void region_draw_azone_tab_arrow(ScrArea *area, ARegion *region, AZone *a float alpha = WM_region_use_viewport(area, region) ? 0.6f : 0.4f; const float color[4] = {0.05f, 0.05f, 0.05f, alpha}; UI_draw_roundbox_aa( - true, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, color); + &(const rctf){ + .xmin = (float)az->x1, + .xmax = (float)az->x2, + .ymin = (float)az->y1, + .ymax = (float)az->y2, + }, + true, + 4.0f, + color); draw_azone_arrow((float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, az->edge); } @@ -373,7 +383,16 @@ static void region_draw_status_text(ScrArea *area, ARegion *region) float color[4] = {0.0f, 0.0f, 0.0f, 0.5f}; UI_GetThemeColor3fv(TH_BACK, color); UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(true, x1, y1, x2, y2, 4.0f, color); + UI_draw_roundbox_aa( + &(const rctf){ + .xmin = x1, + .xmax = x2, + .ymin = y1, + .ymax = y2, + }, + true, + 4.0f, + color); UI_FontThemeColor(fontid, TH_TEXT); } @@ -418,16 +437,13 @@ void ED_area_do_msg_notify_tag_refresh( ED_area_tag_refresh(area); } -void ED_area_do_mgs_subscribe_for_tool_header( - /* Follow ARegionType.message_subscribe */ - const struct bContext *UNUSED(C), - struct WorkSpace *workspace, - struct Scene *UNUSED(scene), - struct bScreen *UNUSED(screen), - struct ScrArea *UNUSED(area), - struct ARegion *region, - struct wmMsgBus *mbus) +/* Follow ARegionType.message_subscribe */ +void ED_area_do_mgs_subscribe_for_tool_header(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + WorkSpace *workspace = params->workspace; + ARegion *region = params->region; + BLI_assert(region->regiontype == RGN_TYPE_TOOL_HEADER); wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { .owner = region, @@ -438,16 +454,12 @@ void ED_area_do_mgs_subscribe_for_tool_header( mbus, &workspace->id, workspace, WorkSpace, tools, &msg_sub_value_region_tag_redraw); } -void ED_area_do_mgs_subscribe_for_tool_ui( - /* Follow ARegionType.message_subscribe */ - const struct bContext *UNUSED(C), - struct WorkSpace *workspace, - struct Scene *UNUSED(scene), - struct bScreen *UNUSED(screen), - struct ScrArea *UNUSED(area), - struct ARegion *region, - struct wmMsgBus *mbus) +void ED_area_do_mgs_subscribe_for_tool_ui(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + WorkSpace *workspace = params->workspace; + ARegion *region = params->region; + BLI_assert(region->regiontype == RGN_TYPE_UI); const char *panel_category_tool = "Tool"; const char *category = UI_panel_category_active_get(region, false); @@ -634,7 +646,16 @@ void ED_region_do_draw(bContext *C, ARegion *region) WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_region_tag_redraw, __func__); } - ED_region_message_subscribe(C, workspace, scene, screen, area, region, mbus); + wmRegionMessageSubscribeParams message_subscribe_params = { + .context = C, + .message_bus = mbus, + .workspace = workspace, + .scene = scene, + .screen = screen, + .area = area, + .region = region, + }; + ED_region_message_subscribe(&message_subscribe_params); } } @@ -4027,14 +4048,12 @@ void ED_region_cache_draw_cached_segments( /** * Generate subscriptions for this region. */ -void ED_region_message_subscribe(bContext *C, - struct WorkSpace *workspace, - struct Scene *scene, - struct bScreen *screen, - struct ScrArea *area, - struct ARegion *region, - struct wmMsgBus *mbus) +void ED_region_message_subscribe(wmRegionMessageSubscribeParams *params) { + ARegion *region = params->region; + const bContext *C = params->context; + struct wmMsgBus *mbus = params->message_bus; + if (region->gizmo_map != NULL) { WM_gizmomap_message_subscribe(C, region->gizmo_map, region, mbus); } @@ -4044,7 +4063,7 @@ void ED_region_message_subscribe(bContext *C, } if (region->type->message_subscribe != NULL) { - region->type->message_subscribe(C, workspace, scene, screen, area, region, mbus); + region->type->message_subscribe(params); } } diff --git a/source/blender/editors/screen/area_utils.c b/source/blender/editors/screen/area_utils.c index 075759f1120..b784a4b0056 100644 --- a/source/blender/editors/screen/area_utils.c +++ b/source/blender/editors/screen/area_utils.c @@ -22,6 +22,8 @@ #include "DNA_userdef_types.h" +#include "BKE_screen.h" + #include "BLI_blenlib.h" #include "BLI_utildefines.h" @@ -42,14 +44,11 @@ /** * Callback for #ARegionType.message_subscribe */ -void ED_region_generic_tools_region_message_subscribe(const struct bContext *UNUSED(C), - struct WorkSpace *UNUSED(workspace), - struct Scene *UNUSED(scene), - struct bScreen *UNUSED(screen), - struct ScrArea *UNUSED(area), - struct ARegion *region, - struct wmMsgBus *mbus) +void ED_region_generic_tools_region_message_subscribe(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + ARegion *region = params->region; + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { .owner = region, .user_data = region, diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index 33b918e6d4d..a88afecd064 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -57,11 +57,12 @@ static void immDrawPixelsTexSetupAttributes(IMMDrawPixelsTexState *state) vert_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); } -/* To be used before calling immDrawPixelsTex - * Default shader is GPU_SHADER_2D_IMAGE_COLOR - * You can still set uniforms with : - * GPU_shader_uniform_int(shader, GPU_shader_get_uniform(shader, "name"), 0); - * */ +/** + * To be used before calling #immDrawPixelsTex + * Default shader is #GPU_SHADER_2D_IMAGE_COLOR + * You can still set uniforms with: + * `GPU_shader_uniform_int(shader, GPU_shader_get_uniform(shader, "name"), 0);` + */ IMMDrawPixelsTexState immDrawPixelsTexSetup(int builtin) { IMMDrawPixelsTexState state; @@ -77,10 +78,11 @@ IMMDrawPixelsTexState immDrawPixelsTexSetup(int builtin) return state; } -/* Use the currently bound shader. +/** + * Use the currently bound shader. * - * Use immDrawPixelsTexSetup to bind the shader you - * want before calling immDrawPixelsTex. + * Use #immDrawPixelsTexSetup to bind the shader you + * want before calling #immDrawPixelsTex. * * If using a special shader double check it uses the same * attributes "pos" "texCoord" and uniform "image". @@ -89,7 +91,7 @@ IMMDrawPixelsTexState immDrawPixelsTexSetup(int builtin) * * Be also aware that this function unbinds the shader when * it's finished. - * */ + */ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, float x, float y, diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 1dfe606be78..627a67358f2 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -866,7 +866,7 @@ static eContextResult screen_ctx_editable_gpencil_strokes(const bContext *C, LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { if (ED_gpencil_stroke_can_use_direct(area, gps)) { /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) { continue; } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 6e6386b24aa..27726b8370e 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -2415,9 +2415,10 @@ static bool IsectPT2Df_limit( (area_tri_v2(v1, v2, v3))) < limit; } -/* Clip the face by a bucket and set the uv-space bucket_bounds_uv +/** + * Clip the face by a bucket and set the uv-space bucket_bounds_uv * so we have the clipped UV's to do pixel intersection tests with - * */ + */ static int float_z_sort_flip(const void *p1, const void *p2) { return (((float *)p1)[2] < ((float *)p2)[2] ? 1 : -1); diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index ff0201f9702..691287a543f 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -305,12 +305,11 @@ static void action_header_region_draw(const bContext *C, ARegion *region) ED_region_header(C, region); } -static void action_channel_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void action_channel_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_ANIMATION: @@ -356,14 +355,13 @@ static void action_channel_region_listener(wmWindow *UNUSED(win), } } -static void saction_channel_region_message_subscribe(const struct bContext *UNUSED(C), - struct WorkSpace *UNUSED(workspace), - struct Scene *UNUSED(scene), - struct bScreen *screen, - struct ScrArea *area, - struct ARegion *region, - struct wmMsgBus *mbus) +static void saction_channel_region_message_subscribe(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + bScreen *screen = params->screen; + ScrArea *area = params->area; + ARegion *region = params->region; + PointerRNA ptr; RNA_pointer_create(&screen->id, &RNA_SpaceDopeSheetEditor, area->spacedata.first, &ptr); @@ -401,12 +399,11 @@ static void saction_channel_region_message_subscribe(const struct bContext *UNUS } } -static void action_main_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void action_main_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_ANIMATION: @@ -460,14 +457,14 @@ static void action_main_region_listener(wmWindow *UNUSED(win), } } -static void saction_main_region_message_subscribe(const struct bContext *C, - struct WorkSpace *workspace, - struct Scene *scene, - struct bScreen *screen, - struct ScrArea *area, - struct ARegion *region, - struct wmMsgBus *mbus) +static void saction_main_region_message_subscribe(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + Scene *scene = params->scene; + bScreen *screen = params->screen; + ScrArea *area = params->area; + ARegion *region = params->region; + PointerRNA ptr; RNA_pointer_create(&screen->id, &RNA_SpaceDopeSheetEditor, area->spacedata.first, &ptr); @@ -502,15 +499,14 @@ static void saction_main_region_message_subscribe(const struct bContext *C, } /* Now run the general "channels region" one - since channels and main should be in sync */ - saction_channel_region_message_subscribe(C, workspace, scene, screen, area, region, mbus); + saction_channel_region_message_subscribe(params); } /* editor level listener */ -static void action_listener(wmWindow *UNUSED(win), - ScrArea *area, - wmNotifier *wmn, - Scene *UNUSED(scene)) +static void action_listener(const wmSpaceTypeListenerParams *params) { + ScrArea *area = params->area; + wmNotifier *wmn = params->notifier; SpaceAction *saction = (SpaceAction *)area->spacedata.first; /* context changes */ @@ -660,12 +656,11 @@ static void action_listener(wmWindow *UNUSED(win), } } -static void action_header_region_listener(wmWindow *UNUSED(win), - ScrArea *area, - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void action_header_region_listener(const wmRegionListenerParams *params) { + ScrArea *area = params->area; + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; SpaceAction *saction = (SpaceAction *)area->spacedata.first; /* context changes */ @@ -737,12 +732,11 @@ static void action_buttons_area_draw(const bContext *C, ARegion *region) ED_region_panels(C, region); } -static void action_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void action_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_ANIMATION: diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index 10fa2c19919..01e57eab7c2 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -70,16 +70,13 @@ #include "io_ops.h" -/* only call once on startup, storage is global in BKE kernel listbase */ +/* Only called once on startup. storage is global in BKE kernel listbase. */ void ED_spacetypes_init(void) { - const ListBase *spacetypes; - SpaceType *type; - - /* UI_UNIT_X is now a variable, is used in some spacetype inits? */ + /* UI unit is a variable, may be used in some space type inits. */ U.widget_unit = 20; - /* create space types */ + /* Create space types. */ ED_spacetype_outliner(); ED_spacetype_view3d(); ED_spacetype_ipo(); @@ -98,9 +95,8 @@ void ED_spacetypes_init(void) ED_spacetype_clip(); ED_spacetype_statusbar(); ED_spacetype_topbar(); - // ... - /* register operator types for screen and all spaces */ + /* Register operator types for screen and all spaces. */ ED_operatortypes_userpref(); ED_operatortypes_workspace(); ED_operatortypes_scene(); @@ -132,7 +128,7 @@ void ED_spacetypes_init(void) ED_screen_user_menu_register(); - /* gizmo types */ + /* Gizmo types. */ ED_gizmotypes_button_2d(); ED_gizmotypes_dial_3d(); ED_gizmotypes_move_3d(); @@ -144,10 +140,10 @@ void ED_spacetypes_init(void) ED_gizmotypes_cage_3d(); ED_gizmotypes_snap_3d(); - /* register types for operators and gizmos */ - spacetypes = BKE_spacetypes_list(); - for (type = spacetypes->first; type; type = type->next) { - /* init gizmo types first, operator-types need them */ + /* Register types for operators and gizmos. */ + const ListBase *spacetypes = BKE_spacetypes_list(); + LISTBASE_FOREACH (const SpaceType *, type, spacetypes) { + /* Initialize gizmo types first, operator types need them. */ if (type->gizmos) { type->gizmos(); } @@ -159,11 +155,8 @@ void ED_spacetypes_init(void) void ED_spacemacros_init(void) { - const ListBase *spacetypes; - SpaceType *type; - - /* Macros's must go last since they reference other operators. - * We need to have them go after python operators too */ + /* Macros must go last since they reference other operators. + * They need to be registered after python operators too. */ ED_operatormacros_armature(); ED_operatormacros_mesh(); ED_operatormacros_uvedit(); @@ -180,24 +173,21 @@ void ED_spacemacros_init(void) ED_operatormacros_paint(); ED_operatormacros_gpencil(); - /* register dropboxes (can use macros) */ - spacetypes = BKE_spacetypes_list(); - for (type = spacetypes->first; type; type = type->next) { + /* Register dropboxes (can use macros). */ + const ListBase *spacetypes = BKE_spacetypes_list(); + LISTBASE_FOREACH (const SpaceType *, type, spacetypes) { if (type->dropboxes) { type->dropboxes(); } } } -/* called in wm.c */ -/* keymap definitions are registered only once per WM initialize, usually on file read, - * using the keymap the actual areas/regions add the handlers */ +/** + * \note Keymap definitions are registered only once per WM initialize, + * usually on file read, using the keymap the actual areas/regions add the handlers. + * \note Called in wm.c. */ void ED_spacetypes_keymap(wmKeyConfig *keyconf) { - const ListBase *spacetypes; - SpaceType *stype; - ARegionType *atype; - ED_keymap_screen(keyconf); ED_keymap_anim(keyconf); ED_keymap_animchannels(keyconf); @@ -219,20 +209,20 @@ void ED_spacetypes_keymap(wmKeyConfig *keyconf) ED_keymap_transform(keyconf); - spacetypes = BKE_spacetypes_list(); - for (stype = spacetypes->first; stype; stype = stype->next) { - if (stype->keymap) { - stype->keymap(keyconf); + const ListBase *spacetypes = BKE_spacetypes_list(); + LISTBASE_FOREACH (const SpaceType *, type, spacetypes) { + if (type->keymap) { + type->keymap(keyconf); } - for (atype = stype->regiontypes.first; atype; atype = atype->next) { - if (atype->keymap) { - atype->keymap(keyconf); + LISTBASE_FOREACH (ARegionType *, region_type, &type->regiontypes) { + if (region_type->keymap) { + region_type->keymap(keyconf); } } } } -/* ********************** custom drawcall api ***************** */ +/* ********************** Custom Draw Call API ***************** */ typedef struct RegionDrawCB { struct RegionDrawCB *next, *prev; @@ -261,9 +251,7 @@ void *ED_region_draw_cb_activate(ARegionType *art, void ED_region_draw_cb_exit(ARegionType *art, void *handle) { - RegionDrawCB *rdc; - - for (rdc = art->drawcalls.first; rdc; rdc = rdc->next) { + LISTBASE_FOREACH (RegionDrawCB *, rdc, &art->drawcalls) { if (rdc == (RegionDrawCB *)handle) { BLI_remlink(&art->drawcalls, rdc); MEM_freeN(rdc); @@ -274,9 +262,7 @@ void ED_region_draw_cb_exit(ARegionType *art, void *handle) void ED_region_draw_cb_draw(const bContext *C, ARegion *region, int type) { - RegionDrawCB *rdc; - - for (rdc = region->type->drawcalls.first; rdc; rdc = rdc->next) { + LISTBASE_FOREACH (RegionDrawCB *, rdc, ®ion->type->drawcalls) { if (rdc->type == type) { rdc->draw(C, region, rdc->customdata); diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index e50ca2ec92b..07bc1d42c3f 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -514,12 +514,11 @@ static void buttons_main_region_layout(const bContext *C, ARegion *region) sbuts->mainbo = sbuts->mainb; } -static void buttons_main_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void buttons_main_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_SCREEN: @@ -567,15 +566,13 @@ static void buttons_header_region_draw(const bContext *C, ARegion *region) ED_region_header(C, region); } -static void buttons_header_region_message_subscribe(const bContext *UNUSED(C), - WorkSpace *UNUSED(workspace), - Scene *UNUSED(scene), - bScreen *UNUSED(screen), - ScrArea *area, - ARegion *region, - struct wmMsgBus *mbus) +static void buttons_header_region_message_subscribe(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + ScrArea *area = params->area; + ARegion *region = params->region; SpaceProperties *sbuts = area->spacedata.first; + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { .owner = region, .user_data = region, @@ -621,14 +618,12 @@ static void buttons_navigation_bar_region_draw(const bContext *C, ARegion *regio ED_region_panels_draw(C, region); } -static void buttons_navigation_bar_region_message_subscribe(const bContext *UNUSED(C), - WorkSpace *UNUSED(workspace), - Scene *UNUSED(scene), - bScreen *UNUSED(screen), - ScrArea *UNUSED(area), - ARegion *region, - struct wmMsgBus *mbus) +static void buttons_navigation_bar_region_message_subscribe( + const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + ARegion *region = params->region; + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { .owner = region, .user_data = region, @@ -657,11 +652,10 @@ static void buttons_area_redraw(ScrArea *area, short buttons) * \{ */ /* reused! */ -static void buttons_area_listener(wmWindow *UNUSED(win), - ScrArea *area, - wmNotifier *wmn, - Scene *UNUSED(scene)) +static void buttons_area_listener(const wmSpaceTypeListenerParams *params) { + ScrArea *area = params->area; + wmNotifier *wmn = params->notifier; SpaceProperties *sbuts = area->spacedata.first; /* context changes */ diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c index bd11a746e11..af1d082d317 100644 --- a/source/blender/editors/space_clip/clip_editor.c +++ b/source/blender/editors/space_clip/clip_editor.c @@ -327,118 +327,18 @@ void ED_clip_update_frame(const Main *mainp, int cfra) } } -static bool selected_tracking_boundbox(SpaceClip *sc, float min[2], float max[2]) +bool ED_clip_view_selection(const bContext *C, ARegion *UNUSED(region), bool fit) { - MovieClip *clip = ED_space_clip_get_clip(sc); - MovieTrackingTrack *track; - int width, height; - bool ok = false; - ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking); - int framenr = ED_space_clip_get_clip_frame_number(sc); - - INIT_MINMAX2(min, max); - - ED_space_clip_get_size(sc, &width, &height); - - track = tracksbase->first; - while (track) { - if (TRACK_VIEW_SELECTED(sc, track)) { - MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); - - if (marker) { - float pos[3]; - - pos[0] = marker->pos[0] + track->offset[0]; - pos[1] = marker->pos[1] + track->offset[1]; - pos[2] = 0.0f; - - /* undistortion happens for normalized coords */ - if (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) { - /* undistortion happens for normalized coords */ - ED_clip_point_undistorted_pos(sc, pos, pos); - } - - pos[0] *= width; - pos[1] *= height; - - mul_v3_m4v3(pos, sc->stabmat, pos); - - minmax_v2v2_v2(min, max, pos); - - ok = true; - } - } - - track = track->next; - } - - return ok; -} - -static bool selected_boundbox(const bContext *C, float min[2], float max[2]) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - if (sc->mode == SC_MODE_TRACKING) { - return selected_tracking_boundbox(sc, min, max); - } - - if (ED_mask_selected_minmax(C, min, max)) { - MovieClip *clip = ED_space_clip_get_clip(sc); - int width, height; - ED_space_clip_get_size(sc, &width, &height); - BKE_mask_coord_to_movieclip(clip, &sc->user, min, min); - BKE_mask_coord_to_movieclip(clip, &sc->user, max, max); - min[0] *= width; - min[1] *= height; - max[0] *= width; - max[1] *= height; - return true; - } - return false; -} - -bool ED_clip_view_selection(const bContext *C, ARegion *region, bool fit) -{ - SpaceClip *sc = CTX_wm_space_clip(C); - int w, h, frame_width, frame_height; - float min[2], max[2]; - - ED_space_clip_get_size(sc, &frame_width, &frame_height); - - if ((frame_width == 0) || (frame_height == 0) || (sc->clip == NULL)) { + float offset_x, offset_y; + float zoom; + if (!clip_view_calculate_view_selection(C, fit, &offset_x, &offset_y, &zoom)) { return false; } - if (!selected_boundbox(C, min, max)) { - return false; - } - - /* center view */ - clip_view_center_to_point( - sc, (max[0] + min[0]) / (2 * frame_width), (max[1] + min[1]) / (2 * frame_height)); - - w = max[0] - min[0]; - h = max[1] - min[1]; - - /* set zoom to see all selection */ - if (w > 0 && h > 0) { - int width, height; - float zoomx, zoomy, newzoom, aspx, aspy; - - ED_space_clip_get_aspect(sc, &aspx, &aspy); - - width = BLI_rcti_size_x(®ion->winrct) + 1; - height = BLI_rcti_size_y(®ion->winrct) + 1; - - zoomx = (float)width / w / aspx; - zoomy = (float)height / h / aspy; - - newzoom = 1.0f / power_of_2(1.0f / min_ff(zoomx, zoomy)); - - if (fit || sc->zoom > newzoom) { - sc->zoom = newzoom; - } - } + SpaceClip *sc = CTX_wm_space_clip(C); + sc->xof = offset_x; + sc->yof = offset_y; + sc->zoom = zoom; return true; } @@ -1177,3 +1077,47 @@ void clip_start_prefetch_job(const bContext *C) /* and finally start the job */ WM_jobs_start(CTX_wm_manager(C), wm_job); } + +void ED_clip_view_lock_state_store(const bContext *C, ClipViewLockState *state) +{ + SpaceClip *space_clip = CTX_wm_space_clip(C); + BLI_assert(space_clip != NULL); + + state->offset_x = space_clip->xof; + state->offset_y = space_clip->yof; + state->zoom = space_clip->zoom; + + state->lock_offset_x = 0.0f; + state->lock_offset_y = 0.0f; + + if ((space_clip->flag & SC_LOCK_SELECTION) == 0) { + return; + } + + if (!clip_view_calculate_view_selection( + C, false, &state->offset_x, &state->offset_y, &state->zoom)) { + return; + } + + state->lock_offset_x = space_clip->xlockof; + state->lock_offset_y = space_clip->ylockof; +} + +void ED_clip_view_lock_state_restore_no_jump(const bContext *C, const ClipViewLockState *state) +{ + SpaceClip *space_clip = CTX_wm_space_clip(C); + BLI_assert(space_clip != NULL); + + if ((space_clip->flag & SC_LOCK_SELECTION) == 0) { + return; + } + + float offset_x, offset_y; + float zoom; + if (!clip_view_calculate_view_selection(C, false, &offset_x, &offset_y, &zoom)) { + return; + } + + space_clip->xlockof = state->offset_x + state->lock_offset_x - offset_x; + space_clip->ylockof = state->offset_y + state->lock_offset_y - offset_y; +} diff --git a/source/blender/editors/space_clip/clip_intern.h b/source/blender/editors/space_clip/clip_intern.h index 4848ec72f79..b9a69204281 100644 --- a/source/blender/editors/space_clip/clip_intern.h +++ b/source/blender/editors/space_clip/clip_intern.h @@ -171,8 +171,13 @@ void clip_delete_plane_track(struct bContext *C, struct MovieClip *clip, struct MovieTrackingPlaneTrack *plane_track); +void clip_view_offset_for_center_to_point( + SpaceClip *sc, const float x, const float y, float *r_offset_x, float *r_offset_y); void clip_view_center_to_point(SpaceClip *sc, float x, float y); +bool clip_view_calculate_view_selection( + const struct bContext *C, bool fit, float *r_offset_x, float *r_offset_y, float *r_zoom); + void clip_draw_sfra_efra(struct View2D *v2d, struct Scene *scene); /* tracking_ops.c */ @@ -191,6 +196,7 @@ void CLIP_OT_clear_solution(struct wmOperatorType *ot); void CLIP_OT_clear_track_path(struct wmOperatorType *ot); void CLIP_OT_join_tracks(struct wmOperatorType *ot); +void CLIP_OT_average_tracks(struct wmOperatorType *ot); void CLIP_OT_disable_markers(struct wmOperatorType *ot); void CLIP_OT_hide_tracks(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index cd4a1ffb526..cb84ea6571c 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -1840,8 +1840,16 @@ void CLIP_OT_cursor_set(wmOperatorType *ot) static int lock_selection_togglee_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceClip *space_clip = CTX_wm_space_clip(C); + + ClipViewLockState lock_state; + ED_clip_view_lock_state_store(C, &lock_state); + space_clip->flag ^= SC_LOCK_SELECTION; + + ED_clip_view_lock_state_restore_no_jump(C, &lock_state); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_CLIP, NULL); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c index c7f2a027ba8..939245288fa 100644 --- a/source/blender/editors/space_clip/clip_utils.c +++ b/source/blender/editors/space_clip/clip_utils.c @@ -27,10 +27,12 @@ #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_rect.h" #include "BLI_utildefines.h" #include "BKE_animsys.h" #include "BKE_context.h" +#include "BKE_mask.h" #include "BKE_movieclip.h" #include "BKE_tracking.h" @@ -44,6 +46,7 @@ #include "WM_types.h" #include "ED_clip.h" +#include "ED_mask.h" #include "ED_screen.h" #include "UI_interface.h" @@ -395,16 +398,152 @@ void clip_delete_plane_track(bContext *C, MovieClip *clip, MovieTrackingPlaneTra DEG_id_tag_update(&clip->id, 0); } -void clip_view_center_to_point(SpaceClip *sc, float x, float y) +/* Calculate space clip offset to be centered at the given point. */ +void clip_view_offset_for_center_to_point( + SpaceClip *sc, const float x, const float y, float *r_offset_x, float *r_offset_y) { int width, height; + ED_space_clip_get_size(sc, &width, &height); + float aspx, aspy; + ED_space_clip_get_aspect(sc, &aspx, &aspy); + + *r_offset_x = (x - 0.5f) * width * aspx; + *r_offset_y = (y - 0.5f) * height * aspy; +} + +void clip_view_center_to_point(SpaceClip *sc, float x, float y) +{ + clip_view_offset_for_center_to_point(sc, x, y, &sc->xof, &sc->yof); +} + +static bool selected_tracking_boundbox(SpaceClip *sc, float min[2], float max[2]) +{ + MovieClip *clip = ED_space_clip_get_clip(sc); + MovieTrackingTrack *track; + int width, height; + bool ok = false; + ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking); + int framenr = ED_space_clip_get_clip_frame_number(sc); + + INIT_MINMAX2(min, max); ED_space_clip_get_size(sc, &width, &height); - ED_space_clip_get_aspect(sc, &aspx, &aspy); - sc->xof = (x - 0.5f) * width * aspx; - sc->yof = (y - 0.5f) * height * aspy; + track = tracksbase->first; + while (track) { + if (TRACK_VIEW_SELECTED(sc, track)) { + MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); + + if (marker) { + float pos[3]; + + pos[0] = marker->pos[0] + track->offset[0]; + pos[1] = marker->pos[1] + track->offset[1]; + pos[2] = 0.0f; + + /* undistortion happens for normalized coords */ + if (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) { + /* undistortion happens for normalized coords */ + ED_clip_point_undistorted_pos(sc, pos, pos); + } + + pos[0] *= width; + pos[1] *= height; + + mul_v3_m4v3(pos, sc->stabmat, pos); + + minmax_v2v2_v2(min, max, pos); + + ok = true; + } + } + + track = track->next; + } + + return ok; +} + +static bool selected_boundbox(const bContext *C, float min[2], float max[2], bool include_handles) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + if (sc->mode == SC_MODE_TRACKING) { + return selected_tracking_boundbox(sc, min, max); + } + + if (ED_mask_selected_minmax(C, min, max, include_handles)) { + MovieClip *clip = ED_space_clip_get_clip(sc); + int width, height; + ED_space_clip_get_size(sc, &width, &height); + BKE_mask_coord_to_movieclip(clip, &sc->user, min, min); + BKE_mask_coord_to_movieclip(clip, &sc->user, max, max); + min[0] *= width; + min[1] *= height; + max[0] *= width; + max[1] *= height; + return true; + } + return false; +} + +bool clip_view_calculate_view_selection( + const bContext *C, bool fit, float *r_offset_x, float *r_offset_y, float *r_zoom) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + + int frame_width, frame_height; + ED_space_clip_get_size(sc, &frame_width, &frame_height); + + if ((frame_width == 0) || (frame_height == 0) || (sc->clip == NULL)) { + return false; + } + + /* NOTE: The `fit` argument is set to truth when doing "View to Selected" operator, and it set to + * false when this function is used for Lock-to-Selection functionality. When locking to + * selection the handles are to be ignored. So we can derive the `include_handles` from `fit`. + * + * TODO(sergey): Make such decision more explicit. Maybe pass use-case for the calculation to + * tell operator from lock-to-selection apart. */ + float min[2], max[2]; + if (!selected_boundbox(C, min, max, fit)) { + return false; + } + + /* center view */ + clip_view_offset_for_center_to_point(sc, + (max[0] + min[0]) / (2 * frame_width), + (max[1] + min[1]) / (2 * frame_height), + r_offset_x, + r_offset_y); + + const int w = max[0] - min[0]; + const int h = max[1] - min[1]; + + /* set zoom to see all selection */ + *r_zoom = sc->zoom; + if (w > 0 && h > 0) { + ARegion *region = CTX_wm_region(C); + + int width, height; + float zoomx, zoomy, newzoom, aspx, aspy; + + ED_space_clip_get_aspect(sc, &aspx, &aspy); + + width = BLI_rcti_size_x(®ion->winrct) + 1; + height = BLI_rcti_size_y(®ion->winrct) + 1; + + zoomx = (float)width / w / aspx; + zoomy = (float)height / h / aspy; + + newzoom = 1.0f / power_of_2(1.0f / min_ff(zoomx, zoomy)); + + if (fit) { + *r_zoom = newzoom; + } + } + + return true; } void clip_draw_sfra_efra(View2D *v2d, Scene *scene) diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index 3bdf016b64c..33783f19a79 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -229,7 +229,7 @@ static void clip_scopes_check_gpencil_change(ScrArea *area) } } -static void clip_area_sync_frame_from_scene(ScrArea *area, Scene *scene) +static void clip_area_sync_frame_from_scene(ScrArea *area, const Scene *scene) { SpaceClip *space_clip = (SpaceClip *)area->spacedata.first; BKE_movieclip_user_set_frame(&space_clip->user, scene->r.cfra); @@ -334,8 +334,12 @@ static SpaceLink *clip_duplicate(SpaceLink *sl) return (SpaceLink *)scn; } -static void clip_listener(wmWindow *UNUSED(win), ScrArea *area, wmNotifier *wmn, Scene *scene) +static void clip_listener(const wmSpaceTypeListenerParams *params) { + ScrArea *area = params->area; + wmNotifier *wmn = params->notifier; + const Scene *scene = params->scene; + /* context changes */ switch (wmn->category) { case NC_SCENE: @@ -514,6 +518,7 @@ static void clip_operatortypes(void) /* clean-up */ WM_operatortype_append(CLIP_OT_clear_track_path); WM_operatortype_append(CLIP_OT_join_tracks); + WM_operatortype_append(CLIP_OT_average_tracks); WM_operatortype_append(CLIP_OT_track_copy_color); WM_operatortype_append(CLIP_OT_clean_tracks); @@ -988,7 +993,14 @@ static void clip_main_region_draw(const bContext *C, ARegion *region) } /* callback */ + /* TODO(sergey): For being consistent with space image the projection needs to be configured + * the way how the commented out code does it. This works correct for tracking data, but it + * causes wrong aspect correction for mask editor (see T84990). */ + // GPU_matrix_push_projection(); + // wmOrtho2(region->v2d.cur.xmin, region->v2d.cur.xmax, region->v2d.cur.ymin, + // region->v2d.cur.ymax); ED_region_draw_cb_draw(C, region, REGION_DRAW_POST_VIEW); + // GPU_matrix_pop_projection(); /* reset view matrix */ UI_view2d_view_restore(C); @@ -1001,12 +1013,11 @@ static void clip_main_region_draw(const bContext *C, ARegion *region) WM_gizmomap_draw(region->gizmo_map, C, WM_GIZMOMAP_DRAWSTEP_2D); } -static void clip_main_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void clip_main_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_GPENCIL: @@ -1137,11 +1148,7 @@ static void clip_preview_region_draw(const bContext *C, ARegion *region) } } -static void clip_preview_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *UNUSED(region), - wmNotifier *UNUSED(wmn), - const Scene *UNUSED(scene)) +static void clip_preview_region_listener(const wmRegionListenerParams *UNUSED(params)) { } @@ -1182,11 +1189,7 @@ static void clip_channels_region_draw(const bContext *C, ARegion *region) UI_view2d_view_restore(C); } -static void clip_channels_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *UNUSED(region), - wmNotifier *UNUSED(wmn), - const Scene *UNUSED(scene)) +static void clip_channels_region_listener(const wmRegionListenerParams *UNUSED(params)) { } @@ -1203,12 +1206,11 @@ static void clip_header_region_draw(const bContext *C, ARegion *region) ED_region_header(C, region); } -static void clip_header_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void clip_header_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_SCENE: @@ -1246,12 +1248,11 @@ static void clip_tools_region_draw(const bContext *C, ARegion *region) /****************** tool properties region ******************/ -static void clip_props_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void clip_props_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_WM: @@ -1299,12 +1300,11 @@ static void clip_properties_region_draw(const bContext *C, ARegion *region) ED_region_panels(C, region); } -static void clip_properties_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void clip_properties_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_GPENCIL: diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index 49313005c43..0f4fc2a2160 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -88,17 +88,16 @@ static int add_marker_exec(bContext *C, wmOperator *op) MovieClip *clip = ED_space_clip_get_clip(sc); float pos[2]; + ClipViewLockState lock_state; + ED_clip_view_lock_state_store(C, &lock_state); + RNA_float_get_array(op->ptr, "location", pos); if (!add_marker(C, pos[0], pos[1])) { return OPERATOR_CANCELLED; } - /* Reset offset from locked position, so frame jumping wouldn't be so - * confusing. - */ - sc->xlockof = 0; - sc->ylockof = 0; + ED_clip_view_lock_state_restore_no_jump(C, &lock_state); WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip); @@ -1490,6 +1489,97 @@ void CLIP_OT_join_tracks(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/********************** Average tracks operator *********************/ + +static int average_tracks_exec(bContext *C, wmOperator *op) +{ + SpaceClip *space_clip = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(space_clip); + MovieTracking *tracking = &clip->tracking; + + /* Collect source tracks. */ + int num_source_tracks; + MovieTrackingTrack **source_tracks = BKE_tracking_selected_tracks_in_active_object( + tracking, &num_source_tracks); + if (num_source_tracks == 0) { + return OPERATOR_CANCELLED; + } + + /* Create new empty track, which will be the averaged result. + * Makes it simple to average all selection to it. */ + ListBase *tracks_list = BKE_tracking_get_active_tracks(tracking); + MovieTrackingTrack *result_track = BKE_tracking_track_add_empty(tracking, tracks_list); + + /* Perform averaging. */ + BKE_tracking_tracks_average(result_track, source_tracks, num_source_tracks); + + const bool keep_original = RNA_boolean_get(op->ptr, "keep_original"); + if (!keep_original) { + for (int i = 0; i < num_source_tracks; i++) { + clip_delete_track(C, clip, source_tracks[i]); + } + } + + /* Update selection, making the result track active and selected. */ + /* TODO(sergey): Should become some sort of utility function available for all operators. */ + + BKE_tracking_track_select(tracks_list, result_track, TRACK_AREA_ALL, 0); + ListBase *plane_tracks_list = BKE_tracking_get_active_plane_tracks(tracking); + BKE_tracking_plane_tracks_deselect_all(plane_tracks_list); + + clip->tracking.act_track = result_track; + clip->tracking.act_plane_track = NULL; + + /* Inform the dependency graph and interface about changes. */ + DEG_id_tag_update(&clip->id, 0); + WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip); + + /* Free memory. */ + MEM_freeN(source_tracks); + + return OPERATOR_FINISHED; +} + +static int average_tracks_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + PropertyRNA *prop_keep_original = RNA_struct_find_property(op->ptr, "keep_original"); + if (!RNA_property_is_set(op->ptr, prop_keep_original)) { + SpaceClip *space_clip = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(space_clip); + MovieTracking *tracking = &clip->tracking; + + const int num_selected_tracks = BKE_tracking_count_selected_tracks_in_active_object(tracking); + + if (num_selected_tracks == 1) { + RNA_property_boolean_set(op->ptr, prop_keep_original, false); + } + } + + return average_tracks_exec(C, op); +} + +void CLIP_OT_average_tracks(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Average Tracks"; + ot->description = "Average selected tracks into active"; + ot->idname = "CLIP_OT_average_tracks"; + + /* API callbacks. */ + ot->exec = average_tracks_exec; + ot->invoke = average_tracks_invoke; + ot->poll = ED_space_clip_tracking_poll; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties. */ + PropertyRNA *prop; + + prop = RNA_def_boolean(ot->srna, "keep_original", 1, "Keep Original", "Keep original tracks"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + /********************** lock tracks operator *********************/ enum { diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c index 063ea9592aa..ecd73f82e22 100644 --- a/source/blender/editors/space_clip/tracking_select.c +++ b/source/blender/editors/space_clip/tracking_select.c @@ -304,6 +304,9 @@ static int mouse_select(bContext *C, const float co[2], const bool extend, const track = find_nearest_track(sc, tracksbase, co, &distance_to_track); plane_track = find_nearest_plane_track(sc, plane_tracks_base, co, &distance_to_plane_track); + ClipViewLockState lock_state; + ED_clip_view_lock_state_store(C, &lock_state); + /* Do not select beyond some reasonable distance, that is useless and * prevents the 'deselect on nothing' behavior. */ if (distance_to_track > 0.05f) { @@ -377,10 +380,7 @@ static int mouse_select(bContext *C, const float co[2], const bool extend, const ED_mask_deselect_all(C); } - if (!extend) { - sc->xlockof = 0.0f; - sc->ylockof = 0.0f; - } + ED_clip_view_lock_state_restore_no_jump(C, &lock_state); BKE_tracking_dopesheet_tag_update(tracking); @@ -867,15 +867,20 @@ static int select_all_exec(bContext *C, wmOperator *op) MovieClip *clip = ED_space_clip_get_clip(sc); MovieTracking *tracking = &clip->tracking; - int action = RNA_enum_get(op->ptr, "action"); + const int action = RNA_enum_get(op->ptr, "action"); - bool has_selection = false; + ClipViewLockState lock_state; + ED_clip_view_lock_state_store(C, &lock_state); + bool has_selection = false; ED_clip_select_all(sc, action, &has_selection); if (!has_selection) { sc->flag &= ~SC_LOCK_SELECTION; } + else { + ED_clip_view_lock_state_restore_no_jump(C, &lock_state); + } BKE_tracking_dopesheet_tag_update(tracking); diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index 9b8e9e0e871..6265cbdd920 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -273,13 +273,11 @@ static void console_header_region_draw(const bContext *C, ARegion *region) ED_region_header(C, region); } -static void console_main_region_listener(wmWindow *UNUSED(win), - ScrArea *area, - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void console_main_region_listener(const wmRegionListenerParams *params) { - // SpaceInfo *sinfo = area->spacedata.first; + ScrArea *area = params->area; + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; /* context changes */ switch (wmn->category) { diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 8e9093151ba..a12c9633798 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -131,7 +131,15 @@ static void draw_tile(int sx, int sy, int width, int height, int colorid, int sh UI_GetThemeColorShade4fv(colorid, shade, color); UI_draw_roundbox_corner_set(UI_CNR_ALL); UI_draw_roundbox_aa( - true, (float)sx, (float)(sy - height), (float)(sx + width), (float)sy, 5.0f, color); + &(const rctf){ + .xmin = (float)sx, + .xmax = (float)(sx + width), + .ymin = (float)(sy - height), + .ymax = (float)sy, + }, + true, + 5.0f, + color); } static void file_draw_icon(uiBlock *block, diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index d66219c7549..33c37875372 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -276,6 +276,7 @@ typedef struct FileListInternEntry { char *redirection_path; /** not strictly needed, but used during sorting, avoids to have to recompute it there... */ char *name; + bool free_name; /** * This is data from the current main, represented by this file. It's crucial that this is @@ -1366,7 +1367,7 @@ static bool filelist_checkdir_main_assets(struct FileList *UNUSED(filelist), static void filelist_entry_clear(FileDirEntry *entry) { - if (entry->name) { + if (entry->name && ((entry->flags & FILE_ENTRY_NAME_FREE) != 0)) { MEM_freeN(entry->name); } if (entry->description) { @@ -1451,7 +1452,7 @@ static void filelist_intern_entry_free(FileListInternEntry *entry) if (entry->redirection_path) { MEM_freeN(entry->redirection_path); } - if (entry->name) { + if (entry->name && entry->free_name) { MEM_freeN(entry->name); } /* If we own the asset-data (it was generated from external file data), free it. */ @@ -1953,7 +1954,13 @@ static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int in ret->entry = rev; ret->relpath = BLI_strdup(entry->relpath); - ret->name = BLI_strdup(entry->name); + if (entry->free_name) { + ret->name = BLI_strdup(entry->name); + ret->flags |= FILE_ENTRY_NAME_FREE; + } + else { + ret->name = entry->name; + } ret->description = BLI_strdupcat(filelist->filelist.root, entry->relpath); memcpy(ret->uuid, entry->uuid, sizeof(ret->uuid)); ret->blentype = entry->blentype; @@ -3175,6 +3182,7 @@ static void filelist_readjob_do(const bool do_lib, entry->relpath = BLI_strdup(dir + 2); /* + 2 to remove '//' * added by BLI_path_rel to rel_subdir. */ entry->name = BLI_strdup(fileentry_uiname(root, entry->relpath, entry->typeflag, dir)); + entry->free_name = true; /* Here we decide whether current filedirentry is to be listed too, or not. */ if (max_recursion && (is_lib || (recursion_level <= max_recursion))) { @@ -3288,7 +3296,8 @@ static void filelist_readjob_main_assets(Main *current_main, entry = MEM_callocN(sizeof(*entry), __func__); entry->relpath = BLI_strdup(id_code_name); - entry->name = BLI_strdup(id_iter->name + 2); + entry->name = id_iter->name + 2; + entry->free_name = false; entry->typeflag |= FILE_TYPE_BLENDERLIB | FILE_TYPE_ASSET; entry->blentype = GS(id_iter->name); *((uint32_t *)entry->uuid) = atomic_add_and_fetch_uint32( diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 7f33b0212d6..d4cd888c662 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -104,6 +104,7 @@ static ARegion *file_tool_props_region_ensure(ScrArea *area, ARegion *region_pre BLI_insertlinkafter(&area->regionbase, region_prev, region); region->regiontype = RGN_TYPE_TOOL_PROPS; region->alignment = RGN_ALIGN_RIGHT; + region->flag = RGN_FLAG_HIDDEN; return region; } @@ -246,13 +247,13 @@ static void file_ensure_valid_region_state(bContext *C, BLI_assert(region_tools); if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS) { - ARegion *region_execute = file_execute_region_ensure(area, region_tools); - ARegion *region_props = file_tool_props_region_ensure(area, region_execute); - - /* Hide specific regions by default. */ - region_props->flag |= RGN_FLAG_HIDDEN; - region_execute->flag |= RGN_FLAG_HIDDEN; + file_tool_props_region_ensure(area, region_tools); + ARegion *region_execute = BKE_area_find_region_type(area, RGN_TYPE_EXECUTE); + if (region_execute) { + ED_region_remove(C, area, region_execute); + needs_init = true; + } ARegion *region_ui = BKE_area_find_region_type(area, RGN_TYPE_UI); if (region_ui) { ED_region_remove(C, area, region_ui); @@ -281,11 +282,12 @@ static void file_ensure_valid_region_state(bContext *C, ARegion *region_ui = file_ui_region_ensure(area, region_tools); UNUSED_VARS(region_ui); + if (region_execute) { + ED_region_remove(C, area, region_execute); + needs_init = true; + } if (region_props) { - BLI_assert(region_execute); - ED_region_remove(C, area, region_props); - ED_region_remove(C, area, region_execute); needs_init = true; } } @@ -390,11 +392,10 @@ static void file_refresh(const bContext *C, ScrArea *area) ED_area_tag_redraw(area); } -static void file_listener(wmWindow *UNUSED(win), - ScrArea *area, - wmNotifier *wmn, - Scene *UNUSED(scene)) +static void file_listener(const wmSpaceTypeListenerParams *params) { + ScrArea *area = params->area; + wmNotifier *wmn = params->notifier; SpaceFile *sfile = (SpaceFile *)area->spacedata.first; /* context changes */ @@ -445,12 +446,11 @@ static void file_main_region_init(wmWindowManager *wm, ARegion *region) WM_event_add_keymap_handler_v2d_mask(®ion->handlers, keymap); } -static void file_main_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void file_main_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_SPACE: @@ -463,19 +463,24 @@ static void file_main_region_listener(wmWindow *UNUSED(win), break; } break; + case NC_ID: + if (ELEM(wmn->action, NA_RENAME)) { + /* In case the filelist shows ID names. */ + ED_region_tag_redraw(region); + } + break; } } -static void file_main_region_message_subscribe(const struct bContext *UNUSED(C), - struct WorkSpace *UNUSED(workspace), - struct Scene *UNUSED(scene), - struct bScreen *screen, - struct ScrArea *area, - struct ARegion *region, - struct wmMsgBus *mbus) +static void file_main_region_message_subscribe(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + bScreen *screen = params->screen; + ScrArea *area = params->area; + ARegion *region = params->region; SpaceFile *sfile = area->spacedata.first; - FileSelectParams *params = ED_fileselect_ensure_active_params(sfile); + + FileSelectParams *file_params = ED_fileselect_ensure_active_params(sfile); /* This is a bit odd that a region owns the subscriber for an area, * keep for now since all subscribers for WM are regions. * May be worth re-visiting later. */ @@ -497,7 +502,7 @@ static void file_main_region_message_subscribe(const struct bContext *UNUSED(C), /* FileSelectParams */ { PointerRNA ptr; - RNA_pointer_create(&screen->id, &RNA_FileSelectParams, params, &ptr); + RNA_pointer_create(&screen->id, &RNA_FileSelectParams, file_params, &ptr); /* All properties for this space type. */ WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_area_tag_refresh, __func__); @@ -647,18 +652,23 @@ static void file_tools_region_draw(const bContext *C, ARegion *region) ED_region_panels(C, region); } -static void file_tools_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *UNUSED(region), - wmNotifier *UNUSED(wmn), - const Scene *UNUSED(scene)) +static void file_tools_region_listener(const wmRegionListenerParams *UNUSED(params)) { -#if 0 - /* context changes */ +} + +static void file_tool_props_region_listener(const wmRegionListenerParams *params) +{ + const wmNotifier *wmn = params->notifier; + ARegion *region = params->region; + switch (wmn->category) { - /* pass */ + case NC_ID: + if (ELEM(wmn->action, NA_RENAME)) { + /* In case the filelist shows ID names. */ + ED_region_tag_redraw(region); + } + break; } -#endif } /* add handlers, stuff you only do once or on area/region changes */ @@ -715,12 +725,11 @@ static void file_execution_region_draw(const bContext *C, ARegion *region) ED_region_panels(C, region); } -static void file_ui_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void file_ui_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_SPACE: @@ -928,7 +937,7 @@ void ED_spacetype_file(void) art->prefsizex = 240; art->prefsizey = 60; art->keymapflag = ED_KEYMAP_UI; - art->listener = file_tools_region_listener; + art->listener = file_tool_props_region_listener; art->init = file_tools_region_init; art->draw = file_tools_region_draw; BLI_addhead(&st->regiontypes, art); diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index 4cda6b34a01..0818ebe9c0a 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -505,15 +505,15 @@ void GRAPH_OT_decimate(wmOperatorType *ot) "Mode", "Which mode to use for decimation"); - RNA_def_float_percentage(ot->srna, - "remove_ratio", - 1.0f / 3.0f, - 0.0f, - 1.0f, - "Remove", - "The percentage of keyframes to remove", - 0.0f, - 1.0f); + RNA_def_float_factor(ot->srna, + "remove_ratio", + 1.0f / 3.0f, + 0.0f, + 1.0f, + "Remove", + "The ratio of remaining keyframes after the operation", + 0.0f, + 1.0f); RNA_def_float(ot->srna, "remove_error_margin", 0.0f, diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 791039498e8..9f01773eadf 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -403,12 +403,11 @@ static void graph_buttons_region_draw(const bContext *C, ARegion *region) ED_region_panels(C, region); } -static void graph_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void graph_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_ANIMATION: @@ -470,14 +469,14 @@ static void graph_region_listener(wmWindow *UNUSED(win), } } -static void graph_region_message_subscribe(const struct bContext *UNUSED(C), - struct WorkSpace *UNUSED(workspace), - struct Scene *scene, - struct bScreen *screen, - struct ScrArea *area, - struct ARegion *region, - struct wmMsgBus *mbus) +static void graph_region_message_subscribe(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + Scene *scene = params->scene; + bScreen *screen = params->screen; + ScrArea *area = params->area; + ARegion *region = params->region; + PointerRNA ptr; RNA_pointer_create(&screen->id, &RNA_SpaceGraphEditor, area->spacedata.first, &ptr); @@ -546,11 +545,10 @@ static void graph_region_message_subscribe(const struct bContext *UNUSED(C), } /* editor level listener */ -static void graph_listener(wmWindow *UNUSED(win), - ScrArea *area, - wmNotifier *wmn, - Scene *UNUSED(scene)) +static void graph_listener(const wmSpaceTypeListenerParams *params) { + ScrArea *area = params->area; + wmNotifier *wmn = params->notifier; SpaceGraph *sipo = (SpaceGraph *)area->spacedata.first; /* context changes */ diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 86e52814d6f..097e304c893 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -900,7 +900,7 @@ static int image_view_selected_exec(bContext *C, wmOperator *UNUSED(op)) } } else if (ED_space_image_check_show_maskedit(sima, obedit)) { - if (!ED_mask_selected_minmax(C, min, max)) { + if (!ED_mask_selected_minmax(C, min, max, true)) { return OPERATOR_CANCELLED; } } diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 776fa34d1c4..b0571d47736 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -320,8 +320,11 @@ static void image_refresh(const bContext *C, ScrArea *area) } } -static void image_listener(wmWindow *win, ScrArea *area, wmNotifier *wmn, Scene *UNUSED(scene)) +static void image_listener(const wmSpaceTypeListenerParams *params) { + wmWindow *win = params->window; + ScrArea *area = params->area; + wmNotifier *wmn = params->notifier; SpaceImage *sima = (SpaceImage *)area->spacedata.first; /* context changes */ @@ -728,12 +731,12 @@ static void image_main_region_draw(const bContext *C, ARegion *region) draw_image_cache(C, region); } -static void image_main_region_listener(wmWindow *UNUSED(win), - ScrArea *area, - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void image_main_region_listener(const wmRegionListenerParams *params) { + ScrArea *area = params->area; + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_GEOM: @@ -843,12 +846,11 @@ static void image_buttons_region_draw(const bContext *C, ARegion *region) ED_region_panels_draw(C, region); } -static void image_buttons_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void image_buttons_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_TEXTURE: @@ -906,12 +908,11 @@ static void image_tools_region_draw(const bContext *C, ARegion *region) ED_region_panels(C, region); } -static void image_tools_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void image_tools_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_GPENCIL: @@ -963,12 +964,11 @@ static void image_header_region_draw(const bContext *C, ARegion *region) ED_region_header(C, region); } -static void image_header_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void image_header_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_SCENE: diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c index 8d3f21aefeb..5e44db4c791 100644 --- a/source/blender/editors/space_info/space_info.c +++ b/source/blender/editors/space_info/space_info.c @@ -204,13 +204,10 @@ static void info_header_region_draw(const bContext *C, ARegion *region) ED_region_header(C, region); } -static void info_main_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void info_main_region_listener(const wmRegionListenerParams *params) { - // SpaceInfo *sinfo = area->spacedata.first; + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; /* context changes */ switch (wmn->category) { @@ -223,12 +220,11 @@ static void info_main_region_listener(wmWindow *UNUSED(win), } } -static void info_header_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void info_header_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_SCREEN: @@ -259,14 +255,11 @@ static void info_header_listener(wmWindow *UNUSED(win), } } -static void info_header_region_message_subscribe(const bContext *UNUSED(C), - WorkSpace *UNUSED(workspace), - Scene *UNUSED(scene), - bScreen *UNUSED(screen), - ScrArea *UNUSED(area), - ARegion *region, - struct wmMsgBus *mbus) +static void info_header_region_message_subscribe(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + ARegion *region = params->region; + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { .owner = region, .user_data = region, diff --git a/source/blender/editors/space_info/textview.c b/source/blender/editors/space_info/textview.c index a489216afd1..ef5d9c0aab0 100644 --- a/source/blender/editors/space_info/textview.c +++ b/source/blender/editors/space_info/textview.c @@ -225,13 +225,16 @@ static bool textview_draw_string(TextViewDrawState *tds, rgba_uchar_to_float(col, icon_bg); UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(true, - hpadding, - line_top - bg_size - vpadding, - bg_size + hpadding, - line_top - vpadding, - 4 * UI_DPI_FAC, - col); + UI_draw_roundbox_aa( + &(const rctf){ + .xmin = hpadding, + .xmax = bg_size + hpadding, + .ymin = line_top - bg_size - vpadding, + .ymax = line_top - vpadding, + }, + true, + 4 * UI_DPI_FAC, + col); } if (icon) { diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index 6fe980cf657..eea81e425c2 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -493,7 +493,18 @@ static void nla_draw_strip(SpaceNla *snla, /* strip is in normal track */ UI_draw_roundbox_corner_set(UI_CNR_ALL); /* all corners rounded */ - UI_draw_roundbox_shade_x(true, strip->start, yminc, strip->end, ymaxc, 0.0, 0.5, 0.1, color); + UI_draw_roundbox_shade_x( + &(const rctf){ + .xmin = strip->start, + .xmax = strip->end, + .ymin = yminc, + .ymax = ymaxc, + }, + true, + 0.0, + 0.5, + 0.1, + color); /* restore current vertex format & program (roundbox trashes it) */ shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); @@ -547,7 +558,18 @@ static void nla_draw_strip(SpaceNla *snla, } else { /* non-muted - draw solid, rounded outline */ - UI_draw_roundbox_shade_x(false, strip->start, yminc, strip->end, ymaxc, 0.0, 0.0, 0.1, color); + UI_draw_roundbox_shade_x( + &(const rctf){ + .xmin = strip->start, + .xmax = strip->end, + .ymin = yminc, + .ymax = ymaxc, + }, + false, + 0.0, + 0.0, + 0.1, + color); /* restore current vertex format & program (roundbox trashes it) */ shdr_pos = nla_draw_use_dashed_outlines(color, muted); diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index c6fe1b8539e..efe851c64ed 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -324,12 +324,11 @@ static void nla_buttons_region_draw(const bContext *C, ARegion *region) ED_region_panels(C, region); } -static void nla_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void nla_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_ANIMATION: @@ -364,12 +363,11 @@ static void nla_region_listener(wmWindow *UNUSED(win), } } -static void nla_main_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void nla_main_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_ANIMATION: @@ -423,14 +421,14 @@ static void nla_main_region_listener(wmWindow *UNUSED(win), } } -static void nla_main_region_message_subscribe(const struct bContext *UNUSED(C), - struct WorkSpace *UNUSED(workspace), - struct Scene *scene, - struct bScreen *screen, - struct ScrArea *area, - struct ARegion *region, - struct wmMsgBus *mbus) +static void nla_main_region_message_subscribe(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + Scene *scene = params->scene; + bScreen *screen = params->screen; + ScrArea *area = params->area; + ARegion *region = params->region; + PointerRNA ptr; RNA_pointer_create(&screen->id, &RNA_SpaceNLA, area->spacedata.first, &ptr); @@ -465,12 +463,11 @@ static void nla_main_region_message_subscribe(const struct bContext *UNUSED(C), } } -static void nla_channel_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void nla_channel_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_ANIMATION: @@ -508,14 +505,13 @@ static void nla_channel_region_listener(wmWindow *UNUSED(win), } } -static void nla_channel_region_message_subscribe(const struct bContext *UNUSED(C), - struct WorkSpace *UNUSED(workspace), - struct Scene *UNUSED(scene), - struct bScreen *screen, - struct ScrArea *area, - struct ARegion *region, - struct wmMsgBus *mbus) +static void nla_channel_region_message_subscribe(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + bScreen *screen = params->screen; + ScrArea *area = params->area; + ARegion *region = params->region; + PointerRNA ptr; RNA_pointer_create(&screen->id, &RNA_SpaceNLA, area->spacedata.first, &ptr); @@ -543,11 +539,11 @@ static void nla_channel_region_message_subscribe(const struct bContext *UNUSED(C } /* editor level listener */ -static void nla_listener(wmWindow *UNUSED(win), - ScrArea *area, - wmNotifier *wmn, - Scene *UNUSED(scene)) +static void nla_listener(const wmSpaceTypeListenerParams *params) { + ScrArea *area = params->area; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_ANIMATION: diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index f2ee94af9b3..7905483fac9 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -441,7 +441,7 @@ static void node_draw_frame(const bContext *C, const rctf *rct = &node->totr; UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(true, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD, color); + UI_draw_roundbox_aa(rct, true, BASIS_RAD, color); /* outline active and selected emphasis */ if (node->flag & SELECT) { @@ -452,11 +452,11 @@ static void node_draw_frame(const bContext *C, UI_GetThemeColorShadeAlpha4fv(TH_SELECT, 0, -40, color); } - UI_draw_roundbox_aa(false, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD, color); + UI_draw_roundbox_aa(rct, false, BASIS_RAD, color); } /* label */ - node_draw_frame_label(ntree, node, snode->aspect); + node_draw_frame_label(ntree, node, snode->runtime->aspect); UI_block_end(C, node->block); UI_block_draw(C, node->block); @@ -3166,13 +3166,80 @@ static void node_geometry_buts_random_attribute(uiLayout *layout, uiItemR(layout, ptr, "data_type", DEFAULT_FLAGS, "", ICON_NONE); } +static bool node_attribute_math_operation_use_input_b(const NodeMathOperation operation) +{ + switch (operation) { + case NODE_MATH_ADD: + case NODE_MATH_SUBTRACT: + case NODE_MATH_MULTIPLY: + case NODE_MATH_DIVIDE: + case NODE_MATH_POWER: + case NODE_MATH_LOGARITHM: + case NODE_MATH_MINIMUM: + case NODE_MATH_MAXIMUM: + case NODE_MATH_LESS_THAN: + case NODE_MATH_GREATER_THAN: + case NODE_MATH_MODULO: + case NODE_MATH_ARCTAN2: + case NODE_MATH_SNAP: + case NODE_MATH_WRAP: + case NODE_MATH_COMPARE: + case NODE_MATH_MULTIPLY_ADD: + case NODE_MATH_PINGPONG: + case NODE_MATH_SMOOTH_MIN: + case NODE_MATH_SMOOTH_MAX: + return true; + case NODE_MATH_SINE: + case NODE_MATH_COSINE: + case NODE_MATH_TANGENT: + case NODE_MATH_ARCSINE: + case NODE_MATH_ARCCOSINE: + case NODE_MATH_ARCTANGENT: + case NODE_MATH_ROUND: + case NODE_MATH_ABSOLUTE: + case NODE_MATH_FLOOR: + case NODE_MATH_CEIL: + case NODE_MATH_FRACTION: + case NODE_MATH_SQRT: + case NODE_MATH_INV_SQRT: + case NODE_MATH_SIGN: + case NODE_MATH_EXPONENT: + case NODE_MATH_RADIANS: + case NODE_MATH_DEGREES: + case NODE_MATH_SINH: + case NODE_MATH_COSH: + case NODE_MATH_TANH: + case NODE_MATH_TRUNC: + return false; + } + BLI_assert(false); + return false; +} + static void node_geometry_buts_attribute_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + bNode *node = (bNode *)ptr->data; + NodeAttributeMath *node_storage = (NodeAttributeMath *)node->storage; + NodeMathOperation operation = (NodeMathOperation)node_storage->operation; + uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE); uiItemR(layout, ptr, "input_type_a", DEFAULT_FLAGS, IFACE_("Type A"), ICON_NONE); - uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE); + + /* These "use input b / c" checks are copied from the node's code. + * They could be de-duplicated if the drawing code was moved to the node's file. */ + if (node_attribute_math_operation_use_input_b(operation)) { + uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE); + } + if (ELEM(operation, + NODE_MATH_MULTIPLY_ADD, + NODE_MATH_SMOOTH_MIN, + NODE_MATH_SMOOTH_MAX, + NODE_MATH_WRAP, + NODE_MATH_COMPARE)) { + uiItemR(layout, ptr, "input_type_c", DEFAULT_FLAGS, IFACE_("Type C"), ICON_NONE); + } } static void node_geometry_buts_attribute_vector_math(uiLayout *layout, @@ -3290,6 +3357,23 @@ static void node_geometry_buts_object_info(uiLayout *layout, bContext *UNUSED(C) uiItemR(layout, ptr, "transform_space", UI_ITEM_R_EXPAND, NULL, ICON_NONE); } +static void node_geometry_buts_attribute_sample_texture(uiLayout *layout, + bContext *C, + PointerRNA *ptr) +{ + uiTemplateID(layout, C, ptr, "texture", "texture.new", NULL, NULL, 0, ICON_NONE, NULL); +} + +static void node_geometry_buts_points_to_volume(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "resolution_mode", DEFAULT_FLAGS, IFACE_("Resolution"), ICON_NONE); + uiItemR(layout, ptr, "input_type_radius", DEFAULT_FLAGS, IFACE_("Radius"), ICON_NONE); +} + static void node_geometry_set_butfunc(bNodeType *ntype) { switch (ntype->type) { @@ -3344,6 +3428,12 @@ static void node_geometry_set_butfunc(bNodeType *ntype) case GEO_NODE_OBJECT_INFO: ntype->draw_buttons = node_geometry_buts_object_info; break; + case GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE: + ntype->draw_buttons = node_geometry_buts_attribute_sample_texture; + break; + case GEO_NODE_POINTS_TO_VOLUME: + ntype->draw_buttons = node_geometry_buts_points_to_volume; + break; } } @@ -3828,18 +3918,18 @@ void draw_nodespace_back_pix(const bContext *C, } /* return quadratic beziers points for a given nodelink and clip if v2d is not NULL. */ -static bool node_link_bezier_handles(View2D *v2d, - SpaceNode *snode, - bNodeLink *link, +static bool node_link_bezier_handles(const View2D *v2d, + const SpaceNode *snode, + const bNodeLink *link, float vec[4][2]) { float cursor[2] = {0.0f, 0.0f}; /* this function can be called with snode null (via cut_links_intersect) */ - /* XXX map snode->cursor back to view space */ + /* XXX map snode->runtime->cursor back to view space */ if (snode) { - cursor[0] = snode->cursor[0] * UI_DPI_FAC; - cursor[1] = snode->cursor[1] * UI_DPI_FAC; + cursor[0] = snode->runtime->cursor[0] * UI_DPI_FAC; + cursor[1] = snode->runtime->cursor[1] * UI_DPI_FAC; } /* in v0 and v3 we put begin/end points */ @@ -3923,8 +4013,11 @@ static bool node_link_bezier_handles(View2D *v2d, } /* if v2d not NULL, it clips and returns 0 if not visible */ -bool node_link_bezier_points( - View2D *v2d, SpaceNode *snode, bNodeLink *link, float coord_array[][2], int resol) +bool node_link_bezier_points(const View2D *v2d, + const SpaceNode *snode, + const bNodeLink *link, + float coord_array[][2], + const int resol) { float vec[4][2]; @@ -4087,7 +4180,7 @@ static char nodelink_get_color_id(int th_col) return 0; } -static void nodelink_batch_draw(SpaceNode *snode) +static void nodelink_batch_draw(const SpaceNode *snode) { if (g_batch_link.count == 0) { return; @@ -4107,7 +4200,7 @@ static void nodelink_batch_draw(SpaceNode *snode) GPU_batch_program_set_builtin(g_batch_link.batch, GPU_SHADER_2D_NODELINK_INST); GPU_batch_uniform_4fv_array(g_batch_link.batch, "colors", 6, colors); - GPU_batch_uniform_1f(g_batch_link.batch, "expandSize", snode->aspect * LINK_WIDTH); + GPU_batch_uniform_1f(g_batch_link.batch, "expandSize", snode->runtime->aspect * LINK_WIDTH); GPU_batch_uniform_1f(g_batch_link.batch, "arrowSize", ARROW_SIZE); GPU_batch_draw(g_batch_link.batch); @@ -4127,7 +4220,7 @@ void nodelink_batch_end(SpaceNode *snode) g_batch_link.enabled = false; } -static void nodelink_batch_add_link(SpaceNode *snode, +static void nodelink_batch_add_link(const SpaceNode *snode, const float p0[2], const float p1[2], const float p2[2], @@ -4159,8 +4252,12 @@ static void nodelink_batch_add_link(SpaceNode *snode, } /* don't do shadows if th_col3 is -1. */ -void node_draw_link_bezier( - View2D *v2d, SpaceNode *snode, bNodeLink *link, int th_col1, int th_col2, int th_col3) +void node_draw_link_bezier(const View2D *v2d, + const SpaceNode *snode, + const bNodeLink *link, + int th_col1, + int th_col2, + int th_col3) { float vec[4][2]; @@ -4190,7 +4287,7 @@ void node_draw_link_bezier( GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_NODELINK); GPU_batch_uniform_2fv_array(batch, "bezierPts", 4, vec); GPU_batch_uniform_4fv_array(batch, "colors", 3, colors); - GPU_batch_uniform_1f(batch, "expandSize", snode->aspect * LINK_WIDTH); + GPU_batch_uniform_1f(batch, "expandSize", snode->runtime->aspect * LINK_WIDTH); GPU_batch_uniform_1f(batch, "arrowSize", ARROW_SIZE); GPU_batch_uniform_1i(batch, "doArrow", drawarrow); GPU_batch_draw(batch); diff --git a/source/blender/editors/space_node/node_add.c b/source/blender/editors/space_node/node_add.c index 508c0a47e21..131bacbdfef 100644 --- a/source/blender/editors/space_node/node_add.c +++ b/source/blender/editors/space_node/node_add.c @@ -312,6 +312,13 @@ void NODE_OT_add_reroute(wmOperatorType *ot) /* ****************** Add File Node Operator ******************* */ +static bool node_add_file_poll(bContext *C) +{ + const SpaceNode *snode = CTX_wm_space_node(C); + return ED_operator_node_editable(C) && + ELEM(snode->nodetree->type, NTREE_SHADER, NTREE_TEXTURE, NTREE_COMPOSIT); +} + static int node_add_file_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -341,7 +348,7 @@ static int node_add_file_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - node = node_add_node(C, NULL, type, snode->cursor[0], snode->cursor[1]); + node = node_add_node(C, NULL, type, snode->runtime->cursor[0], snode->runtime->cursor[1]); if (!node) { BKE_report(op->reports, RPT_WARNING, "Could not add an image node"); @@ -370,11 +377,14 @@ static int node_add_file_invoke(bContext *C, wmOperator *op, const wmEvent *even SpaceNode *snode = CTX_wm_space_node(C); /* convert mouse coordinates to v2d space */ - UI_view2d_region_to_view( - ®ion->v2d, event->mval[0], event->mval[1], &snode->cursor[0], &snode->cursor[1]); + UI_view2d_region_to_view(®ion->v2d, + event->mval[0], + event->mval[1], + &snode->runtime->cursor[0], + &snode->runtime->cursor[1]); - snode->cursor[0] /= UI_DPI_FAC; - snode->cursor[1] /= UI_DPI_FAC; + snode->runtime->cursor[0] /= UI_DPI_FAC; + snode->runtime->cursor[1] /= UI_DPI_FAC; if (RNA_struct_property_is_set(op->ptr, "filepath") || RNA_struct_property_is_set(op->ptr, "name")) { @@ -393,7 +403,7 @@ void NODE_OT_add_file(wmOperatorType *ot) /* callbacks */ ot->exec = node_add_file_exec; ot->invoke = node_add_file_invoke; - ot->poll = ED_operator_node_editable; + ot->poll = node_add_file_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -435,7 +445,8 @@ static int node_add_mask_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - node = node_add_node(C, NULL, CMP_NODE_MASK, snode->cursor[0], snode->cursor[1]); + node = node_add_node( + C, NULL, CMP_NODE_MASK, snode->runtime->cursor[0], snode->runtime->cursor[1]); if (!node) { BKE_report(op->reports, RPT_WARNING, "Could not add a mask node"); diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index d3fec7257f5..037587809b0 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -317,14 +317,14 @@ static void node_uiblocks_init(const bContext *C, bNodeTree *ntree) } } -void node_to_view(struct bNode *node, float x, float y, float *rx, float *ry) +void node_to_view(const bNode *node, float x, float y, float *rx, float *ry) { nodeToView(node, x, y, rx, ry); *rx *= UI_DPI_FAC; *ry *= UI_DPI_FAC; } -void node_to_updated_rect(struct bNode *node, rctf *r_rect) +void node_to_updated_rect(const bNode *node, rctf *r_rect) { node_to_view(node, node->offsetx, node->offsety, &r_rect->xmin, &r_rect->ymax); node_to_view(node, @@ -334,7 +334,7 @@ void node_to_updated_rect(struct bNode *node, rctf *r_rect) &r_rect->ymin); } -void node_from_view(struct bNode *node, float x, float y, float *rx, float *ry) +void node_from_view(const bNode *node, float x, float y, float *rx, float *ry) { x /= UI_DPI_FAC; y /= UI_DPI_FAC; @@ -689,11 +689,11 @@ int node_get_colorid(bNode *node) /* note: in cmp_util.c is similar code, for node_compo_pass_on() * the same goes for shader and texture nodes. */ /* note: in node_edit.c is similar code, for untangle node */ -static void node_draw_mute_line(View2D *v2d, SpaceNode *snode, bNode *node) +static void node_draw_mute_line(const View2D *v2d, const SpaceNode *snode, const bNode *node) { GPU_blend(GPU_BLEND_ALPHA); - LISTBASE_FOREACH (bNodeLink *, link, &node->internal_links) { + LISTBASE_FOREACH (const bNodeLink *, link, &node->internal_links) { node_draw_link_bezier(v2d, snode, link, TH_REDALERT, TH_REDALERT, -1); } @@ -947,14 +947,14 @@ static void node_toggle_button_cb(struct bContext *C, void *node_argv, void *op_ WM_operator_name_call(C, opname, WM_OP_INVOKE_DEFAULT, NULL); } -void node_draw_shadow(SpaceNode *snode, bNode *node, float radius, float alpha) +void node_draw_shadow(const SpaceNode *snode, const bNode *node, float radius, float alpha) { - rctf *rct = &node->totr; + const rctf *rct = &node->totr; UI_draw_roundbox_corner_set(UI_CNR_ALL); - ui_draw_dropshadow(rct, radius, snode->aspect, alpha, node->flag & SELECT); + ui_draw_dropshadow(rct, radius, snode->runtime->aspect, alpha, node->flag & SELECT); } -void node_draw_sockets(View2D *v2d, +void node_draw_sockets(const View2D *v2d, const bContext *C, bNodeTree *ntree, bNode *node, @@ -1118,8 +1118,8 @@ void node_draw_sockets(View2D *v2d, } static void node_draw_basis(const bContext *C, - ARegion *region, - SpaceNode *snode, + const View2D *v2d, + const SpaceNode *snode, bNodeTree *ntree, bNode *node, bNodeInstanceKey key) @@ -1127,8 +1127,6 @@ static void node_draw_basis(const bContext *C, /* float socket_size = NODE_SOCKSIZE*U.dpi/72; */ /* UNUSED */ float iconbutw = 0.8f * UI_UNIT_X; - View2D *v2d = ®ion->v2d; - /* skip if out of view */ if (BLI_rctf_isect(&node->totr, &v2d->cur, NULL) == false) { UI_block_end(C, node->block); @@ -1157,7 +1155,15 @@ static void node_draw_basis(const bContext *C, rctf *rct = &node->totr; UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT); UI_draw_roundbox_aa( - true, rct->xmin, rct->ymax - NODE_DY, rct->xmax, rct->ymax, BASIS_RAD, color); + &(const rctf){ + .xmin = rct->xmin, + .xmax = rct->xmax, + .ymin = rct->ymax - NODE_DY, + .ymax = rct->ymax, + }, + true, + BASIS_RAD, + color); /* show/hide icons */ float iconofs = rct->xmax - 0.35f * U.widget_unit; @@ -1308,7 +1314,15 @@ static void node_draw_basis(const bContext *C, UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT); UI_draw_roundbox_aa( - true, rct->xmin, rct->ymin, rct->xmax, rct->ymax - NODE_DY, BASIS_RAD, color); + &(const rctf){ + .xmin = rct->xmin, + .xmax = rct->xmax, + .ymin = rct->ymin, + .ymax = rct->ymax - NODE_DY, + }, + true, + BASIS_RAD, + color); /* outline active and selected emphasis */ if (node->flag & SELECT) { @@ -1316,7 +1330,16 @@ static void node_draw_basis(const bContext *C, (node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, 0, -40, color); UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(false, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD, color); + UI_draw_roundbox_aa( + &(const rctf){ + .xmin = rct->xmin, + .xmax = rct->xmax, + .ymin = rct->ymin, + .ymax = rct->ymax, + }, + false, + BASIS_RAD, + color); } /* disable lines */ @@ -1343,8 +1366,8 @@ static void node_draw_basis(const bContext *C, } static void node_draw_hidden(const bContext *C, - ARegion *region, - SpaceNode *snode, + const View2D *v2d, + const SpaceNode *snode, bNodeTree *ntree, bNode *node, bNodeInstanceKey UNUSED(key)) @@ -1353,8 +1376,6 @@ static void node_draw_hidden(const bContext *C, float centy = BLI_rctf_cent_y(rct); float hiddenrad = BLI_rctf_size_y(rct) / 2.0f; - View2D *v2d = ®ion->v2d; - float scale; UI_view2d_scale_get(v2d, &scale, NULL); @@ -1373,14 +1394,14 @@ static void node_draw_hidden(const bContext *C, UI_GetThemeColor4fv(color_id, color); } - UI_draw_roundbox_aa(true, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad, color); + UI_draw_roundbox_aa(rct, true, hiddenrad, color); /* outline active and selected emphasis */ if (node->flag & SELECT) { UI_GetThemeColorShadeAlpha4fv( (node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, 0, -40, color); - UI_draw_roundbox_aa(false, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad, color); + UI_draw_roundbox_aa(rct, false, hiddenrad, color); } /* custom color inline */ @@ -1388,14 +1409,17 @@ static void node_draw_hidden(const bContext *C, GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); - UI_draw_roundbox_3fv_alpha(false, - rct->xmin + 1, - rct->ymin + 1, - rct->xmax - 1, - rct->ymax - 1, - hiddenrad, - node->color, - 1.0f); + UI_draw_roundbox_3fv_alpha( + &(const rctf){ + .xmin = rct->xmin + 1, + .xmax = rct->xmax - 1, + .ymin = rct->ymin + 1, + .ymax = rct->ymax - 1, + }, + false, + hiddenrad, + node->color, + 1.0f); GPU_line_smooth(false); GPU_blend(GPU_BLEND_NONE); @@ -1438,7 +1462,7 @@ static void node_draw_hidden(const bContext *C, /* disable lines */ if (node->flag & NODE_MUTED) { - node_draw_mute_line(®ion->v2d, snode, node); + node_draw_mute_line(v2d, snode, node); } char showname[128]; /* 128 is used below */ @@ -1477,19 +1501,19 @@ static void node_draw_hidden(const bContext *C, immVertex2f(pos, rct->xmax - dx, centy - 4.0f); immVertex2f(pos, rct->xmax - dx, centy + 4.0f); - immVertex2f(pos, rct->xmax - dx - 3.0f * snode->aspect, centy - 4.0f); - immVertex2f(pos, rct->xmax - dx - 3.0f * snode->aspect, centy + 4.0f); + immVertex2f(pos, rct->xmax - dx - 3.0f * snode->runtime->aspect, centy - 4.0f); + immVertex2f(pos, rct->xmax - dx - 3.0f * snode->runtime->aspect, centy + 4.0f); immEnd(); immUniformThemeColorShade(color_id, 30); - dx -= snode->aspect; + dx -= snode->runtime->aspect; immBegin(GPU_PRIM_LINES, 4); immVertex2f(pos, rct->xmax - dx, centy - 4.0f); immVertex2f(pos, rct->xmax - dx, centy + 4.0f); - immVertex2f(pos, rct->xmax - dx - 3.0f * snode->aspect, centy - 4.0f); - immVertex2f(pos, rct->xmax - dx - 3.0f * snode->aspect, centy + 4.0f); + immVertex2f(pos, rct->xmax - dx - 3.0f * snode->runtime->aspect, centy - 4.0f); + immVertex2f(pos, rct->xmax - dx - 3.0f * snode->runtime->aspect, centy + 4.0f); immEnd(); immUnbindProgram(); @@ -1550,11 +1574,12 @@ void node_draw_default(const bContext *C, bNode *node, bNodeInstanceKey key) { + const View2D *v2d = ®ion->v2d; if (node->flag & NODE_HIDDEN) { - node_draw_hidden(C, region, snode, ntree, node, key); + node_draw_hidden(C, v2d, snode, ntree, node, key); } else { - node_draw_basis(C, region, snode, ntree, node, key); + node_draw_basis(C, v2d, snode, ntree, node, key); } } @@ -1607,8 +1632,7 @@ void node_draw_nodetree(const bContext *C, #endif /* draw background nodes, last nodes in front */ - int a = 0; - LISTBASE_FOREACH_INDEX (bNode *, node, &ntree->nodes, a) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { #ifdef USE_DRAW_TOT_UPDATE /* unrelated to background nodes, update the v2d->tot, * can be anywhere before we draw the scroll bars */ @@ -1635,8 +1659,7 @@ void node_draw_nodetree(const bContext *C, GPU_blend(GPU_BLEND_NONE); /* draw foreground nodes, last nodes in front */ - a = 0; - LISTBASE_FOREACH_INDEX (bNode *, node, &ntree->nodes, a) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->flag & NODE_BACKGROUND) { continue; } @@ -1666,7 +1689,7 @@ static void snode_setup_v2d(SpaceNode *snode, ARegion *region, const float cente UI_view2d_view_ortho(v2d); /* aspect+font, set each time */ - snode->aspect = BLI_rctf_size_x(&v2d->cur) / (float)region->winx; + snode->runtime->aspect = BLI_rctf_size_x(&v2d->cur) / (float)region->winx; // XXX snode->curfont = uiSetCurFont_ext(snode->aspect); } @@ -1686,8 +1709,8 @@ static void draw_nodetree(const bContext *C, /* shade the parent node group and add a uiBlock to clip mouse events */ static void draw_group_overlay(const bContext *C, ARegion *region) { - View2D *v2d = ®ion->v2d; - rctf rect = v2d->cur; + const View2D *v2d = ®ion->v2d; + const rctf rect = v2d->cur; float color[4]; /* shade node groups to separate them visually */ @@ -1695,7 +1718,7 @@ static void draw_group_overlay(const bContext *C, ARegion *region) UI_GetThemeColorShadeAlpha4fv(TH_NODE_GROUP, 0, 0, color); UI_draw_roundbox_corner_set(UI_CNR_NONE); - UI_draw_roundbox_4fv(true, rect.xmin, rect.ymin, rect.xmax, rect.ymax, 0, color); + UI_draw_roundbox_4fv(&rect, true, 0, color); GPU_blend(GPU_BLEND_NONE); /* set the block bounds to clip mouse events from underlying nodes */ @@ -1722,14 +1745,15 @@ void node_draw_space(const bContext *C, ARegion *region) GPU_depth_test(GPU_DEPTH_NONE); GPU_scissor_test(true); - /* XXX snode->cursor set in coordspace for placing new nodes, used for drawing noodles too */ + /* XXX snode->runtime->cursor set in coordspace for placing new nodes, used for drawing noodles + * too */ UI_view2d_region_to_view(®ion->v2d, win->eventstate->x - region->winrct.xmin, win->eventstate->y - region->winrct.ymin, - &snode->cursor[0], - &snode->cursor[1]); - snode->cursor[0] /= UI_DPI_FAC; - snode->cursor[1] /= UI_DPI_FAC; + &snode->runtime->cursor[0], + &snode->runtime->cursor[1]); + snode->runtime->cursor[0] /= UI_DPI_FAC; + snode->runtime->cursor[1] /= UI_DPI_FAC; int grid_levels = UI_GetThemeValueType(TH_NODE_GRID_LEVELS, SPACE_NODE); @@ -1819,7 +1843,7 @@ void node_draw_space(const bContext *C, ARegion *region) /* temporary links */ GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); - LISTBASE_FOREACH (bNodeLinkDrag *, nldrag, &snode->linkdrag) { + LISTBASE_FOREACH (bNodeLinkDrag *, nldrag, &snode->runtime->linkdrag) { LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { node_draw_link(v2d, snode, (bNodeLink *)linkdata->data); } diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 30eee416b12..ced81401874 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -880,8 +880,8 @@ static void node_resize_init( NodeSizeWidget *nsw = MEM_callocN(sizeof(NodeSizeWidget), "size widget op data"); op->customdata = nsw; - nsw->mxstart = snode->cursor[0] * UI_DPI_FAC; - nsw->mystart = snode->cursor[1] * UI_DPI_FAC; + nsw->mxstart = snode->runtime->cursor[0] * UI_DPI_FAC; + nsw->mystart = snode->runtime->cursor[1] * UI_DPI_FAC; /* store old */ nsw->oldlocx = node->locx; diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index aa59b7293a3..a2b04fa9665 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -37,6 +37,7 @@ struct bContext; struct bNode; struct bNodeLink; struct bNodeSocket; +struct NodeInsertOfsData; struct wmGizmoGroupType; struct wmKeyConfig; struct wmWindow; @@ -53,6 +54,23 @@ typedef struct bNodeLinkDrag { int in_out; } bNodeLinkDrag; +typedef struct SpaceNode_Runtime { + float aspect; + + /** Mouse position for drawing socket-less links and adding nodes. */ + float cursor[2]; + + /** For auto compositing. */ + bool recalc; + + /** Temporary data for modal linking operator. */ + struct ListBase linkdrag; + + /* XXX hack for translate_attach op-macros to pass data from transform op to insert_offset op */ + /** Temporary data for node insert offset (in UI called Auto-offset). */ + struct NodeInsertOfsData *iofsd; +} SpaceNode_Runtime; + /* space_node.c */ /* transform between View2Ds in the tree path */ @@ -61,14 +79,17 @@ void space_node_group_offset(struct SpaceNode *snode, float *x, float *y); /* node_draw.c */ int node_get_colorid(struct bNode *node); int node_get_resize_cursor(int directions); -void node_draw_shadow(struct SpaceNode *snode, struct bNode *node, float radius, float alpha); +void node_draw_shadow(const struct SpaceNode *snode, + const struct bNode *node, + float radius, + float alpha); void node_draw_default(const struct bContext *C, struct ARegion *region, struct SpaceNode *snode, struct bNodeTree *ntree, struct bNode *node, bNodeInstanceKey key); -void node_draw_sockets(struct View2D *v2d, +void node_draw_sockets(const struct View2D *v2d, const struct bContext *C, struct bNodeTree *ntree, struct bNode *node, @@ -92,9 +113,9 @@ void node_draw_space(const bContext *C, ARegion *region); void node_set_cursor(struct wmWindow *win, struct SpaceNode *snode, float cursor[2]); /* DPI scaled coords */ -void node_to_view(struct bNode *node, float x, float y, float *rx, float *ry); -void node_to_updated_rect(struct bNode *node, rctf *r_rect); -void node_from_view(struct bNode *node, float x, float y, float *rx, float *ry); +void node_to_view(const struct bNode *node, float x, float y, float *rx, float *ry); +void node_to_updated_rect(const struct bNode *node, rctf *r_rect); +void node_from_view(const struct bNode *node, float x, float y, float *rx, float *ry); /* node_buttons.c */ void node_buttons_register(struct ARegionType *art); @@ -145,17 +166,17 @@ void nodelink_batch_start(struct SpaceNode *snode); void nodelink_batch_end(struct SpaceNode *snode); void node_draw_link(struct View2D *v2d, struct SpaceNode *snode, struct bNodeLink *link); -void node_draw_link_bezier(struct View2D *v2d, - struct SpaceNode *snode, - struct bNodeLink *link, +void node_draw_link_bezier(const struct View2D *v2d, + const struct SpaceNode *snode, + const struct bNodeLink *link, int th_col1, int th_col2, int th_col3); -bool node_link_bezier_points(struct View2D *v2d, - struct SpaceNode *snode, - struct bNodeLink *link, +bool node_link_bezier_points(const struct View2D *v2d, + const struct SpaceNode *snode, + const struct bNodeLink *link, float coord_array[][2], - int resol); + const int resol); void draw_nodespace_back_pix(const struct bContext *C, struct ARegion *region, struct SpaceNode *snode, diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index ba1e752e276..ee7c8bca2f8 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -58,7 +58,7 @@ static bool ntree_has_drivers(bNodeTree *ntree) { - AnimData *adt = BKE_animdata_from_id(&ntree->id); + const AnimData *adt = BKE_animdata_from_id(&ntree->id); if (adt == NULL) { return false; } @@ -663,7 +663,7 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links) snode_dag_update(C, snode); } - BLI_remlink(&snode->linkdrag, nldrag); + BLI_remlink(&snode->runtime->linkdrag, nldrag); /* links->data pointers are either held by the tree or freed already */ BLI_freelistN(&nldrag->links); MEM_freeN(nldrag); @@ -903,7 +903,7 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (nldrag) { op->customdata = nldrag; - BLI_addtail(&snode->linkdrag, nldrag); + BLI_addtail(&snode->runtime->linkdrag, nldrag); /* add modal handler */ WM_event_add_modal_handler(C, op); @@ -918,7 +918,7 @@ static void node_link_cancel(bContext *C, wmOperator *op) SpaceNode *snode = CTX_wm_space_node(C); bNodeLinkDrag *nldrag = op->customdata; - BLI_remlink(&snode->linkdrag, nldrag); + BLI_remlink(&snode->runtime->linkdrag, nldrag); BLI_freelistN(&nldrag->links); MEM_freeN(nldrag); @@ -1798,7 +1798,7 @@ static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd, static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { SpaceNode *snode = CTX_wm_space_node(C); - NodeInsertOfsData *iofsd = snode->iofsd; + NodeInsertOfsData *iofsd = snode->runtime->iofsd; bool redraw = false; if (!snode || event->type != TIMER || iofsd == NULL || iofsd->anim_timer != event->customdata) { @@ -1837,7 +1837,7 @@ static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const w node->anim_init_locx = node->anim_ofsx = 0.0f; } - snode->iofsd = NULL; + snode->runtime->iofsd = NULL; MEM_freeN(iofsd); return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); @@ -1851,7 +1851,7 @@ static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const w static int node_insert_offset_invoke(bContext *C, wmOperator *op, const wmEvent *event) { const SpaceNode *snode = CTX_wm_space_node(C); - NodeInsertOfsData *iofsd = snode->iofsd; + NodeInsertOfsData *iofsd = snode->runtime->iofsd; if (!iofsd || !iofsd->insert) { return OPERATOR_CANCELLED; @@ -1927,7 +1927,7 @@ void ED_node_link_insert(Main *bmain, ScrArea *area) iofsd->prev = link->fromnode; iofsd->next = node; - snode->iofsd = iofsd; + snode->runtime->iofsd = iofsd; } ntreeUpdateTree(bmain, snode->edittree); /* needed for pointers */ diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c index 5060ac0db8a..a41f99afb4c 100644 --- a/source/blender/editors/space_node/node_select.c +++ b/source/blender/editors/space_node/node_select.c @@ -62,14 +62,15 @@ #include "node_intern.h" /* own include */ -/* Function to detect if there is a visible view3d that uses workbench in texture mode. +/** + * Function to detect if there is a visible view3d that uses workbench in texture mode. * This function is for fixing T76970 for Blender 2.83. The actual fix should add a mechanism in * the depsgraph that can be used by the draw engines to check if they need to be redrawn. * * We don't want to add these risky changes this close before releasing 2.83 without good testing * hence this workaround. There are still cases were too many updates happen. For example when you * have both a Cycles and workbench with textures viewport. - * */ + */ static bool has_workbench_in_texture_color(const wmWindowManager *wm, const Scene *scene, const Object *ob) diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index ad7632377a3..94915022ce9 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -321,18 +321,25 @@ static void node_free(SpaceLink *sl) LISTBASE_FOREACH_MUTABLE (bNodeTreePath *, path, &snode->treepath) { MEM_freeN(path); } + + MEM_SAFE_FREE(snode->runtime); } /* spacetype; init callback */ -static void node_init(struct wmWindowManager *UNUSED(wm), ScrArea *UNUSED(area)) +static void node_init(struct wmWindowManager *UNUSED(wm), ScrArea *area) { + SpaceNode *snode = (SpaceNode *)area->spacedata.first; + + if (snode->runtime == NULL) { + snode->runtime = MEM_callocN(sizeof(SpaceNode_Runtime), __func__); + } } -static void node_area_listener(wmWindow *UNUSED(win), - ScrArea *area, - wmNotifier *wmn, - Scene *UNUSED(scene)) +static void node_area_listener(const wmSpaceTypeListenerParams *params) { + ScrArea *area = params->area; + wmNotifier *wmn = params->notifier; + /* note, ED_area_tag_refresh will re-execute compositor */ SpaceNode *snode = area->spacedata.first; /* shaderfrom is only used for new shading nodes, otherwise all shaders are from objects */ @@ -362,7 +369,7 @@ static void node_area_listener(wmWindow *UNUSED(win), case ND_TRANSFORM_DONE: if (ED_node_is_compositor(snode)) { if (snode->flag & SNODE_AUTO_RENDER) { - snode->recalc = 1; + snode->runtime->recalc = true; ED_area_tag_refresh(area); } } @@ -521,8 +528,8 @@ static void node_area_refresh(const struct bContext *C, ScrArea *area) Scene *scene = (Scene *)snode->id; if (scene->use_nodes) { /* recalc is set on 3d view changes for auto compo */ - if (snode->recalc) { - snode->recalc = 0; + if (snode->runtime->recalc) { + snode->runtime->recalc = false; node_render_changed_exec((struct bContext *)C, NULL); } else { @@ -546,8 +553,10 @@ static SpaceLink *node_duplicate(SpaceLink *sl) BLI_duplicatelist(&snoden->treepath, &snode->treepath); - /* clear or remove stuff from old */ - BLI_listbase_clear(&snoden->linkdrag); + if (snode->runtime != NULL) { + snoden->runtime = MEM_dupallocN(snode->runtime); + BLI_listbase_clear(&snoden->runtime->linkdrag); + } /* Note: no need to set node tree user counts, * the editor only keeps at least 1 (id_us_ensure_real), @@ -589,6 +598,16 @@ static void node_toolbar_region_draw(const bContext *C, ARegion *region) ED_region_panels(C, region); } +void ED_node_cursor_location_get(const SpaceNode *snode, float value[2]) +{ + copy_v2_v2(value, snode->runtime->cursor); +} + +void ED_node_cursor_location_set(SpaceNode *snode, const float value[2]) +{ + copy_v2_v2(snode->runtime->cursor, value); +} + static void node_cursor(wmWindow *win, ScrArea *area, ARegion *region) { SpaceNode *snode = area->spacedata.first; @@ -597,15 +616,15 @@ static void node_cursor(wmWindow *win, ScrArea *area, ARegion *region) UI_view2d_region_to_view(®ion->v2d, win->eventstate->x - region->winrct.xmin, win->eventstate->y - region->winrct.ymin, - &snode->cursor[0], - &snode->cursor[1]); + &snode->runtime->cursor[0], + &snode->runtime->cursor[1]); - /* here snode->cursor is used to detect the node edge for sizing */ - node_set_cursor(win, snode, snode->cursor); + /* here snode->runtime->cursor is used to detect the node edge for sizing */ + node_set_cursor(win, snode, snode->runtime->cursor); - /* XXX snode->cursor is in placing new nodes space */ - snode->cursor[0] /= UI_DPI_FAC; - snode->cursor[1] /= UI_DPI_FAC; + /* XXX snode->runtime->cursor is in placing new nodes space */ + snode->runtime->cursor[0] /= UI_DPI_FAC; + snode->runtime->cursor[1] /= UI_DPI_FAC; } /* Initialize main region, setting handlers. */ @@ -645,7 +664,7 @@ static bool node_ima_drop_poll(bContext *UNUSED(C), /* rule might not work? */ return (ELEM(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_MOVIE)); } - return WM_drag_get_local_ID(drag, ID_IM) != NULL; + return WM_drag_is_ID_type(drag, ID_IM); } static bool node_mask_drop_poll(bContext *UNUSED(C), @@ -653,19 +672,19 @@ static bool node_mask_drop_poll(bContext *UNUSED(C), const wmEvent *UNUSED(event), const char **UNUSED(r_tooltip)) { - return WM_drag_get_local_ID(drag, ID_MSK) != NULL; + return WM_drag_is_ID_type(drag, ID_MSK); } static void node_id_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = WM_drag_get_local_ID(drag, 0); + ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); RNA_string_set(drop->ptr, "name", id->name + 2); } static void node_id_path_drop_copy(wmDrag *drag, wmDropBox *drop) { - ID *id = WM_drag_get_local_ID(drag, 0); + ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); if (id) { RNA_string_set(drop->ptr, "name", id->name + 2); @@ -703,12 +722,10 @@ static void node_header_region_draw(const bContext *C, ARegion *region) } /* used for header + main region */ -static void node_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void node_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; wmGizmoMap *gzmap = region->gizmo_map; /* context changes */ diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index ab515c0c3a8..467802d181b 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -140,6 +140,11 @@ static TreeElement *outliner_drop_insert_find(bContext *C, TreeElement *te_hovered; float view_mval[2]; + /* Emtpy tree, e.g. while filtered. */ + if (BLI_listbase_is_empty(&space_outliner->tree)) { + return NULL; + } + UI_view2d_region_to_view( ®ion->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); te_hovered = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]); diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 8104c1e0d58..d424bb2319a 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -314,7 +314,7 @@ static void outliner_object_set_flag_recursive_fn(bContext *C, /** * Object properties. - * */ + */ static void outliner__object_set_flag_recursive_fn(bContext *C, void *poin, void *poin2) { Object *ob = poin; @@ -324,7 +324,7 @@ static void outliner__object_set_flag_recursive_fn(bContext *C, void *poin, void /** * Base properties. - * */ + */ static void outliner__base_set_flag_recursive_fn(bContext *C, void *poin, void *poin2) { Base *base = poin; @@ -697,13 +697,13 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL); break; } default: - WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL); break; } + WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL); + /* Check the library target exists */ if (te->idcode == ID_LI) { Library *lib = (Library *)tselem->id; @@ -2695,13 +2695,16 @@ static void outliner_draw_iconrow_number(const uiFontStyle *fstyle, float offset_x = (float)offsx + UI_UNIT_X * 0.35f; UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(true, - offset_x + ufac, - (float)ys - UI_UNIT_Y * 0.2f + ufac, - offset_x + UI_UNIT_X - ufac, - (float)ys - UI_UNIT_Y * 0.2f + UI_UNIT_Y - ufac, - (float)UI_UNIT_Y / 2.0f - ufac, - color); + UI_draw_roundbox_aa( + &(const rctf){ + .xmin = offset_x + ufac, + .xmax = offset_x + UI_UNIT_X - ufac, + .ymin = (float)ys - UI_UNIT_Y * 0.2f + ufac, + .ymax = (float)ys - UI_UNIT_Y * 0.2f + UI_UNIT_Y - ufac, + }, + true, + (float)UI_UNIT_Y / 2.0f - ufac, + color); /* Now the numbers. */ uchar text_col[4]; @@ -2751,8 +2754,26 @@ static void outliner_draw_active_indicator(const float minx, const float radius = UI_UNIT_Y / 4.0f; UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(true, minx, miny + ufac, maxx, maxy - ufac, radius, icon_color); - UI_draw_roundbox_aa(false, minx, miny + ufac, maxx, maxy - ufac, radius, icon_border); + UI_draw_roundbox_aa( + &(const rctf){ + .xmin = minx, + .xmax = maxx, + .ymin = miny + ufac, + .ymax = maxy - ufac, + }, + true, + radius, + icon_color); + UI_draw_roundbox_aa( + &(const rctf){ + .xmin = minx, + .xmax = maxx, + .ymin = miny + ufac, + .ymax = maxy - ufac, + }, + false, + radius, + icon_border); GPU_blend(GPU_BLEND_ALPHA); /* Roundbox disables. */ } diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 600047c4b11..1ef5735d7d2 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -1543,7 +1543,7 @@ static bool outliner_is_co_within_active_mode_column(bContext *C, * Action to run when clicking in the outliner, * * May expend/collapse branches or activate items. - * */ + */ static int outliner_item_do_activate_from_cursor(bContext *C, const int mval[2], const bool extend, diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index 6c9d4433abd..88e88ab0c66 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -102,12 +102,11 @@ static void outliner_main_region_free(ARegion *UNUSED(region)) { } -static void outliner_main_region_listener(wmWindow *UNUSED(win), - ScrArea *area, - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void outliner_main_region_listener(const wmRegionListenerParams *params) { + ScrArea *area = params->area; + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; SpaceOutliner *space_outliner = area->spacedata.first; /* context changes */ @@ -264,15 +263,13 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win), } } -static void outliner_main_region_message_subscribe(const struct bContext *UNUSED(C), - struct WorkSpace *UNUSED(workspace), - struct Scene *UNUSED(scene), - struct bScreen *UNUSED(screen), - struct ScrArea *area, - struct ARegion *region, - struct wmMsgBus *mbus) +static void outliner_main_region_message_subscribe(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + ScrArea *area = params->area; + ARegion *region = params->region; SpaceOutliner *space_outliner = area->spacedata.first; + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { .owner = region, .user_data = region, @@ -301,12 +298,11 @@ static void outliner_header_region_free(ARegion *UNUSED(region)) { } -static void outliner_header_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void outliner_header_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_SCENE: diff --git a/source/blender/editors/space_script/space_script.c b/source/blender/editors/space_script/space_script.c index 3330ba14530..47de18e8faf 100644 --- a/source/blender/editors/space_script/space_script.c +++ b/source/blender/editors/space_script/space_script.c @@ -156,13 +156,8 @@ static void script_header_region_draw(const bContext *C, ARegion *region) ED_region_header(C, region); } -static void script_main_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *UNUSED(region), - wmNotifier *UNUSED(wmn), - const Scene *UNUSED(scene)) +static void script_main_region_listener(const wmRegionListenerParams *UNUSED(params)) { -/* context changes */ /* XXX - Todo, need the ScriptSpace accessible to get the python script to run. */ #if 0 BPY_run_script_space_listener() diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index f9076145f2f..516d3bba16f 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -1069,6 +1069,9 @@ static int sequencer_add_effect_strip_exec(bContext *C, wmOperator *op) else if (seq->type == SEQ_TYPE_TEXT) { seq->blend_mode = SEQ_TYPE_ALPHAOVER; } + else if (SEQ_effect_get_num_inputs(seq->type) == 1) { + seq->blend_mode = seq1->blend_mode; + } /* Set channel. If unset, use lowest free one above strips. */ if (!RNA_struct_property_is_set(op->ptr, "channel")) { diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 72c3e43185b..201df1ceed6 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1447,15 +1447,24 @@ static void sequencer_draw_borders_overlay(const SpaceSeq *sseq, if (sseq->flag & SEQ_SHOW_SAFE_MARGINS) { immUniformThemeColorBlend(TH_VIEW_OVERLAY, TH_BACK, 0.25f); - UI_draw_safe_areas( - shdr_pos, x1, x2, y1, y2, scene->safe_areas.title, scene->safe_areas.action); + UI_draw_safe_areas(shdr_pos, + &(const rctf){ + .xmin = x1, + .xmax = x2, + .ymin = y1, + .ymax = y2, + }, + scene->safe_areas.title, + scene->safe_areas.action); if (sseq->flag & SEQ_SHOW_SAFE_CENTER) { UI_draw_safe_areas(shdr_pos, - x1, - x2, - y1, - y2, + &(const rctf){ + .xmin = x1, + .xmax = x2, + .ymin = y1, + .ymax = y2, + }, scene->safe_areas.title_center, scene->safe_areas.action_center); } diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index b11e2a32b87..51c2d3ebdf1 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -334,11 +334,11 @@ static SpaceLink *sequencer_duplicate(SpaceLink *sl) return (SpaceLink *)sseqn; } -static void sequencer_listener(wmWindow *UNUSED(win), - ScrArea *area, - wmNotifier *wmn, - Scene *UNUSED(scene)) +static void sequencer_listener(const wmSpaceTypeListenerParams *params) { + ScrArea *area = params->area; + wmNotifier *wmn = params->notifier; + /* Context changes. */ switch (wmn->category) { case NC_SCENE: @@ -534,12 +534,11 @@ static void sequencer_main_region_draw_overlay(const bContext *C, ARegion *regio draw_timeline_seq_display(C, region); } -static void sequencer_main_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void sequencer_main_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* Context changes. */ switch (wmn->category) { case NC_SCENE: @@ -579,14 +578,12 @@ static void sequencer_main_region_listener(wmWindow *UNUSED(win), } } -static void sequencer_main_region_message_subscribe(const struct bContext *UNUSED(C), - struct WorkSpace *UNUSED(workspace), - struct Scene *scene, - struct bScreen *UNUSED(screen), - struct ScrArea *UNUSED(area), - struct ARegion *region, - struct wmMsgBus *mbus) +static void sequencer_main_region_message_subscribe(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + Scene *scene = params->scene; + ARegion *region = params->region; + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { .owner = region, .user_data = region, @@ -746,12 +743,11 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region) } } -static void sequencer_preview_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void sequencer_preview_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* Context changes. */ switch (wmn->category) { case NC_GPENCIL: @@ -816,12 +812,11 @@ static void sequencer_buttons_region_draw(const bContext *C, ARegion *region) ED_region_panels(C, region); } -static void sequencer_buttons_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void sequencer_buttons_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* Context changes. */ switch (wmn->category) { case NC_GPENCIL: diff --git a/source/blender/editors/space_statusbar/space_statusbar.c b/source/blender/editors/space_statusbar/space_statusbar.c index ae56b111360..e877cf8ffcf 100644 --- a/source/blender/editors/space_statusbar/space_statusbar.c +++ b/source/blender/editors/space_statusbar/space_statusbar.c @@ -95,12 +95,11 @@ static void statusbar_keymap(struct wmKeyConfig *UNUSED(keyconf)) { } -static void statusbar_header_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void statusbar_header_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_SCREEN: @@ -131,14 +130,11 @@ static void statusbar_header_region_listener(wmWindow *UNUSED(win), } } -static void statusbar_header_region_message_subscribe(const bContext *UNUSED(C), - WorkSpace *UNUSED(workspace), - Scene *UNUSED(scene), - bScreen *UNUSED(screen), - ScrArea *UNUSED(area), - ARegion *region, - struct wmMsgBus *mbus) +static void statusbar_header_region_message_subscribe(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + ARegion *region = params->region; + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { .owner = region, .user_data = region, diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index 0f5ac5abe1d..045305e0f5d 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -122,11 +122,10 @@ static SpaceLink *text_duplicate(SpaceLink *sl) return (SpaceLink *)stextn; } -static void text_listener(wmWindow *UNUSED(win), - ScrArea *area, - wmNotifier *wmn, - Scene *UNUSED(scene)) +static void text_listener(const wmSpaceTypeListenerParams *params) { + ScrArea *area = params->area; + wmNotifier *wmn = params->notifier; SpaceText *st = area->spacedata.first; /* context changes */ diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 849766851aa..b38f92402b3 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -1010,13 +1010,16 @@ static void draw_textscroll(const SpaceText *st, rcti *scroll, rcti *back) BLI_rcti_size_y(&st->runtime.scroll_region_select)); UI_GetThemeColor3fv(TH_HILITE, col); col[3] = 0.18f; - UI_draw_roundbox_aa(true, - st->runtime.scroll_region_select.xmin + 1, - st->runtime.scroll_region_select.ymin, - st->runtime.scroll_region_select.xmax - 1, - st->runtime.scroll_region_select.ymax, - rad, - col); + UI_draw_roundbox_aa( + &(const rctf){ + .xmin = st->runtime.scroll_region_select.xmin + 1, + .xmax = st->runtime.scroll_region_select.xmax - 1, + .ymin = st->runtime.scroll_region_select.ymin, + .ymax = st->runtime.scroll_region_select.ymax, + }, + true, + rad, + col); } /*********************** draw documentation *******************************/ @@ -1180,7 +1183,14 @@ static void draw_suggestion_list(const SpaceText *st, const TextDrawContext *tdc } /* not needed but stands out nicer */ - UI_draw_box_shadow(220, x, y - boxh, x + boxw, y); + UI_draw_box_shadow( + &(const rctf){ + .xmin = x, + .xmax = x + boxw, + .ymin = y - boxh, + .ymax = y, + }, + 220); uint pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); diff --git a/source/blender/editors/space_topbar/space_topbar.c b/source/blender/editors/space_topbar/space_topbar.c index dc357cdd355..6a7439df6e3 100644 --- a/source/blender/editors/space_topbar/space_topbar.c +++ b/source/blender/editors/space_topbar/space_topbar.c @@ -129,12 +129,11 @@ static void topbar_header_region_init(wmWindowManager *UNUSED(wm), ARegion *regi ED_region_header_init(region); } -static void topbar_main_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void topbar_main_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_WM: @@ -160,12 +159,11 @@ static void topbar_main_region_listener(wmWindow *UNUSED(win), } } -static void topbar_header_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void topbar_header_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_WM: @@ -191,14 +189,12 @@ static void topbar_header_listener(wmWindow *UNUSED(win), } } -static void topbar_header_region_message_subscribe(const struct bContext *UNUSED(C), - struct WorkSpace *workspace, - struct Scene *UNUSED(scene), - struct bScreen *UNUSED(screen), - struct ScrArea *UNUSED(area), - struct ARegion *region, - struct wmMsgBus *mbus) +static void topbar_header_region_message_subscribe(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + WorkSpace *workspace = params->workspace; + ARegion *region = params->region; + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { .owner = region, .user_data = region, diff --git a/source/blender/editors/space_userpref/space_userpref.c b/source/blender/editors/space_userpref/space_userpref.c index 3efdee9cec9..d4692f156d3 100644 --- a/source/blender/editors/space_userpref/space_userpref.c +++ b/source/blender/editors/space_userpref/space_userpref.c @@ -183,46 +183,20 @@ static void userpref_execute_region_init(wmWindowManager *wm, ARegion *region) region->v2d.keepzoom |= V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y; } -static void userpref_main_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *UNUSED(region), - wmNotifier *UNUSED(wmn), - const Scene *UNUSED(scene)) +static void userpref_main_region_listener(const wmRegionListenerParams *UNUSED(params)) { - /* context changes */ } -static void userpref_header_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *UNUSED(region), - wmNotifier *UNUSED(wmn), - const Scene *UNUSED(scene)) +static void userpref_header_listener(const wmRegionListenerParams *UNUSED(params)) { - /* context changes */ -#if 0 - switch (wmn->category) { - default: - break; - } -#endif } -static void userpref_navigation_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *UNUSED(region), - wmNotifier *UNUSED(wmn), - const Scene *UNUSED(scene)) +static void userpref_navigation_region_listener(const wmRegionListenerParams *UNUSED(params)) { - /* context changes */ } -static void userpref_execute_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *UNUSED(region), - wmNotifier *UNUSED(wmn), - const Scene *UNUSED(scene)) +static void userpref_execute_region_listener(const wmRegionListenerParams *UNUSED(params)) { - /* context changes */ } /* only called once, from space/spacetypes.c */ diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 34d342ddd94..215166a6158 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -468,16 +468,30 @@ static bool view3d_drop_in_main_region_poll(bContext *C, const wmEvent *event) return ED_region_overlap_isect_any_xy(area, &event->x) == false; } -static ID *view3d_drop_id_in_main_region_poll_id(bContext *C, - wmDrag *drag, - const wmEvent *event, - ID_Type id_type) +static ID_Type view3d_drop_id_in_main_region_poll_get_id_type(bContext *C, + wmDrag *drag, + const wmEvent *event) { - ScrArea *area = CTX_wm_area(C); + const ScrArea *area = CTX_wm_area(C); + if (ED_region_overlap_isect_any_xy(area, &event->x)) { - return NULL; + return 0; + } + if (!view3d_drop_in_main_region_poll(C, event)) { + return 0; + } + + ID *local_id = WM_drag_get_local_ID(drag, 0); + if (local_id) { + return GS(local_id->name); + } + + wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0); + if (asset_drag) { + return asset_drag->id_type; } - return view3d_drop_in_main_region_poll(C, event) ? WM_drag_get_local_ID(drag, id_type) : NULL; + + return 0; } static bool view3d_drop_id_in_main_region_poll(bContext *C, @@ -489,7 +503,7 @@ static bool view3d_drop_id_in_main_region_poll(bContext *C, return false; } - return WM_drag_get_local_ID(drag, id_type) || WM_drag_get_asset_data(drag, id_type); + return WM_drag_is_ID_type(drag, id_type); } static bool view3d_ob_drop_poll(bContext *C, @@ -521,9 +535,9 @@ static bool view3d_object_data_drop_poll(bContext *C, const wmEvent *event, const char **r_tooltip) { - ID *id = view3d_drop_id_in_main_region_poll_id(C, drag, event, 0); - if (id != NULL) { - if (BKE_object_obdata_to_type(id) != -1) { + ID_Type id_type = view3d_drop_id_in_main_region_poll_get_id_type(C, drag, event); + if (id_type) { + if (OB_DATA_SUPPORT_ID(id_type)) { *r_tooltip = TIP_("Create object instance from object-data"); return true; } @@ -544,7 +558,7 @@ static bool view3d_ima_drop_poll(bContext *C, return (ELEM(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_MOVIE)); } - return WM_drag_get_local_ID(drag, ID_IM) || WM_drag_get_asset_data(drag, ID_IM); + return WM_drag_is_ID_type(drag, ID_IM); } static bool view3d_ima_bg_is_camera_view(bContext *C) @@ -628,7 +642,7 @@ static void view3d_id_drop_copy(wmDrag *drag, wmDropBox *drop) static void view3d_id_drop_copy_with_type(wmDrag *drag, wmDropBox *drop) { - ID *id = WM_drag_get_local_ID(drag, 0); + ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); RNA_string_set(drop->ptr, "name", id->name + 2); RNA_enum_set(drop->ptr, "type", GS(id->name)); @@ -783,9 +797,12 @@ static void *view3d_main_region_duplicate(void *poin) return NULL; } -static void view3d_main_region_listener( - wmWindow *UNUSED(win), ScrArea *area, ARegion *region, wmNotifier *wmn, const Scene *scene) +static void view3d_main_region_listener(const wmRegionListenerParams *params) { + ScrArea *area = params->area; + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + const Scene *scene = params->scene; View3D *v3d = area->spacedata.first; RegionView3D *rv3d = region->regiondata; wmGizmoMap *gzmap = region->gizmo_map; @@ -1041,14 +1058,13 @@ static void view3d_main_region_listener( } } -static void view3d_main_region_message_subscribe(const struct bContext *C, - struct WorkSpace *UNUSED(workspace), - struct Scene *UNUSED(scene), - struct bScreen *UNUSED(screen), - struct ScrArea *area, - struct ARegion *region, - struct wmMsgBus *mbus) +static void view3d_main_region_message_subscribe(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + const bContext *C = params->context; + ScrArea *area = params->area; + ARegion *region = params->region; + /* Developer note: there are many properties that impact 3D view drawing, * so instead of subscribing to individual properties, just subscribe to types * accepting some redundant redraws. @@ -1163,12 +1179,11 @@ static void view3d_header_region_draw(const bContext *C, ARegion *region) ED_region_header(C, region); } -static void view3d_header_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void view3d_header_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_SCENE: @@ -1233,14 +1248,11 @@ static void view3d_header_region_listener(wmWindow *UNUSED(win), #endif } -static void view3d_header_region_message_subscribe(const struct bContext *UNUSED(C), - struct WorkSpace *UNUSED(workspace), - struct Scene *UNUSED(scene), - struct bScreen *UNUSED(screen), - struct ScrArea *UNUSED(area), - struct ARegion *region, - struct wmMsgBus *mbus) +static void view3d_header_region_message_subscribe(const wmRegionMessageSubscribeParams *params) { + struct wmMsgBus *mbus = params->message_bus; + ARegion *region = params->region; + wmMsgParams_RNA msg_key_params = {{0}}; /* Only subscribe to types. */ @@ -1378,12 +1390,11 @@ static void view3d_buttons_region_layout(const bContext *C, ARegion *region) ED_view3d_buttons_region_layout_ex(C, region, NULL); } -static void view3d_buttons_region_listener(wmWindow *UNUSED(win), - ScrArea *UNUSED(area), - ARegion *region, - wmNotifier *wmn, - const Scene *UNUSED(scene)) +static void view3d_buttons_region_listener(const wmRegionListenerParams *params) { + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + /* context changes */ switch (wmn->category) { case NC_ANIMATION: @@ -1502,11 +1513,10 @@ static void view3d_tools_region_draw(const bContext *C, ARegion *region) } /* area (not region) level listener */ -static void space_view3d_listener(wmWindow *UNUSED(win), - ScrArea *area, - struct wmNotifier *wmn, - Scene *UNUSED(scene)) +static void space_view3d_listener(const wmSpaceTypeListenerParams *params) { + ScrArea *area = params->area; + wmNotifier *wmn = params->notifier; View3D *v3d = area->spacedata.first; /* context changes */ diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 8ae0e3b94fe..af9f5937b4f 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -723,15 +723,24 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region, } if (ca->flag & CAM_SHOW_SAFE_MARGINS) { - UI_draw_safe_areas( - shdr_pos, x1, x2, y1, y2, scene->safe_areas.title, scene->safe_areas.action); + UI_draw_safe_areas(shdr_pos, + &(const rctf){ + .xmin = x1, + .xmax = x2, + .ymin = y1, + .ymax = y2, + }, + scene->safe_areas.title, + scene->safe_areas.action); if (ca->flag & CAM_SHOW_SAFE_CENTER) { UI_draw_safe_areas(shdr_pos, - x1, - x2, - y1, - y2, + &(const rctf){ + .xmin = x1, + .xmax = x2, + .ymin = y1, + .ymax = y2, + }, scene->safe_areas.title_center, scene->safe_areas.action_center); } diff --git a/source/blender/editors/space_view3d/view3d_gizmo_navigate.c b/source/blender/editors/space_view3d/view3d_gizmo_navigate.c index 7a201d8841c..c145497fa09 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_navigate.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate.c @@ -45,14 +45,17 @@ /** \name View3D Navigation Gizmo Group * \{ */ -/* Offset from screen edge. */ -#define GIZMO_OFFSET_FAC 1.2f /* Size of main icon. */ -#define GIZMO_SIZE 80 -/* Factor for size of smaller button. */ -#define GIZMO_MINI_FAC 0.35f -/* How much mini buttons offset from the primary. */ -#define GIZMO_MINI_OFFSET_FAC 0.38f +#define GIZMO_SIZE U.gizmo_size_navigate_v3d + +/* Main gizmo offset from screen edges in unscaled pixels. */ +#define GIZMO_OFFSET 10.0f + +/* Width of smaller buttons in unscaled pixels. */ +#define GIZMO_MINI_SIZE 28.0f + +/* Margin around the smaller buttons. */ +#define GIZMO_MINI_OFFSET 2.0f enum { GZ_INDEX_MOVE = 0, @@ -174,7 +177,7 @@ static void WIDGETGROUP_navigate_setup(const bContext *C, wmGizmoGroup *gzgroup) } /* may be overwritten later */ - gz->scale_basis = (GIZMO_SIZE * GIZMO_MINI_FAC) / 2; + gz->scale_basis = GIZMO_MINI_SIZE / 2.0f; if (info->icon != 0) { PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon"); RNA_property_enum_set(gz->ptr, prop, info->icon); @@ -212,7 +215,7 @@ static void WIDGETGROUP_navigate_setup(const bContext *C, wmGizmoGroup *gzgroup) { wmGizmo *gz = navgroup->gz_array[GZ_INDEX_ROTATE]; - gz->scale_basis = GIZMO_SIZE / 2; + gz->scale_basis = GIZMO_SIZE / 2.0f; const char mapping[6] = { RV3D_VIEW_LEFT, RV3D_VIEW_RIGHT, @@ -263,9 +266,8 @@ static void WIDGETGROUP_navigate_draw_prepare(const bContext *C, wmGizmoGroup *g const bool show_navigate = (U.uiflag & USER_SHOW_GIZMO_NAVIGATE) != 0; const bool show_rotate_gizmo = (U.mini_axis_type == USER_MINI_AXIS_TYPE_GIZMO); - const float icon_size = GIZMO_SIZE; - const float icon_offset = (icon_size * 0.52f) * GIZMO_OFFSET_FAC * UI_DPI_FAC; - const float icon_offset_mini = icon_size * GIZMO_MINI_OFFSET_FAC * UI_DPI_FAC; + const float icon_offset = ((GIZMO_SIZE / 2.0f) + GIZMO_OFFSET) * UI_DPI_FAC; + const float icon_offset_mini = (GIZMO_MINI_SIZE + GIZMO_MINI_OFFSET) * UI_DPI_FAC; const float co_rotate[2] = { rect_visible->xmax - icon_offset, rect_visible->ymax - icon_offset, diff --git a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c index 8f3d40584aa..4ac16e8fbe8 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c @@ -40,6 +40,8 @@ #include "GPU_matrix.h" #include "GPU_state.h" +#include "BLF_api.h" + #include "RNA_access.h" #include "RNA_define.h" @@ -53,169 +55,34 @@ #include "view3d_intern.h" -#define USE_AXIS_FONT -#define USE_FADE_BACKGROUND - -#ifdef USE_AXIS_FONT -# include "BLF_api.h" -#endif - -#define DIAL_RESOLUTION 32 - -/* Sizes of axis spheres containing XYZ characters. */ -#define AXIS_HANDLE_SIZE_FG 0.19f -/* When pointing away from the view. */ -#define AXIS_HANDLE_SIZE_BG 0.15f -/* How far axis handles are away from the center. */ -#define AXIS_HANDLE_OFFSET (1.0f - AXIS_HANDLE_SIZE_FG) - -struct AxisDrawInfo { - /* Matrix is needed for screen-aligned font drawing. */ -#ifdef USE_AXIS_FONT - float matrix_final[4][4]; -#endif -#ifdef USE_FADE_BACKGROUND - float color_bg[3]; -#endif -}; - -#ifndef USE_AXIS_FONT -/** - * \param viewmat_local_unit: is typically the 'rv3d->viewmatob' - * copied into a 3x3 matrix and normalized. - */ -static void draw_xyz_wire( - uint pos_id, const float viewmat_local_unit[3][3], const float c[3], float size, int axis) -{ - int line_type; - float buffer[4][3]; - int n = 0; - - float v1[3] = {0.0f, 0.0f, 0.0f}, v2[3] = {0.0f, 0.0f, 0.0f}; - float dim = size * 0.1f; - float dx[3], dy[3]; - - dx[0] = dim; - dx[1] = 0.0f; - dx[2] = 0.0f; - dy[0] = 0.0f; - dy[1] = dim; - dy[2] = 0.0f; - - switch (axis) { - case 0: /* x axis */ - line_type = GPU_PRIM_LINES; - - /* bottom left to top right */ - negate_v3_v3(v1, dx); - sub_v3_v3(v1, dy); - copy_v3_v3(v2, dx); - add_v3_v3(v2, dy); - - copy_v3_v3(buffer[n++], v1); - copy_v3_v3(buffer[n++], v2); - - /* top left to bottom right */ - mul_v3_fl(dy, 2.0f); - add_v3_v3(v1, dy); - sub_v3_v3(v2, dy); - - copy_v3_v3(buffer[n++], v1); - copy_v3_v3(buffer[n++], v2); - - break; - case 1: /* y axis */ - line_type = GPU_PRIM_LINES; - - /* bottom left to top right */ - mul_v3_fl(dx, 0.75f); - negate_v3_v3(v1, dx); - sub_v3_v3(v1, dy); - copy_v3_v3(v2, dx); - add_v3_v3(v2, dy); - - copy_v3_v3(buffer[n++], v1); - copy_v3_v3(buffer[n++], v2); - - /* top left to center */ - mul_v3_fl(dy, 2.0f); - add_v3_v3(v1, dy); - zero_v3(v2); - - copy_v3_v3(buffer[n++], v1); - copy_v3_v3(buffer[n++], v2); - - break; - case 2: /* z axis */ - line_type = GPU_PRIM_LINE_STRIP; - - /* start at top left */ - negate_v3_v3(v1, dx); - add_v3_v3(v1, dy); - - copy_v3_v3(buffer[n++], v1); - - mul_v3_fl(dx, 2.0f); - add_v3_v3(v1, dx); +/* Radius of the entire background. */ +#define WIDGET_RADIUS ((U.gizmo_size_navigate_v3d / 2.0f) * UI_DPI_FAC) - copy_v3_v3(buffer[n++], v1); +/* Sizes of axis spheres containing XYZ characters in relation to above. */ +#define AXIS_HANDLE_SIZE 0.20f - mul_v3_fl(dy, 2.0f); - sub_v3_v3(v1, dx); - sub_v3_v3(v1, dy); +#define AXIS_LINE_WIDTH ((U.gizmo_size_navigate_v3d / 40.0f) * U.pixelsize) +#define AXIS_RING_WIDTH ((U.gizmo_size_navigate_v3d / 60.0f) * U.pixelsize) +#define AXIS_TEXT_SIZE (WIDGET_RADIUS * AXIS_HANDLE_SIZE * 1.25f) - copy_v3_v3(buffer[n++], v1); +/* distance within this from center is considered positive. */ +#define AXIS_DEPTH_BIAS 0.01f - add_v3_v3(v1, dx); - - copy_v3_v3(buffer[n++], v1); - - break; - default: - BLI_assert(0); - return; - } - - for (int i = 0; i < n; i++) { - mul_transposed_m3_v3((float(*)[3])viewmat_local_unit, buffer[i]); - add_v3_v3(buffer[i], c); - } - - immBegin(line_type, n); - for (int i = 0; i < n; i++) { - immVertex3fv(pos_id, buffer[i]); - } - immEnd(); -} -#endif /* !USE_AXIS_FONT */ - -/** - * \param draw_info: Extra data needed for drawing. - */ -static void axis_geom_draw(const wmGizmo *gz, - const float color[4], - const bool select, - const struct AxisDrawInfo *draw_info) +static void gizmo_axis_draw(const bContext *C, wmGizmo *gz) { - float viewport[4]; - GPU_viewport_size_get_f(viewport); - - GPUVertFormat *format = immVertexFormat(); - const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - struct { float depth; char index; char axis; + char axis_opposite; bool is_pos; } axis_order[6] = { - {-gz->matrix_offset[0][2], 0, 0, false}, - {+gz->matrix_offset[0][2], 1, 0, true}, - {-gz->matrix_offset[1][2], 2, 1, false}, - {+gz->matrix_offset[1][2], 3, 1, true}, - {-gz->matrix_offset[2][2], 4, 2, false}, - {+gz->matrix_offset[2][2], 5, 2, true}, + {-gz->matrix_offset[0][2], 0, 0, 1, false}, + {+gz->matrix_offset[0][2], 1, 0, 0, true}, + {-gz->matrix_offset[1][2], 2, 1, 3, false}, + {+gz->matrix_offset[1][2], 3, 1, 2, true}, + {-gz->matrix_offset[2][2], 4, 2, 5, false}, + {+gz->matrix_offset[2][2], 5, 2, 4, true}, }; int axis_align = -1; @@ -226,25 +93,35 @@ static void axis_geom_draw(const wmGizmo *gz, } } - /* Show backwards pointing highlight on-top (else we can't see it at all). */ - if ((select == false) && (gz->highlight_part > 0) && (axis_align != -1)) { - if (axis_order[gz->highlight_part - 1].is_pos == false) { - axis_order[gz->highlight_part - 1].depth = FLT_MAX; - } - } - qsort(&axis_order, ARRAY_SIZE(axis_order), sizeof(axis_order[0]), BLI_sortutil_cmp_float); - static const float axis_highlight[4] = {1, 1, 1, 1}; - static const float axis_black[4] = {0, 0, 0, 1}; - static float axis_color[3][4]; + /* When the cursor is over any of the gizmos (show circle backdrop). */ + const bool is_active = ((gz->state & WM_GIZMO_STATE_HIGHLIGHT) != 0); - const float axis_depth_bias = 0.01f; - const float sphere_scale = 1.15f; - /* TODO(fclem): Is there a way to get the widget radius? */ - const float widget_pix_size = 40.0f * U.dpi_fac; + /* Background color of the View3D, used to mix colors. */ + float view_color[4]; + ED_view3d_background_color_get(CTX_data_scene(C), CTX_wm_view3d(C), view_color); + view_color[3] = 1.0f; + + float matrix_screen[4][4]; + float matrix_unit[4][4]; + unit_m4(matrix_unit); + WM_gizmo_calc_matrix_final_params(gz, + &((struct WM_GizmoMatrixParams){ + .matrix_offset = matrix_unit, + }), + matrix_screen); + GPU_matrix_push(); + GPU_matrix_mul(matrix_screen); + + GPUVertFormat *format = immVertexFormat(); + const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + const uint color_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + float viewport_size[4]; + GPU_viewport_size_get_f(viewport_size); + + static float axis_color[3][4]; -#ifdef USE_AXIS_FONT struct { float matrix[4][4]; float matrix_m3[3][3]; @@ -252,38 +129,29 @@ static void axis_geom_draw(const wmGizmo *gz, int id; } font; - if (select == false) { - font.id = blf_mono_font; - BLF_disable(font.id, BLF_ROTATION | BLF_SHADOW | BLF_MATRIX | BLF_ASPECT | BLF_WORD_WRAP); - BLF_color4fv(font.id, axis_black); - BLF_size(font.id, 12 * U.dpi_fac, 72); - - /* The view matrix is used to position the text. */ - BLF_position(font.id, 0, 0, 0); - - /* Calculate the inverse of the (matrix_final * matrix_offset). - * This allows us to use the final location, while reversing the rotation so fonts - * show without any rotation. */ - float m3[3][3]; - float m3_offset[3][3]; - copy_m3_m4(m3, draw_info->matrix_final); - copy_m3_m4(m3_offset, gz->matrix_offset); - mul_m3_m3m3(m3, m3, m3_offset); - copy_m3_m3(font.matrix_m3_invert, m3); - invert_m3(m3); - copy_m3_m3(font.matrix_m3, m3); - copy_m4_m3(font.matrix, m3); - } -#endif - - /* When the cursor is over any of the gizmos (show circle backdrop). */ - const bool is_active = (color[3] != 0.0f); - - const float clip_range = gz->scale_final * sphere_scale; - bool use_project_matrix = (clip_range >= -GPU_MATRIX_ORTHO_CLIP_NEAR_DEFAULT); + font.id = BLF_default(); + BLF_disable(font.id, BLF_ROTATION | BLF_SHADOW | BLF_MATRIX | BLF_ASPECT | BLF_WORD_WRAP); + BLF_enable(font.id, BLF_BOLD); + BLF_size(font.id, AXIS_TEXT_SIZE, 72); + BLF_position(font.id, 0, 0, 0); + + /* Calculate the inverse of the (matrix_final * matrix_offset). + * This allows us to use the final location, while reversing the rotation so fonts + * show without any rotation. */ + float m3[3][3]; + float m3_offset[3][3]; + copy_m3_m4(m3, matrix_screen); + copy_m3_m4(m3_offset, gz->matrix_offset); + mul_m3_m3m3(m3, m3, m3_offset); + copy_m3_m3(font.matrix_m3_invert, m3); + invert_m3(m3); + copy_m3_m3(font.matrix_m3, m3); + copy_m4_m3(font.matrix, m3); + + bool use_project_matrix = (gz->scale_final >= -GPU_MATRIX_ORTHO_CLIP_NEAR_DEFAULT); if (use_project_matrix) { GPU_matrix_push_projection(); - GPU_matrix_ortho_set_z(-clip_range, clip_range); + GPU_matrix_ortho_set_z(-gz->scale_final, gz->scale_final); } UI_draw_roundbox_corner_set(UI_CNR_ALL); @@ -291,263 +159,161 @@ static void axis_geom_draw(const wmGizmo *gz, /* Circle defining active area. */ if (is_active) { - immUnbindProgram(); - - float rad = widget_pix_size; + const float rad = WIDGET_RADIUS; GPU_matrix_push(); GPU_matrix_scale_1f(1.0f / rad); - - UI_draw_roundbox_4fv(true, -rad, -rad, rad, rad, rad, color); - + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = -rad, + .xmax = rad, + .ymin = -rad, + .ymax = rad, + }, + true, + rad, + gz->color_hi); GPU_matrix_pop(); - - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); } - GPU_matrix_push(); GPU_matrix_mul(gz->matrix_offset); for (int axis_index = 0; axis_index < ARRAY_SIZE(axis_order); axis_index++) { const int index = axis_order[axis_index].index; const int axis = axis_order[axis_index].axis; const bool is_pos = axis_order[axis_index].is_pos; - const bool is_highlight = index + 1 == gz->highlight_part; + const float depth = axis_order[axis_index].depth; + const bool is_behind = (depth <= (AXIS_DEPTH_BIAS * (is_pos ? -1 : 1))); + bool is_aligned_front = (axis_align != -1 && axis_align == axis && !is_behind); + bool is_aligned_back = (axis_align != -1 && axis_align == axis && is_behind); + + const float v[3] = {0, 0, (1.0f - AXIS_HANDLE_SIZE) * (is_pos ? 1 : -1)}; + const float v_final[3] = {v[(axis + 2) % 3], v[(axis + 1) % 3], v[axis]}; + + bool is_highlight = index + 1 == gz->highlight_part; + /* Check if highlight part is the other side when axis aligned. */ + if (is_aligned_front && (axis_order[axis_index].axis_opposite + 1 == gz->highlight_part)) { + is_highlight = true; + } UI_GetThemeColor3fv(TH_AXIS_X + axis, axis_color[axis]); axis_color[axis][3] = 1.0f; - const int index_z = axis; - const int index_y = (axis + 1) % 3; - const int index_x = (axis + 2) % 3; - - bool ok = true; - - /* Skip view align axis when selecting (allows to switch to opposite side). */ - if (select && ((axis_align == axis) && (gz->matrix_offset[axis][2] > 0.0f) == is_pos)) { - ok = false; + /* Color that is full at front, but 50% view background when in back. */ + float fading_color[4]; + interp_v4_v4v4(fading_color, view_color, axis_color[axis], ((depth + 1) * 0.25) + 0.5); + + /* Color that is midway between front and back. */ + float middle_color[4]; + interp_v4_v4v4(middle_color, view_color, axis_color[axis], 0.75f); + + GPU_blend(GPU_BLEND_ALPHA); + + /* Axis Line. */ + if (is_pos || axis_align != -1) { + + /* Extend slightly to meet better at the center. */ + float v_start[3] = {0.0f, 0.0f, 0.0f}; + mul_v3_v3fl(v_start, v_final, -(AXIS_LINE_WIDTH / WIDGET_RADIUS * 0.66f)); + + /* Decrease length of line by ball radius. */ + float v_end[3] = {0.0f, 0.0f, 0.0f}; + mul_v3_v3fl(v_end, v_final, 1.0f - AXIS_HANDLE_SIZE); + + immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR); + immUniform2fv("viewportSize", &viewport_size[2]); + immUniform1f("lineWidth", AXIS_LINE_WIDTH); + immBegin(GPU_PRIM_LINES, 2); + immAttr4fv(color_id, middle_color); + immVertex3fv(pos_id, v_start); + immAttr4fv(color_id, fading_color); + immVertex3fv(pos_id, v_end); + immEnd(); + immUnbindProgram(); } - if (ok) { - /* Check aligned, since the front axis won't display in this case, - * and we want to make sure all 3 axes have a character at all times. */ - const bool show_axis_char = (is_pos || (axis == axis_align)); - const float v[3] = {0, 0, AXIS_HANDLE_OFFSET * (is_pos ? 1 : -1)}; - const float v_final[3] = {v[index_x], v[index_y], v[index_z]}; - const float *color_current = is_highlight ? axis_highlight : axis_color[axis]; - float color_current_fade[4]; - - /* Flip the faded state when axis aligned, since we're hiding the front-mode axis - * otherwise we see the color for the back-most axis, which is useful for - * click-to-rotate 180d but not useful to visualize. - * - * Use depth bias so axis-aligned views show the positive axis as being in-front. - * This is a detail so primary axes show as dominant. - */ - const bool is_pos_color = (axis_order[axis_index].depth > - (axis_depth_bias * (is_pos ? -1 : 1))); - - if (select == false) { -#ifdef USE_FADE_BACKGROUND - interp_v3_v3v3( - color_current_fade, draw_info->color_bg, color_current, is_highlight ? 1.0 : 0.5f); - color_current_fade[3] = color_current[3]; -#else - copy_v4_v4(color_current_fade, color_current); - color_current_fade[3] *= 0.2; -#endif - } - else { - copy_v4_fl(color_current_fade, 1.0f); - } - - /* Axis Line. */ - if (is_pos) { - float v_start[3]; - immUnbindProgram(); - - GPU_blend(GPU_BLEND_ALPHA); - - immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); - immUniform2fv("viewportSize", &viewport[2]); - immUniform1f("lineWidth", 2.0f * U.pixelsize); - immUniformColor4fv(is_pos_color ? color_current : color_current_fade); - immBegin(GPU_PRIM_LINES, 2); - if (axis_align == -1) { - zero_v3(v_start); + /* Axis Ball. */ + if (!is_aligned_back) { + float *inner_color = fading_color; + float *outline_color = fading_color; + float negative_color[4]; + if (!is_pos) { + if (is_aligned_front) { + interp_v4_v4v4( + negative_color, (float[4]){1.0f, 1.0f, 1.0f, 1.0f}, axis_color[axis], 0.5f); + negative_color[3] = MIN2(depth + 1, 1.0f); + outline_color = negative_color; } else { - /* When axis aligned we don't draw the front most axis - * (allowing us to switch to the opposite side). - * In this case don't draw lines over axis pointing away from us - * because it obscures character and looks noisy. - */ - mul_v3_v3fl(v_start, v_final, 0.3f); + interp_v4_v4v4(negative_color, view_color, axis_color[axis], 0.25f); + negative_color[3] = MIN2(depth + 1, 1.0f); + inner_color = negative_color; } - immVertex3fv(pos_id, v_start); - immVertex3fv(pos_id, v_final); - immEnd(); - - immUnbindProgram(); - - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); } - /* Axis Ball. */ -#ifdef USE_AXIS_FONT - if (select == false) { - immUnbindProgram(); - - GPU_matrix_push(); - GPU_matrix_translate_3fv(v_final); - GPU_matrix_mul(font.matrix); - - float rad = widget_pix_size * (is_pos ? AXIS_HANDLE_SIZE_FG : AXIS_HANDLE_SIZE_BG); - - /* Black outlines for negative axis balls, otherwise they can be hard to see since - * they use a faded color which can be similar to the circle backdrop in tone. */ - if (is_active && !is_highlight && !is_pos && !select && !(axis_align == axis)) { - static const float axis_black_faded[4] = {0.0f, 0.0f, 0.0f, 0.2f}; - float outline = rad * sphere_scale; - UI_draw_roundbox_4fv( - true, -outline, -outline, outline, outline, outline, axis_black_faded); - } - - const float *col = is_pos_color ? color_current : color_current_fade; - UI_draw_roundbox_4fv(true, -rad, -rad, rad, rad, rad, col); - - GPU_matrix_pop(); - - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - } - else -#endif - { - GPU_matrix_push(); - GPU_matrix_translate_3fv(v_final); - GPU_matrix_scale_1f(is_pos ? AXIS_HANDLE_SIZE_FG : AXIS_HANDLE_SIZE_BG); - - GPUBatch *sphere = GPU_batch_preset_sphere(0); - GPU_batch_program_set_builtin(sphere, GPU_SHADER_3D_UNIFORM_COLOR); - - /* Black outlines for negative axis balls, otherwise they can be hard to see since - * they use a faded color which can be similar to the circle backdrop in tone. */ - if (is_active && !is_highlight && !is_pos && !select && !(axis_align == axis)) { - static const float axis_black_faded[4] = {0, 0, 0, 0.2f}; - GPU_matrix_scale_1f(sphere_scale); - GPU_batch_uniform_4fv(sphere, "color", axis_black_faded); - GPU_batch_draw(sphere); - GPU_matrix_scale_1f(1.0 / sphere_scale); - } + GPU_matrix_push(); + GPU_matrix_translate_3fv(v_final); + GPU_matrix_mul(font.matrix); + /* Size change from back to front: 0.92f - 1.08f. */ + float scale = ((depth + 1) * 0.08f) + 0.92f; + const float rad = WIDGET_RADIUS * AXIS_HANDLE_SIZE * scale; + UI_draw_roundbox_4fv_ex( + &(const rctf){ + .xmin = -rad, + .xmax = rad, + .ymin = -rad, + .ymax = rad, + }, + inner_color, + NULL, + 0.0f, + outline_color, + AXIS_RING_WIDTH, + rad); + GPU_matrix_pop(); + } - GPU_batch_program_set_builtin(sphere, GPU_SHADER_3D_UNIFORM_COLOR); - GPU_batch_uniform_4fv(sphere, "color", is_pos_color ? color_current : color_current_fade); - GPU_batch_draw(sphere); - GPU_matrix_pop(); + /* Axis XYZ Character. */ + if ((is_pos || is_highlight || (axis == axis_align)) && !is_aligned_back) { + float axis_str_width, axis_string_height; + char axis_str[3] = {'X' + axis, 0, 0}; + if (!is_pos) { + axis_str[0] = '-'; + axis_str[1] = 'X' + axis; } - - /* Axis XYZ Character. */ - if (show_axis_char && (select == false)) { -#ifdef USE_AXIS_FONT - float axis_str_size[2] = {0}; - const char axis_str[2] = {'X' + axis, 0}; - BLF_width_and_height(font.id, axis_str, 2, &axis_str_size[0], &axis_str_size[1]); - - /* Calculate pixel aligned location, without this text draws fuzzy. */ - float v_final_px[3]; - mul_v3_m3v3(v_final_px, font.matrix_m3_invert, v_final); - /* Center the test and pixel align, it's important to round once - * otherwise the characters are noticeably not-centered. - * If this wasn't an issue we could use #BLF_position to place the text. */ - v_final_px[0] = roundf(v_final_px[0] - (axis_str_size[0] / 2.0f)); - v_final_px[1] = roundf(v_final_px[1] - (axis_str_size[1] / 2.0f)); - mul_m3_v3(font.matrix_m3, v_final_px); - - immUnbindProgram(); - - GPU_matrix_push(); - GPU_matrix_translate_3fv(v_final_px); - GPU_matrix_mul(font.matrix); - - BLF_draw_ascii(font.id, axis_str, 2); - GPU_blend(GPU_BLEND_ALPHA); /* XXX, blf disables */ - GPU_matrix_pop(); - - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); -#else - immUnbindProgram(); - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - GPU_line_width(1.0f); - float m3[3][3]; - copy_m3_m4(m3, gz->matrix_offset); - immUniformColor4fv(axis_black); - draw_xyz_wire(pos_id, m3, v_final, 1.0, axis); - immUnbindProgram(); -#endif + BLF_width_and_height(font.id, axis_str, 3, &axis_str_width, &axis_string_height); + + /* Calculate pixel-aligned location, without this text draws fuzzy. */ + float v_final_px[3]; + mul_v3_m3v3(v_final_px, font.matrix_m3_invert, v_final); + /* Center the text and pixel align, it's important to round once + * otherwise the characters are noticeably not-centered. + * If this wasn't an issue we could use #BLF_position to place the text. */ + v_final_px[0] = roundf(v_final_px[0] - (axis_str_width * (is_pos ? 0.5f : 0.55f))); + v_final_px[1] = roundf(v_final_px[1] - (axis_string_height / 2.0f)); + mul_m3_v3(font.matrix_m3, v_final_px); + GPU_matrix_push(); + GPU_matrix_translate_3fv(v_final_px); + GPU_matrix_mul(font.matrix); + float text_color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + if (!is_highlight) { + zero_v4(text_color); + text_color[3] = is_active ? 1.0f : 0.9f; } + BLF_color4fv(font.id, text_color); + BLF_draw_ascii(font.id, axis_str, 2); + GPU_matrix_pop(); } } - GPU_matrix_pop(); - immUnbindProgram(); - if (use_project_matrix) { GPU_matrix_pop_projection(); } -} - -static void axis3d_draw_intern(const bContext *C, - wmGizmo *gz, - const bool select, - const bool highlight) -{ - const float *color = highlight ? gz->color_hi : gz->color; - float matrix_final[4][4]; - float matrix_unit[4][4]; - - unit_m4(matrix_unit); - - WM_gizmo_calc_matrix_final_params(gz, - &((struct WM_GizmoMatrixParams){ - .matrix_offset = matrix_unit, - }), - matrix_final); - - GPU_matrix_push(); - GPU_matrix_mul(matrix_final); - - struct AxisDrawInfo draw_info; -#ifdef USE_AXIS_FONT - if (select == false) { - copy_m4_m4(draw_info.matrix_final, matrix_final); - } -#endif -#ifdef USE_FADE_BACKGROUND - if (select == false) { - ED_view3d_background_color_get(CTX_data_scene(C), CTX_wm_view3d(C), draw_info.color_bg); - } -#else - UNUSED_VARS(C); -#endif - - GPU_blend(GPU_BLEND_ALPHA); - axis_geom_draw(gz, color, select, &draw_info); GPU_blend(GPU_BLEND_NONE); + BLF_disable(font.id, BLF_BOLD); GPU_matrix_pop(); } -static void gizmo_axis_draw(const bContext *C, wmGizmo *gz) -{ - const bool is_modal = gz->state & WM_GIZMO_STATE_MODAL; - const bool is_highlight = (gz->state & WM_GIZMO_STATE_HIGHLIGHT) != 0; - - (void)is_modal; - - GPU_blend(GPU_BLEND_ALPHA); - axis3d_draw_intern(C, gz, false, is_highlight); - GPU_blend(GPU_BLEND_NONE); -} - static int gizmo_axis_test_select(bContext *UNUSED(C), wmGizmo *gz, const int mval[2]) { float point_local[2] = {UNPACK2(mval)}; @@ -601,12 +367,19 @@ static int gizmo_axis_test_select(bContext *UNUSED(C), wmGizmo *gz, const int mv return -1; } -static int gizmo_axis_cursor_get(wmGizmo *gz) +static int gizmo_axis_cursor_get(wmGizmo *UNUSED(gz)) { - if (gz->highlight_part > 0) { - return WM_CURSOR_EDIT; - } - return WM_CURSOR_NSEW_SCROLL; + return WM_CURSOR_DEFAULT; +} + +static void gizmo_axis_screen_bounds_get(bContext *C, wmGizmo *gz, rcti *r_bounding_box) +{ + ScrArea *area = CTX_wm_area(C); + const float rad = WIDGET_RADIUS; + r_bounding_box->xmin = gz->matrix_basis[3][0] + area->totrct.xmin - rad; + r_bounding_box->ymin = gz->matrix_basis[3][1] + area->totrct.ymin - rad; + r_bounding_box->xmax = r_bounding_box->xmin + rad; + r_bounding_box->ymax = r_bounding_box->ymin + rad; } void VIEW3D_GT_navigate_rotate(wmGizmoType *gzt) @@ -618,6 +391,7 @@ void VIEW3D_GT_navigate_rotate(wmGizmoType *gzt) gzt->draw = gizmo_axis_draw; gzt->test_select = gizmo_axis_test_select; gzt->cursor_get = gizmo_axis_cursor_get; + gzt->screen_bounds_get = gizmo_axis_screen_bounds_get; gzt->struct_size = sizeof(wmGizmo); } diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 35ec4de5077..9ee6e44f825 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -2494,8 +2494,7 @@ static int view3d_select_exec(bContext *C, wmOperator *op) } /* Pass-through allows tweaks - * FINISHED to signal one operator worked - * */ + * FINISHED to signal one operator worked. */ if (retval) { WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 2b7b8255068..a6a77ecd5f7 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -224,13 +224,11 @@ void view3d_region_operator_needs_opengl(wmWindow *UNUSED(win), ARegion *region) */ void ED_view3d_polygon_offset(const RegionView3D *rv3d, const float dist) { - float viewdist; - if (rv3d->rflag & RV3D_ZOFFSET_DISABLED) { return; } - viewdist = rv3d->dist; + float viewdist = rv3d->dist; /* special exception for ortho camera (viewdist isnt used for perspective cameras) */ if (dist != 0.0f) { @@ -248,7 +246,6 @@ bool ED_view3d_context_activate(bContext *C) { bScreen *screen = CTX_wm_screen(C); ScrArea *area = CTX_wm_area(C); - ARegion *region; /* area can be NULL when called from python */ if (area == NULL || area->spacetype != SPACE_VIEW3D) { @@ -259,7 +256,7 @@ bool ED_view3d_context_activate(bContext *C) return false; } - region = BKE_area_find_region_active_win(area); + ARegion *region = BKE_area_find_region_active_win(area); if (region == NULL) { return false; } @@ -282,9 +279,7 @@ void ED_view3d_clipping_calc_from_boundbox(float clip[4][4], const BoundBox *bb, const bool is_flip) { - int val; - - for (val = 0; val < 4; val++) { + for (int val = 0; val < 4; val++) { normal_tri_v3(clip[val], bb->vec[val], bb->vec[val == 3 ? 0 : val + 1], bb->vec[val + 4]); if (UNLIKELY(is_flip)) { negate_v3(clip[val]); @@ -752,14 +747,12 @@ bool ED_view3d_camera_lock_autokey(View3D *v3d, static void view3d_boxview_clip(ScrArea *area) { - ARegion *region; BoundBox *bb = MEM_callocN(sizeof(BoundBox), "clipbb"); float clip[6][4]; float x1 = 0.0f, y1 = 0.0f, z1 = 0.0f, ofs[3] = {0.0f, 0.0f, 0.0f}; - int val; /* create bounding box */ - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d = region->regiondata; @@ -794,7 +787,7 @@ static void view3d_boxview_clip(ScrArea *area) } } - for (val = 0; val < 8; val++) { + for (int val = 0; val < 8; val++) { if (ELEM(val, 0, 3, 4, 7)) { bb->vec[val][0] = -x1 - ofs[0]; } @@ -826,12 +819,12 @@ static void view3d_boxview_clip(ScrArea *area) normal_tri_v3(clip[5], bb->vec[0], bb->vec[2], bb->vec[1]); /* then plane equations */ - for (val = 0; val < 6; val++) { + for (int val = 0; val < 6; val++) { clip[val][3] = -dot_v3v3(clip[val], bb->vec[val % 5]); } /* create bounding box */ - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d = region->regiondata; @@ -950,11 +943,10 @@ void ED_view3d_quadview_update(ScrArea *area, ARegion *region, bool do_clip) { ARegion *region_sync = NULL; RegionView3D *rv3d = region->regiondata; - short viewlock; /* this function copies flags from the first of the 3 other quadview * regions to the 2 other, so it assumes this is the region whose * properties are always being edited, weak */ - viewlock = rv3d->viewlock; + short viewlock = rv3d->viewlock; if ((viewlock & RV3D_LOCK_ROTATION) == 0) { do_clip = (viewlock & RV3D_BOXCLIP) != 0; @@ -1015,10 +1007,7 @@ void ED_view3d_quadview_update(ScrArea *area, ARegion *region, bool do_clip) static float view_autodist_depth_margin(ARegion *region, const int mval[2], int margin) { - ViewDepths depth_temp = {0}; rcti rect; - float depth_close; - if (margin == 0) { /* Get Z Depths, needed for perspective, nice for ortho */ rect.xmin = mval[0]; @@ -1030,8 +1019,9 @@ static float view_autodist_depth_margin(ARegion *region, const int mval[2], int BLI_rcti_init_pt_radius(&rect, mval, margin); } + ViewDepths depth_temp = {0}; view3d_update_depths_rect(region, &depth_temp, &rect); - depth_close = view3d_depth_near(&depth_temp); + float depth_close = view3d_depth_near(&depth_temp); MEM_SAFE_FREE(depth_temp.depths); return depth_close; } @@ -1053,14 +1043,13 @@ bool ED_view3d_autodist(Depsgraph *depsgraph, { float depth_close; int margin_arr[] = {0, 2, 4}; - int i; bool depth_ok = false; /* Get Z Depths, needed for perspective, nice for ortho */ ED_view3d_draw_depth(depsgraph, region, v3d, alphaoverride); /* Attempt with low margin's first */ - i = 0; + int i = 0; do { depth_close = view_autodist_depth_margin(region, mval, margin_arr[i++] * U.pixelsize); depth_ok = (depth_close != FLT_MAX); @@ -1104,9 +1093,8 @@ bool ED_view3d_autodist_simple(ARegion *region, int margin, const float *force_depth) { - float depth; - /* Get Z Depths, needed for perspective, nice for ortho */ + float depth; if (force_depth) { depth = *force_depth; } @@ -1237,7 +1225,6 @@ float ED_view3d_radius_to_dist(const View3D *v3d, } else { float lens, sensor_size, zoom; - float angle; if (persp == RV3D_CAMOB) { CameraParams params; @@ -1259,7 +1246,7 @@ float ED_view3d_radius_to_dist(const View3D *v3d, zoom = CAMERA_PARAM_ZOOM_INIT_PERSP; } - angle = focallength_to_fov(lens, sensor_size); + float angle = focallength_to_fov(lens, sensor_size); /* zoom influences lens, correct this by scaling the angle as a distance * (by the zoom-level) */ @@ -1319,14 +1306,13 @@ float ED_view3d_offset_distance(const float mat[4][4], { float pos[4] = {0.0f, 0.0f, 0.0f, 1.0f}; float dir[4] = {0.0f, 0.0f, 1.0f, 0.0f}; - float dist; mul_m4_v4(mat, pos); add_v3_v3(pos, ofs); mul_m4_v4(mat, dir); normalize_v3(dir); - dist = dot_v3v3(pos, dir); + float dist = dot_v3v3(pos, dir); if ((dist < FLT_EPSILON) && (fallback_dist != 0.0f)) { dist = fallback_dist; diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 54533bf43e5..5abe92b385a 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -675,7 +675,7 @@ void setAxisMatrixConstraint(TransInfo *t, int mode, const char text[]) void setLocalConstraint(TransInfo *t, int mode, const char text[]) { - if (t->flag & T_EDIT) { + if ((t->flag & T_EDIT) || t->data_len_all == 1) { /* Although in edit-mode each object has its local space, use the * orientation of the active object. */ setConstraint(t, mode, text); @@ -703,34 +703,25 @@ void setUserConstraint(TransInfo *t, int mode, const char ftext[]) const char *spacename = transform_orientations_spacename_get(t, orientation); BLI_snprintf(text, sizeof(text), ftext, spacename); - if (t->modifiers & (MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE)) { - /* Force the orientation of the active object. - * Although possible, it is not convenient to use the local or axis constraint - * with the modifier to select constraint. - * This also follows the convention of older versions. */ - setConstraint(t, mode, text); - } - else { - switch (orientation) { - case V3D_ORIENT_LOCAL: - setLocalConstraint(t, mode, text); - break; - case V3D_ORIENT_NORMAL: - if (checkUseAxisMatrix(t)) { - setAxisMatrixConstraint(t, mode, text); - break; - } - ATTR_FALLTHROUGH; - case V3D_ORIENT_GLOBAL: - case V3D_ORIENT_VIEW: - case V3D_ORIENT_CURSOR: - case V3D_ORIENT_GIMBAL: - case V3D_ORIENT_CUSTOM_MATRIX: - case V3D_ORIENT_CUSTOM: - default: { - setConstraint(t, mode, text); + switch (orientation) { + case V3D_ORIENT_LOCAL: + setLocalConstraint(t, mode, text); + break; + case V3D_ORIENT_NORMAL: + if (checkUseAxisMatrix(t)) { + setAxisMatrixConstraint(t, mode, text); break; } + ATTR_FALLTHROUGH; + case V3D_ORIENT_GLOBAL: + case V3D_ORIENT_VIEW: + case V3D_ORIENT_CURSOR: + case V3D_ORIENT_GIMBAL: + case V3D_ORIENT_CUSTOM_MATRIX: + case V3D_ORIENT_CUSTOM: + default: { + setConstraint(t, mode, text); + break; } } t->con.mode |= CON_USER; diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c index 0a742ec4470..17362ae65e5 100644 --- a/source/blender/editors/transform/transform_convert_gpencil.c +++ b/source/blender/editors/transform/transform_convert_gpencil.c @@ -135,7 +135,7 @@ static void createTransGPencil_curves(bContext *C, continue; } /* Check if the color is editable. */ - if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) { continue; } /* Check if stroke has an editcurve */ @@ -242,7 +242,7 @@ static void createTransGPencil_curves(bContext *C, } /* Calculate difference matrix. */ - BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); copy_m3_m4(mtx, diff_mat); pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); @@ -263,7 +263,7 @@ static void createTransGPencil_curves(bContext *C, continue; } /* Check if the color is editable. */ - if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) { continue; } /* Check if stroke has an editcurve */ @@ -436,7 +436,7 @@ static void createTransGPencil_strokes(bContext *C, continue; } /* Check if the color is editable. */ - if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) { continue; } @@ -507,7 +507,7 @@ static void createTransGPencil_strokes(bContext *C, } /* Calculate difference matrix. */ - BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); /* Undo matrix. */ invert_m4_m4(inverse_diff_mat, diff_mat); @@ -551,7 +551,7 @@ static void createTransGPencil_strokes(bContext *C, continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) { + if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) { continue; } /* What we need to include depends on proportional editing settings... */ diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index b3bd6b31879..ce74c5f5a36 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -251,29 +251,55 @@ void transform_convert_mesh_islanddata_free(struct TransIslandData *island_data) * * \{ */ -static bool bmesh_test_dist_add(BMVert *v, - BMVert *v_other, +/* Propagate distance from v1 and v2 to v0. */ +static bool bmesh_test_dist_add(BMVert *v0, + BMVert *v1, + BMVert *v2, float *dists, - const float *dists_prev, /* optionally track original index */ int *index, - const int *index_prev, const float mtx[3][3]) { - if ((BM_elem_flag_test(v_other, BM_ELEM_SELECT) == 0) && - (BM_elem_flag_test(v_other, BM_ELEM_HIDDEN) == 0)) { - const int i = BM_elem_index_get(v); - const int i_other = BM_elem_index_get(v_other); - float vec[3]; - float dist_other; - sub_v3_v3v3(vec, v->co, v_other->co); - mul_m3_v3(mtx, vec); - - dist_other = dists_prev[i] + len_v3(vec); - if (dist_other < dists[i_other]) { - dists[i_other] = dist_other; + if ((BM_elem_flag_test(v0, BM_ELEM_SELECT) == 0) && + (BM_elem_flag_test(v0, BM_ELEM_HIDDEN) == 0)) { + const int i0 = BM_elem_index_get(v0); + const int i1 = BM_elem_index_get(v1); + + BLI_assert(dists[i1] != FLT_MAX); + if (dists[i0] <= dists[i1]) { + return false; + } + + float dist0; + + if (v2) { + /* Distance across triangle. */ + const int i2 = BM_elem_index_get(v2); + BLI_assert(dists[i2] != FLT_MAX); + if (dists[i0] <= dists[i2]) { + return false; + } + + float vm0[3], vm1[3], vm2[3]; + mul_v3_m3v3(vm0, mtx, v0->co); + mul_v3_m3v3(vm1, mtx, v1->co); + mul_v3_m3v3(vm2, mtx, v2->co); + + dist0 = geodesic_distance_propagate_across_triangle(vm0, vm1, vm2, dists[i1], dists[i2]); + } + else { + /* Distance along edge. */ + float vec[3]; + sub_v3_v3v3(vec, v1->co, v0->co); + mul_m3_v3(mtx, vec); + + dist0 = dists[i1] + len_v3(vec); + } + + if (dist0 < dists[i0]) { + dists[i0] = dist0; if (index != NULL) { - index[i_other] = index_prev[i]; + index[i0] = index[i1]; } return true; } @@ -292,15 +318,16 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm, float *dists, int *index) { - BLI_LINKSTACK_DECLARE(queue, BMVert *); + BLI_LINKSTACK_DECLARE(queue, BMEdge *); - /* any BM_ELEM_TAG'd vertex is in 'queue_next', so we don't add in twice */ - BLI_LINKSTACK_DECLARE(queue_next, BMVert *); + /* any BM_ELEM_TAG'd edge is in 'queue_next', so we don't add in twice */ + BLI_LINKSTACK_DECLARE(queue_next, BMEdge *); BLI_LINKSTACK_INIT(queue); BLI_LINKSTACK_INIT(queue_next); { + /* Set indexes and initial distances for selected vertices. */ BMIter viter; BMVert *v; int i; @@ -308,7 +335,6 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm, BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) { float dist; BM_elem_index_set(v, i); /* set_inline */ - BM_elem_flag_disable(v, BM_ELEM_TAG); if (BM_elem_flag_test(v, BM_ELEM_SELECT) == 0 || BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { dist = FLT_MAX; @@ -317,7 +343,6 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm, } } else { - BLI_LINKSTACK_PUSH(queue, v); dist = 0.0f; if (index != NULL) { index[i] = i; @@ -329,103 +354,99 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm, bm->elem_index_dirty &= ~BM_VERT; } - /* need to be very careful of feedback loops here, store previous dist's to avoid feedback */ - float *dists_prev = MEM_dupallocN(dists); - int *index_prev = MEM_dupallocN(index); /* may be NULL */ + { + /* Add edges with at least one selected vertex to the queue. */ + BMIter eiter; + BMEdge *e; + + BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) { + BMVert *v1 = e->v1; + BMVert *v2 = e->v2; + int i1 = BM_elem_index_get(v1); + int i2 = BM_elem_index_get(v2); + + if (dists[i1] != FLT_MAX || dists[i2] != FLT_MAX) { + BLI_LINKSTACK_PUSH(queue, e); + } + BM_elem_flag_disable(e, BM_ELEM_TAG); + } + } do { - BMVert *v; - LinkNode *lnk; - - /* this is correct but slow to do each iteration, - * instead sync the dist's while clearing BM_ELEM_TAG (below) */ -#if 0 - memcpy(dists_prev, dists, sizeof(float) * bm->totvert); -#endif - - while ((v = BLI_LINKSTACK_POP(queue))) { - BLI_assert(dists[BM_elem_index_get(v)] != FLT_MAX); - - /* connected edge-verts */ - if (v->e != NULL) { - BMEdge *e_iter, *e_first; - - e_iter = e_first = v->e; - - /* would normally use BM_EDGES_OF_VERT, but this runs so often, - * its faster to iterate on the data directly */ - do { - - if (BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN) == 0) { + BMEdge *e; + + while ((e = BLI_LINKSTACK_POP(queue))) { + BMVert *v1 = e->v1; + BMVert *v2 = e->v2; + int i1 = BM_elem_index_get(v1); + int i2 = BM_elem_index_get(v2); + + if (e->l == NULL || (dists[i1] == FLT_MAX || dists[i2] == FLT_MAX)) { + /* Propagate along edge from vertex with smallest to largest distance. */ + if (dists[i1] > dists[i2]) { + SWAP(int, i1, i2); + SWAP(BMVert *, v1, v2); + } - /* edge distance */ - { - BMVert *v_other = BM_edge_other_vert(e_iter, v); - if (bmesh_test_dist_add(v, v_other, dists, dists_prev, index, index_prev, mtx)) { - if (BM_elem_flag_test(v_other, BM_ELEM_TAG) == 0) { - BM_elem_flag_enable(v_other, BM_ELEM_TAG); - BLI_LINKSTACK_PUSH(queue_next, v_other); - } - } + if (bmesh_test_dist_add(v2, v1, NULL, dists, index, mtx)) { + /* Add adjacent loose edges to the queue, or all edges if this is a loose edge. + * Other edges are handled by propagation across edges below. */ + BMEdge *e_other; + BMIter eiter; + BM_ITER_ELEM (e_other, &eiter, v2, BM_EDGES_OF_VERT) { + if (e_other != e && BM_elem_flag_test(e_other, BM_ELEM_TAG) == 0 && + (e->l == NULL || e_other->l == NULL)) { + BM_elem_flag_enable(e_other, BM_ELEM_TAG); + BLI_LINKSTACK_PUSH(queue_next, e_other); } + } + } + } - /* face distance */ - if (e_iter->l) { - BMLoop *l_iter_radial, *l_first_radial; - /** - * imaginary edge diagonally across quad. - * \note This takes advantage of the rules of winding that we - * know 2 or more of a verts edges wont reference the same face twice. - * Also, if the edge is hidden, the face will be hidden too. - */ - l_iter_radial = l_first_radial = e_iter->l; - - do { - if ((l_iter_radial->v == v) && (l_iter_radial->f->len == 4) && - (BM_elem_flag_test(l_iter_radial->f, BM_ELEM_HIDDEN) == 0)) { - BMVert *v_other = l_iter_radial->next->next->v; - if (bmesh_test_dist_add(v, v_other, dists, dists_prev, index, index_prev, mtx)) { - if (BM_elem_flag_test(v_other, BM_ELEM_TAG) == 0) { - BM_elem_flag_enable(v_other, BM_ELEM_TAG); - BLI_LINKSTACK_PUSH(queue_next, v_other); - } - } + if (e->l != NULL) { + /* Propagate across edge to vertices in adjacent faces. */ + BMLoop *l; + BMIter liter; + BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) { + for (BMLoop *l_other = l->next->next; l_other != l; l_other = l_other->next) { + BMVert *v_other = l_other->v; + BLI_assert(!ELEM(v_other, v1, v2)); + + if (bmesh_test_dist_add(v_other, v1, v2, dists, index, mtx)) { + /* Add adjacent edges to the queue, if they are ready to propagate across/along. + * Always propagate along loose edges, and for other edges only propagate across + * if both vertices have a known distances. */ + BMEdge *e_other; + BMIter eiter; + BM_ITER_ELEM (e_other, &eiter, v_other, BM_EDGES_OF_VERT) { + if (e_other != e && BM_elem_flag_test(e_other, BM_ELEM_TAG) == 0 && + (e_other->l == NULL || + dists[BM_elem_index_get(BM_edge_other_vert(e_other, v_other))] != FLT_MAX)) { + BM_elem_flag_enable(e_other, BM_ELEM_TAG); + BLI_LINKSTACK_PUSH(queue_next, e_other); } - } while ((l_iter_radial = l_iter_radial->radial_next) != l_first_radial); + } } } - } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first); + } } } - /* clear for the next loop */ - for (lnk = queue_next; lnk; lnk = lnk->next) { - BMVert *v_link = lnk->link; - const int i = BM_elem_index_get(v_link); + /* Clear for the next loop. */ + for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) { + BMEdge *e_link = lnk->link; - BM_elem_flag_disable(v_link, BM_ELEM_TAG); - - /* keep in sync, avoid having to do full memcpy each iteration */ - dists_prev[i] = dists[i]; - if (index != NULL) { - index_prev[i] = index[i]; - } + BM_elem_flag_disable(e_link, BM_ELEM_TAG); } BLI_LINKSTACK_SWAP(queue, queue_next); - /* none should be tagged now since 'queue_next' is empty */ - BLI_assert(BM_iter_mesh_count_flag(BM_VERTS_OF_MESH, bm, BM_ELEM_TAG, true) == 0); - + /* None should be tagged now since 'queue_next' is empty. */ + BLI_assert(BM_iter_mesh_count_flag(BM_EDGES_OF_MESH, bm, BM_ELEM_TAG, true) == 0); } while (BLI_LINKSTACK_SIZE(queue)); BLI_LINKSTACK_FREE(queue); BLI_LINKSTACK_FREE(queue_next); - - MEM_freeN(dists_prev); - if (index_prev != NULL) { - MEM_freeN(index_prev); - } } /** \} */ @@ -1445,7 +1466,7 @@ static void mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tcld, * * Interpolate from every other loop (not ideal) * However values will only be taken from loops which overlap other mdisps. - * */ + */ const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1); if (update_loop_mdisps) { float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num); diff --git a/source/blender/editors/transform/transform_convert_mesh_skin.c b/source/blender/editors/transform/transform_convert_mesh_skin.c index 1807e9adcd1..b1024f5efe4 100644 --- a/source/blender/editors/transform/transform_convert_mesh_skin.c +++ b/source/blender/editors/transform/transform_convert_mesh_skin.c @@ -55,9 +55,7 @@ static float *mesh_skin_transdata_center(const struct TransIslandData *island_da if (island_data->center && island_index != -1) { return island_data->center[island_index]; } - else { - return eve->co; - } + return eve->co; } static void mesh_skin_transdata_create(TransDataBasic *td, diff --git a/source/blender/editors/transform/transform_convert_tracking.c b/source/blender/editors/transform/transform_convert_tracking.c index c3f48adca68..b25af709981 100644 --- a/source/blender/editors/transform/transform_convert_tracking.c +++ b/source/blender/editors/transform/transform_convert_tracking.c @@ -52,6 +52,7 @@ typedef struct TransDataTracking { float (*smarkers)[2]; int markersnr; + int framenr; MovieTrackingMarker *markers; /* marker transformation from curves editor */ @@ -73,9 +74,25 @@ enum transDataTracking_Mode { * * \{ */ -static void markerToTransDataInit(TransData *td, - TransData2D *td2d, - TransDataTracking *tdt, +typedef struct TransformInitContext { + SpaceClip *space_clip; + + TransInfo *t; + TransDataContainer *tc; + + /* NOTE: These pointers will be `nullptr` during counting step. + * This means, that the transformation data initialization functions are to increment + * `tc->data_len` instead of filling in the transformation data when these pointers are + * `nullptr`. For simplicity, check the `current.td` against `nullptr`. + * Do not `tc->data_len` when filling in the transformation data. */ + struct { + TransData *td; + TransData2D *td2d; + TransDataTracking *tdt; + } current; +} TransformInitContext; + +static void markerToTransDataInit(TransformInitContext *init_context, MovieTrackingTrack *track, MovieTrackingMarker *marker, int area, @@ -84,8 +101,19 @@ static void markerToTransDataInit(TransData *td, const float off[2], const float aspect[2]) { + TransData *td = init_context->current.td; + TransData2D *td2d = init_context->current.td2d; + TransDataTracking *tdt = init_context->current.tdt; + + if (td == NULL) { + init_context->tc->data_len++; + return; + } + int anchor = area == TRACK_AREA_POINT && off; + tdt->flag = marker->flag; + tdt->framenr = marker->framenr; tdt->mode = transDataTracking_ModeTracks; if (anchor) { @@ -143,23 +171,20 @@ static void markerToTransDataInit(TransData *td, unit_m3(td->mtx); unit_m3(td->smtx); + + init_context->current.td++; + init_context->current.td2d++; + init_context->current.tdt++; } -static void trackToTransData(const int framenr, - TransData *td, - TransData2D *td2d, - TransDataTracking *tdt, +static void trackToTransData(TransformInitContext *init_context, + const int framenr, MovieTrackingTrack *track, const float aspect[2]) { MovieTrackingMarker *marker = BKE_tracking_marker_ensure(track, framenr); - tdt->flag = marker->flag; - marker->flag &= ~(MARKER_DISABLED | MARKER_TRACKED); - - markerToTransDataInit(td++, - td2d++, - tdt++, + markerToTransDataInit(init_context, track, marker, TRACK_AREA_POINT, @@ -170,16 +195,14 @@ static void trackToTransData(const int framenr, if (track->flag & SELECT) { markerToTransDataInit( - td++, td2d++, tdt++, track, marker, TRACK_AREA_POINT, marker->pos, NULL, NULL, aspect); + init_context, track, marker, TRACK_AREA_POINT, marker->pos, NULL, NULL, aspect); } if (track->pat_flag & SELECT) { int a; for (a = 0; a < 4; a++) { - markerToTransDataInit(td++, - td2d++, - tdt++, + markerToTransDataInit(init_context, track, marker, TRACK_AREA_PAT, @@ -191,9 +214,7 @@ static void trackToTransData(const int framenr, } if (track->search_flag & SELECT) { - markerToTransDataInit(td++, - td2d++, - tdt++, + markerToTransDataInit(init_context, track, marker, TRACK_AREA_SEARCH, @@ -202,9 +223,7 @@ static void trackToTransData(const int framenr, NULL, aspect); - markerToTransDataInit(td++, - td2d++, - tdt++, + markerToTransDataInit(init_context, track, marker, TRACK_AREA_SEARCH, @@ -213,15 +232,43 @@ static void trackToTransData(const int framenr, NULL, aspect); } + + if (init_context->current.td != NULL) { + marker->flag &= ~(MARKER_DISABLED | MARKER_TRACKED); + } } -static void planeMarkerToTransDataInit(TransData *td, - TransData2D *td2d, - TransDataTracking *tdt, +static void trackToTransDataIfNeeded(TransformInitContext *init_context, + const int framenr, + MovieTrackingTrack *track, + const float aspect[2]) +{ + if (!TRACK_VIEW_SELECTED(init_context->space_clip, track)) { + return; + } + if (track->flag & TRACK_LOCKED) { + return; + } + trackToTransData(init_context, framenr, track, aspect); +} + +static void planeMarkerToTransDataInit(TransformInitContext *init_context, MovieTrackingPlaneTrack *plane_track, + MovieTrackingPlaneMarker *plane_marker, float corner[2], const float aspect[2]) { + TransData *td = init_context->current.td; + TransData2D *td2d = init_context->current.td2d; + TransDataTracking *tdt = init_context->current.tdt; + + if (td == NULL) { + init_context->tc->data_len++; + return; + } + + tdt->flag = plane_marker->flag; + tdt->framenr = plane_marker->framenr; tdt->mode = transDataTracking_ModePlaneTracks; tdt->plane_track = plane_track; @@ -247,24 +294,38 @@ static void planeMarkerToTransDataInit(TransData *td, unit_m3(td->mtx); unit_m3(td->smtx); + + init_context->current.td++; + init_context->current.td2d++; + init_context->current.tdt++; } -static void planeTrackToTransData(const int framenr, - TransData *td, - TransData2D *td2d, - TransDataTracking *tdt, +static void planeTrackToTransData(TransformInitContext *init_context, + const int framenr, MovieTrackingPlaneTrack *plane_track, const float aspect[2]) { MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_ensure(plane_track, framenr); - int i; - tdt->flag = plane_marker->flag; - plane_marker->flag &= ~PLANE_MARKER_TRACKED; + for (int i = 0; i < 4; i++) { + planeMarkerToTransDataInit( + init_context, plane_track, plane_marker, plane_marker->corners[i], aspect); + } - for (i = 0; i < 4; i++) { - planeMarkerToTransDataInit(td++, td2d++, tdt++, plane_track, plane_marker->corners[i], aspect); + if (init_context->current.td != NULL) { + plane_marker->flag &= ~PLANE_MARKER_TRACKED; + } +} + +static void planeTrackToTransDataIfNeeded(TransformInitContext *init_context, + const int framenr, + MovieTrackingPlaneTrack *plane_track, + const float aspect[2]) +{ + if (!PLANE_TRACK_VIEW_SELECTED(plane_track)) { + return; } + planeTrackToTransData(init_context, framenr, plane_track, aspect); } static void transDataTrackingFree(TransInfo *UNUSED(t), @@ -284,101 +345,53 @@ static void transDataTrackingFree(TransInfo *UNUSED(t), static void createTransTrackingTracksData(bContext *C, TransInfo *t) { - TransData *td; - TransData2D *td2d; - SpaceClip *sc = CTX_wm_space_clip(C); - MovieClip *clip = ED_space_clip_get_clip(sc); - ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking); - ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(&clip->tracking); - MovieTrackingTrack *track; - MovieTrackingPlaneTrack *plane_track; - TransDataTracking *tdt; - int framenr = ED_space_clip_get_clip_frame_number(sc); + SpaceClip *space_clip = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(space_clip); + const ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking); + const ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(&clip->tracking); + const int framenr = ED_space_clip_get_clip_frame_number(space_clip); TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); - /* count */ - tc->data_len = 0; - - track = tracksbase->first; - while (track) { - if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) { - tc->data_len++; /* offset */ + TransformInitContext init_context = {NULL}; + init_context.space_clip = space_clip; + init_context.t = t; + init_context.tc = tc; - if (track->flag & SELECT) { - tc->data_len++; - } + /* Count required tranformation data. */ - if (track->pat_flag & SELECT) { - tc->data_len += 4; - } - - if (track->search_flag & SELECT) { - tc->data_len += 2; - } - } + tc->data_len = 0; - track = track->next; + LISTBASE_FOREACH (MovieTrackingTrack *, track, tracksbase) { + trackToTransDataIfNeeded(&init_context, framenr, track, t->aspect); } - for (plane_track = plane_tracks_base->first; plane_track; plane_track = plane_track->next) { - if (PLANE_TRACK_VIEW_SELECTED(plane_track)) { - tc->data_len += 4; - } + LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, plane_tracks_base) { + planeTrackToTransDataIfNeeded(&init_context, framenr, plane_track, t->aspect); } if (tc->data_len == 0) { return; } - td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransTracking TransData"); - td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), - "TransTracking TransData2D"); - tdt = tc->custom.type.data = MEM_callocN(tc->data_len * sizeof(TransDataTracking), - "TransTracking TransDataTracking"); - + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransTracking TransData"); + tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransTracking TransData2D"); + tc->custom.type.data = MEM_callocN(tc->data_len * sizeof(TransDataTracking), + "TransTracking TransDataTracking"); tc->custom.type.free_cb = transDataTrackingFree; - /* create actual data */ - track = tracksbase->first; - while (track) { - if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) { - trackToTransData(framenr, td, td2d, tdt, track, t->aspect); - - /* offset */ - td++; - td2d++; - tdt++; + init_context.current.td = tc->data; + init_context.current.td2d = tc->data_2d; + init_context.current.tdt = tc->custom.type.data; - if (track->flag & SELECT) { - td++; - td2d++; - tdt++; - } - - if (track->pat_flag & SELECT) { - td += 4; - td2d += 4; - tdt += 4; - } + /* Create actual transformation data. */ - if (track->search_flag & SELECT) { - td += 2; - td2d += 2; - tdt += 2; - } - } - - track = track->next; + LISTBASE_FOREACH (MovieTrackingTrack *, track, tracksbase) { + trackToTransDataIfNeeded(&init_context, framenr, track, t->aspect); } - for (plane_track = plane_tracks_base->first; plane_track; plane_track = plane_track->next) { - if (PLANE_TRACK_VIEW_SELECTED(plane_track)) { - planeTrackToTransData(framenr, td, td2d, tdt, plane_track, t->aspect); - td += 4; - td2d += 4; - tdt += 4; - } + LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, plane_tracks_base) { + planeTrackToTransDataIfNeeded(&init_context, framenr, plane_track, t->aspect); } } @@ -560,17 +573,17 @@ void createTransTrackingData(bContext *C, TransInfo *t) static void cancelTransTracking(TransInfo *t) { TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); - SpaceClip *sc = t->area->spacedata.first; - int i, framenr = ED_space_clip_get_clip_frame_number(sc); TransDataTracking *tdt_array = tc->custom.type.data; - i = 0; + int i = 0; while (i < tc->data_len) { TransDataTracking *tdt = &tdt_array[i]; if (tdt->mode == transDataTracking_ModeTracks) { MovieTrackingTrack *track = tdt->track; - MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); + MovieTrackingMarker *marker = BKE_tracking_marker_get_exact(track, tdt->framenr); + + BLI_assert(marker != NULL); marker->flag = tdt->flag; @@ -606,7 +619,10 @@ static void cancelTransTracking(TransInfo *t) } else if (tdt->mode == transDataTracking_ModePlaneTracks) { MovieTrackingPlaneTrack *plane_track = tdt->plane_track; - MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get(plane_track, framenr); + MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get_exact(plane_track, + tdt->framenr); + + BLI_assert(plane_marker != NULL); plane_marker->flag = tdt->flag; i += 3; diff --git a/source/blender/editors/transform/transform_draw_cursors.c b/source/blender/editors/transform/transform_draw_cursors.c index 84fc45e2b45..b4b28ea0ac0 100644 --- a/source/blender/editors/transform/transform_draw_cursors.c +++ b/source/blender/editors/transform/transform_draw_cursors.c @@ -45,103 +45,49 @@ enum eArrowDirection { RIGHT, }; -struct ArrowDims { - int offset; - int length; - int size; -}; - -#define POS_INDEX 0 -/* NOTE: this --^ is a bit hackish, but simplifies GPUVertFormat usage among functions - * private to this file - merwin - */ +#define ARROW_WIDTH (2.0f * U.pixelsize) +#define DASH_WIDTH (1.0f) +#define DASH_LENGTH (8.0f * DASH_WIDTH * U.pixelsize) -static void drawArrow(enum eArrowDirection dir, const struct ArrowDims *arrow_dims) +static void drawArrow(const uint pos_id, const enum eArrowDirection dir) { - int offset = arrow_dims->offset; - int length = arrow_dims->length; - int size = arrow_dims->size; + int offset = 5.0f * UI_DPI_FAC; + int length = (6.0f * UI_DPI_FAC) + (4.0f * U.pixelsize); + int size = (3.0f * UI_DPI_FAC) + (2.0f * U.pixelsize); + + /* To line up the arrow point nicely, one end has to be extended by half its width. But + * being on a 45 degree angle, Pythagoras says a movement of sqrt(2)/2 * (line width /2) */ + float adjust = (M_SQRT2 * ARROW_WIDTH / 4.0f); + + if (ELEM(dir, LEFT, DOWN)) { + offset = -offset; + length = -length; + size = -size; + adjust = -adjust; + } immBegin(GPU_PRIM_LINES, 6); - switch (dir) { - case LEFT: - offset = -offset; - length = -length; - size = -size; - ATTR_FALLTHROUGH; - case RIGHT: - immVertex2f(POS_INDEX, offset, 0); - immVertex2f(POS_INDEX, offset + length, 0); - immVertex2f(POS_INDEX, offset + length, 0); - immVertex2f(POS_INDEX, offset + length - size, -size); - immVertex2f(POS_INDEX, offset + length, 0); - immVertex2f(POS_INDEX, offset + length - size, size); - break; - - case DOWN: - offset = -offset; - length = -length; - size = -size; - ATTR_FALLTHROUGH; - case UP: - immVertex2f(POS_INDEX, 0, offset); - immVertex2f(POS_INDEX, 0, offset + length); - immVertex2f(POS_INDEX, 0, offset + length); - immVertex2f(POS_INDEX, -size, offset + length - size); - immVertex2f(POS_INDEX, 0, offset + length); - immVertex2f(POS_INDEX, size, offset + length - size); - break; + if (ELEM(dir, LEFT, RIGHT)) { + immVertex2f(pos_id, offset, 0); + immVertex2f(pos_id, offset + length, 0); + immVertex2f(pos_id, offset + length + adjust, adjust); + immVertex2f(pos_id, offset + length - size, -size); + immVertex2f(pos_id, offset + length, 0); + immVertex2f(pos_id, offset + length - size, size); } - - immEnd(); -} - -static void drawArrowHead(enum eArrowDirection dir, int size) -{ - immBegin(GPU_PRIM_LINES, 4); - - switch (dir) { - case LEFT: - size = -size; - ATTR_FALLTHROUGH; - case RIGHT: - immVertex2f(POS_INDEX, 0, 0); - immVertex2f(POS_INDEX, -size, -size); - immVertex2f(POS_INDEX, 0, 0); - immVertex2f(POS_INDEX, -size, size); - break; - - case DOWN: - size = -size; - ATTR_FALLTHROUGH; - case UP: - immVertex2f(POS_INDEX, 0, 0); - immVertex2f(POS_INDEX, -size, -size); - immVertex2f(POS_INDEX, 0, 0); - immVertex2f(POS_INDEX, size, -size); - break; + else { + immVertex2f(pos_id, 0, offset); + immVertex2f(pos_id, 0, offset + length); + immVertex2f(pos_id, adjust, offset + length + adjust); + immVertex2f(pos_id, -size, offset + length - size); + immVertex2f(pos_id, 0, offset + length); + immVertex2f(pos_id, size, offset + length - size); } immEnd(); } -static void drawArc(float angle_start, float angle_end, int segments, float size) -{ - float delta = (angle_end - angle_start) / segments; - float angle; - int a; - - immBegin(GPU_PRIM_LINE_STRIP, segments + 1); - - for (angle = angle_start, a = 0; a < segments; angle += delta, a++) { - immVertex2f(POS_INDEX, cosf(angle) * size, sinf(angle) * size); - } - immVertex2f(POS_INDEX, cosf(angle_end) * size, sinf(angle_end) * size); - - immEnd(); -} - /** * Poll callback for cursor drawing: * #WM_paint_cursor_activate @@ -149,11 +95,7 @@ static void drawArc(float angle_start, float angle_end, int segments, float size bool transform_draw_cursor_poll(bContext *C) { ARegion *region = CTX_wm_region(C); - - if (region && region->regiontype == RGN_TYPE_WINDOW) { - return 1; - } - return 0; + return (region && region->regiontype == RGN_TYPE_WINDOW) ? 1 : 0; } /** @@ -164,181 +106,115 @@ void transform_draw_cursor_draw(bContext *UNUSED(C), int x, int y, void *customd { TransInfo *t = (TransInfo *)customdata; - if (t->helpline != HLP_NONE) { - struct ArrowDims arrow_dims = { - .offset = 5 * UI_DPI_FAC, - .length = 10 * UI_DPI_FAC, - .size = 5 * UI_DPI_FAC, - }; - - float cent[2]; - const float mval[3] = {x, y, 0.0f}; - float tmval[2] = { - (float)t->mval[0], - (float)t->mval[1], - }; - - projectFloatViewEx(t, t->center_global, cent, V3D_PROJ_TEST_CLIP_ZERO); - /* Offset the values for the area region. */ - const float offset[2] = { - t->region->winrct.xmin, - t->region->winrct.ymin, - }; - - for (int i = 0; i < 2; i++) { - cent[i] += offset[i]; - tmval[i] += offset[i]; - } - - GPU_line_smooth(true); - GPU_blend(GPU_BLEND_ALPHA); - - GPU_matrix_push(); - - /* Dashed lines first. */ - if (ELEM(t->helpline, HLP_SPRING, HLP_ANGLE)) { - const uint shdr_pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - UNUSED_VARS_NDEBUG(shdr_pos); /* silence warning */ - BLI_assert(shdr_pos == POS_INDEX); + if (t->helpline == HLP_NONE) { + return; + } - GPU_line_width(1.0f); + float cent[2]; + const float mval[3] = {x, y, 0.0f}; + float tmval[2] = { + (float)t->mval[0], + (float)t->mval[1], + }; + + projectFloatViewEx(t, t->center_global, cent, V3D_PROJ_TEST_CLIP_ZERO); + /* Offset the values for the area region. */ + const float offset[2] = { + t->region->winrct.xmin, + t->region->winrct.ymin, + }; + + for (int i = 0; i < 2; i++) { + cent[i] += offset[i]; + tmval[i] += offset[i]; + } - immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + float viewport_size[4]; + GPU_viewport_size_get_f(viewport_size); + + GPU_line_smooth(true); + GPU_blend(GPU_BLEND_ALPHA); + const uint pos_id = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + /* Dashed lines first. */ + if (ELEM(t->helpline, HLP_SPRING, HLP_ANGLE)) { + GPU_line_width(DASH_WIDTH); + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + immUniform1i("colors_len", 0); /* "simple" mode */ + immUniformThemeColor3(TH_VIEW_OVERLAY); + immUniform1f("dash_width", DASH_LENGTH); + immUniform1f("dash_factor", 0.5f); + immBegin(GPU_PRIM_LINES, 2); + immVertex2fv(pos_id, cent); + immVertex2f(pos_id, tmval[0], tmval[1]); + immEnd(); + immUnbindProgram(); + } - float viewport_size[4]; - GPU_viewport_size_get_f(viewport_size); - immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + /* And now, solid lines. */ - immUniform1i("colors_len", 0); /* "simple" mode */ - immUniformThemeColor3(TH_VIEW_OVERLAY); - immUniform1f("dash_width", 6.0f * UI_DPI_FAC); - immUniform1f("dash_factor", 0.5f); + immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); + immUniformThemeColor3(TH_VIEW_OVERLAY); + immUniform2fv("viewportSize", &viewport_size[2]); + immUniform1f("lineWidth", ARROW_WIDTH); - immBegin(GPU_PRIM_LINES, 2); - immVertex2fv(POS_INDEX, cent); - immVertex2f(POS_INDEX, tmval[0], tmval[1]); - immEnd(); + GPU_matrix_push(); + GPU_matrix_translate_3fv(mval); - immUnbindProgram(); + switch (t->helpline) { + case HLP_SPRING: + GPU_matrix_rotate_axis(-RAD2DEGF(atan2f(cent[0] - tmval[0], cent[1] - tmval[1])), 'Z'); + drawArrow(pos_id, UP); + drawArrow(pos_id, DOWN); + break; + case HLP_HARROW: + drawArrow(pos_id, RIGHT); + drawArrow(pos_id, LEFT); + break; + case HLP_VARROW: + drawArrow(pos_id, UP); + drawArrow(pos_id, DOWN); + break; + case HLP_CARROW: { + /* Draw arrow based on direction defined by custom-points. */ + const int *data = t->mouse.data; + const float angle = -atan2f(data[2] - data[0], data[3] - data[1]); + GPU_matrix_rotate_axis(RAD2DEGF(angle), 'Z'); + drawArrow(pos_id, UP); + drawArrow(pos_id, DOWN); + break; } - - /* And now, solid lines. */ - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - UNUSED_VARS_NDEBUG(pos); /* silence warning */ - BLI_assert(pos == POS_INDEX); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - switch (t->helpline) { - case HLP_SPRING: - immUniformThemeColor3(TH_VIEW_OVERLAY); - - GPU_matrix_translate_3fv(mval); - GPU_matrix_rotate_axis(-RAD2DEGF(atan2f(cent[0] - tmval[0], cent[1] - tmval[1])), 'Z'); - - GPU_line_width(3.0f); - drawArrow(UP, &arrow_dims); - drawArrow(DOWN, &arrow_dims); - break; - case HLP_HARROW: - immUniformThemeColor3(TH_VIEW_OVERLAY); - GPU_matrix_translate_3fv(mval); - - GPU_line_width(3.0f); - drawArrow(RIGHT, &arrow_dims); - drawArrow(LEFT, &arrow_dims); - break; - case HLP_VARROW: - immUniformThemeColor3(TH_VIEW_OVERLAY); - - GPU_matrix_translate_3fv(mval); - - GPU_line_width(3.0f); - drawArrow(UP, &arrow_dims); - drawArrow(DOWN, &arrow_dims); - break; - case HLP_CARROW: { - /* Draw arrow based on direction defined by custom-points. */ - immUniformThemeColor3(TH_VIEW_OVERLAY); - - GPU_matrix_translate_3fv(mval); - - GPU_line_width(3.0f); - - const int *data = t->mouse.data; - const float dx = data[2] - data[0], dy = data[3] - data[1]; - const float angle = -atan2f(dx, dy); - - GPU_matrix_push(); - - GPU_matrix_rotate_axis(RAD2DEGF(angle), 'Z'); - - drawArrow(UP, &arrow_dims); - drawArrow(DOWN, &arrow_dims); - - GPU_matrix_pop(); - break; - } - case HLP_ANGLE: { - float dx = tmval[0] - cent[0], dy = tmval[1] - cent[1]; - float angle = atan2f(dy, dx); - float dist = hypotf(dx, dy); - float delta_angle = min_ff(15.0f / (dist / UI_DPI_FAC), (float)M_PI / 4.0f); - float spacing_angle = min_ff(5.0f / (dist / UI_DPI_FAC), (float)M_PI / 12.0f); - - immUniformThemeColor3(TH_VIEW_OVERLAY); - - GPU_matrix_translate_3f(cent[0] - tmval[0] + mval[0], cent[1] - tmval[1] + mval[1], 0); - - GPU_line_width(3.0f); - drawArc(angle - delta_angle, angle - spacing_angle, 10, dist); - drawArc(angle + spacing_angle, angle + delta_angle, 10, dist); - - GPU_matrix_push(); - - GPU_matrix_translate_3f( - cosf(angle - delta_angle) * dist, sinf(angle - delta_angle) * dist, 0); - GPU_matrix_rotate_axis(RAD2DEGF(angle - delta_angle), 'Z'); - - drawArrowHead(DOWN, arrow_dims.size); - - GPU_matrix_pop(); - - GPU_matrix_translate_3f( - cosf(angle + delta_angle) * dist, sinf(angle + delta_angle) * dist, 0); - GPU_matrix_rotate_axis(RAD2DEGF(angle + delta_angle), 'Z'); - - drawArrowHead(UP, arrow_dims.size); - break; - } - case HLP_TRACKBALL: { - uchar col[3], col2[3]; - UI_GetThemeColor3ubv(TH_GRID, col); - - GPU_matrix_translate_3fv(mval); - - GPU_line_width(3.0f); - - UI_make_axis_color(col, col2, 'X'); - immUniformColor3ubv(col2); - - drawArrow(RIGHT, &arrow_dims); - drawArrow(LEFT, &arrow_dims); - - UI_make_axis_color(col, col2, 'Y'); - immUniformColor3ubv(col2); - - drawArrow(UP, &arrow_dims); - drawArrow(DOWN, &arrow_dims); - break; - } + case HLP_ANGLE: { + GPU_matrix_push(); + float angle = atan2f(tmval[1] - cent[1], tmval[0] - cent[0]); + GPU_matrix_translate_3f(cosf(angle), sinf(angle), 0); + GPU_matrix_rotate_axis(RAD2DEGF(angle), 'Z'); + drawArrow(pos_id, DOWN); + GPU_matrix_pop(); + GPU_matrix_translate_3f(cosf(angle), sinf(angle), 0); + GPU_matrix_rotate_axis(RAD2DEGF(angle), 'Z'); + drawArrow(pos_id, UP); + break; + } + case HLP_TRACKBALL: { + uchar col[3], col2[3]; + UI_GetThemeColor3ubv(TH_GRID, col); + UI_make_axis_color(col, col2, 'X'); + immUniformColor3ubv(col2); + drawArrow(pos_id, RIGHT); + drawArrow(pos_id, LEFT); + UI_make_axis_color(col, col2, 'Y'); + immUniformColor3ubv(col2); + drawArrow(pos_id, UP); + drawArrow(pos_id, DOWN); + break; } - - immUnbindProgram(); - GPU_matrix_pop(); - - GPU_line_smooth(false); - GPU_blend(GPU_BLEND_NONE); } + + GPU_matrix_pop(); + immUnbindProgram(); + GPU_line_smooth(false); + GPU_blend(GPU_BLEND_NONE); } diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index 3c82365fdb4..7eaed2e1924 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -704,7 +704,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C, if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { /* calculate difference matrix */ - BKE_gpencil_parent_matrix_get(depsgraph, ob, gpl, diff_mat); + BKE_gpencil_layer_transform_matrix_get(depsgraph, ob, gpl, diff_mat); LISTBASE_FOREACH (bGPDstroke *, gps, &gpl->actframe->strokes) { /* skip strokes that are invalid for current view */ diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index c19dd4598cf..508189c5d77 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -299,83 +299,93 @@ eRedrawFlag handleSnapping(TransInfo *t, const wmEvent *event) void applyProject(TransInfo *t) { - /* XXX FLICKER IN OBJECT MODE */ - if ((t->tsnap.project) && activeSnap(t) && (t->flag & T_NO_PROJECT) == 0) { - float tvec[3]; - int i; + if (!t->tsnap.project) { + return; + } - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - TransData *td = tc->data; - for (i = 0; i < tc->data_len; i++, td++) { - float iloc[3], loc[3], no[3]; - float mval_fl[2]; - if (td->flag & TD_SKIP) { - continue; - } + if (!activeSnap(t) || (t->flag & T_NO_PROJECT)) { + return; + } - if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) { - continue; - } + if (doForceIncrementSnap(t)) { + return; + } - copy_v3_v3(iloc, td->loc); - if (tc->use_local_mat) { - mul_m4_v3(tc->mat, iloc); - } - else if (t->flag & T_OBJECT) { - BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob); - copy_v3_v3(iloc, td->ob->obmat[3]); - } + float tvec[3]; + int i; + + /* XXX FLICKER IN OBJECT MODE */ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + float iloc[3], loc[3], no[3]; + float mval_fl[2]; + if (td->flag & TD_SKIP) { + continue; + } + + if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) { + continue; + } - if (ED_view3d_project_float_global(t->region, iloc, mval_fl, V3D_PROJ_TEST_NOP) == - V3D_PROJ_RET_OK) { - if (ED_transform_snap_object_project_view3d( - t->tsnap.object_context, - t->depsgraph, - SCE_SNAP_MODE_FACE, - &(const struct SnapObjectParams){ - .snap_select = t->tsnap.modeSelect, - .use_object_edit_cage = (t->flag & T_EDIT) != 0, - .use_occlusion_test = false, - .use_backface_culling = t->tsnap.use_backface_culling, - }, - mval_fl, - NULL, - 0, - loc, - no)) { + copy_v3_v3(iloc, td->loc); + if (tc->use_local_mat) { + mul_m4_v3(tc->mat, iloc); + } + else if (t->flag & T_OBJECT) { + BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob); + copy_v3_v3(iloc, td->ob->obmat[3]); + } + + if (ED_view3d_project_float_global(t->region, iloc, mval_fl, V3D_PROJ_TEST_NOP) == + V3D_PROJ_RET_OK) { + if (ED_transform_snap_object_project_view3d( + t->tsnap.object_context, + t->depsgraph, + SCE_SNAP_MODE_FACE, + &(const struct SnapObjectParams){ + .snap_select = t->tsnap.modeSelect, + .use_object_edit_cage = (t->flag & T_EDIT) != 0, + .use_occlusion_test = false, + .use_backface_culling = t->tsnap.use_backface_culling, + }, + mval_fl, + NULL, + 0, + loc, + no)) { #if 0 if (tc->use_local_mat) { mul_m4_v3(tc->imat, loc); } #endif - sub_v3_v3v3(tvec, loc, iloc); + sub_v3_v3v3(tvec, loc, iloc); - mul_m3_v3(td->smtx, tvec); + mul_m3_v3(td->smtx, tvec); - add_v3_v3(td->loc, tvec); + add_v3_v3(td->loc, tvec); - if (t->tsnap.align && (t->flag & T_OBJECT)) { - /* handle alignment as well */ - const float *original_normal; - float mat[3][3]; + if (t->tsnap.align && (t->flag & T_OBJECT)) { + /* handle alignment as well */ + const float *original_normal; + float mat[3][3]; - /* In pose mode, we want to align normals with Y axis of bones... */ - original_normal = td->axismtx[2]; + /* In pose mode, we want to align normals with Y axis of bones... */ + original_normal = td->axismtx[2]; - rotation_between_vecs_to_mat3(mat, original_normal, no); + rotation_between_vecs_to_mat3(mat, original_normal, no); - transform_data_ext_rotate(td, mat, true); + transform_data_ext_rotate(td, mat, true); - /* TODO support constraints for rotation too? see ElementRotation */ - } + /* TODO support constraints for rotation too? see ElementRotation */ } } + } #if 0 /* TODO: sipport this? */ constraintTransLim(t, td); #endif - } } } } diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 533416bf85e..883c13f20db 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -70,6 +70,15 @@ /** We only need this locally. */ static CLG_LogRef LOG = {"ed.undo"}; +/** + * \warning Values are used in #ED_undo_gpencil_step, + * which should eventually be replaced with the undo-system. + */ +enum eUndoStepDir { + STEP_REDO = 1, + STEP_UNDO = -1, +}; + /* -------------------------------------------------------------------- */ /** \name Generic Undo System Access * @@ -169,16 +178,16 @@ void ED_undo_push(bContext *C, const char *str) } /** - * \note Also check #undo_history_exec in bottom if you change notifiers. + * Common pre management of undo/redo (killing all running jobs, calling pre handlers, etc.). */ -static int ed_undo_step_impl( - bContext *C, int step, const char *undoname, int undo_index, ReportList *reports) +static void ed_undo_step_pre(bContext *C, + wmWindowManager *wm, + const enum eUndoStepDir undo_dir, + ReportList *reports) { - /* Mutually exclusives, ensure correct input. */ - BLI_assert(((undoname || undo_index != -1) && !step) || - (!(undoname || undo_index != -1) && step)); - CLOG_INFO(&LOG, 1, "name='%s', step=%d", undoname, step); - wmWindowManager *wm = CTX_wm_manager(C); + BLI_assert(ELEM(undo_dir, STEP_UNDO, STEP_REDO)); + + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ScrArea *area = CTX_wm_area(C); @@ -187,18 +196,12 @@ static int ed_undo_step_impl( WM_jobs_kill_all(wm); if (G.debug & G_DEBUG_IO) { - Main *bmain = CTX_data_main(C); if (bmain->lock != NULL) { BKE_report(reports, RPT_INFO, "Checking sanity of current .blend file *BEFORE* undo step"); BLO_main_validate_libraries(bmain, reports); } } - /* TODO(campbell): undo_system: use undo system */ - /* grease pencil can be can be used in plenty of spaces, so check it first */ - if (ED_gpencil_session_active()) { - return ED_undo_gpencil_step(C, step, undoname); - } if (area && (area->spacetype == SPACE_VIEW3D)) { Object *obact = CTX_data_active_object(C); if (obact && (obact->type == OB_GPENCIL)) { @@ -206,89 +209,40 @@ static int ed_undo_step_impl( } } - UndoStep *step_data_from_name = NULL; - int step_for_callback = step; - if (undoname != NULL) { - step_data_from_name = BKE_undosys_step_find_by_name(wm->undo_stack, undoname); - if (step_data_from_name == NULL) { - return OPERATOR_CANCELLED; - } - - /* TODO(campbell), could use simple optimization. */ - /* Pointers match on redo. */ - step_for_callback = (BLI_findindex(&wm->undo_stack->steps, step_data_from_name) < - BLI_findindex(&wm->undo_stack->steps, wm->undo_stack->step_active)) ? - 1 : - -1; - } - else if (undo_index != -1) { - step_for_callback = (undo_index < - BLI_findindex(&wm->undo_stack->steps, wm->undo_stack->step_active)) ? - 1 : - -1; - } - /* App-Handlers (pre). */ { /* Note: ignore grease pencil for now. */ - Main *bmain = CTX_data_main(C); wm->op_undo_depth++; BKE_callback_exec_id( - bmain, &scene->id, (step_for_callback > 0) ? BKE_CB_EVT_UNDO_PRE : BKE_CB_EVT_REDO_PRE); + bmain, &scene->id, (undo_dir == STEP_UNDO) ? BKE_CB_EVT_UNDO_PRE : BKE_CB_EVT_REDO_PRE); wm->op_undo_depth--; } +} - /* Undo System */ - { - if (undoname) { - BKE_undosys_step_undo_with_data(wm->undo_stack, C, step_data_from_name); - } - else if (undo_index != -1) { - BKE_undosys_step_undo_from_index(wm->undo_stack, C, undo_index); - } - else { - if (step == 1) { - BKE_undosys_step_undo(wm->undo_stack, C); - } - else { - BKE_undosys_step_redo(wm->undo_stack, C); - } - } +/** + * Common post management of undo/redo (calling post handlers, adding notifiers etc.). + * + * \note Also check #undo_history_exec in bottom if you change notifiers. + */ +static void ed_undo_step_post(bContext *C, + wmWindowManager *wm, + const enum eUndoStepDir undo_dir, + ReportList *reports) +{ + BLI_assert(ELEM(undo_dir, STEP_UNDO, STEP_REDO)); - /* Set special modes for grease pencil */ - if (area && (area->spacetype == SPACE_VIEW3D)) { - Object *obact = CTX_data_active_object(C); - if (obact && (obact->type == OB_GPENCIL)) { - /* set cursor */ - if (ELEM(obact->mode, - OB_MODE_PAINT_GPENCIL, - OB_MODE_SCULPT_GPENCIL, - OB_MODE_WEIGHT_GPENCIL, - OB_MODE_VERTEX_GPENCIL)) { - ED_gpencil_toggle_brush_cursor(C, true, NULL); - } - else { - ED_gpencil_toggle_brush_cursor(C, false, NULL); - } - /* set workspace mode */ - Base *basact = CTX_data_active_base(C); - ED_object_base_activate(C, basact); - } - } - } + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); /* App-Handlers (post). */ { - Main *bmain = CTX_data_main(C); - scene = CTX_data_scene(C); wm->op_undo_depth++; BKE_callback_exec_id( - bmain, &scene->id, step_for_callback > 0 ? BKE_CB_EVT_UNDO_POST : BKE_CB_EVT_REDO_POST); + bmain, &scene->id, (undo_dir == STEP_UNDO) ? BKE_CB_EVT_UNDO_POST : BKE_CB_EVT_REDO_POST); wm->op_undo_depth--; } if (G.debug & G_DEBUG_IO) { - Main *bmain = CTX_data_main(C); if (bmain->lock != NULL) { BKE_report(reports, RPT_INFO, "Checking sanity of current .blend file *AFTER* undo step"); BLO_main_validate_libraries(bmain, reports); @@ -299,30 +253,125 @@ static int ed_undo_step_impl( WM_event_add_notifier(C, NC_WM | ND_UNDO, NULL); WM_toolsystem_refresh_active(C); - - Main *bmain = CTX_data_main(C); WM_toolsystem_refresh_screen_all(bmain); if (CLOG_CHECK(&LOG, 1)) { BKE_undosys_print(wm->undo_stack); } - - return OPERATOR_FINISHED; } -static int ed_undo_step_direction(bContext *C, int step, ReportList *reports) +/** Undo or redo one step from current active one. + * May undo or redo several steps at once only if the target step is a 'skipped' one. + * The target step will be the one immediately before or after the active one. */ +static int ed_undo_step_direction(bContext *C, enum eUndoStepDir step, ReportList *reports) { - return ed_undo_step_impl(C, step, NULL, -1, reports); + BLI_assert(ELEM(step, STEP_UNDO, STEP_REDO)); + + CLOG_INFO(&LOG, 1, "direction=%s", (step == STEP_UNDO) ? "STEP_UNDO" : "STEP_REDO"); + + /* TODO(campbell): undo_system: use undo system */ + /* grease pencil can be can be used in plenty of spaces, so check it first */ + /* FIXME: This gpencil undo effectively only supports the one step undo/redo, undo based on name + * or index is fully not implemented. + * FIXME: However, it seems to never be used in current code (`ED_gpencil_session_active` seems + * to always return false). */ + if (ED_gpencil_session_active()) { + return ED_undo_gpencil_step(C, (int)step); + } + + wmWindowManager *wm = CTX_wm_manager(C); + + ed_undo_step_pre(C, wm, step, reports); + + if (step == STEP_UNDO) { + BKE_undosys_step_undo(wm->undo_stack, C); + } + else { + BKE_undosys_step_redo(wm->undo_stack, C); + } + + ed_undo_step_post(C, wm, step, reports); + + return OPERATOR_FINISHED; } +/** Undo the step matching given name. + * May undo several steps at once. + * The target step will be the one immediately before given named one. + * Only supposed to undo (will assert in case given named step is after current active one). */ static int ed_undo_step_by_name(bContext *C, const char *undo_name, ReportList *reports) { - return ed_undo_step_impl(C, 0, undo_name, -1, reports); + BLI_assert(undo_name != NULL); + + /* FIXME: See comments in `ed_undo_step_direction`. */ + if (ED_gpencil_session_active()) { + BLI_assert(!"Not implemented currently."); + } + + wmWindowManager *wm = CTX_wm_manager(C); + UndoStep *undo_step_from_name = BKE_undosys_step_find_by_name(wm->undo_stack, undo_name); + if (undo_step_from_name == NULL) { + CLOG_ERROR(&LOG, "Step name='%s' not found in current undo stack", undo_name); + + return OPERATOR_CANCELLED; + } + + /* TODO(campbell), could use simple optimization. */ + /* Pointers match on redo. */ + const int target_step_index = BLI_findindex(&wm->undo_stack->steps, undo_step_from_name); + const int active_step_index = BLI_findindex(&wm->undo_stack->steps, wm->undo_stack->step_active); + const enum eUndoStepDir undo_dir = (target_step_index < active_step_index) ? STEP_UNDO : + STEP_REDO; + + CLOG_INFO(&LOG, + 1, + "name='%s', found direction=%s, index=%d", + undo_name, + (undo_dir == STEP_UNDO) ? "STEP_UNDO" : "STEP_REDO", + target_step_index); + + /* This function is currently not supposed to redo ever. + * TODO: Will be fixed in future in continuing undo code refactor effort. */ + BLI_assert(undo_dir == STEP_UNDO); + + ed_undo_step_pre(C, wm, undo_dir, reports); + + BKE_undosys_step_undo_with_data(wm->undo_stack, C, undo_step_from_name); + + ed_undo_step_post(C, wm, undo_dir, reports); + + return OPERATOR_FINISHED; } -static int ed_undo_step_by_index(bContext *C, int index, ReportList *reports) +/** Load the step matching given index in the stack. + * May undo or redo several steps at once. + * The target step will be the one indicated by the given index. */ +static int ed_undo_step_by_index(bContext *C, const int undo_index, ReportList *reports) { - return ed_undo_step_impl(C, 0, NULL, index, reports); + BLI_assert(undo_index >= 0); + + /* FIXME: See comments in `ed_undo_step_direction`. */ + if (ED_gpencil_session_active()) { + BLI_assert(!"Not implemented currently."); + } + + wmWindowManager *wm = CTX_wm_manager(C); + const int active_step_index = BLI_findindex(&wm->undo_stack->steps, wm->undo_stack->step_active); + const enum eUndoStepDir undo_dir = (undo_index < active_step_index) ? STEP_UNDO : STEP_REDO; + + CLOG_INFO(&LOG, + 1, + "index='%d', found direction=%s", + undo_index, + (undo_dir == STEP_UNDO) ? "STEP_UNDO" : "STEP_REDO"); + + ed_undo_step_pre(C, wm, undo_dir, reports); + + BKE_undosys_step_undo_from_index(wm->undo_stack, C, undo_index); + + ed_undo_step_post(C, wm, undo_dir, reports); + + return OPERATOR_FINISHED; } void ED_undo_grouped_push(bContext *C, const char *str) @@ -340,11 +389,11 @@ void ED_undo_grouped_push(bContext *C, const char *str) void ED_undo_pop(bContext *C) { - ed_undo_step_direction(C, 1, NULL); + ed_undo_step_direction(C, STEP_UNDO, NULL); } void ED_undo_redo(bContext *C) { - ed_undo_step_direction(C, -1, NULL); + ed_undo_step_direction(C, STEP_REDO, NULL); } void ED_undo_push_op(bContext *C, wmOperator *op) @@ -448,7 +497,7 @@ static int ed_undo_exec(bContext *C, wmOperator *op) { /* "last operator" should disappear, later we can tie this with undo stack nicer */ WM_operator_stack_clear(CTX_wm_manager(C)); - int ret = ed_undo_step_direction(C, 1, op->reports); + int ret = ed_undo_step_direction(C, STEP_UNDO, op->reports); if (ret & OPERATOR_FINISHED) { /* Keep button under the cursor active. */ WM_event_add_mousemove(CTX_wm_window(C)); @@ -477,7 +526,7 @@ static int ed_undo_push_exec(bContext *C, wmOperator *op) static int ed_redo_exec(bContext *C, wmOperator *op) { - int ret = ed_undo_step_direction(C, -1, op->reports); + int ret = ed_undo_step_direction(C, STEP_REDO, op->reports); if (ret & OPERATOR_FINISHED) { /* Keep button under the cursor active. */ WM_event_add_mousemove(CTX_wm_window(C)); diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp index 930da3ef471..a16479873e7 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp @@ -56,11 +56,6 @@ NodeGroup *BlenderFileLoader::Load() // creation of the scene root node _Scene = new NodeGroup; - _viewplane_left = _re->viewplane.xmin; - _viewplane_right = _re->viewplane.xmax; - _viewplane_bottom = _re->viewplane.ymin; - _viewplane_top = _re->viewplane.ymax; - if (_re->clip_start < 0.0f) { // Adjust clipping start/end and set up a Z offset when the viewport preview // is used with the orthographic view. In this case, _re->clip_start is negative, @@ -76,14 +71,6 @@ NodeGroup *BlenderFileLoader::Load() _z_offset = 0.0f; } -#if 0 - if (G.debug & G_DEBUG_FREESTYLE) { - cout << "Frustum: l " << _viewplane_left << " r " << _viewplane_right << " b " - << _viewplane_bottom << " t " << _viewplane_top << " n " << _z_near << " f " << _z_far - << endl; - } -#endif - int id = 0; const eEvaluationMode eval_mode = DEG_get_mode(_depsgraph); diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.h b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.h index 50834db3c5c..1be2fc3bc99 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.h +++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.h @@ -152,10 +152,6 @@ class BlenderFileLoader { real _minEdgeSize; #endif bool _smooth; /* if true, face smoothness is taken into account */ - float _viewplane_left; - float _viewplane_right; - float _viewplane_bottom; - float _viewplane_top; float _z_near, _z_far; float _z_offset; diff --git a/source/blender/freestyle/intern/geometry/GeomUtils.cpp b/source/blender/freestyle/intern/geometry/GeomUtils.cpp index 51727fd2288..d3a3d8bc76e 100644 --- a/source/blender/freestyle/intern/geometry/GeomUtils.cpp +++ b/source/blender/freestyle/intern/geometry/GeomUtils.cpp @@ -435,12 +435,12 @@ bool overlapTriangleBox(Vec3r &boxcenter, Vec3r &boxhalfsize, Vec3r triverts[3]) // Tomas Möller // Prosolvia Clarus AB // Sweden -// tompa@clarus.se +// <tompa@clarus.se> // // Ben Trumbore // Cornell University // Ithaca, New York -// wbt@graphics.cornell.edu +// <wbt@graphics.cornell.edu> bool intersectRayTriangle(const Vec3r &orig, const Vec3r &dir, const Vec3r &v0, diff --git a/source/blender/freestyle/intern/geometry/matrix_util.cpp b/source/blender/freestyle/intern/geometry/matrix_util.cpp index 5b5d9582e8f..95a24d85677 100644 --- a/source/blender/freestyle/intern/geometry/matrix_util.cpp +++ b/source/blender/freestyle/intern/geometry/matrix_util.cpp @@ -17,7 +17,7 @@ * GXML/Graphite: Geometry and Graphics Programming Library + Utilities * Copyright (C) 2000 Bruno Levy * Contact: Bruno Levy - * levy@loria.fr + * <levy@loria.fr> * ISA Project * LORIA, INRIA Lorraine, * Campus Scientifique, BP 239 diff --git a/source/blender/freestyle/intern/geometry/matrix_util.h b/source/blender/freestyle/intern/geometry/matrix_util.h index 8a20cb31300..8c2eb799d13 100644 --- a/source/blender/freestyle/intern/geometry/matrix_util.h +++ b/source/blender/freestyle/intern/geometry/matrix_util.h @@ -17,7 +17,7 @@ * GXML/Graphite: Geometry and Graphics Programming Library + Utilities * Copyright (C) 2000 Bruno Levy * Contact: Bruno Levy - * levy@loria.fr + * <levy@loria.fr> * ISA Project * LORIA, INRIA Lorraine, * Campus Scientifique, BP 239 diff --git a/source/blender/freestyle/intern/geometry/normal_cycle.cpp b/source/blender/freestyle/intern/geometry/normal_cycle.cpp index 01d52646eb0..2310525a1e1 100644 --- a/source/blender/freestyle/intern/geometry/normal_cycle.cpp +++ b/source/blender/freestyle/intern/geometry/normal_cycle.cpp @@ -17,7 +17,7 @@ * OGF/Graphite: Geometry and Graphics Programming Library + Utilities * Copyright (C) 2000 Bruno Levy * Contact: Bruno Levy - * levy@loria.fr + * <levy@loria.fr> * ISA Project * LORIA, INRIA Lorraine, * Campus Scientifique, BP 239 diff --git a/source/blender/freestyle/intern/geometry/normal_cycle.h b/source/blender/freestyle/intern/geometry/normal_cycle.h index 6ac9779e7c2..5adef773be1 100644 --- a/source/blender/freestyle/intern/geometry/normal_cycle.h +++ b/source/blender/freestyle/intern/geometry/normal_cycle.h @@ -17,7 +17,7 @@ * OGF/Graphite: Geometry and Graphics Programming Library + Utilities * Copyright (C) 2000 Bruno Levy * Contact: Bruno Levy - * levy@loria.fr + * <levy@loria.fr> * ISA Project * LORIA, INRIA Lorraine, * Campus Scientifique, BP 239 diff --git a/source/blender/freestyle/intern/winged_edge/Curvature.cpp b/source/blender/freestyle/intern/winged_edge/Curvature.cpp index 0ee491a071c..1984aceb51c 100644 --- a/source/blender/freestyle/intern/winged_edge/Curvature.cpp +++ b/source/blender/freestyle/intern/winged_edge/Curvature.cpp @@ -19,7 +19,7 @@ * and: * OGF/Graphite: Geometry and Graphics Programming Library + Utilities * Copyright (C) 2000-2003 Bruno Levy - * Contact: Bruno Levy levy@loria.fr + * Contact: Bruno Levy <levy@loria.fr> * ISA Project * LORIA, INRIA Lorraine, * Campus Scientifique, BP 239 diff --git a/source/blender/freestyle/intern/winged_edge/Curvature.h b/source/blender/freestyle/intern/winged_edge/Curvature.h index d73674d02cd..0eefc57c3a2 100644 --- a/source/blender/freestyle/intern/winged_edge/Curvature.h +++ b/source/blender/freestyle/intern/winged_edge/Curvature.h @@ -19,7 +19,7 @@ * and: * OGF/Graphite: Geometry and Graphics Programming Library + Utilities * Copyright (C) 2000-2003 Bruno Levy - * Contact: Bruno Levy levy@loria.fr + * Contact: Bruno Levy <levy@loria.fr> * ISA Project * LORIA, INRIA Lorraine, * Campus Scientifique, BP 239 diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c index e2f4d34ff40..f80ad60eb07 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c @@ -110,11 +110,11 @@ static bool dependsOnTime(GpencilModifierData *md) return (mmd->flag & GP_NOISE_USE_RANDOM) != 0; } -static float *noise_table(int len, int seed) +static float *noise_table(int len, int offset, int seed) { float *table = MEM_callocN(sizeof(float) * len, __func__); for (int i = 0; i < len; i++) { - table[i] = BLI_hash_int_01(BLI_hash_int_2d(seed, i + 1)); + table[i] = BLI_hash_int_01(BLI_hash_int_2d(seed, i + offset + 1)); } return table; } @@ -172,11 +172,19 @@ static void deformStroke(GpencilModifierData *md, /* Sanitize as it can create out of bound reads. */ float noise_scale = clamp_f(mmd->noise_scale, 0.0f, 1.0f); - int len = ceilf(gps->totpoints * noise_scale) + 1; - float *noise_table_position = (mmd->factor > 0.0f) ? noise_table(len, seed + 2) : NULL; - float *noise_table_strength = (mmd->factor_strength > 0.0f) ? noise_table(len, seed + 3) : NULL; - float *noise_table_thickness = (mmd->factor_thickness > 0.0f) ? noise_table(len, seed) : NULL; - float *noise_table_uvs = (mmd->factor_uvs > 0.0f) ? noise_table(len, seed + 4) : NULL; + int len = ceilf(gps->totpoints * noise_scale) + 2; + float *noise_table_position = (mmd->factor > 0.0f) ? + noise_table(len, (int)floor(mmd->noise_offset), seed + 2) : + NULL; + float *noise_table_strength = (mmd->factor_strength > 0.0f) ? + noise_table(len, (int)floor(mmd->noise_offset), seed + 3) : + NULL; + float *noise_table_thickness = (mmd->factor_thickness > 0.0f) ? + noise_table(len, (int)floor(mmd->noise_offset), seed) : + NULL; + float *noise_table_uvs = (mmd->factor_uvs > 0.0f) ? + noise_table(len, (int)floor(mmd->noise_offset), seed + 4) : + NULL; /* Calculate stroke normal. */ if (gps->totpoints > 2) { @@ -225,24 +233,27 @@ static void deformStroke(GpencilModifierData *md, cross_v3_v3v3(vec2, vec1, normal); normalize_v3(vec2); - float noise = table_sample(noise_table_position, i * noise_scale); + float noise = table_sample(noise_table_position, + i * noise_scale + fractf(mmd->noise_offset)); madd_v3_v3fl(&pt->x, vec2, (noise * 2.0f - 1.0f) * weight * mmd->factor * 0.1f); } if (mmd->factor_thickness > 0.0f) { - float noise = table_sample(noise_table_thickness, i * noise_scale); + float noise = table_sample(noise_table_thickness, + i * noise_scale + fractf(mmd->noise_offset)); pt->pressure *= max_ff(1.0f + (noise * 2.0f - 1.0f) * weight * mmd->factor_thickness, 0.0f); CLAMP_MIN(pt->pressure, GPENCIL_STRENGTH_MIN); } if (mmd->factor_strength > 0.0f) { - float noise = table_sample(noise_table_strength, i * noise_scale); + float noise = table_sample(noise_table_strength, + i * noise_scale + fractf(mmd->noise_offset)); pt->strength *= max_ff(1.0f - noise * weight * mmd->factor_strength, 0.0f); CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); } if (mmd->factor_uvs > 0.0f) { - float noise = table_sample(noise_table_uvs, i * noise_scale); + float noise = table_sample(noise_table_uvs, i * noise_scale + fractf(mmd->noise_offset)); pt->uv_rot += (noise * 2.0f - 1.0f) * weight * mmd->factor_uvs * M_PI_2; CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); } @@ -292,6 +303,8 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(col, ptr, "factor_thickness", 0, IFACE_("Thickness"), ICON_NONE); uiItemR(col, ptr, "factor_uvs", 0, IFACE_("UV"), ICON_NONE); uiItemR(col, ptr, "noise_scale", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "noise_offset", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "seed", 0, NULL, ICON_NONE); gpencil_modifier_panel_end(layout, ptr); } @@ -316,7 +329,6 @@ static void random_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetActive(layout, RNA_boolean_get(ptr, "random")); uiItemR(layout, ptr, "step", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "seed", 0, NULL, ICON_NONE); } static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc index 407a8dd6e2b..e7a160d1ea3 100644 --- a/source/blender/gpu/intern/gpu_state.cc +++ b/source/blender/gpu/intern/gpu_state.cc @@ -165,6 +165,11 @@ void GPU_depth_range(float near, float far) copy_v2_fl2(state.depth_range, near, far); } +/** + * \note By convention, this is set as needed and not reset back to 1.0. + * This means code that draws lines must always set the line width beforehand, + * but is not expected to restore it's previous value. + */ void GPU_line_width(float width) { width = max_ff(1.0f, width * PIXELSIZE); diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc index 014c70033fc..625ad604b7c 100644 --- a/source/blender/gpu/intern/gpu_vertex_format.cc +++ b/source/blender/gpu/intern/gpu_vertex_format.cc @@ -200,7 +200,7 @@ void GPU_vertformat_alias_add(GPUVertFormat *format, const char *alias) * * WARNING: this function creates a lot of aliases/attributes, make sure to keep the attribute * name short to avoid overflowing the name-buffer. - * */ + */ void GPU_vertformat_multiload_enable(GPUVertFormat *format, int load_count) { /* Sanity check. Maximum can be upgraded if needed. */ diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 1d76b07c966..c49158176c6 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -244,15 +244,16 @@ static void detect_workarounds() if (!GLEW_VERSION_4_0) { GLContext::base_instance_support = false; } - /* The renderers include: - * Mobility Radeon HD 5000; - * Radeon HD 7500M; - * Radeon HD 7570M; - * Radeon HD 7600M; - * And many others... */ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_WIN, GPU_DRIVER_OFFICIAL) && (strstr(version, "4.5.13399") || strstr(version, "4.5.13417") || - strstr(version, "4.5.13422"))) { + strstr(version, "4.5.13422") || strstr(version, "4.5.13467"))) { + /* The renderers include: + * Radeon HD 5000; + * Radeon HD 7500M; + * Radeon HD 7570M; + * Radeon HD 7600M; + * Radeon R5 Graphics; + * And others... */ GLContext::unused_fb_slot_workaround = true; GCaps.mip_render_workaround = true; GCaps.shader_image_load_store_support = false; diff --git a/source/blender/ikplugin/intern/itasc_plugin.cpp b/source/blender/ikplugin/intern/itasc_plugin.cpp index 3f444172499..eb31b0e9f87 100644 --- a/source/blender/ikplugin/intern/itasc_plugin.cpp +++ b/source/blender/ikplugin/intern/itasc_plugin.cpp @@ -1878,7 +1878,7 @@ static void execute_scene(struct Depsgraph *depsgraph, /*--------------------------------------------------- * plugin interface - * */ + */ void itasc_initialize_tree(struct Depsgraph *depsgraph, struct Scene *scene, Object *ob, diff --git a/source/blender/imbuf/intern/IMB_allocimbuf.h b/source/blender/imbuf/intern/IMB_allocimbuf.h index 08aa1936a6f..c92d764a104 100644 --- a/source/blender/imbuf/intern/IMB_allocimbuf.h +++ b/source/blender/imbuf/intern/IMB_allocimbuf.h @@ -32,7 +32,7 @@ struct ImBuf; void imb_refcounter_lock_init(void); void imb_refcounter_lock_exit(void); -#ifdef WIN32 +#ifndef WIN32 void imb_mmap_lock_init(void); void imb_mmap_lock_exit(void); void imb_mmap_lock(void); diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c index 8dfb3ada7d6..bfb7bc93ac7 100644 --- a/source/blender/imbuf/intern/allocimbuf.c +++ b/source/blender/imbuf/intern/allocimbuf.c @@ -53,7 +53,7 @@ void imb_refcounter_lock_exit(void) BLI_spin_end(&refcounter_spin); } -#ifdef WIN32 +#ifndef WIN32 static SpinLock mmap_spin; void imb_mmap_lock_init(void) diff --git a/source/blender/imbuf/intern/dds/ColorBlock.h b/source/blender/imbuf/intern/dds/ColorBlock.h index 0271c7964b8..9c3d73bbb0d 100644 --- a/source/blender/imbuf/intern/dds/ColorBlock.h +++ b/source/blender/imbuf/intern/dds/ColorBlock.h @@ -25,7 +25,7 @@ * Original license from NVIDIA follows. */ -// This code is in the public domain -- castanyo@yahoo.es +// This code is in the public domain -- <castanyo@yahoo.es> #pragma once diff --git a/source/blender/imbuf/intern/filter.c b/source/blender/imbuf/intern/filter.c index cce264624ce..343c8cd8f64 100644 --- a/source/blender/imbuf/intern/filter.c +++ b/source/blender/imbuf/intern/filter.c @@ -425,7 +425,7 @@ static int check_pixel_assigned( * * When a mask is given, only effect pixels with a mask value of 1, * defined as #BAKE_MASK_MARGIN in rendercore.c - * */ + */ void IMB_filter_extend(struct ImBuf *ibuf, char *mask, int filter) { const int width = ibuf->x; diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c index f0daa4543e1..50210650f05 100644 --- a/source/blender/imbuf/intern/readimage.c +++ b/source/blender/imbuf/intern/readimage.c @@ -23,13 +23,13 @@ */ #ifdef _WIN32 -# include "mmap_win.h" # include <io.h> # include <stddef.h> # include <sys/types.h> #endif #include "BLI_fileops.h" +#include "BLI_mmap.h" #include "BLI_path_util.h" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -186,20 +186,19 @@ ImBuf *IMB_loadifffile( size = BLI_file_descriptor_size(file); imb_mmap_lock(); - mem = mmap(NULL, size, PROT_READ, MAP_SHARED, file, 0); + BLI_mmap_file *mmap_file = BLI_mmap_open(file); imb_mmap_unlock(); - - if (mem == (unsigned char *)-1) { + if (mmap_file == NULL) { fprintf(stderr, "%s: couldn't get mapping %s\n", __func__, descr); return NULL; } + mem = BLI_mmap_get_pointer(mmap_file); + ibuf = IMB_ibImageFromMemory(mem, size, flags, colorspace, descr); imb_mmap_lock(); - if (munmap(mem, size)) { - fprintf(stderr, "%s: couldn't unmap file %s\n", __func__, descr); - } + BLI_mmap_free(mmap_file); imb_mmap_unlock(); return ibuf; @@ -292,14 +291,15 @@ static void imb_loadtilefile(ImBuf *ibuf, int file, int tx, int ty, unsigned int size = BLI_file_descriptor_size(file); imb_mmap_lock(); - mem = mmap(NULL, size, PROT_READ, MAP_SHARED, file, 0); + BLI_mmap_file *mmap_file = BLI_mmap_open(file); imb_mmap_unlock(); - - if (mem == (unsigned char *)-1) { + if (mmap_file == NULL) { fprintf(stderr, "Couldn't get memory mapping for %s\n", ibuf->cachename); return; } + mem = BLI_mmap_get_pointer(mmap_file); + const ImFileType *type = IMB_file_type_from_ibuf(ibuf); if (type != NULL) { if (type->load_tile != NULL) { @@ -308,9 +308,7 @@ static void imb_loadtilefile(ImBuf *ibuf, int file, int tx, int ty, unsigned int } imb_mmap_lock(); - if (munmap(mem, size)) { - fprintf(stderr, "Couldn't unmap memory for %s.\n", ibuf->cachename); - } + BLI_mmap_free(mmap_file); imb_mmap_unlock(); } diff --git a/source/blender/io/alembic/exporter/abc_archive.cc b/source/blender/io/alembic/exporter/abc_archive.cc index 68ad2089a3e..90a4baf97bc 100644 --- a/source/blender/io/alembic/exporter/abc_archive.cc +++ b/source/blender/io/alembic/exporter/abc_archive.cc @@ -112,7 +112,7 @@ static OArchive *create_archive(std::ofstream *abc_ostream, * * If 'time_relative' is true, samples are returned as time (in seconds) from params.frame_start. * If 'time_relative' is false, samples are returned as fractional frames from 0. - * */ + */ static void get_shutter_samples(double scene_fps, const AlembicExportParams ¶ms, int nr_of_samples, diff --git a/source/blender/io/common/IO_dupli_persistent_id.hh b/source/blender/io/common/IO_dupli_persistent_id.hh index 6fabafd9d51..afc539636ac 100644 --- a/source/blender/io/common/IO_dupli_persistent_id.hh +++ b/source/blender/io/common/IO_dupli_persistent_id.hh @@ -41,7 +41,7 @@ class PersistentID { PersistentID(); explicit PersistentID(const DupliObject *dupli_ob); - /* Return true iff the persistent IDs are the same, ignoring the first digit. */ + /* Return true if the persistent IDs are the same, ignoring the first digit. */ bool is_from_same_instancer_as(const PersistentID &other) const; /* Construct the persistent ID of this instance's instancer. */ diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 51c47e917f1..9ca1c655066 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -193,6 +193,10 @@ enum { IDOVERRIDE_LIBRARY_FLAG_MANDATORY = 1 << 0, /** User cannot change that override operation. */ IDOVERRIDE_LIBRARY_FLAG_LOCKED = 1 << 1, + + /** For overrides of ID pointers: this override still matches (follows) the hierarchy of the + * reference linked data. */ + IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE = 1 << 8, }; /** A single overridden property, contain all operations on this one. */ @@ -254,7 +258,7 @@ typedef struct IDOverrideLibrary { /** * ID is the first thing included in all serializable types. It * provides a common handle to place all data in double-linked lists. - * */ + */ /* 2 characters for ID code and 64 for actual name */ #define MAX_ID_NAME 66 @@ -449,7 +453,7 @@ typedef enum ID_Type { ID_TXT = MAKE_ID2('T', 'X'), /* Text */ ID_SPK = MAKE_ID2('S', 'K'), /* Speaker */ ID_SO = MAKE_ID2('S', 'O'), /* Sound */ - ID_GR = MAKE_ID2('G', 'R'), /* Group */ + ID_GR = MAKE_ID2('G', 'R'), /* Collection */ ID_AR = MAKE_ID2('A', 'R'), /* bArmature */ ID_AC = MAKE_ID2('A', 'C'), /* bAction */ ID_NT = MAKE_ID2('N', 'T'), /* bNodeTree */ @@ -468,7 +472,7 @@ typedef enum ID_Type { ID_HA = MAKE_ID2('H', 'A'), /* Hair */ ID_PT = MAKE_ID2('P', 'T'), /* PointCloud */ ID_VO = MAKE_ID2('V', 'O'), /* Volume */ - ID_SIM = MAKE_ID2('S', 'I'), /* Simulation (currently unused) */ + ID_SIM = MAKE_ID2('S', 'I'), /* Simulation (geometry node groups) */ } ID_Type; /* Only used as 'placeholder' in .blend files for directly linked data-blocks. */ @@ -684,7 +688,7 @@ typedef enum IDRecalcFlag { * redraw update in that case. */ /* Selection of the ID itself or its components (for example, vertices) did - * change, and all the drawing data is to eb updated. */ + * change, and all the drawing data is to be updated. */ ID_RECALC_SELECT = (1 << 9), /* Flags on the base did change, and is to be copied onto all the copies of * corresponding objects. */ diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 4b020019062..e373500a0ed 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -47,10 +47,13 @@ typedef struct BrushClone { char _pad[4]; } BrushClone; +#define GPENCIL_MIN_FILL_FAC 0.05f + typedef struct BrushGpencilSettings { /** Amount of smoothing to apply to newly created strokes. */ float draw_smoothfac; - char _pad2[4]; + /** Fill zoom factor */ + float fill_factor; /** Amount of alpha strength to apply to newly created strokes. */ float draw_strength; /** Amount of jitter to apply to newly created strokes. */ @@ -75,8 +78,8 @@ typedef struct BrushGpencilSettings { float fill_threshold; /** Number of pixel to consider the leak is too small (x 2). */ short fill_leak; - /** Fill zoom factor */ - short fill_factor; + char _pad2[2]; + int flag2; /** Number of simplify steps. */ diff --git a/source/blender/makesdna/DNA_dynamicpaint_types.h b/source/blender/makesdna/DNA_dynamicpaint_types.h index c8e09225432..e3dcd283efa 100644 --- a/source/blender/makesdna/DNA_dynamicpaint_types.h +++ b/source/blender/makesdna/DNA_dynamicpaint_types.h @@ -242,10 +242,12 @@ typedef struct DynamicPaintBrushSettings { /** For fast RNA access. */ struct DynamicPaintModifierData *pmd; - /* NOTE: Storing the particle system pointer here is very weak, as it prevents modfiers' data + /** + * \note Storing the particle system pointer here is very weak, as it prevents modifiers' data * copying to be self-sufficient (extra external code needs to ensure the pointer remains valid * when the modifier data is copied from one object to another). See e.g. - * `BKE_object_copy_particlesystems` or `BKE_object_copy_modifier`. */ + * `BKE_object_copy_particlesystems` or `BKE_object_copy_modifier`. + */ struct ParticleSystem *psys; int flags; diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index fdb77bfd196..399bf6f0a6d 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -152,6 +152,7 @@ .factor_thickness = 0.0f, \ .factor_uvs = 0.0f, \ .noise_scale = 0.0f, \ + .noise_offset = 0.0f, \ .step = 4, \ .layer_pass = 0, \ .seed = 1, \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 9ac40495887..1f7283a1c70 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -107,6 +107,8 @@ typedef struct NoiseGpencilModifierData { float factor_uvs; /** Noise Frequency scaling */ float noise_scale; + float noise_offset; + char _pad[4]; /** How many frames before recalculate randoms. */ int step; /** Custom index for passes. */ diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 949b0bb5bf5..1dbfd547673 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -512,6 +512,11 @@ typedef struct bGPDlayer { int act_mask; char _pad2[4]; + /** Layer transforms. */ + float location[3], rotation[3], scale[3]; + float layer_mat[4][4], layer_invmat[4][4]; + char _pad3[4]; + bGPDlayer_Runtime runtime; } bGPDlayer; @@ -574,7 +579,9 @@ typedef struct bGPdata_Runtime { /** Temp stroke used for drawing. */ struct bGPDstroke *sbuffer_gps; - char _pad[2]; + /** Animation playing flag. */ + short playing; + /** Material index of the stroke. */ short matid; @@ -840,6 +847,8 @@ typedef enum eGP_DrawMode { ((flag & (GP_VERTEX_MASK_SELECTMODE_POINT | GP_VERTEX_MASK_SELECTMODE_STROKE | \ GP_VERTEX_MASK_SELECTMODE_SEGMENT))) +#define GPENCIL_PLAY_ON(gpd) ((gpd) && ((gpd)->runtime.playing == 1)) + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index 8601dcf76ac..860a9affb3e 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -440,7 +440,7 @@ .renderlvl = 0, \ .totlvl = 0, \ .flags = eMultiresModifierFlag_UseCrease | eMultiresModifierFlag_ControlEdges, \ - .uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_CORNERS, \ + .uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES, \ .quality = 4, \ .boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL, \ } @@ -622,7 +622,7 @@ .levels = 1, \ .renderLevels = 2, \ .flags = eSubsurfModifierFlag_UseCrease | eSubsurfModifierFlag_ControlEdges, \ - .uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_CORNERS, \ + .uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES, \ .quality = 3, \ .boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL, \ .emCache = NULL, \ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 3de4299e0bd..29421430e5d 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -984,10 +984,12 @@ enum { typedef struct ParticleSystemModifierData { ModifierData modifier; - /* NOTE: Storing the particle system pointer here is very weak, as it prevents modfiers' data + /** + * \note Storing the particle system pointer here is very weak, as it prevents modifiers' data * copying to be self-sufficient (extra external code needs to ensure the pointer remains valid * when the modifier data is copied from one object to another). See e.g. - * `BKE_object_copy_particlesystems` or `BKE_object_copy_modifier`. */ + * `BKE_object_copy_particlesystems` or `BKE_object_copy_modifier`. + */ struct ParticleSystem *psys; /** Final Mesh - its topology may differ from orig mesh. */ struct Mesh *mesh_final; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 7d18ff3ed58..0c92099e23b 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1089,14 +1089,15 @@ typedef struct NodeAttributeCompare { } NodeAttributeCompare; typedef struct NodeAttributeMath { - /* e.g. NODE_MATH_ADD. */ + /* NodeMathOperation. */ uint8_t operation; /* GeometryNodeAttributeInputMode */ uint8_t input_type_a; uint8_t input_type_b; + uint8_t input_type_c; - char _pad[5]; + char _pad[4]; } NodeAttributeMath; typedef struct NodeAttributeMix { @@ -1174,6 +1175,24 @@ typedef struct NodeGeometryObjectInfo { char _pad[7]; } NodeGeometryObjectInfo; +typedef struct NodeGeometryPointInstance { + /* GeometryNodePointInstanceType. */ + uint8_t instance_type; + /* GeometryNodePointInstanceFlag. */ + uint8_t flag; + + char _pad[6]; +} NodeGeometryPointInstance; + +typedef struct NodeGeometryPointsToVolume { + /* GeometryNodePointsToVolumeResolutionMode */ + uint8_t resolution_mode; + /* GeometryNodeAttributeInputMode */ + uint8_t input_type_radius; + + char _pad[6]; +} NodeGeometryPointsToVolume; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -1369,7 +1388,7 @@ enum { #define SHD_MATH_CLAMP 1 /* Math node operations. */ -enum { +typedef enum NodeMathOperation { NODE_MATH_ADD = 0, NODE_MATH_SUBTRACT = 1, NODE_MATH_MULTIPLY = 2, @@ -1410,7 +1429,7 @@ enum { NODE_MATH_PINGPONG = 37, NODE_MATH_SMOOTH_MIN = 38, NODE_MATH_SMOOTH_MAX = 39, -}; +} NodeMathOperation; /* Vector Math node operations. */ typedef enum NodeVectorMathOperation { @@ -1586,6 +1605,10 @@ typedef enum GeometryNodePointInstanceType { GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION = 1, } GeometryNodePointInstanceType; +typedef enum GeometryNodePointInstanceFlag { + GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION = (1 << 0), +} GeometryNodePointInstanceFlag; + typedef enum GeometryNodeAttributeInputMode { GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE = 0, GEO_NODE_ATTRIBUTE_INPUT_FLOAT = 1, @@ -1620,6 +1643,11 @@ typedef enum GeometryNodeTransformSpace { GEO_NODE_TRANSFORM_SPACE_RELATIVE = 1, } GeometryNodeTransformSpace; +typedef enum GeometryNodePointsToVolumeResolutionMode { + GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT = 0, + GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE = 1, +} GeometryNodePointsToVolumeResolutionMode; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 03f5d3f8d47..51f8b58da62 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -63,6 +63,9 @@ struct wmTimer; /* Defined in `buttons_intern.h`. */ typedef struct SpaceProperties_Runtime SpaceProperties_Runtime; +/* Defined in `node_intern.h`. */ +typedef struct SpaceNode_Runtime SpaceNode_Runtime; + /* -------------------------------------------------------------------- */ /** \name SpaceLink (Base) * \{ */ @@ -1084,6 +1087,8 @@ typedef struct FileDirEntry { struct FileDirEntry *next, *prev; int uuid[4]; + /* Name needs freeing if FILE_ENTRY_NAME_FREE is set. Otherwise this is a direct pointer to a + * name buffer. */ char *name; char *description; @@ -1162,6 +1167,7 @@ enum { /* FileDirEntry.flags */ enum { FILE_ENTRY_INVALID_PREVIEW = 1 << 0, /* The preview for this entry could not be generated. */ + FILE_ENTRY_NAME_FREE = 1 << 1, }; /** \} */ @@ -1518,19 +1524,17 @@ typedef struct SpaceNode { /** Context, no need to save in file? well... pinning... */ struct ID *id, *from; - /** Menunr: browse id block in header. */ + short flag; - char _pad1[2]; - /** Internal state variables. */ - float aspect; - char _pad2[4]; + + /** Direction for offsetting nodes on insertion. */ + char insert_ofs_dir; + char _pad1; /** Offset for drawing the backdrop. */ float xof, yof; /** Zoom for backdrop. */ float zoom; - /** Mouse pos for drawing socketless link and adding nodes. */ - float cursor[2]; /** * XXX nodetree pointer info is all in the path stack now, @@ -1541,33 +1545,25 @@ typedef struct SpaceNode { */ ListBase treepath; - struct bNodeTree *nodetree, *edittree; + /* The tree farthest down in the group heirarchy. */ + struct bNodeTree *edittree; + + struct bNodeTree *nodetree; /* tree type for the current node tree */ char tree_idname[64]; /** Treetype: as same nodetree->type. */ int treetype DNA_DEPRECATED; - char _pad3[4]; /** Texfrom object, world or brush. */ short texfrom; /** Shader from object or world. */ short shaderfrom; - /** Currently on 0/1, for auto compo. */ - short recalc; - - /** Direction for offsetting nodes on insertion. */ - char insert_ofs_dir; - char _pad4; - - /** Temporary data for modal linking operator. */ - ListBase linkdrag; - /* XXX hack for translate_attach op-macros to pass data from transform op to insert_offset op */ - /** Temporary data for node insert offset (in UI called Auto-offset). */ - struct NodeInsertOfsData *iofsd; /** Grease-pencil data. */ struct bGPdata *gpd; + + SpaceNode_Runtime *runtime; } SpaceNode; /* SpaceNode.flag */ diff --git a/source/blender/makesdna/DNA_tracking_types.h b/source/blender/makesdna/DNA_tracking_types.h index 5b6b706c83c..f08aee317a3 100644 --- a/source/blender/makesdna/DNA_tracking_types.h +++ b/source/blender/makesdna/DNA_tracking_types.h @@ -141,7 +141,7 @@ typedef struct MovieTrackingTrack { /** Count of markers in track. */ int markersnr; /** Most recently used marker. */ - int last_marker; + int _pad; /** Markers in track. */ MovieTrackingMarker *markers; @@ -251,8 +251,6 @@ typedef struct MovieTrackingPlaneTrack { } MovieTrackingPlaneTrack; typedef struct MovieTrackingSettings { - int flag; - /* ** default tracker settings */ /** Model of the motion for this track. */ short default_motion_model; @@ -309,8 +307,6 @@ typedef struct MovieTrackingSettings { /* set object scale */ /** Distance between two bundles used for object scaling. */ float object_distance; - - char _pad3[4]; } MovieTrackingSettings; typedef struct MovieTrackingStabilization { @@ -526,12 +522,6 @@ typedef enum eTrackFrameMatch { TRACK_MATCH_PREVIOS_FRAME = 1, } eTrackFrameMatch; -/* MovieTrackingSettings->flag */ -enum { - TRACKING_SETTINGS_SHOW_DEFAULT_EXPANDED = (1 << 0), - TRACKING_SETTINGS_SHOW_EXTRA_EXPANDED = (1 << 1), -}; - /* MovieTrackingSettings->motion_flag */ enum { TRACKING_MOTION_TRIPOD = (1 << 0), diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index f620602f051..790f3423eef 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -762,8 +762,12 @@ typedef struct UserDef { char _pad13[4]; struct SolidLight light_param[4]; float light_ambient[3]; - char _pad3[4]; - short gizmo_flag, gizmo_size; + char gizmo_flag; + /** Generic gizmo size. */ + char gizmo_size; + /** Navigate gizmo size. */ + char gizmo_size_navigate_v3d; + char _pad3[5]; short edit_studio_light; short lookdev_sphere_size; short vbotimeout, vbocollectrate; diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt index eb7779213fd..db34cf83fa9 100644 --- a/source/blender/makesdna/intern/CMakeLists.txt +++ b/source/blender/makesdna/intern/CMakeLists.txt @@ -48,12 +48,6 @@ set(SRC ../../../../intern/guardedalloc/intern/mallocn_lockfree_impl.c ) -if(WIN32 AND NOT UNIX) - list(APPEND SRC - ../../../../intern/guardedalloc/intern/mmap_win.c - ) -endif() - # SRC_DNA_INC is defined in the parent dir add_cc_flags_custom_test(makesdna) diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 54d2bc88d16..843e6935416 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -397,13 +397,11 @@ static int add_type(const char *str, int size) } /** - * * Because of the weird way of tokenizing, we have to 'cast' function * pointers to ... (*f)(), whatever the original signature. In fact, * we add name and type at the same time... There are two special * cases, unfortunately. These are explicitly checked. - * - * */ + */ static int add_name(const char *str) { char buf[255]; /* stupid limit, change it :) */ @@ -479,17 +477,16 @@ static int add_name(const char *str) } /* - * Put )(void) at the end? Maybe )(). Should check this with - * old sdna. Actually, sometimes )(), sometimes )(void...) + * Put `)(void)` at the end? Maybe `)()`. Should check this with + * old `sdna`. Actually, sometimes `)()`, sometimes `)(void...)` * Alas.. such is the nature of brain-damage :( * - * Sorted it out: always do )(), except for headdraw and - * windraw, part of ScrArea. This is important, because some + * Sorted it out: always do )(), except for `headdraw` and + * `windraw`, part of #ScrArea. This is important, because some * linkers will treat different fp's differently when called * !!! This has to do with interference in byte-alignment and - * the way args are pushed on the stack. - * - * */ + * the way arguments are pushed on the stack. + */ buf[i] = 0; DEBUG_PRINTF(3, "Name before chomping: %s\n", buf); if ((strncmp(buf, "(*headdraw", 10) == 0) || (strncmp(buf, "(*windraw", 9) == 0)) { diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 72cdaecb2c3..94cfd8464ae 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -189,7 +189,6 @@ set(SRC ../../../../intern/guardedalloc/intern/mallocn.c ../../../../intern/guardedalloc/intern/mallocn_guarded_impl.c ../../../../intern/guardedalloc/intern/mallocn_lockfree_impl.c - ../../../../intern/guardedalloc/intern/mmap_win.c # Needed for defaults. ../../../../release/datafiles/userdef/userdef_default.c diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index e9e00ff6f71..bec3db10905 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -4758,8 +4758,14 @@ static const char *cpp_classes = "class CollectionIterator {\n" "public:\n" " CollectionIterator() : iter(), t(iter.ptr), init(false) { iter.valid = false; }\n" + " CollectionIterator(const PointerRNA &ptr) : CollectionIterator() { this->begin(ptr); }\n" " ~CollectionIterator(void) { if (init) Tend(&iter); };\n" "\n" + " CollectionIterator(const CollectionIterator &other) = delete;\n" + " CollectionIterator(CollectionIterator &&other) = delete;\n" + " CollectionIterator &operator=(const CollectionIterator &other) = delete;\n" + " CollectionIterator &operator=(CollectionIterator &&other) = delete;\n" + "\n" " operator bool(void)\n" " { return iter.valid != 0; }\n" " const CollectionIterator<T, Tbegin, Tnext, Tend>& operator++() { Tnext(&iter); t = " @@ -4777,9 +4783,6 @@ static const char *cpp_classes = "true; }\n" "\n" "private:\n" - " const CollectionIterator<T, Tbegin, Tnext, Tend>& operator = " - "(const CollectionIterator<T, Tbegin, Tnext, Tend>& /*copy*/) {}\n" - "" " CollectionPropertyIterator iter;\n" " T t;\n" " bool init;\n" @@ -4794,6 +4797,8 @@ static const char *cpp_classes = "\n" " void begin(CollectionIterator<T, Tbegin, Tnext, Tend>& iter)\n" " { iter.begin(ptr); }\n" + " CollectionIterator<T, Tbegin, Tnext, Tend> begin()\n" + " { return CollectionIterator<T, Tbegin, Tnext, Tend>(ptr); }\n" " CollectionIterator<T, Tbegin, Tnext, Tend> end()\n" " { return CollectionIterator<T, Tbegin, Tnext, Tend>(); } /* test */ \n" "" diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 1e7f5e841ba..2d6f1bbc059 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -42,6 +42,7 @@ #include "BKE_armature.h" #include "BKE_idprop.h" +#include "BKE_idtype.h" #include "BKE_lib_override.h" #include "BKE_main.h" @@ -1178,6 +1179,17 @@ void RNA_struct_override_apply(Main *bmain, #endif } } + + /* Some cases (like point caches) may require additional post-processing. */ + if (RNA_struct_is_a(ptr_dst->type, &RNA_ID)) { + ID *id_dst = ptr_dst->data; + ID *id_src = ptr_src->data; + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id_dst); + if (id_type->lib_override_apply_post != NULL) { + id_type->lib_override_apply_post(id_dst, id_src); + } + } + #ifdef DEBUG_OVERRIDE_TIMEIT TIMEIT_END_AVERAGED(RNA_struct_override_apply); #endif diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 2af6c04147c..4f98c6e8e07 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1464,13 +1464,13 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); /* fill factor size */ - prop = RNA_def_property(srna, "fill_factor", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "fill_factor"); - RNA_def_property_range(prop, 1, 8); + prop = RNA_def_property(srna, "fill_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "fill_factor"); + RNA_def_property_range(prop, GPENCIL_MIN_FILL_FAC, 8.0f); RNA_def_property_ui_text( prop, - "Resolution", - "Multiplier for fill resolution, higher resolution is more accurate but slower"); + "Precision", + "Factor for fill boundary accuracy, higher values are more accurate but slower"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index cec6a50d170..da04ac63ee3 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -4111,6 +4111,16 @@ PropertyRNA *RNA_def_float_percentage(StructOrFunctionRNA *cont_, ASSERT_SOFT_HARD_LIMITS; +#ifdef DEBUG + /* Properties with PROP_PERCENTAGE should use a range like 0 to 100, unlike PROP_FACTOR. */ + if (hardmax < 2.0f) { + CLOG_WARN(&LOG, + "Percentage property with incorrect range: %s.%s", + CONTAINER_RNA_ID(cont), + identifier); + } +#endif + prop = RNA_def_property(cont, identifier, PROP_FLOAT, PROP_PERCENTAGE); RNA_def_property_float_default(prop, default_value); if (hardmin != hardmax) { diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 81e20003d8f..209dc21d386 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -182,6 +182,16 @@ static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Pointe WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); } +static void rna_GpencilLayerMatrix_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + bGPDlayer *gpl = (bGPDlayer *)ptr->data; + + loc_eul_size_to_mat4(gpl->layer_mat, gpl->location, gpl->rotation, gpl->scale); + invert_m4_m4(gpl->layer_invmat, gpl->layer_mat); + + rna_GPencil_update(bmain, scene, ptr); +} + static void rna_GPencil_curve_edit_mode_toggle(Main *bmain, Scene *scene, PointerRNA *ptr) { ToolSettings *ts = scene->toolsettings; @@ -940,7 +950,7 @@ static void rna_GPencil_frame_remove(bGPDlayer *layer, ReportList *reports, Poin static bGPDframe *rna_GPencil_frame_copy(bGPDlayer *layer, bGPDframe *src) { - bGPDframe *frame = BKE_gpencil_frame_duplicate(src); + bGPDframe *frame = BKE_gpencil_frame_duplicate(src, true); while (BKE_gpencil_layer_frame_find(layer, frame->framenum)) { frame->framenum++; @@ -2012,6 +2022,45 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Blend Mode", "Blend mode"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Layer transforms. */ + prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_float_sdna(prop, NULL, "location"); + RNA_def_property_ui_text(prop, "Location", "Values for change location"); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); + RNA_def_property_update(prop, 0, "rna_GpencilLayerMatrix_update"); + + prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_EULER); + RNA_def_property_float_sdna(prop, NULL, "rotation"); + RNA_def_property_ui_text(prop, "Rotation", "Values for changes in rotation"); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); + RNA_def_property_update(prop, 0, "rna_GpencilLayerMatrix_update"); + + prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "scale"); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_ui_text(prop, "Scale", "Values for changes in scale"); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); + RNA_def_property_update(prop, 0, "rna_GpencilLayerMatrix_update"); + + /* Layer matrix. */ + prop = RNA_def_property(srna, "matrix_layer", PROP_FLOAT, PROP_MATRIX); + RNA_def_property_float_sdna(prop, NULL, "layer_mat"); + RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); + RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_ui_text(prop, "Matrix Layer", "Local Layer transformation matrix"); + + /* Layer inverse matrix. */ + prop = RNA_def_property(srna, "matrix_inverse_layer", PROP_FLOAT, PROP_MATRIX); + RNA_def_property_float_sdna(prop, NULL, "layer_invmat"); + RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); + RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_ui_text( + prop, "Matrix Layer Inverse", "Local Layer transformation inverse matrix"); + /* Flags */ prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_HIDE); diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 89eb989a442..71d5a53adb2 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -489,7 +489,7 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "seed", PROP_INT, PROP_UNSIGNED); - RNA_def_property_ui_text(prop, "Seed", "Random seed"); + RNA_def_property_ui_text(prop, "Noise Seed", "Random seed"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "noise_scale", PROP_FLOAT, PROP_FACTOR); @@ -498,6 +498,13 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Noise Scale", "Scale the noise frequency"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "noise_offset", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "noise_offset"); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 100.0, 0.1, 3); + RNA_def_property_ui_text(prop, "Noise Offset", "Offset the noise along the strokes"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_custom_curve", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_NOISE_CUSTOM_CURVE); RNA_def_property_ui_text( diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index e02e47745b0..c13f592f7fb 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -1632,33 +1632,35 @@ static IDProperty *rna_NodesModifier_properties(PointerRNA *ptr, bool create) static void rna_def_property_subdivision_common(StructRNA *srna) { static const EnumPropertyItem prop_uv_smooth_items[] = { - {SUBSURF_UV_SMOOTH_NONE, "NONE", 0, "None", "UVs are not smoothed, boundaries are kept sharp"}, - {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS, - "PRESERVE_CORNERS", - 0, - "Keep Corners", - "UVs are smoothed, corners on discontinuous boundary are kept sharp"}, -# if 0 - {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_AND_JUNCTIONS, - "PRESERVE_CORNERS_AND_JUNCTIONS", - 0, - "Smooth, keep corners+junctions", - "UVs are smoothed, corners on discontinuous boundary and " - "junctions of 3 or more regions are kept sharp"}, - {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE, - "PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE", - 0, - "Smooth, keep corners+junctions+concave", - "UVs are smoothed, corners on discontinuous boundary, " - "junctions of 3 or more regions and darts and concave corners are kept sharp"}, - {SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES, - "PRESERVE_BOUNDARIES", - 0, - "Smooth, keep corners", - "UVs are smoothed, boundaries are kept sharp"}, -# endif - {SUBSURF_UV_SMOOTH_ALL, "PRESERVE_BOUNDARIES", 0, "All", "UVs and boundaries are smoothed"}, - {0, NULL, 0, NULL, NULL}, + {SUBSURF_UV_SMOOTH_NONE, + "NONE", + 0, + "None", + "UVs are not smoothed, boundaries are kept sharp"}, + {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS, + "PRESERVE_CORNERS", + 0, + "Keep Corners", + "UVs are smoothed, corners on discontinuous boundary are kept sharp"}, + {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_AND_JUNCTIONS, + "PRESERVE_CORNERS_AND_JUNCTIONS", + 0, + "Keep Corners, Junctions", + "UVs are smoothed, corners on discontinuous boundary and " + "junctions of 3 or more regions are kept sharp"}, + {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE, + "PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE", + 0, + "Keep Corners, Junctions, Concave", + "UVs are smoothed, corners on discontinuous boundary, " + "junctions of 3 or more regions and darts and concave corners are kept sharp"}, + {SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES, + "PRESERVE_BOUNDARIES", + 0, + "Keep boundaries", + "UVs are smoothed, boundaries are kept sharp"}, + {SUBSURF_UV_SMOOTH_ALL, "SMOOTH_ALL", 0, "All", "UVs and boundaries are smoothed"}, + {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem prop_boundary_smooth_items[] = { diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 1cd3593e4a4..a4dcc677035 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -1980,22 +1980,6 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeFill_domain_itemf( return itemf_function_check(rna_enum_attribute_domain_items, attribute_fill_domain_supported); } -static bool attribute_math_operation_supported(const EnumPropertyItem *item) -{ - return ELEM(item->value, - NODE_MATH_ADD, - NODE_MATH_SUBTRACT, - NODE_MATH_MULTIPLY, - NODE_MATH_DIVIDE) && - (item->identifier[0] != '\0'); -} -static const EnumPropertyItem *rna_GeometryNodeAttributeMath_operation_itemf( - bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) -{ - *r_free = true; - return itemf_function_check(rna_enum_node_math_items, attribute_math_operation_supported); -} - /** * This bit of ugly code makes sure the float / attribute option shows up instead of * vector / attribute if the node uses an operation that uses a float for input B. @@ -4757,9 +4741,10 @@ static void def_sh_tex_sky(StructRNA *srna) RNA_def_property_float_default(prop, 0.0f); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); - prop = RNA_def_property(srna, "altitude", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "altitude", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_ui_text(prop, "Altitude", "Height from sea level"); - RNA_def_property_range(prop, 0.0f, 60.0f); + RNA_def_property_range(prop, 0.0f, 60000.0f); + RNA_def_property_ui_range(prop, 0.0f, 60000.0f, 10, 1); RNA_def_property_float_default(prop, 0.0f); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); @@ -8579,10 +8564,9 @@ static void def_geo_attribute_math(StructRNA *srna) prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "operation"); RNA_def_property_enum_items(prop, rna_enum_node_math_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeMath_operation_itemf"); RNA_def_property_enum_default(prop, NODE_MATH_ADD); RNA_def_property_ui_text(prop, "Operation", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_a"); @@ -8595,6 +8579,12 @@ static void def_geo_attribute_math(StructRNA *srna) RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); RNA_def_property_ui_text(prop, "Input Type B", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "input_type_c", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_c"); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); + RNA_def_property_ui_text(prop, "Input Type C", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } static void def_geo_attribute_vector_math(StructRNA *srna) @@ -8648,16 +8638,17 @@ static void def_geo_point_instance(StructRNA *srna) }; PropertyRNA *prop; + RNA_def_struct_sdna_from(srna, "NodeGeometryPointInstance", "storage"); prop = RNA_def_property(srna, "instance_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_sdna(prop, NULL, "instance_type"); RNA_def_property_enum_items(prop, instance_type_items); RNA_def_property_enum_default(prop, GEO_NODE_POINT_INSTANCE_TYPE_OBJECT); RNA_def_property_ui_text(prop, "Instance Type", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); prop = RNA_def_property(srna, "use_whole_collection", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_negative_sdna(prop, NULL, "custom2", 1); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION); RNA_def_property_ui_text(prop, "Whole Collection", "Instance entire collection on each point"); RNA_def_property_update(prop, 0, "rna_Node_socket_update"); } @@ -8698,7 +8689,7 @@ static void def_geo_attribute_attribute_compare(StructRNA *srna) prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_node_float_compare_items); - RNA_def_property_enum_default(prop, NODE_MATH_ADD); + RNA_def_property_enum_default(prop, NODE_FLOAT_COMPARE_GREATER_THAN); RNA_def_property_ui_text(prop, "Operation", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); @@ -8862,6 +8853,19 @@ static void def_geo_point_translate(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_attribute_sample_texture(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "texture", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "id"); + RNA_def_property_struct_type(prop, "Texture"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_ui_text(prop, "Texture", "Texture to sample values from"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update_relations"); +} + static void def_geo_object_info(StructRNA *srna) { PropertyRNA *prop; @@ -8875,6 +8879,37 @@ static void def_geo_object_info(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_points_to_volume(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem resolution_mode_items[] = { + {GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT, + "VOXEL_AMOUNT", + 0, + "Amount", + "Specify the approximate number of voxels along the diagonal"}, + {GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE, + "VOXEL_SIZE", + 0, + "Size", + "Specify the voxel side length"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryPointsToVolume", "storage"); + + prop = RNA_def_property(srna, "resolution_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, resolution_mode_items); + RNA_def_property_ui_text(prop, "Resolution Mode", "How the voxel size is specified"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "input_type_radius", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); + RNA_def_property_ui_text(prop, "Radius Input Type", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index faa20e642cf..c418c8eb4dc 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -85,16 +85,16 @@ const EnumPropertyItem rna_enum_object_mode_items[] = { ICON_GREASEPENCIL, "Draw", "Paint Grease Pencil Strokes"}, - {OB_MODE_VERTEX_GPENCIL, - "VERTEX_GPENCIL", - ICON_VPAINT_HLT, - "Vertex Paint", - "Grease Pencil Vertex Paint Strokes"}, {OB_MODE_WEIGHT_GPENCIL, "WEIGHT_GPENCIL", ICON_WPAINT_HLT, "Weight Paint", "Grease Pencil Weight Paint Strokes"}, + {OB_MODE_VERTEX_GPENCIL, + "VERTEX_GPENCIL", + ICON_VPAINT_HLT, + "Vertex Paint", + "Grease Pencil Vertex Paint Strokes"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index c929e3ab1aa..cad0d77607b 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -1279,12 +1279,22 @@ static int rna_property_override_diff_propptr(Main *bmain, 0); if (is_id) { - /* For now, once we deal with nodetrees we'll want to get rid of that one. */ - // BLI_assert(no_ownership); + /* Owned IDs (the ones we want to actually compare in depth, instead of just comparing pointer + * values) should be always properly tagged as 'virtual' overrides. */ + ID *id = propptr_a->owner_id; + if (id != NULL && !ID_IS_OVERRIDE_LIBRARY(id)) { + id = propptr_b->owner_id; + if (id != NULL && !ID_IS_OVERRIDE_LIBRARY(id)) { + id = NULL; + } + } + + BLI_assert(no_ownership || id == NULL || ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id)); + UNUSED_VARS_NDEBUG(id); } if (override) { - if (no_ownership /* || is_id */ || is_null || is_type_diff || !is_valid_for_diffing) { + if (no_ownership || is_null || is_type_diff || !is_valid_for_diffing) { /* In case this pointer prop does not own its data (or one is NULL), do not compare structs! * This is a quite safe path to infinite loop, among other nasty issues. * Instead, just compare pointers themselves. */ @@ -1304,9 +1314,9 @@ static int rna_property_override_diff_propptr(Main *bmain, BLI_assert(op->rna_prop_type == property_type); } + IDOverrideLibraryPropertyOperation *opop = NULL; if (created || rna_itemname_a != NULL || rna_itemname_b != NULL || rna_itemindex_a != -1 || rna_itemindex_b != -1) { - IDOverrideLibraryPropertyOperation *opop; opop = BKE_lib_override_library_property_operation_get(op, IDOVERRIDE_LIBRARY_OP_REPLACE, rna_itemname_b, @@ -1327,6 +1337,38 @@ static int rna_property_override_diff_propptr(Main *bmain, else { BKE_lib_override_library_operations_tag(op, IDOVERRIDE_LIBRARY_TAG_UNUSED, false); } + + if (is_id && no_ownership) { + if (opop == NULL) { + opop = BKE_lib_override_library_property_operation_find(op, + rna_itemname_b, + rna_itemname_a, + rna_itemindex_b, + rna_itemindex_a, + true, + NULL); + BLI_assert(opop != NULL); + } + + BLI_assert(propptr_a->data == propptr_a->owner_id); + BLI_assert(propptr_b->data == propptr_b->owner_id); + ID *id_a = propptr_a->data; + ID *id_b = propptr_b->data; + if (ELEM(NULL, id_a, id_b)) { + /* In case one of the pointer is NULL and not the other, we consider that the + * override is not matching its reference anymore. */ + opop->flag &= ~IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE; + } + else if (id_a->override_library != NULL && id_a->override_library->reference == id_b) { + opop->flag |= IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE; + } + else if (id_b->override_library != NULL && id_b->override_library->reference == id_a) { + opop->flag |= IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE; + } + else { + opop->flag &= ~IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE; + } + } } } diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 2ee84f70752..160d93dea58 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -2279,6 +2279,20 @@ static bool rna_SpaceNodeEditor_tree_type_poll(void *Cv, bNodeTreeType *type) } } +static void rna_SpaceNodeEditor_cursor_location_get(PointerRNA *ptr, float value[2]) +{ + const SpaceNode *snode = (SpaceNode *)ptr->data; + + ED_node_cursor_location_get(snode, value); +} + +static void rna_SpaceNodeEditor_cursor_location_set(PointerRNA *ptr, const float value[2]) +{ + SpaceNode *snode = (SpaceNode *)ptr->data; + + ED_node_cursor_location_set(snode, value); +} + const EnumPropertyItem *RNA_enum_node_tree_types_itemf_impl(bContext *C, bool *r_free) { return rna_node_tree_type_itemf(C, rna_SpaceNodeEditor_tree_type_poll, r_free); @@ -2346,9 +2360,13 @@ static void rna_SpaceNodeEditor_cursor_location_from_region(SpaceNode *snode, { ARegion *region = CTX_wm_region(C); - UI_view2d_region_to_view(®ion->v2d, x, y, &snode->cursor[0], &snode->cursor[1]); - snode->cursor[0] /= UI_DPI_FAC; - snode->cursor[1] /= UI_DPI_FAC; + float cursor_location[2]; + + UI_view2d_region_to_view(®ion->v2d, x, y, &cursor_location[0], &cursor_location[1]); + cursor_location[0] /= UI_DPI_FAC; + cursor_location[1] /= UI_DPI_FAC; + + ED_node_cursor_location_set(snode, cursor_location); } static void rna_SpaceClipEditor_clip_set(PointerRNA *ptr, @@ -6815,11 +6833,13 @@ static void rna_def_space_node(BlenderRNA *brna) RNA_def_property_enum_items(prop, backdrop_channels_items); RNA_def_property_ui_text(prop, "Display Channels", "Channels of the image to draw"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL); - /* the mx/my "cursor" in the node editor is used only by operators to store the mouse position */ prop = RNA_def_property(srna, "cursor_location", PROP_FLOAT, PROP_XYZ); RNA_def_property_array(prop, 2); - RNA_def_property_float_sdna(prop, NULL, "cursor"); + RNA_def_property_float_funcs(prop, + "rna_SpaceNodeEditor_cursor_location_get", + "rna_SpaceNodeEditor_cursor_location_set", + NULL); RNA_def_property_ui_text(prop, "Cursor Location", "Location for adding new nodes"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL); diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c index 1870c660efd..bcf75fb284f 100644 --- a/source/blender/makesrna/intern/rna_tracking.c +++ b/source/blender/makesrna/intern/rna_tracking.c @@ -976,22 +976,6 @@ static void rna_def_trackingSettings(BlenderRNA *brna) RNA_def_property_enum_items(prop, cleanup_items); RNA_def_property_ui_text(prop, "Action", "Cleanup action to execute"); - /* ** default tracker settings ** */ - prop = RNA_def_property(srna, "show_default_expanded", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", TRACKING_SETTINGS_SHOW_DEFAULT_EXPANDED); - RNA_def_property_ui_text( - prop, "Show Expanded", "Show default options expanded in the user interface"); - RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1); - - /* ** extra tracker settings ** */ - prop = RNA_def_property(srna, "show_extra_expanded", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", TRACKING_SETTINGS_SHOW_EXTRA_EXPANDED); - RNA_def_property_ui_text( - prop, "Show Expanded", "Show extra options expanded in the user interface"); - RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1); - /* solver settings */ prop = RNA_def_property(srna, "use_tripod_solver", PROP_BOOLEAN, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 3d123f07c2b..445ff773868 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -4756,6 +4756,11 @@ static void rna_def_userdef_view(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Gizmo Size", "Diameter of the gizmo"); RNA_def_property_update(prop, 0, "rna_userdef_update"); + prop = RNA_def_property(srna, "gizmo_size_navigate_v3d", PROP_INT, PROP_PIXEL); + RNA_def_property_range(prop, 30, 200); + RNA_def_property_ui_text(prop, "Navigate Gizmo Size", "The Navigate Gizmo size"); + RNA_def_property_update(prop, 0, "rna_userdef_gizmo_update"); + /* Lookdev */ prop = RNA_def_property(srna, "lookdev_sphere_size", PROP_INT, PROP_PIXEL); RNA_def_property_int_sdna(prop, NULL, "lookdev_sphere_size"); diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index f3d6cf49dd6..6ce288e8ac5 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -221,18 +221,21 @@ class GeometryNodesEvaluator { const blender::nodes::DataTypeConversions &conversions_; const PersistentDataHandleMap &handle_map_; const Object *self_object_; + Depsgraph *depsgraph_; public: GeometryNodesEvaluator(const Map<const DOutputSocket *, GMutablePointer> &group_input_data, Vector<const DInputSocket *> group_outputs, blender::nodes::MultiFunctionByNode &mf_by_node, const PersistentDataHandleMap &handle_map, - const Object *self_object) + const Object *self_object, + Depsgraph *depsgraph) : group_outputs_(std::move(group_outputs)), mf_by_node_(mf_by_node), conversions_(blender::nodes::get_implicit_type_conversions()), handle_map_(handle_map), - self_object_(self_object) + self_object_(self_object), + depsgraph_(depsgraph) { for (auto item : group_input_data.items()) { this->forward_to_inputs(*item.key, item.value); @@ -306,7 +309,8 @@ class GeometryNodesEvaluator { /* Execute the node. */ GValueMap<StringRef> node_outputs_map{allocator_}; - GeoNodeExecParams params{bnode, node_inputs_map, node_outputs_map, handle_map_, self_object_}; + GeoNodeExecParams params{ + bnode, node_inputs_map, node_outputs_map, handle_map_, self_object_, depsgraph_}; this->execute_node(node, params); /* Forward computed outputs to linked input sockets. */ @@ -926,7 +930,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, group_outputs.append(&socket_to_compute); GeometryNodesEvaluator evaluator{ - group_inputs, group_outputs, mf_by_node, handle_map, ctx->object}; + group_inputs, group_outputs, mf_by_node, handle_map, ctx->object, ctx->depsgraph}; Vector<GMutablePointer> results = evaluator.execute(); BLI_assert(results.size() == 1); GMutablePointer result = results[0]; diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index ba370b401f3..b59907cc1e9 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -486,9 +486,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * /* * Normal Calculation (for face flipping) * Sort edge verts for correct face flipping - * NOT REALLY NEEDED but face flipping is nice. - * - * */ + * NOT REALLY NEEDED but face flipping is nice. */ /* Notice! * diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index ed4d658eb4f..405a8dcbf46 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -146,6 +146,7 @@ set(SRC geometry/nodes/node_geo_attribute_fill.cc geometry/nodes/node_geo_attribute_math.cc geometry/nodes/node_geo_attribute_mix.cc + geometry/nodes/node_geo_attribute_sample_texture.cc geometry/nodes/node_geo_attribute_randomize.cc geometry/nodes/node_geo_attribute_vector_math.cc geometry/nodes/node_geo_boolean.cc @@ -159,6 +160,7 @@ set(SRC geometry/nodes/node_geo_point_scale.cc geometry/nodes/node_geo_point_separate.cc geometry/nodes/node_geo_point_translate.cc + geometry/nodes/node_geo_points_to_volume.cc geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_transform.cc geometry/nodes/node_geo_triangulate.cc @@ -366,4 +368,11 @@ if(WITH_OPENSUBDIV) add_definitions(-DWITH_OPENSUBDIV) endif() +if(WITH_OPENVDB) + list(APPEND INC_SYS + ${OPENVDB_INCLUDE_DIRS} + ) + add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS}) +endif() + blender_add_lib(bf_nodes "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 523d0cfa24d..d78f76e0b52 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -47,6 +47,8 @@ void register_node_type_geo_attribute_mix(void); void register_node_type_geo_attribute_color_ramp(void); void register_node_type_geo_point_rotate(void); void register_node_type_geo_align_rotation_to_vector(void); +void register_node_type_geo_sample_texture(void); +void register_node_type_geo_points_to_volume(void); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index f278d6b4107..454c9e96246 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -24,12 +24,16 @@ #include "DNA_node_types.h" +struct Depsgraph; + namespace blender::nodes { using bke::BooleanReadAttribute; using bke::BooleanWriteAttribute; using bke::Color4fReadAttribute; using bke::Color4fWriteAttribute; +using bke::Float2ReadAttribute; +using bke::Float2WriteAttribute; using bke::Float3ReadAttribute; using bke::Float3WriteAttribute; using bke::FloatReadAttribute; @@ -54,18 +58,21 @@ class GeoNodeExecParams { GValueMap<StringRef> &output_values_; const PersistentDataHandleMap &handle_map_; const Object *self_object_; + Depsgraph *depsgraph_; public: GeoNodeExecParams(const bNode &node, GValueMap<StringRef> &input_values, GValueMap<StringRef> &output_values, const PersistentDataHandleMap &handle_map, - const Object *self_object) + const Object *self_object, + Depsgraph *depsgraph) : node_(node), input_values_(input_values), output_values_(output_values), handle_map_(handle_map), - self_object_(self_object) + self_object_(self_object), + depsgraph_(depsgraph) { } @@ -163,6 +170,11 @@ class GeoNodeExecParams { return self_object_; } + Depsgraph *depsgraph() const + { + return depsgraph_; + } + /** * Creates a read-only attribute based on node inputs. The method automatically detects which * input with the given name is available. diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index e91b385a87e..cc2f6a294f2 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -289,6 +289,8 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_m DefNode(GeometryNode, GEO_NODE_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "") DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", PointScale, "Point Scale", "") DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, def_geo_attribute_sample_texture, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "") +DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "") /* undefine macros */ #undef DefNode diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc index 53af6073793..daaccf4450b 100644 --- a/source/blender/nodes/geometry/node_geometry_util.cc +++ b/source/blender/nodes/geometry/node_geometry_util.cc @@ -54,6 +54,8 @@ static int attribute_data_type_complexity(const CustomDataType data_type) return 1; case CD_PROP_FLOAT: return 2; + case CD_PROP_FLOAT2: + return 3; case CD_PROP_FLOAT3: return 4; case CD_PROP_COLOR: diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc index 20a2b2127c2..194b062021d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc @@ -51,7 +51,7 @@ static bNodeSocketTemplate geo_node_attribute_compare_out[] = { static void geo_node_attribute_compare_init(bNodeTree *UNUSED(tree), bNode *node) { NodeAttributeCompare *data = (NodeAttributeCompare *)MEM_callocN(sizeof(NodeAttributeCompare), - "attribute mix node"); + __func__); data->operation = NODE_FLOAT_COMPARE_GREATER_THAN; data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc index f8ec9124db3..f3fc45fc1be 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc @@ -34,6 +34,8 @@ static bNodeSocketTemplate geo_node_attribute_math_in[] = { {SOCK_FLOAT, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, {SOCK_STRING, N_("B")}, {SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, + {SOCK_STRING, N_("C")}, + {SOCK_FLOAT, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, {SOCK_STRING, N_("Result")}, {-1, ""}, }; @@ -51,45 +53,132 @@ static void geo_node_attribute_math_init(bNodeTree *UNUSED(tree), bNode *node) data->operation = NODE_MATH_ADD; data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; + data->input_type_c = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; node->storage = data; } +static bool operation_use_input_c(const NodeMathOperation operation) +{ + return ELEM(operation, + NODE_MATH_MULTIPLY_ADD, + NODE_MATH_SMOOTH_MIN, + NODE_MATH_SMOOTH_MAX, + NODE_MATH_WRAP, + NODE_MATH_COMPARE); +} + +static bool operation_use_input_b(const NodeMathOperation operation) +{ + switch (operation) { + case NODE_MATH_ADD: + case NODE_MATH_SUBTRACT: + case NODE_MATH_MULTIPLY: + case NODE_MATH_DIVIDE: + case NODE_MATH_POWER: + case NODE_MATH_LOGARITHM: + case NODE_MATH_MINIMUM: + case NODE_MATH_MAXIMUM: + case NODE_MATH_LESS_THAN: + case NODE_MATH_GREATER_THAN: + case NODE_MATH_MODULO: + case NODE_MATH_ARCTAN2: + case NODE_MATH_SNAP: + case NODE_MATH_WRAP: + case NODE_MATH_COMPARE: + case NODE_MATH_MULTIPLY_ADD: + case NODE_MATH_PINGPONG: + case NODE_MATH_SMOOTH_MIN: + case NODE_MATH_SMOOTH_MAX: + return true; + case NODE_MATH_SINE: + case NODE_MATH_COSINE: + case NODE_MATH_TANGENT: + case NODE_MATH_ARCSINE: + case NODE_MATH_ARCCOSINE: + case NODE_MATH_ARCTANGENT: + case NODE_MATH_ROUND: + case NODE_MATH_ABSOLUTE: + case NODE_MATH_FLOOR: + case NODE_MATH_CEIL: + case NODE_MATH_FRACTION: + case NODE_MATH_SQRT: + case NODE_MATH_INV_SQRT: + case NODE_MATH_SIGN: + case NODE_MATH_EXPONENT: + case NODE_MATH_RADIANS: + case NODE_MATH_DEGREES: + case NODE_MATH_SINH: + case NODE_MATH_COSH: + case NODE_MATH_TANH: + case NODE_MATH_TRUNC: + return false; + } + BLI_assert(false); + return false; +} + namespace blender::nodes { static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node) { - NodeAttributeMath *node_storage = (NodeAttributeMath *)node->storage; + NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage; + NodeMathOperation operation = static_cast<NodeMathOperation>(node_storage.operation); update_attribute_input_socket_availabilities( - *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a); + *node, "A", (GeometryNodeAttributeInputMode)node_storage.input_type_a); update_attribute_input_socket_availabilities( - *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b); + *node, + "B", + (GeometryNodeAttributeInputMode)node_storage.input_type_b, + operation_use_input_b(operation)); + update_attribute_input_socket_availabilities( + *node, + "C", + (GeometryNodeAttributeInputMode)node_storage.input_type_c, + operation_use_input_c(operation)); } -static void do_math_operation(const FloatReadAttribute &input_a, - const FloatReadAttribute &input_b, - FloatWriteAttribute result, - const int operation) +static void do_math_operation(Span<float> span_a, + Span<float> span_b, + Span<float> span_c, + MutableSpan<float> span_result, + const NodeMathOperation operation) { - const int size = input_a.size(); - - Span<float> span_a = input_a.get_span(); - Span<float> span_b = input_b.get_span(); - MutableSpan<float> span_result = result.get_span_for_write_only(); + bool success = try_dispatch_float_math_fl_fl_fl_to_fl( + operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { + for (const int i : IndexRange(span_result.size())) { + span_result[i] = math_function(span_a[i], span_b[i], span_c[i]); + } + }); + BLI_assert(success); + UNUSED_VARS_NDEBUG(success); +} +static void do_math_operation(Span<float> span_a, + Span<float> span_b, + MutableSpan<float> span_result, + const NodeMathOperation operation) +{ bool success = try_dispatch_float_math_fl_fl_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float in1 = span_a[i]; - const float in2 = span_b[i]; - const float out = math_function(in1, in2); - span_result[i] = out; + for (const int i : IndexRange(span_result.size())) { + span_result[i] = math_function(span_a[i], span_b[i]); } }); + BLI_assert(success); + UNUSED_VARS_NDEBUG(success); +} - result.apply_span(); - - /* The operation is not supported by this node currently. */ +static void do_math_operation(Span<float> span_input, + MutableSpan<float> span_result, + const NodeMathOperation operation) +{ + bool success = try_dispatch_float_math_fl_to_fl( + operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { + for (const int i : IndexRange(span_result.size())) { + span_result[i] = math_function(span_input[i]); + } + }); BLI_assert(success); UNUSED_VARS_NDEBUG(success); } @@ -98,7 +187,7 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP { const bNode &node = params.node(); const NodeAttributeMath *node_storage = (const NodeAttributeMath *)node.storage; - const int operation = node_storage->operation; + const NodeMathOperation operation = static_cast<NodeMathOperation>(node_storage->operation); /* The result type of this node is always float. */ const CustomDataType result_type = CD_PROP_FLOAT; @@ -115,15 +204,44 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP ReadAttributePtr attribute_a = params.get_input_attribute( "A", component, result_domain, result_type, nullptr); - ReadAttributePtr attribute_b = params.get_input_attribute( - "B", component, result_domain, result_type, nullptr); - if (!attribute_a || !attribute_b) { - /* Attribute wasn't found. */ + if (!attribute_a) { return; } - do_math_operation(*attribute_a, *attribute_b, *attribute_result, operation); - attribute_result.save(); + /* Note that passing the data with `get_span<float>()` works + * because the attributes were accessed with #CD_PROP_FLOAT. */ + if (operation_use_input_b(operation)) { + ReadAttributePtr attribute_b = params.get_input_attribute( + "B", component, result_domain, result_type, nullptr); + if (!attribute_b) { + return; + } + if (operation_use_input_c(operation)) { + ReadAttributePtr attribute_c = params.get_input_attribute( + "C", component, result_domain, result_type, nullptr); + if (!attribute_c) { + return; + } + do_math_operation(attribute_a->get_span<float>(), + attribute_b->get_span<float>(), + attribute_c->get_span<float>(), + attribute_result->get_span_for_write_only<float>(), + operation); + } + else { + do_math_operation(attribute_a->get_span<float>(), + attribute_b->get_span<float>(), + attribute_result->get_span_for_write_only<float>(), + operation); + } + } + else { + do_math_operation(attribute_a->get_span<float>(), + attribute_result->get_span_for_write_only<float>(), + operation); + } + + attribute_result.apply_span_and_save(); } static void geo_node_attribute_math_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc index 0e7bb25e659..3ee7df7fe72 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc @@ -50,8 +50,7 @@ static void geo_node_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode bNodeSocket *sock_min_float = sock_max_vector->next; bNodeSocket *sock_max_float = sock_min_float->next; - const int data_type = node->custom1; - + const CustomDataType data_type = static_cast<CustomDataType>(node->custom1); nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3); nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3); nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT); @@ -86,8 +85,11 @@ static void randomize_attribute_bool(BooleanWriteAttribute attribute, attribute.apply_span(); } -static void randomize_attribute_float( - FloatWriteAttribute attribute, float min, float max, Span<uint32_t> hashes, const int seed) +static void randomize_attribute_float(FloatWriteAttribute attribute, + const float min, + const float max, + Span<uint32_t> hashes, + const int seed) { MutableSpan<float> attribute_span = attribute.get_span(); for (const int i : IndexRange(attribute.size())) { @@ -97,8 +99,11 @@ static void randomize_attribute_float( attribute.apply_span(); } -static void randomize_attribute_float3( - Float3WriteAttribute attribute, float3 min, float3 max, Span<uint32_t> hashes, const int seed) +static void randomize_attribute_float3(Float3WriteAttribute attribute, + const float3 min, + const float3 max, + Span<uint32_t> hashes, + const int seed) { MutableSpan<float3> attribute_span = attribute.get_span(); for (const int i : IndexRange(attribute.size())) { @@ -129,8 +134,7 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo } else { /* If there is no "id" attribute for per-point variation, just create it here. */ - RandomNumberGenerator rng; - rng.seed(0); + RandomNumberGenerator rng(0); for (const int i : hashes.index_range()) { hashes[i] = rng.get_uint32(); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc new file mode 100644 index 00000000000..66495bfa53b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc @@ -0,0 +1,107 @@ +/* + * 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. + */ + +#include "BLI_compiler_attrs.h" + +#include "DNA_texture_types.h" + +#include "BKE_texture.h" + +#include "RE_texture.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_attribute_sample_texture_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Mapping")}, + {SOCK_STRING, N_("Result")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_sample_texture_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void execute_on_component(GeometryComponent &component, const GeoNodeExecParams ¶ms) +{ + const bNode &node = params.node(); + Tex *texture = reinterpret_cast<Tex *>(node.id); + const std::string result_attribute_name = params.get_input<std::string>("Result"); + + if (texture == nullptr) { + return; + } + + const std::string mapping_name = params.get_input<std::string>("Mapping"); + if (!component.attribute_exists(mapping_name)) { + return; + } + + OutputAttributePtr attribute_out = component.attribute_try_get_for_output( + result_attribute_name, ATTR_DOMAIN_POINT, CD_PROP_COLOR); + if (!attribute_out) { + return; + } + + Float3ReadAttribute mapping_attribute = component.attribute_get_for_read<float3>( + mapping_name, ATTR_DOMAIN_POINT, {0, 0, 0}); + + MutableSpan<Color4f> colors = attribute_out->get_span<Color4f>(); + for (const int i : IndexRange(mapping_attribute.size())) { + TexResult texture_result = {0}; + const float3 position = mapping_attribute[i]; + /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */ + const float3 remapped_position = position * 2.0f - float3(1.0f); + BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false); + colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta}; + } + attribute_out.apply_span_and_save(); +} + +static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + if (geometry_set.has<MeshComponent>()) { + execute_on_component(geometry_set.get_component_for_write<MeshComponent>(), params); + } + if (geometry_set.has<PointCloudComponent>()) { + execute_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params); + } + + params.set_output("Geometry", geometry_set); +} + +} // namespace blender::nodes + +void register_node_type_geo_sample_texture() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, + GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, + "Attribute Sample Texture", + NODE_CLASS_ATTRIBUTE, + 0); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_socket_templates( + &ntype, geo_node_attribute_sample_texture_in, geo_node_attribute_sample_texture_out); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_sample_texture_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 67bc095fa31..7549305aedb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -138,17 +138,17 @@ static void determine_final_data_type_and_domain(Span<const GeometryComponent *> CustomDataType *r_type, AttributeDomain *r_domain) { + Vector<CustomDataType> data_types; for (const GeometryComponent *component : components) { ReadAttributePtr attribute = component->attribute_try_get_for_read(attribute_name); if (attribute) { - /* TODO: Use data type with most information. */ - *r_type = bke::cpp_type_to_custom_data_type(attribute->cpp_type()); + data_types.append(attribute->custom_data_type()); /* TODO: Use highest priority domain. */ *r_domain = attribute->domain(); - return; } } - BLI_assert(false); + + *r_type = attribute_data_type_highest_complexity(data_types); } static void fill_new_attribute(Span<const GeometryComponent *> src_components, @@ -240,6 +240,14 @@ static void join_components(Span<const InstancesComponent *> src_components, Geo } } +static void join_components(Span<const VolumeComponent *> src_components, GeometrySet &result) +{ + /* Not yet supported. Joining volume grids with the same name requires resampling of at least one + * of the grids. The cell size of the resulting volume has to be determined somehow. */ + VolumeComponent &dst_component = result.get_component_for_write<VolumeComponent>(); + UNUSED_VARS(src_components, dst_component); +} + template<typename Component> static void join_component_type(Span<const GeometrySet *> src_geometry_sets, GeometrySet &result) { @@ -272,6 +280,7 @@ static void geo_node_join_geometry_exec(GeoNodeExecParams params) join_component_type<MeshComponent>(src_geometry_sets, geometry_set_result); join_component_type<PointCloudComponent>(src_geometry_sets, geometry_set_result); join_component_type<InstancesComponent>(src_geometry_sets, geometry_set_result); + join_component_type<VolumeComponent>(src_geometry_sets, geometry_set_result); params.set_output("Geometry", std::move(geometry_set_result)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc index ab5e4f8964a..d713c191d5d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc @@ -19,6 +19,7 @@ #include "BKE_mesh.h" #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" +#include "BKE_volume.h" #include "BLI_math_matrix.h" @@ -86,6 +87,18 @@ static void geo_node_object_info_exec(GeoNodeExecParams params) mesh_component.copy_vertex_group_names_from_object(*object); } } + if (object->type == OB_VOLUME) { + InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + + if (transform_space_relative) { + instances.add_instance(object, transform); + } + else { + float unit_transform[4][4]; + unit_m4(unit_transform); + instances.add_instance(object, unit_transform); + } + } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc index 2f1aa276532..eaf13b94eb9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc @@ -26,6 +26,7 @@ #include "DNA_meshdata_types.h" #include "DNA_pointcloud_types.h" +#include "BKE_attribute_math.hh" #include "BKE_bvhutils.h" #include "BKE_deform.h" #include "BKE_mesh.h" @@ -217,13 +218,141 @@ BLI_NOINLINE static void eliminate_points_based_on_mask(Span<bool> elimination_m } } -BLI_NOINLINE static void compute_remaining_point_data(const Mesh &mesh, - Span<float3> bary_coords, - Span<int> looptri_indices, - MutableSpan<float3> r_normals, - MutableSpan<int> r_ids, - MutableSpan<float3> r_rotations) +template<typename T> +BLI_NOINLINE static void interpolate_attribute_point(const Mesh &mesh, + const Span<float3> bary_coords, + const Span<int> looptri_indices, + const Span<T> data_in, + MutableSpan<T> data_out) { + BLI_assert(data_in.size() == mesh.totvert); + Span<MLoopTri> looptris = get_mesh_looptris(mesh); + + for (const int i : bary_coords.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + const float3 &bary_coord = bary_coords[i]; + + const int v0_index = mesh.mloop[looptri.tri[0]].v; + const int v1_index = mesh.mloop[looptri.tri[1]].v; + const int v2_index = mesh.mloop[looptri.tri[2]].v; + + const T &v0 = data_in[v0_index]; + const T &v1 = data_in[v1_index]; + const T &v2 = data_in[v2_index]; + + const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2); + data_out[i] = interpolated_value; + } +} + +template<typename T> +BLI_NOINLINE static void interpolate_attribute_corner(const Mesh &mesh, + const Span<float3> bary_coords, + const Span<int> looptri_indices, + const Span<T> data_in, + MutableSpan<T> data_out) +{ + BLI_assert(data_in.size() == mesh.totloop); + Span<MLoopTri> looptris = get_mesh_looptris(mesh); + + for (const int i : bary_coords.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + const float3 &bary_coord = bary_coords[i]; + + const int loop_index_0 = looptri.tri[0]; + const int loop_index_1 = looptri.tri[1]; + const int loop_index_2 = looptri.tri[2]; + + const T &v0 = data_in[loop_index_0]; + const T &v1 = data_in[loop_index_1]; + const T &v2 = data_in[loop_index_2]; + + const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2); + data_out[i] = interpolated_value; + } +} + +BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, + Span<float3> bary_coords, + Span<int> looptri_indices, + const StringRef attribute_name, + const ReadAttribute &attribute_in, + GeometryComponent &component) +{ + const CustomDataType data_type = attribute_in.custom_data_type(); + const AttributeDomain domain = attribute_in.domain(); + if (!ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) { + /* Not supported currently. */ + return; + } + + OutputAttributePtr attribute_out = component.attribute_try_get_for_output( + attribute_name, ATTR_DOMAIN_POINT, data_type); + if (!attribute_out) { + return; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + + Span data_in = attribute_in.get_span<T>(); + MutableSpan data_out = attribute_out->get_span_for_write_only<T>(); + + switch (domain) { + case ATTR_DOMAIN_POINT: { + interpolate_attribute_point<T>(mesh, bary_coords, looptri_indices, data_in, data_out); + break; + } + case ATTR_DOMAIN_CORNER: { + interpolate_attribute_corner<T>(mesh, bary_coords, looptri_indices, data_in, data_out); + break; + } + default: { + BLI_assert(false); + break; + } + } + }); + attribute_out.apply_span_and_save(); +} + +BLI_NOINLINE static void interpolate_existing_attributes(const MeshComponent &mesh_component, + GeometryComponent &component, + Span<float3> bary_coords, + Span<int> looptri_indices) +{ + const Mesh &mesh = *mesh_component.get_for_read(); + + Set<std::string> attribute_names = mesh_component.attribute_names(); + for (StringRefNull attribute_name : attribute_names) { + if (ELEM(attribute_name, "position", "normal", "id")) { + continue; + } + + ReadAttributePtr attribute_in = mesh_component.attribute_try_get_for_read(attribute_name); + interpolate_attribute( + mesh, bary_coords, looptri_indices, attribute_name, *attribute_in, component); + } +} + +BLI_NOINLINE static void compute_special_attributes(const Mesh &mesh, + GeometryComponent &component, + Span<float3> bary_coords, + Span<int> looptri_indices) +{ + OutputAttributePtr id_attribute = component.attribute_try_get_for_output( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + OutputAttributePtr normal_attribute = component.attribute_try_get_for_output( + "normal", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output( + "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + + MutableSpan<int> ids = id_attribute->get_span_for_write_only<int>(); + MutableSpan<float3> normals = normal_attribute->get_span_for_write_only<float3>(); + MutableSpan<float3> rotations = rotation_attribute->get_span_for_write_only<float3>(); + Span<MLoopTri> looptris = get_mesh_looptris(mesh); for (const int i : bary_coords.index_range()) { const int looptri_index = looptri_indices[i]; @@ -237,10 +366,24 @@ BLI_NOINLINE static void compute_remaining_point_data(const Mesh &mesh, const float3 v1_pos = mesh.mvert[v1_index].co; const float3 v2_pos = mesh.mvert[v2_index].co; - r_ids[i] = (int)(bary_coord.hash()) + looptri_index; - normal_tri_v3(r_normals[i], v0_pos, v1_pos, v2_pos); - r_rotations[i] = normal_to_euler_rotation(r_normals[i]); + ids[i] = (int)(bary_coord.hash()) + looptri_index; + normal_tri_v3(normals[i], v0_pos, v1_pos, v2_pos); + rotations[i] = normal_to_euler_rotation(normals[i]); } + + id_attribute.apply_span_and_save(); + normal_attribute.apply_span_and_save(); + rotation_attribute.apply_span_and_save(); +} + +BLI_NOINLINE static void add_remaining_point_attributes(const MeshComponent &mesh_component, + GeometryComponent &component, + Span<float3> bary_coords, + Span<int> looptri_indices) +{ + interpolate_existing_attributes(mesh_component, component, bary_coords, looptri_indices); + compute_special_attributes( + *mesh_component.get_for_read(), component, bary_coords, looptri_indices); } static void sample_mesh_surface_with_minimum_distance(const Mesh &mesh, @@ -315,11 +458,6 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params) break; } const int tot_points = positions.size(); - Array<float3> normals(tot_points); - Array<int> stable_ids(tot_points); - Array<float3> rotations(tot_points); - compute_remaining_point_data( - *mesh_in, bary_coords, looptri_indices, normals, stable_ids, rotations); PointCloud *pointcloud = BKE_pointcloud_new_nomain(tot_points); memcpy(pointcloud->co, positions.data(), sizeof(float3) * tot_points); @@ -332,29 +470,7 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params) geometry_set_out.get_component_for_write<PointCloudComponent>(); point_component.replace(pointcloud); - { - OutputAttributePtr stable_id_attribute = point_component.attribute_try_get_for_output( - "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); - MutableSpan<int> stable_ids_span = stable_id_attribute->get_span<int>(); - stable_ids_span.copy_from(stable_ids); - stable_id_attribute.apply_span_and_save(); - } - - { - OutputAttributePtr normals_attribute = point_component.attribute_try_get_for_output( - "normal", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); - MutableSpan<float3> normals_span = normals_attribute->get_span<float3>(); - normals_span.copy_from(normals); - normals_attribute.apply_span_and_save(); - } - - { - OutputAttributePtr rotations_attribute = point_component.attribute_try_get_for_output( - "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); - MutableSpan<float3> rotations_span = rotations_attribute->get_span<float3>(); - rotations_span.copy_from(rotations); - rotations_attribute.apply_span_and_save(); - } + add_remaining_point_attributes(mesh_component, point_component, bary_coords, looptri_indices); params.set_output("Geometry", std::move(geometry_set_out)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc index a96dc710427..3bd8c355124 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -47,8 +47,10 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node) bNodeSocket *collection_socket = object_socket->next; bNodeSocket *seed_socket = collection_socket->next; - GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)node->custom1; - const bool use_whole_collection = node->custom2 == 0; + NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node->storage; + GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)node_storage->instance_type; + const bool use_whole_collection = (node_storage->flag & + GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) != 0; nodeSetSocketAvailability(object_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT); nodeSetSocketAvailability(collection_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION); @@ -79,6 +81,8 @@ static void get_instanced_data__collection( MutableSpan<std::optional<InstancedData>> r_instances_data) { const bNode &node = params.node(); + NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage; + bke::PersistentCollectionHandle collection_handle = params.get_input<bke::PersistentCollectionHandle>("Collection"); Collection *collection = params.handle_map().lookup(collection_handle); @@ -86,7 +90,8 @@ static void get_instanced_data__collection( return; } - const bool use_whole_collection = node.custom2 == 0; + const bool use_whole_collection = (node_storage->flag & + GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) != 0; if (use_whole_collection) { InstancedData instance; instance.type = INSTANCE_DATA_TYPE_COLLECTION; @@ -128,8 +133,9 @@ static Array<std::optional<InstancedData>> get_instanced_data(const GeoNodeExecP const int amount) { const bNode &node = params.node(); - const GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)node.custom1; - + NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage; + const GeometryNodePointInstanceType type = (GeometryNodePointInstanceType) + node_storage->instance_type; Array<std::optional<InstancedData>> instances_data(amount); switch (type) { @@ -189,6 +195,16 @@ static void geo_node_point_instance_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set_out)); } + +static void geo_node_point_instance_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN( + sizeof(NodeGeometryPointInstance), __func__); + data->instance_type = GEO_NODE_POINT_INSTANCE_TYPE_OBJECT; + data->flag |= GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION; + node->storage = data; +} + } // namespace blender::nodes void register_node_type_geo_point_instance() @@ -197,6 +213,9 @@ void register_node_type_geo_point_instance() geo_node_type_base(&ntype, GEO_NODE_POINT_INSTANCE, "Point Instance", NODE_CLASS_GEOMETRY, 0); node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out); + node_type_init(&ntype, blender::nodes::geo_node_point_instance_init); + node_type_storage( + &ntype, "NodeGeometryPointInstance", node_free_standard_storage, node_copy_standard_storage); node_type_update(&ntype, blender::nodes::geo_node_point_instance_update); ntype.geometry_node_execute = blender::nodes::geo_node_point_instance_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc new file mode 100644 index 00000000000..b90ef2034a8 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -0,0 +1,259 @@ +/* + * 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. + */ + +#ifdef WITH_OPENVDB +# include <openvdb/openvdb.h> +# include <openvdb/tools/LevelSetUtil.h> +# include <openvdb/tools/ParticlesToLevelSet.h> +#endif + +#include "node_geometry_util.hh" + +#include "BKE_lib_id.h" +#include "BKE_volume.h" + +static bNodeSocketTemplate geo_node_points_to_volume_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_FLOAT, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, + {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX}, + {SOCK_FLOAT, N_("Voxel Amount"), 64.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, + {SOCK_STRING, N_("Radius")}, + {SOCK_FLOAT, N_("Radius"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_point_translate_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +#ifdef WITH_OPENVDB +namespace { +/* Implements the interface required by #openvdb::tools::ParticlesToLevelSet. */ +struct ParticleList { + using PosType = openvdb::Vec3R; + + Span<float3> positions; + Span<float> radii; + + size_t size() const + { + return (size_t)positions.size(); + } + + void getPos(size_t n, openvdb::Vec3R &xyz) const + { + xyz = &positions[n].x; + } + + void getPosRad(size_t n, openvdb::Vec3R &xyz, openvdb::Real &radius) const + { + xyz = &positions[n].x; + radius = radii[n]; + } +}; +} // namespace + +static openvdb::FloatGrid::Ptr generate_volume_from_points(const Span<float3> positions, + const Span<float> radii, + const float density) +{ + /* Create a new grid that will be filled. #ParticlesToLevelSet requires the background value to + * be positive. It will be set to zero later on. */ + openvdb::FloatGrid::Ptr new_grid = openvdb::FloatGrid::create(1.0f); + + /* Create a narrow-band level set grid based on the positions and radii. */ + openvdb::tools::ParticlesToLevelSet op{*new_grid}; + /* Don't ignore particles based on their radius. */ + op.setRmin(0.0f); + op.setRmax(FLT_MAX); + ParticleList particles{positions, radii}; + op.rasterizeSpheres(particles); + op.finalize(); + + /* Convert the level set to a fog volume. This also sets the background value to zero. Inside the + * fog there will be a density of 1. */ + openvdb::tools::sdfToFogVolume(*new_grid); + + /* Take the desired density into account. */ + openvdb::tools::foreach (new_grid->beginValueOn(), + [&](const openvdb::FloatGrid::ValueOnIter &iter) { + iter.modifyValue([&](float &value) { value *= density; }); + }); + return new_grid; +} + +static float compute_voxel_size(const GeoNodeExecParams ¶ms, + Span<float3> positions, + const float radius) +{ + const NodeGeometryPointsToVolume &storage = + *(const NodeGeometryPointsToVolume *)params.node().storage; + + if (storage.resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE) { + return params.get_input<float>("Voxel Size"); + } + + if (positions.is_empty()) { + return 0.0f; + } + + float3 min, max; + INIT_MINMAX(min, max); + minmax_v3v3_v3_array(min, max, (float(*)[3])positions.data(), positions.size()); + + const float voxel_amount = params.get_input<float>("Voxel Amount"); + if (voxel_amount <= 1) { + return 0.0f; + } + + /* The voxel size adapts to the final size of the volume. */ + const float diagonal = float3::distance(min, max); + const float extended_diagonal = diagonal + 2.0f * radius; + const float voxel_size = extended_diagonal / voxel_amount; + return voxel_size; +} + +static void gather_point_data_from_component(const GeoNodeExecParams ¶ms, + const GeometryComponent &component, + Vector<float3> &r_positions, + Vector<float> &r_radii) +{ + Float3ReadAttribute positions = component.attribute_get_for_read<float3>( + "position", ATTR_DOMAIN_POINT, {0, 0, 0}); + FloatReadAttribute radii = params.get_input_attribute<float>( + "Radius", component, ATTR_DOMAIN_POINT, 0.0f); + + r_positions.extend(positions.get_span()); + r_radii.extend(radii.get_span()); +} + +static void convert_to_grid_index_space(const float voxel_size, + MutableSpan<float3> positions, + MutableSpan<float> radii) +{ + const float voxel_size_inv = 1.0f / voxel_size; + for (const int i : positions.index_range()) { + positions[i] *= voxel_size_inv; + /* Better align generated grid with source points. */ + positions[i] -= float3(0.5f); + radii[i] *= voxel_size_inv; + } +} + +static void initialize_volume_component_from_points(const GeometrySet &geometry_set_in, + GeometrySet &geometry_set_out, + const GeoNodeExecParams ¶ms) +{ + Vector<float3> positions; + Vector<float> radii; + + if (geometry_set_in.has<MeshComponent>()) { + gather_point_data_from_component( + params, *geometry_set_in.get_component_for_read<MeshComponent>(), positions, radii); + } + if (geometry_set_in.has<PointCloudComponent>()) { + gather_point_data_from_component( + params, *geometry_set_in.get_component_for_read<PointCloudComponent>(), positions, radii); + } + + const float max_radius = *std::max_element(radii.begin(), radii.end()); + const float voxel_size = compute_voxel_size(params, positions, max_radius); + if (voxel_size == 0.0f || positions.is_empty()) { + return; + } + + Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr); + BKE_volume_init_grids(volume); + + VolumeGrid *c_density_grid = BKE_volume_grid_add(volume, "density", VOLUME_GRID_FLOAT); + openvdb::FloatGrid::Ptr density_grid = openvdb::gridPtrCast<openvdb::FloatGrid>( + BKE_volume_grid_openvdb_for_write(volume, c_density_grid, false)); + + const float density = params.get_input<float>("Density"); + convert_to_grid_index_space(voxel_size, positions, radii); + openvdb::FloatGrid::Ptr new_grid = generate_volume_from_points(positions, radii, density); + /* This merge is cheap, because the #density_grid is empty. */ + density_grid->merge(*new_grid); + density_grid->transform().postScale(voxel_size); + + VolumeComponent &volume_component = geometry_set_out.get_component_for_write<VolumeComponent>(); + volume_component.replace(volume); +} +#endif + +static void geo_node_points_to_volume_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set_out; + +#ifdef WITH_OPENVDB + initialize_volume_component_from_points(geometry_set_in, geometry_set_out, params); +#endif + + params.set_output("Geometry", std::move(geometry_set_out)); +} + +static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN( + sizeof(NodeGeometryPointsToVolume), __func__); + data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT; + data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; + node->storage = data; + + bNodeSocket *radius_attribute_socket = nodeFindSocket(node, SOCK_IN, "Radius"); + bNodeSocketValueString *radius_attribute_socket_value = + (bNodeSocketValueString *)radius_attribute_socket->default_value; + STRNCPY(radius_attribute_socket_value->value, "radius"); +} + +static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage; + bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); + bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); + nodeSetSocketAvailability(voxel_amount_socket, + data->resolution_mode == + GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT); + nodeSetSocketAvailability( + voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE); + + update_attribute_input_socket_availabilities( + *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius); +} + +} // namespace blender::nodes + +void register_node_type_geo_points_to_volume() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_points_to_volume_in, geo_node_point_translate_out); + node_type_storage(&ntype, + "NodeGeometryPointsToVolume", + node_free_standard_storage, + node_copy_standard_storage); + node_type_size(&ntype, 170, 120, 700); + node_type_init(&ntype, blender::nodes::geo_node_points_to_volume_init); + node_type_update(&ntype, blender::nodes::geo_node_points_to_volume_update); + ntype.geometry_node_execute = blender::nodes::geo_node_points_to_volume_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 1fcd1063993..539a7551be9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -14,11 +14,19 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#ifdef WITH_OPENVDB +# include <openvdb/openvdb.h> +#endif + #include "BLI_math_matrix.h" #include "DNA_pointcloud_types.h" +#include "DNA_volume_types.h" #include "BKE_mesh.h" +#include "BKE_volume.h" + +#include "DEG_depsgraph_query.h" #include "node_geometry_util.hh" @@ -109,6 +117,43 @@ static void transform_instances(InstancesComponent &instances, } } +static void transform_volume(Volume *volume, + const float3 translation, + const float3 rotation, + const float3 scale, + GeoNodeExecParams ¶ms) +{ +#ifdef WITH_OPENVDB + /* Scaling an axis to zero is not supported for volumes. */ + const float3 limited_scale = { + (scale.x == 0.0f) ? FLT_EPSILON : scale.x, + (scale.y == 0.0f) ? FLT_EPSILON : scale.y, + (scale.z == 0.0f) ? FLT_EPSILON : scale.z, + }; + + Main *bmain = DEG_get_bmain(params.depsgraph()); + BKE_volume_load(volume, bmain); + + float matrix[4][4]; + loc_eul_size_to_mat4(matrix, translation, rotation, limited_scale); + + openvdb::Mat4s vdb_matrix; + memcpy(vdb_matrix.asPointer(), matrix, sizeof(float[4][4])); + openvdb::Mat4d vdb_matrix_d{vdb_matrix}; + + const int num_grids = BKE_volume_num_grids(volume); + for (const int i : IndexRange(num_grids)) { + VolumeGrid *volume_grid = BKE_volume_grid_get(volume, i); + + openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid, false); + openvdb::math::Transform &grid_transform = grid->transform(); + grid_transform.postMult(vdb_matrix_d); + } +#else + UNUSED_VARS(volume, translation, rotation, scale, params); +#endif +} + static void geo_node_transform_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -131,6 +176,11 @@ static void geo_node_transform_exec(GeoNodeExecParams params) transform_instances(instances, translation, rotation, scale); } + if (geometry_set.has_volume()) { + Volume *volume = geometry_set.get_volume_for_write(); + transform_volume(volume, translation, rotation, scale, params); + } + params.set_output("Geometry", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc index 2e4196af156..c2391667e86 100644 --- a/source/blender/nodes/intern/node_tree_multi_function.cc +++ b/source/blender/nodes/intern/node_tree_multi_function.cc @@ -19,6 +19,7 @@ #include "FN_multi_function_network_evaluation.hh" #include "BLI_color.hh" +#include "BLI_float2.hh" #include "BLI_float3.hh" namespace blender::nodes { @@ -191,27 +192,58 @@ static void add_implicit_conversion(DataTypeConversions &conversions, static DataTypeConversions create_implicit_conversions() { DataTypeConversions conversions; - add_implicit_conversion<float, int32_t>(conversions); + add_implicit_conversion<float, float2>(conversions); add_implicit_conversion<float, float3>(conversions); - add_implicit_conversion<int32_t, float>(conversions); + add_implicit_conversion<float, int32_t>(conversions); add_implicit_conversion<float, bool>(conversions); - add_implicit_conversion<bool, float>(conversions); - add_implicit_conversion<float3, float>( - conversions, "Vector Length", [](float3 a) { return a.length(); }); - add_implicit_conversion<int32_t, float3>( - conversions, "int32 to float3", [](int32_t a) { return float3((float)a); }); - add_implicit_conversion<float3, Color4f>( - conversions, "float3 to Color4f", [](float3 a) { return Color4f(a.x, a.y, a.z, 1.0f); }); - add_implicit_conversion<Color4f, float3>( - conversions, "Color4f to float3", [](Color4f a) { return float3(a.r, a.g, a.b); }); add_implicit_conversion<float, Color4f>( conversions, "float to Color4f", [](float a) { return Color4f(a, a, a, 1.0f); }); - add_implicit_conversion<Color4f, float>( - conversions, "Color4f to float", [](Color4f a) { return rgb_to_grayscale(a); }); + + add_implicit_conversion<float2, float3>( + conversions, "float2 to float3", [](float2 a) { return float3(a.x, a.y, 0.0f); }); + add_implicit_conversion<float2, float>( + conversions, "float2 to float", [](float2 a) { return a.length(); }); + add_implicit_conversion<float2, int32_t>( + conversions, "float2 to int32_t", [](float2 a) { return (int32_t)a.length(); }); + add_implicit_conversion<float2, bool>( + conversions, "float2 to bool", [](float2 a) { return a.length_squared() == 0.0f; }); + add_implicit_conversion<float2, Color4f>( + conversions, "float2 to Color4f", [](float2 a) { return Color4f(a.x, a.y, 0.0f, 1.0f); }); + add_implicit_conversion<float3, bool>( conversions, "float3 to boolean", [](float3 a) { return a.length_squared() == 0.0f; }); + add_implicit_conversion<float3, float>( + conversions, "Vector Length", [](float3 a) { return a.length(); }); + add_implicit_conversion<float3, int32_t>( + conversions, "float3 to int32_t", [](float3 a) { return (int)a.length(); }); + add_implicit_conversion<float3, float2>(conversions); + add_implicit_conversion<float3, Color4f>( + conversions, "float3 to Color4f", [](float3 a) { return Color4f(a.x, a.y, a.z, 1.0f); }); + + add_implicit_conversion<int32_t, bool>(conversions); + add_implicit_conversion<int32_t, float>(conversions); + add_implicit_conversion<int32_t, float2>( + conversions, "int32 to float2", [](int32_t a) { return float2((float)a); }); + add_implicit_conversion<int32_t, float3>( + conversions, "int32 to float3", [](int32_t a) { return float3((float)a); }); + + add_implicit_conversion<bool, float>(conversions); + add_implicit_conversion<bool, int32_t>(conversions); + add_implicit_conversion<bool, float2>( + conversions, "boolean to float2", [](bool a) { return (a) ? float2(1.0f) : float2(0.0f); }); add_implicit_conversion<bool, float3>( conversions, "boolean to float3", [](bool a) { return (a) ? float3(1.0f) : float3(0.0f); }); + add_implicit_conversion<bool, Color4f>(conversions, "boolean to Color4f", [](bool a) { + return (a) ? Color4f(1.0f, 1.0f, 1.0f, 1.0f) : Color4f(0.0f, 0.0f, 0.0f, 1.0f); + }); + + add_implicit_conversion<Color4f, float>( + conversions, "Color4f to float", [](Color4f a) { return rgb_to_grayscale(a); }); + add_implicit_conversion<Color4f, float2>( + conversions, "Color4f to float2", [](Color4f a) { return float2(a.r, a.g); }); + add_implicit_conversion<Color4f, float3>( + conversions, "Color4f to float3", [](Color4f a) { return float3(a.r, a.g, a.b); }); + return conversions; } diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index 38122c45ef1..22c7a394906 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1079,7 +1079,12 @@ PyDoc_STRVAR( " :arg cage: Get the mesh as a deformed cage.\n" " :type cage: boolean\n" " :arg face_normals: Calculate face normals.\n" - " :type face_normals: boolean\n"); + " :type face_normals: boolean\n" + "\n" + " .. deprecated:: 2.93\n" + "\n" + " The deform parameter is deprecated, assumed to be True, and will be removed in version " + "3.0.\n"); static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject *kw) { static const char *kwlist[] = {"object", "depsgraph", "deform", "cage", "face_normals", NULL}; @@ -1120,45 +1125,36 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject return NULL; } + if (use_deform == false) { + PyErr_WarnEx(PyExc_FutureWarning, + "from_object(...): the deform parameter is deprecated, assumed to be True, and " + "will be removed in version 3.0", + 1); + } + const bool use_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER; scene_eval = DEG_get_evaluated_scene(depsgraph); ob_eval = DEG_get_evaluated_object(depsgraph, ob); bool need_free = false; /* Write the display mesh into the dummy mesh */ - if (use_deform) { - if (use_render) { - if (use_cage) { - PyErr_SetString(PyExc_ValueError, - "from_object(...): cage arg is unsupported when dependency graph " - "evaluation mode is RENDER"); - return NULL; - } - - me_eval = BKE_mesh_new_from_object(depsgraph, ob_eval, true); - need_free = true; - } - else { - if (use_cage) { - me_eval = mesh_get_eval_deform(depsgraph, scene_eval, ob_eval, &data_masks); - } - else { - me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &data_masks); - } - } - } - else { - /* !use_deform */ + if (use_render) { if (use_cage) { PyErr_SetString(PyExc_ValueError, - "from_object(...): cage arg is unsupported when deform=False"); + "from_object(...): cage arg is unsupported when dependency graph " + "evaluation mode is RENDER"); return NULL; } - if (use_render) { - me_eval = mesh_create_eval_no_deform_render(depsgraph, scene_eval, ob, &data_masks); + + me_eval = BKE_mesh_new_from_object(depsgraph, ob_eval, true); + need_free = true; + } + else { + if (use_cage) { + me_eval = mesh_get_eval_deform(depsgraph, scene_eval, ob_eval, &data_masks); } else { - me_eval = mesh_create_eval_no_deform(depsgraph, scene_eval, ob, &data_masks); + me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &data_masks); } } diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 64e992bd76f..c4789407e4e 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -341,12 +341,20 @@ void BPY_python_start(bContext *C, int argc, const char **argv) } } - /* Without this the `sys.stdout` may be set to 'ascii' - * (it is on my system at least), where printing unicode values will raise - * an error, this is highly annoying, another stumbling block for developers, - * so use a more relaxed error handler and enforce utf-8 since the rest of - * Blender is utf-8 too - campbell */ - Py_SetStandardStreamEncoding("utf-8", "surrogateescape"); + /* Force `utf-8` on all platforms, since this is what's used for Blender's internal strings, + * providing consistent encoding behavior across all Blender installations. + * + * This also uses the `surrogateescape` error handler ensures any unexpected bytes are escaped + * instead of raising an error. + * + * Without this `sys.getfilesystemencoding()` and `sys.stdout` for example may be set to ASCII + * or some other encoding - where printing some `utf-8` values will raise an error. + * + * This can cause scripts to fail entirely on some systems. + * + * This assignment is the equivalent of enabling the `PYTHONUTF8` environment variable. + * See `PEP-540` for details on exactly what this changes. */ + Py_UTF8Mode = 1; /* Suppress error messages when calculating the module search path. * While harmless, it's noisy. */ diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index a2125a5dff9..2242cecab20 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -8737,15 +8737,17 @@ void pyrna_free_types(void) RNA_PROP_END; } -/* Note! MemLeak XXX +/** + * \warning memory leak! * * There is currently a bug where moving the registration of a Python class does - * not properly manage reference-counts from the Python class. As the srna owns + * not properly manage reference-counts from the Python class. As the `srna` owns * the Python class this should not be so tricky, but changing the references as * you'd expect when changing ownership crashes blender on exit so I had to comment out - * the decref. This is not so bad because the leak only happens when re-registering (hold F8) + * the #Py_DECREF. This is not so bad because the leak only happens when re-registering + * (continuously running `SCRIPT_OT_reload`). * - Should still be fixed - Campbell - * */ + */ PyDoc_STRVAR(pyrna_register_class_doc, ".. method:: register_class(cls)\n" "\n" diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h index 3e73ac77fc6..c0b87f4246a 100644 --- a/source/blender/render/RE_pipeline.h +++ b/source/blender/render/RE_pipeline.h @@ -252,8 +252,6 @@ void RE_ChangeModeFlag(struct Render *re, int flag, bool clear); struct Object *RE_GetCamera(struct Render *re); /* return camera override if set */ void RE_SetOverrideCamera(struct Render *re, struct Object *cam_ob); void RE_SetCamera(struct Render *re, struct Object *cam_ob); -void RE_SetWindow(struct Render *re, const rctf *viewplane, float clip_start, float clip_end); -void RE_SetOrtho(struct Render *re, const rctf *viewplane, float clip_start, float clip_end); /* get current view and window transform */ void RE_GetViewPlane(struct Render *re, rctf *r_viewplane, rcti *r_disprect); diff --git a/source/blender/render/intern/initrender.c b/source/blender/render/intern/initrender.c index fb7f7270c13..80f6badb924 100644 --- a/source/blender/render/intern/initrender.c +++ b/source/blender/render/intern/initrender.c @@ -172,26 +172,11 @@ struct Object *RE_GetCamera(Render *re) return BKE_camera_multiview_render(re->scene, camera, re->viewname); } -static void re_camera_params_get(Render *re, CameraParams *params) -{ - copy_m4_m4(re->winmat, params->winmat); - - re->clip_start = params->clip_start; - re->clip_end = params->clip_end; - - re->viewplane = params->viewplane; -} - void RE_SetOverrideCamera(Render *re, Object *cam_ob) { re->camera_override = cam_ob; } -static void re_camera_params_stereo3d(Render *re, CameraParams *params, Object *cam_ob) -{ - BKE_camera_multiview_params(&re->r, params, cam_ob, re->viewname); -} - /* call this after InitState() */ /* per render, there's one persistent viewplane. Parts will set their own viewplanes */ void RE_SetCamera(Render *re, Object *cam_ob) @@ -201,14 +186,17 @@ void RE_SetCamera(Render *re, Object *cam_ob) /* setup parameters */ BKE_camera_params_init(¶ms); BKE_camera_params_from_object(¶ms, cam_ob); - re_camera_params_stereo3d(re, ¶ms, cam_ob); + BKE_camera_multiview_params(&re->r, ¶ms, cam_ob, re->viewname); /* compute matrix, viewplane, .. */ BKE_camera_params_compute_viewplane(¶ms, re->winx, re->winy, re->r.xasp, re->r.yasp); BKE_camera_params_compute_matrix(¶ms); /* extract results */ - re_camera_params_get(re, ¶ms); + copy_m4_m4(re->winmat, params.winmat); + re->clip_start = params.clip_start; + re->clip_end = params.clip_end; + re->viewplane = params.viewplane; } void RE_GetCameraWindow(struct Render *re, struct Object *camera, float r_winmat[4][4]) @@ -241,6 +229,19 @@ void RE_GetCameraModelMatrix(Render *re, struct Object *camera, float r_modelmat BKE_camera_multiview_model_matrix(&re->r, camera, re->viewname, r_modelmat); } +void RE_GetViewPlane(Render *re, rctf *r_viewplane, rcti *r_disprect) +{ + *r_viewplane = re->viewplane; + + /* make disprect zero when no border render, is needed to detect changes in 3d view render */ + if (re->r.mode & R_BORDER) { + *r_disprect = re->disprect; + } + else { + BLI_rcti_init(r_disprect, 0, 0, 0, 0); + } +} + /* ~~~~~~~~~~~~~~~~ part (tile) calculus ~~~~~~~~~~~~~~~~~~~~~~ */ void RE_parts_free(Render *re) diff --git a/source/blender/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c index a4f68419c67..1859886f563 100644 --- a/source/blender/render/intern/multires_bake.c +++ b/source/blender/render/intern/multires_bake.c @@ -768,7 +768,7 @@ static void *init_heights_data(MultiresBakeRender *bkr, Image *ima) if (ss_lvl > 0) { smd.levels = smd.renderLevels = ss_lvl; - smd.uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_CORNERS; + smd.uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES; smd.quality = 3; height_data->ssdm = subsurf_make_derived_from_derived( diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 4628a5a132b..320f8b8ed03 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -983,53 +983,6 @@ void render_update_anim_renderdata(Render *re, RenderData *rd, ListBase *render_ BLI_duplicatelist(&re->r.views, &rd->views); } -void RE_SetWindow(Render *re, const rctf *viewplane, float clip_start, float clip_end) -{ - /* re->ok flag? */ - - re->viewplane = *viewplane; - re->clip_start = clip_start; - re->clip_end = clip_end; - - perspective_m4(re->winmat, - re->viewplane.xmin, - re->viewplane.xmax, - re->viewplane.ymin, - re->viewplane.ymax, - re->clip_start, - re->clip_end); -} - -void RE_SetOrtho(Render *re, const rctf *viewplane, float clip_start, float clip_end) -{ - /* re->ok flag? */ - - re->viewplane = *viewplane; - re->clip_start = clip_start; - re->clip_end = clip_end; - - orthographic_m4(re->winmat, - re->viewplane.xmin, - re->viewplane.xmax, - re->viewplane.ymin, - re->viewplane.ymax, - re->clip_start, - re->clip_end); -} - -void RE_GetViewPlane(Render *re, rctf *r_viewplane, rcti *r_disprect) -{ - *r_viewplane = re->viewplane; - - /* make disprect zero when no border render, is needed to detect changes in 3d view render */ - if (re->r.mode & R_BORDER) { - *r_disprect = re->disprect; - } - else { - BLI_rcti_init(r_disprect, 0, 0, 0, 0); - } -} - /* image and movie output has to move to either imbuf or kernel */ void RE_display_init_cb(Render *re, void *handle, void (*f)(void *handle, RenderResult *rr)) { diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c index 344b5af26aa..299ebffe8a8 100644 --- a/source/blender/sequencer/intern/image_cache.c +++ b/source/blender/sequencer/intern/image_cache.c @@ -824,16 +824,59 @@ static void seq_cache_valfree(void *val) BLI_mempool_free(item->cache_owner->items_pool, item); } -static void seq_cache_put_ex(SeqCache *cache, SeqCacheKey *key, ImBuf *ibuf) +static int get_stored_types_flag(Scene *scene, SeqCacheKey *key) { + int flag; + if (key->seq->cache_flag & SEQ_CACHE_OVERRIDE) { + flag = key->seq->cache_flag; + } + else { + flag = scene->ed->cache_flag; + } + + /* SEQ_CACHE_STORE_FINAL_OUT can not be overridden by strip cache */ + flag |= (scene->ed->cache_flag & SEQ_CACHE_STORE_FINAL_OUT); + + return flag; +} + +static void seq_cache_put_ex(Scene *scene, SeqCacheKey *key, ImBuf *ibuf) +{ + SeqCache *cache = seq_cache_get_from_scene(scene); SeqCacheItem *item; item = BLI_mempool_alloc(cache->items_pool); item->cache_owner = cache; item->ibuf = ibuf; + const int stored_types_flag = get_stored_types_flag(scene, key); + + /* Item stored for later use. */ + if (stored_types_flag & key->type) { + key->is_temp_cache = false; + key->link_prev = cache->last_key; + } + + /* Store pointer to last cached key. */ + SeqCacheKey *temp_last_key = cache->last_key; + if (BLI_ghash_reinsert(cache->hash, key, item, seq_cache_keyfree, seq_cache_valfree)) { IMB_refImBuf(ibuf); - cache->last_key = key; + + if (!key->is_temp_cache) { + cache->last_key = key; + } + } + + /* Set last_key's reference to this key so we can look up chain backwards. + * Item is already put in cache, so cache->last_key points to current key. + */ + if (!key->is_temp_cache && temp_last_key) { + temp_last_key->link_next = cache->last_key; + } + + /* Reset linking. */ + if (key->type == SEQ_CACHE_STORE_FINAL_OUT) { + cache->last_key = NULL; } } @@ -1095,6 +1138,35 @@ static void seq_cache_create(Main *bmain, Scene *scene) BLI_mutex_unlock(&cache_create_lock); } +static void seq_cache_populate_key(SeqCacheKey *key, + const SeqRenderData *context, + Sequence *seq, + const float timeline_frame, + const int type) +{ + key->cache_owner = seq_cache_get_from_scene(context->scene); + key->seq = seq; + key->context = *context; + key->frame_index = seq_cache_timeline_frame_to_frame_index(seq, timeline_frame, type); + key->timeline_frame = timeline_frame; + key->type = type; + key->link_prev = NULL; + key->link_next = NULL; + key->is_temp_cache = true; + key->task_id = context->task_id; +} + +static SeqCacheKey *seq_cache_allocate_key(SeqCache *cache, + const SeqRenderData *context, + Sequence *seq, + const float timeline_frame, + const int type) +{ + SeqCacheKey *key = BLI_mempool_alloc(cache->keys_pool); + seq_cache_populate_key(key, context, seq, timeline_frame, type); + return key; +} + /* ***************************** API ****************************** */ void seq_cache_free_temp_cache(Scene *scene, short id, int timeline_frame) @@ -1243,8 +1315,7 @@ void seq_cache_cleanup_sequence(Scene *scene, struct ImBuf *seq_cache_get(const SeqRenderData *context, Sequence *seq, float timeline_frame, - int type, - bool skip_disk_cache) + int type) { if (context->skip_cache || context->is_proxy_render || !seq) { @@ -1274,11 +1345,7 @@ struct ImBuf *seq_cache_get(const SeqRenderData *context, /* Try RAM cache: */ if (cache && seq) { - key.seq = seq; - key.context = *context; - key.frame_index = seq_cache_timeline_frame_to_frame_index(seq, timeline_frame, type); - key.type = type; - + seq_cache_populate_key(&key, context, seq, timeline_frame, type); ibuf = seq_cache_get_ex(cache, &key); } seq_cache_unlock(scene); @@ -1288,7 +1355,7 @@ struct ImBuf *seq_cache_get(const SeqRenderData *context, } /* Try disk cache: */ - if (!skip_disk_cache && seq_disk_cache_is_enabled(context->bmain)) { + if (seq_disk_cache_is_enabled(context->bmain)) { if (cache->disk_cache == NULL) { seq_disk_cache_create(context->bmain, context->scene); } @@ -1296,25 +1363,23 @@ struct ImBuf *seq_cache_get(const SeqRenderData *context, BLI_mutex_lock(&cache->disk_cache->read_write_mutex); ibuf = seq_disk_cache_read_file(cache->disk_cache, &key); BLI_mutex_unlock(&cache->disk_cache->read_write_mutex); - if (ibuf) { - if (key.type == SEQ_CACHE_STORE_FINAL_OUT) { - seq_cache_put_if_possible(context, seq, timeline_frame, type, ibuf, true); - } - else { - seq_cache_put(context, seq, timeline_frame, type, ibuf, true); - } + + if (ibuf == NULL) { + return NULL; + } + + /* Store read image in RAM. Only recycle item for final type. */ + if (key.type != SEQ_CACHE_STORE_FINAL_OUT || seq_cache_recycle_item(scene)) { + SeqCacheKey *new_key = seq_cache_allocate_key(cache, context, seq, timeline_frame, type); + seq_cache_put_ex(scene, new_key, ibuf); } } return ibuf; } -bool seq_cache_put_if_possible(const SeqRenderData *context, - Sequence *seq, - float timeline_frame, - int type, - ImBuf *ibuf, - bool skip_disk_cache) +bool seq_cache_put_if_possible( + const SeqRenderData *context, Sequence *seq, float timeline_frame, int type, ImBuf *ibuf) { Scene *scene = context->scene; @@ -1329,7 +1394,7 @@ bool seq_cache_put_if_possible(const SeqRenderData *context, } if (seq_cache_recycle_item(scene)) { - seq_cache_put(context, seq, timeline_frame, type, ibuf, skip_disk_cache); + seq_cache_put(context, seq, timeline_frame, type, ibuf); return true; } @@ -1338,12 +1403,8 @@ bool seq_cache_put_if_possible(const SeqRenderData *context, return false; } -void seq_cache_put(const SeqRenderData *context, - Sequence *seq, - float timeline_frame, - int type, - ImBuf *i, - bool skip_disk_cache) +void seq_cache_put( + const SeqRenderData *context, Sequence *seq, float timeline_frame, int type, ImBuf *i) { if (i == NULL || context->skip_cache || context->is_proxy_render || !seq) { return; @@ -1359,7 +1420,7 @@ void seq_cache_put(const SeqRenderData *context, } /* Prevent reinserting, it breaks cache key linking. */ - ImBuf *test = seq_cache_get(context, seq, timeline_frame, type, true); + ImBuf *test = seq_cache_get(context, seq, timeline_frame, type); if (test) { IMB_freeImBuf(test); return; @@ -1370,63 +1431,12 @@ void seq_cache_put(const SeqRenderData *context, } seq_cache_lock(scene); - SeqCache *cache = seq_cache_get_from_scene(scene); - int flag; - - if (seq->cache_flag & SEQ_CACHE_OVERRIDE) { - flag = seq->cache_flag; - /* Final_out is invalid in context of sequence override. */ - flag -= seq->cache_flag & SEQ_CACHE_STORE_FINAL_OUT; - /* If global setting is enabled however, use it. */ - flag |= scene->ed->cache_flag & SEQ_CACHE_STORE_FINAL_OUT; - } - else { - flag = scene->ed->cache_flag; - } - - SeqCacheKey *key; - key = BLI_mempool_alloc(cache->keys_pool); - key->cache_owner = cache; - key->seq = seq; - key->context = *context; - key->frame_index = seq_cache_timeline_frame_to_frame_index(seq, timeline_frame, type); - key->timeline_frame = timeline_frame; - key->type = type; - key->link_prev = NULL; - key->link_next = NULL; - key->is_temp_cache = true; - key->task_id = context->task_id; - - /* Item stored for later use */ - if (flag & type) { - key->is_temp_cache = false; - key->link_prev = cache->last_key; - } - - SeqCacheKey *temp_last_key = cache->last_key; - seq_cache_put_ex(cache, key, i); - - /* Restore pointer to previous item as this one will be freed when stack is rendered. */ - if (key->is_temp_cache) { - cache->last_key = temp_last_key; - } - - /* Set last_key's reference to this key so we can look up chain backwards. - * Item is already put in cache, so cache->last_key points to current key. - */ - if (flag & type && temp_last_key) { - temp_last_key->link_next = cache->last_key; - } - - /* Reset linking. */ - if (key->type == SEQ_CACHE_STORE_FINAL_OUT) { - cache->last_key = NULL; - } - + SeqCacheKey *key = seq_cache_allocate_key(cache, context, seq, timeline_frame, type); + seq_cache_put_ex(scene, key, i); seq_cache_unlock(scene); - if (!key->is_temp_cache && !skip_disk_cache) { + if (!key->is_temp_cache) { if (seq_disk_cache_is_enabled(context->bmain)) { if (cache->disk_cache == NULL) { seq_disk_cache_create(context->bmain, context->scene); diff --git a/source/blender/sequencer/intern/image_cache.h b/source/blender/sequencer/intern/image_cache.h index 41e8c4d1d48..63c559caee9 100644 --- a/source/blender/sequencer/intern/image_cache.h +++ b/source/blender/sequencer/intern/image_cache.h @@ -40,20 +40,17 @@ struct Sequence; struct ImBuf *seq_cache_get(const struct SeqRenderData *context, struct Sequence *seq, float timeline_frame, - int type, - bool skip_disk_cache); + int type); void seq_cache_put(const struct SeqRenderData *context, struct Sequence *seq, float timeline_frame, int type, - struct ImBuf *i, - bool skip_disk_cache); + struct ImBuf *i); bool seq_cache_put_if_possible(const struct SeqRenderData *context, struct Sequence *seq, float timeline_frame, int type, - struct ImBuf *nval, - bool skip_disk_cache); + struct ImBuf *nval); bool seq_cache_recycle_item(struct Scene *scene); void seq_cache_free_temp_cache(struct Scene *scene, short id, int timeline_frame); void seq_cache_destruct(struct Scene *scene); diff --git a/source/blender/sequencer/intern/prefetch.c b/source/blender/sequencer/intern/prefetch.c index b0e9e3c5003..4317fa3a850 100644 --- a/source/blender/sequencer/intern/prefetch.c +++ b/source/blender/sequencer/intern/prefetch.c @@ -379,28 +379,28 @@ static bool seq_prefetch_do_skip_frame(PrefetchJob *pfjob, ListBase *seqbase) if (seq_arr[i]->type == SEQ_TYPE_SCENE && (seq_arr[i]->flag & SEQ_SCENE_STRIPS) == 0) { int cached_types = 0; - ibuf = seq_cache_get(ctx, seq_arr[i], cfra, SEQ_CACHE_STORE_FINAL_OUT, false); + ibuf = seq_cache_get(ctx, seq_arr[i], cfra, SEQ_CACHE_STORE_FINAL_OUT); if (ibuf != NULL) { cached_types |= SEQ_CACHE_STORE_FINAL_OUT; IMB_freeImBuf(ibuf); ibuf = NULL; } - ibuf = seq_cache_get(ctx, seq_arr[i], cfra, SEQ_CACHE_STORE_COMPOSITE, false); + ibuf = seq_cache_get(ctx, seq_arr[i], cfra, SEQ_CACHE_STORE_COMPOSITE); if (ibuf != NULL) { cached_types |= SEQ_CACHE_STORE_COMPOSITE; IMB_freeImBuf(ibuf); ibuf = NULL; } - ibuf = seq_cache_get(ctx, seq_arr[i], cfra, SEQ_CACHE_STORE_PREPROCESSED, false); + ibuf = seq_cache_get(ctx, seq_arr[i], cfra, SEQ_CACHE_STORE_PREPROCESSED); if (ibuf != NULL) { cached_types |= SEQ_CACHE_STORE_PREPROCESSED; IMB_freeImBuf(ibuf); ibuf = NULL; } - ibuf = seq_cache_get(ctx, seq_arr[i], cfra, SEQ_CACHE_STORE_RAW, false); + ibuf = seq_cache_get(ctx, seq_arr[i], cfra, SEQ_CACHE_STORE_RAW); if (ibuf != NULL) { cached_types |= SEQ_CACHE_STORE_RAW; IMB_freeImBuf(ibuf); diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index cd993ae45c1..db317ffe7d6 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -730,14 +730,14 @@ static ImBuf *seq_render_preprocess_ibuf(const SeqRenderData *context, /* Proxies and effect strips are not stored in cache. */ if (!is_proxy_image && (seq->type & SEQ_TYPE_EFFECT) == 0) { - seq_cache_put(context, seq, timeline_frame, SEQ_CACHE_STORE_RAW, ibuf, false); + seq_cache_put(context, seq, timeline_frame, SEQ_CACHE_STORE_RAW, ibuf); } if (use_preprocess) { ibuf = input_preprocess(context, seq, timeline_frame, ibuf); } - seq_cache_put(context, seq, timeline_frame, SEQ_CACHE_STORE_PREPROCESSED, ibuf, false); + seq_cache_put(context, seq, timeline_frame, SEQ_CACHE_STORE_PREPROCESSED, ibuf); return ibuf; } @@ -1605,8 +1605,7 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, } if (view_id != context->view_id) { - seq_cache_put( - &localcontext, seq, timeline_frame, SEQ_CACHE_STORE_RAW, ibufs_arr[view_id], false); + seq_cache_put(&localcontext, seq, timeline_frame, SEQ_CACHE_STORE_RAW, ibufs_arr[view_id]); } RE_ReleaseResultImage(re); @@ -1781,14 +1780,14 @@ ImBuf *seq_render_strip(const SeqRenderData *context, bool use_preprocess = false; bool is_proxy_image = false; - ibuf = seq_cache_get(context, seq, timeline_frame, SEQ_CACHE_STORE_PREPROCESSED, false); + ibuf = seq_cache_get(context, seq, timeline_frame, SEQ_CACHE_STORE_PREPROCESSED); if (ibuf != NULL) { return ibuf; } /* Proxies are not stored in cache. */ if (!SEQ_can_use_proxy(seq, SEQ_rendersize_to_proxysize(context->preview_render_size))) { - ibuf = seq_cache_get(context, seq, timeline_frame, SEQ_CACHE_STORE_RAW, false); + ibuf = seq_cache_get(context, seq, timeline_frame, SEQ_CACHE_STORE_RAW); } if (ibuf == NULL) { @@ -1895,7 +1894,7 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, int early_out; Sequence *seq = seq_arr[i]; - out = seq_cache_get(context, seq, timeline_frame, SEQ_CACHE_STORE_COMPOSITE, false); + out = seq_cache_get(context, seq, timeline_frame, SEQ_CACHE_STORE_COMPOSITE); if (out) { break; @@ -1924,8 +1923,7 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, out = seq_render_strip_stack_apply_effect(context, seq, timeline_frame, ibuf1, ibuf2); - seq_cache_put( - context, seq_arr[i], timeline_frame, SEQ_CACHE_STORE_COMPOSITE, out, false); + seq_cache_put(context, seq_arr[i], timeline_frame, SEQ_CACHE_STORE_COMPOSITE, out); IMB_freeImBuf(ibuf1); IMB_freeImBuf(ibuf2); @@ -1951,7 +1949,7 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, IMB_freeImBuf(ibuf2); } - seq_cache_put(context, seq_arr[i], timeline_frame, SEQ_CACHE_STORE_COMPOSITE, out, false); + seq_cache_put(context, seq_arr[i], timeline_frame, SEQ_CACHE_STORE_COMPOSITE, out); } return out; @@ -1990,8 +1988,7 @@ ImBuf *SEQ_render_give_ibuf(const SeqRenderData *context, float timeline_frame, count = seq_get_shown_sequences(seqbasep, timeline_frame, chanshown, seq_arr); if (count) { - out = seq_cache_get( - context, seq_arr[count - 1], timeline_frame, SEQ_CACHE_STORE_FINAL_OUT, false); + out = seq_cache_get(context, seq_arr[count - 1], timeline_frame, SEQ_CACHE_STORE_FINAL_OUT); } seq_cache_free_temp_cache(context->scene, context->task_id, timeline_frame); @@ -2001,12 +1998,11 @@ ImBuf *SEQ_render_give_ibuf(const SeqRenderData *context, float timeline_frame, out = seq_render_strip_stack(context, &state, seqbasep, timeline_frame, chanshown); if (context->is_prefetch_render) { - seq_cache_put( - context, seq_arr[count - 1], timeline_frame, SEQ_CACHE_STORE_FINAL_OUT, out, false); + seq_cache_put(context, seq_arr[count - 1], timeline_frame, SEQ_CACHE_STORE_FINAL_OUT, out); } else { seq_cache_put_if_possible( - context, seq_arr[count - 1], timeline_frame, SEQ_CACHE_STORE_FINAL_OUT, out, false); + context, seq_arr[count - 1], timeline_frame, SEQ_CACHE_STORE_FINAL_OUT, out); } BLI_mutex_unlock(&seq_render_mutex); } diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 540896664da..69177f39c04 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -685,6 +685,7 @@ ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid); void WM_drag_add_local_ID(struct wmDrag *drag, struct ID *id, struct ID *from_parent); struct ID *WM_drag_get_local_ID(const struct wmDrag *drag, short idcode); struct ID *WM_drag_get_local_ID_from_event(const struct wmEvent *event, short idcode); +bool WM_drag_is_ID_type(const struct wmDrag *drag, int idcode); struct wmDragAsset *WM_drag_get_asset_data(const struct wmDrag *drag, int idcode); struct ID *WM_drag_get_local_ID_or_import_from_asset(const struct wmDrag *drag, int idcode); diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_api.h b/source/blender/windowmanager/gizmo/WM_gizmo_api.h index 70ddae4d724..cf1a7628267 100644 --- a/source/blender/windowmanager/gizmo/WM_gizmo_api.h +++ b/source/blender/windowmanager/gizmo/WM_gizmo_api.h @@ -301,7 +301,7 @@ void WM_gizmomap_draw(struct wmGizmoMap *gzmap, void WM_gizmomap_add_handlers(struct ARegion *region, struct wmGizmoMap *gzmap); bool WM_gizmomap_select_all(struct bContext *C, struct wmGizmoMap *gzmap, const int action); bool WM_gizmomap_cursor_set(const struct wmGizmoMap *gzmap, struct wmWindow *win); -void WM_gizmomap_message_subscribe(struct bContext *C, +void WM_gizmomap_message_subscribe(const struct bContext *C, struct wmGizmoMap *gzmap, struct ARegion *region, struct wmMsgBus *mbus); diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h index 8f84c02be12..fd7f9c2de7c 100644 --- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h +++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h @@ -371,6 +371,14 @@ typedef struct wmGizmoType { */ wmGizmoFnMatrixBasisGet matrix_basis_get; + /** + * Returns screen-space bounding box in the window space + * (compatible with #wmEvent.x #wmEvent.y). + * + * Used for tool-tip placement (otherwise the cursor location is used). + */ + wmGizmoFnScreenBoundsGet screen_bounds_get; + /** Activate a gizmo state when the user clicks on it. */ wmGizmoFnInvoke invoke; diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 479768c3536..12c6eb46c12 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -1156,7 +1156,7 @@ ListBase *wm_gizmomap_groups_get(wmGizmoMap *gzmap) return &gzmap->groups; } -void WM_gizmomap_message_subscribe(bContext *C, +void WM_gizmomap_message_subscribe(const bContext *C, wmGizmoMap *gzmap, ARegion *region, struct wmMsgBus *mbus) diff --git a/source/blender/windowmanager/gizmo/wm_gizmo_fn.h b/source/blender/windowmanager/gizmo/wm_gizmo_fn.h index 418e848e35b..84e6308223f 100644 --- a/source/blender/windowmanager/gizmo/wm_gizmo_fn.h +++ b/source/blender/windowmanager/gizmo/wm_gizmo_fn.h @@ -62,6 +62,9 @@ typedef void (*wmGizmoFnMatrixBasisGet)(const struct wmGizmo *, float[4][4]); typedef int (*wmGizmoFnInvoke)(struct bContext *, struct wmGizmo *, const struct wmEvent *); typedef void (*wmGizmoFnExit)(struct bContext *, struct wmGizmo *, const bool); typedef int (*wmGizmoFnCursorGet)(struct wmGizmo *); +typedef void (*wmGizmoFnScreenBoundsGet)(struct bContext *, + struct wmGizmo *, + rcti *r_bounding_box); typedef void (*wmGizmoFnSelectRefresh)(struct wmGizmo *); typedef void (*wmGizmoFnFree)(struct wmGizmo *); diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index a10284e9740..549b59e9e1d 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -279,6 +279,8 @@ IDTypeInfo IDType_ID_WM = { .blend_read_expand = NULL, .blend_read_undo_preserve = NULL, + + .lib_override_apply_post = NULL, }; #define MAX_OP_REGISTERED 32 diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c index d50516dfab2..e32552063af 100644 --- a/source/blender/windowmanager/intern/wm_cursors.c +++ b/source/blender/windowmanager/intern/wm_cursors.c @@ -246,8 +246,7 @@ void WM_cursor_wait(bool val) void WM_cursor_grab_enable(wmWindow *win, int wrap, bool hide, int bounds[4]) { /* Only grab cursor when not running debug. - * It helps not to get a stuck WM when hitting a breakpoint - * */ + * It helps not to get a stuck WM when hitting a break-point. */ GHOST_TGrabCursorMode mode = GHOST_kGrabNormal; GHOST_TAxisFlag mode_axis = GHOST_kAxisX | GHOST_kGrabAxisY; diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 08f60fef0d2..6fdcbab889c 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -225,7 +225,8 @@ static const char *dropbox_active(bContext *C, if (handler->dropboxes) { LISTBASE_FOREACH (wmDropBox *, drop, handler->dropboxes) { const char *tooltip = NULL; - if (drop->poll(C, drag, event, &tooltip)) { + if (drop->poll(C, drag, event, &tooltip) && + WM_operator_poll_context(C, drop->ot, drop->opcontext)) { /* XXX Doing translation here might not be ideal, but later we have no more * access to ot (and hence op context)... */ return (tooltip) ? tooltip : WM_operatortype_name(drop->ot, drop->ptr); @@ -353,6 +354,14 @@ ID *WM_drag_get_local_ID_from_event(const wmEvent *event, short idcode) return WM_drag_get_local_ID(lb->first, idcode); } +/** + * Check if the drag data is either a local ID or an external ID asset of type \a idcode. + */ +bool WM_drag_is_ID_type(const wmDrag *drag, int idcode) +{ + return WM_drag_get_local_ID(drag, idcode) || WM_drag_get_asset_data(drag, idcode); +} + wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode) { if (drag->type != WM_DRAG_ASSET) { diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 859336d0338..67372675805 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -543,13 +543,31 @@ void wm_event_do_notifiers(bContext *C) ED_screen_do_listen(C, note); LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) { - ED_region_do_listen(win, NULL, region, note, scene); + wmRegionListenerParams region_params = { + .area = NULL, + .region = region, + .scene = scene, + .notifier = note, + }; + ED_region_do_listen(®ion_params); } ED_screen_areas_iter (win, screen, area) { - ED_area_do_listen(win, area, note, scene); + wmSpaceTypeListenerParams area_params = { + .window = win, + .area = area, + .notifier = note, + .scene = scene, + }; + ED_area_do_listen(&area_params); LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { - ED_region_do_listen(win, area, region, note, scene); + wmRegionListenerParams region_params = { + .area = area, + .region = region, + .scene = scene, + .notifier = note, + }; + ED_region_do_listen(®ion_params); } } } @@ -2788,8 +2806,10 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers LISTBASE_FOREACH (wmDrag *, drag, lb) { const char *tooltip = NULL; if (drop->poll(C, drag, event, &tooltip)) { - /* Optionally copy drag information to operator properties. */ - if (drop->copy) { + /* Optionally copy drag information to operator properties. Don't call it if the + * operator fails anyway, it might do more than just set properties (e.g. + * typically import an asset). */ + if (drop->copy && WM_operator_poll_context(C, drop->ot, drop->opcontext)) { drop->copy(drag, drop); } diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c index 8dfe26fbf79..aeab1ee6fca 100644 --- a/source/blender/windowmanager/intern/wm_operator_props.c +++ b/source/blender/windowmanager/intern/wm_operator_props.c @@ -284,15 +284,15 @@ void WM_operator_properties_select_action_simple(wmOperatorType *ot, */ void WM_operator_properties_select_random(wmOperatorType *ot) { - RNA_def_float_percentage(ot->srna, - "percent", - 50.0f, - 0.0f, - 100.0f, - "Percent", - "Percentage of objects to select randomly", - 0.0f, - 100.0f); + RNA_def_float_factor(ot->srna, + "ratio", + 0.5f, + 0.0f, + 1.0f, + "Ratio", + "Portion of items to select randomly", + 0.f, + 1.0f); RNA_def_int(ot->srna, "seed", 0, diff --git a/source/blender/windowmanager/intern/wm_operator_type.c b/source/blender/windowmanager/intern/wm_operator_type.c index 4c4fd2b1a8e..0e57a92b685 100644 --- a/source/blender/windowmanager/intern/wm_operator_type.c +++ b/source/blender/windowmanager/intern/wm_operator_type.c @@ -424,9 +424,8 @@ static int wm_macro_modal(bContext *C, wmOperator *op, const wmEvent *event) wm_event_free_handler(&handler->head); } - /* if operator is blocking, grab cursor - * This may end up grabbing twice, but we don't care. - * */ + /* If operator is blocking, grab cursor. + * This may end up grabbing twice, but we don't care. */ if (op->opm->type->flag & OPTYPE_BLOCKING) { int bounds[4] = {-1, -1, -1, -1}; int wrap = WM_CURSOR_WRAP_NONE; |