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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Stockner <lukas.stockner@freenet.de>2017-01-12 06:56:28 +0300
committerLukas Stockner <lukas.stockner@freenet.de>2017-01-12 06:56:28 +0300
commit59bc9a1d514923cf21438292e71e53c2deb9cb7b (patch)
tree6d23adc32fd9d53aedda9c64fcaa4bda24e85c55
parent73c48d7347ee9ca77aff7e558d97130c8eb43f87 (diff)
parent092cbcd1d2c31b10af31ce326bdf4efc2819a5fa (diff)
Merge remote-tracking branch 'origin/master' into soc-2016-cycles_denoising
-rw-r--r--CMakeLists.txt4
-rw-r--r--doc/python_api/rst/bge_types/bge.types.KX_GameObject.rst18
-rw-r--r--doc/python_api/rst/bgl.rst4
-rw-r--r--doc/python_api/rst/info_api_reference.rst2
-rw-r--r--doc/python_api/rst/info_overview.rst9
-rw-r--r--doc/python_api/sphinx_doc_gen.py11
-rw-r--r--intern/cycles/blender/blender_mesh.cpp68
-rw-r--r--intern/cycles/blender/blender_session.cpp2
-rw-r--r--intern/cycles/blender/blender_util.h15
-rw-r--r--intern/smoke/intern/VEC3.h14
-rw-r--r--release/scripts/startup/bl_operators/image.py4
-rw-r--r--release/scripts/startup/bl_operators/object_quick_effects.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_game.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_grease_pencil_common.py5
-rw-r--r--release/scripts/startup/bl_ui/properties_particle.py20
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py13
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py7
-rw-r--r--release/scripts/templates_py/addon_add_object.py4
-rw-r--r--source/blender/blenkernel/BKE_sound.h2
-rw-r--r--source/blender/blenkernel/intern/collision.c6
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c6
-rw-r--r--source/blender/blenkernel/intern/library.c8
-rw-r--r--source/blender/blenkernel/intern/library_remap.c12
-rw-r--r--source/blender/blenkernel/intern/mesh.c5
-rw-r--r--source/blender/blenkernel/intern/mesh_evaluate.c24
-rw-r--r--source/blender/blenkernel/intern/object.c4
-rw-r--r--source/blender/blenkernel/intern/smoke.c9
-rw-r--r--source/blender/blenkernel/intern/sound.c30
-rw-r--r--source/blender/blenkernel/intern/text.c1
-rw-r--r--source/blender/blenkernel/intern/writeffmpeg.c48
-rw-r--r--source/blender/blenlib/BLI_math_geom.h9
-rw-r--r--source/blender/blenlib/BLI_math_vector.h1
-rw-r--r--source/blender/blenlib/intern/math_geom.c71
-rw-r--r--source/blender/blenlib/intern/math_vector.c10
-rw-r--r--source/blender/blenlib/intern/math_vector_inline.c13
-rw-r--r--source/blender/blenlib/intern/rct.c12
-rw-r--r--source/blender/blenlib/intern/string_utf8.c141
-rw-r--r--source/blender/blenloader/intern/readfile.c105
-rw-r--r--source/blender/bmesh/intern/bmesh_interp.c2
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c48
-rw-r--r--source/blender/editors/animation/anim_deps.c26
-rw-r--r--source/blender/editors/gpencil/drawgpencil.c12
-rw-r--r--source/blender/editors/include/ED_util.h1
-rw-r--r--source/blender/editors/interface/interface_layout.c55
-rw-r--r--source/blender/editors/interface/interface_utils.c8
-rw-r--r--source/blender/editors/object/object_add.c8
-rw-r--r--source/blender/editors/object/object_modifier.c6
-rw-r--r--source/blender/editors/screen/screen_ops.c1
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c6
-rw-r--r--source/blender/editors/sound/sound_ops.c2
-rw-r--r--source/blender/editors/space_action/space_action.c13
-rw-r--r--source/blender/editors/space_image/image_ops.c5
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c10
-rw-r--r--source/blender/editors/space_text/text_ops.c1
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c6
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c7
-rw-r--r--source/blender/editors/space_view3d/view3d_header.c23
-rw-r--r--source/blender/editors/util/undo.c18
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c4
-rw-r--r--source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp4
-rw-r--r--source/blender/freestyle/intern/geometry/GeomUtils.cpp2
-rw-r--r--source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp40
-rw-r--r--source/blender/freestyle/intern/view_map/ViewMapBuilder.h2
-rw-r--r--source/blender/gpu/shaders/gpu_shader_fx_ssao_frag.glsl3
-rw-r--r--source/blender/makesdna/DNA_ID.h4
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h5
-rw-r--r--source/blender/makesrna/RNA_types.h6
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c14
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c127
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c8
-rw-r--r--source/blender/makesrna/intern/rna_mesh_api.c3
-rw-r--r--source/blender/makesrna/intern/rna_nla.c5
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c2
-rw-r--r--source/blender/makesrna/intern/rna_space.c2
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c4
-rw-r--r--source/blender/python/intern/bpy_rna_anim.c104
-rw-r--r--source/blender/render/intern/source/convertblender.c15
-rw-r--r--source/blender/render/intern/source/occlusion.c13
-rw-r--r--source/blender/render/intern/source/pipeline.c2
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c23
-rw-r--r--source/blender/windowmanager/intern/wm_stereo.c6
-rw-r--r--source/blender/windowmanager/wm_draw.h2
-rw-r--r--source/blenderplayer/bad_level_call_stubs/stubs.c2
-rw-r--r--source/creator/creator_args.c2
-rw-r--r--tests/gtests/blenlib/BLI_string_utf8_test.cc304
-rw-r--r--tests/gtests/blenlib/CMakeLists.txt1
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(&lt->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()