diff options
author | Lukas Stockner <lukas.stockner@freenet.de> | 2017-01-12 06:56:28 +0300 |
---|---|---|
committer | Lukas Stockner <lukas.stockner@freenet.de> | 2017-01-12 06:56:28 +0300 |
commit | 59bc9a1d514923cf21438292e71e53c2deb9cb7b (patch) | |
tree | 6d23adc32fd9d53aedda9c64fcaa4bda24e85c55 | |
parent | 73c48d7347ee9ca77aff7e558d97130c8eb43f87 (diff) | |
parent | 092cbcd1d2c31b10af31ce326bdf4efc2819a5fa (diff) |
Merge remote-tracking branch 'origin/master' into soc-2016-cycles_denoising
86 files changed, 1231 insertions, 459 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ab5f8a5b42..d61a7de1a33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -874,7 +874,7 @@ endif() # linux only, not cached set(WITH_BINRELOC OFF) -# MAXOSX only, set to avoid uninitialized +# MACOSX only, set to avoid uninitialized set(EXETYPE "") # C/C++ flags @@ -1580,7 +1580,7 @@ if(WITH_CXX11) if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") # TODO(sergey): Do we want c++11 or gnu-c++11 here? set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - elseif(MSVC12) + elseif(MSVC) # Nothing special is needed, C++11 features are available by default. else() message(FATAL_ERROR "Compiler ${CMAKE_C_COMPILER_ID} is not supported for C++11 build yet") diff --git a/doc/python_api/rst/bge_types/bge.types.KX_GameObject.rst b/doc/python_api/rst/bge_types/bge.types.KX_GameObject.rst index d8cc5e45e83..c5729fd5b19 100644 --- a/doc/python_api/rst/bge_types/bge.types.KX_GameObject.rst +++ b/doc/python_api/rst/bge_types/bge.types.KX_GameObject.rst @@ -405,7 +405,7 @@ base class --- :class:`SCA_IObject` .. note:: - This attribute is experemental and may be removed (but probably wont be). + This attribute is experimental and may be removed (but probably wont be). .. note:: @@ -419,7 +419,7 @@ base class --- :class:`SCA_IObject` .. note:: - This attribute is experemental and may be removed (but probably wont be). + This attribute is experimental and may be removed (but probably wont be). .. note:: @@ -453,7 +453,7 @@ base class --- :class:`SCA_IObject` .. attribute:: childrenRecursive - all children of this object including childrens children, (read-only). + all children of this object including children's children, (read-only). :type: :class:`CListValue` of :class:`KX_GameObject`'s @@ -536,7 +536,7 @@ base class --- :class:`SCA_IObject` .. method:: getAxisVect(vect) - Returns the axis vector rotates by the objects worldspace orientation. + Returns the axis vector rotates by the object's worldspace orientation. This is the equivalent of multiplying the vector by the orientation matrix. :arg vect: a vector to align the axis. @@ -596,7 +596,7 @@ base class --- :class:`SCA_IObject` Gets the game object's linear velocity. - This method returns the game object's velocity through it's centre of mass, ie no angular velocity component. + This method returns the game object's velocity through it's center of mass, ie no angular velocity component. :arg local: * False: you get the "global" velocity ie: relative to world orientation. @@ -609,7 +609,7 @@ base class --- :class:`SCA_IObject` Sets the game object's linear velocity. - This method sets game object's velocity through it's centre of mass, + This method sets game object's velocity through it's center of mass, ie no angular velocity component. This requires a dynamic object. @@ -814,7 +814,7 @@ base class --- :class:`SCA_IObject` # do something pass - The face paremeter determines the orientation of the normal. + The face parameter determines the orientation of the normal. * 0 => hit normal is always oriented towards the ray origin (as if you casted the ray from outside) * 1 => hit normal is the real face normal (only for mesh object, otherwise face has no effect) @@ -911,7 +911,7 @@ base class --- :class:`SCA_IObject` .. note:: - The gameObject argument has an advantage that it can convert from a mesh with modifiers applied (such as subsurf). + The gameObject argument has an advantage that it can convert from a mesh with modifiers applied (such as the Subdivision Surface modifier). .. warning:: @@ -919,7 +919,7 @@ base class --- :class:`SCA_IObject` .. warning:: - If the object is a part of a combound object it will fail (parent or child) + If the object is a part of a compound object it will fail (parent or child) .. warning:: diff --git a/doc/python_api/rst/bgl.rst b/doc/python_api/rst/bgl.rst index 99f481ce998..0b31e62963c 100644 --- a/doc/python_api/rst/bgl.rst +++ b/doc/python_api/rst/bgl.rst @@ -12,7 +12,7 @@ contents: dir(bgl). A simple search on the web can point to more than enough material to teach OpenGL programming, from books to many collections of tutorials. -Here is a comprehensive `list of books <https://www.opengl.org/documentation/books/>`__ (non free). +Here is a comprehensive `list of books <https://www.khronos.org/developers/books/>`__ (non free). The `arcsynthesis tutorials <https://web.archive.org/web/20150225192611/http://www.arcsynthesis.org/gltut/index.html>`__ is one of the best resources to learn modern OpenGL and `g-truc <http://www.g-truc.net/post-opengl-samples.html#menu>`__ @@ -2067,7 +2067,7 @@ offers a set of extensive examples, including advanced features. :arg length: Returns the length of the string returned in source (excluding the null terminator). :type source: :class:`bgl.Buffer` char. :arg source: Specifies an array of characters that is used to return the source code string. - + .. function:: glShaderSource(shader, shader_string): diff --git a/doc/python_api/rst/info_api_reference.rst b/doc/python_api/rst/info_api_reference.rst index 43469fc0cb7..5ef5866c44a 100644 --- a/doc/python_api/rst/info_api_reference.rst +++ b/doc/python_api/rst/info_api_reference.rst @@ -204,7 +204,7 @@ Lets say we want to access the texture of a brush via Python, to adjust its ``co - Start in the default scene and enable 'Sculpt' mode from the 3D-View header. - From the toolbar expand the **Texture** panel and add a new texture. - *Notice the texture button its self doesn't have very useful links (you can check the tool-tips).* + *Notice the texture button its self doesn't have very useful links (you can check the tooltips).* - The contrast setting isn't exposed in the sculpt toolbar, so view the texture in the properties panel... - In the properties button select the Texture context. diff --git a/doc/python_api/rst/info_overview.rst b/doc/python_api/rst/info_overview.rst index b4ae906277d..ba2e6949b81 100644 --- a/doc/python_api/rst/info_overview.rst +++ b/doc/python_api/rst/info_overview.rst @@ -19,7 +19,7 @@ This is a typical Python environment so tutorials on how to write Python scripts will work running the scripts in Blender too. Blender provides the :mod:`bpy` module to the Python interpreter. This module can be imported in a script and gives access to Blender data, classes, and functions. -Scripts that deal with Blender data will need to import this module. +Scripts that deal with Blender data will need to import this module. Here is a simple example of moving a vertex of the object named **Cube**: @@ -80,7 +80,7 @@ To run as modules: Add-ons ------- +------- Some of Blenders functionality is best kept optional, alongside scripts loaded at startup we have add-ons which are kept in their own directory ``scripts/addons``, @@ -213,7 +213,7 @@ A simple Blender/Python module can look like this: bpy.utils.register_class(SimpleOperator) def unregister(): - bpy.utils.unregister_class(SimpleOperator) + bpy.utils.unregister_class(SimpleOperator) if __name__ == "__main__": register() @@ -327,7 +327,7 @@ Say you want to store material settings for a custom engine. .. note:: *The class must be registered before being used in a property, failing to do so will raise an error:* - + ``ValueError: bpy_struct "Material" registration error: my_custom_props could not register`` @@ -429,4 +429,3 @@ Calling these operators: >>> bpy.ops.object.operator_2() Hello World OBJECT_OT_operator_2 {'FINISHED'} - diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 432cceece1c..3cb98c677c1 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1565,9 +1565,9 @@ def pyrna2sphinx(basepath): # operators def write_ops(): - API_BASEURL = "https://developer.blender.org/diffusion/B/browse/master/release/scripts/ " - API_BASEURL_ADDON = "https://developer.blender.org/diffusion/BA/" - API_BASEURL_ADDON_CONTRIB = "https://developer.blender.org/diffusion/BAC/" + API_BASEURL = "https://developer.blender.org/diffusion/B/browse/master/release/scripts " + API_BASEURL_ADDON = "https://developer.blender.org/diffusion/BA" + API_BASEURL_ADDON_CONTRIB = "https://developer.blender.org/diffusion/BAC" op_modules = {} for op in ops.values(): @@ -1659,8 +1659,11 @@ def write_sphinx_conf_py(basepath): fw("\n") # needed for latex, pdf gen + fw("latex_elements = {\n") + fw(" 'papersize': 'a4paper',\n") + fw("}\n\n") + fw("latex_documents = [ ('contents', 'contents.tex', 'Blender Index', 'Blender Foundation', 'manual'), ]\n") - fw("latex_paper_size = 'a4paper'\n") file.close() diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index fab03c7659b..66893d4d668 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -597,8 +597,8 @@ static void create_mesh(Scene *scene, Mesh *mesh, BL::Mesh& b_mesh, const vector<Shader*>& used_shaders, - bool subdivision=false, - bool subdivide_uvs=true) + bool subdivision = false, + bool subdivide_uvs = true) { /* count vertices and faces */ int numverts = b_mesh.vertices.length(); @@ -671,28 +671,10 @@ static void create_mesh(Scene *scene, int shader = clamp(f->material_index(), 0, used_shaders.size()-1); bool smooth = f->use_smooth() || use_loop_normals; - /* split vertices if normal is different + /* Create triangles. * - * note all vertex attributes must have been set here so we can split - * and copy attributes in split_vertex without remapping later */ - if(use_loop_normals) { - BL::Array<float, 12> loop_normals = f->split_normals(); - - for(int i = 0; i < n; i++) { - float3 loop_N = make_float3(loop_normals[i * 3], loop_normals[i * 3 + 1], loop_normals[i * 3 + 2]); - - if(N[vi[i]] != loop_N) { - int new_vi = mesh->split_vertex(vi[i]); - - /* set new normal and vertex index */ - N = attr_N->data_float3(); - N[new_vi] = loop_N; - vi[i] = new_vi; - } - } - } - - /* create triangles */ + * NOTE: Autosmooth is already taken care about. + */ if(n == 4) { if(is_zero(cross(mesh->verts[vi[1]] - mesh->verts[vi[0]], mesh->verts[vi[2]] - mesh->verts[vi[0]])) || is_zero(cross(mesh->verts[vi[2]] - mesh->verts[vi[0]], mesh->verts[vi[3]] - mesh->verts[vi[0]]))) @@ -724,24 +706,8 @@ static void create_mesh(Scene *scene, vi.reserve(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(); - - /* split vertices if normal is different - * - * note all vertex attributes must have been set here so we can split - * and copy attributes in split_vertex without remapping later */ - if(use_loop_normals) { - float3 loop_N = get_float3(b_mesh.loops[p->loop_start() + i].normal()); - - if(N[vi[i]] != loop_N) { - int new_vi = mesh->split_vertex(vi[i]); - - /* set new normal and vertex index */ - N = attr_N->data_float3(); - N[new_vi] = loop_N; - vi[i] = new_vi; - } - } } /* create subd faces */ @@ -961,7 +927,13 @@ Mesh *BlenderSync::sync_mesh(BL::Object& b_ob, mesh->subdivision_type = object_subdivision_type(b_ob, preview, experimental); - BL::Mesh b_mesh = object_to_mesh(b_data, b_ob, b_scene, true, !preview, need_undeformed, mesh->subdivision_type); + BL::Mesh b_mesh = object_to_mesh(b_data, + b_ob, + b_scene, + true, + !preview, + need_undeformed, + mesh->subdivision_type); if(b_mesh) { if(render_layer.use_surfaces && !hide_tris) { @@ -1086,7 +1058,13 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob, if(ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview)) { /* get derived mesh */ - b_mesh = object_to_mesh(b_data, b_ob, b_scene, true, !preview, false, false); + b_mesh = object_to_mesh(b_data, + b_ob, + b_scene, + true, + !preview, + false, + Mesh::SUBDIVISION_NONE); } if(!b_mesh) { @@ -1157,10 +1135,12 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob, { /* no motion, remove attributes again */ if(b_mesh.vertices.length() != numverts) { - VLOG(1) << "Topology differs, disabling motion blur."; + VLOG(1) << "Topology differs, disabling motion blur for object " + << b_ob.name(); } else { - VLOG(1) << "No actual deformation motion for object " << b_ob.name(); + VLOG(1) << "No actual deformation motion for object " + << b_ob.name(); } mesh->attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION); if(attr_mN) diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 2982c7b2868..4f87a54adfc 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -577,7 +577,7 @@ static void populate_bake_data(BakeData *data, const BL::BakePixel bp = pixel_array; int i; - for(i=0; i < num_pixels; i++) { + for(i = 0; i < num_pixels; i++) { if(bp.object_id() == object_id) { data->set(i, bp.primitive_id(), bp.uv(), bp.du_dx(), bp.du_dy(), bp.dv_dx(), bp.dv_dy()); } else { diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index de9ab2d63d3..91af27d64a3 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -65,12 +65,12 @@ static inline BL::Mesh object_to_mesh(BL::BlendData& data, bool apply_modifiers, bool render, bool calc_undeformed, - bool subdivision) + Mesh::SubdivisionType subdivision_type) { bool subsurf_mod_show_render; bool subsurf_mod_show_viewport; - if(subdivision) { + if(subdivision_type != Mesh::SUBDIVISION_NONE) { BL::Modifier subsurf_mod = object.modifiers[object.modifiers.length()-1]; subsurf_mod_show_render = subsurf_mod.show_render(); @@ -82,7 +82,7 @@ static inline BL::Mesh object_to_mesh(BL::BlendData& data, BL::Mesh me = data.meshes.new_from_object(scene, object, apply_modifiers, (render)? 2: 1, false, calc_undeformed); - if(subdivision) { + if(subdivision_type != Mesh::SUBDIVISION_NONE) { BL::Modifier subsurf_mod = object.modifiers[object.modifiers.length()-1]; subsurf_mod.show_render(subsurf_mod_show_render); @@ -91,9 +91,14 @@ static inline BL::Mesh object_to_mesh(BL::BlendData& data, if((bool)me) { if(me.use_auto_smooth()) { - me.calc_normals_split(); + if(subdivision_type == Mesh::SUBDIVISION_CATMULL_CLARK) { + me.calc_normals_split(); + } + else { + me.split_faces(); + } } - if(!subdivision) { + if(subdivision_type == Mesh::SUBDIVISION_NONE) { me.calc_tessface(true); } } diff --git a/intern/smoke/intern/VEC3.h b/intern/smoke/intern/VEC3.h index dafe5e52dbd..ffc7da88138 100644 --- a/intern/smoke/intern/VEC3.h +++ b/intern/smoke/intern/VEC3.h @@ -943,14 +943,16 @@ operator<<( std::ostream& os, const BasicVector::Vector3Dim<Scalar>& i ) { #if 0 char buf[256]; -#if _WIN32 - sprintf(buf,globVecFormatStr, (double)i[0],(double)i[1],(double)i[2]); -#else - snprintf(buf,256,globVecFormatStr, (double)i[0],(double)i[1],(double)i[2]); -#endif +# if _WIN32 + sprintf(buf,globVecFormatStr, (double)i[0],(double)i[1],(double)i[2]); +# else + snprintf(buf,256,globVecFormatStr, (double)i[0],(double)i[1],(double)i[2]); +# endif os << std::string(buf); +#else + (void)i; /* Ignored. */ #endif - return os; + return os; } diff --git a/release/scripts/startup/bl_operators/image.py b/release/scripts/startup/bl_operators/image.py index f00f5d97c5e..d3460383fe7 100644 --- a/release/scripts/startup/bl_operators/image.py +++ b/release/scripts/startup/bl_operators/image.py @@ -82,8 +82,8 @@ class EditExternally(Operator): import traceback traceback.print_exc() self.report({'ERROR'}, - "Image editor not found, " - "please specify in User Preferences > File") + "Image editor could not be launched, please ensure that " + "the path in User Preferences > File is valid, and Blender has rights to launch it") return {'CANCELLED'} diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py index cdab380bb9c..ef10e279bb4 100644 --- a/release/scripts/startup/bl_operators/object_quick_effects.py +++ b/release/scripts/startup/bl_operators/object_quick_effects.py @@ -210,8 +210,9 @@ class QuickExplode(Operator): settings = obj.particle_systems[-1].settings settings.count = self.amount - settings.frame_start = self.frame_start + # first set frame end, to prevent frame start clamping settings.frame_end = self.frame_end - self.frame_duration + settings.frame_start = self.frame_start settings.lifetime = self.frame_duration settings.normal_factor = self.velocity settings.render_type = 'NONE' diff --git a/release/scripts/startup/bl_ui/properties_game.py b/release/scripts/startup/bl_ui/properties_game.py index 386ad254892..98b7a76e541 100644 --- a/release/scripts/startup/bl_ui/properties_game.py +++ b/release/scripts/startup/bl_ui/properties_game.py @@ -289,7 +289,6 @@ class RENDER_PT_embedded(RenderButtonsPanel, Panel): row = layout.row() row.operator("view3d.game_start", text="Start") - row.label() row = layout.row() row.label(text="Resolution:") row = layout.row(align=True) @@ -310,8 +309,6 @@ class RENDER_PT_game_player(RenderButtonsPanel, Panel): row = layout.row() row.operator("wm.blenderplayer_start", text="Start") - row.label() - row = layout.row() row.label(text="Resolution:") row = layout.row(align=True) 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 08e07b8ed93..f8ee7c9a851 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -946,7 +946,10 @@ class GreasePencilDataPanel: row = col.row() row.prop(gpl, "use_onion_skinning") - row.prop(gpl, "use_ghost_custom_colors", text="", icon='COLOR') + sub = row.row(align=True) + icon = 'RESTRICT_RENDER_OFF' if gpl.use_ghosts_always else 'RESTRICT_RENDER_ON' + sub.prop(gpl, "use_ghosts_always", text="", icon=icon) + sub.prop(gpl, "use_ghost_custom_colors", text="", icon='COLOR') split = col.split(percentage=0.5) split.active = gpl.use_onion_skinning diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py index 4e2666d7e40..29fc56c3fad 100644 --- a/release/scripts/startup/bl_ui/properties_particle.py +++ b/release/scripts/startup/bl_ui/properties_particle.py @@ -752,8 +752,8 @@ class PARTICLE_PT_physics(ParticleButtonsPanel, Panel): subsub.operator("particle.target_remove", icon='ZOOMOUT', text="") sub = col.row() subsub = sub.column(align=True) - subsub.operator("particle.target_move_up", icon='MOVE_UP_VEC', text="") - subsub.operator("particle.target_move_down", icon='MOVE_DOWN_VEC', text="") + subsub.operator("particle.target_move_up", icon='TRIA_UP', text="") + subsub.operator("particle.target_move_down", icon='TRIA_DOWN', text="") key = psys.active_particle_target if key: @@ -816,8 +816,8 @@ class PARTICLE_PT_boidbrain(ParticleButtonsPanel, Panel): #sub.operator("boid.state_add", icon='ZOOMIN', text="") #sub.operator("boid.state_del", icon='ZOOMOUT', text="") #sub = row.row(align=True) - #sub.operator("boid.state_move_up", icon='MOVE_UP_VEC', text="") - #sub.operator("boid.state_move_down", icon='MOVE_DOWN_VEC', text="") + #sub.operator("boid.state_move_up", icon='TRIA_UP', text="") + #sub.operator("boid.state_move_down", icon='TRIA_DOWN', text="") state = boids.active_boid_state @@ -840,8 +840,8 @@ class PARTICLE_PT_boidbrain(ParticleButtonsPanel, Panel): subsub.operator("boid.rule_del", icon='ZOOMOUT', text="") sub = col.row() subsub = sub.column(align=True) - subsub.operator("boid.rule_move_up", icon='MOVE_UP_VEC', text="") - subsub.operator("boid.rule_move_down", icon='MOVE_DOWN_VEC', text="") + subsub.operator("boid.rule_move_up", icon='TRIA_UP', text="") + subsub.operator("boid.rule_move_down", icon='TRIA_DOWN', text="") rule = state.active_boid_rule @@ -849,8 +849,8 @@ class PARTICLE_PT_boidbrain(ParticleButtonsPanel, Panel): row = layout.row() row.prop(rule, "name", text="") #somebody make nice icons for boids here please! -jahka - row.prop(rule, "use_in_air", icon='MOVE_UP_VEC', text="") - row.prop(rule, "use_on_land", icon='MOVE_DOWN_VEC', text="") + row.prop(rule, "use_in_air", icon='TRIA_UP', text="") + row.prop(rule, "use_on_land", icon='TRIA_DOWN', text="") row = layout.row() @@ -1009,8 +1009,8 @@ class PARTICLE_PT_render(ParticleButtonsPanel, Panel): subsub = sub.column(align=True) subsub.operator("particle.dupliob_copy", icon='ZOOMIN', text="") subsub.operator("particle.dupliob_remove", icon='ZOOMOUT', text="") - subsub.operator("particle.dupliob_move_up", icon='MOVE_UP_VEC', text="") - subsub.operator("particle.dupliob_move_down", icon='MOVE_DOWN_VEC', text="") + subsub.operator("particle.dupliob_move_up", icon='TRIA_UP', text="") + subsub.operator("particle.dupliob_move_down", icon='TRIA_DOWN', text="") weight = part.active_dupliweight if weight: diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index e5b6a94c1ab..075a6f870fa 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -1180,11 +1180,18 @@ class USERPREF_PT_input(Panel): col.separator() col.label(text="NDOF Device:") sub = col.column(align=True) - sub.prop(inputs, "ndof_sensitivity", text="NDOF Sensitivity") - sub.prop(inputs, "ndof_orbit_sensitivity", text="NDOF Orbit Sensitivity") - sub.prop(inputs, "ndof_deadzone", text="NDOF Deadzone") + sub.prop(inputs, "ndof_sensitivity", text="Pan Sensitivity") + sub.prop(inputs, "ndof_orbit_sensitivity", text="Orbit Sensitivity") + sub.prop(inputs, "ndof_deadzone", text="Deadzone") + + sub.separator() + col.label(text="Navigation Style:") sub = col.column(align=True) sub.row().prop(inputs, "ndof_view_navigate_method", expand=True) + + sub.separator() + col.label(text="Rotation Style:") + sub = col.column(align=True) sub.row().prop(inputs, "ndof_view_rotate_method", expand=True) row.separator() diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 55fa2c28011..5e936076d0e 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2528,7 +2528,14 @@ class VIEW3D_MT_edit_mesh_edges(Menu): layout.operator("mesh.bevel").vertex_only = False layout.operator("mesh.edge_split") layout.operator("mesh.bridge_edge_loops") + + layout.separator() + layout.operator("transform.edge_slide") + layout.operator("mesh.loop_multi_select", text="Edge Loops").ring = False + layout.operator("mesh.loop_multi_select", text="Edge Rings").ring = True + layout.operator("mesh.loop_to_region") + layout.operator("mesh.region_to_loop") class VIEW3D_MT_edit_mesh_faces(Menu): diff --git a/release/scripts/templates_py/addon_add_object.py b/release/scripts/templates_py/addon_add_object.py index 8e57d7ef8f8..d294838d3a2 100644 --- a/release/scripts/templates_py/addon_add_object.py +++ b/release/scripts/templates_py/addon_add_object.py @@ -69,9 +69,9 @@ def add_object_button(self, context): # This allows you to right click on a button and link to the manual def add_object_manual_map(): - url_manual_prefix = "http://wiki.blender.org/index.php/Doc:2.6/Manual/" + url_manual_prefix = "https://www.blender.org/manual/" url_manual_mapping = ( - ("bpy.ops.mesh.add_object", "Modeling/Objects"), + ("bpy.ops.mesh.add_object", "editors/3dview/object"), ) return url_manual_prefix, url_manual_mapping diff --git a/source/blender/blenkernel/BKE_sound.h b/source/blender/blenkernel/BKE_sound.h index 28b15b2a310..a5c626e74d7 100644 --- a/source/blender/blenkernel/BKE_sound.h +++ b/source/blender/blenkernel/BKE_sound.h @@ -90,6 +90,8 @@ void BKE_sound_create_scene(struct Scene *scene); void BKE_sound_destroy_scene(struct Scene *scene); +void BKE_sound_reset_scene_specs(struct Scene *scene); + void BKE_sound_mute_scene(struct Scene *scene, int muted); void BKE_sound_update_fps(struct Scene *scene); diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c index 18ca1407ba0..ee25be36855 100644 --- a/source/blender/blenkernel/intern/collision.c +++ b/source/blender/blenkernel/intern/collision.c @@ -1014,7 +1014,7 @@ static bool cloth_points_collision_response_static(ClothModifierData *clmd, Coll } BLI_INLINE bool cloth_point_face_collision_params(const float p1[3], const float p2[3], const float v0[3], const float v1[3], const float v2[3], - float r_nor[3], float *r_lambda, float r_w[4]) + float r_nor[3], float *r_lambda, float r_w[3]) { float edge1[3], edge2[3], p2face[3], p1p2[3], v0p2[3]; float nor_v0p2, nor_p1p2; @@ -1026,7 +1026,7 @@ BLI_INLINE bool cloth_point_face_collision_params(const float p1[3], const float nor_v0p2 = dot_v3v3(v0p2, r_nor); madd_v3_v3v3fl(p2face, p2, r_nor, -nor_v0p2); - interp_weights_face_v3(r_w, v0, v1, v2, NULL, p2face); + interp_weights_tri_v3(r_w, v0, v1, v2, p2face); sub_v3_v3v3(p1p2, p2, p1); sub_v3_v3v3(v0p2, p2, v0); @@ -1085,7 +1085,7 @@ static CollPair *cloth_point_collpair( const float *co1 = mverts[bp1].co, *co2 = mverts[bp2].co, *co3 = mverts[bp3].co; float lambda /*, distance1 */, distance2; float facenor[3], v1p1[3], v1p2[3]; - float w[4]; + float w[3]; if (!cloth_point_face_collision_params(p1, p2, co1, co2, co3, facenor, &lambda, w)) return collpair; diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 66070923153..1d198e36e05 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -3739,7 +3739,7 @@ static void dynamic_paint_paint_mesh_cell_point_cb_ex( /* velocity brush, only do on main sample */ if (brush->flags & MOD_DPAINT_USES_VELOCITY && ss == 0 && brushVelocity) { - float weights[4]; + float weights[3]; float brushPointVelocity[3]; float velocity[3]; @@ -3748,7 +3748,7 @@ static void dynamic_paint_paint_mesh_cell_point_cb_ex( const int v3 = mloop[mlooptri[hitTri].tri[2]].v; /* calculate barycentric weights for hit point */ - interp_weights_face_v3(weights, mvert[v1].co, mvert[v2].co, mvert[v3].co, NULL, hitCoord); + interp_weights_tri_v3(weights, mvert[v1].co, mvert[v2].co, mvert[v3].co, hitCoord); /* simple check based on brush surface velocity, * todo: perhaps implement something that handles volume movement as well */ @@ -4737,7 +4737,7 @@ static void dynamic_paint_effect_spread_cb(void *userdata, const int index) CLAMP(w_factor, 0.0f, 1.0f); /* mix new wetness and color */ - pPoint->wetness = (1.0f - w_factor) * pPoint->wetness + w_factor * pPoint_prev->wetness; + pPoint->wetness = pPoint->wetness + w_factor * (pPoint_prev->wetness - pPoint->wetness); pPoint->e_color[3] = mixColors(pPoint->e_color, pPoint->e_color[3], pPoint_prev->e_color, pPoint_prev->e_color[3], w_factor); } diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 65f37d0a5b1..64535f229a9 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -1431,14 +1431,18 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name) /* if new name will be too long, truncate it */ if (nr > 999 && left_len > (MAX_ID_NAME - 8)) { /* assumption: won't go beyond 9999 */ - left[MAX_ID_NAME - 8] = 0; + left[MAX_ID_NAME - 8] = '\0'; left_len = MAX_ID_NAME - 8; } else if (left_len > (MAX_ID_NAME - 7)) { - left[MAX_ID_NAME - 7] = 0; + left[MAX_ID_NAME - 7] = '\0'; left_len = MAX_ID_NAME - 7; } + /* Code above may have generated invalid utf-8 string, due to raw truncation. + * Ensure we get a valid one now! */ + left_len -= BLI_utf8_invalid_strip(left, left_len); + for (idtest = lb->first; idtest; idtest = idtest->next) { int nrtest; if ( (id != idtest) && diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c index 4f1f6d963ed..d7d566a9ec0 100644 --- a/source/blender/blenkernel/intern/library_remap.c +++ b/source/blender/blenkernel/intern/library_remap.c @@ -522,8 +522,7 @@ void BKE_libblock_remap_locked( * been incremented for that, we have to decrease once more its user count... unless we had to skip * some 'user_one' cases. */ if ((old_id->tag & LIB_TAG_EXTRAUSER_SET) && !(id_remap_data.status & ID_REMAP_IS_USER_ONE_SKIPPED)) { - id_us_min(old_id); - old_id->tag &= ~LIB_TAG_EXTRAUSER_SET; + id_us_clear_real(old_id); } BLI_assert(old_id->us - skipped_refcounted >= 0); @@ -563,8 +562,14 @@ void BKE_libblock_remap_locked( default: break; } + /* Node trees may virtually use any kind of data-block... */ + /* XXX Yuck!!!! nodetree update can do pretty much any thing when talking about py nodes, + * including creating new data-blocks (see T50385), so we need to unlock main here. :( + * Why can't we have re-entrent locks? */ + BKE_main_unlock(bmain); libblock_remap_data_postprocess_nodetree_update(bmain, new_id); + BKE_main_lock(bmain); /* Full rebuild of DAG! */ DAG_relations_tag_update(bmain); @@ -883,9 +888,10 @@ void BKE_libblock_free_us(Main *bmain, void *idv) /* test users */ * Since only 'user_one' usage of objects is groups, and only 'real user' usage of objects is scenes, * removing that 'user_one' tag when there is no more real (scene) users of an object ensures it gets * fully unlinked. + * But only for local objects, not linked ones! * Otherwise, there is no real way to get rid of an object anymore - better handling of this is TODO. */ - if ((GS(id->name) == ID_OB) && (id->us == 1)) { + if ((GS(id->name) == ID_OB) && (id->us == 1) && (id->lib == NULL)) { id_us_clear_real(id); } diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index d21f43ac484..af02e02b017 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -2346,6 +2346,11 @@ Mesh *BKE_mesh_new_from_object( tmpmesh = BKE_mesh_add(bmain, "Mesh"); DM_to_mesh(dm, tmpmesh, ob, mask, true); + + /* Copy autosmooth settings from original mesh. */ + Mesh *me = (Mesh *)ob->data; + tmpmesh->flag |= (me->flag & ME_AUTOSMOOTH); + tmpmesh->smoothresh = me->smoothresh; } /* BKE_mesh_add/copy gives us a user count we don't need */ diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index a3fe73e4b11..f9eba118383 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -1909,19 +1909,19 @@ void BKE_mesh_calc_poly_center( const MVert *mvarray, float r_cent[3]) { if (mpoly->totloop == 3) { - cent_tri_v3(r_cent, - mvarray[loopstart[0].v].co, - mvarray[loopstart[1].v].co, - mvarray[loopstart[2].v].co - ); + mid_v3_v3v3v3(r_cent, + mvarray[loopstart[0].v].co, + mvarray[loopstart[1].v].co, + mvarray[loopstart[2].v].co + ); } else if (mpoly->totloop == 4) { - cent_quad_v3(r_cent, - mvarray[loopstart[0].v].co, - mvarray[loopstart[1].v].co, - mvarray[loopstart[2].v].co, - mvarray[loopstart[3].v].co - ); + mid_v3_v3v3v3v3(r_cent, + mvarray[loopstart[0].v].co, + mvarray[loopstart[1].v].co, + mvarray[loopstart[2].v].co, + mvarray[loopstart[3].v].co + ); } else { mesh_calc_ngon_center(mpoly, loopstart, mvarray, r_cent); @@ -1978,7 +1978,7 @@ static float mesh_calc_poly_planar_area_centroid( tri_area = area_tri_signed_v3(v1, v2, v3, normal); total_area += tri_area; - cent_tri_v3(tri_cent, v1, v2, v3); + mid_v3_v3v3v3(tri_cent, v1, v2, v3); madd_v3_v3fl(r_cent, tri_cent, tri_area); copy_v3_v3(v2, v3); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index c6666ec5ced..e93bfcdda83 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -246,6 +246,10 @@ bool BKE_object_support_modifier_type_check(Object *ob, int modifier_type) mti = modifierType_getInfo(modifier_type); + /* only geometry objects should be able to get modifiers [#25291] */ + if (!ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { + return false; + } if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsLattice) == 0) { return false; diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c index e8970d416e9..d0ef5cfc092 100644 --- a/source/blender/blenkernel/intern/smoke.c +++ b/source/blender/blenkernel/intern/smoke.c @@ -758,15 +758,14 @@ static void obstacles_from_derivedmesh_task_cb(void *userdata, const int z) /* find the nearest point on the mesh */ if (BLI_bvhtree_find_nearest(data->tree->tree, ray_start, &nearest, data->tree->nearest_callback, data->tree) != -1) { const MLoopTri *lt = &data->looptri[nearest.index]; - float weights[4]; + float weights[3]; int v1, v2, v3; /* calculate barycentric weights for nearest point */ v1 = data->mloop[lt->tri[0]].v; v2 = data->mloop[lt->tri[1]].v; v3 = data->mloop[lt->tri[2]].v; - interp_weights_face_v3( - weights, data->mvert[v1].co, data->mvert[v2].co, data->mvert[v3].co, NULL, nearest.co); + interp_weights_tri_v3(weights, data->mvert[v1].co, data->mvert[v2].co, data->mvert[v3].co, nearest.co); // DG TODO if (data->has_velocity) @@ -1454,7 +1453,7 @@ static void sample_derivedmesh( /* find the nearest point on the mesh */ if (BLI_bvhtree_find_nearest(treeData->tree, ray_start, &nearest, treeData->nearest_callback, treeData) != -1) { - float weights[4]; + float weights[3]; int v1, v2, v3, f_index = nearest.index; float n1[3], n2[3], n3[3], hit_normal[3]; @@ -1471,7 +1470,7 @@ static void sample_derivedmesh( v1 = mloop[mlooptri[f_index].tri[0]].v; v2 = mloop[mlooptri[f_index].tri[1]].v; v3 = mloop[mlooptri[f_index].tri[2]].v; - interp_weights_face_v3(weights, mvert[v1].co, mvert[v2].co, mvert[v3].co, NULL, nearest.co); + interp_weights_tri_v3(weights, mvert[v1].co, mvert[v2].co, mvert[v3].co, nearest.co); if (sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY && velocity_map) { /* apply normal directional velocity */ diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index f20885b1e8f..22288127119 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -167,6 +167,10 @@ static const char *force_device = NULL; #ifdef WITH_JACK static void sound_sync_callback(void *data, int mode, float time) { + // Ugly: Blender doesn't like it when the animation is played back during rendering + if (G.is_rendering) + return; + struct Main *bmain = (struct Main *)data; struct Scene *scene; @@ -450,6 +454,16 @@ void BKE_sound_destroy_scene(struct Scene *scene) AUD_destroySet(scene->speaker_handles); } +void BKE_sound_reset_scene_specs(struct Scene *scene) +{ + AUD_Specs specs; + + specs.channels = AUD_Device_getChannels(sound_device); + specs.rate = AUD_Device_getRate(sound_device); + + AUD_Sequence_setSpecs(scene->sound_scene, specs); +} + void BKE_sound_mute_scene(struct Scene *scene, int muted) { if (scene->sound_scene) @@ -576,15 +590,10 @@ void BKE_sound_update_sequencer(struct Main *main, bSound *sound) static void sound_start_play_scene(struct Scene *scene) { - AUD_Specs specs; - if (scene->playback_handle) AUD_Handle_stop(scene->playback_handle); - specs.channels = AUD_Device_getChannels(sound_device); - specs.rate = AUD_Device_getRate(sound_device); - - AUD_Sequence_setSpecs(scene->sound_scene, specs); + BKE_sound_reset_scene_specs(scene); if ((scene->playback_handle = AUD_Device_play(sound_device, scene->sound_scene, 1))) AUD_Handle_setLoopCount(scene->playback_handle, -1); @@ -693,6 +702,10 @@ void BKE_sound_seek_scene(struct Main *bmain, struct Scene *scene) float BKE_sound_sync_scene(struct Scene *scene) { + // Ugly: Blender doesn't like it when the animation is played back during rendering + if (G.is_rendering) + return NAN_FLT; + if (scene->playback_handle) { if (scene->audio.flag & AUDIO_SYNC) return AUD_getSynchronizerPosition(scene->playback_handle); @@ -704,6 +717,10 @@ float BKE_sound_sync_scene(struct Scene *scene) int BKE_sound_scene_playing(struct Scene *scene) { + // Ugly: Blender doesn't like it when the animation is played back during rendering + if (G.is_rendering) + return -1; + if (scene->audio.flag & AUDIO_SYNC) return AUD_isSynchronizerPlaying(); else @@ -898,6 +915,7 @@ void BKE_sound_delete_cache(struct bSound *UNUSED(sound)) {} void BKE_sound_load(struct Main *UNUSED(bmain), struct bSound *UNUSED(sound)) {} void BKE_sound_create_scene(struct Scene *UNUSED(scene)) {} void BKE_sound_destroy_scene(struct Scene *UNUSED(scene)) {} +void BKE_sound_reset_scene_specs(struct Scene *UNUSED(scene)) {} void BKE_sound_mute_scene(struct Scene *UNUSED(scene), int UNUSED(muted)) {} void *BKE_sound_scene_add_scene_sound(struct Scene *UNUSED(scene), struct Sequence *UNUSED(sequence), int UNUSED(startframe), int UNUSED(endframe), int UNUSED(frameskip)) { return NULL; } diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 1636042f479..672857e88fe 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -410,6 +410,7 @@ Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const } ta = BKE_libblock_alloc(bmain, ID_TXT, BLI_path_basename(filepath_abs)); + ta->id.us = 0; BLI_listbase_clear(&ta->lines); ta->curl = ta->sell = NULL; diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index b0ab6f707fa..9994d479ce7 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -444,7 +444,7 @@ static void set_ffmpeg_property_option(AVCodecContext *c, IDProperty *prop, AVDi param = strchr(name, ':'); if (param) { - *param++ = 0; + *param++ = '\0'; } switch (prop->type) { @@ -1114,7 +1114,7 @@ static void ffmpeg_filepath_get(FFMpegContext *context, char *string, RenderData BLI_make_existing_file(string); - autosplit[0] = 0; + autosplit[0] = '\0'; if ((rd->ffcodecdata.flags & FFMPEG_AUTOSPLIT_OUTPUT) != 0) { if (context) { @@ -1137,7 +1137,7 @@ static void ffmpeg_filepath_get(FFMpegContext *context, char *string, RenderData strcat(string, *exts); } else { - *(string + strlen(string) - strlen(*fe)) = 0; + *(string + strlen(string) - strlen(*fe)) = '\0'; strcat(string, autosplit); strcat(string, *fe); } @@ -1267,7 +1267,7 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit) if (is_autosplit == false) { if (context->audio_mixdown_device) { AUD_Device_free(context->audio_mixdown_device); - context->audio_mixdown_device = 0; + context->audio_mixdown_device = NULL; } } #endif @@ -1283,50 +1283,50 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit) /* Close the video codec */ - if (context->video_stream && context->video_stream->codec) { + if (context->video_stream != NULL && context->video_stream->codec != NULL) { avcodec_close(context->video_stream->codec); PRINT("zero video stream %p\n", context->video_stream); - context->video_stream = 0; + context->video_stream = NULL; } - if (context->audio_stream && context->audio_stream->codec) { + if (context->audio_stream != NULL && context->audio_stream->codec != NULL) { avcodec_close(context->audio_stream->codec); - context->audio_stream = 0; + context->audio_stream = NULL; } /* free the temp buffer */ - if (context->current_frame) { + if (context->current_frame != NULL) { delete_picture(context->current_frame); - context->current_frame = 0; + context->current_frame = NULL; } - if (context->outfile && context->outfile->oformat) { + if (context->outfile != NULL && context->outfile->oformat) { if (!(context->outfile->oformat->flags & AVFMT_NOFILE)) { avio_close(context->outfile->pb); } } - if (context->outfile) { + if (context->outfile != NULL) { avformat_free_context(context->outfile); - context->outfile = 0; + context->outfile = NULL; } - if (context->audio_input_buffer) { + if (context->audio_input_buffer != NULL) { av_free(context->audio_input_buffer); - context->audio_input_buffer = 0; + context->audio_input_buffer = NULL; } #ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - if (context->audio_output_buffer) { + if (context->audio_output_buffer != NULL) { av_free(context->audio_output_buffer); - context->audio_output_buffer = 0; + context->audio_output_buffer = NULL; } #endif - if (context->audio_deinterleave_buffer) { + if (context->audio_deinterleave_buffer != NULL) { av_free(context->audio_deinterleave_buffer); - context->audio_deinterleave_buffer = 0; + context->audio_deinterleave_buffer = NULL; } - if (context->img_convert_ctx) { + if (context->img_convert_ctx != NULL) { sws_freeContext(context->img_convert_ctx); - context->img_convert_ctx = 0; + context->img_convert_ctx = NULL; } } @@ -1425,8 +1425,8 @@ static IDProperty *BKE_ffmpeg_property_add(RenderData *rd, const char *type, con int BKE_ffmpeg_property_add_string(RenderData *rd, const char *type, const char *str) { AVCodecContext c; - const AVOption *o = 0; - const AVOption *p = 0; + const AVOption *o = NULL; + const AVOption *p = NULL; char name_[128]; char *name; char *param; @@ -1445,7 +1445,7 @@ int BKE_ffmpeg_property_add_string(RenderData *rd, const char *type, const char param = strchr(name, ' '); } if (param) { - *param++ = 0; + *param++ = '\0'; while (*param == ' ') param++; } diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 514b0300274..d33c2cb3279 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -44,9 +44,6 @@ extern "C" { /********************************** Polygons *********************************/ -void cent_tri_v3(float r[3], const float a[3], const float b[3], const float c[3]); -void cent_quad_v3(float r[3], const float a[3], const float b[3], const float c[3], const float d[3]); - float normal_tri_v3(float r[3], const float a[3], const float b[3], const float c[3]); float normal_quad_v3(float r[3], const float a[3], const float b[3], const float c[3], const float d[3]); float normal_poly_v3(float r[3], const float verts[][3], unsigned int nr); @@ -326,10 +323,8 @@ bool clip_segment_v3_plane_n( float r_p1[3], float r_p2[3]); /****************************** Interpolation ********************************/ - -/* tri or quad, d can be NULL */ -void interp_weights_face_v3(float w[4], - const float a[3], const float b[3], const float c[3], const float d[3], const float p[3]); +void interp_weights_tri_v3(float w[3], const float a[3], const float b[3], const float c[3], const float p[3]); +void interp_weights_quad_v3(float w[4], const float a[3], const float b[3], const float c[3], const float d[3], const float p[3]); void interp_weights_poly_v3(float w[], float v[][3], const int n, const float co[3]); void interp_weights_poly_v2(float w[], float v[][2], const int n, const float co[2]); diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index d15fe1a95dc..8e0884ba347 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -234,6 +234,7 @@ void mid_v3_v3v3(float r[3], const float a[3], const float b[3]); void mid_v2_v2v2(float r[2], const float a[2], const float b[2]); void mid_v3_v3v3v3(float v[3], const float v1[3], const float v2[3], const float v3[3]); void mid_v3_v3v3v3v3(float v[3], const float v1[3], const float v2[3], const float v3[3], const float v4[3]); +void mid_v3_v3_array(float r[3], const float (*vec_arr)[3], const unsigned int nbr); void mid_v3_v3v3_angle_weighted(float r[3], const float a[3], const float b[3]); void mid_v3_angle_weighted(float r[3]); diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 38947e139ff..74ede1e7559 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -37,20 +37,6 @@ /********************************** Polygons *********************************/ -void cent_tri_v3(float cent[3], const float v1[3], const float v2[3], const float v3[3]) -{ - cent[0] = (v1[0] + v2[0] + v3[0]) / 3.0f; - cent[1] = (v1[1] + v2[1] + v3[1]) / 3.0f; - cent[2] = (v1[2] + v2[2] + v3[2]) / 3.0f; -} - -void cent_quad_v3(float cent[3], const float v1[3], const float v2[3], const float v3[3], const float v4[3]) -{ - cent[0] = 0.25f * (v1[0] + v2[0] + v3[0] + v4[0]); - cent[1] = 0.25f * (v1[1] + v2[1] + v3[1] + v4[1]); - cent[2] = 0.25f * (v1[2] + v2[2] + v3[2] + v4[2]); -} - void cross_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3]) { float n1[3], n2[3]; @@ -2964,7 +2950,15 @@ static bool barycentric_weights(const float v1[3], const float v2[3], const floa } } -void interp_weights_face_v3(float w[4], const float v1[3], const float v2[3], const float v3[3], const float v4[3], const float co[3]) +void interp_weights_tri_v3(float w[3], const float v1[3], const float v2[3], const float v3[3], const float co[3]) +{ + float n[3]; + + normal_tri_v3(n, v1, v2, v3); + barycentric_weights(v1, v2, v3, co, n, w); +} + +void interp_weights_quad_v3(float w[4], const float v1[3], const float v2[3], const float v3[3], const float v4[3], const float co[3]) { float w2[3]; @@ -2977,7 +2971,7 @@ void interp_weights_face_v3(float w[4], const float v1[3], const float v2[3], co w[1] = 1.0f; else if (equals_v3v3(co, v3)) w[2] = 1.0f; - else if (v4 && equals_v3v3(co, v4)) + else if (equals_v3v3(co, v4)) w[3] = 1.0f; else { /* otherwise compute barycentric interpolation weights */ @@ -2985,35 +2979,24 @@ void interp_weights_face_v3(float w[4], const float v1[3], const float v2[3], co bool degenerate; sub_v3_v3v3(n1, v1, v3); - if (v4) { - sub_v3_v3v3(n2, v2, v4); - } - else { - sub_v3_v3v3(n2, v2, v3); - } + sub_v3_v3v3(n2, v2, v4); cross_v3_v3v3(n, n1, n2); - /* OpenGL seems to split this way, so we do too */ - if (v4) { - degenerate = barycentric_weights(v1, v2, v4, co, n, w); - SWAP(float, w[2], w[3]); - - if (degenerate || (w[0] < 0.0f)) { - /* if w[1] is negative, co is on the other side of the v1-v3 edge, - * so we interpolate using the other triangle */ - degenerate = barycentric_weights(v2, v3, v4, co, n, w2); - - if (!degenerate) { - w[0] = 0.0f; - w[1] = w2[0]; - w[2] = w2[1]; - w[3] = w2[2]; - } + degenerate = barycentric_weights(v1, v2, v4, co, n, w); + SWAP(float, w[2], w[3]); + + if (degenerate || (w[0] < 0.0f)) { + /* if w[1] is negative, co is on the other side of the v1-v3 edge, + * so we interpolate using the other triangle */ + degenerate = barycentric_weights(v2, v3, v4, co, n, w2); + + if (!degenerate) { + w[0] = 0.0f; + w[1] = w2[0]; + w[2] = w2[1]; + w[3] = w2[2]; } } - else { - barycentric_weights(v1, v2, v3, co, n, w); - } } } @@ -3763,6 +3746,9 @@ void interp_barycentric_tri_v3(float data[3][3], float u, float v, float res[3]) /***************************** View & Projection *****************************/ +/** + * Matches `glOrtho` result. + */ void orthographic_m4(float matrix[4][4], const float left, const float right, const float bottom, const float top, const float nearClip, const float farClip) { @@ -3783,6 +3769,9 @@ void orthographic_m4(float matrix[4][4], const float left, const float right, co matrix[3][2] = -(farClip + nearClip) / Zdelta; } +/** + * Matches `glFrustum` result. + */ void perspective_m4(float mat[4][4], const float left, const float right, const float bottom, const float top, const float nearClip, const float farClip) { diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c index 95d5c9fde87..dfecc3b556a 100644 --- a/source/blender/blenlib/intern/math_vector.c +++ b/source/blender/blenlib/intern/math_vector.c @@ -280,6 +280,16 @@ void mid_v3_v3v3v3v3(float v[3], const float v1[3], const float v2[3], const flo v[2] = (v1[2] + v2[2] + v3[2] + v4[2]) / 4.0f; } +void mid_v3_v3_array(float r[3], const float (*vec_arr)[3], const unsigned int nbr) +{ + const float factor = 1.0f / (float)nbr; + zero_v3(r); + + for (unsigned int i = 0; i < nbr; i++) { + madd_v3_v3fl(r, vec_arr[i], factor); + } +} + /** * Specialized function for calculating normals. * fastpath for: diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index e9fb77f6302..ee5e8651bd3 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -479,7 +479,18 @@ MINLINE void mul_v2_v2_ccw(float r[2], const float mat[2], const float vec[2]) r[1] = mat[1] * vec[0] + (+mat[0]) * vec[1]; } -/* note: could add a matrix inline */ +/** + * Convenience function to get the projected depth of a position. + * This avoids creating a temporary 4D vector and multiplying it - only for the 4th component. + * + * Matches logic for: + * + * \code{.c} + * float co_4d[4] = {co[0], co[1], co[2], 1.0}; + * mul_m4_v4(mat, co_4d); + * return co_4d[3]; + * \endcode + */ MINLINE float mul_project_m4_v3_zfac(float mat[4][4], const float co[3]) { return (mat[0][3] * co[0]) + diff --git a/source/blender/blenlib/intern/rct.c b/source/blender/blenlib/intern/rct.c index 9d5a4630f68..ac73a981b45 100644 --- a/source/blender/blenlib/intern/rct.c +++ b/source/blender/blenlib/intern/rct.c @@ -420,20 +420,16 @@ void BLI_rctf_recenter(rctf *rect, float x, float y) /* change width & height around the central location */ void BLI_rcti_resize(rcti *rect, int x, int y) { - rect->xmin = rect->xmax = BLI_rcti_cent_x(rect); - rect->ymin = rect->ymax = BLI_rcti_cent_y(rect); - rect->xmin -= x / 2; - rect->ymin -= y / 2; + rect->xmin = BLI_rcti_cent_x(rect) - (x / 2); + rect->ymin = BLI_rcti_cent_y(rect) - (y / 2); rect->xmax = rect->xmin + x; rect->ymax = rect->ymin + y; } void BLI_rctf_resize(rctf *rect, float x, float y) { - rect->xmin = rect->xmax = BLI_rctf_cent_x(rect); - rect->ymin = rect->ymax = BLI_rctf_cent_y(rect); - rect->xmin -= x * 0.5f; - rect->ymin -= y * 0.5f; + rect->xmin = BLI_rctf_cent_x(rect) - (x * 0.5f); + rect->ymin = BLI_rctf_cent_y(rect) - (y * 0.5f); rect->xmax = rect->xmin + x; rect->ymax = rect->ymin + y; } diff --git a/source/blender/blenlib/intern/string_utf8.c b/source/blender/blenlib/intern/string_utf8.c index 96033615cf5..0ab11810b48 100644 --- a/source/blender/blenlib/intern/string_utf8.c +++ b/source/blender/blenlib/intern/string_utf8.c @@ -47,6 +47,19 @@ // #define DEBUG_STRSIZE +/* array copied from glib's gutf8.c, */ +/* Note: last two values (0xfe and 0xff) are forbidden in utf-8, so they are considered 1 byte length too. */ +static const size_t utf8_skip_data[256] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1 +}; + /* from libswish3, originally called u8_isvalid(), * modified to return the index of the bad character (byte index not utf). * http://svn.swish-e.org/libswish3/trunk/src/libswish3/utf8.c r3044 - campbell */ @@ -56,73 +69,91 @@ * length is in bytes, since without knowing whether the string is valid * it's hard to know how many characters there are! */ -static const char trailingBytesForUTF8[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 -}; - +/** + * Find first utf-8 invalid byte in given \a str, of \a length bytes. + * + * \return the offset of the first invalid byte. + */ int BLI_utf8_invalid_byte(const char *str, int length) { - const unsigned char *p, *pend = (const unsigned char *)str + length; + const unsigned char *p, *perr, *pend = (const unsigned char *)str + length; unsigned char c; int ab; - for (p = (const unsigned char *)str; p < pend; p++) { + for (p = (const unsigned char *)str; p < pend; p++, length--) { c = *p; + perr = p; /* Erroneous char is always the first of an invalid utf8 sequence... */ + if (ELEM(c, 0xfe, 0xff, 0x00)) /* Those three values are not allowed in utf8 string. */ + goto utf8_error; if (c < 128) continue; if ((c & 0xc0) != 0xc0) goto utf8_error; - ab = trailingBytesForUTF8[c]; - if (length < ab) + + /* Note that since we always increase p (and decrease length) by one byte in main loop, we only add/subtract + * extra utf8 bytes in code below + * (ab number, aka number of bytes remaining in the utf8 sequence after the initial one). */ + ab = (int)utf8_skip_data[c] - 1; + if (length <= ab) { goto utf8_error; - length -= ab; + } - p++; /* Check top bits in the second byte */ + p++; + length--; if ((*p & 0xc0) != 0x80) goto utf8_error; /* Check for overlong sequences for each different length */ switch (ab) { - /* Check for xx00 000x */ - case 1: - if ((c & 0x3e) == 0) goto utf8_error; - continue; /* We know there aren't any more bytes to check */ - - /* Check for 1110 0000, xx0x xxxx */ - case 2: - if (c == 0xe0 && (*p & 0x20) == 0) goto utf8_error; - break; - - /* Check for 1111 0000, xx00 xxxx */ - case 3: - if (c == 0xf0 && (*p & 0x30) == 0) goto utf8_error; - break; - - /* Check for 1111 1000, xx00 0xxx */ - case 4: - if (c == 0xf8 && (*p & 0x38) == 0) goto utf8_error; - break; - - /* Check for leading 0xfe or 0xff, - * and then for 1111 1100, xx00 00xx */ - case 5: - if (c == 0xfe || c == 0xff || - (c == 0xfc && (*p & 0x3c) == 0)) goto utf8_error; - break; + case 1: + /* Check for xx00 000x */ + if ((c & 0x3e) == 0) goto utf8_error; + continue; /* We know there aren't any more bytes to check */ + + case 2: + /* Check for 1110 0000, xx0x xxxx */ + if (c == 0xe0 && (*p & 0x20) == 0) goto utf8_error; + /* Some special cases, see section 5 of utf-8 decoder stress-test by Markus Kuhn + * (https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt). */ + /* From section 5.1 (and 5.2) */ + if (c == 0xed) { + if (*p == 0xa0 && *(p + 1) == 0x80) goto utf8_error; + if (*p == 0xad && *(p + 1) == 0xbf) goto utf8_error; + if (*p == 0xae && *(p + 1) == 0x80) goto utf8_error; + if (*p == 0xaf && *(p + 1) == 0xbf) goto utf8_error; + if (*p == 0xb0 && *(p + 1) == 0x80) goto utf8_error; + if (*p == 0xbe && *(p + 1) == 0x80) goto utf8_error; + if (*p == 0xbf && *(p + 1) == 0xbf) goto utf8_error; + } + /* From section 5.3 */ + if (c == 0xef) { + if (*p == 0xbf && *(p + 1) == 0xbe) goto utf8_error; + if (*p == 0xbf && *(p + 1) == 0xbf) goto utf8_error; + } + break; + + case 3: + /* Check for 1111 0000, xx00 xxxx */ + if (c == 0xf0 && (*p & 0x30) == 0) goto utf8_error; + break; + + case 4: + /* Check for 1111 1000, xx00 0xxx */ + if (c == 0xf8 && (*p & 0x38) == 0) goto utf8_error; + break; + + case 5: + /* Check for 1111 1100, xx00 00xx */ + if (c == 0xfc && (*p & 0x3c) == 0) goto utf8_error; + break; } /* Check for valid bytes after the 2nd, if any; all must start 10 */ while (--ab > 0) { - if ((*(p + 1) & 0xc0) != 0x80) goto utf8_error; - p++; /* do this after so we get usable offset - campbell */ + p++; + length--; + if ((*p & 0xc0) != 0x80) goto utf8_error; } } @@ -130,7 +161,7 @@ int BLI_utf8_invalid_byte(const char *str, int length) utf8_error: - return (int)((const char *)p - (const char *)str) - 1; + return (int)((const char *)perr - (const char *)str); } int BLI_utf8_invalid_strip(char *str, int length) @@ -141,7 +172,7 @@ int BLI_utf8_invalid_strip(char *str, int length) while ((bad_char = BLI_utf8_invalid_byte(str, length)) != -1) { str += bad_char; - length -= bad_char; + length -= (bad_char + 1); if (length == 0) { /* last character bad, strip it */ @@ -151,7 +182,7 @@ int BLI_utf8_invalid_strip(char *str, int length) } else { /* strip, keep looking */ - memmove(str, str + 1, (size_t)length); + memmove(str, str + 1, (size_t)length + 1); /* +1 for NULL char! */ tot++; } } @@ -162,20 +193,6 @@ int BLI_utf8_invalid_strip(char *str, int length) /* compatible with BLI_strncpy, but esnure no partial utf8 chars */ -/* array copied from glib's gutf8.c, - * note: this looks to be at odd's with 'trailingBytesForUTF8', - * need to find out what gives here! - campbell */ -static const size_t utf8_skip_data[256] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1 -}; - #define BLI_STR_UTF8_CPY(dst, src, maxncpy) \ { \ size_t utf8_size; \ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index a154d283f07..a63b9ed7d19 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -531,6 +531,8 @@ void blo_split_main(ListBase *mainlist, Main *main) for (Library *lib = main->library.first; lib; lib = lib->id.next, i++) { Main *libmain = BKE_main_new(); libmain->curlib = lib; + libmain->versionfile = lib->versionfile; + libmain->subversionfile = lib->subversionfile; BLI_addtail(mainlist, libmain); lib->temp_index = i; lib_main_array[i] = libmain; @@ -562,6 +564,10 @@ static void read_file_version(FileData *fd, Main *main) break; } } + if (main->curlib) { + main->curlib->versionfile = main->versionfile; + main->curlib->subversionfile = main->subversionfile; + } } #ifdef USE_GHASH_BHEAD @@ -8373,14 +8379,12 @@ static void do_versions(FileData *fd, Library *lib, Main *main) /* don't forget to set version number in BKE_blender_version.h! */ } -#if 0 // XXX: disabled for now... we still don't have this in the right place in the loading code for it to work -static void do_versions_after_linking(FileData *fd, Library *lib, Main *main) +static void do_versions_after_linking(Main *main) { - /* old Animation System (using IPO's) needs to be converted to the new Animato system */ - if (main->versionfile < 250) - do_versions_ipos_to_animato(main); + UNUSED_VARS(main); +// printf("%s for %s (%s), %d.%d\n", __func__, main->curlib ? main->curlib->name : main->name, +// main->curlib ? "LIB" : "MAIN", main->versionfile, main->subversionfile); } -#endif static void lib_link_all(FileData *fd, Main *main) { @@ -8582,7 +8586,17 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) lib_link_all(fd, bfd->main); - //do_versions_after_linking(fd, NULL, bfd->main); // XXX: not here (or even in this function at all)! this causes crashes on many files - Aligorith (July 04, 2010) + /* Skip in undo case. */ + if (fd->memfile == NULL) { + /* Yep, second splitting... but this is a very cheap operation, so no big deal. */ + blo_split_main(&mainlist, bfd->main); + for (Main *mainvar = mainlist.first; mainvar; mainvar = mainvar->next) { + BLI_assert(mainvar->versionfile != 0); + do_versions_after_linking(mainvar); + } + blo_join_main(&mainlist); + } + BKE_main_id_tag_all(bfd->main, LIB_TAG_NEW, false); lib_verify_nodetree(bfd->main, true); @@ -9794,9 +9808,15 @@ static void give_base_to_objects(Main *mainvar, Scene *scene, View3D *v3d, Libra if (active_lay) { ob->lay = active_lay; } + if (flag & FILE_AUTOSELECT) { + /* Note that link_object_postprocess() already checks for FILE_AUTOSELECT flag, + * but it will miss objects from non-instanciated groups... */ + ob->flag |= SELECT; + /* do NOT make base active here! screws up GUI stuff, if you want it do it on src/ level */ + } - base->lay = ob->lay; base->object = ob; + base->lay = ob->lay; base->flag = ob->flag; CLAMP_MIN(ob->id.us, 0); @@ -10116,6 +10136,32 @@ Main *BLO_library_link_begin(Main *mainvar, BlendHandle **bh, const char *filepa return library_link_begin(mainvar, &fd, filepath); } +static void split_main_newid(Main *mainptr, Main *main_newid) +{ + /* We only copy the necessary subset of data in this temp main. */ + main_newid->versionfile = mainptr->versionfile; + main_newid->subversionfile = mainptr->subversionfile; + BLI_strncpy(main_newid->name, mainptr->name, sizeof(main_newid->name)); + main_newid->curlib = mainptr->curlib; + + ListBase *lbarray[MAX_LIBARRAY]; + ListBase *lbarray_newid[MAX_LIBARRAY]; + int i = set_listbasepointers(mainptr, lbarray); + set_listbasepointers(main_newid, lbarray_newid); + while (i--) { + BLI_listbase_clear(lbarray_newid[i]); + + for (ID *id = lbarray[i]->first, *idnext; id; id = idnext) { + idnext = id->next; + + if (id->tag & LIB_TAG_NEW) { + BLI_remlink(lbarray[i], id); + BLI_addtail(lbarray_newid[i], id); + } + } + } +} + /* scene and v3d may be NULL. */ static void library_link_end(Main *mainl, FileData **fd, const short flag, Scene *scene, View3D *v3d) { @@ -10144,11 +10190,26 @@ static void library_link_end(Main *mainl, FileData **fd, const short flag, Scene blo_join_main((*fd)->mainlist); mainvar = (*fd)->mainlist->first; - MEM_freeN((*fd)->mainlist); mainl = NULL; /* blo_join_main free's mainl, cant use anymore */ lib_link_all(*fd, mainvar); + /* Yep, second splitting... but this is a very cheap operation, so no big deal. */ + blo_split_main((*fd)->mainlist, mainvar); + Main main_newid = {0}; + for (mainvar = ((Main *)(*fd)->mainlist->first)->next; mainvar; mainvar = mainvar->next) { + BLI_assert(mainvar->versionfile != 0); + /* We need to split out IDs already existing, or they will go again through do_versions - bad, very bad! */ + split_main_newid(mainvar, &main_newid); + + do_versions_after_linking(&main_newid); + + add_main_to_main(mainvar, &main_newid); + } + blo_join_main((*fd)->mainlist); + mainvar = (*fd)->mainlist->first; + MEM_freeN((*fd)->mainlist); + BKE_main_id_tag_all(mainvar, LIB_TAG_NEW, false); lib_verify_nodetree(mainvar, false); @@ -10221,32 +10282,6 @@ static int mainvar_id_tag_any_check(Main *mainvar, const short tag) return false; } -static void split_main_newid(Main *mainptr, Main *main_newid) -{ - /* We only copy the necessary subset of data in this temp main. */ - main_newid->versionfile = mainptr->versionfile; - main_newid->subversionfile = mainptr->subversionfile; - BLI_strncpy(main_newid->name, mainptr->name, sizeof(main_newid->name)); - main_newid->curlib = mainptr->curlib; - - ListBase *lbarray[MAX_LIBARRAY]; - ListBase *lbarray_newid[MAX_LIBARRAY]; - int i = set_listbasepointers(mainptr, lbarray); - set_listbasepointers(main_newid, lbarray_newid); - while (i--) { - BLI_listbase_clear(lbarray_newid[i]); - - for (ID *id = lbarray[i]->first, *idnext; id; id = idnext) { - idnext = id->next; - - if (id->tag & LIB_TAG_NEW) { - BLI_remlink(lbarray[i], id); - BLI_addtail(lbarray_newid[i], id); - } - } - } -} - static void read_libraries(FileData *basefd, ListBase *mainlist) { Main *mainl = mainlist->first; diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index f51013c3f1c..20ee31251e8 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -339,7 +339,7 @@ static bool mdisp_in_mdispquad( compute_mdisp_quad(l_dst, l_dst_f_center, v1, v2, v3, v4, e1, e2); /* expand quad a bit */ - cent_quad_v3(c, v1, v2, v3, v4); + mid_v3_v3v3v3v3(c, v1, v2, v3, v4); sub_v3_v3(v1, c); sub_v3_v3(v2, c); sub_v3_v3(v3, c); sub_v3_v3(v4, c); diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 8340be81aa8..05169a2a976 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -57,6 +57,7 @@ #define BEVEL_EPSILON_ANG DEG2RADF(2.0f) #define BEVEL_SMALL_ANG DEG2RADF(10.0f) #define BEVEL_MAX_ADJUST_PCT 10.0f +#define BEVEL_MAX_AUTO_ADJUST_PCT 300.0f /* happens far too often, uncomment for development */ // #define BEVEL_ASSERT_PROJECT @@ -323,15 +324,33 @@ static bool edge_half_offset_changed(EdgeHalf *e) e->offset_r != e->offset_r_spec; } -static bool any_edge_half_offset_changed(BevVert *bv) +static float adjusted_rel_change(float val, float spec) +{ + float relchg; + + relchg = 0.0f; + if (val != spec) { + if (spec == 0) + relchg = 1000.0f; /* arbitrary large value */ + else + relchg = fabsf((val - spec) / spec); + } + return relchg; +} + +static float max_edge_half_offset_rel_change(BevVert *bv) { int i; + float max_rel_change; + EdgeHalf *e; + max_rel_change = 0.0f; for (i = 0; i < bv->edgecount; i++) { - if (edge_half_offset_changed(&bv->edges[i])) - return true; + e = &bv->edges[i]; + max_rel_change = max_ff(max_rel_change, adjusted_rel_change(e->offset_l, e->offset_l_spec)); + max_rel_change = max_ff(max_rel_change, adjusted_rel_change(e->offset_r, e->offset_r_spec)); } - return false; + return max_rel_change; } /* Return the next EdgeHalf after from_e that is beveled. @@ -1951,6 +1970,7 @@ static void adjust_offsets(BevelParams *bp) GHashIterator giter; EdgeHalf *e, *efirst, *eother; GSQueue *q; + float max_rel_adj; BLI_assert(!bp->vertex_only); GHASH_ITER(giter, bp->vert_hash) { @@ -1966,7 +1986,7 @@ static void adjust_offsets(BevelParams *bp) searchi = -1; GHASH_ITER(giter, bp->vert_hash) { bv = BLI_ghashIterator_getValue(&giter); - if (!bv->visited && any_edge_half_offset_changed(bv)) { + if (!bv->visited && max_edge_half_offset_rel_change(bv) > 0.0f) { i = BM_elem_index_get(bv->v); if (!searchbv || i < searchi) { searchbv = bv; @@ -1996,6 +2016,24 @@ static void adjust_offsets(BevelParams *bp) } } BLI_gsqueue_free(q); + + /* Should we auto-limit the error accumulation? Typically, spirals can lead to 100x relative adjustments, + * and somewhat hacky mechanism of using bp->limit_offset to indicate "clamp the adjustments" is not + * obvious to users, who almost certainaly want clamping in this situation. + * The reason not to clamp always is that some models work better without it (e.g., Bent_test in regression + * suite, where relative adjust maximum is about .6). */ + if (!bp->limit_offset) { + max_rel_adj = 0.0f; + GHASH_ITER(giter, bp->vert_hash) { + bv = BLI_ghashIterator_getValue(&giter); + max_rel_adj = max_ff(max_rel_adj, max_edge_half_offset_rel_change(bv)); + } + if (max_rel_adj > BEVEL_MAX_AUTO_ADJUST_PCT / 100.0f) { + bp->limit_offset = true; + adjust_offsets(bp); + bp->limit_offset = false; + } + } } /* Do the edges at bv form a "pipe"? diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index 437dd2b2de2..cc77a321a89 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -309,6 +309,28 @@ static void animchan_sync_fcurve(bAnimContext *ac, bAnimListElem *ale, FCurve ** } } +/* perform syncing updates for GPencil Layers */ +static void animchan_sync_gplayer(bAnimContext *UNUSED(ac), bAnimListElem *ale) +{ + bGPDlayer *gpl = (bGPDlayer *)ale->data; + + /* Make sure the selection flags agree with the "active" flag. + * The selection flags are used in the Dopesheet only, whereas + * the active flag is used everywhere else. Hence, we try to + * sync these here so that it all seems to be have as the user + * expects - T50184 + * + * Assume that we only really do this when the active status changes. + * (NOTE: This may prove annoying if it means selection is always lost) + */ + if (gpl->flag & GP_LAYER_ACTIVE) { + gpl->flag |= GP_LAYER_SELECT; + } + else { + gpl->flag &= ~GP_LAYER_SELECT; + } +} + /* ---------------- */ /* Main call to be exported to animation editors */ @@ -343,6 +365,10 @@ void ANIM_sync_animchannels_to_data(const bContext *C) case ANIMTYPE_FCURVE: animchan_sync_fcurve(&ac, ale, &active_fcurve); break; + + case ANIMTYPE_GPLAYER: + animchan_sync_gplayer(&ac, ale); + break; } } diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 9da3c3ee088..2fd574d6523 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -1419,8 +1419,16 @@ static void gp_draw_data_layers( #undef GP_DRAWFLAG_APPLY - /* draw 'onionskins' (frame left + right) */ - if ((gpl->flag & GP_LAYER_ONIONSKIN) && !(dflag & GP_DRAWDATA_NO_ONIONS)) { + /* Draw 'onionskins' (frame left + right) + * - It is only possible to show these if the option is enabled + * - The "no onions" flag prevents ghosts from appearing during animation playback/scrubbing + * and in renders + * - The per-layer "always show" flag however overrides the playback/render restriction, + * allowing artists to selectively turn onionskins on/off during playback + */ + if ((gpl->flag & GP_LAYER_ONIONSKIN) && + ((dflag & GP_DRAWDATA_NO_ONIONS) == 0 || (gpl->flag & GP_LAYER_GHOST_ALWAYS))) + { /* Drawing method - only immediately surrounding (gstep = 0), * or within a frame range on either side (gstep > 0) */ diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index a4afa958450..60c4b3593aa 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -60,6 +60,7 @@ void ED_undo_redo(struct bContext *C); void ED_OT_undo(struct wmOperatorType *ot); void ED_OT_undo_push(struct wmOperatorType *ot); void ED_OT_redo(struct wmOperatorType *ot); +void ED_OT_undo_redo(struct wmOperatorType *ot); void ED_OT_undo_history(struct wmOperatorType *ot); int ED_undo_operator_repeat(struct bContext *C, struct wmOperator *op); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 80d091c0fdb..b02a909d009 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -125,6 +125,11 @@ typedef struct uiItem { int flag; } uiItem; +enum { + UI_ITEM_FIXED = 1 << 0, + UI_ITEM_MIN = 1 << 1, +}; + typedef struct uiButtonItem { uiItem item; uiBut *but; @@ -232,6 +237,7 @@ static int ui_text_icon_width(uiLayout *layout, const char *name, int icon, bool variable = (ui_layout_vary_direction(layout) == UI_ITEM_VARY_X); if (variable) { + layout->item.flag |= UI_ITEM_MIN; const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; /* it may seem odd that the icon only adds (UI_UNIT_X / 4) * but taking margins into account its fine */ @@ -1463,8 +1469,9 @@ void uiItemEnumR_string(uiLayout *layout, struct PointerRNA *ptr, const char *pr for (a = 0; item[a].identifier; a++) { if (item[a].value == ivalue) { const char *item_name = name ? name : CTX_IFACE_(RNA_property_translation_context(prop), item[a].name); + const int flag = item_name[0] ? 0 : UI_ITEM_R_ICON_ONLY; - uiItemFullR(layout, ptr, prop, RNA_ENUM_VALUE, ivalue, 0, item_name, icon ? icon : item[a].icon); + uiItemFullR(layout, ptr, prop, RNA_ENUM_VALUE, ivalue, flag, item_name, icon ? icon : item[a].icon); break; } } @@ -2059,6 +2066,7 @@ static void ui_litem_estimate_row(uiLayout *litem) { uiItem *item; int itemw, itemh; + bool min_size_flag = true; litem->w = 0; litem->h = 0; @@ -2066,12 +2074,26 @@ static void ui_litem_estimate_row(uiLayout *litem) for (item = litem->items.first; item; item = item->next) { ui_item_size(item, &itemw, &itemh); + if (item->type == ITEM_BUTTON) { + const uiBut *but = ((uiButtonItem *)item)->but; + const bool icon_only = (but->flag & UI_HAS_ICON) && (but->str == NULL || but->str[0] == '\0'); + + min_size_flag = min_size_flag && icon_only; + } + else { + min_size_flag = min_size_flag && (item->flag & UI_ITEM_MIN); + } + litem->w += itemw; litem->h = MAX2(itemh, litem->h); if (item->next) litem->w += litem->space; } + + if (min_size_flag) { + litem->item.flag |= UI_ITEM_MIN; + } } static int ui_litem_min_width(int itemw) @@ -2112,7 +2134,7 @@ static void ui_litem_layout_row(uiLayout *litem) newtotw = totw; for (item = litem->items.first; item; item = item->next) { - if (item->flag) + if (item->flag & UI_ITEM_FIXED) continue; ui_item_size(item, &itemw, &itemh); @@ -2125,16 +2147,19 @@ static void ui_litem_layout_row(uiLayout *litem) x += neww; - if ((neww < minw || itemw == minw) && w != 0) { + if ((neww < minw || itemw == minw || item->flag & UI_ITEM_MIN) && w != 0) { /* fixed size */ - item->flag = 1; + item->flag |= UI_ITEM_FIXED; + if (item->type != ITEM_BUTTON && item->flag & UI_ITEM_MIN) { + minw = itemw; + } fixedw += minw; flag = 1; newtotw -= itemw; } else { /* keep free size */ - item->flag = 0; + item->flag &= ~UI_ITEM_FIXED; freew += itemw; } } @@ -2151,8 +2176,11 @@ static void ui_litem_layout_row(uiLayout *litem) ui_item_size(item, &itemw, &itemh); minw = ui_litem_min_width(itemw); - if (item->flag) { + if (item->flag & UI_ITEM_FIXED) { /* fixed minimum size items */ + if (item->type != ITEM_BUTTON && item->flag & UI_ITEM_MIN) { + minw = itemw; + } itemw = ui_item_fit(minw, fixedx, fixedw, min_ii(w, fixedw), !item->next, litem->alignment); fixedx += itemw; } @@ -2192,6 +2220,7 @@ static void ui_litem_estimate_column(uiLayout *litem) { uiItem *item; int itemw, itemh; + bool min_size_flag = true; litem->w = 0; litem->h = 0; @@ -2199,12 +2228,26 @@ static void ui_litem_estimate_column(uiLayout *litem) for (item = litem->items.first; item; item = item->next) { ui_item_size(item, &itemw, &itemh); + if (item->type == ITEM_BUTTON) { + const uiBut *but = ((uiButtonItem *)item)->but; + const bool icon_only = (but->flag & UI_HAS_ICON) && (but->str == NULL || but->str[0] == '\0'); + + min_size_flag = min_size_flag && icon_only; + } + else { + min_size_flag = min_size_flag && (item->flag & UI_ITEM_MIN); + } + litem->w = MAX2(litem->w, itemw); litem->h += itemh; if (item->next) litem->h += litem->space; } + + if (min_size_flag) { + litem->item.flag |= UI_ITEM_MIN; + } } static void ui_litem_layout_column(uiLayout *litem) diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index 8dfbbdd02eb..df6f098ee81 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -119,12 +119,10 @@ uiBut *uiDefAutoButR(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int ind else but = uiDefButR_prop(block, UI_BTYPE_TEXT, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL); - PropertySubType subtype = RNA_property_subtype(prop); - if (!(ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH, PROP_FILENAME) || (block->flag & UI_BLOCK_LIST_ITEM))) { - UI_but_flag_enable(but, UI_BUT_VALUE_CLEAR); - } if (RNA_property_flag(prop) & PROP_TEXTEDIT_UPDATE) { - UI_but_flag_enable(but, UI_BUT_TEXTEDIT_UPDATE); + /* TEXTEDIT_UPDATE is usally used for search buttons. For these we also want + * the 'x' icon to clear search string, so setting VALUE_CLEAR flag, too. */ + UI_but_flag_enable(but, UI_BUT_TEXTEDIT_UPDATE | UI_BUT_VALUE_CLEAR); } break; case PROP_POINTER: diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index f42dafd094c..02b2d8492b4 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1111,7 +1111,9 @@ static void object_delete_check_glsl_update(Object *ob) /* note: now unlinks constraints as well */ void ED_base_object_free_and_unlink(Main *bmain, Scene *scene, Base *base) { - if (BKE_library_ID_is_indirectly_used(bmain, base->object) && ID_REAL_USERS(base->object) <= 1) { + if (BKE_library_ID_is_indirectly_used(bmain, base->object) && + ID_REAL_USERS(base->object) <= 1 && ID_EXTRA_USERS(base->object) == 0) + { /* We cannot delete indirectly used object... */ printf("WARNING, undeletable object '%s', should have been catched before reaching this function!", base->object->id.name + 2); @@ -1145,7 +1147,7 @@ static int object_delete_exec(bContext *C, wmOperator *op) BKE_reportf(op->reports, RPT_WARNING, "Cannot delete indirectly linked object '%s'", base->object->id.name + 2); continue; } - else if (is_indirectly_used && ID_REAL_USERS(base->object) <= 1) { + else if (is_indirectly_used && ID_REAL_USERS(base->object) <= 1 && ID_EXTRA_USERS(base->object) == 0) { BKE_reportf(op->reports, RPT_WARNING, "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user", base->object->id.name + 2, scene->id.name + 2); @@ -1179,7 +1181,7 @@ static int object_delete_exec(bContext *C, wmOperator *op) if (scene_iter != scene && !ID_IS_LINKED_DATABLOCK(scene_iter)) { base_other = BKE_scene_base_find(scene_iter, base->object); if (base_other) { - if (is_indirectly_used && ID_REAL_USERS(base->object) <= 1) { + if (is_indirectly_used && ID_REAL_USERS(base->object) <= 1 && ID_EXTRA_USERS(base->object) == 0) { BKE_reportf(op->reports, RPT_WARNING, "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user", base->object->id.name + 2, scene_iter->id.name + 2); diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index b44ddf925a8..06f495fb9f1 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -98,12 +98,12 @@ ModifierData *ED_object_modifier_add(ReportList *reports, Main *bmain, Scene *sc ModifierData *md = NULL, *new_md = NULL; const ModifierTypeInfo *mti = modifierType_getInfo(type); - /* only geometry objects should be able to get modifiers [#25291] */ - if (!ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { + /* Check compatibility of modifier [T25291, T50373]. */ + if (!BKE_object_support_modifier_type_check(ob, type)) { BKE_reportf(reports, RPT_WARNING, "Modifiers cannot be added to object '%s'", ob->id.name + 2); return NULL; } - + if (mti->flags & eModifierTypeFlag_Single) { if (modifiers_findByType(ob, type)) { BKE_report(reports, RPT_WARNING, "Only one modifier of this type is allowed"); diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index c69e01422e0..a7a0a240259 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -4329,6 +4329,7 @@ void ED_operatortypes_screen(void) WM_operatortype_append(ED_OT_undo); WM_operatortype_append(ED_OT_undo_push); WM_operatortype_append(ED_OT_redo); + WM_operatortype_append(ED_OT_undo_redo); WM_operatortype_append(ED_OT_undo_history); WM_operatortype_append(ED_OT_flush_edits); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 47f0220726b..84e98181dfb 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -5693,11 +5693,11 @@ static int sculpt_set_detail_size_exec(bContext *C, wmOperator *UNUSED(op)) WM_operator_properties_create_ptr(&props_ptr, ot); if (sd->flags & SCULPT_DYNTOPO_DETAIL_CONSTANT) { - set_brush_rc_props(&props_ptr, "sculpt", "constant_detail", NULL, 0); - RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.constant_detail"); + set_brush_rc_props(&props_ptr, "sculpt", "constant_detail_resolution", NULL, 0); + RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.constant_detail_resolution"); } else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { - set_brush_rc_props(&props_ptr, "sculpt", "constant_detail", NULL, 0); + set_brush_rc_props(&props_ptr, "sculpt", "constant_detail_resolution", NULL, 0); RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_percent"); } else { diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index 432918f0e37..c2c52f58181 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -383,6 +383,8 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op) result = AUD_mixdown(scene->sound_scene, SFRA * specs.rate / FPS, (EFRA - SFRA + 1) * specs.rate / FPS, accuracy, filename, specs, container, codec, bitrate); + BKE_sound_reset_scene_specs(scene); + if (result) { BKE_report(op->reports, RPT_ERROR, result); return OPERATOR_CANCELLED; diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 671d6bb083e..83655a2ba9e 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -337,7 +337,7 @@ static void action_channel_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED( } break; case NC_GPENCIL: - if (wmn->action == NA_RENAME) + if (ELEM(wmn->action, NA_RENAME, NA_SELECTED)) ED_region_tag_redraw(ar); break; case NC_ID: @@ -407,10 +407,15 @@ static void action_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn) /* context changes */ switch (wmn->category) { case NC_GPENCIL: - if (wmn->action == NA_EDITED) { - /* only handle this event in GPencil mode for performance considerations */ - if (saction->mode == SACTCONT_GPENCIL) + /* only handle these events in GPencil mode for performance considerations */ + if (saction->mode == SACTCONT_GPENCIL) { + if (wmn->action == NA_EDITED) { ED_area_tag_redraw(sa); + } + else if (wmn->action == NA_SELECTED) { + saction->flag |= SACTION_TEMP_NEEDCHANSYNC; + ED_area_tag_refresh(sa); + } } break; case NC_ANIMATION: diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 116f621dd76..2de3238de6c 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1475,7 +1475,10 @@ static int image_match_len_exec(bContext *C, wmOperator *UNUSED(op)) if (!ima || !iuser || !BKE_image_has_anim(ima)) return OPERATOR_CANCELLED; - iuser->frames = IMB_anim_get_duration(((ImageAnim *) ima->anims.first)->anim, IMB_TC_RECORD_RUN); + struct anim *anim = ((ImageAnim *)ima->anims.first)->anim; + if (!anim) + return OPERATOR_CANCELLED; + iuser->frames = IMB_anim_get_duration(anim, IMB_TC_RECORD_RUN); BKE_image_user_frame_calc(iuser, scene->r.cfra, 0); return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 7739d241bd3..4eda7977622 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -411,7 +411,9 @@ static void object_delete_cb( BKE_reportf(reports, RPT_WARNING, "Cannot delete indirectly linked object '%s'", base->object->id.name + 2); return; } - else if (BKE_library_ID_is_indirectly_used(bmain, base->object) && ID_REAL_USERS(base->object) <= 1) { + else if (BKE_library_ID_is_indirectly_used(bmain, base->object) && + ID_REAL_USERS(base->object) <= 1 && ID_EXTRA_USERS(base->object) == 0) + { BKE_reportf(reports, RPT_WARNING, "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user", base->object->id.name + 2, scene->id.name + 2); @@ -525,7 +527,7 @@ static void group_linkobs2scene_cb( if (!base) { /* link to scene */ base = BKE_scene_base_add(scene, gob->ob); - id_lib_extern((ID *)gob->ob); /* in case these are from a linked group */ + id_us_plus(&gob->ob->id); } base->object->flag |= SELECT; base->flag |= SELECT; @@ -842,7 +844,9 @@ static Base *outline_delete_hierarchy(bContext *C, ReportList *reports, Scene *s BKE_reportf(reports, RPT_WARNING, "Cannot delete indirectly linked object '%s'", base->object->id.name + 2); return base_next; } - else if (BKE_library_ID_is_indirectly_used(bmain, base->object) && ID_REAL_USERS(base->object) <= 1) { + else if (BKE_library_ID_is_indirectly_used(bmain, base->object) && + ID_REAL_USERS(base->object) <= 1 && ID_EXTRA_USERS(base->object) == 0) + { BKE_reportf(reports, RPT_WARNING, "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user", base->object->id.name + 2, scene->id.name + 2); diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index 83012eac39e..df3620843ad 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -254,6 +254,7 @@ static int text_open_exec(bContext *C, wmOperator *op) } else if (st) { st->text = text; + id_us_ensure_real(&text->id); st->left = 0; st->top = 0; st->scroll_accum[0] = 0.0f; diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index b9c8c98b62f..351c7ccec15 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -476,7 +476,7 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN, totedgedata == 1 ? IFACE_("Crease:") : IFACE_("Mean Crease:"), 0, yi -= buth + but_margin, 200, buth, - &(tfp->ve_median[M_CREASE]), 0.0, 1.0, 1, 2, TIP_("Weight used by SubSurf modifier")); + &(tfp->ve_median[M_CREASE]), 0.0, 1.0, 1, 2, TIP_("Weight used by the Subdivision Surface modifier")); } } /* Curve... */ @@ -491,7 +491,7 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float else if (totcurvedata > 1) { uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN, IFACE_("Mean Weight:"), 0, yi -= buth + but_margin, 200, buth, - &(tfp->ve_median[C_WEIGHT]), 0.0, 1.0, 1, 3, TIP_("Weight used for SoftBody Goal")); + &(tfp->ve_median[C_WEIGHT]), 0.0, 1.0, 1, 3, TIP_("Weight used for Soft Body Goal")); uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN, IFACE_("Mean Radius:"), 0, yi -= buth + but_margin, 200, buth, &(tfp->ve_median[C_RADIUS]), 0.0, 100.0, 1, 3, TIP_("Radius of curve control points")); @@ -509,7 +509,7 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float else if (totlattdata > 1) { uiDefButF(block, UI_BTYPE_NUM, B_OBJECTPANELMEDIAN, IFACE_("Mean Weight:"), 0, yi -= buth + but_margin, 200, buth, - &(tfp->ve_median[L_WEIGHT]), 0.0, 1.0, 1, 3, TIP_("Weight used for SoftBody Goal")); + &(tfp->ve_median[L_WEIGHT]), 0.0, 1.0, 1, 3, TIP_("Weight used for Soft Body Goal")); } UI_block_align_end(block); diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 620bbf03f32..2b53eb71d99 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -4064,6 +4064,9 @@ static int vieworbit_exec(bContext *C, wmOperator *op) mul_qt_qtqt(quat_new, rv3d->viewquat, quat_mul); + /* avoid precision loss over time */ + normalize_qt(quat_new); + if (view_opposite != RV3D_VIEW_USER) { rv3d->view = view_opposite; /* avoid float in-precision, just get a new orientation */ @@ -4130,6 +4133,10 @@ static void view_roll_angle(ARegion *ar, float quat[4], const float orig_quat[4] axis_angle_normalized_to_quat(quat_mul, dvec, angle); mul_qt_qtqt(quat, orig_quat, quat_mul); + + /* avoid precision loss over time */ + normalize_qt(quat); + rv3d->view = RV3D_VIEW_USER; } diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c index 0713377d210..bf1bdf68619 100644 --- a/source/blender/editors/space_view3d/view3d_header.c +++ b/source/blender/editors/space_view3d/view3d_header.c @@ -337,13 +337,13 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C) /* Draw type */ uiItemR(layout, &v3dptr, "viewport_shade", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); - if (obedit == NULL && is_paint) { - if (ob->mode & OB_MODE_ALL_PAINT) { - /* Only for Weight Paint. makes no sense in other paint modes. */ - row = uiLayoutRow(layout, true); - uiItemR(row, &v3dptr, "pivot_point", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); - } + row = uiLayoutRow(layout, true); + uiItemR(row, &v3dptr, "pivot_point", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); + if (!ob || ELEM(ob->mode, OB_MODE_OBJECT, OB_MODE_POSE, OB_MODE_WEIGHT_PAINT)) { + uiItemR(row, &v3dptr, "use_pivot_point_align", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); + } + if (obedit == NULL && is_paint) { /* Manipulators aren't used in paint modes */ if (!ELEM(ob->mode, OB_MODE_SCULPT, OB_MODE_PARTICLE_EDIT)) { /* masks aren't used for sculpt and particle painting */ @@ -361,17 +361,6 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C) } } else { - row = uiLayoutRow(layout, true); - uiItemR(row, &v3dptr, "pivot_point", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); - - /* pose/object only however we want to allow in weight paint mode too - * so don't be totally strict and just check not-editmode for now - * XXX We never get here when we are in Weight Paint mode - */ - if (obedit == NULL) { - uiItemR(row, &v3dptr, "use_pivot_point_align", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); - } - /* Transform widget / manipulators */ row = uiLayoutRow(layout, true); uiItemR(row, &v3dptr, "show_manipulator", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c index 4a9311416b3..fab5b7e821f 100644 --- a/source/blender/editors/util/undo.c +++ b/source/blender/editors/util/undo.c @@ -327,6 +327,13 @@ static int ed_redo_exec(bContext *C, wmOperator *UNUSED(op)) return ed_undo_step(C, -1, NULL); } +static int ed_undo_redo_exec(bContext *C, wmOperator *UNUSED(op)) +{ + wmOperator *last_op = WM_operator_last_redo(C); + const int ret = ED_undo_operator_repeat(C, last_op); + return ret ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +} + /* ********************** */ @@ -369,6 +376,17 @@ void ED_OT_redo(wmOperatorType *ot) ot->poll = ED_operator_screenactive; } +void ED_OT_undo_redo(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Undo and Redo"; + ot->description = "Undo and redo previous action"; + ot->idname = "ED_OT_undo_redo"; + + /* api callbacks */ + ot->exec = ed_undo_redo_exec; + ot->poll = ED_operator_screenactive; +} /* ui callbacks should call this rather than calling WM_operator_repeat() themselves */ int ED_undo_operator_repeat(bContext *C, struct wmOperator *op) diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 8e4ba4c0afa..d8080002818 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -1222,7 +1222,7 @@ static int unwrap_exec(bContext *C, wmOperator *op) * pass operator for warning append */ modifier_unwrap_state(obedit, scene, &use_subsurf_final); if (use_subsurf != use_subsurf_final) - BKE_report(op->reports, RPT_INFO, "Subsurf modifier needs to be first to work with unwrap"); + BKE_report(op->reports, RPT_INFO, "Subdivision Surface modifier needs to be first to work with unwrap"); /* execute unwrap */ ED_unwrap_lscm(scene, obedit, true); @@ -1259,7 +1259,7 @@ void UV_OT_unwrap(wmOperatorType *ot) RNA_def_boolean(ot->srna, "correct_aspect", 1, "Correct Aspect", "Map UVs taking image aspect ratio into account"); RNA_def_boolean(ot->srna, "use_subsurf_data", 0, "Use Subsurf Modifier", - "Map UVs taking vertex position after subsurf into account"); + "Map UVs taking vertex position after Subdivision Surface modifier has been applied"); RNA_def_float_factor(ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f); } diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp index 2af4447e4dc..223bc607e21 100644 --- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp +++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp @@ -433,8 +433,8 @@ static void prepare(Render *re, SceneRenderLayer *srl) cout << "Crease angle : " << controller->getCreaseAngle() << endl; cout << "Sphere radius : " << controller->getSphereRadius() << endl; cout << "Face smoothness : " << (controller->getFaceSmoothness() ? "enabled" : "disabled") << endl; - cout << "Redges and valleys : " << (controller->getComputeRidgesAndValleysFlag() ? "enabled" : "disabled") << - endl; + cout << "Ridges and valleys : " << + (controller->getComputeRidgesAndValleysFlag() ? "enabled" : "disabled") << endl; cout << "Suggestive contours : " << (controller->getComputeSuggestiveContoursFlag() ? "enabled" : "disabled") << endl; cout << "Suggestive contour Kr derivative epsilon : " << diff --git a/source/blender/freestyle/intern/geometry/GeomUtils.cpp b/source/blender/freestyle/intern/geometry/GeomUtils.cpp index 3eb92c559fe..cd7c1b83a4e 100644 --- a/source/blender/freestyle/intern/geometry/GeomUtils.cpp +++ b/source/blender/freestyle/intern/geometry/GeomUtils.cpp @@ -470,6 +470,8 @@ bool intersectRayTriangle(const Vec3r& orig, const Vec3r& dir, const Vec3r& v0, } // Intersection between plane and ray, adapted from Graphics Gems, Didier Badouel +// The plane is represented by a set of points P implicitly defined as dot(norm, P) + d = 0. +// The ray is represented as r(t) = orig + dir * t. intersection_test intersectRayPlane(const Vec3r& orig, const Vec3r& dir, const Vec3r& norm, const real d, real& t, const real epsilon) { diff --git a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp index 380bb0dd3ca..794c782bb73 100644 --- a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp +++ b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp @@ -57,7 +57,7 @@ using namespace std; template <typename G, typename I> static void findOccludee(FEdge *fe, G& /*grid*/, I& occluders, real epsilon, WFace **oaWFace, - Vec3r& u, Vec3r& A, Vec3r& origin, Vec3r& edge, vector<WVertex*>& faceVertices) + Vec3r& u, Vec3r& A, Vec3r& origin, Vec3r& edgeDir, vector<WVertex*>& faceVertices) { WFace *face = NULL; if (fe->isSmooth()) { @@ -125,7 +125,7 @@ static void findOccludee(FEdge *fe, G& /*grid*/, I& occluders, real epsilon, WFa // check whether the edge and the polygon plane are coincident: //------------------------------------------------------------- //first let us compute the plane equation. - if (GeomUtils::COINCIDENT == GeomUtils::intersectRayPlane(origin, edge, p->getNormal(), d, t, epsilon)) + if (GeomUtils::COINCIDENT == GeomUtils::intersectRayPlane(origin, edgeDir, p->getNormal(), d, t, epsilon)) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { @@ -172,10 +172,11 @@ template <typename G, typename I> static void findOccludee(FEdge *fe, G& grid, real epsilon, ViewEdge * /*ve*/, WFace **oaFace) { Vec3r A; - Vec3r edge; + Vec3r edgeDir; Vec3r origin; A = Vec3r(((fe)->vertexA()->point3D() + (fe)->vertexB()->point3D()) / 2.0); - edge = Vec3r((fe)->vertexB()->point3D() - (fe)->vertexA()->point3D()); + edgeDir = Vec3r((fe)->vertexB()->point3D() - (fe)->vertexA()->point3D()); + edgeDir.normalize(); origin = Vec3r((fe)->vertexA()->point3D()); Vec3r u; if (grid.orthographicProjection()) { @@ -199,7 +200,7 @@ static void findOccludee(FEdge *fe, G& grid, real epsilon, ViewEdge * /*ve*/, WF } I occluders(grid, A, epsilon); - findOccludee<G, I>(fe, grid, occluders, epsilon, oaFace, u, A, origin, edge, faceVertices); + findOccludee<G, I>(fe, grid, occluders, epsilon, oaFace, u, A, origin, edgeDir, faceVertices); } // computeVisibility takes a pointer to foundOccluders, instead of using a reference, @@ -211,11 +212,12 @@ static int computeVisibility(ViewMap *viewMap, FEdge *fe, G& grid, real epsilon, int qi = 0; Vec3r center; - Vec3r edge; + Vec3r edgeDir; Vec3r origin; center = fe->center3d(); - edge = Vec3r(fe->vertexB()->point3D() - fe->vertexA()->point3D()); + edgeDir = Vec3r(fe->vertexB()->point3D() - fe->vertexA()->point3D()); + edgeDir.normalize(); origin = Vec3r(fe->vertexA()->point3D()); Vec3r vp; @@ -337,7 +339,7 @@ static int computeVisibility(ViewMap *viewMap, FEdge *fe, G& grid, real epsilon, // check whether the edge and the polygon plane are coincident: //------------------------------------------------------------- //first let us compute the plane equation. - if (GeomUtils::COINCIDENT == GeomUtils::intersectRayPlane(origin, edge, p->getNormal(), d, t, epsilon)) { + if (GeomUtils::COINCIDENT == GeomUtils::intersectRayPlane(origin, edgeDir, p->getNormal(), d, t, epsilon)) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tRejecting occluder for target coincidence." << endl; @@ -391,7 +393,7 @@ static int computeVisibility(ViewMap *viewMap, FEdge *fe, G& grid, real epsilon, } // Find occludee - findOccludee<G, I>(fe, grid, occluders, epsilon, oaWFace, u, center, origin, edge, faceVertices); + findOccludee<G, I>(fe, grid, occluders, epsilon, oaWFace, u, center, origin, edgeDir, faceVertices); return qi; } @@ -1788,7 +1790,7 @@ void ViewMapBuilder::ComputeVeryFastRayCastingVisibility(ViewMap *ioViewMap, rea } void ViewMapBuilder::FindOccludee(FEdge *fe, Grid *iGrid, real epsilon, Polygon3r **oaPolygon, unsigned timestamp, - Vec3r& u, Vec3r& A, Vec3r& origin, Vec3r& edge, vector<WVertex*>& faceVertices) + Vec3r& u, Vec3r& A, Vec3r& origin, Vec3r& edgeDir, vector<WVertex*>& faceVertices) { WFace *face = NULL; if (fe->isSmooth()) { @@ -1856,7 +1858,7 @@ void ViewMapBuilder::FindOccludee(FEdge *fe, Grid *iGrid, real epsilon, Polygon3 continue; } else { - if (GeomUtils::COINCIDENT == GeomUtils::intersectRayPlane(origin, edge, normal, d, t, epsilon)) + if (GeomUtils::COINCIDENT == GeomUtils::intersectRayPlane(origin, edgeDir, normal, d, t, epsilon)) continue; } if ((*p)->rayIntersect(A, v, t, t_u, t_v)) { @@ -1883,10 +1885,11 @@ void ViewMapBuilder::FindOccludee(FEdge *fe, Grid *iGrid, real epsilon, Polygon3 OccludersSet occluders; Vec3r A; - Vec3r edge; + Vec3r edgeDir; Vec3r origin; A = Vec3r(((fe)->vertexA()->point3D() + (fe)->vertexB()->point3D()) / 2.0); - edge = Vec3r((fe)->vertexB()->point3D() - (fe)->vertexA()->point3D()); + edgeDir = Vec3r((fe)->vertexB()->point3D() - (fe)->vertexA()->point3D()); + edgeDir.normalize(); origin = Vec3r((fe)->vertexA()->point3D()); Vec3r u; if (_orthographicProjection) { @@ -1910,7 +1913,7 @@ void ViewMapBuilder::FindOccludee(FEdge *fe, Grid *iGrid, real epsilon, Polygon3 if (face) face->RetrieveVertexList(faceVertices); - return FindOccludee(fe, iGrid, epsilon, oaPolygon, timestamp, u, A, origin, edge, faceVertices); + return FindOccludee(fe, iGrid, epsilon, oaPolygon, timestamp, u, A, origin, edgeDir, faceVertices); } int ViewMapBuilder::ComputeRayCastingVisibility(FEdge *fe, Grid *iGrid, real epsilon, set<ViewShape*>& oOccluders, @@ -1920,11 +1923,12 @@ int ViewMapBuilder::ComputeRayCastingVisibility(FEdge *fe, Grid *iGrid, real eps int qi = 0; Vec3r center; - Vec3r edge; + Vec3r edgeDir; Vec3r origin; center = fe->center3d(); - edge = Vec3r(fe->vertexB()->point3D() - fe->vertexA()->point3D()); + edgeDir = Vec3r(fe->vertexB()->point3D() - fe->vertexA()->point3D()); + edgeDir.normalize(); origin = Vec3r(fe->vertexA()->point3D()); // Is the edge outside the view frustum ? Vec3r gridOrigin(iGrid->getOrigin()); @@ -2062,7 +2066,7 @@ int ViewMapBuilder::ComputeRayCastingVisibility(FEdge *fe, Grid *iGrid, real eps //------------------------------------------------------------- //first let us compute the plane equation. - if (GeomUtils::COINCIDENT == GeomUtils::intersectRayPlane(origin, edge, normal, d, t, epsilon)) { + if (GeomUtils::COINCIDENT == GeomUtils::intersectRayPlane(origin, edgeDir, normal, d, t, epsilon)) { #if LOGGING if (_global.debug & G_DEBUG_FREESTYLE) { cout << "\t\tRejecting occluder for target coincidence." << endl; @@ -2099,7 +2103,7 @@ int ViewMapBuilder::ComputeRayCastingVisibility(FEdge *fe, Grid *iGrid, real eps } // Find occludee - FindOccludee(fe, iGrid, epsilon, oaPolygon, timestamp, u, center, origin, edge, faceVertices); + FindOccludee(fe, iGrid, epsilon, oaPolygon, timestamp, u, center, origin, edgeDir, faceVertices); return qi; } diff --git a/source/blender/freestyle/intern/view_map/ViewMapBuilder.h b/source/blender/freestyle/intern/view_map/ViewMapBuilder.h index 36497bf8d22..440ae93c7df 100644 --- a/source/blender/freestyle/intern/view_map/ViewMapBuilder.h +++ b/source/blender/freestyle/intern/view_map/ViewMapBuilder.h @@ -250,7 +250,7 @@ protected: // FIXME void FindOccludee(FEdge *fe, Grid *iGrid, real epsilon, Polygon3r **oaPolygon, unsigned timestamp); void FindOccludee(FEdge *fe, Grid *iGrid, real epsilon, Polygon3r **oaPolygon, unsigned timestamp, - Vec3r& u, Vec3r& A, Vec3r& origin, Vec3r& edge, vector<WVertex*>& faceVertices); + Vec3r& u, Vec3r& A, Vec3r& origin, Vec3r& edgeDir, vector<WVertex*>& faceVertices); #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:ViewMapBuilder") diff --git a/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl b/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl index 054a2f795ee..50c8e255162 100644 --- a/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl @@ -27,8 +27,7 @@ vec3 calculate_view_space_normal(in vec3 viewposition) { vec3 normal = cross(normalize(dFdx(viewposition)), ssao_params.w * normalize(dFdy(viewposition))); - normalize(normal); - return normal; + return normalize(normal); } float calculate_ssao_factor(float depth) diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 9a8f3da3396..2c6f3d2fc66 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -155,8 +155,9 @@ typedef struct Library { struct PackedFile *packedfile; + /* Temp data needed by read/write code. */ int temp_index; - int _pad; + short versionfile, subversionfile; /* see BLENDER_VERSION, BLENDER_SUBVERSION, needed for do_versions */ } Library; enum eIconSizes { @@ -276,6 +277,7 @@ typedef enum ID_Type { #define ID_FAKE_USERS(id) ((((ID *)id)->flag & LIB_FAKEUSER) ? 1 : 0) #define ID_REAL_USERS(id) (((ID *)id)->us - ID_FAKE_USERS(id)) +#define ID_EXTRA_USERS(id) (((ID *)id)->tag & LIB_TAG_EXTRAUSER ? 1 : 0) #define ID_CHECK_UNDO(id) ((GS((id)->name) != ID_SCR) && (GS((id)->name) != ID_WM)) diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 23b73424da5..0364d855f69 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -244,6 +244,7 @@ typedef struct bGPDlayer { float inverse[4][4]; /* inverse matrix (only used if parented) */ char parsubstr[64]; /* String describing subobject info, MAX_ID_NAME-2 */ short partype, pad; + float tintcolor[4]; /* Color used to tint layer, alpha value is used as factor */ float opacity; /* Opacity of the layer */ } bGPDlayer; @@ -275,7 +276,9 @@ typedef enum eGPDlayer_Flag { /* Use high quality fill (instead of buggy legacy OpenGL Fill) */ GP_LAYER_HQ_FILL = (1 << 11), /* Unlock color */ - GP_LAYER_UNLOCK_COLOR = (1 << 12) + GP_LAYER_UNLOCK_COLOR = (1 << 12), + /* always show onion skins (i.e. even during renders/animation playback) */ + GP_LAYER_GHOST_ALWAYS = (1 << 13), } eGPDlayer_Flag; /* Grease-Pencil Annotations - 'DataBlock' */ diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index 1a191a68668..dee8df7d933 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -174,8 +174,10 @@ typedef enum PropertyFlag { * and collections */ PROP_ANIMATABLE = (1 << 1), - /* This flag means when the property's widget is in 'textedit' mode, it will be updated after every typed char, - * instead of waiting final validation. Used e.g. for text searchbox. */ + /* This flag means when the property's widget is in 'textedit' mode, it will be updated + * after every typed char, instead of waiting final validation. Used e.g. for text searchbox. + * It will also cause UI_BUT_VALUE_CLEAR to be set for text buttons. We could add an own flag + * for search/filter properties, but this works just fine for now. */ PROP_TEXTEDIT_UPDATE = (1 << 31), /* icon */ diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 69ee671901a..14da990278c 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -1242,6 +1242,13 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "After Color", "Base color for ghosts after the active frame"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + prop = RNA_def_property(srna, "use_ghosts_always", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_GHOST_ALWAYS); + RNA_def_property_ui_text(prop, "Always Show Ghosts", + "Ghosts are shown in renders and animation playback. Useful for special effects (e.g. motion blur)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Flags */ prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_HIDE); @@ -1276,13 +1283,13 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_ACTIVE); RNA_def_property_boolean_funcs(prop, NULL, "rna_GPencilLayer_active_set"); RNA_def_property_ui_text(prop, "Active", "Set active layer for editing"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | NA_SELECTED, NULL); #endif prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_SELECT); RNA_def_property_ui_text(prop, "Select", "Layer is selected for editing in the Dope Sheet"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | NA_SELECTED, "rna_GPencil_update"); /* XXX keep this option? */ prop = RNA_def_property(srna, "show_points", PROP_BOOLEAN, PROP_NONE); @@ -1370,14 +1377,15 @@ static void rna_def_gpencil_layers_api(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_pointer_funcs(prop, "rna_GPencil_active_layer_get", "rna_GPencil_active_layer_set", NULL, NULL); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Active Layer", "Active grease pencil layer"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | NA_SELECTED, NULL); prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_funcs(prop, "rna_GPencil_active_layer_index_get", "rna_GPencil_active_layer_index_set", "rna_GPencil_active_layer_index_range"); RNA_def_property_ui_text(prop, "Active Layer Index", "Index of active grease pencil layer"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | NA_SELECTED, NULL); } static void rna_def_gpencil_palettecolor(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index cecdb6bad51..c110dbff6c4 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -116,6 +116,13 @@ #endif +static void rna_idname_validate(const char *name, char *r_name) +{ + BLI_strncpy(r_name, name, MAX_ID_NAME - 2); + BLI_utf8_invalid_strip(r_name, strlen(r_name)); +} + + static void rna_Main_ID_remove(Main *bmain, ReportList *reports, PointerRNA *id_ptr, int do_unlink) { ID *id = id_ptr->data; @@ -137,14 +144,20 @@ static void rna_Main_ID_remove(Main *bmain, ReportList *reports, PointerRNA *id_ static Camera *rna_Main_cameras_new(Main *bmain, const char *name) { - ID *id = BKE_camera_add(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + ID *id = BKE_camera_add(bmain, safe_name); id_us_min(id); return (Camera *)id; } static Scene *rna_Main_scenes_new(Main *bmain, const char *name) { - return BKE_scene_add(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + return BKE_scene_add(bmain, safe_name); } static void rna_Main_scenes_remove(Main *bmain, bContext *C, ReportList *reports, PointerRNA *scene_ptr, int do_unlink) { @@ -180,6 +193,9 @@ static void rna_Main_scenes_remove(Main *bmain, bContext *C, ReportList *reports static Object *rna_Main_objects_new(Main *bmain, ReportList *reports, const char *name, ID *data) { + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + Object *ob; int type = OB_EMPTY; if (data) { @@ -223,7 +239,7 @@ static Object *rna_Main_objects_new(Main *bmain, ReportList *reports, const char id_us_plus(data); } - ob = BKE_object_add_only_object(bmain, type, name); + ob = BKE_object_add_only_object(bmain, type, safe_name); id_us_min(&ob->id); ob->data = data; @@ -234,7 +250,10 @@ static Object *rna_Main_objects_new(Main *bmain, ReportList *reports, const char static Material *rna_Main_materials_new(Main *bmain, const char *name) { - ID *id = (ID *)BKE_material_add(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + ID *id = (ID *)BKE_material_add(bmain, safe_name); id_us_min(id); return (Material *)id; } @@ -245,20 +264,27 @@ static EnumPropertyItem *rna_Main_nodetree_type_itemf(bContext *UNUSED(C), Point } static struct bNodeTree *rna_Main_nodetree_new(Main *bmain, const char *name, int type) { + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + bNodeTreeType *typeinfo = rna_node_tree_type_from_enum(type); if (typeinfo) { - bNodeTree *ntree = ntreeAddTree(bmain, name, typeinfo->idname); + bNodeTree *ntree = ntreeAddTree(bmain, safe_name, typeinfo->idname); id_us_min(&ntree->id); return ntree; } - else + else { return NULL; + } } static Mesh *rna_Main_meshes_new(Main *bmain, const char *name) { - Mesh *me = BKE_mesh_add(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + Mesh *me = BKE_mesh_add(bmain, safe_name); id_us_min(&me->id); return me; } @@ -286,7 +312,10 @@ Mesh *rna_Main_meshes_new_from_object( static Lamp *rna_Main_lamps_new(Main *bmain, const char *name, int type) { - Lamp *lamp = BKE_lamp_add(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + Lamp *lamp = BKE_lamp_add(bmain, safe_name); lamp->type = type; id_us_min(&lamp->id); return lamp; @@ -294,8 +323,11 @@ static Lamp *rna_Main_lamps_new(Main *bmain, const char *name, int type) static Image *rna_Main_images_new(Main *bmain, const char *name, int width, int height, int alpha, int float_buffer, int stereo3d) { + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + float color[4] = {0.0, 0.0, 0.0, 1.0}; - Image *image = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, float_buffer, 0, color, stereo3d); + Image *image = BKE_image_add_generated(bmain, width, height, safe_name, alpha ? 32 : 24, float_buffer, 0, color, stereo3d); id_us_min(&image->id); return image; } @@ -322,21 +354,30 @@ static Image *rna_Main_images_load(Main *bmain, ReportList *reports, const char static Lattice *rna_Main_lattices_new(Main *bmain, const char *name) { - Lattice *lt = BKE_lattice_add(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + Lattice *lt = BKE_lattice_add(bmain, safe_name); id_us_min(<->id); return lt; } static Curve *rna_Main_curves_new(Main *bmain, const char *name, int type) { - Curve *cu = BKE_curve_add(bmain, name, type); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + Curve *cu = BKE_curve_add(bmain, safe_name, type); id_us_min(&cu->id); return cu; } static MetaBall *rna_Main_metaballs_new(Main *bmain, const char *name) { - MetaBall *mb = BKE_mball_add(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + MetaBall *mb = BKE_mball_add(bmain, safe_name); id_us_min(&mb->id); return mb; } @@ -364,7 +405,10 @@ static VFont *rna_Main_fonts_load(Main *bmain, ReportList *reports, const char * static Tex *rna_Main_textures_new(Main *bmain, const char *name, int type) { - Tex *tex = BKE_texture_add(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + Tex *tex = BKE_texture_add(bmain, safe_name); BKE_texture_type_set(tex, type); id_us_min(&tex->id); return tex; @@ -372,26 +416,38 @@ static Tex *rna_Main_textures_new(Main *bmain, const char *name, int type) static Brush *rna_Main_brushes_new(Main *bmain, const char *name, int mode) { - Brush *brush = BKE_brush_add(bmain, name, mode); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + Brush *brush = BKE_brush_add(bmain, safe_name, mode); id_us_min(&brush->id); return brush; } static World *rna_Main_worlds_new(Main *bmain, const char *name) { - World *world = add_world(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + World *world = add_world(bmain, safe_name); id_us_min(&world->id); return world; } static Group *rna_Main_groups_new(Main *bmain, const char *name) { - return BKE_group_add(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + return BKE_group_add(bmain, safe_name); } static Speaker *rna_Main_speakers_new(Main *bmain, const char *name) { - Speaker *speaker = BKE_speaker_add(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + Speaker *speaker = BKE_speaker_add(bmain, safe_name); id_us_min(&speaker->id); return speaker; } @@ -413,7 +469,10 @@ static bSound *rna_Main_sounds_load(Main *bmain, const char *name, int check_exi static Text *rna_Main_texts_new(Main *bmain, const char *name) { - return BKE_text_add(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + return BKE_text_add(bmain, safe_name); } static Text *rna_Main_texts_load(Main *bmain, ReportList *reports, const char *filepath, int is_internal) @@ -432,28 +491,40 @@ static Text *rna_Main_texts_load(Main *bmain, ReportList *reports, const char *f static bArmature *rna_Main_armatures_new(Main *bmain, const char *name) { - bArmature *arm = BKE_armature_add(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + bArmature *arm = BKE_armature_add(bmain, safe_name); id_us_min(&arm->id); return arm; } static bAction *rna_Main_actions_new(Main *bmain, const char *name) { - bAction *act = add_empty_action(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + bAction *act = add_empty_action(bmain, safe_name); id_fake_user_clear(&act->id); return act; } static ParticleSettings *rna_Main_particles_new(Main *bmain, const char *name) { - ParticleSettings *part = psys_new_settings(name, bmain); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + ParticleSettings *part = psys_new_settings(safe_name, bmain); id_us_min(&part->id); return part; } static Palette *rna_Main_palettes_new(Main *bmain, const char *name) { - Palette *palette = BKE_palette_add(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + Palette *palette = BKE_palette_add(bmain, safe_name); id_us_min(&palette->id); return (Palette *)palette; } @@ -481,16 +552,18 @@ static MovieClip *rna_Main_movieclip_load(Main *bmain, ReportList *reports, cons static Mask *rna_Main_mask_new(Main *bmain, const char *name) { - Mask *mask; - - mask = BKE_mask_new(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); - return mask; + return BKE_mask_new(bmain, safe_name); } static FreestyleLineStyle *rna_Main_linestyles_new(Main *bmain, const char *name) { - FreestyleLineStyle *linestyle = BKE_linestyle_new(bmain, name); + char safe_name[MAX_ID_NAME - 2]; + rna_idname_validate(name, safe_name); + + FreestyleLineStyle *linestyle = BKE_linestyle_new(bmain, safe_name); id_us_min(&linestyle->id); return linestyle; } diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 3dc58442851..ad5f320625c 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -1962,7 +1962,7 @@ static void rna_def_medge(BlenderRNA *brna) prop = RNA_def_property(srna, "crease", PROP_FLOAT, PROP_NONE); RNA_def_property_float_funcs(prop, "rna_MEdge_crease_get", "rna_MEdge_crease_set", NULL); - RNA_def_property_ui_text(prop, "Crease", "Weight used by the Subsurf modifier for creasing"); + RNA_def_property_ui_text(prop, "Crease", "Weight used by the Subdivision Surface modifier for creasing"); RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); prop = RNA_def_property(srna, "bevel_weight", PROP_FLOAT, PROP_NONE); @@ -1987,7 +1987,7 @@ static void rna_def_medge(BlenderRNA *brna) prop = RNA_def_property(srna, "use_edge_sharp", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_SHARP); - RNA_def_property_ui_text(prop, "Sharp", "Sharp edge for the EdgeSplit modifier"); + RNA_def_property_ui_text(prop, "Sharp", "Sharp edge for the Edge Split modifier"); RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); prop = RNA_def_property(srna, "is_loose", PROP_BOOLEAN, PROP_NONE); @@ -3560,7 +3560,7 @@ static void rna_def_mesh(BlenderRNA *brna) prop = RNA_def_property(srna, "show_edge_crease", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "drawflag", ME_DRAWCREASES); - RNA_def_property_ui_text(prop, "Draw Creases", "Display creases created for subsurf weighting"); + RNA_def_property_ui_text(prop, "Draw Creases", "Display creases created for Subdivision Surface modifier"); RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); prop = RNA_def_property(srna, "show_edge_bevel_weight", PROP_BOOLEAN, PROP_NONE); @@ -3575,7 +3575,7 @@ static void rna_def_mesh(BlenderRNA *brna) prop = RNA_def_property(srna, "show_edge_sharp", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "drawflag", ME_DRAWSHARP); - RNA_def_property_ui_text(prop, "Draw Sharp", "Display sharp edges, used with the EdgeSplit modifier"); + RNA_def_property_ui_text(prop, "Draw Sharp", "Display sharp edges, used with the Edge Split modifier"); RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); prop = RNA_def_property(srna, "show_freestyle_edge_marks", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index 4a078ef9182..cd48bc1a3df 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -240,6 +240,9 @@ void RNA_api_mesh(StructRNA *srna) func = RNA_def_function(srna, "free_normals_split", "rna_Mesh_free_normals_split"); RNA_def_function_ui_description(func, "Free split vertex normals"); + func = RNA_def_function(srna, "split_faces", "BKE_mesh_split_faces"); + RNA_def_function_ui_description(func, "Spli faces based on the edge angle"); + func = RNA_def_function(srna, "calc_tangents", "rna_Mesh_calc_tangents"); RNA_def_function_flag(func, FUNC_USE_REPORTS); RNA_def_function_ui_description(func, diff --git a/source/blender/makesrna/intern/rna_nla.c b/source/blender/makesrna/intern/rna_nla.c index 078f6e237f0..e44a6420045 100644 --- a/source/blender/makesrna/intern/rna_nla.c +++ b/source/blender/makesrna/intern/rna_nla.c @@ -663,7 +663,10 @@ static void rna_def_nlastrip(BlenderRNA *brna) prop = RNA_def_property(srna, "strip_time", PROP_FLOAT, PROP_TIME); RNA_def_property_ui_text(prop, "Strip Time", "Frame of referenced Action to evaluate"); - RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_update"); + /* XXX: Update temporarily disabled so that the property can be edited at all! + * Even autokey only applies after the curves have been re-evaluated, causing the unkeyed values to be lost + */ + RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, /*"rna_NlaStrip_update"*/ NULL); /* TODO: should the animated_influence/time settings be animatable themselves? */ prop = RNA_def_property(srna, "use_animated_influence", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index ae444acc432..3e6d8441363 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -1674,7 +1674,7 @@ static void rna_def_filter_video(StructRNA *srna) prop = RNA_def_property(srna, "use_deinterlace", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_FILTERY); - RNA_def_property_ui_text(prop, "De-Interlace", "For video movies to remove fields"); + RNA_def_property_ui_text(prop, "Deinterlace", "Remove fields from video movies"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_update_reopen_files"); prop = RNA_def_property(srna, "alpha_mode", PROP_ENUM, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 1feae9e0bca..5e364a3adf1 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -2624,7 +2624,7 @@ static void rna_def_space_view3d(BlenderRNA *brna) prop = RNA_def_property(srna, "use_pivot_point_align", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_ALIGN); - RNA_def_property_ui_text(prop, "Align", "Manipulate center points (object and pose mode only)"); + RNA_def_property_ui_text(prop, "Align", "Manipulate center points (object, pose and weight paint mode only)"); RNA_def_property_ui_icon(prop, ICON_ALIGN, 0); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_SpaceView3D_pivot_update"); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index beb1d890ba9..e68e67586e9 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -3287,7 +3287,7 @@ static void rna_def_userdef_walk_navigation(BlenderRNA *brna) prop = RNA_def_property(srna, "use_mouse_reverse", PROP_BOOLEAN, PROP_BOOLEAN); RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_WALK_MOUSE_REVERSE); - RNA_def_property_ui_text(prop, "Reverse Mouse", "Reverse the mouse look"); + RNA_def_property_ui_text(prop, "Reverse Mouse", "Reverse the vertical movement of the mouse"); } static void rna_def_userdef_view(BlenderRNA *brna) @@ -4332,7 +4332,7 @@ static void rna_def_userdef_input(BlenderRNA *brna) prop = RNA_def_property(srna, "ndof_deadzone", PROP_FLOAT, PROP_FACTOR); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Deadzone", "Deadzone of the 3D Mouse"); + RNA_def_property_ui_text(prop, "Deadzone", "Threshold of initial movement needed from the device's rest position"); RNA_def_property_update(prop, 0, "rna_userdef_ndof_deadzone_update"); prop = RNA_def_property(srna, "ndof_pan_yz_swap_axis", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c index 92931eb8090..8a0130babd5 100644 --- a/source/blender/python/intern/bpy_rna_anim.c +++ b/source/blender/python/intern/bpy_rna_anim.c @@ -36,12 +36,15 @@ #include "DNA_scene_types.h" #include "DNA_anim_types.h" + #include "ED_keyframing.h" +#include "ED_keyframes_edit.h" #include "BKE_report.h" #include "BKE_context.h" #include "BKE_animsys.h" #include "BKE_fcurve.h" +#include "BKE_idcode.h" #include "RNA_access.h" #include "RNA_enum_types.h" @@ -223,9 +226,47 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb { return NULL; } + else if (self->ptr.type == &RNA_NlaStrip) { + /* Handle special properties for NLA Strips, whose F-Curves are stored on the + * strips themselves. These are stored separately or else the properties will + * not have any effect. + */ + ReportList reports; + short result = 0; + + PointerRNA ptr = self->ptr; + PropertyRNA *prop = NULL; + const char *prop_name; + + BKE_reports_init(&reports, RPT_STORE); + + /* Retrieve the property identifier from the full path, since we can't get it any other way */ + prop_name = strrchr(path_full, '.'); + if ((prop_name >= path_full) && + (prop_name + 1 < path_full + strlen(path_full))) + { + prop = RNA_struct_find_property(&ptr, prop_name + 1); + } + + if (prop) { + NlaStrip *strip = (NlaStrip *)ptr.data; + FCurve *fcu = list_find_fcurve(&strip->fcurves, RNA_property_identifier(prop), index); + + result = insert_keyframe_direct(&reports, ptr, prop, fcu, cfra, keytype, options); + } + else { + BKE_reportf(&reports, RPT_ERROR, "Could not resolve path (%s)", path_full); + } + MEM_freeN((void *)path_full); + + if (BPy_reports_to_error(&reports, PyExc_RuntimeError, true) == -1) + return NULL; + + return PyBool_FromLong(result); + } else { - short result; ReportList reports; + short result; BKE_reports_init(&reports, RPT_STORE); @@ -272,6 +313,67 @@ PyObject *pyrna_struct_keyframe_delete(BPy_StructRNA *self, PyObject *args, PyOb { return NULL; } + else if (self->ptr.type == &RNA_NlaStrip) { + /* Handle special properties for NLA Strips, whose F-Curves are stored on the + * strips themselves. These are stored separately or else the properties will + * not have any effect. + */ + ReportList reports; + short result = 0; + + PointerRNA ptr = self->ptr; + PropertyRNA *prop = NULL; + const char *prop_name; + + BKE_reports_init(&reports, RPT_STORE); + + /* Retrieve the property identifier from the full path, since we can't get it any other way */ + prop_name = strrchr(path_full, '.'); + if ((prop_name >= path_full) && + (prop_name + 1 < path_full + strlen(path_full))) + { + prop = RNA_struct_find_property(&ptr, prop_name + 1); + } + + if (prop) { + ID *id = ptr.id.data; + NlaStrip *strip = (NlaStrip *)ptr.data; + FCurve *fcu = list_find_fcurve(&strip->fcurves, RNA_property_identifier(prop), index); + + BLI_assert(fcu != NULL); /* NOTE: This should be true, or else we wouldn't be able to get here */ + + if (BKE_fcurve_is_protected(fcu)) { + BKE_reportf(&reports, RPT_WARNING, + "Not deleting keyframe for locked F-Curve for NLA Strip influence on %s - %s '%s'", + strip->name, BKE_idcode_to_name(GS(id->name)), id->name + 2); + } + else { + /* remove the keyframe directly + * NOTE: cannot use delete_keyframe_fcurve(), as that will free the curve, + * and delete_keyframe() expects the FCurve to be part of an action + */ + bool found = false; + int i; + + /* try to find index of beztriple to get rid of */ + i = binarysearch_bezt_index(fcu->bezt, cfra, fcu->totvert, &found); + if (found) { + /* delete the key at the index (will sanity check + do recalc afterwards) */ + delete_fcurve_key(fcu, i, 1); + result = true; + } + } + } + else { + BKE_reportf(&reports, RPT_ERROR, "Could not resolve path (%s)", path_full); + } + MEM_freeN((void *)path_full); + + if (BPy_reports_to_error(&reports, PyExc_RuntimeError, true) == -1) + return NULL; + + return PyBool_FromLong(result); + } else { short result; ReportList reports; diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index 86961cdd169..263ea3d4ef2 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -5569,12 +5569,17 @@ static void calculate_speedvectors(Render *re, ObjectInstanceRen *obi, float *ve /* interpolate speed vectors from strand surface */ face= mesh->face[*index]; - co1= mesh->co[face[0]]; - co2= mesh->co[face[1]]; - co3= mesh->co[face[2]]; - co4= (face[3])? mesh->co[face[3]]: NULL; + co1 = mesh->co[face[0]]; + co2 = mesh->co[face[1]]; + co3 = mesh->co[face[2]]; - interp_weights_face_v3(w, co1, co2, co3, co4, strand->vert->co); + if (face[3]) { + co4 = mesh->co[face[3]]; + interp_weights_quad_v3(w, co1, co2, co3, co4, strand->vert->co); + } + else { + interp_weights_tri_v3(w, co1, co2, co3, strand->vert->co); + } zero_v4(speed); madd_v4_v4fl(speed, winspeed[face[0]], w[0]); diff --git a/source/blender/render/intern/source/occlusion.c b/source/blender/render/intern/source/occlusion.c index b3d31e3b93a..cd93898d846 100644 --- a/source/blender/render/intern/source/occlusion.c +++ b/source/blender/render/intern/source/occlusion.c @@ -329,7 +329,7 @@ static void occ_face(const OccFace *face, float co[3], float normal[3], float *a if (vlr->v4) mid_v3_v3v3(co, vlr->v1->co, vlr->v3->co); else - cent_tri_v3(co, vlr->v1->co, vlr->v2->co, vlr->v3->co); + mid_v3_v3v3v3(co, vlr->v1->co, vlr->v2->co, vlr->v3->co); if (obi->flag & R_TRANSFORMED) mul_m4_v3(obi->mat, co); @@ -1190,9 +1190,14 @@ static void sample_occ_surface(ShadeInput *shi) co1 = mesh->co[face[0]]; co2 = mesh->co[face[1]]; co3 = mesh->co[face[2]]; - co4 = (face[3]) ? mesh->co[face[3]] : NULL; - interp_weights_face_v3(w, co1, co2, co3, co4, strand->vert->co); + if (face[3]) { + co4 = mesh->co[face[3]]; + interp_weights_quad_v3(w, co1, co2, co3, co4, strand->vert->co); + } + else { + interp_weights_tri_v3(w, co1, co2, co3, strand->vert->co); + } zero_v3(shi->ao); zero_v3(shi->env); @@ -1245,7 +1250,7 @@ static void *exec_strandsurface_sample(void *data) normal_quad_v3(n, co1, co2, co3, co4); } else { - cent_tri_v3(co, co1, co2, co3); + mid_v3_v3v3v3(co, co1, co2, co3); normal_tri_v3(n, co1, co2, co3); } negate_v3(n); diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 98cd95ebefb..6d74e521aca 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -74,6 +74,7 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_sequencer.h" +#include "BKE_sound.h" #include "BKE_writeavi.h" /* <------ should be replaced once with generic movie module */ #include "BKE_object.h" @@ -3795,6 +3796,7 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri re->flag &= ~R_ANIMATION; BLI_callback_exec(re->main, (ID *)scene, G.is_break ? BLI_CB_EVT_RENDER_CANCEL : BLI_CB_EVT_RENDER_COMPLETE); + BKE_sound_reset_scene_specs(scene); /* UGLY WARNING */ G.is_rendering = false; diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 3825db14e93..77ffa46b990 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -430,7 +430,7 @@ static int wm_triple_gen_textures(wmWindow *win, wmDrawTriple *triple) return 1; } -void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha) +void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha, bool is_interlace) { const int sizex = WM_window_pixels_x(win); const int sizey = WM_window_pixels_y(win); @@ -451,7 +451,13 @@ void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha) halfy /= triple->y; } - GPU_basic_shader_bind((triple->target == GL_TEXTURE_2D) ? GPU_SHADER_TEXTURE_2D : GPU_SHADER_TEXTURE_RECT); + /* interlace stereo buffer bind the shader before calling wm_triple_draw_textures */ + if (is_interlace) { + glEnable(triple->target); + } + else { + GPU_basic_shader_bind((triple->target == GL_TEXTURE_2D) ? GPU_SHADER_TEXTURE_2D : GPU_SHADER_TEXTURE_RECT); + } glBindTexture(triple->target, triple->bind); @@ -472,7 +478,12 @@ void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha) glBindTexture(triple->target, 0); - GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + if (is_interlace) { + glDisable(triple->target); + } + else { + GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); + } } static void wm_triple_copy_textures(wmWindow *win, wmDrawTriple *triple) @@ -495,7 +506,7 @@ static void wm_draw_region_blend(wmWindow *win, ARegion *ar, wmDrawTriple *tripl wmSubWindowScissorSet(win, win->screen->mainwin, &ar->winrct, true); glEnable(GL_BLEND); - wm_triple_draw_textures(win, triple, 1.0f - fac); + wm_triple_draw_textures(win, triple, 1.0f - fac, false); glDisable(GL_BLEND); } } @@ -516,7 +527,7 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) wmSubWindowSet(win, screen->mainwin); - wm_triple_draw_textures(win, drawdata->triple, 1.0f); + wm_triple_draw_textures(win, drawdata->triple, 1.0f, false); } else { /* we run it when we start OR when we turn stereo on */ @@ -656,7 +667,7 @@ static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, StereoVi wmSubWindowSet(win, screen->mainwin); - wm_triple_draw_textures(win, drawdata->triple, 1.0f); + wm_triple_draw_textures(win, drawdata->triple, 1.0f, false); } } else { diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c index 1c1c2ad35af..46cee907991 100644 --- a/source/blender/windowmanager/intern/wm_stereo.c +++ b/source/blender/windowmanager/intern/wm_stereo.c @@ -77,7 +77,7 @@ static void wm_method_draw_stereo3d_pageflip(wmWindow *win) else //STEREO_RIGHT_ID glDrawBuffer(GL_BACK_RIGHT); - wm_triple_draw_textures(win, drawdata->triple, 1.0f); + wm_triple_draw_textures(win, drawdata->triple, 1.0f, false); } glDrawBuffer(GL_BACK); @@ -120,7 +120,7 @@ static void wm_method_draw_stereo3d_interlace(wmWindow *win) break; } - wm_triple_draw_textures(win, drawdata->triple, 1.0f); + wm_triple_draw_textures(win, drawdata->triple, 1.0f, true); GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); } interlace_prev_type = interlace_type; @@ -157,7 +157,7 @@ static void wm_method_draw_stereo3d_anaglyph(wmWindow *win) break; } - wm_triple_draw_textures(win, drawdata->triple, 1.0f); + wm_triple_draw_textures(win, drawdata->triple, 1.0f, false); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } diff --git a/source/blender/windowmanager/wm_draw.h b/source/blender/windowmanager/wm_draw.h index 0f125309045..5257bba45ff 100644 --- a/source/blender/windowmanager/wm_draw.h +++ b/source/blender/windowmanager/wm_draw.h @@ -56,7 +56,7 @@ void wm_draw_region_clear (struct wmWindow *win, struct ARegion *ar); void wm_tag_redraw_overlay (struct wmWindow *win, struct ARegion *ar); -void wm_triple_draw_textures (struct wmWindow *win, struct wmDrawTriple *triple, float alpha); +void wm_triple_draw_textures (struct wmWindow *win, struct wmDrawTriple *triple, float alpha, bool is_interlace); void wm_draw_data_free (struct wmWindow *win); diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index 5f4d0eae483..1d162812e8c 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -674,7 +674,7 @@ void RE_point_density_sample(struct Scene *scene, struct PointDensity *pd, int r void RE_point_density_free(struct PointDensity *pd) RET_NONE; void RE_instance_get_particle_info(struct ObjectInstanceRen *obi, float *index, float *age, float *lifetime, float co[3], float *size, float vel[3], float angvel[3]) RET_NONE void RE_FreeAllPersistentData(void) RET_NONE -float RE_fresnel_dielectric(float incoming[3], float normal[3], float eta) RET_NONE +float RE_fresnel_dielectric(float incoming[3], float normal[3], float eta) RET_ZERO /* python */ struct wmOperatorType *WM_operatortype_find(const char *idname, bool quiet) RET_NULL diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 9f845d29c18..4eb72fb1619 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -1886,7 +1886,7 @@ void main_args_setup(bContext *C, bArgs *ba, SYS_SystemHandle *syshandle) BLI_argsAdd(ba, 4, "-E", "--engine", CB(arg_handle_engine_set), C); BLI_argsAdd(ba, 4, "-F", "--render-format", CB(arg_handle_image_type_set), C); - BLI_argsAdd(ba, 4, "-t", "--threads", CB(arg_handle_threads_set), NULL); + BLI_argsAdd(ba, 1, "-t", "--threads", CB(arg_handle_threads_set), NULL); BLI_argsAdd(ba, 4, "-x", "--use-extension", CB(arg_handle_extension_set), C); #undef CB diff --git a/tests/gtests/blenlib/BLI_string_utf8_test.cc b/tests/gtests/blenlib/BLI_string_utf8_test.cc new file mode 100644 index 00000000000..c0beb92eeec --- /dev/null +++ b/tests/gtests/blenlib/BLI_string_utf8_test.cc @@ -0,0 +1,304 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +extern "C" { +#include "BLI_utildefines.h" +#include "BLI_string.h" +#include "BLI_string_utf8.h" +} + +/* Note that 'common' utf-8 variants of string functions (like copy, etc.) are tested in BLI_string_test.cc + * However, tests below are specific utf-8 conformance ones, and since they eat quite their share of lines, + * they deserved their own file. */ + +/* -------------------------------------------------------------------- */ +/* stubs */ + +extern "C" { + +int mk_wcwidth(wchar_t ucs); +int mk_wcswidth(const wchar_t *pwcs, size_t n); + +int mk_wcwidth(wchar_t ucs) +{ + return 0; +} + +int mk_wcswidth(const wchar_t *pwcs, size_t n) +{ + return 0; +} + +} + +/* -------------------------------------------------------------------- */ +/* tests */ + +/* Each test is made of a 79 bytes (80 with NULL char) string to test, expected string result after + * stripping invalid utf8 bytes, and a single-byte string encoded with expected number of errors. + * + * Based on utf-8 decoder stress-test (https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt) + * by Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> - 2015-08-28 - CC BY 4.0 + */ +const char *utf8_invalid_tests[][3] = { +// 1 Some correct UTF-8 text + {"You should see the Greek word 'kosme': \"\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5\" |", + "You should see the Greek word 'kosme': \"\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5\" |", "\x00"}, + +// 2 Boundary condition test cases +// Note that those will pass for us, those are not erronéous unicode code points +// (asside from \x00, which is only valid as string terminator). +// 2.1 First possible sequence of a certain length + {"2.1.1 1 byte (U-00000000): \"\x00\" |", + "2.1.1 1 byte (U-00000000): \"\" |", "\x01"}, + {"2.1.2 2 bytes (U-00000080): \"\xc2\x80\" |", + "2.1.2 2 bytes (U-00000080): \"\xc2\x80\" |", "\x00"}, + {"2.1.3 3 bytes (U-00000800): \"\xe0\xa0\x80\" |", + "2.1.3 3 bytes (U-00000800): \"\xe0\xa0\x80\" |", "\x00"}, + {"2.1.4 4 bytes (U-00010000): \"\xf0\x90\x80\x80\" |", + "2.1.4 4 bytes (U-00010000): \"\xf0\x90\x80\x80\" |", "\x00"}, + {"2.1.5 5 bytes (U-00200000): \"\xf8\x88\x80\x80\x80\" |", + "2.1.5 5 bytes (U-00200000): \"\xf8\x88\x80\x80\x80\" |", "\x00"}, + {"2.1.6 6 bytes (U-04000000): \"\xfc\x84\x80\x80\x80\x80\" |", + "2.1.6 6 bytes (U-04000000): \"\xfc\x84\x80\x80\x80\x80\" |", "\x00"}, +// 2.2 Last possible sequence of a certain length + {"2.2.1 1 byte (U-0000007F): \"\x7f\" |", + "2.2.1 1 byte (U-0000007F): \"\x7f\" |", "\x00"}, + {"2.2.2 2 bytes (U-000007FF): \"\xdf\xbf\" |", + "2.2.2 2 bytes (U-000007FF): \"\xdf\xbf\" |", "\x00"}, + {"2.2.3 3 bytes (U-0000FFFF): \"\xef\xbf\xbf\" |", + "2.2.3 3 bytes (U-0000FFFF): \"\" |", "\x03"}, /* matches one of 5.3 sequences... */ + {"2.2.4 4 bytes (U-001FFFFF): \"\xf7\xbf\xbf\xbf\" |", + "2.2.4 4 bytes (U-001FFFFF): \"\xf7\xbf\xbf\xbf\" |", "\x00"}, + {"2.2.5 5 bytes (U-03FFFFFF): \"\xfb\xbf\xbf\xbf\xbf\" |", + "2.2.5 5 bytes (U-03FFFFFF): \"\xfb\xbf\xbf\xbf\xbf\" |", "\x00"}, + {"2.2.6 6 bytes (U-7FFFFFFF): \"\xfd\xbf\xbf\xbf\xbf\xbf\" |", + "2.2.6 6 bytes (U-7FFFFFFF): \"\xfd\xbf\xbf\xbf\xbf\xbf\" |", "\x00"}, +// 2.3 Other boundary conditions + {"2.3.1 U-0000D7FF = ed 9f bf = \"\xed\x9f\xbf\" |", + "2.3.1 U-0000D7FF = ed 9f bf = \"\xed\x9f\xbf\" |", "\x00"}, + {"2.3.2 U-0000E000 = ee 80 80 = \"\xee\x80\x80\" |", + "2.3.2 U-0000E000 = ee 80 80 = \"\xee\x80\x80\" |", "\x00"}, + {"2.3.3 U-0000FFFD = ef bf bd = \"\xef\xbf\xbd\" |", + "2.3.3 U-0000FFFD = ef bf bd = \"\xef\xbf\xbd\" |", "\x00"}, + {"2.3.4 U-0010FFFF = f4 8f bf bf = \"\xf4\x8f\xbf\xbf\" |", + "2.3.4 U-0010FFFF = f4 8f bf bf = \"\xf4\x8f\xbf\xbf\" |", "\x00"}, + {"2.3.5 U-00110000 = f4 90 80 80 = \"\xf4\x90\x80\x80\" |", + "2.3.5 U-00110000 = f4 90 80 80 = \"\xf4\x90\x80\x80\" |", "\x00"}, + +// 3 Malformed sequences +// 3.1 Unexpected continuation bytes +// Each unexpected continuation byte should be separately signalled as a malformed sequence of its own. + {"3.1.1 First continuation byte 0x80: \"\x80\" |", + "3.1.1 First continuation byte 0x80: \"\" |", "\x01"}, + {"3.1.2 Last continuation byte 0xbf: \"\xbf\" |", + "3.1.2 Last continuation byte 0xbf: \"\" |", "\x01"}, + {"3.1.3 2 continuation bytes: \"\x80\xbf\" |", + "3.1.3 2 continuation bytes: \"\" |", "\x02"}, + {"3.1.4 3 continuation bytes: \"\x80\xbf\x80\" |", + "3.1.4 3 continuation bytes: \"\" |", "\x03"}, + {"3.1.5 4 continuation bytes: \"\x80\xbf\x80\xbf\" |", + "3.1.5 4 continuation bytes: \"\" |", "\x04"}, + {"3.1.6 5 continuation bytes: \"\x80\xbf\x80\xbf\x80\" |", + "3.1.6 5 continuation bytes: \"\" |", "\x05"}, + {"3.1.7 6 continuation bytes: \"\x80\xbf\x80\xbf\x80\xbf\" |", + "3.1.7 6 continuation bytes: \"\" |", "\x06"}, + {"3.1.8 7 continuation bytes: \"\x80\xbf\x80\xbf\x80\xbf\x80\" |", + "3.1.8 7 continuation bytes: \"\" |", "\x07"}, +// 3.1.9 Sequence of all 64 possible continuation bytes (0x80-0xbf): | + {"3.1.9 \"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\" |", + "3.1.9 \"\" |", "\x40"}, +// 3.2 Lonely start characters +// 3.2.1 All 32 first bytes of 2-byte sequences (0xc0-0xdf), each followed by a space character: + {"3.2.1 \"\xc0 \xc1 \xc2 \xc3 \xc4 \xc5 \xc6 \xc7 \xc8 \xc9 \xca \xcb \xcc \xcd \xce \xcf " + "\xd0 \xd1 \xd2 \xd3 \xd4 \xd5 \xd6 \xd7 \xd8 \xd9 \xda \xdb \xdc \xdd \xde \xdf \" |", + "3.2.1 \" \" |", "\x20"}, +// 3.2.2 All 16 first bytes of 3-byte sequences (0xe0-0xef), each followed by a space character: + {"3.2.2 \"\xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 \xe8 \xe9 \xea \xeb \xec \xed \xee \xef \" |", + "3.2.2 \" \" |", "\x10"}, +// 3.2.3 All 8 first bytes of 4-byte sequences (0xf0-0xf7), each followed by a space character: + {"3.2.3 \"\xf0 \xf1 \xf2 \xf3 \xf4 \xf5 \xf6 \xf7 \" |", + "3.2.3 \" \" |", "\x08"}, +// 3.2.4 All 4 first bytes of 5-byte sequences (0xf8-0xfb), each followed by a space character: + {"3.2.4 \"\xf8 \xf9 \xfa \xfb \" |", + "3.2.4 \" \" |", "\x04"}, +// 3.2.5 All 2 first bytes of 6-byte sequences (0xfc-0xfd), each followed by a space character: + {"3.2.4 \"\xfc \xfd \" |", + "3.2.4 \" \" |", "\x02"}, +// 3.3 Sequences with last continuation byte missing +// All bytes of an incomplete sequence should be signalled as a single malformed sequence, +// i.e., you should see only a single replacement character in each of the next 10 tests. +// (Characters as in section 2) + {"3.3.1 2-byte sequence with last byte missing (U+0000): \"\xc0\" |", + "3.3.1 2-byte sequence with last byte missing (U+0000): \"\" |", "\x01"}, + {"3.3.2 3-byte sequence with last byte missing (U+0000): \"\xe0\x80\" |", + "3.3.2 3-byte sequence with last byte missing (U+0000): \"\" |", "\x02"}, + {"3.3.3 4-byte sequence with last byte missing (U+0000): \"\xf0\x80\x80\" |", + "3.3.3 4-byte sequence with last byte missing (U+0000): \"\" |", "\x03"}, + {"3.3.4 5-byte sequence with last byte missing (U+0000): \"\xf8\x80\x80\x80\" |", + "3.3.4 5-byte sequence with last byte missing (U+0000): \"\" |", "\x04"}, + {"3.3.5 6-byte sequence with last byte missing (U+0000): \"\xfc\x80\x80\x80\x80\" |", + "3.3.5 6-byte sequence with last byte missing (U+0000): \"\" |", "\x05"}, + {"3.3.6 2-byte sequence with last byte missing (U-000007FF): \"\xdf\" |", + "3.3.6 2-byte sequence with last byte missing (U-000007FF): \"\" |", "\x01"}, + {"3.3.7 3-byte sequence with last byte missing (U-0000FFFF): \"\xef\xbf\" |", + "3.3.7 3-byte sequence with last byte missing (U-0000FFFF): \"\" |", "\x02"}, + {"3.3.8 4-byte sequence with last byte missing (U-001FFFFF): \"\xf7\xbf\xbf\" |", + "3.3.8 4-byte sequence with last byte missing (U-001FFFFF): \"\" |", "\x03"}, + {"3.3.9 5-byte sequence with last byte missing (U-03FFFFFF): \"\xfb\xbf\xbf\xbf\" |", + "3.3.9 5-byte sequence with last byte missing (U-03FFFFFF): \"\" |", "\x04"}, + {"3.3.10 6-byte sequence with last byte missing (U-7FFFFFFF): \"\xfd\xbf\xbf\xbf\xbf\" |", + "3.3.10 6-byte sequence with last byte missing (U-7FFFFFFF): \"\" |", "\x05"}, +// 3.4 Concatenation of incomplete sequences +// All the 10 sequences of 3.3 concatenated, you should see 10 malformed sequences being signalled: + {"3.4 \"\xc0\xe0\x80\xf0\x80\x80\xf8\x80\x80\x80\xfc\x80\x80\x80\x80" + "\xdf\xef\xbf\xf7\xbf\xbf\xfb\xbf\xbf\xbf\xfd\xbf\xbf\xbf\xbf\"" + " |", + "3.4 \"\" |", "\x1e"}, +// 3.5 Impossible bytes +// The following two bytes cannot appear in a correct UTF-8 string + {"3.5.1 fe = \"\xfe\" |", + "3.5.1 fe = \"\" |", "\x01"}, + {"3.5.2 ff = \"\xff\" |", + "3.5.2 ff = \"\" |", "\x01"}, + {"3.5.3 fe fe ff ff = \"\xfe\xfe\xff\xff\" |", + "3.5.3 fe fe ff ff = \"\" |", "\x04"}, + +// 4 Overlong sequences +// The following sequences are not malformed according to the letter of the Unicode 2.0 standard. +// However, they are longer then necessary and a correct UTF-8 encoder is not allowed to produce them. +// A "safe UTF-8 decoder" should reject them just like malformed sequences for two reasons: +// (1) It helps to debug applications if overlong sequences are not treated as valid representations +// of characters, because this helps to spot problems more quickly. (2) Overlong sequences provide +// alternative representations of characters, that could maliciously be used to bypass filters that check +// only for ASCII characters. For instance, a 2-byte encoded line feed (LF) would not be caught by a +// line counter that counts only 0x0a bytes, but it would still be processed as a line feed by an unsafe +// UTF-8 decoder later in the pipeline. From a security point of view, ASCII compatibility of UTF-8 +// sequences means also, that ASCII characters are *only* allowed to be represented by ASCII bytes +// in the range 0x00-0x7f. To ensure this aspect of ASCII compatibility, use only "safe UTF-8 decoders" +// that reject overlong UTF-8 sequences for which a shorter encoding exists. +// +// 4.1 Examples of an overlong ASCII character +// With a safe UTF-8 decoder, all of the following five overlong representations of the ASCII character +// slash ("/") should be rejected like a malformed UTF-8 sequence, for instance by substituting it with +// a replacement character. If you see a slash below, you do not have a safe UTF-8 decoder! + {"4.1.1 U+002F = c0 af = \"\xc0\xaf\" |", + "4.1.1 U+002F = c0 af = \"\" |", "\x02"}, + {"4.1.2 U+002F = e0 80 af = \"\xe0\x80\xaf\" |", + "4.1.2 U+002F = e0 80 af = \"\" |", "\x03"}, + {"4.1.3 U+002F = f0 80 80 af = \"\xf0\x80\x80\xaf\" |", + "4.1.3 U+002F = f0 80 80 af = \"\" |", "\x04"}, + {"4.1.4 U+002F = f8 80 80 80 af = \"\xf8\x80\x80\x80\xaf\" |", + "4.1.4 U+002F = f8 80 80 80 af = \"\" |", "\x05"}, + {"4.1.5 U+002F = fc 80 80 80 80 af = \"\xfc\x80\x80\x80\x80\xaf\" |", + "4.1.5 U+002F = fc 80 80 80 80 af = \"\" |", "\x06"}, +// 4.2 Maximum overlong sequences +// Below you see the highest Unicode value that is still resulting in an overlong sequence if represented +// with the given number of bytes. This is a boundary test for safe UTF-8 decoders. All five characters +// should be rejected like malformed UTF-8 sequences. + {"4.2.1 U-0000007F = c1 bf = \"\xc1\xbf\" |", + "4.2.1 U-0000007F = c1 bf = \"\" |", "\x02"}, + {"4.2.2 U-000007FF = e0 9f bf = \"\xe0\x9f\xbf\" |", + "4.2.2 U-000007FF = e0 9f bf = \"\" |", "\x03"}, + {"4.2.3 U-0000FFFF = f0 8f bf bf = \"\xf0\x8f\xbf\xbf\" |", + "4.2.3 U-0000FFFF = f0 8f bf bf = \"\" |", "\x04"}, + {"4.2.4 U-001FFFFF = f8 87 bf bf bf = \"\xf8\x87\xbf\xbf\xbf\" |", + "4.2.4 U-001FFFFF = f8 87 bf bf bf = \"\" |", "\x05"}, + {"4.2.5 U+0000 = fc 83 bf bf bf bf = \"\xfc\x83\xbf\xbf\xbf\xbf\" |", + "4.2.5 U+0000 = fc 83 bf bf bf bf = \"\" |", "\x06"}, +// 4.3 Overlong representation of the NUL character +// The following five sequences should also be rejected like malformed UTF-8 sequences and should not be +// treated like the ASCII NUL character. + {"4.3.1 U+0000 = c0 80 = \"\xc0\x80\" |", + "4.3.1 U+0000 = c0 80 = \"\" |", "\x02"}, + {"4.3.2 U+0000 = e0 80 80 = \"\xe0\x80\x80\" |", + "4.3.2 U+0000 = e0 80 80 = \"\" |", "\x03"}, + {"4.3.3 U+0000 = f0 80 80 80 = \"\xf0\x80\x80\x80\" |", + "4.3.3 U+0000 = f0 80 80 80 = \"\" |", "\x04"}, + {"4.3.4 U+0000 = f8 80 80 80 80 = \"\xf8\x80\x80\x80\x80\" |", + "4.3.4 U+0000 = f8 80 80 80 80 = \"\" |", "\x05"}, + {"4.3.5 U+0000 = fc 80 80 80 80 80 = \"\xfc\x80\x80\x80\x80\x80\" |", + "4.3.5 U+0000 = fc 80 80 80 80 80 = \"\" |", "\x06"}, + +// 5 Illegal code positions +// The following UTF-8 sequences should be rejected like malformed sequences, because they never represent +// valid ISO 10646 characters and a UTF-8 decoder that accepts them might introduce security problems +// comparable to overlong UTF-8 sequences. +// 5.1 Single UTF-16 surrogates + {"5.1.1 U+D800 = ed a0 80 = \"\xed\xa0\x80\" |", + "5.1.1 U+D800 = ed a0 80 = \"\" |", "\x03"}, + {"5.1.2 U+DB7F = ed ad bf = \"\xed\xad\xbf\" |", + "5.1.2 U+DB7F = ed ad bf = \"\" |", "\x03"}, + {"5.1.3 U+DB80 = ed ae 80 = \"\xed\xae\x80\" |", + "5.1.3 U+DB80 = ed ae 80 = \"\" |", "\x03"}, + {"5.1.4 U+DBFF = ed af bf = \"\xed\xaf\xbf\" |", + "5.1.4 U+DBFF = ed af bf = \"\" |", "\x03"}, + {"5.1.5 U+DC00 = ed b0 80 = \"\xed\xb0\x80\" |", + "5.1.5 U+DC00 = ed b0 80 = \"\" |", "\x03"}, + {"5.1.6 U+DF80 = ed be 80 = \"\xed\xbe\x80\" |", + "5.1.6 U+DF80 = ed be 80 = \"\" |", "\x03"}, + {"5.1.7 U+DFFF = ed bf bf = \"\xed\xbf\xbf\" |", + "5.1.7 U+DFFF = ed bf bf = \"\" |", "\x03"}, +// 5.2 Paired UTF-16 surrogates + {"5.2.1 U+D800 U+DC00 = ed a0 80 ed b0 80 = \"\xed\xa0\x80\xed\xb0\x80\" |", + "5.2.1 U+D800 U+DC00 = ed a0 80 ed b0 80 = \"\" |", "\x06"}, + {"5.2.2 U+D800 U+DFFF = ed a0 80 ed bf bf = \"\xed\xa0\x80\xed\xbf\xbf\" |", + "5.2.2 U+D800 U+DFFF = ed a0 80 ed bf bf = \"\" |", "\x06"}, + {"5.2.3 U+DB7F U+DC00 = ed ad bf ed b0 80 = \"\xed\xad\xbf\xed\xb0\x80\" |", + "5.2.3 U+DB7F U+DC00 = ed ad bf ed b0 80 = \"\" |", "\x06"}, + {"5.2.4 U+DB7F U+DFFF = ed ad bf ed bf bf = \"\xed\xad\xbf\xed\xbf\xbf\" |", + "5.2.4 U+DB7F U+DFFF = ed ad bf ed bf bf = \"\" |", "\x06"}, + {"5.2.5 U+DB80 U+DC00 = ed ae 80 ed b0 80 = \"\xed\xae\x80\xed\xb0\x80\" |", + "5.2.5 U+DB80 U+DC00 = ed ae 80 ed b0 80 = \"\" |", "\x06"}, + {"5.2.6 U+DB80 U+DFFF = ed ae 80 ed bf bf = \"\xed\xae\x80\xed\xbf\xbf\" |", + "5.2.6 U+DB80 U+DFFF = ed ae 80 ed bf bf = \"\" |", "\x06"}, + {"5.2.7 U+DBFF U+DC00 = ed af bf ed b0 80 = \"\xed\xaf\xbf\xed\xb0\x80\" |", + "5.2.7 U+DBFF U+DC00 = ed af bf ed b0 80 = \"\" |", "\x06"}, + {"5.2.8 U+DBFF U+DFFF = ed af bf ed bf bf = \"\xed\xaf\xbf\xed\xbf\xbf\" |", + "5.2.8 U+DBFF U+DFFF = ed af bf ed bf bf = \"\" |", "\x06"}, +// 5.3 Noncharacter code positions +// The following "noncharacters" are "reserved for internal use" by applications, and according to older versions +// of the Unicode Standard "should never be interchanged". Unicode Corrigendum #9 dropped the latter restriction. +// Nevertheless, their presence in incoming UTF-8 data can remain a potential security risk, depending +// on what use is made of these codes subsequently. Examples of such internal use: +// - Some file APIs with 16-bit characters may use the integer value -1 = U+FFFF to signal +// an end-of-file (EOF) or error condition. +// - In some UTF-16 receivers, code point U+FFFE might trigger a byte-swap operation +// (to convert between UTF-16LE and UTF-16BE). +// With such internal use of noncharacters, it may be desirable and safer to block those code points in +// UTF-8 decoders, as they should never occur legitimately in incoming UTF-8 data, and could trigger +// unsafe behaviour in subsequent processing. +// +// Particularly problematic noncharacters in 16-bit applications: + {"5.3.1 U+FFFE = ef bf be = \"\xef\xbf\xbe\" |", + "5.3.1 U+FFFE = ef bf be = \"\" |", "\x03"}, + {"5.3.2 U+FFFF = ef bf bf = \"\xef\xbf\xbf\" |", + "5.3.2 U+FFFF = ef bf bf = \"\" |", "\x03"}, + /* Fo now, we ignore those, they do not seem to be crucial anyway... */ +// 5.3.3 U+FDD0 .. U+FDEF +// 5.3.4 U+nFFFE U+nFFFF (for n = 1..10) + {NULL, NULL, NULL} +}; + +/* BLI_utf8_invalid_strip (and indirectly, BLI_utf8_invalid_byte). */ +TEST(string, Utf8InvalidBytes) +{ + for (int i = 0; utf8_invalid_tests[i][0] != NULL; i++) { + const char *tst = utf8_invalid_tests[i][0]; + const char *tst_stripped = utf8_invalid_tests[i][1]; + const int num_errors = (int)utf8_invalid_tests[i][2][0]; + + char buff[80]; + memcpy(buff, tst, sizeof(buff)); + + const int num_errors_found = BLI_utf8_invalid_strip(buff, sizeof(buff) - 1); + + printf("[%02d] -> [%02d] \"%s\" -> \"%s\"\n", num_errors, num_errors_found, tst, buff); + EXPECT_EQ(num_errors, num_errors_found); + EXPECT_STREQ(buff, tst_stripped); + } +} diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt index 12112e7a481..a190d9cd8c5 100644 --- a/tests/gtests/blenlib/CMakeLists.txt +++ b/tests/gtests/blenlib/CMakeLists.txt @@ -42,6 +42,7 @@ BLENDER_TEST(BLI_math_color "bf_blenlib") BLENDER_TEST(BLI_math_geom "bf_blenlib;bf_intern_eigen") BLENDER_TEST(BLI_math_base "bf_blenlib") BLENDER_TEST(BLI_string "bf_blenlib") +BLENDER_TEST(BLI_string_utf8 "bf_blenlib") if(WIN32) BLENDER_TEST(BLI_path_util "bf_blenlib;bf_intern_utfconv;extern_wcwidth;${ZLIB_LIBRARIES}") else() |