diff options
author | Bastien Montagne <montagne29@wanadoo.fr> | 2016-11-01 16:50:41 +0300 |
---|---|---|
committer | Bastien Montagne <montagne29@wanadoo.fr> | 2016-11-01 16:50:41 +0300 |
commit | 607b8aee086625f8f45ca4f47939c27f8b81000e (patch) | |
tree | b9483d0f5a7c5828b6ac596e8b45fca7ae135ae3 | |
parent | fc383cd56ef9223ef2087001a810f56c5002886e (diff) | |
parent | 1ee43c5aef03c1a3218163d9450545fdb9ad4482 (diff) |
Merge branch 'master' into multi_previews_id
248 files changed, 6987 insertions, 3849 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index c7addc81b0c..f3c22d9b717 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -343,9 +343,9 @@ option(WITH_OPENCOLLADA "Enable OpenCollada Support (http://www.opencollada.or # Sound output option(WITH_SDL "Enable SDL for sound and joystick support" ${_init_SDL}) option(WITH_OPENAL "Enable OpenAL Support (http://www.openal.org)" ON) -option(WITH_JACK "Enable Jack Support (http://www.jackaudio.org)" ${_init_JACK}) +option(WITH_JACK "Enable JACK Support (http://www.jackaudio.org)" ${_init_JACK}) if(UNIX AND NOT APPLE) - option(WITH_JACK_DYNLOAD "Enable runtime dynamic Jack libraries loading" OFF) + option(WITH_JACK_DYNLOAD "Enable runtime dynamic JACK libraries loading" OFF) endif() if(UNIX AND NOT APPLE) option(WITH_SDL_DYNLOAD "Enable runtime dynamic SDL libraries loading" OFF) diff --git a/build_files/cmake/Modules/FindJack.cmake b/build_files/cmake/Modules/FindJack.cmake index 9a847fabf70..caafa3c34a1 100644 --- a/build_files/cmake/Modules/FindJack.cmake +++ b/build_files/cmake/Modules/FindJack.cmake @@ -1,15 +1,15 @@ -# - Find Jack library -# Find the native Jack includes and library +# - Find JACK library +# Find the native JACK includes and library # This module defines # JACK_INCLUDE_DIRS, where to find jack.h, Set when # JACK_INCLUDE_DIR is found. -# JACK_LIBRARIES, libraries to link against to use Jack. -# JACK_ROOT_DIR, The base directory to search for Jack. +# JACK_LIBRARIES, libraries to link against to use JACK. +# JACK_ROOT_DIR, The base directory to search for JACK. # This can also be an environment variable. -# JACK_FOUND, If false, do not try to use Jack. +# JACK_FOUND, If false, do not try to use JACK. # # also defined, but not for general use are -# JACK_LIBRARY, where to find the Jack library. +# JACK_LIBRARY, where to find the JACK library. #============================================================================= # Copyright 2011 Blender Foundation. diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 5a67ac981a3..2e3a1907063 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -518,7 +518,8 @@ function(setup_liblinks target_link_libraries(${target} ${BLENDER_GL_LIBRARIES}) - target_link_libraries(${target} ${PLATFORM_LINKLIBS} ${CMAKE_DL_LIBS}) + #target_link_libraries(${target} ${PLATFORM_LINKLIBS} ${CMAKE_DL_LIBS}) + target_link_libraries(${target} ${PLATFORM_LINKLIBS}) endfunction() diff --git a/doc/blender_file_format/mystery_of_the_blend.html b/doc/blender_file_format/mystery_of_the_blend.html index b34493ffa3e..599cb4a05bc 100644 --- a/doc/blender_file_format/mystery_of_the_blend.html +++ b/doc/blender_file_format/mystery_of_the_blend.html @@ -187,7 +187,7 @@ The next table describes the information in the file-header. </table> <p> -<a href="http://en.wikipedia.org/wiki/Endianness">Endianness</a> addresses the way values are ordered in a sequence of bytes(see the <a href="#example-endianess">example</a> below): +<a href="https://en.wikipedia.org/wiki/Endianness">Endianness</a> addresses the way values are ordered in a sequence of bytes(see the <a href="#example-endianess">example</a> below): </p> <ul> diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index 9834cda43bc..8b3a97816ba 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -699,7 +699,7 @@ LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. @@ -1145,7 +1145,7 @@ HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. @@ -1752,7 +1752,7 @@ LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. diff --git a/doc/python_api/examples/bpy.app.translations.py b/doc/python_api/examples/bpy.app.translations.py index 41b024a4d2b..e41623d2885 100644 --- a/doc/python_api/examples/bpy.app.translations.py +++ b/doc/python_api/examples/bpy.app.translations.py @@ -24,8 +24,8 @@ Then, call ``bpy.app.translations.register(__name__, your_dict)`` in your ``regi The ``Manage UI translations`` add-on has several functions to help you collect strings to translate, and generate the needed python code (the translation dictionary), as well as optional intermediary po files if you want some... See -`How to Translate Blender <http://wiki.blender.org/index.php/Dev:Doc/Process/Translate_Blender>`_ and -`Using i18n in Blender Code <http://wiki.blender.org/index.php/Dev:Source/Interface/Internationalization>`_ +`How to Translate Blender <https://wiki.blender.org/index.php/Dev:Doc/Process/Translate_Blender>`_ and +`Using i18n in Blender Code <https://wiki.blender.org/index.php/Dev:Source/Interface/Internationalization>`_ for more info. Module References diff --git a/doc/python_api/examples/mathutils.Vector.py b/doc/python_api/examples/mathutils.Vector.py index 3f79fdebff1..a8db4aa6691 100644 --- a/doc/python_api/examples/mathutils.Vector.py +++ b/doc/python_api/examples/mathutils.Vector.py @@ -49,7 +49,7 @@ vec2d[:] = vec3d[:2] # Vectors support 'swizzle' operations -# See http://en.wikipedia.org/wiki/Swizzling_(computer_graphics) +# See https://en.wikipedia.org/wiki/Swizzling_(computer_graphics) vec.xyz = vec.zyx vec.xy = vec4d.zw vec.xyz = vec4d.wzz diff --git a/doc/python_api/rst/bgl.rst b/doc/python_api/rst/bgl.rst index 36e07a17cdc..99f481ce998 100644 --- a/doc/python_api/rst/bgl.rst +++ b/doc/python_api/rst/bgl.rst @@ -12,10 +12,10 @@ contents: dir(bgl). A simple search on the web can point to more than enough material to teach OpenGL programming, from books to many collections of tutorials. -Here is a comprehensive `list of books <http://www.opengl.org/documentation/books/>`__ (non free). -The `arcsynthesis tutorials <http://www.arcsynthesis.org/gltut/>`__ +Here is a comprehensive `list of books <https://www.opengl.org/documentation/books/>`__ (non free). +The `arcsynthesis tutorials <https://web.archive.org/web/20150225192611/http://www.arcsynthesis.org/gltut/index.html>`__ is one of the best resources to learn modern OpenGL and -`g-truc <http://www.g-truc.net/post-tech-content-sample.html>`__ +`g-truc <http://www.g-truc.net/post-opengl-samples.html#menu>`__ offers a set of extensive examples, including advanced features. @@ -30,7 +30,7 @@ offers a set of extensive examples, including advanced features. Operate on the accumulation buffer. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glAccum.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glAccum.xml>`__ :type op: Enumerated constant :arg op: The accumulation buffer operation. @@ -42,7 +42,7 @@ offers a set of extensive examples, including advanced features. Specify the alpha test function. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glAlphaFunc.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glAlphaFunc.xml>`__ :type func: Enumerated constant :arg func: Specifies the alpha comparison function. @@ -55,7 +55,7 @@ offers a set of extensive examples, including advanced features. Determine if textures are loaded in texture memory - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glAreTexturesResident.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glAreTexturesResident.xml>`__ :type n: int :arg n: Specifies the number of textures to be queried. @@ -71,7 +71,7 @@ offers a set of extensive examples, including advanced features. Delimit the vertices of a primitive or a group of like primatives - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glBegin.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glBegin.xml>`__ :type mode: Enumerated constant :arg mode: Specifies the primitive that will be create from vertices between @@ -82,7 +82,7 @@ offers a set of extensive examples, including advanced features. Bind a named texture to a texturing target - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glBindTexture.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glBindTexture.xml>`__ :type target: Enumerated constant :arg target: Specifies the target to which the texture is bound. @@ -94,7 +94,7 @@ offers a set of extensive examples, including advanced features. Draw a bitmap - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glBitmap.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glBitmap.xml>`__ :type width, height: int :arg width, height: Specify the pixel width and height of the bitmap image. @@ -112,7 +112,7 @@ offers a set of extensive examples, including advanced features. Specify pixel arithmetic - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glBlendFunc.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glBlendFunc.xml>`__ :type sfactor: Enumerated constant :arg sfactor: Specifies how the red, green, blue, and alpha source blending factors are @@ -126,7 +126,7 @@ offers a set of extensive examples, including advanced features. Execute a display list - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glCallList.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glCallList.xml>`__ :type list: unsigned int :arg list: Specifies the integer name of the display list to be executed. @@ -136,7 +136,7 @@ offers a set of extensive examples, including advanced features. Execute a list of display lists - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glCallLists.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glCallLists.xml>`__ :type n: int :arg n: Specifies the number of display lists to be executed. @@ -152,7 +152,7 @@ offers a set of extensive examples, including advanced features. Clear buffers to preset values - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glClear.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glClear.xml>`__ :type mask: Enumerated constant(s) :arg mask: Bitwise OR of masks that indicate the buffers to be cleared. @@ -162,7 +162,7 @@ offers a set of extensive examples, including advanced features. Specify clear values for the accumulation buffer - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glClearAccum.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glClearAccum.xml>`__ :type red, green, blue, alpha: float :arg red, green, blue, alpha: Specify the red, green, blue, and alpha values used when the @@ -173,7 +173,7 @@ offers a set of extensive examples, including advanced features. Specify clear values for the color buffers - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glClearColor.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glClearColor.xml>`__ :type red, green, blue, alpha: float :arg red, green, blue, alpha: Specify the red, green, blue, and alpha values used when the @@ -184,7 +184,7 @@ offers a set of extensive examples, including advanced features. Specify the clear value for the depth buffer - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glClearDepth.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glClearDepth.xml>`__ :type depth: int :arg depth: Specifies the depth value used when the depth buffer is cleared. @@ -195,7 +195,7 @@ offers a set of extensive examples, including advanced features. Specify the clear value for the color index buffers - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glClearIndex.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glClearIndex.xml>`__ :type c: float :arg c: Specifies the index used when the color index buffers are cleared. @@ -206,7 +206,7 @@ offers a set of extensive examples, including advanced features. Specify the clear value for the stencil buffer - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glClearStencil.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glClearStencil.xml>`__ :type s: int :arg s: Specifies the index used when the stencil buffer is cleared. The initial value is 0. @@ -216,7 +216,7 @@ offers a set of extensive examples, including advanced features. Specify a plane against which all geometry is clipped - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glClipPlane.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glClipPlane.xml>`__ :type plane: Enumerated constant :arg plane: Specifies which clipping plane is being positioned. @@ -235,7 +235,7 @@ offers a set of extensive examples, including advanced features. Set a new color. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glColor.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glColor.xml>`__ :type red, green, blue, alpha: Depends on function prototype. :arg red, green, blue: Specify new red, green, and blue values for the current color. @@ -247,7 +247,7 @@ offers a set of extensive examples, including advanced features. Enable and disable writing of frame buffer color components - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glColorMask.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glColorMask.xml>`__ :type red, green, blue, alpha: int (boolean) :arg red, green, blue, alpha: Specify whether red, green, blue, and alpha can or cannot be @@ -259,7 +259,7 @@ offers a set of extensive examples, including advanced features. Cause a material color to track the current color - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glColorMaterial.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glColorMaterial.xml>`__ :type face: Enumerated constant :arg face: Specifies whether front, back, or both front and back material parameters should @@ -272,7 +272,7 @@ offers a set of extensive examples, including advanced features. Copy pixels in the frame buffer - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glCopyPixels.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glCopyPixels.xml>`__ :type x, y: int :arg x, y: Specify the window coordinates of the lower left corner of the rectangular @@ -288,7 +288,7 @@ offers a set of extensive examples, including advanced features. Copy pixels into a 2D texture image - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glCopyTexImage2D.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glCopyTexImage2D.xml>`__ :type target: Enumerated constant :arg target: Specifies the target texture. @@ -317,7 +317,7 @@ offers a set of extensive examples, including advanced features. Specify whether front- or back-facing facets can be culled - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glCullFace.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glCullFace.xml>`__ :type mode: Enumerated constant :arg mode: Specifies whether front- or back-facing facets are candidates for culling. @@ -327,7 +327,7 @@ offers a set of extensive examples, including advanced features. Delete a contiguous group of display lists - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glDeleteLists.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glDeleteLists.xml>`__ :type list: unsigned int :arg list: Specifies the integer name of the first display list to delete @@ -339,7 +339,7 @@ offers a set of extensive examples, including advanced features. Delete named textures - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glDeleteTextures.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glDeleteTextures.xml>`__ :type n: int :arg n: Specifies the number of textures to be deleted @@ -351,7 +351,7 @@ offers a set of extensive examples, including advanced features. Specify the value used for depth buffer comparisons - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glDepthFunc.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glDepthFunc.xml>`__ :type func: Enumerated constant :arg func: Specifies the depth comparison function. @@ -361,7 +361,7 @@ offers a set of extensive examples, including advanced features. Enable or disable writing into the depth buffer - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glDepthMask.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glDepthMask.xml>`__ :type flag: int (boolean) :arg flag: Specifies whether the depth buffer is enabled for writing. If flag is GL_FALSE, @@ -373,7 +373,7 @@ offers a set of extensive examples, including advanced features. Specify mapping of depth values from normalized device coordinates to window coordinates - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glDepthRange.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glDepthRange.xml>`__ :type zNear: int :arg zNear: Specifies the mapping of the near clipping plane to window coordinates. @@ -387,7 +387,7 @@ offers a set of extensive examples, including advanced features. Disable server-side GL capabilities - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glEnable.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glEnable.xml>`__ :type cap: Enumerated constant :arg cap: Specifies a symbolic constant indicating a GL capability. @@ -397,7 +397,7 @@ offers a set of extensive examples, including advanced features. Specify which color buffers are to be drawn into - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glDrawBuffer.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glDrawBuffer.xml>`__ :type mode: Enumerated constant :arg mode: Specifies up to four color buffers to be drawn into. @@ -407,7 +407,7 @@ offers a set of extensive examples, including advanced features. Write a block of pixels to the frame buffer - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glDrawPixels.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glDrawPixels.xml>`__ :type width, height: int :arg width, height: Specify the dimensions of the pixel rectangle to be @@ -426,7 +426,7 @@ offers a set of extensive examples, including advanced features. Flag edges as either boundary or non-boundary - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glEdgeFlag.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glEdgeFlag.xml>`__ :type flag: Depends of function prototype :arg flag: Specifies the current edge flag value.The initial value is GL_TRUE. @@ -436,7 +436,7 @@ offers a set of extensive examples, including advanced features. Enable server-side GL capabilities - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glEnable.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glEnable.xml>`__ :type cap: Enumerated constant :arg cap: Specifies a symbolic constant indicating a GL capability. @@ -446,14 +446,14 @@ offers a set of extensive examples, including advanced features. Delimit the vertices of a primitive or group of like primitives - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glBegin.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glBegin.xml>`__ .. function:: glEndList(): Create or replace a display list - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glNewList.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glNewList.xml>`__ .. function:: glEvalCoord (u,v): @@ -463,7 +463,7 @@ offers a set of extensive examples, including advanced features. Evaluate enabled one- and two-dimensional maps - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glEvalCoord.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glEvalCoord.xml>`__ :type u: Depends on function prototype. :arg u: Specifies a value that is the domain coordinate u to the basis function defined @@ -481,7 +481,7 @@ offers a set of extensive examples, including advanced features. Compute a one- or two-dimensional grid of points or lines - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glEvalMesh.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glEvalMesh.xml>`__ :type mode: Enumerated constant :arg mode: In glEvalMesh1, specifies whether to compute a one-dimensional @@ -496,7 +496,7 @@ offers a set of extensive examples, including advanced features. Generate and evaluate a single point in a mesh - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glEvalPoint.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glEvalPoint.xml>`__ :type i: int :arg i: Specifies the integer value for grid domain variable i. @@ -508,7 +508,7 @@ offers a set of extensive examples, including advanced features. Controls feedback mode - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glFeedbackBuffer.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glFeedbackBuffer.xml>`__ :type size: int :arg size: Specifies the maximum number of values that can be written into buffer. @@ -523,14 +523,14 @@ offers a set of extensive examples, including advanced features. Block until all GL execution is complete - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glFinish.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glFinish.xml>`__ .. function:: glFlush(): Force Execution of GL commands in finite time - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glFlush.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glFlush.xml>`__ .. function:: glFog (pname, param): @@ -539,7 +539,7 @@ offers a set of extensive examples, including advanced features. Specify fog parameters - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glFog.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glFog.xml>`__ :type pname: Enumerated constant :arg pname: Specifies a single-valued fog parameter. If the function prototype @@ -554,7 +554,7 @@ offers a set of extensive examples, including advanced features. Define front- and back-facing polygons - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glFrontFace.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glFrontFace.xml>`__ :type mode: Enumerated constant :arg mode: Specifies the orientation of front-facing polygons. @@ -564,7 +564,7 @@ offers a set of extensive examples, including advanced features. Multiply the current matrix by a perspective matrix - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glFrustum.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glFrustum.xml>`__ :type left, right: double (float) :arg left, right: Specify the coordinates for the left and right vertical @@ -581,7 +581,7 @@ offers a set of extensive examples, including advanced features. Generate a contiguous set of empty display lists - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGenLists.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGenLists.xml>`__ :type range: int :arg range: Specifies the number of contiguous empty display lists to be generated. @@ -591,7 +591,7 @@ offers a set of extensive examples, including advanced features. Generate texture names - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGenTextures.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGenTextures.xml>`__ :type n: int :arg n: Specifies the number of textures name to be generated. @@ -605,7 +605,7 @@ offers a set of extensive examples, including advanced features. Return the value or values of a selected parameter - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGet.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGet.xml>`__ :type pname: Enumerated constant :arg pname: Specifies the parameter value to be returned. @@ -617,7 +617,7 @@ offers a set of extensive examples, including advanced features. Return the coefficients of the specified clipping plane - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetClipPlane.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetClipPlane.xml>`__ :type plane: Enumerated constant :arg plane: Specifies a clipping plane. The number of clipping planes depends on the @@ -632,7 +632,7 @@ offers a set of extensive examples, including advanced features. Return error information - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetError.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetError.xml>`__ .. function:: glGetLight (light, pname, params): @@ -641,7 +641,7 @@ offers a set of extensive examples, including advanced features. Return light source parameter values - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetLight.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetLight.xml>`__ :type light: Enumerated constant :arg light: Specifies a light source. The number of possible lights depends on the @@ -659,7 +659,7 @@ offers a set of extensive examples, including advanced features. Return evaluator parameters - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetMap.xml>`_ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetMap.xml>`_ :type target: Enumerated constant :arg target: Specifies the symbolic name of a map. @@ -675,7 +675,7 @@ offers a set of extensive examples, including advanced features. Return material parameters - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetMaterial.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetMaterial.xml>`__ :type face: Enumerated constant :arg face: Specifies which of the two materials is being queried. @@ -692,7 +692,7 @@ offers a set of extensive examples, including advanced features. Return the specified pixel map - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetPixelMap.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetPixelMap.xml>`__ :type map: Enumerated constant :arg map: Specifies the name of the pixel map to return. @@ -704,7 +704,7 @@ offers a set of extensive examples, including advanced features. Return the polygon stipple pattern - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetPolygonStipple.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetPolygonStipple.xml>`__ :type mask: :class:`bgl.Buffer` object I{type GL_BYTE} :arg mask: Returns the stipple pattern. The initial value is all 1's. @@ -714,7 +714,7 @@ offers a set of extensive examples, including advanced features. Return a string describing the current GL connection - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetString.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetString.xml>`__ :type name: Enumerated constant :arg name: Specifies a symbolic constant. @@ -727,7 +727,7 @@ offers a set of extensive examples, including advanced features. Return texture environment parameters - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetTexEnv.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetTexEnv.xml>`__ :type target: Enumerated constant :arg target: Specifies a texture environment. Must be GL_TEXTURE_ENV. @@ -743,7 +743,7 @@ offers a set of extensive examples, including advanced features. Return texture coordinate generation parameters - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetTexGen.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetTexGen.xml>`__ :type coord: Enumerated constant :arg coord: Specifies a texture coordinate. @@ -757,7 +757,7 @@ offers a set of extensive examples, including advanced features. Return a texture image - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetTexImage.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetTexImage.xml>`__ :type target: Enumerated constant :arg target: Specifies which texture is to be obtained. @@ -779,7 +779,7 @@ offers a set of extensive examples, including advanced features. return texture parameter values for a specific level of detail - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetTexLevelParameter.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetTexLevelParameter.xml>`__ :type target: Enumerated constant :arg target: Specifies the symbolic name of the target texture. @@ -798,7 +798,7 @@ offers a set of extensive examples, including advanced features. Return texture parameter values - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetTexParameter.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetTexParameter.xml>`__ :type target: Enumerated constant :arg target: Specifies the symbolic name of the target texture. @@ -812,7 +812,7 @@ offers a set of extensive examples, including advanced features. Specify implementation-specific hints - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glHint.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glHint.xml>`__ :type target: Enumerated constant :arg target: Specifies a symbolic constant indicating the behavior to be @@ -827,7 +827,7 @@ offers a set of extensive examples, including advanced features. Set the current color index - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glIndex.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glIndex.xml>`__ :type c: :class:`bgl.Buffer` object. Depends on function prototype. :arg c: Specifies a pointer to a one element array that contains the new value for @@ -838,7 +838,7 @@ offers a set of extensive examples, including advanced features. Control the writing of individual bits in the color index buffers - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glIndexMask.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glIndexMask.xml>`__ :type mask: int :arg mask: Specifies a bit mask to enable and disable the writing of individual bits @@ -850,14 +850,14 @@ offers a set of extensive examples, including advanced features. Initialize the name stack - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glInitNames.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glInitNames.xml>`__ .. function:: glIsEnabled(cap): Test whether a capability is enabled - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glIsEnabled.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glIsEnabled.xml>`__ :type cap: Enumerated constant :arg cap: Specifies a constant representing a GL capability. @@ -867,7 +867,7 @@ offers a set of extensive examples, including advanced features. Determine if a name corresponds to a display-list - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glIsList.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glIsList.xml>`__ :type list: unsigned int :arg list: Specifies a potential display-list name. @@ -877,7 +877,7 @@ offers a set of extensive examples, including advanced features. Determine if a name corresponds to a texture - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glIsTexture.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glIsTexture.xml>`__ :type texture: unsigned int :arg texture: Specifies a value that may be the name of a texture. @@ -889,7 +889,7 @@ offers a set of extensive examples, including advanced features. Set the light source parameters - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml>`__ :type light: Enumerated constant :arg light: Specifies a light. The number of lights depends on the implementation, @@ -909,7 +909,7 @@ offers a set of extensive examples, including advanced features. Set the lighting model parameters - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glLightModel.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glLightModel.xml>`__ :type pname: Enumerated constant :arg pname: Specifies a single-value light model parameter. @@ -922,7 +922,7 @@ offers a set of extensive examples, including advanced features. Specify the line stipple pattern - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glLineStipple.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glLineStipple.xml>`__ :type factor: int :arg factor: Specifies a multiplier for each bit in the line stipple pattern. @@ -939,7 +939,7 @@ offers a set of extensive examples, including advanced features. Specify the width of rasterized lines. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glLineWidth.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glLineWidth.xml>`__ :type width: float :arg width: Specifies the width of rasterized lines. The initial value is 1. @@ -949,7 +949,7 @@ offers a set of extensive examples, including advanced features. Set the display-list base for glCallLists - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glListBase.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glListBase.xml>`__ :type base: unsigned int :arg base: Specifies an integer offset that will be added to glCallLists @@ -960,7 +960,7 @@ offers a set of extensive examples, including advanced features. Replace the current matrix with the identity matrix - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glLoadIdentity.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glLoadIdentity.xml>`__ .. function:: glLoadMatrix (m): @@ -969,7 +969,7 @@ offers a set of extensive examples, including advanced features. Replace the current matrix with the specified matrix - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glLoadMatrix.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glLoadMatrix.xml>`__ :type m: :class:`bgl.Buffer` object. Depends on function prototype. :arg m: Specifies a pointer to 16 consecutive values, which are used as the elements @@ -980,7 +980,7 @@ offers a set of extensive examples, including advanced features. Load a name onto the name stack. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glLoadName.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glLoadName.xml>`__ :type name: unsigned int :arg name: Specifies a name that will replace the top value on the name stack. @@ -990,7 +990,7 @@ offers a set of extensive examples, including advanced features. Specify a logical pixel operation for color index rendering - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glLogicOp.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glLogicOp.xml>`__ :type opcode: Enumerated constant :arg opcode: Specifies a symbolic constant that selects a logical operation. @@ -1002,7 +1002,7 @@ offers a set of extensive examples, including advanced features. Define a one-dimensional evaluator - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glMap1.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glMap1.xml>`__ :type target: Enumerated constant :arg target: Specifies the kind of values that are generated by the evaluator. @@ -1027,7 +1027,7 @@ offers a set of extensive examples, including advanced features. Define a two-dimensional evaluator - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glMap2.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glMap2.xml>`__ :type target: Enumerated constant :arg target: Specifies the kind of values that are generated by the evaluator. @@ -1068,7 +1068,7 @@ offers a set of extensive examples, including advanced features. Define a one- or two-dimensional mesh - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glMapGrid.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glMapGrid.xml>`__ :type un: int :arg un: Specifies the number of partitions in the grid range interval @@ -1087,7 +1087,7 @@ offers a set of extensive examples, including advanced features. Specify material parameters for the lighting model. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glMaterial.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glMaterial.xml>`__ :type face: Enumerated constant :arg face: Specifies which face or faces are being updated. Must be one of: @@ -1104,7 +1104,7 @@ offers a set of extensive examples, including advanced features. Specify which matrix is the current matrix. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glMatrixMode.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glMatrixMode.xml>`__ :type mode: Enumerated constant :arg mode: Specifies which matrix stack is the target for subsequent matrix operations. @@ -1116,7 +1116,7 @@ offers a set of extensive examples, including advanced features. Multiply the current matrix with the specified matrix - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glMultMatrix.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glMultMatrix.xml>`__ :type m: :class:`bgl.Buffer` object. Depends on function prototype. :arg m: Points to 16 consecutive values that are used as the elements of a 4x4 column @@ -1127,7 +1127,7 @@ offers a set of extensive examples, including advanced features. Create or replace a display list - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glNewList.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glNewList.xml>`__ :type list: unsigned int :arg list: Specifies the display list name @@ -1142,7 +1142,7 @@ offers a set of extensive examples, including advanced features. Set the current normal vector - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glNormal.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glNormal.xml>`__ :type nx, ny, nz: Depends on function prototype. (non - 'v' prototypes only) :arg nx, ny, nz: Specify the x, y, and z coordinates of the new current normal. @@ -1156,7 +1156,7 @@ offers a set of extensive examples, including advanced features. Multiply the current matrix with an orthographic matrix - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glOrtho.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glOrtho.xml>`__ :type left, right: double (float) :arg left, right: Specify the coordinates for the left and @@ -1173,7 +1173,7 @@ offers a set of extensive examples, including advanced features. Place a marker in the feedback buffer - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPassThrough.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPassThrough.xml>`__ :type token: float :arg token: Specifies a marker value to be placed in the feedback @@ -1186,7 +1186,7 @@ offers a set of extensive examples, including advanced features. Set up pixel transfer maps - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPixelMap.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPixelMap.xml>`__ :type map: Enumerated constant :arg map: Specifies a symbolic map name. @@ -1202,7 +1202,7 @@ offers a set of extensive examples, including advanced features. Set pixel storage modes - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPixelStore.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPixelStore.xml>`__ :type pname: Enumerated constant :arg pname: Specifies the symbolic name of the parameter to be set. @@ -1218,7 +1218,7 @@ offers a set of extensive examples, including advanced features. Set pixel transfer modes - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPixelTransfer.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPixelTransfer.xml>`__ :type pname: Enumerated constant :arg pname: Specifies the symbolic name of the pixel transfer parameter to be set. @@ -1230,7 +1230,7 @@ offers a set of extensive examples, including advanced features. Specify the pixel zoom factors - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPixelZoom.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPixelZoom.xml>`__ :type xfactor, yfactor: float :arg xfactor, yfactor: Specify the x and y zoom factors for pixel write operations. @@ -1240,7 +1240,7 @@ offers a set of extensive examples, including advanced features. Specify the diameter of rasterized points - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPointSize.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPointSize.xml>`__ :type size: float :arg size: Specifies the diameter of rasterized points. The initial value is 1. @@ -1250,7 +1250,7 @@ offers a set of extensive examples, including advanced features. Select a polygon rasterization mode - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPolygonMode.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPolygonMode.xml>`__ :type face: Enumerated constant :arg face: Specifies the polygons that mode applies to. @@ -1265,7 +1265,7 @@ offers a set of extensive examples, including advanced features. Set the scale and units used to calculate depth values - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPolygonOffset.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPolygonOffset.xml>`__ :type factor: float :arg factor: Specifies a scale factor that is used to create a variable depth @@ -1279,7 +1279,7 @@ offers a set of extensive examples, including advanced features. Set the polygon stippling pattern - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPolygonStipple.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPolygonStipple.xml>`__ :type mask: :class:`bgl.Buffer` object I{type GL_BYTE} :arg mask: Specifies a pointer to a 32x32 stipple pattern that will be unpacked @@ -1290,35 +1290,35 @@ offers a set of extensive examples, including advanced features. Pop the server attribute stack - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPopAttrib.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPopAttrib.xml>`__ .. function:: glPopClientAttrib(): Pop the client attribute stack - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPopClientAttrib.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPopClientAttrib.xml>`__ .. function:: glPopMatrix(): Pop the current matrix stack - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPopMatrix.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPopMatrix.xml>`__ .. function:: glPopName(): Pop the name stack - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPopName.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPopName.xml>`__ .. function:: glPrioritizeTextures(n, textures, priorities): Set texture residence priority - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPrioritizeTextures.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPrioritizeTextures.xml>`__ :type n: int :arg n: Specifies the number of textures to be prioritized. @@ -1334,7 +1334,7 @@ offers a set of extensive examples, including advanced features. Push the server attribute stack - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPushAttrib.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPushAttrib.xml>`__ :type mask: Enumerated constant(s) :arg mask: Specifies a mask that indicates which attributes to save. @@ -1344,7 +1344,7 @@ offers a set of extensive examples, including advanced features. Push the client attribute stack - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPushClientAttrib.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPushClientAttrib.xml>`__ :type mask: Enumerated constant(s) :arg mask: Specifies a mask that indicates which attributes to save. @@ -1354,14 +1354,14 @@ offers a set of extensive examples, including advanced features. Push the current matrix stack - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPushMatrix.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPushMatrix.xml>`__ .. function:: glPushName(name): Push the name stack - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glPushName.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glPushName.xml>`__ :type name: unsigned int :arg name: Specifies a name that will be pushed onto the name stack. @@ -1377,7 +1377,7 @@ offers a set of extensive examples, including advanced features. Specify the raster position for pixel operations - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glRasterPos.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glRasterPos.xml>`__ :type x, y, z, w: Depends on function prototype. (z and w for '3' and '4' prototypes only) :arg x, y, z, w: Specify the x,y,z, and w object coordinates (if present) for the @@ -1409,7 +1409,7 @@ offers a set of extensive examples, including advanced features. Select a color buffer source for pixels. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glReadBuffer.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glReadBuffer.xml>`__ :type mode: Enumerated constant :arg mode: Specifies a color buffer. @@ -1419,7 +1419,7 @@ offers a set of extensive examples, including advanced features. Read a block of pixels from the frame buffer - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glReadPixels.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glReadPixels.xml>`__ :type x, y: int :arg x, y: Specify the window coordinates of the first pixel that is read @@ -1442,7 +1442,7 @@ offers a set of extensive examples, including advanced features. Draw a rectangle - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glRect.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glRect.xml>`__ :type x1, y1: Depends on function prototype. (for non 'v' prototypes only) :arg x1, y1: Specify one vertex of a rectangle @@ -1457,7 +1457,7 @@ offers a set of extensive examples, including advanced features. Set rasterization mode - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glRenderMode.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glRenderMode.xml>`__ :type mode: Enumerated constant :arg mode: Specifies the rasterization mode. @@ -1469,7 +1469,7 @@ offers a set of extensive examples, including advanced features. Multiply the current matrix by a rotation matrix - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glRotate.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glRotate.xml>`__ :type angle: Depends on function prototype. :arg angle: Specifies the angle of rotation in degrees. @@ -1483,7 +1483,7 @@ offers a set of extensive examples, including advanced features. Multiply the current matrix by a general scaling matrix - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glScale.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glScale.xml>`__ :type x, y, z: Depends on function prototype. :arg x, y, z: Specify scale factors along the x, y, and z axes, respectively. @@ -1493,7 +1493,7 @@ offers a set of extensive examples, including advanced features. Define the scissor box - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glScissor.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glScissor.xml>`__ :type x, y: int :arg x, y: Specify the lower left corner of the scissor box. Initially (0, 0). @@ -1507,7 +1507,7 @@ offers a set of extensive examples, including advanced features. Establish a buffer for selection mode values - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glSelectBuffer.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glSelectBuffer.xml>`__ :type size: int :arg size: Specifies the size of buffer @@ -1519,7 +1519,7 @@ offers a set of extensive examples, including advanced features. Select flat or smooth shading - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glShadeModel.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glShadeModel.xml>`__ :type mode: Enumerated constant :arg mode: Specifies a symbolic value representing a shading technique. @@ -1529,7 +1529,7 @@ offers a set of extensive examples, including advanced features. Set function and reference value for stencil testing - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glStencilFuc.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glStencilFunc.xml>`__ :type func: Enumerated constant :arg func: Specifies the test function. @@ -1546,7 +1546,7 @@ offers a set of extensive examples, including advanced features. Control the writing of individual bits in the stencil planes - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glStencilMask.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glStencilMask.xml>`__ :type mask: unsigned int :arg mask: Specifies a bit mask to enable and disable writing of individual bits @@ -1557,7 +1557,7 @@ offers a set of extensive examples, including advanced features. Set stencil test actions - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glStencilOp.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glStencilOp.xml>`__ :type fail: Enumerated constant :arg fail: Specifies the action to take when the stencil test fails. @@ -1585,7 +1585,7 @@ offers a set of extensive examples, including advanced features. Set the current texture coordinates - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glTexCoord.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glTexCoord.xml>`__ :type s, t, r, q: Depends on function prototype. (r and q for '3' and '4' prototypes only) :arg s, t, r, q: Specify s, t, r, and q texture coordinates. Not all parameters are @@ -1601,7 +1601,7 @@ offers a set of extensive examples, including advanced features. Set texture environment parameters - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml>`__ :type target: Enumerated constant :arg target: Specifies a texture environment. Must be GL_TEXTURE_ENV. @@ -1620,7 +1620,7 @@ offers a set of extensive examples, including advanced features. Control the generation of texture coordinates - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glTexGen.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glTexGen.xml>`__ :type coord: Enumerated constant :arg coord: Specifies a texture coordinate. @@ -1638,7 +1638,7 @@ offers a set of extensive examples, including advanced features. Specify a one-dimensional texture image - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glTexImage1D.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glTexImage1D.xml>`__ :type target: Enumerated constant :arg target: Specifies the target texture. @@ -1665,7 +1665,7 @@ offers a set of extensive examples, including advanced features. Specify a two-dimensional texture image - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glTexImage2D.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glTexImage2D.xml>`__ :type target: Enumerated constant :arg target: Specifies the target texture. @@ -1698,7 +1698,7 @@ offers a set of extensive examples, including advanced features. Set texture parameters - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glTexParameter.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glTexParameter.xml>`__ :type target: Enumerated constant :arg target: Specifies the target texture. @@ -1715,7 +1715,7 @@ offers a set of extensive examples, including advanced features. Multiply the current matrix by a translation matrix - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glTranslate.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glTranslate.xml>`__ :type x, y, z: Depends on function prototype. :arg x, y, z: Specify the x, y, and z coordinates of a translation vector. @@ -1730,7 +1730,7 @@ offers a set of extensive examples, including advanced features. Specify a vertex - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glVertex.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glVertex.xml>`__ :type x, y, z, w: Depends on function prototype (z and w for '3' and '4' prototypes only) :arg x, y, z, w: Specify x, y, z, and w coordinates of a vertex. Not all parameters @@ -1746,7 +1746,7 @@ offers a set of extensive examples, including advanced features. Set the viewport - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glViewport.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glViewport.xml>`__ :type x, y: int :arg x, y: Specify the lower left corner of the viewport rectangle, @@ -1761,7 +1761,7 @@ offers a set of extensive examples, including advanced features. Set up a perspective projection matrix. - .. seealso:: U{http://biology.ncsa.uiuc.edu/cgi-bin/infosrch.cgi?cmd=getdoc&coll=0650&db=bks&fname=/SGI_Developer/OpenGL_RM/ch06.html#id5577288} + .. seealso:: https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml :type fovY: double :arg fovY: Specifies the field of view angle, in degrees, in the y direction. @@ -1778,7 +1778,7 @@ offers a set of extensive examples, including advanced features. Define a viewing transformation. - .. seealso:: U{http://biology.ncsa.uiuc.edu/cgi-bin/infosrch.cgi?cmd=getdoc&coll=0650&db=bks&fname=/SGI_Developer/OpenGL_RM/ch06.html#id5573042} + .. seealso:: https://www.opengl.org/sdk/docs/man2/xhtml/gluLookAt.xml :type eyex, eyey, eyez: double :arg eyex, eyey, eyez: Specifies the position of the eye point. @@ -1792,7 +1792,7 @@ offers a set of extensive examples, including advanced features. Define a 2-D orthographic projection matrix. - .. seealso:: U{http://biology.ncsa.uiuc.edu/cgi-bin/infosrch.cgi?cmd=getdoc&coll=0650&db=bks&fname=/SGI_Developer/OpenGL_RM/ch06.html#id5578074} + .. seealso:: https://www.opengl.org/sdk/docs/man2/xhtml/gluOrtho2D.xml :type left, right: double :arg left, right: Specify the coordinates for the left and right vertical clipping planes. @@ -1804,7 +1804,7 @@ offers a set of extensive examples, including advanced features. Define a picking region. - .. seealso:: U{http://biology.ncsa.uiuc.edu/cgi-bin/infosrch.cgi?cmd=getdoc&coll=0650&db=bks&fname=/SGI_Developer/OpenGL_RM/ch06.html#id5578074} + .. seealso:: https://www.opengl.org/sdk/docs/man2/xhtml/gluPickMatrix.xml :type x, y: double :arg x, y: Specify the center of a picking region in window coordinates. @@ -1818,7 +1818,7 @@ offers a set of extensive examples, including advanced features. Map object coordinates to window coordinates. - .. seealso:: U{http://biology.ncsa.uiuc.edu/cgi-bin/infosrch.cgi?cmd=getdoc&coll=0650&db=bks&fname=/SGI_Developer/OpenGL_RM/ch06.html#id5578074} + .. seealso:: https://www.opengl.org/sdk/docs/man2/xhtml/gluProject.xml :type objx, objy, objz: double :arg objx, objy, objz: Specify the object coordinates. @@ -1836,7 +1836,7 @@ offers a set of extensive examples, including advanced features. Map object coordinates to window coordinates. - .. seealso:: U{http://biology.ncsa.uiuc.edu/cgi-bin/infosrch.cgi?cmd=getdoc&coll=0650&db=bks&fname=/SGI_Developer/OpenGL_RM/ch06.html#id5582204} + .. seealso:: https://www.opengl.org/sdk/docs/man2/xhtml/gluUnProject.xml :type winx, winy, winz: double :arg winx, winy, winz: Specify the window coordinates to be mapped. @@ -1854,7 +1854,7 @@ offers a set of extensive examples, including advanced features. Installs a program object as part of current rendering state - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glUseProgram.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glUseProgram.xml>`__ :type program: int :arg program: Specifies the handle of the program object whose executables are to be used as part of current rendering state. @@ -1864,7 +1864,7 @@ offers a set of extensive examples, including advanced features. Validates a program object - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glValidateProgram.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glValidateProgram.xml>`__ :type program: int :arg program: Specifies the handle of the program object to be validated. @@ -1874,7 +1874,7 @@ offers a set of extensive examples, including advanced features. Links a program object. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glLinkProgram.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glLinkProgram.xml>`__ :type program: int :arg program: Specifies the handle of the program object to be linked. @@ -1884,7 +1884,7 @@ offers a set of extensive examples, including advanced features. Select active texture unit. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glActiveTexture.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glActiveTexture.xml>`__ :type texture: int :arg texture: Constant in ``GL_TEXTURE0`` 0 - 8 @@ -1894,7 +1894,7 @@ offers a set of extensive examples, including advanced features. Attaches a shader object to a program object. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glAttachShader.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glAttachShader.xml>`__ :type program: int :arg program: Specifies the program object to which a shader object will be attached. @@ -1906,7 +1906,7 @@ offers a set of extensive examples, including advanced features. Compiles a shader object. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glCompileShader.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glCompileShader.xml>`__ :type shader: int :arg shader: Specifies the shader object to be compiled. @@ -1916,7 +1916,7 @@ offers a set of extensive examples, including advanced features. Creates a program object - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glCreateProgram.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glCreateProgram.xml>`__ :rtype: int :return: The new program or zero if an error occurs. @@ -1926,7 +1926,7 @@ offers a set of extensive examples, including advanced features. Creates a shader object. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glCreateShader.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glCreateShader.xml>`__ :type shaderType: Specifies the type of shader to be created. Must be one of ``GL_VERTEX_SHADER``, @@ -1943,7 +1943,7 @@ offers a set of extensive examples, including advanced features. Deletes a program object. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glDeleteProgram.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glDeleteProgram.xml>`__ :type program: int :arg program: Specifies the program object to be deleted. @@ -1953,7 +1953,7 @@ offers a set of extensive examples, including advanced features. Deletes a shader object. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glDeleteShader.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glDeleteShader.xml>`__ :type shader: int :arg shader: Specifies the shader object to be deleted. @@ -1963,7 +1963,7 @@ offers a set of extensive examples, including advanced features. Detaches a shader object from a program object to which it is attached. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glDetachShader.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glDetachShader.xml>`__ :type program: int :arg program: Specifies the program object from which to detach the shader object. @@ -1975,7 +1975,7 @@ offers a set of extensive examples, including advanced features. Returns the handles of the shader objects attached to a program object. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetAttachedShaders.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetAttachedShaders.xml>`__ :type program: int :arg program: Specifies the program object to be queried. @@ -1991,7 +1991,7 @@ offers a set of extensive examples, including advanced features. Returns the information log for a program object. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetProgramInfoLog.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetProgramInfoLog.xml>`__ :type program: int :arg program: Specifies the program object whose information log is to be queried. @@ -2007,7 +2007,7 @@ offers a set of extensive examples, including advanced features. Returns the information log for a shader object. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetShaderInfoLog.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetShaderInfoLog.xml>`__ :type shader: int :arg shader: Specifies the shader object whose information log is to be queried. @@ -2023,7 +2023,7 @@ offers a set of extensive examples, including advanced features. Returns a parameter from a program object. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetProgram.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetProgram.xml>`__ :type program: int :arg program: Specifies the program object to be queried. @@ -2037,7 +2037,7 @@ offers a set of extensive examples, including advanced features. Determines if a name corresponds to a shader object. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glIsShader.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glIsShader.xml>`__ :type shader: int :arg shader: Specifies a potential shader object. @@ -2047,7 +2047,7 @@ offers a set of extensive examples, including advanced features. Determines if a name corresponds to a program object - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glIsProgram.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glIsProgram.xml>`__ :type program: int :arg program: Specifies a potential program object. @@ -2057,7 +2057,7 @@ offers a set of extensive examples, including advanced features. Returns the source code string from a shader object - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man2/xhtml/glGetShaderSource.xml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man2/xhtml/glGetShaderSource.xml>`__ :type shader: int :arg shader: Specifies the shader object to be queried. @@ -2073,7 +2073,7 @@ offers a set of extensive examples, including advanced features. Replaces the source code in a shader object. - .. seealso:: `OpenGL Docs <http://www.opengl.org/sdk/docs/man/html/glShaderSource.xhtml>`__ + .. seealso:: `OpenGL Docs <https://www.opengl.org/sdk/docs/man/html/glShaderSource.xhtml>`__ :type shader: int :arg shader: Specifies the handle of the shader object whose source code is to be replaced. diff --git a/doc/python_api/rst/include__bmesh.rst b/doc/python_api/rst/include__bmesh.rst index dc43d2c016e..83e3e73cea4 100644 --- a/doc/python_api/rst/include__bmesh.rst +++ b/doc/python_api/rst/include__bmesh.rst @@ -23,7 +23,7 @@ The features exposed closely follow the C API, giving python access to the functions used by blenders own mesh editing tools. For an overview of BMesh data types and how they reference each other see: -`BMesh Design Document <http://wiki.blender.org/index.php/Dev:2.6/Source/Modeling/BMesh/Design>`_ . +`BMesh Design Document <https://wiki.blender.org/index.php/Dev:Source/Modeling/BMesh/Design>`_ . .. note:: @@ -31,13 +31,12 @@ For an overview of BMesh data types and how they reference each other see: **Disk** and **Radial** data is not exposed by the python api since this is for internal use only. -.. warning:: - - TODO items are... +.. warning:: TODO items are... * add access to BMesh **walkers** * add custom-data manipulation functions add/remove/rename. + Example Script -------------- diff --git a/doc/python_api/rst/info_best_practice.rst b/doc/python_api/rst/info_best_practice.rst index 9b95ada2b2c..418f636030c 100644 --- a/doc/python_api/rst/info_best_practice.rst +++ b/doc/python_api/rst/info_best_practice.rst @@ -18,7 +18,7 @@ amongst our own scripts and make it easier to use python scripts from other proj Using our style guide for your own scripts makes it easier if you eventually want to contribute them to blender. -This style guide is known as pep8 and can be found `here <http://www.python.org/dev/peps/pep-0008>`_ +This style guide is known as pep8 and can be found `here <https://www.python.org/dev/peps/pep-0008/>`_ A brief listing of pep8 criteria. @@ -316,7 +316,7 @@ use to join a list of strings (the list may be temporary). In the following exam Join is fastest on many strings, -`string formatting <http://docs.python.org/py3k/library/string.html#string-formatting>`__ +`string formatting <https://wiki.blender.org/index.php/Dev:Source/Modeling/BMesh/Design>`__ is quite fast too (better for converting data types). String arithmetic is slowest. diff --git a/doc/python_api/rst/info_gotcha.rst b/doc/python_api/rst/info_gotcha.rst index 430a862cf7b..2b361d476da 100644 --- a/doc/python_api/rst/info_gotcha.rst +++ b/doc/python_api/rst/info_gotcha.rst @@ -1,3 +1,4 @@ + ******* Gotchas ******* @@ -38,7 +39,6 @@ but some operators are more picky about when they run. In most cases you can figure out what context an operator needs simply be seeing how it's used in Blender and thinking about what it does. - Unfortunately if you're still stuck - the only way to **really** know whats going on is to read the source code for the poll function and see what its checking. @@ -82,7 +82,6 @@ it should be reported to the bug tracker. Stale Data ========== - No updates after setting values ------------------------------- @@ -174,8 +173,8 @@ In this situation you can... .. _info_gotcha_mesh_faces: -NGons and Tessellation Faces -============================ +N-Gons and Tessellation Faces +============================= Since 2.63 NGons are supported, this adds some complexity since in some cases you need to access triangles/quads still (some exporters for example). @@ -509,7 +508,7 @@ Unicode Problems Python supports many different encodings so there is nothing stopping you from writing a script in ``latin1`` or ``iso-8859-15``. -See `pep-0263 <http://www.python.org/dev/peps/pep-0263/>`_ +See `pep-0263 <https://www.python.org/dev/peps/pep-0263/>`_ However this complicates matters for Blender's Python API because ``.blend`` files don't have an explicit encoding. @@ -657,7 +656,7 @@ Here are some general hints to avoid running into these problems. .. note:: To find the line of your script that crashes you can use the ``faulthandler`` module. - See `faulthandler docs <http://docs.python.org/dev/library/faulthandler.html>`_. + See the `faulthandler docs <https://docs.python.org/dev/library/faulthandler.html>`_. While the crash may be in Blenders C/C++ code, this can help a lot to track down the area of the script that causes the crash. diff --git a/doc/python_api/rst/info_overview.rst b/doc/python_api/rst/info_overview.rst index 07c7d5793c2..b4ae906277d 100644 --- a/doc/python_api/rst/info_overview.rst +++ b/doc/python_api/rst/info_overview.rst @@ -43,8 +43,7 @@ scene manipulation, automation, defining your own toolset and customization. On startup Blender scans the ``scripts/startup/`` directory for Python modules and imports them. The exact location of this directory depends on your installation. -`See the directory layout docs -<https://www.blender.org/manual/getting_started/installing_blender/directorylayout.html>`__ +See the :ref:`directory layout docs <blender_manual:getting-started_installing-config-directories>`. Script Loading @@ -92,7 +91,7 @@ variable which Blender uses to read metadata such as name, author, category and The User Preferences add-on listing uses **bl_info** to display information about each add-on. -`See Add-ons <http://wiki.blender.org/index.php/Dev:2.5/Py/Scripts/Guidelines/Addons>`__ +`See Add-ons <https://wiki.blender.org/index.php/Dev:Py/Scripts/Guidelines/Addons>`__ for details on the ``bl_info`` dictionary. diff --git a/doc/python_api/rst/info_quickstart.rst b/doc/python_api/rst/info_quickstart.rst index f0e1bee58e7..7a899e040a6 100644 --- a/doc/python_api/rst/info_quickstart.rst +++ b/doc/python_api/rst/info_quickstart.rst @@ -51,8 +51,7 @@ A quick list of helpful things to know before starting: | ``scripts/startup/bl_operators`` for operators. Exact location depends on platform, see: - `Configuration and Data Paths - <https://www.blender.org/manual/getting_started/installing_blender/directorylayout.html>`__. + :ref:`Configuration and Data Paths <blender_manual:getting-started_installing-config-directories>`. Running Scripts diff --git a/doc/python_api/rst/info_tips_and_tricks.rst b/doc/python_api/rst/info_tips_and_tricks.rst index e8928e07671..97bc682894a 100644 --- a/doc/python_api/rst/info_tips_and_tricks.rst +++ b/doc/python_api/rst/info_tips_and_tricks.rst @@ -27,7 +27,7 @@ There are 3 main uses for the terminal, these are: .. note:: - For Linux and OSX users this means starting the terminal first, then running Blender from within it. + For Linux and macOS users this means starting the terminal first, then running Blender from within it. On Windows the terminal can be enabled from the help menu. @@ -306,7 +306,7 @@ Advantages include: This is marked advanced because to run Blender as a Python module requires a special build option. For instructions on building see -`Building Blender as a Python module <http://wiki.blender.org/index.php/User:Ideasman42/BlenderAsPyModule>`_ +`Building Blender as a Python module <https://wiki.blender.org/index.php/User:Ideasman42/BlenderAsPyModule>`_ Python Safety (Build Option) diff --git a/doc/python_api/rst/info_tutorial_addon.rst b/doc/python_api/rst/info_tutorial_addon.rst index 9326f80d4c3..92fbf9b8787 100644 --- a/doc/python_api/rst/info_tutorial_addon.rst +++ b/doc/python_api/rst/info_tutorial_addon.rst @@ -121,14 +121,8 @@ Add the following script to the text editor in Blender. obj.location.x += 1.0 -.. image:: run_script.png - :width: 924px - :align: center - :height: 574px - :alt: Run Script button - -Click the Run Script button, all objects in the active scene are moved by 1.0 Blender unit. -Next we will make this script into an add-on. +Click the :ref:`Run Script button <blender_manual:editors-text-run-script>`, +all objects in the active scene are moved by 1.0 Blender unit. Write the Add-on (Simple) @@ -238,7 +232,7 @@ if you want it to be enabled on restart, press *Save as Default*. print(addon_utils.paths()) More is written on this topic here: - `Directory Layout <https://www.blender.org/manual/getting_started/installing_blender/directorylayout.html>`_ + :ref:`Directory Layout <blender_manual:getting-started_installing-config-directories>`. Your Second Add-on @@ -636,6 +630,6 @@ Here are some sites you might like to check on after completing this tutorial. *Great info for those who are still learning Python.* - `Blender Development (Wiki) <https://wiki.blender.org/index.php/Dev:Contents>`_ - *Blender Development, general information and helpful links.* -- `Blender Artists (Coding Section) <http://blenderartists.org/forum/forumdisplay.php?47-Coding>`_ - +- `Blender Artists (Coding Section) <https://blenderartists.org/forum/forumdisplay.php?47-Coding>`_ - *forum where people ask Python development questions* diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 5034de7c02b..432cceece1c 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1632,6 +1632,13 @@ def write_sphinx_conf_py(basepath): file = open(filepath, "w", encoding="utf-8") fw = file.write + fw("import sys, os\n") + fw("\n") + fw("extensions = ['sphinx.ext.intersphinx']\n") + fw("\n") + fw("intersphinx_mapping = {'blender_manual': ('https://www.blender.org/manual/', None)}\n") + fw("\n") + fw("project = 'Blender'\n") # fw("master_doc = 'index'\n") fw("copyright = u'Blender Foundation'\n") @@ -1648,6 +1655,7 @@ def write_sphinx_conf_py(basepath): # not helpful since the source is generated, adds to upload size. fw("html_copy_source = False\n") + fw("html_split_index = True\n") fw("\n") # needed for latex, pdf gen diff --git a/extern/ceres/CMakeLists.txt b/extern/ceres/CMakeLists.txt index 2ad8c543088..a6e9cd9c356 100644 --- a/extern/ceres/CMakeLists.txt +++ b/extern/ceres/CMakeLists.txt @@ -73,10 +73,12 @@ set(SRC internal/ceres/file.cc internal/ceres/generated/partitioned_matrix_view_d_d_d.cc internal/ceres/generated/schur_eliminator_d_d_d.cc + internal/ceres/gradient_checker.cc internal/ceres/gradient_checking_cost_function.cc internal/ceres/gradient_problem.cc internal/ceres/gradient_problem_solver.cc internal/ceres/implicit_schur_complement.cc + internal/ceres/is_close.cc internal/ceres/iterative_schur_complement_solver.cc internal/ceres/lapack.cc internal/ceres/levenberg_marquardt_strategy.cc @@ -116,6 +118,7 @@ set(SRC internal/ceres/triplet_sparse_matrix.cc internal/ceres/trust_region_minimizer.cc internal/ceres/trust_region_preprocessor.cc + internal/ceres/trust_region_step_evaluator.cc internal/ceres/trust_region_strategy.cc internal/ceres/types.cc internal/ceres/wall_time.cc @@ -204,6 +207,7 @@ set(SRC internal/ceres/householder_vector.h internal/ceres/implicit_schur_complement.h internal/ceres/integral_types.h + internal/ceres/is_close.h internal/ceres/iterative_schur_complement_solver.h internal/ceres/lapack.h internal/ceres/levenberg_marquardt_strategy.h @@ -248,6 +252,7 @@ set(SRC internal/ceres/triplet_sparse_matrix.h internal/ceres/trust_region_minimizer.h internal/ceres/trust_region_preprocessor.h + internal/ceres/trust_region_step_evaluator.h internal/ceres/trust_region_strategy.h internal/ceres/visibility_based_preconditioner.h internal/ceres/wall_time.h diff --git a/extern/ceres/ChangeLog b/extern/ceres/ChangeLog index 0e6c195174c..ae8d42a7c95 100644 --- a/extern/ceres/ChangeLog +++ b/extern/ceres/ChangeLog @@ -1,659 +1,588 @@ -commit aef9c9563b08d5f39eee1576af133a84749d1b48 -Author: Alessandro Gentilini <agentilini@gmail.com> -Date: Tue Oct 6 20:43:45 2015 +0200 +commit 8590e6e8e057adba4ec0083446d00268565bb444 +Author: Sameer Agarwal <sameeragarwal@google.com> +Date: Thu Oct 27 12:29:37 2016 -0700 - Add test for Bessel functions. + Remove two checks from rotation.h + + This allows rotation.h to remove its dependency on glog. - Change-Id: Ief5881e8027643d7ef627e60a88fdbad17f3d884 + Change-Id: Ia6aede93ee51a4bd4039570dc8edd100a7045329 -commit 49c86018e00f196c4aa9bd25daccb9919917efee -Author: Alessandro Gentilini <agentilini@gmail.com> -Date: Wed Sep 23 21:59:44 2015 +0200 +commit e892499e8d8977b9178a760348bdd201ec5f3489 +Author: Je Hyeong Hong <jhh37@outlook.com> +Date: Tue Oct 18 22:49:11 2016 +0100 - Add Bessel functions in order to use them in residual code. + Relax the tolerance in QuaternionParameterizationTestHelper. - See "How can I use the Bessel function in the residual function?" at - https://groups.google.com/d/msg/ceres-solver/Vh1gpqac8v0/NIK1EiWJCAAJ + This commit relaxes the tolerance value for comparing between the actual + local matrix and the expected local matrix. Without this fix, + EigenQuaternionParameterization.ZeroTest could fail as the difference + exactly matches the value of std::numeric_limits<double>::epsilon(). - Change-Id: I3e80d9f9d1cadaf7177076e493ff46ace5233b76 + Change-Id: Ic4d3f26c0acdf5f16fead80dfdc53df9e7dabbf9 -commit dfb201220c034fde00a242d0533bef3f73b2907d -Author: Simon Rutishauser <simon.rutishauser@pix4d.com> -Date: Tue Oct 13 07:33:58 2015 +0200 +commit 7ed9e2fb7f1dff264c5e4fbaa89ee1c4c99df269 +Author: Sameer Agarwal <sameeragarwal@google.com> +Date: Wed Oct 19 04:45:23 2016 -0700 - Make miniglog threadsafe on non-windows system by using - localtime_r() instead of localtime() for time formatting + Occured -> Occurred. - Change-Id: Ib8006c685cd8ed4f374893bef56c4061ca2c9747 + Thanks to Phillip Huebner for reporting this. + + Change-Id: I9cddfbb373aeb496961d08e434fe661bff4abd29 -commit 41455566ac633e55f222bce7c4d2cb4cc33d5c72 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Mon Sep 28 22:43:42 2015 +0100 +commit b82f97279682962d8c8ae1b6d9e801ba072a0ab1 +Author: Je Hyeong Hong <jhh37@outlook.com> +Date: Tue Oct 18 21:18:32 2016 +0100 - Remove link-time optimisation (LTO). + Fix a test error in autodiff_test.cc. - - On GCC 4.9+ although GCC supports LTO, it requires use of the - non-default gcc-ar & gcc-ranlib. Whilst we can ensure Ceres is - compiled with these, doing so with GCC 4.9 causes multiple definition - linker errors of static ints inside Eigen when compiling the tests - and examples when they are not also built with LTO. - - On OS X (Xcode 6 & 7) after the latest update to gtest, if LTO - is used when compiling the tests (& examples), two tests fail - due to typeinfo::operator== (things are fine if only Ceres itself is - compiled with LTO). - - This patch disables LTO for all compilers. It should be revisited when - the performance is more stable across our supported compilers. + Previously, the test for the projective camera model would fail as no + tolerance is set in line 144. To resolve this, this commit changes + assert_equal to assert_near. - Change-Id: I17b52957faefbdeff0aa40846dc9b342db1b02e3 + Change-Id: I6cd3379083b1a10c7cd0a9cc83fd6962bb993cc9 -commit 89c40005bfceadb4163bd16b7464b3c2ce740daf -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sun Sep 27 13:37:26 2015 +0100 - - Only use LTO when compiling Ceres itself, not tests or examples. - - - If Ceres is built as a shared library, and LTO is enabled for Ceres - and the tests, then type_info::operator==() incorrectly returns false - in gtests' CheckedDowncastToActualType() in the following tests: - -- levenberg_marquardt_strategy_test. - -- gradient_checking_cost_function_test. - on at least Xcode 6 & 7 as reported here: - https://github.com/google/googletest/issues/595. - - This does not appear to be a gtest issue, but is perhaps an LLVM bug - or an RTTI shared library issue. Either way, disabling the use of - LTO when compiling the test application resolves the issue. - - Allow LTO to be enabled for GCC, if it is supported. - - Add CMake function to allow easy appending to target properties s/t - Ceres library-specific compile flags can be iteratively constructed. - - Change-Id: I923e6aae4f7cefa098cf32b2f8fc19389e7918c9 - -commit 0794f41cca440f7f65d9a44e671f66f6e498ef7c +commit 5690b447de5beed6bdda99b7f30f372283c2fb1a Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Sat Sep 26 14:10:15 2015 -0700 +Date: Thu Oct 13 09:52:02 2016 -0700 - Documentation updates. + Fix documentation source for templated functions in rotation.h - 1. Fix a typo in the Trust Region algorithm. - 2. Add ARL in the list of users. - 3. Update the version history. + Change-Id: Ic1b2e6f0e6eb9914f419fd0bb5af77b66252e57c + +commit 2f8f98f7e8940e465de126fb51282396f42bea20 +Author: Sameer Agarwal <sameeragarwal@google.com> +Date: Thu Oct 13 09:35:18 2016 -0700 + + Prepare for 1.12.0RC1 - Change-Id: Ic286e8ef1a71af07f3890b7592dd3aed9c5f87ce + Change-Id: I23eaf0b46117a01440143001b74dacfa5e57cbf0 -commit 90e32a8dc437dfb0e6747ce15a1f3193c13b7d5b -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Mon Sep 21 21:08:25 2015 +0100 +commit 55c12d2e9569fe4aeac3ba688ac36810935a37ba +Author: Damon Kohler <damonkohler@google.com> +Date: Wed Oct 5 16:30:31 2016 +0200 + + Adds package.xml to support Catkin. + + Change-Id: I8ad4d36a8b036417604a54644e0bb70dd1615feb + +commit 0bcce6565202f5476e40f12afc0a99eb44bd9dfb +Author: Sameer Agarwal <sameeragarwal@google.com> +Date: Mon Oct 10 23:30:42 2016 -0700 - Use old minimum iOS version flags on Xcode < 7.0. + Fix tabs in Android.mk - - The newer style, which are more specific and match the SDK names - are not available on Xcode < 7.0. + Change-Id: Ie5ab9a8ba2b727721565e1ded242609b6df5f8f5 + +commit e6ffe2667170d2fc435443685c0163396fc52d7b +Author: Sameer Agarwal <sameeragarwal@google.com> +Date: Mon Oct 10 22:47:08 2016 -0700 + + Update the version history. - Change-Id: I2f07a0365183d2781157cdb05fd49b30ae001ac5 + Change-Id: I9a57b0541d6cebcb695ecb364a1d4ca04ea4e06c -commit 26cd5326a1fb99ae02c667eab9942e1308046984 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Mon Sep 21 10:16:01 2015 +0100 +commit 0a4ccb7ee939ab35b22e26758401e039b033b176 +Author: David Gossow <dgossow@google.com> +Date: Wed Sep 7 21:38:12 2016 +0200 - Add gtest-specific flags when building/using as a shared library. + Relaxing Jacobian matching in Gradient Checker test. - - Currently these flags are only used to define the relevant DLL export - prefix for Windows. + Any result of an arithmetic operation on floating-point matrices + should never be checked for strict equality with some expected + value, due to limited floating point precision on different machines. + This fixes some occurences of exact checks in the gradient checker + unit test that were causing problems on some platforms. - Change-Id: I0c05207b512cb4a985390aefc779b91febdabb38 + Change-Id: I48e804c9c705dc485ce74ddfe51037d4957c8fcb -commit c4c79472112a49bc1340da0074af2d15b1c89749 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sun Sep 20 18:26:59 2015 +0100 +commit ee44fc91b59584921c1d1c8db153fda6d633b092 +Author: Je Hyeong Hong <jhh37@outlook.com> +Date: Mon Oct 3 12:19:30 2016 +0100 - Clean up iOS.cmake to use xcrun/xcodebuild & libtool. + Fix an Intel compiler error in covariance_impl.cc. - - Substantial cleanup of iOS.cmake to use xcrun & xcodebuild to - determine the SDK & tool paths. - - Use libtool -static to link libraries instead of ar + ranlib, which - is not compatible with Xcode 7+, this change should be backwards - compatible to at least Xcode 6. - - Force locations of unordered_map & shared_ptr on iOS to work around - check_cxx_source_compiles() running in a forked CMake instance without - access to the variables (IOS_PLATFORM) defined by the user. - - Minor CMake style updates. + Intel C compiler strictly asks for parallel loops with collapse to be + perfectly nested. Otherwise, compiling Ceres with ICC will throw an + error at line 348 of covariance_impl.cc. - Change-Id: I5f83a60607db34d461ebe85f9dce861f53d98277 + Change-Id: I1ecb68e89b7faf79e4153dfe6675c390d1780db4 -commit 155765bbb358f1d19f072a4b54825faf1c059910 +commit 9026d69d1ce1e0bcd21debd54a38246d85c7c6e4 Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Wed Sep 16 06:56:08 2015 -0700 +Date: Thu Sep 22 17:20:14 2016 -0700 + + Allow SubsetParameterization to hold all parameters constant + + 1. SubsetParameterization can now be constructed such that all + parameters are constant. This is required for it be used as part + of a ProductParameterization to hold a part of parameter block + constant. For example, a parameter block consisting of a rotation + as a quaternion and a translation vector can now have a local + parameterization where the translation part is constant and the + quaternion part has a QuaternionParameterization associated with it. + + 2. The check for the tangent space of a parameterization being + positive dimensional. We were not doing this check up till now + and the user could accidentally create parameterizations like this + and create a problem for themselves. This will ensure that even + though one can construct a SubsetParameterization where all + parameters are constant, you cannot actually use it as a local + parameterization for an entire parameter block. Which is how + it was before, but the check was inside the SubsetParameterization + constructor. + + 3. Added more tests and refactored existing tests to be more + granular. + + Change-Id: Ic0184a1f30e3bd8a416b02341781a9d98e855ff7 + +commit a36693f83da7a3fd19dce473d060231d4cc97499 +Author: Sameer Agarwal <sameeragarwal@google.com> +Date: Sat Sep 17 16:31:41 2016 -0700 - Import the latest version of gtest and gmock. + Update version history - Change-Id: I4b686c44bba823cab1dae40efa99e31340d2b52a + Change-Id: Ib2f0138ed7a1879ca3b2173e54092f7ae8dd5c9d -commit 0c4647b8f1496c97c6b9376d9c49ddc204aa08dd -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Wed Sep 16 20:01:11 2015 +0100 +commit 01e23e3d33178fdd050973666505c1080cfe04c3 +Author: David Gossow <dgossow@google.com> +Date: Thu Sep 8 12:22:28 2016 +0200 - Remove FAQ about increasing inlining threshold for Clang. + Removing duplicate include directive. - - Changing the inlining threshold for Clang as described has a minimal - effect on user performance. - - The problem that originally prompted the belief that it did was - due to an erroneous CXX flag configuration (in user code). - - Change-Id: I03017241c0f87b8dcefb8c984ec3b192afd97fc2 + Change-Id: I729ae6501497746d1bb615cb893ad592e16ddf3f -commit f4b768b69afcf282568f9ab3a3f0eb8078607468 +commit 99b8210cee92cb972267537fb44bebf56f812d52 Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Sep 14 13:53:24 2015 -0700 +Date: Wed Sep 7 15:31:30 2016 -0700 - Lint changes from William Rucklidge + Update Android.mk to include new files. - Change-Id: I0dac2549a8fa2bfd12f745a8d8a0db623b7ec1ac + Change-Id: Id543ee7d2a65b65c868554a17f593c0a4958e873 -commit 5f2f05c726443e35767d677daba6d25dbc2d7ff8 +commit 195d8d13a6a3962ac39ef7fcdcc6add0216eb8bc Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Fri Sep 11 22:19:38 2015 -0700 +Date: Tue Sep 6 07:12:23 2016 -0700 - Refactor system_test + Remove two DCHECKs from CubicHermiteSpline. - 1. Move common test infrastructure into test_util. - 2. system_test now only contains powells function. - 3. Add bundle_adjustment_test. + They were present as debugging checks but were causing problems + with the build on 32bit i386 due to numerical cancellation issues, + where x ~ -epsilon. - Instead of a single function which computes everything, - there is now a test for each solver configuration which - uses the reference solution computed by the fixture. + Removing these checks only changes the behaviour in Debug mode. + We are already handling such small negative numbers in production + if they occur. All that this change does is to remove the crash. - Change-Id: I16a9a9a83a845a7aaf28762bcecf1a8ff5aee805 - -commit 1936d47e213142b8bf29d3f548905116092b093d -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Tue Sep 8 23:27:42 2015 +0100 - - Revert increased inline threshold (iff Clang) to exported Ceres target. + https://github.com/ceres-solver/ceres-solver/issues/212 - - Increasing the inline threshold results in very variable performance - improvements, and could potentially confuse users if they are trying - to set the inline threshold themselves. - - As such, we no longer export our inline threshold configuration for - Clang, but instead document how to change it in the FAQs. + Thanks to @NeroBurner and @debalance for reporting this. - Change-Id: I88e2e0001e4586ba2718535845ed1e4b1a5b72bc + Change-Id: I66480e86d4fa0a4b621204f2ff44cc3ff8d01c04 -commit a66d89dcda47cefda83758bfb9e7374bec4ce866 +commit 83041ac84f2d67c28559c67515e0e596a3f3aa20 Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Sat Sep 5 16:50:20 2015 -0700 +Date: Fri Sep 2 19:10:35 2016 -0700 - Get ready for 1.11.0RC1 + Fix some compiler warnings. - Update version numbers. - Drop CERES_VERSION_ABI macro. + Reported by Richard Trieu. - Change-Id: Ib3eadabb318afe206bb196a5221b195d26cbeaa0 + Change-Id: I202b7a7df09cc19c92582d276ccf171edf88a9fb -commit 1ac3dd223c179fbadaed568ac532af4139c75d84 +commit 8c4623c63a2676e79e7917bb0561f903760f19b9 Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Sat Sep 5 15:30:01 2015 -0700 +Date: Thu Sep 1 00:05:09 2016 -0700 - Fix a bug in CompressedRowSparseMatrix::AppendRows + Update ExpectArraysClose to use ExpectClose instead of EXPECT_NEAR - The test for CompressedRowSparseMatrix::AppendRows tries to add - a matrix of size zero, which results in an invalid pointer deferencing - even though that pointer is never written to. + The documentation for ExpectArraysClose and its implementation + did not match. - Change-Id: I97dba37082bd5dad242ae1af0447a9178cd92027 - -commit 67622b080c8d37b5e932120a53d4ce76b80543e5 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Sat Sep 5 13:18:38 2015 -0700 - - Fix a pointer access bug in Ridders' algorithm. + This change makes the polynomial_test not fail on 64bit AMD builds. - A pointer to an Eigen matrix was being used as an array. + Thanks to Phillip Huebner for reporting this. - Change-Id: Ifaea14fa3416eda5953de49afb78dc5a6ea816eb + Change-Id: I503f2d3317a28d5885a34f8bdbccd49d20ae9ba2 -commit 5742b7d0f14d2d170054623ccfee09ea214b8ed9 +commit 2fd39fcecb47eebce727081c9ffb8edf86c33669 Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Wed Aug 26 09:24:33 2015 -0700 +Date: Thu Sep 1 16:05:06 2016 -0700 - Improve performance of SPARSE_NORMAL_CHOLESKY + dynamic_sparsity + FindWithDefault returns by value rather than reference. + + Returning by reference leads to lifetime issues with the default + value which may go out of scope by the time it is used. + + Thanks to @Ardavel for reporting this, as this causes graph_test + to fail on VS2015x64. - The outer product computation logic in SparseNormalCholeskySolver - does not work well with dynamic sparsity. The overhead of computing - the sparsity pattern of the normal equations is only amortized if - the sparsity is constant. If the sparsity can change from call to call - SparseNormalCholeskySolver will actually be more expensive. + https://github.com/ceres-solver/ceres-solver/issues/216 - For Eigen and for CXSparse we now explicitly compute the normal - equations using their respective matrix-matrix product routines and solve. - Change-Id: Ifbd8ed78987cdf71640e66ed69500442526a23d4 + Change-Id: I596481219cfbf7622d49a6511ea29193b82c8ba3 -commit d0b6cf657d6ef0dd739e958af9a5768f2eecfd35 -Author: Keir Mierle <mierle@gmail.com> -Date: Fri Sep 4 18:43:41 2015 -0700 +commit 716f049a7b91a8f3a4632c367d9534d1d9190a81 +Author: Mike Vitus <vitus@google.com> +Date: Wed Aug 31 13:38:30 2016 -0700 - Fix incorrect detect structure test + Convert pose graph 2D example to glog and gflags. - Change-Id: I7062f3639147c40b57947790d3b18331a39a366b + Change-Id: I0ed75a60718ef95199bb36f33d9eb99157d11d40 -commit 0e8264cc47661651a11e2dd8570c210082963545 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sat Aug 22 16:23:05 2015 +0100 +commit 46c5ce89dda308088a5fdc238d0c126fdd2c2b58 +Author: David Gossow <dgossow@google.com> +Date: Wed Aug 31 18:40:57 2016 +0200 - Add increased inline threshold (iff Clang) to exported Ceres target. + Fix compiler errors on some systems - - When compiled with Clang, Ceres and all of the examples are compiled - with an increased inlining-threshold, as the default value can result - in poor Eigen performance. - - Previously, client code using Ceres would typically not use an - increased inlining-threshold (unless the user has specifically added - it themselves). However, increasing the inlining threshold can result - in significant performance improvements in auto-diffed CostFunctions. - - This patch adds the inlining-threshold flags to the interface flags - for the Ceres CMake target s/t any client code using Ceres (via - CMake), and compiled with Clang, will now be compiled with the same - increased inlining threshold as used by Ceres itself. + This fixes some signed-unsigned comparisons and a missing header + include. - Change-Id: I31e8f1abfda140d22e85bb48aa57f028a68a415e + Change-Id: Ieb2bf6e905faa74851bc4ac4658d2f1da24b6ecc -commit a1b3fce9e0a4141b973f6b4dd9b08c4c13052d52 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Mon Aug 31 14:14:56 2015 +0100 +commit b102d53e1dd7dab132e58411183b6fffc2090590 +Author: David Gossow <dgossow@google.com> +Date: Wed Aug 31 10:21:20 2016 +0200 - Add optional export of Ceres build directory to new features list. + Gradient checker multithreading bugfix. - Change-Id: I6f1e42b41957ae9cc98fd9dcd1969ef64c4cd96f - -commit e46777d8df068866ef80902401a03e29348d11ae -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Mon Aug 31 12:41:54 2015 +0100 - - Credit reporters of buildsystem bugs in version history. + This is a follow-up on c/7470. GradientCheckingCostFunction calls + callback_->SetGradientErrorDetected() in its Evaluate method, + which will run in multiple threads simultaneously when enabling + this option in the solver. Thus, the string append operation + inside that method has to be protected by a mutex. - Change-Id: I16fe7973534cd556d97215e84268ae0b8ec4e11a + Change-Id: I314ef1df2be52595370d9af05851bf6da39bb45e -commit 01548282cb620e5e3ac79a63a391cd0afd5433e4 +commit 79a28d1e49af53f67af7f3387d07e7c9b7339433 Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Sun Aug 30 22:29:27 2015 -0700 +Date: Wed Aug 31 06:47:45 2016 -0700 - Update the version history. + Rename a confusingly named member of Solver::Options + + Solver::Options::numeric_derivative_relative_step_size to + Solver::Options::gradient_check_numeric_derivative_relative_step_size - Change-Id: I29873bed31675e0108f1a44f53f7bc68976b7f98 + Change-Id: Ib89ae3f87e588d4aba2a75361770d2cec26f07aa -commit 2701429f770fce69ed0c77523fa43d7bc20ac6dc +commit 358ae741c8c4545b03d95c91fa546d9a36683677 Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Sun Aug 30 21:33:57 2015 -0700 +Date: Wed Aug 31 06:58:41 2016 -0700 - Use Eigen::Dynamic instead of ceres::DYNAMIC in numeric_diff.h + Note that Problem::Evaluate cannot be called from an IterationCallback - Change-Id: Iccb0284a8fb4c2160748dfae24bcd595f1d4cb5c + Change-Id: Ieabdc2d40715e6b547ab22156ba32e9c8444b7ed -commit 4f049db7c2a3ee8cf9910c6eac96be6a28a5999c -Author: Tal Ben-Nun <tbennun@gmail.com> -Date: Wed May 13 15:43:51 2015 +0300 +commit 44044e25b14d7e623baae4505a17c913bdde59f8 +Author: Sameer Agarwal <sameeragarwal@google.com> +Date: Wed Aug 31 05:50:58 2016 -0700 - Adaptive numeric differentiation using Ridders' method. + Update the NumTraits for Jets - This method numerically computes function derivatives in different - scales, extrapolating between intermediate results to conserve function - evaluations. Adaptive differentiation is essential to produce accurate - results for functions with noisy derivatives. + 1. Use AVX if EIGEN_VECTORIZE_AVX is defined. + 2. Make the cost of division same as the cost of multiplication. - Full changelist: - -Created a new type of NumericDiffMethod (RIDDERS). - -Implemented EvaluateRiddersJacobianColumn in NumericDiff. - -Created unit tests with f(x) = x^2 + [random noise] and - f(x) = exp(x). + These are updates to the original numtraits update needed for eigen 3.3 + that Shaheen Gandhi sent out. - Change-Id: I2d6e924d7ff686650272f29a8c981351e6f72091 + Change-Id: Ic1e3ed7d05a659c7badc79a894679b2dd61c51b9 -commit 070bba4b43b4b7449628bf456a10452fd2b34d28 +commit 4b6ad5d88e45ce8638c882d3e8f08161089b6dba Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Aug 25 13:37:33 2015 -0700 +Date: Sat Aug 27 23:21:55 2016 -0700 - Lint fixes from William Rucklidge + Use ProductParameterization in bundle_adjuster.cc - Change-Id: I719e8852859c970091df842e59c44e02e2c65827 - -commit 887a20ca7f02a1504e35f7cabbdfb2e0842a0b0b -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Wed Aug 12 21:41:43 2015 +0100 - - Build position independent code when compiling Ceres statically. + Previously, when using a quaternion to parameterize the camera + orientation, the camera parameter block was split into two + parameter blocks. One for the rotation and another for the + translation and intrinsics. This was to enable the use of the + Quaternion parameterization. - - Previously, when Ceres was built as a static library we did not - compile position independent code. This means that the resulting - static library could not be linked against shared libraries, but - could be used by executables. - - To enable the use of a static Ceres library by other shared libraries - as reported in [1], the static library must be generated from - position independent code (except on Windows, where PIC does not - apply). + Now that we have a ProductParameterization which allows us + to compose multiple parameterizations, this is no longer needed + and we use a size 10 parameter block instead. - [1] https://github.com/Itseez/opencv_contrib/pull/290#issuecomment-130389471 + This leads to a more than 2x improvements in the linear solver time. - Change-Id: I99388f1784ece688f91b162d009578c5c97ddaf6 + Change-Id: I78b8f06696f81fee54cfe1a4ae193ee8a5f8e920 -commit 860bba588b981a5718f6b73e7e840e5b8757fe65 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Aug 25 09:43:21 2015 -0700 +commit bfc916cf1cf753b85c1e2ac037e2019ee891f6f9 +Author: Shaheen Gandhi <visigoth@gmail.com> +Date: Thu Aug 4 12:10:14 2016 -0700 - Fix a bug in DetectStructure + Allow ceres to be used with the latest version of Eigen - The logic for determing static/dynamic f-block size in - DetectStructure was broken in a corner case, where the very first - row block which was used to initialize the f_block_size contained - more than one f blocks of varying sizes. The way the if block - was structured, no iteration was performed on the remaining - f-blocks and the loop failed to detect that the f-block size - was actually changing. - - If in the remaining row blocks, there were no row blocks - with varying f-block sizes, the function will erroneously - return a static f-block size. - - Thanks to Johannes Schonberger for providing a reproduction for this - rather tricky corner case. - - Change-Id: Ib442a041d8b7efd29f9653be6a11a69d0eccd1ec + Change-Id: Ief3b0f6b405484ec04ecd9ab6a1e1e5409a594c2 -commit b0cbc0f0b0a22f01724b7b647a4a94db959cc4e4 -Author: Johannes Schönberger <hannesschoenberger@gmail.com> -Date: Thu Aug 20 14:21:30 2015 -0400 +commit edbd48ab502aa418ad9700ee5c3ada5f9268b90a +Author: Alex Stewart <alexs.mac@gmail.com> +Date: Sun Jul 10 14:13:51 2016 +0100 - Reduce memory footprint of SubsetParameterization + Enable support for OpenMP in Clang if detected. + + - Previously we disabled OpenMP if Clang was detected, as it did not + support it. However as of Clang 3.8 (and potentially Xcode 8) OpenMP + is supported. - Change-Id: If113cb4696d5aef3e50eed01fba7a3d4143b7ec8 + Change-Id: Ia39dac9fe746f1fc6310e08553f85f3c37349707 -commit ad2a99777786101411a971e59576ca533a297013 -Author: Sergey Sharybin <sergey.vfx@gmail.com> -Date: Sat Aug 22 11:18:45 2015 +0200 +commit f6df6c05dd83b19fa90044106ebaca40957998ae +Author: Mike Vitus <vitus@google.com> +Date: Thu Aug 18 19:27:43 2016 -0700 - Fix for reoder program unit test when built without suitesparse - - This commit fixes failure of reorder_program_test when Ceres is built without - any suitesparse. + Add an example for modeling and solving a 3D pose graph SLAM problem. - Change-Id: Ia23ae8dfd20c482cb9cd1301f17edf9a34df3235 + Change-Id: I750ca5f20c495edfee5f60ffedccc5bd8ba2bb37 -commit 4bf3868beca9c17615f72ec03730cddb3676acaa -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Sun Aug 9 15:24:45 2015 -0700 +commit ac3b8e82175122e38bafaaa9cd419ba3cee11087 +Author: David Gossow <dgossow@google.com> +Date: Fri Apr 29 16:07:11 2016 +0200 - Fix a bug in the Schur eliminator + Gradient checking cleanup and local parameterization bugfix - The schur eliminator treats rows with e blocks and row with - no e blocks separately. The template specialization logic only - applies to the rows with e blocks. + Change the Ceres gradient checking API to make is useful for + unit testing, clean up code duplication and fix interaction between + gradient checking and local parameterizations. - So, in cases where the rows with e-blocks have a fixed size f-block - but the rows without e-blocks have f-blocks of varying sizes, - DetectStructure will return a static f-block size, but we need to be - careful that we do not blindly use that static f-block size everywhere. + There were two gradient checking implementations, one being used + when using the check_gradients flag in the Solver, the other + being a standalone class. The standalone version was restricted + to cost functions with fixed parameter sizes at compile time, which + is being lifted here. This enables it to be used inside the + GradientCheckingCostFunction as well. - This patch fixes a bug where such care was not being taken, where - it was assumed that the static f-block size could be assumed for all - f-block sizes. + In addition, this installs new hooks in the Solver to ensure + that Solve will fail if any incorrect gradients are detected. This + way, you can set the check_gradient flags to true and detect + errors in an automated way, instead of just printing error information + to the log. The error log is now also returned in the Solver summary + instead of being printed directly. The user can then decide what to + do with it. The existing hooks for user callbacks are used for + this purpose to keep the internal API changes minimal and non-invasive. - A new test is added, which triggers an exception in debug mode. In - release mode this error does not present itself, due to a peculiarity - of the way Eigen works. + The last and biggest change is the way the the interaction between + local parameterizations and the gradient checker works. Before, + local parameterizations would be ignored by the checker. However, + if a cost function does not compute its Jacobian along the null + space of the local parameterization, this wil not have any effect + on the solver, but would result in a gradient checker error. + With this change, the Jacobians are multiplied by the Jacobians + of the respective local parameterization and thus being compared + in the tangent space only. - Thanks to Werner Trobin for reporting this bug. + The typical use case for this are quaternion parameters, where + a cost function will typically assume that the quaternion is + always normalized, skipping the correct computation of the Jacobian + along the normal to save computation cost. - Change-Id: I8ae7aabf8eed8c3f9cf74b6c74d632ba44f82581 + Change-Id: I5e1bb97b8a899436cea25101efe5011b0bb13282 -commit 1635ce726078f00264b89d7fb6e76fd1c2796e59 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Wed Aug 19 00:26:02 2015 -0700 +commit d4264ec10d9a270b53b5db86c0245ae8cbd2cf18 +Author: Mike Vitus <vitus@google.com> +Date: Wed Aug 17 13:39:05 2016 -0700 - Fix a bug in the reordering code. - - When the user provides an ordering which starts at a non-zero group id, - or has gaps in the groups, then CAMD, the algorithm used to reorder - the program can crash or return garbage results. - - The solution is to map the ordering into grouping constraints, and then - to re-number the groups to be contiguous using a call to - MapValuesToContiguousRange. This was already done for CAMD based - ordering for Schur type solvers, but was not done for SPARSE_NORMAL_CHOLESKY. - - Thanks to Bernhard Zeisl for not only reporting the bug but also - providing a reproduction. + Add a quaternion local parameterization for Eigen's quaternion element convention. - Change-Id: I5cfae222d701dfdb8e1bda7f0b4670a30417aa89 + Change-Id: I7046e8b24805313c5fb6a767de581d0054fcdb83 -commit 4c3f8987e7f0c51fd367cf6d43d7eb879e79589f -Author: Simon Rutishauser <simon.rutishauser@pix4d.com> -Date: Thu Aug 13 11:10:44 2015 +0200 +commit fd7cab65ef30fbc33612220abed52dd5073413c4 +Author: Mike Vitus <vitus@google.com> +Date: Wed Aug 10 09:29:12 2016 -0700 - Add missing CERES_EXPORT to ComposedLoss + Fix typos in the pose graph 2D example. - Change-Id: Id7db388d41bf53e6e5704039040c9d2c6bf4c29c + Change-Id: Ie024ff6b6cab9f2e8011d21121a91931bd987bd1 -commit 1a740cc787b85b883a0703403a99fe49662acb79 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Tue Aug 11 18:08:05 2015 -0700 +commit 375dc348745081f89693607142d8b6744a7fb6b4 +Author: Mike Vitus <vitus@google.com> +Date: Wed Aug 3 18:51:16 2016 -0700 - Add the option to use numeric differentiation to nist and more_garbow_hillstrom + Remove duplicate entry for the NIST example in the docs. - Change-Id: If0a5caef90b524dcf5e2567c5b681987f5459401 + Change-Id: Ic4e8f9b68b77b5235b5c96fe588cc56866dab759 -commit ea667ede5c038d6bf3d1c9ec3dbdc5072d1beec6 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sun Aug 9 16:56:13 2015 +0100 +commit f554681bf22d769abc12dd6d346ef65f9bb22431 +Author: Mike Vitus <vitus@google.com> +Date: Mon Jul 25 18:30:48 2016 -0700 - Fix EIGENSPARSE option help s/t it displays in CMake ncurses GUI. - - - Shorten description for EIGENSPARSE to a single line, as otherwise - it is not correctly displayed in the ncurses CMake GUI. - - Made explicit in description that this results in an LGPL licensed - version of Ceres (this is also made clear in the CMake log output if - EIGENSPARSE is enabled). + Add an example for modeling and solving a 2D pose graph SLAM problem. - Change-Id: I11678a9cbc7a817133c22128da01055a3cb8a26d + Change-Id: Ia89b12af7afa33e7b1b9a68d69cf2a0b53416737 -commit a14ec27fb28ab2e8d7f1c9d88e41101dc6c0aab5 -Author: Richard Stebbing <richie.stebbing@gmail.com> -Date: Fri Aug 7 08:42:03 2015 -0700 +commit e1bcc6e0f51512f43aa7bfb7b0d62f7ac1d0cd4b +Author: Sameer Agarwal <sameeragarwal@google.com> +Date: Wed May 18 07:52:48 2016 -0700 - Fix SparseNormalCholeskySolver with dynamic sparsity. - - The previous implementation incorrectly cached the outer product matrix - pattern even when `dynamic_sparsity = true`. + Add additional logging for analyzing orderings - Change-Id: I1e58315a9b44f2f457d07c56b203ab2668bfb8a2 + Change-Id: Ic68d2959db35254e2895f11294fb25de4d4b8a81 -commit 3dd7fced44ff00197fa9fcb1f2081d12be728062 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sun Aug 9 16:38:50 2015 +0100 +commit 16980b4fec846f86910c18772b8145bcb55f4728 +Author: Mike Vitus <vitus@google.com> +Date: Fri Jul 15 13:37:49 2016 -0700 - Remove legacy dependency detection macros. + Delete the remove_definitons command from sampled_functions + CMakeLists.txt because it will be inherited from the top level examples + CMakeLists.txt. - - Before the new CMake buildsystem in 1.8, Ceres used non-standard - HINTS variables for dependencies. For backwards compatibility CMake - macros were added to translate these legacy variables into the new - (standard) variables. - - As it has now been multiple releases since the legacy variables - were used and they no longer appear in any of the documentation - support for them has now expired. - - Change-Id: I2cc72927ed711142ba7943df334ee008181f86a2 + Change-Id: I25593587df0ae84fd8ddddc589bc2a13f3777427 -commit 8b32e258ccce1eed2a50bb002add16cad13aff1e -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sun Aug 9 15:42:39 2015 +0100 +commit a04490be97800e78e59db5eb67fa46226738ffba +Author: Mike Vitus <vitus@google.com> +Date: Thu Jul 14 10:10:13 2016 -0700 - Fix failed if() condition expansion if gflags is not found. - - - If a CMake-ified version of gflags is not detected, then - gflags_LIBRARIES is not set and the TARGET condition within a - multiconditional if() statement prevents configuration. + Add readme for the sampled_function example. - Change-Id: Ia92e97523d7a1478ab36539726b9540d7cfee5d0 + Change-Id: I9468b6a7b9f2ffdd2bf9f0dd1f4e1d5f894e540c -commit cc8d47aabb9d63ba4588ba7295058a6191c2df83 +commit ff11d0e63d4678188e8cabd40a532ba06912fe5a Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sun Aug 9 15:18:42 2015 +0100 +Date: Wed Jun 29 09:31:45 2016 +0100 - Update all CMake to lowercase function name style. + Use _j[0,1,n]() Bessel functions on MSVC to avoid deprecation errors. - - Updated to new CMake style where function names are all lowercase, - this will be backwards compatible as CMake function names are - case insensitive. - - Updated using Emacs' M-x unscreamify-cmake-buffer. + - Microsoft deprecated the POSIX Bessel functions: j[0,1,n]() in favour + of _j[0,1,n](), it appears since at least MSVC 2005: + https://msdn.microsoft.com/en-us/library/ms235384(v=vs.100).aspx. + - As this occurs in jet.h (templated public header), although Ceres + suppresses the warning when it itself is built (to suppress a warning + about the insecurity of using std::copy), it will crop up again in + client code (without this fix) unless it is explicitly suppressed + there also. + - Raised as Issue #190: + https://github.com/ceres-solver/ceres-solver/issues/190. - Change-Id: If7219816f560270e59212813aeb021353a64a0e2 + Change-Id: If7ac5dbb856748f9900be93ec0452a40c0b00524 -commit 1f106904c1f47460c35ac03258d6506bb2d60838 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sun Aug 9 14:55:02 2015 +0100 +commit 8ea86e1614cf77644ce782e43cde6565a54444f5 +Author: Nicolai Wojke <nwojke@uni-koblenz.de> +Date: Mon Apr 25 14:24:41 2016 +0200 - Update minimum iOS version to 7.0 for shared_ptr/unordered_map. - - - In order to correctly detect shared_ptr (& unordered_map) - the iOS version must be >= 7.0 (Xcode 5.0+). This only affects the - SIMULATOR(64) platform builds, as the OS (device) build uses the - latest SDK which is now likely 8.0+. + Fix: Copy minimizer option 'is_silent' to LinSearchDirection::Options - Change-Id: Iefec8f03408b8cdc7a495f442ebba081f800adb0 + Change-Id: I23b4c3383cad30033c539ac93883d77c8dd4ba1a -commit 16ecd40523a408e7705c9fdb0e159cef2007b8ab -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sat Aug 8 17:32:31 2015 +0100 +commit 080ca4c5f2ac42620971a07f06d2d13deb7befa8 +Author: Sameer Agarwal <sameeragarwal@google.com> +Date: Sun Apr 24 22:46:54 2016 -0700 - Fix bug in gflags' <= 2.1.2 exported CMake configuration. - - - gflags <= 2.1.2 has a bug in its exported gflags-config.cmake: - https://github.com/gflags/gflags/issues/110 whereby it sets - gflags_LIBRARIES to a non-existent 'gflags' target. - - This causes linker errors if gflags is installed in a non-standard - location (as otherwise CMake resolves gflags to -lgflags which - links if gflags is installed somewhere on the current path). - - We now check for this case, and search for the correct gflags imported - target and update gflags_LIBRARIES to reference it if found, otherwise - proceed on to the original manual search to try to find gflags. + Fix typos in users.rst - Change-Id: Iceccc3ee53c7c2010e41cc45255f966e7b13d526 + Change-Id: Ifdc67638a39403354bc9589f42a1b42cb9984dd2 -commit 56be8de007dfd65ed5a31c795eb4a08ad765f411 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Thu Jun 25 21:31:00 2015 +0100 +commit 21ab397dc55335c147fdd795899b1f8981037b09 +Author: Sameer Agarwal <sameeragarwal@google.com> +Date: Sun Apr 24 21:13:00 2016 -0700 - Add docs for new CXX11 option & mask option for Windows. - - - The CXX11 option has no effect on Windows, as there, any new C++11 - features are enabled by default, as such to avoid confusion we only - present the option for non-Windows. + Make some Jet comparisons exact. - Change-Id: I38925ae3bb8c16682d404468ba95c611a519b9b9 + Change-Id: Ia08c72f3b8779df96f5c0d5a954b2c0a1dd3a061 -commit cf863b6415ac4dbf3626e70adeac1ac0f3d87ee5 +commit ee40f954cf464087eb8943abf4d9db8917a33fbe Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Thu Aug 6 14:52:18 2015 -0700 +Date: Sun Apr 24 07:49:55 2016 -0700 - Remove the spec file needed for generating RPMs. + Add colmap to users.rst - Now that ceres is part of RawHide, there is no need to carry - this spec file with the ceres distribution. - - Change-Id: Icc400b9874ba05ba05b353e2658f1de94c72299e + Change-Id: I452a8c1dc6a3bc55734b2fc3a4002ff7939ba863 -commit 560940fa277a469c1ab34f1aa303ff1af9c3cacf +commit 9665e099022bd06e53b0779550e9aebded7f274d Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Sat Jul 11 22:21:31 2015 -0700 +Date: Mon Apr 18 06:00:58 2016 -0700 - A refactor of the cubic interpolation code + Fix step norm evaluation in LineSearchMinimizer - 1. Push the boundary handling logic into the underlying array - object. This has two very significant impacts: + TrustRegionMinimizer evaluates the size of the step + taken in the ambient space, where as the LineSearchMinimizer + was using the norm in the tangent space. This change fixes + this discrepancy. - a. The interpolation code becomes extremely simple to write - and to test. + Change-Id: I9fef64cbb5622c9769c0413003cfb1dc6e89cfa3 + +commit 620ca9d0668cd4a00402264fddca3cf6bd2e7265 +Author: Alex Stewart <alexs.mac@gmail.com> +Date: Mon Apr 18 15:14:11 2016 +0100 + + Remove use of -Werror when compiling Ceres. - b. The user has more flexibility in implementing how out of bounds - values are handled. We provide one default implementation. + - As noted in Issue #193 (in that case for GCC 6), Ceres' use of -Werror + when compiling on *nix can prevent compilation on new compilers that + add new warnings and there is an inevitable delay between new compiler + versions and Ceres versions. + - Removing the explicit use of -Werror, and relying on indirect + verification by maintainers should fix build issues for Ceres releases + on newer compilers. - Change-Id: Ic2f6cf9257ce7110c62e492688e5a6c8be1e7df2 + Change-Id: I38e9ade28d4a90e53dcd918a7d470f1a1debd7b4 -commit dfdf19e111c2b0e6daeb6007728ec2f784106d49 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Wed Aug 5 15:20:57 2015 -0700 +commit 0c63bd3efbf1d41151c9fab41d4b77dc64c572c8 +Author: Mike Vitus <vitus@google.com> +Date: Thu Apr 14 10:25:52 2016 -0700 - Lint cleanup from Jim Roseborough + Add floor and ceil functions to the Jet implementation. - Change-Id: Id6845c85644d40e635ed196ca74fc51a387aade4 + Change-Id: I72ebfb0e9ade2964dbf3a014225ead345d5ae352 -commit 7444f23ae245476a7ac8421cc2f88d6947fd3e5f -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Mon Aug 3 12:22:44 2015 -0700 +commit 9843f3280356c158d23c06a16085c6c5ba35e053 +Author: Alex Stewart <alexs.mac@gmail.com> +Date: Mon Mar 7 21:24:32 2016 +0000 - Fix a typo in small_blas.h - - The reason this rather serious looking typo has not - caused any problems uptil now is because NUM_ROW_B is - computed but never actually used. + Report Ceres compile options as components in find_package(). - Thanks to Werner Trobin for pointing this out. + - Users can now specify particular components from Ceres, such as + SuiteSparse support) that must be present in a detected version of + Ceres in order for it to be reported as found by find_package(). + - This allows users to specify for example that they require a version + of Ceres with SuiteSparse support at configure time, rather than + finding out only at run time that Ceres was not compiled with the + options they require. + - The list of available components are built directly from the Ceres + compile options. + - The meta-module SparseLinearAlgebraLibrary is present if at least + one sparse linear algebra backend is available. - Change-Id: Id2b4d9326ec21baec8a85423e3270aefbafb611e + Change-Id: I65f1ddfd7697e6dd25bb4ac7e54f5097d3ca6266 -commit 5a48b92123b30a437f031eb24b0deaadc8f60d26 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sat Jul 4 17:59:52 2015 +0100 +commit e4d4d88bbe51b9cc0f7450171511abbea0779790 +Author: Timer <linyicx@126.com> +Date: Fri Apr 8 15:42:18 2016 +0800 - Export Ceres build directory into local CMake package registry. - - - Optionally use CMake's export() functionality to export the Ceres - build directory as a package into the local CMake package registry. - - This enables the detection & use of Ceres from CMake *without* - requiring that Ceres be installed. + Fix a spelling error in nnls_modeling.rst - Change-Id: Ib5a7588446f490e1b405878475b6b1dd13accd1f + Change-Id: I341d901d3df993bc5397ed15e6cb330b0c38fd72 -commit d9790e77894ea99d38137d359d6118315b2d1601 -Author: Sameer Agarwal <sameeragarwal@google.com> -Date: Sun Jul 12 19:39:47 2015 -0700 +commit 5512f58536e1be0d92010d8325b606e7b4733a08 +Author: Keir Mierle <mierle@gmail.com> +Date: Thu Apr 7 12:03:16 2016 -0700 - Add ProductParameterization + Only use collapse() directive with OpenMP 3.0 or higher - Often a parameter block is the Cartesian product of a number of - manifolds. For example, a rigid transformation SE(3) = SO(3) x R^3 - In such cases, where you have the local parameterization - of the individual manifolds available, - ProductParameterization can be used to construct a local - parameterization of the cartesian product. + Change-Id: Icba544c0494763c57eb6dc61e98379312ca15972 + +commit d61e94da5225217cab7b4f93b72f97055094681f +Author: Thomas Schneider <schneith@ethz.ch> +Date: Wed Apr 6 10:40:29 2016 +0200 + + Add IsParameterBlockConstant to the ceres::Problem class. - Change-Id: I4b5bcbd2407a38739c7725b129789db5c3d65a20 + Change-Id: I7d0e828e81324443209c17fa54dd1d37605e5bfe -commit 7b4fb69dad49eaefb5d2d47ef0d76f48ad7fef73 +commit 77d94b34741574e958a417561702d6093fba87fb Author: Alex Stewart <alexs.mac@gmail.com> -Date: Sun Jun 28 21:43:46 2015 +0100 - - Cleanup FindGflags & use installed gflags CMake config if present. - - - Split out gflags namespace detection methods: - check_cxx_source_compiles() & regex, into separate functions. - - Use installed/exported gflags CMake configuration (present for - versions >= 2.1) if available, unless user expresses a preference not - to, or specifies search directories, in which case fall back to manual - search for components. - -- Prefer installed gflags CMake configurations over exported gflags - build directories on all OSs. - - Remove custom version of check_cxx_source_compiles() that attempted - to force the build type of the test project. This only worked for - NMake on Windows, not MSVC as msbuild ignored our attempts to force - the build type. Now we always use the regex method on Windows if - we cannot find an installed gflags CMake configuration which works - even on MSVC by bypassing msbuild. - - Add default search paths for gflags on Windows. - - Change-Id: I083b267d97a7a5838a1314f3d41a61ae48d5a2d7 - -commit b3063c047906d4a44503dc0187fdcbbfcdda5f38 -Author: Alex Stewart <alexs.mac@gmail.com> -Date: Wed Jul 15 20:56:56 2015 +0100 +Date: Sun Feb 14 16:54:03 2016 +0000 - Add default glog install location on Windows to search paths. + Fix install path for CeresConfig.cmake to be architecture-aware. + + - Previously we were auto-detecting a "64" suffix for the install path + for the Ceres library on non-Debian/Arch Linux distributions, but + we were installing CeresConfig.cmake to an architecture independent + location. + - We now install CeresConfig.cmake to lib${LIB_SUFFIX}/cmake/Ceres. + - Also make LIB_SUFFIX visible to the user in the CMake GUI s/t they can + easily override the auto-detected value if desired. + - Reported by jpgr87@gmail.com as Issue #194. - Change-Id: I083d368be48986e6780c11460f5a07b2f3b6c900 + Change-Id: If126260d7af685779487c01220ae178ac31f7aea diff --git a/extern/ceres/bundle.sh b/extern/ceres/bundle.sh index 0eaf00f3989..a4f703ac33d 100755 --- a/extern/ceres/bundle.sh +++ b/extern/ceres/bundle.sh @@ -173,26 +173,5 @@ if(WITH_OPENMP) ) endif() -TEST_UNORDERED_MAP_SUPPORT() -if(HAVE_STD_UNORDERED_MAP_HEADER) - if(HAVE_UNORDERED_MAP_IN_STD_NAMESPACE) - add_definitions(-DCERES_STD_UNORDERED_MAP) - else() - if(HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE) - add_definitions(-DCERES_STD_UNORDERED_MAP_IN_TR1_NAMESPACE) - else() - add_definitions(-DCERES_NO_UNORDERED_MAP) - message(STATUS "Replacing unordered_map/set with map/set (warning: slower!)") - endif() - endif() -else() - if(HAVE_UNORDERED_MAP_IN_TR1_NAMESPACE) - add_definitions(-DCERES_TR1_UNORDERED_MAP) - else() - add_definitions(-DCERES_NO_UNORDERED_MAP) - message(STATUS "Replacing unordered_map/set with map/set (warning: slower!)") - endif() -endif() - blender_add_lib(extern_ceres "\${SRC}" "\${INC}" "\${INC_SYS}") EOF diff --git a/extern/ceres/files.txt b/extern/ceres/files.txt index f49f1fb0ded..4d973bbcdc2 100644 --- a/extern/ceres/files.txt +++ b/extern/ceres/files.txt @@ -149,6 +149,7 @@ internal/ceres/generated/schur_eliminator_4_4_d.cc internal/ceres/generated/schur_eliminator_d_d_d.cc internal/ceres/generate_eliminator_specialization.py internal/ceres/generate_partitioned_matrix_view_specializations.py +internal/ceres/gradient_checker.cc internal/ceres/gradient_checking_cost_function.cc internal/ceres/gradient_checking_cost_function.h internal/ceres/gradient_problem.cc @@ -160,6 +161,8 @@ internal/ceres/householder_vector.h internal/ceres/implicit_schur_complement.cc internal/ceres/implicit_schur_complement.h internal/ceres/integral_types.h +internal/ceres/is_close.cc +internal/ceres/is_close.h internal/ceres/iterative_schur_complement_solver.cc internal/ceres/iterative_schur_complement_solver.h internal/ceres/lapack.cc @@ -243,6 +246,8 @@ internal/ceres/trust_region_minimizer.cc internal/ceres/trust_region_minimizer.h internal/ceres/trust_region_preprocessor.cc internal/ceres/trust_region_preprocessor.h +internal/ceres/trust_region_step_evaluator.cc +internal/ceres/trust_region_step_evaluator.h internal/ceres/trust_region_strategy.cc internal/ceres/trust_region_strategy.h internal/ceres/types.cc diff --git a/extern/ceres/include/ceres/cost_function_to_functor.h b/extern/ceres/include/ceres/cost_function_to_functor.h index 6c67ac0f937..d2dc94725e4 100644 --- a/extern/ceres/include/ceres/cost_function_to_functor.h +++ b/extern/ceres/include/ceres/cost_function_to_functor.h @@ -130,7 +130,8 @@ class CostFunctionToFunctor { const int num_parameter_blocks = (N0 > 0) + (N1 > 0) + (N2 > 0) + (N3 > 0) + (N4 > 0) + (N5 > 0) + (N6 > 0) + (N7 > 0) + (N8 > 0) + (N9 > 0); - CHECK_EQ(parameter_block_sizes.size(), num_parameter_blocks); + CHECK_EQ(static_cast<int>(parameter_block_sizes.size()), + num_parameter_blocks); CHECK_EQ(N0, parameter_block_sizes[0]); if (parameter_block_sizes.size() > 1) CHECK_EQ(N1, parameter_block_sizes[1]); // NOLINT diff --git a/extern/ceres/include/ceres/covariance.h b/extern/ceres/include/ceres/covariance.h index dd20dc36ba1..930f96cf3ae 100644 --- a/extern/ceres/include/ceres/covariance.h +++ b/extern/ceres/include/ceres/covariance.h @@ -357,6 +357,28 @@ class CERES_EXPORT Covariance { const double*> >& covariance_blocks, Problem* problem); + // Compute a part of the covariance matrix. + // + // The vector parameter_blocks contains the parameter blocks that + // are used for computing the covariance matrix. From this vector + // all covariance pairs are generated. This allows the covariance + // estimation algorithm to only compute and store these blocks. + // + // parameter_blocks cannot contain duplicates. Bad things will + // happen if they do. + // + // Note that the list of covariance_blocks is only used to determine + // what parts of the covariance matrix are computed. The full + // Jacobian is used to do the computation, i.e. they do not have an + // impact on what part of the Jacobian is used for computation. + // + // The return value indicates the success or failure of the + // covariance computation. Please see the documentation for + // Covariance::Options for more on the conditions under which this + // function returns false. + bool Compute(const std::vector<const double*>& parameter_blocks, + Problem* problem); + // Return the block of the cross-covariance matrix corresponding to // parameter_block1 and parameter_block2. // @@ -394,6 +416,40 @@ class CERES_EXPORT Covariance { const double* parameter_block2, double* covariance_block) const; + // Return the covariance matrix corresponding to all parameter_blocks. + // + // Compute must be called before calling GetCovarianceMatrix and all + // parameter_blocks must have been present in the vector + // parameter_blocks when Compute was called. Otherwise + // GetCovarianceMatrix returns false. + // + // covariance_matrix must point to a memory location that can store + // the size of the covariance matrix. The covariance matrix will be + // a square matrix whose row and column count is equal to the sum of + // the sizes of the individual parameter blocks. The covariance + // matrix will be a row-major matrix. + bool GetCovarianceMatrix(const std::vector<const double *> ¶meter_blocks, + double *covariance_matrix); + + // Return the covariance matrix corresponding to parameter_blocks + // in the tangent space if a local parameterization is associated + // with one of the parameter blocks else returns the covariance + // matrix in the ambient space. + // + // Compute must be called before calling GetCovarianceMatrix and all + // parameter_blocks must have been present in the vector + // parameters_blocks when Compute was called. Otherwise + // GetCovarianceMatrix returns false. + // + // covariance_matrix must point to a memory location that can store + // the size of the covariance matrix. The covariance matrix will be + // a square matrix whose row and column count is equal to the sum of + // the sizes of the tangent spaces of the individual parameter + // blocks. The covariance matrix will be a row-major matrix. + bool GetCovarianceMatrixInTangentSpace( + const std::vector<const double*>& parameter_blocks, + double* covariance_matrix); + private: internal::scoped_ptr<internal::CovarianceImpl> impl_; }; diff --git a/extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h b/extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h index c852d57a3fc..5770946a115 100644 --- a/extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h +++ b/extern/ceres/include/ceres/dynamic_numeric_diff_cost_function.h @@ -85,22 +85,6 @@ class DynamicNumericDiffCostFunction : public CostFunction { options_(options) { } - // Deprecated. New users should avoid using this constructor. Instead, use the - // constructor with NumericDiffOptions. - DynamicNumericDiffCostFunction( - const CostFunctor* functor, - Ownership ownership, - double relative_step_size) - : functor_(functor), - ownership_(ownership), - options_() { - LOG(WARNING) << "This constructor is deprecated and will be removed in " - "a future version. Please use the NumericDiffOptions " - "constructor instead."; - - options_.relative_step_size = relative_step_size; - } - virtual ~DynamicNumericDiffCostFunction() { if (ownership_ != TAKE_OWNERSHIP) { functor_.release(); @@ -138,19 +122,19 @@ class DynamicNumericDiffCostFunction : public CostFunction { std::vector<double> parameters_copy(parameters_size); std::vector<double*> parameters_references_copy(block_sizes.size()); parameters_references_copy[0] = ¶meters_copy[0]; - for (int block = 1; block < block_sizes.size(); ++block) { + for (size_t block = 1; block < block_sizes.size(); ++block) { parameters_references_copy[block] = parameters_references_copy[block - 1] + block_sizes[block - 1]; } // Copy the parameters into the local temp space. - for (int block = 0; block < block_sizes.size(); ++block) { + for (size_t block = 0; block < block_sizes.size(); ++block) { memcpy(parameters_references_copy[block], parameters[block], block_sizes[block] * sizeof(*parameters[block])); } - for (int block = 0; block < block_sizes.size(); ++block) { + for (size_t block = 0; block < block_sizes.size(); ++block) { if (jacobians[block] != NULL && !NumericDiff<CostFunctor, method, DYNAMIC, DYNAMIC, DYNAMIC, DYNAMIC, DYNAMIC, DYNAMIC, diff --git a/extern/ceres/include/ceres/gradient_checker.h b/extern/ceres/include/ceres/gradient_checker.h index 28304159b44..6d285daf1d9 100644 --- a/extern/ceres/include/ceres/gradient_checker.h +++ b/extern/ceres/include/ceres/gradient_checker.h @@ -27,194 +27,121 @@ // POSSIBILITY OF SUCH DAMAGE. // Copyright 2007 Google Inc. All Rights Reserved. // -// Author: wjr@google.com (William Rucklidge) -// -// This file contains a class that exercises a cost function, to make sure -// that it is computing reasonable derivatives. It compares the Jacobians -// computed by the cost function with those obtained by finite -// differences. +// Authors: wjr@google.com (William Rucklidge), +// keir@google.com (Keir Mierle), +// dgossow@google.com (David Gossow) #ifndef CERES_PUBLIC_GRADIENT_CHECKER_H_ #define CERES_PUBLIC_GRADIENT_CHECKER_H_ -#include <cstddef> -#include <algorithm> #include <vector> +#include <string> +#include "ceres/cost_function.h" +#include "ceres/dynamic_numeric_diff_cost_function.h" #include "ceres/internal/eigen.h" #include "ceres/internal/fixed_array.h" #include "ceres/internal/macros.h" #include "ceres/internal/scoped_ptr.h" -#include "ceres/numeric_diff_cost_function.h" +#include "ceres/local_parameterization.h" #include "glog/logging.h" namespace ceres { -// An object that exercises a cost function, to compare the answers that it -// gives with derivatives estimated using finite differencing. +// GradientChecker compares the Jacobians returned by a cost function against +// derivatives estimated using finite differencing. // -// The only likely usage of this is for testing. +// The condition enforced is that // -// How to use: Fill in an array of pointers to parameter blocks for your -// CostFunction, and then call Probe(). Check that the return value is -// 'true'. See prober_test.cc for an example. +// (J_actual(i, j) - J_numeric(i, j)) +// ------------------------------------ < relative_precision +// max(J_actual(i, j), J_numeric(i, j)) +// +// where J_actual(i, j) is the jacobian as computed by the supplied cost +// function (by the user) multiplied by the local parameterization Jacobian +// and J_numeric is the jacobian as computed by finite differences, multiplied +// by the local parameterization Jacobian as well. // -// This is templated similarly to NumericDiffCostFunction, as it internally -// uses that. -template <typename CostFunctionToProbe, - int M = 0, int N0 = 0, int N1 = 0, int N2 = 0, int N3 = 0, int N4 = 0> +// How to use: Fill in an array of pointers to parameter blocks for your +// CostFunction, and then call Probe(). Check that the return value is 'true'. class GradientChecker { public: - // Here we stash some results from the probe, for later - // inspection. - struct GradientCheckResults { - // Computed cost. - Vector cost; - - // The sizes of these matrices are dictated by the cost function's - // parameter and residual block sizes. Each vector's length will - // term->parameter_block_sizes().size(), and each matrix is the - // Jacobian of the residual with respect to the corresponding parameter - // block. + // This will not take ownership of the cost function or local + // parameterizations. + // + // function: The cost function to probe. + // local_parameterization: A vector of local parameterizations for each + // parameter. May be NULL or contain NULL pointers to indicate that the + // respective parameter does not have a local parameterization. + // options: Options to use for numerical differentiation. + GradientChecker( + const CostFunction* function, + const std::vector<const LocalParameterization*>* local_parameterizations, + const NumericDiffOptions& options); + + // Contains results from a call to Probe for later inspection. + struct ProbeResults { + // The return value of the cost function. + bool return_value; + + // Computed residual vector. + Vector residuals; + + // The sizes of the Jacobians below are dictated by the cost function's + // parameter block size and residual block sizes. If a parameter block + // has a local parameterization associated with it, the size of the "local" + // Jacobian will be determined by the local parameterization dimension and + // residual block size, otherwise it will be identical to the regular + // Jacobian. // Derivatives as computed by the cost function. - std::vector<Matrix> term_jacobians; + std::vector<Matrix> jacobians; + + // Derivatives as computed by the cost function in local space. + std::vector<Matrix> local_jacobians; - // Derivatives as computed by finite differencing. - std::vector<Matrix> finite_difference_jacobians; + // Derivatives as computed by nuerical differentiation in local space. + std::vector<Matrix> numeric_jacobians; - // Infinity-norm of term_jacobians - finite_difference_jacobians. - double error_jacobians; + // Derivatives as computed by nuerical differentiation in local space. + std::vector<Matrix> local_numeric_jacobians; + + // Contains the maximum relative error found in the local Jacobians. + double maximum_relative_error; + + // If an error was detected, this will contain a detailed description of + // that error. + std::string error_log; }; - // Checks the Jacobian computed by a cost function. - // - // probe_point: The parameter values at which to probe. - // error_tolerance: A threshold for the infinity-norm difference - // between the Jacobians. If the Jacobians differ by more than - // this amount, then the probe fails. + // Call the cost function, compute alternative Jacobians using finite + // differencing and compare results. If local parameterizations are given, + // the Jacobians will be multiplied by the local parameterization Jacobians + // before performing the check, which effectively means that all errors along + // the null space of the local parameterization will be ignored. + // Returns false if the Jacobians don't match, the cost function return false, + // or if the cost function returns different residual when called with a + // Jacobian output argument vs. calling it without. Otherwise returns true. // - // term: The cost function to test. Not retained after this call returns. - // - // results: On return, the two Jacobians (and other information) - // will be stored here. May be NULL. + // parameters: The parameter values at which to probe. + // relative_precision: A threshold for the relative difference between the + // Jacobians. If the Jacobians differ by more than this amount, then the + // probe fails. + // results: On return, the Jacobians (and other information) will be stored + // here. May be NULL. // // Returns true if no problems are detected and the difference between the // Jacobians is less than error_tolerance. - static bool Probe(double const* const* probe_point, - double error_tolerance, - CostFunctionToProbe *term, - GradientCheckResults* results) { - CHECK_NOTNULL(probe_point); - CHECK_NOTNULL(term); - LOG(INFO) << "-------------------- Starting Probe() --------------------"; - - // We need a GradientCheckeresults, whether or not they supplied one. - internal::scoped_ptr<GradientCheckResults> owned_results; - if (results == NULL) { - owned_results.reset(new GradientCheckResults); - results = owned_results.get(); - } - - // Do a consistency check between the term and the template parameters. - CHECK_EQ(M, term->num_residuals()); - const int num_residuals = M; - const std::vector<int32>& block_sizes = term->parameter_block_sizes(); - const int num_blocks = block_sizes.size(); - - CHECK_LE(num_blocks, 5) << "Unable to test functions that take more " - << "than 5 parameter blocks"; - if (N0) { - CHECK_EQ(N0, block_sizes[0]); - CHECK_GE(num_blocks, 1); - } else { - CHECK_LT(num_blocks, 1); - } - if (N1) { - CHECK_EQ(N1, block_sizes[1]); - CHECK_GE(num_blocks, 2); - } else { - CHECK_LT(num_blocks, 2); - } - if (N2) { - CHECK_EQ(N2, block_sizes[2]); - CHECK_GE(num_blocks, 3); - } else { - CHECK_LT(num_blocks, 3); - } - if (N3) { - CHECK_EQ(N3, block_sizes[3]); - CHECK_GE(num_blocks, 4); - } else { - CHECK_LT(num_blocks, 4); - } - if (N4) { - CHECK_EQ(N4, block_sizes[4]); - CHECK_GE(num_blocks, 5); - } else { - CHECK_LT(num_blocks, 5); - } - - results->term_jacobians.clear(); - results->term_jacobians.resize(num_blocks); - results->finite_difference_jacobians.clear(); - results->finite_difference_jacobians.resize(num_blocks); - - internal::FixedArray<double*> term_jacobian_pointers(num_blocks); - internal::FixedArray<double*> - finite_difference_jacobian_pointers(num_blocks); - for (int i = 0; i < num_blocks; i++) { - results->term_jacobians[i].resize(num_residuals, block_sizes[i]); - term_jacobian_pointers[i] = results->term_jacobians[i].data(); - results->finite_difference_jacobians[i].resize( - num_residuals, block_sizes[i]); - finite_difference_jacobian_pointers[i] = - results->finite_difference_jacobians[i].data(); - } - results->cost.resize(num_residuals, 1); - - CHECK(term->Evaluate(probe_point, results->cost.data(), - term_jacobian_pointers.get())); - NumericDiffCostFunction<CostFunctionToProbe, CENTRAL, M, N0, N1, N2, N3, N4> - numeric_term(term, DO_NOT_TAKE_OWNERSHIP); - CHECK(numeric_term.Evaluate(probe_point, results->cost.data(), - finite_difference_jacobian_pointers.get())); - - results->error_jacobians = 0; - for (int i = 0; i < num_blocks; i++) { - Matrix jacobian_difference = results->term_jacobians[i] - - results->finite_difference_jacobians[i]; - results->error_jacobians = - std::max(results->error_jacobians, - jacobian_difference.lpNorm<Eigen::Infinity>()); - } - - LOG(INFO) << "========== term-computed derivatives =========="; - for (int i = 0; i < num_blocks; i++) { - LOG(INFO) << "term_computed block " << i; - LOG(INFO) << "\n" << results->term_jacobians[i]; - } - - LOG(INFO) << "========== finite-difference derivatives =========="; - for (int i = 0; i < num_blocks; i++) { - LOG(INFO) << "finite_difference block " << i; - LOG(INFO) << "\n" << results->finite_difference_jacobians[i]; - } - - LOG(INFO) << "========== difference =========="; - for (int i = 0; i < num_blocks; i++) { - LOG(INFO) << "difference block " << i; - LOG(INFO) << (results->term_jacobians[i] - - results->finite_difference_jacobians[i]); - } - - LOG(INFO) << "||difference|| = " << results->error_jacobians; - - return results->error_jacobians < error_tolerance; - } + bool Probe(double const* const* parameters, + double relative_precision, + ProbeResults* results) const; private: CERES_DISALLOW_IMPLICIT_CONSTRUCTORS(GradientChecker); + + std::vector<const LocalParameterization*> local_parameterizations_; + const CostFunction* function_; + internal::scoped_ptr<CostFunction> finite_diff_cost_function_; }; } // namespace ceres diff --git a/extern/ceres/include/ceres/internal/port.h b/extern/ceres/include/ceres/internal/port.h index e57049dde4b..f4dcaee7bd8 100644 --- a/extern/ceres/include/ceres/internal/port.h +++ b/extern/ceres/include/ceres/internal/port.h @@ -33,9 +33,8 @@ // This file needs to compile as c code. #ifdef __cplusplus - +#include <cstddef> #include "ceres/internal/config.h" - #if defined(CERES_TR1_MEMORY_HEADER) #include <tr1/memory> #else @@ -50,6 +49,25 @@ using std::tr1::shared_ptr; using std::shared_ptr; #endif +// We allocate some Eigen objects on the stack and other places they +// might not be aligned to 16-byte boundaries. If we have C++11, we +// can specify their alignment anyway, and thus can safely enable +// vectorization on those matrices; in C++99, we are out of luck. Figure out +// what case we're in and write macros that do the right thing. +#ifdef CERES_USE_CXX11 +namespace port_constants { +static constexpr size_t kMaxAlignBytes = + // Work around a GCC 4.8 bug + // (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56019) where + // std::max_align_t is misplaced. +#if defined (__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 8 + alignof(::max_align_t); +#else + alignof(std::max_align_t); +#endif +} // namespace port_constants +#endif + } // namespace ceres #endif // __cplusplus diff --git a/extern/ceres/include/ceres/iteration_callback.h b/extern/ceres/include/ceres/iteration_callback.h index 6bab00439c5..db5d0efe53a 100644 --- a/extern/ceres/include/ceres/iteration_callback.h +++ b/extern/ceres/include/ceres/iteration_callback.h @@ -69,7 +69,7 @@ struct CERES_EXPORT IterationSummary { // Step was numerically valid, i.e., all values are finite and the // step reduces the value of the linearized model. // - // Note: step_is_valid is false when iteration = 0. + // Note: step_is_valid is always true when iteration = 0. bool step_is_valid; // Step did not reduce the value of the objective function @@ -77,7 +77,7 @@ struct CERES_EXPORT IterationSummary { // acceptance criterion used by the non-monotonic trust region // algorithm. // - // Note: step_is_nonmonotonic is false when iteration = 0; + // Note: step_is_nonmonotonic is always false when iteration = 0; bool step_is_nonmonotonic; // Whether or not the minimizer accepted this step or not. If the @@ -89,7 +89,7 @@ struct CERES_EXPORT IterationSummary { // relative decrease is not sufficient, the algorithm may accept the // step and the step is declared successful. // - // Note: step_is_successful is false when iteration = 0. + // Note: step_is_successful is always true when iteration = 0. bool step_is_successful; // Value of the objective function. diff --git a/extern/ceres/include/ceres/jet.h b/extern/ceres/include/ceres/jet.h index a21fd7adb90..a104707298c 100644 --- a/extern/ceres/include/ceres/jet.h +++ b/extern/ceres/include/ceres/jet.h @@ -164,6 +164,7 @@ #include "Eigen/Core" #include "ceres/fpclassify.h" +#include "ceres/internal/port.h" namespace ceres { @@ -227,21 +228,23 @@ struct Jet { T a; // The infinitesimal part. - // - // Note the Eigen::DontAlign bit is needed here because this object - // gets allocated on the stack and as part of other arrays and - // structs. Forcing the right alignment there is the source of much - // pain and suffering. Even if that works, passing Jets around to - // functions by value has problems because the C++ ABI does not - // guarantee alignment for function arguments. - // - // Setting the DontAlign bit prevents Eigen from using SSE for the - // various operations on Jets. This is a small performance penalty - // since the AutoDiff code will still expose much of the code as - // statically sized loops to the compiler. But given the subtle - // issues that arise due to alignment, especially when dealing with - // multiple platforms, it seems to be a trade off worth making. + + // We allocate Jets on the stack and other places they + // might not be aligned to 16-byte boundaries. If we have C++11, we + // can specify their alignment anyway, and thus can safely enable + // vectorization on those matrices; in C++99, we are out of luck. Figure out + // what case we're in and do the right thing. +#ifndef CERES_USE_CXX11 + // fall back to safe version: Eigen::Matrix<T, N, 1, Eigen::DontAlign> v; +#else + static constexpr bool kShouldAlignMatrix = + 16 <= ::ceres::port_constants::kMaxAlignBytes; + static constexpr int kAlignHint = kShouldAlignMatrix ? + Eigen::AutoAlign : Eigen::DontAlign; + static constexpr size_t kAlignment = kShouldAlignMatrix ? 16 : 1; + alignas(kAlignment) Eigen::Matrix<T, N, 1, kAlignHint> v; +#endif }; // Unary + @@ -388,6 +391,8 @@ inline double atan (double x) { return std::atan(x); } inline double sinh (double x) { return std::sinh(x); } inline double cosh (double x) { return std::cosh(x); } inline double tanh (double x) { return std::tanh(x); } +inline double floor (double x) { return std::floor(x); } +inline double ceil (double x) { return std::ceil(x); } inline double pow (double x, double y) { return std::pow(x, y); } inline double atan2(double y, double x) { return std::atan2(y, x); } @@ -482,10 +487,51 @@ Jet<T, N> tanh(const Jet<T, N>& f) { return Jet<T, N>(tanh_a, tmp * f.v); } +// The floor function should be used with extreme care as this operation will +// result in a zero derivative which provides no information to the solver. +// +// floor(a + h) ~= floor(a) + 0 +template <typename T, int N> inline +Jet<T, N> floor(const Jet<T, N>& f) { + return Jet<T, N>(floor(f.a)); +} + +// The ceil function should be used with extreme care as this operation will +// result in a zero derivative which provides no information to the solver. +// +// ceil(a + h) ~= ceil(a) + 0 +template <typename T, int N> inline +Jet<T, N> ceil(const Jet<T, N>& f) { + return Jet<T, N>(ceil(f.a)); +} + // Bessel functions of the first kind with integer order equal to 0, 1, n. -inline double BesselJ0(double x) { return j0(x); } -inline double BesselJ1(double x) { return j1(x); } -inline double BesselJn(int n, double x) { return jn(n, x); } +// +// Microsoft has deprecated the j[0,1,n]() POSIX Bessel functions in favour of +// _j[0,1,n](). Where available on MSVC, use _j[0,1,n]() to avoid deprecated +// function errors in client code (the specific warning is suppressed when +// Ceres itself is built). +inline double BesselJ0(double x) { +#if defined(_MSC_VER) && defined(_j0) + return _j0(x); +#else + return j0(x); +#endif +} +inline double BesselJ1(double x) { +#if defined(_MSC_VER) && defined(_j1) + return _j1(x); +#else + return j1(x); +#endif +} +inline double BesselJn(int n, double x) { +#if defined(_MSC_VER) && defined(_jn) + return _jn(n, x); +#else + return jn(n, x); +#endif +} // For the formulae of the derivatives of the Bessel functions see the book: // Olver, Lozier, Boisvert, Clark, NIST Handbook of Mathematical Functions, @@ -743,7 +789,15 @@ template<typename T, int N> inline Jet<T, N> ei_pow (const Jet<T, N>& x, // strange compile errors. template <typename T, int N> inline std::ostream &operator<<(std::ostream &s, const Jet<T, N>& z) { - return s << "[" << z.a << " ; " << z.v.transpose() << "]"; + s << "[" << z.a << " ; "; + for (int i = 0; i < N; ++i) { + s << z.v[i]; + if (i != N - 1) { + s << ", "; + } + } + s << "]"; + return s; } } // namespace ceres @@ -757,6 +811,7 @@ struct NumTraits<ceres::Jet<T, N> > { typedef ceres::Jet<T, N> Real; typedef ceres::Jet<T, N> NonInteger; typedef ceres::Jet<T, N> Nested; + typedef ceres::Jet<T, N> Literal; static typename ceres::Jet<T, N> dummy_precision() { return ceres::Jet<T, N>(1e-12); @@ -777,6 +832,21 @@ struct NumTraits<ceres::Jet<T, N> > { HasFloatingPoint = 1, RequireInitialization = 1 }; + + template<bool Vectorized> + struct Div { + enum { +#if defined(EIGEN_VECTORIZE_AVX) + AVX = true, +#else + AVX = false, +#endif + + // Assuming that for Jets, division is as expensive as + // multiplication. + Cost = 3 + }; + }; }; } // namespace Eigen diff --git a/extern/ceres/include/ceres/local_parameterization.h b/extern/ceres/include/ceres/local_parameterization.h index 67633de309f..379fc684921 100644 --- a/extern/ceres/include/ceres/local_parameterization.h +++ b/extern/ceres/include/ceres/local_parameterization.h @@ -211,6 +211,28 @@ class CERES_EXPORT QuaternionParameterization : public LocalParameterization { virtual int LocalSize() const { return 3; } }; +// Implements the quaternion local parameterization for Eigen's representation +// of the quaternion. Eigen uses a different internal memory layout for the +// elements of the quaternion than what is commonly used. Specifically, Eigen +// stores the elements in memory as [x, y, z, w] where the real part is last +// whereas it is typically stored first. Note, when creating an Eigen quaternion +// through the constructor the elements are accepted in w, x, y, z order. Since +// Ceres operates on parameter blocks which are raw double pointers this +// difference is important and requires a different parameterization. +// +// Plus(x, delta) = [sin(|delta|) delta / |delta|, cos(|delta|)] * x +// with * being the quaternion multiplication operator. +class EigenQuaternionParameterization : public ceres::LocalParameterization { + public: + virtual ~EigenQuaternionParameterization() {} + virtual bool Plus(const double* x, + const double* delta, + double* x_plus_delta) const; + virtual bool ComputeJacobian(const double* x, + double* jacobian) const; + virtual int GlobalSize() const { return 4; } + virtual int LocalSize() const { return 3; } +}; // This provides a parameterization for homogeneous vectors which are commonly // used in Structure for Motion problems. One example where they are used is diff --git a/extern/ceres/include/ceres/numeric_diff_cost_function.h b/extern/ceres/include/ceres/numeric_diff_cost_function.h index fa96078df02..5dfaeab6241 100644 --- a/extern/ceres/include/ceres/numeric_diff_cost_function.h +++ b/extern/ceres/include/ceres/numeric_diff_cost_function.h @@ -206,29 +206,6 @@ class NumericDiffCostFunction } } - // Deprecated. New users should avoid using this constructor. Instead, use the - // constructor with NumericDiffOptions. - NumericDiffCostFunction(CostFunctor* functor, - Ownership ownership, - int num_residuals, - const double relative_step_size) - :functor_(functor), - ownership_(ownership), - options_() { - LOG(WARNING) << "This constructor is deprecated and will be removed in " - "a future version. Please use the NumericDiffOptions " - "constructor instead."; - - if (kNumResiduals == DYNAMIC) { - SizedCostFunction<kNumResiduals, - N0, N1, N2, N3, N4, - N5, N6, N7, N8, N9> - ::set_num_residuals(num_residuals); - } - - options_.relative_step_size = relative_step_size; - } - ~NumericDiffCostFunction() { if (ownership_ != TAKE_OWNERSHIP) { functor_.release(); diff --git a/extern/ceres/include/ceres/problem.h b/extern/ceres/include/ceres/problem.h index 409274c62c2..27ed4ef15da 100644 --- a/extern/ceres/include/ceres/problem.h +++ b/extern/ceres/include/ceres/problem.h @@ -309,6 +309,9 @@ class CERES_EXPORT Problem { // Allow the indicated parameter block to vary during optimization. void SetParameterBlockVariable(double* values); + // Returns true if a parameter block is set constant, and false otherwise. + bool IsParameterBlockConstant(double* values) const; + // Set the local parameterization for one of the parameter blocks. // The local_parameterization is owned by the Problem by default. It // is acceptable to set the same parameterization for multiple @@ -461,6 +464,10 @@ class CERES_EXPORT Problem { // parameter block has a local parameterization, then it contributes // "LocalSize" entries to the gradient vector (and the number of // columns in the jacobian). + // + // Note 3: This function cannot be called while the problem is being + // solved, for example it cannot be called from an IterationCallback + // at the end of an iteration during a solve. bool Evaluate(const EvaluateOptions& options, double* cost, std::vector<double>* residuals, diff --git a/extern/ceres/include/ceres/rotation.h b/extern/ceres/include/ceres/rotation.h index e9496d772e4..b6a06f772c4 100644 --- a/extern/ceres/include/ceres/rotation.h +++ b/extern/ceres/include/ceres/rotation.h @@ -48,7 +48,6 @@ #include <algorithm> #include <cmath> #include <limits> -#include "glog/logging.h" namespace ceres { @@ -418,7 +417,6 @@ template <typename T> inline void EulerAnglesToRotationMatrix(const T* euler, const int row_stride_parameter, T* R) { - CHECK_EQ(row_stride_parameter, 3); EulerAnglesToRotationMatrix(euler, RowMajorAdapter3x3(R)); } @@ -496,7 +494,6 @@ void QuaternionToRotation(const T q[4], QuaternionToScaledRotation(q, R); T normalizer = q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]; - CHECK_NE(normalizer, T(0)); normalizer = T(1) / normalizer; for (int i = 0; i < 3; ++i) { diff --git a/extern/ceres/include/ceres/solver.h b/extern/ceres/include/ceres/solver.h index 318cf48cb83..0d77d242dfe 100644 --- a/extern/ceres/include/ceres/solver.h +++ b/extern/ceres/include/ceres/solver.h @@ -134,7 +134,7 @@ class CERES_EXPORT Solver { trust_region_problem_dump_format_type = TEXTFILE; check_gradients = false; gradient_check_relative_precision = 1e-8; - numeric_derivative_relative_step_size = 1e-6; + gradient_check_numeric_derivative_relative_step_size = 1e-6; update_state_every_iteration = false; } @@ -701,12 +701,22 @@ class CERES_EXPORT Solver { // this number, then the jacobian for that cost term is dumped. double gradient_check_relative_precision; - // Relative shift used for taking numeric derivatives. For finite - // differencing, each dimension is evaluated at slightly shifted - // values; for the case of central difference, this is what gets - // evaluated: + // WARNING: This option only applies to the to the numeric + // differentiation used for checking the user provided derivatives + // when when Solver::Options::check_gradients is true. If you are + // using NumericDiffCostFunction and are interested in changing + // the step size for numeric differentiation in your cost + // function, please have a look at + // include/ceres/numeric_diff_options.h. // - // delta = numeric_derivative_relative_step_size; + // Relative shift used for taking numeric derivatives when + // Solver::Options::check_gradients is true. + // + // For finite differencing, each dimension is evaluated at + // slightly shifted values; for the case of central difference, + // this is what gets evaluated: + // + // delta = gradient_check_numeric_derivative_relative_step_size; // f_initial = f(x) // f_forward = f((1 + delta) * x) // f_backward = f((1 - delta) * x) @@ -723,7 +733,7 @@ class CERES_EXPORT Solver { // theory a good choice is sqrt(eps) * x, which for doubles means // about 1e-8 * x. However, I have found this number too // optimistic. This number should be exposed for users to change. - double numeric_derivative_relative_step_size; + double gradient_check_numeric_derivative_relative_step_size; // If true, the user's parameter blocks are updated at the end of // every Minimizer iteration, otherwise they are updated when the @@ -801,6 +811,13 @@ class CERES_EXPORT Solver { // Number of times inner iterations were performed. int num_inner_iteration_steps; + // Total number of iterations inside the line search algorithm + // across all invocations. We call these iterations "steps" to + // distinguish them from the outer iterations of the line search + // and trust region minimizer algorithms which call the line + // search algorithm as a subroutine. + int num_line_search_steps; + // All times reported below are wall times. // When the user calls Solve, before the actual optimization diff --git a/extern/ceres/include/ceres/version.h b/extern/ceres/include/ceres/version.h index 66505a515c9..2f1cc297a38 100644 --- a/extern/ceres/include/ceres/version.h +++ b/extern/ceres/include/ceres/version.h @@ -32,7 +32,7 @@ #define CERES_PUBLIC_VERSION_H_ #define CERES_VERSION_MAJOR 1 -#define CERES_VERSION_MINOR 11 +#define CERES_VERSION_MINOR 12 #define CERES_VERSION_REVISION 0 // Classic CPP stringifcation; the extra level of indirection allows the diff --git a/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc b/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc index 64b6ac00447..40977b74c67 100644 --- a/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc +++ b/extern/ceres/internal/ceres/compressed_row_jacobian_writer.cc @@ -46,6 +46,7 @@ namespace internal { using std::make_pair; using std::pair; using std::vector; +using std::adjacent_find; void CompressedRowJacobianWriter::PopulateJacobianRowAndColumnBlockVectors( const Program* program, CompressedRowSparseMatrix* jacobian) { @@ -140,12 +141,21 @@ SparseMatrix* CompressedRowJacobianWriter::CreateJacobian() const { // Sort the parameters by their position in the state vector. sort(parameter_indices.begin(), parameter_indices.end()); - CHECK(unique(parameter_indices.begin(), parameter_indices.end()) == - parameter_indices.end()) - << "Ceres internal error: " - << "Duplicate parameter blocks detected in a cost function. " - << "This should never happen. Please report this to " - << "the Ceres developers."; + if (adjacent_find(parameter_indices.begin(), parameter_indices.end()) != + parameter_indices.end()) { + std::string parameter_block_description; + for (int j = 0; j < num_parameter_blocks; ++j) { + ParameterBlock* parameter_block = residual_block->parameter_blocks()[j]; + parameter_block_description += + parameter_block->ToString() + "\n"; + } + LOG(FATAL) << "Ceres internal error: " + << "Duplicate parameter blocks detected in a cost function. " + << "This should never happen. Please report this to " + << "the Ceres developers.\n" + << "Residual Block: " << residual_block->ToString() << "\n" + << "Parameter Blocks: " << parameter_block_description; + } // Update the row indices. const int num_residuals = residual_block->NumResiduals(); diff --git a/extern/ceres/internal/ceres/covariance.cc b/extern/ceres/internal/ceres/covariance.cc index 690847945a9..cb280a36847 100644 --- a/extern/ceres/internal/ceres/covariance.cc +++ b/extern/ceres/internal/ceres/covariance.cc @@ -38,6 +38,7 @@ namespace ceres { +using std::make_pair; using std::pair; using std::vector; @@ -54,6 +55,12 @@ bool Covariance::Compute( return impl_->Compute(covariance_blocks, problem->problem_impl_.get()); } +bool Covariance::Compute( + const vector<const double*>& parameter_blocks, + Problem* problem) { + return impl_->Compute(parameter_blocks, problem->problem_impl_.get()); +} + bool Covariance::GetCovarianceBlock(const double* parameter_block1, const double* parameter_block2, double* covariance_block) const { @@ -73,4 +80,20 @@ bool Covariance::GetCovarianceBlockInTangentSpace( covariance_block); } +bool Covariance::GetCovarianceMatrix( + const vector<const double*>& parameter_blocks, + double* covariance_matrix) { + return impl_->GetCovarianceMatrixInTangentOrAmbientSpace(parameter_blocks, + true, // ambient + covariance_matrix); +} + +bool Covariance::GetCovarianceMatrixInTangentSpace( + const std::vector<const double *>& parameter_blocks, + double *covariance_matrix) { + return impl_->GetCovarianceMatrixInTangentOrAmbientSpace(parameter_blocks, + false, // tangent + covariance_matrix); +} + } // namespace ceres diff --git a/extern/ceres/internal/ceres/covariance_impl.cc b/extern/ceres/internal/ceres/covariance_impl.cc index 3e8302bed55..d698f88fa9b 100644 --- a/extern/ceres/internal/ceres/covariance_impl.cc +++ b/extern/ceres/internal/ceres/covariance_impl.cc @@ -36,6 +36,8 @@ #include <algorithm> #include <cstdlib> +#include <numeric> +#include <sstream> #include <utility> #include <vector> @@ -43,6 +45,7 @@ #include "Eigen/SparseQR" #include "Eigen/SVD" +#include "ceres/collections_port.h" #include "ceres/compressed_col_sparse_matrix_utils.h" #include "ceres/compressed_row_sparse_matrix.h" #include "ceres/covariance.h" @@ -51,6 +54,7 @@ #include "ceres/map_util.h" #include "ceres/parameter_block.h" #include "ceres/problem_impl.h" +#include "ceres/residual_block.h" #include "ceres/suitesparse.h" #include "ceres/wall_time.h" #include "glog/logging.h" @@ -61,6 +65,7 @@ namespace internal { using std::make_pair; using std::map; using std::pair; +using std::sort; using std::swap; using std::vector; @@ -86,8 +91,38 @@ CovarianceImpl::CovarianceImpl(const Covariance::Options& options) CovarianceImpl::~CovarianceImpl() { } +template <typename T> void CheckForDuplicates(vector<T> blocks) { + sort(blocks.begin(), blocks.end()); + typename vector<T>::iterator it = + std::adjacent_find(blocks.begin(), blocks.end()); + if (it != blocks.end()) { + // In case there are duplicates, we search for their location. + map<T, vector<int> > blocks_map; + for (int i = 0; i < blocks.size(); ++i) { + blocks_map[blocks[i]].push_back(i); + } + + std::ostringstream duplicates; + while (it != blocks.end()) { + duplicates << "("; + for (int i = 0; i < blocks_map[*it].size() - 1; ++i) { + duplicates << blocks_map[*it][i] << ", "; + } + duplicates << blocks_map[*it].back() << ")"; + it = std::adjacent_find(it + 1, blocks.end()); + if (it < blocks.end()) { + duplicates << " and "; + } + } + + LOG(FATAL) << "Covariance::Compute called with duplicate blocks at " + << "indices " << duplicates.str(); + } +} + bool CovarianceImpl::Compute(const CovarianceBlocks& covariance_blocks, ProblemImpl* problem) { + CheckForDuplicates<pair<const double*, const double*> >(covariance_blocks); problem_ = problem; parameter_block_to_row_index_.clear(); covariance_matrix_.reset(NULL); @@ -97,6 +132,20 @@ bool CovarianceImpl::Compute(const CovarianceBlocks& covariance_blocks, return is_valid_; } +bool CovarianceImpl::Compute(const vector<const double*>& parameter_blocks, + ProblemImpl* problem) { + CheckForDuplicates<const double*>(parameter_blocks); + CovarianceBlocks covariance_blocks; + for (int i = 0; i < parameter_blocks.size(); ++i) { + for (int j = i; j < parameter_blocks.size(); ++j) { + covariance_blocks.push_back(make_pair(parameter_blocks[i], + parameter_blocks[j])); + } + } + + return Compute(covariance_blocks, problem); +} + bool CovarianceImpl::GetCovarianceBlockInTangentOrAmbientSpace( const double* original_parameter_block1, const double* original_parameter_block2, @@ -120,9 +169,17 @@ bool CovarianceImpl::GetCovarianceBlockInTangentOrAmbientSpace( ParameterBlock* block2 = FindOrDie(parameter_map, const_cast<double*>(original_parameter_block2)); + const int block1_size = block1->Size(); const int block2_size = block2->Size(); - MatrixRef(covariance_block, block1_size, block2_size).setZero(); + const int block1_local_size = block1->LocalSize(); + const int block2_local_size = block2->LocalSize(); + if (!lift_covariance_to_ambient_space) { + MatrixRef(covariance_block, block1_local_size, block2_local_size) + .setZero(); + } else { + MatrixRef(covariance_block, block1_size, block2_size).setZero(); + } return true; } @@ -240,6 +297,94 @@ bool CovarianceImpl::GetCovarianceBlockInTangentOrAmbientSpace( return true; } +bool CovarianceImpl::GetCovarianceMatrixInTangentOrAmbientSpace( + const vector<const double*>& parameters, + bool lift_covariance_to_ambient_space, + double* covariance_matrix) const { + CHECK(is_computed_) + << "Covariance::GetCovarianceMatrix called before Covariance::Compute"; + CHECK(is_valid_) + << "Covariance::GetCovarianceMatrix called when Covariance::Compute " + << "returned false."; + + const ProblemImpl::ParameterMap& parameter_map = problem_->parameter_map(); + // For OpenMP compatibility we need to define these vectors in advance + const int num_parameters = parameters.size(); + vector<int> parameter_sizes; + vector<int> cum_parameter_size; + parameter_sizes.reserve(num_parameters); + cum_parameter_size.resize(num_parameters + 1); + cum_parameter_size[0] = 0; + for (int i = 0; i < num_parameters; ++i) { + ParameterBlock* block = + FindOrDie(parameter_map, const_cast<double*>(parameters[i])); + if (lift_covariance_to_ambient_space) { + parameter_sizes.push_back(block->Size()); + } else { + parameter_sizes.push_back(block->LocalSize()); + } + } + std::partial_sum(parameter_sizes.begin(), parameter_sizes.end(), + cum_parameter_size.begin() + 1); + const int max_covariance_block_size = + *std::max_element(parameter_sizes.begin(), parameter_sizes.end()); + const int covariance_size = cum_parameter_size.back(); + + // Assemble the blocks in the covariance matrix. + MatrixRef covariance(covariance_matrix, covariance_size, covariance_size); + const int num_threads = options_.num_threads; + scoped_array<double> workspace( + new double[num_threads * max_covariance_block_size * + max_covariance_block_size]); + + bool success = true; + +// The collapse() directive is only supported in OpenMP 3.0 and higher. OpenMP +// 3.0 was released in May 2008 (hence the version number). +#if _OPENMP >= 200805 +# pragma omp parallel for num_threads(num_threads) schedule(dynamic) collapse(2) +#else +# pragma omp parallel for num_threads(num_threads) schedule(dynamic) +#endif + for (int i = 0; i < num_parameters; ++i) { + for (int j = 0; j < num_parameters; ++j) { + // The second loop can't start from j = i for compatibility with OpenMP + // collapse command. The conditional serves as a workaround + if (j >= i) { + int covariance_row_idx = cum_parameter_size[i]; + int covariance_col_idx = cum_parameter_size[j]; + int size_i = parameter_sizes[i]; + int size_j = parameter_sizes[j]; +#ifdef CERES_USE_OPENMP + int thread_id = omp_get_thread_num(); +#else + int thread_id = 0; +#endif + double* covariance_block = + workspace.get() + + thread_id * max_covariance_block_size * max_covariance_block_size; + if (!GetCovarianceBlockInTangentOrAmbientSpace( + parameters[i], parameters[j], lift_covariance_to_ambient_space, + covariance_block)) { + success = false; + } + + covariance.block(covariance_row_idx, covariance_col_idx, + size_i, size_j) = + MatrixRef(covariance_block, size_i, size_j); + + if (i != j) { + covariance.block(covariance_col_idx, covariance_row_idx, + size_j, size_i) = + MatrixRef(covariance_block, size_i, size_j).transpose(); + + } + } + } + } + return success; +} + // Determine the sparsity pattern of the covariance matrix based on // the block pairs requested by the user. bool CovarianceImpl::ComputeCovarianceSparsity( @@ -252,18 +397,28 @@ bool CovarianceImpl::ComputeCovarianceSparsity( vector<double*> all_parameter_blocks; problem->GetParameterBlocks(&all_parameter_blocks); const ProblemImpl::ParameterMap& parameter_map = problem->parameter_map(); + HashSet<ParameterBlock*> parameter_blocks_in_use; + vector<ResidualBlock*> residual_blocks; + problem->GetResidualBlocks(&residual_blocks); + + for (int i = 0; i < residual_blocks.size(); ++i) { + ResidualBlock* residual_block = residual_blocks[i]; + parameter_blocks_in_use.insert(residual_block->parameter_blocks(), + residual_block->parameter_blocks() + + residual_block->NumParameterBlocks()); + } + constant_parameter_blocks_.clear(); vector<double*>& active_parameter_blocks = evaluate_options_.parameter_blocks; active_parameter_blocks.clear(); for (int i = 0; i < all_parameter_blocks.size(); ++i) { double* parameter_block = all_parameter_blocks[i]; - ParameterBlock* block = FindOrDie(parameter_map, parameter_block); - if (block->IsConstant()) { - constant_parameter_blocks_.insert(parameter_block); - } else { + if (!block->IsConstant() && (parameter_blocks_in_use.count(block) > 0)) { active_parameter_blocks.push_back(parameter_block); + } else { + constant_parameter_blocks_.insert(parameter_block); } } @@ -386,8 +541,8 @@ bool CovarianceImpl::ComputeCovarianceValues() { switch (options_.algorithm_type) { case DENSE_SVD: return ComputeCovarianceValuesUsingDenseSVD(); -#ifndef CERES_NO_SUITESPARSE case SUITE_SPARSE_QR: +#ifndef CERES_NO_SUITESPARSE return ComputeCovarianceValuesUsingSuiteSparseQR(); #else LOG(ERROR) << "SuiteSparse is required to use the " @@ -624,7 +779,10 @@ bool CovarianceImpl::ComputeCovarianceValuesUsingDenseSVD() { if (automatic_truncation) { break; } else { - LOG(ERROR) << "Cholesky factorization of J'J is not reliable. " + LOG(ERROR) << "Error: Covariance matrix is near rank deficient " + << "and the user did not specify a non-zero" + << "Covariance::Options::null_space_rank " + << "to enable the computation of a Pseudo-Inverse. " << "Reciprocal condition number: " << singular_value_ratio * singular_value_ratio << " " << "min_reciprocal_condition_number: " diff --git a/extern/ceres/internal/ceres/covariance_impl.h b/extern/ceres/internal/ceres/covariance_impl.h index eb0cd040666..a3f0761f57c 100644 --- a/extern/ceres/internal/ceres/covariance_impl.h +++ b/extern/ceres/internal/ceres/covariance_impl.h @@ -55,12 +55,21 @@ class CovarianceImpl { const double*> >& covariance_blocks, ProblemImpl* problem); + bool Compute( + const std::vector<const double*>& parameter_blocks, + ProblemImpl* problem); + bool GetCovarianceBlockInTangentOrAmbientSpace( const double* parameter_block1, const double* parameter_block2, bool lift_covariance_to_ambient_space, double* covariance_block) const; + bool GetCovarianceMatrixInTangentOrAmbientSpace( + const std::vector<const double*>& parameters, + bool lift_covariance_to_ambient_space, + double *covariance_matrix) const; + bool ComputeCovarianceSparsity( const std::vector<std::pair<const double*, const double*> >& covariance_blocks, diff --git a/extern/ceres/internal/ceres/gradient_checker.cc b/extern/ceres/internal/ceres/gradient_checker.cc new file mode 100644 index 00000000000..c16c141db09 --- /dev/null +++ b/extern/ceres/internal/ceres/gradient_checker.cc @@ -0,0 +1,276 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2016 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wjr@google.com (William Rucklidge), +// keir@google.com (Keir Mierle), +// dgossow@google.com (David Gossow) + +#include "ceres/gradient_checker.h" + +#include <algorithm> +#include <cmath> +#include <numeric> +#include <string> +#include <vector> + +#include "ceres/is_close.h" +#include "ceres/stringprintf.h" +#include "ceres/types.h" + +namespace ceres { + +using internal::IsClose; +using internal::StringAppendF; +using internal::StringPrintf; +using std::string; +using std::vector; + +namespace { +// Evaluate the cost function and transform the returned Jacobians to +// the local space of the respective local parameterizations. +bool EvaluateCostFunction( + const ceres::CostFunction* function, + double const* const * parameters, + const std::vector<const ceres::LocalParameterization*>& + local_parameterizations, + Vector* residuals, + std::vector<Matrix>* jacobians, + std::vector<Matrix>* local_jacobians) { + CHECK_NOTNULL(residuals); + CHECK_NOTNULL(jacobians); + CHECK_NOTNULL(local_jacobians); + + const vector<int32>& block_sizes = function->parameter_block_sizes(); + const int num_parameter_blocks = block_sizes.size(); + + // Allocate Jacobian matrices in local space. + local_jacobians->resize(num_parameter_blocks); + vector<double*> local_jacobian_data(num_parameter_blocks); + for (int i = 0; i < num_parameter_blocks; ++i) { + int block_size = block_sizes.at(i); + if (local_parameterizations.at(i) != NULL) { + block_size = local_parameterizations.at(i)->LocalSize(); + } + local_jacobians->at(i).resize(function->num_residuals(), block_size); + local_jacobians->at(i).setZero(); + local_jacobian_data.at(i) = local_jacobians->at(i).data(); + } + + // Allocate Jacobian matrices in global space. + jacobians->resize(num_parameter_blocks); + vector<double*> jacobian_data(num_parameter_blocks); + for (int i = 0; i < num_parameter_blocks; ++i) { + jacobians->at(i).resize(function->num_residuals(), block_sizes.at(i)); + jacobians->at(i).setZero(); + jacobian_data.at(i) = jacobians->at(i).data(); + } + + // Compute residuals & jacobians. + CHECK_NE(0, function->num_residuals()); + residuals->resize(function->num_residuals()); + residuals->setZero(); + if (!function->Evaluate(parameters, residuals->data(), + jacobian_data.data())) { + return false; + } + + // Convert Jacobians from global to local space. + for (size_t i = 0; i < local_jacobians->size(); ++i) { + if (local_parameterizations.at(i) == NULL) { + local_jacobians->at(i) = jacobians->at(i); + } else { + int global_size = local_parameterizations.at(i)->GlobalSize(); + int local_size = local_parameterizations.at(i)->LocalSize(); + CHECK_EQ(jacobians->at(i).cols(), global_size); + Matrix global_J_local(global_size, local_size); + local_parameterizations.at(i)->ComputeJacobian( + parameters[i], global_J_local.data()); + local_jacobians->at(i) = jacobians->at(i) * global_J_local; + } + } + return true; +} +} // namespace + +GradientChecker::GradientChecker( + const CostFunction* function, + const vector<const LocalParameterization*>* local_parameterizations, + const NumericDiffOptions& options) : + function_(function) { + CHECK_NOTNULL(function); + if (local_parameterizations != NULL) { + local_parameterizations_ = *local_parameterizations; + } else { + local_parameterizations_.resize(function->parameter_block_sizes().size(), + NULL); + } + DynamicNumericDiffCostFunction<CostFunction, CENTRAL>* + finite_diff_cost_function = + new DynamicNumericDiffCostFunction<CostFunction, CENTRAL>( + function, DO_NOT_TAKE_OWNERSHIP, options); + finite_diff_cost_function_.reset(finite_diff_cost_function); + + const vector<int32>& parameter_block_sizes = + function->parameter_block_sizes(); + const int num_parameter_blocks = parameter_block_sizes.size(); + for (int i = 0; i < num_parameter_blocks; ++i) { + finite_diff_cost_function->AddParameterBlock(parameter_block_sizes[i]); + } + finite_diff_cost_function->SetNumResiduals(function->num_residuals()); +} + +bool GradientChecker::Probe(double const* const * parameters, + double relative_precision, + ProbeResults* results_param) const { + int num_residuals = function_->num_residuals(); + + // Make sure that we have a place to store results, no matter if the user has + // provided an output argument. + ProbeResults* results; + ProbeResults results_local; + if (results_param != NULL) { + results = results_param; + results->residuals.resize(0); + results->jacobians.clear(); + results->numeric_jacobians.clear(); + results->local_jacobians.clear(); + results->local_numeric_jacobians.clear(); + results->error_log.clear(); + } else { + results = &results_local; + } + results->maximum_relative_error = 0.0; + results->return_value = true; + + // Evaluate the derivative using the user supplied code. + vector<Matrix>& jacobians = results->jacobians; + vector<Matrix>& local_jacobians = results->local_jacobians; + if (!EvaluateCostFunction(function_, parameters, local_parameterizations_, + &results->residuals, &jacobians, &local_jacobians)) { + results->error_log = "Function evaluation with Jacobians failed."; + results->return_value = false; + } + + // Evaluate the derivative using numeric derivatives. + vector<Matrix>& numeric_jacobians = results->numeric_jacobians; + vector<Matrix>& local_numeric_jacobians = results->local_numeric_jacobians; + Vector finite_diff_residuals; + if (!EvaluateCostFunction(finite_diff_cost_function_.get(), parameters, + local_parameterizations_, &finite_diff_residuals, + &numeric_jacobians, &local_numeric_jacobians)) { + results->error_log += "\nFunction evaluation with numerical " + "differentiation failed."; + results->return_value = false; + } + + if (!results->return_value) { + return false; + } + + for (int i = 0; i < num_residuals; ++i) { + if (!IsClose( + results->residuals[i], + finite_diff_residuals[i], + relative_precision, + NULL, + NULL)) { + results->error_log = "Function evaluation with and without Jacobians " + "resulted in different residuals."; + LOG(INFO) << results->residuals.transpose(); + LOG(INFO) << finite_diff_residuals.transpose(); + return false; + } + } + + // See if any elements have relative error larger than the threshold. + int num_bad_jacobian_components = 0; + double& worst_relative_error = results->maximum_relative_error; + worst_relative_error = 0; + + // Accumulate the error message for all the jacobians, since it won't get + // output if there are no bad jacobian components. + string error_log; + for (int k = 0; k < function_->parameter_block_sizes().size(); k++) { + StringAppendF(&error_log, + "========== " + "Jacobian for " "block %d: (%ld by %ld)) " + "==========\n", + k, + static_cast<long>(local_jacobians[k].rows()), + static_cast<long>(local_jacobians[k].cols())); + // The funny spacing creates appropriately aligned column headers. + error_log += + " block row col user dx/dy num diff dx/dy " + "abs error relative error parameter residual\n"; + + for (int i = 0; i < local_jacobians[k].rows(); i++) { + for (int j = 0; j < local_jacobians[k].cols(); j++) { + double term_jacobian = local_jacobians[k](i, j); + double finite_jacobian = local_numeric_jacobians[k](i, j); + double relative_error, absolute_error; + bool bad_jacobian_entry = + !IsClose(term_jacobian, + finite_jacobian, + relative_precision, + &relative_error, + &absolute_error); + worst_relative_error = std::max(worst_relative_error, relative_error); + + StringAppendF(&error_log, + "%6d %4d %4d %17g %17g %17g %17g %17g %17g", + k, i, j, + term_jacobian, finite_jacobian, + absolute_error, relative_error, + parameters[k][j], + results->residuals[i]); + + if (bad_jacobian_entry) { + num_bad_jacobian_components++; + StringAppendF( + &error_log, + " ------ (%d,%d,%d) Relative error worse than %g", + k, i, j, relative_precision); + } + error_log += "\n"; + } + } + } + + // Since there were some bad errors, dump comprehensive debug info. + if (num_bad_jacobian_components) { + string header = StringPrintf("\nDetected %d bad Jacobian component(s). " + "Worst relative error was %g.\n", + num_bad_jacobian_components, + worst_relative_error); + results->error_log = header + "\n" + error_log; + return false; + } + return true; +} + +} // namespace ceres diff --git a/extern/ceres/internal/ceres/gradient_checking_cost_function.cc b/extern/ceres/internal/ceres/gradient_checking_cost_function.cc index 580fd260e15..f2c73367891 100644 --- a/extern/ceres/internal/ceres/gradient_checking_cost_function.cc +++ b/extern/ceres/internal/ceres/gradient_checking_cost_function.cc @@ -26,7 +26,8 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -// Author: keir@google.com (Keir Mierle) +// Authors: keir@google.com (Keir Mierle), +// dgossow@google.com (David Gossow) #include "ceres/gradient_checking_cost_function.h" @@ -36,7 +37,7 @@ #include <string> #include <vector> -#include "ceres/cost_function.h" +#include "ceres/gradient_checker.h" #include "ceres/internal/eigen.h" #include "ceres/internal/scoped_ptr.h" #include "ceres/parameter_block.h" @@ -59,55 +60,25 @@ using std::vector; namespace { -// True if x and y have an absolute relative difference less than -// relative_precision and false otherwise. Stores the relative and absolute -// difference in relative/absolute_error if non-NULL. -bool IsClose(double x, double y, double relative_precision, - double *relative_error, - double *absolute_error) { - double local_absolute_error; - double local_relative_error; - if (!absolute_error) { - absolute_error = &local_absolute_error; - } - if (!relative_error) { - relative_error = &local_relative_error; - } - *absolute_error = abs(x - y); - *relative_error = *absolute_error / max(abs(x), abs(y)); - if (x == 0 || y == 0) { - // If x or y is exactly zero, then relative difference doesn't have any - // meaning. Take the absolute difference instead. - *relative_error = *absolute_error; - } - return abs(*relative_error) < abs(relative_precision); -} - class GradientCheckingCostFunction : public CostFunction { public: - GradientCheckingCostFunction(const CostFunction* function, - const NumericDiffOptions& options, - double relative_precision, - const string& extra_info) + GradientCheckingCostFunction( + const CostFunction* function, + const std::vector<const LocalParameterization*>* local_parameterizations, + const NumericDiffOptions& options, + double relative_precision, + const string& extra_info, + GradientCheckingIterationCallback* callback) : function_(function), + gradient_checker_(function, local_parameterizations, options), relative_precision_(relative_precision), - extra_info_(extra_info) { - DynamicNumericDiffCostFunction<CostFunction, CENTRAL>* - finite_diff_cost_function = - new DynamicNumericDiffCostFunction<CostFunction, CENTRAL>( - function, - DO_NOT_TAKE_OWNERSHIP, - options); - + extra_info_(extra_info), + callback_(callback) { + CHECK_NOTNULL(callback_); const vector<int32>& parameter_block_sizes = function->parameter_block_sizes(); - for (int i = 0; i < parameter_block_sizes.size(); ++i) { - finite_diff_cost_function->AddParameterBlock(parameter_block_sizes[i]); - } *mutable_parameter_block_sizes() = parameter_block_sizes; set_num_residuals(function->num_residuals()); - finite_diff_cost_function->SetNumResiduals(num_residuals()); - finite_diff_cost_function_.reset(finite_diff_cost_function); } virtual ~GradientCheckingCostFunction() { } @@ -120,133 +91,92 @@ class GradientCheckingCostFunction : public CostFunction { return function_->Evaluate(parameters, residuals, NULL); } - int num_residuals = function_->num_residuals(); + GradientChecker::ProbeResults results; + bool okay = gradient_checker_.Probe(parameters, + relative_precision_, + &results); - // Make space for the jacobians of the two methods. - const vector<int32>& block_sizes = function_->parameter_block_sizes(); - vector<Matrix> term_jacobians(block_sizes.size()); - vector<Matrix> finite_difference_jacobians(block_sizes.size()); - vector<double*> term_jacobian_pointers(block_sizes.size()); - vector<double*> finite_difference_jacobian_pointers(block_sizes.size()); - for (int i = 0; i < block_sizes.size(); i++) { - term_jacobians[i].resize(num_residuals, block_sizes[i]); - term_jacobian_pointers[i] = term_jacobians[i].data(); - finite_difference_jacobians[i].resize(num_residuals, block_sizes[i]); - finite_difference_jacobian_pointers[i] = - finite_difference_jacobians[i].data(); - } - - // Evaluate the derivative using the user supplied code. - if (!function_->Evaluate(parameters, - residuals, - &term_jacobian_pointers[0])) { - LOG(WARNING) << "Function evaluation failed."; + // If the cost function returned false, there's nothing we can say about + // the gradients. + if (results.return_value == false) { return false; } - // Evaluate the derivative using numeric derivatives. - finite_diff_cost_function_->Evaluate( - parameters, - residuals, - &finite_difference_jacobian_pointers[0]); + // Copy the residuals. + const int num_residuals = function_->num_residuals(); + MatrixRef(residuals, num_residuals, 1) = results.residuals; - // See if any elements have relative error larger than the threshold. - int num_bad_jacobian_components = 0; - double worst_relative_error = 0; - - // Accumulate the error message for all the jacobians, since it won't get - // output if there are no bad jacobian components. - string m; + // Copy the original jacobian blocks into the jacobians array. + const vector<int32>& block_sizes = function_->parameter_block_sizes(); for (int k = 0; k < block_sizes.size(); k++) { - // Copy the original jacobian blocks into the jacobians array. if (jacobians[k] != NULL) { MatrixRef(jacobians[k], - term_jacobians[k].rows(), - term_jacobians[k].cols()) = term_jacobians[k]; - } - - StringAppendF(&m, - "========== " - "Jacobian for " "block %d: (%ld by %ld)) " - "==========\n", - k, - static_cast<long>(term_jacobians[k].rows()), - static_cast<long>(term_jacobians[k].cols())); - // The funny spacing creates appropriately aligned column headers. - m += " block row col user dx/dy num diff dx/dy " - "abs error relative error parameter residual\n"; - - for (int i = 0; i < term_jacobians[k].rows(); i++) { - for (int j = 0; j < term_jacobians[k].cols(); j++) { - double term_jacobian = term_jacobians[k](i, j); - double finite_jacobian = finite_difference_jacobians[k](i, j); - double relative_error, absolute_error; - bool bad_jacobian_entry = - !IsClose(term_jacobian, - finite_jacobian, - relative_precision_, - &relative_error, - &absolute_error); - worst_relative_error = max(worst_relative_error, relative_error); - - StringAppendF(&m, "%6d %4d %4d %17g %17g %17g %17g %17g %17g", - k, i, j, - term_jacobian, finite_jacobian, - absolute_error, relative_error, - parameters[k][j], - residuals[i]); - - if (bad_jacobian_entry) { - num_bad_jacobian_components++; - StringAppendF( - &m, " ------ (%d,%d,%d) Relative error worse than %g", - k, i, j, relative_precision_); - } - m += "\n"; - } + results.jacobians[k].rows(), + results.jacobians[k].cols()) = results.jacobians[k]; } } - // Since there were some bad errors, dump comprehensive debug info. - if (num_bad_jacobian_components) { - string header = StringPrintf("Detected %d bad jacobian component(s). " - "Worst relative error was %g.\n", - num_bad_jacobian_components, - worst_relative_error); - if (!extra_info_.empty()) { - header += "Extra info for this residual: " + extra_info_ + "\n"; - } - LOG(WARNING) << "\n" << header << m; + if (!okay) { + std::string error_log = "Gradient Error detected!\nExtra info for " + "this residual: " + extra_info_ + "\n" + results.error_log; + callback_->SetGradientErrorDetected(error_log); } return true; } private: const CostFunction* function_; - internal::scoped_ptr<CostFunction> finite_diff_cost_function_; + GradientChecker gradient_checker_; double relative_precision_; string extra_info_; + GradientCheckingIterationCallback* callback_; }; } // namespace -CostFunction *CreateGradientCheckingCostFunction( - const CostFunction *cost_function, +GradientCheckingIterationCallback::GradientCheckingIterationCallback() + : gradient_error_detected_(false) { +} + +CallbackReturnType GradientCheckingIterationCallback::operator()( + const IterationSummary& summary) { + if (gradient_error_detected_) { + LOG(ERROR)<< "Gradient error detected. Terminating solver."; + return SOLVER_ABORT; + } + return SOLVER_CONTINUE; +} +void GradientCheckingIterationCallback::SetGradientErrorDetected( + std::string& error_log) { + mutex_.Lock(); + gradient_error_detected_ = true; + error_log_ += "\n" + error_log; + mutex_.Unlock(); +} + +CostFunction* CreateGradientCheckingCostFunction( + const CostFunction* cost_function, + const std::vector<const LocalParameterization*>* local_parameterizations, double relative_step_size, double relative_precision, - const string& extra_info) { + const std::string& extra_info, + GradientCheckingIterationCallback* callback) { NumericDiffOptions numeric_diff_options; numeric_diff_options.relative_step_size = relative_step_size; return new GradientCheckingCostFunction(cost_function, + local_parameterizations, numeric_diff_options, - relative_precision, - extra_info); + relative_precision, extra_info, + callback); } -ProblemImpl* CreateGradientCheckingProblemImpl(ProblemImpl* problem_impl, - double relative_step_size, - double relative_precision) { +ProblemImpl* CreateGradientCheckingProblemImpl( + ProblemImpl* problem_impl, + double relative_step_size, + double relative_precision, + GradientCheckingIterationCallback* callback) { + CHECK_NOTNULL(callback); // We create new CostFunctions by wrapping the original CostFunction // in a gradient checking CostFunction. So its okay for the // ProblemImpl to take ownership of it and destroy it. The @@ -260,6 +190,9 @@ ProblemImpl* CreateGradientCheckingProblemImpl(ProblemImpl* problem_impl, gradient_checking_problem_options.local_parameterization_ownership = DO_NOT_TAKE_OWNERSHIP; + NumericDiffOptions numeric_diff_options; + numeric_diff_options.relative_step_size = relative_step_size; + ProblemImpl* gradient_checking_problem_impl = new ProblemImpl( gradient_checking_problem_options); @@ -294,19 +227,26 @@ ProblemImpl* CreateGradientCheckingProblemImpl(ProblemImpl* problem_impl, string extra_info = StringPrintf( "Residual block id %d; depends on parameters [", i); vector<double*> parameter_blocks; + vector<const LocalParameterization*> local_parameterizations; + parameter_blocks.reserve(residual_block->NumParameterBlocks()); + local_parameterizations.reserve(residual_block->NumParameterBlocks()); for (int j = 0; j < residual_block->NumParameterBlocks(); ++j) { ParameterBlock* parameter_block = residual_block->parameter_blocks()[j]; parameter_blocks.push_back(parameter_block->mutable_user_state()); StringAppendF(&extra_info, "%p", parameter_block->mutable_user_state()); extra_info += (j < residual_block->NumParameterBlocks() - 1) ? ", " : "]"; + local_parameterizations.push_back(problem_impl->GetParameterization( + parameter_block->mutable_user_state())); } // Wrap the original CostFunction in a GradientCheckingCostFunction. CostFunction* gradient_checking_cost_function = - CreateGradientCheckingCostFunction(residual_block->cost_function(), - relative_step_size, - relative_precision, - extra_info); + new GradientCheckingCostFunction(residual_block->cost_function(), + &local_parameterizations, + numeric_diff_options, + relative_precision, + extra_info, + callback); // The const_cast is necessary because // ProblemImpl::AddResidualBlock can potentially take ownership of diff --git a/extern/ceres/internal/ceres/gradient_checking_cost_function.h b/extern/ceres/internal/ceres/gradient_checking_cost_function.h index cf92cb72bc5..497f8e2a594 100644 --- a/extern/ceres/internal/ceres/gradient_checking_cost_function.h +++ b/extern/ceres/internal/ceres/gradient_checking_cost_function.h @@ -26,7 +26,8 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // -// Author: keir@google.com (Keir Mierle) +// Authors: keir@google.com (Keir Mierle), +// dgossow@google.com (David Gossow) #ifndef CERES_INTERNAL_GRADIENT_CHECKING_COST_FUNCTION_H_ #define CERES_INTERNAL_GRADIENT_CHECKING_COST_FUNCTION_H_ @@ -34,50 +35,76 @@ #include <string> #include "ceres/cost_function.h" +#include "ceres/iteration_callback.h" +#include "ceres/local_parameterization.h" +#include "ceres/mutex.h" namespace ceres { namespace internal { class ProblemImpl; -// Creates a CostFunction that checks the jacobians that cost_function computes -// with finite differences. Bad results are logged; required precision is -// controlled by relative_precision and the numeric differentiation step size is -// controlled with relative_step_size. See solver.h for a better explanation of -// relative_step_size. Caller owns result. -// -// The condition enforced is that -// -// (J_actual(i, j) - J_numeric(i, j)) -// ------------------------------------ < relative_precision -// max(J_actual(i, j), J_numeric(i, j)) -// -// where J_actual(i, j) is the jacobian as computed by the supplied cost -// function (by the user) and J_numeric is the jacobian as computed by finite -// differences. -// -// Note: This is quite inefficient and is intended only for debugging. +// Callback that collects information about gradient checking errors, and +// will abort the solve as soon as an error occurs. +class GradientCheckingIterationCallback : public IterationCallback { + public: + GradientCheckingIterationCallback(); + + // Will return SOLVER_CONTINUE until a gradient error has been detected, + // then return SOLVER_ABORT. + virtual CallbackReturnType operator()(const IterationSummary& summary); + + // Notify this that a gradient error has occurred (thread safe). + void SetGradientErrorDetected(std::string& error_log); + + // Retrieve error status (not thread safe). + bool gradient_error_detected() const { return gradient_error_detected_; } + const std::string& error_log() const { return error_log_; } + private: + bool gradient_error_detected_; + std::string error_log_; + // Mutex protecting member variables. + ceres::internal::Mutex mutex_; +}; + +// Creates a CostFunction that checks the Jacobians that cost_function computes +// with finite differences. This API is only intended for unit tests that intend +// to check the functionality of the GradientCheckingCostFunction +// implementation directly. CostFunction* CreateGradientCheckingCostFunction( const CostFunction* cost_function, + const std::vector<const LocalParameterization*>* local_parameterizations, double relative_step_size, double relative_precision, - const std::string& extra_info); + const std::string& extra_info, + GradientCheckingIterationCallback* callback); -// Create a new ProblemImpl object from the input problem_impl, where -// each CostFunctions in problem_impl are wrapped inside a -// GradientCheckingCostFunctions. This gives us a ProblemImpl object -// which checks its derivatives against estimates from numeric -// differentiation everytime a ResidualBlock is evaluated. +// Create a new ProblemImpl object from the input problem_impl, where all +// cost functions are wrapped so that each time their Evaluate method is called, +// an additional check is performed that compares the Jacobians computed by +// the original cost function with alternative Jacobians computed using +// numerical differentiation. If local parameterizations are given for any +// parameters, the Jacobians will be compared in the local space instead of the +// ambient space. For details on the gradient checking procedure, see the +// documentation of the GradientChecker class. If an error is detected in any +// iteration, the respective cost function will notify the +// GradientCheckingIterationCallback. +// +// The caller owns the returned ProblemImpl object. +// +// Note: This is quite inefficient and is intended only for debugging. // // relative_step_size and relative_precision are parameters to control // the numeric differentiation and the relative tolerance between the // jacobian computed by the CostFunctions in problem_impl and -// jacobians obtained by numerically differentiating them. For more -// details see the documentation for -// CreateGradientCheckingCostFunction above. -ProblemImpl* CreateGradientCheckingProblemImpl(ProblemImpl* problem_impl, - double relative_step_size, - double relative_precision); +// jacobians obtained by numerically differentiating them. See the +// documentation of 'numeric_derivative_relative_step_size' in solver.h for a +// better explanation. +ProblemImpl* CreateGradientCheckingProblemImpl( + ProblemImpl* problem_impl, + double relative_step_size, + double relative_precision, + GradientCheckingIterationCallback* callback); } // namespace internal } // namespace ceres diff --git a/extern/ceres/internal/ceres/gradient_problem_solver.cc b/extern/ceres/internal/ceres/gradient_problem_solver.cc index 9a549c23dac..8709f8f3fbd 100644 --- a/extern/ceres/internal/ceres/gradient_problem_solver.cc +++ b/extern/ceres/internal/ceres/gradient_problem_solver.cc @@ -84,6 +84,12 @@ Solver::Options GradientProblemSolverOptionsToSolverOptions( } // namespace +bool GradientProblemSolver::Options::IsValid(std::string* error) const { + const Solver::Options solver_options = + GradientProblemSolverOptionsToSolverOptions(*this); + return solver_options.IsValid(error); +} + GradientProblemSolver::~GradientProblemSolver() { } @@ -99,8 +105,6 @@ void GradientProblemSolver::Solve(const GradientProblemSolver::Options& options, using internal::SetSummaryFinalCost; double start_time = WallTimeInSeconds(); - Solver::Options solver_options = - GradientProblemSolverOptionsToSolverOptions(options); *CHECK_NOTNULL(summary) = Summary(); summary->num_parameters = problem.NumParameters(); @@ -112,14 +116,16 @@ void GradientProblemSolver::Solve(const GradientProblemSolver::Options& options, summary->nonlinear_conjugate_gradient_type = options.nonlinear_conjugate_gradient_type; // NOLINT // Check validity - if (!solver_options.IsValid(&summary->message)) { + if (!options.IsValid(&summary->message)) { LOG(ERROR) << "Terminating: " << summary->message; return; } - // Assuming that the parameter blocks in the program have been - Minimizer::Options minimizer_options; - minimizer_options = Minimizer::Options(solver_options); + // TODO(sameeragarwal): This is a bit convoluted, we should be able + // to convert to minimizer options directly, but this will do for + // now. + Minimizer::Options minimizer_options = + Minimizer::Options(GradientProblemSolverOptionsToSolverOptions(options)); minimizer_options.evaluator.reset(new GradientProblemEvaluator(problem)); scoped_ptr<IterationCallback> logging_callback; diff --git a/extern/ceres/internal/ceres/is_close.cc b/extern/ceres/internal/ceres/is_close.cc new file mode 100644 index 00000000000..a91a17454d9 --- /dev/null +++ b/extern/ceres/internal/ceres/is_close.cc @@ -0,0 +1,59 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2016 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Authors: keir@google.com (Keir Mierle), dgossow@google.com (David Gossow) + +#include "ceres/is_close.h" + +#include <algorithm> +#include <cmath> + +namespace ceres { +namespace internal { +bool IsClose(double x, double y, double relative_precision, + double *relative_error, + double *absolute_error) { + double local_absolute_error; + double local_relative_error; + if (!absolute_error) { + absolute_error = &local_absolute_error; + } + if (!relative_error) { + relative_error = &local_relative_error; + } + *absolute_error = std::fabs(x - y); + *relative_error = *absolute_error / std::max(std::fabs(x), std::fabs(y)); + if (x == 0 || y == 0) { + // If x or y is exactly zero, then relative difference doesn't have any + // meaning. Take the absolute difference instead. + *relative_error = *absolute_error; + } + return *relative_error < std::fabs(relative_precision); +} +} // namespace internal +} // namespace ceres diff --git a/extern/ceres/internal/ceres/is_close.h b/extern/ceres/internal/ceres/is_close.h new file mode 100644 index 00000000000..7789448c8e8 --- /dev/null +++ b/extern/ceres/internal/ceres/is_close.h @@ -0,0 +1,51 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2016 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Authors: keir@google.com (Keir Mierle), dgossow@google.com (David Gossow) +// +// Utility routine for comparing two values. + +#ifndef CERES_INTERNAL_IS_CLOSE_H_ +#define CERES_INTERNAL_IS_CLOSE_H_ + +namespace ceres { +namespace internal { +// Returns true if x and y have a relative (unsigned) difference less than +// relative_precision and false otherwise. Stores the relative and absolute +// difference in relative/absolute_error if non-NULL. If one of the two values +// is exactly zero, the absolute difference will be compared, and relative_error +// will be set to the absolute difference. +bool IsClose(double x, + double y, + double relative_precision, + double *relative_error, + double *absolute_error); +} // namespace internal +} // namespace ceres + +#endif // CERES_INTERNAL_IS_CLOSE_H_ diff --git a/extern/ceres/internal/ceres/line_search_minimizer.cc b/extern/ceres/internal/ceres/line_search_minimizer.cc index 62264fb0b64..fdde1ca9c86 100644 --- a/extern/ceres/internal/ceres/line_search_minimizer.cc +++ b/extern/ceres/internal/ceres/line_search_minimizer.cc @@ -191,6 +191,7 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options, options.line_search_sufficient_curvature_decrease; line_search_options.max_step_expansion = options.max_line_search_step_expansion; + line_search_options.is_silent = options.is_silent; line_search_options.function = &line_search_function; scoped_ptr<LineSearch> @@ -341,10 +342,12 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options, "as the step was valid when it was selected by the line search."; LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message; break; - } else if (!Evaluate(evaluator, - x_plus_delta, - ¤t_state, - &summary->message)) { + } + + if (!Evaluate(evaluator, + x_plus_delta, + ¤t_state, + &summary->message)) { summary->termination_type = FAILURE; summary->message = "Step failed to evaluate. This should not happen as the step was " @@ -352,15 +355,17 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options, summary->message; LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message; break; - } else { - x = x_plus_delta; } + // Compute the norm of the step in the ambient space. + iteration_summary.step_norm = (x_plus_delta - x).norm(); + x = x_plus_delta; + iteration_summary.gradient_max_norm = current_state.gradient_max_norm; iteration_summary.gradient_norm = sqrt(current_state.gradient_squared_norm); iteration_summary.cost_change = previous_state.cost - current_state.cost; iteration_summary.cost = current_state.cost + summary->fixed_cost; - iteration_summary.step_norm = delta.norm(); + iteration_summary.step_is_valid = true; iteration_summary.step_is_successful = true; iteration_summary.step_size = current_state.step_size; @@ -376,6 +381,13 @@ void LineSearchMinimizer::Minimize(const Minimizer::Options& options, WallTimeInSeconds() - start_time + summary->preprocessor_time_in_seconds; + // Iterations inside the line search algorithm are considered + // 'steps' in the broader context, to distinguish these inner + // iterations from from the outer iterations of the line search + // minimizer. The number of line search steps is the total number + // of inner line search iterations (or steps) across the entire + // minimization. + summary->num_line_search_steps += line_search_summary.num_iterations; summary->line_search_cost_evaluation_time_in_seconds += line_search_summary.cost_evaluation_time_in_seconds; summary->line_search_gradient_evaluation_time_in_seconds += diff --git a/extern/ceres/internal/ceres/local_parameterization.cc b/extern/ceres/internal/ceres/local_parameterization.cc index 82004761ec0..a6bf1f6ddcc 100644 --- a/extern/ceres/internal/ceres/local_parameterization.cc +++ b/extern/ceres/internal/ceres/local_parameterization.cc @@ -30,6 +30,8 @@ #include "ceres/local_parameterization.h" +#include <algorithm> +#include "Eigen/Geometry" #include "ceres/householder_vector.h" #include "ceres/internal/eigen.h" #include "ceres/internal/fixed_array.h" @@ -87,28 +89,17 @@ bool IdentityParameterization::MultiplyByJacobian(const double* x, } SubsetParameterization::SubsetParameterization( - int size, - const vector<int>& constant_parameters) - : local_size_(size - constant_parameters.size()), - constancy_mask_(size, 0) { - CHECK_GT(constant_parameters.size(), 0) - << "The set of constant parameters should contain at least " - << "one element. If you do not wish to hold any parameters " - << "constant, then do not use a SubsetParameterization"; - + int size, const vector<int>& constant_parameters) + : local_size_(size - constant_parameters.size()), constancy_mask_(size, 0) { vector<int> constant = constant_parameters; - sort(constant.begin(), constant.end()); - CHECK(unique(constant.begin(), constant.end()) == constant.end()) + std::sort(constant.begin(), constant.end()); + CHECK_GE(constant.front(), 0) + << "Indices indicating constant parameter must be greater than zero."; + CHECK_LT(constant.back(), size) + << "Indices indicating constant parameter must be less than the size " + << "of the parameter block."; + CHECK(std::adjacent_find(constant.begin(), constant.end()) == constant.end()) << "The set of constant parameters cannot contain duplicates"; - CHECK_LT(constant_parameters.size(), size) - << "Number of parameters held constant should be less " - << "than the size of the parameter block. If you wish " - << "to hold the entire parameter block constant, then a " - << "efficient way is to directly mark it as constant " - << "instead of using a LocalParameterization to do so."; - CHECK_GE(*min_element(constant.begin(), constant.end()), 0); - CHECK_LT(*max_element(constant.begin(), constant.end()), size); - for (int i = 0; i < constant_parameters.size(); ++i) { constancy_mask_[constant_parameters[i]] = 1; } @@ -129,6 +120,10 @@ bool SubsetParameterization::Plus(const double* x, bool SubsetParameterization::ComputeJacobian(const double* x, double* jacobian) const { + if (local_size_ == 0) { + return true; + } + MatrixRef m(jacobian, constancy_mask_.size(), local_size_); m.setZero(); for (int i = 0, j = 0; i < constancy_mask_.size(); ++i) { @@ -143,6 +138,10 @@ bool SubsetParameterization::MultiplyByJacobian(const double* x, const int num_rows, const double* global_matrix, double* local_matrix) const { + if (local_size_ == 0) { + return true; + } + for (int row = 0; row < num_rows; ++row) { for (int col = 0, j = 0; col < constancy_mask_.size(); ++col) { if (!constancy_mask_[col]) { @@ -184,6 +183,39 @@ bool QuaternionParameterization::ComputeJacobian(const double* x, return true; } +bool EigenQuaternionParameterization::Plus(const double* x_ptr, + const double* delta, + double* x_plus_delta_ptr) const { + Eigen::Map<Eigen::Quaterniond> x_plus_delta(x_plus_delta_ptr); + Eigen::Map<const Eigen::Quaterniond> x(x_ptr); + + const double norm_delta = + sqrt(delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]); + if (norm_delta > 0.0) { + const double sin_delta_by_delta = sin(norm_delta) / norm_delta; + + // Note, in the constructor w is first. + Eigen::Quaterniond delta_q(cos(norm_delta), + sin_delta_by_delta * delta[0], + sin_delta_by_delta * delta[1], + sin_delta_by_delta * delta[2]); + x_plus_delta = delta_q * x; + } else { + x_plus_delta = x; + } + + return true; +} + +bool EigenQuaternionParameterization::ComputeJacobian(const double* x, + double* jacobian) const { + jacobian[0] = x[3]; jacobian[1] = x[2]; jacobian[2] = -x[1]; // NOLINT + jacobian[3] = -x[2]; jacobian[4] = x[3]; jacobian[5] = x[0]; // NOLINT + jacobian[6] = x[1]; jacobian[7] = -x[0]; jacobian[8] = x[3]; // NOLINT + jacobian[9] = -x[0]; jacobian[10] = -x[1]; jacobian[11] = -x[2]; // NOLINT + return true; +} + HomogeneousVectorParameterization::HomogeneousVectorParameterization(int size) : size_(size) { CHECK_GT(size_, 1) << "The size of the homogeneous vector needs to be " @@ -332,9 +364,9 @@ bool ProductParameterization::ComputeJacobian(const double* x, if (!param->ComputeJacobian(x + x_cursor, buffer.get())) { return false; } - jacobian.block(x_cursor, delta_cursor, global_size, local_size) = MatrixRef(buffer.get(), global_size, local_size); + delta_cursor += local_size; x_cursor += global_size; } diff --git a/extern/ceres/internal/ceres/map_util.h b/extern/ceres/internal/ceres/map_util.h index 61c531f297c..f55aee37689 100644 --- a/extern/ceres/internal/ceres/map_util.h +++ b/extern/ceres/internal/ceres/map_util.h @@ -67,7 +67,7 @@ FindOrDie(const Collection& collection, // If the key is present in the map then the value associated with that // key is returned, otherwise the value passed as a default is returned. template <class Collection> -const typename Collection::value_type::second_type& +const typename Collection::value_type::second_type FindWithDefault(const Collection& collection, const typename Collection::value_type::first_type& key, const typename Collection::value_type::second_type& value) { diff --git a/extern/ceres/internal/ceres/parameter_block.h b/extern/ceres/internal/ceres/parameter_block.h index cb7140d9582..8e21553c668 100644 --- a/extern/ceres/internal/ceres/parameter_block.h +++ b/extern/ceres/internal/ceres/parameter_block.h @@ -161,25 +161,34 @@ class ParameterBlock { // does not take ownership of the parameterization. void SetParameterization(LocalParameterization* new_parameterization) { CHECK(new_parameterization != NULL) << "NULL parameterization invalid."; + // Nothing to do if the new parameterization is the same as the + // old parameterization. + if (new_parameterization == local_parameterization_) { + return; + } + + CHECK(local_parameterization_ == NULL) + << "Can't re-set the local parameterization; it leads to " + << "ambiguous ownership. Current local parameterization is: " + << local_parameterization_; + CHECK(new_parameterization->GlobalSize() == size_) << "Invalid parameterization for parameter block. The parameter block " << "has size " << size_ << " while the parameterization has a global " << "size of " << new_parameterization->GlobalSize() << ". Did you " << "accidentally use the wrong parameter block or parameterization?"; - if (new_parameterization != local_parameterization_) { - CHECK(local_parameterization_ == NULL) - << "Can't re-set the local parameterization; it leads to " - << "ambiguous ownership."; - local_parameterization_ = new_parameterization; - local_parameterization_jacobian_.reset( - new double[local_parameterization_->GlobalSize() * - local_parameterization_->LocalSize()]); - CHECK(UpdateLocalParameterizationJacobian()) - << "Local parameterization Jacobian computation failed for x: " - << ConstVectorRef(state_, Size()).transpose(); - } else { - // Ignore the case that the parameterizations match. - } + + CHECK_GT(new_parameterization->LocalSize(), 0) + << "Invalid parameterization. Parameterizations must have a positive " + << "dimensional tangent space."; + + local_parameterization_ = new_parameterization; + local_parameterization_jacobian_.reset( + new double[local_parameterization_->GlobalSize() * + local_parameterization_->LocalSize()]); + CHECK(UpdateLocalParameterizationJacobian()) + << "Local parameterization Jacobian computation failed for x: " + << ConstVectorRef(state_, Size()).transpose(); } void SetUpperBound(int index, double upper_bound) { diff --git a/extern/ceres/internal/ceres/problem.cc b/extern/ceres/internal/ceres/problem.cc index 03b7d6afa48..730ce642036 100644 --- a/extern/ceres/internal/ceres/problem.cc +++ b/extern/ceres/internal/ceres/problem.cc @@ -174,6 +174,10 @@ void Problem::SetParameterBlockVariable(double* values) { problem_impl_->SetParameterBlockVariable(values); } +bool Problem::IsParameterBlockConstant(double* values) const { + return problem_impl_->IsParameterBlockConstant(values); +} + void Problem::SetParameterization( double* values, LocalParameterization* local_parameterization) { diff --git a/extern/ceres/internal/ceres/problem_impl.cc b/extern/ceres/internal/ceres/problem_impl.cc index 8547d5d3f77..4abea8b33ee 100644 --- a/extern/ceres/internal/ceres/problem_impl.cc +++ b/extern/ceres/internal/ceres/problem_impl.cc @@ -249,10 +249,11 @@ ResidualBlock* ProblemImpl::AddResidualBlock( // Check for duplicate parameter blocks. vector<double*> sorted_parameter_blocks(parameter_blocks); sort(sorted_parameter_blocks.begin(), sorted_parameter_blocks.end()); - vector<double*>::const_iterator duplicate_items = - unique(sorted_parameter_blocks.begin(), - sorted_parameter_blocks.end()); - if (duplicate_items != sorted_parameter_blocks.end()) { + const bool has_duplicate_items = + (std::adjacent_find(sorted_parameter_blocks.begin(), + sorted_parameter_blocks.end()) + != sorted_parameter_blocks.end()); + if (has_duplicate_items) { string blocks; for (int i = 0; i < parameter_blocks.size(); ++i) { blocks += StringPrintf(" %p ", parameter_blocks[i]); @@ -572,6 +573,16 @@ void ProblemImpl::SetParameterBlockConstant(double* values) { parameter_block->SetConstant(); } +bool ProblemImpl::IsParameterBlockConstant(double* values) const { + const ParameterBlock* parameter_block = + FindWithDefault(parameter_block_map_, values, NULL); + CHECK(parameter_block != NULL) + << "Parameter block not found: " << values << ". You must add the " + << "parameter block to the problem before it can be queried."; + + return parameter_block->IsConstant(); +} + void ProblemImpl::SetParameterBlockVariable(double* values) { ParameterBlock* parameter_block = FindWithDefault(parameter_block_map_, values, NULL); diff --git a/extern/ceres/internal/ceres/problem_impl.h b/extern/ceres/internal/ceres/problem_impl.h index f42bde6c793..a4689c362f6 100644 --- a/extern/ceres/internal/ceres/problem_impl.h +++ b/extern/ceres/internal/ceres/problem_impl.h @@ -128,6 +128,8 @@ class ProblemImpl { void SetParameterBlockConstant(double* values); void SetParameterBlockVariable(double* values); + bool IsParameterBlockConstant(double* values) const; + void SetParameterization(double* values, LocalParameterization* local_parameterization); const LocalParameterization* GetParameterization(double* values) const; diff --git a/extern/ceres/internal/ceres/reorder_program.cc b/extern/ceres/internal/ceres/reorder_program.cc index d0e8f32b3b7..a7c37107591 100644 --- a/extern/ceres/internal/ceres/reorder_program.cc +++ b/extern/ceres/internal/ceres/reorder_program.cc @@ -142,6 +142,11 @@ void OrderingForSparseNormalCholeskyUsingSuiteSparse( ordering); } + VLOG(2) << "Block ordering stats: " + << " flops: " << ss.mutable_cc()->fl + << " lnz : " << ss.mutable_cc()->lnz + << " anz : " << ss.mutable_cc()->anz; + ss.Free(block_jacobian_transpose); #endif // CERES_NO_SUITESPARSE } diff --git a/extern/ceres/internal/ceres/residual_block.h b/extern/ceres/internal/ceres/residual_block.h index 05e6d1f81e5..a32f1c36cd3 100644 --- a/extern/ceres/internal/ceres/residual_block.h +++ b/extern/ceres/internal/ceres/residual_block.h @@ -127,7 +127,7 @@ class ResidualBlock { int index() const { return index_; } void set_index(int index) { index_ = index; } - std::string ToString() { + std::string ToString() const { return StringPrintf("{residual block; index=%d}", index_); } diff --git a/extern/ceres/internal/ceres/schur_complement_solver.cc b/extern/ceres/internal/ceres/schur_complement_solver.cc index 2491060dcdc..65449832c4c 100644 --- a/extern/ceres/internal/ceres/schur_complement_solver.cc +++ b/extern/ceres/internal/ceres/schur_complement_solver.cc @@ -33,6 +33,7 @@ #include <algorithm> #include <ctime> #include <set> +#include <sstream> #include <vector> #include "ceres/block_random_access_dense_matrix.h" @@ -563,6 +564,12 @@ SparseSchurComplementSolver::SolveReducedLinearSystemUsingEigen( // worse than the one computed using the block version of the // algorithm. simplicial_ldlt_->analyzePattern(eigen_lhs); + if (VLOG_IS_ON(2)) { + std::stringstream ss; + simplicial_ldlt_->dumpMemory(ss); + VLOG(2) << "Symbolic Analysis\n" + << ss.str(); + } event_logger.AddEvent("Analysis"); if (simplicial_ldlt_->info() != Eigen::Success) { summary.termination_type = LINEAR_SOLVER_FATAL_ERROR; diff --git a/extern/ceres/internal/ceres/solver.cc b/extern/ceres/internal/ceres/solver.cc index 9f3228bb0be..8411350986a 100644 --- a/extern/ceres/internal/ceres/solver.cc +++ b/extern/ceres/internal/ceres/solver.cc @@ -94,7 +94,7 @@ bool CommonOptionsAreValid(const Solver::Options& options, string* error) { OPTION_GT(num_linear_solver_threads, 0); if (options.check_gradients) { OPTION_GT(gradient_check_relative_precision, 0.0); - OPTION_GT(numeric_derivative_relative_step_size, 0.0); + OPTION_GT(gradient_check_numeric_derivative_relative_step_size, 0.0); } return true; } @@ -351,6 +351,7 @@ void PreSolveSummarize(const Solver::Options& options, summary->dense_linear_algebra_library_type = options.dense_linear_algebra_library_type; // NOLINT summary->dogleg_type = options.dogleg_type; summary->inner_iteration_time_in_seconds = 0.0; + summary->num_line_search_steps = 0; summary->line_search_cost_evaluation_time_in_seconds = 0.0; summary->line_search_gradient_evaluation_time_in_seconds = 0.0; summary->line_search_polynomial_minimization_time_in_seconds = 0.0; @@ -495,21 +496,28 @@ void Solver::Solve(const Solver::Options& options, // values provided by the user. program->SetParameterBlockStatePtrsToUserStatePtrs(); + // If gradient_checking is enabled, wrap all cost functions in a + // gradient checker and install a callback that terminates if any gradient + // error is detected. scoped_ptr<internal::ProblemImpl> gradient_checking_problem; + internal::GradientCheckingIterationCallback gradient_checking_callback; + Solver::Options modified_options = options; if (options.check_gradients) { + modified_options.callbacks.push_back(&gradient_checking_callback); gradient_checking_problem.reset( CreateGradientCheckingProblemImpl( problem_impl, - options.numeric_derivative_relative_step_size, - options.gradient_check_relative_precision)); + options.gradient_check_numeric_derivative_relative_step_size, + options.gradient_check_relative_precision, + &gradient_checking_callback)); problem_impl = gradient_checking_problem.get(); program = problem_impl->mutable_program(); } scoped_ptr<Preprocessor> preprocessor( - Preprocessor::Create(options.minimizer_type)); + Preprocessor::Create(modified_options.minimizer_type)); PreprocessedProblem pp; - const bool status = preprocessor->Preprocess(options, problem_impl, &pp); + const bool status = preprocessor->Preprocess(modified_options, problem_impl, &pp); summary->fixed_cost = pp.fixed_cost; summary->preprocessor_time_in_seconds = WallTimeInSeconds() - start_time; @@ -534,6 +542,13 @@ void Solver::Solve(const Solver::Options& options, summary->postprocessor_time_in_seconds = WallTimeInSeconds() - postprocessor_start_time; + // If the gradient checker reported an error, we want to report FAILURE + // instead of USER_FAILURE and provide the error log. + if (gradient_checking_callback.gradient_error_detected()) { + summary->termination_type = FAILURE; + summary->message = gradient_checking_callback.error_log(); + } + summary->total_time_in_seconds = WallTimeInSeconds() - start_time; } @@ -556,6 +571,7 @@ Solver::Summary::Summary() num_successful_steps(-1), num_unsuccessful_steps(-1), num_inner_iteration_steps(-1), + num_line_search_steps(-1), preprocessor_time_in_seconds(-1.0), minimizer_time_in_seconds(-1.0), postprocessor_time_in_seconds(-1.0), @@ -696,16 +712,14 @@ string Solver::Summary::FullReport() const { num_linear_solver_threads_given, num_linear_solver_threads_used); - if (IsSchurType(linear_solver_type_used)) { - string given; - StringifyOrdering(linear_solver_ordering_given, &given); - string used; - StringifyOrdering(linear_solver_ordering_used, &used); - StringAppendF(&report, - "Linear solver ordering %22s %24s\n", - given.c_str(), - used.c_str()); - } + string given; + StringifyOrdering(linear_solver_ordering_given, &given); + string used; + StringifyOrdering(linear_solver_ordering_used, &used); + StringAppendF(&report, + "Linear solver ordering %22s %24s\n", + given.c_str(), + used.c_str()); if (inner_iterations_given) { StringAppendF(&report, @@ -784,9 +798,14 @@ string Solver::Summary::FullReport() const { num_inner_iteration_steps); } - const bool print_line_search_timing_information = - minimizer_type == LINE_SEARCH || - (minimizer_type == TRUST_REGION && is_constrained); + const bool line_search_used = + (minimizer_type == LINE_SEARCH || + (minimizer_type == TRUST_REGION && is_constrained)); + + if (line_search_used) { + StringAppendF(&report, "Line search steps % 14d\n", + num_line_search_steps); + } StringAppendF(&report, "\nTime (in seconds):\n"); StringAppendF(&report, "Preprocessor %25.4f\n", @@ -794,13 +813,13 @@ string Solver::Summary::FullReport() const { StringAppendF(&report, "\n Residual evaluation %23.4f\n", residual_evaluation_time_in_seconds); - if (print_line_search_timing_information) { + if (line_search_used) { StringAppendF(&report, " Line search cost evaluation %10.4f\n", line_search_cost_evaluation_time_in_seconds); } StringAppendF(&report, " Jacobian evaluation %23.4f\n", jacobian_evaluation_time_in_seconds); - if (print_line_search_timing_information) { + if (line_search_used) { StringAppendF(&report, " Line search gradient evaluation %6.4f\n", line_search_gradient_evaluation_time_in_seconds); } @@ -815,7 +834,7 @@ string Solver::Summary::FullReport() const { inner_iteration_time_in_seconds); } - if (print_line_search_timing_information) { + if (line_search_used) { StringAppendF(&report, " Line search polynomial minimization %.4f\n", line_search_polynomial_minimization_time_in_seconds); } diff --git a/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc b/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc index ed00879b47a..a4c2c766ddc 100644 --- a/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc +++ b/extern/ceres/internal/ceres/sparse_normal_cholesky_solver.cc @@ -33,6 +33,7 @@ #include <algorithm> #include <cstring> #include <ctime> +#include <sstream> #include "ceres/compressed_row_sparse_matrix.h" #include "ceres/cxsparse.h" @@ -71,6 +72,12 @@ LinearSolver::Summary SimplicialLDLTSolve( if (do_symbolic_analysis) { solver->analyzePattern(lhs); + if (VLOG_IS_ON(2)) { + std::stringstream ss; + solver->dumpMemory(ss); + VLOG(2) << "Symbolic Analysis\n" + << ss.str(); + } event_logger->AddEvent("Analyze"); if (solver->info() != Eigen::Success) { summary.termination_type = LINEAR_SOLVER_FATAL_ERROR; diff --git a/extern/ceres/internal/ceres/stringprintf.cc b/extern/ceres/internal/ceres/stringprintf.cc index d1d8b5fe8ab..b3b7474d8f8 100644 --- a/extern/ceres/internal/ceres/stringprintf.cc +++ b/extern/ceres/internal/ceres/stringprintf.cc @@ -43,14 +43,27 @@ namespace internal { using std::string; -#ifdef _MSC_VER -enum { IS_COMPILER_MSVC = 1 }; -#if _MSC_VER < 1800 -#define va_copy(d, s) ((d) = (s)) -#endif +// va_copy() was defined in the C99 standard. However, it did not appear in the +// C++ standard until C++11. This means that if Ceres is being compiled with a +// strict pre-C++11 standard (e.g. -std=c++03), va_copy() will NOT be defined, +// as we are using the C++ compiler (it would however be defined if we were +// using the C compiler). Note however that both GCC & Clang will in fact +// define va_copy() when compiling for C++ if the C++ standard is not explicitly +// specified (i.e. no -std=c++<XX> arg), even though it should not strictly be +// defined unless -std=c++11 (or greater) was passed. +#if !defined(va_copy) +#if defined (__GNUC__) +// On GCC/Clang, if va_copy() is not defined (C++ standard < C++11 explicitly +// specified), use the internal __va_copy() version, which should be present +// in even very old GCC versions. +#define va_copy(d, s) __va_copy(d, s) #else -enum { IS_COMPILER_MSVC = 0 }; -#endif +// Some older versions of MSVC do not have va_copy(), in which case define it. +// Although this is required for older MSVC versions, it should also work for +// other non-GCC/Clang compilers which also do not defined va_copy(). +#define va_copy(d, s) ((d) = (s)) +#endif // defined (__GNUC__) +#endif // !defined(va_copy) void StringAppendV(string* dst, const char* format, va_list ap) { // First try with a small fixed size buffer @@ -71,13 +84,13 @@ void StringAppendV(string* dst, const char* format, va_list ap) { return; } - if (IS_COMPILER_MSVC) { - // Error or MSVC running out of space. MSVC 8.0 and higher - // can be asked about space needed with the special idiom below: - va_copy(backup_ap, ap); - result = vsnprintf(NULL, 0, format, backup_ap); - va_end(backup_ap); - } +#if defined (_MSC_VER) + // Error or MSVC running out of space. MSVC 8.0 and higher + // can be asked about space needed with the special idiom below: + va_copy(backup_ap, ap); + result = vsnprintf(NULL, 0, format, backup_ap); + va_end(backup_ap); +#endif if (result < 0) { // Just an error. diff --git a/extern/ceres/internal/ceres/trust_region_minimizer.cc b/extern/ceres/internal/ceres/trust_region_minimizer.cc index d654d0867f1..d809906ab54 100644 --- a/extern/ceres/internal/ceres/trust_region_minimizer.cc +++ b/extern/ceres/internal/ceres/trust_region_minimizer.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2016 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -43,674 +43,747 @@ #include "ceres/coordinate_descent_minimizer.h" #include "ceres/evaluator.h" #include "ceres/file.h" -#include "ceres/internal/eigen.h" -#include "ceres/internal/scoped_ptr.h" #include "ceres/line_search.h" -#include "ceres/linear_least_squares_problems.h" -#include "ceres/sparse_matrix.h" #include "ceres/stringprintf.h" -#include "ceres/trust_region_strategy.h" #include "ceres/types.h" #include "ceres/wall_time.h" #include "glog/logging.h" +// Helper macro to simplify some of the control flow. +#define RETURN_IF_ERROR_AND_LOG(expr) \ + do { \ + if (!(expr)) { \ + LOG(ERROR) << "Terminating: " << solver_summary_->message; \ + return; \ + } \ + } while (0) + namespace ceres { namespace internal { -namespace { -LineSearch::Summary DoLineSearch(const Minimizer::Options& options, - const Vector& x, - const Vector& gradient, - const double cost, - const Vector& delta, - Evaluator* evaluator) { - LineSearchFunction line_search_function(evaluator); +TrustRegionMinimizer::~TrustRegionMinimizer() {} - LineSearch::Options line_search_options; - line_search_options.is_silent = true; - line_search_options.interpolation_type = - options.line_search_interpolation_type; - line_search_options.min_step_size = options.min_line_search_step_size; - line_search_options.sufficient_decrease = - options.line_search_sufficient_function_decrease; - line_search_options.max_step_contraction = - options.max_line_search_step_contraction; - line_search_options.min_step_contraction = - options.min_line_search_step_contraction; - line_search_options.max_num_iterations = - options.max_num_line_search_step_size_iterations; - line_search_options.sufficient_curvature_decrease = - options.line_search_sufficient_curvature_decrease; - line_search_options.max_step_expansion = - options.max_line_search_step_expansion; - line_search_options.function = &line_search_function; +void TrustRegionMinimizer::Minimize(const Minimizer::Options& options, + double* parameters, + Solver::Summary* solver_summary) { + start_time_in_secs_ = WallTimeInSeconds(); + iteration_start_time_in_secs_ = start_time_in_secs_; + Init(options, parameters, solver_summary); + RETURN_IF_ERROR_AND_LOG(IterationZero()); + + // Create the TrustRegionStepEvaluator. The construction needs to be + // delayed to this point because we need the cost for the starting + // point to initialize the step evaluator. + step_evaluator_.reset(new TrustRegionStepEvaluator( + x_cost_, + options_.use_nonmonotonic_steps + ? options_.max_consecutive_nonmonotonic_steps + : 0)); + + while (FinalizeIterationAndCheckIfMinimizerCanContinue()) { + iteration_start_time_in_secs_ = WallTimeInSeconds(); + iteration_summary_ = IterationSummary(); + iteration_summary_.iteration = + solver_summary->iterations.back().iteration + 1; + + RETURN_IF_ERROR_AND_LOG(ComputeTrustRegionStep()); + if (!iteration_summary_.step_is_valid) { + RETURN_IF_ERROR_AND_LOG(HandleInvalidStep()); + continue; + } - std::string message; - scoped_ptr<LineSearch> line_search( - CHECK_NOTNULL(LineSearch::Create(ceres::ARMIJO, - line_search_options, - &message))); - LineSearch::Summary summary; - line_search_function.Init(x, delta); - line_search->Search(1.0, cost, gradient.dot(delta), &summary); - return summary; -} + if (options_.is_constrained) { + // Use a projected line search to enforce the bounds constraints + // and improve the quality of the step. + DoLineSearch(x_, gradient_, x_cost_, &delta_); + } + + ComputeCandidatePointAndEvaluateCost(); + DoInnerIterationsIfNeeded(); -} // namespace + if (ParameterToleranceReached()) { + return; + } + + if (FunctionToleranceReached()) { + return; + } -// Compute a scaling vector that is used to improve the conditioning -// of the Jacobian. -void TrustRegionMinimizer::EstimateScale(const SparseMatrix& jacobian, - double* scale) const { - jacobian.SquaredColumnNorm(scale); - for (int i = 0; i < jacobian.num_cols(); ++i) { - scale[i] = 1.0 / (1.0 + sqrt(scale[i])); + if (IsStepSuccessful()) { + RETURN_IF_ERROR_AND_LOG(HandleSuccessfulStep()); + continue; + } + + HandleUnsuccessfulStep(); } } -void TrustRegionMinimizer::Init(const Minimizer::Options& options) { +// Initialize the minimizer, allocate working space and set some of +// the fields in the solver_summary. +void TrustRegionMinimizer::Init(const Minimizer::Options& options, + double* parameters, + Solver::Summary* solver_summary) { options_ = options; sort(options_.trust_region_minimizer_iterations_to_dump.begin(), options_.trust_region_minimizer_iterations_to_dump.end()); + + parameters_ = parameters; + + solver_summary_ = solver_summary; + solver_summary_->termination_type = NO_CONVERGENCE; + solver_summary_->num_successful_steps = 0; + solver_summary_->num_unsuccessful_steps = 0; + solver_summary_->is_constrained = options.is_constrained; + + evaluator_ = CHECK_NOTNULL(options_.evaluator.get()); + jacobian_ = CHECK_NOTNULL(options_.jacobian.get()); + strategy_ = CHECK_NOTNULL(options_.trust_region_strategy.get()); + + is_not_silent_ = !options.is_silent; + inner_iterations_are_enabled_ = + options.inner_iteration_minimizer.get() != NULL; + inner_iterations_were_useful_ = false; + + num_parameters_ = evaluator_->NumParameters(); + num_effective_parameters_ = evaluator_->NumEffectiveParameters(); + num_residuals_ = evaluator_->NumResiduals(); + num_consecutive_invalid_steps_ = 0; + + x_ = ConstVectorRef(parameters_, num_parameters_); + x_norm_ = x_.norm(); + residuals_.resize(num_residuals_); + trust_region_step_.resize(num_effective_parameters_); + delta_.resize(num_effective_parameters_); + candidate_x_.resize(num_parameters_); + gradient_.resize(num_effective_parameters_); + model_residuals_.resize(num_residuals_); + negative_gradient_.resize(num_effective_parameters_); + projected_gradient_step_.resize(num_parameters_); + + // By default scaling is one, if the user requests Jacobi scaling of + // the Jacobian, we will compute and overwrite this vector. + jacobian_scaling_ = Vector::Ones(num_effective_parameters_); + + x_norm_ = -1; // Invalid value + x_cost_ = std::numeric_limits<double>::max(); + minimum_cost_ = x_cost_; + model_cost_change_ = 0.0; } -void TrustRegionMinimizer::Minimize(const Minimizer::Options& options, - double* parameters, - Solver::Summary* summary) { - double start_time = WallTimeInSeconds(); - double iteration_start_time = start_time; - Init(options); - - Evaluator* evaluator = CHECK_NOTNULL(options_.evaluator.get()); - SparseMatrix* jacobian = CHECK_NOTNULL(options_.jacobian.get()); - TrustRegionStrategy* strategy = - CHECK_NOTNULL(options_.trust_region_strategy.get()); - - const bool is_not_silent = !options.is_silent; - - // If the problem is bounds constrained, then enable the use of a - // line search after the trust region step has been computed. This - // line search will automatically use a projected test point onto - // the feasible set, there by guaranteeing the feasibility of the - // final output. - // - // TODO(sameeragarwal): Make line search available more generally. - const bool use_line_search = options.is_constrained; - - summary->termination_type = NO_CONVERGENCE; - summary->num_successful_steps = 0; - summary->num_unsuccessful_steps = 0; - summary->is_constrained = options.is_constrained; - - const int num_parameters = evaluator->NumParameters(); - const int num_effective_parameters = evaluator->NumEffectiveParameters(); - const int num_residuals = evaluator->NumResiduals(); - - Vector residuals(num_residuals); - Vector trust_region_step(num_effective_parameters); - Vector delta(num_effective_parameters); - Vector x_plus_delta(num_parameters); - Vector gradient(num_effective_parameters); - Vector model_residuals(num_residuals); - Vector scale(num_effective_parameters); - Vector negative_gradient(num_effective_parameters); - Vector projected_gradient_step(num_parameters); - - IterationSummary iteration_summary; - iteration_summary.iteration = 0; - iteration_summary.step_is_valid = false; - iteration_summary.step_is_successful = false; - iteration_summary.cost_change = 0.0; - iteration_summary.gradient_max_norm = 0.0; - iteration_summary.gradient_norm = 0.0; - iteration_summary.step_norm = 0.0; - iteration_summary.relative_decrease = 0.0; - iteration_summary.trust_region_radius = strategy->Radius(); - iteration_summary.eta = options_.eta; - iteration_summary.linear_solver_iterations = 0; - iteration_summary.step_solver_time_in_seconds = 0; - - VectorRef x_min(parameters, num_parameters); - Vector x = x_min; - // Project onto the feasible set. - if (options.is_constrained) { - delta.setZero(); - if (!evaluator->Plus(x.data(), delta.data(), x_plus_delta.data())) { - summary->message = +// 1. Project the initial solution onto the feasible set if needed. +// 2. Compute the initial cost, jacobian & gradient. +// +// Return true if all computations can be performed successfully. +bool TrustRegionMinimizer::IterationZero() { + iteration_summary_ = IterationSummary(); + iteration_summary_.iteration = 0; + iteration_summary_.step_is_valid = false; + iteration_summary_.step_is_successful = false; + iteration_summary_.cost_change = 0.0; + iteration_summary_.gradient_max_norm = 0.0; + iteration_summary_.gradient_norm = 0.0; + iteration_summary_.step_norm = 0.0; + iteration_summary_.relative_decrease = 0.0; + iteration_summary_.eta = options_.eta; + iteration_summary_.linear_solver_iterations = 0; + iteration_summary_.step_solver_time_in_seconds = 0; + + if (options_.is_constrained) { + delta_.setZero(); + if (!evaluator_->Plus(x_.data(), delta_.data(), candidate_x_.data())) { + solver_summary_->message = "Unable to project initial point onto the feasible set."; - summary->termination_type = FAILURE; - LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message; - return; + solver_summary_->termination_type = FAILURE; + return false; } - x_min = x_plus_delta; - x = x_plus_delta; - } - double x_norm = x.norm(); - - // Do initial cost and Jacobian evaluation. - double cost = 0.0; - if (!evaluator->Evaluate(x.data(), - &cost, - residuals.data(), - gradient.data(), - jacobian)) { - summary->message = "Residual and Jacobian evaluation failed."; - summary->termination_type = FAILURE; - LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message; - return; + x_ = candidate_x_; + x_norm_ = x_.norm(); } - negative_gradient = -gradient; - if (!evaluator->Plus(x.data(), - negative_gradient.data(), - projected_gradient_step.data())) { - summary->message = "Unable to compute gradient step."; - summary->termination_type = FAILURE; - LOG(ERROR) << "Terminating: " << summary->message; - return; + if (!EvaluateGradientAndJacobian()) { + return false; } - summary->initial_cost = cost + summary->fixed_cost; - iteration_summary.cost = cost + summary->fixed_cost; - iteration_summary.gradient_max_norm = - (x - projected_gradient_step).lpNorm<Eigen::Infinity>(); - iteration_summary.gradient_norm = (x - projected_gradient_step).norm(); - - if (iteration_summary.gradient_max_norm <= options.gradient_tolerance) { - summary->message = StringPrintf("Gradient tolerance reached. " - "Gradient max norm: %e <= %e", - iteration_summary.gradient_max_norm, - options_.gradient_tolerance); - summary->termination_type = CONVERGENCE; - VLOG_IF(1, is_not_silent) << "Terminating: " << summary->message; - - // Ensure that there is an iteration summary object for iteration - // 0 in Summary::iterations. - iteration_summary.iteration_time_in_seconds = - WallTimeInSeconds() - iteration_start_time; - iteration_summary.cumulative_time_in_seconds = - WallTimeInSeconds() - start_time + - summary->preprocessor_time_in_seconds; - summary->iterations.push_back(iteration_summary); - return; - } + solver_summary_->initial_cost = x_cost_ + solver_summary_->fixed_cost; + iteration_summary_.step_is_valid = true; + iteration_summary_.step_is_successful = true; + return true; +} - if (options_.jacobi_scaling) { - EstimateScale(*jacobian, scale.data()); - jacobian->ScaleColumns(scale.data()); - } else { - scale.setOnes(); +// For the current x_, compute +// +// 1. Cost +// 2. Jacobian +// 3. Gradient +// 4. Scale the Jacobian if needed (and compute the scaling if we are +// in iteration zero). +// 5. Compute the 2 and max norm of the gradient. +// +// Returns true if all computations could be performed +// successfully. Any failures are considered fatal and the +// Solver::Summary is updated to indicate this. +bool TrustRegionMinimizer::EvaluateGradientAndJacobian() { + if (!evaluator_->Evaluate(x_.data(), + &x_cost_, + residuals_.data(), + gradient_.data(), + jacobian_)) { + solver_summary_->message = "Residual and Jacobian evaluation failed."; + solver_summary_->termination_type = FAILURE; + return false; } - iteration_summary.iteration_time_in_seconds = - WallTimeInSeconds() - iteration_start_time; - iteration_summary.cumulative_time_in_seconds = - WallTimeInSeconds() - start_time - + summary->preprocessor_time_in_seconds; - summary->iterations.push_back(iteration_summary); - - int num_consecutive_nonmonotonic_steps = 0; - double minimum_cost = cost; - double reference_cost = cost; - double accumulated_reference_model_cost_change = 0.0; - double candidate_cost = cost; - double accumulated_candidate_model_cost_change = 0.0; - int num_consecutive_invalid_steps = 0; - bool inner_iterations_are_enabled = - options.inner_iteration_minimizer.get() != NULL; - while (true) { - bool inner_iterations_were_useful = false; - if (!RunCallbacks(options, iteration_summary, summary)) { - return; - } + iteration_summary_.cost = x_cost_ + solver_summary_->fixed_cost; - iteration_start_time = WallTimeInSeconds(); - if (iteration_summary.iteration >= options_.max_num_iterations) { - summary->message = "Maximum number of iterations reached."; - summary->termination_type = NO_CONVERGENCE; - VLOG_IF(1, is_not_silent) << "Terminating: " << summary->message; - return; + if (options_.jacobi_scaling) { + if (iteration_summary_.iteration == 0) { + // Compute a scaling vector that is used to improve the + // conditioning of the Jacobian. + // + // jacobian_scaling_ = diag(J'J)^{-1} + jacobian_->SquaredColumnNorm(jacobian_scaling_.data()); + for (int i = 0; i < jacobian_->num_cols(); ++i) { + // Add one to the denominator to prevent division by zero. + jacobian_scaling_[i] = 1.0 / (1.0 + sqrt(jacobian_scaling_[i])); + } } - const double total_solver_time = iteration_start_time - start_time + - summary->preprocessor_time_in_seconds; - if (total_solver_time >= options_.max_solver_time_in_seconds) { - summary->message = "Maximum solver time reached."; - summary->termination_type = NO_CONVERGENCE; - VLOG_IF(1, is_not_silent) << "Terminating: " << summary->message; - return; - } + // jacobian = jacobian * diag(J'J) ^{-1} + jacobian_->ScaleColumns(jacobian_scaling_.data()); + } + + // The gradient exists in the local tangent space. To account for + // the bounds constraints correctly, instead of just computing the + // norm of the gradient vector, we compute + // + // |Plus(x, -gradient) - x| + // + // Where the Plus operator lifts the negative gradient to the + // ambient space, adds it to x and projects it on the hypercube + // defined by the bounds. + negative_gradient_ = -gradient_; + if (!evaluator_->Plus(x_.data(), + negative_gradient_.data(), + projected_gradient_step_.data())) { + solver_summary_->message = + "projected_gradient_step = Plus(x, -gradient) failed."; + solver_summary_->termination_type = FAILURE; + return false; + } - const double strategy_start_time = WallTimeInSeconds(); - TrustRegionStrategy::PerSolveOptions per_solve_options; - per_solve_options.eta = options_.eta; - if (find(options_.trust_region_minimizer_iterations_to_dump.begin(), - options_.trust_region_minimizer_iterations_to_dump.end(), - iteration_summary.iteration) != - options_.trust_region_minimizer_iterations_to_dump.end()) { - per_solve_options.dump_format_type = - options_.trust_region_problem_dump_format_type; - per_solve_options.dump_filename_base = - JoinPath(options_.trust_region_problem_dump_directory, - StringPrintf("ceres_solver_iteration_%03d", - iteration_summary.iteration)); + iteration_summary_.gradient_max_norm = + (x_ - projected_gradient_step_).lpNorm<Eigen::Infinity>(); + iteration_summary_.gradient_norm = (x_ - projected_gradient_step_).norm(); + return true; +} + +// 1. Add the final timing information to the iteration summary. +// 2. Run the callbacks +// 3. Check for termination based on +// a. Run time +// b. Iteration count +// c. Max norm of the gradient +// d. Size of the trust region radius. +// +// Returns true if user did not terminate the solver and none of these +// termination criterion are met. +bool TrustRegionMinimizer::FinalizeIterationAndCheckIfMinimizerCanContinue() { + if (iteration_summary_.step_is_successful) { + ++solver_summary_->num_successful_steps; + if (x_cost_ < minimum_cost_) { + minimum_cost_ = x_cost_; + VectorRef(parameters_, num_parameters_) = x_; + iteration_summary_.step_is_nonmonotonic = false; } else { - per_solve_options.dump_format_type = TEXTFILE; - per_solve_options.dump_filename_base.clear(); + iteration_summary_.step_is_nonmonotonic = true; } + } else { + ++solver_summary_->num_unsuccessful_steps; + } - TrustRegionStrategy::Summary strategy_summary = - strategy->ComputeStep(per_solve_options, - jacobian, - residuals.data(), - trust_region_step.data()); - - if (strategy_summary.termination_type == LINEAR_SOLVER_FATAL_ERROR) { - summary->message = - "Linear solver failed due to unrecoverable " - "non-numeric causes. Please see the error log for clues. "; - summary->termination_type = FAILURE; - LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message; - return; - } + iteration_summary_.trust_region_radius = strategy_->Radius(); + iteration_summary_.iteration_time_in_seconds = + WallTimeInSeconds() - iteration_start_time_in_secs_; + iteration_summary_.cumulative_time_in_seconds = + WallTimeInSeconds() - start_time_in_secs_ + + solver_summary_->preprocessor_time_in_seconds; - iteration_summary = IterationSummary(); - iteration_summary.iteration = summary->iterations.back().iteration + 1; - iteration_summary.step_solver_time_in_seconds = - WallTimeInSeconds() - strategy_start_time; - iteration_summary.linear_solver_iterations = - strategy_summary.num_iterations; - iteration_summary.step_is_valid = false; - iteration_summary.step_is_successful = false; - - double model_cost_change = 0.0; - if (strategy_summary.termination_type != LINEAR_SOLVER_FAILURE) { - // new_model_cost - // = 1/2 [f + J * step]^2 - // = 1/2 [ f'f + 2f'J * step + step' * J' * J * step ] - // model_cost_change - // = cost - new_model_cost - // = f'f/2 - 1/2 [ f'f + 2f'J * step + step' * J' * J * step] - // = -f'J * step - step' * J' * J * step / 2 - model_residuals.setZero(); - jacobian->RightMultiply(trust_region_step.data(), model_residuals.data()); - model_cost_change = - - model_residuals.dot(residuals + model_residuals / 2.0); - - if (model_cost_change < 0.0) { - VLOG_IF(1, is_not_silent) - << "Invalid step: current_cost: " << cost - << " absolute difference " << model_cost_change - << " relative difference " << (model_cost_change / cost); - } else { - iteration_summary.step_is_valid = true; - } - } + solver_summary_->iterations.push_back(iteration_summary_); - if (!iteration_summary.step_is_valid) { - // Invalid steps can happen due to a number of reasons, and we - // allow a limited number of successive failures, and return with - // FAILURE if this limit is exceeded. - if (++num_consecutive_invalid_steps >= - options_.max_num_consecutive_invalid_steps) { - summary->message = StringPrintf( - "Number of successive invalid steps more " - "than Solver::Options::max_num_consecutive_invalid_steps: %d", - options_.max_num_consecutive_invalid_steps); - summary->termination_type = FAILURE; - LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message; - return; - } + if (!RunCallbacks(options_, iteration_summary_, solver_summary_)) { + return false; + } - // We are going to try and reduce the trust region radius and - // solve again. To do this, we are going to treat this iteration - // as an unsuccessful iteration. Since the various callbacks are - // still executed, we are going to fill the iteration summary - // with data that assumes a step of length zero and no progress. - iteration_summary.cost = cost + summary->fixed_cost; - iteration_summary.cost_change = 0.0; - iteration_summary.gradient_max_norm = - summary->iterations.back().gradient_max_norm; - iteration_summary.gradient_norm = - summary->iterations.back().gradient_norm; - iteration_summary.step_norm = 0.0; - iteration_summary.relative_decrease = 0.0; - iteration_summary.eta = options_.eta; - } else { - // The step is numerically valid, so now we can judge its quality. - num_consecutive_invalid_steps = 0; + if (MaxSolverTimeReached()) { + return false; + } - // Undo the Jacobian column scaling. - delta = (trust_region_step.array() * scale.array()).matrix(); + if (MaxSolverIterationsReached()) { + return false; + } - // Try improving the step further by using an ARMIJO line - // search. - // - // TODO(sameeragarwal): What happens to trust region sizing as - // it interacts with the line search ? - if (use_line_search) { - const LineSearch::Summary line_search_summary = - DoLineSearch(options, x, gradient, cost, delta, evaluator); - - summary->line_search_cost_evaluation_time_in_seconds += - line_search_summary.cost_evaluation_time_in_seconds; - summary->line_search_gradient_evaluation_time_in_seconds += - line_search_summary.gradient_evaluation_time_in_seconds; - summary->line_search_polynomial_minimization_time_in_seconds += - line_search_summary.polynomial_minimization_time_in_seconds; - summary->line_search_total_time_in_seconds += - line_search_summary.total_time_in_seconds; - - if (line_search_summary.success) { - delta *= line_search_summary.optimal_step_size; - } - } + if (GradientToleranceReached()) { + return false; + } - double new_cost = std::numeric_limits<double>::max(); - if (evaluator->Plus(x.data(), delta.data(), x_plus_delta.data())) { - if (!evaluator->Evaluate(x_plus_delta.data(), - &new_cost, - NULL, - NULL, - NULL)) { - LOG_IF(WARNING, is_not_silent) - << "Step failed to evaluate. " - << "Treating it as a step with infinite cost"; - new_cost = std::numeric_limits<double>::max(); - } - } else { - LOG_IF(WARNING, is_not_silent) - << "x_plus_delta = Plus(x, delta) failed. " - << "Treating it as a step with infinite cost"; - } + if (MinTrustRegionRadiusReached()) { + return false; + } - if (new_cost < std::numeric_limits<double>::max()) { - // Check if performing an inner iteration will make it better. - if (inner_iterations_are_enabled) { - ++summary->num_inner_iteration_steps; - double inner_iteration_start_time = WallTimeInSeconds(); - const double x_plus_delta_cost = new_cost; - Vector inner_iteration_x = x_plus_delta; - Solver::Summary inner_iteration_summary; - options.inner_iteration_minimizer->Minimize(options, - inner_iteration_x.data(), - &inner_iteration_summary); - if (!evaluator->Evaluate(inner_iteration_x.data(), - &new_cost, - NULL, NULL, NULL)) { - VLOG_IF(2, is_not_silent) << "Inner iteration failed."; - new_cost = x_plus_delta_cost; - } else { - x_plus_delta = inner_iteration_x; - // Boost the model_cost_change, since the inner iteration - // improvements are not accounted for by the trust region. - model_cost_change += x_plus_delta_cost - new_cost; - VLOG_IF(2, is_not_silent) - << "Inner iteration succeeded; Current cost: " << cost - << " Trust region step cost: " << x_plus_delta_cost - << " Inner iteration cost: " << new_cost; - - inner_iterations_were_useful = new_cost < cost; - - const double inner_iteration_relative_progress = - 1.0 - new_cost / x_plus_delta_cost; - // Disable inner iterations once the relative improvement - // drops below tolerance. - inner_iterations_are_enabled = - (inner_iteration_relative_progress > - options.inner_iteration_tolerance); - VLOG_IF(2, is_not_silent && !inner_iterations_are_enabled) - << "Disabling inner iterations. Progress : " - << inner_iteration_relative_progress; - } - summary->inner_iteration_time_in_seconds += - WallTimeInSeconds() - inner_iteration_start_time; - } - } + return true; +} - iteration_summary.step_norm = (x - x_plus_delta).norm(); - - // Convergence based on parameter_tolerance. - const double step_size_tolerance = options_.parameter_tolerance * - (x_norm + options_.parameter_tolerance); - if (iteration_summary.step_norm <= step_size_tolerance) { - summary->message = - StringPrintf("Parameter tolerance reached. " - "Relative step_norm: %e <= %e.", - (iteration_summary.step_norm / - (x_norm + options_.parameter_tolerance)), - options_.parameter_tolerance); - summary->termination_type = CONVERGENCE; - VLOG_IF(1, is_not_silent) << "Terminating: " << summary->message; - return; - } +// Compute the trust region step using the TrustRegionStrategy chosen +// by the user. +// +// If the strategy returns with LINEAR_SOLVER_FATAL_ERROR, which +// indicates an unrecoverable error, return false. This is the only +// condition that returns false. +// +// If the strategy returns with LINEAR_SOLVER_FAILURE, which indicates +// a numerical failure that could be recovered from by retrying +// (e.g. by increasing the strength of the regularization), we set +// iteration_summary_.step_is_valid to false and return true. +// +// In all other cases, we compute the decrease in the trust region +// model problem. In exact arithmetic, this should always be +// positive, but due to numerical problems in the TrustRegionStrategy +// or round off error when computing the decrease it may be +// negative. In which case again, we set +// iteration_summary_.step_is_valid to false. +bool TrustRegionMinimizer::ComputeTrustRegionStep() { + const double strategy_start_time = WallTimeInSeconds(); + iteration_summary_.step_is_valid = false; + TrustRegionStrategy::PerSolveOptions per_solve_options; + per_solve_options.eta = options_.eta; + if (find(options_.trust_region_minimizer_iterations_to_dump.begin(), + options_.trust_region_minimizer_iterations_to_dump.end(), + iteration_summary_.iteration) != + options_.trust_region_minimizer_iterations_to_dump.end()) { + per_solve_options.dump_format_type = + options_.trust_region_problem_dump_format_type; + per_solve_options.dump_filename_base = + JoinPath(options_.trust_region_problem_dump_directory, + StringPrintf("ceres_solver_iteration_%03d", + iteration_summary_.iteration)); + } - iteration_summary.cost_change = cost - new_cost; - const double absolute_function_tolerance = - options_.function_tolerance * cost; - if (fabs(iteration_summary.cost_change) <= absolute_function_tolerance) { - summary->message = - StringPrintf("Function tolerance reached. " - "|cost_change|/cost: %e <= %e", - fabs(iteration_summary.cost_change) / cost, - options_.function_tolerance); - summary->termination_type = CONVERGENCE; - VLOG_IF(1, is_not_silent) << "Terminating: " << summary->message; - return; - } + TrustRegionStrategy::Summary strategy_summary = + strategy_->ComputeStep(per_solve_options, + jacobian_, + residuals_.data(), + trust_region_step_.data()); + + if (strategy_summary.termination_type == LINEAR_SOLVER_FATAL_ERROR) { + solver_summary_->message = + "Linear solver failed due to unrecoverable " + "non-numeric causes. Please see the error log for clues. "; + solver_summary_->termination_type = FAILURE; + return false; + } - const double relative_decrease = - iteration_summary.cost_change / model_cost_change; + iteration_summary_.step_solver_time_in_seconds = + WallTimeInSeconds() - strategy_start_time; + iteration_summary_.linear_solver_iterations = strategy_summary.num_iterations; - const double historical_relative_decrease = - (reference_cost - new_cost) / - (accumulated_reference_model_cost_change + model_cost_change); + if (strategy_summary.termination_type == LINEAR_SOLVER_FAILURE) { + return true; + } - // If monotonic steps are being used, then the relative_decrease - // is the usual ratio of the change in objective function value - // divided by the change in model cost. - // - // If non-monotonic steps are allowed, then we take the maximum - // of the relative_decrease and the - // historical_relative_decrease, which measures the increase - // from a reference iteration. The model cost change is - // estimated by accumulating the model cost changes since the - // reference iteration. The historical relative_decrease offers - // a boost to a step which is not too bad compared to the - // reference iteration, allowing for non-monotonic steps. - iteration_summary.relative_decrease = - options.use_nonmonotonic_steps - ? std::max(relative_decrease, historical_relative_decrease) - : relative_decrease; - - // Normally, the quality of a trust region step is measured by - // the ratio - // - // cost_change - // r = ----------------- - // model_cost_change - // - // All the change in the nonlinear objective is due to the trust - // region step so this ratio is a good measure of the quality of - // the trust region radius. However, when inner iterations are - // being used, cost_change includes the contribution of the - // inner iterations and its not fair to credit it all to the - // trust region algorithm. So we change the ratio to be - // - // cost_change - // r = ------------------------------------------------ - // (model_cost_change + inner_iteration_cost_change) - // - // In most cases this is fine, but it can be the case that the - // change in solution quality due to inner iterations is so large - // and the trust region step is so bad, that this ratio can become - // quite small. - // - // This can cause the trust region loop to reject this step. To - // get around this, we expicitly check if the inner iterations - // led to a net decrease in the objective function value. If - // they did, we accept the step even if the trust region ratio - // is small. - // - // Notice that we do not just check that cost_change is positive - // which is a weaker condition and would render the - // min_relative_decrease threshold useless. Instead, we keep - // track of inner_iterations_were_useful, which is true only - // when inner iterations lead to a net decrease in the cost. - iteration_summary.step_is_successful = - (inner_iterations_were_useful || - iteration_summary.relative_decrease > - options_.min_relative_decrease); - - if (iteration_summary.step_is_successful) { - accumulated_candidate_model_cost_change += model_cost_change; - accumulated_reference_model_cost_change += model_cost_change; - - if (!inner_iterations_were_useful && - relative_decrease <= options_.min_relative_decrease) { - iteration_summary.step_is_nonmonotonic = true; - VLOG_IF(2, is_not_silent) - << "Non-monotonic step! " - << " relative_decrease: " - << relative_decrease - << " historical_relative_decrease: " - << historical_relative_decrease; - } - } - } + // new_model_cost + // = 1/2 [f + J * step]^2 + // = 1/2 [ f'f + 2f'J * step + step' * J' * J * step ] + // model_cost_change + // = cost - new_model_cost + // = f'f/2 - 1/2 [ f'f + 2f'J * step + step' * J' * J * step] + // = -f'J * step - step' * J' * J * step / 2 + // = -(J * step)'(f + J * step / 2) + model_residuals_.setZero(); + jacobian_->RightMultiply(trust_region_step_.data(), model_residuals_.data()); + model_cost_change_ = + -model_residuals_.dot(residuals_ + model_residuals_ / 2.0); + + // TODO(sameeragarwal) + // + // 1. What happens if model_cost_change_ = 0 + // 2. What happens if -epsilon <= model_cost_change_ < 0 for some + // small epsilon due to round off error. + iteration_summary_.step_is_valid = (model_cost_change_ > 0.0); + if (iteration_summary_.step_is_valid) { + // Undo the Jacobian column scaling. + delta_ = (trust_region_step_.array() * jacobian_scaling_.array()).matrix(); + num_consecutive_invalid_steps_ = 0; + } - if (iteration_summary.step_is_successful) { - ++summary->num_successful_steps; - strategy->StepAccepted(iteration_summary.relative_decrease); - - x = x_plus_delta; - x_norm = x.norm(); - - // Step looks good, evaluate the residuals and Jacobian at this - // point. - if (!evaluator->Evaluate(x.data(), - &cost, - residuals.data(), - gradient.data(), - jacobian)) { - summary->message = "Residual and Jacobian evaluation failed."; - summary->termination_type = FAILURE; - LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message; - return; - } + VLOG_IF(1, is_not_silent_ && !iteration_summary_.step_is_valid) + << "Invalid step: current_cost: " << x_cost_ + << " absolute model cost change: " << model_cost_change_ + << " relative model cost change: " << (model_cost_change_ / x_cost_); + return true; +} - negative_gradient = -gradient; - if (!evaluator->Plus(x.data(), - negative_gradient.data(), - projected_gradient_step.data())) { - summary->message = - "projected_gradient_step = Plus(x, -gradient) failed."; - summary->termination_type = FAILURE; - LOG(ERROR) << "Terminating: " << summary->message; - return; - } +// Invalid steps can happen due to a number of reasons, and we allow a +// limited number of consecutive failures, and return false if this +// limit is exceeded. +bool TrustRegionMinimizer::HandleInvalidStep() { + // TODO(sameeragarwal): Should we be returning FAILURE or + // NO_CONVERGENCE? The solution value is still usable in many cases, + // it is not clear if we should declare the solver a failure + // entirely. For example the case where model_cost_change ~ 0.0, but + // just slightly negative. + if (++num_consecutive_invalid_steps_ >= + options_.max_num_consecutive_invalid_steps) { + solver_summary_->message = StringPrintf( + "Number of consecutive invalid steps more " + "than Solver::Options::max_num_consecutive_invalid_steps: %d", + options_.max_num_consecutive_invalid_steps); + solver_summary_->termination_type = FAILURE; + return false; + } - iteration_summary.gradient_max_norm = - (x - projected_gradient_step).lpNorm<Eigen::Infinity>(); - iteration_summary.gradient_norm = (x - projected_gradient_step).norm(); + strategy_->StepIsInvalid(); + + // We are going to try and reduce the trust region radius and + // solve again. To do this, we are going to treat this iteration + // as an unsuccessful iteration. Since the various callbacks are + // still executed, we are going to fill the iteration summary + // with data that assumes a step of length zero and no progress. + iteration_summary_.cost = x_cost_ + solver_summary_->fixed_cost; + iteration_summary_.cost_change = 0.0; + iteration_summary_.gradient_max_norm = + solver_summary_->iterations.back().gradient_max_norm; + iteration_summary_.gradient_norm = + solver_summary_->iterations.back().gradient_norm; + iteration_summary_.step_norm = 0.0; + iteration_summary_.relative_decrease = 0.0; + iteration_summary_.eta = options_.eta; + return true; +} - if (options_.jacobi_scaling) { - jacobian->ScaleColumns(scale.data()); - } +// Use the supplied coordinate descent minimizer to perform inner +// iterations and compute the improvement due to it. Returns the cost +// after performing the inner iterations. +// +// The optimization is performed with candidate_x_ as the starting +// point, and if the optimization is successful, candidate_x_ will be +// updated with the optimized parameters. +void TrustRegionMinimizer::DoInnerIterationsIfNeeded() { + inner_iterations_were_useful_ = false; + if (!inner_iterations_are_enabled_ || + candidate_cost_ >= std::numeric_limits<double>::max()) { + return; + } - // Update the best, reference and candidate iterates. - // - // Based on algorithm 10.1.2 (page 357) of "Trust Region - // Methods" by Conn Gould & Toint, or equations 33-40 of - // "Non-monotone trust-region algorithms for nonlinear - // optimization subject to convex constraints" by Phil Toint, - // Mathematical Programming, 77, 1997. - if (cost < minimum_cost) { - // A step that improves solution quality was found. - x_min = x; - minimum_cost = cost; - // Set the candidate iterate to the current point. - candidate_cost = cost; - num_consecutive_nonmonotonic_steps = 0; - accumulated_candidate_model_cost_change = 0.0; - } else { - ++num_consecutive_nonmonotonic_steps; - if (cost > candidate_cost) { - // The current iterate is has a higher cost than the - // candidate iterate. Set the candidate to this point. - VLOG_IF(2, is_not_silent) - << "Updating the candidate iterate to the current point."; - candidate_cost = cost; - accumulated_candidate_model_cost_change = 0.0; - } - - // At this point we have made too many non-monotonic steps and - // we are going to reset the value of the reference iterate so - // as to force the algorithm to descend. - // - // This is the case because the candidate iterate has a value - // greater than minimum_cost but smaller than the reference - // iterate. - if (num_consecutive_nonmonotonic_steps == - options.max_consecutive_nonmonotonic_steps) { - VLOG_IF(2, is_not_silent) - << "Resetting the reference point to the candidate point"; - reference_cost = candidate_cost; - accumulated_reference_model_cost_change = - accumulated_candidate_model_cost_change; - } - } - } else { - ++summary->num_unsuccessful_steps; - if (iteration_summary.step_is_valid) { - strategy->StepRejected(iteration_summary.relative_decrease); - } else { - strategy->StepIsInvalid(); - } - } + double inner_iteration_start_time = WallTimeInSeconds(); + ++solver_summary_->num_inner_iteration_steps; + inner_iteration_x_ = candidate_x_; + Solver::Summary inner_iteration_summary; + options_.inner_iteration_minimizer->Minimize( + options_, inner_iteration_x_.data(), &inner_iteration_summary); + double inner_iteration_cost; + if (!evaluator_->Evaluate( + inner_iteration_x_.data(), &inner_iteration_cost, NULL, NULL, NULL)) { + VLOG_IF(2, is_not_silent_) << "Inner iteration failed."; + return; + } - iteration_summary.cost = cost + summary->fixed_cost; - iteration_summary.trust_region_radius = strategy->Radius(); - iteration_summary.iteration_time_in_seconds = - WallTimeInSeconds() - iteration_start_time; - iteration_summary.cumulative_time_in_seconds = - WallTimeInSeconds() - start_time - + summary->preprocessor_time_in_seconds; - summary->iterations.push_back(iteration_summary); - - // If the step was successful, check for the gradient norm - // collapsing to zero, and if the step is unsuccessful then check - // if the trust region radius has collapsed to zero. - // - // For correctness (Number of IterationSummary objects, correct - // final cost, and state update) these convergence tests need to - // be performed at the end of the iteration. - if (iteration_summary.step_is_successful) { - // Gradient norm can only go down in successful steps. - if (iteration_summary.gradient_max_norm <= options.gradient_tolerance) { - summary->message = StringPrintf("Gradient tolerance reached. " - "Gradient max norm: %e <= %e", - iteration_summary.gradient_max_norm, - options_.gradient_tolerance); - summary->termination_type = CONVERGENCE; - VLOG_IF(1, is_not_silent) << "Terminating: " << summary->message; - return; - } - } else { - // Trust region radius can only go down if the step if - // unsuccessful. - if (iteration_summary.trust_region_radius < - options_.min_trust_region_radius) { - summary->message = "Termination. Minimum trust region radius reached."; - summary->termination_type = CONVERGENCE; - VLOG_IF(1, is_not_silent) << summary->message; - return; - } - } + VLOG_IF(2, is_not_silent_) + << "Inner iteration succeeded; Current cost: " << x_cost_ + << " Trust region step cost: " << candidate_cost_ + << " Inner iteration cost: " << inner_iteration_cost; + + candidate_x_ = inner_iteration_x_; + + // Normally, the quality of a trust region step is measured by + // the ratio + // + // cost_change + // r = ----------------- + // model_cost_change + // + // All the change in the nonlinear objective is due to the trust + // region step so this ratio is a good measure of the quality of + // the trust region radius. However, when inner iterations are + // being used, cost_change includes the contribution of the + // inner iterations and its not fair to credit it all to the + // trust region algorithm. So we change the ratio to be + // + // cost_change + // r = ------------------------------------------------ + // (model_cost_change + inner_iteration_cost_change) + // + // Practically we do this by increasing model_cost_change by + // inner_iteration_cost_change. + + const double inner_iteration_cost_change = + candidate_cost_ - inner_iteration_cost; + model_cost_change_ += inner_iteration_cost_change; + inner_iterations_were_useful_ = inner_iteration_cost < x_cost_; + const double inner_iteration_relative_progress = + 1.0 - inner_iteration_cost / candidate_cost_; + + // Disable inner iterations once the relative improvement + // drops below tolerance. + inner_iterations_are_enabled_ = + (inner_iteration_relative_progress > options_.inner_iteration_tolerance); + VLOG_IF(2, is_not_silent_ && !inner_iterations_are_enabled_) + << "Disabling inner iterations. Progress : " + << inner_iteration_relative_progress; + candidate_cost_ = inner_iteration_cost; + + solver_summary_->inner_iteration_time_in_seconds += + WallTimeInSeconds() - inner_iteration_start_time; +} + +// Perform a projected line search to improve the objective function +// value along delta. +// +// TODO(sameeragarwal): The current implementation does not do +// anything illegal but is incorrect and not terribly effective. +// +// https://github.com/ceres-solver/ceres-solver/issues/187 +void TrustRegionMinimizer::DoLineSearch(const Vector& x, + const Vector& gradient, + const double cost, + Vector* delta) { + LineSearchFunction line_search_function(evaluator_); + + LineSearch::Options line_search_options; + line_search_options.is_silent = true; + line_search_options.interpolation_type = + options_.line_search_interpolation_type; + line_search_options.min_step_size = options_.min_line_search_step_size; + line_search_options.sufficient_decrease = + options_.line_search_sufficient_function_decrease; + line_search_options.max_step_contraction = + options_.max_line_search_step_contraction; + line_search_options.min_step_contraction = + options_.min_line_search_step_contraction; + line_search_options.max_num_iterations = + options_.max_num_line_search_step_size_iterations; + line_search_options.sufficient_curvature_decrease = + options_.line_search_sufficient_curvature_decrease; + line_search_options.max_step_expansion = + options_.max_line_search_step_expansion; + line_search_options.function = &line_search_function; + + std::string message; + scoped_ptr<LineSearch> line_search(CHECK_NOTNULL( + LineSearch::Create(ceres::ARMIJO, line_search_options, &message))); + LineSearch::Summary line_search_summary; + line_search_function.Init(x, *delta); + line_search->Search(1.0, cost, gradient.dot(*delta), &line_search_summary); + + solver_summary_->num_line_search_steps += line_search_summary.num_iterations; + solver_summary_->line_search_cost_evaluation_time_in_seconds += + line_search_summary.cost_evaluation_time_in_seconds; + solver_summary_->line_search_gradient_evaluation_time_in_seconds += + line_search_summary.gradient_evaluation_time_in_seconds; + solver_summary_->line_search_polynomial_minimization_time_in_seconds += + line_search_summary.polynomial_minimization_time_in_seconds; + solver_summary_->line_search_total_time_in_seconds += + line_search_summary.total_time_in_seconds; + + if (line_search_summary.success) { + *delta *= line_search_summary.optimal_step_size; + } +} + +// Check if the maximum amount of time allowed by the user for the +// solver has been exceeded, and if so return false after updating +// Solver::Summary::message. +bool TrustRegionMinimizer::MaxSolverTimeReached() { + const double total_solver_time = + WallTimeInSeconds() - start_time_in_secs_ + + solver_summary_->preprocessor_time_in_seconds; + if (total_solver_time < options_.max_solver_time_in_seconds) { + return false; + } + + solver_summary_->message = StringPrintf("Maximum solver time reached. " + "Total solver time: %e >= %e.", + total_solver_time, + options_.max_solver_time_in_seconds); + solver_summary_->termination_type = NO_CONVERGENCE; + VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message; + return true; +} + +// Check if the maximum number of iterations allowed by the user for +// the solver has been exceeded, and if so return false after updating +// Solver::Summary::message. +bool TrustRegionMinimizer::MaxSolverIterationsReached() { + if (iteration_summary_.iteration < options_.max_num_iterations) { + return false; + } + + solver_summary_->message = + StringPrintf("Maximum number of iterations reached. " + "Number of iterations: %d.", + iteration_summary_.iteration); + + solver_summary_->termination_type = NO_CONVERGENCE; + VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message; + return true; +} + +// Check convergence based on the max norm of the gradient (only for +// iterations where the step was declared successful). +bool TrustRegionMinimizer::GradientToleranceReached() { + if (!iteration_summary_.step_is_successful || + iteration_summary_.gradient_max_norm > options_.gradient_tolerance) { + return false; + } + + solver_summary_->message = StringPrintf( + "Gradient tolerance reached. " + "Gradient max norm: %e <= %e", + iteration_summary_.gradient_max_norm, + options_.gradient_tolerance); + solver_summary_->termination_type = CONVERGENCE; + VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message; + return true; +} + +// Check convergence based the size of the trust region radius. +bool TrustRegionMinimizer::MinTrustRegionRadiusReached() { + if (iteration_summary_.trust_region_radius > + options_.min_trust_region_radius) { + return false; + } + + solver_summary_->message = + StringPrintf("Minimum trust region radius reached. " + "Trust region radius: %e <= %e", + iteration_summary_.trust_region_radius, + options_.min_trust_region_radius); + solver_summary_->termination_type = CONVERGENCE; + VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message; + return true; +} + +// Solver::Options::parameter_tolerance based convergence check. +bool TrustRegionMinimizer::ParameterToleranceReached() { + // Compute the norm of the step in the ambient space. + iteration_summary_.step_norm = (x_ - candidate_x_).norm(); + const double step_size_tolerance = + options_.parameter_tolerance * (x_norm_ + options_.parameter_tolerance); + + if (iteration_summary_.step_norm > step_size_tolerance) { + return false; } + + solver_summary_->message = StringPrintf( + "Parameter tolerance reached. " + "Relative step_norm: %e <= %e.", + (iteration_summary_.step_norm / (x_norm_ + options_.parameter_tolerance)), + options_.parameter_tolerance); + solver_summary_->termination_type = CONVERGENCE; + VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message; + return true; +} + +// Solver::Options::function_tolerance based convergence check. +bool TrustRegionMinimizer::FunctionToleranceReached() { + iteration_summary_.cost_change = x_cost_ - candidate_cost_; + const double absolute_function_tolerance = + options_.function_tolerance * x_cost_; + + if (fabs(iteration_summary_.cost_change) > absolute_function_tolerance) { + return false; + } + + solver_summary_->message = StringPrintf( + "Function tolerance reached. " + "|cost_change|/cost: %e <= %e", + fabs(iteration_summary_.cost_change) / x_cost_, + options_.function_tolerance); + solver_summary_->termination_type = CONVERGENCE; + VLOG_IF(1, is_not_silent_) << "Terminating: " << solver_summary_->message; + return true; } +// Compute candidate_x_ = Plus(x_, delta_) +// Evaluate the cost of candidate_x_ as candidate_cost_. +// +// Failure to compute the step or the cost mean that candidate_cost_ +// is set to std::numeric_limits<double>::max(). Unlike +// EvaluateGradientAndJacobian, failure in this function is not fatal +// as we are only computing and evaluating a candidate point, and if +// for some reason we are unable to evaluate it, we consider it to be +// a point with very high cost. This allows the user to deal with edge +// cases/constraints as part of the LocalParameterization and +// CostFunction objects. +void TrustRegionMinimizer::ComputeCandidatePointAndEvaluateCost() { + if (!evaluator_->Plus(x_.data(), delta_.data(), candidate_x_.data())) { + LOG_IF(WARNING, is_not_silent_) + << "x_plus_delta = Plus(x, delta) failed. " + << "Treating it as a step with infinite cost"; + candidate_cost_ = std::numeric_limits<double>::max(); + return; + } + + if (!evaluator_->Evaluate( + candidate_x_.data(), &candidate_cost_, NULL, NULL, NULL)) { + LOG_IF(WARNING, is_not_silent_) + << "Step failed to evaluate. " + << "Treating it as a step with infinite cost"; + candidate_cost_ = std::numeric_limits<double>::max(); + } +} + +bool TrustRegionMinimizer::IsStepSuccessful() { + iteration_summary_.relative_decrease = + step_evaluator_->StepQuality(candidate_cost_, model_cost_change_); + + // In most cases, boosting the model_cost_change by the + // improvement caused by the inner iterations is fine, but it can + // be the case that the original trust region step was so bad that + // the resulting improvement in the cost was negative, and the + // change caused by the inner iterations was large enough to + // improve the step, but also to make relative decrease quite + // small. + // + // This can cause the trust region loop to reject this step. To + // get around this, we expicitly check if the inner iterations + // led to a net decrease in the objective function value. If + // they did, we accept the step even if the trust region ratio + // is small. + // + // Notice that we do not just check that cost_change is positive + // which is a weaker condition and would render the + // min_relative_decrease threshold useless. Instead, we keep + // track of inner_iterations_were_useful, which is true only + // when inner iterations lead to a net decrease in the cost. + return (inner_iterations_were_useful_ || + iteration_summary_.relative_decrease > + options_.min_relative_decrease); +} + +// Declare the step successful, move to candidate_x, update the +// derivatives and let the trust region strategy and the step +// evaluator know that the step has been accepted. +bool TrustRegionMinimizer::HandleSuccessfulStep() { + x_ = candidate_x_; + x_norm_ = x_.norm(); + + if (!EvaluateGradientAndJacobian()) { + return false; + } + + iteration_summary_.step_is_successful = true; + strategy_->StepAccepted(iteration_summary_.relative_decrease); + step_evaluator_->StepAccepted(candidate_cost_, model_cost_change_); + return true; +} + +// Declare the step unsuccessful and inform the trust region strategy. +void TrustRegionMinimizer::HandleUnsuccessfulStep() { + iteration_summary_.step_is_successful = false; + strategy_->StepRejected(iteration_summary_.relative_decrease); + iteration_summary_.cost = candidate_cost_ + solver_summary_->fixed_cost; +} } // namespace internal } // namespace ceres diff --git a/extern/ceres/internal/ceres/trust_region_minimizer.h b/extern/ceres/internal/ceres/trust_region_minimizer.h index ed52c2642d1..43141da58a1 100644 --- a/extern/ceres/internal/ceres/trust_region_minimizer.h +++ b/extern/ceres/internal/ceres/trust_region_minimizer.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2016 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -31,35 +31,136 @@ #ifndef CERES_INTERNAL_TRUST_REGION_MINIMIZER_H_ #define CERES_INTERNAL_TRUST_REGION_MINIMIZER_H_ +#include "ceres/internal/eigen.h" +#include "ceres/internal/scoped_ptr.h" #include "ceres/minimizer.h" #include "ceres/solver.h" +#include "ceres/sparse_matrix.h" +#include "ceres/trust_region_step_evaluator.h" +#include "ceres/trust_region_strategy.h" #include "ceres/types.h" namespace ceres { namespace internal { -// Generic trust region minimization algorithm. The heavy lifting is -// done by a TrustRegionStrategy object passed in as part of options. +// Generic trust region minimization algorithm. // // For example usage, see SolverImpl::Minimize. class TrustRegionMinimizer : public Minimizer { public: - ~TrustRegionMinimizer() {} + ~TrustRegionMinimizer(); + + // This method is not thread safe. virtual void Minimize(const Minimizer::Options& options, double* parameters, - Solver::Summary* summary); + Solver::Summary* solver_summary); private: - void Init(const Minimizer::Options& options); - void EstimateScale(const SparseMatrix& jacobian, double* scale) const; - bool MaybeDumpLinearLeastSquaresProblem(const int iteration, - const SparseMatrix* jacobian, - const double* residuals, - const double* step) const; + void Init(const Minimizer::Options& options, + double* parameters, + Solver::Summary* solver_summary); + bool IterationZero(); + bool FinalizeIterationAndCheckIfMinimizerCanContinue(); + bool ComputeTrustRegionStep(); + + bool EvaluateGradientAndJacobian(); + void ComputeCandidatePointAndEvaluateCost(); + + void DoLineSearch(const Vector& x, + const Vector& gradient, + const double cost, + Vector* delta); + void DoInnerIterationsIfNeeded(); + + bool ParameterToleranceReached(); + bool FunctionToleranceReached(); + bool GradientToleranceReached(); + bool MaxSolverTimeReached(); + bool MaxSolverIterationsReached(); + bool MinTrustRegionRadiusReached(); + + bool IsStepSuccessful(); + void HandleUnsuccessfulStep(); + bool HandleSuccessfulStep(); + bool HandleInvalidStep(); Minimizer::Options options_; + + // These pointers are shortcuts to objects passed to the + // TrustRegionMinimizer. The TrustRegionMinimizer does not own them. + double* parameters_; + Solver::Summary* solver_summary_; + Evaluator* evaluator_; + SparseMatrix* jacobian_; + TrustRegionStrategy* strategy_; + + scoped_ptr<TrustRegionStepEvaluator> step_evaluator_; + + bool is_not_silent_; + bool inner_iterations_are_enabled_; + bool inner_iterations_were_useful_; + + // Summary of the current iteration. + IterationSummary iteration_summary_; + + // Dimensionality of the problem in the ambient space. + int num_parameters_; + // Dimensionality of the problem in the tangent space. This is the + // number of columns in the Jacobian. + int num_effective_parameters_; + // Length of the residual vector, also the number of rows in the Jacobian. + int num_residuals_; + + // Current point. + Vector x_; + // Residuals at x_; + Vector residuals_; + // Gradient at x_. + Vector gradient_; + // Solution computed by the inner iterations. + Vector inner_iteration_x_; + // model_residuals = J * trust_region_step + Vector model_residuals_; + Vector negative_gradient_; + // projected_gradient_step = Plus(x, -gradient), an intermediate + // quantity used to compute the projected gradient norm. + Vector projected_gradient_step_; + // The step computed by the trust region strategy. If Jacobi scaling + // is enabled, this is a vector in the scaled space. + Vector trust_region_step_; + // The current proposal for how far the trust region algorithm + // thinks we should move. In the most basic case, it is just the + // trust_region_step_ with the Jacobi scaling undone. If bounds + // constraints are present, then it is the result of the projected + // line search. + Vector delta_; + // candidate_x = Plus(x, delta) + Vector candidate_x_; + // Scaling vector to scale the columns of the Jacobian. + Vector jacobian_scaling_; + + // Euclidean norm of x_. + double x_norm_; + // Cost at x_. + double x_cost_; + // Minimum cost encountered up till now. + double minimum_cost_; + // How much did the trust region strategy reduce the cost of the + // linearized Gauss-Newton model. + double model_cost_change_; + // Cost at candidate_x_. + double candidate_cost_; + + // Time at which the minimizer was started. + double start_time_in_secs_; + // Time at which the current iteration was started. + double iteration_start_time_in_secs_; + // Number of consecutive steps where the minimizer loop computed a + // numerically invalid step. + int num_consecutive_invalid_steps_; }; } // namespace internal } // namespace ceres + #endif // CERES_INTERNAL_TRUST_REGION_MINIMIZER_H_ diff --git a/extern/ceres/internal/ceres/trust_region_step_evaluator.cc b/extern/ceres/internal/ceres/trust_region_step_evaluator.cc new file mode 100644 index 00000000000..c9167e623ef --- /dev/null +++ b/extern/ceres/internal/ceres/trust_region_step_evaluator.cc @@ -0,0 +1,107 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2016 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#include <algorithm> +#include "ceres/trust_region_step_evaluator.h" +#include "glog/logging.h" + +namespace ceres { +namespace internal { + +TrustRegionStepEvaluator::TrustRegionStepEvaluator( + const double initial_cost, + const int max_consecutive_nonmonotonic_steps) + : max_consecutive_nonmonotonic_steps_(max_consecutive_nonmonotonic_steps), + minimum_cost_(initial_cost), + current_cost_(initial_cost), + reference_cost_(initial_cost), + candidate_cost_(initial_cost), + accumulated_reference_model_cost_change_(0.0), + accumulated_candidate_model_cost_change_(0.0), + num_consecutive_nonmonotonic_steps_(0){ +} + +double TrustRegionStepEvaluator::StepQuality( + const double cost, + const double model_cost_change) const { + const double relative_decrease = (current_cost_ - cost) / model_cost_change; + const double historical_relative_decrease = + (reference_cost_ - cost) / + (accumulated_reference_model_cost_change_ + model_cost_change); + return std::max(relative_decrease, historical_relative_decrease); +} + +void TrustRegionStepEvaluator::StepAccepted( + const double cost, + const double model_cost_change) { + // Algorithm 10.1.2 from Trust Region Methods by Conn, Gould & + // Toint. + // + // Step 3a + current_cost_ = cost; + accumulated_candidate_model_cost_change_ += model_cost_change; + accumulated_reference_model_cost_change_ += model_cost_change; + + // Step 3b. + if (current_cost_ < minimum_cost_) { + minimum_cost_ = current_cost_; + num_consecutive_nonmonotonic_steps_ = 0; + candidate_cost_ = current_cost_; + accumulated_candidate_model_cost_change_ = 0.0; + } else { + // Step 3c. + ++num_consecutive_nonmonotonic_steps_; + if (current_cost_ > candidate_cost_) { + candidate_cost_ = current_cost_; + accumulated_candidate_model_cost_change_ = 0.0; + } + } + + // Step 3d. + // + // At this point we have made too many non-monotonic steps and + // we are going to reset the value of the reference iterate so + // as to force the algorithm to descend. + // + // Note: In the original algorithm by Toint, this step was only + // executed if the step was non-monotonic, but that would not handle + // the case of max_consecutive_nonmonotonic_steps = 0. The small + // modification of doing this always handles that corner case + // correctly. + if (num_consecutive_nonmonotonic_steps_ == + max_consecutive_nonmonotonic_steps_) { + reference_cost_ = candidate_cost_; + accumulated_reference_model_cost_change_ = + accumulated_candidate_model_cost_change_; + } +} + +} // namespace internal +} // namespace ceres diff --git a/extern/ceres/internal/ceres/trust_region_step_evaluator.h b/extern/ceres/internal/ceres/trust_region_step_evaluator.h new file mode 100644 index 00000000000..06df102a308 --- /dev/null +++ b/extern/ceres/internal/ceres/trust_region_step_evaluator.h @@ -0,0 +1,122 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2016 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: sameeragarwal@google.com (Sameer Agarwal) + +#ifndef CERES_INTERNAL_TRUST_REGION_STEP_EVALUATOR_H_ +#define CERES_INTERNAL_TRUST_REGION_STEP_EVALUATOR_H_ + +namespace ceres { +namespace internal { + +// The job of the TrustRegionStepEvaluator is to evaluate the quality +// of a step, i.e., how the cost of a step compares with the reduction +// in the objective of the trust region problem. +// +// Classic trust region methods are descent methods, in that they only +// accept a point if it strictly reduces the value of the objective +// function. They do this by measuring the quality of a step as +// +// cost_change / model_cost_change. +// +// Relaxing the monotonic descent requirement allows the algorithm to +// be more efficient in the long term at the cost of some local +// increase in the value of the objective function. +// +// This is because allowing for non-decreasing objective function +// values in a principled manner allows the algorithm to "jump over +// boulders" as the method is not restricted to move into narrow +// valleys while preserving its convergence properties. +// +// The parameter max_consecutive_nonmonotonic_steps controls the +// window size used by the step selection algorithm to accept +// non-monotonic steps. Setting this parameter to zero, recovers the +// classic montonic descent algorithm. +// +// Based on algorithm 10.1.2 (page 357) of "Trust Region +// Methods" by Conn Gould & Toint, or equations 33-40 of +// "Non-monotone trust-region algorithms for nonlinear +// optimization subject to convex constraints" by Phil Toint, +// Mathematical Programming, 77, 1997. +// +// Example usage: +// +// TrustRegionStepEvaluator* step_evaluator = ... +// +// cost = ... // Compute the non-linear objective function value. +// model_cost_change = ... // Change in the value of the trust region objective. +// if (step_evaluator->StepQuality(cost, model_cost_change) > threshold) { +// x = x + delta; +// step_evaluator->StepAccepted(cost, model_cost_change); +// } +class TrustRegionStepEvaluator { + public: + // initial_cost is as the name implies the cost of the starting + // state of the trust region minimizer. + // + // max_consecutive_nonmonotonic_steps controls the window size used + // by the step selection algorithm to accept non-monotonic + // steps. Setting this parameter to zero, recovers the classic + // montonic descent algorithm. + TrustRegionStepEvaluator(double initial_cost, + int max_consecutive_nonmonotonic_steps); + + // Return the quality of the step given its cost and the decrease in + // the cost of the model. model_cost_change has to be positive. + double StepQuality(double cost, double model_cost_change) const; + + // Inform the step evaluator that a step with the given cost and + // model_cost_change has been accepted by the trust region + // minimizer. + void StepAccepted(double cost, double model_cost_change); + + private: + const int max_consecutive_nonmonotonic_steps_; + // The minimum cost encountered up till now. + double minimum_cost_; + // The current cost of the trust region minimizer as informed by the + // last call to StepAccepted. + double current_cost_; + double reference_cost_; + double candidate_cost_; + // Accumulated model cost since the last time the reference model + // cost was updated, i.e., when a step with cost less than the + // current known minimum cost is accepted. + double accumulated_reference_model_cost_change_; + // Accumulated model cost since the last time the candidate model + // cost was updated, i.e., a non-monotonic step was taken with a + // cost that was greater than the current candidate cost. + double accumulated_candidate_model_cost_change_; + // Number of steps taken since the last time minimum_cost was updated. + int num_consecutive_nonmonotonic_steps_; +}; + +} // namespace internal +} // namespace ceres + +#endif // CERES_INTERNAL_TRUST_REGION_STEP_EVALUATOR_H_ diff --git a/extern/ceres/internal/ceres/trust_region_strategy.h b/extern/ceres/internal/ceres/trust_region_strategy.h index 9560e67459a..36e8e981cc0 100644 --- a/extern/ceres/internal/ceres/trust_region_strategy.h +++ b/extern/ceres/internal/ceres/trust_region_strategy.h @@ -86,20 +86,20 @@ class TrustRegionStrategy { struct PerSolveOptions { PerSolveOptions() : eta(0), - dump_filename_base(""), dump_format_type(TEXTFILE) { } // Forcing sequence for inexact solves. double eta; + DumpFormatType dump_format_type; + // If non-empty and dump_format_type is not CONSOLE, the trust // regions strategy will write the linear system to file(s) with // name starting with dump_filename_base. If dump_format_type is // CONSOLE then dump_filename_base will be ignored and the linear // system will be written to the standard error. std::string dump_filename_base; - DumpFormatType dump_format_type; }; struct Summary { diff --git a/extern/rangetree/CMakeLists.txt b/extern/rangetree/CMakeLists.txt index ba682233381..e8e9b15f8b1 100644 --- a/extern/rangetree/CMakeLists.txt +++ b/extern/rangetree/CMakeLists.txt @@ -21,10 +21,10 @@ set(INC ) set(SRC - range_tree.hh - range_tree_c_api.h + range_tree.h + intern/generic_alloc_impl.h - range_tree_c_api.cc + intern/range_tree.c ) blender_add_lib(extern_rangetree "${SRC}" "${INC}" "") diff --git a/extern/rangetree/README.blender b/extern/rangetree/README.blender index cb5967137ac..6a940c14d60 100644 --- a/extern/rangetree/README.blender +++ b/extern/rangetree/README.blender @@ -1,5 +1,5 @@ Project: RangeTree -URL: https://github.com/nicholasbishop/RangeTree -License: GPLv2+ -Upstream version: c4ecf6bb7dfd +URL: https://github.com/ideasman42/rangetree-c +License: Apache 2.0 +Upstream version: 40ebed8aa209 Local modifications: None diff --git a/extern/rangetree/README.org b/extern/rangetree/README.org deleted file mode 100644 index 46a4cedaf8f..00000000000 --- a/extern/rangetree/README.org +++ /dev/null @@ -1,13 +0,0 @@ -* Overview - Basic class for storing non-overlapping scalar ranges. Underlying - representation is a C++ STL set for fast lookups. - -* License - GPL version 2 or later (see COPYING) - -* Author Note - This implementation is intended for storing free unique IDs in a new - undo system for BMesh in Blender, but could be useful elsewhere. - -* Website - https://github.com/nicholasbishop/RangeTree diff --git a/extern/rangetree/intern/generic_alloc_impl.h b/extern/rangetree/intern/generic_alloc_impl.h new file mode 100644 index 00000000000..0f9f5184637 --- /dev/null +++ b/extern/rangetree/intern/generic_alloc_impl.h @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2016, Blender Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "Apache License") + * with the following modification; you may not use this file except in + * compliance with the Apache License and the following modification to it: + * Section 6. Trademarks. is deleted and replaced with: + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor + * and its affiliates, except as required to comply with Section 4(c) of + * the License and to reproduce the content of the NOTICE file. + * + * You may obtain a copy of the Apache License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Apache License with the above modification is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the Apache License for the specific + * language governing permissions and limitations under the Apache License. + */ + +/** + * Simple Memory Chunking Allocator + * ================================ + * + * Defines need to be set: + * - #TPOOL_IMPL_PREFIX: Prefix to use for the API. + * - #TPOOL_ALLOC_TYPE: Struct type this pool handles. + * - #TPOOL_STRUCT: Name for pool struct name. + * - #TPOOL_CHUNK_SIZE: Chunk size (optional), use 64kb when not defined. + * + * \note #TPOOL_ALLOC_TYPE must be at least ``sizeof(void *)``. + * + * Defines the API, uses #TPOOL_IMPL_PREFIX to prefix each function. + * + * - *_pool_create() + * - *_pool_destroy() + * - *_pool_clear() + * + * - *_pool_elem_alloc() + * - *_pool_elem_calloc() + * - *_pool_elem_free() + */ + +/* check we're not building directly */ +#if !defined(TPOOL_IMPL_PREFIX) || \ + !defined(TPOOL_ALLOC_TYPE) || \ + !defined(TPOOL_STRUCT) +# error "This file can't be compiled directly, include in another source file" +#endif + +#define _CONCAT_AUX(MACRO_ARG1, MACRO_ARG2) MACRO_ARG1 ## MACRO_ARG2 +#define _CONCAT(MACRO_ARG1, MACRO_ARG2) _CONCAT_AUX(MACRO_ARG1, MACRO_ARG2) +#define _TPOOL_PREFIX(id) _CONCAT(TPOOL_IMPL_PREFIX, _##id) + +/* local identifiers */ +#define pool_create _TPOOL_PREFIX(pool_create) +#define pool_destroy _TPOOL_PREFIX(pool_destroy) +#define pool_clear _TPOOL_PREFIX(pool_clear) + +#define pool_elem_alloc _TPOOL_PREFIX(pool_elem_alloc) +#define pool_elem_calloc _TPOOL_PREFIX(pool_elem_calloc) +#define pool_elem_free _TPOOL_PREFIX(pool_elem_free) + +/* private identifiers (only for this file, undefine after) */ +#define pool_alloc_chunk _TPOOL_PREFIX(pool_alloc_chunk) +#define TPoolChunk _TPOOL_PREFIX(TPoolChunk) +#define TPoolChunkElemFree _TPOOL_PREFIX(TPoolChunkElemFree) + +#ifndef TPOOL_CHUNK_SIZE +#define TPOOL_CHUNK_SIZE (1 << 16) /* 64kb */ +#define _TPOOL_CHUNK_SIZE_UNDEF +#endif + +#ifndef UNLIKELY +# ifdef __GNUC__ +# define UNLIKELY(x) __builtin_expect(!!(x), 0) +# else +# define UNLIKELY(x) (x) +# endif +#endif + +#ifdef __GNUC__ +# define MAYBE_UNUSED __attribute__((unused)) +#else +# define MAYBE_UNUSED +#endif + + +struct TPoolChunk { + struct TPoolChunk *prev; + unsigned int size; + unsigned int bufsize; + TPOOL_ALLOC_TYPE buf[0]; +}; + +struct TPoolChunkElemFree { + struct TPoolChunkElemFree *next; +}; + +struct TPOOL_STRUCT { + /* Always keep at least one chunk (never NULL) */ + struct TPoolChunk *chunk; + /* when NULL, allocate a new chunk */ + struct TPoolChunkElemFree *free; +}; + +/** + * Number of elems to include per #TPoolChunk when no reserved size is passed, + * or we allocate past the reserved number. + * + * \note Optimize number for 64kb allocs. + */ +#define _TPOOL_CHUNK_DEFAULT_NUM \ + (((1 << 16) - sizeof(struct TPoolChunk)) / sizeof(TPOOL_ALLOC_TYPE)) + + +/** \name Internal Memory Management + * \{ */ + +static struct TPoolChunk *pool_alloc_chunk( + unsigned int tot_elems, struct TPoolChunk *chunk_prev) +{ + struct TPoolChunk *chunk = malloc( + sizeof(struct TPoolChunk) + (sizeof(TPOOL_ALLOC_TYPE) * tot_elems)); + chunk->prev = chunk_prev; + chunk->bufsize = tot_elems; + chunk->size = 0; + return chunk; +} + +static TPOOL_ALLOC_TYPE *pool_elem_alloc(struct TPOOL_STRUCT *pool) +{ + TPOOL_ALLOC_TYPE *elem; + + if (pool->free) { + elem = (TPOOL_ALLOC_TYPE *)pool->free; + pool->free = pool->free->next; + } + else { + struct TPoolChunk *chunk = pool->chunk; + if (UNLIKELY(chunk->size == chunk->bufsize)) { + chunk = pool->chunk = pool_alloc_chunk(_TPOOL_CHUNK_DEFAULT_NUM, chunk); + } + elem = &chunk->buf[chunk->size++]; + } + + return elem; +} + +MAYBE_UNUSED +static TPOOL_ALLOC_TYPE *pool_elem_calloc(struct TPOOL_STRUCT *pool) +{ + TPOOL_ALLOC_TYPE *elem = pool_elem_alloc(pool); + memset(elem, 0, sizeof(*elem)); + return elem; +} + +static void pool_elem_free(struct TPOOL_STRUCT *pool, TPOOL_ALLOC_TYPE *elem) +{ + struct TPoolChunkElemFree *elem_free = (struct TPoolChunkElemFree *)elem; + elem_free->next = pool->free; + pool->free = elem_free; +} + +static void pool_create(struct TPOOL_STRUCT *pool, unsigned int tot_reserve) +{ + pool->chunk = pool_alloc_chunk((tot_reserve > 1) ? tot_reserve : _TPOOL_CHUNK_DEFAULT_NUM, NULL); + pool->free = NULL; +} + +MAYBE_UNUSED +static void pool_clear(struct TPOOL_STRUCT *pool) +{ + /* Remove all except the last chunk */ + while (pool->chunk->prev) { + struct TPoolChunk *chunk_prev = pool->chunk->prev; + free(pool->chunk); + pool->chunk = chunk_prev; + } + pool->chunk->size = 0; + pool->free = NULL; +} + +static void pool_destroy(struct TPOOL_STRUCT *pool) +{ + struct TPoolChunk *chunk = pool->chunk; + do { + struct TPoolChunk *chunk_prev; + chunk_prev = chunk->prev; + free(chunk); + chunk = chunk_prev; + } while (chunk); + + pool->chunk = NULL; + pool->free = NULL; +} + +/** \} */ + +#undef _TPOOL_CHUNK_DEFAULT_NUM +#undef _CONCAT_AUX +#undef _CONCAT +#undef _TPOOL_PREFIX + +#undef TPoolChunk +#undef TPoolChunkElemFree + +#ifdef _TPOOL_CHUNK_SIZE_UNDEF +# undef TPOOL_CHUNK_SIZE +# undef _TPOOL_CHUNK_SIZE_UNDEF +#endif diff --git a/extern/rangetree/intern/range_tree.c b/extern/rangetree/intern/range_tree.c new file mode 100644 index 00000000000..4c81036dceb --- /dev/null +++ b/extern/rangetree/intern/range_tree.c @@ -0,0 +1,873 @@ +/* + * Copyright (c) 2016, Campbell Barton. + * + * Licensed under the Apache License, Version 2.0 (the "Apache License") + * with the following modification; you may not use this file except in + * compliance with the Apache License and the following modification to it: + * Section 6. Trademarks. is deleted and replaced with: + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor + * and its affiliates, except as required to comply with Section 4(c) of + * the License and to reproduce the content of the NOTICE file. + * + * You may obtain a copy of the Apache License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Apache License with the above modification is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the Apache License for the specific + * language governing permissions and limitations under the Apache License. + */ + +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> + +#include <assert.h> + +#include "range_tree.h" + +typedef unsigned int uint; + +/* Use binary-tree for lookups, else fallback to full search */ +#define USE_BTREE +/* Use memory pool for nodes, else do individual allocations */ +#define USE_TPOOL + +/* Node representing a range in the RangeTreeUInt. */ +typedef struct Node { + struct Node *next, *prev; + + /* range (inclusive) */ + uint min, max; + +#ifdef USE_BTREE + /* Left leaning red-black tree, for reference implementation see: + * https://gitlab.com/ideasman42/btree-mini-py */ + struct Node *left, *right; + /* RED/BLACK */ + bool color; +#endif +} Node; + +#ifdef USE_TPOOL +/* rt_pool_* pool allocator */ +#define TPOOL_IMPL_PREFIX rt_node +#define TPOOL_ALLOC_TYPE Node +#define TPOOL_STRUCT ElemPool_Node +#include "generic_alloc_impl.h" +#undef TPOOL_IMPL_PREFIX +#undef TPOOL_ALLOC_TYPE +#undef TPOOL_STRUCT +#endif /* USE_TPOOL */ + +typedef struct LinkedList { + Node *first, *last; +} LinkedList; + +typedef struct RangeTreeUInt { + uint range[2]; + LinkedList list; +#ifdef USE_BTREE + Node *root; +#endif +#ifdef USE_TPOOL + struct ElemPool_Node epool; +#endif +} RangeTreeUInt; + +/* ------------------------------------------------------------------------- */ +/* List API */ + +static void list_push_front(LinkedList *list, Node *node) +{ + if (list->first != NULL) { + node->next = list->first; + node->next->prev = node; + node->prev = NULL; + } + else { + list->last = node; + } + list->first = node; +} + +static void list_push_back(LinkedList *list, Node *node) +{ + if (list->first != NULL) { + node->prev = list->last; + node->prev->next = node; + node->next = NULL; + } + else { + list->first = node; + } + list->last = node; +} + +static void list_push_after(LinkedList *list, Node *node_prev, Node *node_new) +{ + /* node_new before node_next */ + + /* empty list */ + if (list->first == NULL) { + list->first = node_new; + list->last = node_new; + return; + } + + /* insert at head of list */ + if (node_prev == NULL) { + node_new->prev = NULL; + node_new->next = list->first; + node_new->next->prev = node_new; + list->first = node_new; + return; + } + + /* at end of list */ + if (list->last == node_prev) { + list->last = node_new; + } + + node_new->next = node_prev->next; + node_new->prev = node_prev; + node_prev->next = node_new; + if (node_new->next) { + node_new->next->prev = node_new; + } +} + +static void list_push_before(LinkedList *list, Node *node_next, Node *node_new) +{ + /* node_new before node_next */ + + /* empty list */ + if (list->first == NULL) { + list->first = node_new; + list->last = node_new; + return; + } + + /* insert at end of list */ + if (node_next == NULL) { + node_new->prev = list->last; + node_new->next = NULL; + list->last->next = node_new; + list->last = node_new; + return; + } + + /* at beginning of list */ + if (list->first == node_next) { + list->first = node_new; + } + + node_new->next = node_next; + node_new->prev = node_next->prev; + node_next->prev = node_new; + if (node_new->prev) { + node_new->prev->next = node_new; + } +} + +static void list_remove(LinkedList *list, Node *node) +{ + if (node->next != NULL) { + node->next->prev = node->prev; + } + if (node->prev != NULL) { + node->prev->next = node->next; + } + + if (list->last == node) { + list->last = node->prev; + } + if (list->first == node) { + list->first = node->next; + } +} + +static void list_clear(LinkedList *list) +{ + list->first = NULL; + list->last = NULL; +} + +/* end list API */ + + +/* forward declarations */ +static void rt_node_free(RangeTreeUInt *rt, Node *node); + + +#ifdef USE_BTREE + +#ifdef DEBUG +static bool rb_is_balanced_root(const Node *root); +#endif + +/* ------------------------------------------------------------------------- */ +/* Internal BTree API + * + * Left-leaning red-black tree. + */ + +/* use minimum, could use max too since nodes never overlap */ +#define KEY(n) ((n)->min) + +enum { + RED = 0, + BLACK = 1, +}; + + +static bool is_red(const Node *node) +{ + return (node && (node->color == RED)); +} + +static int key_cmp(uint key1, uint key2) +{ + return (key1 == key2) ? 0 : ((key1 < key2) ? -1 : 1); +} + +/* removed from the tree */ +static void rb_node_invalidate(Node *node) +{ +#ifdef DEBUG + node->left = NULL; + node->right = NULL; + node->color = false; +#else + (void)node; +#endif +} + +static void rb_flip_color(Node *node) +{ + node->color ^= 1; + node->left->color ^= 1; + node->right->color ^= 1; +} + +static Node *rb_rotate_left(Node *left) +{ + /* Make a right-leaning 3-node lean to the left. */ + Node *right = left->right; + left->right = right->left; + right->left = left; + right->color = left->color; + left->color = RED; + return right; +} + +static Node *rb_rotate_right(Node *right) +{ + /* Make a left-leaning 3-node lean to the right. */ + Node *left = right->left; + right->left = left->right; + left->right = right; + left->color = right->color; + right->color = RED; + return left; +} + +/* Fixup colors when insert happened */ +static Node *rb_fixup_insert(Node *node) +{ + if (is_red(node->right) && !is_red(node->left)) { + node = rb_rotate_left(node); + } + if (is_red(node->left) && is_red(node->left->left)) { + node = rb_rotate_right(node); + } + + if (is_red(node->left) && is_red(node->right)) { + rb_flip_color(node); + } + + return node; +} + +static Node *rb_insert_recursive(Node *node, Node *node_to_insert) +{ + if (node == NULL) { + return node_to_insert; + } + + const int cmp = key_cmp(KEY(node_to_insert), KEY(node)); + if (cmp == 0) { + /* caller ensures no collisions */ + assert(0); + } + else if (cmp == -1) { + node->left = rb_insert_recursive(node->left, node_to_insert); + } + else { + node->right = rb_insert_recursive(node->right, node_to_insert); + } + + return rb_fixup_insert(node); +} + +static Node *rb_insert_root(Node *root, Node *node_to_insert) +{ + root = rb_insert_recursive(root, node_to_insert); + root->color = BLACK; + return root; +} + +static Node *rb_move_red_to_left(Node *node) +{ + /* Assuming that h is red and both h->left and h->left->left + * are black, make h->left or one of its children red. + */ + rb_flip_color(node); + if (node->right && is_red(node->right->left)) { + node->right = rb_rotate_right(node->right); + node = rb_rotate_left(node); + rb_flip_color(node); + } + return node; +} + +static Node *rb_move_red_to_right(Node *node) +{ + /* Assuming that h is red and both h->right and h->right->left + * are black, make h->right or one of its children red. + */ + rb_flip_color(node); + if (node->left && is_red(node->left->left)) { + node = rb_rotate_right(node); + rb_flip_color(node); + } + return node; +} + +/* Fixup colors when remove happened */ +static Node *rb_fixup_remove(Node *node) +{ + if (is_red(node->right)) { + node = rb_rotate_left(node); + } + if (is_red(node->left) && is_red(node->left->left)) { + node = rb_rotate_right(node); + } + if (is_red(node->left) && is_red(node->right)) { + rb_flip_color(node); + } + return node; +} + +static Node *rb_pop_min_recursive(Node *node, Node **r_node_pop) +{ + if (node == NULL) { + return NULL; + } + if (node->left == NULL) { + rb_node_invalidate(node); + *r_node_pop = node; + return NULL; + } + if ((!is_red(node->left)) && (!is_red(node->left->left))) { + node = rb_move_red_to_left(node); + } + node->left = rb_pop_min_recursive(node->left, r_node_pop); + return rb_fixup_remove(node); +} + +static Node *rb_remove_recursive(Node *node, const Node *node_to_remove) +{ + if (node == NULL) { + return NULL; + } + if (key_cmp(KEY(node_to_remove), KEY(node)) == -1) { + if (node->left != NULL) { + if ((!is_red(node->left)) && (!is_red(node->left->left))) { + node = rb_move_red_to_left(node); + } + } + node->left = rb_remove_recursive(node->left, node_to_remove); + } + else { + if (is_red(node->left)) { + node = rb_rotate_right(node); + } + if ((node == node_to_remove) && (node->right == NULL)) { + rb_node_invalidate(node); + return NULL; + } + assert(node->right != NULL); + if ((!is_red(node->right)) && (!is_red(node->right->left))) { + node = rb_move_red_to_right(node); + } + + if (node == node_to_remove) { + /* minor improvement over original method: + * no need to double lookup min */ + Node *node_free; /* will always be set */ + node->right = rb_pop_min_recursive(node->right, &node_free); + + node_free->left = node->left; + node_free->right = node->right; + node_free->color = node->color; + + rb_node_invalidate(node); + node = node_free; + } + else { + node->right = rb_remove_recursive(node->right, node_to_remove); + } + } + return rb_fixup_remove(node); +} + +static Node *rb_btree_remove(Node *root, const Node *node_to_remove) +{ + root = rb_remove_recursive(root, node_to_remove); + if (root != NULL) { + root->color = BLACK; + } + return root; +} + +/* + * Returns the node closest to and including 'key', + * excluding anything below. + */ +static Node *rb_get_or_upper_recursive(Node *n, const uint key) +{ + if (n == NULL) { + return NULL; + } + const int cmp_upper = key_cmp(KEY(n), key); + if (cmp_upper == 0) { + return n; // exact match + } + else if (cmp_upper == 1) { + assert(KEY(n) >= key); + Node *n_test = rb_get_or_upper_recursive(n->left, key); + return n_test ? n_test : n; + } + else { // cmp_upper == -1 + return rb_get_or_upper_recursive(n->right, key); + } +} + +/* + * Returns the node closest to and including 'key', + * excluding anything above. + */ +static Node *rb_get_or_lower_recursive(Node *n, const uint key) +{ + if (n == NULL) { + return NULL; + } + const int cmp_lower = key_cmp(KEY(n), key); + if (cmp_lower == 0) { + return n; // exact match + } + else if (cmp_lower == -1) { + assert(KEY(n) <= key); + Node *n_test = rb_get_or_lower_recursive(n->right, key); + return n_test ? n_test : n; + } + else { // cmp_lower == 1 + return rb_get_or_lower_recursive(n->left, key); + } +} + +#ifdef DEBUG + +static bool rb_is_balanced_recursive(const Node *node, int black) +{ + // Does every path from the root to a leaf have the given number + // of black links? + if (node == NULL) { + return black == 0; + } + if (!is_red(node)) { + black--; + } + return rb_is_balanced_recursive(node->left, black) && + rb_is_balanced_recursive(node->right, black); +} + +static bool rb_is_balanced_root(const Node *root) +{ + // Do all paths from root to leaf have same number of black edges? + int black = 0; // number of black links on path from root to min + const Node *node = root; + while (node != NULL) { + if (!is_red(node)) { + black++; + } + node = node->left; + } + return rb_is_balanced_recursive(root, black); +} + +#endif // DEBUG + + +/* End BTree API */ +#endif // USE_BTREE + + +/* ------------------------------------------------------------------------- */ +/* Internal RangeTreeUInt API */ + +#ifdef _WIN32 +#define inline __inline +#endif + +static inline Node *rt_node_alloc(RangeTreeUInt *rt) +{ +#ifdef USE_TPOOL + return rt_node_pool_elem_alloc(&rt->epool); +#else + (void)rt; + return malloc(sizeof(Node)); +#endif +} + +static Node *rt_node_new(RangeTreeUInt *rt, uint min, uint max) +{ + Node *node = rt_node_alloc(rt); + + assert(min <= max); + node->prev = NULL; + node->next = NULL; + node->min = min; + node->max = max; +#ifdef USE_BTREE + node->left = NULL; + node->right = NULL; +#endif + return node; +} + +static void rt_node_free(RangeTreeUInt *rt, Node *node) +{ +#ifdef USE_TPOOL + rt_node_pool_elem_free(&rt->epool, node); +#else + (void)rt; + free(node); +#endif +} + +#ifdef USE_BTREE +static void rt_btree_insert(RangeTreeUInt *rt, Node *node) +{ + node->color = RED; + node->left = NULL; + node->right = NULL; + rt->root = rb_insert_root(rt->root, node); +} +#endif + +static void rt_node_add_back(RangeTreeUInt *rt, Node *node) +{ + list_push_back(&rt->list, node); +#ifdef USE_BTREE + rt_btree_insert(rt, node); +#endif +} +static void rt_node_add_front(RangeTreeUInt *rt, Node *node) +{ + list_push_front(&rt->list, node); +#ifdef USE_BTREE + rt_btree_insert(rt, node); +#endif +} +static void rt_node_add_before(RangeTreeUInt *rt, Node *node_next, Node *node) +{ + list_push_before(&rt->list, node_next, node); +#ifdef USE_BTREE + rt_btree_insert(rt, node); +#endif +} +static void rt_node_add_after(RangeTreeUInt *rt, Node *node_prev, Node *node) +{ + list_push_after(&rt->list, node_prev, node); +#ifdef USE_BTREE + rt_btree_insert(rt, node); +#endif +} + +static void rt_node_remove(RangeTreeUInt *rt, Node *node) +{ + list_remove(&rt->list, node); +#ifdef USE_BTREE + rt->root = rb_btree_remove(rt->root, node); +#endif + rt_node_free(rt, node); +} + +static Node *rt_find_node_from_value(RangeTreeUInt *rt, const uint value) +{ +#ifdef USE_BTREE + Node *node = rb_get_or_lower_recursive(rt->root, value); + if (node != NULL) { + if ((value >= node->min) && (value <= node->max)) { + return node; + } + } + return NULL; +#else + for (Node *node = rt->list.first; node; node = node->next) { + if ((value >= node->min) && (value <= node->max)) { + return node; + } + } + return NULL; +#endif // USE_BTREE +} + +static void rt_find_node_pair_around_value(RangeTreeUInt *rt, const uint value, + Node **r_node_prev, Node **r_node_next) +{ + if (value < rt->list.first->min) { + *r_node_prev = NULL; + *r_node_next = rt->list.first; + return; + } + else if (value > rt->list.last->max) { + *r_node_prev = rt->list.last; + *r_node_next = NULL; + return; + } + else { +#ifdef USE_BTREE + Node *node_next = rb_get_or_upper_recursive(rt->root, value); + if (node_next != NULL) { + Node *node_prev = node_next->prev; + if ((node_prev->max < value) && (value < node_next->min)) { + *r_node_prev = node_prev; + *r_node_next = node_next; + return; + } + } +#else + Node *node_prev = rt->list.first; + Node *node_next; + while ((node_next = node_prev->next)) { + if ((node_prev->max < value) && (value < node_next->min)) { + *r_node_prev = node_prev; + *r_node_next = node_next; + return; + } + node_prev = node_next; + } +#endif // USE_BTREE + } + *r_node_prev = NULL; + *r_node_next = NULL; +} + + +/* ------------------------------------------------------------------------- */ +/* Public API */ + +static RangeTreeUInt *rt_create_empty(uint min, uint max) +{ + RangeTreeUInt *rt = malloc(sizeof(*rt)); + rt->range[0] = min; + rt->range[1] = max; + + list_clear(&rt->list); + +#ifdef USE_BTREE + rt->root = NULL; +#endif +#ifdef USE_TPOOL + rt_node_pool_create(&rt->epool, 512); +#endif + + return rt; +} + +RangeTreeUInt *range_tree_uint_alloc(uint min, uint max) +{ + RangeTreeUInt *rt = rt_create_empty(min, max); + + Node *node = rt_node_new(rt, min, max); + rt_node_add_front(rt, node); + return rt; +} + +void range_tree_uint_free(RangeTreeUInt *rt) +{ +#ifdef DEBUG +#ifdef USE_BTREE + assert(rb_is_balanced_root(rt->root)); +#endif +#endif + +#ifdef USE_TPOOL + + rt_node_pool_destroy(&rt->epool); +#else + for (Node *node = rt->list.first, *node_next; node; node = node_next) { + node_next = node->next; + rt_node_free(rt, node); + } +#endif + + free(rt); +} + +#ifdef USE_BTREE +static Node *rt_copy_recursive(RangeTreeUInt *rt_dst, const Node *node_src) +{ + if (node_src == NULL) { + return NULL; + } + + Node *node_dst = rt_node_alloc(rt_dst); + + *node_dst = *node_src; + node_dst->left = rt_copy_recursive(rt_dst, node_dst->left); + list_push_back(&rt_dst->list, node_dst); + node_dst->right = rt_copy_recursive(rt_dst, node_dst->right); + + return node_dst; +} +#endif // USE_BTREE + +RangeTreeUInt *range_tree_uint_copy(const RangeTreeUInt *rt_src) +{ + RangeTreeUInt *rt_dst = rt_create_empty(rt_src->range[0], rt_src->range[1]); +#ifdef USE_BTREE + rt_dst->root = rt_copy_recursive(rt_dst, rt_src->root); +#else + for (Node *node_src = rt_src->list.first; node_src; node_src = node_src->next) { + Node *node_dst = rt_node_alloc(rt_dst); + *node_dst = *node_src; + list_push_back(&rt_dst->list, node_dst); + } +#endif + return rt_dst; +} + +/** + * Return true if the tree has the value (not taken). + */ +bool range_tree_uint_has(RangeTreeUInt *rt, const uint value) +{ + assert(value >= rt->range[0] && value <= rt->range[1]); + Node *node = rt_find_node_from_value(rt, value); + return (node != NULL); +} + +static void range_tree_uint_take_impl(RangeTreeUInt *rt, const uint value, Node *node) +{ + assert(node == rt_find_node_from_value(rt, value)); + if (node->min == value) { + if (node->max != value) { + node->min += 1; + } + else { + assert(node->min == node->max); + rt_node_remove(rt, node); + } + } + else if (node->max == value) { + node->max -= 1; + } + else { + Node *node_next = rt_node_new(rt, value + 1, node->max); + node->max = value - 1; + rt_node_add_after(rt, node, node_next); + } +} + +void range_tree_uint_take(RangeTreeUInt *rt, const uint value) +{ + Node *node = rt_find_node_from_value(rt, value); + assert(node != NULL); + range_tree_uint_take_impl(rt, value, node); +} + +bool range_tree_uint_retake(RangeTreeUInt *rt, const uint value) +{ + Node *node = rt_find_node_from_value(rt, value); + if (node != NULL) { + range_tree_uint_take_impl(rt, value, node); + return true; + } + else { + return false; + } +} + +uint range_tree_uint_take_any(RangeTreeUInt *rt) +{ + Node *node = node = rt->list.first; + uint value = node->min; + if (value == node->max) { + rt_node_remove(rt, node); + } + else { + node->min += 1; + } + return value; +} + +void range_tree_uint_release(RangeTreeUInt *rt, const uint value) +{ + bool touch_prev, touch_next; + Node *node_prev, *node_next; + + if (rt->list.first != NULL) { + rt_find_node_pair_around_value(rt, value, &node_prev, &node_next); + /* the value must have been already taken */ + assert(node_prev || node_next); + + /* Cases: + * 1) fill the gap between prev & next (two spans into one span). + * 2) touching prev, (grow node_prev->max up one). + * 3) touching next, (grow node_next->min down one). + * 4) touching neither, add a new segment. */ + touch_prev = (node_prev != NULL && node_prev->max + 1 == value); + touch_next = (node_next != NULL && node_next->min - 1 == value); + } + else { + // we could handle this case (4) inline, + // since its not a common case - use regular logic. + node_prev = node_next = NULL; + touch_prev = false; + touch_next = false; + } + + if (touch_prev && touch_next) { // 1) + node_prev->max = node_next->max; + rt_node_remove(rt, node_next); + } + else if (touch_prev) { // 2) + assert(node_prev->max + 1 == value); + node_prev->max = value; + } + else if (touch_next) { // 3) + assert(node_next->min - 1 == value); + node_next->min = value; + } + else { // 4) + Node *node_new = rt_node_new(rt, value, value); + if (node_prev != NULL) { + rt_node_add_after(rt, node_prev, node_new); + } + else if (node_next != NULL) { + rt_node_add_before(rt, node_next, node_new); + } + else { + assert(rt->list.first == NULL); + rt_node_add_back(rt, node_new); + } + } +} diff --git a/extern/rangetree/range_tree.h b/extern/rangetree/range_tree.h new file mode 100644 index 00000000000..b46832b5cdb --- /dev/null +++ b/extern/rangetree/range_tree.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016, Campbell Barton. + * + * Licensed under the Apache License, Version 2.0 (the "Apache License") + * with the following modification; you may not use this file except in + * compliance with the Apache License and the following modification to it: + * Section 6. Trademarks. is deleted and replaced with: + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor + * and its affiliates, except as required to comply with Section 4(c) of + * the License and to reproduce the content of the NOTICE file. + * + * You may obtain a copy of the Apache License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Apache License with the above modification is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the Apache License for the specific + * language governing permissions and limitations under the Apache License. + */ + +#ifndef __RANGE_TREE_H__ +#define __RANGE_TREE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct RangeTreeUInt RangeTreeUInt; + +struct RangeTreeUInt *range_tree_uint_alloc(unsigned int min, unsigned int max); +void range_tree_uint_free(struct RangeTreeUInt *rt); +struct RangeTreeUInt *range_tree_uint_copy(const struct RangeTreeUInt *rt_src); + +bool range_tree_uint_has(struct RangeTreeUInt *rt, const unsigned int value); +void range_tree_uint_take(struct RangeTreeUInt *rt, const unsigned int value); +bool range_tree_uint_retake(struct RangeTreeUInt *rt, const unsigned int value); +unsigned int range_tree_uint_take_any(struct RangeTreeUInt *rt); +void range_tree_uint_release(struct RangeTreeUInt *rt, const unsigned int value); + +#ifdef __cplusplus +} +#endif + +#endif /* __RANGE_TREE_H__ */ diff --git a/extern/rangetree/range_tree.hh b/extern/rangetree/range_tree.hh deleted file mode 100644 index b247a0c6a1e..00000000000 --- a/extern/rangetree/range_tree.hh +++ /dev/null @@ -1,251 +0,0 @@ -/* This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#include <cassert> -#include <climits> -#include <iostream> -#include <set> - -#ifndef RANGE_TREE_DEBUG_PRINT_FUNCTION -# define RANGE_TREE_DEBUG_PRINT_FUNCTION 0 -#endif - -template <typename T> -struct RangeTree { - struct Range { - Range(T min_, T max_) - : min(min_), max(max_), single(min_ == max_) { - assert(min_ <= max_); - } - - Range(T t) - : min(t), max(t), single(true) - {} - - Range& operator=(const Range& v) { - *this = v; - return *this; - } - - bool operator<(const Range& v) const { - return max < v.min; - } - - const T min; - const T max; - const bool single; - }; - - typedef std::set<Range> Tree; - typedef typename Tree::iterator TreeIter; - typedef typename Tree::reverse_iterator TreeIterReverse; - typedef typename Tree::const_iterator TreeIterConst; - - /* Initialize with a single range from 'min' to 'max', inclusive. */ - RangeTree(T min, T max) { - tree.insert(Range(min, max)); - } - - /* Initialize with a single range from 0 to 'max', inclusive. */ - RangeTree(T max) { - tree.insert(Range(0, max)); - } - - RangeTree(const RangeTree<T>& src) { - tree = src.tree; - } - - /* Remove 't' from the associated range in the tree. Precondition: - a range including 't' must exist in the tree. */ - void take(T t) { - #if RANGE_TREE_DEBUG_PRINT_FUNCTION - std::cout << __func__ << "(" << t << ")\n"; - #endif - - /* Find the range that includes 't' and its neighbors */ - TreeIter iter = tree.find(Range(t)); - assert(iter != tree.end()); - Range cur = *iter; - - /* Remove the original range (note that this does not - invalidate the prev/next iterators) */ - tree.erase(iter); - - /* Construct two new ranges that together cover the original - range, except for 't' */ - if (t > cur.min) - tree.insert(Range(cur.min, t - 1)); - if (t + 1 <= cur.max) - tree.insert(Range(t + 1, cur.max)); - } - - /* clone of 'take' that checks if the item exists */ - bool retake(T t) { - #if RANGE_TREE_DEBUG_PRINT_FUNCTION - std::cout << __func__ << "(" << t << ")\n"; - #endif - - TreeIter iter = tree.find(Range(t)); - if (iter == tree.end()) { - return false; - } - - Range cur = *iter; - tree.erase(iter); - if (t > cur.min) - tree.insert(Range(cur.min, t - 1)); - if (t + 1 <= cur.max) - tree.insert(Range(t + 1, cur.max)); - - return true; - } - - - /* Take the first element out of the first range in the - tree. Precondition: tree must not be empty. */ - T take_any() { - #if RANGE_TREE_DEBUG_PRINT_FUNCTION - std::cout << __func__ << "()\n"; - #endif - - /* Find the first element */ - TreeIter iter = tree.begin(); - assert(iter != tree.end()); - T first = iter->min; - - /* Take the first element */ - take(first); - return first; - } - - /* Return 't' to the tree, either expanding/merging existing - ranges or adding a range to cover it. Precondition: 't' cannot - be in an existing range. */ - void release(T t) { - #if RANGE_TREE_DEBUG_PRINT_FUNCTION - std::cout << __func__ << "(" << t << ")\n"; - #endif - - /* TODO: these cases should be simplified/unified */ - - TreeIter right = tree.upper_bound(t); - if (right != tree.end()) { - TreeIter left = right; - if (left != tree.begin()) - --left; - - if (left == right) { - /* 't' lies before any existing ranges */ - if (t + 1 == left->min) { - /* 't' lies directly before the first range, - resize and replace that range */ - const Range r(t, left->max); - tree.erase(left); - tree.insert(r); - } - else { - /* There's a gap between 't' and the first range, - add a new range */ - tree.insert(Range(t)); - } - } - else if ((left->max + 1 == t) && - (t + 1 == right->min)) { - /* 't' fills a hole. Remove left and right, and insert a - new range that covers both. */ - const Range r(left->min, right->max); - tree.erase(left); - tree.erase(right); - tree.insert(r); - } - else if (left->max + 1 == t) { - /* 't' lies directly after 'left' range, resize and - replace that range */ - const Range r(left->min, t); - tree.erase(left); - tree.insert(r); - } - else if (t + 1 == right->min) { - /* 't' lies directly before 'right' range, resize and - replace that range */ - const Range r(t, right->max); - tree.erase(right); - tree.insert(r); - } - else { - /* There's a gap between 't' and both adjacent ranges, - add a new range */ - tree.insert(Range(t)); - } - } - else { - /* 't' lies after any existing ranges */ - right = tree.end(); - right--; - if (right->max + 1 == t) { - /* 't' lies directly after last range, resize and - replace that range */ - const Range r(right->min, t); - tree.erase(right); - tree.insert(r); - } - else { - /* There's a gap between the last range and 't', add a - new range */ - tree.insert(Range(t)); - } - } - } - - bool has(T t) const { - TreeIterConst iter = tree.find(Range(t)); - return (iter != tree.end()) && (t <= iter->max); - } - - bool has_range(T min, T max) const { - TreeIterConst iter = tree.find(Range(min, max)); - return (iter != tree.end()) && (min == iter->min && max == iter->max); - } - - bool empty() const { - return tree.empty(); - } - - int size() const { - return tree.size(); - } - - void print() const { - std::cout << "RangeTree:\n"; - for (TreeIterConst iter = tree.begin(); iter != tree.end(); ++iter) { - const Range& r = *iter; - if (r.single) - std::cout << " [" << r.min << "]\n"; - else - std::cout << " [" << r.min << ", " << r.max << "]\n"; - } - if (empty()) - std::cout << " <empty>"; - std::cout << "\n"; - } - - unsigned int allocation_lower_bound() const { - return tree.size() * sizeof(Range); - } - -private: - Tree tree; -}; diff --git a/extern/rangetree/range_tree_c_api.cc b/extern/rangetree/range_tree_c_api.cc deleted file mode 100644 index f040b5eaeb6..00000000000 --- a/extern/rangetree/range_tree_c_api.cc +++ /dev/null @@ -1,92 +0,0 @@ -/* This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#include "range_tree.hh" - -/* Give RangeTreeUInt a real type rather than the opaque struct type - defined for external use. */ -#define RANGE_TREE_C_API_INTERNAL -typedef RangeTree<unsigned> RangeTreeUInt; - -#include "range_tree_c_api.h" - -RangeTreeUInt *range_tree_uint_alloc(unsigned min, unsigned max) -{ - return new RangeTreeUInt(min, max); -} - -RangeTreeUInt *range_tree_uint_copy(RangeTreeUInt *src) -{ - return new RangeTreeUInt(*src); -} - -void range_tree_uint_free(RangeTreeUInt *rt) -{ - delete rt; -} - -void range_tree_uint_take(RangeTreeUInt *rt, unsigned v) -{ - rt->take(v); -} - -bool range_tree_uint_retake(RangeTreeUInt *rt, unsigned v) -{ - return rt->retake(v); -} - -unsigned range_tree_uint_take_any(RangeTreeUInt *rt) -{ - return rt->take_any(); -} - -void range_tree_uint_release(RangeTreeUInt *rt, unsigned v) -{ - rt->release(v); -} - -bool range_tree_uint_has(const RangeTreeUInt *rt, unsigned v) -{ - return rt->has(v); -} - -bool range_tree_uint_has_range( - const RangeTreeUInt *rt, - unsigned vmin, - unsigned vmax) -{ - return rt->has_range(vmin, vmax); -} - -bool range_tree_uint_empty(const RangeTreeUInt *rt) -{ - return rt->empty(); -} - -unsigned range_tree_uint_size(const RangeTreeUInt *rt) -{ - return rt->size(); -} - -void range_tree_uint_print(const RangeTreeUInt *rt) -{ - rt->print(); -} - -unsigned int range_tree_uint_allocation_lower_bound(const RangeTreeUInt *rt) -{ - return rt->allocation_lower_bound(); -} diff --git a/extern/rangetree/range_tree_c_api.h b/extern/rangetree/range_tree_c_api.h deleted file mode 100644 index 6abfb6bd55e..00000000000 --- a/extern/rangetree/range_tree_c_api.h +++ /dev/null @@ -1,62 +0,0 @@ -/* This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. -*/ - -#ifndef __RANGE_TREE_C_API_H__ -#define __RANGE_TREE_C_API_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -/* Simple C-accessible wrapper for RangeTree<unsigned> */ - -#ifndef RANGE_TREE_C_API_INTERNAL -typedef struct RangeTreeUInt RangeTreeUInt; -#endif - -RangeTreeUInt *range_tree_uint_alloc(unsigned min, unsigned max); - -RangeTreeUInt *range_tree_uint_copy(RangeTreeUInt *src); - -void range_tree_uint_free(RangeTreeUInt *rt); - -void range_tree_uint_take(RangeTreeUInt *rt, unsigned v); - -bool range_tree_uint_retake(RangeTreeUInt *rt, unsigned v); - -unsigned range_tree_uint_take_any(RangeTreeUInt *rt); - -void range_tree_uint_release(RangeTreeUInt *rt, unsigned v); - -bool range_tree_uint_has(const RangeTreeUInt *rt, unsigned v); - -bool range_tree_uint_has_range( - const RangeTreeUInt *rt, - unsigned vmin, unsigned vmax); - -bool range_tree_uint_empty(const RangeTreeUInt *rt); - -unsigned range_tree_uint_size(const RangeTreeUInt *rt); - -void range_tree_uint_print(const RangeTreeUInt *rt); - -unsigned int range_tree_uint_allocation_lower_bound(const RangeTreeUInt *rt); - -#ifdef __cplusplus -} -#endif - -#endif /* __RANGE_TREE_C_API_H__ */ diff --git a/intern/audaspace/Python/AUD_PyAPI.cpp b/intern/audaspace/Python/AUD_PyAPI.cpp index de5c0a2f463..6d4939bf96c 100644 --- a/intern/audaspace/Python/AUD_PyAPI.cpp +++ b/intern/audaspace/Python/AUD_PyAPI.cpp @@ -2698,7 +2698,7 @@ Device_set_doppler_factor(Device *self, PyObject *args, void* nothing) PyDoc_STRVAR(M_aud_Device_distance_model_doc, "The distance model of the device.\n\n" - ".. seealso:: http://connect.creativelabs.com/openal/Documentation/OpenAL%201.1%20Specification.htm#_Toc199835864"); + ".. seealso:: `OpenAL documentation <https://www.openal.org/documentation>`"); static PyObject * Device_get_distance_model(Device *self, void* nothing) diff --git a/intern/audaspace/intern/AUD_C-API.cpp b/intern/audaspace/intern/AUD_C-API.cpp index b326c9f4281..52cf256147f 100644 --- a/intern/audaspace/intern/AUD_C-API.cpp +++ b/intern/audaspace/intern/AUD_C-API.cpp @@ -155,18 +155,18 @@ AUD_Device* AUD_init(const char* device, AUD_DeviceSpecs specs, int buffersize, } #endif #ifdef WITH_JACK - else if(dname == "Jack") + else if(dname == "JACK") { #ifdef __APPLE__ struct stat st; if (stat("/Library/Frameworks/Jackmp.framework", &st) != 0) { - printf("Warning: Jack Framework not installed\n"); + printf("Warning: JACK Framework not installed\n"); return NULL; } else #endif if (!AUD_jack_supported()) { - printf("Warning: Jack cllient not installed\n"); + printf("Warning: JACK cllient not installed\n"); return NULL; } else { diff --git a/intern/audaspace/intern/AUD_C-API.h b/intern/audaspace/intern/AUD_C-API.h index bdbe751b140..cc9059a8e09 100644 --- a/intern/audaspace/intern/AUD_C-API.h +++ b/intern/audaspace/intern/AUD_C-API.h @@ -61,7 +61,7 @@ typedef struct #endif /** - * Initializes audio rutines (FFMPEG/Jack if it is enabled). + * Initializes audio routines (FFMPEG/JACK if it is enabled). */ extern void AUD_initOnce(void); diff --git a/intern/audaspace/intern/AUD_Mixer.cpp b/intern/audaspace/intern/AUD_Mixer.cpp index 380affa6bd1..78dfaddd27a 100644 --- a/intern/audaspace/intern/AUD_Mixer.cpp +++ b/intern/audaspace/intern/AUD_Mixer.cpp @@ -95,6 +95,21 @@ void AUD_Mixer::mix(sample_t* buffer, int start, int length, float volume) out[i + start] += buffer[i] * volume; } +void AUD_Mixer::mix(sample_t* buffer, int start, int length, float volume_to, float volume_from) +{ + sample_t* out = m_buffer.getBuffer(); + + length = (std::min(m_length, length + start) - start); + + for(int i = 0; i < length; i++) + { + float volume = volume_from * (1.0f - i / float(length)) + volume_to * (i / float(length)); + + for(int c = 0; c < m_specs.channels; c++) + out[(i + start) * m_specs.channels + c] += buffer[i * m_specs.channels + c] * volume; + } +} + void AUD_Mixer::read(data_t* buffer, float volume) { sample_t* out = m_buffer.getBuffer(); diff --git a/intern/audaspace/intern/AUD_Mixer.h b/intern/audaspace/intern/AUD_Mixer.h index 3dd03b0a3fe..0735fee715b 100644 --- a/intern/audaspace/intern/AUD_Mixer.h +++ b/intern/audaspace/intern/AUD_Mixer.h @@ -95,6 +95,8 @@ public: */ void mix(sample_t* buffer, int start, int length, float volume); + void mix(sample_t* buffer, int start, int length, float volume_to, float volume_from); + /** * Writes the mixing buffer into an output buffer. * \param buffer The target buffer for superposing. diff --git a/intern/audaspace/intern/AUD_SoftwareDevice.cpp b/intern/audaspace/intern/AUD_SoftwareDevice.cpp index c4277a6d02e..15594d340be 100644 --- a/intern/audaspace/intern/AUD_SoftwareDevice.cpp +++ b/intern/audaspace/intern/AUD_SoftwareDevice.cpp @@ -89,7 +89,7 @@ bool AUD_SoftwareDevice::AUD_SoftwareHandle::pause(bool keep) } AUD_SoftwareDevice::AUD_SoftwareHandle::AUD_SoftwareHandle(AUD_SoftwareDevice* device, boost::shared_ptr<AUD_IReader> reader, boost::shared_ptr<AUD_PitchReader> pitch, boost::shared_ptr<AUD_ResampleReader> resampler, boost::shared_ptr<AUD_ChannelMapperReader> mapper, bool keep) : - m_reader(reader), m_pitch(pitch), m_resampler(resampler), m_mapper(mapper), m_keep(keep), m_user_pitch(1.0f), m_user_volume(1.0f), m_user_pan(0.0f), m_volume(1.0f), m_loopcount(0), + m_reader(reader), m_pitch(pitch), m_resampler(resampler), m_mapper(mapper), m_keep(keep), m_user_pitch(1.0f), m_user_volume(1.0f), m_user_pan(0.0f), m_volume(1.0f), m_old_volume(1.0f), m_loopcount(0), m_relative(true), m_volume_max(1.0f), m_volume_min(0), m_distance_max(std::numeric_limits<float>::max()), m_distance_reference(1.0f), m_attenuation(1.0f), m_cone_angle_outer(M_PI), m_cone_angle_inner(M_PI), m_cone_volume_outer(0), m_flags(AUD_RENDER_CONE), m_stop(NULL), m_stop_data(NULL), m_status(AUD_STATUS_PLAYING), m_device(device) @@ -100,6 +100,8 @@ void AUD_SoftwareDevice::AUD_SoftwareHandle::update() { int flags = 0; + m_old_volume = m_volume; + AUD_Vector3 SL; if(m_relative) SL = -m_location; @@ -404,7 +406,7 @@ bool AUD_SoftwareDevice::AUD_SoftwareHandle::setVolume(float volume) if(volume == 0) { - m_volume = volume; + m_old_volume = m_volume = volume; m_flags |= AUD_RENDER_VOLUME; } else @@ -772,7 +774,7 @@ void AUD_SoftwareDevice::mix(data_t* buffer, int length) // in case of looping while(pos + len < length && sound->m_loopcount && eos) { - m_mixer->mix(buf, pos, len, sound->m_volume); + m_mixer->mix(buf, pos, len, sound->m_volume, sound->m_old_volume); pos += len; @@ -789,7 +791,7 @@ void AUD_SoftwareDevice::mix(data_t* buffer, int length) break; } - m_mixer->mix(buf, pos, len, sound->m_volume); + m_mixer->mix(buf, pos, len, sound->m_volume, sound->m_old_volume); // in case the end of the sound is reached if(eos && !sound->m_loopcount) diff --git a/intern/audaspace/intern/AUD_SoftwareDevice.h b/intern/audaspace/intern/AUD_SoftwareDevice.h index 3c8c1e438a3..54e49c87b27 100644 --- a/intern/audaspace/intern/AUD_SoftwareDevice.h +++ b/intern/audaspace/intern/AUD_SoftwareDevice.h @@ -84,6 +84,7 @@ protected: /// The calculated final volume of the source. float m_volume; + float m_old_volume; /// The loop count of the source. int m_loopcount; diff --git a/intern/audaspace/jack/AUD_JackDevice.h b/intern/audaspace/jack/AUD_JackDevice.h index a82a6bc5c38..ccf3bfd6341 100644 --- a/intern/audaspace/jack/AUD_JackDevice.h +++ b/intern/audaspace/jack/AUD_JackDevice.h @@ -41,7 +41,7 @@ typedef void (*AUD_syncFunction)(void*, int, float); /** - * This device plays back through Jack. + * This device plays back through JACK. */ class AUD_JackDevice : public AUD_SoftwareDevice { @@ -90,7 +90,7 @@ private: static int jack_sync(jack_transport_state_t state, jack_position_t* pos, void* data); /** - * Next Jack Transport state (-1 if not expected to change). + * Next JACK Transport state (-1 if not expected to change). */ jack_transport_state_t m_nextState; @@ -150,7 +150,7 @@ protected: public: /** - * Creates a Jack client for audio output. + * Creates a JACK client for audio output. * \param name The client name. * \param specs The wanted audio specification, where only the channel count * is important. @@ -160,7 +160,7 @@ public: AUD_JackDevice(std::string name, AUD_DeviceSpecs specs, int buffersize = AUD_DEFAULT_BUFFER_SIZE); /** - * Closes the Jack client. + * Closes the JACK client. */ virtual ~AUD_JackDevice(); diff --git a/intern/audaspace/jack/AUD_JackLibrary.h b/intern/audaspace/jack/AUD_JackLibrary.h index 2a737a1a4e2..d74d9ba8021 100644 --- a/intern/audaspace/jack/AUD_JackLibrary.h +++ b/intern/audaspace/jack/AUD_JackLibrary.h @@ -42,7 +42,7 @@ # define JACK_SYM extern #endif -/* All loadable Jack sumbols, prototypes from original jack.h */ +/* All loadable JACK sumbols, prototypes from original jack.h */ JACK_SYM jack_transport_state_t (*AUD_jack_transport_query) ( const jack_client_t *client, diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 977d7f75bb7..a8ab9100ded 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -266,6 +266,13 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): description="Sample all lights (for indirect samples), rather than randomly picking one", default=True, ) + cls.light_sampling_threshold = FloatProperty( + name="Light Sampling Threshold", + description="Probabilistically terminate light samples when the light contribution is below this threshold (more noise but faster rendering). " + "Zero disables the test and never ignores lights", + min=0.0, max=1.0, + default=0.05, + ) cls.caustics_reflective = BoolProperty( name="Reflective Caustics", diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 52872d2b83f..4942a71ce4d 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -166,6 +166,7 @@ class CyclesRender_PT_sampling(CyclesButtonsPanel, Panel): sub.prop(cscene, "sample_clamp_direct") sub.prop(cscene, "sample_clamp_indirect") + sub.prop(cscene, "light_sampling_threshold") if cscene.progressive == 'PATH' or use_branched_path(context) is False: col = split.column() diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index ff1d49ffd12..fab03c7659b 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -847,7 +847,7 @@ static void sync_mesh_fluid_motion(BL::Object& b_ob, Scene *scene, Mesh *mesh) /* Only export previous and next frame, we don't have any in between data. */ float motion_times[2] = {-1.0f, 1.0f}; - for (int step = 0; step < 2; step++) { + for(int step = 0; step < 2; step++) { float relative_time = motion_times[step] * scene->motion_shutter_time() * 0.5f; float3 *mP = attr_mP->data_float3() + step*mesh->verts.size(); @@ -1081,7 +1081,7 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob, /* fluid motion is exported immediate with mesh, skip here */ BL::DomainFluidSettings b_fluid_domain = object_fluid_domain_find(b_ob); - if (b_fluid_domain) + if(b_fluid_domain) return; if(ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview)) { diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index f7f77dfb4cb..0d961c5bf88 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -153,6 +153,7 @@ void BlenderSync::sync_light(BL::Object& b_parent, /* location and (inverted!) direction */ light->co = transform_get_column(&tfm, 3); light->dir = -transform_get_column(&tfm, 2); + light->tfm = tfm; /* shader */ vector<Shader*> used_shaders; diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp index 0161b5b192c..a50f5edb1df 100644 --- a/intern/cycles/blender/blender_python.cpp +++ b/intern/cycles/blender/blender_python.cpp @@ -26,6 +26,7 @@ #include "util_md5.h" #include "util_opengl.h" #include "util_path.h" +#include "util_string.h" #include "util_types.h" #ifdef WITH_OSL @@ -437,13 +438,13 @@ static PyObject *osl_update_node_func(PyObject * /*self*/, PyObject *args) continue; /* determine socket type */ - std::string socket_type; + string socket_type; BL::NodeSocket::type_enum data_type = BL::NodeSocket::type_VALUE; float4 default_float4 = make_float4(0.0f, 0.0f, 0.0f, 1.0f); float default_float = 0.0f; int default_int = 0; - std::string default_string = ""; - + string default_string = ""; + if(param->isclosure) { socket_type = "NodeSocketShader"; data_type = BL::NodeSocket::type_SHADER; diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 534bc6cc897..f63f94ab37a 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -27,12 +27,13 @@ #include "blender_util.h" #include "util_debug.h" +#include "util_string.h" CCL_NAMESPACE_BEGIN typedef map<void*, ShaderInput*> PtrInputMap; typedef map<void*, ShaderOutput*> PtrOutputMap; -typedef map<std::string, ConvertNode*> ProxyMap; +typedef map<string, ConvertNode*> ProxyMap; /* Find */ diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index 4ca202ac40d..bc5c3bb8096 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -284,6 +284,7 @@ void BlenderSync::sync_integrator() integrator->sample_all_lights_direct = get_boolean(cscene, "sample_all_lights_direct"); integrator->sample_all_lights_indirect = get_boolean(cscene, "sample_all_lights_indirect"); + integrator->light_sampling_threshold = get_float(cscene, "light_sampling_threshold"); int diffuse_samples = get_int(cscene, "diffuse_samples"); int glossy_samples = get_int(cscene, "glossy_samples"); diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index b8b9597914e..9a01b4f2b6e 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -165,7 +165,7 @@ private: id_map<ParticleSystemKey, ParticleSystem> particle_system_map; set<Mesh*> mesh_synced; set<Mesh*> mesh_motion_synced; - std::set<float> motion_times; + set<float> motion_times; void *world_map; bool world_recalc; diff --git a/intern/cycles/bvh/bvh.cpp b/intern/cycles/bvh/bvh.cpp index 39b2a0cf436..4851de5b481 100644 --- a/intern/cycles/bvh/bvh.cpp +++ b/intern/cycles/bvh/bvh.cpp @@ -147,7 +147,7 @@ void BVH::pack_primitives() /* Count number of triangles primitives in BVH. */ for(unsigned int i = 0; i < tidx_size; i++) { if((pack.prim_index[i] != -1)) { - if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { + if((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { ++num_prim_triangles; } } @@ -343,7 +343,7 @@ void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size) size_t leaf_nodes_offset_size = bvh->pack.leaf_nodes.size(); for(size_t i = 0, j = 0; i < leaf_nodes_offset_size; - i+= BVH_NODE_LEAF_SIZE, j++) + i += BVH_NODE_LEAF_SIZE, j++) { int4 data = leaf_nodes_offset[i]; data.x += prim_offset; @@ -450,7 +450,7 @@ void RegularBVH::pack_inner(const BVHStackEntry& e, const BVHStackEntry& e0, const BVHStackEntry& e1) { - if (e0.node->is_unaligned() || e1.node->is_unaligned()) { + if(e0.node->is_unaligned() || e1.node->is_unaligned()) { pack_unaligned_inner(e, e0, e1); } else { pack_aligned_inner(e, e0, e1); @@ -597,8 +597,8 @@ void RegularBVH::pack_nodes(const BVHNode *root) else { /* innner node */ int idx[2]; - for (int i = 0; i < 2; ++i) { - if (e.node->get_child(i)->is_leaf()) { + for(int i = 0; i < 2; ++i) { + if(e.node->get_child(i)->is_leaf()) { idx[i] = nextLeafNodeIdx++; } else { diff --git a/intern/cycles/bvh/bvh_build.cpp b/intern/cycles/bvh/bvh_build.cpp index f2a735d12e3..14f66aca70f 100644 --- a/intern/cycles/bvh/bvh_build.cpp +++ b/intern/cycles/bvh/bvh_build.cpp @@ -477,6 +477,7 @@ BVHNode* BVHBuild::build_node(const BVHObjectBinning& range, int level) float unalignedSplitSAH = FLT_MAX; float unalignedLeafSAH = FLT_MAX; Transform aligned_space; + bool do_unalinged_split = false; if(params.use_unaligned_nodes && splitSAH > params.unaligned_split_threshold*leafSAH) { @@ -496,11 +497,15 @@ BVHNode* BVHBuild::build_node(const BVHObjectBinning& range, int level) return create_leaf_node(range, references); } } + /* Check whether unaligned split is better than the regulat one. */ + if(unalignedSplitSAH < splitSAH) { + do_unalinged_split = true; + } } /* Perform split. */ BVHObjectBinning left, right; - if(unalignedSplitSAH < splitSAH) { + if(do_unalinged_split) { unaligned_range.split(&references[0], left, right); } else { @@ -508,7 +513,7 @@ BVHNode* BVHBuild::build_node(const BVHObjectBinning& range, int level) } BoundBox bounds; - if(unalignedSplitSAH < splitSAH) { + if(do_unalinged_split) { bounds = unaligned_heuristic.compute_aligned_boundbox( range, &references[0], aligned_space); } @@ -533,7 +538,7 @@ BVHNode* BVHBuild::build_node(const BVHObjectBinning& range, int level) task_pool.push(new BVHBuildTask(this, inner, 1, right, level + 1), true); } - if(unalignedSplitSAH < splitSAH) { + if(do_unalinged_split) { inner->set_aligned_space(aligned_space); } @@ -583,6 +588,7 @@ BVHNode* BVHBuild::build_node(const BVHRange& range, float unalignedSplitSAH = FLT_MAX; /* float unalignedLeafSAH = FLT_MAX; */ Transform aligned_space; + bool do_unalinged_split = false; if(params.use_unaligned_nodes && splitSAH > params.unaligned_split_threshold*leafSAH) { @@ -599,11 +605,15 @@ BVHNode* BVHBuild::build_node(const BVHRange& range, unalignedSplitSAH = params.sah_node_cost * unaligned_split.bounds.half_area() + params.sah_primitive_cost * unaligned_split.nodeSAH; /* TOOD(sergey): Check we can create leaf already. */ + /* Check whether unaligned split is better than the regulat one. */ + if(unalignedSplitSAH < splitSAH) { + do_unalinged_split = true; + } } /* Do split. */ BVHRange left, right; - if(unalignedSplitSAH < splitSAH) { + if(do_unalinged_split) { unaligned_split.split(this, left, right, range); } else { @@ -613,7 +623,7 @@ BVHNode* BVHBuild::build_node(const BVHRange& range, progress_total += left.size() + right.size() - range.size(); BoundBox bounds; - if(unalignedSplitSAH < splitSAH) { + if(do_unalinged_split) { bounds = unaligned_heuristic.compute_aligned_boundbox( range, &references->at(0), aligned_space); } @@ -657,7 +667,7 @@ BVHNode* BVHBuild::build_node(const BVHRange& range, true); } - if(unalignedSplitSAH < splitSAH) { + if(do_unalinged_split) { inner->set_aligned_space(aligned_space); } @@ -787,7 +797,7 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range, if(params.use_unaligned_nodes && !alignment_found) { alignment_found = unaligned_heuristic.compute_aligned_space(p_ref[i][j], - &aligned_space); + &aligned_space); } } LeafNode *leaf_node = new LeafNode(bounds[i], diff --git a/intern/cycles/bvh/bvh_node.h b/intern/cycles/bvh/bvh_node.h index f2965a785e6..2faa40ab657 100644 --- a/intern/cycles/bvh/bvh_node.h +++ b/intern/cycles/bvh/bvh_node.h @@ -66,7 +66,7 @@ public: inline void set_aligned_space(const Transform& aligned_space) { m_is_unaligned = true; - if (m_aligned_space == NULL) { + if(m_aligned_space == NULL) { m_aligned_space = new Transform(aligned_space); } else { diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp index 85e736ad635..909ec7a6d60 100644 --- a/intern/cycles/device/device.cpp +++ b/intern/cycles/device/device.cpp @@ -49,17 +49,17 @@ std::ostream& operator <<(std::ostream &os, /* TODO(sergey): Decode bitflag into list of names. */ os << "Nodes features: " << requested_features.nodes_features << std::endl; os << "Use hair: " - << string_from_bool(requested_features.use_hair) << std::endl; + << string_from_bool(requested_features.use_hair) << std::endl; os << "Use object motion: " - << string_from_bool(requested_features.use_object_motion) << std::endl; + << string_from_bool(requested_features.use_object_motion) << std::endl; os << "Use camera motion: " - << string_from_bool(requested_features.use_camera_motion) << std::endl; + << string_from_bool(requested_features.use_camera_motion) << std::endl; os << "Use Baking: " - << string_from_bool(requested_features.use_baking) << std::endl; + << string_from_bool(requested_features.use_baking) << std::endl; os << "Use Subsurface: " << string_from_bool(requested_features.use_subsurface) << std::endl; os << "Use Volume: " - << string_from_bool(requested_features.use_volume) << std::endl; + << string_from_bool(requested_features.use_volume) << std::endl; os << "Use Branched Integrator: " << string_from_bool(requested_features.use_integrator_branched) << std::endl; os << "Use Patch Evaluation: " diff --git a/intern/cycles/device/opencl/opencl_base.cpp b/intern/cycles/device/opencl/opencl_base.cpp index e67ffb1a836..a2b900312e7 100644 --- a/intern/cycles/device/opencl/opencl_base.cpp +++ b/intern/cycles/device/opencl/opencl_base.cpp @@ -409,7 +409,7 @@ void OpenCLDeviceBase::enqueue_kernel(cl_kernel kernel, size_t w, size_t h) * 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) { + if(h == 1) { global_size[h] = 1; } diff --git a/intern/cycles/graph/node_type.h b/intern/cycles/graph/node_type.h index 60c3244028d..1fb135f6d22 100644 --- a/intern/cycles/graph/node_type.h +++ b/intern/cycles/graph/node_type.h @@ -125,8 +125,8 @@ struct NodeType ustring name; Type type; - std::vector<SocketType> inputs; - std::vector<SocketType> outputs; + vector<SocketType, std::allocator<SocketType> > inputs; + vector<SocketType, std::allocator<SocketType> > outputs; CreateFunc create; static NodeType *add(const char *name, CreateFunc create, Type type = NONE); diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index e4341c8aca1..56bcafbce38 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -57,6 +57,7 @@ set(SRC_HEADERS kernel_emission.h kernel_film.h kernel_globals.h + kernel_image_opencl.h kernel_jitter.h kernel_light.h kernel_math.h @@ -176,6 +177,7 @@ set(SRC_UTIL_HEADERS ../util/util_atomic.h ../util/util_color.h ../util/util_half.h + ../util/util_hash.h ../util/util_math.h ../util/util_math_fast.h ../util/util_static_assert.h diff --git a/intern/cycles/kernel/bvh/qbvh_nodes.h b/intern/cycles/kernel/bvh/qbvh_nodes.h index 2ee2a393e80..6d22f0b0d6a 100644 --- a/intern/cycles/kernel/bvh/qbvh_nodes.h +++ b/intern/cycles/kernel/bvh/qbvh_nodes.h @@ -21,6 +21,36 @@ struct QBVHStackItem { float dist; }; +ccl_device_inline void qbvh_near_far_idx_calc(const float3& idir, + int *ccl_restrict near_x, + int *ccl_restrict near_y, + int *ccl_restrict near_z, + int *ccl_restrict far_x, + int *ccl_restrict far_y, + int *ccl_restrict far_z) + +{ +#ifdef __KERNEL_SSE__ + *near_x = 0; *far_x = 1; + *near_y = 2; *far_y = 3; + *near_z = 4; *far_z = 5; + + const size_t mask = movemask(ssef(idir.m128)); + + const int mask_x = mask & 1; + const int mask_y = (mask & 2) >> 1; + const int mask_z = (mask & 4) >> 2; + + *near_x += mask_x; *far_x -= mask_x; + *near_y += mask_y; *far_y -= mask_y; + *near_z += mask_z; *far_z -= mask_z; +#else + if(idir.x >= 0.0f) { *near_x = 0; *far_x = 1; } else { *near_x = 1; *far_x = 0; } + if(idir.y >= 0.0f) { *near_y = 2; *far_y = 3; } else { *near_y = 3; *far_y = 2; } + if(idir.z >= 0.0f) { *near_z = 4; *far_z = 5; } else { *near_z = 5; *far_z = 4; } +#endif +} + /* TOOD(sergey): Investigate if using intrinsics helps for both * stack item swap and float comparison. */ diff --git a/intern/cycles/kernel/bvh/qbvh_shadow_all.h b/intern/cycles/kernel/bvh/qbvh_shadow_all.h index ae7aec2082f..5f4d06f12ea 100644 --- a/intern/cycles/kernel/bvh/qbvh_shadow_all.h +++ b/intern/cycles/kernel/bvh/qbvh_shadow_all.h @@ -92,10 +92,9 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, /* Offsets to select the side that becomes the lower or upper bound. */ int near_x, near_y, near_z; int far_x, far_y, far_z; - - if(idir.x >= 0.0f) { near_x = 0; far_x = 1; } else { near_x = 1; far_x = 0; } - if(idir.y >= 0.0f) { near_y = 2; far_y = 3; } else { near_y = 3; far_y = 2; } - if(idir.z >= 0.0f) { near_z = 4; far_z = 5; } else { near_z = 5; far_z = 4; } + qbvh_near_far_idx_calc(idir, + &near_x, &near_y, &near_z, + &far_x, &far_y, &far_z); IsectPrecalc isect_precalc; triangle_intersect_precalc(dir, &isect_precalc); @@ -392,9 +391,9 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, num_hits_in_instance = 0; isect_array->t = isect_t; - if(idir.x >= 0.0f) { near_x = 0; far_x = 1; } else { near_x = 1; far_x = 0; } - if(idir.y >= 0.0f) { near_y = 2; far_y = 3; } else { near_y = 3; far_y = 2; } - if(idir.z >= 0.0f) { near_z = 4; far_z = 5; } else { near_z = 5; far_z = 4; } + qbvh_near_far_idx_calc(idir, + &near_x, &near_y, &near_z, + &far_x, &far_y, &far_z); tfar = ssef(isect_t); # if BVH_FEATURE(BVH_HAIR) dir4 = sse3f(ssef(dir.x), ssef(dir.y), ssef(dir.z)); @@ -450,9 +449,9 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, isect_t = tmax; isect_array->t = isect_t; - if(idir.x >= 0.0f) { near_x = 0; far_x = 1; } else { near_x = 1; far_x = 0; } - if(idir.y >= 0.0f) { near_y = 2; far_y = 3; } else { near_y = 3; far_y = 2; } - if(idir.z >= 0.0f) { near_z = 4; far_z = 5; } else { near_z = 5; far_z = 4; } + qbvh_near_far_idx_calc(idir, + &near_x, &near_y, &near_z, + &far_x, &far_y, &far_z); tfar = ssef(isect_t); # if BVH_FEATURE(BVH_HAIR) dir4 = sse3f(ssef(dir.x), ssef(dir.y), ssef(dir.z)); diff --git a/intern/cycles/kernel/bvh/qbvh_subsurface.h b/intern/cycles/kernel/bvh/qbvh_subsurface.h index 24aca96a298..ccd36df034a 100644 --- a/intern/cycles/kernel/bvh/qbvh_subsurface.h +++ b/intern/cycles/kernel/bvh/qbvh_subsurface.h @@ -101,10 +101,9 @@ ccl_device void BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, /* Offsets to select the side that becomes the lower or upper bound. */ int near_x, near_y, near_z; int far_x, far_y, far_z; - - if(idir.x >= 0.0f) { near_x = 0; far_x = 1; } else { near_x = 1; far_x = 0; } - if(idir.y >= 0.0f) { near_y = 2; far_y = 3; } else { near_y = 3; far_y = 2; } - if(idir.z >= 0.0f) { near_z = 4; far_z = 5; } else { near_z = 5; far_z = 4; } + qbvh_near_far_idx_calc(idir, + &near_x, &near_y, &near_z, + &far_x, &far_y, &far_z); IsectPrecalc isect_precalc; triangle_intersect_precalc(dir, &isect_precalc); diff --git a/intern/cycles/kernel/bvh/qbvh_traversal.h b/intern/cycles/kernel/bvh/qbvh_traversal.h index a1e154d6dcf..f2d8e558dcc 100644 --- a/intern/cycles/kernel/bvh/qbvh_traversal.h +++ b/intern/cycles/kernel/bvh/qbvh_traversal.h @@ -102,10 +102,9 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, /* Offsets to select the side that becomes the lower or upper bound. */ int near_x, near_y, near_z; int far_x, far_y, far_z; - - if(idir.x >= 0.0f) { near_x = 0; far_x = 1; } else { near_x = 1; far_x = 0; } - if(idir.y >= 0.0f) { near_y = 2; far_y = 3; } else { near_y = 3; far_y = 2; } - if(idir.z >= 0.0f) { near_z = 4; far_z = 5; } else { near_z = 5; far_z = 4; } + qbvh_near_far_idx_calc(idir, + &near_x, &near_y, &near_z, + &far_x, &far_y, &far_z); IsectPrecalc isect_precalc; triangle_intersect_precalc(dir, &isect_precalc); @@ -427,9 +426,9 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, qbvh_instance_push(kg, object, ray, &P, &dir, &idir, &isect->t, &node_dist); # endif - if(idir.x >= 0.0f) { near_x = 0; far_x = 1; } else { near_x = 1; far_x = 0; } - if(idir.y >= 0.0f) { near_y = 2; far_y = 3; } else { near_y = 3; far_y = 2; } - if(idir.z >= 0.0f) { near_z = 4; far_z = 5; } else { near_z = 5; far_z = 4; } + qbvh_near_far_idx_calc(idir, + &near_x, &near_y, &near_z, + &far_x, &far_y, &far_z); tfar = ssef(isect->t); # if BVH_FEATURE(BVH_HAIR) dir4 = sse3f(ssef(dir.x), ssef(dir.y), ssef(dir.z)); @@ -469,9 +468,9 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, bvh_instance_pop(kg, object, ray, &P, &dir, &idir, &isect->t); # endif - if(idir.x >= 0.0f) { near_x = 0; far_x = 1; } else { near_x = 1; far_x = 0; } - if(idir.y >= 0.0f) { near_y = 2; far_y = 3; } else { near_y = 3; far_y = 2; } - if(idir.z >= 0.0f) { near_z = 4; far_z = 5; } else { near_z = 5; far_z = 4; } + qbvh_near_far_idx_calc(idir, + &near_x, &near_y, &near_z, + &far_x, &far_y, &far_z); tfar = ssef(isect->t); # if BVH_FEATURE(BVH_HAIR) dir4 = sse3f(ssef(dir.x), ssef(dir.y), ssef(dir.z)); diff --git a/intern/cycles/kernel/bvh/qbvh_volume.h b/intern/cycles/kernel/bvh/qbvh_volume.h index db9779351d2..424710b69f2 100644 --- a/intern/cycles/kernel/bvh/qbvh_volume.h +++ b/intern/cycles/kernel/bvh/qbvh_volume.h @@ -87,10 +87,9 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, /* Offsets to select the side that becomes the lower or upper bound. */ int near_x, near_y, near_z; int far_x, far_y, far_z; - - if(idir.x >= 0.0f) { near_x = 0; far_x = 1; } else { near_x = 1; far_x = 0; } - if(idir.y >= 0.0f) { near_y = 2; far_y = 3; } else { near_y = 3; far_y = 2; } - if(idir.z >= 0.0f) { near_z = 4; far_z = 5; } else { near_z = 5; far_z = 4; } + qbvh_near_far_idx_calc(idir, + &near_x, &near_y, &near_z, + &far_x, &far_y, &far_z); IsectPrecalc isect_precalc; triangle_intersect_precalc(dir, &isect_precalc); @@ -303,9 +302,9 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, bvh_instance_push(kg, object, ray, &P, &dir, &idir, &isect->t); # endif - if(idir.x >= 0.0f) { near_x = 0; far_x = 1; } else { near_x = 1; far_x = 0; } - if(idir.y >= 0.0f) { near_y = 2; far_y = 3; } else { near_y = 3; far_y = 2; } - if(idir.z >= 0.0f) { near_z = 4; far_z = 5; } else { near_z = 5; far_z = 4; } + qbvh_near_far_idx_calc(idir, + &near_x, &near_y, &near_z, + &far_x, &far_y, &far_z); tfar = ssef(isect->t); # if BVH_FEATURE(BVH_HAIR) dir4 = sse3f(ssef(dir.x), ssef(dir.y), ssef(dir.z)); @@ -349,9 +348,9 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, bvh_instance_pop(kg, object, ray, &P, &dir, &idir, &isect->t); # endif - if(idir.x >= 0.0f) { near_x = 0; far_x = 1; } else { near_x = 1; far_x = 0; } - if(idir.y >= 0.0f) { near_y = 2; far_y = 3; } else { near_y = 3; far_y = 2; } - if(idir.z >= 0.0f) { near_z = 4; far_z = 5; } else { near_z = 5; far_z = 4; } + qbvh_near_far_idx_calc(idir, + &near_x, &near_y, &near_z, + &far_x, &far_y, &far_z); tfar = ssef(isect->t); # if BVH_FEATURE(BVH_HAIR) dir4 = sse3f(ssef(dir.x), ssef(dir.y), ssef(dir.z)); diff --git a/intern/cycles/kernel/bvh/qbvh_volume_all.h b/intern/cycles/kernel/bvh/qbvh_volume_all.h index 88f1f764e4c..eb48af6fc68 100644 --- a/intern/cycles/kernel/bvh/qbvh_volume_all.h +++ b/intern/cycles/kernel/bvh/qbvh_volume_all.h @@ -91,10 +91,9 @@ ccl_device uint BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, /* Offsets to select the side that becomes the lower or upper bound. */ int near_x, near_y, near_z; int far_x, far_y, far_z; - - if(idir.x >= 0.0f) { near_x = 0; far_x = 1; } else { near_x = 1; far_x = 0; } - if(idir.y >= 0.0f) { near_y = 2; far_y = 3; } else { near_y = 3; far_y = 2; } - if(idir.z >= 0.0f) { near_z = 4; far_z = 5; } else { near_z = 5; far_z = 4; } + qbvh_near_far_idx_calc(idir, + &near_x, &near_y, &near_z, + &far_x, &far_y, &far_z); IsectPrecalc isect_precalc; triangle_intersect_precalc(dir, &isect_precalc); @@ -354,9 +353,9 @@ ccl_device uint BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, bvh_instance_push(kg, object, ray, &P, &dir, &idir, &isect_t); # endif - if(idir.x >= 0.0f) { near_x = 0; far_x = 1; } else { near_x = 1; far_x = 0; } - if(idir.y >= 0.0f) { near_y = 2; far_y = 3; } else { near_y = 3; far_y = 2; } - if(idir.z >= 0.0f) { near_z = 4; far_z = 5; } else { near_z = 5; far_z = 4; } + qbvh_near_far_idx_calc(idir, + &near_x, &near_y, &near_z, + &far_x, &far_y, &far_z); tfar = ssef(isect_t); idir4 = sse3f(ssef(idir.x), ssef(idir.y), ssef(idir.z)); # if BVH_FEATURE(BVH_HAIR) @@ -420,9 +419,9 @@ ccl_device uint BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, isect_t = tmax; isect_array->t = isect_t; - if(idir.x >= 0.0f) { near_x = 0; far_x = 1; } else { near_x = 1; far_x = 0; } - if(idir.y >= 0.0f) { near_y = 2; far_y = 3; } else { near_y = 3; far_y = 2; } - if(idir.z >= 0.0f) { near_z = 4; far_z = 5; } else { near_z = 5; far_z = 4; } + qbvh_near_far_idx_calc(idir, + &near_x, &near_y, &near_z, + &far_x, &far_y, &far_z); tfar = ssef(isect_t); # if BVH_FEATURE(BVH_HAIR) dir4 = sse3f(ssef(dir.x), ssef(dir.y), ssef(dir.z)); diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h index c2ec774362a..6b42f66b0d5 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/geom_object.h @@ -55,6 +55,21 @@ ccl_device_inline Transform object_fetch_transform(KernelGlobals *kg, int object return tfm; } +/* Lamp to world space transformation */ + +ccl_device_inline Transform lamp_fetch_transform(KernelGlobals *kg, int lamp, bool inverse) +{ + int offset = lamp*LIGHT_SIZE + (inverse? 8 : 5); + + Transform tfm; + tfm.x = kernel_tex_fetch(__light_data, offset + 0); + tfm.y = kernel_tex_fetch(__light_data, offset + 1); + tfm.z = kernel_tex_fetch(__light_data, offset + 2); + tfm.w = make_float4(0.0f, 0.0f, 0.0f, 1.0f); + + return tfm; +} + /* Object to world space transformation for motion vectors */ ccl_device_inline Transform object_fetch_vector_transform(KernelGlobals *kg, int object, enum ObjectVectorTransform type) @@ -376,15 +391,33 @@ ccl_device float3 particle_angular_velocity(KernelGlobals *kg, int particle) ccl_device_inline float3 bvh_clamp_direction(float3 dir) { /* clamp absolute values by exp2f(-80.0f) to avoid division by zero when calculating inverse direction */ - float ooeps = 8.271806E-25f; +#if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE2__) + const ssef oopes(8.271806E-25f,8.271806E-25f,8.271806E-25f,0.0f); + const ssef mask = _mm_cmpgt_ps(fabs(dir), oopes); + const ssef signdir = signmsk(dir.m128) | oopes; +# ifndef __KERNEL_AVX__ + ssef res = mask & ssef(dir); + res = _mm_or_ps(res,_mm_andnot_ps(mask, signdir)); +# else + ssef res = _mm_blendv_ps(signdir, dir, mask); +# endif + return float3(res); +#else /* __KERNEL_SSE__ && __KERNEL_SSE2__ */ + const float ooeps = 8.271806E-25f; return make_float3((fabsf(dir.x) > ooeps)? dir.x: copysignf(ooeps, dir.x), (fabsf(dir.y) > ooeps)? dir.y: copysignf(ooeps, dir.y), (fabsf(dir.z) > ooeps)? dir.z: copysignf(ooeps, dir.z)); +#endif /* __KERNEL_SSE__ && __KERNEL_SSE2__ */ } ccl_device_inline float3 bvh_inverse_direction(float3 dir) { + /* TODO(sergey): Currently disabled, gives speedup but causes precision issues. */ +#if defined(__KERNEL_SSE__) && 0 + return rcp(dir); +#else return 1.0f / dir; +#endif } /* Transform ray into object space to enter static object in BVH */ diff --git a/intern/cycles/kernel/geom/geom_triangle_intersect.h b/intern/cycles/kernel/geom/geom_triangle_intersect.h index b505bd54e5e..eb7340583c8 100644 --- a/intern/cycles/kernel/geom/geom_triangle_intersect.h +++ b/intern/cycles/kernel/geom/geom_triangle_intersect.h @@ -59,21 +59,33 @@ void triangle_intersect_precalc(float3 dir, IsectPrecalc *isect_precalc) { /* Calculate dimension where the ray direction is maximal. */ +#ifndef __KERNEL_SSE__ int kz = util_max_axis(make_float3(fabsf(dir.x), fabsf(dir.y), fabsf(dir.z))); int kx = kz + 1; if(kx == 3) kx = 0; int ky = kx + 1; if(ky == 3) ky = 0; +#else + int kx, ky, kz; + /* Avoiding mispredicted branch on direction. */ + kz = util_max_axis(fabs(dir)); + static const char inc_xaxis[] = {1, 2, 0, 55}; + static const char inc_yaxis[] = {2, 0, 1, 55}; + kx = inc_xaxis[kz]; + ky = inc_yaxis[kz]; +#endif + + float dir_kz = IDX(dir, kz); /* Swap kx and ky dimensions to preserve winding direction of triangles. */ - if(IDX(dir, kz) < 0.0f) { + if(dir_kz < 0.0f) { int tmp = kx; kx = ky; ky = tmp; } /* Calculate the shear constants. */ - float inv_dir_z = 1.0f / IDX(dir, kz); + float inv_dir_z = 1.0f / dir_kz; isect_precalc->Sx = IDX(dir, kx) * inv_dir_z; isect_precalc->Sy = IDX(dir, ky) * inv_dir_z; isect_precalc->Sz = inv_dir_z; @@ -108,7 +120,7 @@ ccl_device_inline bool triangle_intersect(KernelGlobals *kg, /* Calculate vertices relative to ray origin. */ const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, triAddr); -#if defined(__KERNEL_AVX2__) +#if defined(__KERNEL_AVX2__) && defined(__KERNEL_SSE__) const avxf avxf_P(P.m128, P.m128); const avxf tri_ab = kernel_tex_fetch_avxf(__prim_tri_verts, tri_vindex + 0); @@ -269,6 +281,67 @@ ccl_device_inline void triangle_intersect_subsurface( const float4 tri_a = kernel_tex_fetch(__prim_tri_verts, tri_vindex+0), tri_b = kernel_tex_fetch(__prim_tri_verts, tri_vindex+1), tri_c = kernel_tex_fetch(__prim_tri_verts, tri_vindex+2); + +#if defined(__KERNEL_AVX2__) && defined(__KERNEL_SSE__) + const avxf avxf_P(P.m128, P.m128); + + const avxf tri_ab = kernel_tex_fetch_avxf(__prim_tri_verts, tri_vindex + 0); + const avxf tri_bc = kernel_tex_fetch_avxf(__prim_tri_verts, tri_vindex + 1); + + const avxf AB = tri_ab - avxf_P; + const avxf BC = tri_bc - avxf_P; + + const __m256i permuteMask = _mm256_set_epi32(0x3, kz, ky, kx, 0x3, kz, ky, kx); + + const avxf AB_k = shuffle(AB, permuteMask); + const avxf BC_k = shuffle(BC, permuteMask); + + /* Akz, Akz, Bkz, Bkz, Bkz, Bkz, Ckz, Ckz */ + const avxf ABBC_kz = shuffle<2>(AB_k, BC_k); + + /* Akx, Aky, Bkx, Bky, Bkx,Bky, Ckx, Cky */ + const avxf ABBC_kxy = shuffle<0,1,0,1>(AB_k, BC_k); + + const avxf Sxy(Sy, Sx, Sy, Sx); + + /* Ax, Ay, Bx, By, Bx, By, Cx, Cy */ + const avxf ABBC_xy = nmadd(ABBC_kz, Sxy, ABBC_kxy); + + float ABBC_kz_array[8]; + _mm256_storeu_ps((float*)&ABBC_kz_array, ABBC_kz); + + const float A_kz = ABBC_kz_array[0]; + const float B_kz = ABBC_kz_array[2]; + const float C_kz = ABBC_kz_array[6]; + + /* By, Bx, Cy, Cx, By, Bx, Ay, Ax */ + const avxf BCBA_yx = permute<3,2,7,6,3,2,1,0>(ABBC_xy); + + const avxf negMask(0,0,0,0,0x80000000, 0x80000000, 0x80000000, 0x80000000); + + /* W U V + * (AxBy-AyBx) (BxCy-ByCx) XX XX (BxBy-ByBx) (CxAy-CyAx) XX XX + */ + const avxf WUxxxxVxx_neg = _mm256_hsub_ps(ABBC_xy * BCBA_yx, negMask /* Dont care */); + + const avxf WUVWnegWUVW = permute<0,1,5,0,0,1,5,0>(WUxxxxVxx_neg) ^ negMask; + + /* Calculate scaled barycentric coordinates. */ + float WUVW_array[4]; + _mm_storeu_ps((float*)&WUVW_array, _mm256_castps256_ps128 (WUVWnegWUVW)); + + const float W = WUVW_array[0]; + const float U = WUVW_array[1]; + const float V = WUVW_array[2]; + + const int WUVW_mask = 0x7 & _mm256_movemask_ps(WUVWnegWUVW); + const int WUVW_zero = 0x7 & _mm256_movemask_ps(_mm256_cmp_ps(WUVWnegWUVW, + _mm256_setzero_ps(), 0)); + + if(!((WUVW_mask == 7) || (WUVW_mask == 0)) && ((WUVW_mask | WUVW_zero) != 7)) { + return; + } +#else const float3 A = make_float3(tri_a.x - P.x, tri_a.y - P.y, tri_a.z - P.z); const float3 B = make_float3(tri_b.x - P.x, tri_b.y - P.y, tri_b.z - P.z); const float3 C = make_float3(tri_c.x - P.x, tri_c.y - P.y, tri_c.z - P.z); @@ -295,6 +368,7 @@ ccl_device_inline void triangle_intersect_subsurface( { return; } +#endif /* Calculate determinant. */ float det = U + V + W; diff --git a/intern/cycles/kernel/geom/geom_volume.h b/intern/cycles/kernel/geom/geom_volume.h index fd97a63efb5..03724c955be 100644 --- a/intern/cycles/kernel/geom/geom_volume.h +++ b/intern/cycles/kernel/geom/geom_volume.h @@ -29,7 +29,7 @@ CCL_NAMESPACE_BEGIN /* Return position normalized to 0..1 in mesh bounds */ -#if defined(__KERNEL_GPU__) && __CUDA_ARCH__ < 300 +#if defined(__KERNEL_CUDA__) && __CUDA_ARCH__ < 300 ccl_device float4 volume_image_texture_3d(int id, float x, float y, float z) { float4 r; @@ -42,7 +42,7 @@ ccl_device float4 volume_image_texture_3d(int id, float x, float y, float z) } return r; } -#endif /* __KERNEL_GPU__ */ +#endif /* __KERNEL_CUDA__ */ ccl_device_inline float3 volume_normalized_position(KernelGlobals *kg, const ShaderData *sd, @@ -64,8 +64,8 @@ ccl_device_inline float3 volume_normalized_position(KernelGlobals *kg, ccl_device float volume_attribute_float(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy) { - float3 P = volume_normalized_position(kg, sd, sd->P); -#ifdef __KERNEL_GPU__ + float3 P = volume_normalized_position(kg, sd, ccl_fetch(sd, P)); +#ifdef __KERNEL_CUDA__ # if __CUDA_ARCH__ >= 300 CUtexObject tex = kernel_tex_fetch(__bindless_mapping, desc.offset); float f = kernel_tex_image_interp_3d_float(tex, P.x, P.y, P.z); @@ -73,6 +73,8 @@ ccl_device float volume_attribute_float(KernelGlobals *kg, const ShaderData *sd, # else float4 r = volume_image_texture_3d(desc.offset, P.x, P.y, P.z); # endif +#elif defined(__KERNEL_OPENCL__) + float4 r = kernel_tex_image_interp_3d(kg, desc.offset, P.x, P.y, P.z); #else float4 r; if(sd->flag & SD_VOLUME_CUBIC) @@ -89,14 +91,16 @@ ccl_device float volume_attribute_float(KernelGlobals *kg, const ShaderData *sd, ccl_device float3 volume_attribute_float3(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float3 *dx, float3 *dy) { - float3 P = volume_normalized_position(kg, sd, sd->P); -#ifdef __KERNEL_GPU__ + float3 P = volume_normalized_position(kg, sd, ccl_fetch(sd, P)); +#ifdef __KERNEL_CUDA__ # if __CUDA_ARCH__ >= 300 CUtexObject tex = kernel_tex_fetch(__bindless_mapping, desc.offset); float4 r = kernel_tex_image_interp_3d_float4(tex, P.x, P.y, P.z); # else float4 r = volume_image_texture_3d(desc.offset, P.x, P.y, P.z); # endif +#elif defined(__KERNEL_OPENCL__) + float4 r = kernel_tex_image_interp_3d(kg, desc.offset, P.x, P.y, P.z); #else float4 r; if(sd->flag & SD_VOLUME_CUBIC) diff --git a/intern/cycles/kernel/kernel_accumulate.h b/intern/cycles/kernel/kernel_accumulate.h index 623c1dcaaa1..6c3ee6b8098 100644 --- a/intern/cycles/kernel/kernel_accumulate.h +++ b/intern/cycles/kernel/kernel_accumulate.h @@ -96,7 +96,7 @@ ccl_device_inline bool bsdf_eval_is_zero(BsdfEval *eval) } } -ccl_device_inline void bsdf_eval_mul(BsdfEval *eval, float3 value) +ccl_device_inline void bsdf_eval_mul(BsdfEval *eval, float value) { #ifdef __PASSES__ if(eval->use_light_pass) { @@ -115,6 +115,36 @@ ccl_device_inline void bsdf_eval_mul(BsdfEval *eval, float3 value) } } +ccl_device_inline void bsdf_eval_mul3(BsdfEval *eval, float3 value) +{ +#ifdef __PASSES__ + if(eval->use_light_pass) { + eval->diffuse *= value; + eval->glossy *= value; + eval->transmission *= value; + eval->subsurface *= value; + eval->scatter *= value; + + /* skipping transparent, this function is used by for eval(), will be zero then */ + } + else + eval->diffuse *= value; +#else + eval->diffuse *= value; +#endif +} + +ccl_device_inline float3 bsdf_eval_sum(BsdfEval *eval) +{ +#ifdef __PASSES__ + if(eval->use_light_pass) { + return eval->diffuse + eval->glossy + eval->transmission + eval->subsurface + eval->scatter; + } + else +#endif + return eval->diffuse; +} + /* Path Radiance * * We accumulate different render passes separately. After summing at the end @@ -193,8 +223,7 @@ ccl_device_inline void path_radiance_bsdf_bounce(PathRadiance *L, ccl_addr_space } else { /* transparent bounce before first hit, or indirectly visible through BSDF */ - float3 sum = (bsdf_eval->diffuse + bsdf_eval->glossy + bsdf_eval->transmission + bsdf_eval->transparent + - bsdf_eval->subsurface + bsdf_eval->scatter) * inverse_pdf; + float3 sum = (bsdf_eval_sum(bsdf_eval) + bsdf_eval->transparent) * inverse_pdf; *throughput *= sum; } } @@ -264,8 +293,7 @@ ccl_device_inline void path_radiance_accum_light(PathRadiance *L, float3 through } else { /* indirectly visible lighting after BSDF bounce */ - float3 sum = bsdf_eval->diffuse + bsdf_eval->glossy + bsdf_eval->transmission + bsdf_eval->subsurface + bsdf_eval->scatter; - L->indirect += throughput*sum*shadow; + L->indirect += throughput*bsdf_eval_sum(bsdf_eval)*shadow; } } else diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h index fd9207fd69f..c32ac6ccf41 100644 --- a/intern/cycles/kernel/kernel_bake.h +++ b/intern/cycles/kernel/kernel_bake.h @@ -63,7 +63,7 @@ ccl_device_inline void compute_light_pass(KernelGlobals *kg, /* sample ambient occlusion */ if(pass_filter & BAKE_FILTER_AO) { - kernel_path_ao(kg, sd, &emission_sd, &L_sample, &state, &rng, throughput); + kernel_path_ao(kg, sd, &emission_sd, &L_sample, &state, &rng, throughput, shader_bsdf_alpha(kg, sd)); } /* sample emission */ @@ -320,7 +320,8 @@ ccl_device void kernel_bake_evaluate(KernelGlobals *kg, ccl_global uint4 *input, P, Ng, Ng, shader, object, prim, u, v, 1.0f, 0.5f, - !(kernel_tex_fetch(__object_flag, object) & SD_TRANSFORM_APPLIED)); + !(kernel_tex_fetch(__object_flag, object) & SD_TRANSFORM_APPLIED), + LAMP_NONE); sd.I = sd.N; /* update differentials */ diff --git a/intern/cycles/kernel/kernel_camera.h b/intern/cycles/kernel/kernel_camera.h index ed9726e1b51..dedac6b1465 100644 --- a/intern/cycles/kernel/kernel_camera.h +++ b/intern/cycles/kernel/kernel_camera.h @@ -68,8 +68,8 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, float raster_x, flo } #endif - ray->P = make_float3(0.0f, 0.0f, 0.0f); - ray->D = Pcamera; + float3 P = make_float3(0.0f, 0.0f, 0.0f); + float3 D = Pcamera; /* modify ray for depth of field */ float aperturesize = kernel_data.cam.aperturesize; @@ -79,12 +79,12 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, float raster_x, flo float2 lensuv = camera_sample_aperture(kg, lens_u, lens_v)*aperturesize; /* compute point on plane of focus */ - float ft = kernel_data.cam.focaldistance/ray->D.z; - float3 Pfocus = ray->D*ft; + float ft = kernel_data.cam.focaldistance/D.z; + float3 Pfocus = D*ft; /* update ray for effect of lens */ - ray->P = make_float3(lensuv.x, lensuv.y, 0.0f); - ray->D = normalize(Pfocus - ray->P); + P = make_float3(lensuv.x, lensuv.y, 0.0f); + D = normalize(Pfocus - P); } /* transform ray from camera to world */ @@ -105,39 +105,66 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, float raster_x, flo } #endif - float3 tP = transform_point(&cameratoworld, ray->P); - float3 tD = transform_direction(&cameratoworld, ray->D); - ray->P = spherical_stereo_position(kg, tD, tP); - ray->D = spherical_stereo_direction(kg, tD, tP, ray->P); + P = transform_point(&cameratoworld, P); + D = normalize(transform_direction(&cameratoworld, D)); + + bool use_stereo = kernel_data.cam.interocular_offset != 0.0f; + if(!use_stereo) { + /* No stereo */ + ray->P = P; + ray->D = D; #ifdef __RAY_DIFFERENTIALS__ - /* ray differential */ - ray->dP = differential3_zero(); - - float3 tD_diff = transform_direction(&cameratoworld, Pcamera); - float3 Pdiff = spherical_stereo_position(kg, tD_diff, Pcamera); - float3 Ddiff = spherical_stereo_direction(kg, tD_diff, Pcamera, Pdiff); - - tP = transform_perspective(&rastertocamera, - make_float3(raster_x + 1.0f, raster_y, 0.0f)); - tD = tD_diff + float4_to_float3(kernel_data.cam.dx); - Pcamera = spherical_stereo_position(kg, tD, tP); - ray->dD.dx = spherical_stereo_direction(kg, tD, tP, Pcamera) - Ddiff; - ray->dP.dx = Pcamera - Pdiff; - - tP = transform_perspective(&rastertocamera, - make_float3(raster_x, raster_y + 1.0f, 0.0f)); - tD = tD_diff + float4_to_float3(kernel_data.cam.dy); - Pcamera = spherical_stereo_position(kg, tD, tP); - ray->dD.dy = spherical_stereo_direction(kg, tD, tP, Pcamera) - Ddiff; - /* dP.dy is zero, since the omnidirectional panorama only shift the eyes horizontally */ + float3 Dcenter = transform_direction(&cameratoworld, Pcamera); + + ray->dP = differential3_zero(); + ray->dD.dx = normalize(Dcenter + float4_to_float3(kernel_data.cam.dx)) - normalize(Dcenter); + ray->dD.dy = normalize(Dcenter + float4_to_float3(kernel_data.cam.dy)) - normalize(Dcenter); #endif + } + else { + /* Spherical stereo */ + spherical_stereo_transform(kg, &P, &D); + ray->P = P; + ray->D = D; + +#ifdef __RAY_DIFFERENTIALS__ + /* Ray differentials, computed from scratch using the raster coordinates + * because we don't want to be affected by depth of field. We compute + * ray origin and direction for the center and two neighbouring pixels + * and simply take their differences. */ + float3 Pnostereo = transform_point(&cameratoworld, make_float3(0.0f, 0.0f, 0.0f)); + + float3 Pcenter = Pnostereo; + float3 Dcenter = Pcamera; + Dcenter = normalize(transform_direction(&cameratoworld, Dcenter)); + spherical_stereo_transform(kg, &Pcenter, &Dcenter); + + float3 Px = Pnostereo; + float3 Dx = transform_perspective(&rastertocamera, make_float3(raster_x + 1.0f, raster_y, 0.0f)); + Dx = normalize(transform_direction(&cameratoworld, Dx)); + spherical_stereo_transform(kg, &Px, &Dx); + + ray->dP.dx = Px - Pcenter; + ray->dD.dx = Dx - Dcenter; + + float3 Py = Pnostereo; + float3 Dy = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y + 1.0f, 0.0f)); + Dy = normalize(transform_direction(&cameratoworld, Dy)); + spherical_stereo_transform(kg, &Py, &Dy); + + ray->dP.dy = Py - Pcenter; + ray->dD.dy = Dy - Dcenter; +#endif + } #ifdef __CAMERA_CLIPPING__ /* clipping */ - float3 Pclip = normalize(Pcamera); - float z_inv = 1.0f / Pclip.z; - ray->P += kernel_data.cam.nearclip*ray->D * z_inv; + float z_inv = 1.0f / normalize(Pcamera).z; + float nearclip = kernel_data.cam.nearclip * z_inv; + ray->P += nearclip * ray->D; + ray->dP.dx += nearclip * ray->dD.dx; + ray->dP.dy += nearclip * ray->dD.dy; ray->t = kernel_data.cam.cliplength * z_inv; #else ray->t = FLT_MAX; @@ -151,7 +178,8 @@ ccl_device void camera_sample_orthographic(KernelGlobals *kg, float raster_x, fl Transform rastertocamera = kernel_data.cam.rastertocamera; float3 Pcamera = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y, 0.0f)); - ray->D = make_float3(0.0f, 0.0f, 1.0f); + float3 P; + float3 D = make_float3(0.0f, 0.0f, 1.0f); /* modify ray for depth of field */ float aperturesize = kernel_data.cam.aperturesize; @@ -161,15 +189,15 @@ ccl_device void camera_sample_orthographic(KernelGlobals *kg, float raster_x, fl float2 lensuv = camera_sample_aperture(kg, lens_u, lens_v)*aperturesize; /* compute point on plane of focus */ - float3 Pfocus = ray->D * kernel_data.cam.focaldistance; + float3 Pfocus = D * kernel_data.cam.focaldistance; /* update ray for effect of lens */ float3 lensuvw = make_float3(lensuv.x, lensuv.y, 0.0f); - ray->P = Pcamera + lensuvw; - ray->D = normalize(Pfocus - lensuvw); + P = Pcamera + lensuvw; + D = normalize(Pfocus - lensuvw); } else { - ray->P = Pcamera; + P = Pcamera; } /* transform ray from camera to world */ Transform cameratoworld = kernel_data.cam.cameratoworld; @@ -189,9 +217,8 @@ ccl_device void camera_sample_orthographic(KernelGlobals *kg, float raster_x, fl } #endif - ray->P = transform_point(&cameratoworld, ray->P); - ray->D = transform_direction(&cameratoworld, ray->D); - ray->D = normalize(ray->D); + ray->P = transform_point(&cameratoworld, P); + ray->D = normalize(transform_direction(&cameratoworld, D)); #ifdef __RAY_DIFFERENTIALS__ /* ray differential */ @@ -220,11 +247,11 @@ ccl_device_inline void camera_sample_panorama(KernelGlobals *kg, float3 Pcamera = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y, 0.0f)); /* create ray form raster position */ - ray->P = make_float3(0.0f, 0.0f, 0.0f); - ray->D = panorama_to_direction(kg, Pcamera.x, Pcamera.y); + float3 P = make_float3(0.0f, 0.0f, 0.0f); + float3 D = panorama_to_direction(kg, Pcamera.x, Pcamera.y); /* indicates ray should not receive any light, outside of the lens */ - if(is_zero(ray->D)) { + if(is_zero(D)) { ray->t = 0.0f; return; } @@ -237,17 +264,17 @@ ccl_device_inline void camera_sample_panorama(KernelGlobals *kg, float2 lensuv = camera_sample_aperture(kg, lens_u, lens_v)*aperturesize; /* compute point on plane of focus */ - float3 D = normalize(ray->D); - float3 Pfocus = D * kernel_data.cam.focaldistance; + float3 Dfocus = normalize(D); + float3 Pfocus = Dfocus * kernel_data.cam.focaldistance; - /* calculate orthonormal coordinates perpendicular to D */ + /* calculate orthonormal coordinates perpendicular to Dfocus */ float3 U, V; - U = normalize(make_float3(1.0f, 0.0f, 0.0f) - D.x * D); - V = normalize(cross(D, U)); + U = normalize(make_float3(1.0f, 0.0f, 0.0f) - Dfocus.x * Dfocus); + V = normalize(cross(Dfocus, U)); /* update ray for effect of lens */ - ray->P = U * lensuv.x + V * lensuv.y; - ray->D = normalize(Pfocus - ray->P); + P = U * lensuv.x + V * lensuv.y; + D = normalize(Pfocus - P); } /* transform ray from camera to world */ @@ -268,36 +295,60 @@ ccl_device_inline void camera_sample_panorama(KernelGlobals *kg, } #endif - float3 tP = transform_point(&cameratoworld, ray->P); - float3 tD = transform_direction(&cameratoworld, ray->D); - ray->P = spherical_stereo_position(kg, tD, tP); - ray->D = spherical_stereo_direction(kg, tD, tP, ray->P); + P = transform_point(&cameratoworld, P); + D = normalize(transform_direction(&cameratoworld, D)); + + /* Stereo transform */ + bool use_stereo = kernel_data.cam.interocular_offset != 0.0f; + if(use_stereo) { + spherical_stereo_transform(kg, &P, &D); + } + + ray->P = P; + ray->D = D; #ifdef __RAY_DIFFERENTIALS__ - /* ray differential */ - ray->dP = differential3_zero(); - - tP = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y, 0.0f)); - tD = transform_direction(&cameratoworld, panorama_to_direction(kg, tP.x, tP.y)); - float3 Pdiff = spherical_stereo_position(kg, tD, tP); - float3 Ddiff = spherical_stereo_direction(kg, tD, tP, Pdiff); - - tP = transform_perspective(&rastertocamera, make_float3(raster_x + 1.0f, raster_y, 0.0f)); - tD = transform_direction(&cameratoworld, panorama_to_direction(kg, tP.x, tP.y)); - Pcamera = spherical_stereo_position(kg, tD, tP); - ray->dD.dx = spherical_stereo_direction(kg, tD, tP, Pcamera) - Ddiff; - ray->dP.dx = Pcamera - Pdiff; - - tP = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y + 1.0f, 0.0f)); - tD = transform_direction(&cameratoworld, panorama_to_direction(kg, tP.x, tP.y)); - Pcamera = spherical_stereo_position(kg, tD, tP); - ray->dD.dy = spherical_stereo_direction(kg, tD, tP, Pcamera) - Ddiff; - /* dP.dy is zero, since the omnidirectional panorama only shift the eyes horizontally */ + /* Ray differentials, computed from scratch using the raster coordinates + * because we don't want to be affected by depth of field. We compute + * ray origin and direction for the center and two neighbouring pixels + * and simply take their differences. */ + float3 Pcenter = Pcamera; + float3 Dcenter = panorama_to_direction(kg, Pcenter.x, Pcenter.y); + Pcenter = transform_point(&cameratoworld, Pcenter); + Dcenter = normalize(transform_direction(&cameratoworld, Dcenter)); + if(use_stereo) { + spherical_stereo_transform(kg, &Pcenter, &Dcenter); + } + + float3 Px = transform_perspective(&rastertocamera, make_float3(raster_x + 1.0f, raster_y, 0.0f)); + float3 Dx = panorama_to_direction(kg, Px.x, Px.y); + Px = transform_point(&cameratoworld, Px); + Dx = normalize(transform_direction(&cameratoworld, Dx)); + if(use_stereo) { + spherical_stereo_transform(kg, &Px, &Dx); + } + + ray->dP.dx = Px - Pcenter; + ray->dD.dx = Dx - Dcenter; + + float3 Py = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y + 1.0f, 0.0f)); + float3 Dy = panorama_to_direction(kg, Py.x, Py.y); + Py = transform_point(&cameratoworld, Py); + Dy = normalize(transform_direction(&cameratoworld, Dy)); + if(use_stereo) { + spherical_stereo_transform(kg, &Py, &Dy); + } + + ray->dP.dy = Py - Pcenter; + ray->dD.dy = Dy - Dcenter; #endif #ifdef __CAMERA_CLIPPING__ /* clipping */ - ray->P += kernel_data.cam.nearclip*ray->D; + float nearclip = kernel_data.cam.nearclip; + ray->P += nearclip * ray->D; + ray->dP.dx += nearclip * ray->dD.dx; + ray->dP.dy += nearclip * ray->dD.dy; ray->t = kernel_data.cam.cliplength; #else ray->t = FLT_MAX; diff --git a/intern/cycles/kernel/kernel_emission.h b/intern/cycles/kernel/kernel_emission.h index ac498ba3592..8c7c651a053 100644 --- a/intern/cycles/kernel/kernel_emission.h +++ b/intern/cycles/kernel/kernel_emission.h @@ -65,7 +65,7 @@ ccl_device_noinline float3 direct_emissive_eval(KernelGlobals *kg, shader_setup_from_sample(kg, emission_sd, ls->P, ls->Ng, I, ls->shader, ls->object, ls->prim, - ls->u, ls->v, t, time, false); + ls->u, ls->v, t, time, false, ls->lamp); ls->Ng = ccl_fetch(emission_sd, Ng); @@ -94,7 +94,8 @@ ccl_device_noinline bool direct_emission(KernelGlobals *kg, ccl_addr_space PathState *state, Ray *ray, BsdfEval *eval, - bool *is_lamp) + bool *is_lamp, + float rand_terminate) { if(ls->pdf == 0.0f) return false; @@ -134,7 +135,7 @@ ccl_device_noinline bool direct_emission(KernelGlobals *kg, shader_bsdf_eval(kg, sd, ls->D, eval, ls->pdf, ls->shader & SHADER_USE_MIS); #endif - bsdf_eval_mul(eval, light_eval/ls->pdf); + bsdf_eval_mul3(eval, light_eval/ls->pdf); #ifdef __PASSES__ /* use visibility flag to skip lights */ @@ -155,6 +156,16 @@ ccl_device_noinline bool direct_emission(KernelGlobals *kg, if(bsdf_eval_is_zero(eval)) return false; + if(kernel_data.integrator.light_inv_rr_threshold > 0.0f) { + float probability = max3(bsdf_eval_sum(eval)) * kernel_data.integrator.light_inv_rr_threshold; + if(probability < 1.0f) { + if(rand_terminate >= probability) { + return false; + } + bsdf_eval_mul(eval, 1.0f / probability); + } + } + if(ls->shader & SHADER_CAST_SHADOW) { /* setup ray */ bool transmit = (dot(ccl_fetch(sd, Ng), ls->D) < 0.0f); diff --git a/intern/cycles/kernel/kernel_image_opencl.h b/intern/cycles/kernel/kernel_image_opencl.h new file mode 100644 index 00000000000..0352c58037d --- /dev/null +++ b/intern/cycles/kernel/kernel_image_opencl.h @@ -0,0 +1,231 @@ +/* + * Copyright 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. + */ + + +/* For OpenCL all images are packed in a single array, and we do manual lookup + * and interpolation. */ + +ccl_device_inline float4 svm_image_texture_read(KernelGlobals *kg, int id, int offset) +{ + /* Float4 */ + if(id < TEX_START_BYTE4_OPENCL) { + return kernel_tex_fetch(__tex_image_float4_packed, offset); + } + /* Byte4 */ + else if(id < TEX_START_FLOAT_OPENCL) { + uchar4 r = kernel_tex_fetch(__tex_image_byte4_packed, offset); + float f = 1.0f/255.0f; + return make_float4(r.x*f, r.y*f, r.z*f, r.w*f); + } + /* Float */ + else if(id < TEX_START_BYTE_OPENCL) { + float f = kernel_tex_fetch(__tex_image_float_packed, offset); + return make_float4(f, f, f, 1.0f); + } + /* Byte */ + else { + uchar r = kernel_tex_fetch(__tex_image_byte_packed, offset); + float f = r * (1.0f/255.0f); + return make_float4(f, f, f, 1.0f); + } +} + +ccl_device_inline int svm_image_texture_wrap_periodic(int x, int width) +{ + x %= width; + if(x < 0) + x += width; + return x; +} + +ccl_device_inline int svm_image_texture_wrap_clamp(int x, int width) +{ + return clamp(x, 0, width-1); +} + +ccl_device_inline float svm_image_texture_frac(float x, int *ix) +{ + int i = float_to_int(x) - ((x < 0.0f)? 1: 0); + *ix = i; + return x - (float)i; +} + +ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, float y) +{ + uint4 info = kernel_tex_fetch(__tex_image_packed_info, id*2); + uint width = info.x; + uint height = info.y; + uint offset = info.z; + + /* Image Options */ + uint interpolation = (info.w & (1 << 0)) ? INTERPOLATION_CLOSEST : INTERPOLATION_LINEAR; + uint extension; + if(info.w & (1 << 1)) + extension = EXTENSION_REPEAT; + else if(info.w & (1 << 2)) + extension = EXTENSION_EXTEND; + else + extension = EXTENSION_CLIP; + + float4 r; + int ix, iy, nix, niy; + if(interpolation == INTERPOLATION_CLOSEST) { + svm_image_texture_frac(x*width, &ix); + svm_image_texture_frac(y*height, &iy); + + if(extension == EXTENSION_REPEAT) { + ix = svm_image_texture_wrap_periodic(ix, width); + iy = svm_image_texture_wrap_periodic(iy, height); + } + else { + if(extension == EXTENSION_CLIP) { + if(x < 0.0f || y < 0.0f || x > 1.0f || y > 1.0f) { + return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + } + } + /* Fall through. */ + /* EXTENSION_EXTEND */ + ix = svm_image_texture_wrap_clamp(ix, width); + iy = svm_image_texture_wrap_clamp(iy, height); + } + + r = svm_image_texture_read(kg, id, offset + ix + iy*width); + } + else { /* INTERPOLATION_LINEAR */ + float tx = svm_image_texture_frac(x*width - 0.5f, &ix); + float ty = svm_image_texture_frac(y*height - 0.5f, &iy); + + if(extension == EXTENSION_REPEAT) { + ix = svm_image_texture_wrap_periodic(ix, width); + iy = svm_image_texture_wrap_periodic(iy, height); + + nix = svm_image_texture_wrap_periodic(ix+1, width); + niy = svm_image_texture_wrap_periodic(iy+1, height); + } + else { + if(extension == EXTENSION_CLIP) { + if(x < 0.0f || y < 0.0f || x > 1.0f || y > 1.0f) { + return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + } + } + nix = svm_image_texture_wrap_clamp(ix+1, width); + niy = svm_image_texture_wrap_clamp(iy+1, height); + ix = svm_image_texture_wrap_clamp(ix, width); + iy = svm_image_texture_wrap_clamp(iy, height); + } + + r = (1.0f - ty)*(1.0f - tx)*svm_image_texture_read(kg, id, offset + ix + iy*width); + r += (1.0f - ty)*tx*svm_image_texture_read(kg, id, offset + nix + iy*width); + r += ty*(1.0f - tx)*svm_image_texture_read(kg, id, offset + ix + niy*width); + r += ty*tx*svm_image_texture_read(kg, id, offset + nix + niy*width); + } + + return r; +} + + +ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg, int id, float x, float y, float z) +{ + uint4 info = kernel_tex_fetch(__tex_image_packed_info, id*2); + uint width = info.x; + uint height = info.y; + uint offset = info.z; + uint depth = kernel_tex_fetch(__tex_image_packed_info, id*2+1).x; + + /* Image Options */ + uint interpolation = (info.w & (1 << 0)) ? INTERPOLATION_CLOSEST : INTERPOLATION_LINEAR; + uint extension; + if(info.w & (1 << 1)) + extension = EXTENSION_REPEAT; + else if(info.w & (1 << 2)) + extension = EXTENSION_EXTEND; + else + extension = EXTENSION_CLIP; + + float4 r; + int ix, iy, iz, nix, niy, niz; + if(interpolation == INTERPOLATION_CLOSEST) { + svm_image_texture_frac(x*width, &ix); + svm_image_texture_frac(y*height, &iy); + svm_image_texture_frac(z*depth, &iz); + + if(extension == EXTENSION_REPEAT) { + ix = svm_image_texture_wrap_periodic(ix, width); + iy = svm_image_texture_wrap_periodic(iy, height); + iz = svm_image_texture_wrap_periodic(iz, depth); + } + else { + if(extension == EXTENSION_CLIP) { + if(x < 0.0f || y < 0.0f || z < 0.0f || + x > 1.0f || y > 1.0f || z > 1.0f) + { + return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + } + } + /* Fall through. */ + /* EXTENSION_EXTEND */ + ix = svm_image_texture_wrap_clamp(ix, width); + iy = svm_image_texture_wrap_clamp(iy, height); + iz = svm_image_texture_wrap_clamp(iz, depth); + } + r = svm_image_texture_read(kg, id, offset + ix + iy*width + iz*width*height); + } + else { /* INTERPOLATION_LINEAR */ + float tx = svm_image_texture_frac(x*(float)width - 0.5f, &ix); + float ty = svm_image_texture_frac(y*(float)height - 0.5f, &iy); + float tz = svm_image_texture_frac(z*(float)depth - 0.5f, &iz); + + if(extension == EXTENSION_REPEAT) { + ix = svm_image_texture_wrap_periodic(ix, width); + iy = svm_image_texture_wrap_periodic(iy, height); + iz = svm_image_texture_wrap_periodic(iz, depth); + + nix = svm_image_texture_wrap_periodic(ix+1, width); + niy = svm_image_texture_wrap_periodic(iy+1, height); + niz = svm_image_texture_wrap_periodic(iz+1, depth); + } + else { + if(extension == EXTENSION_CLIP) + if(x < 0.0f || y < 0.0f || z < 0.0f || + x > 1.0f || y > 1.0f || z > 1.0f) + { + return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + } + /* Fall through. */ + /* EXTENSION_EXTEND */ + nix = svm_image_texture_wrap_clamp(ix+1, width); + niy = svm_image_texture_wrap_clamp(iy+1, height); + niz = svm_image_texture_wrap_clamp(iz+1, depth); + + ix = svm_image_texture_wrap_clamp(ix, width); + iy = svm_image_texture_wrap_clamp(iy, height); + iz = svm_image_texture_wrap_clamp(iz, depth); + } + + r = (1.0f - tz)*(1.0f - ty)*(1.0f - tx)*svm_image_texture_read(kg, id, offset + ix + iy*width + iz*width*height); + r += (1.0f - tz)*(1.0f - ty)*tx*svm_image_texture_read(kg, id, offset + nix + iy*width + iz*width*height); + r += (1.0f - tz)*ty*(1.0f - tx)*svm_image_texture_read(kg, id, offset + ix + niy*width + iz*width*height); + r += (1.0f - tz)*ty*tx*svm_image_texture_read(kg, id, offset + nix + niy*width + iz*width*height); + + r += tz*(1.0f - ty)*(1.0f - tx)*svm_image_texture_read(kg, id, offset + ix + iy*width + niz*width*height); + r += tz*(1.0f - ty)*tx*svm_image_texture_read(kg, id, offset + nix + iy*width + niz*width*height); + r += tz*ty*(1.0f - tx)*svm_image_texture_read(kg, id, offset + ix + niy*width + niz*width*height); + r += tz*ty*tx*svm_image_texture_read(kg, id, offset + nix + niy*width + niz*width*height); + + } + + return r; +} diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h index fffa9afb342..d4cc36d1495 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/kernel_light.h @@ -297,7 +297,7 @@ ccl_device_inline float background_portal_pdf(KernelGlobals *kg, float3 axisu = make_float3(data1.y, data1.z, data1.w); float3 axisv = make_float3(data2.y, data2.z, data2.w); - if(!ray_quad_intersect(P, direction, 1e-4f, FLT_MAX, lightpos, axisu, axisv, dir, NULL, NULL)) + if(!ray_quad_intersect(P, direction, 1e-4f, FLT_MAX, lightpos, axisu, axisv, dir, NULL, NULL, NULL, NULL)) continue; portal_pdf += area_light_sample(P, &lightpos, axisu, axisv, 0.0f, 0.0f, false); @@ -585,6 +585,10 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg, return false; } } + float2 uv = map_to_sphere(ls->Ng); + ls->u = uv.x; + ls->v = uv.y; + ls->pdf *= lamp_light_pdf(kg, ls->Ng, -ls->D, ls->t); } else { @@ -600,11 +604,16 @@ ccl_device_inline bool lamp_light_sample(KernelGlobals *kg, return false; } + float3 inplane = ls->P; ls->pdf = area_light_sample(P, &ls->P, axisu, axisv, randu, randv, true); + inplane = ls->P - inplane; + ls->u = dot(inplane, axisu) * (1.0f / dot(axisu, axisu)) + 0.5f; + ls->v = dot(inplane, axisv) * (1.0f / dot(axisv, axisv)) + 0.5f; + ls->Ng = D; ls->D = normalize_len(ls->P - P, &ls->t); @@ -706,6 +715,9 @@ ccl_device bool lamp_light_eval(KernelGlobals *kg, int lamp, float3 P, float3 D, if(ls->eval_fac == 0.0f) return false; } + float2 uv = map_to_sphere(ls->Ng); + ls->u = uv.x; + ls->v = uv.y; /* compute pdf */ if(ls->t != FLT_MAX) @@ -730,8 +742,10 @@ 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, 0.0f, t, - light_P, axisu, axisv, Ng, &ls->P, &ls->t)) + if(!ray_quad_intersect(P, D, 0.0f, t, light_P, + axisu, axisv, Ng, + &ls->P, &ls->t, + &ls->u, &ls->v)) { return false; } @@ -887,4 +901,3 @@ ccl_device int light_select_num_samples(KernelGlobals *kg, int index) } CCL_NAMESPACE_END - diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h index 7558fb94478..4237fdb32ff 100644 --- a/intern/cycles/kernel/kernel_path.h +++ b/intern/cycles/kernel/kernel_path.h @@ -53,6 +53,47 @@ CCL_NAMESPACE_BEGIN +ccl_device_noinline void kernel_path_ao(KernelGlobals *kg, + ShaderData *sd, + ShaderData *emission_sd, + PathRadiance *L, + PathState *state, + RNG *rng, + float3 throughput, + float3 ao_alpha) +{ + /* todo: solve correlation */ + float bsdf_u, bsdf_v; + + path_state_rng_2D(kg, rng, state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); + + float ao_factor = kernel_data.background.ao_factor; + float3 ao_N; + float3 ao_bsdf = shader_bsdf_ao(kg, sd, ao_factor, &ao_N); + float3 ao_D; + float ao_pdf; + + sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf); + + if(dot(ccl_fetch(sd, Ng), ao_D) > 0.0f && ao_pdf != 0.0f) { + Ray light_ray; + float3 ao_shadow; + + light_ray.P = ray_offset(ccl_fetch(sd, P), ccl_fetch(sd, Ng)); + light_ray.D = ao_D; + light_ray.t = kernel_data.background.ao_distance; +#ifdef __OBJECT_MOTION__ + light_ray.time = ccl_fetch(sd, time); +#endif + light_ray.dP = ccl_fetch(sd, dP); + light_ray.dD = differential3_zero(); + + if(!shadow_blocked(kg, emission_sd, state, &light_ray, &ao_shadow)) { + path_radiance_accum_ao(L, throughput, ao_alpha, ao_bsdf, ao_shadow, state->bounce); + } + } +} + ccl_device void kernel_path_indirect(KernelGlobals *kg, ShaderData *sd, ShaderData *emission_sd, @@ -305,40 +346,7 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, #ifdef __AO__ /* ambient occlusion */ if(kernel_data.integrator.use_ambient_occlusion || (sd->flag & SD_AO)) { - float bsdf_u, bsdf_v; - path_state_rng_2D(kg, rng, state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); - - float ao_factor = kernel_data.background.ao_factor; - float3 ao_N; - float3 ao_bsdf = shader_bsdf_ao(kg, sd, ao_factor, &ao_N); - float3 ao_D; - float ao_pdf; - float3 ao_alpha = make_float3(0.0f, 0.0f, 0.0f); - - sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf); - - if(dot(sd->Ng, ao_D) > 0.0f && ao_pdf != 0.0f) { - Ray light_ray; - float3 ao_shadow; - - light_ray.P = ray_offset(sd->P, sd->Ng); - light_ray.D = ao_D; - light_ray.t = kernel_data.background.ao_distance; -# ifdef __OBJECT_MOTION__ - light_ray.time = sd->time; -# endif - light_ray.dP = sd->dP; - light_ray.dD = differential3_zero(); - - if(!shadow_blocked(kg, emission_sd, state, &light_ray, &ao_shadow)) { - path_radiance_accum_ao(L, - throughput, - ao_alpha, - ao_bsdf, - ao_shadow, - state->bounce); - } - } + kernel_path_ao(kg, sd, emission_sd, L, state, rng, throughput, make_float3(0.0f, 0.0f, 0.0f)); } #endif @@ -394,46 +402,6 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, } } -ccl_device_noinline void kernel_path_ao(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - PathRadiance *L, - PathState *state, - RNG *rng, - float3 throughput) -{ - /* todo: solve correlation */ - float bsdf_u, bsdf_v; - - path_state_rng_2D(kg, rng, state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); - - float ao_factor = kernel_data.background.ao_factor; - float3 ao_N; - float3 ao_bsdf = shader_bsdf_ao(kg, sd, ao_factor, &ao_N); - float3 ao_D; - float ao_pdf; - float3 ao_alpha = shader_bsdf_alpha(kg, sd); - - sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf); - - if(dot(ccl_fetch(sd, Ng), ao_D) > 0.0f && ao_pdf != 0.0f) { - Ray light_ray; - float3 ao_shadow; - - light_ray.P = ray_offset(ccl_fetch(sd, P), ccl_fetch(sd, Ng)); - light_ray.D = ao_D; - light_ray.t = kernel_data.background.ao_distance; -#ifdef __OBJECT_MOTION__ - light_ray.time = ccl_fetch(sd, time); -#endif - light_ray.dP = ccl_fetch(sd, dP); - light_ray.dD = differential3_zero(); - - if(!shadow_blocked(kg, emission_sd, state, &light_ray, &ao_shadow)) - path_radiance_accum_ao(L, throughput, ao_alpha, ao_bsdf, ao_shadow, state->bounce); - } -} - #ifdef __SUBSURFACE__ # ifndef __KERNEL_CUDA__ ccl_device @@ -851,7 +819,6 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg, } else if(probability != 1.0f) { float terminate = path_state_rng_1D_for_decision(kg, rng, &state, PRNG_TERMINATE); - if(terminate >= probability) break; @@ -861,7 +828,7 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg, #ifdef __AO__ /* ambient occlusion */ if(kernel_data.integrator.use_ambient_occlusion || (sd.flag & SD_AO)) { - kernel_path_ao(kg, &sd, &emission_sd, &L, &state, rng, throughput); + kernel_path_ao(kg, &sd, &emission_sd, &L, &state, rng, throughput, shader_bsdf_alpha(kg, &sd)); } #endif diff --git a/intern/cycles/kernel/kernel_path_common.h b/intern/cycles/kernel/kernel_path_common.h index 1912dfa16ed..13597eab287 100644 --- a/intern/cycles/kernel/kernel_path_common.h +++ b/intern/cycles/kernel/kernel_path_common.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "util_hash.h" + CCL_NAMESPACE_BEGIN ccl_device_inline void kernel_path_trace_setup(KernelGlobals *kg, @@ -28,6 +30,10 @@ ccl_device_inline void kernel_path_trace_setup(KernelGlobals *kg, int num_samples = kernel_data.integrator.aa_samples; + if(sample == 0) { + *rng_state = hash_int_2d(x, y); + } + path_rng_init(kg, rng_state, sample, num_samples, rng, x, y, &filter_u, &filter_v); /* sample camera ray */ diff --git a/intern/cycles/kernel/kernel_path_surface.h b/intern/cycles/kernel/kernel_path_surface.h index 45f0f1cbfaa..fea503d06e5 100644 --- a/intern/cycles/kernel/kernel_path_surface.h +++ b/intern/cycles/kernel/kernel_path_surface.h @@ -49,6 +49,7 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal for(int j = 0; j < num_samples; j++) { float light_u, light_v; path_branched_rng_2D(kg, &lamp_rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v); + float terminate = path_branched_rng_light_termination(kg, &lamp_rng, state, j, num_samples); LightSample ls; if(lamp_light_sample(kg, i, light_u, light_v, ccl_fetch(sd, P), &ls)) { @@ -57,7 +58,7 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal if(kernel_data.integrator.pdf_triangles != 0.0f) ls.pdf *= 2.0f; - if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) { + if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) { /* trace shadow ray */ float3 shadow; @@ -79,6 +80,7 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal float light_t = path_branched_rng_1D(kg, rng, state, j, num_samples, PRNG_LIGHT); float light_u, light_v; path_branched_rng_2D(kg, rng, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v); + float terminate = path_branched_rng_light_termination(kg, rng, state, j, num_samples); /* only sample triangle lights */ if(kernel_data.integrator.num_all_lights) @@ -90,7 +92,7 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal if(kernel_data.integrator.num_all_lights) ls.pdf *= 2.0f; - if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) { + if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) { /* trace shadow ray */ float3 shadow; @@ -108,11 +110,12 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT); float light_u, light_v; path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v); + float terminate = path_state_rng_light_termination(kg, rng, state); LightSample ls; if(light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls)) { /* sample random light */ - if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) { + if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) { /* trace shadow ray */ float3 shadow; @@ -210,7 +213,8 @@ ccl_device_inline void kernel_path_surface_connect_light(KernelGlobals *kg, ccl_ LightSample ls; if(light_sample(kg, light_t, light_u, light_v, ccl_fetch(sd, time), ccl_fetch(sd, P), state->bounce, &ls)) { - if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) { + float terminate = path_state_rng_light_termination(kg, rng, state); + if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) { /* trace shadow ray */ float3 shadow; diff --git a/intern/cycles/kernel/kernel_path_volume.h b/intern/cycles/kernel/kernel_path_volume.h index 5ee1912c913..3d3b7385d8b 100644 --- a/intern/cycles/kernel/kernel_path_volume.h +++ b/intern/cycles/kernel/kernel_path_volume.h @@ -48,7 +48,8 @@ ccl_device_inline void kernel_path_volume_connect_light( if(light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { - if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) { + float terminate = path_state_rng_light_termination(kg, rng, state); + if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) { /* trace shadow ray */ float3 shadow; @@ -161,7 +162,8 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG if(kernel_data.integrator.pdf_triangles != 0.0f) ls.pdf *= 2.0f; - if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) { + float terminate = path_branched_rng_light_termination(kg, rng, state, j, num_samples); + if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) { /* trace shadow ray */ float3 shadow; @@ -209,7 +211,8 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG if(kernel_data.integrator.num_all_lights) ls.pdf *= 2.0f; - if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) { + float terminate = path_branched_rng_light_termination(kg, rng, state, j, num_samples); + if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) { /* trace shadow ray */ float3 shadow; @@ -246,7 +249,8 @@ ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, RNG /* todo: split up light_sample so we don't have to call it again with new position */ if(light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { /* sample random light */ - if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp)) { + float terminate = path_state_rng_light_termination(kg, rng, state); + if(direct_emission(kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) { /* trace shadow ray */ float3 shadow; diff --git a/intern/cycles/kernel/kernel_projection.h b/intern/cycles/kernel/kernel_projection.h index 3437d83ed7d..9a2b0884a7e 100644 --- a/intern/cycles/kernel/kernel_projection.h +++ b/intern/cycles/kernel/kernel_projection.h @@ -224,24 +224,18 @@ ccl_device_inline float2 direction_to_panorama(KernelGlobals *kg, float3 dir) } } -ccl_device_inline float3 spherical_stereo_position(KernelGlobals *kg, - float3 dir, - float3 pos) +ccl_device_inline void spherical_stereo_transform(KernelGlobals *kg, float3 *P, float3 *D) { float interocular_offset = kernel_data.cam.interocular_offset; /* Interocular offset of zero means either non stereo, or stereo without - * spherical stereo. - */ - if(interocular_offset == 0.0f) { - return pos; - } + * spherical stereo. */ + kernel_assert(interocular_offset != 0.0f); if(kernel_data.cam.pole_merge_angle_to > 0.0f) { - float3 normalized_direction = normalize(dir); const float pole_merge_angle_from = kernel_data.cam.pole_merge_angle_from, pole_merge_angle_to = kernel_data.cam.pole_merge_angle_to; - float altitude = fabsf(safe_asinf(normalized_direction.z)); + float altitude = fabsf(safe_asinf((*D).z)); if(altitude > pole_merge_angle_to) { interocular_offset = 0.0f; } @@ -253,32 +247,20 @@ ccl_device_inline float3 spherical_stereo_position(KernelGlobals *kg, } float3 up = make_float3(0.0f, 0.0f, 1.0f); - float3 side = normalize(cross(dir, up)); + float3 side = normalize(cross(*D, up)); + float3 stereo_offset = side * interocular_offset; - return pos + (side * interocular_offset); -} + *P += stereo_offset; -/* NOTE: Ensures direction is normalized. */ -ccl_device float3 spherical_stereo_direction(KernelGlobals *kg, - float3 dir, - float3 pos, - float3 newpos) -{ + /* Convergence distance is FLT_MAX in the case of parallel convergence mode, + * no need to modify direction in this case either. */ const float convergence_distance = kernel_data.cam.convergence_distance; - const float3 normalized_dir = normalize(dir); - /* Interocular offset of zero means either no stereo, or stereo without - * spherical stereo. - * Convergence distance is FLT_MAX in the case of parallel convergence mode, - * no need to mdify direction in this case either. - */ - if(kernel_data.cam.interocular_offset == 0.0f || - convergence_distance == FLT_MAX) + + if(convergence_distance != FLT_MAX) { - return normalized_dir; + float3 screen_offset = convergence_distance * (*D); + *D = normalize(screen_offset - stereo_offset); } - - float3 screenpos = pos + (normalized_dir * convergence_distance); - return normalize(screenpos - newpos); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_random.h b/intern/cycles/kernel/kernel_random.h index 4a76ffddbe7..2372b07d974 100644 --- a/intern/cycles/kernel/kernel_random.h +++ b/intern/cycles/kernel/kernel_random.h @@ -300,6 +300,23 @@ ccl_device_inline void path_branched_rng_2D(KernelGlobals *kg, ccl_addr_space RN path_rng_2D(kg, rng, state->sample*num_branches + branch, state->num_samples*num_branches, state->rng_offset + dimension, fx, fy); } +/* Utitility functions to get light termination value, since it might not be needed in many cases. */ +ccl_device_inline float path_state_rng_light_termination(KernelGlobals *kg, ccl_addr_space RNG *rng, const PathState *state) +{ + if(kernel_data.integrator.light_inv_rr_threshold > 0.0f) { + return path_state_rng_1D_for_decision(kg, rng, state, PRNG_LIGHT_TERMINATE); + } + return 0.0f; +} + +ccl_device_inline float path_branched_rng_light_termination(KernelGlobals *kg, ccl_addr_space RNG *rng, const PathState *state, int branch, int num_branches) +{ + if(kernel_data.integrator.light_inv_rr_threshold > 0.0f) { + return path_branched_rng_1D_for_decision(kg, rng, state, branch, num_branches, PRNG_LIGHT_TERMINATE); + } + return 0.0f; +} + ccl_device_inline void path_state_branch(PathState *state, int branch, int num_branches) { /* path is splitting into a branch, adjust so that each branch diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index 3e098c922dc..b4b980c4e90 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -242,7 +242,8 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg, int shader, int object, int prim, float u, float v, float t, float time, - bool object_space) + bool object_space, + int lamp) { /* vectors */ ccl_fetch(sd, P) = P; @@ -250,7 +251,12 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg, ccl_fetch(sd, Ng) = Ng; ccl_fetch(sd, I) = I; ccl_fetch(sd, shader) = shader; - ccl_fetch(sd, type) = (prim == PRIM_NONE)? PRIMITIVE_NONE: PRIMITIVE_TRIANGLE; + if(prim != PRIM_NONE) + ccl_fetch(sd, type) = PRIMITIVE_TRIANGLE; + else if(lamp != LAMP_NONE) + ccl_fetch(sd, type) = PRIMITIVE_LAMP; + else + ccl_fetch(sd, type) = PRIMITIVE_NONE; /* primitive */ #ifdef __INSTANCING__ @@ -270,11 +276,15 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg, #ifdef __OBJECT_MOTION__ shader_setup_object_transforms(kg, sd, time); +#endif + } + else if(lamp != LAMP_NONE) { + ccl_fetch(sd, ob_tfm) = lamp_fetch_transform(kg, lamp, false); + ccl_fetch(sd, ob_itfm) = lamp_fetch_transform(kg, lamp, true); } +#ifdef __OBJECT_MOTION__ ccl_fetch(sd, time) = time; -#else - } #endif /* transform into world space */ @@ -357,7 +367,8 @@ ccl_device void shader_setup_from_displace(KernelGlobals *kg, ShaderData *sd, P, Ng, I, shader, object, prim, u, v, 0.0f, 0.5f, - !(kernel_tex_fetch(__object_flag, object) & SD_TRANSFORM_APPLIED)); + !(kernel_tex_fetch(__object_flag, object) & SD_TRANSFORM_APPLIED), + LAMP_NONE); } /* ShaderData setup from ray into background */ @@ -561,7 +572,7 @@ void shader_bsdf_eval(KernelGlobals *kg, _shader_bsdf_multi_eval(kg, sd, omega_in, &pdf, -1, eval, 0.0f, 0.0f); if(use_mis) { float weight = power_heuristic(light_pdf, pdf); - bsdf_eval_mul(eval, make_float3(weight, weight, weight)); + bsdf_eval_mul(eval, weight); } } } diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 734b4462a91..a6c31d4a518 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -37,7 +37,7 @@ CCL_NAMESPACE_BEGIN /* constants */ #define OBJECT_SIZE 12 #define OBJECT_VECTOR_SIZE 6 -#define LIGHT_SIZE 5 +#define LIGHT_SIZE 11 #define FILTER_TABLE_SIZE 1024 #define RAMP_TABLE_SIZE 256 #define SHUTTER_TABLE_SIZE 256 @@ -250,7 +250,7 @@ enum PathTraceDimension { PRNG_LIGHT = 3, PRNG_LIGHT_U = 4, PRNG_LIGHT_V = 5, - PRNG_UNUSED_3 = 6, + PRNG_LIGHT_TERMINATE = 6, PRNG_TERMINATE = 7, #ifdef __VOLUME__ @@ -552,6 +552,8 @@ typedef enum PrimitiveType { PRIMITIVE_MOTION_TRIANGLE = 2, PRIMITIVE_CURVE = 4, PRIMITIVE_MOTION_CURVE = 8, + /* Lamp primitive is not included below on purpose, since it is no real traceable primitive */ + PRIMITIVE_LAMP = 16, PRIMITIVE_ALL_TRIANGLE = (PRIMITIVE_TRIANGLE|PRIMITIVE_MOTION_TRIANGLE), PRIMITIVE_ALL_CURVE = (PRIMITIVE_CURVE|PRIMITIVE_MOTION_CURVE), @@ -1121,8 +1123,9 @@ typedef struct KernelIntegrator { float volume_step_size; int volume_samples; + float light_inv_rr_threshold; + int pad1; - int pad2; } KernelIntegrator; static_assert_align(KernelIntegrator, 16); diff --git a/intern/cycles/kernel/kernels/cpu/kernel.cpp b/intern/cycles/kernel/kernels/cpu/kernel.cpp index 1559b0d7322..72dbbd9a416 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel.cpp +++ b/intern/cycles/kernel/kernels/cpu/kernel.cpp @@ -42,6 +42,7 @@ # define __KERNEL_SSE41__ # endif # ifdef __AVX__ +# define __KERNEL_SSE__ # define __KERNEL_AVX__ # endif # ifdef __AVX2__ diff --git a/intern/cycles/kernel/kernels/cpu/kernel_avx.cpp b/intern/cycles/kernel/kernels/cpu/kernel_avx.cpp index 533ab46b741..1350d9e5c2e 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel_avx.cpp +++ b/intern/cycles/kernel/kernels/cpu/kernel_avx.cpp @@ -20,6 +20,7 @@ /* SSE optimization disabled for now on 32 bit, see bug #36316 */ #if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) +# define __KERNEL_SSE__ # define __KERNEL_SSE2__ # define __KERNEL_SSE3__ # define __KERNEL_SSSE3__ diff --git a/intern/cycles/kernel/kernels/opencl/kernel.cl b/intern/cycles/kernel/kernels/opencl/kernel.cl index 37907cd8fdc..a68f97857b6 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel.cl +++ b/intern/cycles/kernel/kernels/opencl/kernel.cl @@ -20,6 +20,7 @@ #include "../../kernel_math.h" #include "../../kernel_types.h" #include "../../kernel_globals.h" +#include "../../kernel_image_opencl.h" #include "../../kernel_film.h" diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index 0f3edcb7eaa..26543862b80 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -168,6 +168,12 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 &result return true; } + else if(sd->type == PRIMITIVE_LAMP) { + Transform tfm = transform_transpose(sd->ob_tfm); + COPY_MATRIX44(&result, &tfm); + + return true; + } } return false; @@ -198,6 +204,12 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 return true; } + else if(sd->type == PRIMITIVE_LAMP) { + Transform tfm = transform_transpose(sd->ob_itfm); + COPY_MATRIX44(&result, &tfm); + + return true; + } } return false; @@ -287,6 +299,12 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 &result return true; } + else if(sd->type == PRIMITIVE_LAMP) { + Transform tfm = transform_transpose(sd->ob_tfm); + COPY_MATRIX44(&result, &tfm); + + return true; + } } return false; @@ -312,6 +330,12 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 return true; } + else if(sd->type == PRIMITIVE_LAMP) { + Transform tfm = transform_transpose(sd->ob_itfm); + COPY_MATRIX44(&result, &tfm); + + return true; + } } return false; diff --git a/intern/cycles/kernel/shaders/node_brick_texture.osl b/intern/cycles/kernel/shaders/node_brick_texture.osl index d5e0a7d4c8c..c303594681c 100644 --- a/intern/cycles/kernel/shaders/node_brick_texture.osl +++ b/intern/cycles/kernel/shaders/node_brick_texture.osl @@ -28,7 +28,7 @@ float brick_noise(int n) /* fast integer noise */ return 0.5 * ((float)nn / 1073741824.0); } -float brick(point p, float mortar_size, float bias, +float brick(point p, float mortar_size, float mortar_smooth, float bias, float BrickWidth, float row_height, float offset_amount, int offset_frequency, float squash_amount, int squash_frequency, float tint) { @@ -51,9 +51,17 @@ float brick(point p, float mortar_size, float bias, tint = clamp((brick_noise((rownum << 16) + (bricknum & 65535)) + bias), 0.0, 1.0); - return (x < mortar_size || y < mortar_size || - x > (brick_width - mortar_size) || - y > (row_height - mortar_size)) ? 1.0 : 0.0; + float min_dist = min(min(x, y), min(brick_width - x, row_height - y)); + if(min_dist >= mortar_size) { + return 0.0; + } + else if(mortar_smooth == 0.0) { + return 1.0; + } + else { + min_dist = 1.0 - min_dist/mortar_size; + return smoothstep(0.0, mortar_smooth, min_dist); + } } shader node_brick_texture( @@ -69,6 +77,7 @@ shader node_brick_texture( color Mortar = 0.0, float Scale = 5.0, float MortarSize = 0.02, + float MortarSmooth = 0.0, float Bias = 0.0, float BrickWidth = 0.5, float RowHeight = 0.25, @@ -83,7 +92,7 @@ shader node_brick_texture( float tint = 0.0; color Col = Color1; - Fac = brick(p * Scale, MortarSize, Bias, BrickWidth, RowHeight, + Fac = brick(p * Scale, MortarSize, MortarSmooth, Bias, BrickWidth, RowHeight, offset, offset_frequency, squash, squash_frequency, tint); if (Fac != 1.0) { @@ -91,6 +100,6 @@ shader node_brick_texture( Col = facm * Color1 + tint * Color2; } - Color = (Fac == 1.0) ? Mortar : Col; + Color = mix(Col, Mortar, Fac); } diff --git a/intern/cycles/kernel/split/kernel_direct_lighting.h b/intern/cycles/kernel/split/kernel_direct_lighting.h index 6ad736fc2c1..82ca18829d3 100644 --- a/intern/cycles/kernel/split/kernel_direct_lighting.h +++ b/intern/cycles/kernel/split/kernel_direct_lighting.h @@ -72,6 +72,7 @@ ccl_device char kernel_direct_lighting( float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT); float light_u, light_v; path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v); + float terminate = path_state_rng_light_termination(kg, rng, state); LightSample ls; if(light_sample(kg, @@ -88,7 +89,7 @@ ccl_device char kernel_direct_lighting( BsdfEval L_light; bool is_lamp; - if(direct_emission(kg, sd, kg->sd_input, &ls, state, &light_ray, &L_light, &is_lamp)) { + if(direct_emission(kg, sd, kg->sd_input, &ls, state, &light_ray, &L_light, &is_lamp, terminate)) { /* Write intermediate data to global memory to access from * the next kernel. */ diff --git a/intern/cycles/kernel/split/kernel_split_common.h b/intern/cycles/kernel/split/kernel_split_common.h index 88d6dab04d0..2135ee22b2e 100644 --- a/intern/cycles/kernel/split/kernel_split_common.h +++ b/intern/cycles/kernel/split/kernel_split_common.h @@ -21,6 +21,7 @@ #include "kernel_math.h" #include "kernel_types.h" #include "kernel_globals.h" +#include "kernel_image_opencl.h" #include "util_atomic.h" diff --git a/intern/cycles/kernel/svm/svm_brick.h b/intern/cycles/kernel/svm/svm_brick.h index 9b0cf5ab8c4..14245cf0522 100644 --- a/intern/cycles/kernel/svm/svm_brick.h +++ b/intern/cycles/kernel/svm/svm_brick.h @@ -27,7 +27,7 @@ ccl_device_noinline float brick_noise(int n) /* fast integer noise */ return 0.5f * ((float)nn / 1073741824.0f); } -ccl_device_noinline float2 svm_brick(float3 p, float mortar_size, float bias, +ccl_device_noinline float2 svm_brick(float3 p, float mortar_size, float mortar_smooth, float bias, float brick_width, float row_height, float offset_amount, int offset_frequency, float squash_amount, int squash_frequency) { @@ -47,30 +47,41 @@ ccl_device_noinline float2 svm_brick(float3 p, float mortar_size, float bias, x = (p.x+offset) - brick_width*bricknum; y = p.y - row_height*rownum; - return make_float2( - saturate((brick_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias)), + float tint = saturate((brick_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias)); + float min_dist = min(min(x, y), min(brick_width - x, row_height - y)); - (x < mortar_size || y < mortar_size || - x > (brick_width - mortar_size) || - y > (row_height - mortar_size)) ? 1.0f : 0.0f); + float mortar; + if(min_dist >= mortar_size) { + mortar = 0.0f; + } + else if(mortar_smooth == 0.0f) { + mortar = 1.0f; + } + else { + min_dist = 1.0f - min_dist/mortar_size; + mortar = (min_dist < mortar_smooth)? smoothstepf(min_dist / mortar_smooth) : 1.0f; + } + + return make_float2(tint, mortar); } ccl_device void svm_node_tex_brick(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) { uint4 node2 = read_node(kg, offset); uint4 node3 = read_node(kg, offset); + uint4 node4 = read_node(kg, offset); /* Input and Output Sockets */ uint co_offset, color1_offset, color2_offset, mortar_offset, scale_offset; uint mortar_size_offset, bias_offset, brick_width_offset, row_height_offset; - uint color_offset, fac_offset; + uint color_offset, fac_offset, mortar_smooth_offset; /* RNA properties */ uint offset_frequency, squash_frequency; decode_node_uchar4(node.y, &co_offset, &color1_offset, &color2_offset, &mortar_offset); decode_node_uchar4(node.z, &scale_offset, &mortar_size_offset, &bias_offset, &brick_width_offset); - decode_node_uchar4(node.w, &row_height_offset, &color_offset, &fac_offset, NULL); + decode_node_uchar4(node.w, &row_height_offset, &color_offset, &fac_offset, &mortar_smooth_offset); decode_node_uchar4(node2.x, &offset_frequency, &squash_frequency, NULL, NULL); @@ -82,13 +93,14 @@ ccl_device void svm_node_tex_brick(KernelGlobals *kg, ShaderData *sd, float *sta float scale = stack_load_float_default(stack, scale_offset, node2.y); float mortar_size = stack_load_float_default(stack, mortar_size_offset, node2.z); + float mortar_smooth = stack_load_float_default(stack, mortar_smooth_offset, node4.x); float bias = stack_load_float_default(stack, bias_offset, node2.w); float brick_width = stack_load_float_default(stack, brick_width_offset, node3.x); float row_height = stack_load_float_default(stack, row_height_offset, node3.y); float offset_amount = __int_as_float(node3.z); float squash_amount = __int_as_float(node3.w); - float2 f2 = svm_brick(co*scale, mortar_size, bias, brick_width, row_height, + float2 f2 = svm_brick(co*scale, mortar_size, mortar_smooth, bias, brick_width, row_height, offset_amount, offset_frequency, squash_amount, squash_frequency); float tint = f2.x; @@ -100,7 +112,7 @@ ccl_device void svm_node_tex_brick(KernelGlobals *kg, ShaderData *sd, float *sta } if(stack_valid(color_offset)) - stack_store_float3(stack, color_offset, (f == 1.0f)? mortar: color1); + stack_store_float3(stack, color_offset, color1*(1.0f-f) + mortar*f); if(stack_valid(fac_offset)) stack_store_float(stack, fac_offset, f); } diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/svm_image.h index 378ce650129..9606064492e 100644 --- a/intern/cycles/kernel/svm/svm_image.h +++ b/intern/cycles/kernel/svm/svm_image.h @@ -29,147 +29,6 @@ CCL_NAMESPACE_BEGIN # define TEX_NUM_FLOAT4_IMAGES TEX_NUM_FLOAT4_OPENCL #endif -#ifdef __KERNEL_OPENCL__ - -/* For OpenCL all images are packed in a single array, and we do manual lookup - * and interpolation. */ - -ccl_device_inline float4 svm_image_texture_read(KernelGlobals *kg, int id, int offset) -{ - /* Float4 */ - if(id < TEX_START_BYTE4_OPENCL) { - return kernel_tex_fetch(__tex_image_float4_packed, offset); - } - /* Byte4 */ - else if(id < TEX_START_FLOAT_OPENCL) { - uchar4 r = kernel_tex_fetch(__tex_image_byte4_packed, offset); - float f = 1.0f/255.0f; - return make_float4(r.x*f, r.y*f, r.z*f, r.w*f); - } - /* Float */ - else if(id < TEX_START_BYTE_OPENCL) { - float f = kernel_tex_fetch(__tex_image_float_packed, offset); - return make_float4(f, f, f, 1.0f); - } - /* Byte */ - else { - uchar r = kernel_tex_fetch(__tex_image_byte_packed, offset); - float f = r * (1.0f/255.0f); - return make_float4(f, f, f, 1.0f); - } -} - -ccl_device_inline int svm_image_texture_wrap_periodic(int x, int width) -{ - x %= width; - if(x < 0) - x += width; - return x; -} - -ccl_device_inline int svm_image_texture_wrap_clamp(int x, int width) -{ - return clamp(x, 0, width-1); -} - -ccl_device_inline float svm_image_texture_frac(float x, int *ix) -{ - int i = float_to_int(x) - ((x < 0.0f)? 1: 0); - *ix = i; - return x - (float)i; -} - -ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y, uint srgb, uint use_alpha) -{ - uint4 info = kernel_tex_fetch(__tex_image_packed_info, id); - uint width = info.x; - uint height = info.y; - uint offset = info.z; - - /* Image Options */ - uint interpolation = (info.w & (1 << 0)) ? INTERPOLATION_CLOSEST : INTERPOLATION_LINEAR; - uint extension; - if(info.w & (1 << 1)) - extension = EXTENSION_REPEAT; - else if(info.w & (1 << 2)) - extension = EXTENSION_EXTEND; - else - extension = EXTENSION_CLIP; - - float4 r; - int ix, iy, nix, niy; - if(interpolation == INTERPOLATION_CLOSEST) { - svm_image_texture_frac(x*width, &ix); - svm_image_texture_frac(y*height, &iy); - - if(extension == EXTENSION_REPEAT) { - ix = svm_image_texture_wrap_periodic(ix, width); - iy = svm_image_texture_wrap_periodic(iy, height); - } - else if(extension == EXTENSION_CLIP) { - if(x < 0.0f || y < 0.0f || x > 1.0f || y > 1.0f) - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); - } - else { /* EXTENSION_EXTEND */ - ix = svm_image_texture_wrap_clamp(ix, width); - iy = svm_image_texture_wrap_clamp(iy, height); - } - - r = svm_image_texture_read(kg, id, offset + ix + iy*width); - } - else { /* INTERPOLATION_LINEAR */ - float tx = svm_image_texture_frac(x*width - 0.5f, &ix); - float ty = svm_image_texture_frac(y*height - 0.5f, &iy); - - if(extension == EXTENSION_REPEAT) { - ix = svm_image_texture_wrap_periodic(ix, width); - iy = svm_image_texture_wrap_periodic(iy, height); - - nix = svm_image_texture_wrap_periodic(ix+1, width); - niy = svm_image_texture_wrap_periodic(iy+1, height); - } - else { - if(extension == EXTENSION_CLIP) { - if(x < 0.0f || y < 0.0f || x > 1.0f || y > 1.0f) { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); - } - } - nix = svm_image_texture_wrap_clamp(ix+1, width); - niy = svm_image_texture_wrap_clamp(iy+1, height); - ix = svm_image_texture_wrap_clamp(ix, width); - iy = svm_image_texture_wrap_clamp(iy, height); - } - - r = (1.0f - ty)*(1.0f - tx)*svm_image_texture_read(kg, id, offset + ix + iy*width); - r += (1.0f - ty)*tx*svm_image_texture_read(kg, id, offset + nix + iy*width); - r += ty*(1.0f - tx)*svm_image_texture_read(kg, id, offset + ix + niy*width); - r += ty*tx*svm_image_texture_read(kg, id, offset + nix + niy*width); - } - - if(use_alpha && r.w != 1.0f && r.w != 0.0f) { - float invw = 1.0f/r.w; - r.x *= invw; - r.y *= invw; - r.z *= invw; - - if(id >= TEX_NUM_FLOAT4_IMAGES) { - r.x = min(r.x, 1.0f); - r.y = min(r.y, 1.0f); - r.z = min(r.z, 1.0f); - } - } - - if(srgb) { - r.x = color_srgb_to_scene_linear(r.x); - r.y = color_srgb_to_scene_linear(r.y); - r.z = color_srgb_to_scene_linear(r.z); - } - - return r; -} - -#else - ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y, uint srgb, uint use_alpha) { #ifdef __KERNEL_CPU__ @@ -180,6 +39,8 @@ ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y, # else float4 r = kernel_tex_image_interp(id, x, y); # endif +#elif defined(__KERNEL_OPENCL__) + float4 r = kernel_tex_image_interp(kg, id, x, y); #else float4 r; @@ -339,8 +200,6 @@ ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y, return r; } -#endif - /* Remap coordnate from 0..1 box to -1..-1 */ ccl_device_inline float3 texco_remap_square(float3 co) { diff --git a/intern/cycles/kernel/svm/svm_ramp_util.h b/intern/cycles/kernel/svm/svm_ramp_util.h index 9f2ce1276f9..b0adadae9b7 100644 --- a/intern/cycles/kernel/svm/svm_ramp_util.h +++ b/intern/cycles/kernel/svm/svm_ramp_util.h @@ -27,9 +27,9 @@ ccl_device_inline float3 rgb_ramp_lookup(const float3 *ramp, bool extrapolate, int table_size) { - if ((f < 0.0f || f > 1.0f) && extrapolate) { + if((f < 0.0f || f > 1.0f) && extrapolate) { float3 t0, dy; - if (f < 0.0f) { + if(f < 0.0f) { t0 = ramp[0]; dy = t0 - ramp[1], f = -f; @@ -50,8 +50,9 @@ ccl_device_inline float3 rgb_ramp_lookup(const float3 *ramp, float3 result = ramp[i]; - if (interpolate && t > 0.0f) + if(interpolate && t > 0.0f) { result = (1.0f - t) * result + t * ramp[i + 1]; + } return result; } @@ -62,9 +63,9 @@ ccl_device float float_ramp_lookup(const float *ramp, bool extrapolate, int table_size) { - if ((f < 0.0f || f > 1.0f) && extrapolate) { + if((f < 0.0f || f > 1.0f) && extrapolate) { float t0, dy; - if (f < 0.0f) { + if(f < 0.0f) { t0 = ramp[0]; dy = t0 - ramp[1], f = -f; @@ -85,8 +86,9 @@ ccl_device float float_ramp_lookup(const float *ramp, float result = ramp[i]; - if (interpolate && t > 0.0f) + if(interpolate && t > 0.0f) { result = (1.0f - t) * result + t * ramp[i + 1]; + } return result; } diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h index 6ea2539c543..6c3394adbce 100644 --- a/intern/cycles/kernel/svm/svm_tex_coord.h +++ b/intern/cycles/kernel/svm/svm_tex_coord.h @@ -49,7 +49,7 @@ ccl_device void svm_node_tex_coord(KernelGlobals *kg, } case NODE_TEXCO_NORMAL: { data = ccl_fetch(sd, N); - if(ccl_fetch(sd, object) != OBJECT_NONE) + if((ccl_fetch(sd, object) != OBJECT_NONE) || (ccl_fetch(sd, type) == PRIMITIVE_LAMP)) object_inverse_normal_transform(kg, sd, &data); break; } @@ -131,7 +131,7 @@ ccl_device void svm_node_tex_coord_bump_dx(KernelGlobals *kg, } case NODE_TEXCO_NORMAL: { data = ccl_fetch(sd, N); - if(ccl_fetch(sd, object) != OBJECT_NONE) + if((ccl_fetch(sd, object) != OBJECT_NONE) || (ccl_fetch(sd, type) == PRIMITIVE_LAMP)) object_inverse_normal_transform(kg, sd, &data); break; } @@ -216,7 +216,7 @@ ccl_device void svm_node_tex_coord_bump_dy(KernelGlobals *kg, } case NODE_TEXCO_NORMAL: { data = ccl_fetch(sd, N); - if(ccl_fetch(sd, object) != OBJECT_NONE) + if((ccl_fetch(sd, object) != OBJECT_NONE) || (ccl_fetch(sd, type) == PRIMITIVE_LAMP)) object_inverse_normal_transform(kg, sd, &data); break; } diff --git a/intern/cycles/kernel/svm/svm_voxel.h b/intern/cycles/kernel/svm/svm_voxel.h index f54f4e8e888..a8b3604a8a7 100644 --- a/intern/cycles/kernel/svm/svm_voxel.h +++ b/intern/cycles/kernel/svm/svm_voxel.h @@ -43,7 +43,7 @@ ccl_device void svm_node_tex_voxel(KernelGlobals *kg, co = transform_point(&tfm, co); } float4 r; -# if defined(__KERNEL_GPU__) +# if defined(__KERNEL_CUDA__) # if __CUDA_ARCH__ >= 300 CUtexObject tex = kernel_tex_fetch(__bindless_mapping, id); if(id < 2048) /* TODO(dingto): Make this a variable */ @@ -55,9 +55,11 @@ ccl_device void svm_node_tex_voxel(KernelGlobals *kg, # else /* __CUDA_ARCH__ >= 300 */ r = volume_image_texture_3d(id, co.x, co.y, co.z); # endif -# else /* __KERNEL_GPU__ */ +# elif defined(__KERNEL_OPENCL__) + r = kernel_tex_image_interp_3d(kg, id, co.x, co.y, co.z); +# else r = kernel_tex_image_interp_3d(id, co.x, co.y, co.z); -# endif +# endif /* __KERNEL_CUDA__ */ #else float4 r = make_float4(0.0f, 0.0f, 0.0f, 0.0f); #endif diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp index 1e170d3a96e..cb20e811708 100644 --- a/intern/cycles/render/buffers.cpp +++ b/intern/cycles/render/buffers.cpp @@ -135,15 +135,7 @@ void RenderBuffers::reset(Device *device, BufferParams& params_) /* allocate rng state */ rng_state.resize(params.width, params.height); - uint *init_state = rng_state.resize(params.width, params.height); - int x, y, width = params.width, height = params.height; - - for(y = 0; y < height; y++) - for(x = 0; x < width; x++) - init_state[y*width + x] = hash_int_2d(params.full_x+x, params.full_y+y); - device->mem_alloc(rng_state, MEM_READ_WRITE); - device->mem_copy_to(rng_state); } bool RenderBuffers::copy_from_device() diff --git a/intern/cycles/render/camera.cpp b/intern/cycles/render/camera.cpp index a6df656d220..c8c51ec96d2 100644 --- a/intern/cycles/render/camera.cpp +++ b/intern/cycles/render/camera.cpp @@ -32,8 +32,9 @@ CCL_NAMESPACE_BEGIN static float shutter_curve_eval(float x, array<float>& shutter_curve) { - if (shutter_curve.size() == 0) + if(shutter_curve.size() == 0) { return 1.0f; + } x *= shutter_curve.size(); int index = (int)x; diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp index 131ec824be3..f6c83fb5c7e 100644 --- a/intern/cycles/render/graph.cpp +++ b/intern/cycles/render/graph.cpp @@ -148,8 +148,9 @@ void ShaderNode::attributes(Shader *shader, AttributeRequestSet *attributes) bool ShaderNode::equals(const ShaderNode& other) { - if (type != other.type || bump != other.bump) + if(type != other.type || bump != other.bump) { return false; + } assert(inputs.size() == other.inputs.size()); @@ -597,13 +598,13 @@ void ShaderGraph::deduplicate_nodes() /* Try to merge this node with another one. */ ShaderNode *merge_with = NULL; foreach(ShaderNode *other_node, candidates[node->type->name]) { - if (node != other_node && node->equals(*other_node)) { + if(node != other_node && node->equals(*other_node)) { merge_with = other_node; break; } } /* If found an equivalent, merge; otherwise keep node for later merges */ - if (merge_with != NULL) { + if(merge_with != NULL) { for(int i = 0; i < node->outputs.size(); ++i) { relink(node, node->outputs[i], merge_with->outputs[i]); } diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp index 7e24664b3fe..073a0aa2ac9 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/render/image.cpp @@ -43,7 +43,7 @@ ImageManager::ImageManager(const DeviceInfo& info) * be screwed on so many levels.. */ DeviceType device_type = info.type; - if (device_type == DEVICE_MULTI) { + if(device_type == DEVICE_MULTI) { device_type = info.multi_devices[0].type; } @@ -1107,7 +1107,7 @@ void ImageManager::device_pack_images(Device *device, int info_size = tex_num_images[IMAGE_DATA_TYPE_FLOAT4] + tex_num_images[IMAGE_DATA_TYPE_BYTE4] + tex_num_images[IMAGE_DATA_TYPE_FLOAT] + tex_num_images[IMAGE_DATA_TYPE_BYTE]; - uint4 *info = dscene->tex_image_packed_info.resize(info_size); + uint4 *info = dscene->tex_image_packed_info.resize(info_size*2); /* Byte4 Textures*/ type = IMAGE_DATA_TYPE_BYTE4; @@ -1130,7 +1130,9 @@ void ImageManager::device_pack_images(Device *device, uint8_t options = pack_image_options(type, slot); - info[type_index_to_flattened_slot(slot, type)] = make_uint4(tex_img.data_width, tex_img.data_height, offset, options); + int index = type_index_to_flattened_slot(slot, type) * 2; + info[index] = make_uint4(tex_img.data_width, tex_img.data_height, offset, options); + info[index+1] = make_uint4(tex_img.data_depth, 0, 0, 0); memcpy(pixels_byte4+offset, (void*)tex_img.data_pointer, tex_img.memory_size()); offset += tex_img.size(); @@ -1159,7 +1161,10 @@ void ImageManager::device_pack_images(Device *device, /* todo: support 3D textures, only CPU for now */ uint8_t options = pack_image_options(type, slot); - info[type_index_to_flattened_slot(slot, type)] = make_uint4(tex_img.data_width, tex_img.data_height, offset, options); + + int index = type_index_to_flattened_slot(slot, type) * 2; + info[index] = make_uint4(tex_img.data_width, tex_img.data_height, offset, options); + info[index+1] = make_uint4(tex_img.data_depth, 0, 0, 0); memcpy(pixels_float4+offset, (void*)tex_img.data_pointer, tex_img.memory_size()); offset += tex_img.size(); @@ -1187,7 +1192,9 @@ void ImageManager::device_pack_images(Device *device, uint8_t options = pack_image_options(type, slot); - info[type_index_to_flattened_slot(slot, type)] = make_uint4(tex_img.data_width, tex_img.data_height, offset, options); + int index = type_index_to_flattened_slot(slot, type) * 2; + info[index] = make_uint4(tex_img.data_width, tex_img.data_height, offset, options); + info[index+1] = make_uint4(tex_img.data_depth, 0, 0, 0); memcpy(pixels_byte+offset, (void*)tex_img.data_pointer, tex_img.memory_size()); offset += tex_img.size(); @@ -1216,7 +1223,10 @@ void ImageManager::device_pack_images(Device *device, /* todo: support 3D textures, only CPU for now */ uint8_t options = pack_image_options(type, slot); - info[type_index_to_flattened_slot(slot, type)] = make_uint4(tex_img.data_width, tex_img.data_height, offset, options); + + int index = type_index_to_flattened_slot(slot, type) * 2; + info[index] = make_uint4(tex_img.data_width, tex_img.data_height, offset, options); + info[index+1] = make_uint4(tex_img.data_depth, 0, 0, 0); memcpy(pixels_float+offset, (void*)tex_img.data_pointer, tex_img.memory_size()); offset += tex_img.size(); diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp index 63914e57319..a9a33d2e789 100644 --- a/intern/cycles/render/integrator.cpp +++ b/intern/cycles/render/integrator.cpp @@ -65,6 +65,7 @@ NODE_DEFINE(Integrator) SOCKET_BOOLEAN(sample_all_lights_direct, "Sample All Lights Direct", true); SOCKET_BOOLEAN(sample_all_lights_indirect, "Sample All Lights Indirect", true); + SOCKET_FLOAT(light_sampling_threshold, "Light Sampling Threshold", 0.05f); static NodeEnum method_enum; method_enum.insert("path", PATH); @@ -164,6 +165,13 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene kintegrator->sampling_pattern = sampling_pattern; kintegrator->aa_samples = aa_samples; + if(light_sampling_threshold > 0.0f) { + kintegrator->light_inv_rr_threshold = 1.0f / light_sampling_threshold; + } + else { + kintegrator->light_inv_rr_threshold = 0.0f; + } + /* sobol directions table */ int max_samples = 1; diff --git a/intern/cycles/render/integrator.h b/intern/cycles/render/integrator.h index 39eaaf246d4..17fdd0ef1db 100644 --- a/intern/cycles/render/integrator.h +++ b/intern/cycles/render/integrator.h @@ -64,8 +64,10 @@ public: int mesh_light_samples; int subsurface_samples; int volume_samples; + bool sample_all_lights_direct; bool sample_all_lights_indirect; + float light_sampling_threshold; enum Method { BRANCHED_PATH = 0, diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index 93f6d7902f0..777f3229ce6 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -126,6 +126,8 @@ NODE_DEFINE(Light) SOCKET_FLOAT(spot_angle, "Spot Angle", M_PI_4_F); SOCKET_FLOAT(spot_smooth, "Spot Smooth", 0.0f); + SOCKET_TRANSFORM(tfm, "Transform", transform_identity()); + SOCKET_BOOLEAN(cast_shadow, "Cast Shadow", true); SOCKET_BOOLEAN(use_mis, "Use Mis", false); SOCKET_BOOLEAN(use_diffuse, "Use Diffuse", true); @@ -762,6 +764,11 @@ void LightManager::device_update_points(Device *device, light_data[light_index*LIGHT_SIZE + 4] = make_float4(max_bounces, 0.0f, 0.0f, 0.0f); + Transform tfm = light->tfm; + Transform itfm = transform_inverse(tfm); + memcpy(&light_data[light_index*LIGHT_SIZE + 5], &tfm, sizeof(float4)*3); + memcpy(&light_data[light_index*LIGHT_SIZE + 8], &itfm, sizeof(float4)*3); + light_index++; } @@ -788,6 +795,11 @@ void LightManager::device_update_points(Device *device, light_data[light_index*LIGHT_SIZE + 3] = make_float4(-1, dir.x, dir.y, dir.z); light_data[light_index*LIGHT_SIZE + 4] = make_float4(-1, 0.0f, 0.0f, 0.0f); + Transform tfm = light->tfm; + Transform itfm = transform_inverse(tfm); + memcpy(&light_data[light_index*LIGHT_SIZE + 5], &tfm, sizeof(float4)*3); + memcpy(&light_data[light_index*LIGHT_SIZE + 8], &itfm, sizeof(float4)*3); + light_index++; } diff --git a/intern/cycles/render/light.h b/intern/cycles/render/light.h index 040a672937d..f56530b6490 100644 --- a/intern/cycles/render/light.h +++ b/intern/cycles/render/light.h @@ -50,6 +50,8 @@ public: float3 axisv; float sizev; + Transform tfm; + int map_resolution; float spot_angle; diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index 039bb49d82f..ac369a0d5f8 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -1462,7 +1462,7 @@ void MeshManager::device_update_mesh(Device *device, else { PackedBVH& pack = bvh->pack; for(size_t i = 0; i < pack.prim_index.size(); ++i) { - if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { + if((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { tri_prim_index[pack.prim_index[i]] = pack.prim_tri_index[i]; } } diff --git a/intern/cycles/render/mesh_subdivision.cpp b/intern/cycles/render/mesh_subdivision.cpp index 813b23ed91e..913c3c74b42 100644 --- a/intern/cycles/render/mesh_subdivision.cpp +++ b/intern/cycles/render/mesh_subdivision.cpp @@ -92,7 +92,7 @@ namespace Far { if(vert_edges.size() == 2) { float sharpness = refiner.getLevel(0).getEdgeSharpness(vert_edges[0]); - sharpness = std::min(sharpness, refiner.getLevel(0).getEdgeSharpness(vert_edges[1])); + sharpness = min(sharpness, refiner.getLevel(0).getEdgeSharpness(vert_edges[1])); setBaseVertexSharpness(refiner, i, sharpness); } diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 7ea52b28b9c..f293af3c40a 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -1257,6 +1257,7 @@ NODE_DEFINE(BrickTextureNode) SOCKET_IN_COLOR(mortar, "Mortar", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_IN_FLOAT(scale, "Scale", 5.0f); SOCKET_IN_FLOAT(mortar_size, "Mortar Size", 0.02f); + SOCKET_IN_FLOAT(mortar_smooth, "Mortar Smooth", 0.0f); SOCKET_IN_FLOAT(bias, "Bias", 0.0f); SOCKET_IN_FLOAT(brick_width, "Brick Width", 0.5f); SOCKET_IN_FLOAT(row_height, "Row Height", 0.25f); @@ -1280,6 +1281,7 @@ void BrickTextureNode::compile(SVMCompiler& compiler) ShaderInput *mortar_in = input("Mortar"); ShaderInput *scale_in = input("Scale"); ShaderInput *mortar_size_in = input("Mortar Size"); + ShaderInput *mortar_smooth_in = input("Mortar Smooth"); ShaderInput *bias_in = input("Bias"); ShaderInput *brick_width_in = input("Brick Width"); ShaderInput *row_height_in = input("Row Height"); @@ -1303,7 +1305,8 @@ void BrickTextureNode::compile(SVMCompiler& compiler) compiler.encode_uchar4( compiler.stack_assign_if_linked(row_height_in), compiler.stack_assign_if_linked(color_out), - compiler.stack_assign_if_linked(fac_out))); + compiler.stack_assign_if_linked(fac_out), + compiler.stack_assign_if_linked(mortar_smooth_in))); compiler.add_node(compiler.encode_uchar4(offset_frequency, squash_frequency), __float_as_int(scale), @@ -1315,6 +1318,11 @@ void BrickTextureNode::compile(SVMCompiler& compiler) __float_as_int(offset), __float_as_int(squash)); + compiler.add_node(__float_as_int(mortar_smooth), + SVM_STACK_INVALID, + SVM_STACK_INVALID, + SVM_STACK_INVALID); + tex_mapping.compile_end(compiler, vector_in, vector_offset); } @@ -2378,8 +2386,9 @@ void EmissionNode::constant_fold(const ConstantFolder& folder) ShaderInput *color_in = input("Color"); ShaderInput *strength_in = input("Strength"); - if ((!color_in->link && color == make_float3(0.0f, 0.0f, 0.0f)) || - (!strength_in->link && strength == 0.0f)) { + if((!color_in->link && color == make_float3(0.0f, 0.0f, 0.0f)) || + (!strength_in->link && strength == 0.0f)) + { folder.discard(); } } @@ -2430,8 +2439,9 @@ void BackgroundNode::constant_fold(const ConstantFolder& folder) ShaderInput *color_in = input("Color"); ShaderInput *strength_in = input("Strength"); - if ((!color_in->link && color == make_float3(0.0f, 0.0f, 0.0f)) || - (!strength_in->link && strength == 0.0f)) { + if((!color_in->link && color == make_float3(0.0f, 0.0f, 0.0f)) || + (!strength_in->link && strength == 0.0f)) + { folder.discard(); } } @@ -4864,8 +4874,9 @@ void CurvesNode::constant_fold(const ConstantFolder& folder, ShaderInput *value_ /* evaluate fully constant node */ if(folder.all_inputs_constant()) { - if (curves.size() == 0) + if(curves.size() == 0) { return; + } float3 pos = (value - make_float3(min_x, min_x, min_x)) / (max_x - min_x); float3 result; @@ -5140,7 +5151,7 @@ OSLNode* OSLNode::create(size_t num_inputs, const OSLNode *from) char *node_memory = (char*) operator new(node_size + inputs_size); memset(node_memory, 0, node_size + inputs_size); - if (!from) { + if(!from) { return new(node_memory) OSLNode(); } else { diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 13791c668ed..eb0f7977dd1 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -243,7 +243,7 @@ public: int offset_frequency, squash_frequency; float3 color1, color2, mortar; - float scale, mortar_size, bias, brick_width, row_height; + float scale, mortar_size, mortar_smooth, bias, brick_width, row_height; float3 vector; virtual int get_group() { return NODE_GROUP_LEVEL_2; } diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index d8f3ce58505..8b8b988b969 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -253,7 +253,7 @@ vector<float> Object::motion_times() bool Object::is_traceable() { /* Mesh itself can be empty,can skip all such objects. */ - if (!bounds.valid() || bounds.size() == make_float3(0.0f, 0.0f, 0.0f)) { + if(!bounds.valid() || bounds.size() == make_float3(0.0f, 0.0f, 0.0f)) { return false; } /* TODO(sergey): Check for mesh vertices/curves. visibility flags. */ @@ -624,8 +624,9 @@ void ObjectManager::device_update_flags(Device *device, void ObjectManager::device_update_patch_map_offsets(Device *device, DeviceScene *dscene, Scene *scene) { - if (scene->objects.size() == 0) + if(scene->objects.size() == 0) { return; + } uint4* objects = (uint4*)dscene->objects.get_data(); diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index 18a32f7f328..67b68e63cb2 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -825,7 +825,7 @@ void OSLCompiler::parameter(ShaderNode* node, const char *name) // OSL does not support booleans, so convert to int const array<bool>& value = node->get_bool_array(socket); array<int> intvalue(value.size()); - for (size_t i = 0; i < value.size(); i++) + for(size_t i = 0; i < value.size(); i++) intvalue[i] = value[i]; ss->Parameter(uname, array_typedesc(TypeDesc::TypeInt, value.size()), intvalue.data()); break; @@ -861,8 +861,7 @@ void OSLCompiler::parameter(ShaderNode* node, const char *name) // convert to tightly packed array since float3 has padding const array<float3>& value = node->get_float3_array(socket); array<float> fvalue(value.size() * 3); - for (size_t i = 0, j = 0; i < value.size(); i++) - { + for(size_t i = 0, j = 0; i < value.size(); i++) { fvalue[j++] = value[i].x; fvalue[j++] = value[i].y; fvalue[j++] = value[i].z; diff --git a/intern/cycles/subd/subd_patch_table.cpp b/intern/cycles/subd/subd_patch_table.cpp index 62572efa88a..d437b045c07 100644 --- a/intern/cycles/subd/subd_patch_table.cpp +++ b/intern/cycles/subd/subd_patch_table.cpp @@ -46,7 +46,7 @@ struct PatchMapQuadNode { /* sets all the children to point to the patch of index */ void set_child(int index) { - for (int i = 0; i < 4; i++) { + for(int i = 0; i < 4; i++) { children[i] = index | PATCH_MAP_NODE_IS_SET | PATCH_MAP_NODE_IS_LEAF; } } diff --git a/intern/cycles/test/render_graph_finalize_test.cpp b/intern/cycles/test/render_graph_finalize_test.cpp index 6f1c0b88b51..32b4c7265ee 100644 --- a/intern/cycles/test/render_graph_finalize_test.cpp +++ b/intern/cycles/test/render_graph_finalize_test.cpp @@ -1407,8 +1407,9 @@ void init_test_curve(array<T> &buffer, T start, T end, int steps) { buffer.resize(steps); - for (int i = 0; i < steps; i++) + for(int i = 0; i < steps; i++) { buffer[i] = lerp(start, end, float(i)/(steps-1)); + } } /* diff --git a/intern/cycles/util/util_guarded_allocator.cpp b/intern/cycles/util/util_guarded_allocator.cpp index 8de6e254cbf..615ac95f324 100644 --- a/intern/cycles/util/util_guarded_allocator.cpp +++ b/intern/cycles/util/util_guarded_allocator.cpp @@ -19,7 +19,7 @@ CCL_NAMESPACE_BEGIN -static Stats global_stats; +static Stats global_stats(Stats::static_init); /* Internal API. */ diff --git a/intern/cycles/util/util_hash.h b/intern/cycles/util/util_hash.h index 3ff2802b46d..98c3a681ff2 100644 --- a/intern/cycles/util/util_hash.h +++ b/intern/cycles/util/util_hash.h @@ -21,7 +21,7 @@ CCL_NAMESPACE_BEGIN -static inline uint hash_int_2d(uint kx, uint ky) +ccl_device_inline uint hash_int_2d(uint kx, uint ky) { #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) @@ -44,11 +44,12 @@ static inline uint hash_int_2d(uint kx, uint ky) #undef rot } -static inline uint hash_int(uint k) +ccl_device_inline uint hash_int(uint k) { return hash_int_2d(k, 0); } +#ifndef __KERNEL_GPU__ static inline uint hash_string(const char *str) { uint i = 0, c; @@ -58,6 +59,7 @@ static inline uint hash_string(const char *str) return i; } +#endif CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h index b9594f7ec69..3f4d3e06c0b 100644 --- a/intern/cycles/util/util_math.h +++ b/intern/cycles/util/util_math.h @@ -162,6 +162,11 @@ ccl_device_inline float max4(float a, float b, float c, float d) return max(max(a, b), max(c, d)); } +ccl_device_inline float max3(float3 a) +{ + return max(max(a.x, a.y), a.z); +} + #ifndef __KERNEL_OPENCL__ ccl_device_inline int clamp(int a, int mn, int mx) @@ -453,8 +458,9 @@ ccl_device_inline float3 operator*(const float3& a, const float f) ccl_device_inline float3 operator*(const float f, const float3& a) { -#ifdef __KERNEL_SSE__ - return float3(_mm_mul_ps(a.m128, _mm_set1_ps(f))); + /* TODO(sergey): Currently disabled, gives speedup but causes precision issues. */ +#if defined(__KERNEL_SSE__) && 0 + return float3(_mm_mul_ps(_mm_set1_ps(f), a.m128)); #else return make_float3(a.x*f, a.y*f, a.z*f); #endif @@ -462,13 +468,13 @@ ccl_device_inline float3 operator*(const float f, const float3& a) ccl_device_inline float3 operator/(const float f, const float3& a) { - /* TODO(sergey): Currently disabled, gives speedup but makes intersection tets non-watertight. */ -// #ifdef __KERNEL_SSE__ -// __m128 rc = _mm_rcp_ps(a.m128); -// return float3(_mm_mul_ps(_mm_set1_ps(f),rc)); -// #else + /* TODO(sergey): Currently disabled, gives speedup but causes precision issues. */ +#if defined(__KERNEL_SSE__) && 0 + __m128 rc = _mm_rcp_ps(a.m128); + return float3(_mm_mul_ps(_mm_set1_ps(f),rc)); +#else return make_float3(f / a.x, f / a.y, f / a.z); -// #endif +#endif } ccl_device_inline float3 operator/(const float3& a, const float f) @@ -479,7 +485,8 @@ ccl_device_inline float3 operator/(const float3& a, const float f) ccl_device_inline float3 operator/(const float3& a, const float3& b) { -#ifdef __KERNEL_SSE__ + /* TODO(sergey): Currently disabled, gives speedup but causes precision issues. */ +#if defined(__KERNEL_SSE__) && 0 __m128 rc = _mm_rcp_ps(b.m128); return float3(_mm_mul_ps(a, rc)); #else @@ -589,7 +596,8 @@ ccl_device_inline float len_squared(const float4& a) ccl_device_inline float3 normalize(const float3& a) { -#if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) + /* TODO(sergey): Disabled for now, causes crashes in certain cases. */ +#if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) && 0 __m128 norm = _mm_sqrt_ps(_mm_dp_ps(a.m128, a.m128, 0x7F)); return _mm_div_ps(a.m128, norm); #else @@ -790,7 +798,8 @@ ccl_device_inline float4 operator-(const float4& a) ccl_device_inline float4 operator*(const float4& a, const float4& b) { -#ifdef __KERNEL_SSE__ + /* TODO(sergey): Disabled for now, causes crashes in certain cases. */ +#if defined(__KERNEL_SSE__) && 0 return _mm_mul_ps(a.m128, b.m128); #else return make_float4(a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w); @@ -799,7 +808,7 @@ ccl_device_inline float4 operator*(const float4& a, const float4& b) ccl_device_inline float4 operator*(const float4& a, float f) { -#ifdef __KERNEL_SSE__ +#if defined(__KERNEL_SSE__) return a * make_float4(f); #else return make_float4(a.x*f, a.y*f, a.z*f, a.w*f); @@ -838,7 +847,8 @@ ccl_device_inline float4 operator/(const float4& a, const float4& b) ccl_device_inline float4 operator+(const float4& a, const float4& b) { -#ifdef __KERNEL_SSE__ + /* TODO(sergey): Disabled for now, causes crashes in certain cases. */ +#if defined(__KERNEL_SSE__) && 0 return _mm_add_ps(a.m128, b.m128); #else return make_float4(a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w); @@ -1574,7 +1584,7 @@ ccl_device_inline bool ray_triangle_intersect_uv( 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 *isect_P, float *isect_t, float *isect_u, float *isect_v) { float t = -(dot(ray_P, quad_n) - dot(quad_P, quad_n)) / dot(ray_D, quad_n); if(t < ray_mint || t > ray_maxt) @@ -1582,13 +1592,19 @@ ccl_device bool ray_quad_intersect(float3 ray_P, float3 ray_D, float ray_mint, f 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) + + float u = dot(inplane, quad_u) / dot(quad_u, quad_u) + 0.5f; + if(u < 0.0f || u > 1.0f) return false; - if(fabsf(dot(inplane, quad_v) / dot(quad_v, quad_v)) > 0.5f) + + float v = dot(inplane, quad_v) / dot(quad_v, quad_v) + 0.5f; + if(v < 0.0f || v > 1.0f) return false; if(isect_P) *isect_P = hit; if(isect_t) *isect_t = t; + if(isect_u) *isect_u = u; + if(isect_v) *isect_v = v; return true; } @@ -1629,6 +1645,14 @@ ccl_device_inline float2 map_to_sphere(const float3 co) ccl_device_inline int util_max_axis(float3 vec) { +#ifdef __KERNEL_SSE__ + __m128 a = shuffle<0,0,1,1>(vec.m128); + __m128 b = shuffle<1,2,2,1>(vec.m128); + __m128 c = _mm_cmpgt_ps(a, b); + int mask = _mm_movemask_ps(c) & 0x7; + static const char tab[8] = {2, 2, 2, 0, 1, 2, 1, 0}; + return tab[mask]; +#else if(vec.x > vec.y) { if(vec.x > vec.z) return 0; @@ -1641,6 +1665,7 @@ ccl_device_inline int util_max_axis(float3 vec) else return 2; } +#endif } CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_stats.h b/intern/cycles/util/util_stats.h index ce27067dc5e..b970b017270 100644 --- a/intern/cycles/util/util_stats.h +++ b/intern/cycles/util/util_stats.h @@ -23,7 +23,10 @@ CCL_NAMESPACE_BEGIN class Stats { public: + enum static_init_t { static_init = 0 }; + Stats() : mem_used(0), mem_peak(0) {} + explicit Stats(static_init_t) {} void mem_alloc(size_t size) { atomic_add_z(&mem_used, size); diff --git a/intern/cycles/util/util_transform.h b/intern/cycles/util/util_transform.h index bfc8f55feed..ea5eb3b25b0 100644 --- a/intern/cycles/util/util_transform.h +++ b/intern/cycles/util/util_transform.h @@ -73,22 +73,60 @@ ccl_device_inline float3 transform_perspective(const Transform *t, const float3 ccl_device_inline float3 transform_point(const Transform *t, const float3 a) { + /* TODO(sergey): Disabled for now, causes crashes in certain cases. */ +#if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE2__) && 0 + ssef x, y, z, w, aa; + aa = a.m128; + + x = _mm_loadu_ps(&t->x.x); + y = _mm_loadu_ps(&t->y.x); + z = _mm_loadu_ps(&t->z.x); + w = _mm_loadu_ps(&t->w.x); + + _MM_TRANSPOSE4_PS(x, y, z, w); + + ssef tmp = shuffle<0>(aa) * x; + tmp = madd(shuffle<1>(aa), y, tmp); + tmp = madd(shuffle<2>(aa), z, tmp); + tmp += w; + + return float3(tmp.m128); +#else float3 c = make_float3( a.x*t->x.x + a.y*t->x.y + a.z*t->x.z + t->x.w, a.x*t->y.x + a.y*t->y.y + a.z*t->y.z + t->y.w, a.x*t->z.x + a.y*t->z.y + a.z*t->z.z + t->z.w); return c; +#endif } ccl_device_inline float3 transform_direction(const Transform *t, const float3 a) { + /* TODO(sergey): Disabled for now, causes crashes in certain cases. */ +#if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE2__) && 0 + ssef x, y, z, w, aa; + aa = a.m128; + x = _mm_loadu_ps(&t->x.x); + y = _mm_loadu_ps(&t->y.x); + z = _mm_loadu_ps(&t->z.x); + w = _mm_setzero_ps(); + + _MM_TRANSPOSE4_PS(x, y, z, w); + + ssef tmp = shuffle<0>(aa) * x; + tmp = madd(shuffle<1>(aa), y, tmp); + tmp = madd(shuffle<2>(aa), z, tmp); + + return float3(tmp.m128); +#else float3 c = make_float3( a.x*t->x.x + a.y*t->x.y + a.z*t->x.z, a.x*t->y.x + a.y*t->y.y + a.z*t->y.z, a.x*t->z.x + a.y*t->z.y + a.z*t->z.z); return c; +#endif } ccl_device_inline float3 transform_direction_transposed(const Transform *t, const float3 a) @@ -225,7 +225,8 @@ msbuild ^ /property:Configuration=%BUILD_TYPE% ^ /maxcpucount ^ /verbosity:minimal ^ - /p:platform=%MSBUILD_PLATFORM% + /p:platform=%MSBUILD_PLATFORM% ^ + /flp:Summary;Verbosity=minimal;LogFile=%BUILD_DIR%\Build.log if %ERRORLEVEL% NEQ 0 ( echo "Build Failed" diff --git a/release/scripts/modules/bpy_extras/object_utils.py b/release/scripts/modules/bpy_extras/object_utils.py index 87bb84b5844..88cd7398fe0 100644 --- a/release/scripts/modules/bpy_extras/object_utils.py +++ b/release/scripts/modules/bpy_extras/object_utils.py @@ -137,12 +137,14 @@ def object_data_add(context, obdata, operator=None, use_active_layer=True, name= if context.space_data and context.space_data.type == 'VIEW_3D': v3d = context.space_data + if v3d and v3d.local_view: + base.layers_from_view(context.space_data) + if operator is not None and any(operator.layers): base.layers = operator.layers else: if use_active_layer: if v3d and v3d.local_view: - base.layers_from_view(context.space_data) base.layers[scene.active_layer] = True else: if v3d and not v3d.lock_camera_and_layers: diff --git a/release/scripts/modules/sys_info.py b/release/scripts/modules/sys_info.py index 30b9cdfaf37..5b81df28524 100644 --- a/release/scripts/modules/sys_info.py +++ b/release/scripts/modules/sys_info.py @@ -39,177 +39,188 @@ def write_sysinfo(filepath): r = r[1:-1] return r - output = open(filepath, 'w', encoding="utf-8") - - header = "= Blender %s System Information =\n" % bpy.app.version_string - lilies = "%s\n\n" % ((len(header) - 1) * "=") - output.write(lilies[:-1]) - output.write(header) - output.write(lilies) - - def title(text): - return "\n%s:\n%s" % (text, lilies) - - # build info - output.write(title("Blender")) - output.write( - "version: %s, branch: %s, commit date: %s %s, hash: %s, type: %s\n" % - (bpy.app.version_string, - prepr(bpy.app.build_branch), - prepr(bpy.app.build_commit_date), - prepr(bpy.app.build_commit_time), - prepr(bpy.app.build_hash), - prepr(bpy.app.build_type), - )) - - output.write("build date: %s, %s\n" % (prepr(bpy.app.build_date), prepr(bpy.app.build_time))) - output.write("platform: %s\n" % prepr(bpy.app.build_platform)) - output.write("binary path: %s\n" % prepr(bpy.app.binary_path)) - output.write("build cflags: %s\n" % prepr(bpy.app.build_cflags)) - output.write("build cxxflags: %s\n" % prepr(bpy.app.build_cxxflags)) - output.write("build linkflags: %s\n" % prepr(bpy.app.build_linkflags)) - output.write("build system: %s\n" % prepr(bpy.app.build_system)) - - # python info - output.write(title("Python")) - output.write("version: %s\n" % (sys.version)) - output.write("paths:\n") - for p in sys.path: - output.write("\t%r\n" % p) - - output.write(title("Python (External Binary)")) - output.write("binary path: %s\n" % prepr(bpy.app.binary_path_python)) - try: - py_ver = prepr(subprocess.check_output([ - bpy.app.binary_path_python, - "--version", - ]).strip()) - except Exception as e: - py_ver = str(e) - output.write("version: %s\n" % py_ver) - del py_ver - - output.write(title("Directories")) - output.write("scripts:\n") - for p in bpy.utils.script_paths(): - output.write("\t%r\n" % p) - output.write("user scripts: %r\n" % (bpy.utils.script_path_user())) - output.write("pref scripts: %r\n" % (bpy.utils.script_path_pref())) - output.write("datafiles: %r\n" % (bpy.utils.user_resource('DATAFILES'))) - output.write("config: %r\n" % (bpy.utils.user_resource('CONFIG'))) - output.write("scripts : %r\n" % (bpy.utils.user_resource('SCRIPTS'))) - output.write("autosave: %r\n" % (bpy.utils.user_resource('AUTOSAVE'))) - output.write("tempdir: %r\n" % (bpy.app.tempdir)) - - output.write(title("FFmpeg")) - ffmpeg = bpy.app.ffmpeg - if ffmpeg.supported: - for lib in ("avcodec", "avdevice", "avformat", "avutil", "swscale"): + with open(filepath, 'w', encoding="utf-8") as output: + try: + header = "= Blender %s System Information =\n" % bpy.app.version_string + lilies = "%s\n\n" % ((len(header) - 1) * "=") + output.write(lilies[:-1]) + output.write(header) + output.write(lilies) + + def title(text): + return "\n%s:\n%s" % (text, lilies) + + # build info + output.write(title("Blender")) output.write( - "%s:%s%r\n" % (lib, " " * (10 - len(lib)), - getattr(ffmpeg, lib + "_version_string"))) - else: - output.write("Blender was built without FFmpeg support\n") - - if bpy.app.build_options.sdl: - output.write(title("SDL")) - output.write("Version: %s\n" % bpy.app.sdl.version_string) - output.write("Loading method: ") - if bpy.app.build_options.sdl_dynload: - output.write("dynamically loaded by Blender (WITH_SDL_DYNLOAD=ON)\n") - else: - output.write("linked (WITH_SDL_DYNLOAD=OFF)\n") - if not bpy.app.sdl.available: - output.write("WARNING: Blender could not load SDL library\n") - - output.write(title("Other Libraries")) - ocio = bpy.app.ocio - output.write("OpenColorIO: ") - if ocio.supported: - if ocio.version_string == "fallback": - output.write("Blender was built with OpenColorIO, " + - "but it currently uses fallback color management.\n") - else: - output.write("%s\n" % (ocio.version_string)) - else: - output.write("Blender was built without OpenColorIO support\n") - - oiio = bpy.app.oiio - output.write("OpenImageIO: ") - if ocio.supported: - output.write("%s\n" % (oiio.version_string)) - else: - output.write("Blender was built without OpenImageIO support\n") - - output.write("OpenShadingLanguage: ") - if bpy.app.build_options.cycles: - if bpy.app.build_options.cycles_osl: - from _cycles import osl_version_string - output.write("%s\n" % (osl_version_string)) - else: - output.write("Blender was built without OpenShadingLanguage support in Cycles\n") - else: - output.write("Blender was built without Cycles support\n") - - openvdb = bpy.app.openvdb - output.write("OpenVDB: ") - if openvdb.supported: - output.write("%s\n" % openvdb.version_string) - else: - output.write("Blender was built without OpenVDB support\n") - - alembic = bpy.app.alembic - output.write("Alembic: ") - if alembic.supported: - output.write("%s\n" % alembic.version_string) - else: - output.write("Blender was built without Alembic support\n") - - if not bpy.app.build_options.sdl: - output.write("SDL: Blender was built without SDL support\n") - - if bpy.app.background: - output.write("\nOpenGL: missing, background mode\n") - else: - output.write(title("OpenGL")) - version = bgl.glGetString(bgl.GL_RENDERER) - output.write("renderer:\t%r\n" % version) - output.write("vendor:\t\t%r\n" % (bgl.glGetString(bgl.GL_VENDOR))) - output.write("version:\t%r\n" % (bgl.glGetString(bgl.GL_VERSION))) - output.write("extensions:\n") - - glext = sorted(bgl.glGetString(bgl.GL_EXTENSIONS).split()) - for l in glext: - output.write("\t%s\n" % l) - - output.write(title("Implementation Dependent OpenGL Limits")) - limit = bgl.Buffer(bgl.GL_INT, 1) - bgl.glGetIntegerv(bgl.GL_MAX_TEXTURE_UNITS, limit) - output.write("Maximum Fixed Function Texture Units:\t%d\n" % limit[0]) - bgl.glGetIntegerv(bgl.GL_MAX_ELEMENTS_VERTICES, limit) - output.write("Maximum DrawElements Vertices:\t%d\n" % limit[0]) - bgl.glGetIntegerv(bgl.GL_MAX_ELEMENTS_INDICES, limit) - output.write("Maximum DrawElements Indices:\t%d\n" % limit[0]) - - output.write("\nGLSL:\n") - bgl.glGetIntegerv(bgl.GL_MAX_VARYING_FLOATS, limit) - output.write("Maximum Varying Floats:\t%d\n" % limit[0]) - bgl.glGetIntegerv(bgl.GL_MAX_VERTEX_ATTRIBS, limit) - output.write("Maximum Vertex Attributes:\t%d\n" % limit[0]) - bgl.glGetIntegerv(bgl.GL_MAX_VERTEX_UNIFORM_COMPONENTS, limit) - output.write("Maximum Vertex Uniform Components:\t%d\n" % limit[0]) - bgl.glGetIntegerv(bgl.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, limit) - output.write("Maximum Fragment Uniform Components:\t%d\n" % limit[0]) - bgl.glGetIntegerv(bgl.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, limit) - output.write("Maximum Vertex Image Units:\t%d\n" % limit[0]) - bgl.glGetIntegerv(bgl.GL_MAX_TEXTURE_IMAGE_UNITS, limit) - output.write("Maximum Fragment Image Units:\t%d\n" % limit[0]) - bgl.glGetIntegerv(bgl.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, limit) - output.write("Maximum Pipeline Image Units:\t%d\n" % limit[0]) - - if bpy.app.build_options.cycles: - import cycles - output.write(title("Cycles")) - output.write(cycles.engine.system_info()) - - output.close() + "version: %s, branch: %s, commit date: %s %s, hash: %s, type: %s\n" % + (bpy.app.version_string, + prepr(bpy.app.build_branch), + prepr(bpy.app.build_commit_date), + prepr(bpy.app.build_commit_time), + prepr(bpy.app.build_hash), + prepr(bpy.app.build_type), + )) + + output.write("build date: %s, %s\n" % (prepr(bpy.app.build_date), prepr(bpy.app.build_time))) + output.write("platform: %s\n" % prepr(bpy.app.build_platform)) + output.write("binary path: %s\n" % prepr(bpy.app.binary_path)) + output.write("build cflags: %s\n" % prepr(bpy.app.build_cflags)) + output.write("build cxxflags: %s\n" % prepr(bpy.app.build_cxxflags)) + output.write("build linkflags: %s\n" % prepr(bpy.app.build_linkflags)) + output.write("build system: %s\n" % prepr(bpy.app.build_system)) + + # python info + output.write(title("Python")) + output.write("version: %s\n" % (sys.version)) + output.write("paths:\n") + for p in sys.path: + output.write("\t%r\n" % p) + + output.write(title("Python (External Binary)")) + output.write("binary path: %s\n" % prepr(bpy.app.binary_path_python)) + try: + py_ver = prepr(subprocess.check_output([ + bpy.app.binary_path_python, + "--version", + ]).strip()) + except Exception as e: + py_ver = str(e) + output.write("version: %s\n" % py_ver) + del py_ver + + output.write(title("Directories")) + output.write("scripts:\n") + for p in bpy.utils.script_paths(): + output.write("\t%r\n" % p) + output.write("user scripts: %r\n" % (bpy.utils.script_path_user())) + output.write("pref scripts: %r\n" % (bpy.utils.script_path_pref())) + output.write("datafiles: %r\n" % (bpy.utils.user_resource('DATAFILES'))) + output.write("config: %r\n" % (bpy.utils.user_resource('CONFIG'))) + output.write("scripts : %r\n" % (bpy.utils.user_resource('SCRIPTS'))) + output.write("autosave: %r\n" % (bpy.utils.user_resource('AUTOSAVE'))) + output.write("tempdir: %r\n" % (bpy.app.tempdir)) + + output.write(title("FFmpeg")) + ffmpeg = bpy.app.ffmpeg + if ffmpeg.supported: + for lib in ("avcodec", "avdevice", "avformat", "avutil", "swscale"): + output.write( + "%s:%s%r\n" % (lib, " " * (10 - len(lib)), + getattr(ffmpeg, lib + "_version_string"))) + else: + output.write("Blender was built without FFmpeg support\n") + + if bpy.app.build_options.sdl: + output.write(title("SDL")) + output.write("Version: %s\n" % bpy.app.sdl.version_string) + output.write("Loading method: ") + if bpy.app.build_options.sdl_dynload: + output.write("dynamically loaded by Blender (WITH_SDL_DYNLOAD=ON)\n") + else: + output.write("linked (WITH_SDL_DYNLOAD=OFF)\n") + if not bpy.app.sdl.available: + output.write("WARNING: Blender could not load SDL library\n") + + output.write(title("Other Libraries")) + ocio = bpy.app.ocio + output.write("OpenColorIO: ") + if ocio.supported: + if ocio.version_string == "fallback": + output.write("Blender was built with OpenColorIO, " + + "but it currently uses fallback color management.\n") + else: + output.write("%s\n" % (ocio.version_string)) + else: + output.write("Blender was built without OpenColorIO support\n") + + oiio = bpy.app.oiio + output.write("OpenImageIO: ") + if ocio.supported: + output.write("%s\n" % (oiio.version_string)) + else: + output.write("Blender was built without OpenImageIO support\n") + + output.write("OpenShadingLanguage: ") + if bpy.app.build_options.cycles: + if bpy.app.build_options.cycles_osl: + from _cycles import osl_version_string + output.write("%s\n" % (osl_version_string)) + else: + output.write("Blender was built without OpenShadingLanguage support in Cycles\n") + else: + output.write("Blender was built without Cycles support\n") + + openvdb = bpy.app.openvdb + output.write("OpenVDB: ") + if openvdb.supported: + output.write("%s\n" % openvdb.version_string) + else: + output.write("Blender was built without OpenVDB support\n") + + alembic = bpy.app.alembic + output.write("Alembic: ") + if alembic.supported: + output.write("%s\n" % alembic.version_string) + else: + output.write("Blender was built without Alembic support\n") + + if not bpy.app.build_options.sdl: + output.write("SDL: Blender was built without SDL support\n") + + if bpy.app.background: + output.write("\nOpenGL: missing, background mode\n") + else: + output.write(title("OpenGL")) + version = bgl.glGetString(bgl.GL_RENDERER) + output.write("renderer:\t%r\n" % version) + output.write("vendor:\t\t%r\n" % (bgl.glGetString(bgl.GL_VENDOR))) + output.write("version:\t%r\n" % (bgl.glGetString(bgl.GL_VERSION))) + output.write("extensions:\n") + + glext = sorted(bgl.glGetString(bgl.GL_EXTENSIONS).split()) + for l in glext: + output.write("\t%s\n" % l) + + output.write(title("Implementation Dependent OpenGL Limits")) + limit = bgl.Buffer(bgl.GL_INT, 1) + bgl.glGetIntegerv(bgl.GL_MAX_TEXTURE_UNITS, limit) + output.write("Maximum Fixed Function Texture Units:\t%d\n" % limit[0]) + bgl.glGetIntegerv(bgl.GL_MAX_ELEMENTS_VERTICES, limit) + output.write("Maximum DrawElements Vertices:\t%d\n" % limit[0]) + bgl.glGetIntegerv(bgl.GL_MAX_ELEMENTS_INDICES, limit) + output.write("Maximum DrawElements Indices:\t%d\n" % limit[0]) + + output.write("\nGLSL:\n") + bgl.glGetIntegerv(bgl.GL_MAX_VARYING_FLOATS, limit) + output.write("Maximum Varying Floats:\t%d\n" % limit[0]) + bgl.glGetIntegerv(bgl.GL_MAX_VERTEX_ATTRIBS, limit) + output.write("Maximum Vertex Attributes:\t%d\n" % limit[0]) + bgl.glGetIntegerv(bgl.GL_MAX_VERTEX_UNIFORM_COMPONENTS, limit) + output.write("Maximum Vertex Uniform Components:\t%d\n" % limit[0]) + bgl.glGetIntegerv(bgl.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, limit) + output.write("Maximum Fragment Uniform Components:\t%d\n" % limit[0]) + bgl.glGetIntegerv(bgl.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, limit) + output.write("Maximum Vertex Image Units:\t%d\n" % limit[0]) + bgl.glGetIntegerv(bgl.GL_MAX_TEXTURE_IMAGE_UNITS, limit) + output.write("Maximum Fragment Image Units:\t%d\n" % limit[0]) + bgl.glGetIntegerv(bgl.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, limit) + output.write("Maximum Pipeline Image Units:\t%d\n" % limit[0]) + + if bpy.app.build_options.cycles: + import cycles + output.write(title("Cycles")) + output.write(cycles.engine.system_info()) + + import addon_utils + addon_utils.modules() + output.write(title("Enabled add-ons")) + for addon in bpy.context.user_preferences.addons.keys(): + addon_mod = addon_utils.addons_fake_modules.get(addon, None) + if addon_mod is None: + output.write("%s (MISSING)\n" % (addon)) + else: + output.write("%s (version: %s, path: %s)\n" % + (addon, addon_mod.bl_info.get('version', "UNKNOWN"), addon_mod.__file__)) + except Exception as e: + output.write("ERROR: %s\n" % e) diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index 5416735494b..59907692fe0 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -38,6 +38,7 @@ class MESH_MT_vertex_group_specials(Menu): layout.operator("object.vertex_group_mirror", text="Mirror Vertex Group (Topology)", icon='ARROW_LEFTRIGHT').use_topology = True layout.operator("object.vertex_group_remove_from", icon='X', text="Remove from All Groups").use_all_groups = True layout.operator("object.vertex_group_remove_from", icon='X', text="Clear Active Group").use_all_verts = True + layout.operator("object.vertex_group_remove", icon='X', text="Delete All Unlocked Groups").all_unlocked = True layout.operator("object.vertex_group_remove", icon='X', text="Delete All Groups").all = True layout.separator() layout.operator("object.vertex_group_lock", icon='LOCKED', text="Lock All").action = 'LOCK' diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index 84ee06c7d70..540c29f31e4 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -336,6 +336,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col = split.column(align=True) col.label(text="Direction:") col.prop(md, "direction", text="") + if md.direction in {'X', 'Y', 'Z', 'RGB_TO_XYZ'}: + col.label(text="Space:") + col.prop(md, "space", text="") col.label(text="Vertex Group:") col.prop_search(md, "vertex_group", ob, "vertex_groups", text="") diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 84442f9b2d9..bc40932018d 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -52,6 +52,12 @@ def gpencil_stroke_placement_settings(context, layout): row.active = getattr(ts, propname) in {'SURFACE', 'STROKE'} row.prop(ts, "use_gpencil_stroke_endpoints") + if context.scene.tool_settings.gpencil_stroke_placement_view3d == 'CURSOR': + row = col.row(align=True) + row.label("Lock axis:") + row = col.row(align=True) + row.prop(ts.gpencil_sculpt, "lockaxis", expand=True) + def gpencil_active_brush_settings_simple(context, layout): brush = context.active_gpencil_brush diff --git a/release/scripts/startup/bl_ui/properties_physics_smoke.py b/release/scripts/startup/bl_ui/properties_physics_smoke.py index df03f23a4b5..0374d032141 100644 --- a/release/scripts/startup/bl_ui/properties_physics_smoke.py +++ b/release/scripts/startup/bl_ui/properties_physics_smoke.py @@ -397,6 +397,14 @@ class PHYSICS_PT_smoke_display_settings(PhysicButtonsPanel, Panel): col.prop(domain, "vector_draw_type") col.prop(domain, "vector_scale") + layout.separator() + layout.label(text="Color Mapping:") + layout.prop(domain, "use_color_ramp") + col = layout.column(); + col.enabled = domain.use_color_ramp + col.prop(domain, "coba_field") + col.template_color_ramp(domain, "color_ramp", expand=True) + if __name__ == "__main__": # only for live edit. bpy.utils.register_module(__name__) diff --git a/source/blender/alembic/ABC_alembic.h b/source/blender/alembic/ABC_alembic.h index e62713f57f5..e92d5f2d9f7 100644 --- a/source/blender/alembic/ABC_alembic.h +++ b/source/blender/alembic/ABC_alembic.h @@ -28,6 +28,7 @@ extern "C" { #endif struct bContext; +struct CacheReader; struct DerivedMesh; struct ListBase; struct Object; @@ -92,21 +93,25 @@ AbcArchiveHandle *ABC_create_handle(const char *filename, struct ListBase *objec void ABC_free_handle(AbcArchiveHandle *handle); -void ABC_get_transform(AbcArchiveHandle *handle, - struct Object *ob, - const char *object_path, +void ABC_get_transform(struct CacheReader *reader, float r_mat[4][4], float time, float scale); -struct DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle, +struct DerivedMesh *ABC_read_mesh(struct CacheReader *reader, struct Object *ob, struct DerivedMesh *dm, - const char *object_path, const float time, const char **err_str, int flags); +void CacheReader_free(struct CacheReader *reader); + +struct CacheReader *CacheReader_open_alembic_object(struct AbcArchiveHandle *handle, + struct CacheReader *reader, + struct Object *object, + const char *object_path); + #ifdef __cplusplus } #endif diff --git a/source/blender/alembic/intern/abc_camera.cc b/source/blender/alembic/intern/abc_camera.cc index 5c34ec1391f..d5271e3ca31 100644 --- a/source/blender/alembic/intern/abc_camera.cc +++ b/source/blender/alembic/intern/abc_camera.cc @@ -119,7 +119,7 @@ bool AbcCameraReader::valid() const void AbcCameraReader::readObjectData(Main *bmain, float time) { - Camera *bcam = static_cast<Camera *>(BKE_camera_add(bmain, "abc_camera")); + Camera *bcam = static_cast<Camera *>(BKE_camera_add(bmain, m_data_name.c_str())); ISampleSelector sample_sel(time); CameraSample cam_sample; @@ -155,8 +155,6 @@ void AbcCameraReader::readObjectData(Main *bmain, float time) bcam->gpu_dof.focus_distance = cam_sample.getFocusDistance(); bcam->gpu_dof.fstop = cam_sample.getFStop(); - BLI_strncpy(bcam->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1); - m_object = BKE_object_add_only_object(bmain, OB_CAMERA, m_object_name.c_str()); m_object->data = bcam; } diff --git a/source/blender/alembic/intern/abc_curves.cc b/source/blender/alembic/intern/abc_curves.cc index 2b54741a5c5..7e5ea3b1853 100644 --- a/source/blender/alembic/intern/abc_curves.cc +++ b/source/blender/alembic/intern/abc_curves.cc @@ -37,6 +37,7 @@ extern "C" { #include "BLI_listbase.h" +#include "BKE_cdderivedmesh.h" #include "BKE_curve.h" #include "BKE_object.h" @@ -353,3 +354,54 @@ void read_curve_sample(Curve *cu, const ICurvesSchema &schema, const float time) BLI_addtail(BKE_curve_nurbs_get(cu), nu); } } + +/* NOTE: Alembic only stores data about control points, but the DerivedMesh + * passed from the cache modifier contains the displist, which has more data + * than the control points, so to avoid corrupting the displist we modify the + * object directly and create a new DerivedMesh from that. Also we might need to + * create new or delete existing NURBS in the curve. + */ +DerivedMesh *AbcCurveReader::read_derivedmesh(DerivedMesh */*dm*/, const float time, int /*read_flag*/) +{ + ISampleSelector sample_sel(time); + const ICurvesSchema::Sample sample = m_curves_schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Int32ArraySamplePtr num_vertices = sample.getCurvesNumVertices(); + + int vertex_idx = 0; + int curve_idx = 0; + Curve *curve = static_cast<Curve *>(m_object->data); + + const int curve_count = BLI_listbase_count(&curve->nurb); + + if (curve_count != num_vertices->size()) { + BKE_nurbList_free(&curve->nurb); + read_curve_sample(curve, m_curves_schema, time); + } + else { + Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first); + for (; nurbs; nurbs = nurbs->next, ++curve_idx) { + const int totpoint = (*num_vertices)[curve_idx]; + + if (nurbs->bp) { + BPoint *point = nurbs->bp; + + for (int i = 0; i < totpoint; ++i, ++point, ++vertex_idx) { + const Imath::V3f &pos = (*positions)[vertex_idx]; + copy_yup_zup(point->vec, pos.getValue()); + } + } + else if (nurbs->bezt) { + BezTriple *bezier = nurbs->bezt; + + for (int i = 0; i < totpoint; ++i, ++bezier, ++vertex_idx) { + const Imath::V3f &pos = (*positions)[vertex_idx]; + copy_yup_zup(bezier->vec[1], pos.getValue()); + } + } + } + } + + return CDDM_from_curve(m_object); +} diff --git a/source/blender/alembic/intern/abc_curves.h b/source/blender/alembic/intern/abc_curves.h index ee47f1931ea..979ee8af639 100644 --- a/source/blender/alembic/intern/abc_curves.h +++ b/source/blender/alembic/intern/abc_curves.h @@ -56,10 +56,11 @@ public: bool valid() const; void readObjectData(Main *bmain, float time); + DerivedMesh *read_derivedmesh(DerivedMesh *, const float time, int); }; /* ************************************************************************** */ void read_curve_sample(Curve *cu, const Alembic::AbcGeom::ICurvesSchema &schema, const float time); -#endif /* __ABC_CURVES_H__ */
\ No newline at end of file +#endif /* __ABC_CURVES_H__ */ diff --git a/source/blender/alembic/intern/abc_mesh.cc b/source/blender/alembic/intern/abc_mesh.cc index bb5d5ce3566..5b282e3c5bb 100644 --- a/source/blender/alembic/intern/abc_mesh.cc +++ b/source/blender/alembic/intern/abc_mesh.cc @@ -646,75 +646,6 @@ void AbcMeshWriter::getGeoGroups( /* Some helpers for mesh generation */ namespace utils { -void mesh_add_verts(Mesh *mesh, size_t len) -{ - if (len == 0) { - return; - } - - const int totvert = mesh->totvert + len; - CustomData vdata; - CustomData_copy(&mesh->vdata, &vdata, CD_MASK_MESH, CD_DEFAULT, totvert); - CustomData_copy_data(&mesh->vdata, &vdata, 0, 0, mesh->totvert); - - if (!CustomData_has_layer(&vdata, CD_MVERT)) { - CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert); - } - - CustomData_free(&mesh->vdata, mesh->totvert); - mesh->vdata = vdata; - BKE_mesh_update_customdata_pointers(mesh, false); - - mesh->totvert = totvert; -} - -static void mesh_add_mloops(Mesh *mesh, size_t len) -{ - if (len == 0) { - return; - } - - /* new face count */ - const int totloops = mesh->totloop + len; - - CustomData ldata; - CustomData_copy(&mesh->ldata, &ldata, CD_MASK_MESH, CD_DEFAULT, totloops); - CustomData_copy_data(&mesh->ldata, &ldata, 0, 0, mesh->totloop); - - if (!CustomData_has_layer(&ldata, CD_MLOOP)) { - CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloops); - } - - CustomData_free(&mesh->ldata, mesh->totloop); - mesh->ldata = ldata; - BKE_mesh_update_customdata_pointers(mesh, false); - - mesh->totloop = totloops; -} - -static void mesh_add_mpolygons(Mesh *mesh, size_t len) -{ - if (len == 0) { - return; - } - - const int totpolys = mesh->totpoly + len; - - CustomData pdata; - CustomData_copy(&mesh->pdata, &pdata, CD_MASK_MESH, CD_DEFAULT, totpolys); - CustomData_copy_data(&mesh->pdata, &pdata, 0, 0, mesh->totpoly); - - if (!CustomData_has_layer(&pdata, CD_MPOLY)) { - CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpolys); - } - - CustomData_free(&mesh->pdata, mesh->totpoly); - mesh->pdata = pdata; - BKE_mesh_update_customdata_pointers(mesh, false); - - mesh->totpoly = totpolys; -} - static void build_mat_map(const Main *bmain, std::map<std::string, Material *> &mat_map) { Material *material = static_cast<Material *>(bmain->mat.first); @@ -786,45 +717,6 @@ struct AbcMeshData { UInt32ArraySamplePtr uvs_indices; }; -static void *add_customdata_cb(void *user_data, const char *name, int data_type) -{ - Mesh *mesh = static_cast<Mesh *>(user_data); - CustomDataType cd_data_type = static_cast<CustomDataType>(data_type); - void *cd_ptr = NULL; - - int index = -1; - if (cd_data_type == CD_MLOOPUV) { - index = ED_mesh_uv_texture_add(mesh, name, true); - cd_ptr = CustomData_get_layer(&mesh->ldata, cd_data_type); - } - else if (cd_data_type == CD_MLOOPCOL) { - index = ED_mesh_color_add(mesh, name, true); - cd_ptr = CustomData_get_layer(&mesh->ldata, cd_data_type); - } - - if (index == -1) { - return NULL; - } - - return cd_ptr; -} - -CDStreamConfig create_config(Mesh *mesh) -{ - CDStreamConfig config; - - config.mvert = mesh->mvert; - config.mpoly = mesh->mpoly; - config.mloop = mesh->mloop; - config.totpoly = mesh->totpoly; - config.totloop = mesh->totloop; - config.user_data = mesh; - config.loopdata = &mesh->ldata; - config.add_customdata_cb = add_customdata_cb; - - return config; -} - static void read_mverts_interp(MVert *mverts, const P3fArraySamplePtr &positions, const P3fArraySamplePtr &ceil_positions, const float weight) { float tmp[3]; @@ -1002,23 +894,15 @@ void AbcMeshReader::readObjectData(Main *bmain, float time) m_object->data = mesh; const ISampleSelector sample_sel(time); - const IPolyMeshSchema::Sample sample = m_schema.getValue(sample_sel); - - const P3fArraySamplePtr &positions = sample.getPositions(); - const Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); - const Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); - - utils::mesh_add_verts(mesh, positions->size()); - utils::mesh_add_mpolygons(mesh, face_counts->size()); - utils::mesh_add_mloops(mesh, face_indices->size()); - m_mesh_data = create_config(mesh); + DerivedMesh *dm = CDDM_from_mesh(mesh); + DerivedMesh *ndm = this->read_derivedmesh(dm, time, MOD_MESHSEQ_READ_ALL); - bool has_smooth_normals = false; - read_mesh_sample(m_settings, m_schema, sample_sel, m_mesh_data, has_smooth_normals); + if (ndm != dm) { + dm->release(dm); + } - BKE_mesh_calc_normals(mesh); - BKE_mesh_calc_edges(mesh, false, false); + DM_to_mesh(ndm, mesh, m_object, CD_MASK_MESH, true); if (m_settings->validate_meshes) { BKE_mesh_validate(mesh, false, false); @@ -1031,6 +915,120 @@ void AbcMeshReader::readObjectData(Main *bmain, float time) } } +static bool check_smooth_poly_flag(DerivedMesh *dm) +{ + MPoly *mpolys = dm->getPolyArray(dm); + + for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i) { + MPoly &poly = mpolys[i]; + + if ((poly.flag & ME_SMOOTH) != 0) { + return true; + } + } + + return false; +} + +static void set_smooth_poly_flag(DerivedMesh *dm) +{ + MPoly *mpolys = dm->getPolyArray(dm); + + for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i) { + MPoly &poly = mpolys[i]; + poly.flag |= ME_SMOOTH; + } +} + +static void *add_customdata_cb(void *user_data, const char *name, int data_type) +{ + DerivedMesh *dm = static_cast<DerivedMesh *>(user_data); + CustomDataType cd_data_type = static_cast<CustomDataType>(data_type); + void *cd_ptr = NULL; + + if (ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) { + cd_ptr = CustomData_get_layer_named(dm->getLoopDataLayout(dm), cd_data_type, name); + + if (cd_ptr == NULL) { + cd_ptr = CustomData_add_layer_named(dm->getLoopDataLayout(dm), + cd_data_type, + CD_DEFAULT, + NULL, + dm->getNumLoops(dm), + name); + } + } + + return cd_ptr; +} + +CDStreamConfig get_config(DerivedMesh *dm) +{ + CDStreamConfig config; + + config.user_data = dm; + config.mvert = dm->getVertArray(dm); + config.mloop = dm->getLoopArray(dm); + config.mpoly = dm->getPolyArray(dm); + config.totloop = dm->getNumLoops(dm); + config.totpoly = dm->getNumPolys(dm); + config.loopdata = dm->getLoopDataLayout(dm); + config.add_customdata_cb = add_customdata_cb; + + return config; +} + +DerivedMesh *AbcMeshReader::read_derivedmesh(DerivedMesh *dm, const float time, int read_flag) +{ + ISampleSelector sample_sel(time); + const IPolyMeshSchema::Sample sample = m_schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); + const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); + + DerivedMesh *new_dm = NULL; + + /* Only read point data when streaming meshes, unless we need to create new ones. */ + ImportSettings settings; + settings.read_flag |= read_flag; + + if (dm->getNumVerts(dm) != positions->size()) { + new_dm = CDDM_from_template(dm, + positions->size(), + 0, + 0, + face_indices->size(), + face_counts->size()); + + settings.read_flag |= MOD_MESHSEQ_READ_ALL; + } + + CDStreamConfig config = get_config(new_dm ? new_dm : dm); + config.time = time; + + bool do_normals = false; + read_mesh_sample(&settings, m_schema, sample_sel, config, do_normals); + + if (new_dm) { + /* Check if we had ME_SMOOTH flag set to restore it. */ + if (!do_normals && check_smooth_poly_flag(dm)) { + set_smooth_poly_flag(new_dm); + } + + CDDM_calc_normals(new_dm); + CDDM_calc_edges(new_dm); + + return new_dm; + } + + if (do_normals) { + CDDM_calc_normals(dm); + } + + return dm; +} + void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, size_t poly_start, const ISampleSelector &sample_sel) { @@ -1178,21 +1176,17 @@ void AbcSubDReader::readObjectData(Main *bmain, float time) m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); m_object->data = mesh; - const ISampleSelector sample_sel(time); - const ISubDSchema::Sample sample = m_schema.getValue(sample_sel); - - const P3fArraySamplePtr &positions = sample.getPositions(); - const Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); - const Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); - - utils::mesh_add_verts(mesh, positions->size()); - utils::mesh_add_mpolygons(mesh, face_counts->size()); - utils::mesh_add_mloops(mesh, face_indices->size()); + DerivedMesh *dm = CDDM_from_mesh(mesh); + DerivedMesh *ndm = this->read_derivedmesh(dm, time, MOD_MESHSEQ_READ_ALL); - m_mesh_data = create_config(mesh); + if (ndm != dm) { + dm->release(dm); + } - read_subd_sample(m_settings, m_schema, sample_sel, m_mesh_data); + DM_to_mesh(ndm, mesh, m_object, CD_MASK_MESH, true); + const ISampleSelector sample_sel(time); + const ISubDSchema::Sample sample = m_schema.getValue(sample_sel); Int32ArraySamplePtr indices = sample.getCreaseIndices(); Alembic::Abc::FloatArraySamplePtr sharpnesses = sample.getCreaseSharpnesses(); @@ -1262,3 +1256,48 @@ void read_subd_sample(ImportSettings *settings, /* TODO: face sets */ } + +DerivedMesh *AbcSubDReader::read_derivedmesh(DerivedMesh *dm, const float time, int read_flag) +{ + ISampleSelector sample_sel(time); + const ISubDSchema::Sample sample = m_schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); + const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); + + DerivedMesh *new_dm = NULL; + + ImportSettings settings; + settings.read_flag |= read_flag; + + if (dm->getNumVerts(dm) != positions->size()) { + new_dm = CDDM_from_template(dm, + positions->size(), + 0, + 0, + face_indices->size(), + face_counts->size()); + + settings.read_flag |= MOD_MESHSEQ_READ_ALL; + } + + /* Only read point data when streaming meshes, unless we need to create new ones. */ + CDStreamConfig config = get_config(new_dm ? new_dm : dm); + config.time = time; + read_subd_sample(&settings, m_schema, sample_sel, config); + + if (new_dm) { + /* Check if we had ME_SMOOTH flag set to restore it. */ + if (check_smooth_poly_flag(dm)) { + set_smooth_poly_flag(new_dm); + } + + CDDM_calc_normals(new_dm); + CDDM_calc_edges(new_dm); + + return new_dm; + } + + return dm; +} diff --git a/source/blender/alembic/intern/abc_mesh.h b/source/blender/alembic/intern/abc_mesh.h index 41abe78f75f..66e6585a3d3 100644 --- a/source/blender/alembic/intern/abc_mesh.h +++ b/source/blender/alembic/intern/abc_mesh.h @@ -102,6 +102,8 @@ public: void readObjectData(Main *bmain, float time); + DerivedMesh *read_derivedmesh(DerivedMesh *dm, const float time, int read_flag); + private: void readFaceSetsSample(Main *bmain, Mesh *mesh, size_t poly_start, const Alembic::AbcGeom::ISampleSelector &sample_sel); @@ -126,6 +128,7 @@ public: bool valid() const; void readObjectData(Main *bmain, float time); + DerivedMesh *read_derivedmesh(DerivedMesh *dm, const float time, int read_flag); }; void read_subd_sample(ImportSettings *settings, @@ -135,16 +138,10 @@ void read_subd_sample(ImportSettings *settings, /* ************************************************************************** */ -namespace utils { - -void mesh_add_verts(struct Mesh *mesh, size_t len); - -} - void read_mverts(MVert *mverts, const Alembic::AbcGeom::P3fArraySamplePtr &positions, const Alembic::AbcGeom::N3fArraySamplePtr &normals); -CDStreamConfig create_config(Mesh *mesh); +CDStreamConfig get_config(DerivedMesh *dm); #endif /* __ABC_MESH_H__ */ diff --git a/source/blender/alembic/intern/abc_object.cc b/source/blender/alembic/intern/abc_object.cc index 32468fdaded..314b2568bed 100644 --- a/source/blender/alembic/intern/abc_object.cc +++ b/source/blender/alembic/intern/abc_object.cc @@ -126,6 +126,7 @@ AbcObjectReader::AbcObjectReader(const IObject &object, ImportSettings &settings , m_settings(&settings) , m_min_time(std::numeric_limits<chrono_t>::max()) , m_max_time(std::numeric_limits<chrono_t>::min()) + , m_refcount(0) { m_name = object.getFullName(); std::vector<std::string> parts; @@ -153,6 +154,11 @@ Object *AbcObjectReader::object() const return m_object; } +void AbcObjectReader::object(Object *ob) +{ + m_object = ob; +} + static Imath::M44d blend_matrices(const Imath::M44d &m0, const Imath::M44d &m1, const float weight) { float mat0[4][4], mat1[4][4], ret[4][4]; @@ -210,6 +216,28 @@ Imath::M44d get_matrix(const IXformSchema &schema, const float time) void AbcObjectReader::readObjectMatrix(const float time) { + bool is_constant = false; + + this->read_matrix(m_object->obmat, time, m_settings->scale, is_constant); + invert_m4_m4(m_object->imat, m_object->obmat); + + BKE_object_apply_mat4(m_object, m_object->obmat, false, false); + + if (!is_constant) { + bConstraint *con = BKE_constraint_add_for_object(m_object, NULL, CONSTRAINT_TYPE_TRANSFORM_CACHE); + bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data); + BLI_strncpy(data->object_path, m_iobject.getFullName().c_str(), FILE_MAX); + + data->cache_file = m_settings->cache_file; + id_us_plus(&data->cache_file->id); + + data->reader = reinterpret_cast<CacheReader *>(this); + this->incref(); + } +} + +void AbcObjectReader::read_matrix(float mat[4][4], const float time, const float scale, bool &is_constant) +{ IXform ixform; bool has_alembic_parent = false; @@ -250,23 +278,12 @@ void AbcObjectReader::readObjectMatrix(const float time) } const Imath::M44d matrix = get_matrix(schema, time); - convert_matrix(matrix, m_object, m_object->obmat, m_settings->scale, has_alembic_parent); - - invert_m4_m4(m_object->imat, m_object->obmat); - - BKE_object_apply_mat4(m_object, m_object->obmat, false, false); - - if (!schema.isConstant()) { - bConstraint *con = BKE_constraint_add_for_object(m_object, NULL, CONSTRAINT_TYPE_TRANSFORM_CACHE); - bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data); - BLI_strncpy(data->object_path, m_iobject.getFullName().c_str(), FILE_MAX); + convert_matrix(matrix, m_object, mat, scale, has_alembic_parent); - data->cache_file = m_settings->cache_file; - id_us_plus(&data->cache_file->id); - } + is_constant = schema.isConstant(); } -void AbcObjectReader::addCacheModifier() const +void AbcObjectReader::addCacheModifier() { ModifierData *md = modifier_new(eModifierType_MeshSequenceCache); BLI_addtail(&m_object->modifiers, md); @@ -277,6 +294,9 @@ void AbcObjectReader::addCacheModifier() const id_us_plus(&mcmd->cache_file->id); BLI_strncpy(mcmd->object_path, m_iobject.getFullName().c_str(), FILE_MAX); + + mcmd->reader = reinterpret_cast<CacheReader *>(this); + this->incref(); } chrono_t AbcObjectReader::minTime() const @@ -288,3 +308,18 @@ chrono_t AbcObjectReader::maxTime() const { return m_max_time; } + +int AbcObjectReader::refcount() const +{ + return m_refcount; +} + +void AbcObjectReader::incref() +{ + ++m_refcount; +} + +void AbcObjectReader::decref() +{ + --m_refcount; +} diff --git a/source/blender/alembic/intern/abc_object.h b/source/blender/alembic/intern/abc_object.h index a35faa37565..7ff927b4d33 100644 --- a/source/blender/alembic/intern/abc_object.h +++ b/source/blender/alembic/intern/abc_object.h @@ -130,6 +130,8 @@ static bool has_animations(Schema &schema, ImportSettings *settings) /* ************************************************************************** */ +struct DerivedMesh; + using Alembic::AbcCoreAbstract::chrono_t; class AbcObjectReader { @@ -145,6 +147,10 @@ protected: chrono_t m_min_time; chrono_t m_max_time; + /* Use reference counting since the same reader may be used by multiple + * modifiers and/or constraints. */ + int m_refcount; + public: explicit AbcObjectReader(const Alembic::Abc::IObject &object, ImportSettings &settings); @@ -153,17 +159,31 @@ public: const Alembic::Abc::IObject &iobject() const; Object *object() const; + void object(Object *ob); virtual bool valid() const = 0; virtual void readObjectData(Main *bmain, float time) = 0; + virtual DerivedMesh *read_derivedmesh(DerivedMesh *dm, const float time, int read_flag) + { + (void)time; + (void)read_flag; + return dm; + } + void readObjectMatrix(const float time); - void addCacheModifier() const; + void addCacheModifier(); chrono_t minTime() const; chrono_t maxTime() const; + + int refcount() const; + void incref(); + void decref(); + + void read_matrix(float mat[4][4], const float time, const float scale, bool &is_constant); }; Imath::M44d get_matrix(const Alembic::AbcGeom::IXformSchema &schema, const float time); diff --git a/source/blender/alembic/intern/abc_points.cc b/source/blender/alembic/intern/abc_points.cc index 03014547416..c16da621c77 100644 --- a/source/blender/alembic/intern/abc_points.cc +++ b/source/blender/alembic/intern/abc_points.cc @@ -32,6 +32,7 @@ extern "C" { #include "DNA_mesh_types.h" #include "DNA_object_types.h" +#include "BKE_cdderivedmesh.h" #include "BKE_lattice.h" #include "BKE_mesh.h" #include "BKE_object.h" @@ -154,14 +155,14 @@ void AbcPointsReader::readObjectData(Main *bmain, float time) { Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str()); - const ISampleSelector sample_sel(time); - m_sample = m_schema.getValue(sample_sel); + DerivedMesh *dm = CDDM_from_mesh(mesh); + DerivedMesh *ndm = this->read_derivedmesh(dm, time, 0); - const P3fArraySamplePtr &positions = m_sample.getPositions(); - utils::mesh_add_verts(mesh, positions->size()); + if (ndm != dm) { + dm->release(dm); + } - CDStreamConfig config = create_config(mesh); - read_points_sample(m_schema, sample_sel, config, time); + DM_to_mesh(ndm, mesh, m_object, CD_MASK_MESH, true); if (m_settings->validate_meshes) { BKE_mesh_validate(mesh, false, false); @@ -197,3 +198,22 @@ void read_points_sample(const IPointsSchema &schema, read_mverts(config.mvert, positions, vnormals); } + +DerivedMesh *AbcPointsReader::read_derivedmesh(DerivedMesh *dm, const float time, int /*read_flag*/) +{ + ISampleSelector sample_sel(time); + const IPointsSchema::Sample sample = m_schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = sample.getPositions(); + + DerivedMesh *new_dm = NULL; + + if (dm->getNumVerts(dm) != positions->size()) { + new_dm = CDDM_new(positions->size(), 0, 0, 0, 0); + } + + CDStreamConfig config = get_config(new_dm ? new_dm : dm); + read_points_sample(m_schema, sample_sel, config, time); + + return new_dm ? new_dm : dm; +} diff --git a/source/blender/alembic/intern/abc_points.h b/source/blender/alembic/intern/abc_points.h index 51f3103bd8b..54873eed346 100644 --- a/source/blender/alembic/intern/abc_points.h +++ b/source/blender/alembic/intern/abc_points.h @@ -60,6 +60,8 @@ public: bool valid() const; void readObjectData(Main *bmain, float time); + + DerivedMesh *read_derivedmesh(DerivedMesh *dm, const float time, int read_flag); }; void read_points_sample(const Alembic::AbcGeom::IPointsSchema &schema, diff --git a/source/blender/alembic/intern/abc_util.cc b/source/blender/alembic/intern/abc_util.cc index 60c66bca1c8..f87d18605d4 100644 --- a/source/blender/alembic/intern/abc_util.cc +++ b/source/blender/alembic/intern/abc_util.cc @@ -22,6 +22,15 @@ #include "abc_util.h" +#include "abc_camera.h" +#include "abc_curves.h" +#include "abc_mesh.h" +#include "abc_nurbs.h" +#include "abc_points.h" +#include "abc_transform.h" + +#include <Alembic/AbcMaterial/IMaterial.h> + #include <algorithm> extern "C" { @@ -462,3 +471,56 @@ float get_weight_and_index(float time, return bias; } + +//#define USE_NURBS + +AbcObjectReader *create_reader(const Alembic::AbcGeom::IObject &object, ImportSettings &settings) +{ + AbcObjectReader *reader = NULL; + + const Alembic::AbcGeom::MetaData &md = object.getMetaData(); + + if (Alembic::AbcGeom::IXform::matches(md)) { + reader = new AbcEmptyReader(object, settings); + } + else if (Alembic::AbcGeom::IPolyMesh::matches(md)) { + reader = new AbcMeshReader(object, settings); + } + else if (Alembic::AbcGeom::ISubD::matches(md)) { + reader = new AbcSubDReader(object, settings); + } + else if (Alembic::AbcGeom::INuPatch::matches(md)) { +#ifdef USE_NURBS + /* TODO(kevin): importing cyclic NURBS from other software crashes + * at the moment. This is due to the fact that NURBS in other + * software have duplicated points which causes buffer overflows in + * Blender. Need to figure out exactly how these points are + * duplicated, in all cases (cyclic U, cyclic V, and cyclic UV). + * Until this is fixed, disabling NURBS reading. */ + reader = new AbcNurbsReader(child, settings); +#endif + } + else if (Alembic::AbcGeom::ICamera::matches(md)) { + reader = new AbcCameraReader(object, settings); + } + else if (Alembic::AbcGeom::IPoints::matches(md)) { + reader = new AbcPointsReader(object, settings); + } + else if (Alembic::AbcMaterial::IMaterial::matches(md)) { + /* Pass for now. */ + } + else if (Alembic::AbcGeom::ILight::matches(md)) { + /* Pass for now. */ + } + else if (Alembic::AbcGeom::IFaceSet::matches(md)) { + /* Pass, those are handled in the mesh reader. */ + } + else if (Alembic::AbcGeom::ICurves::matches(md)) { + reader = new AbcCurveReader(object, settings); + } + else { + assert(false); + } + + return reader; +} diff --git a/source/blender/alembic/intern/abc_util.h b/source/blender/alembic/intern/abc_util.h index 9e9f0c397ba..2f423a9f8c5 100644 --- a/source/blender/alembic/intern/abc_util.h +++ b/source/blender/alembic/intern/abc_util.h @@ -32,8 +32,13 @@ # define ABC_INLINE static inline #endif +struct CacheReader { + int unused; +}; + using Alembic::Abc::chrono_t; +class AbcObjectReader; class ImportSettings; struct ID; @@ -100,6 +105,8 @@ float get_weight_and_index(float time, Alembic::AbcGeom::index_t &i0, Alembic::AbcGeom::index_t &i1); +AbcObjectReader *create_reader(const Alembic::AbcGeom::IObject &object, ImportSettings &settings); + /* ************************** */ /* TODO(kevin): for now keeping these transformations hardcoded to make sure diff --git a/source/blender/alembic/intern/alembic_capi.cc b/source/blender/alembic/intern/alembic_capi.cc index c6988351db8..e690a255505 100644 --- a/source/blender/alembic/intern/alembic_capi.cc +++ b/source/blender/alembic/intern/alembic_capi.cc @@ -467,6 +467,7 @@ static void visit_object(const IObject &object, if (reader) { readers.push_back(reader); + reader->incref(); AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>( MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath")); @@ -710,7 +711,12 @@ static void import_endjob(void *user_data) } for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { - delete *iter; + AbcObjectReader *reader = *iter; + reader->decref(); + + if (reader->refcount() == 0) { + delete reader; + } } if (data->parent_map) { @@ -771,296 +777,31 @@ void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence WM_jobs_start(CTX_wm_manager(C), wm_job); } -/* ******************************* */ +/* ************************************************************************** */ -void ABC_get_transform(AbcArchiveHandle *handle, Object *ob, const char *object_path, float r_mat[4][4], float time, float scale) +void ABC_get_transform(CacheReader *reader, float r_mat[4][4], float time, float scale) { - ArchiveReader *archive = archive_from_handle(handle); - - if (!archive || !archive->valid()) { + if (!reader) { return; } - IObject tmp; - find_iobject(archive->getTop(), tmp, object_path); - - IXform ixform; - - if (IXform::matches(tmp.getHeader())) { - ixform = IXform(tmp, kWrapExisting); - } - else { - ixform = IXform(tmp.getParent(), kWrapExisting); - } - - IXformSchema schema = ixform.getSchema(); - - if (!schema.valid()) { - return; - } - - const Imath::M44d matrix = get_matrix(schema, time); - convert_matrix(matrix, ob, r_mat, scale); -} - -/* ***************************************** */ - -static bool check_smooth_poly_flag(DerivedMesh *dm) -{ - MPoly *mpolys = dm->getPolyArray(dm); - - for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i) { - MPoly &poly = mpolys[i]; - - if ((poly.flag & ME_SMOOTH) != 0) { - return true; - } - } - - return false; -} - -static void set_smooth_poly_flag(DerivedMesh *dm) -{ - MPoly *mpolys = dm->getPolyArray(dm); - - for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i) { - MPoly &poly = mpolys[i]; - poly.flag |= ME_SMOOTH; - } -} - -static void *add_customdata_cb(void *user_data, const char *name, int data_type) -{ - DerivedMesh *dm = static_cast<DerivedMesh *>(user_data); - CustomDataType cd_data_type = static_cast<CustomDataType>(data_type); - void *cd_ptr = NULL; - - if (ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) { - cd_ptr = CustomData_get_layer_named(dm->getLoopDataLayout(dm), cd_data_type, name); - - if (cd_ptr == NULL) { - cd_ptr = CustomData_add_layer_named(dm->getLoopDataLayout(dm), - cd_data_type, - CD_DEFAULT, - NULL, - dm->getNumLoops(dm), - name); - } - } - - return cd_ptr; -} - -ABC_INLINE CDStreamConfig get_config(DerivedMesh *dm) -{ - CDStreamConfig config; - - config.user_data = dm; - config.mvert = dm->getVertArray(dm); - config.mloop = dm->getLoopArray(dm); - config.mpoly = dm->getPolyArray(dm); - config.totloop = dm->getNumLoops(dm); - config.totpoly = dm->getNumPolys(dm); - config.loopdata = dm->getLoopDataLayout(dm); - config.add_customdata_cb = add_customdata_cb; - - return config; -} - -static DerivedMesh *read_mesh_sample(DerivedMesh *dm, const IObject &iobject, const float time, int read_flag) -{ - IPolyMesh mesh(iobject, kWrapExisting); - IPolyMeshSchema schema = mesh.getSchema(); - ISampleSelector sample_sel(time); - const IPolyMeshSchema::Sample sample = schema.getValue(sample_sel); - - const P3fArraySamplePtr &positions = sample.getPositions(); - const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); - const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); - - DerivedMesh *new_dm = NULL; - - /* Only read point data when streaming meshes, unless we need to create new ones. */ - ImportSettings settings; - settings.read_flag |= read_flag; - - if (dm->getNumVerts(dm) != positions->size()) { - new_dm = CDDM_from_template(dm, - positions->size(), - 0, - 0, - face_indices->size(), - face_counts->size()); - - settings.read_flag |= MOD_MESHSEQ_READ_ALL; - } - - CDStreamConfig config = get_config(new_dm ? new_dm : dm); - config.time = time; - - bool do_normals = false; - read_mesh_sample(&settings, schema, sample_sel, config, do_normals); - - if (new_dm) { - /* Check if we had ME_SMOOTH flag set to restore it. */ - if (!do_normals && check_smooth_poly_flag(dm)) { - set_smooth_poly_flag(new_dm); - } - - CDDM_calc_normals(new_dm); - CDDM_calc_edges(new_dm); - - return new_dm; - } - - if (do_normals) { - CDDM_calc_normals(dm); - } + AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader); - return dm; + bool is_constant = false; + abc_reader->read_matrix(r_mat, time, scale, is_constant); } -using Alembic::AbcGeom::ISubDSchema; - -static DerivedMesh *read_subd_sample(DerivedMesh *dm, const IObject &iobject, const float time, int read_flag) -{ - ISubD mesh(iobject, kWrapExisting); - ISubDSchema schema = mesh.getSchema(); - ISampleSelector sample_sel(time); - const ISubDSchema::Sample sample = schema.getValue(sample_sel); - - const P3fArraySamplePtr &positions = sample.getPositions(); - const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); - const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); - - DerivedMesh *new_dm = NULL; - - ImportSettings settings; - settings.read_flag |= read_flag; - - if (dm->getNumVerts(dm) != positions->size()) { - new_dm = CDDM_from_template(dm, - positions->size(), - 0, - 0, - face_indices->size(), - face_counts->size()); - - settings.read_flag |= MOD_MESHSEQ_READ_ALL; - } - - /* Only read point data when streaming meshes, unless we need to create new ones. */ - CDStreamConfig config = get_config(new_dm ? new_dm : dm); - config.time = time; - read_subd_sample(&settings, schema, sample_sel, config); +/* ************************************************************************** */ - if (new_dm) { - /* Check if we had ME_SMOOTH flag set to restore it. */ - if (check_smooth_poly_flag(dm)) { - set_smooth_poly_flag(new_dm); - } - - CDDM_calc_normals(new_dm); - CDDM_calc_edges(new_dm); - - return new_dm; - } - - return dm; -} - -static DerivedMesh *read_points_sample(DerivedMesh *dm, const IObject &iobject, const float time) -{ - IPoints points(iobject, kWrapExisting); - IPointsSchema schema = points.getSchema(); - ISampleSelector sample_sel(time); - const IPointsSchema::Sample sample = schema.getValue(sample_sel); - - const P3fArraySamplePtr &positions = sample.getPositions(); - - DerivedMesh *new_dm = NULL; - - if (dm->getNumVerts(dm) != positions->size()) { - new_dm = CDDM_new(positions->size(), 0, 0, 0, 0); - } - - CDStreamConfig config = get_config(new_dm ? new_dm : dm); - read_points_sample(schema, sample_sel, config, time); - - return new_dm ? new_dm : dm; -} - -/* NOTE: Alembic only stores data about control points, but the DerivedMesh - * passed from the cache modifier contains the displist, which has more data - * than the control points, so to avoid corrupting the displist we modify the - * object directly and create a new DerivedMesh from that. Also we might need to - * create new or delete existing NURBS in the curve. - */ -static DerivedMesh *read_curves_sample(Object *ob, const IObject &iobject, const float time) -{ - ICurves points(iobject, kWrapExisting); - ICurvesSchema schema = points.getSchema(); - ISampleSelector sample_sel(time); - const ICurvesSchema::Sample sample = schema.getValue(sample_sel); - - const P3fArraySamplePtr &positions = sample.getPositions(); - const Int32ArraySamplePtr num_vertices = sample.getCurvesNumVertices(); - - int vertex_idx = 0; - int curve_idx = 0; - Curve *curve = static_cast<Curve *>(ob->data); - - const int curve_count = BLI_listbase_count(&curve->nurb); - - if (curve_count != num_vertices->size()) { - BKE_nurbList_free(&curve->nurb); - read_curve_sample(curve, schema, time); - } - else { - Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first); - for (; nurbs; nurbs = nurbs->next, ++curve_idx) { - const int totpoint = (*num_vertices)[curve_idx]; - - if (nurbs->bp) { - BPoint *point = nurbs->bp; - - for (int i = 0; i < totpoint; ++i, ++point, ++vertex_idx) { - const Imath::V3f &pos = (*positions)[vertex_idx]; - copy_yup_zup(point->vec, pos.getValue()); - } - } - else if (nurbs->bezt) { - BezTriple *bezier = nurbs->bezt; - - for (int i = 0; i < totpoint; ++i, ++bezier, ++vertex_idx) { - const Imath::V3f &pos = (*positions)[vertex_idx]; - copy_yup_zup(bezier->vec[1], pos.getValue()); - } - } - } - } - - return CDDM_from_curve(ob); -} - -DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle, +DerivedMesh *ABC_read_mesh(CacheReader *reader, Object *ob, DerivedMesh *dm, - const char *object_path, const float time, const char **err_str, int read_flag) { - ArchiveReader *archive = archive_from_handle(handle); - - if (!archive || !archive->valid()) { - *err_str = "Invalid archive!"; - return NULL; - } - - IObject iobject; - find_iobject(archive->getTop(), iobject, object_path); + AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader); + IObject iobject = abc_reader->iobject(); if (!iobject.valid()) { *err_str = "Invalid object: verify object path"; @@ -1075,7 +816,7 @@ DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle, return NULL; } - return read_mesh_sample(dm, iobject, time, read_flag); + return abc_reader->read_derivedmesh(dm, time, read_flag); } else if (ISubD::matches(header)) { if (ob->type != OB_MESH) { @@ -1083,7 +824,7 @@ DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle, return NULL; } - return read_subd_sample(dm, iobject, time, read_flag); + return abc_reader->read_derivedmesh(dm, time, read_flag); } else if (IPoints::matches(header)) { if (ob->type != OB_MESH) { @@ -1091,7 +832,7 @@ DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle, return NULL; } - return read_points_sample(dm, iobject, time); + return abc_reader->read_derivedmesh(dm, time, read_flag); } else if (ICurves::matches(header)) { if (ob->type != OB_CURVE) { @@ -1099,9 +840,48 @@ DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle, return NULL; } - return read_curves_sample(ob, iobject, time); + return abc_reader->read_derivedmesh(dm, time, read_flag); } *err_str = "Unsupported object type: verify object path"; // or poke developer return NULL; } + +/* ************************************************************************** */ + +void CacheReader_free(CacheReader *reader) +{ + AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader); + abc_reader->decref(); + + if (abc_reader->refcount() == 0) { + delete abc_reader; + } +} + +CacheReader *CacheReader_open_alembic_object(AbcArchiveHandle *handle, CacheReader *reader, Object *object, const char *object_path) +{ + if (object_path[0] == '\0') { + return reader; + } + + ArchiveReader *archive = archive_from_handle(handle); + + if (!archive || !archive->valid()) { + return reader; + } + + IObject iobject; + find_iobject(archive->getTop(), iobject, object_path); + + if (reader) { + CacheReader_free(reader); + } + + ImportSettings settings; + AbcObjectReader *abc_reader = create_reader(iobject, settings); + abc_reader->object(object); + abc_reader->incref(); + + return reinterpret_cast<CacheReader *>(abc_reader); +} diff --git a/source/blender/blenkernel/BKE_cachefile.h b/source/blender/blenkernel/BKE_cachefile.h index a55cb51766c..7e1c069df9a 100644 --- a/source/blender/blenkernel/BKE_cachefile.h +++ b/source/blender/blenkernel/BKE_cachefile.h @@ -63,6 +63,8 @@ bool BKE_cachefile_filepath_get( float BKE_cachefile_time_offset(struct CacheFile *cache_file, const float time, const float fps); +void BKE_cachefile_clean(struct Scene *scene, struct CacheFile *cache_file); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_icons.h b/source/blender/blenkernel/BKE_icons.h index ff063a5c762..c81dfbd3cad 100644 --- a/source/blender/blenkernel/BKE_icons.h +++ b/source/blender/blenkernel/BKE_icons.h @@ -131,6 +131,7 @@ struct PreviewImage *BKE_previewimg_cached_thumbnail_read( const char *name, const char *path, const int source, bool force_update); void BKE_previewimg_cached_release(const char *name); +void BKE_previewimg_cached_release_pointer(struct PreviewImage *prv); #define ICON_RENDER_DEFAULT_HEIGHT 32 diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h index 0d82de09165..79373e343a6 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -39,6 +39,7 @@ extern "C" { #include "BLI_compiler_attrs.h" struct BlendThumbnail; +struct GHash; struct ListBase; struct ID; struct ImBuf; @@ -125,7 +126,8 @@ void BKE_id_ui_prefix(char name[66 + 1], const struct ID *id); void BKE_library_free(struct Library *lib); void BKE_library_make_local( - struct Main *bmain, const struct Library *lib, const bool untagged_only, const bool set_fake); + struct Main *bmain, const struct Library *lib, struct GHash *old_to_new_ids, + const bool untagged_only, const bool set_fake); /* use when "" is given to new_id() */ diff --git a/source/blender/blenkernel/BKE_object_deform.h b/source/blender/blenkernel/BKE_object_deform.h index a0a885c2a04..19a2220006a 100644 --- a/source/blender/blenkernel/BKE_object_deform.h +++ b/source/blender/blenkernel/BKE_object_deform.h @@ -51,9 +51,11 @@ bool BKE_object_defgroup_clear(struct Object *ob, struct bDeformGroup *dg, const bool BKE_object_defgroup_clear_all(struct Object *ob, const bool use_selection); void BKE_object_defgroup_remove(struct Object *ob, struct bDeformGroup *defgroup); +void BKE_object_defgroup_remove_all_ex(struct Object *ob, bool only_unlocked); void BKE_object_defgroup_remove_all(struct Object *ob); + /* Select helpers */ enum eVGroupSelect; bool *BKE_object_defgroup_subset_from_select_type( diff --git a/source/blender/blenkernel/intern/blender_copybuffer.c b/source/blender/blenkernel/intern/blender_copybuffer.c index a4c28121040..e57524af546 100644 --- a/source/blender/blenkernel/intern/blender_copybuffer.c +++ b/source/blender/blenkernel/intern/blender_copybuffer.c @@ -101,7 +101,7 @@ bool BKE_copybuffer_read(Main *bmain_dst, const char *libname, ReportList *repor IMB_colormanagement_check_file_config(bmain_dst); /* Append, rather than linking. */ Library *lib = BLI_findstring(&bmain_dst->library, libname, offsetof(Library, filepath)); - BKE_library_make_local(bmain_dst, lib, true, false); + BKE_library_make_local(bmain_dst, lib, NULL, true, false); /* Important we unset, otherwise these object wont * link into other scenes from this blend file. */ @@ -150,7 +150,7 @@ bool BKE_copybuffer_paste(bContext *C, const char *libname, const short flag, Re /* append, rather than linking */ lib = BLI_findstring(&bmain->library, libname, offsetof(Library, filepath)); - BKE_library_make_local(bmain, lib, true, false); + BKE_library_make_local(bmain, lib, NULL, true, false); /* important we unset, otherwise these object wont * link into other scenes from this blend file */ diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index e62e652b4a6..6a08673144e 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -29,6 +29,8 @@ #include "DNA_anim_types.h" #include "DNA_cachefile_types.h" +#include "DNA_constraint_types.h" +#include "DNA_object_types.h" #include "DNA_scene_types.h" #include "BLI_fileops.h" @@ -43,6 +45,7 @@ #include "BKE_global.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_modifier.h" #include "BKE_scene.h" #ifdef WITH_ALEMBIC @@ -196,3 +199,41 @@ float BKE_cachefile_time_offset(CacheFile *cache_file, const float time, const f const float frame = (cache_file->override_frame ? cache_file->frame : time); return cache_file->is_sequence ? frame : frame / fps; } + +/* TODO(kevin): replace this with some depsgraph mechanism, or something similar. */ +void BKE_cachefile_clean(Scene *scene, CacheFile *cache_file) +{ + for (Base *base = scene->base.first; base; base = base->next) { + Object *ob = base->object; + + ModifierData *md = modifiers_findByType(ob, eModifierType_MeshSequenceCache); + + if (md) { + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; + + if (cache_file == mcmd->cache_file) { +#ifdef WITH_ALEMBIC + CacheReader_free(mcmd->reader); +#endif + mcmd->reader = NULL; + mcmd->object_path[0] = '\0'; + } + } + + for (bConstraint *con = ob->constraints.first; con; con = con->next) { + if (con->type != CONSTRAINT_TYPE_TRANSFORM_CACHE) { + continue; + } + + bTransformCacheConstraint *data = con->data; + + if (cache_file == data->cache_file) { +#ifdef WITH_ALEMBIC + CacheReader_free(data->reader); +#endif + data->reader = NULL; + data->object_path[0] = '\0'; + } + } + } +} diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index c4afa58b7d3..b85f1b838ff 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -4364,8 +4364,14 @@ static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBa BKE_cachefile_ensure_handle(G.main, cache_file); - ABC_get_transform(cache_file->handle, cob->ob, data->object_path, - cob->matrix, time, cache_file->scale); + if (!data->reader) { + data->reader = CacheReader_open_alembic_object(cache_file->handle, + data->reader, + cob->ob, + data->object_path); + } + + ABC_get_transform(data->reader, cob->matrix, time, cache_file->scale); #else UNUSED_VARS(con, cob); #endif @@ -4393,6 +4399,12 @@ static void transformcache_free(bConstraint *con) if (data->cache_file) { id_us_min(&data->cache_file->id); } + + if (data->reader) { +#ifdef WITH_ALEMBIC + CacheReader_free(data->reader); +#endif + } } static void transformcache_new_data(void *cdata) diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index 926ca8da192..4c01bfd35f2 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -49,6 +49,7 @@ #include "BKE_context.h" #include "BKE_main.h" #include "BKE_screen.h" +#include "BKE_sound.h" #include "RNA_access.h" @@ -882,6 +883,7 @@ Main *CTX_data_main(const bContext *C) void CTX_data_main_set(bContext *C, Main *bmain) { C->data.main = bmain; + BKE_sound_init_main(bmain); } Scene *CTX_data_scene(const bContext *C) diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index 5f8332dcf0c..02ae123a71e 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -2997,7 +2997,7 @@ void DAG_id_tag_update_ex(Main *bmain, ID *id, short flag) MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; if (mcmd->cache_file && (&mcmd->cache_file->id == id)) { - ob->recalc |= OB_RECALC_DATA; + ob->recalc |= OB_RECALC_ALL; continue; } } @@ -3010,7 +3010,7 @@ void DAG_id_tag_update_ex(Main *bmain, ID *id, short flag) bTransformCacheConstraint *data = con->data; if (data->cache_file && (&data->cache_file->id == id)) { - ob->recalc |= OB_RECALC_DATA; + ob->recalc |= OB_RECALC_ALL; break; } } diff --git a/source/blender/blenkernel/intern/icons.c b/source/blender/blenkernel/intern/icons.c index ea0301c30f2..838a732e387 100644 --- a/source/blender/blenkernel/intern/icons.c +++ b/source/blender/blenkernel/intern/icons.c @@ -143,7 +143,7 @@ static PreviewImage *previewimg_create_ex(size_t deferred_data_size) memset(prv_img, 0, sizeof(*prv_img)); /* leave deferred data dirty */ if (deferred_data_size) { - prv_img->use_deferred = true; + prv_img->tag |= PRV_TAG_DEFFERED; } for (i = 0; i < NUM_ICON_SIZES; ++i) { @@ -481,11 +481,14 @@ PreviewImage *BKE_previewimg_cached_thumbnail_read( return prv; } -void BKE_previewimg_cached_release(const char *name) +void BKE_previewimg_cached_release_pointer(PreviewImage *prv) { - PreviewImage *prv = BLI_ghash_popkey(gCachedPreviews, name, MEM_freeN); - if (prv) { + if (prv->tag & PRV_TAG_DEFFERED_RENDERING) { + /* We cannot delete the preview while it is being loaded in another thread... */ + prv->tag |= PRV_TAG_DEFFERED_DELETE; + return; + } if (prv->icon_id) { BKE_icon_delete(prv->icon_id); } @@ -493,11 +496,18 @@ void BKE_previewimg_cached_release(const char *name) } } +void BKE_previewimg_cached_release(const char *name) +{ + PreviewImage *prv = BLI_ghash_popkey(gCachedPreviews, name, MEM_freeN); + + BKE_previewimg_cached_release_pointer(prv); +} + /** Handle deferred (lazy) loading/generation of preview image, if needed. * For now, only used with file thumbnails. */ void BKE_previewimg_ensure(PreviewImage *prv, const int size) { - if (prv->use_deferred) { + if ((prv->tag & PRV_TAG_DEFFERED) != 0) { const bool do_icon = ((size == ICON_SIZE_ICON) && !prv->rect[ICON_SIZE_ICON]); const bool do_preview = ((size == ICON_SIZE_PREVIEW) && !prv->rect[ICON_SIZE_PREVIEW]); diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index a4eef2f9230..df3a7630bb0 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -1580,24 +1580,7 @@ void BKE_imbuf_to_image_format(struct ImageFormatData *im_format, const ImBuf *i } /* planes */ - /* TODO(sergey): Channels doesn't correspond actual planes used for image buffer - * For example byte buffer will have 4 channels but it might easily - * be BW or RGB image. - * - * Need to use im_format->planes = imbuf->planes instead? - */ - switch (imbuf->channels) { - case 0: - case 4: im_format->planes = R_IMF_PLANES_RGBA; - break; - case 3: im_format->planes = R_IMF_PLANES_RGB; - break; - case 1: im_format->planes = R_IMF_PLANES_BW; - break; - default: im_format->planes = R_IMF_PLANES_RGB; - break; - } - + im_format->planes = imbuf->planes; } diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 14612151a8e..622f79df4ee 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -73,6 +73,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_ghash.h" #include "BLI_linklist.h" #include "BLI_memarena.h" @@ -1640,7 +1641,8 @@ void BKE_main_id_clear_newpoins(Main *bmain) * We'll probably need at some point a true dependency graph between datablocks, but for now this should work * good enough (performances is not a critical point here anyway). */ -void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged_only, const bool set_fake) +void BKE_library_make_local( + Main *bmain, const Library *lib, GHash *old_to_new_ids, const bool untagged_only, const bool set_fake) { ListBase *lbarray[MAX_LIBARRAY]; ID *id, *id_next; @@ -1712,6 +1714,9 @@ void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged BLI_assert(id->lib != NULL); BKE_libblock_remap(bmain, id, id->newid, ID_REMAP_SKIP_INDIRECT_USAGE); + if (old_to_new_ids) { + BLI_ghash_insert(old_to_new_ids, id, id->newid); + } } /* Third step: remove datablocks that have been copied to be localized and are no more used in the end... diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 446aef9be65..d21f43ac484 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -2389,7 +2389,7 @@ Mesh *BKE_mesh_new_from_object( /* are we an object material or data based? */ tmpmesh->mat[i] = give_current_material(ob, i + 1); - if (((ob->matbits[i] && ob->matbits) || do_mat_id_data_us) && tmpmesh->mat[i]) { + if (((ob->matbits && ob->matbits[i]) || do_mat_id_data_us) && tmpmesh->mat[i]) { id_us_plus(&tmpmesh->mat[i]->id); } } diff --git a/source/blender/blenkernel/intern/object_deform.c b/source/blender/blenkernel/intern/object_deform.c index b5f63588423..b5e1ded35bb 100644 --- a/source/blender/blenkernel/intern/object_deform.c +++ b/source/blender/blenkernel/intern/object_deform.c @@ -408,8 +408,9 @@ void BKE_object_defgroup_remove(Object *ob, bDeformGroup *defgroup) /** * Remove all vgroups from object. Work in Object and Edit modes. + * When only_unlocked=true, locked vertex groups are not removed. */ -void BKE_object_defgroup_remove_all(Object *ob) +void BKE_object_defgroup_remove_all_ex(struct Object *ob, bool only_unlocked) { bDeformGroup *dg = (bDeformGroup *)ob->defbase.first; const bool edit_mode = BKE_object_is_in_editmode_vgroup(ob); @@ -418,10 +419,12 @@ void BKE_object_defgroup_remove_all(Object *ob) while (dg) { bDeformGroup *next_dg = dg->next; - if (edit_mode) - object_defgroup_remove_edit_mode(ob, dg); - else - object_defgroup_remove_object_mode(ob, dg); + if (!only_unlocked || (dg->flag & DG_LOCK_WEIGHT) == 0) { + if (edit_mode) + object_defgroup_remove_edit_mode(ob, dg); + else + object_defgroup_remove_object_mode(ob, dg); + } dg = next_dg; } @@ -446,6 +449,15 @@ void BKE_object_defgroup_remove_all(Object *ob) } /** + * Remove all vgroups from object. Work in Object and Edit modes. + */ +void BKE_object_defgroup_remove_all(struct Object *ob) +{ + BKE_object_defgroup_remove_all_ex(ob, false); +} + + +/** * Get MDeformVert vgroup data from given object. Should only be used in Object mode. * * \return True if the id type supports weights. diff --git a/source/blender/blenkernel/intern/particle_child.c b/source/blender/blenkernel/intern/particle_child.c index ec5f73f87ce..842de869291 100644 --- a/source/blender/blenkernel/intern/particle_child.c +++ b/source/blender/blenkernel/intern/particle_child.c @@ -136,7 +136,7 @@ static void do_kink_spiral_deform(ParticleKey *state, const float dir[3], const * * The "density" parameter b is defined by the shape parameter * and goes up to the Golden Spiral for 1.0 - * http://en.wikipedia.org/wiki/Golden_spiral + * https://en.wikipedia.org/wiki/Golden_spiral */ const float b = shape * (1.0f + sqrtf(5.0f)) / (float)M_PI * 0.25f; /* angle of the spiral against the curve (rotated opposite to make a smooth transition) */ diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index efaf1f9df2b..ee435051151 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -2180,7 +2180,7 @@ static void basic_rotate(ParticleSettings *part, ParticleData *pa, float dfra, f * The algorithm is roughly: * 1. Use a BVH tree to search for faces that a particle may collide with. * 2. Use Newton's method to find the exact time at which the collision occurs. - * http://en.wikipedia.org/wiki/Newton's_method + * https://en.wikipedia.org/wiki/Newton's_method * ************************************************/ #define COLLISION_MIN_RADIUS 0.001f diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c index 05540f51588..e8970d416e9 100644 --- a/source/blender/blenkernel/intern/smoke.c +++ b/source/blender/blenkernel/intern/smoke.c @@ -360,6 +360,10 @@ static void smokeModifier_freeDomain(SmokeModifierData *smd) BKE_ptcache_free_list(&(smd->domain->ptcaches[0])); smd->domain->point_cache[0] = NULL; + if (smd->domain->coba) { + MEM_freeN(smd->domain->coba); + } + MEM_freeN(smd->domain); smd->domain = NULL; } @@ -544,6 +548,9 @@ void smokeModifier_createType(struct SmokeModifierData *smd) smd->domain->slice_depth = 0.5f; smd->domain->slice_axis = 0; smd->domain->vector_scale = 1.0f; + + smd->domain->coba = NULL; + smd->domain->coba_field = FLUID_FIELD_DENSITY; } else if (smd->type & MOD_SMOKE_TYPE_FLOW) { @@ -646,6 +653,10 @@ void smokeModifier_copy(struct SmokeModifierData *smd, struct SmokeModifierData tsmd->domain->draw_velocity = smd->domain->draw_velocity; tsmd->domain->vector_draw_type = smd->domain->vector_draw_type; tsmd->domain->vector_scale = smd->domain->vector_scale; + + if (smd->domain->coba) { + tsmd->domain->coba = MEM_dupallocN(smd->domain->coba); + } } else if (tsmd->flow) { tsmd->flow->psys = smd->flow->psys; diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index a86606f1099..96ab8693122 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -44,6 +44,7 @@ #include "DNA_scene_types.h" #include "BLI_utildefines.h" +#include "BLI_bitmap_draw_2d.h" #include "BLI_ghash.h" #include "BLI_math.h" #include "BLI_math_base.h" @@ -997,9 +998,10 @@ static void track_mask_gpencil_layer_rasterize(int frame_width, int frame_height point[1] = (stroke_points[i].y - marker->search_min[1]) * frame_height; } /* TODO: add an option to control whether AA is enabled or not */ - fill_poly_v2i_n(0, 0, mask_width, mask_height, - (const int (*)[2])mask_points, stroke->totpoints, - track_mask_set_pixel_cb, &data); + BLI_bitmap_draw_2d_poly_v2i_n( + 0, 0, mask_width, mask_height, + (const int (*)[2])mask_points, stroke->totpoints, + track_mask_set_pixel_cb, &data); MEM_freeN(mask_points); } stroke = stroke->next; diff --git a/source/blender/blenlib/BLI_bitmap_draw_2d.h b/source/blender/blenlib/BLI_bitmap_draw_2d.h new file mode 100644 index 00000000000..fe890e94f1b --- /dev/null +++ b/source/blender/blenlib/BLI_bitmap_draw_2d.h @@ -0,0 +1,37 @@ +/* + * ***** 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_BITMAP_DRAW_2D_H__ +#define __BLI_BITMAP_DRAW_2D_H__ + +/** \file BLI_bitmap_draw_2d.h + * \ingroup bli + */ + +void BLI_bitmap_draw_2d_line_v2v2i( + const int p1[2], const int p2[2], + bool (*callback)(int, int, void *), void *userData); + +void BLI_bitmap_draw_2d_poly_v2i_n( + const int xmin, const int ymin, const int xmax, const int ymax, + const int polyXY[][2], const int polyCorners, + void (*callback)(int x, int x_end, int y, void *), void *userData); + +#endif /* __BLI_BITMAP_DRAW_2D_H__ */ diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 84a25f533bf..514b0300274 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -325,11 +325,6 @@ bool clip_segment_v3_plane_n( const float p1[3], const float p2[3], const float plane_array[][4], const int plane_tot, float r_p1[3], float r_p2[3]); -void plot_line_v2v2i(const int p1[2], const int p2[2], bool (*callback)(int, int, void *), void *userData); -void fill_poly_v2i_n( - const int xmin, const int ymin, const int xmax, const int ymax, - const int polyXY[][2], const int polyCorners, - void (*callback)(int x, int x_end, int y, void *), void *userData); /****************************** Interpolation ********************************/ /* tri or quad, d can be NULL */ diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 9978d1d19af..6e717a3ae7e 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -56,6 +56,7 @@ set(SRC intern/array_store_utils.c intern/array_utils.c intern/astar.c + intern/bitmap_draw_2d.c intern/boxpack2d.c intern/buffer.c intern/callbacks.c @@ -127,6 +128,7 @@ set(SRC BLI_array_utils.h BLI_astar.h BLI_bitmap.h + BLI_bitmap_draw_2d.h BLI_blenlib.h BLI_boxpack2d.h BLI_buffer.h diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c index f943a8119c4..944ee18e6b2 100644 --- a/source/blender/blenlib/intern/BLI_ghash.c +++ b/source/blender/blenlib/intern/BLI_ghash.c @@ -1588,7 +1588,7 @@ double BLI_ghash_calc_quality_ex( if (r_variance) { /* We already know our mean (i.e. load factor), easy to compute variance. - * See http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Two-pass_algorithm + * See https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Two-pass_algorithm */ double sum = 0.0; for (i = 0; i < gh->nbuckets; i++) { diff --git a/source/blender/blenlib/intern/astar.c b/source/blender/blenlib/intern/astar.c index 21d974de1c4..0020dbe4612 100644 --- a/source/blender/blenlib/intern/astar.c +++ b/source/blender/blenlib/intern/astar.c @@ -35,7 +35,7 @@ * in addition to distance already walked. This heuristic allows more efficiency * in finding optimal path. * - * Implementation based on Wikipedia A* page [http://en.wikipedia.org/wiki/A*_search_algorithm]. + * Implementation based on Wikipedia A* page [https://en.wikipedia.org/wiki/A*_search_algorithm]. * * Note that most memory handling here is done through two different MemArena's. Those should also be used to allocate * custom data needed to a specific use of A*. diff --git a/source/blender/blenlib/intern/bitmap_draw_2d.c b/source/blender/blenlib/intern/bitmap_draw_2d.c new file mode 100644 index 00000000000..afc54511d13 --- /dev/null +++ b/source/blender/blenlib/intern/bitmap_draw_2d.c @@ -0,0 +1,331 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: some of this file. + * + * ***** END GPL LICENSE BLOCK ***** + * */ + +/** \file blender/blenlib/intern/bitmap_draw_2d.c + * \ingroup bli + * + * Utility functions for primitive drawing operations. + */ + +#include <limits.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_bitmap_draw_2d.h" + +#include "BLI_math_base.h" +#include "BLI_sort.h" +#include "BLI_utildefines.h" + +#include "BLI_strict_flags.h" + +/* -------------------------------------------------------------------- */ +/* Draw Line */ + +/** + * Plot a line from \a p1 to \a p2 (inclusive). + */ +void BLI_bitmap_draw_2d_line_v2v2i( + const int p1[2], const int p2[2], + bool (*callback)(int, int, void *), void *userData) +{ + /* Bresenham's line algorithm. */ + int x1 = p1[0]; + int y1 = p1[1]; + int x2 = p2[0]; + int y2 = p2[1]; + + int ix; + int iy; + + /* if x1 == x2 or y1 == y2, then it does not matter what we set here */ + int delta_x = (x2 > x1 ? ((void)(ix = 1), x2 - x1) : ((void)(ix = -1), x1 - x2)) << 1; + int delta_y = (y2 > y1 ? ((void)(iy = 1), y2 - y1) : ((void)(iy = -1), y1 - y2)) << 1; + + if (callback(x1, y1, userData) == 0) { + return; + } + + if (delta_x >= delta_y) { + /* error may go below zero */ + int error = delta_y - (delta_x >> 1); + + while (x1 != x2) { + if (error >= 0) { + if (error || (ix > 0)) { + y1 += iy; + error -= delta_x; + } + /* else do nothing */ + } + /* else do nothing */ + + x1 += ix; + error += delta_y; + + if (callback(x1, y1, userData) == 0) { + return; + } + } + } + else { + /* error may go below zero */ + int error = delta_x - (delta_y >> 1); + + while (y1 != y2) { + if (error >= 0) { + if (error || (iy > 0)) { + x1 += ix; + error -= delta_y; + } + /* else do nothing */ + } + /* else do nothing */ + + y1 += iy; + error += delta_x; + + if (callback(x1, y1, userData) == 0) { + return; + } + } + } +} + + +/* -------------------------------------------------------------------- */ +/* Draw Filled Polygon */ + +/* sort edge-segments on y, then x axis */ +static int draw_poly_v2i_n__span_y_sort(const void *a_p, const void *b_p, void *verts_p) +{ + const int (*verts)[2] = verts_p; + const int *a = a_p; + const int *b = b_p; + const int *co_a = verts[a[0]]; + const int *co_b = verts[b[0]]; + + if (co_a[1] < co_b[1]) { + return -1; + } + else if (co_a[1] > co_b[1]) { + return 1; + } + else if (co_a[0] < co_b[0]) { + return -1; + } + else if (co_a[0] > co_b[0]) { + return 1; + } + else { + /* co_a & co_b are identical, use the line closest to the x-min */ + const int *co = co_a; + co_a = verts[a[1]]; + co_b = verts[b[1]]; + int ord = (((co_b[0] - co[0]) * (co_a[1] - co[1])) - + ((co_a[0] - co[0]) * (co_b[1] - co[1]))); + if (ord > 0) { + return -1; + } + if (ord < 0) { + return 1; + } + } + return 0; +} + +/** + * Draws a filled polyon with support for self intersections. + * + * \param callback: Takes the x, y coords and x-span (\a x_end is not inclusive), + * note that \a x_end will always be greater than \a x, so we can use: + * + * \code{.c} + * do { + * func(x, y); + * } while (++x != x_end); + * \endcode + */ +void BLI_bitmap_draw_2d_poly_v2i_n( + const int xmin, const int ymin, const int xmax, const int ymax, + const int verts[][2], const int nr, + void (*callback)(int x, int x_end, int y, void *), void *userData) +{ + /* Originally by Darel Rex Finley, 2007. + * Optimized by Campbell Barton, 2016 to track sorted intersections. */ + + int (*span_y)[2] = MEM_mallocN(sizeof(*span_y) * (size_t)nr, __func__); + int span_y_len = 0; + + for (int i_curr = 0, i_prev = nr - 1; i_curr < nr; i_prev = i_curr++) { + const int *co_prev = verts[i_prev]; + const int *co_curr = verts[i_curr]; + + if (co_prev[1] != co_curr[1]) { + /* Any segments entirely above or below the area of interest can be skipped. */ + if ((min_ii(co_prev[1], co_curr[1]) >= ymax) || + (max_ii(co_prev[1], co_curr[1]) < ymin)) + { + continue; + } + + int *s = span_y[span_y_len++]; + if (co_prev[1] < co_curr[1]) { + s[0] = i_prev; + s[1] = i_curr; + } + else { + s[0] = i_curr; + s[1] = i_prev; + } + } + } + + BLI_qsort_r(span_y, (size_t)span_y_len, sizeof(*span_y), draw_poly_v2i_n__span_y_sort, (void *)verts); + + struct NodeX { + int span_y_index; + int x; + } *node_x = MEM_mallocN(sizeof(*node_x) * (size_t)(nr + 1), __func__); + int node_x_len = 0; + + int span_y_index = 0; + if (span_y_len != 0 && verts[span_y[0][0]][1] < ymin) { + while ((span_y_index < span_y_len) && + (verts[span_y[span_y_index][0]][1] < ymin)) + { + BLI_assert(verts[span_y[span_y_index][0]][1] < + verts[span_y[span_y_index][1]][1]); + if (verts[span_y[span_y_index][1]][1] >= ymin) { + struct NodeX *n = &node_x[node_x_len++]; + n->span_y_index = span_y_index; + } + span_y_index += 1; + } + } + + /* Loop through the rows of the image. */ + for (int pixel_y = ymin; pixel_y < ymax; pixel_y++) { + bool is_sorted = true; + bool do_remove = false; + + for (int i = 0, x_ix_prev = INT_MIN; i < node_x_len; i++) { + struct NodeX *n = &node_x[i]; + const int *s = span_y[n->span_y_index]; + const int *co_prev = verts[s[0]]; + const int *co_curr = verts[s[1]]; + + BLI_assert(co_prev[1] < pixel_y && co_curr[1] >= pixel_y); + + const double x = (co_prev[0] - co_curr[0]); + const double y = (co_prev[1] - co_curr[1]); + const double y_px = (pixel_y - co_curr[1]); + const int x_ix = (int)((double)co_curr[0] + ((y_px / y) * x)); + n->x = x_ix; + + if (is_sorted && (x_ix_prev > x_ix)) { + is_sorted = false; + } + if (do_remove == false && co_curr[1] == pixel_y) { + do_remove = true; + } + x_ix_prev = x_ix; + } + + /* Sort the nodes, via a simple "Bubble" sort. */ + if (is_sorted == false) { + int i = 0; + const int node_x_end = node_x_len - 1; + while (i < node_x_end) { + if (node_x[i].x > node_x[i + 1].x) { + SWAP(struct NodeX, node_x[i], node_x[i + 1]); + if (i != 0) { + i -= 1; + } + } + else { + i += 1; + } + } + } + + /* Fill the pixels between node pairs. */ + for (int i = 0; i < node_x_len; i += 2) { + int x_src = node_x[i].x; + int x_dst = node_x[i + 1].x; + + if (x_src >= xmax) { + break; + } + + if (x_dst > xmin) { + if (x_src < xmin) { + x_src = xmin; + } + if (x_dst > xmax) { + x_dst = xmax; + } + /* for single call per x-span */ + if (x_src < x_dst) { + callback(x_src - xmin, x_dst - xmin, pixel_y - ymin, userData); + } + } + } + + /* Clear finalized nodes in one pass, only when needed + * (avoids excessive array-resizing). */ + if (do_remove == true) { + int i_dst = 0; + for (int i_src = 0; i_src < node_x_len; i_src += 1) { + const int *s = span_y[node_x[i_src].span_y_index]; + const int *co = verts[s[1]]; + if (co[1] != pixel_y) { + if (i_dst != i_src) { + /* x is initialized for the next pixel_y (no need to adjust here) */ + node_x[i_dst].span_y_index = node_x[i_src].span_y_index; + } + i_dst += 1; + } + } + node_x_len = i_dst; + } + + /* Scan for new x-nodes */ + while ((span_y_index < span_y_len) && + (verts[span_y[span_y_index][0]][1] == pixel_y)) + { + /* note, node_x these are just added at the end, + * not ideal but sorting once will resolve. */ + + /* x is initialized for the next pixel_y */ + struct NodeX *n = &node_x[node_x_len++]; + n->span_y_index = span_y_index; + span_y_index += 1; + } + } + + MEM_freeN(span_y); + MEM_freeN(node_x); +} diff --git a/source/blender/blenlib/intern/math_color_inline.c b/source/blender/blenlib/intern/math_color_inline.c index abb8ff35a45..01a805a09b6 100644 --- a/source/blender/blenlib/intern/math_color_inline.c +++ b/source/blender/blenlib/intern/math_color_inline.c @@ -269,7 +269,7 @@ MINLINE void cpack_cpy_3ub(unsigned char r_col[3], const unsigned int pack) /** * ITU-R BT.709 primaries - * http://en.wikipedia.org/wiki/Relative_luminance + * https://en.wikipedia.org/wiki/Relative_luminance * * Real values are: * ``Y = 0.2126390059(R) + 0.7151686788(G) + 0.0721923154(B)`` diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index dd30f267f78..f31d0935b77 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -2885,142 +2885,6 @@ bool clip_segment_v3_plane_n( return true; } -void plot_line_v2v2i(const int p1[2], const int p2[2], bool (*callback)(int, int, void *), void *userData) -{ - int x1 = p1[0]; - int y1 = p1[1]; - int x2 = p2[0]; - int y2 = p2[1]; - - int ix; - int iy; - - /* if x1 == x2 or y1 == y2, then it does not matter what we set here */ - int delta_x = (x2 > x1 ? ((void)(ix = 1), x2 - x1) : ((void)(ix = -1), x1 - x2)) << 1; - int delta_y = (y2 > y1 ? ((void)(iy = 1), y2 - y1) : ((void)(iy = -1), y1 - y2)) << 1; - - if (callback(x1, y1, userData) == 0) { - return; - } - - if (delta_x >= delta_y) { - /* error may go below zero */ - int error = delta_y - (delta_x >> 1); - - while (x1 != x2) { - if (error >= 0) { - if (error || (ix > 0)) { - y1 += iy; - error -= delta_x; - } - /* else do nothing */ - } - /* else do nothing */ - - x1 += ix; - error += delta_y; - - if (callback(x1, y1, userData) == 0) { - return; - } - } - } - else { - /* error may go below zero */ - int error = delta_x - (delta_y >> 1); - - while (y1 != y2) { - if (error >= 0) { - if (error || (iy > 0)) { - x1 += ix; - error -= delta_y; - } - /* else do nothing */ - } - /* else do nothing */ - - y1 += iy; - error += delta_x; - - if (callback(x1, y1, userData) == 0) { - return; - } - } - } -} - -/** - * \param callback: Takes the x, y coords and x-span (\a x_end is not inclusive), - * note that \a x_end will always be greater than \a x, so we can use: - * - * \code{.c} - * do { - * func(x, y); - * } while (++x != x_end); - * \endcode - */ -void fill_poly_v2i_n( - const int xmin, const int ymin, const int xmax, const int ymax, - const int verts[][2], const int nr, - void (*callback)(int x, int x_end, int y, void *), void *userData) -{ - /* originally by Darel Rex Finley, 2007 */ - - int nodes, pixel_y, i, j, swap; - int *node_x = MEM_mallocN(sizeof(*node_x) * (size_t)(nr + 1), __func__); - - /* Loop through the rows of the image. */ - for (pixel_y = ymin; pixel_y < ymax; pixel_y++) { - - /* Build a list of nodes. */ - nodes = 0; j = nr - 1; - for (i = 0; i < nr; i++) { - if ((verts[i][1] < pixel_y && verts[j][1] >= pixel_y) || - (verts[j][1] < pixel_y && verts[i][1] >= pixel_y)) - { - node_x[nodes++] = (int)(verts[i][0] + - ((double)(pixel_y - verts[i][1]) / (verts[j][1] - verts[i][1])) * - (verts[j][0] - verts[i][0])); - } - j = i; - } - - /* Sort the nodes, via a simple "Bubble" sort. */ - i = 0; - while (i < nodes - 1) { - if (node_x[i] > node_x[i + 1]) { - SWAP_TVAL(swap, node_x[i], node_x[i + 1]); - if (i) i--; - } - else { - i++; - } - } - - /* Fill the pixels between node pairs. */ - for (i = 0; i < nodes; i += 2) { - if (node_x[i] >= xmax) break; - if (node_x[i + 1] > xmin) { - if (node_x[i ] < xmin) node_x[i ] = xmin; - if (node_x[i + 1] > xmax) node_x[i + 1] = xmax; - -#if 0 - /* for many x/y calls */ - for (j = node_x[i]; j < node_x[i + 1]; j++) { - callback(j - xmin, pixel_y - ymin, userData); - } -#else - /* for single call per x-span */ - if (node_x[i] < node_x[i + 1]) { - callback(node_x[i] - xmin, node_x[i + 1] - xmin, pixel_y - ymin, userData); - } -#endif - } - } - } - MEM_freeN(node_x); -} - /****************************** Axis Utils ********************************/ /** diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index ded10ad7713..f0d0bd00dea 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -430,7 +430,7 @@ void BLI_cleanup_file(const char *relabase, char *path) * \return true if \a fname was changed, false otherwise. * * For now, simply replaces reserved chars (as listed in - * http://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words ) + * https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words ) * by underscores ('_'). * * \note Space case ' ' is a bit of an edge case here - in theory it is allowed, but again can be an issue diff --git a/source/blender/blenlib/intern/smallhash.c b/source/blender/blenlib/intern/smallhash.c index 0b976e9612e..ccac221d836 100644 --- a/source/blender/blenlib/intern/smallhash.c +++ b/source/blender/blenlib/intern/smallhash.c @@ -34,7 +34,7 @@ * based on a doubling hashing approach (non-chaining) which uses more buckets then entries * stepping over buckets when two keys share the same hash so any key can find a free bucket. * - * See: http://en.wikipedia.org/wiki/Double_hashing + * See: https://en.wikipedia.org/wiki/Double_hashing * * \warning This should _only_ be used for small hashes where allocating a hash every time is unacceptable. * Otherwise #GHash should be used instead. diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 60653582c74..f0a7aa8b0f3 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -2147,6 +2147,7 @@ static PreviewImage *direct_link_preview_image(FileData *fd, PreviewImage *old_p prv->gputexture[i] = NULL; } prv->icon_id = 0; + prv->tag = 0; } return prv; @@ -5092,6 +5093,7 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb) smd->domain->tex = NULL; smd->domain->tex_shadow = NULL; smd->domain->tex_wt = NULL; + smd->domain->coba = newdataadr(fd, smd->domain->coba); smd->domain->effector_weights = newdataadr(fd, smd->domain->effector_weights); if (!smd->domain->effector_weights) diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index e606f0e659a..6833ad9a8b1 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -1731,6 +1731,10 @@ static void write_modifiers(WriteData *wd, ListBase *modbase) smd->domain->point_cache[1]->step = 1; write_pointcaches(wd, &(smd->domain->ptcaches[1])); + + if (smd->domain->coba) { + writestruct(wd, DATA, ColorBand, 1, smd->domain->coba); + } } writestruct(wd, DATA, SmokeDomainSettings, 1, smd->domain); diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index 72ea7bd7f5d..104df625ee6 100644 --- a/source/blender/bmesh/bmesh_class.h +++ b/source/blender/bmesh/bmesh_class.h @@ -245,7 +245,7 @@ typedef struct BMesh { /* ID of the shape key this bmesh came from */ int shapenr; - int walkers, totflags; + int totflags; ListBase selected; BMFace *act_face; diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index e83b752947c..d2f638fa8f8 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -287,7 +287,7 @@ static BMLoop *bm_face_boundary_add( #endif BMLoop *l = bm_loop_create(bm, startv, starte, f, NULL /* starte->l */, create_flag); - bmesh_radial_append(starte, l); + bmesh_radial_loop_append(starte, l); #ifdef USE_BMESH_HOLES lst->first = lst->last = l; @@ -295,8 +295,6 @@ static BMLoop *bm_face_boundary_add( #else f->l_first = l; #endif - - l->f = f; return l; } @@ -458,14 +456,11 @@ BMFace *BM_face_create( f = bm_face_create__internal(bm); startl = lastl = bm_face_boundary_add(bm, f, verts[0], edges[0], create_flag); - - startl->v = verts[0]; - startl->e = edges[0]; + for (i = 1; i < len; i++) { l = bm_loop_create(bm, verts[i], edges[i], f, NULL /* edges[i]->l */, create_flag); - - l->f = f; - bmesh_radial_append(edges[i], l); + + bmesh_radial_loop_append(edges[i], l); l->prev = lastl; lastl->next = l; @@ -904,7 +899,7 @@ void BM_face_kill(BMesh *bm, BMFace *f) do { l_next = l_iter->next; - bmesh_radial_loop_remove(l_iter, l_iter->e); + bmesh_radial_loop_remove(l_iter->e, l_iter); bm_kill_only_loop(bm, l_iter); } while ((l_iter = l_next) != l_first); @@ -949,7 +944,7 @@ void BM_face_kill_loose(BMesh *bm, BMFace *f) l_next = l_iter->next; e = l_iter->e; - bmesh_radial_loop_remove(l_iter, e); + bmesh_radial_loop_remove(e, l_iter); bm_kill_only_loop(bm, l_iter); if (e->l == NULL) { @@ -981,23 +976,8 @@ void BM_face_kill_loose(BMesh *bm, BMFace *f) */ void BM_edge_kill(BMesh *bm, BMEdge *e) { - - if (e->l) { - BMLoop *l = e->l, *lnext, *startl = e->l; - - do { - lnext = l->radial_next; - if (lnext->f == l->f) { - BM_face_kill(bm, l->f); - break; - } - - BM_face_kill(bm, l->f); - - if (l == lnext) - break; - l = lnext; - } while (l != startl); + while (e->l) { + BM_face_kill(bm, e->l->f); } bmesh_disk_edge_remove(e, e->v1); @@ -1011,15 +991,8 @@ void BM_edge_kill(BMesh *bm, BMEdge *e) */ void BM_vert_kill(BMesh *bm, BMVert *v) { - if (v->e) { - BMEdge *e, *e_next; - - e = v->e; - while (v->e) { - e_next = bmesh_disk_edge_next(e, v); - BM_edge_kill(bm, e); - e = e_next; - } + while (v->e) { + BM_edge_kill(bm, v->e); } bm_kill_only_vert(bm, v); @@ -1077,7 +1050,7 @@ static bool bm_loop_reverse_loop( int i, j, edok; for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) { - bmesh_radial_loop_remove(l_iter, (edar[i] = l_iter->e)); + bmesh_radial_loop_remove((edar[i] = l_iter->e), l_iter); } /* actually reverse the loop */ @@ -1113,7 +1086,7 @@ static bool bm_loop_reverse_loop( } /* rebuild radial */ for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) - bmesh_radial_append(l_iter->e, l_iter); + bmesh_radial_loop_append(l_iter->e, l_iter); #ifndef NDEBUG /* validate radial */ @@ -1585,8 +1558,8 @@ BMFace *bmesh_sfme( } while ((l_iter = l_iter->next) != l_first); /* link up the new loops into the new edges radial */ - bmesh_radial_append(e, l_f1); - bmesh_radial_append(e, l_f2); + bmesh_radial_loop_append(e, l_f1); + bmesh_radial_loop_append(e, l_f2); f2->len = f2len; @@ -1700,7 +1673,7 @@ BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e) l = l_next; l->f->len++; l_next = l_next != l_next->radial_next ? l_next->radial_next : NULL; - bmesh_radial_loop_remove(l, NULL); + bmesh_radial_loop_unlink(l); l_new = bm_loop_create(bm, NULL, NULL, l->f, l, 0); l_new->prev = l; @@ -1725,8 +1698,8 @@ BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e) l->radial_next = l->radial_prev = NULL; } - bmesh_radial_append(l_new->e, l_new); - bmesh_radial_append(l->e, l); + bmesh_radial_loop_append(l_new->e, l_new); + bmesh_radial_loop_append(l->e, l); } else if (BM_verts_in_edge(l_new->v, l_new->next->v, e_new)) { l_new->e = e_new; @@ -1743,8 +1716,8 @@ BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e) l->radial_next = l->radial_prev = NULL; } - bmesh_radial_append(l_new->e, l_new); - bmesh_radial_append(l->e, l); + bmesh_radial_loop_append(l_new->e, l_new); + bmesh_radial_loop_append(l->e, l); } } @@ -2619,8 +2592,8 @@ bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src) l = e_src->l; BLI_assert(BM_vert_in_edge(e_dst, l->v)); BLI_assert(BM_vert_in_edge(e_dst, l->next->v)); - bmesh_radial_loop_remove(l, e_src); - bmesh_radial_append(e_dst, l); + bmesh_radial_loop_remove(e_src, l); + bmesh_radial_loop_append(e_dst, l); } BLI_assert(bmesh_radial_length(e_src->l) == 0); @@ -2667,8 +2640,8 @@ void bmesh_edge_separate( } e_new = BM_edge_create(bm, e->v1, e->v2, e, BM_CREATE_NOP); - bmesh_radial_loop_remove(l_sep, e); - bmesh_radial_append(e_new, l_sep); + bmesh_radial_loop_remove(e, l_sep); + bmesh_radial_loop_append(e_new, l_sep); l_sep->e = e_new; if (copy_select) { @@ -2855,8 +2828,8 @@ BMVert *bmesh_urmv_loop_multi( do { l_next = l_iter->radial_next; if (BM_ELEM_API_FLAG_TEST(l_iter, LOOP_VISIT)) { - bmesh_radial_loop_remove(l_iter, e); - bmesh_radial_append(e_new, l_iter); + bmesh_radial_loop_remove(e, l_iter); + bmesh_radial_loop_append(e_new, l_iter); l_iter->e = e_new; } } while ((l_iter = l_next) != l_first); diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c index 1f64f7b74cc..2591c33fc73 100644 --- a/source/blender/bmesh/intern/bmesh_log.c +++ b/source/blender/bmesh/intern/bmesh_log.c @@ -45,7 +45,7 @@ #include "bmesh.h" #include "bmesh_log.h" -#include "range_tree_c_api.h" +#include "range_tree.h" #include "BLI_strict_flags.h" diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c index 5ee0e904a33..6ce7c100b0d 100644 --- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c +++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c @@ -161,7 +161,7 @@ static bool bm_face_split_edgenet_find_loop_pair( e_pair[1] = BLI_SMALLSTACK_POP(edges_boundary); if (edges_boundary_len > 2) { - BLI_SMALLSTACK_SWAP(edges_search, edges_wire); + BLI_SMALLSTACK_SWAP(edges_search, edges_boundary); } } else { diff --git a/source/blender/bmesh/intern/bmesh_structure.c b/source/blender/bmesh/intern/bmesh_structure.c index cb302139a4c..edde8cb5d31 100644 --- a/source/blender/bmesh/intern/bmesh_structure.c +++ b/source/blender/bmesh/intern/bmesh_structure.c @@ -143,7 +143,7 @@ void bmesh_disk_vert_replace(BMEdge *e, BMVert *v_dst, BMVert *v_src) * to store non-manifold conditions since BM does not keep track of region/shell information. * * Functions relating to this cycle: - * - #bmesh_radial_append + * - #bmesh_radial_loop_append * - #bmesh_radial_loop_remove * - #bmesh_radial_facevert_count * - #bmesh_radial_facevert_check @@ -389,6 +389,30 @@ bool bmesh_radial_validate(int radlen, BMLoop *l) return true; } +void bmesh_radial_loop_append(BMEdge *e, BMLoop *l) +{ + if (e->l == NULL) { + e->l = l; + l->radial_next = l->radial_prev = l; + } + else { + l->radial_prev = e->l; + l->radial_next = e->l->radial_next; + + e->l->radial_next->radial_prev = l; + e->l->radial_next = l; + + e->l = l; + } + + if (UNLIKELY(l->e && l->e != e)) { + /* l is already in a radial cycle for a different edge */ + BMESH_ASSERT(0); + } + + l->e = e; +} + /** * \brief BMESH RADIAL REMOVE LOOP * @@ -397,28 +421,27 @@ bool bmesh_radial_validate(int radlen, BMLoop *l) * updated (in the case that the edge's link into the radial * cycle was the loop which is being removed from the cycle). */ -void bmesh_radial_loop_remove(BMLoop *l, BMEdge *e) +void bmesh_radial_loop_remove(BMEdge *e, BMLoop *l) { /* if e is non-NULL, l must be in the radial cycle of e */ - if (UNLIKELY(e && e != l->e)) { + if (UNLIKELY(e != l->e)) { BMESH_ASSERT(0); } if (l->radial_next != l) { - if (e && l == e->l) + if (l == e->l) { e->l = l->radial_next; + } l->radial_next->radial_prev = l->radial_prev; l->radial_prev->radial_next = l->radial_next; } else { - if (e) { - if (l == e->l) { - e->l = NULL; - } - else { - BMESH_ASSERT(0); - } + if (l == e->l) { + e->l = NULL; + } + else { + BMESH_ASSERT(0); } } @@ -428,6 +451,22 @@ void bmesh_radial_loop_remove(BMLoop *l, BMEdge *e) l->e = NULL; } +/** + * A version of #bmesh_radial_loop_remove which only performs the radial unlink, + * leaving the edge untouched. + */ +void bmesh_radial_loop_unlink(BMLoop *l) +{ + if (l->radial_next != l) { + l->radial_next->radial_prev = l->radial_prev; + l->radial_prev->radial_next = l->radial_next; + } + + /* l is no longer in a radial cycle; empty the links + * to the cycle and the link back to an edge */ + l->radial_next = l->radial_prev = NULL; + l->e = NULL; +} /** * \brief BME RADIAL FIND FIRST FACE VERT @@ -484,30 +523,6 @@ int bmesh_radial_length(const BMLoop *l) return i; } -void bmesh_radial_append(BMEdge *e, BMLoop *l) -{ - if (e->l == NULL) { - e->l = l; - l->radial_next = l->radial_prev = l; - } - else { - l->radial_prev = e->l; - l->radial_next = e->l->radial_next; - - e->l->radial_next->radial_prev = l; - e->l->radial_next = l; - - e->l = l; - } - - if (UNLIKELY(l->e && l->e != e)) { - /* l is already in a radial cycle for a different edge */ - BMESH_ASSERT(0); - } - - l->e = e; -} - /** * \brief RADIAL COUNT FACE VERT * diff --git a/source/blender/bmesh/intern/bmesh_structure.h b/source/blender/bmesh/intern/bmesh_structure.h index 07f94796bb2..679e7a269b3 100644 --- a/source/blender/bmesh/intern/bmesh_structure.h +++ b/source/blender/bmesh/intern/bmesh_structure.h @@ -55,8 +55,9 @@ BMEdge *bmesh_disk_faceedge_find_first(const BMEdge *e, const BMVert *v) ATTR_WA BMEdge *bmesh_disk_faceedge_find_next(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); /* RADIAL CYCLE MANAGMENT */ -void bmesh_radial_append(BMEdge *e, BMLoop *l) ATTR_NONNULL(); -void bmesh_radial_loop_remove(BMLoop *l, BMEdge *e) ATTR_NONNULL(1); +void bmesh_radial_loop_append(BMEdge *e, BMLoop *l) ATTR_NONNULL(); +void bmesh_radial_loop_remove(BMEdge *e, BMLoop *l) ATTR_NONNULL(); +void bmesh_radial_loop_unlink(BMLoop *l) ATTR_NONNULL(); /* note: * bmesh_radial_loop_next(BMLoop *l) / prev. * just use member access l->radial_next, l->radial_prev now */ diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cpp b/source/blender/compositor/operations/COM_SunBeamsOperation.cpp index a681583809c..70e0b2cfb57 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.cpp +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cpp @@ -186,8 +186,8 @@ struct BufferLineAccumulator { } /* TODO implement proper filtering here, see - * http://en.wikipedia.org/wiki/Lanczos_resampling - * http://en.wikipedia.org/wiki/Sinc_function + * https://en.wikipedia.org/wiki/Lanczos_resampling + * https://en.wikipedia.org/wiki/Sinc_function * * using lanczos with x = distance from the line segment, * normalized to a == 0.5f, could give a good result diff --git a/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc b/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc index 76cd81f1b8f..da71db09f3d 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc @@ -47,7 +47,7 @@ namespace DEG { /* -------------------------------------------------- */ /* Performs a transitive reduction to remove redundant relations. - * http://en.wikipedia.org/wiki/Transitive_reduction + * https://en.wikipedia.org/wiki/Transitive_reduction * * XXX The current implementation is somewhat naive and has O(V*E) worst case * runtime. diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index b7b62bd59f9..4f27dab258d 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -235,6 +235,9 @@ void DEG_id_tag_update_ex(Main *bmain, ID *id, short flag) if (flag & (OB_RECALC_OB | OB_RECALC_DATA)) { DEG_graph_id_tag_update(bmain, graph, id); } + else if (flag & OB_RECALC_TIME) { + DEG_graph_id_tag_update(bmain, graph, id); + } } } diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index cc45cbd82af..c23bfb1ff60 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -40,6 +40,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" #include "BLI_rand.h" +#include "BLI_math_geom.h" #include "BLT_translation.h" @@ -160,6 +161,7 @@ typedef struct tGPsdata { bGPDpalettecolor *palettecolor; /* current palette color */ bGPDbrush *brush; /* current drawing brush */ short straight[2]; /* 1: line horizontal, 2: line vertical, other: not defined, second element position */ + int lock_axis; /* lock drawing to one axis */ } tGPsdata; /* ------ */ @@ -278,6 +280,64 @@ static bool gp_stroke_filtermval(tGPsdata *p, const int mval[2], int pmval[2]) return false; } +/* reproject the points of the stroke to a plane locked to axis to avoid stroke offset */ +static void gp_project_points_to_plane(RegionView3D *rv3d, bGPDstroke *gps, const float origin[3], const int axis) +{ + float plane_normal[3]; + float vn[3]; + + float ray[3]; + float rpoint[3]; + + /* normal vector for a plane locked to axis */ + zero_v3(plane_normal); + plane_normal[axis] = 1.0f; + + /* Reproject the points in the plane */ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + + /* get a vector from the point with the current view direction of the viewport */ + ED_view3d_global_to_vector(rv3d, &pt->x, vn); + + /* calculate line extrem point to create a ray that cross the plane */ + mul_v3_fl(vn, -50.0f); + add_v3_v3v3(ray, &pt->x, vn); + + /* if the line never intersect, the point is not changed */ + if (isect_line_plane_v3(rpoint, &pt->x, ray, origin, plane_normal)) { + copy_v3_v3(&pt->x, rpoint); + } + } +} + +/* reproject stroke to plane locked to axis in 3d cursor location */ +static void gp_reproject_toplane(tGPsdata *p, bGPDstroke *gps) +{ + bGPdata *gpd = p->gpd; + float origin[3]; + float cursor[3]; + RegionView3D *rv3d = p->ar->regiondata; + + /* verify the stroke mode is CURSOR 3d space mode */ + if ((gpd->sbuffer_sflag & GP_STROKE_3DSPACE) == 0) { + return; + } + if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { + return; + } + if ((*p->align_flag & GP_PROJECT_DEPTH_VIEW) || (*p->align_flag & GP_PROJECT_DEPTH_STROKE)) { + return; + } + + /* get 3d cursor and set origin for locked axis only. Uses axis-1 because the enum for XYZ start with 1 */ + gp_get_3d_reference(p, cursor); + zero_v3(origin); + origin[p->lock_axis - 1] = cursor[p->lock_axis - 1]; + + gp_project_points_to_plane(rv3d, gps, origin, p->lock_axis - 1); +} + /* convert screen-coordinates to buffer-coordinates */ /* XXX this method needs a total overhaul! */ static void gp_stroke_convertcoords(tGPsdata *p, const int mval[2], float out[3], float *depth) @@ -581,6 +641,10 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL); + /* if axis locked, reproject to plane locked (only in 3d space) */ + if (p->lock_axis > GP_LOCKAXIS_NONE) { + gp_reproject_toplane(p, gps); + } /* if parented change position relative to parent object */ if (gpl->parent != NULL) { gp_apply_parent_point(gpl, pts); @@ -679,7 +743,6 @@ static void gp_stroke_simplify(tGPsdata *p) MEM_freeN(old_points); } - /* make a new stroke from the buffer data */ static void gp_stroke_newfrombuffer(tGPsdata *p) { @@ -756,6 +819,10 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + /* if axis locked, reproject to plane locked (only in 3d space) */ + if (p->lock_axis > GP_LOCKAXIS_NONE) { + gp_reproject_toplane(p, gps); + } /* if parented change position relative to parent object */ if (gpl->parent != NULL) { gp_apply_parent_point(gpl, pt); @@ -775,6 +842,10 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + /* if axis locked, reproject to plane locked (only in 3d space) */ + if (p->lock_axis > GP_LOCKAXIS_NONE) { + gp_reproject_toplane(p, gps); + } /* if parented change position relative to parent object */ if (gpl->parent != NULL) { gp_apply_parent_point(gpl, pt); @@ -793,6 +864,10 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + /* if axis locked, reproject to plane locked (only in 3d space) */ + if (p->lock_axis > GP_LOCKAXIS_NONE) { + gp_reproject_toplane(p, gps); + } /* if parented change position relative to parent object */ if (gpl->parent != NULL) { gp_apply_parent_point(gpl, pt); @@ -805,30 +880,30 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) } else { float *depth_arr = NULL; - + /* get an array of depths, far depths are blended */ if (gpencil_project_check(p)) { - int mval[2], mval_prev[2] = {0}; + int mval[2], mval_prev[2] = { 0 }; int interp_depth = 0; int found_depth = 0; - + depth_arr = MEM_mallocN(sizeof(float) * gpd->sbuffer_size, "depth_points"); - + for (i = 0, ptc = gpd->sbuffer; i < gpd->sbuffer_size; i++, ptc++, pt++) { copy_v2_v2_int(mval, &ptc->x); - + if ((ED_view3d_autodist_depth(p->ar, mval, depth_margin, depth_arr + i) == 0) && - (i && (ED_view3d_autodist_depth_seg(p->ar, mval, mval_prev, depth_margin + 1, depth_arr + i) == 0))) + (i && (ED_view3d_autodist_depth_seg(p->ar, mval, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { interp_depth = true; } else { found_depth = true; } - + copy_v2_v2_int(mval_prev, mval); } - + if (found_depth == false) { /* eeh... not much we can do.. :/, ignore depth in this case, use the 3D cursor */ for (i = gpd->sbuffer_size - 1; i >= 0; i--) @@ -839,54 +914,54 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* remove all info between the valid endpoints */ int first_valid = 0; int last_valid = 0; - + for (i = 0; i < gpd->sbuffer_size; i++) { if (depth_arr[i] != FLT_MAX) break; } first_valid = i; - + for (i = gpd->sbuffer_size - 1; i >= 0; i--) { if (depth_arr[i] != FLT_MAX) break; } last_valid = i; - + /* invalidate non-endpoints, so only blend between first and last */ for (i = first_valid + 1; i < last_valid; i++) depth_arr[i] = FLT_MAX; - + interp_depth = true; } - + if (interp_depth) { interp_sparse_array(depth_arr, gpd->sbuffer_size, FLT_MAX); } } } - - + + pt = gps->points; - + /* convert all points (normal behavior) */ for (i = 0, ptc = gpd->sbuffer; i < gpd->sbuffer_size && ptc; i++, ptc++, pt++) { /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr + i : NULL); - + /* copy pressure and time */ pt->pressure = ptc->pressure; pt->strength = ptc->strength; CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; } - + /* subdivide the stroke */ if (sublevel > 0) { int totpoints = gps->totpoints; for (i = 0; i < sublevel; i++) { /* we're adding one new point between each pair of verts on each step */ totpoints += totpoints - 1; - + gp_subdivide_stroke(gps, totpoints); } } @@ -895,8 +970,8 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) gp_randomize_stroke(gps, brush); } - /* smooth stroke after subdiv - only if there's something to do - * for each iteration, the factor is reduced to get a better smoothing without changing too much + /* smooth stroke after subdiv - only if there's something to do + * for each iteration, the factor is reduced to get a better smoothing without changing too much * the original stroke */ if (brush->draw_smoothfac > 0.0f) { @@ -909,6 +984,11 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) reduce += 0.25f; // reduce the factor } } + + /* if axis locked, reproject to plane locked (only in 3d space) */ + if (p->lock_axis > GP_LOCKAXIS_NONE) { + gp_reproject_toplane(p, gps); + } /* if parented change position relative to parent object */ if (gpl->parent != NULL) { gp_apply_parent(gpl, gps); @@ -1467,6 +1547,8 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) bGPdata *pdata = p->gpd; copy_v4_v4(pdata->scolor, palcolor->color); pdata->sflag = palcolor->flag; + /* lock axis */ + p->lock_axis = ts->gp_sculpt.lock_axis; return 1; } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 863f5e3852c..0555529599c 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -6617,15 +6617,22 @@ static void popup_add_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2)) void ui_panel_menu(bContext *C, ARegion *ar, Panel *pa) { bScreen *sc = CTX_wm_screen(C); + const bool has_panel_category = UI_panel_category_is_visible(ar); + const bool any_item_visible = has_panel_category; PointerRNA ptr; uiPopupMenu *pup; uiLayout *layout; + if (!any_item_visible) { + return; + } + RNA_pointer_create(&sc->id, &RNA_Panel, pa, &ptr); pup = UI_popup_menu_begin(C, IFACE_("Panel"), ICON_NONE); layout = UI_popup_menu_layout(pup); - if (UI_panel_category_is_visible(ar)) { + + if (has_panel_category) { char tmpstr[80]; BLI_snprintf(tmpstr, sizeof(tmpstr), "%s" UI_SEP_CHAR_S "%s", IFACE_("Pin"), IFACE_("Shift+Left Mouse")); uiItemR(layout, &ptr, "use_pin", 0, tmpstr, ICON_NONE); @@ -6636,7 +6643,6 @@ void ui_panel_menu(bContext *C, ARegion *ar, Panel *pa) uiBut *but = block->buttons.last; but->flag |= UI_BUT_HAS_SEP_CHAR; } - } UI_popup_menu_end(C, pup); } diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 2dacbba92ad..20ceb0937d6 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1089,7 +1089,7 @@ void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool bi if (prv) { const int size = big ? ICON_SIZE_PREVIEW : ICON_SIZE_ICON; - if (id || prv->use_deferred) { + if (id || (prv->tag & PRV_TAG_DEFFERED) != 0) { ui_id_preview_image_render_size(C, NULL, id, prv, size, true); } } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 3077464b771..e7ec3235803 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -3886,6 +3886,8 @@ void uiTemplateCacheFile(uiLayout *layout, bContext *C, PointerRNA *ptr, const c return; } + SpaceButs *sbuts = CTX_wm_space_buts(C); + uiLayout *row = uiLayoutRow(layout, false); uiBlock *block = uiLayoutGetBlock(row); uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("File Path:"), 0, 19, 145, 19, NULL, 0, 0, 0, 0, ""); @@ -3911,6 +3913,7 @@ void uiTemplateCacheFile(uiLayout *layout, bContext *C, PointerRNA *ptr, const c uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE, 0); row = uiLayoutRow(layout, false); + uiLayoutSetEnabled(row, (sbuts->mainb == BCONTEXT_CONSTRAINT)); uiItemR(row, &fileptr, "scale", 0, "Scale", ICON_NONE); /* TODO: unused for now, so no need to expose. */ diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index 233caf61ae9..fd9e705eee5 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -68,6 +68,8 @@ static int wm_alembic_export_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + RNA_boolean_set(op->ptr, "init_scene_frame_range", true); + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { Main *bmain = CTX_data_main(C); char filepath[FILE_MAX]; @@ -231,11 +233,22 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(row, imfptr, "ngon_method", 0, NULL, ICON_NONE); } -static void wm_alembic_export_draw(bContext *UNUSED(C), wmOperator *op) +static void wm_alembic_export_draw(bContext *C, wmOperator *op) { PointerRNA ptr; RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + + /* Conveniently set start and end frame to match the scene's frame range. */ + Scene *scene = CTX_data_scene(C); + + if (scene != NULL && RNA_boolean_get(&ptr, "init_scene_frame_range")) { + RNA_int_set(&ptr, "start", SFRA); + RNA_int_set(&ptr, "end", EFRA); + + RNA_boolean_set(&ptr, "init_scene_frame_range", false); + } + ui_alembic_export_settings(op->layout, &ptr); } @@ -334,6 +347,11 @@ void WM_OT_alembic_export(wmOperatorType *ot) RNA_def_enum(ot->srna, "ngon_method", rna_enum_modifier_triangulate_quad_method_items, MOD_TRIANGULATE_NGON_BEAUTY, "Polygon Method", "Method for splitting the polygons into triangles"); + + /* This dummy prop is used to check whether we need to init the start and + * end frame values to that of the scene's, otherwise they are reset at + * every change, draw update. */ + RNA_def_boolean(ot->srna, "init_scene_frame_range", false, "", ""); } /* ************************************************************************** */ diff --git a/source/blender/editors/io/io_cache.c b/source/blender/editors/io/io_cache.c index c5eea94f5e1..ebe8898571d 100644 --- a/source/blender/editors/io/io_cache.c +++ b/source/blender/editors/io/io_cache.c @@ -59,7 +59,9 @@ static int cachefile_open_invoke(bContext *C, wmOperator *op, const wmEvent *eve { if (!RNA_struct_property_is_set(op->ptr, "filepath")) { char filepath[FILE_MAX]; - BLI_strncpy(filepath, G.main->name, sizeof(filepath)); + Main *bmain = CTX_data_main(C); + + BLI_strncpy(filepath, bmain->name, sizeof(filepath)); BLI_replace_extension(filepath, sizeof(filepath), ".abc"); RNA_string_set(op->ptr, "filepath", filepath); } diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 5d5731a7e16..a6de1b284b7 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -32,6 +32,7 @@ #include "MEM_guardedalloc.h" #include "BLI_bitmap.h" +#include "BLI_bitmap_draw_2d.h" #include "BLI_listbase.h" #include "BLI_linklist.h" #include "BLI_linklist_stack.h" @@ -294,7 +295,7 @@ bool EDBM_backbuf_border_mask_init(ViewContext *vc, const int mcords[][2], short lasso_mask_data.px = dr_mask; lasso_mask_data.width = (xmax - xmin) + 1; - fill_poly_v2i_n( + BLI_bitmap_draw_2d_poly_v2i_n( xmin, ymin, xmax + 1, ymax + 1, mcords, tot, edbm_mask_lasso_px_cb, &lasso_mask_data); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 79544f80e95..16ee6f4ed89 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1575,7 +1575,7 @@ static int make_links_data_exec(bContext *C, wmOperator *op) Material *ma = give_current_material(ob_src, a + 1); assign_material(ob_dst, ma, a + 1, BKE_MAT_ASSIGN_USERPREF); /* also works with ma==NULL */ } - DAG_id_tag_update(&ob_dst->id, 0); + DAG_id_tag_update(&ob_dst->id, OB_RECALC_DATA); break; case MAKE_LINKS_ANIMDATA: BKE_animdata_copy_id((ID *)ob_dst, (ID *)ob_src, false); @@ -2235,7 +2235,7 @@ static int make_local_exec(bContext *C, wmOperator *op) "Orphan library objects added to the current scene to avoid loss"); } - BKE_library_make_local(bmain, NULL, false, false); /* NULL is all libs */ + BKE_library_make_local(bmain, NULL, NULL, false, false); /* NULL is all libs */ WM_event_add_notifier(C, NC_WINDOW, NULL); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index bd016b7fcfb..82da6f58912 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -2604,6 +2604,8 @@ static int vertex_group_remove_exec(bContext *C, wmOperator *op) if (RNA_boolean_get(op->ptr, "all")) BKE_object_defgroup_remove_all(ob); + else if (RNA_boolean_get(op->ptr, "all_unlocked")) + BKE_object_defgroup_remove_all_ex(ob, true); else vgroup_delete_active(ob); @@ -2633,6 +2635,7 @@ void OBJECT_OT_vertex_group_remove(wmOperatorType *ot) /* properties */ RNA_def_boolean(ot->srna, "all", 0, "All", "Remove all vertex groups"); + RNA_def_boolean(ot->srna, "all_unlocked", 0, "All Unlocked", "Remove all unlocked vertex groups"); } static int vertex_group_assign_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index ddbf59b2cf7..87c08dc6583 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -1080,13 +1080,19 @@ static void icon_preview_add_size(IconPreview *ip, unsigned int *rect, int sizex static void icon_preview_startjob_all_sizes(void *customdata, short *stop, short *do_update, float *progress) { IconPreview *ip = (IconPreview *)customdata; - IconPreviewSize *cur_size = ip->sizes.first; + IconPreviewSize *cur_size; const bool use_new_shading = BKE_scene_use_new_shading_nodes(ip->scene); - while (cur_size) { + for (cur_size = ip->sizes.first; cur_size; cur_size = cur_size->next) { PreviewImage *prv = ip->owner; + + if (prv->tag & PRV_TAG_DEFFERED_DELETE) { + /* Non-thread-protected reading is not an issue here. */ + continue; + } + ShaderPreview *sp = MEM_callocN(sizeof(ShaderPreview), "Icon ShaderPreview"); - const bool is_render = !prv->use_deferred; + const bool is_render = !(prv->tag & PRV_TAG_DEFFERED); /* construct shader preview from image size and previewcustomdata */ sp->scene = ip->scene; @@ -1117,8 +1123,6 @@ static void icon_preview_startjob_all_sizes(void *customdata, short *stop, short common_preview_startjob(sp, stop, do_update, progress); shader_preview_free(sp); - - cur_size = cur_size->next; } } @@ -1147,6 +1151,15 @@ static void icon_preview_endjob(void *customdata) } #endif } + + if (ip->owner) { + PreviewImage *prv_img = ip->owner; + prv_img->tag &= ~PRV_TAG_DEFFERED_RENDERING; + if (prv_img->tag & PRV_TAG_DEFFERED_DELETE) { + BLI_assert(prv_img->tag & PRV_TAG_DEFFERED); + BKE_previewimg_cached_release_pointer(prv_img); + } + } } static void icon_preview_free(void *customdata) @@ -1205,6 +1218,14 @@ void ED_preview_icon_job(const bContext *C, void *owner, ID *id, unsigned int *r icon_preview_add_size(ip, rect, sizex, sizey); + /* Special threading hack: warn main code that this preview is being rendered and cannot be freed... */ + { + PreviewImage *prv_img = owner; + if (prv_img->tag & PRV_TAG_DEFFERED) { + prv_img->tag |= PRV_TAG_DEFFERED_RENDERING; + } + } + /* setup job */ WM_jobs_customdata_set(wm_job, ip, icon_preview_free); WM_jobs_timer(wm_job, 0.1, NC_WINDOW, NC_WINDOW); diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 677a6472c72..5cd0d33c365 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -2076,7 +2076,13 @@ void ED_screen_animation_timer(bContext *C, int redraws, int refresh, int sync, 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)); + + char spacetype = -1; + + if (sa) + spacetype = sa->spacetype; + + sad->from_anim_edit = (ELEM(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 a6b6ccd5a66..860a865466a 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -4197,7 +4197,7 @@ static int space_context_cycle_poll(bContext *C) /** * Helper to get the correct RNA pointer/property pair for changing - * the display context of active space type in \sa. + * the display context of active space type in \a sa. */ static void context_cycle_prop_get( bScreen *screen, const ScrArea *sa, @@ -4214,6 +4214,9 @@ static void context_cycle_prop_get( RNA_pointer_create(NULL, &RNA_UserPreferences, &U, r_ptr); propname = "active_section"; break; + default: + BLI_assert(0); + propname = ""; } *r_prop = RNA_struct_find_property(r_ptr, propname); diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index a47b9a0b936..a4887c579ac 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -37,6 +37,7 @@ #include "BIF_glutil.h" +#include "BLI_bitmap_draw_2d.h" #include "BLI_math_matrix.h" #include "BLI_math_geom.h" #include "BLI_utildefines.h" @@ -439,7 +440,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) data.width = data.rect.xmax - data.rect.xmin; data.px = BLI_BITMAP_NEW(data.width * (data.rect.ymax - data.rect.ymin), __func__); - fill_poly_v2i_n( + BLI_bitmap_draw_2d_poly_v2i_n( data.rect.xmin, data.rect.ymin, data.rect.xmax, data.rect.ymax, mcords, mcords_tot, mask_lasso_px_cb, &data); diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index ea40d4eb5e1..dd282c427f6 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -7915,10 +7915,6 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short if (!render_override && sds->draw_velocity) { draw_smoke_velocity(sds, viewnormal); } - -#ifdef SMOKE_DEBUG_HEAT - draw_smoke_heat(smd->domain, ob); -#endif } } diff --git a/source/blender/editors/space_view3d/drawvolume.c b/source/blender/editors/space_view3d/drawvolume.c index b0e21601b9c..27ecbf83db5 100644 --- a/source/blender/editors/space_view3d/drawvolume.c +++ b/source/blender/editors/space_view3d/drawvolume.c @@ -1,4 +1,4 @@ -/* +/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or @@ -41,6 +41,7 @@ #include "BLI_math.h" #include "BKE_DerivedMesh.h" +#include "BKE_texture.h" #include "BKE_particle.h" #include "smoke_API.h" @@ -62,28 +63,33 @@ struct GPUTexture; # include "PIL_time_utildefines.h" #endif -static GPUTexture *create_flame_spectrum_texture(void) +/* *************************** Transfer functions *************************** */ + +enum { + TFUNC_FLAME_SPECTRUM = 0, + TFUNC_COLOR_RAMP = 1, +}; + +#define TFUNC_WIDTH 256 + +static void create_flame_spectrum_texture(float *data) { -#define SPEC_WIDTH 256 #define FIRE_THRESH 7 #define MAX_FIRE_ALPHA 0.06f #define FULL_ON_FIRE 100 - GPUTexture *tex; - int i, j, k; - float *spec_data = MEM_mallocN(SPEC_WIDTH * 4 * sizeof(float), "spec_data"); - float *spec_pixels = MEM_mallocN(SPEC_WIDTH * 4 * 16 * 16 * sizeof(float), "spec_pixels"); + float *spec_pixels = MEM_mallocN(TFUNC_WIDTH * 4 * 16 * 16 * sizeof(float), "spec_pixels"); - blackbody_temperature_to_rgb_table(spec_data, SPEC_WIDTH, 1500, 3000); + blackbody_temperature_to_rgb_table(data, TFUNC_WIDTH, 1500, 3000); - for (i = 0; i < 16; i++) { - for (j = 0; j < 16; j++) { - for (k = 0; k < SPEC_WIDTH; k++) { - int index = (j * SPEC_WIDTH * 16 + i * SPEC_WIDTH + k) * 4; + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 16; j++) { + for (int k = 0; k < TFUNC_WIDTH; k++) { + int index = (j * TFUNC_WIDTH * 16 + i * TFUNC_WIDTH + k) * 4; if (k >= FIRE_THRESH) { - spec_pixels[index] = (spec_data[k * 4]); - spec_pixels[index + 1] = (spec_data[k * 4 + 1]); - spec_pixels[index + 2] = (spec_data[k * 4 + 2]); + spec_pixels[index] = (data[k * 4]); + spec_pixels[index + 1] = (data[k * 4 + 1]); + spec_pixels[index + 2] = (data[k * 4 + 2]); spec_pixels[index + 3] = MAX_FIRE_ALPHA * ( (k > FULL_ON_FIRE) ? 1.0f : (k - FIRE_THRESH) / ((float)FULL_ON_FIRE - FIRE_THRESH)); } @@ -94,19 +100,69 @@ static GPUTexture *create_flame_spectrum_texture(void) } } - tex = GPU_texture_create_1D(SPEC_WIDTH, spec_pixels, NULL); + memcpy(data, spec_pixels, sizeof(float) * 4 * TFUNC_WIDTH); - MEM_freeN(spec_data); MEM_freeN(spec_pixels); -#undef SPEC_WIDTH #undef FIRE_THRESH #undef MAX_FIRE_ALPHA #undef FULL_ON_FIRE +} + +static void create_color_ramp(const ColorBand *coba, float *data) +{ + for (int i = 0; i < TFUNC_WIDTH; i++) { + do_colorband(coba, (float)i / TFUNC_WIDTH, &data[i * 4]); + } +} + +static GPUTexture *create_transfer_function(int type, const ColorBand *coba) +{ + float *data = MEM_mallocN(sizeof(float) * 4 * TFUNC_WIDTH, __func__); + + switch (type) { + case TFUNC_FLAME_SPECTRUM: + create_flame_spectrum_texture(data); + break; + case TFUNC_COLOR_RAMP: + create_color_ramp(coba, data); + break; + } + + GPUTexture *tex = GPU_texture_create_1D(TFUNC_WIDTH, data, NULL); + + MEM_freeN(data); return tex; } +static GPUTexture *create_field_texture(SmokeDomainSettings *sds) +{ + float *field = NULL; + + switch (sds->coba_field) { +#ifdef WITH_SMOKE + case FLUID_FIELD_DENSITY: field = smoke_get_density(sds->fluid); break; + case FLUID_FIELD_HEAT: field = smoke_get_heat(sds->fluid); break; + case FLUID_FIELD_FUEL: field = smoke_get_fuel(sds->fluid); break; + case FLUID_FIELD_REACT: field = smoke_get_react(sds->fluid); break; + case FLUID_FIELD_FLAME: field = smoke_get_flame(sds->fluid); break; + case FLUID_FIELD_VELOCITY_X: field = smoke_get_velocity_x(sds->fluid); break; + case FLUID_FIELD_VELOCITY_Y: field = smoke_get_velocity_y(sds->fluid); break; + case FLUID_FIELD_VELOCITY_Z: field = smoke_get_velocity_z(sds->fluid); break; + case FLUID_FIELD_COLOR_R: field = smoke_get_color_r(sds->fluid); break; + case FLUID_FIELD_COLOR_G: field = smoke_get_color_g(sds->fluid); break; + case FLUID_FIELD_COLOR_B: field = smoke_get_color_b(sds->fluid); break; + case FLUID_FIELD_FORCE_X: field = smoke_get_force_x(sds->fluid); break; + case FLUID_FIELD_FORCE_Y: field = smoke_get_force_y(sds->fluid); break; + case FLUID_FIELD_FORCE_Z: field = smoke_get_force_z(sds->fluid); break; +#endif + default: return NULL; + } + + return GPU_texture_create_3D(sds->res[0], sds->res[1], sds->res[2], 1, field); +} + typedef struct VolumeSlicer { float size[3]; float min[3]; @@ -347,6 +403,7 @@ static int create_view_aligned_slices(VolumeSlicer *slicer, } static void bind_shader(SmokeDomainSettings *sds, GPUShader *shader, GPUTexture *tex_spec, + GPUTexture *tex_tfunc, GPUTexture *tex_coba, bool use_fire, const float min[3], const float ob_sizei[3], const float invsize[3]) { @@ -359,6 +416,8 @@ static void bind_shader(SmokeDomainSettings *sds, GPUShader *shader, GPUTexture int densityscale_location; int spec_location, flame_location; int shadow_location, actcol_location; + int tfunc_location = 0; + int coba_location = 0; if (use_fire) { spec_location = GPU_shader_get_uniform(shader, "spectrum_texture"); @@ -370,6 +429,11 @@ static void bind_shader(SmokeDomainSettings *sds, GPUShader *shader, GPUTexture soot_location = GPU_shader_get_uniform(shader, "soot_texture"); stepsize_location = GPU_shader_get_uniform(shader, "step_size"); densityscale_location = GPU_shader_get_uniform(shader, "density_scale"); + + if (sds->use_coba) { + tfunc_location = GPU_shader_get_uniform(shader, "transfer_texture"); + coba_location = GPU_shader_get_uniform(shader, "color_band_texture"); + } } GPU_shader_bind(shader); @@ -397,6 +461,14 @@ static void bind_shader(SmokeDomainSettings *sds, GPUShader *shader, GPUTexture if ((sds->active_fields & SM_ACTIVE_COLORS) == 0) mul_v3_v3(active_color, sds->active_color); GPU_shader_uniform_vector(shader, actcol_location, 3, 1, active_color); + + if (sds->use_coba) { + GPU_texture_bind(tex_tfunc, 4); + GPU_shader_uniform_texture(shader, tfunc_location, tex_tfunc); + + GPU_texture_bind(tex_coba, 5); + GPU_shader_uniform_texture(shader, coba_location, tex_coba); + } } GPU_shader_uniform_vector(shader, min_location, 3, 1, min); @@ -404,7 +476,8 @@ static void bind_shader(SmokeDomainSettings *sds, GPUShader *shader, GPUTexture GPU_shader_uniform_vector(shader, invsize_location, 3, 1, invsize); } -static void unbind_shader(SmokeDomainSettings *sds, GPUTexture *tex_spec, bool use_fire) +static void unbind_shader(SmokeDomainSettings *sds, GPUTexture *tex_spec, + GPUTexture *tex_tfunc, GPUTexture *tex_coba, bool use_fire) { GPU_shader_unbind(); @@ -417,20 +490,30 @@ static void unbind_shader(SmokeDomainSettings *sds, GPUTexture *tex_spec, bool u } else { GPU_texture_unbind(sds->tex_shadow); + + if (sds->use_coba) { + GPU_texture_unbind(tex_tfunc); + GPU_texture_free(tex_tfunc); + + GPU_texture_unbind(tex_coba); + GPU_texture_free(tex_coba); + } } } static void draw_buffer(SmokeDomainSettings *sds, GPUShader *shader, const VolumeSlicer *slicer, const float ob_sizei[3], const float invsize[3], const int num_points, const bool do_fire) { - GPUTexture *tex_spec = (do_fire) ? create_flame_spectrum_texture() : NULL; + GPUTexture *tex_spec = (do_fire) ? create_transfer_function(TFUNC_FLAME_SPECTRUM, NULL) : NULL; + GPUTexture *tex_tfunc = (sds->use_coba) ? create_transfer_function(TFUNC_COLOR_RAMP, sds->coba) : NULL; + GPUTexture *tex_coba = (sds->use_coba) ? create_field_texture(sds) : NULL; GLuint vertex_buffer; glGenBuffers(1, &vertex_buffer); glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * num_points, &slicer->verts[0][0], GL_STATIC_DRAW); - bind_shader(sds, shader, tex_spec, do_fire, slicer->min, ob_sizei, invsize); + bind_shader(sds, shader, tex_spec, tex_tfunc, tex_coba, do_fire, slicer->min, ob_sizei, invsize); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, NULL); @@ -439,7 +522,7 @@ static void draw_buffer(SmokeDomainSettings *sds, GPUShader *shader, const Volum glDisableClientState(GL_VERTEX_ARRAY); - unbind_shader(sds, tex_spec, do_fire); + unbind_shader(sds, tex_spec, tex_tfunc, tex_coba, do_fire); /* cleanup */ @@ -459,7 +542,16 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, const bool use_fire = (sds->active_fields & SM_ACTIVE_FIRE) && sds->tex_flame; - GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_SMOKE); + GPUBuiltinShader builtin_shader; + + if (sds->use_coba) { + builtin_shader = GPU_SHADER_SMOKE_COBA; + } + else { + builtin_shader = GPU_SHADER_SMOKE; + } + + GPUShader *shader = GPU_shader_get_builtin_shader(builtin_shader); if (!shader) { fprintf(stderr, "Unable to create GLSL smoke shader.\n"); @@ -549,7 +641,7 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, draw_buffer(sds, shader, &slicer, ob_sizei, invsize, num_points, false); /* Draw fire separately (T47639). */ - if (use_fire) { + if (use_fire && !sds->use_coba) { glBlendFunc(GL_ONE, GL_ONE); draw_buffer(sds, fire_shader, &slicer, ob_sizei, invsize, num_points, true); } @@ -759,50 +851,3 @@ void draw_smoke_velocity(SmokeDomainSettings *domain, float viewnormal[3]) UNUSED_VARS(domain, viewnormal); #endif } - -#ifdef SMOKE_DEBUG_HEAT -void draw_smoke_heat(SmokeDomainSettings *domain, Object *ob) -{ - float x, y, z; - float x0, y0, z0; - int *base_res = domain->base_res; - int *res = domain->res; - int *res_min = domain->res_min; - int *res_max = domain->res_max; - float *heat = smoke_get_heat(domain->fluid); - - float min[3]; - float *cell_size = domain->cell_size; - float step_size = ((float)max_iii(base_res[0], base_res[1], base_res[2])) / 16.f; - float vf = domain->scale / 16.f * 2.f; /* velocity factor */ - - /* set first position so that it doesn't jump when domain moves */ - x0 = res_min[0] + fmod(-(float)domain->shift[0] + res_min[0], step_size); - y0 = res_min[1] + fmod(-(float)domain->shift[1] + res_min[1], step_size); - z0 = res_min[2] + fmod(-(float)domain->shift[2] + res_min[2], step_size); - if (x0 < res_min[0]) x0 += step_size; - if (y0 < res_min[1]) y0 += step_size; - if (z0 < res_min[2]) z0 += step_size; - add_v3_v3v3(min, domain->p0, domain->obj_shift_f); - - for (x = floor(x0); x < res_max[0]; x += step_size) - for (y = floor(y0); y < res_max[1]; y += step_size) - for (z = floor(z0); z < res_max[2]; z += step_size) { - int index = (floor(x) - res_min[0]) + (floor(y) - res_min[1]) * res[0] + (floor(z) - res_min[2]) * res[0] * res[1]; - - float pos[3] = {min[0] + ((float)x + 0.5f) * cell_size[0], min[1] + ((float)y + 0.5f) * cell_size[1], min[2] + ((float)z + 0.5f) * cell_size[2]}; - - /* draw heat as different sized points */ - if (heat[index] >= 0.01f) { - float col_gb = 1.0f - heat[index]; - CLAMP(col_gb, 0.0f, 1.0f); - glColor3f(1.0f, col_gb, col_gb); - glPointSize(24.0f * heat[index]); - - glBegin(GL_POINTS); - glVertex3f(pos[0], pos[1], pos[2]); - glEnd(); - } - } -} -#endif diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 9b8ca2d26da..9e41ad6a8f6 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -41,6 +41,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_bitmap_draw_2d.h" #include "BLI_blenlib.h" #include "BLI_kdopbvh.h" #include "BLI_math.h" @@ -5052,7 +5053,7 @@ bool ED_view3d_autodist_depth_seg(ARegion *ar, const int mval_sta[2], const int copy_v2_v2_int(p1, mval_sta); copy_v2_v2_int(p2, mval_end); - plot_line_v2v2i(p1, p2, depth_segment_cb, &data); + BLI_bitmap_draw_2d_line_v2v2i(p1, p2, depth_segment_cb, &data); *depth = data.depth; diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index 0e2cb95dd89..b11f42bcfef 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -296,14 +296,8 @@ void draw_smoke_volume(struct SmokeDomainSettings *sds, struct Object *ob, const float min[3], const float max[3], const float viewnormal[3]); -//#define SMOKE_DEBUG_HEAT - void draw_smoke_velocity(struct SmokeDomainSettings *domain, float viewnormal[3]); -#ifdef SMOKE_DEBUG_HEAT -void draw_smoke_heat(struct SmokeDomainSettings *domain, struct Object *ob); -#endif - /* workaround for trivial but noticeable camera bug caused by imprecision * between view border calculation in 2D/3D space, workaround for bug [#28037]. * without this define we get the old behavior which is to try and align them diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 762329ee077..5b94db6e120 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -89,6 +89,7 @@ typedef enum GPUBuiltinShader { GPU_SHADER_SEP_GAUSSIAN_BLUR = 1, GPU_SHADER_SMOKE = 2, GPU_SHADER_SMOKE_FIRE = 3, + GPU_SHADER_SMOKE_COBA = 4, } GPUBuiltinShader; GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader); diff --git a/source/blender/gpu/intern/gpu_shader.c b/source/blender/gpu/intern/gpu_shader.c index 5cfb323bc4b..14f2764b009 100644 --- a/source/blender/gpu/intern/gpu_shader.c +++ b/source/blender/gpu/intern/gpu_shader.c @@ -68,6 +68,7 @@ static struct GPUShadersGlobal { GPUShader *sep_gaussian_blur; GPUShader *smoke; GPUShader *smoke_fire; + GPUShader *smoke_coba; /* cache for shader fx. Those can exist in combinations so store them here */ GPUShader *fx_shaders[MAX_FX_SHADERS * 2]; } shaders; @@ -623,6 +624,13 @@ GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader) NULL, NULL, NULL, 0, 0, 0); retval = GG.shaders.smoke_fire; break; + case GPU_SHADER_SMOKE_COBA: + if (!GG.shaders.smoke_coba) + GG.shaders.smoke_coba = GPU_shader_create( + datatoc_gpu_shader_smoke_vert_glsl, datatoc_gpu_shader_smoke_frag_glsl, + NULL, NULL, "#define USE_COBA;\n", 0, 0, 0); + retval = GG.shaders.smoke_coba; + break; } if (retval == NULL) @@ -734,6 +742,11 @@ void GPU_shader_free_builtin_shaders(void) GG.shaders.smoke_fire = NULL; } + if (GG.shaders.smoke_coba) { + GPU_shader_free(GG.shaders.smoke_coba); + GG.shaders.smoke_coba = NULL; + } + for (i = 0; i < 2 * MAX_FX_SHADERS; ++i) { if (GG.shaders.fx_shaders[i]) { GPU_shader_free(GG.shaders.fx_shaders[i]); diff --git a/source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl b/source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl index fd790009e02..6ded453225e 100644 --- a/source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl @@ -8,10 +8,17 @@ uniform float density_scale; uniform sampler3D soot_texture; uniform sampler3D shadow_texture; +#ifdef USE_COBA +uniform sampler1D transfer_texture; +uniform sampler3D color_band_texture; +#endif + void main() { /* compute color and density from volume texture */ vec4 soot = texture3D(soot_texture, coords); + +#ifndef USE_COBA vec3 soot_color; if (soot.a != 0) { soot_color = active_color * soot.rgb / soot.a; @@ -31,6 +38,11 @@ void main() /* premultiply alpha */ vec4 color = vec4(soot_alpha * soot_color, soot_alpha); +#else + float color_band = texture3D(color_band_texture, coords).r; + vec4 transfer_function = texture1D(transfer_texture, color_band); + vec4 color = transfer_function * density_scale; +#endif gl_FragColor = color; } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 9f7b420cdcd..944fa2e750c 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -172,6 +172,13 @@ enum ePreviewImage_Flag { PRV_USER_EDITED = (1 << 1), /* if user-edited, do not auto-update this anymore! */ }; +/* for PreviewImage->tag */ +enum { + PRV_TAG_DEFFERED = (1 << 0), /* Actual loading of preview is deffered. */ + PRV_TAG_DEFFERED_RENDERING = (1 << 1), /* Deffered preview is being loaded. */ + PRV_TAG_DEFFERED_DELETE = (1 << 2), /* Deffered preview should be deleted asap. */ +}; + typedef struct PreviewImage { /* All values of 2 are really NUM_ICON_SIZES */ unsigned int w[2]; @@ -192,12 +199,11 @@ typedef struct PreviewImage { /* Number of frames stored in each rect (0 means 'old format', rect is only w * h integers). */ unsigned short num_frames; - char pad; - char use_deferred; /* for now a mere bool, if we add more deferred loading methods we can switch to bitflag. */ + short tag; /* Runtime data. */ } PreviewImage; #define PRV_DEFERRED_DATA(prv) \ - (CHECK_TYPE_INLINE(prv, PreviewImage *), BLI_assert((prv)->use_deferred), (void *)((prv) + 1)) + (CHECK_TYPE_INLINE(prv, PreviewImage *), BLI_assert((prv)->tag & PRV_TAG_DEFFERED), (void *)((prv) + 1)) /** * Defines for working with IDs. diff --git a/source/blender/makesdna/DNA_cachefile_types.h b/source/blender/makesdna/DNA_cachefile_types.h index dd47d63fc19..46b1adf2725 100644 --- a/source/blender/makesdna/DNA_cachefile_types.h +++ b/source/blender/makesdna/DNA_cachefile_types.h @@ -36,10 +36,10 @@ extern "C" { #endif - /* CacheFile::flag */ enum { CACHEFILE_DS_EXPAND = (1 << 0), + CACHEFILE_DIRTY = (1 << 1), }; /* CacheFile::draw_flag */ diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index fc4e7de73f5..ca774864e95 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -461,6 +461,7 @@ typedef struct bObjectSolverConstraint { /* Transform matrix cache constraint */ typedef struct bTransformCacheConstraint { struct CacheFile *cache_file; + struct CacheReader *reader; char object_path[1024]; /* FILE_MAX */ } bTransformCacheConstraint; diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 9187b76f012..f95533a88f9 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -383,7 +383,7 @@ typedef struct DisplaceModifierData { int direction; char defgrp_name[64]; /* MAX_VGROUP_NAME */ float midlevel; - int pad; + int space; } DisplaceModifierData; /* DisplaceModifierData->direction */ @@ -404,6 +404,12 @@ enum { MOD_DISP_MAP_UV = 3, }; +/* DisplaceModifierData->space */ +enum { + MOD_DISP_SPACE_LOCAL = 0, + MOD_DISP_SPACE_GLOBAL = 1, +}; + typedef struct UVProjectModifierData { ModifierData modifier; @@ -1549,6 +1555,7 @@ typedef struct MeshSeqCacheModifierData { ModifierData modifier; struct CacheFile *cache_file; + struct CacheReader *reader; char object_path[1024]; /* 1024 = FILE_MAX */ char read_flag; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 94f23197293..9b0781ebe70 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1160,6 +1160,14 @@ typedef enum eGP_EditBrush_Types { TOT_GP_EDITBRUSH_TYPES } eGP_EditBrush_Types; +/* Lock axis options */ +typedef enum eGP_Lockaxis_Types { + GP_LOCKAXIS_NONE = 0, + GP_LOCKAXIS_X = 1, + GP_LOCKAXIS_Y = 2, + GP_LOCKAXIS_Z = 3 +} eGP_Lockaxis_Types; + /* Settings for a GPencil Stroke Sculpting Brush */ typedef struct GP_EditBrush_Data { short size; /* radius of brush */ @@ -1190,7 +1198,7 @@ typedef struct GP_BrushEdit_Settings { int brushtype; /* eGP_EditBrush_Types */ int flag; /* eGP_BrushEdit_SettingsFlag */ - char pad[4]; + int lock_axis; /* lock drawing to one axis */ float alpha; /* alpha factor for selection color */ } GP_BrushEdit_Settings; diff --git a/source/blender/makesdna/DNA_smoke_types.h b/source/blender/makesdna/DNA_smoke_types.h index ba7f73c2f63..c95e0a1f54a 100644 --- a/source/blender/makesdna/DNA_smoke_types.h +++ b/source/blender/makesdna/DNA_smoke_types.h @@ -77,6 +77,23 @@ enum { VECTOR_DRAW_STREAMLINE = 1, }; +enum { + FLUID_FIELD_DENSITY = 0, + FLUID_FIELD_HEAT = 1, + FLUID_FIELD_FUEL = 2, + FLUID_FIELD_REACT = 3, + FLUID_FIELD_FLAME = 4, + FLUID_FIELD_VELOCITY_X = 5, + FLUID_FIELD_VELOCITY_Y = 6, + FLUID_FIELD_VELOCITY_Z = 7, + FLUID_FIELD_COLOR_R = 8, + FLUID_FIELD_COLOR_G = 9, + FLUID_FIELD_COLOR_B = 10, + FLUID_FIELD_FORCE_X = 11, + FLUID_FIELD_FORCE_Y = 12, + FLUID_FIELD_FORCE_Z = 13, +}; + /* cache compression */ #define SM_CACHE_LIGHT 0 #define SM_CACHE_HEAVY 1 @@ -193,9 +210,13 @@ typedef struct SmokeDomainSettings { float slice_per_voxel; float slice_depth; float display_thickness; + + struct ColorBand *coba; float vector_scale; char vector_draw_type; - char pad2[3]; + char use_coba; + char coba_field; /* simulation field used for the color mapping */ + char pad2; } SmokeDomainSettings; diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c index 7249ebd5feb..09fdeb15b10 100644 --- a/source/blender/makesrna/intern/rna_cachefile.c +++ b/source/blender/makesrna/intern/rna_cachefile.c @@ -37,6 +37,8 @@ #include "BKE_cachefile.h" #include "BKE_depsgraph.h" +#include "BLI_string.h" + #include "DEG_depsgraph.h" #include "WM_api.h" @@ -60,6 +62,12 @@ static void rna_CacheFile_update_handle(Main *bmain, Scene *scene, PointerRNA *p { CacheFile *cache_file = ptr->data; + if ((cache_file->flag & CACHEFILE_DIRTY) != 0) { + BKE_cachefile_clean(scene, cache_file); + BLI_freelistN(&cache_file->object_paths); + cache_file->flag &= ~CACHEFILE_DIRTY; + } + BKE_cachefile_reload(bmain, cache_file); rna_CacheFile_update(bmain, scene, ptr); @@ -71,6 +79,20 @@ static void rna_CacheFile_object_paths_begin(CollectionPropertyIterator *iter, P rna_iterator_listbase_begin(iter, &cache_file->object_paths, NULL); } +static void rna_CacheFile_filename_set(PointerRNA *ptr, const char *value) +{ + CacheFile *cache_file = ptr->data; + + if (STREQ(cache_file->filepath, value)) { + return; + } + + /* Different file is opened, close all readers. */ + cache_file->flag |= CACHEFILE_DIRTY; + + BLI_strncpy(cache_file->filepath, value, sizeof(cache_file->filepath)); +} + #else /* cachefile.object_paths */ @@ -103,6 +125,7 @@ static void rna_def_cachefile(BlenderRNA *brna) RNA_def_struct_ui_icon(srna, ICON_FILE); PropertyRNA *prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_CacheFile_filename_set"); RNA_def_property_ui_text(prop, "File Path", "Path to external displacements file"); RNA_def_property_update(prop, 0, "rna_CacheFile_update_handle"); diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index db3f76f3cfc..de1a0f24c31 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -148,12 +148,17 @@ static EnumPropertyItem space_object_items[] = { {0, NULL, 0, NULL, NULL} }; +#include "DNA_cachefile_types.h" + #include "BKE_animsys.h" #include "BKE_action.h" #include "BKE_constraint.h" #include "BKE_context.h" #include "BKE_depsgraph.h" +#ifdef WITH_ALEMBIC +# include "ABC_alembic.h" +#endif static StructRNA *rna_ConstraintType_refine(struct PointerRNA *ptr) { @@ -471,6 +476,22 @@ static void rna_Constraint_objectSolver_camera_set(PointerRNA *ptr, PointerRNA v } } +static void rna_Constraint_transformCache_object_path_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ +#ifdef WITH_ALEMBIC + bConstraint *con = (bConstraint *)ptr->data; + bTransformCacheConstraint *data = (bTransformCacheConstraint *)con->data; + Object *ob = (Object *)ptr->id.data; + + data->reader = CacheReader_open_alembic_object(data->cache_file->handle, + data->reader, + ob, + data->object_path); +#endif + + rna_Constraint_update(bmain, scene, ptr); +} + #else static EnumPropertyItem constraint_distance_items[] = { @@ -2593,7 +2614,7 @@ static void rna_def_constraint_transform_cache(BlenderRNA *brna) prop = RNA_def_property(srna, "object_path", PROP_STRING, PROP_NONE); RNA_def_property_ui_text(prop, "Object Path", "Path to the object in the Alembic archive used to lookup the transform matrix"); - RNA_def_property_update(prop, 0, "rna_Constraint_update"); + RNA_def_property_update(prop, 0, "rna_Constraint_transformCache_object_path_update"); } /* base struct for constraints */ diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 34ca6a12fc3..c4f0db38a16 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -1131,6 +1131,21 @@ static int rna_CorrectiveSmoothModifier_is_bind_get(PointerRNA *ptr) return (csmd->bind_coords != NULL); } +static void rna_MeshSequenceCache_object_path_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ +#ifdef WITH_ALEMBIC + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)ptr->data; + Object *ob = (Object *)ptr->id.data; + + mcmd->reader = CacheReader_open_alembic_object(mcmd->cache_file->handle, + mcmd->reader, + ob, + mcmd->object_path); +#endif + + rna_Modifier_update(bmain, scene, ptr); +} + #else static PropertyRNA *rna_def_property_subdivision_common(StructRNA *srna, const char type[]) @@ -2100,6 +2115,12 @@ static void rna_def_modifier_displace(BlenderRNA *brna) {0, NULL, 0, NULL, NULL} }; + static EnumPropertyItem prop_space_items[] = { + {MOD_DISP_SPACE_LOCAL, "LOCAL", 0, "Local", "Direction is defined in local coordinates"}, + {MOD_DISP_SPACE_GLOBAL, "GLOBAL", 0, "Global", "Direction is defined in global coordinates"}, + {0, NULL, 0, NULL, NULL} + }; + srna = RNA_def_struct(brna, "DisplaceModifier", "Modifier"); RNA_def_struct_ui_text(srna, "Displace Modifier", "Displacement modifier"); RNA_def_struct_sdna(srna, "DisplaceModifierData"); @@ -2130,6 +2151,11 @@ static void rna_def_modifier_displace(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Direction", ""); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "space", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_space_items); + RNA_def_property_ui_text(prop, "Space", ""); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + rna_def_modifier_generic_map_info(srna); } @@ -4246,7 +4272,7 @@ static void rna_def_modifier_meshseqcache(BlenderRNA *brna) prop = RNA_def_property(srna, "object_path", PROP_STRING, PROP_NONE); RNA_def_property_ui_text(prop, "Object Path", "Path to the object in the Alembic archive used to lookup geometric data"); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); + RNA_def_property_update(prop, 0, "rna_MeshSequenceCache_object_path_update"); static EnumPropertyItem read_flag_items[] = { {MOD_MESHSEQ_READ_VERT, "VERT", 0, "Vertex", ""}, diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 7e1d0164eb4..e169ef0d822 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -75,6 +75,14 @@ EnumPropertyItem rna_enum_gpencil_sculpt_brush_items[] = { { 0, NULL, 0, NULL, NULL } }; +EnumPropertyItem rna_enum_gpencil_lockaxis_items[] = { + { GP_LOCKAXIS_NONE, "GP_LOCKAXIS_NONE", 0, "None", "" }, + { GP_LOCKAXIS_X, "GP_LOCKAXIS_X", 0, "X", "Project strokes to plane locked to X" }, + { GP_LOCKAXIS_Y, "GP_LOCKAXIS_Y", 0, "Y", "Project strokes to plane locked to Y" }, + { GP_LOCKAXIS_Z, "GP_LOCKAXIS_Z", 0, "Z", "Project strokes to plane locked to Z" }, + { 0, NULL, 0, NULL, NULL } +}; + EnumPropertyItem rna_enum_symmetrize_direction_items[] = { {BMO_SYMMETRIZE_NEGATIVE_X, "NEGATIVE_X", 0, "-X to +X", ""}, {BMO_SYMMETRIZE_POSITIVE_X, "POSITIVE_X", 0, "+X to -X", ""}, @@ -1053,6 +1061,13 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Alpha", "Alpha value for selected vertices"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_GPencil_update"); + /* lock axis */ + prop = RNA_def_property(srna, "lockaxis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "lock_axis"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_lockaxis_items); + RNA_def_property_ui_text(prop, "Lock", ""); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + /* brush */ srna = RNA_def_struct(brna, "GPencilSculptBrush", NULL); RNA_def_struct_sdna(srna, "GP_EditBrush_Data"); diff --git a/source/blender/makesrna/intern/rna_smoke.c b/source/blender/makesrna/intern/rna_smoke.c index b4ba306df3f..6db370fc152 100644 --- a/source/blender/makesrna/intern/rna_smoke.c +++ b/source/blender/makesrna/intern/rna_smoke.c @@ -30,6 +30,7 @@ #include <limits.h> #include "RNA_define.h" +#include "RNA_enum_types.h" #include "rna_internal.h" @@ -53,6 +54,7 @@ #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_particle.h" +#include "BKE_texture.h" #include "smoke_API.h" @@ -383,6 +385,17 @@ static void rna_SmokeFlow_uvlayer_set(PointerRNA *ptr, const char *value) rna_object_uvlayer_name_set(ptr, value, flow->uvlayer_name, sizeof(flow->uvlayer_name)); } +static void rna_Smoke_use_color_ramp_set(PointerRNA *ptr, int value) +{ + SmokeDomainSettings *sds = (SmokeDomainSettings *)ptr->data; + + sds->use_coba = value; + + if (value && sds->coba == NULL) { + sds->coba = add_colorband(false); + } +} + #else static void rna_def_smoke_domain_settings(BlenderRNA *brna) @@ -805,6 +818,41 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna) RNA_def_property_ui_range(prop, 0.0, 100.0, 0.1, 3); RNA_def_property_ui_text(prop, "Scale", "Multiplier for scaling the vectors"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + + /* --------- Color mapping. --------- */ + + prop = RNA_def_property(srna, "use_color_ramp", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_coba", 0); + RNA_def_property_boolean_funcs(prop, NULL, "rna_Smoke_use_color_ramp_set"); + RNA_def_property_ui_text(prop, "Use Color Ramp", + "Render a simulation field while mapping its voxels values to the colors of a ramp"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + + static EnumPropertyItem coba_field_items[] = { + {FLUID_FIELD_COLOR_R, "COLOR_R", 0, "Red", "Red component of the color field"}, + {FLUID_FIELD_COLOR_G, "COLOR_G", 0, "Green", "Green component of the color field"}, + {FLUID_FIELD_COLOR_B, "COLOR_B", 0, "Blue", "Blue component of the color field"}, + {FLUID_FIELD_DENSITY, "DENSITY", 0, "Density", "Quantity of soot in the fluid"}, + {FLUID_FIELD_FLAME, "FLAME", 0, "Flame", "Flame field"}, + {FLUID_FIELD_FUEL, "FUEL", 0, "Fuel", "Fuel field"}, + {FLUID_FIELD_HEAT, "HEAT", 0, "Heat", "Temperature of the fluid"}, + {FLUID_FIELD_VELOCITY_X, "VELOCITY_X", 0, "X Velocity", "X component of the velocity field"}, + {FLUID_FIELD_VELOCITY_Y, "VELOCITY_Y", 0, "Y Velocity", "Y component of the velocity field"}, + {FLUID_FIELD_VELOCITY_Z, "VELOCITY_Z", 0, "Z Velocity", "Z component of the velocity field"}, + {0, NULL, 0, NULL, NULL} + }; + + prop = RNA_def_property(srna, "coba_field", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "coba_field"); + RNA_def_property_enum_items(prop, coba_field_items); + RNA_def_property_ui_text(prop, "Field", "Simulation field to color map"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + + prop = RNA_def_property(srna, "color_ramp", PROP_POINTER, PROP_NEVER_NULL); + RNA_def_property_pointer_sdna(prop, NULL, "coba"); + RNA_def_property_struct_type(prop, "ColorRamp"); + RNA_def_property_ui_text(prop, "Color Ramp", ""); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); } static void rna_def_smoke_flow_settings(BlenderRNA *brna) diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index 9ba2d214d50..059e096ddb4 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -46,6 +46,7 @@ #include "BKE_modifier.h" #include "BKE_texture.h" #include "BKE_deform.h" +#include "BKE_object.h" #include "depsgraph_private.h" #include "MEM_guardedalloc.h" @@ -65,6 +66,7 @@ static void initData(ModifierData *md) dmd->strength = 1; dmd->direction = MOD_DISP_DIR_NOR; dmd->midlevel = 0.5; + dmd->space = MOD_DISP_SPACE_LOCAL; } static void copyData(ModifierData *md, ModifierData *target) @@ -171,10 +173,13 @@ static void updateDepgraph(ModifierData *md, DagForest *forest, } - if (dmd->texmapping == MOD_DISP_MAP_GLOBAL) + if (dmd->texmapping == MOD_DISP_MAP_GLOBAL || + (ELEM(dmd->direction, MOD_DISP_DIR_X, MOD_DISP_DIR_Y, MOD_DISP_DIR_Z, MOD_DISP_DIR_RGB_XYZ) && + dmd->space == MOD_DISP_SPACE_GLOBAL)) + { dag_add_relation(forest, obNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Displace Modifier"); - + } } static void updateDepsgraph(ModifierData *md, @@ -187,7 +192,10 @@ static void updateDepsgraph(ModifierData *md, if (dmd->map_object != NULL && dmd->texmapping == MOD_DISP_MAP_OBJECT) { DEG_add_object_relation(node, dmd->map_object, DEG_OB_COMP_TRANSFORM, "Displace Modifier"); } - if (dmd->texmapping == MOD_DISP_MAP_GLOBAL) { + if (dmd->texmapping == MOD_DISP_MAP_GLOBAL || + (ELEM(dmd->direction, MOD_DISP_DIR_X, MOD_DISP_DIR_Y, MOD_DISP_DIR_Z, MOD_DISP_DIR_RGB_XYZ) && + dmd->space == MOD_DISP_SPACE_GLOBAL)) + { DEG_add_object_relation(node, ob, DEG_OB_COMP_TRANSFORM, "Displace Modifier"); } } @@ -206,6 +214,8 @@ static void displaceModifier_do( float weight = 1.0f; /* init value unused but some compilers may complain */ const float delta_fixed = 1.0f - dmd->midlevel; /* when no texture is used, we fallback to white */ float (*vert_clnors)[3] = NULL; + float local_mat[4][4]; + const bool use_global_direction = dmd->space == MOD_DISP_SPACE_GLOBAL; if (!dmd->texture && dmd->direction == MOD_DISP_DIR_RGB_XYZ) return; if (dmd->strength == 0.0f) return; @@ -243,11 +253,17 @@ static void displaceModifier_do( direction = MOD_DISP_DIR_NOR; } } + else if (ELEM(direction, MOD_DISP_DIR_X, MOD_DISP_DIR_Y, MOD_DISP_DIR_Z, MOD_DISP_DIR_RGB_XYZ) && + use_global_direction) + { + copy_m4_m4(local_mat, ob->obmat); + } for (i = 0; i < numVerts; i++) { TexResult texres; float strength = dmd->strength; float delta; + float local_vec[3]; if (dvert) { weight = defvert_find_weight(dvert + i, defgrp_index); @@ -270,18 +286,44 @@ static void displaceModifier_do( switch (direction) { case MOD_DISP_DIR_X: - vertexCos[i][0] += delta; + if (use_global_direction) { + vertexCos[i][0] += delta * local_mat[0][0]; + vertexCos[i][1] += delta * local_mat[1][0]; + vertexCos[i][2] += delta * local_mat[2][0]; + } + else { + vertexCos[i][0] += delta; + } break; case MOD_DISP_DIR_Y: - vertexCos[i][1] += delta; + if (use_global_direction) { + vertexCos[i][0] += delta * local_mat[0][1]; + vertexCos[i][1] += delta * local_mat[1][1]; + vertexCos[i][2] += delta * local_mat[2][1]; + } + else { + vertexCos[i][1] += delta; + } break; case MOD_DISP_DIR_Z: - vertexCos[i][2] += delta; + if (use_global_direction) { + vertexCos[i][0] += delta * local_mat[0][2]; + vertexCos[i][1] += delta * local_mat[1][2]; + vertexCos[i][2] += delta * local_mat[2][2]; + } + else { + vertexCos[i][2] += delta; + } break; case MOD_DISP_DIR_RGB_XYZ: - vertexCos[i][0] += (texres.tr - dmd->midlevel) * strength; - vertexCos[i][1] += (texres.tg - dmd->midlevel) * strength; - vertexCos[i][2] += (texres.tb - dmd->midlevel) * strength; + local_vec[0] = texres.tr - dmd->midlevel; + local_vec[1] = texres.tg - dmd->midlevel; + local_vec[2] = texres.tb - dmd->midlevel; + if (use_global_direction) { + mul_transposed_mat3_m4_v3(local_mat, local_vec); + } + mul_v3_fl(local_vec, strength); + add_v3_v3(vertexCos[i], local_vec); break; case MOD_DISP_DIR_NOR: vertexCos[i][0] += delta * (mvert[i].no[0] / 32767.0f); diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c index 355ac9563dd..d25e8e38de3 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -65,6 +65,7 @@ static void copyData(ModifierData *md, ModifierData *target) if (tmcmd->cache_file) { id_us_plus(&tmcmd->cache_file->id); + tmcmd->reader = NULL; } } @@ -75,6 +76,12 @@ static void freeData(ModifierData *md) if (mcmd->cache_file) { id_us_min(&mcmd->cache_file->id); } + + if (mcmd->reader) { +#ifdef WITH_ALEMBIC + CacheReader_free(mcmd->reader); +#endif + } } static bool isDisabled(ModifierData *md, int UNUSED(useRenderParams)) @@ -102,10 +109,16 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, BKE_cachefile_ensure_handle(G.main, cache_file); - DerivedMesh *result = ABC_read_mesh(cache_file->handle, + if (!mcmd->reader) { + mcmd->reader = CacheReader_open_alembic_object(cache_file->handle, + mcmd->reader, + ob, + mcmd->object_path); + } + + DerivedMesh *result = ABC_read_mesh(mcmd->reader, ob, dm, - mcmd->object_path, time, &err_str, mcmd->read_flag); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_brick.c b/source/blender/nodes/shader/nodes/node_shader_tex_brick.c index bb7f2166a8a..0be47c4f751 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_brick.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_brick.c @@ -36,6 +36,7 @@ static bNodeSocketTemplate sh_node_tex_brick_in[] = { { SOCK_RGBA, 1, N_("Mortar"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, { SOCK_FLOAT, 1, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, { SOCK_FLOAT, 1, N_("Mortar Size"), 0.02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.125f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, + { SOCK_FLOAT, 1, N_("Mortar Smooth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, { SOCK_FLOAT, 1, N_("Bias"), 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, { SOCK_FLOAT, 1, N_("Brick Width"), 0.5f, 0.0f, 0.0f, 0.0f, 0.01f, 100.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, { SOCK_FLOAT, 1, N_("Row Height"), 0.25f, 0.0f, 0.0f, 0.0f, 0.01f, 100.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, @@ -60,6 +61,12 @@ static void node_shader_init_tex_brick(bNodeTree *UNUSED(ntree), bNode *node) tex->squash_freq = 2; node->storage = tex; + + for (bNodeSocket *sock = node->inputs.first; sock; sock = sock->next) { + if (STREQ(sock->name, "Mortar Smooth")) { + ((bNodeSocketValueFloat*)sock->default_value)->value = 0.1f; + } + } } static int node_shader_gpu_tex_brick(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) diff --git a/source/blender/physics/intern/hair_volume.cpp b/source/blender/physics/intern/hair_volume.cpp index d79cf7d8c31..5cc1231e6cb 100644 --- a/source/blender/physics/intern/hair_volume.cpp +++ b/source/blender/physics/intern/hair_volume.cpp @@ -710,7 +710,7 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target * div(grad(p)) = div(v) * * The finite difference approximation yields the linear equation system described here: - * http://en.wikipedia.org/wiki/Discrete_Poisson_equation + * https://en.wikipedia.org/wiki/Discrete_Poisson_equation */ lMatrix A(num_cellsA, num_cellsA); /* Reserve space for the base equation system (without boundary conditions). @@ -888,7 +888,7 @@ bool BPH_hair_volume_solve_divergence(HairGrid *grid, float /*dt*/, float target #if 0 /* XXX weighting is incorrect, disabled for now */ /* Velocity filter kernel - * See http://en.wikipedia.org/wiki/Filter_%28large_eddy_simulation%29 + * See https://en.wikipedia.org/wiki/Filter_%28large_eddy_simulation%29 */ BLI_INLINE void hair_volume_filter_box_convolute(HairVertexGrid *grid, float invD, const int kernel_size[3], int i, int j, int k) diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c index ec69abbb1df..15f3c665fcf 100644 --- a/source/blender/python/intern/bpy_library_load.c +++ b/source/blender/python/intern/bpy_library_load.c @@ -33,6 +33,7 @@ #include <stddef.h> #include "BLI_utildefines.h" +#include "BLI_ghash.h" #include "BLI_string.h" #include "BLI_linklist.h" #include "BLI_path_util.h" @@ -405,6 +406,8 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) BLO_blendhandle_close(self->blo_handle); self->blo_handle = NULL; + GHash *old_to_new_ids = BLI_ghash_ptr_new(__func__); + /* copied from wm_operator.c */ { /* mark all library linked objects to be updated */ @@ -412,7 +415,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) /* append, rather than linking */ if ((self->flag & FILE_LINK) == 0) { - BKE_library_make_local(bmain, lib, true, false); + BKE_library_make_local(bmain, lib, old_to_new_ids, true, false); } } @@ -439,6 +442,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) ID *id; id = PyCapsule_GetPointer(item, NULL); + id = BLI_ghash_lookup_default(old_to_new_ids, id, id); Py_DECREF(item); RNA_id_pointer_create(id, &id_ptr); @@ -452,6 +456,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) } #endif /* USE_RNA_DATABLOCKS */ + BLI_ghash_free(old_to_new_ids, NULL, NULL); Py_RETURN_NONE; } } diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 71288871104..4e980e4c0e6 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -1394,7 +1394,7 @@ PyDoc_STRVAR(Matrix_invert_doc, " (instead of raising a :exc:`ValueError` exception).\n" " :type fallback: :class:`Matrix`\n" "\n" -" .. seealso:: <http://en.wikipedia.org/wiki/Inverse_matrix>\n" +" .. seealso:: <https://en.wikipedia.org/wiki/Inverse_matrix>\n" ); static PyObject *Matrix_invert(MatrixObject *self, PyObject *args) { @@ -1505,7 +1505,7 @@ PyDoc_STRVAR(Matrix_invert_safe_doc, " If degenerated (e.g. zero scale on an axis), add some epsilon to its diagonal, to get an invertible one.\n" " If tweaked matrix is still degenerated, set to the identity matrix instead.\n" "\n" -" .. seealso:: <http://en.wikipedia.org/wiki/Inverse_matrix>\n" +" .. seealso:: <https://en.wikipedia.org/wiki/Inverse_matrix>\n" ); static PyObject *Matrix_invert_safe(MatrixObject *self) { @@ -1556,7 +1556,7 @@ PyDoc_STRVAR(Matrix_adjugate_doc, "\n" " .. note:: When the matrix cant be adjugated a :exc:`ValueError` exception is raised.\n" "\n" -" .. seealso:: <http://en.wikipedia.org/wiki/Adjugate_matrix>\n" +" .. seealso:: <https://en.wikipedia.org/wiki/Adjugate_matrix>\n" ); static PyObject *Matrix_adjugate(MatrixObject *self) { @@ -1733,7 +1733,7 @@ PyDoc_STRVAR(Matrix_determinant_doc, " :return: Return the determinant of a matrix.\n" " :rtype: float\n" "\n" -" .. seealso:: <http://en.wikipedia.org/wiki/Determinant>\n" +" .. seealso:: <https://en.wikipedia.org/wiki/Determinant>\n" ); static PyObject *Matrix_determinant(MatrixObject *self) { @@ -1755,7 +1755,7 @@ PyDoc_STRVAR(Matrix_transpose_doc, "\n" " Set the matrix to its transpose.\n" "\n" -" .. seealso:: <http://en.wikipedia.org/wiki/Transpose>\n" +" .. seealso:: <https://en.wikipedia.org/wiki/Transpose>\n" ); static PyObject *Matrix_transpose(MatrixObject *self) { @@ -1890,7 +1890,7 @@ PyDoc_STRVAR(Matrix_identity_doc, " .. note:: An object with zero location and rotation, a scale of one,\n" " will have an identity matrix.\n" "\n" -" .. seealso:: <http://en.wikipedia.org/wiki/Identity_matrix>\n" +" .. seealso:: <https://en.wikipedia.org/wiki/Identity_matrix>\n" ); static PyObject *Matrix_identity(MatrixObject *self) { diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index e872ec1a524..3b733f9558c 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -419,7 +419,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) const bool use_recursive = RNA_boolean_get(op->ptr, "use_recursive"); if (use_recursive) { - BKE_library_make_local(bmain, NULL, true, set_fake); + BKE_library_make_local(bmain, NULL, NULL, true, set_fake); } else { LinkNode *itemlink; @@ -430,7 +430,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) ID *new_id = ((WMLinkAppendDataItem *)(itemlink->link))->new_id; if (new_id && !BLI_gset_haskey(done_libraries, new_id->lib)) { - BKE_library_make_local(bmain, new_id->lib, true, set_fake); + BKE_library_make_local(bmain, new_id->lib, NULL, true, set_fake); BLI_gset_insert(done_libraries, new_id->lib); } } diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c index 1357729e898..46203333eb5 100644 --- a/source/blender/windowmanager/intern/wm_gesture.c +++ b/source/blender/windowmanager/intern/wm_gesture.c @@ -37,6 +37,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_bitmap_draw_2d.h" #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -272,7 +273,7 @@ static void draw_filled_lasso(wmWindow *win, wmGesture *gt) unsigned char *pixel_buf = MEM_callocN(sizeof(*pixel_buf) * w * h, __func__); struct LassoFillData lasso_fill_data = {pixel_buf, w}; - fill_poly_v2i_n( + BLI_bitmap_draw_2d_poly_v2i_n( rect.xmin, rect.ymin, rect.xmax, rect.ymax, (const int (*)[2])moves, tot, draw_filled_lasso_px_cb, &lasso_fill_data); |