diff options
author | Lukas Tönne <lukas.toenne@gmail.com> | 2016-06-08 10:41:04 +0300 |
---|---|---|
committer | Lukas Tönne <lukas.toenne@gmail.com> | 2016-06-08 10:41:04 +0300 |
commit | a6fdd7d8765ca5b87db4f2a7e562e9f4525dcc5a (patch) | |
tree | 4ebcf5e4a137ccf96f227093ab52ed0c197b810a | |
parent | 95a603c607b88f4527c7b8edf4eeacd1ba380867 (diff) | |
parent | fc60689a258a4a9c7c73c0a91cdfeeb57ecc3ccb (diff) |
Merge branch 'master' into object_nodes
232 files changed, 6920 insertions, 1980 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b40b9268fc..b558fe14820 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1959,7 +1959,7 @@ elseif(WIN32) set(OPENAL ${LIBDIR}/openal) set(OPENALDIR ${LIBDIR}/openal) set(OPENAL_INCLUDE_DIR ${OPENAL}/include) - if(MSVC12) + if(MSVC) set(OPENAL_LIBRARY openal32) else() set(OPENAL_LIBRARY wrap_oal) diff --git a/build_files/buildbot/slave_compile.py b/build_files/buildbot/slave_compile.py index 5e25d9f3f04..0e7218405d8 100644 --- a/build_files/buildbot/slave_compile.py +++ b/build_files/buildbot/slave_compile.py @@ -75,11 +75,18 @@ if 'cmake' in builder: cmake_extra_options.append('-DCUDA_NVCC_EXECUTABLE=/usr/local/cuda-hack/bin/nvcc') elif builder.startswith('win'): + if builder.endswith('_vc2015'): if builder.startswith('win64'): - cmake_options.append(['-G', '"Visual Studio 12 2013 Win64"']) + cmake_options.extend(['-G', 'Visual Studio 14 2015 Win64', '-DWITH_CYCLES_CUDA_BINARIES=0']) elif builder.startswith('win32'): bits = 32 - cmake_options.append(['-G', '"Visual Studio 12 2013"']) + cmake_options.extend(['-G', 'Visual Studio 14 2015', '-DWITH_CYCLES_CUDA_BINARIES=0']) + else: + if builder.startswith('win64'): + cmake_options.extend(['-G', 'Visual Studio 12 2013 Win64']) + elif builder.startswith('win32'): + bits = 32 + cmake_options.extend(['-G', 'Visual Studio 12 2013']) elif builder.startswith('linux'): tokens = builder.split("_") diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 09fe757d360..04f655d622b 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -552,6 +552,7 @@ function(SETUP_BLENDER_SORTED_LIBS) bf_modifiers bf_bmesh bf_gpu + bf_blenloader bf_blenkernel bf_blenvm bf_blenvm_compile @@ -559,7 +560,6 @@ function(SETUP_BLENDER_SORTED_LIBS) bf_physics bf_nodes bf_rna - bf_blenloader bf_imbuf bf_blenlib bf_depsgraph @@ -886,8 +886,16 @@ macro(TEST_SHARED_PTR_SUPPORT) # otherwise it's assumed to be defined in std namespace. include(CheckIncludeFileCXX) + include(CheckCXXSourceCompiles) set(SHARED_PTR_FOUND FALSE) - CHECK_INCLUDE_FILE_CXX(memory HAVE_STD_MEMORY_HEADER) + # Workaround for newer GCC (6.x+) where C++11 was enabled by default, which lead us + # to a situation when there is <unordered_map> include but which can't be used uless + # C++11 is enabled. + if(CMAKE_COMPILER_IS_GNUCC AND (NOT "${CMAKE_C_COMPILER_VERSION}" VERSION_LESS "6.0") AND (NOT WITH_CXX11)) + set(HAVE_STD_MEMORY_HEADER False) + else() + CHECK_INCLUDE_FILE_CXX(memory HAVE_STD_MEMORY_HEADER) + endif() if(HAVE_STD_MEMORY_HEADER) # Finding the memory header doesn't mean that shared_ptr is in std # namespace. @@ -895,7 +903,6 @@ macro(TEST_SHARED_PTR_SUPPORT) # In particular, MSVC 2008 has shared_ptr declared in std::tr1. In # order to support this, we do an extra check to see which namespace # should be used. - include(CheckCXXSourceCompiles) CHECK_CXX_SOURCE_COMPILES("#include <memory> int main() { std::shared_ptr<int> int_ptr; diff --git a/doc/python_api/sphinx_doc_gen.sh b/doc/python_api/sphinx_doc_gen.sh index 7095808f251..1ab6bd5afc7 100755 --- a/doc/python_api/sphinx_doc_gen.sh +++ b/doc/python_api/sphinx_doc_gen.sh @@ -61,7 +61,7 @@ if $DO_EXE_BLENDER ; then --python-exit-code 1 \ --python $SPHINXBASE/sphinx_doc_gen.py - if (($? == 1)) ; then + if (($? != 0)) ; then echo "Generating documentation failed, aborting" exit 1 fi diff --git a/extern/wcwidth/README.blender b/extern/wcwidth/README.blender new file mode 100644 index 00000000000..27c8574d1d7 --- /dev/null +++ b/extern/wcwidth/README.blender @@ -0,0 +1,5 @@ +Project: WC Width +URL: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c +License: ICS +Upstream version: 2007-05-26 +Local modifications: None diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp index 896a4906161..9f967a4bde9 100644 --- a/intern/cycles/app/cycles_xml.cpp +++ b/intern/cycles/app/cycles_xml.cpp @@ -31,6 +31,7 @@ #include "mesh.h" #include "nodes.h" #include "object.h" +#include "osl.h" #include "shader.h" #include "scene.h" @@ -214,24 +215,6 @@ static bool xml_equal_string(pugi::xml_node node, const char *name, const char * return false; } -static bool xml_read_enum(ustring *str, NodeEnum& enm, pugi::xml_node node, const char *name) -{ - pugi::xml_attribute attr = node.attribute(name); - - if(attr) { - ustring ustr(attr.value()); - - if(enm.exists(ustr)) { - *str = ustr; - return true; - } - else - fprintf(stderr, "Unknown value \"%s\" for attribute \"%s\".\n", ustr.c_str(), name); - } - - return false; -} - static bool xml_read_enum_value(int *value, NodeEnum& enm, pugi::xml_node node, const char *name) { pugi::xml_attribute attr = node.attribute(name); @@ -250,35 +233,6 @@ static bool xml_read_enum_value(int *value, NodeEnum& enm, pugi::xml_node node, return false; } -static SocketType::Type xml_read_socket_type(pugi::xml_node node, const char *name) -{ - pugi::xml_attribute attr = node.attribute(name); - - if(attr) { - string value = attr.value(); - if(string_iequals(value, "float")) - return SocketType::FLOAT; - else if(string_iequals(value, "int")) - return SocketType::INT; - else if(string_iequals(value, "color")) - return SocketType::COLOR; - else if(string_iequals(value, "vector")) - return SocketType::VECTOR; - else if(string_iequals(value, "point")) - return SocketType::POINT; - else if(string_iequals(value, "normal")) - return SocketType::NORMAL; - else if(string_iequals(value, "closure color")) - return SocketType::CLOSURE; - else if(string_iequals(value, "string")) - return SocketType::STRING; - else - fprintf(stderr, "Unknown shader socket type \"%s\" for attribute \"%s\".\n", value.c_str(), name); - } - - return SocketType::UNDEFINED; -} - /* Camera */ static void xml_read_camera(XMLReadState& state, pugi::xml_node node) @@ -313,6 +267,7 @@ static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml { xml_read_node(state, shader, graph_node); + ShaderManager *manager = state.scene->shader_manager; ShaderGraph *graph = new ShaderGraph(); map<string, ShaderNode*> nodemap; @@ -332,8 +287,8 @@ static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml xml_read_string(&img->filename, node, "src"); img->filename = path_join(state.base, img->filename); - xml_read_enum(&img->color_space, ImageTextureNode::color_space_enum, node, "color_space"); - xml_read_enum(&img->projection, ImageTextureNode::projection_enum, node, "projection"); + xml_read_enum_value((int*)&img->color_space, ImageTextureNode::color_space_enum, node, "color_space"); + xml_read_enum_value((int*)&img->projection, ImageTextureNode::projection_enum, node, "projection"); xml_read_float(&img->projection_blend, node, "projection_blend"); /* ToDo: Interpolation */ @@ -346,56 +301,40 @@ static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml xml_read_string(&env->filename, node, "src"); env->filename = path_join(state.base, env->filename); - xml_read_enum(&env->color_space, EnvironmentTextureNode::color_space_enum, node, "color_space"); - xml_read_enum(&env->projection, EnvironmentTextureNode::projection_enum, node, "projection"); + xml_read_enum_value((int*)&env->color_space, EnvironmentTextureNode::color_space_enum, node, "color_space"); + xml_read_enum_value((int*)&env->projection, EnvironmentTextureNode::projection_enum, node, "projection"); snode = env; } +#ifdef WITH_OSL else if(string_iequals(node.name(), "osl_shader")) { - OSLScriptNode *osl = new OSLScriptNode(); + if(manager->use_osl()) { + std::string filepath; - /* Source */ - xml_read_string(&osl->filepath, node, "src"); - if(path_is_relative(osl->filepath)) { - osl->filepath = path_join(state.base, osl->filepath); - } + if(xml_read_string(&filepath, node, "src")) { + if(path_is_relative(filepath)) { + filepath = path_join(state.base, filepath); + } - /* Generate inputs/outputs from node sockets - * - * Note: ShaderInput/ShaderOutput store shallow string copies only! - * So we register them as ustring to ensure the pointer stays valid. */ - /* read input values */ - for(pugi::xml_node param = node.first_child(); param; param = param.next_sibling()) { - if(string_iequals(param.name(), "input")) { - string name; - if(!xml_read_string(&name, param, "name")) - continue; - - SocketType::Type type = xml_read_socket_type(param, "type"); - if(type == SocketType::UNDEFINED) - continue; - - osl->add_input(ustring(name).c_str(), type); + snode = ((OSLShaderManager*)manager)->osl_node(filepath); + + if(!snode) { + fprintf(stderr, "Failed to create OSL node from \"%s\".\n", filepath.c_str()); + } } - else if(string_iequals(param.name(), "output")) { - string name; - if(!xml_read_string(&name, param, "name")) - continue; - - SocketType::Type type = xml_read_socket_type(param, "type"); - if(type == SocketType::UNDEFINED) - continue; - - osl->add_output(ustring(name).c_str(), type); + else { + fprintf(stderr, "OSL node missing \"src\" attribute.\n"); } } - - snode = osl; + else { + fprintf(stderr, "OSL node without using --shadingsys osl.\n"); + } } +#endif else if(string_iequals(node.name(), "sky_texture")) { SkyTextureNode *sky = new SkyTextureNode(); - xml_read_enum(&sky->type, SkyTextureNode::type_enum, node, "type"); + xml_read_enum_value((int*)&sky->type, SkyTextureNode::type_enum, node, "type"); xml_read_float3(&sky->sun_direction, node, "sun_direction"); xml_read_float(&sky->turbidity, node, "turbidity"); xml_read_float(&sky->ground_albedo, node, "ground_albedo"); @@ -420,17 +359,17 @@ static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml } else if(string_iequals(node.name(), "gradient_texture")) { GradientTextureNode *blend = new GradientTextureNode(); - xml_read_enum(&blend->type, GradientTextureNode::type_enum, node, "type"); + xml_read_enum_value((int*)&blend->type, GradientTextureNode::type_enum, node, "type"); snode = blend; } else if(string_iequals(node.name(), "voronoi_texture")) { VoronoiTextureNode *voronoi = new VoronoiTextureNode(); - xml_read_enum(&voronoi->coloring, VoronoiTextureNode::coloring_enum, node, "coloring"); + xml_read_enum_value((int*)&voronoi->coloring, VoronoiTextureNode::coloring_enum, node, "coloring"); snode = voronoi; } else if(string_iequals(node.name(), "musgrave_texture")) { MusgraveTextureNode *musgrave = new MusgraveTextureNode(); - xml_read_enum(&musgrave->type, MusgraveTextureNode::type_enum, node, "type"); + xml_read_enum_value((int*)&musgrave->type, MusgraveTextureNode::type_enum, node, "type"); snode = musgrave; } else if(string_iequals(node.name(), "magic_texture")) { @@ -440,8 +379,8 @@ static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml } else if(string_iequals(node.name(), "wave_texture")) { WaveTextureNode *wave = new WaveTextureNode(); - xml_read_enum(&wave->type, WaveTextureNode::type_enum, node, "type"); - xml_read_enum(&wave->profile, WaveTextureNode::profile_enum, node, "profile"); + xml_read_enum_value((int*)&wave->type, WaveTextureNode::type_enum, node, "type"); + xml_read_enum_value((int*)&wave->profile, WaveTextureNode::profile_enum, node, "profile"); snode = wave; } else if(string_iequals(node.name(), "normal")) { @@ -476,7 +415,7 @@ static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml } else if(string_iequals(node.name(), "anisotropic_bsdf")) { AnisotropicBsdfNode *aniso = new AnisotropicBsdfNode(); - xml_read_enum(&aniso->distribution, AnisotropicBsdfNode::distribution_enum, node, "distribution"); + xml_read_enum_value((int*)&aniso->distribution, AnisotropicBsdfNode::distribution_enum, node, "distribution"); snode = aniso; } else if(string_iequals(node.name(), "diffuse_bsdf")) { @@ -493,27 +432,27 @@ static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml } else if(string_iequals(node.name(), "toon_bsdf")) { ToonBsdfNode *toon = new ToonBsdfNode(); - xml_read_enum(&toon->component, ToonBsdfNode::component_enum, node, "component"); + xml_read_enum_value((int*)&toon->component, ToonBsdfNode::component_enum, node, "component"); snode = toon; } else if(string_iequals(node.name(), "glossy_bsdf")) { GlossyBsdfNode *glossy = new GlossyBsdfNode(); - xml_read_enum(&glossy->distribution, GlossyBsdfNode::distribution_enum, node, "distribution"); + xml_read_enum_value((int*)&glossy->distribution, GlossyBsdfNode::distribution_enum, node, "distribution"); snode = glossy; } else if(string_iequals(node.name(), "glass_bsdf")) { GlassBsdfNode *diel = new GlassBsdfNode(); - xml_read_enum(&diel->distribution, GlassBsdfNode::distribution_enum, node, "distribution"); + xml_read_enum_value((int*)&diel->distribution, GlassBsdfNode::distribution_enum, node, "distribution"); snode = diel; } else if(string_iequals(node.name(), "refraction_bsdf")) { RefractionBsdfNode *diel = new RefractionBsdfNode(); - xml_read_enum(&diel->distribution, RefractionBsdfNode::distribution_enum, node, "distribution"); + xml_read_enum_value((int*)&diel->distribution, RefractionBsdfNode::distribution_enum, node, "distribution"); snode = diel; } else if(string_iequals(node.name(), "hair_bsdf")) { HairBsdfNode *hair = new HairBsdfNode(); - xml_read_enum(&hair->component, HairBsdfNode::component_enum, node, "component"); + xml_read_enum_value((int*)&hair->component, HairBsdfNode::component_enum, node, "component"); snode = hair; } else if(string_iequals(node.name(), "emission")) { @@ -591,7 +530,7 @@ static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml else if(string_iequals(node.name(), "mix")) { /* ToDo: Tag Mix case for optimization */ MixNode *mix = new MixNode(); - xml_read_enum(&mix->type, MixNode::type_enum, node, "type"); + xml_read_enum_value((int*)&mix->type, MixNode::type_enum, node, "type"); xml_read_bool(&mix->use_clamp, node, "use_clamp"); snode = mix; } @@ -655,32 +594,32 @@ static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml else if(string_iequals(node.name(), "normal_map")) { NormalMapNode *nmap = new NormalMapNode; xml_read_ustring(&nmap->attribute, node, "attribute"); - xml_read_enum(&nmap->space, NormalMapNode::space_enum, node, "space"); + xml_read_enum_value((int*)&nmap->space, NormalMapNode::space_enum, node, "space"); snode = nmap; } else if(string_iequals(node.name(), "tangent")) { TangentNode *tangent = new TangentNode; xml_read_ustring(&tangent->attribute, node, "attribute"); - xml_read_enum(&tangent->direction_type, TangentNode::direction_type_enum, node, "direction_type"); - xml_read_enum(&tangent->axis, TangentNode::axis_enum, node, "axis"); + xml_read_enum_value((int*)&tangent->direction_type, TangentNode::direction_type_enum, node, "direction_type"); + xml_read_enum_value((int*)&tangent->axis, TangentNode::axis_enum, node, "axis"); snode = tangent; } else if(string_iequals(node.name(), "math")) { MathNode *math = new MathNode(); - xml_read_enum(&math->type, MathNode::type_enum, node, "type"); + xml_read_enum_value((int*)&math->type, MathNode::type_enum, node, "type"); xml_read_bool(&math->use_clamp, node, "use_clamp"); snode = math; } else if(string_iequals(node.name(), "vector_math")) { VectorMathNode *vmath = new VectorMathNode(); - xml_read_enum(&vmath->type, VectorMathNode::type_enum, node, "type"); + xml_read_enum_value((int*)&vmath->type, VectorMathNode::type_enum, node, "type"); snode = vmath; } else if(string_iequals(node.name(), "vector_transform")) { VectorTransformNode *vtransform = new VectorTransformNode(); - xml_read_enum(&vtransform->type, VectorTransformNode::type_enum, node, "type"); - xml_read_enum(&vtransform->convert_from, VectorTransformNode::convert_space_enum, node, "convert_from"); - xml_read_enum(&vtransform->convert_to, VectorTransformNode::convert_space_enum, node, "convert_to"); + xml_read_enum_value((int*)&vtransform->type, VectorTransformNode::type_enum, node, "type"); + xml_read_enum_value((int*)&vtransform->convert_from, VectorTransformNode::convert_space_enum, node, "convert_from"); + xml_read_enum_value((int*)&vtransform->convert_to, VectorTransformNode::convert_space_enum, node, "convert_to"); snode = vtransform; } else if(string_iequals(node.name(), "connect")) { @@ -876,6 +815,11 @@ static void xml_read_mesh(const XMLReadState& state, pugi::xml_node node) /* create vertices */ mesh->verts = P; + size_t num_triangles = 0; + for(size_t i = 0; i < nverts.size(); i++) + num_triangles += nverts[i]-2; + mesh->reserve_mesh(mesh->verts.size(), num_triangles); + /* create triangles */ int index_offset = 0; diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 023841a7a17..0961c349fc3 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -76,9 +76,8 @@ def use_cuda(context): def use_branched_path(context): cscene = context.scene.cycles - device_type = context.user_preferences.system.compute_device_type - return (cscene.progressive == 'BRANCHED_PATH' and device_type != 'OPENCL') + return (cscene.progressive == 'BRANCHED_PATH' and not use_opencl(context)) def use_sample_all_lights(context): @@ -704,7 +703,7 @@ class Cycles_PT_mesh_displacement(CyclesButtonsPanel, Panel): col = split.column() sub = col.column(align=True) - sub.label(text="Displacment:") + sub.label(text="Displacement:") sub.prop(cdata, "displacement_method", text="") col = split.column() diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp index 8fbb2414741..378ae67f0c7 100644 --- a/intern/cycles/blender/blender_curves.cpp +++ b/intern/cycles/blender/blender_curves.cpp @@ -604,7 +604,7 @@ void ExportCurveSegments(Scene *scene, Mesh *mesh, ParticleCurveData *CData) num_curve_keys++; } - mesh->add_curve(num_keys, num_curve_keys, CData->psys_shader[sys]); + mesh->add_curve(num_keys, CData->psys_shader[sys]); num_keys += num_curve_keys; num_curves++; } @@ -635,7 +635,7 @@ static void ExportCurveSegmentsMotion(Mesh *mesh, ParticleCurveData *CData, int /* export motion vectors for curve keys */ size_t numkeys = mesh->curve_keys.size(); - float3 *mP = attr_mP->data_float3() + time_index*numkeys; + float4 *mP = attr_mP->data_float4() + time_index*numkeys; bool have_motion = false; int i = 0; @@ -656,12 +656,16 @@ static void ExportCurveSegmentsMotion(Mesh *mesh, ParticleCurveData *CData, int if(CData->psys_closetip[sys] && (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1)) radius = 0.0f; - mP[i] = ickey_loc; + /* curve motion keys store both position and radius in float4 */ + mP[i] = float3_to_float4(ickey_loc); + mP[i].w = radius; /* unlike mesh coordinates, these tend to be slightly different * between frames due to particle transforms into/out of object * space, so we use an epsilon to detect actual changes */ - if(len_squared(mP[i] - mesh->curve_keys[i]) > 1e-5f*1e-5f) + float4 curve_key = float3_to_float4(mesh->curve_keys[i]); + curve_key.w = mesh->curve_radius[i]; + if(len_squared(mP[i] - curve_key) > 1e-5f*1e-5f) have_motion = true; } @@ -683,10 +687,12 @@ static void ExportCurveSegmentsMotion(Mesh *mesh, ParticleCurveData *CData, int /* motion, fill up previous steps that we might have skipped because * they had no motion, but we need them anyway now */ for(int step = 0; step < time_index; step++) { - float3 *mP = attr_mP->data_float3() + step*numkeys; + float4 *mP = attr_mP->data_float4() + step*numkeys; - for(int key = 0; key < numkeys; key++) - mP[key] = mesh->curve_keys[key]; + for(int key = 0; key < numkeys; key++) { + mP[key] = float3_to_float4(mesh->curve_keys[key]); + mP[key].w = mesh->curve_radius[key]; + } } } } diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 346fe0ca6c4..7a13641a312 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -148,29 +148,6 @@ static SocketType::Type convert_socket_type(BL::NodeSocket& b_socket) } } -#ifdef WITH_OSL -static SocketType::Type convert_osl_socket_type(OSL::OSLQuery& query, - BL::NodeSocket& b_socket) -{ - SocketType::Type socket_type = convert_socket_type(b_socket); - if(socket_type == SocketType::VECTOR) { - /* TODO(sergey): Do we need compatible_name() here? */ - const OSL::OSLQuery::Parameter *param = query.getparam(b_socket.name()); - assert(param != NULL); - if(param != NULL) { - if(param->type.vecsemantics == TypeDesc::POINT) { - socket_type = SocketType::POINT; - } - else if(param->type.vecsemantics == TypeDesc::NORMAL) { - socket_type = SocketType::NORMAL; - } - } - } - - return socket_type; -} -#endif /* WITH_OSL */ - static void set_default_value(ShaderInput *input, BL::NodeSocket& b_sock, BL::BlendData& b_data, @@ -179,25 +156,25 @@ static void set_default_value(ShaderInput *input, /* copy values for non linked inputs */ switch(input->type()) { case SocketType::FLOAT: { - input->value_float() = get_float(b_sock.ptr, "default_value"); + input->set(get_float(b_sock.ptr, "default_value")); break; } case SocketType::INT: { - input->value_float() = (float)get_int(b_sock.ptr, "default_value"); + input->set(get_int(b_sock.ptr, "default_value")); break; } case SocketType::COLOR: { - input->value() = float4_to_float3(get_float4(b_sock.ptr, "default_value")); + input->set(float4_to_float3(get_float4(b_sock.ptr, "default_value"))); break; } case SocketType::NORMAL: case SocketType::POINT: case SocketType::VECTOR: { - input->value() = get_float3(b_sock.ptr, "default_value"); + input->set(get_float3(b_sock.ptr, "default_value")); break; } case SocketType::STRING: { - input->value_string() = (ustring)blender_absolute_path(b_data, b_id, get_string(b_sock.ptr, "default_value")); + input->set((ustring)blender_absolute_path(b_data, b_id, get_string(b_sock.ptr, "default_value"))); break; } default: @@ -313,7 +290,7 @@ static ShaderNode *add_node(Scene *scene, else if(b_node.is_a(&RNA_ShaderNodeMixRGB)) { BL::ShaderNodeMixRGB b_mix_node(b_node); MixNode *mix = new MixNode(); - mix->type = MixNode::type_enum[b_mix_node.blend_type()]; + mix->type = (NodeMix)b_mix_node.blend_type(); mix->use_clamp = b_mix_node.use_clamp(); node = mix; } @@ -339,27 +316,27 @@ static ShaderNode *add_node(Scene *scene, node = new HSVNode(); } else if(b_node.is_a(&RNA_ShaderNodeRGBToBW)) { - node = new ConvertNode(SocketType::COLOR, SocketType::FLOAT); + node = new RGBToBWNode(); } else if(b_node.is_a(&RNA_ShaderNodeMath)) { BL::ShaderNodeMath b_math_node(b_node); MathNode *math = new MathNode(); - math->type = MathNode::type_enum[b_math_node.operation()]; + math->type = (NodeMath)b_math_node.operation(); math->use_clamp = b_math_node.use_clamp(); node = math; } else if(b_node.is_a(&RNA_ShaderNodeVectorMath)) { BL::ShaderNodeVectorMath b_vector_math_node(b_node); VectorMathNode *vmath = new VectorMathNode(); - vmath->type = VectorMathNode::type_enum[b_vector_math_node.operation()]; + vmath->type = (NodeVectorMath)b_vector_math_node.operation(); node = vmath; } else if(b_node.is_a(&RNA_ShaderNodeVectorTransform)) { BL::ShaderNodeVectorTransform b_vector_transform_node(b_node); VectorTransformNode *vtransform = new VectorTransformNode(); - vtransform->type = VectorTransformNode::type_enum[b_vector_transform_node.vector_type()]; - vtransform->convert_from = VectorTransformNode::convert_space_enum[b_vector_transform_node.convert_from()]; - vtransform->convert_to = VectorTransformNode::convert_space_enum[b_vector_transform_node.convert_to()]; + vtransform->type = (NodeVectorTransformType)b_vector_transform_node.vector_type(); + vtransform->convert_from = (NodeVectorTransformConvertSpace)b_vector_transform_node.convert_from(); + vtransform->convert_to = (NodeVectorTransformConvertSpace)b_vector_transform_node.convert_to(); node = vtransform; } else if(b_node.is_a(&RNA_ShaderNodeNormal)) { @@ -408,13 +385,13 @@ static ShaderNode *add_node(Scene *scene, switch(b_aniso_node.distribution()) { case BL::ShaderNodeBsdfAnisotropic::distribution_BECKMANN: - aniso->distribution = ustring("Beckmann"); + aniso->distribution = CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID; break; case BL::ShaderNodeBsdfAnisotropic::distribution_GGX: - aniso->distribution = ustring("GGX"); + aniso->distribution = CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID; break; case BL::ShaderNodeBsdfAnisotropic::distribution_ASHIKHMIN_SHIRLEY: - aniso->distribution = ustring("Ashikhmin-Shirley"); + aniso->distribution = CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID; break; } @@ -430,13 +407,13 @@ static ShaderNode *add_node(Scene *scene, switch(b_subsurface_node.falloff()) { case BL::ShaderNodeSubsurfaceScattering::falloff_CUBIC: - subsurface->closure = CLOSURE_BSSRDF_CUBIC_ID; + subsurface->falloff = CLOSURE_BSSRDF_CUBIC_ID; break; case BL::ShaderNodeSubsurfaceScattering::falloff_GAUSSIAN: - subsurface->closure = CLOSURE_BSSRDF_GAUSSIAN_ID; + subsurface->falloff = CLOSURE_BSSRDF_GAUSSIAN_ID; break; case BL::ShaderNodeSubsurfaceScattering::falloff_BURLEY: - subsurface->closure = CLOSURE_BSSRDF_BURLEY_ID; + subsurface->falloff = CLOSURE_BSSRDF_BURLEY_ID; break; } @@ -448,16 +425,16 @@ static ShaderNode *add_node(Scene *scene, switch(b_glossy_node.distribution()) { case BL::ShaderNodeBsdfGlossy::distribution_SHARP: - glossy->distribution = ustring("Sharp"); + glossy->distribution = CLOSURE_BSDF_REFLECTION_ID; break; case BL::ShaderNodeBsdfGlossy::distribution_BECKMANN: - glossy->distribution = ustring("Beckmann"); + glossy->distribution = CLOSURE_BSDF_MICROFACET_BECKMANN_ID; break; case BL::ShaderNodeBsdfGlossy::distribution_GGX: - glossy->distribution = ustring("GGX"); + glossy->distribution = CLOSURE_BSDF_MICROFACET_GGX_ID; break; case BL::ShaderNodeBsdfGlossy::distribution_ASHIKHMIN_SHIRLEY: - glossy->distribution = ustring("Ashikhmin-Shirley"); + glossy->distribution = CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID; break; } node = glossy; @@ -467,13 +444,13 @@ static ShaderNode *add_node(Scene *scene, GlassBsdfNode *glass = new GlassBsdfNode(); switch(b_glass_node.distribution()) { case BL::ShaderNodeBsdfGlass::distribution_SHARP: - glass->distribution = ustring("Sharp"); + glass->distribution = CLOSURE_BSDF_SHARP_GLASS_ID; break; case BL::ShaderNodeBsdfGlass::distribution_BECKMANN: - glass->distribution = ustring("Beckmann"); + glass->distribution = CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID; break; case BL::ShaderNodeBsdfGlass::distribution_GGX: - glass->distribution = ustring("GGX"); + glass->distribution = CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID; break; } node = glass; @@ -483,13 +460,13 @@ static ShaderNode *add_node(Scene *scene, RefractionBsdfNode *refraction = new RefractionBsdfNode(); switch(b_refraction_node.distribution()) { case BL::ShaderNodeBsdfRefraction::distribution_SHARP: - refraction->distribution = ustring("Sharp"); + refraction->distribution = CLOSURE_BSDF_REFRACTION_ID; break; case BL::ShaderNodeBsdfRefraction::distribution_BECKMANN: - refraction->distribution = ustring("Beckmann"); + refraction->distribution = CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID; break; case BL::ShaderNodeBsdfRefraction::distribution_GGX: - refraction->distribution = ustring("GGX"); + refraction->distribution = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; break; } node = refraction; @@ -499,10 +476,10 @@ static ShaderNode *add_node(Scene *scene, ToonBsdfNode *toon = new ToonBsdfNode(); switch(b_toon_node.component()) { case BL::ShaderNodeBsdfToon::component_DIFFUSE: - toon->component = ustring("Diffuse"); + toon->component = CLOSURE_BSDF_DIFFUSE_TOON_ID; break; case BL::ShaderNodeBsdfToon::component_GLOSSY: - toon->component = ustring("Glossy"); + toon->component = CLOSURE_BSDF_GLOSSY_TOON_ID; break; } node = toon; @@ -512,10 +489,10 @@ static ShaderNode *add_node(Scene *scene, HairBsdfNode *hair = new HairBsdfNode(); switch(b_hair_node.component()) { case BL::ShaderNodeBsdfHair::component_Reflection: - hair->component = ustring("Reflection"); + hair->component = CLOSURE_BSDF_HAIR_REFLECTION_ID; break; case BL::ShaderNodeBsdfHair::component_Transmission: - hair->component = ustring("Transmission"); + hair->component = CLOSURE_BSDF_HAIR_TRANSMISSION_ID; break; } node = hair; @@ -582,62 +559,17 @@ static ShaderNode *add_node(Scene *scene, if(scene->shader_manager->use_osl()) { /* create script node */ BL::ShaderNodeScript b_script_node(b_node); - OSLScriptNode *script_node = new OSLScriptNode(); OSLShaderManager *manager = (OSLShaderManager*)scene->shader_manager; string bytecode_hash = b_script_node.bytecode_hash(); - /* Gather additional information from the shader, such as - * input/output type info needed for proper node construction. - */ - OSL::OSLQuery query; - string absolute_filepath; - if(!bytecode_hash.empty()) { - query.open_bytecode(b_script_node.bytecode()); + node = manager->osl_node("", bytecode_hash, b_script_node.bytecode()); } else { - absolute_filepath = blender_absolute_path(b_data, b_ntree, b_script_node.filepath()); - OSLShaderManager::osl_query(query, absolute_filepath); + string absolute_filepath = blender_absolute_path(b_data, b_ntree, b_script_node.filepath()); + node = manager->osl_node(absolute_filepath, ""); } - /* TODO(sergey): Add proper query info error parsing. */ - - /* Generate inputs/outputs from node sockets - * - * Note: the node sockets are generated from OSL parameters, - * so the names match those of the corresponding parameters exactly. - * - * Note 2: ShaderInput/ShaderOutput store shallow string copies only! - * So we register them as ustring to ensure the pointer stays valid. */ - BL::Node::inputs_iterator b_input; - - for(b_script_node.inputs.begin(b_input); b_input != b_script_node.inputs.end(); ++b_input) { - ShaderInput *input = script_node->add_input(ustring(b_input->name()).c_str(), - convert_osl_socket_type(query, *b_input)); - set_default_value(input, *b_input, b_data, b_ntree); - } - - BL::Node::outputs_iterator b_output; - - for(b_script_node.outputs.begin(b_output); b_output != b_script_node.outputs.end(); ++b_output) { - script_node->add_output(ustring(b_output->name()).c_str(), - convert_osl_socket_type(query, *b_output)); - } - - /* load bytecode or filepath */ - if(!bytecode_hash.empty()) { - /* loaded bytecode if not already done */ - if(!manager->shader_test_loaded(bytecode_hash)) - manager->shader_load_bytecode(bytecode_hash, b_script_node.bytecode()); - - script_node->bytecode_hash = bytecode_hash; - } - else { - /* set filepath */ - script_node->filepath = absolute_filepath; - } - - node = script_node; } #else (void)b_data; @@ -690,8 +622,8 @@ static ShaderNode *add_node(Scene *scene, get_image_extension(b_image_node)); } } - image->color_space = ImageTextureNode::color_space_enum[(int)b_image_node.color_space()]; - image->projection = ImageTextureNode::projection_enum[(int)b_image_node.projection()]; + image->color_space = (NodeImageColorSpace)b_image_node.color_space(); + image->projection = (NodeImageProjection)b_image_node.projection(); image->interpolation = get_image_interpolation(b_image_node); image->extension = get_image_extension(b_image_node); image->projection_blend = b_image_node.projection_blend(); @@ -736,9 +668,9 @@ static ShaderNode *add_node(Scene *scene, EXTENSION_REPEAT); } } - env->color_space = EnvironmentTextureNode::color_space_enum[(int)b_env_node.color_space()]; + env->color_space = (NodeImageColorSpace)b_env_node.color_space(); env->interpolation = get_image_interpolation(b_env_node); - env->projection = EnvironmentTextureNode::projection_enum[(int)b_env_node.projection()]; + env->projection = (NodeEnvironmentProjection)b_env_node.projection(); BL::TexMapping b_texture_mapping(b_env_node.texture_mapping()); get_tex_mapping(&env->tex_mapping, b_texture_mapping); node = env; @@ -746,7 +678,7 @@ static ShaderNode *add_node(Scene *scene, else if(b_node.is_a(&RNA_ShaderNodeTexGradient)) { BL::ShaderNodeTexGradient b_gradient_node(b_node); GradientTextureNode *gradient = new GradientTextureNode(); - gradient->type = GradientTextureNode::type_enum[(int)b_gradient_node.gradient_type()]; + gradient->type = (NodeGradientType)b_gradient_node.gradient_type(); BL::TexMapping b_texture_mapping(b_gradient_node.texture_mapping()); get_tex_mapping(&gradient->tex_mapping, b_texture_mapping); node = gradient; @@ -754,7 +686,7 @@ static ShaderNode *add_node(Scene *scene, else if(b_node.is_a(&RNA_ShaderNodeTexVoronoi)) { BL::ShaderNodeTexVoronoi b_voronoi_node(b_node); VoronoiTextureNode *voronoi = new VoronoiTextureNode(); - voronoi->coloring = VoronoiTextureNode::coloring_enum[(int)b_voronoi_node.coloring()]; + voronoi->coloring = (NodeVoronoiColoring)b_voronoi_node.coloring(); BL::TexMapping b_texture_mapping(b_voronoi_node.texture_mapping()); get_tex_mapping(&voronoi->tex_mapping, b_texture_mapping); node = voronoi; @@ -770,8 +702,8 @@ static ShaderNode *add_node(Scene *scene, else if(b_node.is_a(&RNA_ShaderNodeTexWave)) { BL::ShaderNodeTexWave b_wave_node(b_node); WaveTextureNode *wave = new WaveTextureNode(); - wave->type = WaveTextureNode::type_enum[(int)b_wave_node.wave_type()]; - wave->profile = WaveTextureNode::profile_enum[(int)b_wave_node.wave_profile()]; + wave->type = (NodeWaveType)b_wave_node.wave_type(); + wave->profile = (NodeWaveProfile)b_wave_node.wave_profile(); BL::TexMapping b_texture_mapping(b_wave_node.texture_mapping()); get_tex_mapping(&wave->tex_mapping, b_texture_mapping); node = wave; @@ -804,7 +736,7 @@ static ShaderNode *add_node(Scene *scene, else if(b_node.is_a(&RNA_ShaderNodeTexMusgrave)) { BL::ShaderNodeTexMusgrave b_musgrave_node(b_node); MusgraveTextureNode *musgrave = new MusgraveTextureNode(); - musgrave->type = MusgraveTextureNode::type_enum[(int)b_musgrave_node.musgrave_type()]; + musgrave->type = (NodeMusgraveType)b_musgrave_node.musgrave_type(); BL::TexMapping b_texture_mapping(b_musgrave_node.texture_mapping()); get_tex_mapping(&musgrave->tex_mapping, b_texture_mapping); node = musgrave; @@ -822,7 +754,7 @@ static ShaderNode *add_node(Scene *scene, else if(b_node.is_a(&RNA_ShaderNodeTexSky)) { BL::ShaderNodeTexSky b_sky_node(b_node); SkyTextureNode *sky = new SkyTextureNode(); - sky->type = SkyTextureNode::type_enum[(int)b_sky_node.sky_type()]; + sky->type = (NodeSkyType)b_sky_node.sky_type(); sky->sun_direction = normalize(get_float3(b_sky_node.sun_direction())); sky->turbidity = b_sky_node.turbidity(); sky->ground_albedo = b_sky_node.ground_albedo(); @@ -833,15 +765,15 @@ static ShaderNode *add_node(Scene *scene, else if(b_node.is_a(&RNA_ShaderNodeNormalMap)) { BL::ShaderNodeNormalMap b_normal_map_node(b_node); NormalMapNode *nmap = new NormalMapNode(); - nmap->space = NormalMapNode::space_enum[(int)b_normal_map_node.space()]; + nmap->space = (NodeNormalMapSpace)b_normal_map_node.space(); nmap->attribute = b_normal_map_node.uv_map(); node = nmap; } else if(b_node.is_a(&RNA_ShaderNodeTangent)) { BL::ShaderNodeTangent b_tangent_node(b_node); TangentNode *tangent = new TangentNode(); - tangent->direction_type = TangentNode::direction_type_enum[(int)b_tangent_node.direction_type()]; - tangent->axis = TangentNode::axis_enum[(int)b_tangent_node.axis()]; + tangent->direction_type = (NodeTangentDirectionType)b_tangent_node.direction_type(); + tangent->axis = (NodeTangentAxis)b_tangent_node.axis(); tangent->attribute = b_tangent_node.uv_map(); node = tangent; } @@ -856,8 +788,7 @@ static ShaderNode *add_node(Scene *scene, BL::ShaderNodeTexPointDensity b_point_density_node(b_node); PointDensityTextureNode *point_density = new PointDensityTextureNode(); point_density->filename = b_point_density_node.name(); - point_density->space = - PointDensityTextureNode::space_enum[(int)b_point_density_node.space()]; + point_density->space = (NodeTexVoxelSpace)b_point_density_node.space(); point_density->interpolation = get_image_interpolation(b_point_density_node); point_density->builtin_data = b_point_density_node.ptr.data; @@ -1225,7 +1156,7 @@ void BlenderSync::sync_materials(bool update_all) ShaderNode *closure, *out; closure = graph->add(new DiffuseBsdfNode()); - closure->input("Color")->value() = get_float3(b_mat->diffuse_color()); + closure->input("Color")->set(get_float3(b_mat->diffuse_color())); out = graph->output(); graph->connect(closure->output("BSDF"), out->input("Surface")); @@ -1274,7 +1205,7 @@ void BlenderSync::sync_world(bool update_all) ShaderNode *closure, *out; closure = graph->add(new BackgroundNode()); - closure->input("Color")->value() = get_float3(b_world.horizon_color()); + closure->input("Color")->set(get_float3(b_world.horizon_color())); out = graph->output(); graph->connect(closure->output("Background"), out->input("Surface")); @@ -1367,8 +1298,8 @@ void BlenderSync::sync_lamps(bool update_all) } closure = graph->add(new EmissionNode()); - closure->input("Color")->value() = get_float3(b_lamp->color()); - closure->input("Strength")->value_float() = strength; + closure->input("Color")->set(get_float3(b_lamp->color())); + closure->input("Strength")->set(strength); out = graph->output(); graph->connect(closure->output("Emission"), out->input("Surface")); diff --git a/intern/cycles/bvh/bvh_build.cpp b/intern/cycles/bvh/bvh_build.cpp index 8dbd5abc183..d00de007b2d 100644 --- a/intern/cycles/bvh/bvh_build.cpp +++ b/intern/cycles/bvh/bvh_build.cpp @@ -205,6 +205,9 @@ void BVHBuild::add_references(BVHRange& root) foreach(Object *ob, objects) { if(params.top_level) { + if(!ob->is_traceable()) { + continue; + } if(!ob->mesh->is_instanced()) { num_alloc_references += ob->mesh->num_triangles(); num_alloc_references += count_curve_segments(ob->mesh); @@ -226,6 +229,9 @@ void BVHBuild::add_references(BVHRange& root) foreach(Object *ob, objects) { if(params.top_level) { + if(!ob->is_traceable()) { + continue; + } if(!ob->mesh->is_instanced()) add_reference_mesh(bounds, center, ob->mesh, i); else @@ -328,11 +334,11 @@ BVHNode* BVHBuild::run() VLOG(1) << "BVH build statistics:\n" << " Build time: " << time_dt() - build_start_time << "\n" << " Total number of nodes: " - << rootnode->getSubtreeSize(BVH_STAT_NODE_COUNT) << "\n" + << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_NODE_COUNT)) << "\n" << " Number of inner nodes: " - << rootnode->getSubtreeSize(BVH_STAT_INNER_COUNT) << "\n" + << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_INNER_COUNT)) << "\n" << " Number of leaf nodes: " - << rootnode->getSubtreeSize(BVH_STAT_LEAF_COUNT) << "\n" + << string_human_readable_number(rootnode->getSubtreeSize(BVH_STAT_LEAF_COUNT)) << "\n" << " Allocation slop factor: " << ((prim_type.capacity() != 0) ? (float)prim_type.size() / prim_type.capacity() diff --git a/intern/cycles/bvh/bvh_split.cpp b/intern/cycles/bvh/bvh_split.cpp index 3665fb42bc2..bf68b41021f 100644 --- a/intern/cycles/bvh/bvh_split.cpp +++ b/intern/cycles/bvh/bvh_split.cpp @@ -404,7 +404,7 @@ void BVHSpatialSplit::split_object_reference(const Object *object, BoundBox& right_bounds) { Mesh *mesh = object->mesh; - for(int tri_idx = 0; tri_idx < mesh->triangles.size(); ++tri_idx) { + for(int tri_idx = 0; tri_idx < mesh->num_triangles(); ++tri_idx) { split_triangle_primitive(mesh, &object->tfm, tri_idx, diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp index 275ee028eb4..aed86d8d853 100644 --- a/intern/cycles/device/device_cpu.cpp +++ b/intern/cycles/device/device_cpu.cpp @@ -155,7 +155,9 @@ public: InterpolationType interpolation, ExtensionType extension) { - VLOG(1) << "Texture allocate: " << name << ", " << mem.memory_size() << " bytes."; + VLOG(1) << "Texture allocate: " << name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")"; kernel_tex_copy(&kernel_globals, name, mem.data_pointer, diff --git a/intern/cycles/device/device_cuda.cpp b/intern/cycles/device/device_cuda.cpp index 9a78d055580..2d404918a38 100644 --- a/intern/cycles/device/device_cuda.cpp +++ b/intern/cycles/device/device_cuda.cpp @@ -493,7 +493,9 @@ public: InterpolationType interpolation, ExtensionType extension) { - VLOG(1) << "Texture allocate: " << name << ", " << mem.memory_size() << " bytes."; + VLOG(1) << "Texture allocate: " << name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")"; /* Check if we are on sm_30 or above. * We use arrays and bindles textures for storage there */ @@ -1366,6 +1368,7 @@ void device_cuda_info(vector<DeviceInfo>& devices) /* if device has a kernel timeout, assume it is used for display */ if(cuDeviceGetAttribute(&attr, CU_DEVICE_ATTRIBUTE_KERNEL_EXEC_TIMEOUT, num) == CUDA_SUCCESS && attr == 1) { + info.description += " (Display)"; info.display_device = true; display_devices.push_back(info); } diff --git a/intern/cycles/device/device_multi.cpp b/intern/cycles/device/device_multi.cpp index 434d0085d39..c4f8d9e16e0 100644 --- a/intern/cycles/device/device_multi.cpp +++ b/intern/cycles/device/device_multi.cpp @@ -175,7 +175,9 @@ public: interpolation, ExtensionType extension) { - VLOG(1) << "Texture allocate: " << name << ", " << mem.memory_size() << " bytes."; + VLOG(1) << "Texture allocate: " << name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")"; foreach(SubDevice& sub, devices) { mem.device_pointer = 0; diff --git a/intern/cycles/device/device_network.cpp b/intern/cycles/device/device_network.cpp index cf4a05de8fc..3eb5ad2d2db 100644 --- a/intern/cycles/device/device_network.cpp +++ b/intern/cycles/device/device_network.cpp @@ -168,7 +168,9 @@ public: InterpolationType interpolation, ExtensionType extension) { - VLOG(1) << "Texture allocate: " << name << ", " << mem.memory_size() << " bytes."; + VLOG(1) << "Texture allocate: " << name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")"; thread_scoped_lock lock(rpc_lock); diff --git a/intern/cycles/device/device_opencl.cpp b/intern/cycles/device/device_opencl.cpp index 1b4e5421b5a..afe21c49730 100644 --- a/intern/cycles/device/device_opencl.cpp +++ b/intern/cycles/device/device_opencl.cpp @@ -1187,7 +1187,9 @@ public: InterpolationType /*interpolation*/, ExtensionType /*extension*/) { - VLOG(1) << "Texture allocate: " << name << ", " << mem.memory_size() << " bytes."; + VLOG(1) << "Texture allocate: " << name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")"; mem_alloc(mem, MEM_READ_ONLY); mem_copy_to(mem); assert(mem_map.find(name) == mem_map.end()); @@ -1222,18 +1224,28 @@ public: CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), &workgroup_size, NULL); clGetDeviceInfo(cdDevice, CL_DEVICE_MAX_WORK_ITEM_SIZES, sizeof(size_t)*3, max_work_items, NULL); - - /* try to divide evenly over 2 dimensions */ + + /* Try to divide evenly over 2 dimensions. */ size_t sqrt_workgroup_size = max((size_t)sqrt((double)workgroup_size), 1); size_t local_size[2] = {sqrt_workgroup_size, sqrt_workgroup_size}; - /* some implementations have max size 1 on 2nd dimension */ + /* Some implementations have max size 1 on 2nd dimension. */ if(local_size[1] > max_work_items[1]) { local_size[0] = workgroup_size/max_work_items[1]; local_size[1] = max_work_items[1]; } - size_t global_size[2] = {global_size_round_up(local_size[0], w), global_size_round_up(local_size[1], h)}; + size_t global_size[2] = {global_size_round_up(local_size[0], w), + global_size_round_up(local_size[1], h)}; + + /* Vertical size of 1 is coming from bake/shade kernels where we should + * not round anything up because otherwise we'll either be doing too + * much work per pixel (if we don't check global ID on Y axis) or will + * be checking for global ID to always have Y of 0. + */ + if (h == 1) { + global_size[h] = 1; + } /* run kernel */ opencl_assert(clEnqueueNDRangeKernel(cqCommandQueue, kernel, 2, NULL, global_size, NULL, 0, NULL, NULL)); @@ -1318,48 +1330,49 @@ public: else kernel = ckShaderKernel; - for(int sample = 0; sample < task.num_samples; sample++) { - - if(task.get_cancel()) - break; - - cl_int d_sample = sample; - - cl_uint start_arg_index = - kernel_set_args(kernel, - 0, - d_data, - d_input, - d_output); + cl_uint start_arg_index = + kernel_set_args(kernel, + 0, + d_data, + d_input, + d_output); - if(task.shader_eval_type < SHADER_EVAL_BAKE) { - start_arg_index += kernel_set_args(kernel, - start_arg_index, - d_output_luma); - } + if(task.shader_eval_type < SHADER_EVAL_BAKE) { + start_arg_index += kernel_set_args(kernel, + start_arg_index, + d_output_luma); + } #define KERNEL_TEX(type, ttype, name) \ - set_kernel_arg_mem(kernel, &start_arg_index, #name); + set_kernel_arg_mem(kernel, &start_arg_index, #name); #include "kernel_textures.h" #undef KERNEL_TEX + start_arg_index += kernel_set_args(kernel, + start_arg_index, + d_shader_eval_type); + if(task.shader_eval_type >= SHADER_EVAL_BAKE) { start_arg_index += kernel_set_args(kernel, start_arg_index, - d_shader_eval_type); - if(task.shader_eval_type >= SHADER_EVAL_BAKE) { - start_arg_index += kernel_set_args(kernel, - start_arg_index, - d_shader_filter); - } - start_arg_index += kernel_set_args(kernel, - start_arg_index, - d_shader_x, - d_shader_w, - d_offset, - d_sample); + d_shader_filter); + } + start_arg_index += kernel_set_args(kernel, + start_arg_index, + d_shader_x, + d_shader_w, + d_offset); + + for(int sample = 0; sample < task.num_samples; sample++) { + + if(task.get_cancel()) + break; + + kernel_set_args(kernel, start_arg_index, sample); enqueue_kernel(kernel, task.shader_w, 1); + clFinish(cqCommandQueue); + task.update_progress(NULL); } } diff --git a/intern/cycles/graph/node.cpp b/intern/cycles/graph/node.cpp index 98b66fb9a7a..941a66741c5 100644 --- a/intern/cycles/graph/node.cpp +++ b/intern/cycles/graph/node.cpp @@ -36,12 +36,8 @@ Node::Node(const NodeType *type_, ustring name_) } /* initialize default values */ - typedef unordered_map<ustring, SocketType, ustringHash> map_type; - foreach(const map_type::value_type& it, type->inputs) { - const SocketType& socket = it.second; - const void *src = socket.default_value; - void *dst = ((char*)this) + socket.struct_offset; - memcpy(dst, src, socket.size()); + foreach(const SocketType& socket, type->inputs) { + set_default_value(socket); } } @@ -297,7 +293,8 @@ const array<Node*>& Node::get_node_array(const SocketType& input) const return get_socket_value<array<Node*> >(this, input); } -/* default values */ +/* generic value operations */ + bool Node::has_default_value(const SocketType& input) const { const void *src = input.default_value; @@ -305,6 +302,48 @@ bool Node::has_default_value(const SocketType& input) const return memcmp(dst, src, input.size()) == 0; } +void Node::set_default_value(const SocketType& socket) +{ + const void *src = socket.default_value; + void *dst = ((char*)this) + socket.struct_offset; + memcpy(dst, src, socket.size()); +} + +template<typename T> +static void copy_array(const Node *node, const SocketType& socket, const Node *other, const SocketType& other_socket) +{ + const array<T>* src = (const array<T>*)(((char*)other) + other_socket.struct_offset); + array<T>* dst = (array<T>*)(((char*)node) + socket.struct_offset); + *dst = *src; +} + +void Node::copy_value(const SocketType& socket, const Node& other, const SocketType& other_socket) +{ + assert(socket.type == other_socket.type); + + if(socket.is_array()) { + switch(socket.type) { + case SocketType::BOOLEAN_ARRAY: copy_array<bool>(this, socket, &other, other_socket); break; + case SocketType::FLOAT_ARRAY: copy_array<float>(this, socket, &other, other_socket); break; + case SocketType::INT_ARRAY: copy_array<int>(this, socket, &other, other_socket); break; + case SocketType::COLOR_ARRAY: copy_array<float3>(this, socket, &other, other_socket); break; + case SocketType::VECTOR_ARRAY: copy_array<float3>(this, socket, &other, other_socket); break; + case SocketType::POINT_ARRAY: copy_array<float3>(this, socket, &other, other_socket); break; + case SocketType::NORMAL_ARRAY: copy_array<float3>(this, socket, &other, other_socket); break; + case SocketType::POINT2_ARRAY: copy_array<float2>(this, socket, &other, other_socket); break; + case SocketType::STRING_ARRAY: copy_array<ustring>(this, socket, &other, other_socket); break; + case SocketType::TRANSFORM_ARRAY: copy_array<Transform>(this, socket, &other, other_socket); break; + case SocketType::NODE_ARRAY: copy_array<void*>(this, socket, &other, other_socket); break; + default: assert(0); break; + } + } + else { + const void *src = ((char*)&other) + other_socket.struct_offset; + void *dst = ((char*)this) + socket.struct_offset; + memcpy(dst, src, socket.size()); + } +} + template<typename T> static bool is_array_equal(const Node *node, const Node *other, const SocketType& socket) { @@ -313,48 +352,43 @@ static bool is_array_equal(const Node *node, const Node *other, const SocketType return *a == *b; } -/* modified */ -bool Node::modified(const Node& other) +bool Node::equals_value(const Node& other, const SocketType& socket) const +{ + if(socket.is_array()) { + switch(socket.type) { + case SocketType::BOOLEAN_ARRAY: return is_array_equal<bool>(this, &other, socket); + case SocketType::FLOAT_ARRAY: return is_array_equal<float>(this, &other, socket); + case SocketType::INT_ARRAY: return is_array_equal<int>(this, &other, socket); + case SocketType::COLOR_ARRAY: return is_array_equal<float3>(this, &other, socket); + case SocketType::VECTOR_ARRAY: return is_array_equal<float3>(this, &other, socket); + case SocketType::POINT_ARRAY: return is_array_equal<float3>(this, &other, socket); + case SocketType::NORMAL_ARRAY: return is_array_equal<float3>(this, &other, socket); + case SocketType::POINT2_ARRAY: return is_array_equal<float2>(this, &other, socket); + case SocketType::STRING_ARRAY: return is_array_equal<ustring>(this, &other, socket); + case SocketType::TRANSFORM_ARRAY: return is_array_equal<Transform>(this, &other, socket); + case SocketType::NODE_ARRAY: return is_array_equal<void*>(this, &other, socket); + default: assert(0); return true; + } + } + else { + const void *a = ((char*)this) + socket.struct_offset; + const void *b = ((char*)&other) + socket.struct_offset; + return (memcmp(a, b, socket.size()) == 0); + } +} + +/* equals */ + +bool Node::equals(const Node& other) const { assert(type == other.type); - typedef unordered_map<ustring, SocketType, ustringHash> map_type; - foreach(const map_type::value_type& it, type->inputs) { - const SocketType& socket = it.second; - - if(socket.is_array()) { - bool equal = true; - - switch(socket.type) - { - case SocketType::BOOLEAN_ARRAY: equal = is_array_equal<bool>(this, &other, socket); break; - case SocketType::FLOAT_ARRAY: equal = is_array_equal<float>(this, &other, socket); break; - case SocketType::INT_ARRAY: equal = is_array_equal<int>(this, &other, socket); break; - case SocketType::COLOR_ARRAY: equal = is_array_equal<float3>(this, &other, socket); break; - case SocketType::VECTOR_ARRAY: equal = is_array_equal<float3>(this, &other, socket); break; - case SocketType::POINT_ARRAY: equal = is_array_equal<float3>(this, &other, socket); break; - case SocketType::NORMAL_ARRAY: equal = is_array_equal<float3>(this, &other, socket); break; - case SocketType::POINT2_ARRAY: equal = is_array_equal<float2>(this, &other, socket); break; - case SocketType::STRING_ARRAY: equal = is_array_equal<ustring>(this, &other, socket); break; - case SocketType::TRANSFORM_ARRAY: equal = is_array_equal<Transform>(this, &other, socket); break; - case SocketType::NODE_ARRAY: equal = is_array_equal<void*>(this, &other, socket); break; - default: assert(0); break; - } - - if(!equal) { - return true; - } - } - else { - const void *a = ((char*)this) + socket.struct_offset; - const void *b = ((char*)&other) + socket.struct_offset; - if(memcmp(a, b, socket.size()) != 0) { - return true; - } - } + foreach(const SocketType& socket, type->inputs) { + if(!equals_value(other, socket)) + return false; } - return false; + return true; } CCL_NAMESPACE_END diff --git a/intern/cycles/graph/node.h b/intern/cycles/graph/node.h index de06df10265..bb84f982fb3 100644 --- a/intern/cycles/graph/node.h +++ b/intern/cycles/graph/node.h @@ -77,11 +77,14 @@ struct Node const array<Transform>& get_transform_array(const SocketType& input) const; const array<Node*>& get_node_array(const SocketType& input) const; - /* default values */ + /* generic values operations */ bool has_default_value(const SocketType& input) const; + void set_default_value(const SocketType& input); + bool equals_value(const Node& other, const SocketType& input) const; + void copy_value(const SocketType& input, const Node& other, const SocketType& other_input); - /* modified */ - bool modified(const Node& other); + /* equals */ + bool equals(const Node& other) const; ustring name; const NodeType *type; diff --git a/intern/cycles/graph/node_type.cpp b/intern/cycles/graph/node_type.cpp index dc879655d7f..7f68ae9c7c7 100644 --- a/intern/cycles/graph/node_type.cpp +++ b/intern/cycles/graph/node_type.cpp @@ -114,9 +114,15 @@ ustring SocketType::type_name(Type type) return names[(int)type]; } +bool SocketType::is_float3(Type type) +{ + return (type == COLOR || type == VECTOR || type == POINT || type == NORMAL); +} + /* Node Type */ -NodeType::NodeType() +NodeType::NodeType(Type type_) +: type(type_) { } @@ -137,7 +143,7 @@ void NodeType::register_input(ustring name, ustring ui_name, SocketType::Type ty socket.enum_values = enum_values; socket.node_type = node_type; socket.flags = flags | extra_flags; - inputs[name] = socket; + inputs.push_back(socket); } void NodeType::register_output(ustring name, ustring ui_name, SocketType::Type type) @@ -151,7 +157,29 @@ void NodeType::register_output(ustring name, ustring ui_name, SocketType::Type t socket.enum_values = NULL; socket.node_type = NULL; socket.flags = SocketType::LINKABLE; - outputs[name] = socket; + outputs.push_back(socket); +} + +const SocketType *NodeType::find_input(ustring name) const +{ + foreach(const SocketType& socket, inputs) { + if(socket.name == name) { + return &socket; + } + } + + return NULL; +} + +const SocketType *NodeType::find_output(ustring name) const +{ + foreach(const SocketType& socket, outputs) { + if(socket.name == name) { + return &socket; + } + } + + return NULL; } /* Node Type Registry */ @@ -162,7 +190,7 @@ unordered_map<ustring, NodeType, ustringHash>& NodeType::types() return _types; } -NodeType *NodeType::add(const char *name_, CreateFunc create_) +NodeType *NodeType::add(const char *name_, CreateFunc create_, Type type_) { ustring name(name_); @@ -172,7 +200,7 @@ NodeType *NodeType::add(const char *name_, CreateFunc create_) return NULL; } - types()[name] = NodeType(); + types()[name] = NodeType(type_); NodeType *type = &types()[name]; type->name = name; diff --git a/intern/cycles/graph/node_type.h b/intern/cycles/graph/node_type.h index 82ddd29da33..20816f634cd 100644 --- a/intern/cycles/graph/node_type.h +++ b/intern/cycles/graph/node_type.h @@ -21,6 +21,7 @@ #include "util_map.h" #include "util_param.h" #include "util_string.h" +#include "util_vector.h" CCL_NAMESPACE_BEGIN @@ -94,13 +95,19 @@ struct SocketType static size_t max_size(); static ustring type_name(Type type); static void *zero_default_value(); + static bool is_float3(Type type); }; /* Node Type */ struct NodeType { - explicit NodeType(); + enum Type { + NONE, + SHADER + }; + + explicit NodeType(Type type = NONE); ~NodeType(); void register_input(ustring name, ustring ui_name, SocketType::Type type, @@ -110,15 +117,18 @@ struct NodeType int flags = 0, int extra_flags = 0); void register_output(ustring name, ustring ui_name, SocketType::Type type); + const SocketType *find_input(ustring name) const; + const SocketType *find_output(ustring name) const; + typedef Node *(*CreateFunc)(const NodeType *type); - typedef unordered_map<ustring, SocketType, ustringHash> SocketMap; ustring name; - SocketMap inputs; - SocketMap outputs; + Type type; + std::vector<SocketType> inputs; + std::vector<SocketType> outputs; CreateFunc create; - static NodeType *add(const char *name, CreateFunc create); + static NodeType *add(const char *name, CreateFunc create, Type type = NONE); static const NodeType *find(ustring name); static unordered_map<ustring, NodeType, ustringHash>& types(); }; diff --git a/intern/cycles/graph/node_xml.cpp b/intern/cycles/graph/node_xml.cpp index a6040405c30..022de7cf32a 100644 --- a/intern/cycles/graph/node_xml.cpp +++ b/intern/cycles/graph/node_xml.cpp @@ -58,9 +58,7 @@ void xml_read_node(XMLReader& reader, Node *node, pugi::xml_node xml_node) node->name = ustring(name_attr.value()); } - foreach(const NodeType::SocketMap::value_type& it, node->type->inputs) { - const SocketType& socket = it.second; - + foreach(const SocketType& socket, node->type->inputs) { if(socket.type == SocketType::CLOSURE || socket.type == SocketType::UNDEFINED) { continue; } @@ -257,9 +255,7 @@ pugi::xml_node xml_write_node(Node *node, pugi::xml_node xml_root) xml_node.append_attribute("name") = node->name.c_str(); - foreach(const NodeType::SocketMap::value_type& it, node->type->inputs) { - const SocketType& socket = it.second; - + foreach(const SocketType& socket, node->type->inputs) { if(socket.type == SocketType::CLOSURE || socket.type == SocketType::UNDEFINED) { continue; } diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 7c2fc1e4b14..61c484df094 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -234,6 +234,7 @@ if(WITH_CYCLES_CUDA_BINARIES) OUTPUT ${cuda_cubin} COMMAND ${CUDA_NVCC_EXECUTABLE} -arch=${arch} + ${CUDA_NVCC_FLAGS} -m${CUDA_BITS} --cubin ${CMAKE_CURRENT_SOURCE_DIR}/kernels/cuda/kernel.cu -o ${CMAKE_CURRENT_BINARY_DIR}/${cuda_cubin} diff --git a/intern/cycles/kernel/geom/geom_triangle_intersect.h b/intern/cycles/kernel/geom/geom_triangle_intersect.h index d2957ad5474..b6dfc769012 100644 --- a/intern/cycles/kernel/geom/geom_triangle_intersect.h +++ b/intern/cycles/kernel/geom/geom_triangle_intersect.h @@ -159,16 +159,11 @@ ccl_device_inline bool triangle_intersect(KernelGlobals *kg, if(kernel_tex_fetch(__prim_visibility, triAddr) & visibility) #endif { -#ifdef __KERNEL_GPU__ - float4 a = tri_b - tri_a, b = tri_c - tri_a; - if(len_squared(make_float3(a.y*b.z - a.z*b.y, - a.z*b.x - a.x*b.z, - a.x*b.y - a.y*b.x)) == 0.0f) - { +#ifdef __KERNEL_CUDA__ + if(A == B && B == C) { return false; } #endif - /* Normalize U, V, W, and T. */ const float inv_det = 1.0f / det; isect->prim = triAddr; diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h index 3966a06fe33..8d05befe1d4 100644 --- a/intern/cycles/kernel/kernel_bake.h +++ b/intern/cycles/kernel/kernel_bake.h @@ -482,12 +482,10 @@ ccl_device void kernel_bake_evaluate(KernelGlobals *kg, ccl_global uint4 *input, } /* write output */ - float output_fac = is_aa_pass(type)? 1.0f/num_samples: 1.0f; + const float output_fac = is_aa_pass(type)? 1.0f/num_samples: 1.0f; + const float4 scaled_result = make_float4(out.x, out.y, out.z, 1.0f) * output_fac; - if(sample == 0) - output[i] = make_float4(out.x, out.y, out.z, 1.0f) * output_fac; - else - output[i] += make_float4(out.x, out.y, out.z, 1.0f) * output_fac; + output[i] = (sample == 0)? scaled_result: output[i] + scaled_result; } #endif /* __BAKING__ */ diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h index 675eacfc5ee..736a884f819 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/kernel_light.h @@ -291,24 +291,13 @@ ccl_device float background_portal_pdf(KernelGlobals *kg, } num_possible++; - float t = -(dot(P, dir) - dot(lightpos, dir)) / dot(direction, dir); - if(t <= 1e-4f) { - /* Either behind the portal or too close. */ - continue; - } - float4 data1 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 1); float4 data2 = kernel_tex_fetch(__light_data, (p + kernel_data.integrator.portal_offset)*LIGHT_SIZE + 2); float3 axisu = make_float3(data1.y, data1.z, data1.w); float3 axisv = make_float3(data2.y, data2.z, data2.w); - float3 hit = P + t*direction; - float3 inplane = hit - lightpos; - /* Skip if the the ray doesn't pass through portal. */ - if(fabsf(dot(inplane, axisu) / dot(axisu, axisu)) > 0.5f) - continue; - if(fabsf(dot(inplane, axisv) / dot(axisv, axisv)) > 0.5f) + if(!ray_quad_intersect(P, direction, 1e-4f, FLT_MAX, lightpos, axisu, axisv, dir, NULL, NULL)) continue; portal_pdf += area_light_sample(P, &lightpos, axisu, axisv, 0.0f, 0.0f, false); @@ -729,8 +718,8 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, float3 light_P = make_float3(data0.y, data0.z, data0.w); - if(!ray_quad_intersect(P, D, t, - light_P, axisu, axisv, &ls->P, &ls->t)) + if(!ray_quad_intersect(P, D, 0.0f, t, + light_P, axisu, axisv, Ng, &ls->P, &ls->t)) { return false; } diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index ba50d180acf..1ffcfb94a15 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -121,9 +121,7 @@ CCL_NAMESPACE_BEGIN # define __OBJECT_MOTION__ # define __HAIR__ # define __BAKING__ -# ifdef __KERNEL_EXPERIMENTAL__ -# define __TRANSPARENT_SHADOWS__ -# endif +# define __TRANSPARENT_SHADOWS__ # endif /* __KERNEL_OPENCL_AMD__ */ # ifdef __KERNEL_OPENCL_INTEL_CPU__ diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h index e1ea60f372e..bf8301fe5fb 100644 --- a/intern/cycles/kernel/kernel_volume.h +++ b/intern/cycles/kernel/kernel_volume.h @@ -276,7 +276,7 @@ ccl_device float kernel_volume_distance_sample(float max_t, float3 sigma_t, int float sample_t = min(max_t, -logf(1.0f - xi*(1.0f - sample_transmittance))/sample_sigma_t); *transmittance = volume_color_transmittance(sigma_t, sample_t); - *pdf = (sigma_t * *transmittance)/(make_float3(1.0f, 1.0f, 1.0f) - full_transmittance); + *pdf = safe_divide_color(sigma_t * *transmittance, make_float3(1.0f, 1.0f, 1.0f) - full_transmittance); /* todo: optimization: when taken together with hit/miss decision, * the full_transmittance cancels out drops out and xi does not @@ -290,7 +290,7 @@ ccl_device float3 kernel_volume_distance_pdf(float max_t, float3 sigma_t, float float3 full_transmittance = volume_color_transmittance(sigma_t, max_t); float3 transmittance = volume_color_transmittance(sigma_t, sample_t); - return (sigma_t * transmittance)/(make_float3(1.0f, 1.0f, 1.0f) - full_transmittance); + return safe_divide_color(sigma_t * transmittance, make_float3(1.0f, 1.0f, 1.0f) - full_transmittance); } /* Emission */ @@ -625,11 +625,13 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta const int global_max_steps = kernel_data.integrator.volume_max_steps; step_size = kernel_data.integrator.volume_step_size; /* compute exact steps in advance for malloc */ - max_steps = max((int)ceilf(ray->t/step_size), 1); - if(max_steps > global_max_steps) { + if(ray->t > global_max_steps*step_size) { max_steps = global_max_steps; step_size = ray->t / (float)max_steps; } + else { + max_steps = max((int)ceilf(ray->t/step_size), 1); + } #ifdef __KERNEL_CPU__ /* NOTE: For the branched path tracing it's possible to have direct * and indirect light integration both having volume segments allocated. @@ -1216,6 +1218,7 @@ ccl_device void kernel_volume_stack_update_for_subsurface(KernelGlobals *kg, # else Intersection isect; int step = 0; + float3 Pend = ray->P + ray->D*ray->t; while(step < 2 * VOLUME_STACK_SIZE && scene_intersect_volume(kg, &volume_ray, @@ -1227,7 +1230,9 @@ ccl_device void kernel_volume_stack_update_for_subsurface(KernelGlobals *kg, /* Move ray forward. */ volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng); - volume_ray.t -= stack_sd->ray_length; + if(volume_ray.t != FLT_MAX) { + volume_ray.D = normalize_len(Pend - volume_ray.P, &volume_ray.t); + } ++step; } # endif diff --git a/intern/cycles/kernel/shaders/node_brick_texture.osl b/intern/cycles/kernel/shaders/node_brick_texture.osl index 35e01178ba8..d5e0a7d4c8c 100644 --- a/intern/cycles/kernel/shaders/node_brick_texture.osl +++ b/intern/cycles/kernel/shaders/node_brick_texture.osl @@ -59,10 +59,10 @@ float brick(point p, float mortar_size, float bias, shader node_brick_texture( int use_mapping = 0, matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - float Offset = 0.5, - int OffsetFrequency = 2, - float Squash = 1.0, - int SquashFrequency = 1, + float offset = 0.5, + int offset_frequency = 2, + float squash = 1.0, + int squash_frequency = 1, point Vector = P, color Color1 = 0.2, color Color2 = 0.8, @@ -84,7 +84,7 @@ shader node_brick_texture( color Col = Color1; Fac = brick(p * Scale, MortarSize, Bias, BrickWidth, RowHeight, - Offset, OffsetFrequency, Squash, SquashFrequency, tint); + offset, offset_frequency, squash, squash_frequency, tint); if (Fac != 1.0) { float facm = 1.0 - tint; diff --git a/intern/cycles/kernel/shaders/node_convert_from_color.osl b/intern/cycles/kernel/shaders/node_convert_from_color.osl index 44074317f42..e95a17f6fa1 100644 --- a/intern/cycles/kernel/shaders/node_convert_from_color.osl +++ b/intern/cycles/kernel/shaders/node_convert_from_color.osl @@ -17,18 +17,18 @@ #include "stdosl.h" shader node_convert_from_color( - color Color = 0.0, - output string String = "", - output float Val = 0.0, - output int ValInt = 0, - output vector Vector = vector(0.0, 0.0, 0.0), - output point Point = point(0.0, 0.0, 0.0), - output normal Normal = normal(0.0, 0.0, 0.0)) + color value_color = 0.0, + output string value_string = "", + output float value_float = 0.0, + output int value_int = 0, + output vector value_vector = vector(0.0, 0.0, 0.0), + output point value_point = point(0.0, 0.0, 0.0), + output normal value_normal = normal(0.0, 0.0, 0.0)) { - Val = Color[0] * 0.2126 + Color[1] * 0.7152 + Color[2] * 0.0722; - ValInt = (int)(Color[0] * 0.2126 + Color[1] * 0.7152 + Color[2] * 0.0722); - Vector = vector(Color[0], Color[1], Color[2]); - Point = point(Color[0], Color[1], Color[2]); - Normal = normal(Color[0], Color[1], Color[2]); + value_float = value_color[0] * 0.2126 + value_color[1] * 0.7152 + value_color[2] * 0.0722; + value_int = (int)(value_color[0] * 0.2126 + value_color[1] * 0.7152 + value_color[2] * 0.0722); + value_vector = vector(value_color[0], value_color[1], value_color[2]); + value_point = point(value_color[0], value_color[1], value_color[2]); + value_normal = normal(value_color[0], value_color[1], value_color[2]); } diff --git a/intern/cycles/kernel/shaders/node_convert_from_float.osl b/intern/cycles/kernel/shaders/node_convert_from_float.osl index fc5c79c4c64..a5c2e3b26ad 100644 --- a/intern/cycles/kernel/shaders/node_convert_from_float.osl +++ b/intern/cycles/kernel/shaders/node_convert_from_float.osl @@ -17,18 +17,18 @@ #include "stdosl.h" shader node_convert_from_float( - float Val = 0.0, - output string String = "", - output int ValInt = 0, - output color Color = 0.0, - output vector Vector = vector(0.0, 0.0, 0.0), - output point Point = point(0.0, 0.0, 0.0), - output normal Normal = normal(0.0, 0.0, 0.0)) + float value_float = 0.0, + output string value_string = "", + output int value_int = 0, + output color value_color = 0.0, + output vector value_vector = vector(0.0, 0.0, 0.0), + output point value_point = point(0.0, 0.0, 0.0), + output normal value_normal = normal(0.0, 0.0, 0.0)) { - ValInt = (int)Val; - Color = color(Val, Val, Val); - Vector = vector(Val, Val, Val); - Point = point(Val, Val, Val); - Normal = normal(Val, Val, Val); + value_int = (int)value_float; + value_color = color(value_float, value_float, value_float); + value_vector = vector(value_float, value_float, value_float); + value_point = point(value_float, value_float, value_float); + value_normal = normal(value_float, value_float, value_float); } diff --git a/intern/cycles/kernel/shaders/node_convert_from_int.osl b/intern/cycles/kernel/shaders/node_convert_from_int.osl index 3c3785ebc0d..0e6ae711210 100644 --- a/intern/cycles/kernel/shaders/node_convert_from_int.osl +++ b/intern/cycles/kernel/shaders/node_convert_from_int.osl @@ -17,19 +17,19 @@ #include "stdosl.h" shader node_convert_from_int( - int ValInt = 0, - output string String = "", - output float Val = 0.0, - output color Color = 0.0, - output vector Vector = vector(0.0, 0.0, 0.0), - output point Point = point(0.0, 0.0, 0.0), - output normal Normal = normal(0.0, 0.0, 0.0)) + int value_int = 0, + output string value_string = "", + output float value_float = 0.0, + output color value_color = 0.0, + output vector value_vector = vector(0.0, 0.0, 0.0), + output point value_point = point(0.0, 0.0, 0.0), + output normal value_normal = normal(0.0, 0.0, 0.0)) { - float f = (float)ValInt; - Val = f; - Color = color(f, f, f); - Vector = vector(f, f, f); - Point = point(f, f, f); - Normal = normal(f, f, f); + float f = (float)value_int; + value_float = f; + value_color = color(f, f, f); + value_vector = vector(f, f, f); + value_point = point(f, f, f); + value_normal = normal(f, f, f); } diff --git a/intern/cycles/kernel/shaders/node_convert_from_normal.osl b/intern/cycles/kernel/shaders/node_convert_from_normal.osl index 8ecc56ac8ce..7fffa7f6169 100644 --- a/intern/cycles/kernel/shaders/node_convert_from_normal.osl +++ b/intern/cycles/kernel/shaders/node_convert_from_normal.osl @@ -17,18 +17,18 @@ #include "stdosl.h" shader node_convert_from_normal( - normal Normal = normal(0.0, 0.0, 0.0), - output string String = "", - output float Val = 0.0, - output int ValInt = 0, - output vector Vector = vector(0.0, 0.0, 0.0), - output color Color = 0.0, - output point Point = point(0.0, 0.0, 0.0)) + normal value_normal = normal(0.0, 0.0, 0.0), + output string value_string = "", + output float value_float = 0.0, + output int value_int = 0, + output vector value_vector = vector(0.0, 0.0, 0.0), + output color value_color = 0.0, + output point value_point = point(0.0, 0.0, 0.0)) { - Val = (Normal[0] + Normal[1] + Normal[2]) * (1.0 / 3.0); - ValInt = (int)((Normal[0] + Normal[1] + Normal[2]) * (1.0 / 3.0)); - Vector = vector(Normal[0], Normal[1], Normal[2]); - Color = color(Normal[0], Normal[1], Normal[2]); - Point = point(Normal[0], Normal[1], Normal[2]); + value_float = (value_normal[0] + value_normal[1] + value_normal[2]) * (1.0 / 3.0); + value_int = (int)((value_normal[0] + value_normal[1] + value_normal[2]) * (1.0 / 3.0)); + value_vector = vector(value_normal[0], value_normal[1], value_normal[2]); + value_color = color(value_normal[0], value_normal[1], value_normal[2]); + value_point = point(value_normal[0], value_normal[1], value_normal[2]); } diff --git a/intern/cycles/kernel/shaders/node_convert_from_point.osl b/intern/cycles/kernel/shaders/node_convert_from_point.osl index e5913b7a1e4..9e4930296bb 100644 --- a/intern/cycles/kernel/shaders/node_convert_from_point.osl +++ b/intern/cycles/kernel/shaders/node_convert_from_point.osl @@ -17,18 +17,18 @@ #include "stdosl.h" shader node_convert_from_point( - point Point = point(0.0, 0.0, 0.0), - output string String = "", - output float Val = 0.0, - output int ValInt = 0, - output vector Vector = vector(0.0, 0.0, 0.0), - output color Color = 0.0, - output normal Normal = normal(0.0, 0.0, 0.0)) + point value_point = point(0.0, 0.0, 0.0), + output string value_string = "", + output float value_float = 0.0, + output int value_int = 0, + output vector value_vector = vector(0.0, 0.0, 0.0), + output color value_color = 0.0, + output normal value_normal = normal(0.0, 0.0, 0.0)) { - Val = (Point[0] + Point[1] + Point[2]) * (1.0 / 3.0); - ValInt = (int)((Normal[0] + Normal[1] + Normal[2]) * (1.0 / 3.0)); - Vector = vector(Point[0], Point[1], Point[2]); - Color = color(Point[0], Point[1], Point[2]); - Normal = normal(Point[0], Point[1], Point[2]); + value_float = (value_point[0] + value_point[1] + value_point[2]) * (1.0 / 3.0); + value_int = (int)((value_normal[0] + value_normal[1] + value_normal[2]) * (1.0 / 3.0)); + value_vector = vector(value_point[0], value_point[1], value_point[2]); + value_color = color(value_point[0], value_point[1], value_point[2]); + value_normal = normal(value_point[0], value_point[1], value_point[2]); } diff --git a/intern/cycles/kernel/shaders/node_convert_from_string.osl b/intern/cycles/kernel/shaders/node_convert_from_string.osl index 0466734277b..cbc6653eada 100644 --- a/intern/cycles/kernel/shaders/node_convert_from_string.osl +++ b/intern/cycles/kernel/shaders/node_convert_from_string.osl @@ -17,13 +17,13 @@ #include "stdosl.h" shader node_convert_from_string( - string String = "", - output color Color = color(0.0, 0.0, 0.0), - output float Val = 0.0, - output int ValInt = 0, - output vector Vector = vector(0.0, 0.0, 0.0), - output point Point = point(0.0, 0.0, 0.0), - output normal Normal = normal(0.0, 0.0, 0.0)) + string value_string = "", + output color value_color = color(0.0, 0.0, 0.0), + output float value_float = 0.0, + output int value_int = 0, + output vector value_vector = vector(0.0, 0.0, 0.0), + output point value_point = point(0.0, 0.0, 0.0), + output normal value_normal = normal(0.0, 0.0, 0.0)) { } diff --git a/intern/cycles/kernel/shaders/node_convert_from_vector.osl b/intern/cycles/kernel/shaders/node_convert_from_vector.osl index 79c5cb04550..8bdca469b90 100644 --- a/intern/cycles/kernel/shaders/node_convert_from_vector.osl +++ b/intern/cycles/kernel/shaders/node_convert_from_vector.osl @@ -17,18 +17,18 @@ #include "stdosl.h" shader node_convert_from_vector( - vector Vector = vector(0.0, 0.0, 0.0), - output string String = "", - output float Val = 0.0, - output int ValInt = 0, - output color Color = color(0.0, 0.0, 0.0), - output point Point = point(0.0, 0.0, 0.0), - output normal Normal = normal(0.0, 0.0, 0.0)) + vector value_vector = vector(0.0, 0.0, 0.0), + output string value_string = "", + output float value_float = 0.0, + output int value_int = 0, + output color value_color = color(0.0, 0.0, 0.0), + output point value_point = point(0.0, 0.0, 0.0), + output normal value_normal = normal(0.0, 0.0, 0.0)) { - Val = (Vector[0] + Vector[1] + Vector[2]) * (1.0 / 3.0); - ValInt = (int)((Normal[0] + Normal[1] + Normal[2]) * (1.0 / 3.0)); - Color = color(Vector[0], Vector[1], Vector[2]); - Point = point(Vector[0], Vector[1], Vector[2]); - Normal = normal(Vector[0], Vector[1], Vector[2]); + value_float = (value_vector[0] + value_vector[1] + value_vector[2]) * (1.0 / 3.0); + value_int = (int)((value_normal[0] + value_normal[1] + value_normal[2]) * (1.0 / 3.0)); + value_color = color(value_vector[0], value_vector[1], value_vector[2]); + value_point = point(value_vector[0], value_vector[1], value_vector[2]); + value_normal = normal(value_vector[0], value_vector[1], value_vector[2]); } diff --git a/intern/cycles/kernel/shaders/node_gradient_texture.osl b/intern/cycles/kernel/shaders/node_gradient_texture.osl index 52b49688ab3..69e2ee54bdf 100644 --- a/intern/cycles/kernel/shaders/node_gradient_texture.osl +++ b/intern/cycles/kernel/shaders/node_gradient_texture.osl @@ -63,7 +63,7 @@ float gradient(point p, string type) shader node_gradient_texture( int use_mapping = 0, matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - string Type = "Linear", + string type = "Linear", point Vector = P, output float Fac = 0.0, output color Color = 0.0) @@ -73,7 +73,7 @@ shader node_gradient_texture( if (use_mapping) p = transform(mapping, p); - Fac = gradient(p, Type); + Fac = gradient(p, type); Color = color(Fac, Fac, Fac); } diff --git a/intern/cycles/kernel/shaders/node_image_texture.osl b/intern/cycles/kernel/shaders/node_image_texture.osl index d3a347b70db..d09174ff5d3 100644 --- a/intern/cycles/kernel/shaders/node_image_texture.osl +++ b/intern/cycles/kernel/shaders/node_image_texture.osl @@ -62,9 +62,9 @@ color image_texture_lookup(string filename, int use_alpha, int is_float, string interpolation, - string wrap) + string extension) { - color rgb = (color)texture(filename, u, 1.0 - v, "wrap", wrap, "interp", interpolation, "alpha", Alpha); + color rgb = (color)texture(filename, u, 1.0 - v, "wrap", extension, "interp", interpolation, "alpha", Alpha); if (use_alpha) { rgb = color_unpremultiply(rgb, Alpha); diff --git a/intern/cycles/kernel/shaders/node_magic_texture.osl b/intern/cycles/kernel/shaders/node_magic_texture.osl index 55992e3494c..8d6af391e04 100644 --- a/intern/cycles/kernel/shaders/node_magic_texture.osl +++ b/intern/cycles/kernel/shaders/node_magic_texture.osl @@ -93,7 +93,7 @@ color magic(point p, int n, float distortion) shader node_magic_texture( int use_mapping = 0, matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - int Depth = 2, + int depth = 2, float Distortion = 5.0, float Scale = 5.0, point Vector = P, @@ -105,7 +105,7 @@ shader node_magic_texture( if (use_mapping) p = transform(mapping, p); - Color = magic(p * Scale, Depth, Distortion); + Color = magic(p * Scale, depth, Distortion); Fac = (Color[0] + Color[1] + Color[2]) * (1.0 / 3.0); } diff --git a/intern/cycles/kernel/shaders/node_math.osl b/intern/cycles/kernel/shaders/node_math.osl index 7eef97fd7e8..85eac0b97a6 100644 --- a/intern/cycles/kernel/shaders/node_math.osl +++ b/intern/cycles/kernel/shaders/node_math.osl @@ -50,7 +50,7 @@ float safe_log(float a, float b) shader node_math( string type = "Add", - int Clamp = 0, + int use_clamp = 0, float Value1 = 0.0, float Value2 = 0.0, output float Value = 0.0) @@ -96,7 +96,7 @@ shader node_math( else if (type == "Absolute") Value = fabs(Value1); - if (Clamp) + if (use_clamp) Value = clamp(Value, 0.0, 1.0); } diff --git a/intern/cycles/kernel/shaders/node_mix.osl b/intern/cycles/kernel/shaders/node_mix.osl index 9ef58e4cbba..4a66748ed6a 100644 --- a/intern/cycles/kernel/shaders/node_mix.osl +++ b/intern/cycles/kernel/shaders/node_mix.osl @@ -278,7 +278,7 @@ color node_mix_clamp(color col) shader node_mix( string type = "Mix", - int Clamp = 0, + int use_clamp = 0, float Fac = 0.5, color Color1 = 0.0, color Color2 = 0.0, @@ -323,7 +323,7 @@ shader node_mix( if (type == "Linear Light") Color = node_mix_linear(t, Color1, Color2); - if (Clamp) + if (use_clamp) Color = node_mix_clamp(Color); } diff --git a/intern/cycles/kernel/shaders/node_musgrave_texture.osl b/intern/cycles/kernel/shaders/node_musgrave_texture.osl index 4f95dec910a..2f9f62bcfe8 100644 --- a/intern/cycles/kernel/shaders/node_musgrave_texture.osl +++ b/intern/cycles/kernel/shaders/node_musgrave_texture.osl @@ -187,7 +187,7 @@ float noise_musgrave_ridged_multi_fractal(point p, float H, float lacunarity, shader node_musgrave_texture( int use_mapping = 0, matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - string Type = "fBM", + string type = "fBM", float Dimension = 2.0, float Lacunarity = 1.0, float Detail = 2.0, @@ -210,15 +210,15 @@ shader node_musgrave_texture( p = p * Scale; - if (Type == "Multifractal") + if (type == "Multifractal") Fac = intensity * noise_musgrave_multi_fractal(p, dimension, lacunarity, octaves); - else if (Type == "fBM") + else if (type == "fBM") Fac = intensity * noise_musgrave_fBm(p, dimension, lacunarity, octaves); - else if (Type == "Hybrid Multifractal") + else if (type == "Hybrid Multifractal") Fac = intensity * noise_musgrave_hybrid_multi_fractal(p, dimension, lacunarity, octaves, Offset, Gain); - else if (Type == "Ridged Multifractal") + else if (type == "Ridged Multifractal") Fac = intensity * noise_musgrave_ridged_multi_fractal(p, dimension, lacunarity, octaves, Offset, Gain); - else if (Type == "Hetero Terrain") + else if (type == "Hetero Terrain") Fac = intensity * noise_musgrave_hetero_terrain(p, dimension, lacunarity, octaves, Offset); Color = color(Fac, Fac, Fac); diff --git a/intern/cycles/kernel/shaders/node_normal.osl b/intern/cycles/kernel/shaders/node_normal.osl index 2d04978fc72..7307971eddd 100644 --- a/intern/cycles/kernel/shaders/node_normal.osl +++ b/intern/cycles/kernel/shaders/node_normal.osl @@ -17,12 +17,12 @@ #include "stdosl.h" shader node_normal( - normal Direction = normal(0.0, 0.0, 0.0), + normal direction = normal(0.0, 0.0, 0.0), normal NormalIn = normal(0.0, 0.0, 0.0), output normal NormalOut = normal(0.0, 0.0, 0.0), output float Dot = 1.0) { - NormalOut = normalize(Direction); + NormalOut = normalize(direction); Dot = dot(NormalOut, normalize(NormalIn)); } diff --git a/intern/cycles/kernel/shaders/node_rgb_ramp.osl b/intern/cycles/kernel/shaders/node_rgb_ramp.osl index 2ab6b6778b7..c0ae74d6b33 100644 --- a/intern/cycles/kernel/shaders/node_rgb_ramp.osl +++ b/intern/cycles/kernel/shaders/node_rgb_ramp.osl @@ -20,7 +20,7 @@ shader node_rgb_ramp( color ramp_color[] = {0.0}, float ramp_alpha[] = {0.0}, - int ramp_interpolate = 1, + int interpolate = 1, float Fac = 0.0, output color Color = 0.0, @@ -38,7 +38,7 @@ shader node_rgb_ramp( Color = ramp_color[i]; Alpha = ramp_alpha[i]; - if (ramp_interpolate && t > 0.0) { + if (interpolate && t > 0.0) { Color = (1.0 - t) * Color + t * ramp_color[i + 1]; Alpha = (1.0 - t) * Alpha + t * ramp_alpha[i + 1]; } diff --git a/intern/cycles/kernel/shaders/node_subsurface_scattering.osl b/intern/cycles/kernel/shaders/node_subsurface_scattering.osl index a67333c5d4e..1877c7e595f 100644 --- a/intern/cycles/kernel/shaders/node_subsurface_scattering.osl +++ b/intern/cycles/kernel/shaders/node_subsurface_scattering.osl @@ -22,13 +22,13 @@ shader node_subsurface_scattering( vector Radius = vector(0.1, 0.1, 0.1), float TextureBlur = 0.0, float Sharpness = 0.0, - string Falloff = "Cubic", + string falloff = "Cubic", normal Normal = N, output closure color BSSRDF = 0) { - if (Falloff == "Gaussian") + if (falloff == "Gaussian") BSSRDF = Color * bssrdf_gaussian(Normal, Scale * Radius, TextureBlur); - else if (Falloff == "Cubic") + else if (falloff == "Cubic") BSSRDF = Color * bssrdf_cubic(Normal, Scale * Radius, TextureBlur, Sharpness); else BSSRDF = Color * bssrdf_burley(Normal, Scale * Radius, TextureBlur, Color); diff --git a/intern/cycles/kernel/shaders/node_voronoi_texture.osl b/intern/cycles/kernel/shaders/node_voronoi_texture.osl index 29e143ae207..bacdd593c7c 100644 --- a/intern/cycles/kernel/shaders/node_voronoi_texture.osl +++ b/intern/cycles/kernel/shaders/node_voronoi_texture.osl @@ -22,7 +22,7 @@ shader node_voronoi_texture( int use_mapping = 0, matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - string Coloring = "Intensity", + string coloring = "Intensity", float Scale = 5.0, point Vector = P, output float Fac = 0.0, @@ -40,7 +40,7 @@ shader node_voronoi_texture( voronoi(p * Scale, 1.0, da, pa); /* Colored output */ - if (Coloring == "Intensity") { + if (coloring == "Intensity") { Fac = fabs(da[0]); Color = color(Fac); } diff --git a/intern/cycles/kernel/shaders/node_wave_texture.osl b/intern/cycles/kernel/shaders/node_wave_texture.osl index 59f61d3b46a..a07742faefc 100644 --- a/intern/cycles/kernel/shaders/node_wave_texture.osl +++ b/intern/cycles/kernel/shaders/node_wave_texture.osl @@ -48,8 +48,8 @@ float wave(point p, string type, string profile, float detail, float distortion, shader node_wave_texture( int use_mapping = 0, matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - string Type = "Bands", - string Profile = "Sine", + string type = "Bands", + string profile = "Sine", float Scale = 5.0, float Distortion = 0.0, float Detail = 2.0, @@ -63,7 +63,7 @@ shader node_wave_texture( if (use_mapping) p = transform(mapping, p); - Fac = wave(p * Scale, Type, Profile, Detail, Distortion, DetailScale); + Fac = wave(p * Scale, type, profile, Detail, Distortion, DetailScale); Color = Fac; } diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index be87e35326e..e57d22b1b13 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -343,6 +343,11 @@ typedef enum NodeNormalMapSpace { NODE_NORMAL_MAP_BLENDER_WORLD, } NodeNormalMapSpace; +typedef enum NodeImageColorSpace { + NODE_COLOR_SPACE_NONE = 0, + NODE_COLOR_SPACE_COLOR = 1, +} NodeImageColorSpace; + typedef enum NodeImageProjection { NODE_IMAGE_PROJ_FLAT = 0, NODE_IMAGE_PROJ_BOX = 1, @@ -350,6 +355,11 @@ typedef enum NodeImageProjection { NODE_IMAGE_PROJ_TUBE = 3, } NodeImageProjection; +typedef enum NodeEnvironmentProjection { + NODE_ENVIRONMENT_EQUIRECTANGULAR = 0, + NODE_ENVIRONMENT_MIRROR_BALL = 1, +} NodeEnvironmentProjection; + typedef enum NodeBumpOffset { NODE_BUMP_OFFSET_CENTER, NODE_BUMP_OFFSET_DX, diff --git a/intern/cycles/render/background.cpp b/intern/cycles/render/background.cpp index 6f8d1d1d461..20536b74e87 100644 --- a/intern/cycles/render/background.cpp +++ b/intern/cycles/render/background.cpp @@ -116,6 +116,11 @@ void Background::device_free(Device * /*device*/, DeviceScene * /*dscene*/) { } +bool Background::modified(const Background& background) +{ + return !Node::equals(background); +} + void Background::tag_update(Scene *scene) { scene->integrator->tag_update(scene); diff --git a/intern/cycles/render/background.h b/intern/cycles/render/background.h index 843655b00a1..8029c6a9e80 100644 --- a/intern/cycles/render/background.h +++ b/intern/cycles/render/background.h @@ -50,6 +50,7 @@ public: void device_update(Device *device, DeviceScene *dscene, Scene *scene); void device_free(Device *device, DeviceScene *dscene); + bool modified(const Background& background); void tag_update(Scene *scene); }; diff --git a/intern/cycles/render/bake.cpp b/intern/cycles/render/bake.cpp index 5bf5e5113ef..13310a61761 100644 --- a/intern/cycles/render/bake.cpp +++ b/intern/cycles/render/bake.cpp @@ -177,7 +177,7 @@ bool BakeManager::bake(Device *device, DeviceScene *dscene, Scene *scene, Progre device->mem_alloc(d_input, MEM_READ_ONLY); device->mem_copy_to(d_input); - device->mem_alloc(d_output, MEM_WRITE_ONLY); + device->mem_alloc(d_output, MEM_READ_WRITE); DeviceTask task(DeviceTask::SHADER); task.shader_input = d_input.device_pointer; diff --git a/intern/cycles/render/camera.cpp b/intern/cycles/render/camera.cpp index 250357d3c9f..2310798be2e 100644 --- a/intern/cycles/render/camera.cpp +++ b/intern/cycles/render/camera.cpp @@ -482,6 +482,11 @@ void Camera::device_free(Device * /*device*/, scene->lookup_tables->remove_table(&shutter_table_offset); } +bool Camera::modified(const Camera& cam) +{ + return !Node::equals(cam); +} + bool Camera::motion_modified(const Camera& cam) { return !((motion == cam.motion) && diff --git a/intern/cycles/render/camera.h b/intern/cycles/render/camera.h index 9069cc6d53d..141ef9cccef 100644 --- a/intern/cycles/render/camera.h +++ b/intern/cycles/render/camera.h @@ -181,6 +181,7 @@ public: void device_update_volume(Device *device, DeviceScene *dscene, Scene *scene); void device_free(Device *device, DeviceScene *dscene, Scene *scene); + bool modified(const Camera& cam); bool motion_modified(const Camera& cam); void tag_update(); diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp index 12dce6ad999..e10a938e1eb 100644 --- a/intern/cycles/render/film.cpp +++ b/intern/cycles/render/film.cpp @@ -465,7 +465,7 @@ void Film::device_free(Device * /*device*/, bool Film::modified(const Film& film) { - return Node::modified(film) || !Pass::equals(passes, film.passes); + return !Node::equals(film) || !Pass::equals(passes, film.passes); } void Film::tag_passes_update(Scene *scene, const array<Pass>& passes_) diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp index 62998b0166f..29c0eec9b97 100644 --- a/intern/cycles/render/graph.cpp +++ b/intern/cycles/render/graph.cpp @@ -470,8 +470,8 @@ void ShaderGraph::remove_proxy_nodes() disconnect(to); /* transfer the default input value to the target socket */ - to->value() = input->value(); - to->value_string() = input->value_string(); + to->set(input->value()); + to->set(input->value_string()); } } @@ -537,14 +537,13 @@ void ShaderGraph::constant_fold() } } /* Optimize current node. */ - float3 optimized_value = make_float3(0.0f, 0.0f, 0.0f); - if(node->constant_fold(this, output, &optimized_value)) { - /* Apply optimized value to connected sockets. */ + if(node->constant_fold(this, output, output->links[0])) { + /* Apply optimized value to other connected sockets and disconnect. */ vector<ShaderInput*> links(output->links); - foreach(ShaderInput *input, links) { - /* Assign value and disconnect the optimizedinput. */ - input->value() = optimized_value; - disconnect(input); + for(size_t i = 0; i < links.size(); i++) { + if(i > 0) + links[i]->set(links[0]->value()); + disconnect(links[i]); } } } @@ -935,7 +934,7 @@ void ShaderGraph::transform_multi_closure(ShaderNode *node, ShaderOutput *weight if(fin->link) connect(fin->link, fac_in); else - fac_in->value_float() = fin->value_float(); + fac_in->set(fin->value_float()); if(weight_out) connect(weight_out, weight_in); @@ -970,12 +969,12 @@ void ShaderGraph::transform_multi_closure(ShaderNode *node, ShaderOutput *weight if(weight_in->link) connect(weight_in->link, value1_in); else - value1_in->value() = weight_in->value(); + value1_in->set(weight_in->value_float()); if(weight_out) connect(weight_out, value2_in); else - value2_in->value_float() = 1.0f; + value2_in->set(1.0f); weight_out = math_node->output("Value"); if(weight_in->link) @@ -986,7 +985,7 @@ void ShaderGraph::transform_multi_closure(ShaderNode *node, ShaderOutput *weight if(weight_out) connect(weight_out, weight_in); else - weight_in->value_float() += 1.0f; + weight_in->set(weight_in->value_float() + 1.0f); } } diff --git a/intern/cycles/render/graph.h b/intern/cycles/render/graph.h index 760b19f447a..882e495df20 100644 --- a/intern/cycles/render/graph.h +++ b/intern/cycles/render/graph.h @@ -85,6 +85,11 @@ public: int flags() { return flags_; } SocketType::Type type() { return type_; } + void set(float f) { value_.x = f; } + void set(float3 f) { value_ = f; } + void set(int i) { value_.x = (float)i; } + void set(ustring s) { value_string_ = s; } + float3& value() { return value_; } float& value_float() { return value_.x; } ustring& value_string() { return value_string_; } @@ -148,7 +153,7 @@ public: /* ** Node optimization ** */ /* Check whether the node can be replaced with single constant. */ - virtual bool constant_fold(ShaderGraph * /*graph*/, ShaderOutput * /*socket*/, float3 * /*optimized_value*/) { return false; } + virtual bool constant_fold(ShaderGraph * /*graph*/, ShaderOutput * /*socket*/, ShaderInput * /*optimized*/) { return false; } /* Simplify settings used by artists to the ones which are simpler to * evaluate in the kernel but keep the final result unchanged. diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp index 41e2571dc24..2a10eb474a4 100644 --- a/intern/cycles/render/integrator.cpp +++ b/intern/cycles/render/integrator.cpp @@ -204,6 +204,11 @@ void Integrator::device_free(Device *device, DeviceScene *dscene) dscene->sobol_directions.clear(); } +bool Integrator::modified(const Integrator& integrator) +{ + return !Node::equals(integrator); +} + void Integrator::tag_update(Scene *scene) { foreach(Shader *shader, scene->shaders) { diff --git a/intern/cycles/render/integrator.h b/intern/cycles/render/integrator.h index a5cfb0c7863..39eaaf246d4 100644 --- a/intern/cycles/render/integrator.h +++ b/intern/cycles/render/integrator.h @@ -86,6 +86,7 @@ public: void device_update(Device *device, DeviceScene *dscene, Scene *scene); void device_free(Device *device, DeviceScene *dscene); + bool modified(const Integrator& integrator); void tag_update(Scene *scene); }; diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index 00b8e02f87f..e25155630bd 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -206,7 +206,7 @@ void Mesh::clear() int Mesh::split_vertex(int vertex) { /* copy vertex location and vertex attributes */ - add_vertex(verts[vertex]); + add_vertex_slow(verts[vertex]); foreach(Attribute& attr, attributes.attributes) { if(attr.element == ATTR_ELEMENT_VERTEX) { @@ -224,6 +224,11 @@ void Mesh::add_vertex(float3 P) verts.push_back_reserved(P); } +void Mesh::add_vertex_slow(float3 P) +{ + verts.push_back_slow(P); +} + void Mesh::add_triangle(int v0, int v1, int v2, int shader_, bool smooth_, bool forms_quad_) { triangles.push_back_reserved(v0); @@ -240,7 +245,7 @@ void Mesh::add_curve_key(float3 co, float radius) curve_radius.push_back_reserved(radius); } -void Mesh::add_curve(int first_key, int num_keys, int shader) +void Mesh::add_curve(int first_key, int shader) { curve_first_key.push_back_reserved(first_key); curve_shader.push_back_reserved(shader); @@ -1480,7 +1485,7 @@ bool Mesh::need_attribute(Scene * /*scene*/, ustring name) void Mesh::tessellate(DiagSplit *split) { - int num_faces = triangles.size(); + int num_faces = num_triangles(); add_face_normals(); add_vertex_normals(); diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h index 7556b7ccf1e..edad6d32f00 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -156,9 +156,10 @@ public: void reserve_curves(int numcurves, int numkeys); void clear(); void add_vertex(float3 P); + void add_vertex_slow(float3 P); void add_triangle(int v0, int v1, int v2, int shader, bool smooth, bool forms_quad = false); void add_curve_key(float3 loc, float radius); - void add_curve(int first_key, int num_keys, int shader); + void add_curve(int first_key, int shader); int split_vertex(int vertex); void compute_bounds(); diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 7f1fcd81c73..df0fee63113 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -272,8 +272,8 @@ ImageTextureNode::ImageTextureNode() use_alpha = true; filename = ""; builtin_data = NULL; - color_space = ustring("Color"); - projection = ustring("Flat"); + color_space = NODE_COLOR_SPACE_COLOR; + projection = NODE_IMAGE_PROJ_FLAT; interpolation = INTERPOLATION_LINEAR; extension = EXTENSION_REPEAT; projection_blend = 0.0f; @@ -341,10 +341,10 @@ void ImageTextureNode::compile(SVMCompiler& compiler) } if(slot != -1) { - int srgb = (is_linear || color_space != "Color")? 0: 1; + int srgb = (is_linear || color_space != NODE_COLOR_SPACE_COLOR)? 0: 1; int vector_offset = tex_mapping.compile_begin(compiler, vector_in); - if(projection != "Box") { + if(projection != NODE_IMAGE_PROJ_BOX) { compiler.add_node(NODE_TEX_IMAGE, slot, compiler.encode_uchar4( @@ -352,7 +352,7 @@ void ImageTextureNode::compile(SVMCompiler& compiler) compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(alpha_out), srgb), - projection_enum[projection]); + projection); } else { compiler.add_node(NODE_TEX_IMAGE_BOX, @@ -421,7 +421,7 @@ void ImageTextureNode::compile(OSLCompiler& compiler) */ compiler.parameter("filename", string_printf("@%d", slot).c_str()); } - if(is_linear || color_space != "Color") + if(is_linear || color_space != NODE_COLOR_SPACE_COLOR) compiler.parameter("color_space", "Linear"); else compiler.parameter("color_space", "sRGB"); @@ -433,14 +433,14 @@ void ImageTextureNode::compile(OSLCompiler& compiler) switch(extension) { case EXTENSION_EXTEND: - compiler.parameter("wrap", "clamp"); + compiler.parameter("extension", "clamp"); break; case EXTENSION_CLIP: - compiler.parameter("wrap", "black"); + compiler.parameter("extension", "black"); break; case EXTENSION_REPEAT: default: - compiler.parameter("wrap", "periodic"); + compiler.parameter("extension", "periodic"); break; } @@ -472,9 +472,9 @@ EnvironmentTextureNode::EnvironmentTextureNode() use_alpha = true; filename = ""; builtin_data = NULL; - color_space = ustring("Color"); + color_space = NODE_COLOR_SPACE_COLOR; interpolation = INTERPOLATION_LINEAR; - projection = ustring("Equirectangular"); + projection = NODE_ENVIRONMENT_EQUIRECTANGULAR; animated = false; add_input("Vector", SocketType::VECTOR, make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_POSITION); @@ -537,7 +537,7 @@ void EnvironmentTextureNode::compile(SVMCompiler& compiler) } if(slot != -1) { - int srgb = (is_linear || color_space != "Color")? 0: 1; + int srgb = (is_linear || color_space != NODE_COLOR_SPACE_COLOR)? 0: 1; int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_TEX_ENVIRONMENT, @@ -547,7 +547,7 @@ void EnvironmentTextureNode::compile(SVMCompiler& compiler) compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(alpha_out), srgb), - projection_enum[projection]); + projection); tex_mapping.compile_end(compiler, vector_in, vector_offset); } @@ -602,8 +602,8 @@ void EnvironmentTextureNode::compile(OSLCompiler& compiler) else { compiler.parameter("filename", string_printf("@%d", slot).c_str()); } - compiler.parameter("projection", projection); - if(is_linear || color_space != "Color") + compiler.parameter("projection", projection_enum[projection]); + if(is_linear || color_space != NODE_COLOR_SPACE_COLOR) compiler.parameter("color_space", "Linear"); else compiler.parameter("color_space", "sRGB"); @@ -753,7 +753,7 @@ NodeEnum SkyTextureNode::type_enum = sky_type_init(); SkyTextureNode::SkyTextureNode() : TextureNode("sky_texture") { - type = ustring("Hosek / Wilkie"); + type = NODE_SKY_NEW; sun_direction = make_float3(0.0f, 0.0f, 1.0f); turbidity = 2.2f; @@ -769,15 +769,15 @@ void SkyTextureNode::compile(SVMCompiler& compiler) ShaderOutput *color_out = output("Color"); SunSky sunsky; - if(type_enum[type] == NODE_SKY_OLD) + if(type == NODE_SKY_OLD) sky_texture_precompute_old(&sunsky, sun_direction, turbidity); - else if(type_enum[type] == NODE_SKY_NEW) + else if(type == NODE_SKY_NEW) sky_texture_precompute_new(&sunsky, sun_direction, turbidity, ground_albedo); else assert(false); int vector_offset = tex_mapping.compile_begin(compiler, vector_in); - int sky_model = type_enum[type]; + int sky_model = type; compiler.stack_assign(color_out); compiler.add_node(NODE_TEX_SKY, vector_offset, compiler.stack_assign(color_out), sky_model); @@ -799,14 +799,14 @@ void SkyTextureNode::compile(OSLCompiler& compiler) SunSky sunsky; - if(type_enum[type] == NODE_SKY_OLD) + if(type == NODE_SKY_OLD) sky_texture_precompute_old(&sunsky, sun_direction, turbidity); - else if(type_enum[type] == NODE_SKY_NEW) + else if(type == NODE_SKY_NEW) sky_texture_precompute_new(&sunsky, sun_direction, turbidity, ground_albedo); else assert(false); - compiler.parameter("sky_model", type); + compiler.parameter("sky_model", type_enum[type]); compiler.parameter("theta", sunsky.theta); compiler.parameter("phi", sunsky.phi); compiler.parameter_color("radiance", make_float3(sunsky.radiance_x, sunsky.radiance_y, sunsky.radiance_z)); @@ -838,7 +838,7 @@ NodeEnum GradientTextureNode::type_enum = gradient_type_init(); GradientTextureNode::GradientTextureNode() : TextureNode("gradient_texture") { - type = ustring("Linear"); + type = NODE_BLEND_LINEAR; add_input("Vector", SocketType::POINT, make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED); add_output("Color", SocketType::COLOR); @@ -855,7 +855,7 @@ void GradientTextureNode::compile(SVMCompiler& compiler) compiler.add_node(NODE_TEX_GRADIENT, compiler.encode_uchar4( - type_enum[type], + type, vector_offset, compiler.stack_assign_if_linked(fac_out), compiler.stack_assign_if_linked(color_out))); @@ -867,7 +867,7 @@ void GradientTextureNode::compile(OSLCompiler& compiler) { tex_mapping.compile(compiler); - compiler.parameter("Type", type); + compiler.parameter("type", type_enum[type]); compiler.add(this, "node_gradient_texture"); } @@ -937,7 +937,7 @@ NodeEnum VoronoiTextureNode::coloring_enum = voronoi_coloring_init(); VoronoiTextureNode::VoronoiTextureNode() : TextureNode("voronoi_texture") { - coloring = ustring("Intensity"); + coloring = NODE_VORONOI_INTENSITY; add_input("Scale", SocketType::FLOAT, 1.0f); add_input("Vector", SocketType::POINT, make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED); @@ -956,7 +956,7 @@ void VoronoiTextureNode::compile(SVMCompiler& compiler) int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_TEX_VORONOI, - coloring_enum[coloring], + coloring, compiler.encode_uchar4( compiler.stack_assign_if_linked(scale_in), vector_offset, @@ -971,7 +971,7 @@ void VoronoiTextureNode::compile(OSLCompiler& compiler) { tex_mapping.compile(compiler); - compiler.parameter("Coloring", coloring); + compiler.parameter("coloring", coloring_enum[coloring]); compiler.add(this, "node_voronoi_texture"); } @@ -995,7 +995,7 @@ NodeEnum MusgraveTextureNode::type_enum = musgrave_type_init(); MusgraveTextureNode::MusgraveTextureNode() : TextureNode("musgrave_texture") { - type = ustring("fBM"); + type = NODE_MUSGRAVE_FBM; add_input("Scale", SocketType::FLOAT, 1.0f); add_input("Detail", SocketType::FLOAT, 2.0f); @@ -1025,7 +1025,7 @@ void MusgraveTextureNode::compile(SVMCompiler& compiler) compiler.add_node(NODE_TEX_MUSGRAVE, compiler.encode_uchar4( - type_enum[type], + type, vector_offset, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(fac_out)), @@ -1051,7 +1051,7 @@ void MusgraveTextureNode::compile(OSLCompiler& compiler) { tex_mapping.compile(compiler); - compiler.parameter("Type", type); + compiler.parameter("type", type_enum[type]); compiler.add(this, "node_musgrave_texture"); } @@ -1084,8 +1084,8 @@ NodeEnum WaveTextureNode::profile_enum = wave_profile_init(); WaveTextureNode::WaveTextureNode() : TextureNode("wave_texture") { - type = ustring("Bands"); - profile = ustring("Sine"); + type = NODE_WAVE_BANDS; + profile = NODE_WAVE_PROFILE_SIN; add_input("Scale", SocketType::FLOAT, 1.0f); add_input("Distortion", SocketType::FLOAT, 0.0f); @@ -1111,7 +1111,7 @@ void WaveTextureNode::compile(SVMCompiler& compiler) compiler.add_node(NODE_TEX_WAVE, compiler.encode_uchar4( - type_enum[type], + type, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(fac_out), compiler.stack_assign_if_linked(dscale_in)), @@ -1120,7 +1120,7 @@ void WaveTextureNode::compile(SVMCompiler& compiler) compiler.stack_assign_if_linked(scale_in), compiler.stack_assign_if_linked(detail_in), compiler.stack_assign_if_linked(distortion_in)), - profile_enum[profile]); + profile); compiler.add_node( __float_as_int(scale_in->value_float()), @@ -1135,8 +1135,8 @@ void WaveTextureNode::compile(OSLCompiler& compiler) { tex_mapping.compile(compiler); - compiler.parameter("Type", type); - compiler.parameter("Profile", profile); + compiler.parameter("type", type_enum[type]); + compiler.parameter("profile", profile_enum[profile]); compiler.add(this, "node_wave_texture"); } @@ -1186,7 +1186,7 @@ void MagicTextureNode::compile(OSLCompiler& compiler) { tex_mapping.compile(compiler); - compiler.parameter("Depth", depth); + compiler.parameter("depth", depth); compiler.add(this, "node_magic_texture"); } @@ -1311,10 +1311,10 @@ void BrickTextureNode::compile(OSLCompiler& compiler) { tex_mapping.compile(compiler); - compiler.parameter("Offset", offset); - compiler.parameter("OffsetFrequency", offset_frequency); - compiler.parameter("Squash", squash); - compiler.parameter("SquashFrequency", squash_frequency); + compiler.parameter("offset", offset); + compiler.parameter("offset_frequency", offset_frequency); + compiler.parameter("squash", squash); + compiler.parameter("squash_frequency", squash_frequency); compiler.add(this, "node_brick_texture"); } @@ -1338,7 +1338,7 @@ PointDensityTextureNode::PointDensityTextureNode() image_manager = NULL; slot = -1; filename = ""; - space = ustring("Object"); + space = NODE_TEX_VOXEL_SPACE_OBJECT; builtin_data = NULL; interpolation = INTERPOLATION_LINEAR; @@ -1405,8 +1405,8 @@ void PointDensityTextureNode::compile(SVMCompiler& compiler) compiler.encode_uchar4(compiler.stack_assign(vector_in), compiler.stack_assign_if_linked(density_out), compiler.stack_assign_if_linked(color_out), - space_enum[space])); - if(space == "World") { + space)); + if(space == NODE_TEX_VOXEL_SPACE_WORLD) { compiler.add_node(tfm.x); compiler.add_node(tfm.y); compiler.add_node(tfm.z); @@ -1453,7 +1453,7 @@ void PointDensityTextureNode::compile(OSLCompiler& compiler) if(slot != -1) { compiler.parameter("filename", string_printf("@%d", slot).c_str()); } - if(space == "World") { + if(space == NODE_TEX_VOXEL_SPACE_WORLD) { compiler.parameter("mapping", transform_transpose(tfm)); compiler.parameter("use_mapping", 1); } @@ -1504,7 +1504,7 @@ void NormalNode::compile(SVMCompiler& compiler) void NormalNode::compile(OSLCompiler& compiler) { - compiler.parameter_normal("Direction", direction); + compiler.parameter_normal("direction", direction); compiler.add(this, "node_normal"); } @@ -1536,6 +1536,38 @@ void MappingNode::compile(OSLCompiler& compiler) compiler.add(this, "node_mapping"); } +/* RGBToBW */ + +RGBToBWNode::RGBToBWNode() +: ShaderNode("rgb_to_bw") +{ + add_input("Color", SocketType::COLOR); + add_output("Val", SocketType::FLOAT); +} + +bool RGBToBWNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized) +{ + if(inputs[0]->link == NULL) { + optimized->set(linear_rgb_to_gray(inputs[0]->value())); + return true; + } + + return false; +} + +void RGBToBWNode::compile(SVMCompiler& compiler) +{ + compiler.add_node(NODE_CONVERT, + NODE_CONVERT_CF, + compiler.stack_assign(inputs[0]), + compiler.stack_assign(outputs[0])); +} + +void RGBToBWNode::compile(OSLCompiler& compiler) +{ + compiler.add(this, "node_convert_from_color"); +} + /* Convert */ ConvertNode::ConvertNode(SocketType::Type from_, SocketType::Type to_, bool autoconvert) @@ -1552,47 +1584,45 @@ ConvertNode::ConvertNode(SocketType::Type from_, SocketType::Type to_, bool auto } if(from == SocketType::FLOAT) - add_input("Val", SocketType::FLOAT); + add_input("value_float", SocketType::FLOAT); else if(from == SocketType::INT) - add_input("ValInt", SocketType::INT); + add_input("value_int", SocketType::INT); else if(from == SocketType::COLOR) - add_input("Color", SocketType::COLOR); + add_input("value_color", SocketType::COLOR); else if(from == SocketType::VECTOR) - add_input("Vector", SocketType::VECTOR); + add_input("value_vector", SocketType::VECTOR); else if(from == SocketType::POINT) - add_input("Point", SocketType::POINT); + add_input("value_point", SocketType::POINT); else if(from == SocketType::NORMAL) - add_input("Normal", SocketType::NORMAL); + add_input("value_normal", SocketType::NORMAL); else if(from == SocketType::STRING) - add_input("String", SocketType::STRING); + add_input("value_string", SocketType::STRING); else if(from == SocketType::CLOSURE) - add_input("Closure", SocketType::CLOSURE); + add_input("value_closure", SocketType::CLOSURE); else assert(0); if(to == SocketType::FLOAT) - add_output("Val", SocketType::FLOAT); + add_output("value_float", SocketType::FLOAT); else if(to == SocketType::INT) - add_output("ValInt", SocketType::INT); + add_output("value_int", SocketType::INT); else if(to == SocketType::COLOR) - add_output("Color", SocketType::COLOR); + add_output("value_color", SocketType::COLOR); else if(to == SocketType::VECTOR) - add_output("Vector", SocketType::VECTOR); + add_output("value_vector", SocketType::VECTOR); else if(to == SocketType::POINT) - add_output("Point", SocketType::POINT); + add_output("value_point", SocketType::POINT); else if(to == SocketType::NORMAL) - add_output("Normal", SocketType::NORMAL); + add_output("value_normal", SocketType::NORMAL); else if(to == SocketType::STRING) - add_output("String", SocketType::STRING); + add_output("value_string", SocketType::STRING); else if(to == SocketType::CLOSURE) - add_output("Closure", SocketType::CLOSURE); + add_output("value_closure", SocketType::CLOSURE); else assert(0); } -bool ConvertNode::constant_fold(ShaderGraph * /*graph*/, - ShaderOutput * /*socket*/, - float3 *optimized_value) +bool ConvertNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized) { ShaderInput *in = inputs[0]; float3 value = in->value(); @@ -1601,42 +1631,24 @@ bool ConvertNode::constant_fold(ShaderGraph * /*graph*/, if(in->link == NULL) { if(from == SocketType::FLOAT) { - if(to == SocketType::INT) - /* float to int */ - return false; - else - /* float to float3 */ - *optimized_value = make_float3(value.x, value.x, value.x); - } - else if(from == SocketType::INT) { - if(to == SocketType::FLOAT) - /* int to float */ - return false; - else - /* int to vector/point/normal */ - return false; - } - else if(to == SocketType::FLOAT) { - if(from == SocketType::COLOR) - /* color to float */ - optimized_value->x = linear_rgb_to_gray(value); - else - /* vector/point/normal to float */ - optimized_value->x = average(value); - } - else if(to == SocketType::INT) { - if(from == SocketType::COLOR) - /* color to int */ - return false; - else - /* vector/point/normal to int */ - return false; + if(SocketType::is_float3(to)) { + optimized->set(make_float3(value.x, value.x, value.x)); + return true; + } } - else { - *optimized_value = value; + else if(SocketType::is_float3(from)) { + if(to == SocketType::FLOAT) { + if(from == SocketType::COLOR) + optimized->set(linear_rgb_to_gray(value)); + else + optimized->set(average(value)); + return true; + } + else if(SocketType::is_float3(to)) { + optimized->set(value); + return true; + } } - - return true; } return false; @@ -1793,7 +1805,7 @@ NodeEnum AnisotropicBsdfNode::distribution_enum = aniso_distribution_init(); AnisotropicBsdfNode::AnisotropicBsdfNode() { closure = CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID; - distribution = ustring("GGX"); + distribution = CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID; add_input("Tangent", SocketType::VECTOR, make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TANGENT); @@ -1816,14 +1828,14 @@ void AnisotropicBsdfNode::attributes(Shader *shader, AttributeRequestSet *attrib void AnisotropicBsdfNode::compile(SVMCompiler& compiler) { - closure = (ClosureType)distribution_enum[distribution]; + closure = distribution; BsdfNode::compile(compiler, input("Roughness"), input("Anisotropy"), input("Rotation")); } void AnisotropicBsdfNode::compile(OSLCompiler& compiler) { - compiler.parameter("distribution", distribution); + compiler.parameter("distribution", distribution_enum[distribution]); compiler.add(this, "node_anisotropic_bsdf"); } @@ -1846,15 +1858,15 @@ NodeEnum GlossyBsdfNode::distribution_enum = glossy_distribution_init(); GlossyBsdfNode::GlossyBsdfNode() { closure = CLOSURE_BSDF_MICROFACET_GGX_ID; - distribution = ustring("GGX"); - distribution_orig = ustring(""); + distribution = CLOSURE_BSDF_MICROFACET_GGX_ID; + distribution_orig = NBUILTIN_CLOSURES; add_input("Roughness", SocketType::FLOAT, 0.2f); } void GlossyBsdfNode::simplify_settings(Scene *scene) { - if(distribution_orig == "") { + if(distribution_orig == NBUILTIN_CLOSURES) { distribution_orig = distribution; } Integrator *integrator = scene->integrator; @@ -1864,14 +1876,14 @@ void GlossyBsdfNode::simplify_settings(Scene *scene) */ ShaderInput *roughness_input = input("Roughness"); if(!roughness_input->link && roughness_input->value_float() <= 1e-4f) { - distribution = ustring("Sharp"); + distribution = CLOSURE_BSDF_REFLECTION_ID; } } else { /* Rollback to original distribution when filter glossy is used. */ distribution = distribution_orig; } - closure = (ClosureType)distribution_enum[distribution]; + closure = distribution; } bool GlossyBsdfNode::has_integrator_dependency() @@ -1882,7 +1894,7 @@ bool GlossyBsdfNode::has_integrator_dependency() void GlossyBsdfNode::compile(SVMCompiler& compiler) { - closure = (ClosureType)distribution_enum[distribution]; + closure = distribution; if(closure == CLOSURE_BSDF_REFLECTION_ID) BsdfNode::compile(compiler, NULL, NULL); @@ -1892,7 +1904,7 @@ void GlossyBsdfNode::compile(SVMCompiler& compiler) void GlossyBsdfNode::compile(OSLCompiler& compiler) { - compiler.parameter("distribution", distribution); + compiler.parameter("distribution", distribution_enum[distribution]); compiler.add(this, "node_glossy_bsdf"); } @@ -1914,8 +1926,8 @@ NodeEnum GlassBsdfNode::distribution_enum = glass_distribution_init(); GlassBsdfNode::GlassBsdfNode() { closure = CLOSURE_BSDF_SHARP_GLASS_ID; - distribution = ustring("Sharp"); - distribution_orig = ustring(""); + distribution = CLOSURE_BSDF_SHARP_GLASS_ID; + distribution_orig = NBUILTIN_CLOSURES; add_input("Roughness", SocketType::FLOAT, 0.0f); add_input("IOR", SocketType::FLOAT, 0.3f); @@ -1923,7 +1935,7 @@ GlassBsdfNode::GlassBsdfNode() void GlassBsdfNode::simplify_settings(Scene *scene) { - if(distribution_orig == "") { + if(distribution_orig == NBUILTIN_CLOSURES) { distribution_orig = distribution; } Integrator *integrator = scene->integrator; @@ -1933,14 +1945,14 @@ void GlassBsdfNode::simplify_settings(Scene *scene) */ ShaderInput *roughness_input = input("Roughness"); if(!roughness_input->link && roughness_input->value_float() <= 1e-4f) { - distribution = ustring("Sharp"); + distribution = CLOSURE_BSDF_SHARP_GLASS_ID; } } else { /* Rollback to original distribution when filter glossy is used. */ distribution = distribution_orig; } - closure = (ClosureType)distribution_enum[distribution]; + closure = distribution; } bool GlassBsdfNode::has_integrator_dependency() @@ -1951,7 +1963,7 @@ bool GlassBsdfNode::has_integrator_dependency() void GlassBsdfNode::compile(SVMCompiler& compiler) { - closure = (ClosureType)distribution_enum[distribution]; + closure = distribution; if(closure == CLOSURE_BSDF_SHARP_GLASS_ID) BsdfNode::compile(compiler, NULL, input("IOR")); @@ -1961,7 +1973,7 @@ void GlassBsdfNode::compile(SVMCompiler& compiler) void GlassBsdfNode::compile(OSLCompiler& compiler) { - compiler.parameter("distribution", distribution); + compiler.parameter("distribution", distribution_enum[distribution]); compiler.add(this, "node_glass_bsdf"); } @@ -1983,8 +1995,8 @@ NodeEnum RefractionBsdfNode::distribution_enum = refraction_distribution_init(); RefractionBsdfNode::RefractionBsdfNode() { closure = CLOSURE_BSDF_REFRACTION_ID; - distribution = ustring("Sharp"); - distribution_orig = ustring(""); + distribution = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; + distribution_orig = NBUILTIN_CLOSURES; add_input("Roughness", SocketType::FLOAT, 0.0f); add_input("IOR", SocketType::FLOAT, 0.3f); @@ -1992,7 +2004,7 @@ RefractionBsdfNode::RefractionBsdfNode() void RefractionBsdfNode::simplify_settings(Scene *scene) { - if(distribution_orig == "") { + if(distribution_orig == NBUILTIN_CLOSURES) { distribution_orig = distribution; } Integrator *integrator = scene->integrator; @@ -2002,14 +2014,14 @@ void RefractionBsdfNode::simplify_settings(Scene *scene) */ ShaderInput *roughness_input = input("Roughness"); if(!roughness_input->link && roughness_input->value_float() <= 1e-4f) { - distribution = ustring("Sharp"); + distribution = CLOSURE_BSDF_REFRACTION_ID; } } else { /* Rollback to original distribution when filter glossy is used. */ distribution = distribution_orig; } - closure = (ClosureType)distribution_enum[distribution]; + closure = distribution; } bool RefractionBsdfNode::has_integrator_dependency() @@ -2020,7 +2032,7 @@ bool RefractionBsdfNode::has_integrator_dependency() void RefractionBsdfNode::compile(SVMCompiler& compiler) { - closure = (ClosureType)distribution_enum[distribution]; + closure = distribution; if(closure == CLOSURE_BSDF_REFRACTION_ID) BsdfNode::compile(compiler, NULL, input("IOR")); @@ -2030,7 +2042,7 @@ void RefractionBsdfNode::compile(SVMCompiler& compiler) void RefractionBsdfNode::compile(OSLCompiler& compiler) { - compiler.parameter("distribution", distribution); + compiler.parameter("distribution", distribution_enum[distribution]); compiler.add(this, "node_refraction_bsdf"); } @@ -2051,7 +2063,7 @@ NodeEnum ToonBsdfNode::component_enum = toon_component_init(); ToonBsdfNode::ToonBsdfNode() { closure = CLOSURE_BSDF_DIFFUSE_TOON_ID; - component = ustring("Diffuse"); + component = CLOSURE_BSDF_DIFFUSE_TOON_ID; add_input("Size", SocketType::FLOAT, 0.5f); add_input("Smooth", SocketType::FLOAT, 0.0f); @@ -2059,14 +2071,14 @@ ToonBsdfNode::ToonBsdfNode() void ToonBsdfNode::compile(SVMCompiler& compiler) { - closure = (ClosureType)component_enum[component]; + closure = component; BsdfNode::compile(compiler, input("Size"), input("Smooth")); } void ToonBsdfNode::compile(OSLCompiler& compiler) { - compiler.parameter("component", component); + compiler.parameter("component", component_enum[component]); compiler.add(this, "node_toon_bsdf"); } @@ -2161,7 +2173,7 @@ SubsurfaceScatteringNode::SubsurfaceScatteringNode() : BsdfNode(true) { name = "subsurface_scattering"; - closure = CLOSURE_BSSRDF_CUBIC_ID; + falloff = CLOSURE_BSSRDF_CUBIC_ID; add_input("Scale", SocketType::FLOAT, 0.01f); add_input("Radius", SocketType::VECTOR, make_float3(0.1f, 0.1f, 0.1f)); @@ -2171,12 +2183,13 @@ SubsurfaceScatteringNode::SubsurfaceScatteringNode() void SubsurfaceScatteringNode::compile(SVMCompiler& compiler) { + closure = falloff; BsdfNode::compile(compiler, input("Scale"), input("Texture Blur"), input("Radius"), input("Sharpness")); } void SubsurfaceScatteringNode::compile(OSLCompiler& compiler) { - compiler.parameter("Falloff", falloff_enum[closure]); + compiler.parameter("falloff", falloff_enum[closure]); compiler.add(this, "node_subsurface_scattering"); } @@ -2220,7 +2233,7 @@ void EmissionNode::compile(OSLCompiler& compiler) compiler.add(this, "node_emission"); } -bool EmissionNode::constant_fold(ShaderGraph * /*graph*/, ShaderOutput * /*socket*/, float3 * /*optimized_value*/) +bool EmissionNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *) { ShaderInput *color_in = input("Color"); ShaderInput *strength_in = input("Strength"); @@ -2262,7 +2275,7 @@ void BackgroundNode::compile(OSLCompiler& compiler) compiler.add(this, "node_background"); } -bool BackgroundNode::constant_fold(ShaderGraph * /*graph*/, ShaderOutput * /*socket*/, float3 * /*optimized_value*/) +bool BackgroundNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *) { ShaderInput *color_in = input("Color"); ShaderInput *strength_in = input("Strength"); @@ -2419,7 +2432,7 @@ NodeEnum HairBsdfNode::component_enum = hair_component_init(); HairBsdfNode::HairBsdfNode() { closure = CLOSURE_BSDF_HAIR_REFLECTION_ID; - component = ustring("Reflection"); + component = CLOSURE_BSDF_HAIR_REFLECTION_ID; add_input("Offset", SocketType::FLOAT); add_input("RoughnessU", SocketType::FLOAT); @@ -2429,14 +2442,14 @@ HairBsdfNode::HairBsdfNode() void HairBsdfNode::compile(SVMCompiler& compiler) { - closure = (ClosureType)component_enum[component]; + closure = component; BsdfNode::compile(compiler, input("RoughnessU"), input("RoughnessV"), input("Offset")); } void HairBsdfNode::compile(OSLCompiler& compiler) { - compiler.parameter("component", component); + compiler.parameter("component", component_enum[component]); compiler.add(this, "node_hair_bsdf"); } @@ -3113,10 +3126,9 @@ ValueNode::ValueNode() add_output("Value", SocketType::FLOAT); } -bool ValueNode::constant_fold(ShaderGraph * /*graph*/, ShaderOutput * /*socket*/, - float3 *optimized_value) +bool ValueNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized) { - *optimized_value = make_float3(value, value, value); + optimized->set(value); return true; } @@ -3143,10 +3155,9 @@ ColorNode::ColorNode() add_output("Color", SocketType::COLOR); } -bool ColorNode::constant_fold(ShaderGraph * /*graph*/, ShaderOutput * /*socket*/, - float3 *optimized_value) +bool ColorNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized) { - *optimized_value = value; + optimized->set(value); return true; } @@ -3212,7 +3223,7 @@ void MixClosureNode::compile(OSLCompiler& compiler) compiler.add(this, "node_mix_closure"); } -bool MixClosureNode::constant_fold(ShaderGraph *graph, ShaderOutput * /*socket*/, float3 * /*optimized_value*/) +bool MixClosureNode::constant_fold(ShaderGraph *graph, ShaderOutput *, ShaderInput *) { ShaderInput *fac_in = input("Fac"); ShaderInput *closure1_in = input("Closure1"); @@ -3306,7 +3317,7 @@ void InvertNode::compile(OSLCompiler& compiler) MixNode::MixNode() : ShaderNode("mix") { - type = ustring("Mix"); + type = NODE_MIX_BLEND; use_clamp = false; @@ -3355,7 +3366,7 @@ void MixNode::compile(SVMCompiler& compiler) compiler.stack_assign(fac_in), compiler.stack_assign(color1_in), compiler.stack_assign(color2_in)); - compiler.add_node(NODE_MIX, type_enum[type], compiler.stack_assign(color_out)); + compiler.add_node(NODE_MIX, type, compiler.stack_assign(color_out)); if(use_clamp) { compiler.add_node(NODE_MIX, 0, compiler.stack_assign(color_out)); @@ -3365,14 +3376,14 @@ void MixNode::compile(SVMCompiler& compiler) void MixNode::compile(OSLCompiler& compiler) { - compiler.parameter("type", type); - compiler.parameter("Clamp", use_clamp); + compiler.parameter("type", type_enum[type]); + compiler.parameter("use_clamp", use_clamp); compiler.add(this, "node_mix"); } -bool MixNode::constant_fold(ShaderGraph *graph, ShaderOutput * /*socket*/, float3 * optimized_value) +bool MixNode::constant_fold(ShaderGraph *graph, ShaderOutput *, ShaderInput *optimized) { - if(type != ustring("Mix")) { + if(type != NODE_MIX_BLEND) { return false; } @@ -3394,7 +3405,7 @@ bool MixNode::constant_fold(ShaderGraph *graph, ShaderOutput * /*socket*/, float if(color1_in->link) graph->relink(this, color_out, color1_in->link); else - *optimized_value = color1_in->value(); + optimized->set(color1_in->value()); return true; } /* factor 1.0 */ @@ -3402,7 +3413,7 @@ bool MixNode::constant_fold(ShaderGraph *graph, ShaderOutput * /*socket*/, float if(color2_in->link) graph->relink(this, color_out, color2_in->link); else - *optimized_value = color2_in->value(); + optimized->set(color2_in->value()); return true; } } @@ -3519,15 +3530,15 @@ GammaNode::GammaNode() add_output("Color", SocketType::COLOR); } -bool GammaNode::constant_fold(ShaderGraph * /*graph*/, ShaderOutput *socket, float3 *optimized_value) +bool GammaNode::constant_fold(ShaderGraph *, ShaderOutput *socket, ShaderInput *optimized) { ShaderInput *color_in = input("Color"); ShaderInput *gamma_in = input("Gamma"); if(socket == output("Color")) { if(color_in->link == NULL && gamma_in->link == NULL) { - *optimized_value = svm_math_gamma_color(color_in->value(), - gamma_in->value_float()); + optimized->set(svm_math_gamma_color(color_in->value(), + gamma_in->value_float())); return true; } } @@ -3979,13 +3990,13 @@ BlackbodyNode::BlackbodyNode() add_output("Color", SocketType::COLOR); } -bool BlackbodyNode::constant_fold(ShaderGraph * /*graph*/, ShaderOutput *socket, float3 *optimized_value) +bool BlackbodyNode::constant_fold(ShaderGraph *, ShaderOutput *socket, ShaderInput *optimized) { ShaderInput *temperature_in = input("Temperature"); if(socket == output("Color")) { if(temperature_in->link == NULL) { - *optimized_value = svm_math_blackbody_color(temperature_in->value_float()); + optimized->set(svm_math_blackbody_color(temperature_in->value_float())); return true; } } @@ -4047,7 +4058,7 @@ void OutputNode::compile(OSLCompiler& compiler) MathNode::MathNode() : ShaderNode("math") { - type = ustring("Add"); + type = NODE_MATH_ADD; use_clamp = false; @@ -4085,21 +4096,23 @@ static NodeEnum math_type_init() NodeEnum MathNode::type_enum = math_type_init(); -bool MathNode::constant_fold(ShaderGraph * /*graph*/, ShaderOutput *socket, float3 *optimized_value) +bool MathNode::constant_fold(ShaderGraph *, ShaderOutput *socket, ShaderInput *optimized) { ShaderInput *value1_in = input("Value1"); ShaderInput *value2_in = input("Value2"); if(socket == output("Value")) { if(value1_in->link == NULL && value2_in->link == NULL) { - optimized_value->x = svm_math((NodeMath)type_enum[type], - value1_in->value_float(), - value2_in->value_float()); + float value = svm_math(type, + value1_in->value_float(), + value2_in->value_float()); if(use_clamp) { - optimized_value->x = saturate(optimized_value->x); + value = saturate(value); } + optimized->set(value); + return true; } } @@ -4113,10 +4126,7 @@ void MathNode::compile(SVMCompiler& compiler) ShaderInput *value2_in = input("Value2"); ShaderOutput *value_out = output("Value"); - compiler.add_node(NODE_MATH, - type_enum[type], - compiler.stack_assign(value1_in), - compiler.stack_assign(value2_in)); + compiler.add_node(NODE_MATH, type, compiler.stack_assign(value1_in), compiler.stack_assign(value2_in)); compiler.add_node(NODE_MATH, compiler.stack_assign(value_out)); if(use_clamp) { @@ -4127,8 +4137,8 @@ void MathNode::compile(SVMCompiler& compiler) void MathNode::compile(OSLCompiler& compiler) { - compiler.parameter("type", type); - compiler.parameter("Clamp", use_clamp); + compiler.parameter("type", type_enum[type]); + compiler.parameter("use_clamp", use_clamp); compiler.add(this, "node_math"); } @@ -4137,7 +4147,7 @@ void MathNode::compile(OSLCompiler& compiler) VectorMathNode::VectorMathNode() : ShaderNode("vector_math") { - type = ustring("Add"); + type = NODE_VECTOR_MATH_ADD; add_input("Vector1", SocketType::VECTOR); add_input("Vector2", SocketType::VECTOR); @@ -4161,7 +4171,7 @@ static NodeEnum vector_math_type_init() NodeEnum VectorMathNode::type_enum = vector_math_type_init(); -bool VectorMathNode::constant_fold(ShaderGraph * /*graph*/, ShaderOutput *socket, float3 *optimized_value) +bool VectorMathNode::constant_fold(ShaderGraph *, ShaderOutput *socket, ShaderInput *optimized) { ShaderInput *vector1_in = input("Vector1"); ShaderInput *vector2_in = input("Vector2"); @@ -4172,16 +4182,16 @@ bool VectorMathNode::constant_fold(ShaderGraph * /*graph*/, ShaderOutput *socket if(vector1_in->link == NULL && vector2_in->link == NULL) { svm_vector_math(&value, &vector, - (NodeVectorMath)type_enum[type], + type, vector1_in->value(), vector2_in->value()); if(socket == output("Value")) { - optimized_value->x = value; + optimized->set(value); return true; } else if(socket == output("Vector")) { - *optimized_value = vector; + optimized->set(vector); return true; } } @@ -4197,7 +4207,7 @@ void VectorMathNode::compile(SVMCompiler& compiler) ShaderOutput *vector_out = output("Vector"); compiler.add_node(NODE_VECTOR_MATH, - type_enum[type], + type, compiler.stack_assign(vector1_in), compiler.stack_assign(vector2_in)); compiler.add_node(NODE_VECTOR_MATH, @@ -4207,7 +4217,7 @@ void VectorMathNode::compile(SVMCompiler& compiler) void VectorMathNode::compile(OSLCompiler& compiler) { - compiler.parameter("type", type); + compiler.parameter("type", type_enum[type]); compiler.add(this, "node_vector_math"); } @@ -4216,9 +4226,9 @@ void VectorMathNode::compile(OSLCompiler& compiler) VectorTransformNode::VectorTransformNode() : ShaderNode("vector_transform") { - type = ustring("Vector"); - convert_from = ustring("world"); - convert_to = ustring("object"); + type = NODE_VECTOR_TRANSFORM_TYPE_VECTOR; + convert_from = NODE_VECTOR_TRANSFORM_CONVERT_SPACE_WORLD; + convert_to = NODE_VECTOR_TRANSFORM_CONVERT_SPACE_OBJECT; add_input("Vector", SocketType::VECTOR); add_output("Vector", SocketType::VECTOR); @@ -4255,18 +4265,16 @@ void VectorTransformNode::compile(SVMCompiler& compiler) ShaderOutput *vector_out = output("Vector"); compiler.add_node(NODE_VECTOR_TRANSFORM, - compiler.encode_uchar4(type_enum[type], - convert_space_enum[convert_from], - convert_space_enum[convert_to]), + compiler.encode_uchar4(type, convert_from, convert_to), compiler.encode_uchar4(compiler.stack_assign(vector_in), compiler.stack_assign(vector_out))); } void VectorTransformNode::compile(OSLCompiler& compiler) { - compiler.parameter("type", type); - compiler.parameter("convert_from", convert_from); - compiler.parameter("convert_to", convert_to); + compiler.parameter("type", type_enum[type]); + compiler.parameter("convert_from", convert_space_enum[convert_from]); + compiler.parameter("convert_to", convert_space_enum[convert_to]); compiler.add(this, "node_vector_transform"); } @@ -4323,9 +4331,7 @@ void BumpNode::compile(OSLCompiler& compiler) compiler.add(this, "node_bump"); } -bool BumpNode::constant_fold(ShaderGraph *graph, - ShaderOutput * /*socket*/, - float3 * /*optimized_value*/) +bool BumpNode::constant_fold(ShaderGraph *graph, ShaderOutput *, ShaderInput *) { ShaderInput *height_in = input("Height"); ShaderInput *normal_in = input("Normal"); @@ -4477,7 +4483,7 @@ void RGBRampNode::compile(OSLCompiler& compiler) compiler.parameter_color_array("ramp_color", ramp); compiler.parameter_array("ramp_alpha", ramp_alpha.data(), ramp_alpha.size()); - compiler.parameter("ramp_interpolate", interpolate); + compiler.parameter("interpolate", interpolate); compiler.add(this, "node_rgb_ramp"); } @@ -4506,20 +4512,29 @@ void SetNormalNode::compile(OSLCompiler& compiler) compiler.add(this, "node_set_normal"); } -/* OSLScriptNode */ +/* OSLNode */ -OSLScriptNode::OSLScriptNode() -: ShaderNode("osl_script") +OSLNode::OSLNode() +: ShaderNode("osl_shader") { special_type = SHADER_SPECIAL_TYPE_SCRIPT; } -void OSLScriptNode::compile(SVMCompiler& /*compiler*/) +OSLNode::~OSLNode() +{ +} + +OSLNode* OSLNode::create(size_t) +{ + return new OSLNode(); +} + +void OSLNode::compile(SVMCompiler&) { /* doesn't work for SVM, obviously ... */ } -void OSLScriptNode::compile(OSLCompiler& compiler) +void OSLNode::compile(OSLCompiler& compiler) { if(!filepath.empty()) compiler.add(this, filepath.c_str(), true); @@ -4547,7 +4562,7 @@ NodeEnum NormalMapNode::space_enum = normal_map_space_init(); NormalMapNode::NormalMapNode() : ShaderNode("normal_map") { - space = ustring("Tangent"); + space = NODE_NORMAL_MAP_TANGENT; attribute = ustring(""); add_input("NormalIn", SocketType::NORMAL, make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL | SocketType::OSL_INTERNAL); @@ -4559,7 +4574,7 @@ NormalMapNode::NormalMapNode() void NormalMapNode::attributes(Shader *shader, AttributeRequestSet *attributes) { - if(shader->has_surface && space == ustring("Tangent")) { + if(shader->has_surface && space == NODE_NORMAL_MAP_TANGENT) { if(attribute == ustring("")) { attributes->add(ATTR_STD_UV_TANGENT); attributes->add(ATTR_STD_UV_TANGENT_SIGN); @@ -4582,7 +4597,7 @@ void NormalMapNode::compile(SVMCompiler& compiler) ShaderOutput *normal_out = output("Normal"); int attr = 0, attr_sign = 0; - if(space == ustring("Tangent")) { + if(space == NODE_NORMAL_MAP_TANGENT) { if(attribute == ustring("")) { attr = compiler.attribute(ATTR_STD_UV_TANGENT); attr_sign = compiler.attribute(ATTR_STD_UV_TANGENT_SIGN); @@ -4598,13 +4613,13 @@ void NormalMapNode::compile(SVMCompiler& compiler) compiler.stack_assign(color_in), compiler.stack_assign(strength_in), compiler.stack_assign(normal_out), - space_enum[space]), + space), attr, attr_sign); } void NormalMapNode::compile(OSLCompiler& compiler) { - if(space == ustring("Tangent")) { + if(space == NODE_NORMAL_MAP_TANGENT) { if(attribute == ustring("")) { compiler.parameter("attr_name", ustring("geom:tangent")); compiler.parameter("attr_sign_name", ustring("geom:tangent_sign")); @@ -4615,7 +4630,7 @@ void NormalMapNode::compile(OSLCompiler& compiler) } } - compiler.parameter("space", space); + compiler.parameter("space", space_enum[space]); compiler.add(this, "node_normal_map"); } @@ -4649,8 +4664,8 @@ NodeEnum TangentNode::axis_enum = tangent_axis_init(); TangentNode::TangentNode() : ShaderNode("tangent") { - direction_type = ustring("Radial"); - axis = ustring("X"); + direction_type = NODE_TANGENT_RADIAL; + axis = NODE_TANGENT_AXIS_X; attribute = ustring(""); add_input("NormalIn", SocketType::NORMAL, make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL | SocketType::OSL_INTERNAL); @@ -4660,7 +4675,7 @@ TangentNode::TangentNode() void TangentNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if(shader->has_surface) { - if(direction_type == ustring("UV Map")) { + if(direction_type == NODE_TANGENT_UVMAP) { if(attribute == ustring("")) attributes->add(ATTR_STD_UV_TANGENT); else @@ -4678,7 +4693,7 @@ void TangentNode::compile(SVMCompiler& compiler) ShaderOutput *tangent_out = output("Tangent"); int attr; - if(direction_type == ustring("UV Map")) { + if(direction_type == NODE_TANGENT_UVMAP) { if(attribute == ustring("")) attr = compiler.attribute(ATTR_STD_UV_TANGENT); else @@ -4690,21 +4705,21 @@ void TangentNode::compile(SVMCompiler& compiler) compiler.add_node(NODE_TANGENT, compiler.encode_uchar4( compiler.stack_assign(tangent_out), - direction_type_enum[direction_type], - axis_enum[axis]), attr); + direction_type, + axis), attr); } void TangentNode::compile(OSLCompiler& compiler) { - if(direction_type == ustring("UV Map")) { + if(direction_type == NODE_TANGENT_UVMAP) { if(attribute == ustring("")) compiler.parameter("attr_name", ustring("geom:tangent")); else compiler.parameter("attr_name", ustring((string(attribute.c_str()) + ".tangent").c_str())); } - compiler.parameter("direction_type", direction_type); - compiler.parameter("axis", axis); + compiler.parameter("direction_type", direction_type_enum[direction_type]); + compiler.parameter("axis", axis_enum[axis]); compiler.add(this, "node_tangent"); } diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 7c06efc8e06..8b17e455f7a 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -109,8 +109,8 @@ public: bool use_alpha; string filename; void *builtin_data; - ustring color_space; - ustring projection; + NodeImageColorSpace color_space; + NodeImageProjection projection; InterpolationType interpolation; ExtensionType extension; float projection_blend; @@ -148,8 +148,8 @@ public: bool use_alpha; string filename; void *builtin_data; - ustring color_space; - ustring projection; + NodeImageColorSpace color_space; + NodeEnvironmentProjection projection; InterpolationType interpolation; bool animated; @@ -175,11 +175,11 @@ public: virtual int get_group() { return NODE_GROUP_LEVEL_2; } + NodeSkyType type; float3 sun_direction; float turbidity; float ground_albedo; - ustring type; static NodeEnum type_enum; virtual bool equals(const ShaderNode *other) { @@ -206,7 +206,7 @@ public: virtual int get_group() { return NODE_GROUP_LEVEL_2; } - ustring type; + NodeGradientType type; static NodeEnum type_enum; virtual bool equals(const ShaderNode *other) { @@ -227,8 +227,7 @@ public: virtual int get_group() { return NODE_GROUP_LEVEL_2; } - ustring coloring; - + NodeVoronoiColoring coloring; static NodeEnum coloring_enum; virtual bool equals(const ShaderNode *other) { @@ -244,8 +243,7 @@ public: virtual int get_group() { return NODE_GROUP_LEVEL_2; } - ustring type; - + NodeMusgraveType type; static NodeEnum type_enum; virtual bool equals(const ShaderNode *other) { @@ -261,8 +259,8 @@ public: virtual int get_group() { return NODE_GROUP_LEVEL_2; } - ustring type; - ustring profile; + NodeWaveType type; + NodeWaveProfile profile; static NodeEnum type_enum; static NodeEnum profile_enum; @@ -329,7 +327,7 @@ public: ImageManager *image_manager; int slot; string filename; - ustring space; + NodeTexVoxelSpace space; void *builtin_data; InterpolationType interpolation; @@ -362,12 +360,19 @@ public: } }; +class RGBToBWNode : public ShaderNode { +public: + SHADER_NODE_CLASS(RGBToBWNode) + + bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, ShaderInput *optimized); +}; + class ConvertNode : public ShaderNode { public: ConvertNode(SocketType::Type from, SocketType::Type to, bool autoconvert = false); SHADER_NODE_BASE_CLASS(ConvertNode) - bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, float3 *optimized_value); + bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, ShaderInput *optimized); SocketType::Type from, to; @@ -403,7 +408,7 @@ class AnisotropicBsdfNode : public BsdfNode { public: SHADER_NODE_CLASS(AnisotropicBsdfNode) - ustring distribution; + ClosureType distribution; static NodeEnum distribution_enum; void attributes(Shader *shader, AttributeRequestSet *attributes); @@ -438,7 +443,7 @@ public: void simplify_settings(Scene *scene); bool has_integrator_dependency(); - ustring distribution, distribution_orig; + ClosureType distribution, distribution_orig; static NodeEnum distribution_enum; }; @@ -449,7 +454,7 @@ public: void simplify_settings(Scene *scene); bool has_integrator_dependency(); - ustring distribution, distribution_orig; + ClosureType distribution, distribution_orig; static NodeEnum distribution_enum; }; @@ -460,7 +465,7 @@ public: void simplify_settings(Scene *scene); bool has_integrator_dependency(); - ustring distribution, distribution_orig; + ClosureType distribution, distribution_orig; static NodeEnum distribution_enum; }; @@ -468,7 +473,7 @@ class ToonBsdfNode : public BsdfNode { public: SHADER_NODE_CLASS(ToonBsdfNode) - ustring component; + ClosureType component; static NodeEnum component_enum; }; @@ -478,13 +483,14 @@ public: bool has_surface_bssrdf() { return true; } bool has_bssrdf_bump(); + ClosureType falloff; static NodeEnum falloff_enum; }; class EmissionNode : public ShaderNode { public: SHADER_NODE_CLASS(EmissionNode) - bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, float3 *optimized_value); + bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, ShaderInput *optimized); virtual ClosureType get_closure_type() { return CLOSURE_EMISSION_ID; } bool has_surface_emission() { return true; } @@ -493,7 +499,7 @@ public: class BackgroundNode : public ShaderNode { public: SHADER_NODE_CLASS(BackgroundNode) - bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, float3 *optimized_value); + bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, ShaderInput *optimized); virtual ClosureType get_closure_type() { return CLOSURE_BACKGROUND_ID; } }; @@ -547,7 +553,7 @@ class HairBsdfNode : public BsdfNode { public: SHADER_NODE_CLASS(HairBsdfNode) - ustring component; + ClosureType component; static NodeEnum component_enum; }; @@ -639,7 +645,7 @@ class ValueNode : public ShaderNode { public: SHADER_NODE_CLASS(ValueNode) - bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, float3 *optimized_value); + bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, ShaderInput *optimized); float value; @@ -654,7 +660,7 @@ class ColorNode : public ShaderNode { public: SHADER_NODE_CLASS(ColorNode) - bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, float3 *optimized_value); + bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, ShaderInput *optimized); float3 value; @@ -673,7 +679,7 @@ public: class MixClosureNode : public ShaderNode { public: SHADER_NODE_CLASS(MixClosureNode) - bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, float3 *optimized_value); + bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, ShaderInput *optimized); }; class MixClosureWeightNode : public ShaderNode { @@ -691,13 +697,13 @@ public: class MixNode : public ShaderNode { public: SHADER_NODE_CLASS(MixNode) - bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, float3 *optimized_value); + bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, ShaderInput *optimized); virtual int get_group() { return NODE_GROUP_LEVEL_3; } bool use_clamp; - ustring type; + NodeMix type; static NodeEnum type_enum; virtual bool equals(const ShaderNode *other) @@ -734,7 +740,7 @@ class GammaNode : public ShaderNode { public: SHADER_NODE_CLASS(GammaNode) - bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, float3 *optimized_value); + bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, ShaderInput *optimized); virtual int get_group() { return NODE_GROUP_LEVEL_1; } }; @@ -825,7 +831,7 @@ public: class BlackbodyNode : public ShaderNode { public: SHADER_NODE_CLASS(BlackbodyNode) - bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, float3 *optimized_value); + bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, ShaderInput *optimized); virtual int get_group() { return NODE_GROUP_LEVEL_3; } }; @@ -834,11 +840,11 @@ class MathNode : public ShaderNode { public: SHADER_NODE_CLASS(MathNode) virtual int get_group() { return NODE_GROUP_LEVEL_1; } - bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, float3 *optimized_value); + bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, ShaderInput *optimized); bool use_clamp; - ustring type; + NodeMath type; static NodeEnum type_enum; virtual bool equals(const ShaderNode *other) @@ -869,14 +875,14 @@ class VectorMathNode : public ShaderNode { public: SHADER_NODE_CLASS(VectorMathNode) virtual int get_group() { return NODE_GROUP_LEVEL_1; } - bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, float3 *optimized_value); + bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, ShaderInput *optimized); - ustring type; + NodeVectorMath type; static NodeEnum type_enum; virtual bool equals(const ShaderNode *other) { - const MathNode *math_node = (const MathNode*)other; + const VectorMathNode *math_node = (const VectorMathNode*)other; return ShaderNode::equals(other) && type == math_node->type; } @@ -888,9 +894,9 @@ public: virtual int get_group() { return NODE_GROUP_LEVEL_3; } - ustring type; - ustring convert_from; - ustring convert_to; + NodeVectorTransformType type; + NodeVectorTransformConvertSpace convert_from; + NodeVectorTransformConvertSpace convert_to; static NodeEnum type_enum; static NodeEnum convert_space_enum; @@ -907,7 +913,7 @@ public: class BumpNode : public ShaderNode { public: SHADER_NODE_CLASS(BumpNode) - bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, float3 *optimized_value); + bool constant_fold(ShaderGraph *graph, ShaderOutput *socket, ShaderInput *optimized); bool has_spatial_varying() { return true; } virtual int get_feature() { return NODE_FEATURE_BUMP; @@ -959,17 +965,22 @@ public: SHADER_NODE_CLASS(SetNormalNode) }; -class OSLScriptNode : public ShaderNode { +class OSLNode : public ShaderNode { public: - SHADER_NODE_CLASS(OSLScriptNode) + static OSLNode *create(size_t num_inputs); + ~OSLNode(); + + SHADER_NODE_BASE_CLASS(OSLNode) /* ideally we could beter detect this, but we can't query this now */ bool has_spatial_varying() { return true; } + virtual bool equals(const ShaderNode * /*other*/) { return false; } string filepath; string bytecode_hash; - virtual bool equals(const ShaderNode * /*other*/) { return false; } +private: + OSLNode(); }; class NormalMapNode : public ShaderNode { @@ -979,7 +990,7 @@ public: bool has_spatial_varying() { return true; } virtual int get_group() { return NODE_GROUP_LEVEL_3; } - ustring space; + NodeNormalMapSpace space; static NodeEnum space_enum; ustring attribute; @@ -1000,10 +1011,9 @@ public: bool has_spatial_varying() { return true; } virtual int get_group() { return NODE_GROUP_LEVEL_3; } - ustring direction_type; + NodeTangentDirectionType direction_type; + NodeTangentAxis axis; static NodeEnum direction_type_enum; - - ustring axis; static NodeEnum axis_enum; ustring attribute; diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index 9ee1a9ef7a6..644e581bf4b 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -225,6 +225,16 @@ vector<float> Object::motion_times() return times; } +bool Object::is_traceable() +{ + /* Mesh itself can be empty,can skip all such objects. */ + if (bounds.size() == make_float3(0.0f, 0.0f, 0.0f)) { + return false; + } + /* TODO(sergey): Check for mesh vertices/curves. visibility flags. */ + return true; +} + /* Object Manager */ ObjectManager::ObjectManager() diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h index 57614c95580..7ab73f3c91a 100644 --- a/intern/cycles/render/object.h +++ b/intern/cycles/render/object.h @@ -68,6 +68,11 @@ public: void apply_transform(bool apply_to_motion); vector<float> motion_times(); + + /* Check whether object is traceable and it worth adding it to + * kernel scene. + */ + bool is_traceable(); }; /* Object Manager */ diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index c1112ce44f6..1cfe3fb38e2 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -394,16 +394,143 @@ const char *OSLShaderManager::shader_load_bytecode(const string& hash, const str { ss->LoadMemoryCompiledShader(hash.c_str(), bytecode.c_str()); - /* this is a bit weak, but works */ OSLShaderInfo info; + + if(!info.query.open_bytecode(bytecode)) { + fprintf(stderr, "OSL query error: %s\n", info.query.geterror().c_str()); + } + + /* this is a bit weak, but works */ info.has_surface_emission = (bytecode.find("\"emission\"") != string::npos); info.has_surface_transparent = (bytecode.find("\"transparent\"") != string::npos); info.has_surface_bssrdf = (bytecode.find("\"bssrdf\"") != string::npos); + loaded_shaders[hash] = info; return loaded_shaders.find(hash)->first.c_str(); } +OSLNode *OSLShaderManager::osl_node(const std::string& filepath, + const std::string& bytecode_hash, + const std::string& bytecode) +{ + /* create query */ + const char *hash; + + if(!filepath.empty()) { + hash = shader_load_filepath(filepath); + } + else { + hash = shader_test_loaded(bytecode_hash); + if(!hash) + hash = shader_load_bytecode(bytecode_hash, bytecode); + } + + if(!hash) { + return NULL; + } + + OSLShaderInfo *info = shader_loaded_info(hash); + + /* count number of inputs */ + size_t num_inputs = 0; + + for(int i = 0; i < info->query.nparams(); i++) { + const OSL::OSLQuery::Parameter *param = info->query.getparam(i); + + /* skip unsupported types */ + if(param->varlenarray || param->isstruct || param->type.arraylen > 1) + continue; + + if(!param->isoutput) + num_inputs++; + } + + /* create node */ + OSLNode *node = OSLNode::create(num_inputs); + + /* add new sockets from parameters */ + set<void*> used_sockets; + + for(int i = 0; i < info->query.nparams(); i++) { + const OSL::OSLQuery::Parameter *param = info->query.getparam(i); + + /* skip unsupported types */ + if(param->varlenarray || param->isstruct || param->type.arraylen > 1) + continue; + + SocketType::Type socket_type; + + if(param->isclosure) { + socket_type = SocketType::CLOSURE; + } + else if(param->type.vecsemantics != TypeDesc::NOSEMANTICS) { + if(param->type.vecsemantics == TypeDesc::COLOR) + socket_type = SocketType::COLOR; + else if(param->type.vecsemantics == TypeDesc::POINT) + socket_type = SocketType::POINT; + else if(param->type.vecsemantics == TypeDesc::VECTOR) + socket_type = SocketType::VECTOR; + else if(param->type.vecsemantics == TypeDesc::NORMAL) + socket_type = SocketType::NORMAL; + else + continue; + + if(!param->isoutput && param->validdefault) { + node->add_input(param->name.c_str(), socket_type, make_float3(param->fdefault[0], param->fdefault[1], param->fdefault[2])); + continue; + } + } + else if(param->type.aggregate == TypeDesc::SCALAR) { + if(param->type.basetype == TypeDesc::INT) { + socket_type = SocketType::INT; + + if(!param->isoutput && param->validdefault) { + node->add_input(param->name.c_str(), socket_type, (float)param->idefault[0]); + continue; + } + } + else if(param->type.basetype == TypeDesc::FLOAT) { + socket_type = SocketType::FLOAT; + + if(!param->isoutput && param->validdefault) { + node->add_input(param->name.c_str(), socket_type, param->fdefault[0]); + continue; + } + } + else if(param->type.basetype == TypeDesc::STRING) { + socket_type = SocketType::STRING; + + if(!param->isoutput && param->validdefault) { + node->add_input(param->name.c_str(), socket_type); + continue; + } + } + else + continue; + } + else + continue; + + if(param->isoutput) { + node->add_output(param->name.c_str(), socket_type); + } + else { + node->add_input(param->name.c_str(), socket_type); + } + } + + /* set bytcode hash or filepath */ + if(!bytecode_hash.empty()) { + node->bytecode_hash = bytecode_hash; + } + else { + node->filepath = filepath; + } + + return node; +} + /* Graph Compiler */ OSLCompiler::OSLCompiler(void *manager_, void *shadingsys_, ImageManager *image_manager_) diff --git a/intern/cycles/render/osl.h b/intern/cycles/render/osl.h index 110897ff300..13b9d6307f9 100644 --- a/intern/cycles/render/osl.h +++ b/intern/cycles/render/osl.h @@ -22,6 +22,7 @@ #include "util_thread.h" #include "graph.h" +#include "nodes.h" #include "shader.h" #ifdef WITH_OSL @@ -54,6 +55,7 @@ struct OSLShaderInfo { has_surface_bssrdf(false) {} + OSL::OSLQuery query; bool has_surface_emission; bool has_surface_transparent; bool has_surface_bssrdf; @@ -83,6 +85,11 @@ public: const char *shader_load_filepath(string filepath); OSLShaderInfo *shader_loaded_info(const string& hash); + /* create OSL node using OSLQuery */ + OSLNode *osl_node(const std::string& filepath, + const std::string& bytecode_hash = "", + const std::string& bytecode = ""); + protected: void texture_system_init(); void texture_system_free(); diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index b0052c30af4..e8367e1eb36 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -242,9 +242,14 @@ void Scene::device_update(Device *device_, Progress& progress) } if(print_stats) { + size_t mem_used = util_guarded_get_mem_used(); + size_t mem_peak = util_guarded_get_mem_peak(); + VLOG(1) << "System memory statistics after full device sync:\n" - << " Usage: " << util_guarded_get_mem_used() << "\n" - << " Peak: " << util_guarded_get_mem_peak(); + << " Usage: " << string_human_readable_number(mem_used) + << " (" << string_human_readable_size(mem_used) << ")\n" + << " Peak: " << string_human_readable_number(mem_peak) + << " (" << string_human_readable_size(mem_peak) << ")"; } } diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index f03e8e54bcc..708eeef3b50 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -456,7 +456,7 @@ void ShaderManager::add_default(Scene *scene) ShaderGraph *graph = new ShaderGraph(); closure = graph->add(new DiffuseBsdfNode()); - closure->input("Color")->value() = make_float3(0.8f, 0.8f, 0.8f); + closure->input("Color")->set(make_float3(0.8f, 0.8f, 0.8f)); out = graph->output(); graph->connect(closure->output("BSDF"), out->input("Surface")); @@ -473,8 +473,8 @@ void ShaderManager::add_default(Scene *scene) ShaderGraph *graph = new ShaderGraph(); closure = graph->add(new EmissionNode()); - closure->input("Color")->value() = make_float3(0.8f, 0.8f, 0.8f); - closure->input("Strength")->value_float() = 0.0f; + closure->input("Color")->set(make_float3(0.8f, 0.8f, 0.8f)); + closure->input("Strength")->set(0.0f); out = graph->output(); graph->connect(closure->output("Emission"), out->input("Surface")); diff --git a/intern/cycles/subd/subd_dice.cpp b/intern/cycles/subd/subd_dice.cpp index daf0f63d695..7c74f21950e 100644 --- a/intern/cycles/subd/subd_dice.cpp +++ b/intern/cycles/subd/subd_dice.cpp @@ -41,14 +41,14 @@ EdgeDice::EdgeDice(const SubdParams& params_) } } -void EdgeDice::reserve(int num_verts, int num_tris) +void EdgeDice::reserve(int num_verts) { Mesh *mesh = params.mesh; vert_offset = mesh->verts.size(); - tri_offset = mesh->triangles.size(); + tri_offset = mesh->num_triangles(); - mesh->resize_mesh(vert_offset + num_verts, tri_offset + num_tris); + mesh->resize_mesh(vert_offset + num_verts, tri_offset); Attribute *attr_vN = mesh->attributes.add(ATTR_STD_VERTEX_NORMAL); @@ -80,7 +80,13 @@ int EdgeDice::add_vert(Patch *patch, float2 uv) void EdgeDice::add_triangle(Patch *patch, int v0, int v1, int v2) { - params.mesh->add_triangle(v0, v1, v2, params.shader, params.smooth, false); + Mesh *mesh = params.mesh; + + /* todo: optimize so we can reserve in advance, this is like push_back_slow() */ + if(mesh->triangles.size() == mesh->triangles.capacity()) + mesh->reserve_mesh(mesh->verts.size(), size_t(max(mesh->num_triangles() + 1, 1) * 1.2)); + + mesh->add_triangle(v0, v1, v2, params.shader, params.smooth, false); if(params.ptex) { Attribute *attr_ptex_face_id = params.mesh->attributes.add(ATTR_STD_PTEX_FACE_ID); @@ -141,8 +147,7 @@ void QuadDice::reserve(EdgeFactors& ef, int Mu, int Mv) { /* XXX need to make this also work for edge factor 0 and 1 */ int num_verts = (ef.tu0 + ef.tu1 + ef.tv0 + ef.tv1) + (Mu - 1)*(Mv - 1); - int num_tris = 0; - EdgeDice::reserve(num_verts, num_tris); + EdgeDice::reserve(num_verts); } float2 QuadDice::map_uv(SubPatch& sub, float u, float v) @@ -352,7 +357,7 @@ void TriangleDice::reserve(EdgeFactors& ef, int M) if(!(M & 1)) num_verts++; - EdgeDice::reserve(num_verts, 0); + EdgeDice::reserve(num_verts); } float2 TriangleDice::map_uv(SubPatch& sub, float2 uv) diff --git a/intern/cycles/subd/subd_dice.h b/intern/cycles/subd/subd_dice.h index 49f786e949e..85bd0ea28f0 100644 --- a/intern/cycles/subd/subd_dice.h +++ b/intern/cycles/subd/subd_dice.h @@ -72,7 +72,7 @@ public: explicit EdgeDice(const SubdParams& params); - void reserve(int num_verts, int num_tris); + void reserve(int num_verts); int add_vert(Patch *patch, float2 uv); void add_triangle(Patch *patch, int v0, int v1, int v2); diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt index cceec8d444c..e6140b3ed09 100644 --- a/intern/cycles/util/CMakeLists.txt +++ b/intern/cycles/util/CMakeLists.txt @@ -19,8 +19,10 @@ set(SRC util_simd.cpp util_system.cpp util_task.cpp + util_thread.cpp util_time.cpp util_transform.cpp + util_windows.cpp ) if(NOT CYCLES_STANDALONE_REPOSITORY) diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h index 32924f9a8c2..53944ec1cc4 100644 --- a/intern/cycles/util/util_math.h +++ b/intern/cycles/util/util_math.h @@ -1479,21 +1479,25 @@ ccl_device bool ray_triangle_intersect_uv( return true; } -ccl_device bool ray_quad_intersect(float3 ray_P, float3 ray_D, float ray_t, - float3 quad_P, float3 quad_u, float3 quad_v, +ccl_device bool ray_quad_intersect(float3 ray_P, float3 ray_D, float ray_mint, float ray_maxt, + float3 quad_P, float3 quad_u, float3 quad_v, float3 quad_n, float3 *isect_P, float *isect_t) { - float3 v0 = quad_P - quad_u*0.5f - quad_v*0.5f; - float3 v1 = quad_P + quad_u*0.5f - quad_v*0.5f; - float3 v2 = quad_P + quad_u*0.5f + quad_v*0.5f; - float3 v3 = quad_P - quad_u*0.5f + quad_v*0.5f; + float t = -(dot(ray_P, quad_n) - dot(quad_P, quad_n)) / dot(ray_D, quad_n); + if(t < ray_mint || t > ray_maxt) + return false; - if(ray_triangle_intersect(ray_P, ray_D, ray_t, v0, v1, v2, isect_P, isect_t)) - return true; - else if(ray_triangle_intersect(ray_P, ray_D, ray_t, v0, v2, v3, isect_P, isect_t)) - return true; - - return false; + float3 hit = ray_P + t*ray_D; + float3 inplane = hit - quad_P; + if(fabsf(dot(inplane, quad_u) / dot(quad_u, quad_u)) > 0.5f) + return false; + if(fabsf(dot(inplane, quad_v) / dot(quad_v, quad_v)) > 0.5f) + return false; + + if(isect_P) *isect_P = hit; + if(isect_t) *isect_t = t; + + return true; } /* projections */ diff --git a/intern/cycles/util/util_string.cpp b/intern/cycles/util/util_string.cpp index b3a8c6d7c2e..e16a83d56d0 100644 --- a/intern/cycles/util/util_string.cpp +++ b/intern/cycles/util/util_string.cpp @@ -239,5 +239,45 @@ string string_to_ansi(const string& str) #endif /* _WIN32 */ +string string_human_readable_size(size_t size) +{ + static const char suffixes[] = "BKMGTPEZY"; + + const char* suffix = suffixes; + size_t r = 0; + + while(size >= 1024) { + r = size % 1024; + size /= 1024; + suffix++; + } + + if(*suffix != 'B') + return string_printf("%.2f%c", double(size*1024+r)/1024.0, *suffix); + else + return string_printf("%zu", size); +} + +string string_human_readable_number(size_t num) +{ + /* add thousands separators */ + char buf[32]; + + char* p = buf+31; + *p = '\0'; + + int i = -1; + while(num) { + if(++i && i % 3 == 0) + *(--p) = ','; + + *(--p) = '0' + (num % 10); + + num /= 10; + } + + return p; +} + CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_string.h b/intern/cycles/util/util_string.h index c4b51bda432..d3b5248c380 100644 --- a/intern/cycles/util/util_string.h +++ b/intern/cycles/util/util_string.h @@ -62,6 +62,11 @@ string string_from_wstring(const wstring& path); string string_to_ansi(const string& str); #endif +/* Make a string from a size in bytes in human readable form */ +string string_human_readable_size(size_t size); +/* Make a string from a unitless quantity in human readable form */ +string string_human_readable_number(size_t num); + CCL_NAMESPACE_END #endif /* __UTIL_STRING_H__ */ diff --git a/intern/cycles/util/util_system.cpp b/intern/cycles/util/util_system.cpp index 4ff0ee91d73..d5fac9a0e34 100644 --- a/intern/cycles/util/util_system.cpp +++ b/intern/cycles/util/util_system.cpp @@ -15,7 +15,9 @@ */ #include "util_system.h" + #include "util_debug.h" +#include "util_logging.h" #include "util_types.h" #include "util_string.h" @@ -33,28 +35,56 @@ CCL_NAMESPACE_BEGIN -int system_cpu_thread_count() +int system_cpu_group_count() { - static uint count = 0; - - if(count > 0) - return count; +#ifdef _WIN32 + util_windows_init_numa_groups(); + return GetActiveProcessorGroupCount(); +#else + /* TODO(sergey): Need to adopt for other platforms. */ + return 1; +#endif +} +int system_cpu_group_thread_count(int group) +{ + /* TODO(sergey): Need make other platforms aware of groups. */ #ifdef _WIN32 - SYSTEM_INFO info; - GetSystemInfo(&info); - count = (uint)info.dwNumberOfProcessors; + util_windows_init_numa_groups(); + return GetActiveProcessorCount(group); #elif defined(__APPLE__) + (void)group; + int count; size_t len = sizeof(count); int mib[2] = { CTL_HW, HW_NCPU }; - sysctl(mib, 2, &count, &len, NULL, 0); + return count; #else - count = (uint)sysconf(_SC_NPROCESSORS_ONLN); + (void)group; + return sysconf(_SC_NPROCESSORS_ONLN); #endif +} + +int system_cpu_thread_count() +{ + static uint count = 0; - if(count < 1) + if(count > 0) { + return count; + } + + int max_group = system_cpu_group_count(); + VLOG(1) << "Detected " << max_group << " CPU groups."; + for(int group = 0; group < max_group; ++group) { + int num_threads = system_cpu_group_thread_count(group); + VLOG(1) << "Group " << group + << " has " << num_threads << " threads."; + count += num_threads; + } + + if(count < 1) { count = 1; + } return count; } diff --git a/intern/cycles/util/util_system.h b/intern/cycles/util/util_system.h index 4e7e00f85fd..557aab6cbae 100644 --- a/intern/cycles/util/util_system.h +++ b/intern/cycles/util/util_system.h @@ -21,7 +21,15 @@ CCL_NAMESPACE_BEGIN +/* Get number of available CPU groups. */ +int system_cpu_group_count(); + +/* Get number of threads/processors in the specified group. */ +int system_cpu_group_thread_count(int group); + +/* Get total number of threads in all groups. */ int system_cpu_thread_count(); + string system_cpu_brand_string(); int system_cpu_bits(); bool system_cpu_support_sse2(); diff --git a/intern/cycles/util/util_task.cpp b/intern/cycles/util/util_task.cpp index d86aa8a4a46..352ba81c95a 100644 --- a/intern/cycles/util/util_task.cpp +++ b/intern/cycles/util/util_task.cpp @@ -16,6 +16,7 @@ #include "util_debug.h" #include "util_foreach.h" +#include "util_logging.h" #include "util_system.h" #include "util_task.h" #include "util_time.h" @@ -198,12 +199,30 @@ void TaskScheduler::init(int num_threads) /* automatic number of threads */ num_threads = system_cpu_thread_count(); } + VLOG(1) << "Creating pool of " << num_threads << " threads."; /* launch threads that will be waiting for work */ threads.resize(num_threads); - for(size_t i = 0; i < threads.size(); i++) - threads[i] = new thread(function_bind(&TaskScheduler::thread_run, i + 1)); + int num_groups = system_cpu_group_count(); + int thread_index = 0; + for(int group = 0; group < num_groups; ++group) { + /* NOTE: That's not really efficient from threading point of view, + * but it is simple to read and it doesn't make sense to use more + * user-specified threads than logical threads anyway. + */ + int num_group_threads = (group == num_groups - 1) + ? (threads.size() - thread_index) + : system_cpu_group_thread_count(group); + for(int group_thread = 0; + group_thread < num_group_threads && thread_index < threads.size(); + ++group_thread, ++thread_index) + { + threads[thread_index] = new thread(function_bind(&TaskScheduler::thread_run, + thread_index + 1), + group); + } + } } users++; diff --git a/intern/cycles/util/util_thread.cpp b/intern/cycles/util/util_thread.cpp new file mode 100644 index 00000000000..3db8b4bd197 --- /dev/null +++ b/intern/cycles/util/util_thread.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2011-2016 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "util_thread.h" + +#include "util_system.h" +#include "util_windows.h" + +CCL_NAMESPACE_BEGIN + +thread::thread(function<void(void)> run_cb, int group) + : run_cb_(run_cb), + joined_(false), + group_(group) +{ + pthread_create(&pthread_id_, NULL, run, (void*)this); +} + +thread::~thread() +{ + if(!joined_) { + join(); + } +} + +void *thread::run(void *arg) +{ + thread *self = (thread*)(arg); + if(self->group_ != -1) { +#ifdef _WIN32 + HANDLE thread_handle = GetCurrentThread(); + GROUP_AFFINITY group_affinity = { 0 }; + int num_threads = system_cpu_group_thread_count(self->group_); + group_affinity.Group = self->group_; + group_affinity.Mask = (num_threads == 64) + ? -1 + : (1ull << num_threads) - 1; + if(SetThreadGroupAffinity(thread_handle, &group_affinity, NULL) == 0) { + fprintf(stderr, "Error setting thread affinity.\n"); + } +#endif + } + self->run_cb_(); + return NULL; +} + +bool thread::join() +{ + joined_ = true; + return pthread_join(pthread_id_, NULL) == 0; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_thread.h b/intern/cycles/util/util_thread.h index 59575f31c13..427c633d2ce 100644 --- a/intern/cycles/util/util_thread.h +++ b/intern/cycles/util/util_thread.h @@ -52,37 +52,17 @@ typedef boost::condition_variable thread_condition_variable; class thread { public: - thread(function<void(void)> run_cb_) + thread(function<void(void)> run_cb, int group = -1); + ~thread(); - { - joined = false; - run_cb = run_cb_; - - pthread_create(&pthread_id, NULL, run, (void*)this); - } - - ~thread() - { - if(!joined) - join(); - } - - static void *run(void *arg) - { - ((thread*)arg)->run_cb(); - return NULL; - } - - bool join() - { - joined = true; - return pthread_join(pthread_id, NULL) == 0; - } + static void *run(void *arg); + bool join(); protected: - function<void(void)> run_cb; - pthread_t pthread_id; - bool joined; + function<void(void)> run_cb_; + pthread_t pthread_id_; + bool joined_; + int group_; }; /* Own wrapper around pthread's spin lock to make it's use easier. */ diff --git a/intern/cycles/util/util_windows.cpp b/intern/cycles/util/util_windows.cpp new file mode 100644 index 00000000000..ee5b3fd73c0 --- /dev/null +++ b/intern/cycles/util/util_windows.cpp @@ -0,0 +1,88 @@ +/* + * Copyright 2011-2016 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "util_windows.h" + +#ifdef _WIN32 + +CCL_NAMESPACE_BEGIN + +#ifdef _M_X64 +# include <VersionHelpers.h> +#endif + +#if _WIN32_WINNT < 0x0601 +tGetActiveProcessorGroupCount *GetActiveProcessorGroupCount; +tGetActiveProcessorCount *GetActiveProcessorCount; +tSetThreadGroupAffinity *SetThreadGroupAffinity; +#endif + +static WORD GetActiveProcessorGroupCount_stub() +{ + return 1; +} + +static DWORD GetActiveProcessorCount_stub(WORD /*GroupNumber*/) +{ + SYSTEM_INFO info; + GetSystemInfo(&info); + return info.dwNumberOfProcessors; +} + +static BOOL SetThreadGroupAffinity_stub( + HANDLE /*hThread*/, + const GROUP_AFFINITY * /*GroupAffinity*/, + PGROUP_AFFINITY /*PreviousGroupAffinity*/) +{ + return TRUE; +} + +static bool supports_numa() +{ +#ifndef _M_X64 + return false; +#else + return IsWindows7OrGreater(); +#endif +} + +void util_windows_init_numa_groups() +{ + static bool initialized = false; + if(initialized) { + return; + } + initialized = true; +#if _WIN32_WINNT < 0x0601 + if(!supports_numa()) { + /* Use stubs on platforms which doesn't have rean NUMA/Groups. */ + GetActiveProcessorGroupCount = GetActiveProcessorGroupCount_stub; + GetActiveProcessorCount = GetActiveProcessorCount_stub; + SetThreadGroupAffinity = SetThreadGroupAffinity_stub; + return; + } + HMODULE kernel = GetModuleHandleA("kernel32.dll"); +# define READ_SYMBOL(sym) sym = (t##sym*)GetProcAddress(kernel, #sym) + READ_SYMBOL(GetActiveProcessorGroupCount); + READ_SYMBOL(GetActiveProcessorCount); + READ_SYMBOL(SetThreadGroupAffinity); +# undef READ_SUMBOL +#endif +} + +CCL_NAMESPACE_END + +#endif /* _WIN32 */ diff --git a/intern/cycles/util/util_windows.h b/intern/cycles/util/util_windows.h index f67e34d0f31..ac61d5348c3 100644 --- a/intern/cycles/util/util_windows.h +++ b/intern/cycles/util/util_windows.h @@ -31,6 +31,25 @@ #include <windows.h> +CCL_NAMESPACE_BEGIN + +#if _WIN32_WINNT < 0x0601 +typedef WORD tGetActiveProcessorGroupCount(); +typedef DWORD tGetActiveProcessorCount(WORD GroupNumber); +typedef BOOL tSetThreadGroupAffinity(HANDLE hThread, + const GROUP_AFFINITY *GroupAffinity, + PGROUP_AFFINITY PreviousGroupAffinity); + +extern tGetActiveProcessorGroupCount *GetActiveProcessorGroupCount; +extern tGetActiveProcessorCount *GetActiveProcessorCount; +extern tSetThreadGroupAffinity *SetThreadGroupAffinity; +#endif + +/* Make sure NUMA and processor groups API is initialized. */ +void util_windows_init_numa_groups(); + +CCL_NAMESPACE_END + #endif /* WIN32 */ #endif /* __UTIL_WINDOWS_H__ */ diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py index df014e8262b..636b6b0f46b 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py +++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py @@ -58,7 +58,7 @@ class SpellChecker: "vertices", # Merged words - "addon", "addons", + #~ "addon", "addons", "antialiasing", "arcsine", "arccosine", "arctangent", "autoclip", @@ -118,6 +118,7 @@ class SpellChecker: "localview", "lookup", "lookups", "mathutils", + "micropolygon", "midlevel", "midground", "mixdown", @@ -127,6 +128,7 @@ class SpellChecker: "multires", "multiresolution", "multisampling", "multitexture", + "multithreaded", "multiuser", "multiview", "namespace", @@ -412,6 +414,7 @@ class SpellChecker: # Blender terms "audaspace", "bbone", + "bendy", # bones "bmesh", "breakdowner", "bspline", @@ -473,9 +476,10 @@ class SpellChecker: "wpaint", "uvwarp", - # Algorithm names + # Algorithm/library names "ashikhmin", # Ashikhmin-Shirley "beckmann", + "blosc", "catmull", "catrom", "chebychev", diff --git a/release/scripts/presets/interface_theme/blender_24x.xml b/release/scripts/presets/interface_theme/blender_24x.xml index 445c23b4c4e..90730aeaebc 100644 --- a/release/scripts/presets/interface_theme/blender_24x.xml +++ b/release/scripts/presets/interface_theme/blender_24x.xml @@ -179,7 +179,7 @@ </wcol_tooltip> <wcol_menu_item> <ThemeWidgetColors outline="#000000" - inner="#00000000" + inner="#d2d2d200" inner_sel="#7f7f7fff" item="#ffffffff" text="#000000" @@ -371,7 +371,7 @@ header="#b4b4b4" header_text="#000000" header_text_hi="#ffffff" - button="#727272ff" + button="#b4b4b457" button_title="#000000" button_text="#000000" button_text_hi="#ffffff" diff --git a/release/scripts/presets/keyconfig/maya.py b/release/scripts/presets/keyconfig/maya.py index cdd16f26877..67fd1fddcac 100644 --- a/release/scripts/presets/keyconfig/maya.py +++ b/release/scripts/presets/keyconfig/maya.py @@ -698,7 +698,7 @@ kmi.properties.level = 5 km = kc.keymaps.new('Knife Tool Modal Map', space_type='EMPTY', region_type='WINDOW', modal=True) kmi = km.keymap_items.new_modal('CANCEL', 'ESC', 'ANY', any=True) -kmi = km.keymap_items.new_modal('ADD_CUT', 'LEFTMOUSE', 'ANY') +kmi = km.keymap_items.new_modal('ADD_CUT', 'LEFTMOUSE', 'ANY', any=True) kmi = km.keymap_items.new_modal('PANNING', 'LEFTMOUSE', 'ANY', alt=True) kmi = km.keymap_items.new_modal('PANNING', 'MIDDLEMOUSE', 'ANY', alt=True) kmi = km.keymap_items.new_modal('PANNING', 'RIGHTMOUSE', 'ANY', alt=True) diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 76d41d91b78..cc8921f1a8e 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -1357,7 +1357,10 @@ class WM_OT_properties_add(Operator): return prop_new - prop = unique_name(item.keys()) + prop = unique_name( + {*item.keys(), + *type(item).bl_rna.properties.keys(), + }) item[prop] = 1.0 rna_idprop_ui_prop_update(item, prop) diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index 6fc668e67f5..2389be6787d 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -126,7 +126,7 @@ def register(): WindowManager.addon_filter = EnumProperty( items=addon_filter_items, name="Category", - description="Filter addons by category", + description="Filter add-ons by category", ) WindowManager.addon_support = EnumProperty( diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index 48b5d315765..2a17af9f37f 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -1422,6 +1422,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): sub = row.row(align=True) sub.active = has_vgroup sub.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') + subcol.prop(md, "mix_limit") def CORRECTIVE_SMOOTH(self, layout, ob, md): is_bind = md.is_bind diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index 13e7265319b..4ea1c3a5cc7 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -248,6 +248,7 @@ class RENDER_PT_shading(RenderButtonsPanel, Panel): col = split.column() col.prop(rd, "use_raytrace", text="Ray Tracing") col.prop(rd, "alpha_mode", text="Alpha") + col.prop(rd, "use_world_space_shading", text="World Space Shading") class RENDER_PT_performance(RenderButtonsPanel, Panel): diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 41726e41176..aa7d539538b 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -55,6 +55,10 @@ #include "BIF_gl.h" #include "BLF_api.h" +#ifndef BLF_STANDALONE +#include "GPU_basic_shader.h" +#endif + #include "blf_internal_types.h" #include "blf_internal.h" @@ -179,6 +183,16 @@ static void blf_glyph_cache_texture(FontBLF *font, GlyphCacheBLF *gc) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +#ifndef BLF_STANDALONE + /* needed since basic shader doesn't support alpha-only textures, + * while we could add support this is only used in a few places + * (an alternative could be to have a simple shader for BLF). */ + if (GLEW_ARB_texture_swizzle && GPU_basic_shader_use_glsl_get()) { + GLint swizzle_mask[] = {GL_ONE, GL_ONE, GL_ONE, GL_ALPHA}; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle_mask); + } +#endif + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, gc->p2_width, gc->p2_height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, NULL); } diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index 2b13a847e14..8ccc4a6eb0e 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -757,22 +757,22 @@ void DM_update_weight_mcol( typedef struct DMVertexAttribs { struct { struct MLoopUV *array; - int em_offset, gl_index, gl_texco; + int em_offset, gl_index, gl_texco, gl_info_index; } tface[MAX_MTFACE]; struct { struct MLoopCol *array; - int em_offset, gl_index; + int em_offset, gl_index, gl_info_index; } mcol[MAX_MCOL]; struct { float (*array)[4]; - int em_offset, gl_index; + int em_offset, gl_index, gl_info_index; } tang[MAX_MTFACE]; struct { float (*array)[3]; - int em_offset, gl_index, gl_texco; + int em_offset, gl_index, gl_texco, gl_info_index; } orco; int tottface, totmcol, tottang, totorco; diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h index dc751747f32..6524afff051 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -177,7 +177,7 @@ void BKE_animsys_evaluate_all_animation(struct Main *main, struct Scene *scene, /* TODO(sergey): This is mainly a temp public function. */ struct FCurve; -bool BKE_animsys_execute_fcurve(struct PointerRNA *ptr, struct AnimMapper *remap, struct FCurve *fcu); +bool BKE_animsys_execute_fcurve(struct PointerRNA *ptr, struct AnimMapper *remap, struct FCurve *fcu, float curval); /* ------------ Specialized API --------------- */ /* There are a few special tools which require these following functions. They are NOT to be used diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 2022d11d508..bb4eb652ae2 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -279,7 +279,7 @@ void correct_bezpart(float v1[2], float v2[2], float v3[2], float v4[2]); /* evaluate fcurve */ float evaluate_fcurve(struct FCurve *fcu, float evaltime); /* evaluate fcurve and store value */ -void calculate_fcurve(struct FCurve *fcu, float ctime); +float calculate_fcurve(struct FCurve *fcu, float evaltime); /* ************* F-Curve Samples API ******************** */ diff --git a/source/blender/blenkernel/BKE_library_idmap.h b/source/blender/blenkernel/BKE_library_idmap.h new file mode 100644 index 00000000000..971586ea8b7 --- /dev/null +++ b/source/blender/blenkernel/BKE_library_idmap.h @@ -0,0 +1,50 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ +#ifndef __BKE_LIBRARY_IDMAP_H__ +#define __BKE_LIBRARY_IDMAP_H__ + +/** \file BKE_library_idmap.h + * \ingroup bke + */ + +#include "BLI_compiler_attrs.h" + +struct ID; +struct Main; +struct IDNameLib_Map; + +struct IDNameLib_Map *BKE_main_idmap_create( + struct Main *bmain) + ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +void BKE_main_idmap_destroy( + struct IDNameLib_Map *id_typemap) + ATTR_NONNULL(); +struct Main *BKE_main_idmap_main_get( + struct IDNameLib_Map *id_typemap) + ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +struct ID *BKE_main_idmap_lookup( + struct IDNameLib_Map *id_typemap, + short id_type, const char *name, const struct Library *lib) + ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 3); +struct ID *BKE_main_idmap_lookup_id( + struct IDNameLib_Map *id_typemap, const struct ID *id) + ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2); + +#endif /* __BKE_LIBRARY_IDMAP_H__ */ diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index c7d5857b873..d8d869015a3 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -323,7 +323,7 @@ void BKE_mesh_mdisp_flip(struct MDisps *md, const bool use_loop_mdisp_flip); void BKE_mesh_polygon_flip_ex( struct MPoly *mpoly, struct MLoop *mloop, struct CustomData *ldata, - struct MDisps *mdisp, const bool use_loop_mdisp_flip); + float (*lnors)[3], struct MDisps *mdisp, const bool use_loop_mdisp_flip); void BKE_mesh_polygon_flip(struct MPoly *mpoly, struct MLoop *mloop, struct CustomData *ldata); void BKE_mesh_polygons_flip(struct MPoly *mpoly, struct MLoop *mloop, struct CustomData *ldata, int totpoly); diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 7d6096407ff..c591ec2a0aa 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -208,6 +208,8 @@ void BKE_object_eval_uber_data(struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob); +void BKE_object_eval_proxy_backlink(struct EvaluationContext *eval_ctx, struct Object *ob); + void BKE_object_handle_data_update(struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob); diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index a4c44b9934e..12bfc07e958 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -135,6 +135,7 @@ float get_render_aosss_error(const struct RenderData *r, float error); bool BKE_scene_use_new_shading_nodes(const struct Scene *scene); bool BKE_scene_use_shading_nodes_custom(struct Scene *scene); +bool BKE_scene_use_world_space_shading(struct Scene *scene); bool BKE_scene_use_spherical_stereo(struct Scene *scene); bool BKE_scene_uses_blender_internal(const struct Scene *scene); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 1f0a93bdc30..f32172c2806 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -122,6 +122,7 @@ set(SRC intern/lamp.c intern/lattice.c intern/library.c + intern/library_idmap.c intern/library_query.c intern/linestyle.c intern/mask.c @@ -245,6 +246,7 @@ set(SRC BKE_lamp.h BKE_lattice.h BKE_library.h + BKE_library_idmap.h BKE_library_query.h BKE_linestyle.h BKE_main.h diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 195b5e75352..09f4c286f48 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -2067,15 +2067,10 @@ static void mesh_calc_modifiers( DM_add_edge_layer(dm, CD_ORIGINDEX, CD_CALLOC, NULL); DM_add_poly_layer(dm, CD_ORIGINDEX, CD_CALLOC, NULL); -#pragma omp parallel sections if (dm->numVertData + dm->numEdgeData + dm->numPolyData >= BKE_MESH_OMP_LIMIT) - { -#pragma omp section - { range_vn_i(DM_get_vert_data_layer(dm, CD_ORIGINDEX), dm->numVertData, 0); } -#pragma omp section - { range_vn_i(DM_get_edge_data_layer(dm, CD_ORIGINDEX), dm->numEdgeData, 0); } -#pragma omp section - { range_vn_i(DM_get_poly_data_layer(dm, CD_ORIGINDEX), dm->numPolyData, 0); } - } + /* Not worth parallelizing this, gives less than 0.1% overall speedup in best of best cases... */ + range_vn_i(DM_get_vert_data_layer(dm, CD_ORIGINDEX), dm->numVertData, 0); + range_vn_i(DM_get_edge_data_layer(dm, CD_ORIGINDEX), dm->numEdgeData, 0); + range_vn_i(DM_get_poly_data_layer(dm, CD_ORIGINDEX), dm->numPolyData, 0); } } @@ -3742,6 +3737,7 @@ void DM_vertex_attributes_from_gpu(DerivedMesh *dm, GPUVertexAttribs *gattribs, } attribs->tface[a].gl_index = gattribs->layer[b].glindex; + attribs->tface[a].gl_info_index = gattribs->layer[b].glinfoindoex; attribs->tface[a].gl_texco = gattribs->layer[b].gltexco; } else if (type == CD_MCOL) { @@ -3765,6 +3761,7 @@ void DM_vertex_attributes_from_gpu(DerivedMesh *dm, GPUVertexAttribs *gattribs, } attribs->mcol[a].gl_index = gattribs->layer[b].glindex; + attribs->mcol[a].gl_info_index = gattribs->layer[b].glinfoindoex; } else if (type == CD_TANGENT) { /* note, even with 'is_editmesh' this uses the derived-meshes loop data */ @@ -3787,6 +3784,7 @@ void DM_vertex_attributes_from_gpu(DerivedMesh *dm, GPUVertexAttribs *gattribs, } attribs->tang[a].gl_index = gattribs->layer[b].glindex; + attribs->tang[a].gl_info_index = gattribs->layer[b].glinfoindoex; } else if (type == CD_ORCO) { /* original coordinates */ @@ -3806,6 +3804,7 @@ void DM_vertex_attributes_from_gpu(DerivedMesh *dm, GPUVertexAttribs *gattribs, attribs->orco.gl_index = gattribs->layer[b].glindex; attribs->orco.gl_texco = gattribs->layer[b].gltexco; + attribs->orco.gl_info_index = gattribs->layer[b].glinfoindoex; } } } @@ -3834,6 +3833,7 @@ void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert, glTexCoord3fv(orco); else glVertexAttrib3fv(attribs->orco.gl_index, orco); + glUniform1i(attribs->orco.gl_info_index, 0); } /* uv texture coordinates */ @@ -3852,6 +3852,7 @@ void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert, glTexCoord2fv(uv); else glVertexAttrib2fv(attribs->tface[b].gl_index, uv); + glUniform1i(attribs->tface[b].gl_info_index, 0); } /* vertex colors */ @@ -3867,6 +3868,7 @@ void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert, } glVertexAttrib4fv(attribs->mcol[b].gl_index, col); + glUniform1i(attribs->mcol[b].gl_info_index, GPU_ATTR_INFO_SRGB); } /* tangent for normal mapping */ @@ -3876,6 +3878,7 @@ void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert, const float *tang = (array) ? array[a * 4 + vert] : zero; glVertexAttrib4fv(attribs->tang[b].gl_index, tang); } + glUniform1i(attribs->tang[b].gl_info_index, 0); } } diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 41950c59a22..99aae6239e8 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -730,14 +730,10 @@ static char *rna_path_rename_fix(ID *owner_id, const char *prefix, const char *o DynStr *ds = BLI_dynstr_new(); const char *postfixPtr = oldNamePtr + oldNameLen; char *newPath = NULL; - char oldChar; - + /* add the part of the string that goes up to the start of the prefix */ if (prefixPtr > oldpath) { - oldChar = prefixPtr[0]; - prefixPtr[0] = 0; - BLI_dynstr_append(ds, oldpath); - prefixPtr[0] = oldChar; + BLI_dynstr_nappend(ds, oldpath, prefixPtr - oldpath); } /* add the prefix */ @@ -1620,7 +1616,7 @@ static bool animsys_write_rna_setting(PointerRNA *ptr, char *path, int array_ind } /* Simple replacement based data-setting of the FCurve using RNA */ -bool BKE_animsys_execute_fcurve(PointerRNA *ptr, AnimMapper *remap, FCurve *fcu) +bool BKE_animsys_execute_fcurve(PointerRNA *ptr, AnimMapper *remap, FCurve *fcu, float curval) { char *path = NULL; bool free_path = false; @@ -1631,7 +1627,7 @@ bool BKE_animsys_execute_fcurve(PointerRNA *ptr, AnimMapper *remap, FCurve *fcu) /* write value to setting */ if (path) - ok = animsys_write_rna_setting(ptr, path, fcu->array_index, fcu->curval); + ok = animsys_write_rna_setting(ptr, path, fcu->array_index, curval); /* free temp path-info */ if (free_path) @@ -1654,8 +1650,8 @@ static void animsys_evaluate_fcurves(PointerRNA *ptr, ListBase *list, AnimMapper if ((fcu->grp == NULL) || (fcu->grp->flag & AGRP_MUTED) == 0) { /* check if this curve should be skipped */ if ((fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED)) == 0) { - calculate_fcurve(fcu, ctime); - BKE_animsys_execute_fcurve(ptr, remap, fcu); + const float curval = calculate_fcurve(fcu, ctime); + BKE_animsys_execute_fcurve(ptr, remap, fcu, curval); } } } @@ -1684,8 +1680,8 @@ static void animsys_evaluate_drivers(PointerRNA *ptr, AnimData *adt, float ctime /* evaluate this using values set already in other places * NOTE: for 'layering' option later on, we should check if we should remove old value before adding * new to only be done when drivers only changed */ - calculate_fcurve(fcu, ctime); - ok = BKE_animsys_execute_fcurve(ptr, NULL, fcu); + const float curval = calculate_fcurve(fcu, ctime); + ok = BKE_animsys_execute_fcurve(ptr, NULL, fcu, curval); /* clear recalc flag */ driver->flag &= ~DRIVER_FLAG_RECALC; @@ -1753,8 +1749,8 @@ void animsys_evaluate_action_group(PointerRNA *ptr, bAction *act, bActionGroup * for (fcu = agrp->channels.first; (fcu) && (fcu->grp == agrp); fcu = fcu->next) { /* check if this curve should be skipped */ if ((fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED)) == 0) { - calculate_fcurve(fcu, ctime); - BKE_animsys_execute_fcurve(ptr, remap, fcu); + const float curval = calculate_fcurve(fcu, ctime); + BKE_animsys_execute_fcurve(ptr, remap, fcu, curval); } } } @@ -2888,8 +2884,8 @@ void BKE_animsys_eval_driver(EvaluationContext *eval_ctx, * NOTE: for 'layering' option later on, we should check if we should remove old value before adding * new to only be done when drivers only changed */ //printf("\told val = %f\n", fcu->curval); - calculate_fcurve(fcu, eval_ctx->ctime); - ok = BKE_animsys_execute_fcurve(&id_ptr, NULL, fcu); + const float curval = calculate_fcurve(fcu, eval_ctx->ctime); + ok = BKE_animsys_execute_fcurve(&id_ptr, NULL, fcu, curval); //printf("\tnew val = %f\n", fcu->curval); /* clear recalc flag */ diff --git a/source/blender/blenkernel/intern/autoexec.c b/source/blender/blenkernel/intern/autoexec.c index d9462cd0262..bde06b02ae8 100644 --- a/source/blender/blenkernel/intern/autoexec.c +++ b/source/blender/blenkernel/intern/autoexec.c @@ -59,7 +59,10 @@ bool BKE_autoexec_match(const char *path) BLI_assert((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0); for (path_cmp = U.autoexec_paths.first; path_cmp; path_cmp = path_cmp->next) { - if ((path_cmp->flag & USER_PATHCMP_GLOB)) { + if (path_cmp->path[0] == '\0') { + /* pass */ + } + else if ((path_cmp->flag & USER_PATHCMP_GLOB)) { if (fnmatch(path_cmp->path, path, fnmatch_flags) == 0) { return true; } diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 31dac038e43..da7863096e3 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -852,7 +852,7 @@ int BKE_brush_size_get(const Scene *scene, const Brush *brush) UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; int size = (ups->flag & UNIFIED_PAINT_SIZE) ? ups->size : brush->size; - return (int)((float)size * U.pixelsize); + return size; } int BKE_brush_use_locked_size(const Scene *scene, const Brush *brush) diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index af1ad4900b3..392a38773e7 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -1032,6 +1032,7 @@ static void cdDM_drawMappedFacesGLSL( if (matconv[a].attribs.totorco && matconv[a].attribs.orco.array) { matconv[a].datatypes[numdata].index = matconv[a].attribs.orco.gl_index; + matconv[a].datatypes[numdata].info_index = matconv[a].attribs.orco.gl_info_index; matconv[a].datatypes[numdata].size = 3; matconv[a].datatypes[numdata].type = GL_FLOAT; numdata++; @@ -1039,6 +1040,7 @@ static void cdDM_drawMappedFacesGLSL( for (b = 0; b < matconv[a].attribs.tottface; b++) { if (matconv[a].attribs.tface[b].array) { matconv[a].datatypes[numdata].index = matconv[a].attribs.tface[b].gl_index; + matconv[a].datatypes[numdata].info_index = matconv[a].attribs.tface[b].gl_info_index; matconv[a].datatypes[numdata].size = 2; matconv[a].datatypes[numdata].type = GL_FLOAT; numdata++; @@ -1047,6 +1049,7 @@ static void cdDM_drawMappedFacesGLSL( for (b = 0; b < matconv[a].attribs.totmcol; b++) { if (matconv[a].attribs.mcol[b].array) { matconv[a].datatypes[numdata].index = matconv[a].attribs.mcol[b].gl_index; + matconv[a].datatypes[numdata].info_index = matconv[a].attribs.mcol[b].gl_info_index; matconv[a].datatypes[numdata].size = 4; matconv[a].datatypes[numdata].type = GL_UNSIGNED_BYTE; numdata++; @@ -1055,6 +1058,7 @@ static void cdDM_drawMappedFacesGLSL( for (b = 0; b < matconv[a].attribs.tottang; b++) { if (matconv[a].attribs.tang[b].array) { matconv[a].datatypes[numdata].index = matconv[a].attribs.tang[b].gl_index; + matconv[a].datatypes[numdata].info_index = matconv[a].attribs.tang[b].gl_info_index; matconv[a].datatypes[numdata].size = 4; matconv[a].datatypes[numdata].type = GL_FLOAT; numdata++; @@ -2656,6 +2660,9 @@ void CDDM_calc_loop_normals(DerivedMesh *dm, const bool use_split_normals, const } /* #define DEBUG_CLNORS */ +#ifdef DEBUG_CLNORS +# include "BLI_linklist.h" +#endif void CDDM_calc_loop_normals_spacearr( DerivedMesh *dm, const bool use_split_normals, const float split_angle, MLoopNorSpaceArray *r_lnors_spacearr) diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index de79a30bd60..612f1f477e1 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -1853,8 +1853,6 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, int typ (alloctype == CD_DUPLICATE) || (alloctype == CD_REFERENCE)); - BLI_assert(size >= 0); - if (!typeInfo->defaultname && CustomData_has_layer(data, type)) return &data->layers[CustomData_get_layer_index(data, type)]; diff --git a/source/blender/blenkernel/intern/editderivedmesh.c b/source/blender/blenkernel/intern/editderivedmesh.c index 6c117447664..ffd000eed88 100644 --- a/source/blender/blenkernel/intern/editderivedmesh.c +++ b/source/blender/blenkernel/intern/editderivedmesh.c @@ -57,6 +57,7 @@ #include "MEM_guardedalloc.h" #include "GPU_glew.h" +#include "GPU_buffers.h" #include "GPU_shader.h" #include "GPU_basic_shader.h" @@ -1405,6 +1406,24 @@ static void emDM_drawMappedFacesTex( emDM_drawFacesTex_common(dm, NULL, setDrawOptions, compareDrawOptions, userData); } +static void emdm_pass_attrib_update_uniforms(const DMVertexAttribs *attribs) +{ + int i; + if (attribs->totorco) { + glUniform1i(attribs->orco.gl_info_index, 0); + } + for (i = 0; i < attribs->tottface; i++) { + glUniform1i(attribs->tface[i].gl_info_index, 0); + } + for (i = 0; i < attribs->totmcol; i++) { + glUniform1i(attribs->mcol[i].gl_info_index, GPU_ATTR_INFO_SRGB); + } + + for (i = 0; i < attribs->tottang; i++) { + glUniform1i(attribs->tang[i].gl_info_index, 0); + } +} + /** * \note * @@ -1449,15 +1468,15 @@ static void emdm_pass_attrib_vertex_glsl(const DMVertexAttribs *attribs, const B glVertexAttrib2fv(attribs->tface[i].gl_index, uv); } for (i = 0; i < attribs->totmcol; i++) { - GLubyte col[4]; + float col[4]; if (attribs->mcol[i].em_offset != -1) { const MLoopCol *cp = BM_ELEM_CD_GET_VOID_P(loop, attribs->mcol[i].em_offset); - copy_v4_v4_uchar(col, &cp->r); + rgba_uchar_to_float(col, &cp->r); } else { - col[0] = 0; col[1] = 0; col[2] = 0; col[3] = 0; + col[0] = 0.0f; col[1] = 0.0f; col[2] = 0.0f; col[3] = 0.0f; } - glVertexAttrib4ubv(attribs->mcol[i].gl_index, col); + glVertexAttrib4fv(attribs->mcol[i].gl_index, col); } for (i = 0; i < attribs->tottang; i++) { @@ -1527,6 +1546,7 @@ static void emDM_drawMappedFacesGLSL( do_draw = setMaterial(matnr = new_matnr, &gattribs); if (do_draw) { DM_vertex_attributes_from_gpu(dm, &gattribs, &attribs); + emdm_pass_attrib_update_uniforms(&attribs); if (UNLIKELY(attribs.tottang && bm->elem_index_dirty & BM_LOOP)) { BM_mesh_elem_index_ensure(bm, BM_LOOP); } diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index e00d19b2b01..a5c4be364d2 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -596,7 +596,9 @@ int get_effector_data(EffectorCache *eff, EffectorData *efd, EffectedPoint *poin float cfra = eff->scene->r.cfra; int ret = 0; - if (eff->pd && eff->pd->shape==PFIELD_SHAPE_SURFACE && eff->surmd) { + /* In case surface object is in Edit mode when loading the .blend, surface modifier is never executed + * and bvhtree never built, see T48415. */ + if (eff->pd && eff->pd->shape==PFIELD_SHAPE_SURFACE && eff->surmd && eff->surmd->bvhtree) { /* closest point in the object surface is an effector */ float vec[3]; diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index a2b5a05feac..395161aa6ed 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -2671,7 +2671,7 @@ float evaluate_fcurve(FCurve *fcu, float evaltime) } /* Calculate the value of the given F-Curve at the given frame, and set its curval */ -void calculate_fcurve(FCurve *fcu, float ctime) +float calculate_fcurve(FCurve *fcu, float evaltime) { /* only calculate + set curval (overriding the existing value) if curve has * any data which warrants this... @@ -2680,7 +2680,12 @@ void calculate_fcurve(FCurve *fcu, float ctime) list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE)) { /* calculate and set curval (evaluates driver too if necessary) */ - fcu->curval = evaluate_fcurve(fcu, ctime); + float curval = evaluate_fcurve(fcu, evaltime); + fcu->curval = curval; /* debug display only, not thread safe! */ + return curval; + } + else { + return 0.0f; } } diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index aed33d2c64d..98757407e89 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -541,7 +541,7 @@ int BKE_vfont_select_get(Object *ob, int *r_start, int *r_end) BLI_assert(ef->len >= 0); BLI_assert(ef->selstart >= 0 && ef->selstart <= ef->len + 1); - BLI_assert(ef->selend >= 0 && ef->selend <= ef->len); + BLI_assert(ef->selend >= 0 && ef->selend <= ef->len + 1); BLI_assert(ef->pos >= 0 && ef->pos <= ef->len); if (ef->selstart == 0) { diff --git a/source/blender/blenkernel/intern/idcode.c b/source/blender/blenkernel/intern/idcode.c index 68a741bc3fc..899ed548783 100644 --- a/source/blender/blenkernel/intern/idcode.c +++ b/source/blender/blenkernel/intern/idcode.c @@ -39,6 +39,7 @@ #include "BLT_translation.h" +#include "BKE_library.h" #include "BKE_idcode.h" typedef struct { @@ -54,6 +55,7 @@ typedef struct { /* plural need to match rna_main.c's MainCollectionDef */ /* WARNING! Keep it in sync with i18n contexts in BLT_translation.h */ static IDType idtypes[] = { + /** ID's directly below must all be in #Main, and be kept in sync with #MAX_LIBARRAY (membership, not order) */ { ID_AC, "Action", "actions", BLT_I18NCONTEXT_ID_ACTION, IDTYPE_FLAGS_ISLINKABLE }, { ID_AR, "Armature", "armatures", BLT_I18NCONTEXT_ID_ARMATURE, IDTYPE_FLAGS_ISLINKABLE }, { ID_BR, "Brush", "brushes", BLT_I18NCONTEXT_ID_BRUSH, IDTYPE_FLAGS_ISLINKABLE }, @@ -61,7 +63,6 @@ static IDType idtypes[] = { { ID_CU, "Curve", "curves", BLT_I18NCONTEXT_ID_CURVE, IDTYPE_FLAGS_ISLINKABLE }, { ID_GD, "GPencil", "grease_pencil", BLT_I18NCONTEXT_ID_GPENCIL, IDTYPE_FLAGS_ISLINKABLE }, /* rename gpencil */ { ID_GR, "Group", "groups", BLT_I18NCONTEXT_ID_GROUP, IDTYPE_FLAGS_ISLINKABLE }, - { ID_ID, "ID", "ids", BLT_I18NCONTEXT_ID_ID, 0 }, /* plural is fake */ { ID_IM, "Image", "images", BLT_I18NCONTEXT_ID_IMAGE, IDTYPE_FLAGS_ISLINKABLE }, { ID_IP, "Ipo", "ipos", "", IDTYPE_FLAGS_ISLINKABLE }, /* deprecated */ { ID_KE, "Key", "shape_keys", BLT_I18NCONTEXT_ID_SHAPEKEY, 0 }, @@ -89,8 +90,14 @@ static IDType idtypes[] = { { ID_VF, "VFont", "fonts", BLT_I18NCONTEXT_ID_VFONT, IDTYPE_FLAGS_ISLINKABLE }, { ID_WO, "World", "worlds", BLT_I18NCONTEXT_ID_WORLD, IDTYPE_FLAGS_ISLINKABLE }, { ID_WM, "WindowManager", "window_managers", BLT_I18NCONTEXT_ID_WINDOWMANAGER, 0 }, + + /** Keep last, not an ID exactly, only include for completeness */ + { ID_ID, "ID", "ids", BLT_I18NCONTEXT_ID_ID, 0 }, /* plural is fake */ }; +/* -1 for ID_ID */ +BLI_STATIC_ASSERT((ARRAY_SIZE(idtypes) - 1 == MAX_LIBARRAY), "Missing IDType"); + static IDType *idtype_from_name(const char *str) { int i = ARRAY_SIZE(idtypes); diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 911bf21b731..ecebc7336a2 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -109,8 +109,8 @@ static void image_add_view(Image *ima, const char *viewname, const char *filepat #define IMA_NO_INDEX 0x7FEFEFEF /* quick lookup: supports 1 million frames, thousand passes */ -#define IMA_MAKE_INDEX(frame, index) ((frame) << 10) + index -#define IMA_INDEX_FRAME(index) (index >> 10) +#define IMA_MAKE_INDEX(frame, index) (((frame) << 10) + (index)) +#define IMA_INDEX_FRAME(index) ((index) >> 10) /* #define IMA_INDEX_PASS(index) (index & ~1023) */ diff --git a/source/blender/blenkernel/intern/library_idmap.c b/source/blender/blenkernel/intern/library_idmap.c new file mode 100644 index 00000000000..fd78d9b23ce --- /dev/null +++ b/source/blender/blenkernel/intern/library_idmap.c @@ -0,0 +1,174 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <string.h> +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_ghash.h" +#include "BLI_listbase.h" + +#include "DNA_ID.h" + +#include "BKE_idcode.h" +#include "BKE_library.h" +#include "BKE_library_idmap.h" /* own include */ + +/** \file blender/blenkernel/intern/library_map.c + * \ingroup bke + * + * Utility functions for faster ID lookups. + */ + +/** \name BKE_main_idmap API + * + * Cache ID (name, library lookups). + * This doesn't account for adding/removing data-blocks, + * and should only be used when performing many lookups. + * + * \note GHash's are initialized on demand, + * since its likely some types will never have lookups run on them, + * so its a waste to create and never use. + * \{ */ + +struct IDNameLib_Key { + /** ``ID.name + 2``: without the ID type prefix, since each id type gets it's own 'map' */ + const char *name; + /** ``ID.lib``: */ + const Library *lib; +}; + +struct IDNameLib_TypeMap { + GHash *map; + short id_type; + /* only for storage of keys in the ghash, avoid many single allocs */ + struct IDNameLib_Key *keys; +}; + +/** + * Opaque structure, external API users only see this. + */ +struct IDNameLib_Map { + struct IDNameLib_TypeMap type_maps[MAX_LIBARRAY]; + struct Main *bmain; +}; + +static struct IDNameLib_TypeMap *main_idmap_from_idcode(struct IDNameLib_Map *id_map, short id_type) +{ + for (int i = 0; i < MAX_LIBARRAY; i++) { + if (id_map->type_maps[i].id_type == id_type) { + return &id_map->type_maps[i]; + } + } + return NULL; +} + +struct IDNameLib_Map *BKE_main_idmap_create(struct Main *bmain) +{ + struct IDNameLib_Map *id_map = MEM_mallocN(sizeof(*id_map), __func__); + + int index = 0; + while (index < MAX_LIBARRAY) { + id_map->type_maps[index].map = NULL; + id_map->type_maps[index].id_type = BKE_idcode_iter_step(&index); + } + BLI_assert(index == MAX_LIBARRAY); + + id_map->bmain = bmain; + + return id_map; +} + +struct Main *BKE_main_idmap_main_get(struct IDNameLib_Map *id_map) +{ + return id_map->bmain; +} + +static unsigned int idkey_hash(const void *ptr) +{ + const struct IDNameLib_Key *idkey = ptr; + unsigned int key = BLI_ghashutil_strhash(idkey->name); + if (idkey->lib) { + key ^= BLI_ghashutil_ptrhash(idkey->lib); + } + return key; +} + +static bool idkey_cmp(const void *a, const void *b) +{ + const struct IDNameLib_Key *idkey_a = a; + const struct IDNameLib_Key *idkey_b = b; + return strcmp(idkey_a->name, idkey_b->name) || (idkey_a->lib != idkey_b->lib); +} + +ID *BKE_main_idmap_lookup(struct IDNameLib_Map *id_map, short id_type, const char *name, const Library *lib) +{ + struct IDNameLib_TypeMap *type_map = main_idmap_from_idcode(id_map, id_type); + + if (UNLIKELY(type_map == NULL)) { + return NULL; + } + + /* lazy init */ + if (type_map->map == NULL) { + ListBase *lb = which_libbase(id_map->bmain, id_type); + const int lb_len = BLI_listbase_count(lb); + if (lb_len == 0) { + return NULL; + } + type_map->map = BLI_ghash_new_ex(idkey_hash, idkey_cmp, __func__, lb_len); + type_map->keys = MEM_mallocN(sizeof(struct IDNameLib_Key) * lb_len, __func__); + + GHash *map = type_map->map; + struct IDNameLib_Key *key = type_map->keys; + + for (ID *id = lb->first; id; id = id->next, key++) { + key->name = id->name + 2; + key->lib = id->lib; + BLI_ghash_insert(map, key, id); + } + } + + const struct IDNameLib_Key key_lookup = {name, lib}; + return BLI_ghash_lookup(type_map->map, &key_lookup); +} + +ID *BKE_main_idmap_lookup_id(struct IDNameLib_Map *id_map, const ID *id) +{ + return BKE_main_idmap_lookup(id_map, GS(id->name), id->name + 2, id->lib); +} + +void BKE_main_idmap_destroy(struct IDNameLib_Map *id_map) +{ + struct IDNameLib_TypeMap *type_map = id_map->type_maps; + for (int i = 0; i < MAX_LIBARRAY; i++, type_map++) { + if (type_map->map) { + BLI_ghash_free(type_map->map, NULL, NULL); + type_map->map = NULL; + MEM_freeN(type_map->keys); + } + } + + MEM_freeN(id_map); +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 1fec725dbb7..30f82a50ed9 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -1148,7 +1148,6 @@ static void init_render_nodetree(bNodeTree *ntree, Material *basemat, int r_mode /* parses the geom+tex nodes */ ntreeShaderGetTexcoMode(ntree, r_mode, &basemat->texco, &basemat->mode_l); - basemat->nmap_tangent_names_count = 0; for (node = ntree->nodes.first; node; node = node->next) { if (node->id) { if (GS(node->id->name) == ID_MA) { @@ -1199,7 +1198,7 @@ void init_render_material(Material *mat, int r_mode, float *amb) * mode_l will have it set when all node materials are shadeless. */ mat->mode_l = (mat->mode & MA_MODE_PIPELINE) | MA_SHLESS; mat->mode2_l = mat->mode2 & MA_MODE2_PIPELINE; - + mat->nmap_tangent_names_count = 0; init_render_nodetree(mat->nodetree, mat, r_mode, amb); if (!mat->nodetree->execdata) diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index 577a21285f8..1c86fbcfe8e 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -677,7 +677,7 @@ static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopS */ copy_v3_v3(*lnor, polynors[mp_index]); - /* printf("BASIC: handling loop %d / edge %d / vert %d\n", ml_curr_index, ml_curr->e, ml_curr->v); */ + /* printf("BASIC: handling loop %d / edge %d / vert %d / poly %d\n", ml_curr_index, ml_curr->e, ml_curr->v, mp_index); */ /* If needed, generate this (simple!) lnor space. */ if (lnors_spacearr) { @@ -3262,14 +3262,14 @@ void BKE_mesh_mdisp_flip(MDisps *md, const bool use_loop_mdisp_flip) */ void BKE_mesh_polygon_flip_ex( MPoly *mpoly, MLoop *mloop, CustomData *ldata, - MDisps *mdisp, const bool use_loop_mdisp_flip) + float (*lnors)[3], MDisps *mdisp, const bool use_loop_mdisp_flip) { int loopstart = mpoly->loopstart; int loopend = loopstart + mpoly->totloop - 1; const bool loops_in_ldata = (CustomData_get_layer(ldata, CD_MLOOP) == mloop); if (mdisp) { - for (int i = mpoly->loopstart; i <= loopend; i++) { + for (int i = loopstart; i <= loopend; i++) { BKE_mesh_mdisp_flip(&mdisp[i], use_loop_mdisp_flip); } } @@ -3288,6 +3288,9 @@ void BKE_mesh_polygon_flip_ex( if (!loops_in_ldata) { SWAP(MLoop, mloop[loopstart], mloop[loopend]); } + if (lnors) { + swap_v3_v3(lnors[loopstart], lnors[loopend]); + } CustomData_swap(ldata, loopstart, loopend); } /* Even if we did not swap the other 'pivot' loop, we need to set its swapped edge. */ @@ -3299,7 +3302,7 @@ void BKE_mesh_polygon_flip_ex( void BKE_mesh_polygon_flip(MPoly *mpoly, MLoop *mloop, CustomData *ldata) { MDisps *mdisp = CustomData_get_layer(ldata, CD_MDISPS); - BKE_mesh_polygon_flip_ex(mpoly, mloop, ldata, mdisp, true); + BKE_mesh_polygon_flip_ex(mpoly, mloop, ldata, NULL, mdisp, true); } /** @@ -3315,7 +3318,7 @@ void BKE_mesh_polygons_flip( int i; for (mp = mpoly, i = 0; i < totpoly; mp++, i++) { - BKE_mesh_polygon_flip_ex(mp, mloop, ldata, mdisp, true); + BKE_mesh_polygon_flip_ex(mp, mloop, ldata, NULL, mdisp, true); } } diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 03348adeabc..2468cb8b697 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -347,3 +347,10 @@ void BKE_object_eval_uber_data(EvaluationContext *eval_ctx, ob->recalc &= ~(OB_RECALC_DATA | OB_RECALC_TIME); } + +void BKE_object_eval_proxy_backlink(EvaluationContext *UNUSED(eval_ctx), Object *ob) +{ + if (ob->proxy) { + ob->proxy->proxy_from = ob; + } +} diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index af30c1af66b..106720c4f1f 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -141,6 +141,11 @@ int count_particles_mod(ParticleSystem *psys, int totgr, int cur) #define PATH_CACHE_BUF_SIZE 1024 +static ParticleCacheKey *pcache_key_segment_endpoint_safe(ParticleCacheKey *key) +{ + return (key->segments > 0) ? (key + (key->segments - 1)) : key; +} + static ParticleCacheKey **psys_alloc_path_cache_buffers(ListBase *bufs, int tot, int totkeys) { LinkData *buf; @@ -2205,20 +2210,22 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp /* modify weights to create parting */ if (p_fac > 0.f) { + const ParticleCacheKey *key_0_last = pcache_key_segment_endpoint_safe(key[0]); for (w = 0; w < 4; w++) { - if (w && weight[w] > 0.f) { + if (w && (weight[w] > 0.f)) { + const ParticleCacheKey *key_w_last = pcache_key_segment_endpoint_safe(key[w]); float d; if (part->flag & PART_CHILD_LONG_HAIR) { /* For long hair use tip distance/root distance as parting factor instead of root to tip angle. */ float d1 = len_v3v3(key[0]->co, key[w]->co); - float d2 = len_v3v3((key[0] + key[0]->segments - 1)->co, (key[w] + key[w]->segments - 1)->co); + float d2 = len_v3v3(key_0_last->co, key_w_last->co); d = d1 > 0.f ? d2 / d1 - 1.f : 10000.f; } else { float v1[3], v2[3]; - sub_v3_v3v3(v1, (key[0] + key[0]->segments - 1)->co, key[0]->co); - sub_v3_v3v3(v2, (key[w] + key[w]->segments - 1)->co, key[w]->co); + sub_v3_v3v3(v1, key_0_last->co, key[0]->co); + sub_v3_v3v3(v2, key_w_last->co, key[w]->co); normalize_v3(v1); normalize_v3(v2); diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index d73f087a3fe..58ec75dc706 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -637,6 +637,7 @@ void BKE_pbvh_free(PBVH *bvh) BLI_gset_free(node->bm_other_verts, NULL); } } + GPU_free_pbvh_buffer_multires(&bvh->grid_common_gpu_buffer); if (bvh->deformed) { if (bvh->verts) { @@ -1100,7 +1101,7 @@ static void pbvh_update_draw_buffers(PBVH *bvh, PBVHNode **nodes, int totnode) node->totprim, bvh->grid_hidden, bvh->gridkey.grid_size, - &bvh->gridkey); + &bvh->gridkey, &bvh->grid_common_gpu_buffer); break; case PBVH_FACES: node->draw_buffers = diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index bae323dedef..4d2307c3e12 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -145,6 +145,11 @@ struct PBVH { const DMFlagMat *grid_flag_mats; int totgrid; BLI_bitmap **grid_hidden; + /* index_buf of GPU_PBVH_Buffers can be the same for all 'fully drawn' nodes (same size). + * Previously was stored in a static var in gpu_buffer.c, but this breaks in case we handle several different + * objects in sculpt mode with different sizes at the same time, so now storing that common gpu buffer + * in an opaque pointer per pbvh. See T47637. */ + struct GridCommonGPUBuffer *grid_common_gpu_buffer; /* Only used during BVH build and update, * don't need to remain valid after */ diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index d307ba1811b..a3393b6b9c0 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -2193,6 +2193,13 @@ bool BKE_scene_use_shading_nodes_custom(Scene *scene) return (type && type->flag & RE_USE_SHADING_NODES_CUSTOM); } +bool BKE_scene_use_world_space_shading(Scene *scene) +{ + const RenderEngineType *type = RE_engines_find(scene->r.engine); + return ((scene->r.mode & R_USE_WS_SHADING) || + (type && (type->flag & RE_USE_SHADING_NODES))); +} + bool BKE_scene_use_spherical_stereo(Scene *scene) { RenderEngineType *type = RE_engines_find(scene->r.engine); diff --git a/source/blender/blenkernel/intern/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c index e855f6faa22..518d8d68919 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.c +++ b/source/blender/blenkernel/intern/shrinkwrap.c @@ -44,6 +44,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_task.h" #include "BKE_shrinkwrap.h" #include "BKE_DerivedMesh.h" @@ -58,7 +59,7 @@ /* for timing... */ #if 0 -# include "PIL_time.h" +# include "PIL_time_utildefines.h" #else # define TIMEIT_BENCH(expr, id) (expr) #endif @@ -66,16 +67,88 @@ /* Util macros */ #define OUT_OF_MEMORY() ((void)printf("Shrinkwrap: Out of memory\n")) +typedef struct ShrinkwrapCalcCBData { + ShrinkwrapCalcData *calc; + + void *treeData; + void *auxData; + BVHTree *targ_tree; + BVHTree *aux_tree; + void *targ_callback; + void *aux_callback; + + float *proj_axis; + SpaceTransform *local2aux; +} ShrinkwrapCalcCBData; + /* * Shrinkwrap to the nearest vertex * * it builds a kdtree of vertexs we can attach to and then * for each vertex performs a nearest vertex search on the tree */ -static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc) +static void shrinkwrap_calc_nearest_vertex_cb_ex( + void *userdata, void *userdata_chunk, const int i, const int UNUSED(threadid)) { - int i; + ShrinkwrapCalcCBData *data = userdata; + + ShrinkwrapCalcData *calc = data->calc; + BVHTreeFromMesh *treeData = data->treeData; + BVHTreeNearest *nearest = userdata_chunk; + + float *co = calc->vertexCos[i]; + float tmp_co[3]; + float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); + + if (calc->invert_vgroup) { + weight = 1.0f - weight; + } + + if (weight == 0.0f) { + return; + } + + /* Convert the vertex to tree coordinates */ + if (calc->vert) { + copy_v3_v3(tmp_co, calc->vert[i].co); + } + else { + copy_v3_v3(tmp_co, co); + } + BLI_space_transform_apply(&calc->local2target, tmp_co); + + /* Use local proximity heuristics (to reduce the nearest search) + * + * If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex + * so we can initiate the "nearest.dist" with the expected value to that last hit. + * This will lead in pruning of the search tree. */ + if (nearest->index != -1) + nearest->dist_sq = len_squared_v3v3(tmp_co, nearest->co); + else + nearest->dist_sq = FLT_MAX; + + BLI_bvhtree_find_nearest(treeData->tree, tmp_co, nearest, treeData->nearest_callback, treeData); + + + /* Found the nearest vertex */ + if (nearest->index != -1) { + /* Adjusting the vertex weight, + * so that after interpolating it keeps a certain distance from the nearest position */ + if (nearest->dist_sq > FLT_EPSILON) { + const float dist = sqrtf(nearest->dist_sq); + weight *= (dist - calc->keepDist) / dist; + } + + /* Convert the coordinates back to mesh coordinates */ + copy_v3_v3(tmp_co, nearest->co); + BLI_space_transform_invert(&calc->local2target, tmp_co); + + interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */ + } +} +static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc) +{ BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; BVHTreeNearest nearest = NULL_BVHTreeNearest; @@ -89,61 +162,11 @@ static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc) /* Setup nearest */ nearest.index = -1; nearest.dist_sq = FLT_MAX; -#ifndef __APPLE__ -#pragma omp parallel for default(none) private(i) firstprivate(nearest) shared(treeData, calc) schedule(static) if (calc->numVerts > BKE_MESH_OMP_LIMIT) -#endif - for (i = 0; i < calc->numVerts; ++i) { - float *co = calc->vertexCos[i]; - float tmp_co[3]; - float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); - - if (calc->invert_vgroup) { - weight = 1.0f - weight; - } - - if (weight == 0.0f) { - continue; - } - - - /* Convert the vertex to tree coordinates */ - if (calc->vert) { - copy_v3_v3(tmp_co, calc->vert[i].co); - } - else { - copy_v3_v3(tmp_co, co); - } - BLI_space_transform_apply(&calc->local2target, tmp_co); - - /* Use local proximity heuristics (to reduce the nearest search) - * - * If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex - * so we can initiate the "nearest.dist" with the expected value to that last hit. - * This will lead in pruning of the search tree. */ - if (nearest.index != -1) - nearest.dist_sq = len_squared_v3v3(tmp_co, nearest.co); - else - nearest.dist_sq = FLT_MAX; - - BLI_bvhtree_find_nearest(treeData.tree, tmp_co, &nearest, treeData.nearest_callback, &treeData); - - - /* Found the nearest vertex */ - if (nearest.index != -1) { - /* Adjusting the vertex weight, - * so that after interpolating it keeps a certain distance from the nearest position */ - if (nearest.dist_sq > FLT_EPSILON) { - const float dist = sqrtf(nearest.dist_sq); - weight *= (dist - calc->keepDist) / dist; - } - /* Convert the coordinates back to mesh coordinates */ - copy_v3_v3(tmp_co, nearest.co); - BLI_space_transform_invert(&calc->local2target, tmp_co); - - interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */ - } - } + ShrinkwrapCalcCBData data = {.calc = calc, .treeData = &treeData}; + BLI_task_parallel_range_ex( + 0, calc->numVerts, &data, &nearest, sizeof(nearest), shrinkwrap_calc_nearest_vertex_cb_ex, + calc->numVerts > BKE_MESH_OMP_LIMIT, false); free_bvhtree_from_mesh(&treeData); } @@ -230,13 +253,109 @@ bool BKE_shrinkwrap_project_normal( return false; } +static void shrinkwrap_calc_normal_projection_cb_ex( + void *userdata, void *userdata_chunk, const int i, const int UNUSED(threadid)) +{ + ShrinkwrapCalcCBData *data = userdata; + + ShrinkwrapCalcData *calc = data->calc; + void *treeData = data->treeData; + void *auxData = data->auxData; + BVHTree *targ_tree = data->targ_tree; + BVHTree *aux_tree = data->aux_tree; + void *targ_callback = data->targ_callback; + void *aux_callback = data->aux_callback; + + float *proj_axis = data->proj_axis; + SpaceTransform *local2aux = data->local2aux; + + BVHTreeRayHit *hit = userdata_chunk; + + const float proj_limit_squared = calc->smd->projLimit * calc->smd->projLimit; + float *co = calc->vertexCos[i]; + float tmp_co[3], tmp_no[3]; + float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); + + if (calc->invert_vgroup) { + weight = 1.0f - weight; + } + + if (weight == 0.0f) { + return; + } + + if (calc->vert) { + /* calc->vert contains verts from derivedMesh */ + /* this coordinated are deformed by vertexCos only for normal projection (to get correct normals) */ + /* for other cases calc->varts contains undeformed coordinates and vertexCos should be used */ + if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { + copy_v3_v3(tmp_co, calc->vert[i].co); + normal_short_to_float_v3(tmp_no, calc->vert[i].no); + } + else { + copy_v3_v3(tmp_co, co); + copy_v3_v3(tmp_no, proj_axis); + } + } + else { + copy_v3_v3(tmp_co, co); + copy_v3_v3(tmp_no, proj_axis); + } + + + hit->index = -1; + hit->dist = BVH_RAYCAST_DIST_MAX; /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */ + + /* Project over positive direction of axis */ + if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) { + if (aux_tree) { + BKE_shrinkwrap_project_normal( + 0, tmp_co, tmp_no, + local2aux, aux_tree, hit, + aux_callback, auxData); + } + + BKE_shrinkwrap_project_normal( + calc->smd->shrinkOpts, tmp_co, tmp_no, + &calc->local2target, targ_tree, hit, + targ_callback, treeData); + } + + /* Project over negative direction of axis */ + if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR) { + float inv_no[3]; + negate_v3_v3(inv_no, tmp_no); + + if (aux_tree) { + BKE_shrinkwrap_project_normal( + 0, tmp_co, inv_no, + local2aux, aux_tree, hit, + aux_callback, auxData); + } + + BKE_shrinkwrap_project_normal( + calc->smd->shrinkOpts, tmp_co, inv_no, + &calc->local2target, targ_tree, hit, + targ_callback, treeData); + } + + /* don't set the initial dist (which is more efficient), + * because its calculated in the targets space, we want the dist in our own space */ + if (proj_limit_squared != 0.0f) { + if (len_squared_v3v3(hit->co, co) > proj_limit_squared) { + hit->index = -1; + } + } + + if (hit->index != -1) { + madd_v3_v3v3fl(hit->co, hit->co, tmp_no, calc->keepDist); + interp_v3_v3v3(co, co, hit->co, weight); + } +} static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for_render) { - int i; - /* Options about projection direction */ - const float proj_limit_squared = calc->smd->projLimit * calc->smd->projLimit; float proj_axis[3] = {0.0f, 0.0f, 0.0f}; /* Raycast and tree stuff */ @@ -305,7 +424,7 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for } if (targ_tree) { BVHTree *aux_tree = NULL; - void *aux_callback; + void *aux_callback = NULL; if (auxMesh != NULL) { /* use editmesh to avoid array allocation */ if (calc->smd->auxTarget && auxMesh->type == DM_TYPE_EDITBMESH) { @@ -316,99 +435,22 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for } } else { - if ((aux_tree = bvhtree_from_mesh_looptri(&dmauxdata_stack, calc->target, 0.0, 4, 6)) != NULL) { + if ((aux_tree = bvhtree_from_mesh_looptri(&dmauxdata_stack, auxMesh, 0.0, 4, 6)) != NULL) { aux_callback = dmauxdata_stack.raycast_callback; auxData = &dmauxdata_stack; } } } /* After sucessufuly build the trees, start projection vertexs */ - -#ifndef __APPLE__ -#pragma omp parallel for private(i, hit) schedule(static) if (calc->numVerts > BKE_MESH_OMP_LIMIT) -#endif - for (i = 0; i < calc->numVerts; ++i) { - float *co = calc->vertexCos[i]; - float tmp_co[3], tmp_no[3]; - float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); - - if (calc->invert_vgroup) { - weight = 1.0f - weight; - } - - if (weight == 0.0f) { - continue; - } - - if (calc->vert) { - /* calc->vert contains verts from derivedMesh */ - /* this coordinated are deformed by vertexCos only for normal projection (to get correct normals) */ - /* for other cases calc->varts contains undeformed coordinates and vertexCos should be used */ - if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) { - copy_v3_v3(tmp_co, calc->vert[i].co); - normal_short_to_float_v3(tmp_no, calc->vert[i].no); - } - else { - copy_v3_v3(tmp_co, co); - copy_v3_v3(tmp_no, proj_axis); - } - } - else { - copy_v3_v3(tmp_co, co); - copy_v3_v3(tmp_no, proj_axis); - } - - - hit.index = -1; - hit.dist = BVH_RAYCAST_DIST_MAX; /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */ - - /* Project over positive direction of axis */ - if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) { - - if (aux_tree) { - BKE_shrinkwrap_project_normal( - 0, tmp_co, tmp_no, - &local2aux, aux_tree, &hit, - aux_callback, auxData); - } - - BKE_shrinkwrap_project_normal( - calc->smd->shrinkOpts, tmp_co, tmp_no, - &calc->local2target, targ_tree, &hit, - targ_callback, treeData); - } - - /* Project over negative direction of axis */ - if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR) { - float inv_no[3]; - negate_v3_v3(inv_no, tmp_no); - - if (aux_tree) { - BKE_shrinkwrap_project_normal( - 0, tmp_co, inv_no, - &local2aux, aux_tree, &hit, - aux_callback, auxData); - } - - BKE_shrinkwrap_project_normal( - calc->smd->shrinkOpts, tmp_co, inv_no, - &calc->local2target, targ_tree, &hit, - targ_callback, treeData); - } - - /* don't set the initial dist (which is more efficient), - * because its calculated in the targets space, we want the dist in our own space */ - if (proj_limit_squared != 0.0f) { - if (len_squared_v3v3(hit.co, co) > proj_limit_squared) { - hit.index = -1; - } - } - - if (hit.index != -1) { - madd_v3_v3v3fl(hit.co, hit.co, tmp_no, calc->keepDist); - interp_v3_v3v3(co, co, hit.co, weight); - } - } + ShrinkwrapCalcCBData data = { + .calc = calc, + .treeData = treeData, .targ_tree = targ_tree, .targ_callback = targ_callback, + .auxData = auxData, .aux_tree = aux_tree, .aux_callback = aux_callback, + .proj_axis = proj_axis, .local2aux = &local2aux, + }; + BLI_task_parallel_range_ex( + 0, calc->numVerts, &data, &hit, sizeof(hit), shrinkwrap_calc_normal_projection_cb_ex, + calc->numVerts > BKE_MESH_OMP_LIMIT, false); } /* free data structures */ @@ -428,10 +470,75 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc, bool for * it builds a BVHTree from the target mesh and then performs a * NN matches for each vertex */ -static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) +static void shrinkwrap_calc_nearest_surface_point_cb_ex( + void *userdata, void *userdata_chunk, const int i, const int UNUSED(threadid)) { - int i; + ShrinkwrapCalcCBData *data = userdata; + + ShrinkwrapCalcData *calc = data->calc; + BVHTreeFromMesh *treeData = data->treeData; + BVHTreeNearest *nearest = userdata_chunk; + + float *co = calc->vertexCos[i]; + float tmp_co[3]; + float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); + + if (calc->invert_vgroup) { + weight = 1.0f - weight; + } + + if (weight == 0.0f) { + return; + } + + /* Convert the vertex to tree coordinates */ + if (calc->vert) { + copy_v3_v3(tmp_co, calc->vert[i].co); + } + else { + copy_v3_v3(tmp_co, co); + } + BLI_space_transform_apply(&calc->local2target, tmp_co); + + /* Use local proximity heuristics (to reduce the nearest search) + * + * If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex + * so we can initiate the "nearest.dist" with the expected value to that last hit. + * This will lead in pruning of the search tree. */ + if (nearest->index != -1) + nearest->dist_sq = len_squared_v3v3(tmp_co, nearest->co); + else + nearest->dist_sq = FLT_MAX; + + BLI_bvhtree_find_nearest(treeData->tree, tmp_co, nearest, treeData->nearest_callback, treeData); + + /* Found the nearest vertex */ + if (nearest->index != -1) { + if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE) { + /* Make the vertex stay on the front side of the face */ + madd_v3_v3v3fl(tmp_co, nearest->co, nearest->no, calc->keepDist); + } + else { + /* Adjusting the vertex weight, + * so that after interpolating it keeps a certain distance from the nearest position */ + const float dist = sasqrt(nearest->dist_sq); + if (dist > FLT_EPSILON) { + /* linear interpolation */ + interp_v3_v3v3(tmp_co, tmp_co, nearest->co, (dist - calc->keepDist) / dist); + } + else { + copy_v3_v3(tmp_co, nearest->co); + } + } + /* Convert the coordinates back to mesh coordinates */ + BLI_space_transform_invert(&calc->local2target, tmp_co); + interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */ + } +} + +static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) +{ BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; BVHTreeNearest nearest = NULL_BVHTreeNearest; @@ -446,67 +553,11 @@ static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) nearest.index = -1; nearest.dist_sq = FLT_MAX; - /* Find the nearest vertex */ -#ifndef __APPLE__ -#pragma omp parallel for default(none) private(i) firstprivate(nearest) shared(calc, treeData) schedule(static) if (calc->numVerts > BKE_MESH_OMP_LIMIT) -#endif - for (i = 0; i < calc->numVerts; ++i) { - float *co = calc->vertexCos[i]; - float tmp_co[3]; - float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup); - - if (calc->invert_vgroup) { - weight = 1.0f - weight; - } - - if (weight == 0.0f) continue; - - /* Convert the vertex to tree coordinates */ - if (calc->vert) { - copy_v3_v3(tmp_co, calc->vert[i].co); - } - else { - copy_v3_v3(tmp_co, co); - } - BLI_space_transform_apply(&calc->local2target, tmp_co); - - /* Use local proximity heuristics (to reduce the nearest search) - * - * If we already had an hit before.. we assume this vertex is going to have a close hit to that other vertex - * so we can initiate the "nearest.dist" with the expected value to that last hit. - * This will lead in pruning of the search tree. */ - if (nearest.index != -1) - nearest.dist_sq = len_squared_v3v3(tmp_co, nearest.co); - else - nearest.dist_sq = FLT_MAX; - - BLI_bvhtree_find_nearest(treeData.tree, tmp_co, &nearest, treeData.nearest_callback, &treeData); - - /* Found the nearest vertex */ - if (nearest.index != -1) { - if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE) { - /* Make the vertex stay on the front side of the face */ - madd_v3_v3v3fl(tmp_co, nearest.co, nearest.no, calc->keepDist); - } - else { - /* Adjusting the vertex weight, - * so that after interpolating it keeps a certain distance from the nearest position */ - const float dist = sasqrt(nearest.dist_sq); - if (dist > FLT_EPSILON) { - /* linear interpolation */ - interp_v3_v3v3(tmp_co, tmp_co, nearest.co, (dist - calc->keepDist) / dist); - } - else { - copy_v3_v3(tmp_co, nearest.co); - } - } - - /* Convert the coordinates back to mesh coordinates */ - BLI_space_transform_invert(&calc->local2target, tmp_co); - interp_v3_v3v3(co, co, tmp_co, weight); /* linear interpolation */ - } - } + ShrinkwrapCalcCBData data = {.calc = calc, .treeData = &treeData}; + BLI_task_parallel_range_ex( + 0, calc->numVerts, &data, &nearest, sizeof(nearest), shrinkwrap_calc_nearest_surface_point_cb_ex, + calc->numVerts > BKE_MESH_OMP_LIMIT, false); free_bvhtree_from_mesh(&treeData); } diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 5fd418fadfc..88bc3fb9854 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -2996,6 +2996,7 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, if (matconv[a].attribs.totorco && matconv[a].attribs.orco.array) { matconv[a].datatypes[numdata].index = matconv[a].attribs.orco.gl_index; + matconv[a].datatypes[numdata].info_index = matconv[a].attribs.orco.gl_info_index; matconv[a].datatypes[numdata].size = 3; matconv[a].datatypes[numdata].type = GL_FLOAT; numdata++; @@ -3003,6 +3004,7 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, for (b = 0; b < matconv[a].attribs.tottface; b++) { if (matconv[a].attribs.tface[b].array) { matconv[a].datatypes[numdata].index = matconv[a].attribs.tface[b].gl_index; + matconv[a].datatypes[numdata].info_index = matconv[a].attribs.tface[b].gl_info_index; matconv[a].datatypes[numdata].size = 2; matconv[a].datatypes[numdata].type = GL_FLOAT; numdata++; @@ -3011,6 +3013,7 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, for (b = 0; b < matconv[a].attribs.totmcol; b++) { if (matconv[a].attribs.mcol[b].array) { matconv[a].datatypes[numdata].index = matconv[a].attribs.mcol[b].gl_index; + matconv[a].datatypes[numdata].info_index = matconv[a].attribs.mcol[b].gl_info_index; matconv[a].datatypes[numdata].size = 4; matconv[a].datatypes[numdata].type = GL_UNSIGNED_BYTE; numdata++; @@ -3019,6 +3022,7 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, for (b = 0; b < matconv[a].attribs.tottang; b++) { if (matconv[a].attribs.tottang && matconv[a].attribs.tang[b].array) { matconv[a].datatypes[numdata].index = matconv[a].attribs.tang[b].gl_index; + matconv[a].datatypes[numdata].info_index = matconv[a].attribs.tang[b].gl_info_index; matconv[a].datatypes[numdata].size = 4; matconv[a].datatypes[numdata].type = GL_FLOAT; numdata++; diff --git a/source/blender/blenlib/BLI_array_store.h b/source/blender/blenlib/BLI_array_store.h new file mode 100644 index 00000000000..f4cbc07bf26 --- /dev/null +++ b/source/blender/blenlib/BLI_array_store.h @@ -0,0 +1,66 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BLI_ARRAY_STORE_H__ +#define __BLI_ARRAY_STORE_H__ + +/** \file BLI_array_store.h + * \ingroup bli + * \brief Efficient in-memory storage of multiple similar arrays. + */ + +typedef struct BArrayStore BArrayStore; +typedef struct BArrayState BArrayState; + +BArrayStore *BLI_array_store_create( + unsigned int stride, unsigned int chunk_count); +void BLI_array_store_destroy( + BArrayStore *bs); +void BLI_array_store_clear( + BArrayStore *bs); + +/* find the memory used by all states (expanded & real) */ +size_t BLI_array_store_calc_size_expanded_get( + const BArrayStore *bs); +size_t BLI_array_store_calc_size_compacted_get( + const BArrayStore *bs); + +BArrayState *BLI_array_store_state_add( + BArrayStore *bs, + const void *data, const size_t data_len, + const BArrayState *state_reference); +void BLI_array_store_state_remove( + BArrayStore *bs, + BArrayState *state); + +size_t BLI_array_store_state_size_get( + BArrayState *state); +void BLI_array_store_state_data_get( + BArrayState *state, + void *data); +void *BLI_array_store_state_data_get_alloc( + BArrayState *state, + size_t *r_data_len); + +/* only for tests */ +bool BLI_array_store_is_valid( + BArrayStore *bs); + +#endif /* __BLI_ARRAY_STORE_H__ */ diff --git a/source/blender/blenlib/BLI_stackdefines.h b/source/blender/blenlib/BLI_stackdefines.h index b26dc3e26aa..42b11eb9a2b 100644 --- a/source/blender/blenlib/BLI_stackdefines.h +++ b/source/blender/blenlib/BLI_stackdefines.h @@ -31,28 +31,28 @@ /* only validate array-bounds in debug mode */ #ifdef DEBUG # define STACK_DECLARE(stack) unsigned int _##stack##_index, _##stack##_totalloc -# define STACK_INIT(stack, tot) ((void)stack, (void)((_##stack##_index) = 0), (void)((_##stack##_totalloc) = tot)) -# define _STACK_SIZETEST(stack, off) (BLI_assert((_##stack##_index) + off <= _##stack##_totalloc)) +# define STACK_INIT(stack, tot) ((void)stack, (void)((_##stack##_index) = 0), (void)((_##stack##_totalloc) = (tot))) +# define _STACK_SIZETEST(stack, off) (BLI_assert((_##stack##_index) + (off) <= _##stack##_totalloc)) # define _STACK_SWAP_TOTALLOC(stack_a, stack_b) SWAP(unsigned int, _##stack_a##_totalloc, _##stack_b##_totalloc) #else # define STACK_DECLARE(stack) unsigned int _##stack##_index -# define STACK_INIT(stack, tot) ((void)stack, (void)((_##stack##_index) = 0), (void)(0 ? tot : 0)) +# define STACK_INIT(stack, tot) ((void)stack, (void)((_##stack##_index) = 0), (void)(0 ? (tot) : 0)) # define _STACK_SIZETEST(stack, off) (void)(stack), (void)(off) # define _STACK_SWAP_TOTALLOC(stack_a, stack_b) (void)(stack_a), (void)(stack_b) #endif -#define _STACK_BOUNDSTEST(stack, index) ((void)stack, BLI_assert((unsigned int)index < _##stack##_index)) +#define _STACK_BOUNDSTEST(stack, index) ((void)stack, BLI_assert((unsigned int)(index) < _##stack##_index)) #define STACK_SIZE(stack) ((void)stack, (_##stack##_index)) #define STACK_CLEAR(stack) {(void)stack; _##stack##_index = 0; } ((void)0) /** add item to stack */ -#define STACK_PUSH(stack, val) ((void)stack, _STACK_SIZETEST(stack, 1), ((stack)[(_##stack##_index)++] = val)) +#define STACK_PUSH(stack, val) ((void)stack, _STACK_SIZETEST(stack, 1), ((stack)[(_##stack##_index)++] = (val))) #define STACK_PUSH_RET(stack) ((void)stack, _STACK_SIZETEST(stack, 1), ((stack)[(_##stack##_index)++])) #define STACK_PUSH_RET_PTR(stack) ((void)stack, _STACK_SIZETEST(stack, 1), &((stack)[(_##stack##_index)++])) /** take last item from stack */ #define STACK_POP(stack) ((_##stack##_index) ? ((stack)[--(_##stack##_index)]) : NULL) #define STACK_POP_PTR(stack) ((_##stack##_index) ? &((stack)[--(_##stack##_index)]) : NULL) -#define STACK_POP_DEFAULT(stack, r) ((_##stack##_index) ? ((stack)[--(_##stack##_index)]) : r) +#define STACK_POP_DEFAULT(stack, r) ((_##stack##_index) ? ((stack)[--(_##stack##_index)]) : (r)) /** look at last item (assumes non-empty stack) */ #define STACK_PEEK(stack) (BLI_assert(_##stack##_index), ((stack)[_##stack##_index - 1])) #define STACK_PEEK_PTR(stack) (BLI_assert(_##stack##_index), &((stack)[_##stack##_index - 1])) diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 64ec9fb745d..9c0731ce612 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -52,6 +52,7 @@ set(SRC intern/BLI_memarena.c intern/BLI_mempool.c intern/DLRB_tree.c + intern/array_store.c intern/array_utils.c intern/astar.c intern/boxpack2d.c @@ -121,6 +122,7 @@ set(SRC BLI_alloca.h BLI_args.h BLI_array.h + BLI_array_store.h BLI_array_utils.h BLI_astar.h BLI_bitmap.h diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c index 05f2d9221ef..06946e520a8 100644 --- a/source/blender/blenlib/intern/BLI_ghash.c +++ b/source/blender/blenlib/intern/BLI_ghash.c @@ -1287,7 +1287,7 @@ bool BLI_ghashutil_paircmp(const void *a, const void *b) const GHashPair *A = a; const GHashPair *B = b; - return (BLI_ghashutil_ptrcmp(A->first, B->first) && + return (BLI_ghashutil_ptrcmp(A->first, B->first) || BLI_ghashutil_ptrcmp(A->second, B->second)); } diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c index 7338804c685..b7a51f2c48e 100644 --- a/source/blender/blenlib/intern/BLI_mempool.c +++ b/source/blender/blenlib/intern/BLI_mempool.c @@ -57,12 +57,29 @@ #ifdef __BIG_ENDIAN__ /* Big Endian */ # define MAKE_ID(a, b, c, d) ( (int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d) ) +# define MAKE_ID_8(a, b, c, d, e, f, g, h) \ + ((int64_t)(a) << 56 | (int64_t)(b) << 48 | (int64_t)(c) << 40 | (int64_t)(d) << 32 | \ + (int64_t)(e) << 24 | (int64_t)(f) << 16 | (int64_t)(g) << 8 | (h) ) #else /* Little Endian */ # define MAKE_ID(a, b, c, d) ( (int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a) ) +# define MAKE_ID_8(a, b, c, d, e, f, g, h) \ + ((int64_t)(h) << 56 | (int64_t)(g) << 48 | (int64_t)(f) << 40 | (int64_t)(e) << 32 | \ + (int64_t)(d) << 24 | (int64_t)(c) << 16 | (int64_t)(b) << 8 | (a) ) #endif -#define FREEWORD MAKE_ID('f', 'r', 'e', 'e') +/** + * Important that this value is an is _not_ aligned with ``sizeof(void *)``. + * So having a pointer to 2/4/8... aligned memory is enough to ensure the freeword will never be used. + * To be safe, use a word thats the same in both directions. + */ +#define FREEWORD ((sizeof(void *) > sizeof(int32_t)) ? \ + MAKE_ID_8('e', 'e', 'r', 'f', 'f', 'r', 'e', 'e') : \ + MAKE_ID('e', 'f', 'f', 'e')) + +/** + * The 'used' word just needs to be set to something besides FREEWORD. + */ #define USEDWORD MAKE_ID('u', 's', 'e', 'd') /* currently totalloc isnt used */ @@ -87,7 +104,7 @@ static bool mempool_debug_memset = false; */ typedef struct BLI_freenode { struct BLI_freenode *next; - int freeword; /* used to identify this as a freed node */ + intptr_t freeword; /* used to identify this as a freed node */ } BLI_freenode; /** @@ -588,19 +605,26 @@ void *BLI_mempool_iterstep(BLI_mempool_iter *iter) */ void *BLI_mempool_iterstep(BLI_mempool_iter *iter) { - BLI_freenode *ret; + if (UNLIKELY(iter->curchunk == NULL)) { + return NULL; + } + const unsigned int esize = iter->pool->esize; + BLI_freenode *curnode = POINTER_OFFSET(CHUNK_DATA(iter->curchunk), (esize * iter->curindex)); + BLI_freenode *ret; do { - if (LIKELY(iter->curchunk)) { - ret = (BLI_freenode *)(((char *)CHUNK_DATA(iter->curchunk)) + (iter->pool->esize * iter->curindex)); + ret = curnode; + + if (++iter->curindex != iter->pool->pchunk) { + curnode = POINTER_OFFSET(curnode, esize); } else { - return NULL; - } - - if (UNLIKELY(++iter->curindex == iter->pool->pchunk)) { iter->curindex = 0; iter->curchunk = iter->curchunk->next; + if (iter->curchunk == NULL) { + return NULL; + } + curnode = CHUNK_DATA(iter->curchunk); } } while (ret->freeword == FREEWORD); diff --git a/source/blender/blenlib/intern/array_store.c b/source/blender/blenlib/intern/array_store.c new file mode 100644 index 00000000000..9baccf38fa3 --- /dev/null +++ b/source/blender/blenlib/intern/array_store.c @@ -0,0 +1,1812 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenlib/intern/array_store.c + * \ingroup bli + * \brief Array storage to minimize duplication. + * + * This is done by splitting arrays into chunks and using copy-on-write (COW), + * to de-duplicate chunks, + * from the users perspective this is an implementation detail. + * + * + * Overview + * ======== + * + * + * Data Structure + * -------------- + * + * This diagram is an overview of the structure of a single array-store. + * + * \note The only 2 structues here which are referenced externally are the. + * + * - BArrayStore: The whole array store. + * - BArrayState: Represents a single state (array) of data. + * These can be add using a reference state, while this could be considered the previous or parent state. + * no relationship is kept, so the caller is free to add any state from the same BArrayStore as a reference. + * + * <pre> + * <+> BArrayStore: root data-structure, + * | can store many 'states', which share memory. + * | + * | This can store many arrays, however they must share the same 'stride'. + * | Arrays of different types will need to use a new BArrayStore. + * | + * +- <+> states (Collection of BArrayState's): + * | | Each represents an array added by the user of this API. + * | | and references a chunk_list (each state is a chunk_list user). + * | | Note that the list order has no significance. + * | | + * | +- <+> chunk_list (BChunkList): + * | | The chunks that make up this state. + * | | Each state is a chunk_list user, + * | | avoids duplicating lists when there is no change between states. + * | | + * | +- chunk_refs (List of BChunkRef): Each chunk_ref links to a a BChunk. + * | Each reference is a chunk user, + * | avoids duplicating smaller chunks of memory found in multiple states. + * | + * +- info (BArrayInfo): + * | Sizes and offsets for this array-store. + * | Also caches some variables for reuse. + * | + * +- <+> memory (BArrayMemory): + * | Memory pools for storing BArrayStore data. + * | + * +- chunk_list (Pool of BChunkList): + * | All chunk_lists, (reference counted, used by BArrayState). + * | + * +- chunk_ref (Pool of BChunkRef): + * | All chunk_refs (link between BChunkList & BChunk). + * | + * +- chunks (Pool of BChunk): + * All chunks, (reference counted, used by BChunkList). + * These have their headers hashed for reuse so we can quickly check for duplicates. + * </pre> + * + * + * De-Duplication + * -------------- + * + * When creating a new state, a previous state can be given as a reference, + * matching chunks from this state are re-used in the new state. + * + * First matches at either end of the array are detected. + * For identical arrays this is all thats needed. + * + * De-duplication is performed on any remaining chunks, by hasing the first few bytes of the chunk + * (see: BCHUNK_HASH_TABLE_ACCUMULATE_STEPS). + * + * \note This is cached for reuse since the referenced data never changes. + * + * An array is created to store hash values at every 'stride', + * then stepped over to search for matching chunks. + * + * Once a match is found, there is a high chance next chunks match too, + * so this is checked to avoid performing so many hash-lookups. + * Otherwise new chunks are created. + */ + +#include <stdlib.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_mempool.h" + +#include "BLI_strict_flags.h" + +#include "BLI_array_store.h" /* own include */ + +/* only for BLI_array_store_is_valid */ +#include "BLI_ghash.h" + +/** \name Defines + * + * Some of the logic for merging is quite involved, + * support disabling some parts of this. + * \{ */ + +/* Scan first chunks (happy path when beginning of the array matches). + * When the array is a perfect match, we can re-use the entire list. + * + * Note that disabling makes some tests fail that check for output-size. + */ +#define USE_FASTPATH_CHUNKS_FIRST + +/* Scan last chunks (happy path when end of the array matches). + * When the end of the array matches, we can quickly add these chunks. + * note that we will add contiguous matching chunks + * so this isn't as useful as USE_FASTPATH_CHUNKS_FIRST, + * however it avoids adding matching chunks into the lookup table, + * so creating the lookup table won't be as expensive. + */ +#ifdef USE_FASTPATH_CHUNKS_FIRST +# define USE_FASTPATH_CHUNKS_LAST +#endif + +/* For arrays of matching length, test that *enough* of the chunks are aligned, + * and simply step over both arrays, using matching chunks. + * This avoids overhead of using a lookup table for cases when we can assume they're mostly aligned. + */ +#define USE_ALIGN_CHUNKS_TEST + +/* Accumulate hashes from right to left so we can create a hash for the chunk-start. + * This serves to increase uniqueness and will help when there is many values which are the same. + */ +#define USE_HASH_TABLE_ACCUMULATE + +#ifdef USE_HASH_TABLE_ACCUMULATE +/* Number of times to propagate hashes back. + * Effectively a 'triangle-number'. + * so 4 -> 7, 5 -> 10, 6 -> 15... etc. + */ +# define BCHUNK_HASH_TABLE_ACCUMULATE_STEPS 4 +#else +/* How many items to hash (multiplied by stride) + */ +# define BCHUNK_HASH_LEN 4 +#endif + +/* Calculate the key once and reuse it + */ +#define USE_HASH_TABLE_KEY_CACHE +#ifdef USE_HASH_TABLE_KEY_CACHE +# define HASH_TABLE_KEY_UNSET ((uint64_t)-1) +# define HASH_TABLE_KEY_FALLBACK ((uint64_t)-2) +#endif + +/* How much larger the table is then the total number of chunks. + */ +#define BCHUNK_HASH_TABLE_MUL 3 + +/* Merge too small/large chunks: + * + * Using this means chunks below a threshold will be merged together. + * Even though short term this uses more memory, + * long term the overhead of maintaining many small chunks is reduced. + * This is defined by setting the minimum chunk size (as a fraction of the regular chunk size). + * + * Chunks may also become too large (when incrementally growing an array), + * this also enables chunk splitting. + */ +#define USE_MERGE_CHUNKS + +#ifdef USE_MERGE_CHUNKS +/* Merge chunks smaller then: (chunk_size / BCHUNK_MIN_SIZE_DIV) + */ +# define BCHUNK_SIZE_MIN_DIV 8 + +/* Disallow chunks bigger then the regular chunk size scaled by this value + * note: must be at least 2! + * however, this code runs wont run in tests unless its ~1.1 ugh. + * so lower only to check splitting works. + */ +# define BCHUNK_SIZE_MAX_MUL 2 +#endif /* USE_MERGE_CHUNKS */ + +/* slow (keep disabled), but handy for debugging */ +// #define USE_VALIDATE_LIST_SIZE + +// #define USE_VALIDATE_LIST_DATA_PARTIAL + +// #define USE_PARANOID_CHECKS + +/** \} */ + + +/** \name Internal Structs + * \{ */ + +typedef unsigned int uint; +typedef unsigned char ubyte; + +typedef uint64_t hash_key; + + +typedef struct BArrayInfo { + size_t chunk_stride; + uint chunk_count; + + /* pre-calculated */ + size_t chunk_byte_size; + /* min/max limits (inclusive) */ + size_t chunk_byte_size_min; + size_t chunk_byte_size_max; + + size_t accum_read_ahead_bytes; +#ifdef USE_HASH_TABLE_ACCUMULATE + size_t accum_steps; + size_t accum_read_ahead_len; +#endif +} BArrayInfo; + +typedef struct BArrayMemory { + BLI_mempool *chunk_list; /* BChunkList */ + BLI_mempool *chunk_ref; /* BChunkRef */ + BLI_mempool *chunk; /* BChunk */ +} BArrayMemory; + +/** + * Main storage for all states + */ +typedef struct BArrayStore { + /* static */ + BArrayInfo info; + + /* memory storage */ + BArrayMemory memory; + + /** + * #BArrayState may be in any order (logic should never depend on state order). + */ + ListBase states; +} BArrayStore; + +/** + * A single instance of an array. + * + * This is how external API's hold a reference to an in-memory state, + * although the struct is private. + * + * \note Currently each 'state' is allocated separately. + * While this could be moved to a memory pool, + * it makes it easier to trace invalid usage, so leave as-is for now. + */ +typedef struct BArrayState { + /** linked list in #BArrayStore.states */ + struct BArrayState *next, *prev; + + struct BChunkList *chunk_list; /* BChunkList's */ + +} BArrayState; + +typedef struct BChunkList { + ListBase chunk_refs; /* BChunkRef's */ + uint chunk_refs_len; /* BLI_listbase_count(chunks), store for reuse. */ + size_t total_size; /* size of all chunks */ + + /** number of #BArrayState using this. */ + int users; +} BChunkList; + +/* a chunk of an array */ +typedef struct BChunk { + const ubyte *data; + size_t data_len; + /** number of #BChunkList using this. */ + int users; + +#ifdef USE_HASH_TABLE_KEY_CACHE + hash_key key; +#endif +} BChunk; + +/** + * Links to store #BChunk data in #BChunkList.chunks. + */ +typedef struct BChunkRef { + struct BChunkRef *next, *prev; + BChunk *link; +} BChunkRef; + +/** + * Single linked list used when putting chunks into a temporary table, + * used for lookups. + * + * Point to the #BChunkRef, not the #BChunk, + * to allow talking down the chunks in-order until a mis-match is found, + * this avoids having to do so many table lookups. + */ +typedef struct BTableRef { + struct BTableRef *next; + const BChunkRef *cref; +} BTableRef; + +/** \} */ + + +static size_t bchunk_list_size(const BChunkList *chunk_list); + + +/** \name Internal BChunk API + * \{ */ + +static BChunk *bchunk_new( + BArrayMemory *bs_mem, const ubyte *data, const size_t data_len) +{ + BChunk *chunk = BLI_mempool_alloc(bs_mem->chunk); + chunk->data = data; + chunk->data_len = data_len; + chunk->users = 0; +#ifdef USE_HASH_TABLE_KEY_CACHE + chunk->key = HASH_TABLE_KEY_UNSET; +#endif + return chunk; +} + +static BChunk *bchunk_new_copydata( + BArrayMemory *bs_mem, const ubyte *data, const size_t data_len) +{ + ubyte *data_copy = MEM_mallocN(data_len, __func__); + memcpy(data_copy, data, data_len); + return bchunk_new(bs_mem, data_copy, data_len); +} + +static void bchunk_decref( + BArrayMemory *bs_mem, BChunk *chunk) +{ + BLI_assert(chunk->users > 0); + if (chunk->users == 1) { + MEM_freeN((void *)chunk->data); + BLI_mempool_free(bs_mem->chunk, chunk); + } + else { + chunk->users -= 1; + } +} + +static bool bchunk_data_compare( + const BChunk *chunk, + const ubyte *data_base, const size_t data_base_len, + const size_t offset) +{ + if (offset + (size_t)chunk->data_len <= data_base_len) { + return (memcmp(&data_base[offset], chunk->data, chunk->data_len) == 0); + } + else { + return false; + } +} + +/** \} */ + + +/** \name Internal BChunkList API + * \{ */ + +static BChunkList *bchunk_list_new( + BArrayMemory *bs_mem, size_t total_size) +{ + BChunkList *chunk_list = BLI_mempool_alloc(bs_mem->chunk_list); + + BLI_listbase_clear(&chunk_list->chunk_refs); + chunk_list->chunk_refs_len = 0; + chunk_list->total_size = total_size; + chunk_list->users = 0; + return chunk_list; +} + +static void bchunk_list_decref( + BArrayMemory *bs_mem, BChunkList *chunk_list) +{ + BLI_assert(chunk_list->users > 0); + if (chunk_list->users == 1) { + for (BChunkRef *cref = chunk_list->chunk_refs.first, *cref_next; cref; cref = cref_next) { + cref_next = cref->next; + bchunk_decref(bs_mem, cref->link); + BLI_mempool_free(bs_mem->chunk_ref, cref); + } + + BLI_mempool_free(bs_mem->chunk_list, chunk_list); + } + else { + chunk_list->users -= 1; + } +} + +#ifdef USE_VALIDATE_LIST_SIZE +# ifndef NDEBUG +# define ASSERT_CHUNKLIST_SIZE(chunk_list, n) BLI_assert(bchunk_list_size(chunk_list) == n) +# endif +#endif +#ifndef ASSERT_CHUNKLIST_SIZE +# define ASSERT_CHUNKLIST_SIZE(chunk_list, n) (EXPR_NOP(chunk_list), EXPR_NOP(n)) +#endif + + +#ifdef USE_VALIDATE_LIST_DATA_PARTIAL +static size_t bchunk_list_data_check( + const BChunkList *chunk_list, const ubyte *data) +{ + size_t total_size = 0; + for (BChunkRef *cref = chunk_list->chunk_refs.first; cref; cref = cref->next) { + if (memcmp(&data[total_size], cref->link->data, cref->link->data_len) != 0) { + return false; + } + total_size += cref->link->data_len; + } + return true; +} +# define ASSERT_CHUNKLIST_DATA(chunk_list, data) BLI_assert(bchunk_list_data_check(chunk_list, data)) +#else +# define ASSERT_CHUNKLIST_DATA(chunk_list, data) (EXPR_NOP(chunk_list), EXPR_NOP(data)) +#endif + + +#ifdef USE_MERGE_CHUNKS +static void bchunk_list_ensure_min_size_last( + const BArrayInfo *info, BArrayMemory *bs_mem, + BChunkList *chunk_list) +{ + BChunkRef *cref = chunk_list->chunk_refs.last; + if (cref && cref->prev) { + /* both are decref'd after use (end of this block) */ + BChunk *chunk_curr = cref->link; + BChunk *chunk_prev = cref->prev->link; + + if (MIN2(chunk_prev->data_len, chunk_curr->data_len) < info->chunk_byte_size_min) { + const size_t data_merge_len = chunk_prev->data_len + chunk_curr->data_len; + /* we could pass, but no need */ + if (data_merge_len <= info->chunk_byte_size_max) { + /* we have enough space to merge */ + + /* remove last from linklist */ + BLI_assert(chunk_list->chunk_refs.last != chunk_list->chunk_refs.first); + cref->prev->next = NULL; + chunk_list->chunk_refs.last = cref->prev; + chunk_list->chunk_refs_len -= 1; + + ubyte *data_merge = MEM_mallocN(data_merge_len, __func__); + memcpy(data_merge, chunk_prev->data, chunk_prev->data_len); + memcpy(&data_merge[chunk_prev->data_len], chunk_curr->data, chunk_curr->data_len); + + cref->prev->link = bchunk_new(bs_mem, data_merge, data_merge_len); + cref->prev->link->users += 1; + + BLI_mempool_free(bs_mem->chunk_ref, cref); + } + else { + /* If we always merge small slices, we should _almost_ never end up having very large chunks. + * Gradual expanding on contracting will cause this. + * + * if we do, the code below works (test by setting 'BCHUNK_SIZE_MAX_MUL = 1.2') */ + + /* keep chunk on the left hand side a regular size */ + const size_t split = info->chunk_byte_size; + + /* merge and split */ + const size_t data_prev_len = split; + const size_t data_curr_len = data_merge_len - split; + ubyte *data_prev = MEM_mallocN(data_prev_len, __func__); + ubyte *data_curr = MEM_mallocN(data_curr_len, __func__); + + if (data_prev_len <= chunk_prev->data_len) { + const size_t data_curr_shrink_len = chunk_prev->data_len - data_prev_len; + + /* setup 'data_prev' */ + memcpy(data_prev, chunk_prev->data, data_prev_len); + + /* setup 'data_curr' */ + memcpy(data_curr, &chunk_prev->data[data_prev_len], data_curr_shrink_len); + memcpy(&data_curr[data_curr_shrink_len], chunk_curr->data, chunk_curr->data_len); + } + else { + BLI_assert(data_curr_len <= chunk_curr->data_len); + BLI_assert(data_prev_len >= chunk_prev->data_len); + + const size_t data_prev_grow_len = data_prev_len - chunk_prev->data_len; + + /* setup 'data_prev' */ + memcpy(data_prev, chunk_prev->data, chunk_prev->data_len); + memcpy(&data_prev[chunk_prev->data_len], chunk_curr->data, data_prev_grow_len); + + /* setup 'data_curr' */ + memcpy(data_curr, &chunk_curr->data[data_prev_grow_len], data_curr_len); + } + + cref->prev->link = bchunk_new(bs_mem, data_prev, data_prev_len); + cref->prev->link->users += 1; + + cref->link = bchunk_new(bs_mem, data_curr, data_curr_len); + cref->link->users += 1; + } + + /* free zero users */ + bchunk_decref(bs_mem, chunk_curr); + bchunk_decref(bs_mem, chunk_prev); + } + } +} +#endif /* USE_MERGE_CHUNKS */ + + +/** + * Split length into 2 values + * \param r_data_trim_len: Length which is aligned to the #BArrayInfo.chunk_byte_size + * \param r_data_last_chunk_len: The remaining bytes. + * + * \note This function ensures the size of \a r_data_last_chunk_len + * is larger than #BArrayInfo.chunk_byte_size_min. + */ +static void bchunk_list_calc_trim_len( + const BArrayInfo *info, const size_t data_len, + size_t *r_data_trim_len, size_t *r_data_last_chunk_len) +{ + size_t data_last_chunk_len = 0; + size_t data_trim_len = data_len; + +#ifdef USE_MERGE_CHUNKS + /* avoid creating too-small chunks + * more efficient then merging after */ + if (data_len > info->chunk_byte_size) { + data_last_chunk_len = (data_trim_len % info->chunk_byte_size); + data_trim_len = data_trim_len - data_last_chunk_len; + if (data_last_chunk_len) { + if (data_last_chunk_len < info->chunk_byte_size_min) { + /* may be zero and thats OK */ + data_trim_len -= info->chunk_byte_size; + data_last_chunk_len += info->chunk_byte_size; + } + } + } + else { + data_trim_len = 0; + data_last_chunk_len = data_len; + } + + BLI_assert((data_trim_len == 0) || (data_trim_len >= info->chunk_byte_size)); +#else + data_last_chunk_len = (data_trim_len % info->chunk_byte_size); + data_trim_len = data_trim_len - data_last_chunk_len; +#endif + + BLI_assert(data_trim_len + data_last_chunk_len == data_len); + + *r_data_trim_len = data_trim_len; + *r_data_last_chunk_len = data_last_chunk_len; +} + +/** + * Append and don't manage merging small chunks. + */ +static bool bchunk_list_append_only( + BArrayMemory *bs_mem, + BChunkList *chunk_list, BChunk *chunk) +{ + BChunkRef *cref = BLI_mempool_alloc(bs_mem->chunk_ref); + BLI_addtail(&chunk_list->chunk_refs, cref); + cref->link = chunk; + chunk_list->chunk_refs_len += 1; + chunk->users += 1; + return chunk; +} + +/** + * \note This is for writing single chunks, + * use #bchunk_list_append_data_n when writing large blocks of memory into many chunks. + */ +static void bchunk_list_append_data( + const BArrayInfo *info, BArrayMemory *bs_mem, + BChunkList *chunk_list, + const ubyte *data, const size_t data_len) +{ + BLI_assert(data_len != 0); + + // printf("data_len: %d\n", data_len); +#ifdef USE_MERGE_CHUNKS + BLI_assert(data_len <= info->chunk_byte_size_max); + + if (!BLI_listbase_is_empty(&chunk_list->chunk_refs)) { + BChunkRef *cref = chunk_list->chunk_refs.last; + BChunk *chunk_prev = cref->link; + + if (MIN2(chunk_prev->data_len, data_len) < info->chunk_byte_size_min) { + const size_t data_merge_len = chunk_prev->data_len + data_len; + /* realloc for single user */ + if (cref->link->users == 1) { + ubyte *data_merge = MEM_reallocN((void *)cref->link->data, data_merge_len); + memcpy(&data_merge[chunk_prev->data_len], data, data_len); + cref->link->data = data_merge; + cref->link->data_len = data_merge_len; + } + else { + ubyte *data_merge = MEM_mallocN(data_merge_len, __func__); + memcpy(data_merge, chunk_prev->data, chunk_prev->data_len); + memcpy(&data_merge[chunk_prev->data_len], data, data_len); + cref->link = bchunk_new(bs_mem, data_merge, data_merge_len); + cref->link->users += 1; + bchunk_decref(bs_mem, chunk_prev); + } + return; + } + } +#else + UNUSED_VARS(info); +#endif /* USE_MERGE_CHUNKS */ + + BChunk *chunk = bchunk_new_copydata(bs_mem, data, data_len); + bchunk_list_append_only(bs_mem, chunk_list, chunk); + + /* don't run this, instead preemptively avoid creating a chunk only to merge it (above). */ +#if 0 +#ifdef USE_MERGE_CHUNKS + bchunk_list_ensure_min_size_last(info, bs_mem, chunk_list, chunk_size_min); +#endif +#endif +} + +/** + * Similar to #bchunk_list_append_data, but handle multiple chunks. + * Use for adding arrays of arbitrary sized memory at once. + * + * \note This function takes care not to perform redundant chunk-merging checks, + * so we can write succesive fixed size chunks quickly. + */ +static void bchunk_list_append_data_n( + const BArrayInfo *info, BArrayMemory *bs_mem, + BChunkList *chunk_list, + const ubyte *data, size_t data_len) +{ + size_t data_trim_len, data_last_chunk_len; + bchunk_list_calc_trim_len(info, data_len, &data_trim_len, &data_last_chunk_len); + + if (data_trim_len != 0) { + size_t i_prev; + + { + const size_t i = info->chunk_byte_size; + bchunk_list_append_data(info, bs_mem, chunk_list, data, i); + i_prev = i; + } + + while (i_prev != data_trim_len) { + const size_t i = i_prev + info->chunk_byte_size; + BChunk *chunk = bchunk_new_copydata(bs_mem, &data[i_prev], i - i_prev); + bchunk_list_append_only(bs_mem, chunk_list, chunk); + i_prev = i; + } + + if (data_last_chunk_len) { + BChunk *chunk = bchunk_new_copydata(bs_mem, &data[i_prev], data_last_chunk_len); + bchunk_list_append_only(bs_mem, chunk_list, chunk); + // i_prev = data_len; /* UNUSED */ + } + } + else { + /* if we didn't write any chunks previously, + * we may need to merge with the last. */ + if (data_last_chunk_len) { + bchunk_list_append_data(info, bs_mem, chunk_list, data, data_last_chunk_len); + // i_prev = data_len; /* UNUSED */ + } + } + +#ifdef USE_MERGE_CHUNKS + if (data_len > info->chunk_byte_size) { + BLI_assert(((BChunkRef *)chunk_list->chunk_refs.last)->link->data_len >= info->chunk_byte_size_min); + } +#endif +} + +static void bchunk_list_append( + const BArrayInfo *info, BArrayMemory *bs_mem, + BChunkList *chunk_list, + BChunk *chunk) +{ + bchunk_list_append_only(bs_mem, chunk_list, chunk); + +#ifdef USE_MERGE_CHUNKS + bchunk_list_ensure_min_size_last(info, bs_mem, chunk_list); +#else + UNUSED_VARS(info); +#endif +} + +static void bchunk_list_fill_from_array( + const BArrayInfo *info, BArrayMemory *bs_mem, + BChunkList *chunk_list, + const ubyte *data, + const size_t data_len) +{ + BLI_assert(BLI_listbase_is_empty(&chunk_list->chunk_refs)); + + size_t data_trim_len, data_last_chunk_len; + bchunk_list_calc_trim_len(info, data_len, &data_trim_len, &data_last_chunk_len); + + size_t i_prev = 0; + while (i_prev != data_trim_len) { + const size_t i = i_prev + info->chunk_byte_size; + BChunk *chunk = bchunk_new_copydata(bs_mem, &data[i_prev], i - i_prev); + bchunk_list_append_only(bs_mem, chunk_list, chunk); + i_prev = i; + } + + if (data_last_chunk_len) { + BChunk *chunk = bchunk_new_copydata(bs_mem, &data[i_prev], data_last_chunk_len); + bchunk_list_append_only(bs_mem, chunk_list, chunk); + // i_prev = data_len; + } + +#ifdef USE_MERGE_CHUNKS + if (data_len > info->chunk_byte_size) { + BLI_assert(((BChunkRef *)chunk_list->chunk_refs.last)->link->data_len >= info->chunk_byte_size_min); + } +#endif + + /* works but better avoid redundant re-alloc */ +#if 0 +#ifdef USE_MERGE_CHUNKS + bchunk_list_ensure_min_size_last(info, bs_mem, chunk_list); +#endif +#endif + + ASSERT_CHUNKLIST_SIZE(chunk_list, data_len); + ASSERT_CHUNKLIST_DATA(chunk_list, data); +} + + +/* --------------------------------------------------------------------------- + * Internal Table Lookup Functions + */ + +/** \name Internal Hashing/De-Duplication API + * + * Only used by #bchunk_list_from_data_merge + * \{ */ + +#define HASH_INIT (5381) + +BLI_INLINE uint hash_data_single(const ubyte p) +{ + return (HASH_INIT << 5) + HASH_INIT + (unsigned int)p; +} + +/* hash bytes, from BLI_ghashutil_strhash_n */ +static uint hash_data(const ubyte *key, size_t n) +{ + const signed char *p; + unsigned int h = HASH_INIT; + + for (p = (const signed char *)key; n--; p++) { + h = (h << 5) + h + (unsigned int)*p; + } + + return h; +} + +#undef HASH_INIT + + +#ifdef USE_HASH_TABLE_ACCUMULATE +static void hash_array_from_data( + const BArrayInfo *info, const ubyte *data_slice, const size_t data_slice_len, + hash_key *hash_array) +{ + if (info->chunk_stride != 1) { + for (size_t i = 0, i_step = 0; i_step < data_slice_len; i++, i_step += info->chunk_stride) { + hash_array[i] = hash_data(&data_slice[i_step], info->chunk_stride); + } + } + else { + /* fast-path for bytes */ + for (size_t i = 0; i < data_slice_len; i++) { + hash_array[i] = hash_data_single(data_slice[i]); + } + } +} + +/* + * Similar to hash_array_from_data, + * but able to step into the next chunk if we run-out of data. + */ +static void hash_array_from_cref( + const BArrayInfo *info, const BChunkRef *cref, const size_t data_len, + hash_key *hash_array) +{ + const size_t hash_array_len = data_len / info->chunk_stride; + size_t i = 0; + do { + size_t i_next = hash_array_len - i; + size_t data_trim_len = i_next * info->chunk_stride; + if (data_trim_len > cref->link->data_len) { + data_trim_len = cref->link->data_len; + i_next = data_trim_len / info->chunk_stride; + } + BLI_assert(data_trim_len <= cref->link->data_len); + hash_array_from_data(info, cref->link->data, data_trim_len, &hash_array[i]); + i += i_next; + cref = cref->next; + } while ((i < hash_array_len) && (cref != NULL)); + + /* If this isn't equal, the caller didn't properly check + * that there was enough data left in all chunks */ + BLI_assert(i == hash_array_len); +} + +static void hash_accum(hash_key *hash_array, const size_t hash_array_len, size_t iter_steps) +{ + /* _very_ unlikely, can happen if you select a chunk-size of 1 for example. */ + if (UNLIKELY((iter_steps > hash_array_len))) { + iter_steps = hash_array_len; + } + + const size_t hash_array_search_len = hash_array_len - iter_steps; + while (iter_steps != 0) { + const size_t hash_offset = iter_steps; + for (uint i = 0; i < hash_array_search_len; i++) { + hash_array[i] += (hash_array[i + hash_offset]) * ((hash_array[i] & 0xff) + 1); + } + iter_steps -= 1; + } +} + +/** + * When we only need a single value, can use a small optimization. + * we can avoid accumulating the tail of the array a little, each iteration. + */ +static void hash_accum_single(hash_key *hash_array, const size_t hash_array_len, size_t iter_steps) +{ + BLI_assert(iter_steps <= hash_array_len); + if (UNLIKELY(!(iter_steps <= hash_array_len))) { + /* while this shouldn't happen, avoid crashing */ + iter_steps = hash_array_len; + } + /* We can increase this value each step to avoid accumulating quite as much + * while getting the same results as hash_accum */ + size_t iter_steps_sub = iter_steps; + + while (iter_steps != 0) { + const size_t hash_array_search_len = hash_array_len - iter_steps_sub; + const size_t hash_offset = iter_steps; + for (uint i = 0; i < hash_array_search_len; i++) { + hash_array[i] += (hash_array[i + hash_offset]) * ((hash_array[i] & 0xff) + 1); + } + iter_steps -= 1; + iter_steps_sub += iter_steps; + } +} + +static hash_key key_from_chunk_ref( + const BArrayInfo *info, const BChunkRef *cref, + /* avoid reallicating each time */ + hash_key *hash_store, const size_t hash_store_len) +{ + /* in C, will fill in a reusable array */ + BChunk *chunk = cref->link; + BLI_assert(info->accum_read_ahead_bytes * info->chunk_stride); + + if (info->accum_read_ahead_bytes <= chunk->data_len) { + hash_key key; + +#ifdef USE_HASH_TABLE_KEY_CACHE + key = chunk->key; + if (key != HASH_TABLE_KEY_UNSET) { + /* Using key cache! + * avoids calculating every time */ + } + else { + hash_array_from_cref(info, cref, info->accum_read_ahead_bytes, hash_store); + hash_accum_single(hash_store, hash_store_len, info->accum_steps); + key = hash_store[0]; + + /* cache the key */ + if (key == HASH_TABLE_KEY_UNSET) { + key = HASH_TABLE_KEY_FALLBACK; + } + chunk->key = key; + } +#else + hash_array_from_cref(info, cref, info->accum_read_ahead_bytes, hash_store); + hash_accum_single(hash_store, hash_store_len, info->accum_steps); + key = hash_store[0]; +#endif + return key; + } + else { + /* corner case - we're too small, calculate the key each time. */ + + hash_array_from_cref(info, cref, info->accum_read_ahead_bytes, hash_store); + hash_accum_single(hash_store, hash_store_len, info->accum_steps); + hash_key key = hash_store[0]; + +#ifdef USE_HASH_TABLE_KEY_CACHE + if (UNLIKELY(key == HASH_TABLE_KEY_UNSET)) { + key = HASH_TABLE_KEY_FALLBACK; + } +#endif + return key; + } +} + +static const BChunkRef *table_lookup( + const BArrayInfo *info, BTableRef **table, const size_t table_len, const size_t i_table_start, + const ubyte *data, const size_t data_len, const size_t offset, const hash_key *table_hash_array) +{ + size_t size_left = data_len - offset; + hash_key key = table_hash_array[((offset - i_table_start) / info->chunk_stride)]; + size_t key_index = (size_t)(key % (hash_key)table_len); + for (BTableRef *tref = table[key_index]; tref; tref = tref->next) { + const BChunkRef *cref = tref->cref; +#ifdef USE_HASH_TABLE_KEY_CACHE + if (cref->link->key == key) +#endif + { + BChunk *chunk_test = cref->link; + if (chunk_test->data_len <= size_left) { + if (bchunk_data_compare(chunk_test, data, data_len, offset)) { + /* we could remove the chunk from the table, to avoid multiple hits */ + return cref; + } + } + } + } + return NULL; +} + +#else /* USE_HASH_TABLE_ACCUMULATE */ + +/* NON USE_HASH_TABLE_ACCUMULATE code (simply hash each chunk) */ + +static hash_key key_from_chunk_ref(const BArrayInfo *info, const BChunkRef *cref) +{ + const size_t data_hash_len = BCHUNK_HASH_LEN * info->chunk_stride; + hash_key key; + BChunk *chunk = cref->link; + +#ifdef USE_HASH_TABLE_KEY_CACHE + key = chunk->key; + if (key != HASH_TABLE_KEY_UNSET) { + /* Using key cache! + * avoids calculating every time */ + } + else { + /* cache the key */ + key = hash_data(chunk->data, data_hash_len); + if (key == HASH_TABLE_KEY_UNSET) { + key = HASH_TABLE_KEY_FALLBACK; + } + chunk->key = key; + } +#else + key = hash_data(chunk->data, data_hash_len); +#endif + + return key; +} + +static const BChunkRef *table_lookup( + const BArrayInfo *info, BTableRef **table, const size_t table_len, const uint UNUSED(i_table_start), + const ubyte *data, const size_t data_len, const size_t offset, const hash_key *UNUSED(table_hash_array)) +{ + const size_t data_hash_len = BCHUNK_HASH_LEN * info->chunk_stride; /* TODO, cache */ + + size_t size_left = data_len - offset; + hash_key key = hash_data(&data[offset], MIN2(data_hash_len, size_left)); + size_t key_index = (size_t)(key % (hash_key)table_len); + for (BTableRef *tref = table[key_index]; tref; tref = tref->next) { + const BChunkRef *cref = tref->cref; +#ifdef USE_HASH_TABLE_KEY_CACHE + if (cref->link->key == key) +#endif + { + BChunk *chunk_test = cref->link; + if (chunk_test->data_len <= size_left) { + if (bchunk_data_compare(chunk_test, data, data_len, offset)) { + /* we could remove the chunk from the table, to avoid multiple hits */ + return cref; + } + } + } + } + return NULL; +} + +#endif /* USE_HASH_TABLE_ACCUMULATE */ + +/* End Table Lookup + * ---------------- */ + +/** \} */ + +/** + * \param data: Data to store in the returned value. + * \param data_len_original: Length of data in bytes. + * \param chunk_list_reference: Reuse this list or chunks within it, don't modify its content. + * \note Caller is responsible for adding the user. + */ +static BChunkList *bchunk_list_from_data_merge( + const BArrayInfo *info, BArrayMemory *bs_mem, + const ubyte *data, const size_t data_len_original, + const BChunkList *chunk_list_reference) +{ + ASSERT_CHUNKLIST_SIZE(chunk_list_reference, chunk_list_reference->total_size); + + /* ----------------------------------------------------------------------- + * Fast-Path for exact match + * Check for exact match, if so, return the current list. + */ + + const BChunkRef *cref_match_first = NULL; + + uint chunk_list_reference_skip_len = 0; + size_t chunk_list_reference_skip_bytes = 0; + size_t i_prev = 0; + +#ifdef USE_FASTPATH_CHUNKS_FIRST + bool full_match = false; + + { + full_match = true; + + const BChunkRef *cref = chunk_list_reference->chunk_refs.first; + while (i_prev < data_len_original) { + if (cref != NULL && bchunk_data_compare(cref->link, data, data_len_original, i_prev)) { + cref_match_first = cref; + chunk_list_reference_skip_len += 1; + chunk_list_reference_skip_bytes += cref->link->data_len; + i_prev += cref->link->data_len; + cref = cref->next; + } + else { + full_match = false; + break; + } + } + + if (full_match) { + if (chunk_list_reference->total_size == data_len_original) { + return (BChunkList *)chunk_list_reference; + } + } + } + + /* End Fast-Path (first) + * --------------------- */ + +#endif /* USE_FASTPATH_CHUNKS_FIRST */ + + /* Copy until we have a mismatch */ + BChunkList *chunk_list = bchunk_list_new(bs_mem, data_len_original); + if (cref_match_first != NULL) { + size_t chunk_size_step = 0; + const BChunkRef *cref = chunk_list_reference->chunk_refs.first; + while (true) { + BChunk *chunk = cref->link; + chunk_size_step += chunk->data_len; + bchunk_list_append_only(bs_mem, chunk_list, chunk); + ASSERT_CHUNKLIST_SIZE(chunk_list, chunk_size_step); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + if (cref == cref_match_first) { + break; + } + else { + cref = cref->next; + } + } + /* happens when bytes are removed from the end of the array */ + if (chunk_size_step == data_len_original) { + return chunk_list; + } + + i_prev = chunk_size_step; + } + else { + i_prev = 0; + } + + /* ------------------------------------------------------------------------ + * Fast-Path for end chunks + * + * Check for trailing chunks + */ + + /* In this case use 'chunk_list_reference_last' to define the last index + * index_match_last = -1 */ + + /* warning, from now on don't use len(data) + * since we want to ignore chunks already matched */ + size_t data_len = data_len_original; +#define data_len_original invalid_usage +#ifdef data_len_original /* quiet warning */ +#endif + + const BChunkRef *chunk_list_reference_last = NULL; + +#ifdef USE_FASTPATH_CHUNKS_LAST + if (!BLI_listbase_is_empty(&chunk_list_reference->chunk_refs)) { + const BChunkRef *cref = chunk_list_reference->chunk_refs.last; + while ((cref->prev != NULL) && + (cref != cref_match_first) && + (cref->link->data_len <= data_len - i_prev)) + { + BChunk *chunk_test = cref->link; + size_t offset = data_len - chunk_test->data_len; + if (bchunk_data_compare(chunk_test, data, data_len, offset)) { + data_len = offset; + chunk_list_reference_last = cref; + chunk_list_reference_skip_len += 1; + chunk_list_reference_skip_bytes += cref->link->data_len; + cref = cref->prev; + } + else { + break; + } + } + } + + /* End Fast-Path (last) + * -------------------- */ +#endif /* USE_FASTPATH_CHUNKS_LAST */ + + /* ----------------------------------------------------------------------- + * Check for aligned chunks + * + * This saves a lot of searching, so use simple heuristics to detect aligned arrays. + * (may need to tweak exact method). + */ + + bool use_aligned = false; + +#ifdef USE_ALIGN_CHUNKS_TEST + if (chunk_list->total_size == chunk_list_reference->total_size) { + /* if we're already a quarter aligned */ + if (data_len - i_prev <= chunk_list->total_size / 4) { + use_aligned = true; + } + else { + /* TODO, walk over chunks and check if some arbitrary amount align */ + } + } +#endif /* USE_ALIGN_CHUNKS_TEST */ + + /* End Aligned Chunk Case + * ----------------------- */ + + if (use_aligned) { + /* Copy matching chunks, creates using the same 'layout' as the reference */ + const BChunkRef *cref = cref_match_first ? cref_match_first->next : chunk_list_reference->chunk_refs.first; + while (i_prev != data_len) { + const size_t i = i_prev + cref->link->data_len; + BLI_assert(i != i_prev); + + if ((cref != chunk_list_reference_last) && + bchunk_data_compare(cref->link, data, data_len, i_prev)) + { + bchunk_list_append(info, bs_mem, chunk_list, cref->link); + ASSERT_CHUNKLIST_SIZE(chunk_list, i); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + } + else { + bchunk_list_append_data(info, bs_mem, chunk_list, &data[i_prev], i - i_prev); + ASSERT_CHUNKLIST_SIZE(chunk_list, i); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + } + + cref = cref->next; + + i_prev = i; + } + } + else if ((data_len - i_prev >= info->chunk_byte_size) && + (chunk_list_reference->chunk_refs_len >= chunk_list_reference_skip_len) && + (chunk_list_reference->chunk_refs.first != NULL)) + { + + /* -------------------------------------------------------------------- + * Non-Aligned Chunk De-Duplication */ + + /* only create a table if we have at least one chunk to search + * otherwise just make a new one. + * + * Support re-arranged chunks */ + +#ifdef USE_HASH_TABLE_ACCUMULATE + size_t i_table_start = i_prev; + const size_t table_hash_array_len = (data_len - i_prev) / info->chunk_stride; + hash_key *table_hash_array = MEM_mallocN(sizeof(*table_hash_array) * table_hash_array_len, __func__); + hash_array_from_data(info, &data[i_prev], data_len - i_prev, table_hash_array); + + hash_accum(table_hash_array, table_hash_array_len, info->accum_steps); +#else + /* dummy vars */ + uint i_table_start = 0; + hash_key *table_hash_array = NULL; +#endif + + const uint chunk_list_reference_remaining_len = + (chunk_list_reference->chunk_refs_len - chunk_list_reference_skip_len) + 1; + BTableRef *table_ref_stack = MEM_mallocN(chunk_list_reference_remaining_len * sizeof(BTableRef), __func__); + uint table_ref_stack_n = 0; + + const size_t table_len = chunk_list_reference_remaining_len * BCHUNK_HASH_TABLE_MUL; + BTableRef **table = MEM_callocN(table_len * sizeof(*table), __func__); + + /* table_make - inline + * include one matching chunk, to allow for repeating values */ + { +#ifdef USE_HASH_TABLE_ACCUMULATE + const size_t hash_store_len = info->accum_read_ahead_len; + hash_key *hash_store = MEM_mallocN(sizeof(hash_key) * hash_store_len, __func__); +#endif + + const BChunkRef *cref; + size_t chunk_list_reference_bytes_remaining = + chunk_list_reference->total_size - chunk_list_reference_skip_bytes; + + if (cref_match_first) { + cref = cref_match_first; + chunk_list_reference_bytes_remaining += cref->link->data_len; + } + else { + cref = chunk_list_reference->chunk_refs.first; + } + +#ifdef USE_PARANOID_CHECKS + { + size_t test_bytes_len = 0; + const BChunkRef *cr = cref; + while (cr != chunk_list_reference_last) { + test_bytes_len += cr->link->data_len; + cr = cr->next; + } + BLI_assert(test_bytes_len == chunk_list_reference_bytes_remaining); + } +#endif + + while ((cref != chunk_list_reference_last) && + (chunk_list_reference_bytes_remaining >= info->accum_read_ahead_bytes)) + { + hash_key key = key_from_chunk_ref(info, cref + +#ifdef USE_HASH_TABLE_ACCUMULATE + , hash_store, hash_store_len +#endif + ); + size_t key_index = (size_t)(key % (hash_key)table_len); + BTableRef *tref_prev = table[key_index]; + BLI_assert(table_ref_stack_n < chunk_list_reference_remaining_len); + BTableRef *tref = &table_ref_stack[table_ref_stack_n++]; + tref->cref = cref; + tref->next = tref_prev; + table[key_index] = tref; + + chunk_list_reference_bytes_remaining -= cref->link->data_len; + cref = cref->next; + } + + BLI_assert(table_ref_stack_n <= chunk_list_reference_remaining_len); + +#ifdef USE_HASH_TABLE_ACCUMULATE + MEM_freeN(hash_store); +#endif + } + /* done making the table */ + + BLI_assert(i_prev <= data_len); + for (size_t i = i_prev; i < data_len; ) { + /* Assumes exiting chunk isnt a match! */ + + const BChunkRef *cref_found = table_lookup( + info, + table, table_len, i_table_start, + data, data_len, i, table_hash_array); + if (cref_found != NULL) { + BLI_assert(i < data_len); + if (i != i_prev) { + bchunk_list_append_data_n(info, bs_mem, chunk_list, &data[i_prev], i - i_prev); + i_prev = i; + } + + /* now add the reference chunk */ + { + BChunk *chunk_found = cref_found->link; + i += chunk_found->data_len; + bchunk_list_append(info, bs_mem, chunk_list, chunk_found); + } + i_prev = i; + BLI_assert(i_prev <= data_len); + ASSERT_CHUNKLIST_SIZE(chunk_list, i_prev); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + + /* its likely that the next chunk in the list will be a match, so check it! */ + while ((cref_found->next != NULL) && + (cref_found->next != chunk_list_reference_last)) + { + cref_found = cref_found->next; + BChunk *chunk_found = cref_found->link; + + if (bchunk_data_compare(chunk_found, data, data_len, i_prev)) { + /* may be useful to remove table data, assuming we dont have repeating memory + * where it would be useful to re-use chunks. */ + i += chunk_found->data_len; + bchunk_list_append(info, bs_mem, chunk_list, chunk_found); + /* chunk_found may be freed! */ + i_prev = i; + BLI_assert(i_prev <= data_len); + ASSERT_CHUNKLIST_SIZE(chunk_list, i_prev); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + } + else { + break; + } + } + } + else { + i = i + info->chunk_stride; + } + } + +#ifdef USE_HASH_TABLE_ACCUMULATE + MEM_freeN(table_hash_array); +#endif + MEM_freeN(table); + MEM_freeN(table_ref_stack); + + /* End Table Lookup + * ---------------- */ + } + + ASSERT_CHUNKLIST_SIZE(chunk_list, i_prev); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + + /* ----------------------------------------------------------------------- + * No Duplicates to copy, write new chunks + * + * Trailing chunks, no matches found in table lookup above. + * Write all new data. */ + if (i_prev != data_len) { + bchunk_list_append_data_n(info, bs_mem, chunk_list, &data[i_prev], data_len - i_prev); + i_prev = data_len; + } + + BLI_assert(i_prev == data_len); + +#ifdef USE_FASTPATH_CHUNKS_LAST + if (chunk_list_reference_last != NULL) { + /* write chunk_list_reference_last since it hasn't been written yet */ + const BChunkRef *cref = chunk_list_reference_last; + while (cref != NULL) { + BChunk *chunk = cref->link; + // BLI_assert(bchunk_data_compare(chunk, data, data_len, i_prev)); + i_prev += chunk->data_len; + /* use simple since we assume the references chunks have already been sized correctly. */ + bchunk_list_append_only(bs_mem, chunk_list, chunk); + ASSERT_CHUNKLIST_DATA(chunk_list, data); + cref = cref->next; + } + } +#endif + +#undef data_len_original + + BLI_assert(i_prev == data_len_original); + + /* check we're the correct size and that we didn't accidentally modify the reference */ + ASSERT_CHUNKLIST_SIZE(chunk_list, data_len_original); + ASSERT_CHUNKLIST_SIZE(chunk_list_reference, chunk_list_reference->total_size); + + ASSERT_CHUNKLIST_DATA(chunk_list, data); + + return chunk_list; +} +/* end private API */ + +/** \} */ + + +/** \name Main Array Storage API + * \{ */ + + +/** + * Create a new array store, which can store any number of arrays + * as long as their stride matches. + * + * \param stride: ``sizeof()`` each element, + * + * \note while a stride of ``1`` will always work, + * its less efficient since duplicate chunks of memory will be searched + * at positions unaligned with the array data. + * + * \param chunk_count: Number of elements to split each chunk into. + * - A small value increases the ability to de-duplicate chunks, + * but adds overhead by increasing the number of chunks to look-up when searching for duplicates, + * as well as some overhead constructing the original array again, with more calls to ``memcpy``. + * - Larger values reduce the *book keeping* overhead, + * but increase the chance a small, isolated change will cause a larger amount of data to be duplicated. + * + * \return A new array store, to be freed with #BLI_array_store_destroy. + */ +BArrayStore *BLI_array_store_create( + uint stride, + uint chunk_count) +{ + BArrayStore *bs = MEM_callocN(sizeof(BArrayStore), __func__); + + bs->info.chunk_stride = stride; + bs->info.chunk_count = chunk_count; + + bs->info.chunk_byte_size = chunk_count * stride; +#ifdef USE_MERGE_CHUNKS + bs->info.chunk_byte_size_min = MAX2(1u, chunk_count / BCHUNK_SIZE_MIN_DIV) * stride; + bs->info.chunk_byte_size_max = (chunk_count * BCHUNK_SIZE_MAX_MUL) * stride; +#endif + +#ifdef USE_HASH_TABLE_ACCUMULATE + bs->info.accum_steps = BCHUNK_HASH_TABLE_ACCUMULATE_STEPS - 1; + /* Triangle number, identifying now much read-ahead we need: + * https://en.wikipedia.org/wiki/Triangular_number (+ 1) */ + bs->info.accum_read_ahead_len = (uint)((((bs->info.accum_steps * (bs->info.accum_steps + 1))) / 2) + 1); + bs->info.accum_read_ahead_bytes = bs->info.accum_read_ahead_len * stride; +#else + bs->info.accum_read_ahead_bytes = BCHUNK_HASH_LEN * stride; +#endif + + bs->memory.chunk_list = BLI_mempool_create(sizeof(BChunkList), 0, 512, BLI_MEMPOOL_NOP); + bs->memory.chunk_ref = BLI_mempool_create(sizeof(BChunkRef), 0, 512, BLI_MEMPOOL_NOP); + /* allow iteration to simplify freeing, otherwise its not needed + * (we could loop over all states as an alternative). */ + bs->memory.chunk = BLI_mempool_create(sizeof(BChunk), 0, 512, BLI_MEMPOOL_ALLOW_ITER); + + return bs; +} + +static void array_store_free_data(BArrayStore *bs) +{ + /* free chunk data */ + { + BLI_mempool_iter iter; + BChunk *chunk; + BLI_mempool_iternew(bs->memory.chunk, &iter); + while ((chunk = BLI_mempool_iterstep(&iter))) { + BLI_assert(chunk->users > 0); + MEM_freeN((void *)chunk->data); + } + } + + /* free states */ + for (BArrayState *state = bs->states.first, *state_next; state; state = state_next) { + state_next = state->next; + MEM_freeN(state); + } +} + +/** + * Free the #BArrayStore, including all states and chunks. + */ +void BLI_array_store_destroy( + BArrayStore *bs) +{ + array_store_free_data(bs); + + BLI_mempool_destroy(bs->memory.chunk_list); + BLI_mempool_destroy(bs->memory.chunk_ref); + BLI_mempool_destroy(bs->memory.chunk); + + MEM_freeN(bs); +} + +/** + * Clear all contents, allowing reuse of \a bs. + */ +void BLI_array_store_clear( + BArrayStore *bs) +{ + array_store_free_data(bs); + + BLI_listbase_clear(&bs->states); + + BLI_mempool_clear(bs->memory.chunk_list); + BLI_mempool_clear(bs->memory.chunk_ref); + BLI_mempool_clear(bs->memory.chunk); +} + +/** \name BArrayStore Statistics + * \{ */ + +/** + * \return the total amount of memory that would be used by getting the arrays for all states. + */ +size_t BLI_array_store_calc_size_expanded_get( + const BArrayStore *bs) +{ + size_t size_accum = 0; + for (const BArrayState *state = bs->states.first; state; state = state->next) { + size_accum += state->chunk_list->total_size; + } + return size_accum; +} + +/** + * \return the amount of memory used by all #BChunk.data + * (duplicate chunks are only counted once). + */ +size_t BLI_array_store_calc_size_compacted_get( + const BArrayStore *bs) +{ + size_t size_total = 0; + BLI_mempool_iter iter; + BChunk *chunk; + BLI_mempool_iternew(bs->memory.chunk, &iter); + while ((chunk = BLI_mempool_iterstep(&iter))) { + BLI_assert(chunk->users > 0); + size_total += (size_t)chunk->data_len; + } + return size_total; +} + +/** \} */ + + +/** \name BArrayState Access + * \{ */ + +/** + * + * \param data: Data used to create + * \param state_reference: The state to use as a reference when adding the new state, + * typically this is the previous state, + * however it can be any previously created state from this \a bs. + * + * \return The new state, which is used by the caller as a handle to get back the contents of \a data. + * This may be removed using #BLI_array_store_state_remove, + * otherwise it will be removed with #BLI_array_store_destroy. + */ +BArrayState *BLI_array_store_state_add( + BArrayStore *bs, + const void *data, const size_t data_len, + const BArrayState *state_reference) +{ + /* ensure we're aligned to the stride */ + BLI_assert((data_len % bs->info.chunk_stride) == 0); + +#ifdef USE_PARANOID_CHECKS + if (state_reference) { + BLI_assert(BLI_findindex(&bs->states, state_reference) != -1); + } +#endif + + BChunkList *chunk_list; + if (state_reference) { + chunk_list = bchunk_list_from_data_merge( + &bs->info, &bs->memory, + (const ubyte *)data, data_len, + /* re-use reference chunks */ + state_reference->chunk_list); + } + else { + chunk_list = bchunk_list_new(&bs->memory, data_len); + bchunk_list_fill_from_array( + &bs->info, &bs->memory, + chunk_list, + (const ubyte *)data, data_len); + } + + chunk_list->users += 1; + + BArrayState *state = MEM_callocN(sizeof(BArrayState), __func__); + state->chunk_list = chunk_list; + + BLI_addtail(&bs->states, state); + +#ifdef USE_PARANOID_CHECKS + { + size_t data_test_len; + void *data_test = BLI_array_store_state_data_get_alloc(state, &data_test_len); + BLI_assert(data_test_len == data_len); + BLI_assert(memcmp(data_test, data, data_len) == 0); + MEM_freeN(data_test); + } +#endif + + return state; +} + +/** + * Remove a state and free any unused #BChunk data. + * + * The states can be freed in any order. + */ +void BLI_array_store_state_remove( + BArrayStore *bs, + BArrayState *state) +{ +#ifdef USE_PARANOID_CHECKS + BLI_assert(BLI_findindex(&bs->states, state) != -1); +#endif + + bchunk_list_decref(&bs->memory, state->chunk_list); + BLI_remlink(&bs->states, state); + + MEM_freeN(state); +} + +/** + * \return the expanded size of the array, + * use this to know how much memory to allocate #BLI_array_store_state_data_get's argument. + */ +size_t BLI_array_store_state_size_get( + BArrayState *state) +{ + return state->chunk_list->total_size; +} + +/** + * Fill in existing allocated memory with the contents of \a state. + */ +void BLI_array_store_state_data_get( + BArrayState *state, + void *data) +{ +#ifdef USE_PARANOID_CHECKS + size_t data_test_len = 0; + for (BChunkRef *cref = state->chunk_list->chunk_refs.first; cref; cref = cref->next) { + data_test_len += cref->link->data_len; + } + BLI_assert(data_test_len == state->chunk_list->total_size); +#endif + + ubyte *data_step = (ubyte *)data; + for (BChunkRef *cref = state->chunk_list->chunk_refs.first; cref; cref = cref->next) { + BLI_assert(cref->link->users > 0); + memcpy(data_step, cref->link->data, cref->link->data_len); + data_step += cref->link->data_len; + } +} + +/** + * Allocate an array for \a state and return it. + */ +void *BLI_array_store_state_data_get_alloc( + BArrayState *state, + size_t *r_data_len) +{ + void *data = MEM_mallocN(state->chunk_list->total_size, __func__); + BLI_array_store_state_data_get(state, data); + *r_data_len = state->chunk_list->total_size; + return data; +} + +/** \} */ + + +/** \name Debigging API (for testing). + * \{ */ + +/* only for test validation */ +static size_t bchunk_list_size(const BChunkList *chunk_list) +{ + size_t total_size = 0; + for (BChunkRef *cref = chunk_list->chunk_refs.first; cref; cref = cref->next) { + total_size += cref->link->data_len; + } + return total_size; +} + +bool BLI_array_store_is_valid( + BArrayStore *bs) +{ + bool ok = true; + + /* Check Length + * ------------ */ + + for (BArrayState *state = bs->states.first; state; state = state->next) { + BChunkList *chunk_list = state->chunk_list; + if (!(bchunk_list_size(chunk_list) == chunk_list->total_size)) { + return false; + } + + if (BLI_listbase_count(&chunk_list->chunk_refs) != (int)chunk_list->chunk_refs_len) { + return false; + } + +#ifdef USE_MERGE_CHUNKS + /* ensure we merge all chunks that could be merged */ + if (chunk_list->total_size > bs->info.chunk_byte_size_min) { + for (BChunkRef *cref = chunk_list->chunk_refs.first; cref; cref = cref->next) { + if (cref->link->data_len < bs->info.chunk_byte_size_min) { + return false; + } + } + } +#endif + } + + { + BLI_mempool_iter iter; + BChunk *chunk; + BLI_mempool_iternew(bs->memory.chunk, &iter); + while ((chunk = BLI_mempool_iterstep(&iter))) { + if (!(MEM_allocN_len(chunk->data) >= chunk->data_len)) { + return false; + } + } + } + + /* Check User Count & Lost References + * ---------------------------------- */ + { + GHashIterator gh_iter; + +#define GHASH_PTR_ADD_USER(gh, pt) \ + { \ + void **val; \ + if (BLI_ghash_ensure_p((gh), (pt), &val)) { \ + *((int *)val) += 1; \ + } \ + else { \ + *((int *)val) = 1; \ + } \ + } ((void)0) + + + /* count chunk_list's */ + int totrefs = 0; + GHash *chunk_list_map = BLI_ghash_ptr_new(__func__); + for (BArrayState *state = bs->states.first; state; state = state->next) { + GHASH_PTR_ADD_USER(chunk_list_map, state->chunk_list); + } + GHASH_ITER (gh_iter, chunk_list_map) { + const struct BChunkList *chunk_list = BLI_ghashIterator_getKey(&gh_iter); + const int users = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(&gh_iter)); + if (!(chunk_list->users == users)) { + ok = false; + goto user_finally; + } + } + if (!(BLI_mempool_count(bs->memory.chunk_list) == (int)BLI_ghash_size(chunk_list_map))) { + ok = false; + goto user_finally; + } + + /* count chunk's */ + GHash *chunk_map = BLI_ghash_ptr_new(__func__); + GHASH_ITER (gh_iter, chunk_list_map) { + const struct BChunkList *chunk_list = BLI_ghashIterator_getKey(&gh_iter); + for (const BChunkRef *cref = chunk_list->chunk_refs.first; cref; cref = cref->next) { + GHASH_PTR_ADD_USER(chunk_map, cref->link); + totrefs += 1; + } + } + if (!(BLI_mempool_count(bs->memory.chunk) == (int)BLI_ghash_size(chunk_map))) { + ok = false; + goto user_finally; + } + if (!(BLI_mempool_count(bs->memory.chunk_ref) == totrefs)) { + ok = false; + goto user_finally; + } + + GHASH_ITER (gh_iter, chunk_map) { + const struct BChunk *chunk = BLI_ghashIterator_getKey(&gh_iter); + const int users = GET_INT_FROM_POINTER(BLI_ghashIterator_getValue(&gh_iter)); + if (!(chunk->users == users)) { + ok = false; + goto user_finally; + } + } + +#undef GHASH_PTR_ADD_USER + +user_finally: + BLI_ghash_free(chunk_list_map, NULL, NULL); + BLI_ghash_free(chunk_map, NULL, NULL); + } + + + return ok; + /* TODO, dangling pointer checks */ +} + +/** \} */ diff --git a/source/blender/blenlib/intern/noise.c b/source/blender/blenlib/intern/noise.c index f68e11fcde3..e28b1766915 100644 --- a/source/blender/blenlib/intern/noise.c +++ b/source/blender/blenlib/intern/noise.c @@ -1051,8 +1051,8 @@ static float noise3_perlin(float vec[3]) b01 = p[i + by1]; b11 = p[j + by1]; -#define VALUE_AT(rx, ry, rz) (rx * q[0] + ry * q[1] + rz * q[2]) -#define SURVE(t) (t * t * (3.0f - 2.0f * t)) +#define VALUE_AT(rx, ry, rz) ((rx) * q[0] + (ry) * q[1] + (rz) * q[2]) +#define SURVE(t) ((t) * (t) * (3.0f - 2.0f * (t))) /* lerp moved to improved perlin above */ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index e3b55c8ab8e..a0303e53be4 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -124,6 +124,7 @@ #include "BKE_global.h" // for G #include "BKE_group.h" #include "BKE_library.h" // for which_libbase +#include "BKE_library_idmap.h" #include "BKE_library_query.h" #include "BKE_idcode.h" #include "BKE_material.h" @@ -211,17 +212,21 @@ /* use GHash for BHead name-based lookups (speeds up linking) */ #define USE_GHASH_BHEAD +/* Use GHash for restoring pointers by name */ +#define USE_GHASH_RESTORE_POINTER + /***/ typedef struct OldNew { - void *old, *newp; + const void *old; + void *newp; int nr; } OldNew; typedef struct OldNewMap { OldNew *entries; int nentries, entriessize; - int sorted; + bool sorted; int lasthit; } OldNewMap; @@ -287,12 +292,13 @@ static int verg_oldnewmap(const void *v1, const void *v2) static void oldnewmap_sort(FileData *fd) { + BLI_assert(fd->libmap->sorted == false); qsort(fd->libmap->entries, fd->libmap->nentries, sizeof(OldNew), verg_oldnewmap); fd->libmap->sorted = 1; } /* nr is zero for data, and ID code for libdata */ -static void oldnewmap_insert(OldNewMap *onm, void *oldaddr, void *newaddr, int nr) +static void oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void *newaddr, int nr) { OldNew *entry; @@ -309,7 +315,7 @@ static void oldnewmap_insert(OldNewMap *onm, void *oldaddr, void *newaddr, int n entry->nr = nr; } -void blo_do_versions_oldnewmap_insert(OldNewMap *onm, void *oldaddr, void *newaddr, int nr) +void blo_do_versions_oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void *newaddr, int nr) { oldnewmap_insert(onm, oldaddr, newaddr, nr); } @@ -364,7 +370,7 @@ static int oldnewmap_lookup_entry_full(const OldNewMap *onm, const void *addr, i return -1; } -static void *oldnewmap_lookup_and_inc(OldNewMap *onm, void *addr, bool increase_users) +static void *oldnewmap_lookup_and_inc(OldNewMap *onm, const void *addr, bool increase_users) { int i; @@ -394,7 +400,7 @@ static void *oldnewmap_lookup_and_inc(OldNewMap *onm, void *addr, bool increase_ } /* for libdata, nr has ID code, no increment */ -static void *oldnewmap_liblookup(OldNewMap *onm, void *addr, void *lib) +static void *oldnewmap_liblookup(OldNewMap *onm, const void *addr, const void *lib) { if (addr == NULL) { return NULL; @@ -402,11 +408,8 @@ static void *oldnewmap_liblookup(OldNewMap *onm, void *addr, void *lib) /* lasthit works fine for non-libdata, linking there is done in same sequence as writing */ if (onm->sorted) { - OldNew entry_s, *entry; - - entry_s.old = addr; - - entry = bsearch(&entry_s, onm->entries, onm->nentries, sizeof(OldNew), verg_oldnewmap); + const OldNew entry_s = {.old = addr}; + OldNew *entry = bsearch(&entry_s, onm->entries, onm->nentries, sizeof(OldNew), verg_oldnewmap); if (entry) { ID *id = entry->newp; @@ -486,53 +489,57 @@ void blo_join_main(ListBase *mainlist) } } -static void split_libdata(ListBase *lb, Main *first) +static void split_libdata(ListBase *lb_src, Main **lib_main_array, const unsigned int lib_main_array_len) { - ListBase *lbn; - ID *id, *idnext; - Main *mainvar; - - id = lb->first; - while (id) { + for (ID *id = lb_src->first, *idnext; id; id = idnext) { idnext = id->next; + if (id->lib) { - mainvar = first; - while (mainvar) { - if (mainvar->curlib == id->lib) { - lbn= which_libbase(mainvar, GS(id->name)); - BLI_remlink(lb, id); - BLI_addtail(lbn, id); - break; - } - mainvar = mainvar->next; + if (((unsigned int)id->lib->temp_index < lib_main_array_len) && + /* this check should never fail, just incase 'id->lib' is a dangling pointer. */ + (lib_main_array[id->lib->temp_index]->curlib == id->lib)) + { + Main *mainvar = lib_main_array[id->lib->temp_index]; + ListBase *lb_dst = which_libbase(mainvar, GS(id->name)); + BLI_remlink(lb_src, id); + BLI_addtail(lb_dst, id); + } + else { + printf("%s: invalid library for '%s'\n", __func__, id->name); + BLI_assert(0); } - if (mainvar == NULL) printf("error split_libdata\n"); } - id = idnext; } } void blo_split_main(ListBase *mainlist, Main *main) { - ListBase *lbarray[MAX_LIBARRAY]; - Library *lib; - int i; - mainlist->first = mainlist->last = main; main->next = NULL; if (BLI_listbase_is_empty(&main->library)) return; - for (lib = main->library.first; lib; lib = lib->id.next) { + /* (Library.temp_index -> Main), lookup table */ + const unsigned int lib_main_array_len = BLI_listbase_count(&main->library); + Main **lib_main_array = MEM_mallocN(lib_main_array_len * sizeof(*lib_main_array), __func__); + + int i = 0; + for (Library *lib = main->library.first; lib; lib = lib->id.next, i++) { Main *libmain = BKE_main_new(); libmain->curlib = lib; BLI_addtail(mainlist, libmain); + lib->temp_index = i; + lib_main_array[i] = libmain; } + ListBase *lbarray[MAX_LIBARRAY]; i = set_listbasepointers(main, lbarray); - while (i--) - split_libdata(lbarray[i], main->next); + while (i--) { + split_libdata(lbarray[i], lib_main_array, lib_main_array_len); + } + + MEM_freeN(lib_main_array); } static void read_file_version(FileData *fd, Main *main) @@ -1413,7 +1420,7 @@ BlendThumbnail *BLO_thumbnail_from_file(const char *filepath) /* ************** OLD POINTERS ******************* */ -static void *newdataadr(FileData *fd, void *adr) /* only direct databocks */ +static void *newdataadr(FileData *fd, const void *adr) /* only direct databocks */ { return oldnewmap_lookup_and_inc(fd->datamap, adr, true); } @@ -1430,7 +1437,7 @@ static void *newdataadr(FileData *fd, void *adr) /* only direct databocks */ * fcurve group pointer and keeps lasthit optimal for linking all further * fcurves. */ -static void *newdataadr_ex(FileData *fd, void *adr, bool increase_lasthit) /* only direct databocks */ +static void *newdataadr_ex(FileData *fd, const void *adr, bool increase_lasthit) /* only direct databocks */ { if (increase_lasthit) { return newdataadr(fd, adr); @@ -1443,38 +1450,38 @@ static void *newdataadr_ex(FileData *fd, void *adr, bool increase_lasthit) /* o } } -static void *newdataadr_no_us(FileData *fd, void *adr) /* only direct databocks */ +static void *newdataadr_no_us(FileData *fd, const void *adr) /* only direct databocks */ { return oldnewmap_lookup_and_inc(fd->datamap, adr, false); } -static void *newglobadr(FileData *fd, void *adr) /* direct datablocks with global linking */ +static void *newglobadr(FileData *fd, const void *adr) /* direct datablocks with global linking */ { return oldnewmap_lookup_and_inc(fd->globmap, adr, true); } -static void *newimaadr(FileData *fd, void *adr) /* used to restore image data after undo */ +static void *newimaadr(FileData *fd, const void *adr) /* used to restore image data after undo */ { if (fd->imamap && adr) return oldnewmap_lookup_and_inc(fd->imamap, adr, true); return NULL; } -static void *newmclipadr(FileData *fd, void *adr) /* used to restore movie clip data after undo */ +static void *newmclipadr(FileData *fd, const void *adr) /* used to restore movie clip data after undo */ { if (fd->movieclipmap && adr) return oldnewmap_lookup_and_inc(fd->movieclipmap, adr, true); return NULL; } -static void *newsoundadr(FileData *fd, void *adr) /* used to restore sound data after undo */ +static void *newsoundadr(FileData *fd, const void *adr) /* used to restore sound data after undo */ { if (fd->soundmap && adr) return oldnewmap_lookup_and_inc(fd->soundmap, adr, true); return NULL; } -static void *newpackedadr(FileData *fd, void *adr) /* used to restore packed data after undo */ +static void *newpackedadr(FileData *fd, const void *adr) /* used to restore packed data after undo */ { if (fd->packedmap && adr) return oldnewmap_lookup_and_inc(fd->packedmap, adr, true); @@ -1483,17 +1490,17 @@ static void *newpackedadr(FileData *fd, void *adr) /* used to restore packe } -static void *newlibadr(FileData *fd, void *lib, void *adr) /* only lib data */ +static void *newlibadr(FileData *fd, const void *lib, const void *adr) /* only lib data */ { return oldnewmap_liblookup(fd->libmap, adr, lib); } -void *blo_do_versions_newlibadr(FileData *fd, void *lib, void *adr) /* only lib data */ +void *blo_do_versions_newlibadr(FileData *fd, const void *lib, const void *adr) /* only lib data */ { return newlibadr(fd, lib, adr); } -static void *newlibadr_us(FileData *fd, void *lib, void *adr) /* increases user number */ +static void *newlibadr_us(FileData *fd, const void *lib, const void *adr) /* increases user number */ { ID *id = newlibadr(fd, lib, adr); @@ -1502,15 +1509,18 @@ static void *newlibadr_us(FileData *fd, void *lib, void *adr) /* increases user return id; } -void *blo_do_versions_newlibadr_us(FileData *fd, void *lib, void *adr) /* increases user number */ +void *blo_do_versions_newlibadr_us(FileData *fd, const void *lib, const void *adr) /* increases user number */ { return newlibadr_us(fd, lib, adr); } -static void change_idid_adr_fd(FileData *fd, void *old, void *new) +static void change_idid_adr_fd(FileData *fd, const void *old, void *new) { int i; + /* use a binary search if we have a sorted libmap, for now it's not needed. */ + BLI_assert(fd->libmap->sorted == false); + for (i = 0; i < fd->libmap->nentries; i++) { OldNew *entry = &fd->libmap->entries[i]; @@ -6380,68 +6390,96 @@ typedef enum ePointerUserMode { USER_REAL = 1, /* ensure at least one real user (fake user ignored) */ } ePointerUserMode; -static bool restore_pointer(ID *id, ID *newid, ePointerUserMode user) +static void restore_pointer_user(ID *id, ID *newid, ePointerUserMode user) { - if (STREQ(newid->name + 2, id->name + 2)) { - if (newid->lib == id->lib) { - if (user == USER_REAL) { - id_us_ensure_real(newid); + BLI_assert(STREQ(newid->name + 2, id->name + 2)); + BLI_assert(newid->lib == id->lib); + UNUSED_VARS_NDEBUG(id); + + if (user == USER_REAL) { + id_us_ensure_real(newid); + } +} + +#ifndef USE_GHASH_RESTORE_POINTER +/** + * A version of #restore_pointer_by_name that performs a full search (slow!). + * Use only for limited lookups, when the overhead of + * creating a #IDNameLib_Map for a single lookup isn't worthwhile. + */ +static void *restore_pointer_by_name_main(Main *mainp, ID *id, ePointerUserMode user) +{ + if (id) { + ListBase *lb = which_libbase(mainp, GS(id->name)); + if (lb) { /* there's still risk of checking corrupt mem (freed Ids in oops) */ + ID *idn = lb->first; + for (; idn; idn = idn->next) { + if (STREQ(idn->name + 2, id->name + 2)) { + if (idn->lib == id->lib) { + restore_pointer_user(id, idn, user); + break; + } + } } - return true; + return idn; } } - return false; + return NULL; } +#endif /** * Only for undo files, or to restore a screen after reading without UI... * - * user + * \param user: * - USER_IGNORE: no usercount change * - USER_REAL: ensure a real user (even if a fake one is set) + * \param id_map: lookup table, use when performing many lookups. + * this could be made an optional agument (falling back to a full lookup), + * however at the moment it's always available. */ -static void *restore_pointer_by_name(Main *mainp, ID *id, ePointerUserMode user) +static void *restore_pointer_by_name(struct IDNameLib_Map *id_map, ID *id, ePointerUserMode user) { +#ifdef USE_GHASH_RESTORE_POINTER if (id) { - ListBase *lb = which_libbase(mainp, GS(id->name)); - if (lb) { // there's still risk of checking corrupt mem (freed Ids in oops) - ID *idn = lb->first; - - for (; idn; idn = idn->next) { - if (restore_pointer(id, idn, user)) - break; - } - - return idn; + /* use fast lookup when available */ + ID *idn = BKE_main_idmap_lookup_id(id_map, id); + if (idn) { + restore_pointer_user(id, idn, user); } + return idn; } return NULL; +#else + Main *mainp = BKE_main_idmap_main_get(id_map); + return restore_pointer_by_name_main(mainp, id, user); +#endif } -static void lib_link_seq_clipboard_pt_restore(ID *id, Main *newmain) +static void lib_link_seq_clipboard_pt_restore(ID *id, struct IDNameLib_Map *id_map) { if (id) { /* clipboard must ensure this */ BLI_assert(id->newid != NULL); - id->newid = restore_pointer_by_name(newmain, (ID *)id->newid, USER_REAL); + id->newid = restore_pointer_by_name(id_map, id->newid, USER_REAL); } } static int lib_link_seq_clipboard_cb(Sequence *seq, void *arg_pt) { - Main *newmain = (Main *)arg_pt; + struct IDNameLib_Map *id_map = arg_pt; - lib_link_seq_clipboard_pt_restore((ID *)seq->scene, newmain); - lib_link_seq_clipboard_pt_restore((ID *)seq->scene_camera, newmain); - lib_link_seq_clipboard_pt_restore((ID *)seq->clip, newmain); - lib_link_seq_clipboard_pt_restore((ID *)seq->mask, newmain); - lib_link_seq_clipboard_pt_restore((ID *)seq->sound, newmain); + lib_link_seq_clipboard_pt_restore((ID *)seq->scene, id_map); + lib_link_seq_clipboard_pt_restore((ID *)seq->scene_camera, id_map); + lib_link_seq_clipboard_pt_restore((ID *)seq->clip, id_map); + lib_link_seq_clipboard_pt_restore((ID *)seq->mask, id_map); + lib_link_seq_clipboard_pt_restore((ID *)seq->sound, id_map); return 1; } -static void lib_link_clipboard_restore(Main *newmain) +static void lib_link_clipboard_restore(struct IDNameLib_Map *id_map) { /* update IDs stored in sequencer clipboard */ - BKE_sequencer_base_recursive_apply(&seqbase_clipboard, lib_link_seq_clipboard_cb, newmain); + BKE_sequencer_base_recursive_apply(&seqbase_clipboard, lib_link_seq_clipboard_cb, id_map); } /* called from kernel/blender.c */ @@ -6453,11 +6491,13 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc wmWindowManager *wm; bScreen *sc; ScrArea *sa; - + + struct IDNameLib_Map *id_map = BKE_main_idmap_create(newmain); + /* first windowmanager */ for (wm = newmain->wm.first; wm; wm = wm->id.next) { for (win= wm->windows.first; win; win= win->next) { - win->screen = restore_pointer_by_name(newmain, (ID *)win->screen, USER_REAL); + win->screen = restore_pointer_by_name(id_map, (ID *)win->screen, USER_REAL); if (win->screen == NULL) win->screen = curscreen; @@ -6470,7 +6510,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc for (sc = newmain->screen.first; sc; sc = sc->id.next) { Scene *oldscene = sc->scene; - sc->scene= restore_pointer_by_name(newmain, (ID *)sc->scene, USER_REAL); + sc->scene= restore_pointer_by_name(id_map, (ID *)sc->scene, USER_REAL); if (sc->scene == NULL) sc->scene = curscene; @@ -6489,16 +6529,16 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc if (v3d->scenelock) v3d->camera = NULL; /* always get from scene */ else - v3d->camera = restore_pointer_by_name(newmain, (ID *)v3d->camera, USER_REAL); + v3d->camera = restore_pointer_by_name(id_map, (ID *)v3d->camera, USER_REAL); if (v3d->camera == NULL) v3d->camera = sc->scene->camera; - v3d->ob_centre = restore_pointer_by_name(newmain, (ID *)v3d->ob_centre, USER_REAL); + v3d->ob_centre = restore_pointer_by_name(id_map, (ID *)v3d->ob_centre, USER_REAL); for (bgpic= v3d->bgpicbase.first; bgpic; bgpic= bgpic->next) { - if ((bgpic->ima = restore_pointer_by_name(newmain, (ID *)bgpic->ima, USER_IGNORE))) { + if ((bgpic->ima = restore_pointer_by_name(id_map, (ID *)bgpic->ima, USER_IGNORE))) { id_us_plus((ID *)bgpic->ima); } - if ((bgpic->clip = restore_pointer_by_name(newmain, (ID *)bgpic->clip, USER_IGNORE))) { + if ((bgpic->clip = restore_pointer_by_name(id_map, (ID *)bgpic->clip, USER_IGNORE))) { id_us_plus((ID *)bgpic->clip); } } @@ -6542,10 +6582,10 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc bDopeSheet *ads = sipo->ads; if (ads) { - ads->source = restore_pointer_by_name(newmain, (ID *)ads->source, USER_REAL); + ads->source = restore_pointer_by_name(id_map, (ID *)ads->source, USER_REAL); if (ads->filter_grp) - ads->filter_grp = restore_pointer_by_name(newmain, (ID *)ads->filter_grp, USER_IGNORE); + ads->filter_grp = restore_pointer_by_name(id_map, (ID *)ads->filter_grp, USER_IGNORE); } /* force recalc of list of channels (i.e. includes calculating F-Curve colors) @@ -6555,7 +6595,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc } else if (sl->spacetype == SPACE_BUTS) { SpaceButs *sbuts = (SpaceButs *)sl; - sbuts->pinid = restore_pointer_by_name(newmain, sbuts->pinid, USER_IGNORE); + sbuts->pinid = restore_pointer_by_name(id_map, sbuts->pinid, USER_IGNORE); if (sbuts->pinid == NULL) { sbuts->flag &= ~SB_PIN_CONTEXT; } @@ -6572,11 +6612,11 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc else if (sl->spacetype == SPACE_ACTION) { SpaceAction *saction = (SpaceAction *)sl; - saction->action = restore_pointer_by_name(newmain, (ID *)saction->action, USER_REAL); - saction->ads.source = restore_pointer_by_name(newmain, (ID *)saction->ads.source, USER_REAL); + saction->action = restore_pointer_by_name(id_map, (ID *)saction->action, USER_REAL); + saction->ads.source = restore_pointer_by_name(id_map, (ID *)saction->ads.source, USER_REAL); if (saction->ads.filter_grp) - saction->ads.filter_grp = restore_pointer_by_name(newmain, (ID *)saction->ads.filter_grp, USER_IGNORE); + saction->ads.filter_grp = restore_pointer_by_name(id_map, (ID *)saction->ads.filter_grp, USER_IGNORE); /* force recalc of list of channels, potentially updating the active action @@ -6587,7 +6627,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc else if (sl->spacetype == SPACE_IMAGE) { SpaceImage *sima = (SpaceImage *)sl; - sima->image = restore_pointer_by_name(newmain, (ID *)sima->image, USER_REAL); + sima->image = restore_pointer_by_name(id_map, (ID *)sima->image, USER_REAL); /* this will be freed, not worth attempting to find same scene, * since it gets initialized later */ @@ -6602,8 +6642,8 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc /* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data * so assume that here we're doing for undo only... */ - sima->gpd = restore_pointer_by_name(newmain, (ID *)sima->gpd, USER_REAL); - sima->mask_info.mask = restore_pointer_by_name(newmain, (ID *)sima->mask_info.mask, USER_REAL); + sima->gpd = restore_pointer_by_name(id_map, (ID *)sima->gpd, USER_REAL); + sima->mask_info.mask = restore_pointer_by_name(id_map, (ID *)sima->mask_info.mask, USER_REAL); } else if (sl->spacetype == SPACE_SEQ) { SpaceSeq *sseq = (SpaceSeq *)sl; @@ -6611,29 +6651,29 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc /* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data * so assume that here we're doing for undo only... */ - sseq->gpd = restore_pointer_by_name(newmain, (ID *)sseq->gpd, USER_REAL); + sseq->gpd = restore_pointer_by_name(id_map, (ID *)sseq->gpd, USER_REAL); } else if (sl->spacetype == SPACE_NLA) { SpaceNla *snla = (SpaceNla *)sl; bDopeSheet *ads = snla->ads; if (ads) { - ads->source = restore_pointer_by_name(newmain, (ID *)ads->source, USER_REAL); + ads->source = restore_pointer_by_name(id_map, (ID *)ads->source, USER_REAL); if (ads->filter_grp) - ads->filter_grp = restore_pointer_by_name(newmain, (ID *)ads->filter_grp, USER_IGNORE); + ads->filter_grp = restore_pointer_by_name(id_map, (ID *)ads->filter_grp, USER_IGNORE); } } else if (sl->spacetype == SPACE_TEXT) { SpaceText *st = (SpaceText *)sl; - st->text = restore_pointer_by_name(newmain, (ID *)st->text, USER_REAL); + st->text = restore_pointer_by_name(id_map, (ID *)st->text, USER_REAL); if (st->text == NULL) st->text = newmain->text.first; } else if (sl->spacetype == SPACE_SCRIPT) { SpaceScript *scpt = (SpaceScript *)sl; - scpt->script = restore_pointer_by_name(newmain, (ID *)scpt->script, USER_REAL); + scpt->script = restore_pointer_by_name(id_map, (ID *)scpt->script, USER_REAL); /*sc->script = NULL; - 2.45 set to null, better re-run the script */ if (scpt->script) { @@ -6643,7 +6683,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc else if (sl->spacetype == SPACE_OUTLINER) { SpaceOops *so= (SpaceOops *)sl; - so->search_tse.id = restore_pointer_by_name(newmain, so->search_tse.id, USER_IGNORE); + so->search_tse.id = restore_pointer_by_name(id_map, so->search_tse.id, USER_IGNORE); if (so->treestore) { TreeStoreElem *tselem; @@ -6653,7 +6693,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc while ((tselem = BLI_mempool_iterstep(&iter))) { /* Do not try to restore pointers to drivers/sequence/etc., can crash in undo case! */ if (TSE_IS_REAL_ID(tselem)) { - tselem->id = restore_pointer_by_name(newmain, tselem->id, USER_IGNORE); + tselem->id = restore_pointer_by_name(id_map, tselem->id, USER_IGNORE); } else { tselem->id = NULL; @@ -6671,14 +6711,14 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc bNodeTree *ntree; /* node tree can be stored locally in id too, link this first */ - snode->id = restore_pointer_by_name(newmain, snode->id, USER_REAL); - snode->from = restore_pointer_by_name(newmain, snode->from, USER_IGNORE); + snode->id = restore_pointer_by_name(id_map, snode->id, USER_REAL); + snode->from = restore_pointer_by_name(id_map, snode->from, USER_IGNORE); ntree = nodetree_from_id(snode->id); if (ntree) snode->nodetree = ntree; else - snode->nodetree = restore_pointer_by_name(newmain, (ID*)snode->nodetree, USER_REAL); + snode->nodetree = restore_pointer_by_name(id_map, (ID*)snode->nodetree, USER_REAL); for (path = snode->treepath.first; path; path = path->next) { if (path == snode->treepath.first) { @@ -6686,7 +6726,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc path->nodetree = snode->nodetree; } else - path->nodetree= restore_pointer_by_name(newmain, (ID*)path->nodetree, USER_REAL); + path->nodetree= restore_pointer_by_name(id_map, (ID*)path->nodetree, USER_REAL); if (!path->nodetree) break; @@ -6712,22 +6752,24 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc else if (sl->spacetype == SPACE_CLIP) { SpaceClip *sclip = (SpaceClip *)sl; - sclip->clip = restore_pointer_by_name(newmain, (ID *)sclip->clip, USER_REAL); - sclip->mask_info.mask = restore_pointer_by_name(newmain, (ID *)sclip->mask_info.mask, USER_REAL); + sclip->clip = restore_pointer_by_name(id_map, (ID *)sclip->clip, USER_REAL); + sclip->mask_info.mask = restore_pointer_by_name(id_map, (ID *)sclip->mask_info.mask, USER_REAL); sclip->scopes.ok = 0; } else if (sl->spacetype == SPACE_LOGIC) { SpaceLogic *slogic = (SpaceLogic *)sl; - slogic->gpd = restore_pointer_by_name(newmain, (ID *)slogic->gpd, USER_REAL); + slogic->gpd = restore_pointer_by_name(id_map, (ID *)slogic->gpd, USER_REAL); } } } } /* update IDs stored in all possible clipboards */ - lib_link_clipboard_restore(newmain); + lib_link_clipboard_restore(id_map); + + BKE_main_idmap_destroy(id_map); } static void direct_link_region(FileData *fd, ARegion *ar, int spacetype) @@ -7877,7 +7919,7 @@ static BHead *read_data_into_oldnewmap(FileData *fd, BHead *bhead, const char *a return bhead; } -static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, int flag, ID **r_id) +static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const short tag, ID **r_id) { /* this routine reads a libblock and its direct data. Use link functions to connect it all */ @@ -7889,8 +7931,8 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, int flag, ID /* In undo case, most libs and linked data should be kept as is from previous state (see BLO_read_from_memfile). * However, some needed by the snapshot being read may have been removed in previous one, and would go missing. * This leads e.g. to desappearing objects in some undo/redo case, see T34446. - * That means we have to carefully check whether current lib or libdata already exits in old main, if it does - * we merely copy it over into new main area, otherwise we have to do a full read of that bhead... */ + * That means we have to carefully check whether current lib or libdata already exits in old main, if it does + * we merely copy it over into new main area, otherwise we have to do a full read of that bhead... */ if (fd->memfile && ELEM(bhead->code, ID_LI, ID_ID)) { const char *idname = bhead_id_name(fd, bhead); @@ -7963,7 +8005,7 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, int flag, ID if (!id) return blo_nextbhead(fd, bhead); - id->tag = flag | LIB_TAG_NEED_LINK; + id->tag = tag | LIB_TAG_NEED_LINK; id->lib = main->curlib; id->us = ID_FAKE_USERS(id); id->icon_id = 0; @@ -9644,7 +9686,7 @@ static void give_base_to_groups( } } -static ID *create_placeholder(Main *mainvar, const char *idname, const short flag) +static ID *create_placeholder(Main *mainvar, const char *idname, const short tag) { const short idcode = GS(idname); ListBase *lb = which_libbase(mainvar, idcode); @@ -9653,7 +9695,7 @@ static ID *create_placeholder(Main *mainvar, const char *idname, const short fla memcpy(ph_id->name, idname, sizeof(ph_id->name)); BKE_libblock_init_empty(ph_id); ph_id->lib = mainvar->curlib; - ph_id->tag = flag | LIB_TAG_MISSING; + ph_id->tag = tag | LIB_TAG_MISSING; ph_id->us = ID_FAKE_USERS(ph_id); ph_id->icon_id = 0; @@ -9992,21 +10034,22 @@ void *BLO_library_read_struct(FileData *fd, BHead *bh, const char *blockname) /* ************* READ LIBRARY ************** */ -static int mainvar_count_libread_blocks(Main *mainvar) +static int mainvar_id_tag_any_check(Main *mainvar, const short tag) { ListBase *lbarray[MAX_LIBARRAY]; - int a, tot = 0; + int a; a = set_listbasepointers(mainvar, lbarray); while (a--) { ID *id; for (id = lbarray[a]->first; id; id = id->next) { - if (id->tag & LIB_TAG_READ) - tot++; + if (id->tag & tag) { + return true; + } } } - return tot; + return false; } static void read_libraries(FileData *basefd, ListBase *mainlist) @@ -10026,10 +10069,9 @@ static void read_libraries(FileData *basefd, ListBase *mainlist) /* test 1: read libdata */ mainptr= mainl->next; while (mainptr) { - int tot = mainvar_count_libread_blocks(mainptr); - - // printf("found LIB_TAG_READ %s\n", mainptr->curlib->name); - if (tot) { + if (mainvar_id_tag_any_check(mainptr, LIB_TAG_READ)) { + // printf("found LIB_TAG_READ %s\n", mainptr->curlib->name); + FileData *fd = mainptr->curlib->filedata; if (fd == NULL) { diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index f5c19f5ee22..42728fd406f 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -156,9 +156,9 @@ const char *bhead_id_name(const FileData *fd, const BHead *bhead); void blo_reportf_wrap(struct ReportList *reports, ReportType type, const char *format, ...) ATTR_PRINTF_FORMAT(3, 4); -void blo_do_versions_oldnewmap_insert(struct OldNewMap *onm, void *oldaddr, void *newaddr, int nr); -void *blo_do_versions_newlibadr(struct FileData *fd, void *lib, void *adr); -void *blo_do_versions_newlibadr_us(struct FileData *fd, void *lib, void *adr); +void blo_do_versions_oldnewmap_insert(struct OldNewMap *onm, const void *oldaddr, void *newaddr, int nr); +void *blo_do_versions_newlibadr(struct FileData *fd, const void *lib, const void *adr); +void *blo_do_versions_newlibadr_us(struct FileData *fd, const void *lib, const void *adr); struct PartEff *blo_do_version_give_parteff_245(struct Object *ob); void blo_do_version_old_trackto_to_constraints(struct Object *ob); diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 0ea4078a5cb..b7b6ace3c1a 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -1189,9 +1189,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } } } - } - { for (Camera *camera = main->camera.first; camera != NULL; camera = camera->id.next) { if (camera->stereo.pole_merge_angle_from == 0.0f && camera->stereo.pole_merge_angle_to == 0.0f) @@ -1200,5 +1198,19 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) camera->stereo.pole_merge_angle_to = DEG2RAD(75.0f); } } + + if (!DNA_struct_elem_find(fd->filesdna, "NormalEditModifierData", "float", "mix_limit")) { + Object *ob; + + for (ob = main->object.first; ob; ob = ob->id.next) { + ModifierData *md; + for (md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_NormalEdit) { + NormalEditModifierData *nemd = (NormalEditModifierData *)md; + nemd->mix_limit = DEG2RADF(180.0f); + } + } + } + } } } diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index f7e3622e53c..0ed1dffcafb 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -198,6 +198,12 @@ typedef struct BevelParams { // #include "bevdebug.c" +/* some flags to re-enable old behavior for a while, in case fixes broke things not caught by regression tests */ +static int bev_debug_flags = 0; +#define DEBUG_OLD_PLANE_SPECIAL (bev_debug_flags & 1) +#define DEBUG_OLD_PROJ_TO_PERP_PLANE (bev_debug_flags & 2) +#define DEBUG_OLD_FLAT_MID (bev_debug_flags & 4) + /* Make a new BoundVert of the given kind, insert it at the end of the circular linked * list with entry point bv->boundstart, and return it. */ static BoundVert *add_new_bound_vert(MemArena *mem_arena, VMesh *vm, const float co[3]) @@ -1045,15 +1051,21 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv) sub_v3_v3v3(pro->proj_dir, e->e->v1->co, e->e->v2->co); normalize_v3(pro->proj_dir); project_to_edge(e->e, co1, co2, pro->midco); - /* put arc endpoints on plane with normal proj_dir, containing midco */ - add_v3_v3v3(co3, co1, pro->proj_dir); - if (!isect_line_plane_v3(pro->coa, co1, co3, pro->midco, pro->proj_dir)) { - /* shouldn't happen */ - copy_v3_v3(pro->coa, co1); + if (DEBUG_OLD_PROJ_TO_PERP_PLANE) { + /* put arc endpoints on plane with normal proj_dir, containing midco */ + add_v3_v3v3(co3, co1, pro->proj_dir); + if (!isect_line_plane_v3(pro->coa, co1, co3, pro->midco, pro->proj_dir)) { + /* shouldn't happen */ + copy_v3_v3(pro->coa, co1); + } + add_v3_v3v3(co3, co2, pro->proj_dir); + if (!isect_line_plane_v3(pro->cob, co2, co3, pro->midco, pro->proj_dir)) { + /* shouldn't happen */ + copy_v3_v3(pro->cob, co2); + } } - add_v3_v3v3(co3, co2, pro->proj_dir); - if (!isect_line_plane_v3(pro->cob, co2, co3, pro->midco, pro->proj_dir)) { - /* shouldn't happen */ + else { + copy_v3_v3(pro->coa, co1); copy_v3_v3(pro->cob, co2); } /* default plane to project onto is the one with triangle co1 - midco - co2 in it */ @@ -1066,19 +1078,48 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv) if (l <= BEVEL_EPSILON_BIG) { /* co1 - midco -co2 are collinear. * Should be case that beveled edge is coplanar with two boundary verts. + * We want to move the profile to that common plane, if possible. + * That makes the multi-segment bevels curve nicely in that plane, as users expect. + * The new midco should be either v (when neighbor edges are unbeveled) + * or the intersection of the offset lines (if they are). * If the profile is going to lead into unbeveled edges on each side * (that is, both BoundVerts are "on-edge" points on non-beveled edges) - * then in order to get curve in multi-segment case, change projection plane - * to be that common plane, projection dir to be the plane normal, - * and mid to be the original vertex. - * Otherwise, we just want to linearly interpolate between co1 and co2. */ - if (e->prev->is_bev || e->next->is_bev) { + if (DEBUG_OLD_PLANE_SPECIAL && (e->prev->is_bev || e->next->is_bev)) { do_linear_interp = true; } else { - copy_v3_v3(pro->coa, co1); - copy_v3_v3(pro->midco, bv->v->co); + if (DEBUG_OLD_PROJ_TO_PERP_PLANE) { + copy_v3_v3(pro->coa, co1); + copy_v3_v3(pro->cob, co2); + } + if (DEBUG_OLD_FLAT_MID) { + copy_v3_v3(pro->midco, bv->v->co); + } + else { + copy_v3_v3(pro->midco, bv->v->co); + if (e->prev->is_bev && e->next->is_bev && bv->selcount >= 3) { + /* want mid at the meet point of next and prev offset edges */ + float d3[3], d4[3], co4[3], meetco[3], isect2[3]; + int isect_kind; + + sub_v3_v3v3(d3, e->prev->e->v1->co, e->prev->e->v2->co); + sub_v3_v3v3(d4, e->next->e->v1->co, e->next->e->v2->co); + normalize_v3(d3); + normalize_v3(d4); + add_v3_v3v3(co3, co1, d3); + add_v3_v3v3(co4, co2, d4); + isect_kind = isect_line_line_v3(co1, co3, co2, co4, meetco, isect2); + if (isect_kind != 0) { + copy_v3_v3(pro->midco, meetco); + } + else { + /* offset lines are collinear - want linear interpolation */ + mid_v3_v3v3(pro->midco, co1, co2); + do_linear_interp = true; + } + } + } copy_v3_v3(pro->cob, co2); sub_v3_v3v3(d1, pro->midco, co1); normalize_v3(d1); @@ -3436,7 +3477,7 @@ static bool fast_bevel_edge_order(BevVert *bv) } } if (nsucs == 0 || (nsucs == 2 && j != 1) || nsucs > 2 || - (j == bv->edgecount - 1 && !edges_face_connected_at_vert(bmenext, bv->edges[0].e))) + (j == bv->edgecount - 1 && !edges_face_connected_at_vert(bmenext, bv->edges[0].e))) { for (k = 1; k < j; k++) { BM_BEVEL_EDGE_TAG_DISABLE(bv->edges[k].e); @@ -3868,7 +3909,7 @@ static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f) /* going cw */ if (vm->seg > 1) { if (vm->mesh_kind == M_ADJ || bp->vertex_only || - (vm->mesh_kind == M_NONE && v->ebev != e && v->ebev != eprev)) + (vm->mesh_kind == M_NONE && v->ebev != e && v->ebev != eprev)) { i = v->prev->index; for (k = vm->seg - 1; k > 0; k--) { diff --git a/source/blender/collada/ArmatureImporter.cpp b/source/blender/collada/ArmatureImporter.cpp index fca9b9ffa55..1bc2bff74e3 100644 --- a/source/blender/collada/ArmatureImporter.cpp +++ b/source/blender/collada/ArmatureImporter.cpp @@ -107,27 +107,39 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon std::vector<COLLADAFW::Node *>::iterator it; it = std::find(finished_joints.begin(), finished_joints.end(), node); if (it != finished_joints.end()) return chain_length; - - // JointData* jd = get_joint_data(node); - // TODO rename from Node "name" attrs later EditBone *bone = ED_armature_edit_bone_add(arm, (char *)bc_get_joint_name(node)); totbone++; - if (skin && skin->get_joint_inv_bind_matrix(joint_inv_bind_mat, node)) { - // get original world-space matrix - invert_m4_m4(mat, joint_inv_bind_mat); + /* + * We use the inv_bind_shape matrix to apply the armature bind pose as its rest pose. + */ + + std::map<COLLADAFW::UniqueId, SkinInfo>::iterator skin_it; + bool bone_is_not_skinned = true; + for (skin_it = skin_by_data_uid.begin(); skin_it != skin_by_data_uid.end(); skin_it++) { + + SkinInfo *b = &skin_it->second; + if (b->get_joint_inv_bind_matrix(joint_inv_bind_mat, node)) { - // And make local to armature - Object *ob_arm = skin->BKE_armature_from_object(); - if (ob_arm) { - float invmat[4][4]; - invert_m4_m4(invmat, ob_arm->obmat); - mul_m4_m4m4(mat, invmat, mat); + // get original world-space matrix + invert_m4_m4(mat, joint_inv_bind_mat); + + // And make local to armature + Object *ob_arm = skin->BKE_armature_from_object(); + if (ob_arm) { + float invmat[4][4]; + invert_m4_m4(invmat, ob_arm->obmat); + mul_m4_m4m4(mat, invmat, mat); + } + + bone_is_not_skinned = false; + break; } } + // create a bone even if there's no joint data for it (i.e. it has no influence) - else { + if (bone_is_not_skinned) { float obmat[4][4]; // bone-space get_node_mat(obmat, node, NULL, NULL); @@ -145,7 +157,7 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon float loc[3], size[3], rot[3][3]; - BoneExtended &be = add_bone_extended(bone, node, layer_labels); + BoneExtended &be = add_bone_extended(bone, node, totchild, layer_labels); int layer = be.get_bone_layers(); if (layer) bone->layer = layer; arm->layer |= layer; // ensure that all populated bone layers are visible after import @@ -168,7 +180,6 @@ int ArmatureImporter::create_bone(SkinInfo *skin, COLLADAFW::Node *node, EditBon mat4_to_loc_rot_size(loc, rot, size, mat); mat3_to_vec_roll(rot, NULL, &angle); } - copy_v3_v3(bone->head, mat[3]); add_v3_v3v3(bone->tail, bone->head, tail); //tail must be non zero @@ -434,12 +445,12 @@ ArmatureJoints& ArmatureImporter::get_armature_joints(Object *ob_arm) return armature_joints.back(); } #endif -void ArmatureImporter::create_armature_bones( ) +Object *ArmatureImporter::create_armature_bones(std::vector<Object *> &ob_arms) { std::vector<COLLADAFW::Node *>::iterator ri; std::vector<std::string> layer_labels; + Object *ob_arm = NULL; - leaf_bone_length = FLT_MAX; //if there is an armature created for root_joint next root_joint for (ri = root_joints.begin(); ri != root_joints.end(); ri++) { if (get_armature_for_joint(*ri) != NULL) continue; @@ -467,34 +478,21 @@ void ArmatureImporter::create_armature_bones( ) create_bone(NULL, *ri , NULL, (*ri)->getChildNodes().getCount(), NULL, armature, layer_labels); /* exit armature edit mode to populate the Armature object */ + unskinned_armature_map[(*ri)->getUniqueId()] = ob_arm; ED_armature_from_edit(armature); ED_armature_edit_free(armature); - /* and step back to edit mode to fix the leaf nodes */ - ED_armature_to_edit(armature); - - if (this->import_settings->fix_orientation || this->import_settings->find_chains) { - - if (this->import_settings->find_chains) - connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); - - if (this->import_settings->fix_orientation) - fix_leaf_bones(armature, (Bone *)armature->bonebase.first); - - // exit armature edit mode - unskinned_armature_map[(*ri)->getUniqueId()] = ob_arm; + int index = std::find(ob_arms.begin(), ob_arms.end(), ob_arm) - ob_arms.begin(); + if (index == 0) { + ob_arms.push_back(ob_arm); } - fix_parent_connect(armature, (Bone *)armature->bonebase.first); - - ED_armature_from_edit(armature); - ED_armature_edit_free(armature); - DAG_id_tag_update(&ob_arm->id, OB_RECALC_OB | OB_RECALC_DATA); } + return ob_arm; } -void ArmatureImporter::create_armature_bones(SkinInfo& skin) +Object *ArmatureImporter::create_armature_bones(SkinInfo& skin) { // just do like so: // - get armature @@ -590,7 +588,6 @@ void ArmatureImporter::create_armature_bones(SkinInfo& skin) totbone = 0; // bone_direction_row = 1; // TODO: don't default to Y but use asset and based on it decide on default row - leaf_bone_length = FLT_MAX; // create bones /* @@ -606,6 +603,7 @@ void ArmatureImporter::create_armature_bones(SkinInfo& skin) // since root_joints may contain joints for multiple controllers, we need to filter if (skin.uses_joint_or_descendant(*ri)) { + create_bone(&skin, *ri, NULL, (*ri)->getChildNodes().getCount(), NULL, armature, layer_labels); if (joint_parent_map.find((*ri)->getUniqueId()) != joint_parent_map.end() && !skin.get_parent()) @@ -617,18 +615,9 @@ void ArmatureImporter::create_armature_bones(SkinInfo& skin) ED_armature_from_edit(armature); ED_armature_edit_free(armature); - /* and step back to edit mode to fix the leaf nodes */ - ED_armature_to_edit(armature); - - if (armature->bonebase.first) { - /* Do this only if Armature has bones */ - //connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); - //fix_leaf_bones(armature, (Bone *)armature->bonebase.first); - } - // exit armature edit mode - ED_armature_from_edit(armature); - ED_armature_edit_free(armature); DAG_id_tag_update(&ob_arm->id, OB_RECALC_OB | OB_RECALC_DATA); + + return ob_arm; } void ArmatureImporter::set_pose(Object *ob_arm, COLLADAFW::Node *root_node, const char *parentname, float parent_mat[4][4]) @@ -703,22 +692,42 @@ void ArmatureImporter::add_root_joint(COLLADAFW::Node *node) #endif // here we add bones to armatures, having armatures previously created in write_controller -void ArmatureImporter::make_armatures(bContext *C) +void ArmatureImporter::make_armatures(bContext *C, std::vector<Object *> &objects_to_scale) { + std::vector<Object *> ob_arms; std::map<COLLADAFW::UniqueId, SkinInfo>::iterator it; + + leaf_bone_length = FLT_MAX; /*TODO: Make this work for more than one armature in the import file*/ + for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { SkinInfo& skin = it->second; - create_armature_bones(skin); + Object *ob_arm = create_armature_bones(skin); // link armature with a mesh object const COLLADAFW::UniqueId &uid = skin.get_controller_uid(); const COLLADAFW::UniqueId *guid = get_geometry_uid(uid); if (guid != NULL) { Object *ob = mesh_importer->get_object_by_geom_uid(*guid); - if (ob) + if (ob) { skin.link_armature(C, ob, joint_by_uid, this); + + std::vector<Object *>::iterator ob_it = std::find(objects_to_scale.begin(), objects_to_scale.end(), ob); + + if (ob_it != objects_to_scale.end()) { + int index = ob_it - objects_to_scale.begin(); + objects_to_scale.erase(objects_to_scale.begin() + index); + } + + if (std::find(objects_to_scale.begin(), objects_to_scale.end(), ob_arm) == objects_to_scale.end()) { + objects_to_scale.push_back(ob_arm); + } + + if (std::find(ob_arms.begin(), ob_arms.end(), ob_arm) == ob_arms.end()) { + ob_arms.push_back(ob_arm); + } + } else fprintf(stderr, "Cannot find object to link armature with.\n"); } @@ -735,7 +744,35 @@ void ArmatureImporter::make_armatures(bContext *C) } //for bones without skins - create_armature_bones(); + create_armature_bones(ob_arms); + + // Fix bone relations + std::vector<Object *>::iterator ob_arm_it; + for (ob_arm_it = ob_arms.begin(); ob_arm_it != ob_arms.end(); ob_arm_it++) { + + Object *ob_arm = *ob_arm_it; + bArmature *armature = (bArmature *)ob_arm->data; + + /* and step back to edit mode to fix the leaf nodes */ + ED_armature_to_edit(armature); + + if (this->import_settings->fix_orientation || this->import_settings->find_chains) { + + if (this->import_settings->find_chains) + connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); + + if (this->import_settings->fix_orientation) + fix_leaf_bones(armature, (Bone *)armature->bonebase.first); + + // exit armature edit mode + + } + + fix_parent_connect(armature, (Bone *)armature->bonebase.first); + + ED_armature_from_edit(armature); + ED_armature_edit_free(armature); + } } #if 0 @@ -922,7 +959,7 @@ bool ArmatureImporter::get_joint_bind_mat(float m[4][4], COLLADAFW::Node *joint) return found; } -BoneExtended &ArmatureImporter::add_bone_extended(EditBone *bone, COLLADAFW::Node *node, std::vector<std::string> &layer_labels) +BoneExtended &ArmatureImporter::add_bone_extended(EditBone *bone, COLLADAFW::Node *node, int sibcount, std::vector<std::string> &layer_labels) { BoneExtended *be = new BoneExtended(bone); extended_bones[bone->name] = be; @@ -930,11 +967,14 @@ BoneExtended &ArmatureImporter::add_bone_extended(EditBone *bone, COLLADAFW::Nod TagsMap::iterator etit; ExtraTags *et = 0; etit = uid_tags_map.find(node->getUniqueId().toAscii()); + + bool has_connect = false; + int connect_type = -1; + if (etit != uid_tags_map.end()) { float tail[3] = { FLT_MAX, FLT_MAX, FLT_MAX }; float roll = 0; - int use_connect = -1; std::string layers; et = etit->second; @@ -944,21 +984,29 @@ BoneExtended &ArmatureImporter::add_bone_extended(EditBone *bone, COLLADAFW::Nod has_tail |= et->setData("tip_y", &tail[1]); has_tail |= et->setData("tip_z", &tail[2]); - bool has_connect = et->setData("connect", &use_connect); - bool has_roll = et->setData("roll", &roll); + has_connect = et->setData("connect", &connect_type); + bool has_roll = et->setData("roll", &roll); layers = et->setData("layer", layers); if (has_tail && !has_connect) { - use_connect = 0; // got a bone tail definition but no connect info -> bone is not connected + /* got a bone tail definition but no connect info -> bone is not connected */ + has_connect = true; + connect_type = 0; } be->set_bone_layers(layers, layer_labels); if (has_tail) be->set_tail(tail); if (has_roll) be->set_roll(roll); - be->set_use_connect(use_connect); } + + if (!has_connect && this->import_settings->auto_connect) { + /* auto connect only whyen parent has exactly one child*/ + connect_type = sibcount == 1; + } + + be->set_use_connect(connect_type); be->set_leaf_bone(true); return *be; diff --git a/source/blender/collada/ArmatureImporter.h b/source/blender/collada/ArmatureImporter.h index f38bd1a6c66..e006ccbc94a 100644 --- a/source/blender/collada/ArmatureImporter.h +++ b/source/blender/collada/ArmatureImporter.h @@ -108,7 +108,7 @@ private: int create_bone(SkinInfo* skin, COLLADAFW::Node *node, EditBone *parent, int totchild, float parent_mat[4][4], bArmature *arm, std::vector<std::string> &layer_labels); - BoneExtended &add_bone_extended(EditBone *bone, COLLADAFW::Node * node, std::vector<std::string> &layer_labels); + BoneExtended &add_bone_extended(EditBone *bone, COLLADAFW::Node * node, int sibcount, std::vector<std::string> &layer_labels); void clear_extended_boneset(); void fix_leaf_bones(bArmature *armature, Bone *bone); @@ -131,8 +131,8 @@ private: ArmatureJoints& get_armature_joints(Object *ob_arm); #endif - void create_armature_bones(SkinInfo& skin); - void create_armature_bones( ); + Object *create_armature_bones(SkinInfo& skin); + Object *create_armature_bones(std::vector<Object *> &arm_objs); /** TagsMap typedef for uid_tags_map. */ typedef std::map<std::string, ExtraTags*> TagsMap; @@ -145,7 +145,7 @@ public: void add_root_joint(COLLADAFW::Node *node, Object *parent); // here we add bones to armatures, having armatures previously created in write_controller - void make_armatures(bContext *C); + void make_armatures(bContext *C, std::vector<Object *> &objects_to_scale); void make_shape_keys(); diff --git a/source/blender/collada/DocumentImporter.cpp b/source/blender/collada/DocumentImporter.cpp index 45dcf436473..3a709da78e1 100644 --- a/source/blender/collada/DocumentImporter.cpp +++ b/source/blender/collada/DocumentImporter.cpp @@ -239,7 +239,7 @@ void DocumentImporter::finish() mesh_importer.optimize_material_assignements(); armature_importer.set_tags_map(this->uid_tags_map); - armature_importer.make_armatures(mContext); + armature_importer.make_armatures(mContext, *objects_to_scale); armature_importer.make_shape_keys(); DAG_relations_tag_update(bmain); @@ -517,7 +517,7 @@ std::vector<Object *> *DocumentImporter::write_node(COLLADAFW::Node *node, COLLA name.c_str()); if (is_joint) { - if (parent_node == NULL) { + if (parent_node == NULL && !is_library_node) { // A Joint on root level is a skeleton without root node. // Here we add the armature "on the fly": par = bc_add_object(sce, OB_ARMATURE, std::string("Armature").c_str()); diff --git a/source/blender/collada/ImportSettings.h b/source/blender/collada/ImportSettings.h index 783f58e6bff..2c52d73e756 100644 --- a/source/blender/collada/ImportSettings.h +++ b/source/blender/collada/ImportSettings.h @@ -33,6 +33,7 @@ struct ImportSettings { public: bool import_units; bool find_chains; + bool auto_connect; bool fix_orientation; int min_chain_length; char *filepath; diff --git a/source/blender/collada/MeshImporter.cpp b/source/blender/collada/MeshImporter.cpp index 76f24545248..3adddddb8e7 100644 --- a/source/blender/collada/MeshImporter.cpp +++ b/source/blender/collada/MeshImporter.cpp @@ -211,15 +211,27 @@ void VCOLDataWrapper::get_vcol(int v_index, MLoopCol *mloopcol) MeshImporter::MeshImporter(UnitConverter *unitconv, ArmatureImporter *arm, Scene *sce) : unitconverter(unitconv), scene(sce), armature_importer(arm) { } -void MeshImporter::set_poly_indices(MPoly *mpoly, MLoop *mloop, int loop_index, unsigned int *indices, int loop_count) +bool MeshImporter::set_poly_indices(MPoly *mpoly, MLoop *mloop, int loop_index, unsigned int *indices, int loop_count) { mpoly->loopstart = loop_index; mpoly->totloop = loop_count; - + bool broken_loop = false; for (int index=0; index < loop_count; index++) { + + /* Test if loop defines a hole */ + if (!broken_loop) { + for (int i = 0; i < index; i++) { + if (indices[i] == indices[index]) { + // duplicate index -> not good + broken_loop = true; + } + } + } + mloop->v = indices[index]; mloop++; } + return broken_loop; } void MeshImporter::set_vcol(MLoopCol *mlc, VCOLDataWrapper &vob, int loop_index, COLLADAFW::IndexList &index_list, int count) @@ -698,6 +710,7 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me) COLLADAFW::IndexListArray& index_list_array_uvcoord = mp->getUVCoordIndicesArray(); COLLADAFW::IndexListArray& index_list_array_vcolor = mp->getColorIndicesArray(); + int invalid_loop_holes = 0; for (unsigned int j = 0; j < prim_totpoly; j++) { // Vertices in polygon: @@ -705,8 +718,12 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me) if (vcount < 0) { continue; // TODO: add support for holes } - set_poly_indices(mpoly, mloop, loop_index, position_indices, vcount); + bool broken_loop = set_poly_indices(mpoly, mloop, loop_index, position_indices, vcount); + if (broken_loop) + { + invalid_loop_holes += 1; + } for (unsigned int uvset_index = 0; uvset_index < index_list_array_uvcoord.getCount(); uvset_index++) { // get mtface by face index and uv set index @@ -754,6 +771,11 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me) position_indices += vcount; } + + if (invalid_loop_holes > 0) + { + fprintf(stderr, "Collada import: Mesh [%s] : contains %d unsupported loops (holes).\n", me->id.name, invalid_loop_holes); + } } else if (collada_meshtype == COLLADAFW::MeshPrimitive::LINES) { diff --git a/source/blender/collada/MeshImporter.h b/source/blender/collada/MeshImporter.h index 9d5fefb83f2..d6426fbaf56 100644 --- a/source/blender/collada/MeshImporter.h +++ b/source/blender/collada/MeshImporter.h @@ -109,7 +109,7 @@ private: std::map<COLLADAFW::UniqueId, MaterialIdPrimitiveArrayMap> geom_uid_mat_mapping_map; // crazy name! std::multimap<COLLADAFW::UniqueId, COLLADAFW::UniqueId> materials_mapped_to_geom; //< materials that have already been mapped to a geometry. A pair of geom uid and mat uid, one geometry can have several materials - void set_poly_indices(MPoly *mpoly, + bool set_poly_indices(MPoly *mpoly, MLoop *mloop, int loop_index, unsigned int *indices, diff --git a/source/blender/collada/SkinInfo.cpp b/source/blender/collada/SkinInfo.cpp index 71875d6274a..7242a24523c 100644 --- a/source/blender/collada/SkinInfo.cpp +++ b/source/blender/collada/SkinInfo.cpp @@ -230,6 +230,7 @@ void SkinInfo::link_armature(bContext *C, Object *ob, std::map<COLLADAFW::Unique ModifierData *md = ED_object_modifier_add(NULL, bmain, scene, ob, NULL, eModifierType_Armature); ArmatureModifierData *amd = (ArmatureModifierData *)md; amd->object = ob_arm; + struct bArmature *armature = (bArmature *)ob_arm->data; #if 1 bc_set_parent(ob, ob_arm, C); diff --git a/source/blender/collada/collada.cpp b/source/blender/collada/collada.cpp index e1b8a2dd30a..fe8b1d2320a 100644 --- a/source/blender/collada/collada.cpp +++ b/source/blender/collada/collada.cpp @@ -46,6 +46,7 @@ int collada_import(bContext *C, const char *filepath, int import_units, int find_chains, + int auto_connect, int fix_orientation, int min_chain_length) { @@ -53,6 +54,7 @@ int collada_import(bContext *C, ImportSettings import_settings; import_settings.filepath = (char *)filepath; import_settings.import_units = import_units != 0; + import_settings.auto_connect = auto_connect != 0; import_settings.find_chains = find_chains != 0; import_settings.fix_orientation = fix_orientation != 0; import_settings.min_chain_length = min_chain_length; diff --git a/source/blender/collada/collada.h b/source/blender/collada/collada.h index db8ea884222..0017c66836a 100644 --- a/source/blender/collada/collada.h +++ b/source/blender/collada/collada.h @@ -57,6 +57,7 @@ int collada_import(struct bContext *C, const char *filepath, int import_units, int find_chains, + int auto_connect, int fix_orientation, int min_chain_length); diff --git a/source/blender/collada/collada_utils.h b/source/blender/collada/collada_utils.h index 74f8dca1492..ee371f7959e 100644 --- a/source/blender/collada/collada_utils.h +++ b/source/blender/collada/collada_utils.h @@ -120,7 +120,7 @@ private: float roll; int bone_layers; - bool use_connect; + int use_connect; bool has_custom_tail; bool has_custom_roll; diff --git a/source/blender/compositor/operations/COM_ImageOperation.cpp b/source/blender/compositor/operations/COM_ImageOperation.cpp index 624378f2ae9..7d59358831c 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.cpp +++ b/source/blender/compositor/operations/COM_ImageOperation.cpp @@ -160,10 +160,10 @@ static void sampleImageAtLocation(ImBuf *ibuf, float x, float y, PixelSampler sa void ImageOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { int ix = x, iy = y; - if (ix < 0 || iy < 0 || ix >= this->m_buffer->x || iy >= this->m_buffer->y) { + if (this->m_imageFloatBuffer == NULL && this->m_imageByteBuffer == NULL) { zero_v4(output); } - else if (this->m_imageFloatBuffer == NULL && this->m_imageByteBuffer == NULL) { + else if (ix < 0 || iy < 0 || ix >= this->m_buffer->x || iy >= this->m_buffer->y) { zero_v4(output); } else { diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc index 187e5d346ff..b6c3d56eac0 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -61,9 +61,10 @@ void deg_graph_build_finalize(Depsgraph *graph) std::stack<OperationDepsNode *> stack; foreach (OperationDepsNode *node, graph->operations) { + IDDepsNode *id_node = node->owner->owner; node->done = 0; node->num_links_pending = 0; - foreach (DepsRelation *rel, node->inlinks) { + foreach (DepsRelation *rel, node->outlinks) { if ((rel->from->type == DEPSNODE_TYPE_OPERATION) && (rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { @@ -72,36 +73,33 @@ void deg_graph_build_finalize(Depsgraph *graph) } if (node->num_links_pending == 0) { stack.push(node); + node->done = 1; } - IDDepsNode *id_node = node->owner->owner; + node->owner->layers = id_node->layers; id_node->id->tag |= LIB_TAG_DOIT; } while (!stack.empty()) { OperationDepsNode *node = stack.top(); - if (node->done == 0 && node->outlinks.size() != 0) { - foreach (DepsRelation *rel, node->outlinks) { - if (rel->to->type == DEPSNODE_TYPE_OPERATION) { - OperationDepsNode *to = (OperationDepsNode *)rel->to; - if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { - BLI_assert(to->num_links_pending > 0); - --to->num_links_pending; - } - if (to->num_links_pending == 0) { - stack.push(to); - } - } + stack.pop(); + /* Flush layers to parents. */ + foreach (DepsRelation *rel, node->inlinks) { + if (rel->from->type == DEPSNODE_TYPE_OPERATION) { + OperationDepsNode *from = (OperationDepsNode *)rel->from; + from->owner->layers |= node->owner->layers; } - node->done = 1; } - else { - stack.pop(); - IDDepsNode *id_node = node->owner->owner; - foreach (DepsRelation *rel, node->outlinks) { - if (rel->to->type == DEPSNODE_TYPE_OPERATION) { - OperationDepsNode *to = (OperationDepsNode *)rel->to; - IDDepsNode *id_to = to->owner->owner; - id_node->layers |= id_to->layers; + /* Schedule parent nodes. */ + foreach (DepsRelation *rel, node->inlinks) { + if (rel->from->type == DEPSNODE_TYPE_OPERATION) { + OperationDepsNode *from = (OperationDepsNode *)rel->from; + if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { + BLI_assert(from->num_links_pending > 0); + --from->num_links_pending; + } + if (from->num_links_pending == 0 && from->done == 0) { + stack.push(from); + from->done = 1; } } } @@ -110,6 +108,12 @@ void deg_graph_build_finalize(Depsgraph *graph) /* Re-tag IDs for update if it was tagged before the relations update tag. */ GHASH_FOREACH_BEGIN(IDDepsNode *, id_node, graph->id_hash) { + GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp, id_node->components) + { + id_node->layers |= comp->layers; + } + GHASH_FOREACH_END(); + ID *id = id_node->id; if (id->tag & LIB_TAG_ID_RECALC_ALL && id->tag & LIB_TAG_DOIT) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 5ca870cc22f..c383cae7ba7 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -331,16 +331,15 @@ void DepsgraphNodeBuilder::build_scene(Main *bmain, Scene *scene) for (Base *base = (Base *)scene->base.first; base; base = base->next) { Object *ob = base->object; - /* object itself */ - build_object(bmain, scene, base, ob); - /* object that this is a proxy for */ // XXX: the way that proxies work needs to be completely reviewed! if (ob->proxy) { ob->proxy->proxy_from = ob; - build_object(bmain, scene, base, ob->proxy); } + /* object itself */ + build_object(bmain, scene, base, ob); + /* Object dupligroup. */ if (ob->dup_group) { build_group(bmain, scene, base, ob->dup_group); @@ -525,6 +524,12 @@ void DepsgraphNodeBuilder::build_object(Main *bmain, Scene *scene, Base *base, O if (ob->gpd) { build_gpencil(ob->gpd); } + + if (ob->proxy != NULL) { + add_operation_node(&ob->id, DEPSNODE_TYPE_PROXY, DEPSOP_TYPE_POST, + function_bind(BKE_object_eval_proxy_backlink, _1, ob), + DEG_OPCODE_PLACEHOLDER, "Parameters Eval"); + } } void DepsgraphNodeBuilder::build_object_transform(Scene *scene, Object *ob) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 1dc2094c2b3..0e52c3b44b2 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -349,6 +349,13 @@ void DepsgraphRelationBuilder::build_scene(Main *bmain, Scene *scene) for (Base *base = (Base *)scene->base.first; base; base = base->next) { Object *ob = base->object; + /* Object that this is a proxy for. + * Just makes sure backlink is correct. + */ + if (ob->proxy) { + ob->proxy->proxy_from = ob; + } + /* object itself */ build_object(bmain, scene, ob); @@ -516,7 +523,6 @@ void DepsgraphRelationBuilder::build_object(Main *bmain, Scene *scene, Object *o break; } - case OB_ARMATURE: /* Pose */ if (ob->id.lib != NULL && ob->proxy_from != NULL) { build_proxy_rig(ob); diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc index 87313826763..30d243867b0 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -84,6 +84,9 @@ static void flush_init_func(void *data_v, int i) id_node->done = 0; node->scheduled = false; node->owner->flags &= ~DEPSCOMP_FULLY_SCHEDULED; + if (node->owner->type == DEPSNODE_TYPE_PROXY) { + node->flag |= DEPSOP_FLAG_NEEDS_UPDATE; + } } /* Flush updates from tagged nodes outwards until all affected nodes @@ -121,12 +124,7 @@ void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) */ GSET_FOREACH_BEGIN(OperationDepsNode *, node, graph->entry_tags) { - IDDepsNode *id_node = node->owner->owner; queue.push(node); - if (id_node->done == 0) { - deg_editors_id_update(bmain, id_node->id); - id_node->done = 1; - } node->scheduled = true; } GSET_FOREACH_END(); @@ -135,56 +133,74 @@ void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) OperationDepsNode *node = queue.front(); queue.pop(); - IDDepsNode *id_node = node->owner->owner; - lib_id_recalc_tag(bmain, id_node->id); - /* TODO(sergey): For until we've got proper data nodes in the graph. */ - lib_id_recalc_data_tag(bmain, id_node->id); - - ID *id = id_node->id; - /* This code is used to preserve those areas which does direct - * object update, - * - * Plus it ensures visibility changes and relations and layers - * visibility update has proper flags to work with. - */ - if (GS(id->name) == ID_OB) { - Object *object = (Object *)id; - ComponentDepsNode *comp_node = node->owner; - if (comp_node->type == DEPSNODE_TYPE_ANIMATION) { - object->recalc |= OB_RECALC_TIME; - } - else if (comp_node->type == DEPSNODE_TYPE_TRANSFORM) { - object->recalc |= OB_RECALC_OB; + for (;;) { + node->flag |= DEPSOP_FLAG_NEEDS_UPDATE; + + IDDepsNode *id_node = node->owner->owner; + + if (id_node->done == 0) { + deg_editors_id_update(bmain, id_node->id); + id_node->done = 1; } - else { - object->recalc |= OB_RECALC_DATA; + + lib_id_recalc_tag(bmain, id_node->id); + /* TODO(sergey): For until we've got proper data nodes in the graph. */ + lib_id_recalc_data_tag(bmain, id_node->id); + + ID *id = id_node->id; + /* This code is used to preserve those areas which does direct + * object update, + * + * Plus it ensures visibility changes and relations and layers + * visibility update has proper flags to work with. + */ + if (GS(id->name) == ID_OB) { + Object *object = (Object *)id; + ComponentDepsNode *comp_node = node->owner; + if (comp_node->type == DEPSNODE_TYPE_ANIMATION) { + object->recalc |= OB_RECALC_TIME; + } + else if (comp_node->type == DEPSNODE_TYPE_TRANSFORM) { + object->recalc |= OB_RECALC_OB; + } + else { + object->recalc |= OB_RECALC_DATA; + } } - } - /* Flush to nodes along links... */ - foreach (DepsRelation *rel, node->outlinks) { - OperationDepsNode *to_node = (OperationDepsNode *)rel->to; - if (to_node->scheduled == false) { - to_node->flag |= DEPSOP_FLAG_NEEDS_UPDATE; - queue.push(to_node); - to_node->scheduled = true; - if (id_node->done == 0) { - deg_editors_id_update(bmain, id_node->id); - id_node->done = 1; + /* TODO(sergey): For until incremental updates are possible + * witin a component at least we tag the whole component + * for update. + */ + ComponentDepsNode *component = node->owner; + if ((component->flags & DEPSCOMP_FULLY_SCHEDULED) == 0) { + foreach (OperationDepsNode *op, component->operations) { + op->flag |= DEPSOP_FLAG_NEEDS_UPDATE; } + component->flags |= DEPSCOMP_FULLY_SCHEDULED; } - } - /* TODO(sergey): For until incremental updates are possible - * witin a component at least we tag the whole component - * for update. - */ - ComponentDepsNode *component = node->owner; - if ((component->flags & DEPSCOMP_FULLY_SCHEDULED) == 0) { - foreach (OperationDepsNode *op, component->operations) { - op->flag |= DEPSOP_FLAG_NEEDS_UPDATE; + /* Flush to nodes along links... */ + if (node->outlinks.size() == 1) { + OperationDepsNode *to_node = (OperationDepsNode *)node->outlinks[0]->to; + if (to_node->scheduled == false) { + to_node->scheduled = true; + node = to_node; + } + else { + break; + } + } + else { + foreach (DepsRelation *rel, node->outlinks) { + OperationDepsNode *to_node = (OperationDepsNode *)rel->to; + if (to_node->scheduled == false) { + queue.push(to_node); + to_node->scheduled = true; + } + } + break; } - component->flags |= DEPSCOMP_FULLY_SCHEDULED; } } } diff --git a/source/blender/depsgraph/intern/nodes/deg_node_component.cc b/source/blender/depsgraph/intern/nodes/deg_node_component.cc index d18047c5112..7e49fec051f 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node_component.cc +++ b/source/blender/depsgraph/intern/nodes/deg_node_component.cc @@ -86,7 +86,8 @@ static void comp_node_hash_value_free(void *value_v) ComponentDepsNode::ComponentDepsNode() : entry_operation(NULL), exit_operation(NULL), - flags(0) + flags(0), + layers(0) { operations_map = BLI_ghash_new(comp_node_hash_key, comp_node_hash_key_cmp, @@ -119,7 +120,10 @@ string ComponentDepsNode::identifier() const char typebuf[7]; sprintf(typebuf, "(%d)", type); - return string(typebuf) + name + " : " + idname; + char layers[7]; + sprintf(layers, "%d", this->layers); + + return string(typebuf) + name + " : " + idname + " (Layers: " + layers + ")"; } OperationDepsNode *ComponentDepsNode::find_operation(OperationIDKey key) const @@ -226,6 +230,14 @@ void ComponentDepsNode::tag_update(Depsgraph *graph) foreach (OperationDepsNode *op_node, operations) { op_node->tag_update(graph); } + // It is possible that tag happens before finalization. + if (operations_map != NULL) { + GHASH_FOREACH_BEGIN(OperationDepsNode *, op_node, operations_map) + { + op_node->tag_update(graph); + } + GHASH_FOREACH_END(); + } } OperationDepsNode *ComponentDepsNode::get_entry_operation() diff --git a/source/blender/depsgraph/intern/nodes/deg_node_component.h b/source/blender/depsgraph/intern/nodes/deg_node_component.h index 17e6e7e0030..df321ea9299 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node_component.h +++ b/source/blender/depsgraph/intern/nodes/deg_node_component.h @@ -166,6 +166,9 @@ struct ComponentDepsNode : public DepsNode { // XXX: a poll() callback to check if component's first node can be started? int flags; + + /* Temporary bitmask, used during graph construction. */ + int layers; }; /* ---------------------------------------- */ diff --git a/source/blender/depsgraph/util/deg_util_function.h b/source/blender/depsgraph/util/deg_util_function.h index be7d1e13827..1e34ae04d9a 100644 --- a/source/blender/depsgraph/util/deg_util_function.h +++ b/source/blender/depsgraph/util/deg_util_function.h @@ -56,6 +56,7 @@ using boost::function; #define DISABLE_NEW_DEPSGRAPH +#include "BLI_utildefines.h" #include <cstdlib> template<typename T> diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index afc4e5c9e61..a82cca9e52a 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -753,7 +753,7 @@ EnumPropertyItem prop_driver_create_mapping_types[] = { "Create drivers for each pair of corresponding elements"}, {CREATEDRIVER_MAPPING_NONE_ALL, "NONE_ALL", ICON_HAND, "Manually Create Later", - "Create drivers for all properites without assigning any targets yet"}, + "Create drivers for all properties without assigning any targets yet"}, {CREATEDRIVER_MAPPING_NONE, "NONE_SINGLE", 0, "Manually Create Later (Single)", "Create driver for this property only and without assigning any targets yet"}, {0, NULL, 0, NULL, NULL} @@ -866,7 +866,7 @@ static int add_driver_button_exec(bContext *C, wmOperator *op) } /* Show menu or create drivers */ -static int add_driver_button_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int add_driver_button_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { PropertyRNA *prop; @@ -877,7 +877,8 @@ static int add_driver_button_invoke(bContext *C, wmOperator *op, const wmEvent * else { /* Show menu */ // TODO: This should get filtered by the enum filter - return WM_menu_invoke(C, op, event); + /* important to execute in the region we're currently in */ + return WM_menu_invoke_ex(C, op, WM_OP_INVOKE_DEFAULT); } } diff --git a/source/blender/editors/armature/editarmature_sketch.c b/source/blender/editors/armature/editarmature_sketch.c index 5530e293edd..cc4c1809fbc 100644 --- a/source/blender/editors/armature/editarmature_sketch.c +++ b/source/blender/editors/armature/editarmature_sketch.c @@ -980,7 +980,11 @@ static int sk_getStrokeSnapPoint(bContext *C, SK_Point *pt, SK_Sketch *sketch, S if (ts->snap_mode == SCE_SNAP_MODE_VOLUME) { float size; if (peelObjectsSnapContext( - snap_context, mvalf, SNAP_ALL, + snap_context, mvalf, + &(const struct SnapObjectParams){ + .snap_select = SNAP_NOT_SELECTED, + .use_object_edit_cage = false, + }, (ts->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0, loc, dummy_no, &size)) { @@ -1017,9 +1021,10 @@ static int sk_getStrokeSnapPoint(bContext *C, SK_Point *pt, SK_Sketch *sketch, S { if (ED_transform_snap_object_project_view3d( snap_context, + ts->snap_mode, &(const struct SnapObjectParams){ .snap_select = SNAP_NOT_SELECTED, - .snap_to = ts->snap_mode, + .use_object_edit_cage = false, }, mvalf, &dist_px, NULL, loc, dummy_no)) diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 18fdcb546b0..420f72fedb3 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -5004,9 +5004,10 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event) ED_transform_snap_object_project_view3d_mixed( snap_context, + SCE_SELECT_FACE, &(const struct SnapObjectParams){ - .snap_select = SNAP_NOT_OBEDIT, - .snap_to_flag = SCE_SELECT_FACE, + .snap_select = (vc.scene->obedit != NULL) ? SNAP_NOT_ACTIVE : SNAP_ALL, + .use_object_edit_cage = false, }, mval, NULL, true, location, NULL); diff --git a/source/blender/editors/include/ED_physics.h b/source/blender/editors/include/ED_physics.h index 584e9a92bb6..fed842c969e 100644 --- a/source/blender/editors/include/ED_physics.h +++ b/source/blender/editors/include/ED_physics.h @@ -45,12 +45,12 @@ int PE_hair_poll(struct bContext *C); int PE_poll_view3d(struct bContext *C); /* rigidbody_object.c */ -bool ED_rigidbody_object_add(struct Scene *scene, struct Object *ob, int type, struct ReportList *reports); -void ED_rigidbody_object_remove(struct Scene *scene, struct Object *ob); +bool ED_rigidbody_object_add(struct Main *bmain, struct Scene *scene, struct Object *ob, int type, struct ReportList *reports); +void ED_rigidbody_object_remove(struct Main *bmain, struct Scene *scene, struct Object *ob); /* rigidbody_constraint.c */ -bool ED_rigidbody_constraint_add(struct Scene *scene, struct Object *ob, int type, struct ReportList *reports); -void ED_rigidbody_constraint_remove(struct Scene *scene, struct Object *ob); +bool ED_rigidbody_constraint_add(struct Main *bmain, struct Scene *scene, struct Object *ob, int type, struct ReportList *reports); +void ED_rigidbody_constraint_remove(struct Main *bmain, struct Scene *scene, struct Object *ob); /* operators */ void ED_operatortypes_physics(void); diff --git a/source/blender/editors/include/ED_screen_types.h b/source/blender/editors/include/ED_screen_types.h index effecf43839..1c41b14a874 100644 --- a/source/blender/editors/include/ED_screen_types.h +++ b/source/blender/editors/include/ED_screen_types.h @@ -42,6 +42,7 @@ typedef struct ScreenAnimData { int sfra; /* frame that playback was started from */ int nextfra; /* next frame to go to (when ANIMPLAY_FLAG_USE_NEXT_FRAME is set) */ double last_duration; /* used for frame dropping */ + bool from_anim_edit; /* playback was invoked from animation editor */ } ScreenAnimData; /* for animplayer */ diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index 933f480a554..ebd2a3dcb7a 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -45,6 +45,7 @@ struct wmKeyMap; struct wmOperatorType; struct Main; struct SnapObjectContext; +struct SnapObjectParams; void transform_keymap_for_space(struct wmKeyConfig *keyconf, struct wmKeyMap *keymap, int spaceid); void transform_operatortypes(void); @@ -161,25 +162,27 @@ void BIF_draw_manipulator(const struct bContext *C); typedef enum SnapSelect { SNAP_ALL = 0, SNAP_NOT_SELECTED = 1, - SNAP_NOT_OBEDIT = 2 + SNAP_NOT_ACTIVE = 2, } SnapSelect; #define SNAP_MIN_DISTANCE 30 bool peelObjectsTransform( - struct TransInfo *t, const float mval[2], - SnapSelect snap_select, bool use_peel_object, + struct TransInfo *t, + const float mval[2], + const bool use_peel_object, /* return args */ float r_loc[3], float r_no[3], float *r_thickness); bool peelObjectsSnapContext( struct SnapObjectContext *sctx, const float mval[2], - SnapSelect snap_select, bool use_peel_object, + const struct SnapObjectParams *params, + const bool use_peel_object, /* return args */ float r_loc[3], float r_no[3], float *r_thickness); bool snapObjectsTransform( - struct TransInfo *t, const float mval[2], SnapSelect snap_select, + struct TransInfo *t, const float mval[2], float *dist_px, /* return args */ float r_loc[3], float r_no[3]); diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index 900b7593f2e..baf4ed574cf 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -57,17 +57,12 @@ struct SnapObjectHitDepth { unsigned int ob_uuid; }; +/** parameters that define which objects will be used to snap. */ struct SnapObjectParams { - int snap_select; /* SnapSelect */ - union { - unsigned int snap_to : 4; - /* snap_target_flag: Snap to vert/edge/face. */ - unsigned int snap_to_flag : 4; - }; + /* special context sensitive handling for the active or selected object */ + char snap_select; /* use editmode cage */ - unsigned int use_object_edit : 1; - /* special context sensitive handling for the active object */ - unsigned int use_object_active : 1; + unsigned int use_object_edit_cage : 1; }; enum { @@ -93,6 +88,7 @@ void ED_transform_snap_object_context_set_editmesh_callbacks( bool ED_transform_snap_object_project_ray_ex( struct SnapObjectContext *sctx, + const unsigned short snap_to, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], float *ray_depth, /* return args */ @@ -100,11 +96,13 @@ bool ED_transform_snap_object_project_ray_ex( struct Object **r_ob, float r_obmat[4][4]); bool ED_transform_snap_object_project_ray( SnapObjectContext *sctx, + const struct SnapObjectParams *params, const float ray_origin[3], const float ray_direction[3], float *ray_depth, float r_co[3], float r_no[3]); bool ED_transform_snap_object_project_ray_all( SnapObjectContext *sctx, + const unsigned short snap_to, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], float ray_depth, bool sort, @@ -112,12 +110,14 @@ bool ED_transform_snap_object_project_ray_all( bool ED_transform_snap_object_project_view3d_ex( struct SnapObjectContext *sctx, + const unsigned short snap_to, const struct SnapObjectParams *params, const float mval[2], float *dist_px, float *ray_depth, float r_loc[3], float r_no[3], int *r_index); bool ED_transform_snap_object_project_view3d( struct SnapObjectContext *sctx, + const unsigned short snap_to, const struct SnapObjectParams *params, const float mval[2], float *dist_px, float *ray_depth, @@ -125,6 +125,7 @@ bool ED_transform_snap_object_project_view3d( float r_loc[3], float r_no[3]); bool ED_transform_snap_object_project_view3d_mixed( SnapObjectContext *sctx, + const unsigned short snap_to_flag, const struct SnapObjectParams *params, const float mval_fl[2], float *dist_px, bool use_depth, diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 10ab85a6142..5b8b8ae5bdb 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -634,6 +634,15 @@ PointerRNA *ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext, return ptr; } +/** + * Check if a #uiAfterFunc is needed for this button. + */ +static bool ui_afterfunc_check(const uiBlock *block, const uiBut *but) +{ + return (but->func || but->funcN || but->rename_func || but->optype || but->rnaprop || block->handle_func || + (but->type == UI_BTYPE_BUT_MENU && block->butm_func)); +} + static void ui_apply_but_func(bContext *C, uiBut *but) { uiAfterFunc *after; @@ -643,9 +652,7 @@ static void ui_apply_but_func(bContext *C, uiBut *but) * handling is done, i.e. menus are closed, in order to avoid conflicts * with these functions removing the buttons we are working with */ - if (but->func || but->funcN || block->handle_func || but->rename_func || - (but->type == UI_BTYPE_BUT_MENU && block->butm_func) || but->optype || but->rnaprop) - { + if (ui_afterfunc_check(block, but)) { after = ui_afterfunc_new(); if (but->func && ELEM(but, but->func_arg1, but->func_arg2)) { @@ -899,7 +906,8 @@ static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data) * having typed something already. */ but->rename_orig = BLI_strdup(data->origstr); } - else { + /* only if there are afterfuncs, otherwise 'renam_orig' isn't freed */ + else if (ui_afterfunc_check(but->block, but)) { but->rename_orig = data->origstr; data->origstr = NULL; } diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 0a25a8fb3c6..222b0366791 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1220,8 +1220,13 @@ static void icon_draw_rect(float x, float y, int w, int h, float UNUSED(aspect), glaDrawPixelsSafe(draw_x, draw_y, draw_w, draw_h, draw_w, GL_RGBA, GL_UNSIGNED_BYTE, rect); } else { + int bound_options; + GPU_BASIC_SHADER_DISABLE_AND_STORE(bound_options); + glRasterPos2f(draw_x, draw_y); glDrawPixels(draw_w, draw_h, GL_RGBA, GL_UNSIGNED_BYTE, rect); + + GPU_BASIC_SHADER_ENABLE_AND_RESTORE(bound_options); } if (ima) diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 7a9c3e827cf..ff29a6f8e33 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -897,7 +897,7 @@ static int edittranslation_exec(bContext *C, wmOperator *op) } ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0); if (ot == NULL) { - BKE_reportf(op->reports, RPT_ERROR, "Could not find operator '%s'! Please enable ui_translate addon " + BKE_reportf(op->reports, RPT_ERROR, "Could not find operator '%s'! Please enable ui_translate add-on " "in the User Preferences", EDTSRC_I18N_OP_NAME); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index 2bbd5b8dde8..d4d3e1af1fd 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -2327,6 +2327,12 @@ static void ui_block_colorpicker(uiBlock *block, float rgba[4], PointerRNA *ptr, RNA_property_float_range(ptr, prop, &hardmin, &hardmax); RNA_property_float_get_array(ptr, prop, rgba); + /* when the softmax isn't defined in the RNA, + * using very large numbers causes sRGB/linear round trip to fail. */ + if (softmax == FLT_MAX) { + softmax = 1.0f; + } + switch (U.color_picker_type) { case USER_CP_SQUARE_SV: ui_colorpicker_square(block, ptr, prop, UI_GRAD_SV, cpicker); diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c index acb8e8e7512..b1ca95efe04 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -379,6 +379,7 @@ static int wm_collada_import_exec(bContext *C, wmOperator *op) char filename[FILE_MAX]; int import_units; int find_chains; + int auto_connect; int fix_orientation; int min_chain_length; @@ -390,6 +391,7 @@ static int wm_collada_import_exec(bContext *C, wmOperator *op) /* Options panel */ import_units = RNA_boolean_get(op->ptr, "import_units"); find_chains = RNA_boolean_get(op->ptr, "find_chains"); + auto_connect = RNA_boolean_get(op->ptr, "auto_connect"); fix_orientation = RNA_boolean_get(op->ptr, "fix_orientation"); min_chain_length = RNA_int_get(op->ptr, "min_chain_length"); @@ -398,6 +400,7 @@ static int wm_collada_import_exec(bContext *C, wmOperator *op) C, filename, import_units, find_chains, + auto_connect, fix_orientation, min_chain_length)) { @@ -432,6 +435,9 @@ static void uiCollada_importSettings(uiLayout *layout, PointerRNA *imfptr) uiItemR(row, imfptr, "find_chains", 0, NULL, ICON_NONE); row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "auto_connect", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); uiItemR(row, imfptr, "min_chain_length", 0, NULL, ICON_NONE); } @@ -474,6 +480,10 @@ void WM_OT_collada_import(wmOperatorType *ot) "find_chains", 0, "Find Bone Chains", "Find best matching Bone Chains and ensure bones in chain are connected"); + RNA_def_boolean(ot->srna, + "auto_connect", 0, "Auto Connect", + "set use_connect for parent bones which have exactly one child bone"); + RNA_def_int(ot->srna, "min_chain_length", 0, diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 242cbf79a83..0f871cd4127 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -58,6 +58,9 @@ #define MVAL_PIXEL_MARGIN 5.0f +/* until implement profile = 0 case, need to clamp somewhat above zero */ +#define PROFILE_HARD_MIN 0.15f + typedef struct { BMEditMesh *em; float initial_length; @@ -71,13 +74,14 @@ typedef struct { BMBackup mesh_backup; void *draw_handle_pixel; short twtype; + bool mouse_controls_profile; float segments; /* Segments as float so smooth mouse pan works in small increments */ } BevelData; static void edbm_bevel_update_header(bContext *C, wmOperator *op) { const char *str = IFACE_("Confirm: (Enter/LMB), Cancel: (Esc/RMB), Mode: %s (M), Clamp Overlap: %s (C), " - "Vertex Only: %s (V), Offset: %s, Segments: %d"); + "Vertex Only: %s (V), Profile Control: %s (P), Offset: %s, Segments: %d"); char msg[UI_MAX_DRAW_STR]; ScrArea *sa = CTX_wm_area(C); @@ -101,6 +105,7 @@ static void edbm_bevel_update_header(bContext *C, wmOperator *op) BLI_snprintf(msg, sizeof(msg), str, type_str, WM_bool_as_string(RNA_boolean_get(op->ptr, "clamp_overlap")), WM_bool_as_string(RNA_boolean_get(op->ptr, "vertex_only")), + WM_bool_as_string(opdata->mouse_controls_profile), offset_str, RNA_int_get(op->ptr, "segments")); ED_area_headerprint(sa, msg); @@ -123,6 +128,7 @@ static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal) opdata->em = em; opdata->is_modal = is_modal; opdata->shift_factor = -1.0f; + opdata->mouse_controls_profile = false; initNumInput(&opdata->num_input); opdata->num_input.idx_max = 0; @@ -291,7 +297,7 @@ static float edbm_bevel_mval_factor(wmOperator *op, const wmEvent *event) { BevelData *opdata = op->customdata; bool use_dist; - bool is_percent; + bool is_percent, is_profile; float mdiff[2]; float factor; @@ -299,15 +305,20 @@ static float edbm_bevel_mval_factor(wmOperator *op, const wmEvent *event) mdiff[1] = opdata->mcenter[1] - event->mval[1]; is_percent = (RNA_enum_get(op->ptr, "offset_type") == BEVEL_AMT_PERCENT); use_dist = !is_percent; + is_profile = opdata->mouse_controls_profile; factor = ((len_v2(mdiff) - MVAL_PIXEL_MARGIN) - opdata->initial_length) * opdata->pixel_size; /* Fake shift-transform... */ if (event->shift) { if (opdata->shift_factor < 0.0f) { - opdata->shift_factor = RNA_float_get(op->ptr, "offset"); - if (is_percent) { - opdata->shift_factor /= 100.0f; + if (is_profile) + opdata->shift_factor = RNA_float_get(op->ptr, "profile"); + else { + opdata->shift_factor = RNA_float_get(op->ptr, "offset"); + if (is_percent) { + opdata->shift_factor /= 100.0f; + } } } factor = (factor - opdata->shift_factor) * 0.1f + opdata->shift_factor; @@ -316,14 +327,19 @@ static float edbm_bevel_mval_factor(wmOperator *op, const wmEvent *event) opdata->shift_factor = -1.0f; } - /* clamp differently based on distance/factor */ - if (use_dist) { - if (factor < 0.0f) factor = 0.0f; + /* clamp differently based on distance/factor/profile */ + if (is_profile) { + CLAMP(factor, PROFILE_HARD_MIN, 1.0f); } else { - CLAMP(factor, 0.0f, 1.0f); - if (is_percent) { - factor *= 100.0f; + if (use_dist) { + if (factor < 0.0f) factor = 0.0f; + } + else { + CLAMP(factor, 0.0f, 1.0f); + if (is_percent) { + factor *= 100.0f; + } } } @@ -355,7 +371,10 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) case MOUSEMOVE: if (!has_numinput) { const float factor = edbm_bevel_mval_factor(op, event); - RNA_float_set(op->ptr, "offset", factor); + if (opdata->mouse_controls_profile) + RNA_float_set(op->ptr, "profile", factor); + else + RNA_float_set(op->ptr, "offset", factor); edbm_bevel_calc(op); edbm_bevel_update_header(C, op); @@ -448,6 +467,11 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) edbm_bevel_update_header(C, op); handled = true; break; + case PKEY: + if (event->val == KM_RELEASE) + break; + opdata->mouse_controls_profile = !opdata->mouse_controls_profile; + break; case VKEY: if (event->val == KM_RELEASE) break; @@ -519,7 +543,8 @@ void MESH_OT_bevel(wmOperatorType *ot) prop = RNA_def_float(ot->srna, "offset", 0.0f, -1e6f, 1e6f, "Amount", "", 0.0f, 1.0f); RNA_def_property_float_array_funcs_runtime(prop, NULL, NULL, mesh_ot_bevel_offset_range_func); RNA_def_int(ot->srna, "segments", 1, 1, 50, "Segments", "Segments for curved edge", 1, 8); - RNA_def_float(ot->srna, "profile", 0.5f, 0.15f, 1.0f, "Profile", "Controls profile shape (0.5 = round)", 0.15f, 1.0f); + RNA_def_float(ot->srna, "profile", 0.5f, PROFILE_HARD_MIN, 1.0f, "Profile", + "Controls profile shape (0.5 = round)", PROFILE_HARD_MIN, 1.0f); RNA_def_boolean(ot->srna, "vertex_only", false, "Vertex Only", "Bevel only vertices"); RNA_def_boolean(ot->srna, "clamp_overlap", false, "Clamp Overlap", "Do not allow beveled edges/vertices to overlap each other"); diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index ba17684dd39..efe179790da 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -312,9 +312,10 @@ void EMBM_project_snap_verts(bContext *C, ARegion *ar, BMEditMesh *em) if (ED_view3d_project_float_object(ar, eve->co, mval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { if (ED_transform_snap_object_project_view3d_mixed( snap_context, + SCE_SELECT_FACE, &(const struct SnapObjectParams){ - .snap_select = SNAP_NOT_OBEDIT, - .snap_to_flag = SCE_SELECT_FACE, + .snap_select = SNAP_NOT_ACTIVE, + .use_object_edit_cage = false, }, mval, NULL, true, co_proj, NULL)) diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 287ee97e9a0..b9d3fd6c8be 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -39,18 +39,39 @@ #include "ED_mesh.h" #include "ED_util.h" +#define USE_ARRAY_STORE -/* for callbacks */ +#ifdef USE_ARRAY_STORE +// # define DEBUG_PRINT +// # define DEBUG_TIME +# ifdef DEBUG_TIME +# include "PIL_time_utildefines.h" +# endif -static void *getEditMesh(bContext *C) -{ - Object *obedit = CTX_data_edit_object(C); - if (obedit && obedit->type == OB_MESH) { - Mesh *me = obedit->data; - return me->edit_btmesh; - } - return NULL; -} +# include "BLI_array_store.h" +# include "BLI_math_base.h" + /* check on best size later... */ +# define ARRAY_CHUNK_SIZE 256 + +# define USE_ARRAY_STORE_THREAD +#endif + +#ifdef USE_ARRAY_STORE_THREAD +# include "BLI_task.h" +#endif + + +#ifdef USE_ARRAY_STORE + +/* Single linked list of layers stored per type */ +typedef struct BArrayCustomData { + struct BArrayCustomData *next; + CustomDataType type; + int states_len; /* number of layers for each type */ + BArrayState *states[0]; +} BArrayCustomData; + +#endif typedef struct UndoMesh { Mesh me; @@ -65,11 +86,463 @@ typedef struct UndoMesh { * There are a few ways this could be made to work but for now its a known limitation with mixing * object and editmode operations - Campbell */ int shapenr; + +#ifdef USE_ARRAY_STORE + /* NULL arrays are considered empty */ + struct { + /* most data is stored as 'custom' data */ + BArrayCustomData *vdata, *edata, *ldata, *pdata; + BArrayState **keyblocks; + BArrayState *mselect; + } store; +#endif /* USE_ARRAY_STORE */ } UndoMesh; + +#ifdef USE_ARRAY_STORE + +/** \name Array Store + * \{ */ + +static struct { + BArrayStore **bs_all; + int bs_all_len; + int users; + + /* We could have the undo API pass in the previous state, for now store a local list */ + ListBase local_links; + +#ifdef USE_ARRAY_STORE_THREAD + TaskPool *task_pool; +#endif + +} um_arraystore = {NULL}; + +static BArrayStore *array_store_at_size_ensure(const int stride) +{ + if (um_arraystore.bs_all_len < stride) { + um_arraystore.bs_all_len = stride; + um_arraystore.bs_all = MEM_recallocN(um_arraystore.bs_all, sizeof(*um_arraystore.bs_all) * stride); + } + BArrayStore **bs_p = &um_arraystore.bs_all[stride - 1]; + + if ((*bs_p) == NULL) { +#if 0 + unsigned int chunk_count = ARRAY_CHUNK_SIZE; +#else + /* calculate best chunk-count to fit a power of two */ + unsigned int chunk_count = ARRAY_CHUNK_SIZE; + { + unsigned int size = chunk_count * stride; + size = power_of_2_max_u(size); + size = MEM_SIZE_OPTIMAL(size); + chunk_count = size / stride; + } +#endif + + (*bs_p) = BLI_array_store_create(stride, chunk_count); + } + return *bs_p; +} + +static BArrayStore *array_store_at_size_get(const int stride) +{ + BLI_assert(stride > 0 && stride <= um_arraystore.bs_all_len); + return um_arraystore.bs_all[stride - 1]; +} + +#ifdef DEBUG_PRINT +static void um_arraystore_memory_usage(size_t *r_size_expanded, size_t *r_size_compacted) +{ + size_t size_compacted = 0; + size_t size_expanded = 0; + for (int i = 0; i < um_arraystore.bs_all_len; i++) { + BArrayStore *bs = um_arraystore.bs_all[i]; + if (bs) { + size_compacted += BLI_array_store_calc_size_compacted_get(bs); + size_expanded += BLI_array_store_calc_size_expanded_get(bs); + } + } + + *r_size_expanded = size_expanded; + *r_size_compacted = size_compacted; +} +#endif + +static void um_arraystore_cd_compact( + struct CustomData *cdata, const size_t data_len, + bool create, + const BArrayCustomData *bcd_reference, + BArrayCustomData **r_bcd_first) +{ + if (data_len == 0) { + if (create) { + *r_bcd_first = NULL; + } + } + + const BArrayCustomData *bcd_reference_current = bcd_reference; + BArrayCustomData *bcd = NULL, *bcd_first = NULL, *bcd_prev = NULL; + for (int layer_start = 0, layer_end; layer_start < cdata->totlayer; layer_start = layer_end) { + const CustomDataType type = cdata->layers[layer_start].type; + + layer_end = layer_start + 1; + while ((layer_end < cdata->totlayer) && + (type == cdata->layers[layer_end].type)) + { + layer_end++; + } + + const int stride = CustomData_sizeof(type); + BArrayStore *bs = create ? array_store_at_size_ensure(stride) : NULL; + const int layer_len = layer_end - layer_start; + + if (create) { + if (bcd_reference_current && (bcd_reference_current->type == type)) { + /* common case, the reference is aligned */ + } + else { + bcd_reference_current = NULL; + + /* do a full lookup when un-alligned */ + if (bcd_reference) { + const BArrayCustomData *bcd_iter = bcd_reference; + while (bcd_iter) { + if (bcd_iter->type == type) { + bcd_reference_current = bcd_iter; + break; + } + bcd_iter = bcd_iter->next; + } + } + } + } + + if (create) { + bcd = MEM_callocN(sizeof(BArrayCustomData) + (layer_len * sizeof(BArrayState *)), __func__); + bcd->next = NULL; + bcd->type = type; + bcd->states_len = layer_end - layer_start; + + if (bcd_prev) { + bcd_prev->next = bcd; + bcd_prev = bcd; + } + else { + bcd_first = bcd; + bcd_prev = bcd; + } + } + + CustomDataLayer *layer = &cdata->layers[layer_start]; + for (int i = 0; i < layer_len; i++, layer++) { + if (create) { + if (layer->data) { + BArrayState *state_reference = + (bcd_reference_current && i < bcd_reference_current->states_len) ? + bcd_reference_current->states[i] : NULL; + bcd->states[i] = BLI_array_store_state_add( + bs, layer->data, (size_t)data_len * stride, state_reference); + } + else { + bcd->states[i] = NULL; + } + } + + if (layer->data) { + MEM_freeN(layer->data); + layer->data = NULL; + } + } + + if (create) { + if (bcd_reference_current) { + bcd_reference_current = bcd_reference_current->next; + } + } + } + + if (create) { + *r_bcd_first = bcd_first; + } +} + +/** + * \note There is no room for data going out of sync here. + * The layers and the states are stored together so this can be kept working. + */ +static void um_arraystore_cd_expand( + const BArrayCustomData *bcd, struct CustomData *cdata, const size_t data_len) +{ + CustomDataLayer *layer = cdata->layers; + while (bcd) { + const int stride = CustomData_sizeof(bcd->type); + for (int i = 0; i < bcd->states_len; i++) { + BLI_assert(bcd->type == layer->type); + if (bcd->states[i]) { + size_t state_len; + layer->data = BLI_array_store_state_data_get_alloc(bcd->states[i], &state_len); + BLI_assert(stride * data_len == state_len); + UNUSED_VARS_NDEBUG(stride, data_len); + } + else { + layer->data = NULL; + } + layer++; + } + bcd = bcd->next; + } +} + +static void um_arraystore_cd_free(BArrayCustomData *bcd) +{ + while (bcd) { + BArrayCustomData *bcd_next = bcd->next; + const int stride = CustomData_sizeof(bcd->type); + BArrayStore *bs = array_store_at_size_get(stride); + for (int i = 0; i < bcd->states_len; i++) { + if (bcd->states[i]) { + BLI_array_store_state_remove(bs, bcd->states[i]); + } + } + MEM_freeN(bcd); + bcd = bcd_next; + } +} + +/** + * \param create: When false, only free the arrays. + * This is done since when reading from an undo state, they must be temporarily expanded. + * then discarded afterwards, having this argument avoids having 2x code paths. + */ +static void um_arraystore_compact_ex( + UndoMesh *um, const UndoMesh *um_ref, + bool create) +{ + Mesh *me = &um->me; + + um_arraystore_cd_compact(&me->vdata, me->totvert, create, um_ref ? um_ref->store.vdata : NULL, &um->store.vdata); + um_arraystore_cd_compact(&me->edata, me->totedge, create, um_ref ? um_ref->store.edata : NULL, &um->store.edata); + um_arraystore_cd_compact(&me->ldata, me->totloop, create, um_ref ? um_ref->store.ldata : NULL, &um->store.ldata); + um_arraystore_cd_compact(&me->pdata, me->totpoly, create, um_ref ? um_ref->store.pdata : NULL, &um->store.pdata); + + if (me->key && me->key->totkey) { + const size_t stride = me->key->elemsize; + BArrayStore *bs = create ? array_store_at_size_ensure(stride) : NULL; + if (create) { + um->store.keyblocks = MEM_mallocN(me->key->totkey * sizeof(*um->store.keyblocks), __func__); + } + KeyBlock *keyblock = me->key->block.first; + for (int i = 0; i < me->key->totkey; i++, keyblock = keyblock->next) { + if (create) { + BArrayState *state_reference = + (um_ref && um_ref->me.key && (i < um_ref->me.key->totkey)) ? + um_ref->store.keyblocks[i] : NULL; + um->store.keyblocks[i] = BLI_array_store_state_add( + bs, keyblock->data, (size_t)keyblock->totelem * stride, + state_reference); + } + + if (keyblock->data) { + MEM_freeN(keyblock->data); + keyblock->data = NULL; + } + } + } + + if (me->mselect && me->totselect) { + BLI_assert(create == (um->store.mselect == NULL)); + if (create) { + BArrayState *state_reference = um_ref ? um_ref->store.mselect : NULL; + const size_t stride = sizeof(*me->mselect); + BArrayStore *bs = array_store_at_size_ensure(stride); + um->store.mselect = BLI_array_store_state_add( + bs, me->mselect, (size_t)me->totselect * stride, state_reference); + } + + /* keep me->totselect for validation */ + MEM_freeN(me->mselect); + me->mselect = NULL; + } + + if (create) { + um_arraystore.users += 1; + } + + BKE_mesh_update_customdata_pointers(me, false); +} + +/** + * Move data from allocated arrays to de-duplicated states and clear arrays. + */ +static void um_arraystore_compact(UndoMesh *um, const UndoMesh *um_ref) +{ + um_arraystore_compact_ex(um, um_ref, true); +} + +static void um_arraystore_compact_with_info(UndoMesh *um, const UndoMesh *um_ref) +{ +#ifdef DEBUG_PRINT + size_t size_expanded_prev, size_compacted_prev; + um_arraystore_memory_usage(&size_expanded_prev, &size_compacted_prev); +#endif + +#ifdef DEBUG_TIME + TIMEIT_START(mesh_undo_compact); +#endif + + um_arraystore_compact(um, um_ref); + +#ifdef DEBUG_TIME + TIMEIT_END(mesh_undo_compact); +#endif + +#ifdef DEBUG_PRINT + { + size_t size_expanded, size_compacted; + um_arraystore_memory_usage(&size_expanded, &size_compacted); + + const double percent_total = size_expanded ? + (((double)size_compacted / (double)size_expanded) * 100.0) : -1.0; + + size_t size_expanded_step = size_expanded - size_expanded_prev; + size_t size_compacted_step = size_compacted - size_compacted_prev; + const double percent_step = size_expanded_step ? + (((double)size_compacted_step / (double)size_expanded_step) * 100.0) : -1.0; + + printf("overall memory use: %.8f%% of expanded size\n", percent_total); + printf("step memory use: %.8f%% of expanded size\n", percent_step); + } +#endif +} + +#ifdef USE_ARRAY_STORE_THREAD + +struct UMArrayData { + UndoMesh *um; + const UndoMesh *um_ref; /* can be NULL */ +}; +static void um_arraystore_compact_cb(TaskPool *UNUSED(pool), void *taskdata, int UNUSED(threadid)) +{ + struct UMArrayData *um_data = taskdata; + um_arraystore_compact_with_info(um_data->um, um_data->um_ref); +} + +#endif /* USE_ARRAY_STORE_THREAD */ + +/** + * Remove data we only expanded for temporary use. + */ +static void um_arraystore_expand_clear(UndoMesh *um) +{ + um_arraystore_compact_ex(um, NULL, false); +} + +static void um_arraystore_expand(UndoMesh *um) +{ + Mesh *me = &um->me; + + um_arraystore_cd_expand(um->store.vdata, &me->vdata, me->totvert); + um_arraystore_cd_expand(um->store.edata, &me->edata, me->totedge); + um_arraystore_cd_expand(um->store.ldata, &me->ldata, me->totloop); + um_arraystore_cd_expand(um->store.pdata, &me->pdata, me->totpoly); + + if (um->store.keyblocks) { + const size_t stride = me->key->elemsize; + KeyBlock *keyblock = me->key->block.first; + for (int i = 0; i < me->key->totkey; i++, keyblock = keyblock->next) { + BArrayState *state = um->store.keyblocks[i]; + size_t state_len; + keyblock->data = BLI_array_store_state_data_get_alloc(state, &state_len); + BLI_assert(keyblock->totelem == (state_len / stride)); + UNUSED_VARS_NDEBUG(stride); + } + } + + if (um->store.mselect) { + const size_t stride = sizeof(*me->mselect); + BArrayState *state = um->store.mselect; + size_t state_len; + me->mselect = BLI_array_store_state_data_get_alloc(state, &state_len); + BLI_assert(me->totselect == (state_len / stride)); + UNUSED_VARS_NDEBUG(stride); + } + + /* not essential, but prevents accidental dangling pointer access */ + BKE_mesh_update_customdata_pointers(me, false); +} + +static void um_arraystore_free(UndoMesh *um) +{ + Mesh *me = &um->me; + + um_arraystore_cd_free(um->store.vdata); + um_arraystore_cd_free(um->store.edata); + um_arraystore_cd_free(um->store.ldata); + um_arraystore_cd_free(um->store.pdata); + + if (um->store.keyblocks) { + const size_t stride = me->key->elemsize; + BArrayStore *bs = array_store_at_size_get(stride); + for (int i = 0; i < me->key->totkey; i++) { + BArrayState *state = um->store.keyblocks[i]; + BLI_array_store_state_remove(bs, state); + } + MEM_freeN(um->store.keyblocks); + um->store.keyblocks = NULL; + } + + if (um->store.mselect) { + const size_t stride = sizeof(*me->mselect); + BArrayStore *bs = array_store_at_size_get(stride); + BArrayState *state = um->store.mselect; + BLI_array_store_state_remove(bs, state); + um->store.mselect = NULL; + } + + um_arraystore.users -= 1; + + BLI_assert(um_arraystore.users >= 0); + + if (um_arraystore.users == 0) { +#ifdef DEBUG_PRINT + printf("mesh undo store: freeing all data!\n"); +#endif + for (int i = 0; i < um_arraystore.bs_all_len; i += 1) { + if (um_arraystore.bs_all[i]) { + BLI_array_store_destroy(um_arraystore.bs_all[i]); + } + } + + MEM_freeN(um_arraystore.bs_all); + um_arraystore.bs_all = NULL; + um_arraystore.bs_all_len = 0; + +#ifdef USE_ARRAY_STORE_THREAD + BLI_task_pool_free(um_arraystore.task_pool); + um_arraystore.task_pool = NULL; +#endif + } + +} + +/** \} */ + +#endif /* USE_ARRAY_STORE */ + + +/* for callbacks */ /* undo simply makes copies of a bmesh */ static void *editbtMesh_to_undoMesh(void *emv, void *obdata) { + +#ifdef USE_ARRAY_STORE_THREAD + /* changes this waits is low, but must have finished */ + if (um_arraystore.task_pool) { + BLI_task_pool_work_and_wait(um_arraystore.task_pool); + } +#endif + BMEditMesh *em = emv; Mesh *obme = obdata; @@ -88,17 +561,63 @@ static void *editbtMesh_to_undoMesh(void *emv, void *obdata) um->selectmode = em->selectmode; um->shapenr = em->bm->shapenr; +#ifdef USE_ARRAY_STORE + { + /* We could be more clever here, + * the previous undo state may be from a separate mesh. */ + const UndoMesh *um_ref = um_arraystore.local_links.last ? + ((LinkData *)um_arraystore.local_links.last)->data : NULL; + + /* add oursrlves */ + BLI_addtail(&um_arraystore.local_links, BLI_genericNodeN(um)); + +#ifdef USE_ARRAY_STORE_THREAD + if (um_arraystore.task_pool == NULL) { + TaskScheduler *scheduler = BLI_task_scheduler_get(); + um_arraystore.task_pool = BLI_task_pool_create_background(scheduler, NULL); + } + + struct UMArrayData *um_data = MEM_mallocN(sizeof(*um_data), __func__); + um_data->um = um; + um_data->um_ref = um_ref; + + BLI_task_pool_push( + um_arraystore.task_pool, + um_arraystore_compact_cb, um_data, true, TASK_PRIORITY_LOW); +#else + um_arraystore_compact_with_info(um, um_ref); +#endif + } +#endif + return um; } -static void undoMesh_to_editbtMesh(void *umv, void *em_v, void *obdata) +static void undoMesh_to_editbtMesh(void *um_v, void *em_v, void *obdata) { BMEditMesh *em = em_v, *em_tmp; Object *ob = em->ob; - UndoMesh *um = umv; + UndoMesh *um = um_v; BMesh *bm; Key *key = ((Mesh *) obdata)->key; +#ifdef USE_ARRAY_STORE +#ifdef USE_ARRAY_STORE_THREAD + /* changes this waits is low, but must have finished */ + BLI_task_pool_work_and_wait(um_arraystore.task_pool); +#endif + +#ifdef DEBUG_TIME + TIMEIT_START(mesh_undo_expand); +#endif + + um_arraystore_expand(um); + +#ifdef DEBUG_TIME + TIMEIT_END(mesh_undo_expand); +#endif +#endif /* USE_ARRAY_STORE */ + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(&um->me); em->bm->shapenr = um->shapenr; @@ -145,11 +664,35 @@ static void undoMesh_to_editbtMesh(void *umv, void *em_v, void *obdata) ob->shapenr = um->shapenr; MEM_freeN(em_tmp); + +#ifdef USE_ARRAY_STORE + um_arraystore_expand_clear(um); +#endif } -static void free_undo(void *me_v) +static void free_undo(void *um_v) { - Mesh *me = me_v; + UndoMesh *um = um_v; + Mesh *me = &um->me; + +#ifdef USE_ARRAY_STORE + +#ifdef USE_ARRAY_STORE_THREAD + /* changes this waits is low, but must have finished */ + BLI_task_pool_work_and_wait(um_arraystore.task_pool); +#endif + + /* we need to expand so any allocations in custom-data are freed with the mesh */ + um_arraystore_expand(um); + + { + LinkData *link = BLI_findptr(&um_arraystore.local_links, um, offsetof(LinkData, data)); + BLI_remlink(&um_arraystore.local_links, link); + MEM_freeN(link); + } + um_arraystore_free(um); +#endif + if (me->key) { BKE_key_free(me->key); MEM_freeN(me->key); @@ -159,6 +702,16 @@ static void free_undo(void *me_v) MEM_freeN(me); } +static void *getEditMesh(bContext *C) +{ + Object *obedit = CTX_data_edit_object(C); + if (obedit && obedit->type == OB_MESH) { + Mesh *me = obedit->data; + return me->edit_btmesh; + } + return NULL; +} + /* and this is all the undo system needs to know */ void undo_push_mesh(bContext *C, const char *name) { diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 88ab3450b04..09c9442db54 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1601,7 +1601,7 @@ static int convert_exec(bContext *C, wmOperator *op) if (newob->type == OB_CURVE) { BKE_object_free_modifiers(newob); /* after derivedmesh calls! */ - ED_rigidbody_object_remove(scene, newob); + ED_rigidbody_object_remove(bmain, scene, newob); } } else if (ob->type == OB_MESH && ob->modifiers.first) { /* converting a mesh with no modifiers causes a segfault */ diff --git a/source/blender/editors/physics/rigidbody_constraint.c b/source/blender/editors/physics/rigidbody_constraint.c index f95599592b2..1bfc162a331 100644 --- a/source/blender/editors/physics/rigidbody_constraint.c +++ b/source/blender/editors/physics/rigidbody_constraint.c @@ -41,6 +41,7 @@ #include "BKE_depsgraph.h" #include "BKE_global.h" #include "BKE_group.h" +#include "BKE_main.h" #include "BKE_report.h" #include "BKE_rigidbody.h" @@ -70,7 +71,7 @@ static int ED_operator_rigidbody_con_active_poll(bContext *C) } -bool ED_rigidbody_constraint_add(Scene *scene, Object *ob, int type, ReportList *reports) +bool ED_rigidbody_constraint_add(Main *bmain, Scene *scene, Object *ob, int type, ReportList *reports) { RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene); @@ -81,7 +82,7 @@ bool ED_rigidbody_constraint_add(Scene *scene, Object *ob, int type, ReportList } /* create constraint group if it doesn't already exits */ if (rbw->constraints == NULL) { - rbw->constraints = BKE_group_add(G.main, "RigidBodyConstraints"); + rbw->constraints = BKE_group_add(bmain, "RigidBodyConstraints"); } /* make rigidbody constraint settings */ ob->rigidbody_constraint = BKE_rigidbody_create_constraint(scene, ob, type); @@ -90,11 +91,12 @@ bool ED_rigidbody_constraint_add(Scene *scene, Object *ob, int type, ReportList /* add constraint to rigid body constraint group */ BKE_group_object_add(rbw->constraints, ob, scene, NULL); + DAG_relations_tag_update(bmain); DAG_id_tag_update(&ob->id, OB_RECALC_OB); return true; } -void ED_rigidbody_constraint_remove(Scene *scene, Object *ob) +void ED_rigidbody_constraint_remove(Main *bmain, Scene *scene, Object *ob) { RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene); @@ -102,6 +104,7 @@ void ED_rigidbody_constraint_remove(Scene *scene, Object *ob) if (rbw) BKE_group_object_unlink(rbw->constraints, ob, scene, NULL); + DAG_relations_tag_update(bmain); DAG_id_tag_update(&ob->id, OB_RECALC_OB); } @@ -112,6 +115,7 @@ void ED_rigidbody_constraint_remove(Scene *scene, Object *ob) static int rigidbody_con_add_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene); Object *ob = (scene) ? OBACT : NULL; @@ -124,7 +128,7 @@ static int rigidbody_con_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } /* apply to active object */ - changed = ED_rigidbody_constraint_add(scene, ob, type, op->reports); + changed = ED_rigidbody_constraint_add(bmain, scene, ob, type, op->reports); if (changed) { /* send updates */ @@ -160,6 +164,7 @@ void RIGIDBODY_OT_constraint_add(wmOperatorType *ot) static int rigidbody_con_remove_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); Object *ob = (scene) ? OBACT : NULL; @@ -173,7 +178,7 @@ static int rigidbody_con_remove_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } else { - ED_rigidbody_constraint_remove(scene, ob); + ED_rigidbody_constraint_remove(bmain, scene, ob); } /* send updates */ diff --git a/source/blender/editors/physics/rigidbody_object.c b/source/blender/editors/physics/rigidbody_object.c index 26d8af82b2d..30597d95497 100644 --- a/source/blender/editors/physics/rigidbody_object.c +++ b/source/blender/editors/physics/rigidbody_object.c @@ -46,6 +46,7 @@ #include "BKE_depsgraph.h" #include "BKE_global.h" #include "BKE_group.h" +#include "BKE_main.h" #include "BKE_report.h" #include "BKE_rigidbody.h" @@ -87,7 +88,7 @@ static int ED_operator_rigidbody_add_poll(bContext *C) /* ----------------- */ -bool ED_rigidbody_object_add(Scene *scene, Object *ob, int type, ReportList *reports) +bool ED_rigidbody_object_add(Main *bmain, Scene *scene, Object *ob, int type, ReportList *reports) { RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene); @@ -107,7 +108,7 @@ bool ED_rigidbody_object_add(Scene *scene, Object *ob, int type, ReportList *rep scene->rigidbody_world = rbw; } if (rbw->group == NULL) { - rbw->group = BKE_group_add(G.main, "RigidBodyWorld"); + rbw->group = BKE_group_add(bmain, "RigidBodyWorld"); } /* make rigidbody object settings */ @@ -120,12 +121,13 @@ bool ED_rigidbody_object_add(Scene *scene, Object *ob, int type, ReportList *rep /* add object to rigid body group */ BKE_group_object_add(rbw->group, ob, scene, NULL); + DAG_relations_tag_update(bmain); DAG_id_tag_update(&ob->id, OB_RECALC_OB); return true; } -void ED_rigidbody_object_remove(Scene *scene, Object *ob) +void ED_rigidbody_object_remove(Main *bmain, Scene *scene, Object *ob) { RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene); @@ -133,6 +135,7 @@ void ED_rigidbody_object_remove(Scene *scene, Object *ob) if (rbw) BKE_group_object_unlink(rbw->group, ob, scene, NULL); + DAG_relations_tag_update(bmain); DAG_id_tag_update(&ob->id, OB_RECALC_OB); } @@ -143,13 +146,14 @@ void ED_rigidbody_object_remove(Scene *scene, Object *ob) static int rigidbody_object_add_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); Object *ob = ED_object_active_context(C); int type = RNA_enum_get(op->ptr, "type"); bool changed; /* apply to active object */ - changed = ED_rigidbody_object_add(scene, ob, type, op->reports); + changed = ED_rigidbody_object_add(bmain, scene, ob, type, op->reports); if (changed) { /* send updates */ @@ -186,13 +190,14 @@ void RIGIDBODY_OT_object_add(wmOperatorType *ot) static int rigidbody_object_remove_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); Object *ob = ED_object_active_context(C); bool changed = false; /* apply to active object */ if (!ELEM(NULL, ob, ob->rigidbody_object)) { - ED_rigidbody_object_remove(scene, ob); + ED_rigidbody_object_remove(bmain, scene, ob); changed = true; } @@ -232,13 +237,14 @@ void RIGIDBODY_OT_object_remove(wmOperatorType *ot) static int rigidbody_objects_add_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); int type = RNA_enum_get(op->ptr, "type"); bool changed = false; /* create rigid body objects and add them to the world's group */ CTX_DATA_BEGIN(C, Object *, ob, selected_objects) { - changed |= ED_rigidbody_object_add(scene, ob, type, op->reports); + changed |= ED_rigidbody_object_add(bmain, scene, ob, type, op->reports); } CTX_DATA_END; @@ -277,6 +283,7 @@ void RIGIDBODY_OT_objects_add(wmOperatorType *ot) static int rigidbody_objects_remove_exec(bContext *C, wmOperator *UNUSED(op)) { + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); bool changed = false; @@ -284,7 +291,7 @@ static int rigidbody_objects_remove_exec(bContext *C, wmOperator *UNUSED(op)) CTX_DATA_BEGIN(C, Object *, ob, selected_objects) { if (ob->rigidbody_object) { - ED_rigidbody_object_remove(scene, ob); + ED_rigidbody_object_remove(bmain, scene, ob); changed = true; } } diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index cbf87062955..014268262c4 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -579,6 +579,10 @@ void glaDrawPixelsSafe(float x, float y, int img_w, int img_h, int row_w, int fo draw_h = min_ii(img_h - off_y, ceil((scissor[3] - rast_y) / yzoom)); if (draw_w > 0 && draw_h > 0) { + + int bound_options; + GPU_BASIC_SHADER_DISABLE_AND_STORE(bound_options); + /* Don't use safe RasterPos (slower) if we can avoid it. */ if (rast_x >= 0 && rast_y >= 0) { glRasterPos2f(rast_x, rast_y); @@ -610,6 +614,8 @@ void glaDrawPixelsSafe(float x, float y, int img_w, int img_h, int row_w, int fo } glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + GPU_BASIC_SHADER_ENABLE_AND_RESTORE(bound_options); } } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 6413d0e86e7..a93c8c83cbb 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -2070,7 +2070,10 @@ void ED_screen_animation_timer(bContext *C, int redraws, int refresh, int sync, sad->refresh = refresh; sad->flag |= (enable < 0) ? ANIMPLAY_FLAG_REVERSE : 0; sad->flag |= (sync == 0) ? ANIMPLAY_FLAG_NO_SYNC : (sync == 1) ? ANIMPLAY_FLAG_SYNC : 0; - + + ScrArea *sa = CTX_wm_area(C); + sad->from_anim_edit = (ELEM(sa->spacetype, SPACE_IPO, SPACE_ACTION, SPACE_NLA, SPACE_TIME)); + screen->animtimer->customdata = sad; } diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 4111f67553a..f340f716ccb 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -3355,24 +3355,24 @@ static int match_area_with_refresh(int spacetype, int refresh) return 0; } -static int match_region_with_redraws(int spacetype, int regiontype, int redraws) +static int match_region_with_redraws(int spacetype, int regiontype, int redraws, bool from_anim_edit) { if (regiontype == RGN_TYPE_WINDOW) { switch (spacetype) { case SPACE_VIEW3D: - if (redraws & TIME_ALL_3D_WIN) + if ((redraws & TIME_ALL_3D_WIN) || from_anim_edit) return 1; break; case SPACE_IPO: case SPACE_ACTION: case SPACE_NLA: - if (redraws & TIME_ALL_ANIM_WIN) + if ((redraws & TIME_ALL_ANIM_WIN) || from_anim_edit) return 1; break; case SPACE_TIME: /* if only 1 window or 3d windows, we do timeline too */ - if (redraws & (TIME_ALL_ANIM_WIN | TIME_REGION | TIME_ALL_3D_WIN)) + if ((redraws & (TIME_ALL_ANIM_WIN | TIME_REGION | TIME_ALL_3D_WIN)) || from_anim_edit) return 1; break; case SPACE_BUTS: @@ -3380,7 +3380,7 @@ static int match_region_with_redraws(int spacetype, int regiontype, int redraws) return 1; break; case SPACE_SEQ: - if (redraws & (TIME_SEQ | TIME_ALL_ANIM_WIN)) + if ((redraws & (TIME_SEQ | TIME_ALL_ANIM_WIN)) || from_anim_edit) return 1; break; case SPACE_NODE: @@ -3388,11 +3388,11 @@ static int match_region_with_redraws(int spacetype, int regiontype, int redraws) return 1; break; case SPACE_IMAGE: - if (redraws & TIME_ALL_IMAGE_WIN) + if ((redraws & TIME_ALL_IMAGE_WIN) || from_anim_edit) return 1; break; case SPACE_CLIP: - if (redraws & TIME_CLIPS) + if ((redraws & TIME_CLIPS) || from_anim_edit) return 1; break; @@ -3572,7 +3572,7 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv if (ar == sad->ar) { redraw = true; } - else if (match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws)) { + else if (match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws, sad->from_anim_edit)) { redraw = true; } diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index e4e9976c10d..eba9448aa40 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1016,7 +1016,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) translation[1] = y; outline_alpha = 0.5; outline_col = brush->add_col; - final_radius = BKE_brush_size_get(scene, brush) * zoomx; + final_radius = (BKE_brush_size_get(scene, brush) * zoomx) / U.pixelsize; /* don't calculate rake angles while a stroke is active because the rake variables are global and * we may get interference with the stroke itself. For line strokes, such interference is visible */ diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index c0947dacbf0..8261a211ed0 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -707,10 +707,13 @@ static void insert_action_keys(bAnimContext *ac, short mode) * so it's easier for now to just read the F-Curve directly. * (TODO: add the full-blown PointerRNA relative parsing case here...) */ - if (ale->id && !ale->owner) + if (ale->id && !ale->owner) { insert_keyframe(reports, ale->id, NULL, ((fcu->grp) ? (fcu->grp->name) : (NULL)), fcu->rna_path, fcu->array_index, cfra, ts->keyframe_type, flag); - else - insert_vert_fcurve(fcu, cfra, fcu->curval, ts->keyframe_type, 0); + } + else { + const float curval = evaluate_fcurve(fcu, cfra); + insert_vert_fcurve(fcu, cfra, curval, ts->keyframe_type, 0); + } ale->update |= ANIM_UPDATE_DEFAULT; } diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index f1063996ca3..f38d36853d7 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -606,10 +606,13 @@ static void insert_graph_keys(bAnimContext *ac, eGraphKeys_InsertKey_Types mode) * - fcu->driver != NULL: If this is set, then it's a driver. If we don't check for this, we'd end * up adding the keyframes on a new F-Curve in the action data instead. */ - if (ale->id && !ale->owner && !fcu->driver) + if (ale->id && !ale->owner && !fcu->driver) { insert_keyframe(reports, ale->id, NULL, ((fcu->grp) ? (fcu->grp->name) : (NULL)), fcu->rna_path, fcu->array_index, cfra, ts->keyframe_type, flag); - else - insert_vert_fcurve(fcu, cfra, fcu->curval, ts->keyframe_type, 0); + } + else { + const float curval = evaluate_fcurve(fcu, cfra); + insert_vert_fcurve(fcu, cfra, curval, ts->keyframe_type, 0); + } ale->update |= ANIM_UPDATE_DEFAULT; } diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 3c2a66cd3af..7475e8b27fd 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -107,6 +107,7 @@ static void select_active_side(ListBase *seqbase, int sel_side, int channel, int break; case SEQ_SIDE_BOTH: seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL); + seq->flag |= SELECT; break; } } @@ -812,7 +813,7 @@ static int sequencer_select_handles_exec(bContext *C, wmOperator *op) seq->flag |= SEQ_RIGHTSEL; break; case SEQ_SIDE_BOTH: - seq->flag |= SEQ_LEFTSEL + SEQ_RIGHTSEL; + seq->flag |= SEQ_LEFTSEL | SEQ_RIGHTSEL; break; } } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 01e23f26568..445a4cbdfd6 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1179,10 +1179,10 @@ static void drawviewborder(Scene *scene, ARegion *ar, View3D *v3d) if (scene->r.mode & R_BORDER) { float x3, y3, x4, y4; - x3 = x1i + 1 + roundf(scene->r.border.xmin * (x2 - x1)); - y3 = y1i + 1 + roundf(scene->r.border.ymin * (y2 - y1)); - x4 = x1i + 1 + roundf(scene->r.border.xmax * (x2 - x1)); - y4 = y1i + 1 + roundf(scene->r.border.ymax * (y2 - y1)); + x3 = floorf(x1 + (scene->r.border.xmin * (x2 - x1))) - 1; + y3 = floorf(y1 + (scene->r.border.ymin * (y2 - y1))) - 1; + x4 = floorf(x1 + (scene->r.border.xmax * (x2 - x1))) + (U.pixelsize - 1); + y4 = floorf(y1 + (scene->r.border.ymax * (y2 - y1))) + (U.pixelsize - 1); cpack(0x4040FF); sdrawbox(x3, y3, x4, y4); diff --git a/source/blender/editors/space_view3d/view3d_ruler.c b/source/blender/editors/space_view3d/view3d_ruler.c index dfa76753f64..c6951c79609 100644 --- a/source/blender/editors/space_view3d/view3d_ruler.c +++ b/source/blender/editors/space_view3d/view3d_ruler.c @@ -679,9 +679,10 @@ static bool view3d_ruler_item_mousemove( if (ED_transform_snap_object_project_view3d_mixed( ruler_info->snap_context, + SCE_SELECT_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, - .snap_to_flag = SCE_SELECT_FACE, + .use_object_edit_cage = true, }, mval_fl, &dist_px, true, co, ray_normal)) @@ -691,6 +692,10 @@ static bool view3d_ruler_item_mousemove( madd_v3_v3v3fl(ray_start, co, ray_normal, eps_bias); ED_transform_snap_object_project_ray( ruler_info->snap_context, + &(const struct SnapObjectParams){ + .snap_select = SNAP_ALL, + .use_object_edit_cage = true, + }, ray_start, ray_normal, NULL, co_other, NULL); } @@ -703,9 +708,10 @@ static bool view3d_ruler_item_mousemove( if (ED_transform_snap_object_project_view3d_mixed( ruler_info->snap_context, + (SCE_SELECT_VERTEX | SCE_SELECT_EDGE) | (use_depth ? SCE_SELECT_FACE : 0), &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, - .snap_to_flag = (SCE_SELECT_VERTEX | SCE_SELECT_EDGE) | (use_depth ? SCE_SELECT_FACE : 0), + .use_object_edit_cage = true, }, mval_fl, &dist_px, use_depth, co, NULL)) diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index 47f81678699..384da277612 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -49,6 +49,7 @@ #include "ED_screen.h" #include "ED_space_api.h" +#include "ED_transform.h" #include "ED_transform_snap_object_context.h" #include "PIL_time.h" /* smoothview */ @@ -424,6 +425,9 @@ static bool walk_floor_distance_get( ret = ED_transform_snap_object_project_ray( walk->snap_context, + &(const struct SnapObjectParams){ + .snap_select = SNAP_ALL, + }, ray_start, ray_normal, r_distance, r_location, r_normal_dummy); @@ -455,6 +459,9 @@ static bool walk_ray_cast( ret = ED_transform_snap_object_project_ray( walk->snap_context, + &(const struct SnapObjectParams){ + .snap_select = SNAP_ALL, + }, ray_start, ray_normal, NULL, r_location, r_normal); diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 5c0c0bcd6c1..5b1a58497f0 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -2863,7 +2863,7 @@ static void initBend(TransInfo *t) //copy_v3_v3(t->center, ED_view3d_cursor3d_get(t->scene, t->view)); calculateCenterCursor(t, t->center); - calculateCenterGlobal(t); + calculateCenterGlobal(t, t->center, t->center_global); t->val = 0.0f; diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 11151a9c65a..0e0d085bf6f 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -369,6 +369,11 @@ typedef struct TransCustomData { unsigned int use_free : 1; } TransCustomData; +typedef struct TransCenterData { + float local[3], global[3]; + unsigned int is_set : 1; +} TransCenterData; + typedef struct TransInfo { int mode; /* current mode */ int flag; /* generic flags for special behaviors */ @@ -396,6 +401,9 @@ typedef struct TransInfo { float center[3]; /* center of transformation (in local-space) */ float center_global[3]; /* center of transformation (in global-space) */ float center2d[2]; /* center in screen coordinates */ + /* Lazy initialize center data for when we need other center values. + * V3D_AROUND_ACTIVE + 1 (static assert checks this) */ + TransCenterData center_cache[5]; short idx_max; /* maximum index on the input vector */ float snap[3]; /* Snapping Gears */ float snap_spatial[3]; /* Spatial snapping gears(even when rotating, scaling... etc) */ @@ -742,8 +750,11 @@ void restoreTransObjects(TransInfo *t); void recalcData(TransInfo *t); void calculateCenter2D(TransInfo *t); -void calculateCenterGlobal(TransInfo *t); +void calculateCenterGlobal( + TransInfo *t, const float center_local[3], + float r_center_global[3]); +const TransCenterData *transformCenter_from_type(TransInfo *t, int around); void calculateCenter(TransInfo *t); /* API functions for getting center points */ diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index ed6477392d8..67740644afe 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -1610,16 +1610,18 @@ void calculateCenter2D(TransInfo *t) } } -void calculateCenterGlobal(TransInfo *t) +void calculateCenterGlobal( + TransInfo *t, const float center_local[3], + float r_center_global[3]) { /* setting constraint center */ /* note, init functions may over-ride t->center */ if (t->flag & (T_EDIT | T_POSE)) { Object *ob = t->obedit ? t->obedit : t->poseobj; - mul_v3_m4v3(t->center_global, ob->obmat, t->center); + mul_v3_m4v3(r_center_global, ob->obmat, center_local); } else { - copy_v3_v3(t->center_global, t->center); + copy_v3_v3(r_center_global, center_local); } } @@ -1794,43 +1796,55 @@ bool calculateCenterActive(TransInfo *t, bool select_only, float r_center[3]) return ok; } - -void calculateCenter(TransInfo *t) +static void calculateCenter_FromAround(TransInfo *t, int around, float r_center[3]) { - switch (t->around) { + switch (around) { case V3D_AROUND_CENTER_BOUNDS: - calculateCenterBound(t, t->center); + calculateCenterBound(t, r_center); break; case V3D_AROUND_CENTER_MEAN: - calculateCenterMedian(t, t->center); + calculateCenterMedian(t, r_center); break; case V3D_AROUND_CURSOR: if (ELEM(t->spacetype, SPACE_IMAGE, SPACE_CLIP)) - calculateCenterCursor2D(t, t->center); + calculateCenterCursor2D(t, r_center); else if (t->spacetype == SPACE_IPO) - calculateCenterCursorGraph2D(t, t->center); + calculateCenterCursorGraph2D(t, r_center); else - calculateCenterCursor(t, t->center); + calculateCenterCursor(t, r_center); break; case V3D_AROUND_LOCAL_ORIGINS: /* Individual element center uses median center for helpline and such */ - calculateCenterMedian(t, t->center); + calculateCenterMedian(t, r_center); break; case V3D_AROUND_ACTIVE: { - if (calculateCenterActive(t, false, t->center)) { + if (calculateCenterActive(t, false, r_center)) { /* pass */ } else { /* fallback */ - calculateCenterMedian(t, t->center); + calculateCenterMedian(t, r_center); } break; } } +} + +void calculateCenter(TransInfo *t) +{ + calculateCenter_FromAround(t, t->around, t->center); + calculateCenterGlobal(t, t->center, t->center_global); + + /* avoid calculating again */ + { + TransCenterData *cd = &t->center_cache[t->around]; + copy_v3_v3(cd->local, t->center); + copy_v3_v3(cd->global, t->center_global); + cd->is_set = true; + } calculateCenter2D(t); - calculateCenterGlobal(t); /* for panning from cameraview */ if (t->flag & T_OBJECT) { @@ -1884,6 +1898,23 @@ void calculateCenter(TransInfo *t) } } +BLI_STATIC_ASSERT(ARRAY_SIZE(((TransInfo *)NULL)->center_cache) == (V3D_AROUND_ACTIVE + 1), "test size"); + +/** + * Lazy initialize transform center data, when we need to access center values from other types. + */ +const TransCenterData *transformCenter_from_type(TransInfo *t, int around) +{ + BLI_assert(around <= V3D_AROUND_ACTIVE); + TransCenterData *cd = &t->center_cache[around]; + if (cd->is_set == false) { + calculateCenter_FromAround(t, around, cd->local); + calculateCenterGlobal(t, cd->local, cd->global); + cd->is_set = true; + } + return cd; +} + void calculatePropRatio(TransInfo *t) { TransData *td = t->data; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 38f1d37acd6..e1cf7436236 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -324,7 +324,7 @@ void applyProject(TransInfo *t) if (ED_view3d_project_float_global(t->ar, iloc, mval_fl, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { if (snapObjectsTransform( - t, mval_fl, t->tsnap.modeSelect, &dist_px, + t, mval_fl, &dist_px, loc, no)) { // if (t->flag & (T_EDIT|T_POSE)) { @@ -553,10 +553,10 @@ static void initSnappingMode(TransInfo *t) { /* Exclude editmesh if using proportional edit */ if ((obedit->type == OB_MESH) && (t->flag & T_PROP_EDIT)) { - t->tsnap.modeSelect = SNAP_NOT_OBEDIT; + t->tsnap.modeSelect = SNAP_NOT_ACTIVE; } else { - t->tsnap.modeSelect = t->tsnap.snap_self ? SNAP_ALL : SNAP_NOT_OBEDIT; + t->tsnap.modeSelect = t->tsnap.snap_self ? SNAP_ALL : SNAP_NOT_ACTIVE; } } /* Particles edit mode*/ @@ -964,14 +964,14 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec)) if (t->tsnap.mode == SCE_SNAP_MODE_VOLUME) { found = peelObjectsTransform( - t, mval, t->tsnap.modeSelect, + t, mval, (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0, loc, no, NULL); } else { zero_v3(no); /* objects won't set this */ found = snapObjectsTransform( - t, mval, t->tsnap.modeSelect, &dist_px, + t, mval, &dist_px, loc, no); } @@ -1207,17 +1207,16 @@ static void TargetSnapClosest(TransInfo *t) } bool snapObjectsTransform( - TransInfo *t, const float mval[2], SnapSelect snap_select, + TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3]) { return ED_transform_snap_object_project_view3d_ex( t->tsnap.object_context, + t->scene->toolsettings->snap_mode, &(const struct SnapObjectParams){ - .snap_select = snap_select, - .snap_to = t->scene->toolsettings->snap_mode, - .use_object_edit = (t->flag & T_EDIT) != 0, - .use_object_active = (t->options & CTX_GPENCIL_STROKES) == 0, + .snap_select = ((t->options & CTX_GPENCIL_STROKES) != 0) ? SNAP_NOT_ACTIVE : t->tsnap.modeSelect, + .use_object_edit_cage = (t->flag & T_EDIT) != 0, }, mval, dist_px, NULL, r_loc, r_no, NULL); @@ -1228,18 +1227,16 @@ bool snapObjectsTransform( bool peelObjectsSnapContext( SnapObjectContext *sctx, - const float mval[2], SnapSelect snap_select, bool use_peel_object, + const float mval[2], + const struct SnapObjectParams *params, + const bool use_peel_object, /* return args */ float r_loc[3], float r_no[3], float *r_thickness) { ListBase depths_peel = {0}; ED_transform_snap_object_project_all_view3d_ex( sctx, - &(const struct SnapObjectParams){ - .snap_to = SCE_SNAP_MODE_FACE, - .snap_select = snap_select, - .use_object_edit = true, - }, + params, mval, -1.0f, false, &depths_peel); @@ -1299,13 +1296,19 @@ bool peelObjectsSnapContext( bool peelObjectsTransform( TransInfo *t, - const float mval[2], SnapSelect snap_select, bool use_peel_object, + const float mval[2], + const bool use_peel_object, /* return args */ float r_loc[3], float r_no[3], float *r_thickness) { return peelObjectsSnapContext( t->tsnap.object_context, - mval, snap_select, use_peel_object, + mval, + &(const struct SnapObjectParams){ + .snap_select = ((t->options & CTX_GPENCIL_STROKES) != 0) ? SNAP_NOT_ACTIVE : t->tsnap.modeSelect, + .use_object_edit_cage = (t->flag & T_EDIT) != 0, + }, + use_peel_object, r_loc, r_no, r_thickness); } @@ -1520,11 +1523,21 @@ static void applyGridIncrement(TransInfo *t, float *val, int max_index, const fl /* absolute snapping on grid based on global center */ if ((t->tsnap.snap_spatial_grid) && (t->mode == TFM_TRANSLATION)) { + const float *center_global = t->center_global; + + /* use a fallback for cursor selection, + * this isn't useful as a global center for absolute grid snapping + * since its not based on the position of the selection. */ + if (t->around == V3D_AROUND_CURSOR) { + const TransCenterData *cd = transformCenter_from_type(t, V3D_AROUND_CENTER_MEAN); + center_global = cd->global; + } + for (i = 0; i <= max_index; i++) { /* do not let unconstrained axis jump to absolute grid increments */ if (!(t->con.mode & CON_APPLY) || t->con.mode & (CON_AXIS0 << i)) { const float iter_fac = fac[action] * asp[i]; - val[i] = iter_fac * roundf((val[i] + t->center_global[i]) / iter_fac) - t->center_global[i]; + val[i] = iter_fac * roundf((val[i] + center_global[i]) / iter_fac) - center_global[i]; } } } diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index 62ca4e515a5..d7486372c36 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -992,7 +992,7 @@ static bool snapEditMesh( float imat[4][4]; float timat[3][3]; /* transpose inverse matrix for normals */ float ray_start_local[3], ray_normal_local[3]; - float local_scale, local_depth, len_diff; + float local_scale, local_depth; invert_m4_m4(imat, obmat); transpose_m3_m4(timat, imat); @@ -1089,6 +1089,7 @@ static bool snapEditMesh( * been *inside* boundbox, leading to snap failures (see T38409). * Note also ar might be null (see T38435), in this case we assume ray_start is ok! */ + float len_diff = 0.0f; if (do_ray_start_correction) { /* We *need* a reasonably valid len_diff in this case. * Use BHVTree to find the closest face from ray_start_local. @@ -1098,27 +1099,24 @@ static bool snapEditMesh( nearest.index = -1; nearest.dist_sq = FLT_MAX; /* Compute and store result. */ - BLI_bvhtree_find_nearest( - treedata->tree, ray_start_local, &nearest, treedata->nearest_callback, treedata); - if (nearest.index != -1) { + if (BLI_bvhtree_find_nearest( + treedata->tree, ray_start_local, &nearest, treedata->nearest_callback, treedata) != -1) + { len_diff = sqrtf(nearest.dist_sq); + float ray_org_local[3]; + + copy_v3_v3(ray_org_local, ray_origin); + mul_m4_v3(imat, ray_org_local); + + /* We pass a temp ray_start, set from object's boundbox, to avoid precision issues with very far + * away ray_start values (as returned in case of ortho view3d), see T38358. + */ + len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ + madd_v3_v3v3fl(ray_start_local, ray_org_local, ray_normal_local, + len_diff - len_v3v3(ray_start_local, ray_org_local)); + local_depth -= len_diff; } } - float ray_org_local[3]; - - copy_v3_v3(ray_org_local, ray_origin); - mul_m4_v3(imat, ray_org_local); - - /* We pass a temp ray_start, set from object's boundbox, to avoid precision issues with very far - * away ray_start values (as returned in case of ortho view3d), see T38358. - */ - len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ - madd_v3_v3v3fl(ray_start_local, ray_org_local, ray_normal_local, - len_diff - len_v3v3(ray_start_local, ray_org_local)); - local_depth -= len_diff; - } - else { - len_diff = 0.0f; } switch (snap_to) { @@ -1316,39 +1314,28 @@ static bool snapObject( static bool snapObjectsRay( SnapObjectContext *sctx, - SnapSelect snap_select, const short snap_to, + const unsigned short snap_to, const SnapSelect snap_select, + const bool use_object_edit_cage, const float mval[2], float *dist_px, - /* special handling of active and edit objects */ - Base *base_act, Object *obedit, const float ray_start[3], const float ray_normal[3], const float ray_origin[3], float *ray_depth, /* return args */ float r_loc[3], float r_no[3], int *r_index, Object **r_ob, float r_obmat[4][4], ListBase *r_hit_list) { - Base *base; bool retval = false; - bool snap_obedit_first = snap_select == SNAP_ALL && obedit; unsigned int ob_index = 0; - - if (snap_obedit_first) { - Object *ob = obedit; - - retval |= snapObject( - sctx, ob, ob->obmat, true, snap_to, - mval, dist_px, ob_index++, - ray_start, ray_normal, ray_origin, ray_depth, - r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); - } + Object *obedit = use_object_edit_cage ? sctx->scene->obedit : NULL; /* Need an exception for particle edit because the base is flagged with BA_HAS_RECALC_DATA * which makes the loop skip it, even the derived mesh will never change * * To solve that problem, we do it first as an exception. * */ - base = base_act; - if (base && base->object && base->object->mode & OB_MODE_PARTICLE_EDIT) { - Object *ob = base->object; + Base *base_act = sctx->scene->basact; + if (base_act && base_act->object && base_act->object->mode & OB_MODE_PARTICLE_EDIT) { + Object *ob = base_act->object; + retval |= snapObject( sctx, ob, ob->obmat, false, snap_to, mval, dist_px, ob_index++, @@ -1356,16 +1343,25 @@ static bool snapObjectsRay( r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); } - for (base = sctx->scene->base.first; base != NULL; base = base->next) { + bool ignore_object_selected = false, ignore_object_active = false; + switch (snap_select) { + case SNAP_ALL: + break; + case SNAP_NOT_SELECTED: + ignore_object_selected = true; + break; + case SNAP_NOT_ACTIVE: + ignore_object_active = true; + break; + } + for (Base *base = sctx->scene->base.first; base != NULL; base = base->next) { if ((BASE_VISIBLE_BGMODE(sctx->v3d_data.v3d, sctx->scene, base)) && (base->flag & (BA_HAS_RECALC_OB | BA_HAS_RECALC_DATA)) == 0 && - ((snap_select == SNAP_NOT_SELECTED && (base->flag & (SELECT | BA_WAS_SEL)) == 0) || - (ELEM(snap_select, SNAP_ALL, SNAP_NOT_OBEDIT) && base != base_act))) + !((ignore_object_selected && (base->flag & (SELECT | BA_WAS_SEL))) || + (ignore_object_active && base == base_act))) { Object *ob = base->object; - Object *ob_snap = ob; - bool use_obedit = false; if (ob->transflag & OB_DUPLI) { DupliObject *dupli_ob; @@ -1385,19 +1381,8 @@ static bool snapObjectsRay( free_object_duplilist(lb); } - if (obedit) { - if ((ob == obedit) && - (snap_obedit_first || (snap_select == SNAP_NOT_OBEDIT))) - { - continue; - } - - if (ob->data == obedit->data) { - /* for linked objects, use the same object but a different matrix */ - use_obedit = true; - ob_snap = obedit; - } - } + bool use_obedit = (obedit != NULL) && (ob->data == obedit->data); + Object *ob_snap = use_obedit ? obedit : ob; retval |= snapObject( sctx, ob_snap, ob->obmat, use_obedit, snap_to, @@ -1502,22 +1487,18 @@ void ED_transform_snap_object_context_set_editmesh_callbacks( bool ED_transform_snap_object_project_ray_ex( SnapObjectContext *sctx, + const unsigned short snap_to, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], float *ray_depth, float r_loc[3], float r_no[3], int *r_index, Object **r_ob, float r_obmat[4][4]) { - Base *base_act = params->use_object_active ? sctx->scene->basact : NULL; - Object *obedit = params->use_object_edit ? sctx->scene->obedit : NULL; - return snapObjectsRay( sctx, - params->snap_select, params->snap_to, + snap_to, params->snap_select, params->use_object_edit_cage, NULL, NULL, - base_act, obedit, ray_start, ray_normal, ray_start, ray_depth, - r_loc, r_no, r_index, - r_ob, r_obmat, NULL); + r_loc, r_no, r_index, r_ob, r_obmat, NULL); } /** @@ -1529,14 +1510,12 @@ bool ED_transform_snap_object_project_ray_ex( */ bool ED_transform_snap_object_project_ray_all( SnapObjectContext *sctx, + const unsigned short snap_to, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], float ray_depth, bool sort, ListBase *r_hit_list) { - Base *base_act = params->use_object_active ? sctx->scene->basact : NULL; - Object *obedit = params->use_object_edit ? sctx->scene->obedit : NULL; - if (ray_depth == -1.0f) { ray_depth = BVH_RAYCAST_DIST_MAX; } @@ -1547,9 +1526,8 @@ bool ED_transform_snap_object_project_ray_all( bool retval = snapObjectsRay( sctx, - params->snap_select, params->snap_to, + snap_to, params->snap_select, params->use_object_edit_cage, NULL, NULL, - base_act, obedit, ray_start, ray_normal, ray_start, &ray_depth, NULL, NULL, NULL, NULL, NULL, r_hit_list); @@ -1575,6 +1553,7 @@ bool ED_transform_snap_object_project_ray_all( */ static bool transform_snap_context_project_ray_impl( SnapObjectContext *sctx, + const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], float *ray_depth, float r_co[3], float r_no[3]) { @@ -1583,11 +1562,8 @@ static bool transform_snap_context_project_ray_impl( /* try snap edge, then face if it fails */ ret = ED_transform_snap_object_project_ray_ex( sctx, - &(const struct SnapObjectParams){ - .snap_select = SNAP_ALL, - .snap_to = SCE_SNAP_MODE_FACE, - .use_object_edit = (sctx->scene->obedit != NULL), - }, + SCE_SNAP_MODE_FACE, + params, ray_start, ray_normal, ray_depth, r_co, r_no, NULL, NULL, NULL); @@ -1597,6 +1573,7 @@ static bool transform_snap_context_project_ray_impl( bool ED_transform_snap_object_project_ray( SnapObjectContext *sctx, + const struct SnapObjectParams *params, const float ray_origin[3], const float ray_direction[3], float *ray_depth, float r_co[3], float r_no[3]) { @@ -1613,12 +1590,14 @@ bool ED_transform_snap_object_project_ray( return transform_snap_context_project_ray_impl( sctx, + params, ray_origin, ray_direction, ray_depth, r_co, r_no); } static bool transform_snap_context_project_view3d_mixed_impl( SnapObjectContext *sctx, + const unsigned short snap_to_flag, const struct SnapObjectParams *params, const float mval[2], float *dist_px, bool use_depth, @@ -1634,22 +1613,18 @@ static bool transform_snap_context_project_view3d_mixed_impl( const int elem_type[3] = {SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE, SCE_SNAP_MODE_FACE}; - BLI_assert(params->snap_to_flag != 0); - BLI_assert((params->snap_to_flag & ~(1 | 2 | 4)) == 0); - - struct SnapObjectParams params_temp = *params; + BLI_assert(snap_to_flag != 0); + BLI_assert((snap_to_flag & ~(1 | 2 | 4)) == 0); for (int i = 0; i < 3; i++) { - if ((params->snap_to_flag & (1 << i)) && (is_hit == false || use_depth)) { + if ((snap_to_flag & (1 << i)) && (is_hit == false || use_depth)) { if (use_depth == false) { ray_depth = BVH_RAYCAST_DIST_MAX; } - params_temp.snap_to = elem_type[i]; - if (ED_transform_snap_object_project_view3d( sctx, - ¶ms_temp, + elem_type[i], params, mval, dist_px, &ray_depth, r_co, r_no)) { @@ -1676,6 +1651,7 @@ static bool transform_snap_context_project_view3d_mixed_impl( */ bool ED_transform_snap_object_project_view3d_mixed( SnapObjectContext *sctx, + const unsigned short snap_to_flag, const struct SnapObjectParams *params, const float mval_fl[2], float *dist_px, bool use_depth, @@ -1683,13 +1659,14 @@ bool ED_transform_snap_object_project_view3d_mixed( { return transform_snap_context_project_view3d_mixed_impl( sctx, - params, + snap_to_flag, params, mval_fl, dist_px, use_depth, r_co, r_no); } bool ED_transform_snap_object_project_view3d_ex( SnapObjectContext *sctx, + const unsigned short snap_to, const struct SnapObjectParams *params, const float mval[2], float *dist_px, float *ray_depth, @@ -1710,19 +1687,17 @@ bool ED_transform_snap_object_project_view3d_ex( return false; } - Base *base_act = params->use_object_active ? sctx->scene->basact : NULL; - Object *obedit = params->use_object_edit ? sctx->scene->obedit : NULL; return snapObjectsRay( sctx, - params->snap_select, params->snap_to, + snap_to, params->snap_select, params->use_object_edit_cage, mval, dist_px, - base_act, obedit, ray_start, ray_normal, ray_orgigin, ray_depth, r_loc, r_no, r_index, NULL, NULL, NULL); } bool ED_transform_snap_object_project_view3d( SnapObjectContext *sctx, + const unsigned short snap_to, const struct SnapObjectParams *params, const float mval[2], float *dist_px, float *ray_depth, @@ -1730,6 +1705,7 @@ bool ED_transform_snap_object_project_view3d( { return ED_transform_snap_object_project_view3d_ex( sctx, + snap_to, params, mval, dist_px, ray_depth, @@ -1748,8 +1724,6 @@ bool ED_transform_snap_object_project_all_view3d_ex( { float ray_start[3], ray_normal[3]; - BLI_assert(params->snap_to == SCE_SNAP_MODE_FACE); - if (!ED_view3d_win_to_ray_ex( sctx->v3d_data.ar, sctx->v3d_data.v3d, mval, NULL, ray_normal, ray_start, true)) @@ -1759,6 +1733,7 @@ bool ED_transform_snap_object_project_all_view3d_ex( return ED_transform_snap_object_project_ray_all( sctx, + SCE_SNAP_MODE_FACE, params, ray_start, ray_normal, ray_depth, sort, r_hit_list); diff --git a/source/blender/gpu/GPU_basic_shader.h b/source/blender/gpu/GPU_basic_shader.h index df2da971845..1e2db6acc52 100644 --- a/source/blender/gpu/GPU_basic_shader.h +++ b/source/blender/gpu/GPU_basic_shader.h @@ -46,11 +46,12 @@ typedef enum GPUBasicShaderOption { GPU_SHADER_LIGHTING = (1 << 1), /* use lighting */ GPU_SHADER_TWO_SIDED = (1 << 2), /* flip normals towards viewer */ GPU_SHADER_TEXTURE_2D = (1 << 3), /* use 2D texture to replace diffuse color */ + GPU_SHADER_TEXTURE_RECT = (1 << 4), /* same as GPU_SHADER_TEXTURE_2D, for GL_TEXTURE_RECTANGLE */ - GPU_SHADER_SOLID_LIGHTING = (1 << 4), /* use faster lighting (set automatically) */ - GPU_SHADER_STIPPLE = (1 << 5), /* use stipple */ - GPU_SHADER_LINE = (1 << 6), /* draw lines */ - GPU_SHADER_OPTIONS_NUM = 7, + GPU_SHADER_SOLID_LIGHTING = (1 << 5), /* use faster lighting (set automatically) */ + GPU_SHADER_STIPPLE = (1 << 6), /* use stipple */ + GPU_SHADER_LINE = (1 << 7), /* draw lines */ + GPU_SHADER_OPTIONS_NUM = 8, GPU_SHADER_OPTION_COMBINATIONS = (1 << GPU_SHADER_OPTIONS_NUM) } GPUBasicShaderOption; @@ -76,6 +77,22 @@ void GPU_basic_shaders_exit(void); void GPU_basic_shader_bind(int options); int GPU_basic_shader_bound_options(void); +/* Only use for small blocks of code that don't support glsl shader. */ +#define GPU_BASIC_SHADER_DISABLE_AND_STORE(bound_options) \ +if (GPU_basic_shader_use_glsl_get()) { \ + if ((bound_options = GPU_basic_shader_bound_options())) { \ + GPU_basic_shader_bind(0); \ + } \ +} \ +else { bound_options = 0; } ((void)0) +#define GPU_BASIC_SHADER_ENABLE_AND_RESTORE(bound_options) \ +if (GPU_basic_shader_use_glsl_get()) { \ + if (bound_options) { \ + GPU_basic_shader_bind(bound_options); \ + } \ +} ((void)0) + + void GPU_basic_shader_colors(const float diffuse[3], const float specular[3], int shininess, float alpha); @@ -110,6 +127,9 @@ void GPU_basic_shader_stipple(GPUBasicShaderStipple stipple_id); void GPU_basic_shader_line_stipple(GLint stipple_factor, GLushort stipple_pattern); void GPU_basic_shader_line_width(float line_width); +bool GPU_basic_shader_use_glsl_get(void); +void GPU_basic_shader_use_glsl_set(bool enabled); + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h index a8656c05224..aefaf1a0f54 100644 --- a/source/blender/gpu/GPU_buffers.h +++ b/source/blender/gpu/GPU_buffers.h @@ -49,6 +49,7 @@ struct DerivedMesh; struct GSet; struct GPUVertPointLink; struct GPUDrawObject; +struct GridCommonGPUBuffer; struct PBVH; struct MVert; @@ -147,6 +148,7 @@ typedef struct GPUVertPointLink { /* used for GLSL materials */ typedef struct GPUAttrib { int index; + int info_index; int size; int type; } GPUAttrib; @@ -159,9 +161,6 @@ void GPU_buffer_free(GPUBuffer *buffer); void GPU_drawobject_free(struct DerivedMesh *dm); -/* free special global multires grid buffer */ -void GPU_buffer_multires_free(bool force); - /* flag that controls data type to fill buffer with, a modifier will prepare. */ typedef enum { GPU_BUFFER_VERTEX = 0, @@ -179,6 +178,10 @@ typedef enum { GPU_BINDING_INDEX = 1, } GPUBindingType; +typedef enum { + GPU_ATTR_INFO_SRGB = (1 << 0), +} GPUAttrInfo; + /* called before drawing */ void GPU_vertex_setup(struct DerivedMesh *dm); void GPU_normal_setup(struct DerivedMesh *dm); @@ -226,8 +229,9 @@ GPU_PBVH_Buffers *GPU_build_mesh_pbvh_buffers( const int *face_indices, const int face_indices_len); -GPU_PBVH_Buffers *GPU_build_grid_pbvh_buffers(int *grid_indices, int totgrid, - unsigned int **grid_hidden, int gridsize, const struct CCGKey *key); +GPU_PBVH_Buffers *GPU_build_grid_pbvh_buffers( + int *grid_indices, int totgrid,unsigned int **grid_hidden, int gridsize, const struct CCGKey *key, + struct GridCommonGPUBuffer **grid_common_gpu_buffer); GPU_PBVH_Buffers *GPU_build_bmesh_pbvh_buffers(bool smooth_shading); @@ -262,5 +266,6 @@ void GPU_init_draw_pbvh_BB(void); bool GPU_pbvh_buffers_diffuse_changed(GPU_PBVH_Buffers *buffers, struct GSet *bm_faces, bool show_diffuse_color); void GPU_free_pbvh_buffers(GPU_PBVH_Buffers *buffers); +void GPU_free_pbvh_buffer_multires(struct GridCommonGPUBuffer **grid_common_gpu_buffer); #endif diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index fc2ca16db59..a79334df8ce 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -241,6 +241,7 @@ void GPU_material_vertex_attributes(GPUMaterial *material, bool GPU_material_do_color_management(GPUMaterial *mat); bool GPU_material_use_new_shading_nodes(GPUMaterial *mat); +bool GPU_material_use_world_space_shading(GPUMaterial *mat); /* Exported shading */ diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 4c674b460aa..762329ee077 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -104,6 +104,7 @@ typedef struct GPUVertexAttribs { struct { int type; int glindex; + int glinfoindoex; int gltexco; int attribid; char name[64]; /* MAX_CUSTOMDATA_LAYER_NAME */ diff --git a/source/blender/gpu/intern/gpu_basic_shader.c b/source/blender/gpu/intern/gpu_basic_shader.c index 088dac6f6cc..b0669225a4d 100644 --- a/source/blender/gpu/intern/gpu_basic_shader.c +++ b/source/blender/gpu/intern/gpu_basic_shader.c @@ -51,8 +51,6 @@ /* State */ -static const bool USE_GLSL = false; - static struct { GPUShader *cached_shaders[GPU_SHADER_OPTION_COMBINATIONS]; bool failed_shaders[GPU_SHADER_OPTION_COMBINATIONS]; @@ -269,6 +267,24 @@ const GLubyte stipple_hexagon[128] = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}; /* ********************************************* */ +/* GLSL State */ + +static bool USE_GLSL = false; + +/** + * \note this isn't part of the basic shader API, + * only set from the command line once on startup. + */ +void GPU_basic_shader_use_glsl_set(bool enabled) +{ + USE_GLSL = enabled; +} + +bool GPU_basic_shader_use_glsl_get(void) +{ + return USE_GLSL; +} + /* Init / exit */ void GPU_basic_shaders_init(void) @@ -308,6 +324,9 @@ static int detect_options() if (glIsEnabled(GL_TEXTURE_2D)) options |= GPU_SHADER_TEXTURE_2D; + if (glIsEnabled(GL_TEXTURE_RECTANGLE)) + options |= GPU_SHADER_TEXTURE_RECT; + GPU_SHADER_TEXTURE_RECT if (glIsEnabled(GL_COLOR_MATERIAL)) options |= GPU_SHADER_USE_COLOR; @@ -347,8 +366,10 @@ static GPUShader *gpu_basic_shader(int options) strcat(defines, "#define USE_COLOR\n"); if (options & GPU_SHADER_TWO_SIDED) strcat(defines, "#define USE_TWO_SIDED\n"); - if (options & GPU_SHADER_TEXTURE_2D) + if (options & (GPU_SHADER_TEXTURE_2D | GPU_SHADER_TEXTURE_RECT)) strcat(defines, "#define USE_TEXTURE\n"); + if (options & GPU_SHADER_TEXTURE_RECT) + strcat(defines, "#define USE_TEXTURE_RECTANGLE\n"); if (options & GPU_SHADER_STIPPLE) strcat(defines, "#define USE_STIPPLE\n"); if (options & GPU_SHADER_LINE) { @@ -369,7 +390,7 @@ static GPUShader *gpu_basic_shader(int options) if (shader) { /* set texture map to first texture unit */ - if (options & GPU_SHADER_TEXTURE_2D) { + if (options & (GPU_SHADER_TEXTURE_2D | GPU_SHADER_TEXTURE_RECT)) { GPU_shader_bind(shader); glUniform1i(GPU_shader_get_uniform(shader, "texture_map"), 0); GPU_shader_unbind(); @@ -399,6 +420,23 @@ void GPU_basic_shader_bind(int options) { if (USE_GLSL) { if (options) { + const int bound_options = GPU_MATERIAL_STATE.bound_options; + + /* texture options need to be set for basic shader too */ + if (options & GPU_SHADER_TEXTURE_2D) { + glEnable(GL_TEXTURE_2D); + } + else if (bound_options & GPU_SHADER_TEXTURE_2D) { + glDisable(GL_TEXTURE_2D); + } + + if (options & GPU_SHADER_TEXTURE_RECT) { + glEnable(GL_TEXTURE_RECTANGLE); + } + else if (bound_options & GPU_SHADER_TEXTURE_RECT) { + glDisable(GL_TEXTURE_RECTANGLE); + } + GPUShader *shader = gpu_basic_shader(options); if (shader) { @@ -411,7 +449,7 @@ void GPU_basic_shader_bind(int options) } } else { - int bound_options = GPU_MATERIAL_STATE.bound_options; + const int bound_options = GPU_MATERIAL_STATE.bound_options; if (options & GPU_SHADER_LIGHTING) { glEnable(GL_LIGHTING); @@ -438,10 +476,24 @@ void GPU_basic_shader_bind(int options) glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env_mode); } else if (bound_options & GPU_SHADER_TEXTURE_2D) { - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + if ((options & GPU_SHADER_TEXTURE_RECT) == 0) { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } glDisable(GL_TEXTURE_2D); } + if (options & GPU_SHADER_TEXTURE_RECT) { + GLint env_mode = (options & (GPU_SHADER_USE_COLOR | GPU_SHADER_LIGHTING)) ? GL_MODULATE : GL_REPLACE; + glEnable(GL_TEXTURE_RECTANGLE); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env_mode); + } + else if (bound_options & GPU_SHADER_TEXTURE_RECT) { + if ((options & GPU_SHADER_TEXTURE_2D) == 0) { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } + glDisable(GL_TEXTURE_RECTANGLE); + } + if ((options & GPU_SHADER_LINE) && (options & GPU_SHADER_STIPPLE)) { glEnable(GL_LINE_STIPPLE); } diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index 09d0a383426..2c6f204d9d0 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -107,10 +107,12 @@ static GPUAttrib attribData[MAX_GPU_ATTRIB_DATA] = { { -1, 0, 0 } }; static ThreadMutex buffer_mutex = BLI_MUTEX_INITIALIZER; /* multires global buffer, can be used for many grids having the same grid size */ -static GPUBuffer *mres_glob_buffer = NULL; -static int mres_prev_gridsize = -1; -static GLenum mres_prev_index_type = 0; -static unsigned mres_prev_totquad = 0; +typedef struct GridCommonGPUBuffer { + GPUBuffer *mres_buffer; + int mres_prev_gridsize; + GLenum mres_prev_index_type; + unsigned mres_prev_totquad; +} GridCommonGPUBuffer; void GPU_buffer_material_finalize(GPUDrawObject *gdo, GPUBufferMaterial *matinfo, int totmat) { @@ -407,33 +409,6 @@ void GPU_buffer_free(GPUBuffer *buffer) BLI_mutex_unlock(&buffer_mutex); } -void GPU_buffer_multires_free(bool force) -{ - if (!mres_glob_buffer) { - /* Early output, no need to lock in this case, */ - return; - } - - if (force && BLI_thread_is_main()) { - if (mres_glob_buffer) { - if (mres_glob_buffer->id) - glDeleteBuffers(1, &mres_glob_buffer->id); - MEM_freeN(mres_glob_buffer); - } - } - else { - BLI_mutex_lock(&buffer_mutex); - gpu_buffer_free_intern(mres_glob_buffer); - BLI_mutex_unlock(&buffer_mutex); - } - - mres_glob_buffer = NULL; - mres_prev_gridsize = -1; - mres_prev_index_type = 0; - mres_prev_totquad = 0; -} - - void GPU_drawobject_free(DerivedMesh *dm) { GPUDrawObject *gdo; @@ -822,6 +797,12 @@ void GPU_interleaved_attrib_setup(GPUBuffer *buffer, GPUAttrib data[], int numda for (i = 0; i < numdata; i++) { glEnableVertexAttribArray(data[i].index); + int info = 0; + if (data[i].type == GL_UNSIGNED_BYTE) { + info |= GPU_ATTR_INFO_SRGB; + } + glUniform1i(data[i].info_index, info); + glVertexAttribPointer(data[i].index, data[i].size, data[i].type, GL_TRUE, elementsize, BUFFER_OFFSET(offset)); offset += data[i].size * GPU_typesize(data[i].type); @@ -1003,6 +984,7 @@ struct GPU_PBVH_Buffers { const int *grid_indices; int totgrid; bool has_hidden; + bool is_index_buf_global; /* Means index_buf uses global bvh's grid_common_gpu_buffer, **DO NOT** free it! */ bool use_bmesh; @@ -1220,8 +1202,10 @@ GPU_PBVH_Buffers *GPU_build_mesh_pbvh_buffers( /* An element index buffer is used for smooth shading, but flat * shading requires separate vertex normals so an index buffer is * can't be used there. */ - if (buffers->smooth) + if (buffers->smooth) { buffers->index_buf = GPU_buffer_alloc(sizeof(unsigned short) * tottri * 3); + buffers->is_index_buf_global = false; + } if (buffers->index_buf) { /* Fill the triangle buffer */ @@ -1242,8 +1226,11 @@ GPU_PBVH_Buffers *GPU_build_mesh_pbvh_buffers( GPU_buffer_unlock(buffers->index_buf, GPU_BINDING_INDEX); } else { - GPU_buffer_free(buffers->index_buf); + if (!buffers->is_index_buf_global) { + GPU_buffer_free(buffers->index_buf); + } buffers->index_buf = NULL; + buffers->is_index_buf_global = false; } } @@ -1410,22 +1397,33 @@ void GPU_update_grid_pbvh_buffers(GPU_PBVH_Buffers *buffers, CCGElem **grids, } (void)0 /* end FILL_QUAD_BUFFER */ -static GPUBuffer *gpu_get_grid_buffer(int gridsize, GLenum *index_type, unsigned *totquad) +static GPUBuffer *gpu_get_grid_buffer( + int gridsize, GLenum *index_type, unsigned *totquad, GridCommonGPUBuffer **grid_common_gpu_buffer) { /* used in the FILL_QUAD_BUFFER macro */ BLI_bitmap * const *grid_hidden = NULL; const int *grid_indices = NULL; int totgrid = 1; + GridCommonGPUBuffer *gridbuff = *grid_common_gpu_buffer; + + if (gridbuff == NULL) { + *grid_common_gpu_buffer = gridbuff = MEM_mallocN(sizeof(GridCommonGPUBuffer), __func__); + gridbuff->mres_buffer = NULL; + gridbuff->mres_prev_gridsize = -1; + gridbuff->mres_prev_index_type = 0; + gridbuff->mres_prev_totquad = 0; + } + /* VBO is already built */ - if (mres_glob_buffer && mres_prev_gridsize == gridsize) { - *index_type = mres_prev_index_type; - *totquad = mres_prev_totquad; - return mres_glob_buffer; + if (gridbuff->mres_buffer && gridbuff->mres_prev_gridsize == gridsize) { + *index_type = gridbuff->mres_prev_index_type; + *totquad = gridbuff->mres_prev_totquad; + return gridbuff->mres_buffer; } /* we can't reuse old, delete the existing buffer */ - else if (mres_glob_buffer) { - GPU_buffer_free(mres_glob_buffer); + else if (gridbuff->mres_buffer) { + GPU_buffer_free(gridbuff->mres_buffer); } /* Build new VBO */ @@ -1433,17 +1431,17 @@ static GPUBuffer *gpu_get_grid_buffer(int gridsize, GLenum *index_type, unsigned if (gridsize * gridsize < USHRT_MAX) { *index_type = GL_UNSIGNED_SHORT; - FILL_QUAD_BUFFER(unsigned short, *totquad, mres_glob_buffer); + FILL_QUAD_BUFFER(unsigned short, *totquad, gridbuff->mres_buffer); } else { *index_type = GL_UNSIGNED_INT; - FILL_QUAD_BUFFER(unsigned int, *totquad, mres_glob_buffer); + FILL_QUAD_BUFFER(unsigned int, *totquad, gridbuff->mres_buffer); } - mres_prev_gridsize = gridsize; - mres_prev_index_type = *index_type; - mres_prev_totquad = *totquad; - return mres_glob_buffer; + gridbuff->mres_prev_gridsize = gridsize; + gridbuff->mres_prev_index_type = *index_type; + gridbuff->mres_prev_totquad = *totquad; + return gridbuff->mres_buffer; } #define FILL_FAST_BUFFER(type_) \ @@ -1470,8 +1468,9 @@ static GPUBuffer *gpu_get_grid_buffer(int gridsize, GLenum *index_type, unsigned } \ } (void)0 -GPU_PBVH_Buffers *GPU_build_grid_pbvh_buffers(int *grid_indices, int totgrid, - BLI_bitmap **grid_hidden, int gridsize, const CCGKey *key) +GPU_PBVH_Buffers *GPU_build_grid_pbvh_buffers( + int *grid_indices, int totgrid, BLI_bitmap **grid_hidden, int gridsize, const CCGKey *key, + GridCommonGPUBuffer **grid_common_gpu_buffer) { GPU_PBVH_Buffers *buffers; int totquad; @@ -1500,8 +1499,10 @@ GPU_PBVH_Buffers *GPU_build_grid_pbvh_buffers(int *grid_indices, int totgrid, } if (totquad == fully_visible_totquad) { - buffers->index_buf = gpu_get_grid_buffer(gridsize, &buffers->index_type, &buffers->tot_quad); + buffers->index_buf = gpu_get_grid_buffer( + gridsize, &buffers->index_type, &buffers->tot_quad, grid_common_gpu_buffer); buffers->has_hidden = false; + buffers->is_index_buf_global = true; } else { buffers->tot_quad = totquad; @@ -1516,6 +1517,7 @@ GPU_PBVH_Buffers *GPU_build_grid_pbvh_buffers(int *grid_indices, int totgrid, } buffers->has_hidden = true; + buffers->is_index_buf_global = false; } /* Build coord/normal VBO */ @@ -1740,8 +1742,9 @@ void GPU_update_bmesh_pbvh_buffers(GPU_PBVH_Buffers *buffers, const int use_short = (maxvert < USHRT_MAX); /* Initialize triangle index buffer */ - if (buffers->index_buf) + if (buffers->index_buf && !buffers->is_index_buf_global) GPU_buffer_free(buffers->index_buf); + buffers->is_index_buf_global = false; buffers->index_buf = GPU_buffer_alloc((use_short ? sizeof(unsigned short) : sizeof(unsigned int)) * 3 * tottri); @@ -1786,12 +1789,19 @@ void GPU_update_bmesh_pbvh_buffers(GPU_PBVH_Buffers *buffers, } else { /* Memory map failed */ - GPU_buffer_free(buffers->index_buf); + if (!buffers->is_index_buf_global) { + GPU_buffer_free(buffers->index_buf); + } buffers->index_buf = NULL; + buffers->is_index_buf_global = false; } } else if (buffers->index_buf) { - GPU_buffer_free(buffers->index_buf); + if (!buffers->is_index_buf_global) { + GPU_buffer_free(buffers->index_buf); + } + buffers->index_buf = NULL; + buffers->is_index_buf_global = false; } } @@ -1985,7 +1995,7 @@ void GPU_free_pbvh_buffers(GPU_PBVH_Buffers *buffers) if (buffers) { if (buffers->vert_buf) GPU_buffer_free(buffers->vert_buf); - if (buffers->index_buf && (buffers->tot_tri || buffers->has_hidden)) + if (buffers->index_buf && !buffers->is_index_buf_global) GPU_buffer_free(buffers->index_buf); if (buffers->index_buf_fast) GPU_buffer_free(buffers->index_buf_fast); @@ -1998,6 +2008,20 @@ void GPU_free_pbvh_buffers(GPU_PBVH_Buffers *buffers) } } +void GPU_free_pbvh_buffer_multires(GridCommonGPUBuffer **grid_common_gpu_buffer) +{ + GridCommonGPUBuffer *gridbuff = *grid_common_gpu_buffer; + + if (gridbuff) { + if (gridbuff->mres_buffer) { + BLI_mutex_lock(&buffer_mutex); + gpu_buffer_free_intern(gridbuff->mres_buffer); + BLI_mutex_unlock(&buffer_mutex); + } + MEM_freeN(gridbuff); + *grid_common_gpu_buffer = NULL; + } +} /* debug function, draws the pbvh BB */ void GPU_draw_pbvh_BB(float min[3], float max[3], bool leaf) diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 94d52c3617c..58ef4063430 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -749,6 +749,7 @@ static char *code_generate_vertex(ListBase *nodes, const GPUMatType type) BLI_dynstr_appendf(ds, "%s %s att%d;\n", GLEW_VERSION_3_0 ? "in" : "attribute", GPU_DATATYPE_STR[input->type], input->attribid); + BLI_dynstr_appendf(ds, "uniform int att%d_info;\n", input->attribid); BLI_dynstr_appendf(ds, "%s %s var%d;\n", GLEW_VERSION_3_0 ? "out" : "varying", GPU_DATATYPE_STR[input->type], input->attribid); @@ -801,7 +802,8 @@ static char *code_generate_vertex(ListBase *nodes, const GPUMatType type) BLI_dynstr_appendf(ds, "#ifndef USE_OPENSUBDIV\n"); } #endif - BLI_dynstr_appendf(ds, "\tvar%d = att%d;\n", input->attribid, input->attribid); + BLI_dynstr_appendf(ds, "\tset_var_from_attr(att%d, att%d_info, var%d);\n", + input->attribid, input->attribid, input->attribid); #ifdef WITH_OPENSUBDIV if (is_mtface) { BLI_dynstr_appendf(ds, "#endif\n"); diff --git a/source/blender/gpu/intern/gpu_init_exit.c b/source/blender/gpu/intern/gpu_init_exit.c index da4dd65d2e1..8fed6a9ee80 100644 --- a/source/blender/gpu/intern/gpu_init_exit.c +++ b/source/blender/gpu/intern/gpu_init_exit.c @@ -73,7 +73,6 @@ void GPU_exit(void) gpu_codegen_exit(); gpu_extensions_exit(); /* must come last */ - GPU_buffer_multires_free(true); initialized = false; } diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index aaa52b2c3f6..02f58ea6df2 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -212,6 +212,9 @@ static void gpu_material_set_attrib_id(GPUMaterial *material) BLI_snprintf(name, sizeof(name), "att%d", attribs->layer[a].attribid); attribs->layer[a].glindex = GPU_shader_get_attribute(shader, name); + BLI_snprintf(name, sizeof(name), "att%d_info", attribs->layer[a].attribid); + attribs->layer[a].glinfoindoex = GPU_shader_get_uniform(shader, name); + if (attribs->layer[a].glindex >= 0) { attribs->layer[b] = attribs->layer[a]; b++; @@ -514,6 +517,11 @@ bool GPU_material_use_new_shading_nodes(GPUMaterial *mat) return BKE_scene_use_new_shading_nodes(mat->scene); } +bool GPU_material_use_world_space_shading(GPUMaterial *mat) +{ + return BKE_scene_use_world_space_shading(mat->scene); +} + static GPUNodeLink *lamp_get_visibility(GPUMaterial *mat, GPULamp *lamp, GPUNodeLink **lv, GPUNodeLink **dist) { GPUNodeLink *visifac; diff --git a/source/blender/gpu/shaders/gpu_shader_basic_frag.glsl b/source/blender/gpu/shaders/gpu_shader_basic_frag.glsl index 6b6679b60df..ea5f6aef005 100644 --- a/source/blender/gpu/shaders/gpu_shader_basic_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_basic_frag.glsl @@ -3,6 +3,7 @@ * * USE_COLOR: use glColor for diffuse colors * USE_TEXTURE: use texture for diffuse colors + * USE_TEXTURE_RECTANGLE: use GL_TEXTURE_RECTANGLE instead of GL_TEXTURE_2D * USE_SCENE_LIGHTING: use lights (up to 8) * USE_SOLID_LIGHTING: assume 3 directional lights for solid draw mode * USE_TWO_SIDED: flip normal towards viewer @@ -39,8 +40,16 @@ varying vec4 varying_vertex_color; #endif #ifdef USE_TEXTURE +#ifdef USE_TEXTURE_RECTANGLE +#define sampler2D_default sampler2DRect +#define texture2D_default texture2DRect +#else +#define sampler2D_default sampler2D +#define texture2D_default texture2D +#endif + varying vec2 varying_texture_coord; -uniform sampler2D texture_map; +uniform sampler2D_default texture_map; #endif #ifdef USE_STIPPLE @@ -229,12 +238,12 @@ void main() float alpha; #if defined(USE_TEXTURE) && defined(USE_COLOR) - vec4 texture_color = texture2D(texture_map, varying_texture_coord); + vec4 texture_color = texture2D_default(texture_map, varying_texture_coord); L_diffuse *= texture_color.rgb * varying_vertex_color.rgb; alpha = texture_color.a * varying_vertex_color.a; #elif defined(USE_TEXTURE) - vec4 texture_color = texture2D(texture_map, varying_texture_coord); + vec4 texture_color = texture2D_default(texture_map, varying_texture_coord); L_diffuse *= texture_color.rgb; alpha = texture_color.a; @@ -259,9 +268,9 @@ void main() /* no lighting */ #if defined(USE_TEXTURE) && defined(USE_COLOR) - gl_FragColor = texture2D(texture_map, varying_texture_coord) * varying_vertex_color; + gl_FragColor = texture2D_default(texture_map, varying_texture_coord) * varying_vertex_color; #elif defined(USE_TEXTURE) - gl_FragColor = texture2D(texture_map, varying_texture_coord); + gl_FragColor = texture2D_default(texture_map, varying_texture_coord); #elif defined(USE_COLOR) gl_FragColor = varying_vertex_color; #else diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 89de236fd29..9914c4bb362 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -218,6 +218,42 @@ void point_transform_m4v3(vec3 vin, mat4 mat, out vec3 vout) vout = (mat * vec4(vin, 1.0)).xyz; } +void point_texco_remap_square(vec3 vin, out vec3 vout) +{ + vout = vec3(vin - vec3(0.5, 0.5, 0.5)) * 2.0; +} + +void point_map_to_sphere(vec3 vin, out vec3 vout) +{ + float len = length(vin); + float v, u; + if (len > 0.0) { + if (vin.x == 0.0 && vin.y == 0.0) + u = 0.0; + else + u = (1.0 - atan(vin.x, vin.y) / M_PI) / 2.0; + + v = 1.0 - acos(vin.z / len) / M_PI; + } + else + v = u = 0.0; + + vout = vec3(u, v, 0.0); +} + +void point_map_to_tube(vec3 vin, out vec3 vout) +{ + float u, v; + v = (vin.z + 1.0) * 0.5; + float len = sqrt(vin.x * vin.x + vin.y * vin[1]); + if (len > 0.0) + u = (1.0 - (atan(vin.x / len, vin.y / len) / M_PI)) * 0.5; + else + v = u = 0.0; + + vout = vec3(u, v, 0.0); +} + void mapping(vec3 vec, mat4 mat, vec3 minvec, vec3 maxvec, float domin, float domax, out vec3 outvec) { outvec = (mat * vec4(vec, 1.0)).xyz; @@ -2363,6 +2399,17 @@ void generated_from_orco(vec3 orco, out vec3 generated) generated = orco * 0.5 + vec3(0.5); } +int floor_to_int(float x) +{ + return int(floor(x)); +} + +int quick_floor(float x) +{ + return int(x) - ((x < 0) ? 1 : 0); +} + +#ifdef BIT_OPERATIONS float integer_noise(int n) { int nn; @@ -2372,12 +2419,6 @@ float integer_noise(int n) return 0.5 * (float(nn) / 1073741824.0); } -int floor_to_int(float x) -{ - return int(floor(x)); -} - -#ifdef BIT_OPERATIONS uint hash(uint kx, uint ky, uint kz) { #define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) @@ -2418,9 +2459,9 @@ float bits_to_01(uint bits) float cellnoise(vec3 p) { - int ix = floor_to_int(p.x); - int iy = floor_to_int(p.y); - int iz = floor_to_int(p.z); + int ix = quick_floor(p.x); + int iy = quick_floor(p.y); + int iz = quick_floor(p.z); return bits_to_01(hash(uint(ix), uint(iy), uint(iz))); } @@ -2627,9 +2668,6 @@ void node_gamma(vec4 col, float gamma, out vec4 outcol) void node_attribute(vec3 attr, out vec4 outcol, out vec3 outvec, out float outf) { - /* TODO(sergey): This needs linearization for vertex color. - * But how to detect cases when input is linear and when it's srgb? - */ outcol = vec4(attr, 1.0); outvec = attr; outf = (attr.x + attr.y + attr.z) / 3.0; @@ -2763,16 +2801,17 @@ void node_tex_checker(vec3 co, vec4 color1, vec4 color2, float scale, out vec4 c p.y = (p.y + 0.000001) * 0.999999; p.z = (p.z + 0.000001) * 0.999999; - int xi = abs(int(floor(p.x))); - int yi = abs(int(floor(p.y))); - int zi = abs(int(floor(p.z))); + int xi = int(abs(floor(p.x))); + int yi = int(abs(floor(p.y))); + int zi = int(abs(floor(p.z))); - bool check = ((xi % 2 == yi % 2) == bool(zi % 2)); + bool check = ((mod(xi, 2) == mod(yi, 2)) == bool(mod(zi, 2))); color = check ? color1 : color2; fac = check ? 1.0 : 0.0; } +#ifdef BIT_OPERATIONS vec2 calc_brick_texture(vec3 p, float mortar_size, float bias, float brick_width, float row_height, float offset_amount, int offset_frequency, @@ -2799,6 +2838,7 @@ vec2 calc_brick_texture(vec3 p, float mortar_size, float bias, x > (brick_width - mortar_size) || y > (row_height - mortar_size)) ? 1.0 : 0.0); } +#endif void node_tex_brick(vec3 co, vec4 color1, vec4 color2, @@ -2809,6 +2849,7 @@ void node_tex_brick(vec3 co, float squash_amount, float squash_frequency, out vec4 color, out float fac) { +#ifdef BIT_OPERATIONS vec2 f2 = calc_brick_texture(co * scale, mortar_size, bias, brick_width, row_height, @@ -2822,6 +2863,10 @@ void node_tex_brick(vec3 co, } color = (f == 1.0) ? mortar : color1; fac = f; +#else + color = vec4(1.0); + fac = 1.0; +#endif } void node_tex_clouds(vec3 co, float size, out vec4 color, out float fac) @@ -2866,6 +2911,82 @@ void node_tex_image(vec3 co, sampler2D ima, out vec4 color, out float alpha) alpha = color.a; } +void node_tex_image_box(vec3 texco, + vec3 nob, + sampler2D ima, + float blend, + out vec4 color, + out float alpha) +{ + /* project from direction vector to barycentric coordinates in triangles */ + nob = vec3(abs(nob.x), abs(nob.y), abs(nob.z)); + nob /= (nob.x + nob.y + nob.z); + + /* basic idea is to think of this as a triangle, each corner representing + * one of the 3 faces of the cube. in the corners we have single textures, + * in between we blend between two textures, and in the middle we a blend + * between three textures. + * + * the Nxyz values are the barycentric coordinates in an equilateral + * triangle, which in case of blending, in the middle has a smaller + * equilateral triangle where 3 textures blend. this divides things into + * 7 zones, with an if () test for each zone */ + + vec3 weight = vec3(0.0, 0.0, 0.0); + float limit = 0.5 * (1.0 + blend); + + /* first test for corners with single texture */ + if (nob.x > limit * (nob.x + nob.y) && nob.x > limit * (nob.x + nob.z)) { + weight.x = 1.0; + } + else if (nob.y > limit * (nob.x + nob.y) && nob.y > limit * (nob.y + nob.z)) { + weight.y = 1.0; + } + else if (nob.z > limit * (nob.x + nob.z) && nob.z > limit * (nob.y + nob.z)) { + weight.z = 1.0; + } + else if (blend > 0.0) { + /* in case of blending, test for mixes between two textures */ + if (nob.z < (1.0 - limit) * (nob.y + nob.x)) { + weight.x = nob.x / (nob.x + nob.y); + weight.x = clamp((weight.x - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0); + weight.y = 1.0 - weight.x; + } + else if (nob.x < (1.0 - limit) * (nob.y + nob.z)) { + weight.y = nob.y / (nob.y + nob.z); + weight.y = clamp((weight.y - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0); + weight.z = 1.0 - weight.y; + } + else if (nob.y < (1.0 - limit) * (nob.x + nob.z)) { + weight.x = nob.x / (nob.x + nob.z); + weight.x = clamp((weight.x - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0); + weight.z = 1.0 - weight.x; + } + else { + /* last case, we have a mix between three */ + weight.x = ((2.0 - limit) * nob.x + (limit - 1.0)) / (2.0 * limit - 1.0); + weight.y = ((2.0 - limit) * nob.y + (limit - 1.0)) / (2.0 * limit - 1.0); + weight.z = ((2.0 - limit) * nob.z + (limit - 1.0)) / (2.0 * limit - 1.0); + } + } + else { + /* Desperate mode, no valid choice anyway, fallback to one side.*/ + weight.x = 1.0; + } + color = vec4(0); + if (weight.x > 0.0) { + color += weight.x * texture2D(ima, texco.yz); + } + if (weight.y > 0.0) { + color += weight.y * texture2D(ima, texco.xz); + } + if (weight.z > 0.0) { + color += weight.z * texture2D(ima, texco.yx); + } + + alpha = color.a; +} + void node_tex_image_empty(vec3 co, out vec4 color, out float alpha) { color = vec4(0.0); @@ -2929,7 +3050,7 @@ void node_tex_magic(vec3 co, float scale, float distortion, float depth, out vec z /= distortion; } - color = vec4(0.5 - x, 0.5 - y, 0.f - z, 1.0); + color = vec4(0.5 - x, 0.5 - y, 0.5 - z, 1.0); fac = (color.x + color.y + color.z) / 3.0; } diff --git a/source/blender/gpu/shaders/gpu_shader_vertex.glsl b/source/blender/gpu/shaders/gpu_shader_vertex.glsl index 5824d5a80db..9a6537b4f09 100644 --- a/source/blender/gpu/shaders/gpu_shader_vertex.glsl +++ b/source/blender/gpu/shaders/gpu_shader_vertex.glsl @@ -14,6 +14,68 @@ varying vec3 varnormal; varying float gl_ClipDistance[6]; #endif +float srgb_to_linearrgb(float c) +{ + if (c < 0.04045) + return (c < 0.0) ? 0.0 : c * (1.0 / 12.92); + else + return pow((c + 0.055) * (1.0 / 1.055), 2.4); +} + +void srgb_to_linearrgb(vec3 col_from, out vec3 col_to) +{ + col_to.r = srgb_to_linearrgb(col_from.r); + col_to.g = srgb_to_linearrgb(col_from.g); + col_to.b = srgb_to_linearrgb(col_from.b); +} + +void srgb_to_linearrgb(vec4 col_from, out vec4 col_to) +{ + col_to.r = srgb_to_linearrgb(col_from.r); + col_to.g = srgb_to_linearrgb(col_from.g); + col_to.b = srgb_to_linearrgb(col_from.b); + col_to.a = col_from.a; +} + +bool is_srgb(int info) +{ +#ifdef USE_NEW_SHADING + return (info == 1)? true: false; +#else + return false; +#endif +} + +void set_var_from_attr(float attr, int info, out float var) +{ + var = attr; +} + +void set_var_from_attr(vec2 attr, int info, out vec2 var) +{ + var = attr; +} + +void set_var_from_attr(vec3 attr, int info, out vec3 var) +{ + if (is_srgb(info)) { + srgb_to_linearrgb(attr, var); + } + else { + var = attr; + } +} + +void set_var_from_attr(vec4 attr, int info, out vec4 var) +{ + if (is_srgb(info)) { + srgb_to_linearrgb(attr, var); + } + else { + var = attr; + } +} + void main() { #ifndef USE_OPENSUBDIV diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h index f4763883489..d89393b9903 100644 --- a/source/blender/imbuf/intern/IMB_anim.h +++ b/source/blender/imbuf/intern/IMB_anim.h @@ -83,18 +83,10 @@ # include <libswscale/swscale.h> #endif -/* actually hard coded endianness */ -#define GET_BIG_LONG(x) (((uchar *) (x))[0] << 24 | ((uchar *) (x))[1] << 16 | ((uchar *) (x))[2] << 8 | ((uchar *) (x))[3]) -#define GET_LITTLE_LONG(x) (((uchar *) (x))[3] << 24 | ((uchar *) (x))[2] << 16 | ((uchar *) (x))[1] << 8 | ((uchar *) (x))[0]) -#define SWAP_L(x) (((x << 24) & 0xff000000) | ((x << 8) & 0xff0000) | ((x >> 8) & 0xff00) | ((x >> 24) & 0xff)) -#define SWAP_S(x) (((x << 8) & 0xff00) | ((x >> 8) & 0xff)) - /* more endianness... should move to a separate file... */ #ifdef __BIG_ENDIAN__ -# define GET_ID GET_BIG_LONG # define LITTLE_LONG SWAP_LONG #else -# define GET_ID GET_LITTLE_LONG # define LITTLE_LONG ENDIAN_NOP #endif diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 0bf3c350263..b0812a81ee1 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -142,7 +142,6 @@ typedef struct ID { */ typedef struct Library { ID id; - ID *idblock; struct FileData *filedata; char name[1024]; /* path name used for reading, can be relative and edited in the outliner */ @@ -155,6 +154,9 @@ typedef struct Library { struct Library *parent; /* set for indirectly linked libs, used in the outliner and while reading */ struct PackedFile *packedfile; + + int temp_index; + int _pad; } Library; enum eIconSizes { diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index fdad6aae094..4c1283452ff 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -484,7 +484,7 @@ typedef struct FCurve { unsigned int totvert; /* total number of points which define the curve (i.e. size of arrays in FPoints) */ /* value cache + settings */ - float curval; /* value stored from last time curve was evaluated */ + float curval; /* value stored from last time curve was evaluated (not threadsafe, debug display only!) */ short flag; /* user-editable settings for this curve */ short extend; /* value-extending mode for this curve (does not cover */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 457db70cd28..a58e995f1c6 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -1518,7 +1518,9 @@ typedef struct NormalEditModifierData { short mix_mode; char pad[2]; float mix_factor; + float mix_limit; float offset[3]; + float pad_f1; } NormalEditModifierData; /* NormalEditModifierData.mode */ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 1bf044ffecb..79af1813a8f 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1639,6 +1639,7 @@ typedef struct Scene { #define R_SIMPLIFY 0x1000000 #define R_EDGE_FRS 0x2000000 /* R_EDGE reserved for Freestyle */ #define R_PERSISTENT_DATA 0x4000000 /* keep data around for re-render */ +#define R_USE_WS_SHADING 0x8000000 /* use world space interpretation of lighting data */ /* seq_flag */ #define R_SEQ_GL_PREV 1 diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 321ff26f68f..5b533d1ec48 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -298,7 +298,7 @@ typedef struct View3D { #define RV3D_VIEW_CAMERA 8 #define RV3D_VIEW_IS_AXIS(view) \ - ((view >= RV3D_VIEW_FRONT) && (view <= RV3D_VIEW_BOTTOM)) + (((view) >= RV3D_VIEW_FRONT) && ((view) <= RV3D_VIEW_BOTTOM)) /* View3d->flag2 (short) */ #define V3D_RENDER_OVERRIDE (1 << 2) diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index d28dd8f7607..cb7a40a9238 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -1009,13 +1009,15 @@ static void rna_def_font(BlenderRNA *UNUSED(brna), StructRNA *srna) prop = RNA_def_property(srna, "offset_x", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "xof"); - RNA_def_property_range(prop, -50.0f, 50.0f); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, -50.0f, 50.0f, 10, 3); RNA_def_property_ui_text(prop, "X Offset", "Horizontal offset from the object origin"); RNA_def_property_update(prop, 0, "rna_Curve_update_data"); prop = RNA_def_property(srna, "offset_y", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "yof"); - RNA_def_property_range(prop, -50.0f, 50.0f); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, -50.0f, 50.0f, 10, 3); RNA_def_property_ui_text(prop, "Y Offset", "Vertical offset from the object origin"); RNA_def_property_update(prop, 0, "rna_Curve_update_data"); @@ -1117,25 +1119,29 @@ static void rna_def_textbox(BlenderRNA *brna) /* number values */ prop = RNA_def_property(srna, "x", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "x"); - RNA_def_property_range(prop, -50.0f, 50.0f); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, -50.0f, 50.0f, 10, 3); RNA_def_property_ui_text(prop, "Textbox X Offset", ""); RNA_def_property_update(prop, 0, "rna_Curve_update_data"); prop = RNA_def_property(srna, "y", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "y"); - RNA_def_property_range(prop, -50.0f, 50.0f); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, -50.0f, 50.0f, 10, 3); RNA_def_property_ui_text(prop, "Textbox Y Offset", ""); RNA_def_property_update(prop, 0, "rna_Curve_update_data"); prop = RNA_def_property(srna, "width", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "w"); - RNA_def_property_range(prop, 0.0f, 50.0f); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 50.0f, 10, 3); RNA_def_property_ui_text(prop, "Textbox Width", ""); RNA_def_property_update(prop, 0, "rna_Curve_update_data"); prop = RNA_def_property(srna, "height", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "h"); - RNA_def_property_range(prop, 0.0f, 50.0f); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 50.0f, 10, 3); RNA_def_property_ui_text(prop, "Textbox Height", ""); RNA_def_property_update(prop, 0, "rna_Curve_update_data"); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 0c4b3ba485d..5a2113f3f95 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -4602,6 +4602,11 @@ static void rna_def_modifier_normaledit(BlenderRNA *brna) "How much of generated normals to mix with exiting ones", 0.0f, 1.0f); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_float(srna, "mix_limit", 1.0f, 0.0f, DEG2RADF(180.0f), "Max Angle", + "Maximum angle between old and new normals", 0.0f, DEG2RADF(180.0f)); + RNA_def_property_subtype(prop, PROP_ANGLE); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "defgrp_name"); RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name for selecting/weighting the affected areas"); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index ec2802dc2ea..281ca91d085 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -2388,6 +2388,8 @@ static void rna_def_object(BlenderRNA *brna) prop = RNA_def_property(srna, "dimensions", PROP_FLOAT, PROP_XYZ_LENGTH); RNA_def_property_array(prop, 3); + /* only for the transform-panel and conflicts with animating scale */ + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_float_funcs(prop, "rna_Object_dimensions_get", "rna_Object_dimensions_set", NULL); RNA_def_property_ui_range(prop, 0.0f, FLT_MAX, 1, 3); RNA_def_property_ui_text(prop, "Dimensions", "Absolute bounding box dimensions of the object"); diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 743532ab6d0..6f74f3deb5a 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -699,7 +699,7 @@ void RNA_api_object(StructRNA *srna) #endif /* NDEBUG */ func = RNA_def_function(srna, "update_from_editmode", "rna_Object_update_from_editmode"); - RNA_def_function_ui_description(func, "Load the objects edit-mode data intp the object data"); + RNA_def_function_ui_description(func, "Load the objects edit-mode data into the object data"); parm = RNA_def_boolean(func, "result", 0, "", "Success"); RNA_def_function_return(func, parm); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 79b8ddec08a..533c0f86460 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -5567,6 +5567,11 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "mode", R_SSS); RNA_def_property_ui_text(prop, "Subsurface Scattering", "Calculate sub-surface scattering in materials rendering"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_glsl_update"); + + prop = RNA_def_property(srna, "use_world_space_shading", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mode", R_USE_WS_SHADING); + RNA_def_property_ui_text(prop, "World Space Shading", "Use world space interpretation of lighting data for node materials"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_glsl_update"); prop = RNA_def_property(srna, "use_raytrace", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "mode", R_RAYTRACE); diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index e1216e3c85f..c3c66625c84 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -151,9 +151,9 @@ static void rna_Scene_ray_cast( bool ret = ED_transform_snap_object_project_ray_ex( sctx, + SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, - .snap_to = SCE_SNAP_MODE_FACE, }, origin, direction, &ray_dist, r_location, r_normal, r_index, diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 68d4f7f7e51..c3a66058888 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -2327,6 +2327,7 @@ static void rna_def_text(StructRNA *srna) prop = RNA_def_property(srna, "font_size", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "text_size"); RNA_def_property_ui_text(prop, "Size", "Size of the text"); + RNA_def_property_range(prop, 0.0, 2000); RNA_def_property_ui_range(prop, 0.0f, 1000, 1, -1); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_update"); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 0fbc1ce1854..5a1607aa7c7 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -408,7 +408,7 @@ static void rna_userdef_addon_remove(ReportList *reports, PointerRNA *path_cmp_p { bAddon *bext = path_cmp_ptr->data; if (BLI_findindex(&U.addons, bext) == -1) { - BKE_report(reports, RPT_ERROR, "Addon is no longer valid"); + BKE_report(reports, RPT_ERROR, "Add-on is no longer valid"); return; } @@ -705,7 +705,7 @@ static StructRNA *rna_AddonPref_register(Main *bmain, ReportList *reports, void BLI_strncpy(dummyapt.idname, dummyaddon.module, sizeof(dummyapt.idname)); if (strlen(identifier) >= sizeof(dummyapt.idname)) { - BKE_reportf(reports, RPT_ERROR, "Registering addon-prefs class: '%s' is too long, maximum length is %d", + BKE_reportf(reports, RPT_ERROR, "Registering add-on preferences class: '%s' is too long, maximum length is %d", identifier, (int)sizeof(dummyapt.idname)); return NULL; } @@ -3195,7 +3195,7 @@ static void rna_def_userdef_addon(BlenderRNA *brna) srna = RNA_def_struct(brna, "Addon", NULL); RNA_def_struct_sdna(srna, "bAddon"); RNA_def_struct_clear_flag(srna, STRUCT_UNDO); - RNA_def_struct_ui_text(srna, "Addon", "Python addons to be loaded automatically"); + RNA_def_struct_ui_text(srna, "Add-on", "Python add-ons to be loaded automatically"); prop = RNA_def_property(srna, "module", PROP_STRING, PROP_NONE); RNA_def_property_ui_text(prop, "Module", "Module name"); @@ -3232,7 +3232,7 @@ static void rna_def_userdef_addon_pref(BlenderRNA *brna) PropertyRNA *prop; srna = RNA_def_struct(brna, "AddonPreferences", NULL); - RNA_def_struct_ui_text(srna, "Addon Preferences", ""); + RNA_def_struct_ui_text(srna, "Add-on Preferences", ""); RNA_def_struct_sdna(srna, "bAddon"); /* WARNING: only a bAddon during registration */ RNA_def_struct_refine_func(srna, "rna_AddonPref_refine"); @@ -4600,7 +4600,7 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "pythondir"); RNA_def_property_ui_text(prop, "Python Scripts Directory", "Alternate script path, matching the default layout with subdirs: " - "startup, addons & modules (requires restart)"); + "startup, add-ons & modules (requires restart)"); /* TODO, editing should reset sys.path! */ prop = RNA_def_property(srna, "i18n_branches_directory", PROP_STRING, PROP_DIRPATH); @@ -4692,7 +4692,7 @@ static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cpro func = RNA_def_function(srna, "remove", "rna_userdef_addon_remove"); RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_REPORTS); RNA_def_function_ui_description(func, "Remove add-on"); - parm = RNA_def_pointer(func, "addon", "Addon", "", "Addon to remove"); + parm = RNA_def_pointer(func, "addon", "Addon", "", "Add-on to remove"); RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR); RNA_def_property_clear_flag(parm, PROP_THICK_WRAP); } @@ -4768,7 +4768,7 @@ void RNA_def_userdef(BlenderRNA *brna) prop = RNA_def_property(srna, "addons", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "addons", NULL); RNA_def_property_struct_type(prop, "Addon"); - RNA_def_property_ui_text(prop, "Addon", ""); + RNA_def_property_ui_text(prop, "Add-on", ""); rna_def_userdef_addon_collection(brna, prop); prop = RNA_def_property(srna, "autoexec_paths", PROP_COLLECTION, PROP_NONE); diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index 4e4855b6deb..b8f06f69260 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -110,7 +110,7 @@ static void generate_vert_coordinates( /* Note this modifies nos_new in-place. */ static void mix_normals( const float mix_factor, MDeformVert *dvert, const int defgrp_index, const bool use_invert_vgroup, - const short mix_mode, + const float mix_limit, const short mix_mode, const int num_verts, MLoop *mloop, float (*nos_old)[3], float (*nos_new)[3], const int num_loops) { /* Mix with org normals... */ @@ -143,7 +143,9 @@ static void mix_normals( case MOD_NORMALEDIT_MIX_COPY: break; } - interp_v3_v3v3_slerp_safe(*no_new, *no_old, *no_new, fac); + + interp_v3_v3v3_slerp_safe(*no_new, *no_old, *no_new, + (mix_limit < M_PI) ? min_ff(fac, mix_limit / angle_v3v3(*no_new, *no_old)) : fac); } MEM_SAFE_FREE(facs); @@ -156,6 +158,7 @@ static bool polygons_check_flip( MPoly *mpoly, float (*polynors)[3], const int num_polys) { MPoly *mp; + MDisps *mdisp = CustomData_get_layer(ldata, CD_MDISPS); int i; bool flipped = false; @@ -174,7 +177,7 @@ static bool polygons_check_flip( /* If average of new loop normals is opposed to polygon normal, flip polygon. */ if (dot_v3v3(polynors[i], norsum) < 0.0f) { - BKE_mesh_polygon_flip(mp, mloop, ldata); + BKE_mesh_polygon_flip_ex(mp, mloop, ldata, nos, mdisp, true); negate_v3(polynors[i]); flipped = true; } @@ -186,7 +189,7 @@ static bool polygons_check_flip( static void normalEditModifier_do_radial( NormalEditModifierData *smd, Object *ob, DerivedMesh *dm, short (*clnors)[2], float (*loopnors)[3], float (*polynors)[3], - const short mix_mode, const float mix_factor, + const short mix_mode, const float mix_factor, const float mix_limit, MDeformVert *dvert, const int defgrp_index, const bool use_invert_vgroup, MVert *mvert, const int num_verts, MEdge *medge, const int num_edges, MLoop *mloop, const int num_loops, MPoly *mpoly, const int num_polys) @@ -265,11 +268,13 @@ static void normalEditModifier_do_radial( if (loopnors) { mix_normals(mix_factor, dvert, defgrp_index, use_invert_vgroup, - mix_mode, num_verts, mloop, loopnors, nos, num_loops); + mix_limit, mix_mode, num_verts, mloop, loopnors, nos, num_loops); } if (polygons_check_flip(mloop, nos, dm->getLoopDataLayout(dm), mpoly, polynors, num_polys)) { dm->dirty |= DM_DIRTY_TESS_CDLAYERS; + /* We need to recompute vertex normals! */ + dm->calcNormals(dm); } BKE_mesh_normals_loop_custom_set(mvert, num_verts, medge, num_edges, mloop, nos, num_loops, @@ -283,7 +288,7 @@ static void normalEditModifier_do_radial( static void normalEditModifier_do_directional( NormalEditModifierData *smd, Object *ob, DerivedMesh *dm, short (*clnors)[2], float (*loopnors)[3], float (*polynors)[3], - const short mix_mode, const float mix_factor, + const short mix_mode, const float mix_factor, const float mix_limit, MDeformVert *dvert, const int defgrp_index, const bool use_invert_vgroup, MVert *mvert, const int num_verts, MEdge *medge, const int num_edges, MLoop *mloop, const int num_loops, MPoly *mpoly, const int num_polys) @@ -342,7 +347,7 @@ static void normalEditModifier_do_directional( if (loopnors) { mix_normals(mix_factor, dvert, defgrp_index, use_invert_vgroup, - mix_mode, num_verts, mloop, loopnors, nos, num_loops); + mix_limit, mix_mode, num_verts, mloop, loopnors, nos, num_loops); } if (polygons_check_flip(mloop, nos, dm->getLoopDataLayout(dm), mpoly, polynors, num_polys)) { @@ -384,7 +389,8 @@ static DerivedMesh *normalEditModifier_do(NormalEditModifierData *smd, Object *o const bool use_invert_vgroup = ((smd->flag & MOD_NORMALEDIT_INVERT_VGROUP) != 0); const bool use_current_clnors = !((smd->mix_mode == MOD_NORMALEDIT_MIX_COPY) && (smd->mix_factor == 1.0f) && - (smd->defgrp_name[0] == '\0')); + (smd->defgrp_name[0] == '\0') && + (smd->mix_limit == M_PI)); int defgrp_index; MDeformVert *dvert; @@ -439,13 +445,13 @@ static DerivedMesh *normalEditModifier_do(NormalEditModifierData *smd, Object *o if (smd->mode == MOD_NORMALEDIT_MODE_RADIAL) { normalEditModifier_do_radial( smd, ob, dm, clnors, loopnors, polynors, - smd->mix_mode, smd->mix_factor, dvert, defgrp_index, use_invert_vgroup, + smd->mix_mode, smd->mix_factor, smd->mix_limit, dvert, defgrp_index, use_invert_vgroup, mvert, num_verts, medge, num_edges, mloop, num_loops, mpoly, num_polys); } else if (smd->mode == MOD_NORMALEDIT_MODE_DIRECTIONAL) { normalEditModifier_do_directional( smd, ob, dm, clnors, loopnors, polynors, - smd->mix_mode, smd->mix_factor, dvert, defgrp_index, use_invert_vgroup, + smd->mix_mode, smd->mix_factor, smd->mix_limit, dvert, defgrp_index, use_invert_vgroup, mvert, num_verts, medge, num_edges, mloop, num_loops, mpoly, num_polys); } @@ -464,6 +470,7 @@ static void initData(ModifierData *md) smd->mix_mode = MOD_NORMALEDIT_MIX_COPY; smd->mix_factor = 1.0f; + smd->mix_limit = M_PI; } static void copyData(ModifierData *md, ModifierData *target) diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 98d4c60b648..f758ba927b5 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -73,7 +73,7 @@ typedef struct ScrewVertIter { #define SV_UNUSED (UINT_MAX) #define SV_INVALID ((UINT_MAX) - 1) -#define SV_IS_VALID(v) (v < SV_INVALID) +#define SV_IS_VALID(v) ((v) < SV_INVALID) static void screwvert_iter_init(ScrewVertIter *iter, ScrewVertConnect *array, unsigned int v_init, unsigned int dir) { diff --git a/source/blender/modifiers/intern/MOD_shapekey.c b/source/blender/modifiers/intern/MOD_shapekey.c index a543aac74b9..97aae733532 100644 --- a/source/blender/modifiers/intern/MOD_shapekey.c +++ b/source/blender/modifiers/intern/MOD_shapekey.c @@ -124,6 +124,7 @@ ModifierTypeInfo modifierType_ShapeKey = { /* structSize */ sizeof(ShapeKeyModifierData), /* type */ eModifierTypeType_OnlyDeform, /* flags */ eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_AcceptsLattice | eModifierTypeFlag_SupportsEditmode, /* copyData */ NULL, diff --git a/source/blender/nodes/shader/nodes/node_shader_geom.c b/source/blender/nodes/shader/nodes/node_shader_geom.c index cd52c4e2547..b289d66efc3 100644 --- a/source/blender/nodes/shader/nodes/node_shader_geom.c +++ b/source/blender/nodes/shader/nodes/node_shader_geom.c @@ -78,6 +78,10 @@ static void node_shader_exec_geom(void *data, int UNUSED(thread), bNode *node, b copy_v3_v3(out[GEOM_OUT_UV]->vec, suv->uv); copy_v3_v3(out[GEOM_OUT_NORMAL]->vec, shi->vno); + if (shi->use_world_space_shading) { + negate_v3(out[GEOM_OUT_NORMAL]->vec); + mul_mat3_m4_v3((float (*)[4])RE_render_current_get_matrix(RE_VIEWINV_MATRIX), out[GEOM_OUT_NORMAL]->vec); + } if (shi->totcol) { /* find vertex color layer by name */ ShadeInputCol *scol = &shi->col[0]; @@ -132,9 +136,14 @@ static int gpu_shader_geom(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED( GPUNodeLink *mtface = GPU_attribute(CD_MTFACE, ngeo->uvname); GPUNodeLink *mcol = GPU_attribute(CD_MCOL, ngeo->colname); - return GPU_stack_link(mat, "geom", in, out, + bool ret = GPU_stack_link(mat, "geom", in, out, GPU_builtin(GPU_VIEW_POSITION), GPU_builtin(GPU_VIEW_NORMAL), GPU_builtin(GPU_INVERSE_VIEW_MATRIX), orco, mtface, mcol); + if (GPU_material_use_world_space_shading(mat)) { + GPU_link(mat, "vec_math_negate", out[5].link, &out[5].link); + ret &= GPU_link(mat, "direction_transform_m4v3", out[5].link, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &out[5].link); + } + return ret; } /* node type definition */ diff --git a/source/blender/nodes/shader/nodes/node_shader_lamp.c b/source/blender/nodes/shader/nodes/node_shader_lamp.c index d5dac3b7ff8..2c96c91958e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_lamp.c +++ b/source/blender/nodes/shader/nodes/node_shader_lamp.c @@ -54,6 +54,8 @@ static void node_shader_exec_lamp(void *data, int UNUSED(thread), bNode *node, b shi->nodes = 1; /* temp hack to prevent trashadow recursion */ out[4]->vec[0] = RE_lamp_get_data(shi, ob, out[0]->vec, out[1]->vec, out[2]->vec, out[3]->vec); shi->nodes = 0; + if (shi->use_world_space_shading) + mul_mat3_m4_v3((float (*)[4])RE_render_current_get_matrix(RE_VIEWINV_MATRIX), out[1]->vec); } } } @@ -66,7 +68,10 @@ static int gpu_shader_lamp(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED( visifac = GPU_lamp_get_data(mat, lamp, &col, &lv, &dist, &shadow, &energy); - return GPU_stack_link(mat, "lamp", in, out, col, energy, lv, dist, shadow, visifac); + bool ret = GPU_stack_link(mat, "lamp", in, out, col, energy, lv, dist, shadow, visifac); + if (GPU_material_use_world_space_shading(mat)) + ret &= GPU_link(mat, "direction_transform_m4v3", out[1].link, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &out[1].link); + return ret; } return false; diff --git a/source/blender/nodes/shader/nodes/node_shader_material.c b/source/blender/nodes/shader/nodes/node_shader_material.c index fa13f6191ad..8b21b1ff33b 100644 --- a/source/blender/nodes/shader/nodes/node_shader_material.c +++ b/source/blender/nodes/shader/nodes/node_shader_material.c @@ -114,6 +114,10 @@ static void node_shader_exec_material(void *data, int UNUSED(thread), bNode *nod /* retrieve normal */ if (hasinput[MAT_IN_NORMAL]) { nodestack_get_vec(shi->vn, SOCK_VECTOR, in[MAT_IN_NORMAL]); + if (shi->use_world_space_shading) { + negate_v3(shi->vn); + mul_mat3_m4_v3((float (*)[4])RE_render_current_get_matrix(RE_VIEW_MATRIX), shi->vn); + } normalize_v3(shi->vn); } else @@ -181,7 +185,11 @@ static void node_shader_exec_material(void *data, int UNUSED(thread), bNode *nod } copy_v3_v3(out[MAT_OUT_NORMAL]->vec, shi->vn); - + + if (shi->use_world_space_shading) { + negate_v3(out[MAT_OUT_NORMAL]->vec); + mul_mat3_m4_v3((float (*)[4])RE_render_current_get_matrix(RE_VIEWINV_MATRIX), out[MAT_OUT_NORMAL]->vec); + } /* Extended material options */ if (node->type == SH_NODE_MATERIAL_EXT) { /* Shadow, Reflect, Refract, Radiosity, Speed seem to cause problems inside @@ -255,6 +263,10 @@ static int gpu_shader_material(GPUMaterial *mat, bNode *node, bNodeExecData *UNU if (hasinput[MAT_IN_NORMAL]) { GPUNodeLink *tmp; shi.vn = gpu_get_input_link(&in[MAT_IN_NORMAL]); + if (GPU_material_use_world_space_shading(mat)) { + GPU_link(mat, "vec_math_negate", shi.vn, &shi.vn); + GPU_link(mat, "direction_transform_m4v3", shi.vn, GPU_builtin(GPU_VIEW_MATRIX), &shi.vn); + } GPU_link(mat, "vec_math_normalize", shi.vn, &shi.vn, &tmp); } @@ -299,6 +311,10 @@ static int gpu_shader_material(GPUMaterial *mat, bNode *node, bNodeExecData *UNU if (node->custom1 & SH_NODE_MAT_NEG) GPU_link(mat, "vec_math_negate", shi.vn, &shi.vn); out[MAT_OUT_NORMAL].link = shi.vn; + if (GPU_material_use_world_space_shading(mat)) { + GPU_link(mat, "vec_math_negate", out[MAT_OUT_NORMAL].link, &out[MAT_OUT_NORMAL].link); + GPU_link(mat, "direction_transform_m4v3", out[MAT_OUT_NORMAL].link, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &out[MAT_OUT_NORMAL].link); + } if (node->type == SH_NODE_MATERIAL_EXT) { out[MAT_OUT_DIFFUSE].link = shr.diff; diff --git a/source/blender/nodes/shader/nodes/node_shader_normal_map.c b/source/blender/nodes/shader/nodes/node_shader_normal_map.c index 85e2c77662d..57014bdc476 100644 --- a/source/blender/nodes/shader/nodes/node_shader_normal_map.c +++ b/source/blender/nodes/shader/nodes/node_shader_normal_map.c @@ -89,20 +89,34 @@ static void node_shader_exec_normal_map(void *data, int UNUSED(thread), bNode *n for (int j = 0; j < 3; j++) out[0]->vec[j] = vecIn[0] * T[j] + vecIn[1] * B[j] + vecIn[2] * N[j]; interp_v3_v3v3(out[0]->vec, N, out[0]->vec, strength); + if (shi->use_world_space_shading) { + mul_mat3_m4_v3((float (*)[4])RE_render_current_get_matrix(RE_VIEWINV_MATRIX), out[0]->vec); + } break; case SHD_NORMAL_MAP_OBJECT: case SHD_NORMAL_MAP_BLENDER_OBJECT: - mul_mat3_m4_v3((float (*)[4])RE_object_instance_get_matrix(shi->obi, RE_OBJECT_INSTANCE_MATRIX_LOCALTOVIEW), vecIn); + if (shi->use_world_space_shading) { + mul_mat3_m4_v3((float (*)[4])RE_object_instance_get_matrix(shi->obi, RE_OBJECT_INSTANCE_MATRIX_OB), vecIn); + mul_mat3_m4_v3((float (*)[4])RE_render_current_get_matrix(RE_VIEWINV_MATRIX), N); + } + else + mul_mat3_m4_v3((float (*)[4])RE_object_instance_get_matrix(shi->obi, RE_OBJECT_INSTANCE_MATRIX_LOCALTOVIEW), vecIn); interp_v3_v3v3(out[0]->vec, N, vecIn, strength); break; case SHD_NORMAL_MAP_WORLD: case SHD_NORMAL_MAP_BLENDER_WORLD: - mul_mat3_m4_v3((float (*)[4])RE_render_current_get_matrix(RE_VIEW_MATRIX), vecIn); + if (shi->use_world_space_shading) + mul_mat3_m4_v3((float (*)[4])RE_render_current_get_matrix(RE_VIEWINV_MATRIX), N); + else + mul_mat3_m4_v3((float (*)[4])RE_render_current_get_matrix(RE_VIEW_MATRIX), vecIn); interp_v3_v3v3(out[0]->vec, N, vecIn, strength); break; } + if (shi->use_world_space_shading) { + negate_v3(out[0]->vec); + } normalize_v3(out[0]->vec); } } @@ -129,10 +143,13 @@ static int gpu_shader_normal_map(GPUMaterial *mat, bNode *node, bNodeExecData *U negnorm = GPU_builtin(GPU_VIEW_NORMAL); GPU_link(mat, "math_max", strength, GPU_uniform(d), &strength); - if (GPU_material_use_new_shading_nodes(mat)) { + if (GPU_material_use_world_space_shading(mat)) { - /* **************** CYCLES ******************** */ + /* ******* CYCLES or BLENDER INTERNAL with world space shading flag ******* */ + const char *color_to_normal_fnc_name = "color_to_normal_new_shading"; + if (nm->space == SHD_NORMAL_MAP_BLENDER_OBJECT || nm->space == SHD_NORMAL_MAP_BLENDER_WORLD || !GPU_material_use_new_shading_nodes(mat)) + color_to_normal_fnc_name = "color_to_blender_normal_new_shading"; switch (nm->space) { case SHD_NORMAL_MAP_TANGENT: GPU_link(mat, "color_to_normal_new_shading", realnorm, &realnorm); @@ -143,28 +160,21 @@ static int gpu_shader_normal_map(GPUMaterial *mat, bNode *node, bNodeExecData *U GPU_link(mat, "vect_normalize", out[0].link, &out[0].link); return true; case SHD_NORMAL_MAP_OBJECT: - GPU_link(mat, "direction_transform_m4v3", negnorm, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &negnorm); - GPU_link(mat, "color_to_normal_new_shading", realnorm, &realnorm); - GPU_link(mat, "direction_transform_m4v3", realnorm, GPU_builtin(GPU_OBJECT_MATRIX), &realnorm); - break; case SHD_NORMAL_MAP_BLENDER_OBJECT: GPU_link(mat, "direction_transform_m4v3", negnorm, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &negnorm); - GPU_link(mat, "color_to_blender_normal_new_shading", realnorm, &realnorm); - GPU_link(mat, "direction_transform_m4v3", realnorm, GPU_builtin(GPU_OBJECT_MATRIX), &realnorm); + GPU_link(mat, color_to_normal_fnc_name, realnorm, &realnorm); + GPU_link(mat, "direction_transform_m4v3", realnorm, GPU_builtin(GPU_OBJECT_MATRIX), &realnorm); break; case SHD_NORMAL_MAP_WORLD: - GPU_link(mat, "direction_transform_m4v3", negnorm, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &negnorm); - GPU_link(mat, "color_to_normal_new_shading", realnorm, &realnorm); - break; case SHD_NORMAL_MAP_BLENDER_WORLD: GPU_link(mat, "direction_transform_m4v3", negnorm, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &negnorm); - GPU_link(mat, "color_to_blender_normal_new_shading", realnorm, &realnorm); + GPU_link(mat, color_to_normal_fnc_name, realnorm, &realnorm); break; } } else { - /* *********** BLENDER INTERNAL *************** */ + /* ************** BLENDER INTERNAL without world space shading flag ******* */ GPU_link(mat, "color_to_normal", realnorm, &realnorm); GPU_link(mat, "mtex_negate_texnormal", realnorm, &realnorm); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.c b/source/blender/nodes/shader/nodes/node_shader_tex_image.c index f0a8cda045e..71200dfe9d3 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.c @@ -59,17 +59,49 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, bNode *node, bNodeExecDat Image *ima = (Image *)node->id; ImageUser *iuser = NULL; NodeTexImage *tex = node->storage; + + GPUNodeLink *norm; + int isdata = tex->color_space == SHD_COLORSPACE_NONE; + float blend = tex->projection_blend; if (!ima) return GPU_stack_link(mat, "node_tex_image_empty", in, out); - + if (!in[0].link) in[0].link = GPU_attribute(CD_MTFACE, ""); node_shader_gpu_tex_mapping(mat, node, in, out); - GPU_stack_link(mat, "node_tex_image", in, out, GPU_image(ima, iuser, isdata)); + switch (tex->projection) { + case SHD_PROJ_FLAT: + GPU_stack_link(mat, "node_tex_image", in, out, GPU_image(ima, iuser, isdata)); + break; + case SHD_PROJ_BOX: + GPU_link(mat, "direction_transform_m4v3", GPU_builtin(GPU_VIEW_NORMAL), + GPU_builtin(GPU_INVERSE_VIEW_MATRIX), + &norm); + GPU_link(mat, "direction_transform_m4v3", norm, + GPU_builtin(GPU_INVERSE_OBJECT_MATRIX), + &norm); + GPU_link(mat, "node_tex_image_box", in[0].link, + norm, + GPU_image(ima, iuser, isdata), + GPU_uniform(&blend), + &out[0].link, + &out[1].link); + break; + case SHD_PROJ_SPHERE: + GPU_link(mat, "point_texco_remap_square", in[0].link, &in[0].link); + GPU_link(mat, "point_map_to_sphere", in[0].link, &in[0].link); + GPU_stack_link(mat, "node_tex_image", in, out, GPU_image(ima, iuser, isdata)); + break; + case SHD_PROJ_TUBE: + GPU_link(mat, "point_texco_remap_square", in[0].link, &in[0].link); + GPU_link(mat, "point_map_to_tube", in[0].link, &in[0].link); + GPU_stack_link(mat, "node_tex_image", in, out, GPU_image(ima, iuser, isdata)); + break; + } ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); if ((tex->color_space == SHD_COLORSPACE_COLOR) && diff --git a/source/blender/python/mathutils/mathutils_Matrix.h b/source/blender/python/mathutils/mathutils_Matrix.h index 9ae5a4bd61d..542a0e349c7 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.h +++ b/source/blender/python/mathutils/mathutils_Matrix.h @@ -41,7 +41,7 @@ extern PyTypeObject matrix_access_Type; # define MATRIX_ITEM_ASSERT(_mat, _row, _col) (void)0 #endif -#define MATRIX_ITEM_INDEX_NUMROW(_totrow, _row, _col) ((_totrow * (_col)) + (_row)) +#define MATRIX_ITEM_INDEX_NUMROW(_totrow, _row, _col) (((_totrow) * (_col)) + (_row)) #define MATRIX_ITEM_INDEX(_mat, _row, _col) (MATRIX_ITEM_ASSERT(_mat, _row, _col),(((_mat)->num_row * (_col)) + (_row))) #define MATRIX_ITEM_PTR( _mat, _row, _col) ((_mat)->matrix + MATRIX_ITEM_INDEX(_mat, _row, _col)) #define MATRIX_ITEM( _mat, _row, _col) ((_mat)->matrix [MATRIX_ITEM_INDEX(_mat, _row, _col)]) diff --git a/source/blender/render/extern/include/RE_shader_ext.h b/source/blender/render/extern/include/RE_shader_ext.h index 8b6dfb88b73..73867de6b2e 100644 --- a/source/blender/render/extern/include/RE_shader_ext.h +++ b/source/blender/render/extern/include/RE_shader_ext.h @@ -172,6 +172,7 @@ typedef struct ShadeInput { /* from initialize, part or renderlayer */ bool do_preview; /* for nodes, in previewrender */ bool do_manage; /* color management flag */ + bool use_world_space_shading; short thread, sample; /* sample: ShadeSample array index */ short nodes; /* indicate node shading, temp hack to prevent recursion */ diff --git a/source/blender/render/intern/include/rayintersection.h b/source/blender/render/intern/include/rayintersection.h index 3607e66a237..1935e4ef59c 100644 --- a/source/blender/render/intern/include/rayintersection.h +++ b/source/blender/render/intern/include/rayintersection.h @@ -38,6 +38,8 @@ extern "C" { #endif +#include "BLI_math_geom.h" + struct RayObject; /* Ray Hints */ @@ -101,6 +103,9 @@ typedef struct Isect { #ifdef RE_RAYCOUNTER RayCounter *raycounter; #endif + + /* Precalculated coefficients for watertight intersection check. */ + struct IsectRayPrecalc isect_precalc; } Isect; /* ray types */ diff --git a/source/blender/render/intern/raytrace/rayobject.cpp b/source/blender/render/intern/raytrace/rayobject.cpp index de6b9139363..f511042749e 100644 --- a/source/blender/render/intern/raytrace/rayobject.cpp +++ b/source/blender/render/intern/raytrace/rayobject.cpp @@ -138,80 +138,29 @@ MALWAYS_INLINE int vlr_check_bake(Isect *is, ObjectInstanceRen *obi, VlakRen *UN /* Ray Triangle/Quad Intersection */ -MALWAYS_INLINE int isec_tri_quad(float start[3], float dir[3], RayFace *face, float uv[2], float *lambda) +MALWAYS_INLINE int isec_tri_quad(float start[3], const struct IsectRayPrecalc *isect_precalc, RayFace *face, float r_uv[2], float *lambda) { - float co1[3], co2[3], co3[3], co4[3]; - float t0[3], t1[3], x[3], r[3], m[3], u, v, divdet, det1, l; - int quad; - - quad = RE_rayface_isQuad(face); - - copy_v3_v3(co1, face->v1); - copy_v3_v3(co2, face->v2); - copy_v3_v3(co3, face->v3); - - copy_v3_v3(r, dir); - - /* intersect triangle */ - sub_v3_v3v3(t0, co3, co2); - sub_v3_v3v3(t1, co3, co1); - - cross_v3_v3v3(x, r, t1); - divdet = dot_v3v3(t0, x); - - sub_v3_v3v3(m, start, co3); - det1 = dot_v3v3(m, x); - - if (divdet != 0.0f) { - divdet = 1.0f / divdet; - v = det1 * divdet; - - if (v < RE_RAYTRACE_EPSILON && v > -(1.0f + RE_RAYTRACE_EPSILON)) { - float cros[3]; - - cross_v3_v3v3(cros, m, t0); - u = divdet * dot_v3v3(cros, r); - - if (u < RE_RAYTRACE_EPSILON && (v + u) > -(1.0f + RE_RAYTRACE_EPSILON)) { - l = divdet * dot_v3v3(cros, t1); - - /* check if intersection is within ray length */ - if (l > -RE_RAYTRACE_EPSILON && l < *lambda) { - uv[0] = u; - uv[1] = v; - *lambda = l; - return 1; - } - } + float uv[2], l; + + if (isect_ray_tri_watertight_v3(start, isect_precalc, face->v1, face->v2, face->v3, &l, uv)) { + /* check if intersection is within ray length */ + if (l > -RE_RAYTRACE_EPSILON && l < *lambda) { + r_uv[0] = uv[0]; + r_uv[1] = uv[1]; + *lambda = l; + return 1; } } /* intersect second triangle in quad */ - if (quad) { - copy_v3_v3(co4, face->v4); - sub_v3_v3v3(t0, co3, co4); - divdet = dot_v3v3(t0, x); - - if (divdet != 0.0f) { - divdet = 1.0f / divdet; - v = det1 * divdet; - - if (v < RE_RAYTRACE_EPSILON && v > -(1.0f + RE_RAYTRACE_EPSILON)) { - float cros[3]; - - cross_v3_v3v3(cros, m, t0); - u = divdet * dot_v3v3(cros, r); - - if (u < RE_RAYTRACE_EPSILON && (v + u) > -(1.0f + RE_RAYTRACE_EPSILON)) { - l = divdet * dot_v3v3(cros, t1); - - if (l > -RE_RAYTRACE_EPSILON && l < *lambda) { - uv[0] = u; - uv[1] = -(1.0f + v + u); - *lambda = l; - return 2; - } - } + if (RE_rayface_isQuad(face)) { + if (isect_ray_tri_watertight_v3(start, isect_precalc, face->v1, face->v3, face->v4, &l, uv)) { + /* check if intersection is within ray length */ + if (l > -RE_RAYTRACE_EPSILON && l < *lambda) { + r_uv[0] = uv[0]; + r_uv[1] = uv[1]; + *lambda = l; + return 2; } } } @@ -223,62 +172,23 @@ MALWAYS_INLINE int isec_tri_quad(float start[3], float dir[3], RayFace *face, fl MALWAYS_INLINE int isec_tri_quad_neighbour(float start[3], float dir[3], RayFace *face) { - float co1[3], co2[3], co3[3], co4[3]; - float t0[3], t1[3], x[3], r[3], m[3], u, v, divdet, det1; - int quad; - - quad = RE_rayface_isQuad(face); + float r[3]; + struct IsectRayPrecalc isect_precalc; + float uv[2], l; - copy_v3_v3(co1, face->v1); - copy_v3_v3(co2, face->v2); - copy_v3_v3(co3, face->v3); negate_v3_v3(r, dir); /* note, different than above function */ - /* intersect triangle */ - sub_v3_v3v3(t0, co3, co2); - sub_v3_v3v3(t1, co3, co1); - - cross_v3_v3v3(x, r, t1); - divdet = dot_v3v3(t0, x); - - sub_v3_v3v3(m, start, co3); - det1 = dot_v3v3(m, x); - - if (divdet != 0.0f) { - divdet = 1.0f / divdet; - v = det1 * divdet; - - if (v < RE_RAYTRACE_EPSILON && v > -(1.0f + RE_RAYTRACE_EPSILON)) { - float cros[3]; - - cross_v3_v3v3(cros, m, t0); - u = divdet * dot_v3v3(cros, r); + isect_ray_tri_watertight_v3_precalc(&isect_precalc, r); - if (u < RE_RAYTRACE_EPSILON && (v + u) > -(1.0f + RE_RAYTRACE_EPSILON)) - return 1; - } + if (isect_ray_tri_watertight_v3(start, &isect_precalc, face->v1, face->v2, face->v3, &l, uv)) { + return 1; } /* intersect second triangle in quad */ - if (quad) { - copy_v3_v3(co4, face->v4); - sub_v3_v3v3(t0, co3, co4); - divdet = dot_v3v3(t0, x); - - if (divdet != 0.0f) { - divdet = 1.0f / divdet; - v = det1 * divdet; - - if (v < RE_RAYTRACE_EPSILON && v > -(1.0f + RE_RAYTRACE_EPSILON)) { - float cros[3]; - - cross_v3_v3v3(cros, m, t0); - u = divdet * dot_v3v3(cros, r); - - if (u < RE_RAYTRACE_EPSILON && (v + u) > -(1.0f + RE_RAYTRACE_EPSILON)) - return 2; - } + if (RE_rayface_isQuad(face)) { + if (isect_ray_tri_watertight_v3(start, &isect_precalc, face->v1, face->v3, face->v4, &l, uv)) { + return 2; } } @@ -317,7 +227,7 @@ MALWAYS_INLINE int intersect_rayface(RayObject *hit_obj, RayFace *face, Isect *i RE_RC_COUNT(is->raycounter->faces.test); dist = is->dist; - ok = isec_tri_quad(is->start, is->dir, face, uv, &dist); + ok = isec_tri_quad(is->start, &is->isect_precalc, face, uv, &dist); if (ok) { @@ -389,6 +299,9 @@ int RE_rayobject_raycast(RayObject *r, Isect *isec) { int i; + /* Pre-calculate orientation for watertight intersection checks. */ + isect_ray_tri_watertight_v3_precalc(&isec->isect_precalc, isec->dir); + RE_RC_COUNT(isec->raycounter->raycast.test); /* setup vars used on raycast */ diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c index 2d26fcf4905..bddd84c45d7 100644 --- a/source/blender/render/intern/source/render_result.c +++ b/source/blender/render/intern/source/render_result.c @@ -359,102 +359,109 @@ static const char *name_from_passtype(int passtype, int channel) return "Unknown"; } -static int passtype_from_name(const char *str) +static int passtype_from_name(const char *str, int passflag) { + /* We do not really support several pass of the same types, so in case we are opening an EXR file with several pass + * names detected as same pass type, only return that pass type the first time, and return 'uknown' for the others. + * See T48466. */ +#define RETURN_PASS(_passtype) return (passflag & (_passtype)) ? 0 : (_passtype) + if (STRPREFIX(str, "Combined")) - return SCE_PASS_COMBINED; + RETURN_PASS(SCE_PASS_COMBINED); if (STRPREFIX(str, "Depth")) - return SCE_PASS_Z; + RETURN_PASS(SCE_PASS_Z); if (STRPREFIX(str, "Vector")) - return SCE_PASS_VECTOR; + RETURN_PASS(SCE_PASS_VECTOR); if (STRPREFIX(str, "Normal")) - return SCE_PASS_NORMAL; + RETURN_PASS(SCE_PASS_NORMAL); if (STRPREFIX(str, "UV")) - return SCE_PASS_UV; + RETURN_PASS(SCE_PASS_UV); if (STRPREFIX(str, "Color")) - return SCE_PASS_RGBA; + RETURN_PASS(SCE_PASS_RGBA); if (STRPREFIX(str, "Emit")) - return SCE_PASS_EMIT; + RETURN_PASS(SCE_PASS_EMIT); if (STRPREFIX(str, "Diffuse")) - return SCE_PASS_DIFFUSE; + RETURN_PASS(SCE_PASS_DIFFUSE); if (STRPREFIX(str, "Spec")) - return SCE_PASS_SPEC; + RETURN_PASS(SCE_PASS_SPEC); if (STRPREFIX(str, "Shadow")) - return SCE_PASS_SHADOW; + RETURN_PASS(SCE_PASS_SHADOW); if (STRPREFIX(str, "AO")) - return SCE_PASS_AO; + RETURN_PASS(SCE_PASS_AO); if (STRPREFIX(str, "Env")) - return SCE_PASS_ENVIRONMENT; + RETURN_PASS(SCE_PASS_ENVIRONMENT); if (STRPREFIX(str, "Indirect")) - return SCE_PASS_INDIRECT; + RETURN_PASS(SCE_PASS_INDIRECT); if (STRPREFIX(str, "Reflect")) - return SCE_PASS_REFLECT; + RETURN_PASS(SCE_PASS_REFLECT); if (STRPREFIX(str, "Refract")) - return SCE_PASS_REFRACT; + RETURN_PASS(SCE_PASS_REFRACT); if (STRPREFIX(str, "IndexOB")) - return SCE_PASS_INDEXOB; + RETURN_PASS(SCE_PASS_INDEXOB); if (STRPREFIX(str, "IndexMA")) - return SCE_PASS_INDEXMA; + RETURN_PASS(SCE_PASS_INDEXMA); if (STRPREFIX(str, "Mist")) - return SCE_PASS_MIST; + RETURN_PASS(SCE_PASS_MIST); if (STRPREFIX(str, "RayHits")) - return SCE_PASS_RAYHITS; + RETURN_PASS(SCE_PASS_RAYHITS); if (STRPREFIX(str, "DiffDir")) - return SCE_PASS_DIFFUSE_DIRECT; + RETURN_PASS(SCE_PASS_DIFFUSE_DIRECT); if (STRPREFIX(str, "DiffInd")) - return SCE_PASS_DIFFUSE_INDIRECT; + RETURN_PASS(SCE_PASS_DIFFUSE_INDIRECT); if (STRPREFIX(str, "DiffCol")) - return SCE_PASS_DIFFUSE_COLOR; + RETURN_PASS(SCE_PASS_DIFFUSE_COLOR); if (STRPREFIX(str, "GlossDir")) - return SCE_PASS_GLOSSY_DIRECT; + RETURN_PASS(SCE_PASS_GLOSSY_DIRECT); if (STRPREFIX(str, "GlossInd")) - return SCE_PASS_GLOSSY_INDIRECT; + RETURN_PASS(SCE_PASS_GLOSSY_INDIRECT); if (STRPREFIX(str, "GlossCol")) - return SCE_PASS_GLOSSY_COLOR; + RETURN_PASS(SCE_PASS_GLOSSY_COLOR); if (STRPREFIX(str, "TransDir")) - return SCE_PASS_TRANSM_DIRECT; + RETURN_PASS(SCE_PASS_TRANSM_DIRECT); if (STRPREFIX(str, "TransInd")) - return SCE_PASS_TRANSM_INDIRECT; + RETURN_PASS(SCE_PASS_TRANSM_INDIRECT); if (STRPREFIX(str, "TransCol")) - return SCE_PASS_TRANSM_COLOR; + RETURN_PASS(SCE_PASS_TRANSM_COLOR); if (STRPREFIX(str, "SubsurfaceDir")) - return SCE_PASS_SUBSURFACE_DIRECT; + RETURN_PASS(SCE_PASS_SUBSURFACE_DIRECT); if (STRPREFIX(str, "SubsurfaceInd")) - return SCE_PASS_SUBSURFACE_INDIRECT; + RETURN_PASS(SCE_PASS_SUBSURFACE_INDIRECT); if (STRPREFIX(str, "SubsurfaceCol")) - return SCE_PASS_SUBSURFACE_COLOR; + RETURN_PASS(SCE_PASS_SUBSURFACE_COLOR); return 0; + +#undef RETURN_PASS } @@ -838,8 +845,9 @@ static void ml_addpass_cb(void *base, void *lay, const char *str, float *rect, i BLI_addtail(&rl->passes, rpass); rpass->channels = totchan; - rpass->passtype = passtype_from_name(str); - if (rpass->passtype == 0) printf("unknown pass %s\n", str); + rpass->passtype = passtype_from_name(str, rl->passflag); + if (rpass->passtype == 0) + printf("unknown pass %s\n", str); rl->passflag |= rpass->passtype; /* channel id chars */ diff --git a/source/blender/render/intern/source/shadeinput.c b/source/blender/render/intern/source/shadeinput.c index 6e01921a6a7..20602314526 100644 --- a/source/blender/render/intern/source/shadeinput.c +++ b/source/blender/render/intern/source/shadeinput.c @@ -1339,6 +1339,7 @@ void shade_input_initialize(ShadeInput *shi, RenderPart *pa, RenderLayer *rl, in shi->do_preview = (R.r.scemode & R_MATNODE_PREVIEW) != 0; shi->do_manage = BKE_scene_check_color_management_enabled(R.scene); + shi->use_world_space_shading = BKE_scene_use_world_space_shading(R.scene); shi->lay = rl->lay; shi->layflag = rl->layflag; diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index db2547e4fbe..388837af67a 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -220,6 +220,7 @@ void WM_event_timer_sleep(struct wmWindowManager *wm, struct wmWindow *wi /* invoke callback, uses enum property named "type" */ void WM_operator_view3d_unit_defaults(struct bContext *C, struct wmOperator *op); int WM_operator_smooth_viewtx_get(const struct wmOperator *op); +int WM_menu_invoke_ex(struct bContext *C, struct wmOperator *op, int opcontext); int WM_menu_invoke (struct bContext *C, struct wmOperator *op, const struct wmEvent *event); int WM_enum_search_invoke(struct bContext *C, struct wmOperator *op, const struct wmEvent *event); /* invoke callback, confirm menu + exec */ diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 8f15d94f538..962ed3c040d 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -437,8 +437,6 @@ void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha) float halfx, halfy, ratiox, ratioy; - glEnable(triple->target); - /* wmOrtho for the screen has this same offset */ ratiox = sizex; ratioy = sizey; @@ -453,6 +451,8 @@ 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); + glBindTexture(triple->target, triple->bind); glColor4f(1.0f, 1.0f, 1.0f, alpha); @@ -471,7 +471,8 @@ void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha) glEnd(); glBindTexture(triple->target, 0); - glDisable(triple->target); + + GPU_basic_shader_bind(GPU_SHADER_USE_COLOR); } static void wm_triple_copy_textures(wmWindow *win, wmDrawTriple *triple) diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 36b819d3495..729af731dfe 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -435,8 +435,7 @@ void wm_file_read_report(bContext *C) } BKE_reportf(reports, RPT_ERROR, - "Engine '%s' not available for scene '%s' " - "(an addon may need to be installed or enabled)", + "Engine '%s' not available for scene '%s' (an add-on may need to be installed or enabled)", sce->r.engine, sce->id.name + 2); } } diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c index 26d1d4c3266..db933ad2d76 100644 --- a/source/blender/windowmanager/intern/wm_gesture.c +++ b/source/blender/windowmanager/intern/wm_gesture.c @@ -281,6 +281,9 @@ static void draw_filled_lasso(wmWindow *win, wmGesture *gt) (const int (*)[2])moves, tot, draw_filled_lasso_px_cb, &lasso_fill_data); + int bound_options; + GPU_BASIC_SHADER_DISABLE_AND_STORE(bound_options); + glEnable(GL_BLEND); // glColor4f(1.0, 1.0, 1.0, 0.05); @@ -288,6 +291,8 @@ static void draw_filled_lasso(wmWindow *win, wmGesture *gt) glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixel_buf); + GPU_BASIC_SHADER_ENABLE_AND_RESTORE(bound_options); + glDisable(GL_BLEND); MEM_freeN(pixel_buf); } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 6ef8965a408..b4295bb2607 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -1068,7 +1068,7 @@ int WM_operator_smooth_viewtx_get(const wmOperator *op) } /* invoke callback, uses enum property named "type" */ -int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +int WM_menu_invoke_ex(bContext *C, wmOperator *op, int opcontext) { PropertyRNA *prop = op->type->prop; uiPopupMenu *pup; @@ -1090,8 +1090,8 @@ int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) pup = UI_popup_menu_begin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE); layout = UI_popup_menu_layout(pup); /* set this so the default execution context is the same as submenus */ - uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_REGION_WIN); - uiItemsFullEnumO(layout, op->type->idname, RNA_property_identifier(prop), op->ptr->data, WM_OP_EXEC_REGION_WIN, 0); + uiLayoutSetOperatorContext(layout, opcontext); + uiItemsFullEnumO(layout, op->type->idname, RNA_property_identifier(prop), op->ptr->data, opcontext, 0); UI_popup_menu_end(C, pup); return OPERATOR_INTERFACE; } @@ -1099,6 +1099,11 @@ int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) return OPERATOR_CANCELLED; } +int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + return WM_menu_invoke_ex(C, op, WM_OP_INVOKE_REGION_WIN); +} + /* generic enum search invoke popup */ static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg_op) diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 449612bc5fd..3085f138846 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -311,14 +311,17 @@ enum { TIMERNOTIFIER = 0x0118, /* timer event, notifier sender */ TIMERF = 0x011F, /* last timer */ - /* Tweak, gestures: 0x500x, 0x501x */ + /* Actionzones, tweak, gestures: 0x500x, 0x501x */ EVT_ACTIONZONE_AREA = 0x5000, EVT_ACTIONZONE_REGION = 0x5001, EVT_ACTIONZONE_FULLSCREEN = 0x5011, /* NOTE: these values are saved in keymap files, do not change them but just add new ones */ - /* tweak events, for L M R mousebuttons */ + /* Tweak events: + * Sent as additional event with the mouse coordinates from where the initial click was placed. */ + + /* tweak events for L M R mousebuttons */ EVT_TWEAK_L = 0x5002, EVT_TWEAK_M = 0x5003, EVT_TWEAK_R = 0x5004, @@ -343,37 +346,38 @@ enum { /* *********** wmEvent.type helpers. ********** */ /* test whether the event is timer event */ -#define ISTIMER(event_type) (event_type >= TIMER && event_type <= TIMERF) +#define ISTIMER(event_type) ((event_type) >= TIMER && (event_type) <= TIMERF) /* for event checks */ /* only used for KM_TEXTINPUT, so assume that we want all user-inputtable ascii codes included */ /* UNUSED - see wm_eventmatch - BUG [#30479] */ -/* #define ISTEXTINPUT(event_type) (event_type >= ' ' && event_type <= 255) */ +/* #define ISTEXTINPUT(event_type) ((event_type) >= ' ' && (event_type) <= 255) */ /* note, an alternative could be to check 'event->utf8_buf' */ /* test whether the event is a key on the keyboard */ #define ISKEYBOARD(event_type) \ - ((event_type >= 0x0020 && event_type <= 0x00ff) || \ - (event_type >= 0x012c && event_type <= 0x013f)) + (((event_type) >= 0x0020 && (event_type) <= 0x00ff) || \ + ((event_type) >= 0x012c && (event_type) <= 0x013f)) /* test whether the event is a modifier key */ -#define ISKEYMODIFIER(event_type) ((event_type >= LEFTCTRLKEY && event_type <= LEFTSHIFTKEY) || event_type == OSKEY) +#define ISKEYMODIFIER(event_type) \ + (((event_type) >= LEFTCTRLKEY && (event_type) <= LEFTSHIFTKEY) || (event_type) == OSKEY) /* test whether the event is a mouse button */ -#define ISMOUSE(event_type) (event_type >= LEFTMOUSE && event_type <= BUTTON7MOUSE) +#define ISMOUSE(event_type) ((event_type) >= LEFTMOUSE && (event_type) <= BUTTON7MOUSE) /* test whether the event is tweak event */ -#define ISTWEAK(event_type) (event_type >= EVT_TWEAK_L && event_type <= EVT_GESTURE) +#define ISTWEAK(event_type) ((event_type) >= EVT_TWEAK_L && (event_type) <= EVT_GESTURE) /* test whether the event is a NDOF event */ -#define ISNDOF(event_type) (event_type >= NDOF_MOTION && event_type < NDOF_LAST) +#define ISNDOF(event_type) ((event_type) >= NDOF_MOTION && (event_type) < NDOF_LAST) /* test whether event type is acceptable as hotkey, excluding modifiers */ #define ISHOTKEY(event_type) \ ((ISKEYBOARD(event_type) || ISMOUSE(event_type) || ISNDOF(event_type)) && \ - (event_type != ESCKEY) && \ - (event_type >= LEFTCTRLKEY && event_type <= LEFTSHIFTKEY) == false && \ - (event_type >= UNKNOWNKEY && event_type <= GRLESSKEY) == false) + ((event_type) != ESCKEY) && \ + ((event_type) >= LEFTCTRLKEY && (event_type) <= LEFTSHIFTKEY) == false && \ + ((event_type) >= UNKNOWNKEY && (event_type) <= GRLESSKEY) == false) /* internal helpers*/ #define _VA_IS_EVENT_MOD2(v, a) (CHECK_TYPE_INLINE(v, wmEvent *), \ diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index 90340096a43..95abfdc4f4c 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -530,6 +530,7 @@ SnapObjectContext *ED_transform_snap_object_context_create_view3d( void ED_transform_snap_object_context_destroy(SnapObjectContext *sctx) RET_NONE bool ED_transform_snap_object_project_ray_ex( struct SnapObjectContext *sctx, + const unsigned short snap_to, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], float *ray_depth, /* return args */ diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 7d0dddded84..5bae9fc6349 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -693,6 +693,10 @@ elseif(WIN32) ) if(WITH_PYTHON_INSTALL_NUMPY) + set(PYTHON_NUMPY_VERSION 1.9) + if(MSVC_VERSION EQUAL 1900) + set(PYTHON_NUMPY_VERSION 1.11) + endif() add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${BLENDER_VERSION}/python/lib/site-packages COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${BLENDER_VERSION}/python/lib/site-packages) @@ -700,9 +704,9 @@ elseif(WIN32) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${BLENDER_VERSION}/python/lib/site-packages/numpy COMMAND ${CMAKE_COMMAND} -E - tar xzvf "${LIBDIR}/release/python${_PYTHON_VERSION_NO_DOTS}_numpy_1.9.tar.gz" + tar xzvf "${LIBDIR}/release/python${_PYTHON_VERSION_NO_DOTS}_numpy_${PYTHON_NUMPY_VERSION}.tar.gz" DEPENDS - ${LIBDIR}/release/python${_PYTHON_VERSION_NO_DOTS}_numpy_1.9.tar.gz + ${LIBDIR}/release/python${_PYTHON_VERSION_NO_DOTS}_numpy_${PYTHON_NUMPY_VERSION}.tar.gz ${CMAKE_CURRENT_BINARY_DIR}/${BLENDER_VERSION}/python/lib/site-packages WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${BLENDER_VERSION}/python/lib/site-packages ) diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index adaade10fb0..969cbdc4b27 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -72,6 +72,7 @@ #include "WM_api.h" +#include "GPU_basic_shader.h" #include "GPU_draw.h" #include "GPU_extensions.h" @@ -583,9 +584,17 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo BLI_argsPrintArgDoc(ba, "--"); + printf("\n"); printf("Other Options:\n"); BLI_argsPrintOtherDoc(ba); + /* keep last args */ + printf("\n"); + printf("Experimental Features:\n"); + BLI_argsPrintArgDoc(ba, "--enable-legacy-depsgraph"); + BLI_argsPrintArgDoc(ba, "--enable-new-basic-shader-glsl"); + + printf("\n"); printf("Argument Parsing:\n"); printf("\tArguments must be separated by white space, eg:\n"); printf("\t# blender -ba test.blend\n"); @@ -619,11 +628,6 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo #endif printf(" $PYTHONHOME Path to the python directory, eg. /usr/lib/python.\n\n"); - /* keep last */ - printf("\n"); - printf("Experimental Features:\n"); - BLI_argsPrintArgDoc(ba, "--enable-legacy-depsgraph"); - exit(0); return 0; @@ -862,7 +866,7 @@ static const char arg_handle_playback_mode_doc[] = "<options> <file(s)>\n" "\tPlayback <file(s)>, only operates this way when not running in background.\n" "\t\t-p <sx> <sy>\tOpen with lower left corner at <sx>, <sy>\n" -"\t\t-m\t\tRead from disk (Don't buffer)\n" +"\t\t-m\t\tRead from disk (Do not buffer)\n" "\t\t-f <fps> <fps-base>\t\tSpecify FPS to start with\n" "\t\t-j <frame>\tSet frame step to <frame>\n" "\t\t-s <frame>\tPlay from <frame>\n" @@ -950,10 +954,10 @@ static int arg_handle_start_with_console(int UNUSED(argc), const char **UNUSED(a } static const char arg_handle_register_extension_doc[] = -"\n\tRegister .blend extension, then exit (Windows only)" +"\n\tRegister blend-file extension, then exit (Windows only)" ; static const char arg_handle_register_extension_doc_silent[] = -"\n\tSilently register .blend extension, then exit (Windows only)" +"\n\tSilently register blend-file extension, then exit (Windows only)" ; static int arg_handle_register_extension(int UNUSED(argc), const char **UNUSED(argv), void *data) { @@ -1170,6 +1174,16 @@ static int arg_handle_depsgraph_use_legacy(int UNUSED(argc), const char **UNUSED return 0; } +static const char arg_handle_basic_shader_glsl_use_new_doc[] = +"\n\tUse new GLSL basic shader" +; +static int arg_handle_basic_shader_glsl_use_new(int UNUSED(argc), const char **UNUSED(argv), void *UNUSED(data)) +{ + printf("Using new GLSL basic shader.\n"); + GPU_basic_shader_use_glsl_set(true); + return 0; +} + static const char arg_handle_verbosity_set_doc[] = "<verbose>\n" "\tSet logging verbosity level." @@ -1805,6 +1819,7 @@ void main_args_setup(bContext *C, bArgs *ba, SYS_SystemHandle *syshandle) CB_EX(arg_handle_debug_mode_generic_set, gpumem), (void *)G_DEBUG_GPU_MEM); BLI_argsAdd(ba, 1, NULL, "--enable-legacy-depsgraph", CB(arg_handle_depsgraph_use_legacy), NULL); + BLI_argsAdd(ba, 1, NULL, "--enable-new-basic-shader-glsl", CB(arg_handle_basic_shader_glsl_use_new), NULL); BLI_argsAdd(ba, 1, NULL, "--verbose", CB(arg_handle_verbosity_set), NULL); diff --git a/tests/gtests/blenlib/BLI_array_store_test.cc b/tests/gtests/blenlib/BLI_array_store_test.cc new file mode 100644 index 00000000000..33fb454ec2f --- /dev/null +++ b/tests/gtests/blenlib/BLI_array_store_test.cc @@ -0,0 +1,815 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +extern "C" { +#include "BLI_array_store.h" + +#include "MEM_guardedalloc.h" +#include "BLI_sys_types.h" +#include "BLI_utildefines.h" +#include "BLI_listbase.h" +#include "BLI_array_utils.h" +#include "BLI_string.h" +#include "BLI_rand.h" +#include "BLI_ressource_strings.h" +} + +/* print memory savings */ +// #define DEBUG_PRINT + + +/* -------------------------------------------------------------------- */ +/* Helper functions */ + +#ifdef DEBUG_PRINT +static void print_mem_saved(const char *id, const BArrayStore *bs) +{ + const double size_real = BLI_array_store_calc_size_compacted_get(bs); + const double size_expand = BLI_array_store_calc_size_expanded_get(bs); + const double percent = size_expand ? ((size_real / size_expand) * 100.0) : -1.0; + printf("%s: %.8f%%\n", id, percent); +} +#endif + + +/* -------------------------------------------------------------------- */ +/* Test Chunks (building data from list of chunks) */ + +typedef struct TestChunnk { + struct TestChunnk *next, *prev; + const void *data; + size_t data_len; +} TestChunnk; + +static TestChunnk *testchunk_list_add(ListBase *lb, const void *data, size_t data_len) +{ + TestChunnk *tc = (TestChunnk *)MEM_mallocN(sizeof(*tc), __func__); + tc->data = data; + tc->data_len = data_len; + BLI_addtail(lb, tc); + + return tc; +} + +#if 0 +static TestChunnk *testchunk_list_add_copydata(ListBase *lb, const void *data, size_t data_len) +{ + void *data_copy = MEM_mallocN(data_len, __func__); + memcpy(data_copy, data, data_len); + return testchunk_list_add(lb, data_copy, data_len); +} +#endif + +static void testchunk_list_free(ListBase *lb) +{ + for (TestChunnk *tc = (TestChunnk *)lb->first, *tb_next; tc; tc = tb_next) { + tb_next = tc->next; + MEM_freeN((void *)tc->data); + MEM_freeN(tc); + } + BLI_listbase_clear(lb); +} + +#if 0 +static char *testchunk_as_data( + ListBase *lb, + size_t *r_data_len) +{ + size_t data_len = 0; + for (TestChunnk *tc = (TestChunnk *)lb->first; tc; tc = tc->next) { + data_len += tc->data_len; + } + char *data = (char *)MEM_mallocN(data_len, __func__); + size_t i = 0; + for (TestChunnk *tc = (TestChunnk *)lb->first; tc; tc = tc->next) { + memcpy(&data[i], tc->data, tc->data_len); + data_len += tc->data_len; + i += tc->data_len; + } + if (r_data_len) { + *r_data_len = i; + } + return data; +} +#endif + +static char *testchunk_as_data_array( + TestChunnk **tc_array, int tc_array_len, + size_t *r_data_len) +{ + size_t data_len = 0; + for (int tc_index = 0; tc_index < tc_array_len; tc_index++) { + data_len += tc_array[tc_index]->data_len; + } + char *data = (char *)MEM_mallocN(data_len, __func__); + size_t i = 0; + for (int tc_index = 0; tc_index < tc_array_len; tc_index++) { + TestChunnk *tc = tc_array[tc_index]; + memcpy(&data[i], tc->data, tc->data_len); + i += tc->data_len; + } + if (r_data_len) { + *r_data_len = i; + } + return data; +} + + +/* -------------------------------------------------------------------- */ +/* Test Buffer */ + +/* API to handle local allocation of data so we can compare it with the data in the array_store */ +typedef struct TestBuffer { + struct TestBuffer *next, *prev; + const void *data; + size_t data_len; + + /* for reference */ + BArrayState *state; +} TestBuffer; + +static TestBuffer *testbuffer_list_add(ListBase *lb, const void *data, size_t data_len) +{ + TestBuffer *tb = (TestBuffer *)MEM_mallocN(sizeof(*tb), __func__); + tb->data = data; + tb->data_len = data_len; + tb->state = NULL; + BLI_addtail(lb, tb); + return tb; +} + +static TestBuffer *testbuffer_list_add_copydata(ListBase *lb, const void *data, size_t data_len) +{ + void *data_copy = MEM_mallocN(data_len, __func__); + memcpy(data_copy, data, data_len); + return testbuffer_list_add(lb, data_copy, data_len); +} + +static void testbuffer_list_state_from_data( + ListBase *lb, + const char *data, const size_t data_len) +{ + testbuffer_list_add_copydata(lb, (const void *)data, data_len); +} + +/** + * A version of testbuffer_list_state_from_data that expand data by stride, + * handy so we can test data at different strides. + */ +static void testbuffer_list_state_from_data__stride_expand( + ListBase *lb, + const char *data, const size_t data_len, + const size_t stride) +{ + if (stride == 1) { + testbuffer_list_state_from_data(lb, data, data_len); + } + else { + const size_t data_stride_len = data_len * stride; + char *data_stride = (char *)MEM_mallocN(data_stride_len, __func__); + + for (size_t i = 0, i_stride = 0; i < data_len; i += 1, i_stride += stride) { + memset(&data_stride[i_stride], data[i], stride); + } + + testbuffer_list_add(lb, (const void *)data_stride, data_stride_len); + } +} + +#define testbuffer_list_state_from_string_array(lb, data_array) \ +{ \ + unsigned int i_ = 0; \ + const char *data; \ + while ((data = data_array[i_++])) { \ + testbuffer_list_state_from_data(lb, data, strlen(data)); \ + } \ +} ((void)0) + +// + +#define TESTBUFFER_STRINGS_CREATE(lb, ...) \ +{ \ + BLI_listbase_clear(lb); \ + const char *data_array[] = {__VA_ARGS__ NULL}; \ + testbuffer_list_state_from_string_array((lb), data_array); \ +} ((void)0) + +/* test in both directions */ +#define TESTBUFFER_STRINGS_EX(bs, ...) \ +{ \ + ListBase lb; \ + TESTBUFFER_STRINGS_CREATE(&lb, __VA_ARGS__); \ + \ + testbuffer_run_tests(bs, &lb); \ + \ + testbuffer_list_free(&lb); \ +} ((void)0) + +#define TESTBUFFER_STRINGS(stride, chunk_count, ...) \ +{ \ + ListBase lb; \ + TESTBUFFER_STRINGS_CREATE(&lb, __VA_ARGS__); \ + \ + testbuffer_run_tests_simple(&lb, stride, chunk_count); \ + \ + testbuffer_list_free(&lb); \ +} ((void)0) + +static bool testbuffer_item_validate(TestBuffer *tb) +{ + size_t data_state_len; + bool ok = true; + void *data_state = BLI_array_store_state_data_get_alloc(tb->state, &data_state_len); + if (tb->data_len != data_state_len) { + ok = false; + } + else if (memcmp(data_state, tb->data, data_state_len) != 0) { + ok = false; + } + MEM_freeN(data_state); + return ok; +} + +static bool testbuffer_list_validate(const ListBase *lb) +{ + for (TestBuffer *tb = (TestBuffer *)lb->first; tb; tb = tb->next) { + if (!testbuffer_item_validate(tb)) { + return false; + } + } + + return true; +} + +static void testbuffer_list_data_randomize(ListBase *lb, unsigned int random_seed) +{ + for (TestBuffer *tb = (TestBuffer *)lb->first; tb; tb = tb->next) { + BLI_array_randomize((void *)tb->data, 1, tb->data_len, random_seed++); + } +} + +static void testbuffer_list_store_populate( + BArrayStore *bs, ListBase *lb) +{ + for (TestBuffer *tb = (TestBuffer *)lb->first, *tb_prev = NULL; tb; tb_prev = tb, tb = tb->next) { + tb->state = BLI_array_store_state_add(bs, tb->data, tb->data_len, (tb_prev ? tb_prev->state : NULL)); + } +} + +static void testbuffer_list_store_clear( + BArrayStore *bs, ListBase *lb) +{ + for (TestBuffer *tb = (TestBuffer *)lb->first; tb; tb = tb->next) { + BLI_array_store_state_remove(bs, tb->state); + tb->state = NULL; + } +} + +static void testbuffer_list_free(ListBase *lb) +{ + for (TestBuffer *tb = (TestBuffer *)lb->first, *tb_next; tb; tb = tb_next) { + tb_next = tb->next; + MEM_freeN((void *)tb->data); + MEM_freeN(tb); + } + BLI_listbase_clear(lb); +} + +static void testbuffer_run_tests_single( + BArrayStore *bs, ListBase *lb) +{ + testbuffer_list_store_populate(bs, lb); + EXPECT_EQ(true, testbuffer_list_validate(lb)); + EXPECT_EQ(true, BLI_array_store_is_valid(bs)); +#ifdef DEBUG_PRINT + print_mem_saved("data", bs); +#endif +} + +/* avoid copy-paste code to run tests */ +static void testbuffer_run_tests( + BArrayStore *bs, ListBase *lb) +{ + /* forwards */ + testbuffer_run_tests_single(bs, lb); + testbuffer_list_store_clear(bs, lb); + + BLI_listbase_reverse(lb); + + /* backwards */ + testbuffer_run_tests_single(bs, lb); + testbuffer_list_store_clear(bs, lb); +} + +static void testbuffer_run_tests_simple( + ListBase *lb, + const int stride, const int chunk_count) +{ + BArrayStore *bs = BLI_array_store_create(stride, chunk_count); + testbuffer_run_tests(bs, lb); + BLI_array_store_destroy(bs); +} + + +/* -------------------------------------------------------------------- */ +/* Basic Tests */ + +TEST(array_store, Nop) +{ + BArrayStore *bs = BLI_array_store_create(1, 32); + BLI_array_store_destroy(bs); +} + +TEST(array_store, NopState) +{ + BArrayStore *bs = BLI_array_store_create(1, 32); + const unsigned char data[] = "test"; + BArrayState *state = BLI_array_store_state_add(bs, data, sizeof(data) - 1, NULL); + EXPECT_EQ(sizeof(data) - 1, BLI_array_store_state_size_get(state)); + BLI_array_store_state_remove(bs, state); + BLI_array_store_destroy(bs); +} + +TEST(array_store, Single) +{ + BArrayStore *bs = BLI_array_store_create(1, 32); + const char data_src[] = "test"; + const char *data_dst; + BArrayState *state = BLI_array_store_state_add(bs, data_src, sizeof(data_src), NULL); + size_t data_dst_len; + data_dst = (char *)BLI_array_store_state_data_get_alloc(state, &data_dst_len); + EXPECT_STREQ(data_src, data_dst); + EXPECT_EQ(sizeof(data_src), data_dst_len); + BLI_array_store_destroy(bs); + MEM_freeN((void *)data_dst); +} + +TEST(array_store, DoubleNop) +{ + BArrayStore *bs = BLI_array_store_create(1, 32); + const char data_src[] = "test"; + const char *data_dst; + + BArrayState *state_a = BLI_array_store_state_add(bs, data_src, sizeof(data_src), NULL); + BArrayState *state_b = BLI_array_store_state_add(bs, data_src, sizeof(data_src), state_a); + + EXPECT_EQ(sizeof(data_src), BLI_array_store_calc_size_compacted_get(bs)); + EXPECT_EQ(sizeof(data_src) * 2, BLI_array_store_calc_size_expanded_get(bs)); + + size_t data_dst_len; + + data_dst = (char *)BLI_array_store_state_data_get_alloc(state_a, &data_dst_len); + EXPECT_STREQ(data_src, data_dst); + MEM_freeN((void *)data_dst); + + data_dst = (char *)BLI_array_store_state_data_get_alloc(state_b, &data_dst_len); + EXPECT_STREQ(data_src, data_dst); + MEM_freeN((void *)data_dst); + + EXPECT_EQ(sizeof(data_src), data_dst_len); + BLI_array_store_destroy(bs); +} + +TEST(array_store, DoubleDiff) +{ + BArrayStore *bs = BLI_array_store_create(1, 32); + const char data_src_a[] = "test"; + const char data_src_b[] = "####"; + const char *data_dst; + + BArrayState *state_a = BLI_array_store_state_add(bs, data_src_a, sizeof(data_src_a), NULL); + BArrayState *state_b = BLI_array_store_state_add(bs, data_src_b, sizeof(data_src_b), state_a); + size_t data_dst_len; + + EXPECT_EQ(sizeof(data_src_a) * 2, BLI_array_store_calc_size_compacted_get(bs)); + EXPECT_EQ(sizeof(data_src_a) * 2, BLI_array_store_calc_size_expanded_get(bs)); + + data_dst = (char *)BLI_array_store_state_data_get_alloc(state_a, &data_dst_len); + EXPECT_STREQ(data_src_a, data_dst); + MEM_freeN((void *)data_dst); + + data_dst = (char *)BLI_array_store_state_data_get_alloc(state_b, &data_dst_len); + EXPECT_STREQ(data_src_b, data_dst); + MEM_freeN((void *)data_dst); + + BLI_array_store_destroy(bs); +} + +TEST(array_store, TextMixed) +{ + TESTBUFFER_STRINGS(1, 4, "",); + TESTBUFFER_STRINGS(1, 4, "test",); + TESTBUFFER_STRINGS(1, 4, "", "test",); + TESTBUFFER_STRINGS(1, 4, "test", "",); + TESTBUFFER_STRINGS(1, 4, "test", "", "test",); + TESTBUFFER_STRINGS(1, 4, "", "test", "",); +} + +TEST(array_store, TextDupeIncreaseDecrease) +{ + ListBase lb; + +#define D "#1#2#3#4" + TESTBUFFER_STRINGS_CREATE( + &lb, + D, + D D, + D D D, + D D D D, + ); + + BArrayStore *bs = BLI_array_store_create(1, 8); + + /* forward */ + testbuffer_list_store_populate(bs, &lb); + EXPECT_EQ(true, testbuffer_list_validate(&lb)); + EXPECT_EQ(true, BLI_array_store_is_valid(bs)); + EXPECT_EQ(strlen(D), BLI_array_store_calc_size_compacted_get(bs)); + + testbuffer_list_store_clear(bs, &lb); + BLI_listbase_reverse(&lb); + + /* backwards */ + testbuffer_list_store_populate(bs, &lb); + EXPECT_EQ(true, testbuffer_list_validate(&lb)); + EXPECT_EQ(true, BLI_array_store_is_valid(bs)); + /* larger since first block doesn't de-duplicate */ + EXPECT_EQ(strlen(D) * 4, BLI_array_store_calc_size_compacted_get(bs)); + +#undef D + testbuffer_list_free(&lb); \ + + BLI_array_store_destroy(bs); +} + + +/* -------------------------------------------------------------------- */ +/* Plain Text Tests */ + +/** + * Test that uses text input with different params for the array-store + * to ensure no corner cases fail. + */ +static void plain_text_helper( + const char *words, int words_len, const char word_delim, + const int stride, const int chunk_count, const int random_seed) +{ + + ListBase lb; + BLI_listbase_clear(&lb); + + for (int i = 0, i_prev = 0; i < words_len; i++) { + if (ELEM(words[i], word_delim, '\0')) { + if (i != i_prev) { + testbuffer_list_state_from_data__stride_expand(&lb, &words[i_prev], i - i_prev, stride); + } + i_prev = i; + } + } + + if (random_seed) { + testbuffer_list_data_randomize(&lb, random_seed); + } + + testbuffer_run_tests_simple(&lb, stride, chunk_count); + + testbuffer_list_free(&lb); +} + +/* split by '.' (multiple words) */ +#define WORDS words10k, sizeof(words10k) +TEST(array_store, TextSentences_Chunk1) { plain_text_helper(WORDS, '.', 1, 1, 0); } +TEST(array_store, TextSentences_Chunk2) { plain_text_helper(WORDS, '.', 1, 2, 0); } +TEST(array_store, TextSentences_Chunk8) { plain_text_helper(WORDS, '.', 1, 8, 0); } +TEST(array_store, TextSentences_Chunk32) { plain_text_helper(WORDS, '.', 1, 32, 0); } +TEST(array_store, TextSentences_Chunk128) { plain_text_helper(WORDS, '.', 1, 128, 0); } +TEST(array_store, TextSentences_Chunk1024) { plain_text_helper(WORDS, '.', 1, 1024, 0); } +/* odd numbers */ +TEST(array_store, TextSentences_Chunk3) { plain_text_helper(WORDS, '.', 1, 3, 0); } +TEST(array_store, TextSentences_Chunk13) { plain_text_helper(WORDS, '.', 1, 13, 0); } +TEST(array_store, TextSentences_Chunk131) { plain_text_helper(WORDS, '.', 1, 131, 0); } + +/* split by ' ', individual words */ +TEST(array_store, TextWords_Chunk1) { plain_text_helper(WORDS, ' ', 1, 1, 0); } +TEST(array_store, TextWords_Chunk2) { plain_text_helper(WORDS, ' ', 1, 2, 0); } +TEST(array_store, TextWords_Chunk8) { plain_text_helper(WORDS, ' ', 1, 8, 0); } +TEST(array_store, TextWords_Chunk32) { plain_text_helper(WORDS, ' ', 1, 32, 0); } +TEST(array_store, TextWords_Chunk128) { plain_text_helper(WORDS, ' ', 1, 128, 0); } +TEST(array_store, TextWords_Chunk1024) { plain_text_helper(WORDS, ' ', 1, 1024, 0); } +/* odd numbers */ +TEST(array_store, TextWords_Chunk3) { plain_text_helper(WORDS, ' ', 1, 3, 0); } +TEST(array_store, TextWords_Chunk13) { plain_text_helper(WORDS, ' ', 1, 13, 0); } +TEST(array_store, TextWords_Chunk131) { plain_text_helper(WORDS, ' ', 1, 131, 0); } + +/* various tests with different strides & randomizing */ +TEST(array_store, TextSentencesRandom_Stride3_Chunk3) { plain_text_helper(WORDS, 'q', 3, 3, 7337); } +TEST(array_store, TextSentencesRandom_Stride8_Chunk8) { plain_text_helper(WORDS, 'n', 8, 8, 5667); } +TEST(array_store, TextSentencesRandom_Stride32_Chunk1) { plain_text_helper(WORDS, 'a', 1, 32, 1212); } +TEST(array_store, TextSentencesRandom_Stride12_Chunk512) { plain_text_helper(WORDS, 'g', 12, 512, 9999); } +TEST(array_store, TextSentencesRandom_Stride128_Chunk6) { plain_text_helper(WORDS, 'b', 20, 6, 1000); } + +#undef WORDS + + +/* -------------------------------------------------------------------- */ +/* Random Data Tests */ + +static unsigned int rand_range_i(RNG *rng, unsigned int min_i, unsigned int max_i, unsigned int step) +{ + if (min_i == max_i) { + return min_i; + } + BLI_assert(min_i <= max_i); + BLI_assert(((min_i % step) == 0) && ((max_i % step) == 0)); + unsigned int range = (max_i - min_i); + unsigned int value = BLI_rng_get_uint(rng) % range; + value = (value / step) * step; + return min_i + value; +} + +static void rand_bytes(RNG *rng, char *data, int data_len) +{ + BLI_assert(data_len != 0); + while (data_len--) { + *data = BLI_rng_get_uint(rng) % 256; + data++; + } +} + +static void testbuffer_list_state_random_data( + ListBase *lb, + const size_t stride, + const size_t data_min_len, const size_t data_max_len, + + const unsigned int mutate, RNG *rng) +{ + size_t data_len = rand_range_i(rng, data_min_len, data_max_len + stride, stride); + char *data = (char *)MEM_mallocN(data_len, __func__); + + if (lb->last == NULL) { + rand_bytes(rng, data, data_len); + } + else { + TestBuffer *tb_last = (TestBuffer *)lb->last; + if (tb_last->data_len >= data_len) { + memcpy(data, tb_last->data, data_len); + } + else { + memcpy(data, tb_last->data, tb_last->data_len); + rand_bytes(rng, &data[tb_last->data_len], data_len - tb_last->data_len); + } + + /* perform multiple small mutations to the array. */ + for (int i = 0; i < mutate; i++) { + enum { + MUTATE_NOP = 0, + MUTATE_ADD, + MUTATE_REMOVE, + MUTATE_ROTATE, + MUTATE_RANDOMIZE, + MUTATE_TOTAL, + }; + + switch ((BLI_rng_get_uint(rng) % MUTATE_TOTAL)) { + case MUTATE_NOP: + { + break; + } + case MUTATE_ADD: + { + const unsigned int offset = rand_range_i(rng, 0, data_len, stride); + if (data_len < data_max_len) { + data_len += stride; + data = (char *)MEM_reallocN((void *)data, data_len); + memmove(&data[offset + stride], &data[offset], data_len - (offset + stride)); + rand_bytes(rng, &data[offset], stride); + } + break; + } + case MUTATE_REMOVE: + { + const unsigned int offset = rand_range_i(rng, 0, data_len, stride); + if (data_len > data_min_len) { + memmove(&data[offset], &data[offset + stride], data_len - (offset + stride)); + data_len -= stride; + } + break; + } + case MUTATE_ROTATE: + { + int items = data_len / stride; + if (items > 1) { + _bli_array_wrap(data, items, stride, (BLI_rng_get_uint(rng) % 2) ? -1 : 1); + } + break; + } + case MUTATE_RANDOMIZE: + { + if (data_len > 0) { + const unsigned int offset = rand_range_i(rng, 0, data_len - stride, stride); + rand_bytes(rng, &data[offset], stride); + } + break; + } + default: + BLI_assert(0); + } + } + } + + testbuffer_list_add(lb, (const void *)data, data_len); +} + +static void random_data_mutate_helper( + const int items_size_min, const int items_size_max, const int items_total, + const int stride, const int chunk_count, + const int random_seed, const int mutate) +{ + + + ListBase lb; + BLI_listbase_clear(&lb); + + const size_t data_min_len = items_size_min * stride; + const size_t data_max_len = items_size_max * stride; + + { + RNG *rng = BLI_rng_new(random_seed); + for (int i = 0; i < items_total; i++) { + testbuffer_list_state_random_data(&lb, stride, data_min_len, data_max_len, mutate, rng); + } + BLI_rng_free(rng); + } + + testbuffer_run_tests_simple(&lb, stride, chunk_count); + + testbuffer_list_free(&lb); +} + +TEST(array_store, TestData_Stride1_Chunk32_Mutate2) { random_data_mutate_helper(0, 100, 400, 1, 32, 9779, 2); } +TEST(array_store, TestData_Stride8_Chunk512_Mutate2) { random_data_mutate_helper(0, 128, 400, 8, 512, 1001, 2); } +TEST(array_store, TestData_Stride12_Chunk48_Mutate2) { random_data_mutate_helper(200, 256, 400, 12, 48, 1331, 2); } +TEST(array_store, TestData_Stride32_Chunk64_Mutate1) { random_data_mutate_helper(0, 256, 200, 32, 64, 3112, 1); } +TEST(array_store, TestData_Stride32_Chunk64_Mutate8) { random_data_mutate_helper(0, 256, 200, 32, 64, 7117, 8); } + + +/* -------------------------------------------------------------------- */ +/* Randomized Chunks Test */ + +static void random_chunk_generate( + ListBase *lb, + const int chunks_per_buffer, + const int stride, const int chunk_count, + const int random_seed) +{ + RNG *rng = BLI_rng_new(random_seed); + const size_t chunk_size_bytes = stride * chunk_count; + for (int i = 0; i < chunks_per_buffer; i++) { + char *data_chunk = (char *)MEM_mallocN(chunk_size_bytes, __func__); + rand_bytes(rng, data_chunk, chunk_size_bytes); + testchunk_list_add(lb, data_chunk, chunk_size_bytes); + } + BLI_rng_free(rng); +} + +/** + * Add random chunks, then re-order them to ensure chunk de-duplication is working. + */ +static void random_chunk_mutate_helper( + const int chunks_per_buffer, const int items_total, + const int stride, const int chunk_count, + const int random_seed) +{ + /* generate random chunks */ + + ListBase random_chunks; + BLI_listbase_clear(&random_chunks); + random_chunk_generate(&random_chunks, chunks_per_buffer, stride, chunk_count, random_seed); + TestChunnk **chunks_array = (TestChunnk **)MEM_mallocN(chunks_per_buffer * sizeof(TestChunnk *), __func__); + { + TestChunnk *tc = (TestChunnk *)random_chunks.first; + for (int i = 0; i < chunks_per_buffer; i++, tc = tc->next) { + chunks_array[i] = tc; + } + } + + /* add and re-order each time */ + ListBase lb; + BLI_listbase_clear(&lb); + + { + RNG *rng = BLI_rng_new(random_seed); + for (int i = 0; i < items_total; i++) { + BLI_rng_shuffle_array(rng, chunks_array, sizeof(TestChunnk *), chunks_per_buffer); + size_t data_len; + char *data = testchunk_as_data_array(chunks_array, chunks_per_buffer, &data_len); + BLI_assert(data_len == chunks_per_buffer * chunk_count * stride); + testbuffer_list_add(&lb, (const void *)data, data_len); + } + BLI_rng_free(rng); + } + + testchunk_list_free(&random_chunks); + MEM_freeN(chunks_array); + + BArrayStore *bs = BLI_array_store_create(stride, chunk_count); + testbuffer_run_tests_single(bs, &lb); + + size_t expected_size = chunks_per_buffer * chunk_count * stride; + EXPECT_EQ(expected_size, BLI_array_store_calc_size_compacted_get(bs)); + + BLI_array_store_destroy(bs); + + testbuffer_list_free(&lb); + +} + +TEST(array_store, TestChunk_Rand8_Stride1_Chunk64) { random_chunk_mutate_helper(8, 100, 1, 64, 9779); } +TEST(array_store, TestChunk_Rand32_Stride1_Chunk64) { random_chunk_mutate_helper(32, 100, 1, 64, 1331); } +TEST(array_store, TestChunk_Rand64_Stride8_Chunk32) { random_chunk_mutate_helper(64, 100, 8, 32, 2772); } +TEST(array_store, TestChunk_Rand31_Stride11_Chunk21) { random_chunk_mutate_helper(31, 100, 11, 21, 7117); } + + +#if 0 +/* -------------------------------------------------------------------- */ + +/* Test From Files (disabled, keep for local tests.) */ + +void *file_read_binary_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size) +{ + FILE *fp = fopen(filepath, "rb"); + void *mem = NULL; + + if (fp) { + long int filelen_read; + fseek(fp, 0L, SEEK_END); + const long int filelen = ftell(fp); + if (filelen == -1) { + goto finally; + } + fseek(fp, 0L, SEEK_SET); + + mem = MEM_mallocN(filelen + pad_bytes, __func__); + if (mem == NULL) { + goto finally; + } + + filelen_read = fread(mem, 1, filelen, fp); + if ((filelen_read != filelen) || ferror(fp)) { + MEM_freeN(mem); + mem = NULL; + goto finally; + } + + *r_size = filelen_read; + +finally: + fclose(fp); + } + + return mem; +} + + +TEST(array_store, PlainTextFiles) +{ ListBase lb; + BLI_listbase_clear(&lb); + BArrayStore *bs = BLI_array_store_create(1, 128); + + for (int i = 0; i < 629; i++) { + char str[512]; + BLI_snprintf(str, sizeof(str), "/src/py_array_cow/test_data/xz_data/%04d.c.xz", i); + // BLI_snprintf(str, sizeof(str), "/src/py_array_cow/test_data/c_code/%04d.c", i); + // printf("%s\n", str); + size_t data_len; + void *data; + data = file_read_binary_as_mem(str, 0, &data_len); + + testbuffer_list_add(&lb, (const void *)data, data_len); + } + + /* forwards */ + testbuffer_list_store_populate(bs, &lb); + EXPECT_EQ(true, testbuffer_list_validate(&lb)); + EXPECT_EQ(true, BLI_array_store_is_valid(bs)); +#ifdef DEBUG_PRINT + print_mem_saved("source code forward", bs); +#endif + + testbuffer_list_store_clear(bs, &lb); + BLI_listbase_reverse(&lb); + + /* backwards */ + testbuffer_list_store_populate(bs, &lb); + EXPECT_EQ(true, testbuffer_list_validate(&lb)); + EXPECT_EQ(true, BLI_array_store_is_valid(bs)); +#ifdef DEBUG_PRINT + print_mem_saved("source code backwards", bs); +#endif + + + testbuffer_list_free(&lb); + BLI_array_store_destroy(bs); +} +#endif diff --git a/tests/gtests/blenlib/BLI_ghash_performance_test.cc b/tests/gtests/blenlib/BLI_ghash_performance_test.cc index 709302db021..fb32cb3f0a5 100644 --- a/tests/gtests/blenlib/BLI_ghash_performance_test.cc +++ b/tests/gtests/blenlib/BLI_ghash_performance_test.cc @@ -21,6 +21,12 @@ extern "C" { /* Resizing the hash has a huge cost over global filling operation! */ //#define GHASH_RESERVE +/* Run the longest tests! */ +//#define GHASH_RUN_BIG + +/* Size of 'small case' ghash (number of entries). */ +#define TESTCASE_SIZE_SMALL 17 + #define PRINTF_GHASH_STATS(_gh) \ { \ double q, lf, var, pempty, poverloaded; \ @@ -32,7 +38,6 @@ extern "C" { BLI_ghash_size(_gh), q, var, lf, pempty * 100.0, poverloaded * 100.0, bigb); \ } void (0) - /* Str: whole text, lines and words from a 'corpus' text. */ static void str_ghash_tests(GHash *ghash, const char *id) @@ -223,12 +228,14 @@ TEST(ghash, IntGHash12000) int_ghash_tests(ghash, "IntGHash - GHash - 12000", 12000); } +#ifdef GHASH_RUN_BIG TEST(ghash, IntGHash100000000) { GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__); int_ghash_tests(ghash, "IntGHash - GHash - 100000000", 100000000); } +#endif TEST(ghash, IntMurmur2a12000) { @@ -237,13 +244,14 @@ TEST(ghash, IntMurmur2a12000) int_ghash_tests(ghash, "IntGHash - Murmur - 12000", 12000); } +#ifdef GHASH_RUN_BIG TEST(ghash, IntMurmur2a100000000) { GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p_murmur, BLI_ghashutil_intcmp, __func__); int_ghash_tests(ghash, "IntGHash - Murmur - 100000000", 100000000); } - +#endif /* Int: random 50M integers. */ @@ -302,12 +310,14 @@ TEST(ghash, IntRandGHash12000) randint_ghash_tests(ghash, "RandIntGHash - GHash - 12000", 12000); } +#ifdef GHASH_RUN_BIG TEST(ghash, IntRandGHash50000000) { GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__); randint_ghash_tests(ghash, "RandIntGHash - GHash - 50000000", 50000000); } +#endif TEST(ghash, IntRandMurmur2a12000) { @@ -316,12 +326,14 @@ TEST(ghash, IntRandMurmur2a12000) randint_ghash_tests(ghash, "RandIntGHash - Murmur - 12000", 12000); } +#ifdef GHASH_RUN_BIG TEST(ghash, IntRandMurmur2a50000000) { GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p_murmur, BLI_ghashutil_intcmp, __func__); randint_ghash_tests(ghash, "RandIntGHash - Murmur - 50000000", 50000000); } +#endif static unsigned int ghashutil_tests_nohash_p(const void *p) { @@ -340,13 +352,14 @@ TEST(ghash, Int4NoHash12000) randint_ghash_tests(ghash, "RandIntGHash - No Hash - 12000", 12000); } +#ifdef GHASH_RUN_BIG TEST(ghash, Int4NoHash50000000) { GHash *ghash = BLI_ghash_new(ghashutil_tests_nohash_p, ghashutil_tests_cmp_p, __func__); randint_ghash_tests(ghash, "RandIntGHash - No Hash - 50000000", 50000000); } - +#endif /* Int_v4: 20M of randomly-generated integer vectors. */ @@ -409,12 +422,14 @@ TEST(ghash, Int4GHash2000) int4_ghash_tests(ghash, "Int4GHash - GHash - 2000", 2000); } +#ifdef GHASH_RUN_BIG TEST(ghash, Int4GHash20000000) { GHash *ghash = BLI_ghash_new(BLI_ghashutil_uinthash_v4_p, BLI_ghashutil_uinthash_v4_cmp, __func__); int4_ghash_tests(ghash, "Int4GHash - GHash - 20000000", 20000000); } +#endif TEST(ghash, Int4Murmur2a2000) { @@ -423,9 +438,99 @@ TEST(ghash, Int4Murmur2a2000) int4_ghash_tests(ghash, "Int4GHash - Murmur - 2000", 2000); } +#ifdef GHASH_RUN_BIG TEST(ghash, Int4Murmur2a20000000) { GHash *ghash = BLI_ghash_new(BLI_ghashutil_uinthash_v4_p_murmur, BLI_ghashutil_uinthash_v4_cmp, __func__); int4_ghash_tests(ghash, "Int4GHash - Murmur - 20000000", 20000000); } +#endif + +/* MultiSmall: create and manipulate a lot of very small ghashes (90% < 10 items, 9% < 100 items, 1% < 1000 items). */ + +static void multi_small_ghash_tests_one(GHash *ghash, RNG *rng, const unsigned int nbr) +{ + unsigned int *data = (unsigned int *)MEM_mallocN(sizeof(*data) * (size_t)nbr, __func__); + unsigned int *dt; + unsigned int i; + + for (i = nbr, dt = data; i--; dt++) { + *dt = BLI_rng_get_uint(rng); + } + +#ifdef GHASH_RESERVE + BLI_ghash_reserve(ghash, nbr); +#endif + + for (i = nbr, dt = data; i--; dt++) { + BLI_ghash_insert(ghash, SET_UINT_IN_POINTER(*dt), SET_UINT_IN_POINTER(*dt)); + } + + for (i = nbr, dt = data; i--; dt++) { + void *v = BLI_ghash_lookup(ghash, SET_UINT_IN_POINTER(*dt)); + EXPECT_EQ(*dt, GET_UINT_FROM_POINTER(v)); + } + + BLI_ghash_clear(ghash, NULL, NULL); +} + +static void multi_small_ghash_tests(GHash *ghash, const char *id, const unsigned int nbr) +{ + printf("\n========== STARTING %s ==========\n", id); + + RNG *rng = BLI_rng_new(0); + + TIMEIT_START(multi_small_ghash); + + unsigned int i = nbr; + while (i--) { + const int nbr = 1 + (BLI_rng_get_int(rng) % TESTCASE_SIZE_SMALL) * (!(i % 100) ? 100 : (!(i % 10) ? 10 : 1)); + multi_small_ghash_tests_one(ghash, rng, nbr); + } + + TIMEIT_END(multi_small_ghash); + + TIMEIT_START(multi_small2_ghash); + + unsigned int i = nbr; + while (i--) { + const int nbr = 1 + (BLI_rng_get_int(rng) % TESTCASE_SIZE_SMALL) / 2 * (!(i % 100) ? 100 : (!(i % 10) ? 10 : 1)); + multi_small_ghash_tests_one(ghash, rng, nbr); + } + + TIMEIT_END(multi_small2_ghash); + + BLI_ghash_free(ghash, NULL, NULL); + BLI_rng_free(rng); + + printf("========== ENDED %s ==========\n\n", id); +} + +TEST(ghash, MultiRandIntGHash2000) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__); + + multi_small_ghash_tests(ghash, "MultiSmall RandIntGHash - GHash - 2000", 2000); +} + +TEST(ghash, MultiRandIntGHash200000) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__); + + multi_small_ghash_tests(ghash, "MultiSmall RandIntGHash - GHash - 200000", 200000); +} + +TEST(ghash, MultiRandIntMurmur2a2000) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p_murmur, BLI_ghashutil_intcmp, __func__); + + multi_small_ghash_tests(ghash, "MultiSmall RandIntGHash - Murmur2a - 2000", 2000); +} + +TEST(ghash, MultiRandIntMurmur2a200000) +{ + GHash *ghash = BLI_ghash_new(BLI_ghashutil_inthash_p_murmur, BLI_ghashutil_intcmp, __func__); + + multi_small_ghash_tests(ghash, "MultiSmall RandIntGHash - Murmur2a - 200000", 200000); +} diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt index 5e6bddcdeab..12112e7a481 100644 --- a/tests/gtests/blenlib/CMakeLists.txt +++ b/tests/gtests/blenlib/CMakeLists.txt @@ -35,6 +35,7 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_LINKFLAGS}") set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${PLATFORM_LINKFLAGS_DEBUG}") +BLENDER_TEST(BLI_array_store "bf_blenlib") BLENDER_TEST(BLI_array_utils "bf_blenlib") BLENDER_TEST(BLI_stack "bf_blenlib") BLENDER_TEST(BLI_math_color "bf_blenlib") |