diff options
86 files changed, 3620 insertions, 4784 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f140d537d1..aba98f6b342 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,6 +156,7 @@ if(UNIX AND NOT APPLE) option(WITH_INSTALL_PORTABLE "Install redistributeable runtime, otherwise install into CMAKE_INSTALL_PREFIX" ON) endif() option(WITH_PYTHON_INSTALL "Copy system python into the blender install folder" ON) +option(WITH_ONSURFACEBRUSH "Enable use of the 'on-surface brush' for paint/sculpt. Requires a stencil buffer, GL_depth_texture, and GLSL" ON) # disable for now, but plan to support on all platforms eventually option(WITH_MEM_JEMALLOC "Enable malloc replacement (http://www.canonware.com/jemalloc)" OFF) @@ -440,6 +441,9 @@ if(UNIX AND NOT APPLE) # GNU Compiler if(CMAKE_COMPILER_IS_GNUCC) set(PLATFORM_CFLAGS "-pipe -fPIC -funsigned-char -fno-strict-aliasing") + if(WITH_ONSURFACEBRUSH) + add_defintion(-DWITH_ONSURFACEBRUSH) + endif(WITH_ONSURFACEBRUSH) # Intel C++ Compiler elseif(CMAKE_C_COMPILER_ID MATCHES "Intel") # think these next two are broken @@ -534,6 +538,10 @@ elseif(WIN32) add_definitions(-D__SSE__ -D__MMX__) endif() + if(WITH_ONSURFACEBRUSH) + add_defintion(-DWITH_ONSURFACEBRUSH) + endif(WITH_ONSURFACEBRUSH) + if(MSVC) if(CMAKE_CL_64) set(LLIBS ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 advapi32 shfolder shell32 ole32 oleaut32 uuid ) diff --git a/build_files/scons/config/aix4-config.py b/build_files/scons/config/aix4-config.py index e8004338271..bb1a0f3d3ac 100644 --- a/build_files/scons/config/aix4-config.py +++ b/build_files/scons/config/aix4-config.py @@ -149,6 +149,9 @@ BF_REDCODE_LIB = '' BF_REDCODE_INC = '${BF_REDCODE}/include' BF_REDCODE_LIBPATH='${BF_REDCODE}/lib' +#On-Surface Brush +WITH_BF_ONSURFACEBRUSH = True + # Mesa Libs should go here if your using them as well.... WITH_BF_STATICOPENGL = 'false' BF_OPENGL = '/usr' diff --git a/build_files/scons/config/darwin-config.py b/build_files/scons/config/darwin-config.py index 0c51476a6d0..03cae53f5ab 100644 --- a/build_files/scons/config/darwin-config.py +++ b/build_files/scons/config/darwin-config.py @@ -265,6 +265,8 @@ if MACOSX_ARCHITECTURE == 'i386': elif MACOSX_ARCHITECTURE == 'x86_64': BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse','-msse2'] +#On-Surface Brush +WITH_BF_ONSURFACEBRUSH = True ############################################################################# ################### various compile settings and flags ################## diff --git a/build_files/scons/config/freebsd7-config.py b/build_files/scons/config/freebsd7-config.py index a73574abf67..ab8b32d22f7 100644 --- a/build_files/scons/config/freebsd7-config.py +++ b/build_files/scons/config/freebsd7-config.py @@ -174,6 +174,9 @@ WITH_BF_OPENMP = True WITH_BF_RAYOPTIMIZATION = True BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse','-pthread'] +#On-Surface Brush +WITH_BF_ONSURFACEBRUSH = True + CCFLAGS = ['-pipe','-fPIC','-funsigned-char','-fno-strict-aliasing','-D_LARGEFILE_SOURCE','-D_FILE_OFFSET_BITS=64','-D_LARGEFILE64_SOURCE'] CPPFLAGS = [] diff --git a/build_files/scons/config/freebsd8-config.py b/build_files/scons/config/freebsd8-config.py index dd7ab5f9ab9..169bbf1d9c4 100644 --- a/build_files/scons/config/freebsd8-config.py +++ b/build_files/scons/config/freebsd8-config.py @@ -174,6 +174,9 @@ WITH_BF_OPENMP = True WITH_BF_RAYOPTIMIZATION = True BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse','-pthread'] +#On-Surface Brush +WITH_BF_ONSURFACEBRUSH = True + CCFLAGS = ['-pipe','-fPIC','-funsigned-char','-fno-strict-aliasing','-D_LARGEFILE_SOURCE','-D_FILE_OFFSET_BITS=64','-D_LARGEFILE64_SOURCE'] CPPFLAGS = [] diff --git a/build_files/scons/config/freebsd9-config.py b/build_files/scons/config/freebsd9-config.py index 9c53d52ce71..f26c85e361c 100644 --- a/build_files/scons/config/freebsd9-config.py +++ b/build_files/scons/config/freebsd9-config.py @@ -174,6 +174,9 @@ WITH_BF_OPENMP = True WITH_BF_RAYOPTIMIZATION = True BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse','-pthread'] +#On-Surface Brush +WITH_BF_ONSURFACEBRUSH = True + CCFLAGS = ['-pipe','-fPIC','-funsigned-char','-fno-strict-aliasing','-D_LARGEFILE_SOURCE','-D_FILE_OFFSET_BITS=64','-D_LARGEFILE64_SOURCE'] CPPFLAGS = [] diff --git a/build_files/scons/config/irix6-config.py b/build_files/scons/config/irix6-config.py index 4ca7bcc7b64..fdc6b059c98 100644 --- a/build_files/scons/config/irix6-config.py +++ b/build_files/scons/config/irix6-config.py @@ -154,6 +154,8 @@ BF_OPENGL_LIB = 'GL GLU X11 Xi Xext' BF_OPENGL_LIBPATH = '/usr/X11R6/lib' BF_OPENGL_LIB_STATIC = '${BF_OPENGL}/libGL.a ${BF_OPENGL}/libGLU.a ${BF_OPENGL}/libXxf86vm.a ${BF_OPENGL}/libX11.a ${BF_OPENGL}/libXi.a ${BF_OPENGL}/libXext.a ${BF_OPENGL}/libXxf86vm.a' +#On-Surface Brush +WITH_BF_ONSURFACEBRUSH = True CC = 'c99' CXX = 'CC' diff --git a/build_files/scons/config/linux2-config.py b/build_files/scons/config/linux2-config.py index bc2917055fb..e217570e5dc 100644 --- a/build_files/scons/config/linux2-config.py +++ b/build_files/scons/config/linux2-config.py @@ -192,6 +192,9 @@ WITH_BF_OPENMP = True WITH_BF_RAYOPTIMIZATION = True BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse','-pthread'] +#On-Surface Brush +WITH_BF_ONSURFACEBRUSH = True + ## CC = 'gcc' CXX = 'g++' diff --git a/build_files/scons/config/linuxcross-config.py b/build_files/scons/config/linuxcross-config.py index 62474527825..75e3a9bd5dd 100644 --- a/build_files/scons/config/linuxcross-config.py +++ b/build_files/scons/config/linuxcross-config.py @@ -172,6 +172,9 @@ BF_OPENCOLLADA_LIBPATH = '${BF_OPENCOLLADA}/lib ${BF_ICONV_LIBPATH}' WITH_BF_RAYOPTIMIZATION = True BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse'] +#On-Surface Brush +WITH_BF_ONSURFACEBRUSH = True + CCFLAGS = [ '-pipe', '-funsigned-char', '-fno-strict-aliasing' ] CPPFLAGS = ['-DWIN32', '-DFREE_WINDOWS', '-D_LARGEFILE_SOURCE', '-D_FILE_OFFSET_BITS=64', '-D_LARGEFILE64_SOURCE'] diff --git a/build_files/scons/config/openbsd3-config.py b/build_files/scons/config/openbsd3-config.py index 67d850ff48d..700d45deb93 100644 --- a/build_files/scons/config/openbsd3-config.py +++ b/build_files/scons/config/openbsd3-config.py @@ -111,6 +111,9 @@ BF_OPENGL_LIB = 'GL GLU X11 Xi' BF_OPENGL_LIBPATH = '${BF_OPENGL}/lib' BF_OPENGL_LIB_STATIC = '${BF_OPENGL_LIBPATH}/libGL.a ${BF_OPENGL_LIBPATH}/libGLU.a ${BF_OPENGL_LIBPATH}/libXxf86vm.a ${BF_OPENGL_LIBPATH}/libX11.a ${BF_OPENGL_LIBPATH}/libXi.a ${BF_OPENGL_LIBPATH}/libXext.a ${BF_OPENGL_LIBPATH}/libXxf86vm.a' +#On-Surface Brush +WITH_BF_ONSURFACEBRUSH = True + ## ##CC = gcc ##CCC = g++ diff --git a/build_files/scons/config/sunos5-config.py b/build_files/scons/config/sunos5-config.py index 828e286efee..e9768f624e1 100644 --- a/build_files/scons/config/sunos5-config.py +++ b/build_files/scons/config/sunos5-config.py @@ -128,6 +128,9 @@ BF_OPENGL_LIB = 'GL GLU X11 Xi' BF_OPENGL_LIBPATH = '${BF_OPENGL}/lib' BF_OPENGL_LIB_STATIC = '${BF_OPENGL_LIBPATH}/libGL.a ${BF_OPENGL_LIBPATH}/libGLU.a ${BF_OPENGL_LIBPATH}/libXxf86vm.a ${BF_OPENGL_LIBPATH}/libX11.a ${BF_OPENGL_LIBPATH}/libXi.a ${BF_OPENGL_LIBPATH}/libXext.a ${BF_OPENGL_LIBPATH}/libXxf86vm.a' +#On-Surface Brush +WITH_BF_ONSURFACEBRUSH = True + ## CC = 'gcc' CXX = 'g++' diff --git a/build_files/scons/config/win32-mingw-config.py b/build_files/scons/config/win32-mingw-config.py index 6dac29b37f7..e3e4b371681 100644 --- a/build_files/scons/config/win32-mingw-config.py +++ b/build_files/scons/config/win32-mingw-config.py @@ -158,6 +158,9 @@ BF_OPENCOLLADA_LIBPATH = '${BF_OPENCOLLADA}/lib' WITH_BF_RAYOPTIMIZATION = False BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse'] +#On-Surface Brush +WITH_BF_ONSURFACEBRUSH = True + ## CC = 'gcc' CXX = 'g++' diff --git a/build_files/scons/config/win32-vc-config.py b/build_files/scons/config/win32-vc-config.py index 65076be85ed..a7e66ccb471 100644 --- a/build_files/scons/config/win32-vc-config.py +++ b/build_files/scons/config/win32-vc-config.py @@ -153,6 +153,9 @@ BF_OPENCOLLADA_LIBPATH = '${BF_OPENCOLLADA}/lib' WITH_BF_RAYOPTIMIZATION = True BF_RAYOPTIMIZATION_SSE_FLAGS = ['/arch:SSE'] +#On-Surface Brush +WITH_BF_ONSURFACEBRUSH = True + WITH_BF_STATICOPENGL = False BF_OPENGL_INC = '${BF_OPENGL}/include' BF_OPENGL_LIBINC = '${BF_OPENGL}/lib' diff --git a/build_files/scons/config/win64-vc-config.py b/build_files/scons/config/win64-vc-config.py index 2c990941764..e7d467fb8d1 100644 --- a/build_files/scons/config/win64-vc-config.py +++ b/build_files/scons/config/win64-vc-config.py @@ -155,6 +155,9 @@ BF_OPENCOLLADA_LIBPATH = '${BF_OPENCOLLADA}/lib' WITH_BF_RAYOPTIMIZATION = True BF_RAYOPTIMIZATION_SSE_FLAGS = ['/arch:SSE','/arch:SSE2'] +#On-Surface Brush +WITH_BF_ONSURFACEBRUSH = True + WITH_BF_STATICOPENGL = False BF_OPENGL_INC = '${BF_OPENGL}/include' BF_OPENGL_LIBINC = '${BF_OPENGL}/lib' diff --git a/build_files/scons/tools/btools.py b/build_files/scons/tools/btools.py index accdde0d2cf..f920aaefc78 100644 --- a/build_files/scons/tools/btools.py +++ b/build_files/scons/tools/btools.py @@ -132,6 +132,7 @@ def validate_arguments(args, bc): 'BF_GHOST_DEBUG', 'WITH_BF_RAYOPTIMIZATION', 'BF_RAYOPTIMIZATION_SSE_FLAGS', + 'WITH_BF_ONSURFACEBRUSH', 'BF_NO_ELBEEM', 'WITH_BF_CXX_GUARDEDALLOC', 'WITH_BF_JEMALLOC', 'WITH_BF_STATICJEMALLOC', 'BF_JEMALLOC', 'BF_JEMALLOC_INC', 'BF_JEMALLOC_LIBPATH', 'BF_JEMALLOC_LIB', 'BF_JEMALLOC_LIB_STATIC' @@ -501,6 +502,9 @@ def read_opts(env, cfg, args): (BoolVariable('WITH_BF_RAYOPTIMIZATION', 'Enable raytracer SSE/SIMD optimization.', False)), ('BF_RAYOPTIMIZATION_SSE_FLAGS', 'SSE flags', ''), + + (BoolVariable('WITH_BF_ONSURFACEBRUSH', 'Enable use of the "on-surface brush" for paint/sculpt. Requires a stencil buffer, GL_depth_texture, and GLSL', True)), + (BoolVariable('WITH_BF_CXX_GUARDEDALLOC', 'Enable GuardedAlloc for C++ memory allocation tracking.', False)) ) # end of opts.AddOptions() diff --git a/doc/blender_file_format/BlendFileDnaExporter_25.py b/doc/blender_file_format/BlendFileDnaExporter_25.py deleted file mode 100755 index afc58ce6730..00000000000 --- a/doc/blender_file_format/BlendFileDnaExporter_25.py +++ /dev/null @@ -1,477 +0,0 @@ -#! /usr/bin/env python3 - -# ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** - -###################################################### -# -# Name: -# dna.py -# -# Description: -# Creates a browsable DNA output to HTML. -# -# Author: -# Jeroen Bakker -# -# Version: -# v0.1 (12-05-2009) - migration of original source code to python. -# Added code to support blender 2.5 branch -# v0.2 (25-05-2009) - integrated with BlendFileReader.py -# -# Input: -# blender build executable -# -# Output: -# dna.html -# dna.css (will only be created when not existing) -# -# Startup: -# ./blender -P BlendFileDnaExporter.py -# -# Process: -# 1: write blend file with SDNA info -# 2: read blend header from blend file -# 3: seek DNA1 file-block -# 4: read dna record from blend file -# 5: close and eventually delete temp blend file -# 6: export dna to html and css -# 7: quit blender -# -###################################################### - -import struct -import sys -import getopt # command line arguments handling -from string import Template # strings completion - - -# logs -import logging -log = logging.getLogger("BlendFileDnaExporter") - -if '--dna-debug' in sys.argv: - logging.basicConfig(level=logging.DEBUG) -else: - logging.basicConfig(level=logging.INFO) - - -class DNACatalogHTML: - ''' - DNACatalog is a catalog of all information in the DNA1 file-block - ''' - - def __init__(self, catalog, bpy_module = None): - self.Catalog = catalog - self.bpy = bpy_module - - def WriteToHTML(self, handle): - - dna_html_template = """ - <!DOCTYPE html PUBLIC -//W3C//DTD HTML 4.01 Transitional//EN http://www.w3.org/TR/html4/loose.dtd> - <html> - <head> - <link rel="stylesheet" type="text/css" href="dna.css" media="screen, print" /> - <meta http-equiv="Content-Type" content="text/html"; charset="ISO-8859-1" /> - <title>The mystery of the blend</title> - </head> - <body> - <div class=title> - Blender ${version}<br/> - Internal SDNA structures - </div> - Architecture: ${bitness} ${endianness}<br/> - Build revision: <a href="https://svn.blender.org/svnroot/bf-blender/!svn/bc/${revision}/trunk/">${revision}</a><br/> - File format reference: <a href="mystery_of_the_blend.html">The mystery of the blend</a> by Jeroen Bakker<br/> - <h1>Index of blender structures</h1> - <ul class=multicolumn> - ${structs_list} - </ul> - ${structs_content} - </body> - </html>""" - - header = self.Catalog.Header - bpy = self.bpy - - # ${version} and ${revision} - if bpy: - version = '.'.join(map(str, bpy.app.version)) - revision = bpy.app.build_revision[:-1] - else: - version = str(header.Version) - revision = 'Unknown' - - # ${bitness} - if header.PointerSize == 8: - bitness = '64 bit' - else: - bitness = '32 bit' - - # ${endianness} - if header.LittleEndianness: - endianess= 'Little endianness' - else: - endianess= 'Big endianness' - - # ${structs_list} - log.debug("Creating structs index") - structs_list = '' - list_item = '<li class="multicolumn">({0}) <a href="#{1}">{1}</a></li>\n' - structureIndex = 0 - for structure in self.Catalog.Structs: - structs_list += list_item.format(structureIndex, structure.Type.Name) - structureIndex+=1 - - # ${structs_content} - log.debug("Creating structs content") - structs_content = '' - for structure in self.Catalog.Structs: - log.debug(structure.Type.Name) - structs_content += self.Structure(structure) - - d = dict( - version = version, - revision = revision, - bitness = bitness, - endianness = endianess, - structs_list = structs_list, - structs_content = structs_content - ) - - dna_html = Template(dna_html_template).substitute(d) - dna_html = self.format(dna_html) - handle.write(dna_html) - - def Structure(self, structure): - struct_table_template = """ - <table><a name="${struct_name}"></a> - <caption><a href="#${struct_name}">${struct_name}</a></caption> - <thead> - <tr> - <th>reference</th> - <th>structure</th> - <th>type</th> - <th>name</th> - <th>offset</th> - <th>size</th> - </tr> - </thead> - <tbody> - ${fields} - </tbody> - </table> - <label>Total size: ${size} bytes</label><br/> - <label>(<a href="#top">top</a>)</label><br/>""" - - d = dict( - struct_name = structure.Type.Name, - fields = self.StructureFields(structure, None, 0), - size = str(structure.Type.Size) - ) - - struct_table = Template(struct_table_template).substitute(d) - return struct_table - - def StructureFields(self, structure, parentReference, offset): - fields = '' - for field in structure.Fields: - fields += self.StructureField(field, structure, parentReference, offset) - offset += field.Size(self.Catalog.Header) - return fields - - def StructureField(self, field, structure, parentReference, offset): - structure_field_template = """ - <tr> - <td>${reference}</td> - <td>${struct}</td> - <td>${type}</td> - <td>${name}</td> - <td>${offset}</td> - <td>${size}</td> - </tr>""" - - if field.Type.Structure == None or field.Name.IsPointer(): - - # ${reference} - reference = field.Name.AsReference(parentReference) - - # ${struct} - if parentReference != None: - struct = '<a href="#{0}">{0}</a>'.format(structure.Type.Name) - else: - struct = structure.Type.Name - - # ${type} - type = field.Type.Name - - # ${name} - name = field.Name.Name - - # ${offset} - # offset already set - - # ${size} - size = field.Size(self.Catalog.Header) - - d = dict( - reference = reference, - struct = struct, - type = type, - name = name, - offset = offset, - size = size - ) - - structure_field = Template(structure_field_template).substitute(d) - - elif field.Type.Structure != None: - reference = field.Name.AsReference(parentReference) - structure_field = self.StructureFields(field.Type.Structure, reference, offset) - - return structure_field - - def indent(self, input, dent, startswith = ''): - output = '' - if dent < 0: - for line in input.split('\n'): - dent = abs(dent) - output += line[dent:] + '\n' # unindent of a desired amount - elif dent == 0: - for line in input.split('\n'): - output += line.lstrip() + '\n' # remove indentation completely - elif dent > 0: - for line in input.split('\n'): - output += ' '* dent + line + '\n' - return output - - def format(self, input): - diff = { - '\n<!DOCTYPE':'<!DOCTYPE', - '\n</ul>' :'</ul>', - '<a name' :'\n<a name', - '<tr>\n' :'<tr>', - '<tr>' :' <tr>', - '</th>\n' :'</th>', - '</td>\n' :'</td>', - '<tbody>\n' :'<tbody>' - } - output = self.indent(input, 0) - for key, value in diff.items(): - output = output.replace(key, value) - return output - - def WriteToCSS(self, handle): - ''' - Write the Cascading stylesheet template to the handle - It is expected that the handle is a Filehandle - ''' - css = """ - @CHARSET "ISO-8859-1"; - - body { - font-family: verdana; - font-size: small; - } - - div.title { - font-size: large; - text-align: center; - } - - h1 { - page-break-before: always; - } - - h1, h2 { - background-color: #D3D3D3; - color:#404040; - margin-right: 3%; - padding-left: 40px; - } - - h1:hover{ - background-color: #EBEBEB; - } - - h3 { - padding-left: 40px; - } - - table { - border-width: 1px; - border-style: solid; - border-color: #000000; - border-collapse: collapse; - width: 94%; - margin: 20px 3% 10px; - } - - caption { - margin-bottom: 5px; - } - - th { - background-color: #000000; - color:#ffffff; - padding-left:5px; - padding-right:5px; - } - - tr { - } - - td { - border-width: 1px; - border-style: solid; - border-color: #a0a0a0; - padding-left:5px; - padding-right:5px; - } - - label { - float:right; - margin-right: 3%; - } - - ul.multicolumn { - list-style:none; - float:left; - padding-right:0px; - margin-right:0px; - } - - li.multicolumn { - float:left; - width:200px; - margin-right:0px; - } - - a { - color:#a000a0; - text-decoration:none; - } - - a:hover { - color:#a000a0; - text-decoration:underline; - } - """ - - css = self.indent(css, 0) - - handle.write(css) - - -def usage(): - print("\nUsage: \n\tblender2.5 -b -P BlendFileDnaExporter_25.py [-- [options]]") - print("Options:") - print("\t--dna-keep-blend: doesn't delete the produced blend file DNA export to html") - print("\t--dna-debug: sets the logging level to DEBUG (lots of additional info)") - print("\t--dna-versioned saves version informations in the html and blend filenames") - print("\t--dna-overwrite-css overwrite dna.css, useful when modifying css in the script") - print("Examples:") - print("\tdefault: % blender2.5 -b -P BlendFileDnaExporter_25.py") - print("\twith options: % blender2.5 -b -P BlendFileDnaExporter_25.py -- --dna-keep-blend --dna-debug\n") - - -###################################################### -# Main -###################################################### - -def main(): - - import os, os.path - - try: - bpy = __import__('bpy') - - # Files - if '--dna-versioned' in sys.argv: - blender_version = '_'.join(map(str, bpy.app.version)) - filename = 'dna-{0}-{1}_endian-{2}-r{3}'.format(sys.arch, sys.byteorder, blender_version, bpy.app.build_revision[2:-1]) - else: - filename = 'dna' - dir = os.path.dirname(__file__) - Path_Blend = os.path.join(dir, filename + '.blend') # temporary blend file - Path_HTML = os.path.join(dir, filename + '.html') # output html file - Path_CSS = os.path.join(dir, 'dna.css') # output css file - - # create a blend file for dna parsing - if not os.path.exists(Path_Blend): - log.info("1: write temp blend file with SDNA info") - log.info(" saving to: " + Path_Blend) - try: - bpy.ops.wm.save_as_mainfile(filepath = Path_Blend, copy = True, compress = False) - except: - log.error("Filename {0} does not exist and can't be created... quitting".format(Path_Blend)) - return - else: - log.info("1: found blend file with SDNA info") - log.info(" " + Path_Blend) - - # read blend header from blend file - log.info("2: read file:") - - if not dir in sys.path: - sys.path.append(dir) - import BlendFileReader - - handle = BlendFileReader.openBlendFile(Path_Blend) - blendfile = BlendFileReader.BlendFile(handle) - catalog = DNACatalogHTML(blendfile.Catalog, bpy) - - # close temp file - handle.close() - - # deleting or not? - if '--dna-keep-blend' in sys.argv: - # keep the blend, useful for studying hexdumps - log.info("5: closing blend file:") - log.info(" {0}".format(Path_Blend)) - else: - # delete the blend - log.info("5: close and delete temp blend:") - log.info(" {0}".format(Path_Blend)) - os.remove(Path_Blend) - - # export dna to xhtml - log.info("6: export sdna to xhtml file") - handleHTML = open(Path_HTML, "w") - catalog.WriteToHTML(handleHTML) - handleHTML.close() - - # only write the css when doesn't exist or at explicit request - if not os.path.exists(Path_CSS) or '--dna-overwrite-css' in sys.argv: - handleCSS = open(Path_CSS, "w") - catalog.WriteToCSS(handleCSS) - handleCSS.close() - - # quit blender - if not bpy.app.background: - log.info("7: quit blender") - bpy.ops.wm.exit_blender() - - except ImportError: - log.warning(" skipping, not running in Blender") - usage() - sys.exit(2) - - -if __name__ == '__main__': - main() diff --git a/doc/blender_file_format/BlendFileReader.py b/doc/blender_file_format/BlendFileReader.py deleted file mode 100644 index 7003af10ac7..00000000000 --- a/doc/blender_file_format/BlendFileReader.py +++ /dev/null @@ -1,446 +0,0 @@ -#! /usr/bin/env python3 - -# ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# ***** END GPL LICENCE BLOCK ***** - -###################################################### -# Importing modules -###################################################### - -import os -import struct -import gzip -import tempfile - -import logging -log = logging.getLogger("BlendFileReader") - -###################################################### -# module global routines -###################################################### - -def ReadString(handle, length): - ''' - ReadString reads a String of given length or a zero terminating String - from a file handle - ''' - if length != 0: - return handle.read(length).decode() - else: - # length == 0 means we want a zero terminating string - result = "" - s = ReadString(handle, 1) - while s!="\0": - result += s - s = ReadString(handle, 1) - return result - - -def Read(type, handle, fileheader): - ''' - Reads the chosen type from a file handle - ''' - def unpacked_bytes(type_char, size): - return struct.unpack(fileheader.StructPre + type_char, handle.read(size))[0] - - if type == 'ushort': - return unpacked_bytes("H", 2) # unsigned short - elif type == 'short': - return unpacked_bytes("h", 2) # short - elif type == 'uint': - return unpacked_bytes("I", 4) # unsigned int - elif type == 'int': - return unpacked_bytes("i", 4) # int - elif type == 'float': - return unpacked_bytes("f", 4) # float - elif type == 'ulong': - return unpacked_bytes("Q", 8) # unsigned long - elif type == 'pointer': - # The pointersize is given by the header (BlendFileHeader). - if fileheader.PointerSize == 4: - return Read('uint', handle, fileheader) - if fileheader.PointerSize == 8: - return Read('ulong', handle, fileheader) - - -def openBlendFile(filename): - ''' - Open a filename, determine if the file is compressed and returns a handle - ''' - handle = open(filename, 'rb') - magic = ReadString(handle, 7) - if magic in ("BLENDER", "BULLETf"): - log.debug("normal blendfile detected") - handle.seek(0, os.SEEK_SET) - return handle - else: - log.debug("gzip blendfile detected?") - handle.close() - log.debug("decompressing started") - fs = gzip.open(filename, "rb") - handle = tempfile.TemporaryFile() - data = fs.read(1024*1024) - while data: - handle.write(data) - data = fs.read(1024*1024) - log.debug("decompressing finished") - fs.close() - log.debug("resetting decompressed file") - handle.seek(0, os.SEEK_SET) - return handle - - -def Align(handle): - ''' - Aligns the filehandle on 4 bytes - ''' - offset = handle.tell() - trim = offset % 4 - if trim != 0: - handle.seek(4-trim, os.SEEK_CUR) - - -###################################################### -# module classes -###################################################### - -class BlendFile: - ''' - Reads a blendfile and store the header, all the fileblocks, and catalogue - structs foound in the DNA fileblock - - - BlendFile.Header (BlendFileHeader instance) - - BlendFile.Blocks (list of BlendFileBlock instances) - - BlendFile.Catalog (DNACatalog instance) - ''' - - def __init__(self, handle): - log.debug("initializing reading blend-file") - self.Header = BlendFileHeader(handle) - self.Blocks = [] - fileblock = BlendFileBlock(handle, self) - found_dna_block = False - while not found_dna_block: - if fileblock.Header.Code in ("DNA1", "SDNA"): - self.Catalog = DNACatalog(self.Header, handle) - found_dna_block = True - else: - fileblock.Header.skip(handle) - - self.Blocks.append(fileblock) - fileblock = BlendFileBlock(handle, self) - - # appending last fileblock, "ENDB" - self.Blocks.append(fileblock) - - # seems unused? - """ - def FindBlendFileBlocksWithCode(self, code): - #result = [] - #for block in self.Blocks: - #if block.Header.Code.startswith(code) or block.Header.Code.endswith(code): - #result.append(block) - #return result - """ - - -class BlendFileHeader: - ''' - BlendFileHeader allocates the first 12 bytes of a blend file. - It contains information about the hardware architecture. - Header example: BLENDER_v254 - - BlendFileHeader.Magic (str) - BlendFileHeader.PointerSize (int) - BlendFileHeader.LittleEndianness (bool) - BlendFileHeader.StructPre (str) see http://docs.python.org/py3k/library/struct.html#byte-order-size-and-alignment - BlendFileHeader.Version (int) - ''' - - def __init__(self, handle): - log.debug("reading blend-file-header") - - self.Magic = ReadString(handle, 7) - log.debug(self.Magic) - - pointersize = ReadString(handle, 1) - log.debug(pointersize) - if pointersize == "-": - self.PointerSize = 8 - if pointersize == "_": - self.PointerSize = 4 - - endianness = ReadString(handle, 1) - log.debug(endianness) - if endianness == "v": - self.LittleEndianness = True - self.StructPre = "<" - if endianness == "V": - self.LittleEndianness = False - self.StructPre = ">" - - version = ReadString(handle, 3) - log.debug(version) - self.Version = int(version) - - log.debug("{0} {1} {2} {3}".format(self.Magic, self.PointerSize, self.LittleEndianness, version)) - - -class BlendFileBlock: - ''' - BlendFileBlock.File (BlendFile) - BlendFileBlock.Header (FileBlockHeader) - ''' - - def __init__(self, handle, blendfile): - self.File = blendfile - self.Header = FileBlockHeader(handle, blendfile.Header) - - def Get(self, handle, path): - log.debug("find dna structure") - dnaIndex = self.Header.SDNAIndex - dnaStruct = self.File.Catalog.Structs[dnaIndex] - log.debug("found " + dnaStruct.Type.Name) - handle.seek(self.Header.FileOffset, os.SEEK_SET) - return dnaStruct.GetField(self.File.Header, handle, path) - - -class FileBlockHeader: - ''' - FileBlockHeader contains the information in a file-block-header. - The class is needed for searching to the correct file-block (containing Code: DNA1) - - Code (str) - Size (int) - OldAddress (pointer) - SDNAIndex (int) - Count (int) - FileOffset (= file pointer of datablock) - ''' - - def __init__(self, handle, fileheader): - self.Code = ReadString(handle, 4).strip() - if self.Code != "ENDB": - self.Size = Read('uint', handle, fileheader) - self.OldAddress = Read('pointer', handle, fileheader) - self.SDNAIndex = Read('uint', handle, fileheader) - self.Count = Read('uint', handle, fileheader) - self.FileOffset = handle.tell() - else: - self.Size = Read('uint', handle, fileheader) - self.OldAddress = 0 - self.SDNAIndex = 0 - self.Count = 0 - self.FileOffset = handle.tell() - #self.Code += ' ' * (4 - len(self.Code)) - log.debug("found blend-file-block-fileheader {0} {1}".format(self.Code, self.FileOffset)) - - def skip(self, handle): - handle.read(self.Size) - - -class DNACatalog: - ''' - DNACatalog is a catalog of all information in the DNA1 file-block - - Header = None - Names = None - Types = None - Structs = None - ''' - - def __init__(self, fileheader, handle): - log.debug("building DNA catalog") - self.Names=[] - self.Types=[] - self.Structs=[] - self.Header = fileheader - - SDNA = ReadString(handle, 4) - - # names - NAME = ReadString(handle, 4) - numberOfNames = Read('uint', handle, fileheader) - log.debug("building #{0} names".format(numberOfNames)) - for i in range(numberOfNames): - name = ReadString(handle,0) - self.Names.append(DNAName(name)) - Align(handle) - - # types - TYPE = ReadString(handle, 4) - numberOfTypes = Read('uint', handle, fileheader) - log.debug("building #{0} types".format(numberOfTypes)) - for i in range(numberOfTypes): - type = ReadString(handle,0) - self.Types.append(DNAType(type)) - Align(handle) - - # type lengths - TLEN = ReadString(handle, 4) - log.debug("building #{0} type-lengths".format(numberOfTypes)) - for i in range(numberOfTypes): - length = Read('ushort', handle, fileheader) - self.Types[i].Size = length - Align(handle) - - # structs - STRC = ReadString(handle, 4) - numberOfStructures = Read('uint', handle, fileheader) - log.debug("building #{0} structures".format(numberOfStructures)) - for structureIndex in range(numberOfStructures): - type = Read('ushort', handle, fileheader) - Type = self.Types[type] - structure = DNAStructure(Type) - self.Structs.append(structure) - - numberOfFields = Read('ushort', handle, fileheader) - for fieldIndex in range(numberOfFields): - fTypeIndex = Read('ushort', handle, fileheader) - fNameIndex = Read('ushort', handle, fileheader) - fType = self.Types[fTypeIndex] - fName = self.Names[fNameIndex] - structure.Fields.append(DNAField(fType, fName)) - - -class DNAName: - ''' - DNAName is a C-type name stored in the DNA. - - Name = str - ''' - - def __init__(self, name): - self.Name = name - - def AsReference(self, parent): - if parent == None: - result = "" - else: - result = parent+"." - - result = result + self.ShortName() - return result - - def ShortName(self): - result = self.Name; - result = result.replace("*", "") - result = result.replace("(", "") - result = result.replace(")", "") - Index = result.find("[") - if Index != -1: - result = result[0:Index] - return result - - def IsPointer(self): - return self.Name.find("*")>-1 - - def IsMethodPointer(self): - return self.Name.find("(*")>-1 - - def ArraySize(self): - result = 1 - Temp = self.Name - Index = Temp.find("[") - - while Index != -1: - Index2 = Temp.find("]") - result*=int(Temp[Index+1:Index2]) - Temp = Temp[Index2+1:] - Index = Temp.find("[") - - return result - - -class DNAType: - ''' - DNAType is a C-type stored in the DNA - - Name = str - Size = int - Structure = DNAStructure - ''' - - def __init__(self, aName): - self.Name = aName - self.Structure=None - - -class DNAStructure: - ''' - DNAType is a C-type structure stored in the DNA - - Type = DNAType - Fields = [DNAField] - ''' - - def __init__(self, aType): - self.Type = aType - self.Type.Structure = self - self.Fields=[] - - def GetField(self, header, handle, path): - splitted = path.partition(".") - name = splitted[0] - rest = splitted[2] - offset = 0; - for field in self.Fields: - if field.Name.ShortName() == name: - log.debug("found "+name+"@"+str(offset)) - handle.seek(offset, os.SEEK_CUR) - return field.DecodeField(header, handle, rest) - else: - offset += field.Size(header) - - log.debug("error did not find "+path) - return None - - -class DNAField: - ''' - DNAField is a coupled DNAType and DNAName. - - Type = DNAType - Name = DNAName - ''' - - def __init__(self, aType, aName): - self.Type = aType - self.Name = aName - - def Size(self, header): - if self.Name.IsPointer() or self.Name.IsMethodPointer(): - return header.PointerSize*self.Name.ArraySize() - else: - return self.Type.Size*self.Name.ArraySize() - - def DecodeField(self, header, handle, path): - if path == "": - if self.Name.IsPointer(): - return Read('pointer', handle, header) - if self.Type.Name=="int": - return Read('int', handle, header) - if self.Type.Name=="short": - return Read('short', handle, header) - if self.Type.Name=="float": - return Read('float', handle, header) - if self.Type.Name=="char": - return ReadString(handle, self.Name.ArraySize()) - else: - return self.Type.Structure.GetField(header, handle, path) - diff --git a/doc/blender_file_format/README b/doc/blender_file_format/README deleted file mode 100644 index 55dc3b83e49..00000000000 --- a/doc/blender_file_format/README +++ /dev/null @@ -1,29 +0,0 @@ -To inspect the blend-file-format used by a certain version of blender 2.5x, -navigate to this folder and run this command: - -blender2.5 -b -P BlendFileDnaExporter_25.py - -where "blender2.5" is your blender executable or a symlink to it. - -This creates a temporary dna.blend to be inspected and it produces two new files: - -* dna.html: the list of all the structures saved in a blend file with the blender2.5 - executable you have used. If you enable build informations when you build blender, - the dna.html file will also show which svn revision the html refers to. -* dna.css: the css for the html above - -Below you have the help message with a list of options you can use. - - -Usage: - blender2.5 -b -P BlendFileDnaExporter_25.py [-- [options]] -Options: - --dna-keep-blend: doesn't delete the produced blend file DNA export to html - --dna-debug: sets the logging level to DEBUG (lots of additional info) - --dna-versioned saves version informations in the html and blend filenames - --dna-overwrite-css overwrite dna.css, useful when modifying css in the script -Examples: - default: % blender2.5 -b -P BlendFileDnaExporter_25.py - with options: % blender2.5 -b -P BlendFileDnaExporter_25.py -- --dna-keep-blend --dna-debug - - diff --git a/doc/blender_file_format/mystery_of_the_blend.css b/doc/blender_file_format/mystery_of_the_blend.css deleted file mode 100644 index df287b54a06..00000000000 --- a/doc/blender_file_format/mystery_of_the_blend.css +++ /dev/null @@ -1,204 +0,0 @@ -@CHARSET "ISO-8859-1"; - -table { - border-width: 1px; - border-style: solid; - border-color: #000000; - border-collapse: collapse; - width: 94%; - margin: 10px 3%; -} - -DIV.title { - font-size: 30px; - font-weight: bold; - text-align: center -} - -DIV.subtitle { - font-size: large; - text-align: center -} - -DIV.contact { - margin:30px 3%; -} - -@media print { - DIV.contact { - margin-top: 300px; - } - DIV.title { - margin-top: 400px; - } -} - -label { - font-weight: bold; - width: 100px; - float: left; -} - -label:after { - content: ":"; -} - -TH { - background-color: #000000; - color: #ffffff; - padding-left: 5px; - padding-right: 5px; -} - -TR { -} - -TD { - border-width: 1px; - border-style: solid; - border-color: #a0a0a0; - padding-left: 5px; - padding-right: 5px; -} - -BODY { - font-family: verdana; - font-size: small; -} - -H1 { - page-break-before: always; -} - -H1, H2, H3, H4 { - margin-top: 30px; - margin-right: 3%; - padding: 3px 3%; - color: #404040; - cursor: pointer; -} - -H1, H2 { - background-color: #D3D3D3; -} - -H3, H4 { - padding-top: 5px; - padding-bottom: 5px; -} - -H1:hover, H2:hover, H3:hover, H4:hover { - background-color: #EBEBEB; -} - -CODE.evidence { - font-size:larger -} - -CODE.block { - color: #000000; - background-color: #DDDC75; - margin: 10px 0; - padding: 5px; - border-width: 1px; - border-style: dotted; - border-color: #000000; - white-space: pre; - display: block; - font-size: 2 em; -} - -ul { - margin: 10px 3%; -} - -li { - margin: 0 -15px; -} - -ul.multicolumn { - list-style: none; - float: left; - padding-right: 0px; - margin-right: 0px; -} - -li.multicolumn { - float: left; - width: 200px; - margin-right: 0px; -} - -@media screen { - p { - margin: 10px 3%; - line-height: 130%; - } -} - -span.fade { - color: gray; -} - -span.header { - color: green; -} - -span.header-greyed { - color: #4CBE4B; -} - -span.data { - color: blue; -} - -span.data-greyed { - color: #5D99C4; -} - -span.descr { - color: red; -} - -div.box { - margin: 15px 3%; - border-style: dotted; - border-width: 1px; -} - -div.box-solid { - margin: 15px 3%; - border-style: solid; - border-width: 1px; -} - -p.box-title { - font-style: italic; - font-size: 110%; - cursor: pointer; -} - -p.box-title:hover { - background-color: #EBEBEB; -} - -p.code { - font-family: "Courier New", Courier, monospace; -} - -a { - color: #a000a0; - text-decoration: none; -} - -a:hover { - color: #a000a0; - text-decoration: underline; -} - -td.skip { - color: #808080; - padding-top: 10px; - padding-bottom: 10px; - text-align: center; -} diff --git a/doc/blender_file_format/mystery_of_the_blend.html b/doc/blender_file_format/mystery_of_the_blend.html deleted file mode 100644 index b34493ffa3e..00000000000 --- a/doc/blender_file_format/mystery_of_the_blend.html +++ /dev/null @@ -1,835 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> -<html> -<head> - <link rel="stylesheet" type="text/css" href="mystery_of_the_blend.css" media="screen, print"> - <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> - <title>The mystery of the blend</title> -</head> - -<body> -<div class="title">The mystery of the blend</div> -<div class="subtitle">The blender file-format explained</div> -<div class="contact"> -<label>Author</label> Jeroen Bakker<br> -<label>Email</label> <a href="mailto:j.bakker@atmind.nl">j.bakker@atmind.nl</a><br> -<label>Website</label> <a href="http://www.atmind.nl/blender/">http://www.atmind.nl/blender</a><br> -<label>Version</label> 06-10-2010<br> -</div> - -<a name="introduction" href="#introduction" ><h2>Introduction</h2></a> -</a> - -<p>In this article I will describe the - blend-file-format with a request to tool-makers to support blend-file. - -</p> -<p>First I'll describe how Blender works with blend-files. You'll notice - why the blend-file-format is not that well documented, as from -Blender's perspective this is not needed. -We look at the global file-structure of a blend-file (the file-header -and file-blocks). -After this is explained, we go deeper to the core of the blend-file, the - DNA-structures. They hold the blue-prints of the blend-file and the key - asset of understanding blend-files. -When that's done we can use these DNA-structures to read information -from elsewhere in the blend-file. - -</p> -<p> -In this article we'll be using the default blend-file from Blender 2.54, - with the goal to read the output resolution from the Scene. -The article is written to be programming language independent and I've -setup a web-site for support. -</p> - -<a name="loading-and-saving-in-blender" href="#loading-and-saving-in-blender"> -<h2>Loading and saving in Blender</h2> -</a> - -<p> -Loading and saving in Blender is very fast and Blender is known to -have excellent downward and upward compatibility. Ton Roosendaal -demonstrated that in December 2008 by loading a 1.0 blend-file using -Blender 2.48a [ref: <a href="http://www.blendernation.com/2008/12/01/blender-dna-rna-and-backward-compatibility/">http://www.blendernation.com/2008/12/01/blender-dna-rna-and-backward-compatibility/</a>]. -</p> - -<p> -Saving complex scenes in Blender is done within seconds. Blender -achieves this by saving data in memory to disk without any -transformations or translations. Blender only adds file-block-headers to - this data. A file-block-header contains clues on how to interpret the -data. After the data, all internally Blender structures are stored. -These structures will act as blue-prints when Blender loads the file. -Blend-files can be different when stored on different hardware platforms - or Blender releases. There is no effort taken to make blend-files -binary the same. Blender creates the blend-files in this manner since -release 1.0. Backward and upwards compatibility is not implemented when -saving the file, this is done during loading. -</p> - -<p> -When Blender loads a blend-file, the DNA-structures are read first. -Blender creates a catalog of these DNA-structures. Blender uses this -catalog together with the data in the file, the internal Blender -structures of the Blender release you're using and a lot of -transformation and translation logic to implement the backward and -upward compatibility. In the source code of blender there is actually -logic which can transform and translate every structure used by a -Blender release to the one of the release you're using [ref: <a href="http://download.blender.org/source/blender-2.48a.tar.gz">http://download.blender.org/source/blender-2.48a.tar.gz</a> - <a href="https://svn.blender.org/svnroot/bf-blender/tags/blender-2.48-release/source/blender/blenloader/intern/readfile.c">blender/blenloader/intern/readfile.c</a> lines -4946-7960]. The more difference between releases the more logic is -executed. -</p> - -<p> -The blend-file-format is not well documented, as it does not differ from - internally used structures and the file can really explain itself. -</p> - -<a name="global-file-structure" href="#global-file-structure"> -<h2>Global file-structure</h2> -</a> - -<p> -This section explains how the global file-structure can be read. -</p> - -<ul> -<li>A blend-file always start with the <b>file-header</b></li> -<li>After the file-header, follows a list of <b>file-blocks</b> (the default blend file of Blender 2.48 contains more than 400 of these file-blocks).</li> -<li>Each file-block has a <b>file-block header</b> and <b>file-block data</b></li> -<li>At the end of the blend-file there is a section called "<a href="#structure-DNA" style="font-weight:bold">Structure DNA</a>", which lists all the internal structures of the Blender release the file was created in</li> -<li>The blend-file ends with a file-block called 'ENDB'</li> -</ul> - -<!-- file scheme --> -<div class="box-solid" style="width:20%; margin-left:35%; font-size:0.8em;"> - - <p class="code"><b>File.blend</b></p> - - <div class="box"><p class="code">File-header</p></div> - - <div class="box-solid"><p class="code">File-block</p> - <div class="box"><p class="code">Header</p></div> - <div class="box"><p class="code">Data</p></div> - </div> - - <div class="box" style="border-style:dashed"><p class="code">File-block</p></div> - <div class="box" style="border-style:dashed"><p class="code">File-block</p></div> - - <div class="box-solid"><p class="code">File-block 'Structure DNA'</p> - <div class="box"><p class="code">Header ('DNA1')</p></div> - <div class="box-solid"> - <p class="code">Data ('SDNA')</p> - <div class="box"> - <p class="code">Names ('NAME')</p> - </div> - <div class="box"> - <p class="code">Types ('TYPE')</p> - </div> - <div class="box"> - <p class="code">Lengths ('TLEN')</p> - </div> - <div class="box"> - <p class="code">Structures ('STRC')</p> - </div> - </div> - </div> - - <div class="box-solid"><p class="code">File-Block 'ENDB'</p></div> - -</div><!-- end of file scheme --> - -<a name="file-header" href="#file-header"> -<h3>File-Header</h3> -</a> - -<p> -The first 12 bytes of every blend-file is the file-header. The -file-header has information on Blender (version-number) and the PC the -blend-file was saved on (pointer-size and endianness). This is required -as all data inside the blend-file is ordered in that way, because no -translation or transformation is done during saving. -The next table describes the information in the file-header. -</p> - -<table> -<caption>File-header</caption> -<thead> -<tr><th>reference</th> - <th>structure</th> - <th>type</th> - <th>offset</th> - <th>size</th></tr> -</thead> -<tbody> -<tr><td>identifier</td> - <td>char[7]</td> - <td>File identifier (always 'BLENDER')</td> - <td>0</td> - <td>7</td></tr> -<tr><td>pointer-size</td> - <td>char</td> - <td>Size of a pointer; all pointers in the file are stored in this format. '_' means 4 bytes or 32 bit and '-' means 8 bytes or 64 bits.</td> - <td>7</td> - <td>1</td></tr> -<tr><td>endianness</td> - <td>char</td> - <td>Type of byte ordering used; 'v' means little endian and 'V' means big endian.</td> - <td>8</td> - <td>1</td></tr> -<tr><td>version-number</td> - <td>char[3]</td> - <td>Version of Blender the file was created in; '254' means version 2.54</td> - <td>9</td> - <td>3</td></tr> -</tbody> -</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): -</p> - -<ul> - <li>in a big endian ordering, the largest part of the value is placed on the first byte and - the lowest part of the value is placed on the last byte,</li> - <li>in a little endian ordering, largest part of the value is placed on the last byte - and the smallest part of the value is placed on the first byte.</li> -</ul> - -<p> -Nowadays, little-endian is the most commonly used. -</p> - -<a name="example-endianess"></a> -<div class="box"> -<p onclick="location.href='#example-endianess'" class="box-title"> -Endianess Example -</p> -<p> -Writing the integer <code class="evidence">0x4A3B2C1Dh</code>, will be ordered: -<ul> -<li>in big endian as <code class="evidence">0x4Ah</code>, <code class="evidence">0x3Bh</code>, <code class="evidence">0x2Ch</code>, <code class="evidence">0x1Dh</code></li> -<li>in little endian as <code class="evidence">0x1Dh</code>, <code class="evidence">0x2Ch</code>, <code class="evidence">0x3Bh</code>, <code class="evidence">0x4Ah</code></li> -</ul> -</p> -</div> - -<p> -Blender supports little-endian and big-endian.<br> -This means that when the endianness -is different between the blend-file and the PC your using, Blender changes it to the byte ordering -of your PC. -</p> - -<a name="example-file-header"></a> -<div class="box"> -<p onclick="location.href='#example-file-header'" class="box-title"> -File-header Example -</p> - -<p> -This hex-dump describes a file-header created with <code>blender</code> <code>2.54.0</code> on <code>little-endian</code> hardware with a <code>32 bits</code> pointer length. -<code class="block"> <span class="descr">pointer-size version-number - | |</span> -0000 0000: [42 4C 45 4E 44 45 52] [5F] [76] [32 35 34] BLENDER_v254 <span class="descr"> - | | - identifier endianness</span></code> -</p> -</div> - -<a name="file-blocks" href="#file-blocks"><h3>File-blocks</h3></a> - -<p> -File-blocks contain a "<a href="#file-block-header">file-block header</a>" and "file-block data". -</p> - -<a name="file-block-header" href="#file-block-header"><h3>File-block headers</h3></a> - -<p> -The file-block-header describes: -</p> - -<ul> -<li>the type of information stored in the -file-block</li> -<li>the total length of the data</li> -<li>the old memory -pointer at the moment the data was written to disk</li> -<li>the number of items of this information</li> -</ul> - -<p> -As we can see below, depending on the pointer-size stored in the file-header, a file-block-header -can be 20 or 24 bytes long, hence it is always aligned at 4 bytes. -</p> - -<table> -<caption>File-block-header</caption> -<thead> -<tr> -<th>reference</th> - <th>structure</th> - <th>type</th> - <th>offset</th> - <th>size</th></tr></thead> -<tbody> -<tr><td>code</td> - <td>char[4]</td> - <td>File-block identifier</td> - <td>0</td> - <td>4</td></tr> -<tr><td>size</td> - <td>integer</td> - <td>Total length of the data after the file-block-header</td> - <td>4</td> - <td>4</td></tr> -<tr><td>old memory address</td> - <td>void*</td> - <td>Memory address the structure was located when written to disk</td> - <td>8</td> - <td>pointer-size (4/8)</td></tr> -<tr><td>SDNA index</td> - <td>integer</td> - <td>Index of the SDNA structure</td> - <td>8+pointer-size</td> - <td>4</td></tr> -<tr><td>count</td> - <td>integer</td> - <td>Number of structure located in this file-block</td> - <td>12+pointer-size</td> - <td>4</td></tr> -</tbody> -</table> - -<p> -The above table describes how a file-block-header is structured: -</p> - -<ul> -<li><code>Code</code> describes different types of file-blocks. The code determines with what logic the data must be read. <br> -These codes also allows fast finding of data like Library, Scenes, Object or Materials as they all have a specific code. </li> -<li><code>Size</code> contains the total length of data after the file-block-header. -After the data a new file-block starts. The last file-block in the file -has code 'ENDB'.</li> -<li><code>Old memory address</code> contains the memory address when the structure -was last stored. When loading the file the structures can be placed on -different memory addresses. Blender updates pointers to these structures - to the new memory addresses.</li> -<li><code>SDNA index</code> contains the index in the DNA structures to be used when -reading this file-block-data. <br> -More information about this subject will be explained in the <a href="#reading-scene-information">Reading scene information section</a>.</li> -<li><code>Count</code> tells how many elements of the specific SDNA structure can be found in the data.</li> -</ul> - -<a name="example-file-block-header"></a> -<div class="box"> -<p onclick="location.href='#example-file-block-header'" class="box-title"> -Example -</p> -<p> -This hex-dump describes a File-block (= <span class="header">File-block header</span> + <span class="data">File-block data</span>) created with <code>blender</code> <code>2.54</code> on <code>little-endian</code> hardware with a <code>32 bits</code> pointer length.<br> -<code class="block"><span class="descr"> file-block - identifier='SC' data size=1404 old pointer SDNA index=150 - | | | |</span> -0000 4420: <span class="header">[53 43 00 00] [7C 05 00 00] [68 34 FB 0B] [96 00 00 00]</span> SC.. `... ./.. .... -0000 4430: <span class="header">[01 00 00 00]</span> <span class="data">[xx xx xx xx xx xx xx xx xx xx xx xx</span> .... xxxx xxxx xxxx<span class="descr"> - | | - count=1 file-block data (next 1404 bytes)</span> -</code> -</p> - -<ul> -<li>The code <code>'SC'+0x00h</code> identifies that it is a Scene. </li> -<li>Size of the data is 1404 bytes (0x0000057Ch = 0x7Ch + 0x05h * 256 = 124 + 1280)</li> -<li>The old pointer is 0x0BFB3468h</li> -<li>The SDNA index is 150 (0x00000096h = 6 + 9 * 16 = 6 + 144)</li> -<li>The section contains a single scene (count = 1).</li> -</ul> - -<p> -Before we can interpret the data of this file-block we first have to read the DNA structures in the file. -The section "<a href="#structure-DNA">Structure DNA</a>" will show how to do that. -</p> -</div> - -<a name="structure-DNA" href="#structure-DNA"><h2>Structure DNA</h2></a> - -<a name="DNA1-file-block" href="#DNA1-file-block"><h3>The DNA1 file-block</h3></a> - -<p> -Structure DNA is stored in a file-block with code 'DNA1'. It can be just before the 'ENDB' file-block. -</p> - -<p> -The 'DNA1' file-block contains all internal structures of the Blender release the -file was created in. <br> -These structure can be described as C-structures: they can hold fields, arrays and -pointers to other structures, just like a normal C-structure. - -<p> -<code class="block">struct SceneRenderLayer { - struct SceneRenderLayer *next, *prev; - char name[32]; - struct Material *mat_override; - struct Group *light_override; - unsigned int lay; - unsigned int lay_zmask; - int layflag; - int pad; - int passflag; - int pass_xor; -}; -</code> -</p> - -<p> -For example,a blend-file created with Blender 2.54 the 'DNA1' file-block is 57796 bytes long and contains 398 structures. -</p> - -<a name="DNA1-file-block-header" href="#DNA1-file-block-header"><h3>DNA1 file-block-header</h3></a> - -<p> -The DNA1 file-block header follows the same rules of any other file-block, see the example below. -</p> - -<a name="example-DNA1-file-block-header"></a> -<div class="box"> -<p onclick="location.href='#example-DNA1-file-block-header'" class="box-title"> -Example -</p> -<p> -This hex-dump describes the file-block 'DNA1' header created with <code>blender</code> <code>2.54.0</code> on <code>little-endian</code> hardware with a <code>32 bits</code> pointer length.<br> -<code class="block"><span class="descr"> (file-block - identifier='DNA1') data size=57796 old pointer SDNA index=0 - | | | |</span> -0004 B060 <span class="header">[44 4E 41 31] [C4 E1 00 00] [C8 00 84 0B] [00 00 00 00]</span> DNA1............ -0004 B070 <span class="header">[01 00 00 00]</span> <span class="fade">[53 44 4E 41 4E 41 4D 45 CB 0B 00 00</span> ....<span class="fade">SDNANAME....</span><span class="descr"> - | | - count=1 'DNA1' file-block data (next 57796 bytes)</span> -</code> -</p> -</div> - -<a name="DNA1-file-block-data" href="#DNA1-file-block-data"><h3>DNA1 file-block data</h3></a> -<p> -The next section describes how this information is ordered in the <b>data</b> of the 'DNA1' file-block. -</p> - -<table> -<caption>Structure of the DNA file-block-data</caption> -<thead> - <tr><th colspan="2">repeat condition</th> - <th>name</th> - <th>type</th> - <th>length</th> - <th>description</th></tr> -</thead> -<tbody> -<tr><td></td> - <td></td> - <td>identifier</td> - <td>char[4]</td> - <td>4</td> - <td>'SDNA'</td></tr> -<tr><td></td> - <td></td> - <td>name identifier</td> - <td>char[4]</td> - <td>4</td> - <td>'NAME'</td></tr> -<tr><td></td> - <td></td> - <td>#names</td> - <td>integer</td> - <td>4</td> - <td>Number of names follows</td></tr> -<tr><td>for(#names)</td> - <td></td> - <td>name</td> - <td>char[]</td> - <td>?</td> - <td>Zero terminating string of name, also contains pointer and simple array definitions (e.g. '*vertex[3]\0')</td></tr> -<tr><td></td> - <td></td> - <td>type identifier</td> - <td>char[4]</td> - <td>4</td> - <td>'TYPE' this field is aligned at 4 bytes</td></tr> -<tr><td></td> - <td></td> - <td>#types</td> - <td>integer</td> - <td>4</td> - <td>Number of types follows</td></tr> -<tr><td>for(#types)</td> - <td></td> - <td>type</td> - <td>char[]</td> - <td>?</td> - <td>Zero terminating string of type (e.g. 'int\0')</td></tr> -<tr><td></td> - <td></td> - <td>length identifier</td> - <td>char[4]</td> - <td>4</td> - <td>'TLEN' this field is aligned at 4 bytes</td></tr> -<tr><td>for(#types)</td> - <td></td> - <td>length</td> - <td>short</td> - <td>2</td> - <td>Length in bytes of type (e.g. 4)</td></tr> -<tr><td></td> - <td></td> - <td>structure identifier</td> - <td>char[4]</td> - <td>4</td> - <td>'STRC' this field is aligned at 4 bytes</td></tr> -<tr><td></td> - <td></td> - <td>#structures</td> - <td>integer</td> - <td>4</td> - <td>Number of structures follows</td></tr> -<tr><td>for(#structures)</td> - <td></td> - <td>structure type</td> - <td>short</td> - <td>2</td> - <td>Index in types containing the name of the structure</td></tr> -<tr><td>..</td> - <td></td> - <td>#fields</td> - <td>short</td> - <td>2</td> - <td>Number of fields in this structure</td></tr> -<tr><td>..</td> - <td>for(#field)</td> - <td>field type</td> - <td>short</td> - <td>2</td> - <td>Index in type</td></tr> -<tr><td>for end</td> - <td>for end</td> - <td>field name</td> - <td>short</td> - <td>2</td> - <td>Index in name</td></tr> -</tbody> -</table> - -<p> -As you can see, the structures are stored in 4 arrays: names, types, -lengths and structures. Every structure also contains an array of -fields. A field is the combination of a type and a name. From this -information a catalog of all structures can be constructed. -The names are stored as how a C-developer defines them. This means that -the name also defines pointers and arrays. -(When a name starts with '*' it is used as a pointer. when the name -contains for example '[3]' it is used as a array of 3 long.) -In the types you'll find simple-types (like: 'integer', 'char', -'float'), but also complex-types like 'Scene' and 'MetaBall'. -'TLEN' part describes the length of the types. A 'char' is 1 byte, an -'integer' is 4 bytes and a 'Scene' is 1376 bytes long. -</p> - -<div class="box"> -<p class="box-title"> -Note -</p> -<p> -All identifiers, are arrays of 4 chars, hence they are all aligned at 4 bytes. -</p> -</div> - -<a name="example-DNA1-file-block-data"></a> -<div class="box"> -<p onclick="location.href='#example-DNA1-file-block-data'" class="box-title"> -Example -</p> -<p> -Created with <code>blender</code> <code>2.54.0</code> on <code>little-endian</code> hardware with a <code>32 bits</code> pointer length. -</p> - -<a name="DNA1-data-array-names" href="#DNA1-data-array-names"><h4>The names array</h4></a> -<p> -The first names are: *next, *prev, *data, *first, *last, x, y, xmin, xmax, ymin, ymax, *pointer, group, val, val2, type, subtype, flag, name[32], ... -<code class="block"><span class="descr"> file-block-data identifier='SDNA' array-id='NAME' number of names=3019 - | | |</span> -0004 B070 <span class="fade">01 00 00 00 [53 44 4E 41]</span><span class="data">[4E 41 4D 45] [CB 0B 00 00]</span> <span class="fade">....SDNA</span>NAME.... -0004 B080 <span class="data">[2A 6E 65 78 74 00][2A 70 72 65 76 00] [2A 64 61 74</span> *next.*prev.*dat<span class="descr"> - | | | - '*next\0' '*prev\0' '*dat'</span><span class="fade"> - .... - .... (3019 names)</span> -</code> -</p> - -<div class="box"> -<p class="box-title"> -Note -</p> -<p> -While reading the DNA you'll will come across some strange -names like '(*doit)()'. These are method pointers and Blender updates -them to the correct methods. -</p> -</div> - -<a name="DNA1-data-array-types" href="#DNA1-data-array-types"><h4>The types array</h4></a> -<p> -The first types are: char, uchar, short, ushort, int, long, ulong, float, double, void, Link, LinkData, ListBase, vec2s, vec2f, ... -<code class="block"><span class="descr"> array-id='TYPE' - |</span> -0005 2440 <span class="fade">6F 6C 64 5B 34 5D 5B 34 5D 00 00 00</span> [54 59 50 45] <span class="fade">old[4][4]...</span>TYPE -0005 2450 [C9 01 00 00] [63 68 61 72 00] [75 63 68 61 72 00][73 ....char.uchar.s<span class="descr"> - | | | | - number of types=457 'char\0' 'uchar\0' 's'</span><span class="fade"> - .... - .... (457 types)</span> -</code> -</p> - -<a name="DNA1-data-array-lengths" href="#DNA1-data-array-lengths"><h4>The lengths array</h4></a> -<p> -<code class="block"><span class="descr"> char uchar ushort short - array-id length length length length - 'TLEN' 1 1 2 2</span> -0005 3AA0 <span class="fade">45 00 00 00</span> [54 4C 45 4E] [01 00] [01 00] [02 00] [02 00] <span class="fade">E...</span>TLEN........ - <span class="fade">....</span> -0005 3AC0 [08 00] [04 00] [08 00] [10 00] [10 00] [14 00] [4C 00] [34 00] ............L.4.<span class="descr"> - 8 4 8 - ListBase vec2s vec2f ... etc - length len length </span><span class="fade"> - .... - .... (457 lengths, same as number of types)</span> -</code> -</p> - -<a name="DNA1-data-array-structures" href="#DNA1-data-array-structures"><h4>The structures array</h4></a> -<p> -<code class="block"><span class="descr"> array-id='STRC' - |</span> -0005 3E30 <span class="fade">40 00 38 00 60 00 00 00 00 00 00 00</span> [53 54 52 43] <span class="fade">@.8.`.......</span>STRC -0005 3E40 [8E 01 00 00] [0A 00] [02 00] [0A 00] [00 00] [0A 00] [01 00] ................<span class="descr"> - 398 10 2 10 0 10 0 - number of index fields index index index index - structures in <a href="#DNA1-data-array-types">types</a> in <a href="#DNA1-data-array-types">types</a> in <a href="#DNA1-data-array-names">names</a> in <a href="#DNA1-data-array-types">types</a> in <a href="#DNA1-data-array-names">names</a></span><span class="fade"> - ' '----------------' '-----------------' ' - ' field 0 field 1 ' - '--------------------------------------------------------' - structure 0 - .... - .... (398 structures, each one describeing own type, and type/name for each field)</span> -</code> -</p> -</div> - -<p> -The DNA structures inside a Blender 2.48 blend-file can be found at <a href="http://www.atmind.nl/blender/blender-sdna.html">http://www.atmind.nl/blender/blender-sdna.html</a>. - -If we understand the DNA part of the file it is now possible to read -information from other parts file-blocks. The next section will tell us -how. -</p> - -<a name="reading-scene-information" href="#reading-scene-information"><h2>Reading scene information</h2></a> - -<p> -Let us look at <a href="#example-file-block-header">the file-block header we have seen earlier</a>:<br> -</p> -<ul> -<li>the file-block identifier is <code>'SC'+0x00h</code></li> -<li>the SDNA index is 150</li> -<li>the file-block size is 1404 bytes</li> -</ul> -<p> -Now note that: -<ul> -<li>the structure at index 150 in the DNA is a structure of type 'Scene' (counting from 0).</li> -<li>the associated type ('Scene') in the DNA has the length of 1404 bytes.</li> -</ul> -</p> - -<p> -We can map the Scene structure on the data of the file-blocks. -But before we can do that, we have to flatten the Scene-structure. - -<code class="block">struct Scene { - ID id; <span class="descr">// 52 bytes long (ID is different a structure)</span> - AnimData *adt; <span class="descr">// 4 bytes long (pointer to an AnimData structure)</span> - Object *camera; <span class="descr">// 4 bytes long (pointer to an Object structure)</span> - World *world; <span class="descr">// 4 bytes long (pointer to an Object structure)</span> - ... - float cursor[3]; <span class="descr">// 12 bytes long (array of 3 floats)</span> - ... -}; -</code> - -The first field in the Scene-structure is of type 'ID' with the name 'id'. -Inside the list of DNA structures there is a structure defined for type 'ID' (structure index 17). - -<code class="block">struct ID { - void *next, *prev; - struct ID *newid; - struct Library *lib; - char name[24]; - short us; - short flag; - int icon_id; - IDProperty *properties; -}; -</code> - -The first field in this structure has type 'void' and name '*next'. <br> -Looking in the structure list there is no structure defined for type 'void': it is a simple type and therefore the data should be read. -The name '*next' describes a pointer. -As we see, the first 4 bytes of the data can be mapped to 'id.next'. -</p> - -<p> -Using this method we'll map a structure to its data. If we want to -read a specific field we know at which offset in the data it is located -and how much space it takes.<br> -The next table shows the output of this flattening process for some -parts of the Scene-structure. Not all rows are described in the table as - there is a lot of information in a Scene-structure. -</p> - -<table> -<caption>Flattened SDNA structure 150: Scene</caption> -<thead> -<tr><th>reference</th> - <th>structure</th> - <th>type</th><th>name</th> - <th>offset</th><th>size</th> - <th>description</th></tr> -</thead> -<tbody> -<tr><td>id.next</td><td><a href="#struct:ID">ID</a></td> - <td>void</td><td>*next</td> - <td>0</td> - <td>4</td> - <td>Refers to the next scene</td></tr> -<tr><td>id.prev</td><td><a href="#struct:ID">ID</a></td> - <td>void</td><td>*prev</td> - <td>4</td> - <td>4</td> - <td>Refers to the previous scene</td></tr> -<tr><td>id.newid</td><td><a href="#struct:ID">ID</a></td> - <td>ID</td><td>*newid</td> - <td>8</td> - <td>4</td> - <td></td></tr> -<tr><td>id.lib</td><td><a href="#struct:ID">ID</a></td> - <td>Library</td><td>*lib</td> - <td>12</td> - <td>4</td> - <td></td></tr> -<tr><td>id.name</td><td><a href="#struct:ID">ID</a></td> - <td>char</td><td>name[24]</td> - <td>16</td> - <td>24</td> - <td>'SC'+the name of the scene as displayed in Blender</td></tr> -<tr><td>id.us</td><td><a href="#struct:ID">ID</a></td> - <td>short</td><td>us</td> - <td>40</td> - <td>2</td> - <td></td></tr> -<tr><td>id.flag</td><td><a href="#struct:ID">ID</a></td> - <td>short</td><td>flag</td><td>42</td><td>2</td> - <td></td></tr> -<tr><td>id.icon_id</td><td><a href="#struct:ID">ID</a></td> - <td>int</td><td>icon_id</td><td>44</td> - <td>4</td> - <td></td></tr> -<tr><td>id.properties</td><td><a href="#struct:ID">ID</a></td> - <td>IDProperty</td><td>*properties</td> - <td>48</td> - <td>4</td> - <td></td></tr> -<tr><td>adt</td><td>Scene</td><td>AnimData</td> - <td>*adt</td> - <td>52</td> - <td>4</td> - <td></td></tr> -<tr><td>camera</td><td>Scene</td> - <td>Object</td> - <td>*camera</td> - <td>56</td> - <td>4</td> - <td>Pointer to the current camera</td></tr> -<tr><td>world</td><td>Scene</td> - <td>World</td> - <td>*world</td> - <td>60</td> - <td>4</td> - <td>Pointer to the current world</td></tr> - -<tr><td class="skip" colspan="7">Skipped rows</td></tr> - -<tr><td>r.xsch</td><td><a href="#struct:RenderData">RenderData</a> - </td><td>short</td><td>xsch</td><td>382</td><td>2</td> - <td>X-resolution of the output when rendered at 100%</td></tr> -<tr><td>r.ysch</td><td><a href="#struct:RenderData">RenderData</a> - </td><td>short</td><td>ysch</td><td>384</td><td>2</td> - <td>Y-resolution of the output when rendered at 100%</td></tr> -<tr><td>r.xparts</td><td><a href="#struct:RenderData">RenderData</a> - </td><td>short</td><td>xparts</td><td>386</td><td>2</td> - <td>Number of x-part used by the renderer</td></tr> -<tr><td>r.yparts</td><td><a href="#struct:RenderData">RenderData</a> - </td><td>short</td><td>yparts</td><td>388</td><td>2</td> - <td>Number of x-part used by the renderer</td></tr> - -<tr><td class="skip" colspan="7">Skipped rows</td></tr> - -<tr><td>gpd</td><td>Scene</td><td>bGPdata</td><td>*gpd</td><td>1376</td><td>4</td> - <td></td></tr> -<tr><td>physics_settings.gravity</td><td><a href="#struct:PhysicsSettings">PhysicsSettings</a> - </td><td>float</td><td>gravity[3]</td><td>1380</td><td>12</td> - <td></td></tr> -<tr><td>physics_settings.flag</td><td><a href="#struct:PhysicsSettings">PhysicsSettings</a> - </td><td>int</td><td>flag</td><td>1392</td><td>4</td> - <td></td></tr> -<tr><td>physics_settings.quick_cache_step</td><td><a href="#struct:PhysicsSettings">PhysicsSettings</a> - </td><td>int</td><td>quick_cache_step</td><td>1396</td><td>4</td> - <td></td></tr> -<tr><td>physics_settings.rt</td><td><a href="#struct:PhysicsSettings">PhysicsSettings</a> - </td><td>int</td><td>rt</td><td>1400</td><td>4</td> - <td></td></tr> -</tbody> -</table> - -<p> -We can now read the X and Y resolution of the Scene: -<ul> -<li>the X-resolution is located on offset 382 of the file-block-data and must be read as a -short.</li> -<li>the Y-resolution is located on offset 384 and is also a short</li> -</ul> -</p> - -<div class="box"> -<p class="box-title"> -Note -</p> -<p> -An array of chars can mean 2 things. The field contains readable -text or it contains an array of flags (not humanly readable). -</p> -</div> - -<div class="box"> -<p class="box-title"> -Note -</p> -<p> -A file-block containing a list refers to the DNA structure and has a count larger -than 1. For example Vertexes and Faces are stored in this way. -</p> -</div> - -</body> -</html> - diff --git a/intern/ghost/SConscript b/intern/ghost/SConscript index b67545f216a..6843c5fbcb2 100644 --- a/intern/ghost/SConscript +++ b/intern/ghost/SConscript @@ -60,6 +60,9 @@ else: if env['BF_GHOST_DEBUG']: defs.append('BF_GHOST_DEBUG') +if env['WITH_BF_ONSURFACEBRUSH']: + defs.append('WITH_ONSURFACEBRUSH') + incs = '. ../string #extern/glew/include #source/blender/imbuf #source/blender/makesdna ' + env['BF_OPENGL_INC'] if window_system in ('win32-vc', 'win32-mingw', 'cygwin', 'linuxcross', 'win64-vc'): incs = env['BF_WINTAB_INC'] + ' ' + incs diff --git a/intern/ghost/intern/GHOST_WindowCarbon.cpp b/intern/ghost/intern/GHOST_WindowCarbon.cpp index 1ffd117d658..9588ebce1b8 100644 --- a/intern/ghost/intern/GHOST_WindowCarbon.cpp +++ b/intern/ghost/intern/GHOST_WindowCarbon.cpp @@ -52,6 +52,9 @@ AGL_RGBA, AGL_DOUBLEBUFFER, AGL_ACCELERATED, AGL_DEPTH_SIZE, 32, +#ifdef WITH_ONSURFACEBRUSH +AGL_STENCIL_SIZE, 8, +#endif AGL_NONE, }; @@ -61,6 +64,9 @@ AGL_DOUBLEBUFFER, AGL_ACCELERATED, AGL_FULLSCREEN, AGL_DEPTH_SIZE, 32, +#ifdef WITH_ONSURFACEBRUSH +AGL_STENCIL_SIZE, 8, +#endif AGL_NONE, }; diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm index fb7d4a459c7..587046fb3a2 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.mm +++ b/intern/ghost/intern/GHOST_WindowCocoa.mm @@ -365,6 +365,13 @@ GHOST_WindowCocoa::GHOST_WindowCocoa( pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated; //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,; // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway + + +#ifdef WITH_ONSURFACEBRUSH + pixelFormatAttrsWindow[i++] = NSOpenGLPFAStencilSize; + pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 8; +#endif + pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize; pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32; @@ -376,6 +383,11 @@ GHOST_WindowCocoa::GHOST_WindowCocoa( // Multisample anti-aliasing pixelFormatAttrsWindow[i++] = NSOpenGLPFAMultisample; +#ifdef WITH_ONSURFACEBRUSH + pixelFormatAttrsWindow[i++] = NSOpenGLPFAStencilSize; + pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 8; +#endif + pixelFormatAttrsWindow[i++] = NSOpenGLPFASampleBuffers; pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 1; diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index aae1509fda1..b69f8fb6a45 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -98,7 +98,11 @@ static PIXELFORMATDESCRIPTOR sPreferredFormat = { 0, /* no accumulation buffer */ 0, 0, 0, 0, /* accum bits (ignored) */ 32, /* depth buffer */ +#ifdef WITH_ONSURFACEBRUSH + 8, /* stencil buffer */ +#else 0, /* no stencil buffer */ +#endif 0, /* no auxiliary buffers */ PFD_MAIN_PLANE, /* main layer */ 0, /* reserved */ @@ -1281,6 +1285,11 @@ static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd) { !(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this */ ( pfd.cDepthBits <= 8 ) || !(pfd.iPixelType == PFD_TYPE_RGBA)) + !(pfd.iPixelType == PFD_TYPE_RGBA) +#ifdef WITH_ONSURFACEBRUSH + || ( pfd.cStencilBits == 0) +#endif + ) return 0; weight = 1; /* it's usable */ diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp index aea5b5156d9..5dd1042eb6e 100644 --- a/intern/ghost/intern/GHOST_WindowX11.cpp +++ b/intern/ghost/intern/GHOST_WindowX11.cpp @@ -182,7 +182,7 @@ GHOST_WindowX11( Atom atoms[2]; int natom; int glxVersionMajor, glxVersionMinor; // As in GLX major.minor - + /* initialize incase X11 fails to load */ memset(&m_xtablet, 0, sizeof(m_xtablet)); m_visual= NULL; @@ -212,6 +212,9 @@ GHOST_WindowX11( attributes[i++] = GLX_BLUE_SIZE; attributes[i++] = 1; attributes[i++] = GLX_GREEN_SIZE; attributes[i++] = 1; attributes[i++] = GLX_DEPTH_SIZE; attributes[i++] = 1; +#ifdef WITH_ONSURFACEBRUSH + attributes[i++] = GLX_STENCIL_SIZE; attributes[i++] = 8; +#endif /* GLX >= 1.4 required for multi-sample */ if(samples && (glxVersionMajor >= 1) && (glxVersionMinor >= 4)) { attributes[i++] = GLX_SAMPLE_BUFFERS; attributes[i++] = 1; @@ -1289,11 +1292,11 @@ GHOST_WindowX11:: if(m_xtablet.EraserDevice) XCloseDevice(m_display, m_xtablet.EraserDevice); #endif /* WITH_X11_XINPUT */ - + if (m_context != s_firstContext) { glXDestroyContext(m_display, m_context); } - + if (p_owner == m_window) { XSetSelectionOwner(m_display, Primary_atom, None, CurrentTime); } diff --git a/source/SConscript b/source/SConscript index 0002cb4cf0b..3944a3c6df5 100644 --- a/source/SConscript +++ b/source/SConscript @@ -1,13 +1,14 @@ #!/usr/bin/python Import ('env') -SConscript(['blender/SConscript', 'creator/SConscript']) - +SConscript(['blender/SConscript', + 'kernel/SConscript', + 'creator/SConscript']) if env['WITH_BF_GAMEENGINE']: - SConscript (['gameengine/SConscript']) + SConscript (['gameengine/SConscript']) if env['WITH_BF_PLAYER']: - SConscript (['blenderplayer/bad_level_call_stubs/SConscript']) + SConscript (['blenderplayer/bad_level_call_stubs/SConscript']) if env['OURPLATFORM'] in ('win64-vc', 'win32-vc', 'win32-mingw', 'linuxcross'): - SConscript (['icons/SConscript']) + SConscript (['icons/SConscript']) diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index ad736cd07bf..15e9c812505 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -40,7 +40,7 @@ struct Brush; struct ImBuf; struct Scene; struct wmOperator; -// enum CurveMappingPreset; +//enum CurveMappingPreset; /* datablock functions */ struct Brush *add_brush(const char *name); @@ -94,22 +94,22 @@ struct ImBuf *brush_gen_radial_control_imbuf(struct Brush *br); /* unified strength and size */ -int brush_size(struct Brush *brush); +int brush_size(const struct Brush *brush); void brush_set_size(struct Brush *brush, int value); -int brush_use_locked_size(struct Brush *brush); +int brush_use_locked_size(const struct Brush *brush); void brush_set_use_locked_size(struct Brush *brush, int value); -int brush_use_alpha_pressure(struct Brush *brush); +int brush_use_alpha_pressure(const struct Brush *brush); void brush_set_use_alpha_pressure(struct Brush *brush, int value); -int brush_use_size_pressure(struct Brush *brush); +int brush_use_size_pressure(const struct Brush *brush); void brush_set_use_size_pressure(struct Brush *brush, int value); -float brush_unprojected_radius(struct Brush *brush); +float brush_unprojected_radius(const struct Brush *brush); void brush_set_unprojected_radius(struct Brush *brush, float value); -float brush_alpha(struct Brush *brush); +float brush_alpha(const struct Brush *brush); void brush_set_alpha(struct Brush *brush, float value); /* debugging only */ diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index ef16129e1e7..ca6bb7002eb 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -52,7 +52,7 @@ void free_paint(struct Paint *p); void copy_paint(struct Paint *src, struct Paint *tar); struct Paint *paint_get_active(struct Scene *sce); -struct Brush *paint_brush(struct Paint *paint); +struct Brush *paint_brush(const struct Paint *paint); void paint_brush_set(struct Paint *paint, struct Brush *br); /* testing face select mode diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 98c540f53b7..a4dd2270027 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -81,6 +81,7 @@ static void brush_set_defaults(Brush *brush) brush->autosmooth_factor= 0.0f; brush->crease_pinch_factor= 0.5f; brush->sculpt_plane = SCULPT_DISP_DIR_AREA; + brush->sculpt_plane_range= 1; brush->plane_offset= 0.0f; /* how far above or below the plane that is found by averaging the faces */ brush->plane_trim= 0.5f; brush->clone.alpha= 0.5f; @@ -93,7 +94,7 @@ static void brush_set_defaults(Brush *brush) brush->rgb[2]= 1.0f; /* BRUSH STROKE SETTINGS */ - brush->flag |= (BRUSH_SPACE|BRUSH_SPACE_ATTEN); + brush->flag |= (BRUSH_SPACE|BRUSH_SPACE_ATTEN|BRUSH_ADAPTIVE_SPACE); brush->spacing= 10; /* how far each brush dot should be spaced as a percentage of brush diameter */ brush->smooth_stroke_radius= 75; @@ -103,6 +104,10 @@ static void brush_set_defaults(Brush *brush) brush->jitter= 0.0f; + brush->adaptive_space_factor= 1; + + brush->frontface_angle= (float)(M_PI_2 * 80.0/90.0); + /* BRUSH TEXTURE SETTINGS */ default_mtex(&brush->mtex); @@ -192,7 +197,7 @@ void make_local_brush(Brush *brush) * - only local users: set flag * - mixed: make copy */ - + Main *bmain= G.main; Scene *scene; int local= 0, lib= 0; @@ -1139,7 +1144,7 @@ unsigned int *brush_gen_texture_cache(Brush *br, int half_side) TexResult texres= {0}; int hasrgb, ix, iy; int side = half_side * 2; - + if(mtex->tex) { float x, y, step = 2.0 / side, co[3]; @@ -1206,6 +1211,16 @@ struct ImBuf *brush_gen_radial_control_imbuf(Brush *br) } } + /* XXX: combine with loop above */ + /* XXX: sqrt and pow seem like they could be eliminated */ + /* invert the texture */ + for(i=0; i<side; ++i) { + for(j=0; j<side; ++j) { + if (sqrt(pow(i - half, 2) + pow(j - half, 2)) < half) + im->rect_float[i*side+j]= 1.0f - im->rect_float[i*side+j]; + } + } + MEM_freeN(texcache); } @@ -1214,7 +1229,15 @@ struct ImBuf *brush_gen_radial_control_imbuf(Brush *br) /* Unified Size and Strength */ -static void set_unified_settings(Brush *brush, short flag, int value) +// XXX: unified settings are stored in scene toolsettings +// to simplify things (from an API standpoint, not implementation), +// all of the flags in each scene are set to the same value. +// the 'get' functions return first value, since all should be the same +// This arrangement seems odd, is there a better whay? +// At this point, I think UserPrefs had been ruled out, but I do not know why. +// ~~~~jwilkins + +static void set_unified_settings(const Brush *brush, short flag, int value) { Scene *sce; for (sce= G.main->scene.first; sce; sce= sce->id.next) { @@ -1233,7 +1256,7 @@ static void set_unified_settings(Brush *brush, short flag, int value) } } -static short unified_settings(Brush *brush) +static short unified_settings(const Brush *brush) { Scene *sce; for (sce= G.main->scene.first; sce; sce= sce->id.next) { @@ -1251,7 +1274,7 @@ static short unified_settings(Brush *brush) return 0; } -static void set_unified_size(Brush *brush, int value) +static void set_unified_size(const Brush *brush, int value) { Scene *sce; for (sce= G.main->scene.first; sce; sce= sce->id.next) { @@ -1267,7 +1290,7 @@ static void set_unified_size(Brush *brush, int value) } } -static int unified_size(Brush *brush) +static int unified_size(const Brush *brush) { Scene *sce; for (sce= G.main->scene.first; sce; sce= sce->id.next) { @@ -1285,7 +1308,7 @@ static int unified_size(Brush *brush) return 35; // XXX magic number } -static void set_unified_alpha(Brush *brush, float value) +static void set_unified_alpha(const Brush *brush, float value) { Scene *sce; for (sce= G.main->scene.first; sce; sce= sce->id.next) { @@ -1301,7 +1324,7 @@ static void set_unified_alpha(Brush *brush, float value) } } -static float unified_alpha(Brush *brush) +static float unified_alpha(const Brush *brush) { Scene *sce; for (sce= G.main->scene.first; sce; sce= sce->id.next) { @@ -1319,7 +1342,7 @@ static float unified_alpha(Brush *brush) return 0.5f; // XXX magic number } -static void set_unified_unprojected_radius(Brush *brush, float value) +static void set_unified_unprojected_radius(const Brush *brush, float value) { Scene *sce; for (sce= G.main->scene.first; sce; sce= sce->id.next) { @@ -1335,7 +1358,7 @@ static void set_unified_unprojected_radius(Brush *brush, float value) } } -static float unified_unprojected_radius(Brush *brush) +static float unified_unprojected_radius(const Brush *brush) { Scene *sce; for (sce= G.main->scene.first; sce; sce= sce->id.next) { @@ -1358,11 +1381,9 @@ void brush_set_size(Brush *brush, int size) set_unified_size(brush, size); else brush->size= size; - - //WM_main_add_notifier(NC_BRUSH|NA_EDITED, brush); } -int brush_size(Brush *brush) +int brush_size(const Brush *brush) { return (unified_settings(brush) & SCULPT_PAINT_USE_UNIFIED_SIZE) ? unified_size(brush) : brush->size; } @@ -1378,11 +1399,9 @@ void brush_set_use_locked_size(Brush *brush, int value) else brush->flag &= ~BRUSH_LOCK_SIZE; } - - //WM_main_add_notifier(NC_BRUSH|NA_EDITED, brush); } -int brush_use_locked_size(Brush *brush) +int brush_use_locked_size(const Brush *brush) { return (unified_settings(brush) & SCULPT_PAINT_USE_UNIFIED_SIZE) ? (unified_settings(brush) & SCULPT_PAINT_UNIFIED_LOCK_BRUSH_SIZE) : (brush->flag & BRUSH_LOCK_SIZE); } @@ -1398,11 +1417,9 @@ void brush_set_use_size_pressure(Brush *brush, int value) else brush->flag &= ~BRUSH_SIZE_PRESSURE; } - - //WM_main_add_notifier(NC_BRUSH|NA_EDITED, brush); } -int brush_use_size_pressure(Brush *brush) +int brush_use_size_pressure(const Brush *brush) { return (unified_settings(brush) & SCULPT_PAINT_USE_UNIFIED_SIZE) ? (unified_settings(brush) & SCULPT_PAINT_UNIFIED_SIZE_PRESSURE) : (brush->flag & BRUSH_SIZE_PRESSURE); } @@ -1418,11 +1435,9 @@ void brush_set_use_alpha_pressure(Brush *brush, int value) else brush->flag &= ~BRUSH_ALPHA_PRESSURE; } - - //WM_main_add_notifier(NC_BRUSH|NA_EDITED, brush); } -int brush_use_alpha_pressure(Brush *brush) +int brush_use_alpha_pressure(const Brush *brush) { return (unified_settings(brush) & SCULPT_PAINT_USE_UNIFIED_ALPHA) ? (unified_settings(brush) & SCULPT_PAINT_UNIFIED_ALPHA_PRESSURE) : (brush->flag & BRUSH_ALPHA_PRESSURE); } @@ -1433,11 +1448,9 @@ void brush_set_unprojected_radius(Brush *brush, float unprojected_radius) set_unified_unprojected_radius(brush, unprojected_radius); else brush->unprojected_radius= unprojected_radius; - - //WM_main_add_notifier(NC_BRUSH|NA_EDITED, brush); } -float brush_unprojected_radius(Brush *brush) +float brush_unprojected_radius(const Brush *brush) { return (unified_settings(brush) & SCULPT_PAINT_USE_UNIFIED_SIZE) ? unified_unprojected_radius(brush) : brush->unprojected_radius; } @@ -1448,11 +1461,9 @@ void brush_set_alpha(Brush *brush, float alpha) set_unified_alpha(brush, alpha); else brush->alpha= alpha; - - //WM_main_add_notifier(NC_BRUSH|NA_EDITED, brush); } -float brush_alpha(Brush *brush) +float brush_alpha(const Brush *brush) { return (unified_settings(brush) & SCULPT_PAINT_USE_UNIFIED_ALPHA) ? unified_alpha(brush) : brush->alpha; } diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 334f018efc9..954834b51d7 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -68,10 +68,31 @@ /* used in UI and render */ Material defmaterial; +// initialize a slot for the current MatCap +static void init_matcap(void) +{ + default_tex(&matcap_tex); + matcap_tex.type = TEX_IMAGE; + + default_mtex(&matcap_mtex); + matcap_mtex.texco = TEXCO_NORM; + matcap_mtex.tex = &matcap_tex; + + //XXX: this is a hack to eliminate artifacts around the edges + //matcap_mtex.size[0] = 0.95f; + //matcap_mtex.size[1] = 0.95f; + + init_material(&matcap_ma); + matcap_ma.mode |= MA_SHLESS; + matcap_ma.shade_flag |= MA_OBCOLOR; + matcap_ma.mtex[0] = &matcap_mtex; +} + /* called on startup, creator.c */ void init_def_material(void) { init_material(&defmaterial); + init_matcap(); } /* not material itself */ diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index e2a07427ce2..467dad756bc 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1047,7 +1047,9 @@ Object *add_only_object(int type, const char *name) unit_m4(ob->constinv); unit_m4(ob->parentinv); unit_m4(ob->obmat); - ob->dt= OB_TEXTURE; + + ob->dt= OB_MATCAP; + ob->empty_drawtype= OB_PLAINAXES; ob->empty_drawsize= 1.0; diff --git a/source/blender/blenlib/BLI_cpu.h b/source/blender/blenlib/BLI_cpu.h index addcf1273b3..cb25fa0ed2b 100644 --- a/source/blender/blenlib/BLI_cpu.h +++ b/source/blender/blenlib/BLI_cpu.h @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: BLI_cpu.h 34966 2011-02-18 13:58:08Z jesterking $ * * ***** BEGIN GPL LICENSE BLOCK ***** * diff --git a/source/blender/blenlib/BLI_pbvh.h b/source/blender/blenlib/BLI_pbvh.h index 89dcf9bf02d..a4d131104fa 100644 --- a/source/blender/blenlib/BLI_pbvh.h +++ b/source/blender/blenlib/BLI_pbvh.h @@ -245,6 +245,10 @@ void BLI_pbvh_node_free_proxies(PBVHNode* node); PBVHProxyNode* BLI_pbvh_node_add_proxy(PBVH* bvh, PBVHNode* node); void BLI_pbvh_gather_proxies(PBVH* pbvh, PBVHNode*** nodes, int* totnode); +void BLI_pbvh_draw_nodes_in_sphere(PBVH *bvh, float location[3], float radius); +void BLI_pbvh_gather_nodes_in_sphere(PBVH *bvh, float location[3], float radius, PBVHNode ***nodes, int *totnode); +void BLI_pbvh_node_array_draw(PBVH *bvh, PBVHNode **nodes, int totnode); + //void BLI_pbvh_node_BB_reset(PBVHNode* node); //void BLI_pbvh_node_BB_expand(PBVHNode* node, float co[3]); diff --git a/source/blender/blenlib/intern/cpu.c b/source/blender/blenlib/intern/cpu.c index 860a0cae2d1..ad3cac5db84 100644 --- a/source/blender/blenlib/intern/cpu.c +++ b/source/blender/blenlib/intern/cpu.c @@ -1,6 +1,6 @@ /* * - * $Id$ + * $Id: cpu.c 35246 2011-02-27 20:37:56Z jesterking $ * * ***** BEGIN GPL LICENSE BLOCK ***** * diff --git a/source/blender/blenlib/intern/pbvh.c b/source/blender/blenlib/intern/pbvh.c index f7b79b35cbc..3ce389f162b 100644 --- a/source/blender/blenlib/intern/pbvh.c +++ b/source/blender/blenlib/intern/pbvh.c @@ -398,6 +398,7 @@ static void build_grids_leaf_node(PBVH *bvh, PBVHNode *node) GPU_build_grid_buffers(bvh->grids, node->prim_indices, node->totprim, bvh->gridsize); } + node->flag |= PBVH_UpdateDrawBuffers; } @@ -717,7 +718,7 @@ static PBVHNode *pbvh_iter_next_occluded(PBVHIter *iter) node= iter->stack[iter->stacksize].node; /* on a mesh with no faces this can happen - * can remove this check if we know meshes have at least 1 face */ + * can remove this check if we know meshes have at least 1 face */ if(node==NULL) return NULL; if(iter->scb && !iter->scb(node, iter->search_data)) continue; /* don't traverse, outside of search zone */ @@ -792,6 +793,8 @@ void BLI_pbvh_search_callback(PBVH *bvh, pbvh_iter_end(&iter); } +// XXX: todo: benchmark the cost of using malloc/free for this binary tree + typedef struct node_tree { PBVHNode* data; @@ -1321,7 +1324,7 @@ static int ray_face_intersection(float ray_start[3], float ray_normal[3], float dist; if ((isect_ray_tri_epsilon_v3(ray_start, ray_normal, t0, t1, t2, &dist, NULL, 0.1f) && dist < *fdist) || - (t3 && isect_ray_tri_epsilon_v3(ray_start, ray_normal, t0, t2, t3, &dist, NULL, 0.1f) && dist < *fdist)) + (t3 && isect_ray_tri_epsilon_v3(ray_start, ray_normal, t0, t2, t3, &dist, NULL, 0.1f) && dist < *fdist)) { *fdist = dist; return 1; @@ -1633,3 +1636,66 @@ void BLI_pbvh_gather_proxies(PBVH* pbvh, PBVHNode*** r_array, int* r_tot) *r_array= array; *r_tot= tot; } + +typedef struct { + float location[3]; + float radius_squared; +} SearchSphereData; + +/* Test AABB against sphere */ +static int search_sphere_cb(PBVHNode *node, void *data_v) +{ + SearchSphereData *data = data_v; + float *center = data->location, nearest[3]; + float t[3], bb_min[3], bb_max[3]; + int i; + + BLI_pbvh_node_get_BB(node, bb_min, bb_max); + + for(i = 0; i < 3; ++i) { + if(bb_min[i] > center[i]) + nearest[i] = bb_min[i]; + else if(bb_max[i] < center[i]) + nearest[i] = bb_max[i]; + else + nearest[i] = center[i]; + } + + sub_v3_v3v3(t, center, nearest); + + return dot_v3v3(t, t) < data->radius_squared; +} + +void BLI_pbvh_gather_nodes_in_sphere(PBVH *bvh, float location[3], float radius, PBVHNode ***nodes, int *totnode) +{ + SearchSphereData data; + + copy_v3_v3(data.location, location); + data.radius_squared= radius*radius; + + BLI_pbvh_search_gather(bvh, search_sphere_cb, &data, nodes, totnode); +} + +void BLI_pbvh_node_array_draw(PBVH *bvh, PBVHNode **nodes, int totnode) +{ + int i; + + pbvh_update_normals(bvh, nodes, totnode, NULL); + pbvh_update_draw_buffers(bvh, nodes, totnode, 1); + + for (i= 0; i < totnode; i++) + BLI_pbvh_node_draw(nodes[i], 0); +} + +void BLI_pbvh_draw_nodes_in_sphere(PBVH *bvh, float location[3], float radius) +{ + PBVHNode **nodes; + int totnode; + + BLI_pbvh_gather_nodes_in_sphere(bvh, location, radius, &nodes, &totnode); + + if (nodes) { + BLI_pbvh_node_array_draw(bvh, nodes, totnode); + MEM_freeN(nodes); + } +} diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 5f984a9268d..0d349739226 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -3697,7 +3697,7 @@ static void lib_link_object(FileData *fd, Main *main) ob->gpd= newlibadr_us(fd, ob->id.lib, ob->gpd); ob->duplilist= NULL; - + ob->id.flag -= LIB_NEEDLINK; /* if id.us==0 a new base will be created later on */ @@ -4128,7 +4128,7 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb) if(tmd->curfalloff) direct_link_curvemapping(fd, tmd->curfalloff); } - } +} } static void direct_link_object(FileData *fd, Object *ob) @@ -4816,6 +4816,8 @@ static void lib_link_screen(FileData *fd, Main *main) if(v3d->localvd) { v3d->localvd->camera= newlibadr(fd, sc->id.lib, v3d->localvd->camera); } + + v3d->matcap_ima= newlibadr_us(fd, sc->id.lib, v3d->matcap_ima); } else if(sl->spacetype==SPACE_IPO) { SpaceIpo *sipo= (SpaceIpo *)sl; @@ -5048,6 +5050,7 @@ void lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *curscene) /* not very nice, but could help */ if((v3d->layact & v3d->lay)==0) v3d->layact= v3d->lay; + v3d->matcap_ima = restore_pointer_by_name(newmain, (ID *)v3d->matcap_ima, 1); } else if(sl->spacetype==SPACE_IPO) { SpaceIpo *sipo= (SpaceIpo *)sl; @@ -6530,10 +6533,10 @@ static void area_add_window_regions(ScrArea *sa, SpaceLink *sl, ListBase *lb) ar->v2d.min[0]= 0.0f; ar->v2d.min[1]= 0.0f; - + ar->v2d.max[0]= MAXFRAMEF; ar->v2d.max[1]= FLT_MAX; - + ar->v2d.minzoom= 0.01f; ar->v2d.maxzoom= 50; ar->v2d.scroll = (V2D_SCROLL_BOTTOM|V2D_SCROLL_SCALE_HORIZONTAL); @@ -11252,10 +11255,27 @@ static void do_versions(FileData *fd, Library *lib, Main *main) } } - /* GSOC 2010 Sculpt - New settings for Brush */ + /* Sanity check on Sculpt/Paint settings */ + /* These sanity checks apply to all versions */ + { + Scene *sce; + for (sce= main->scene.first; sce; sce= sce->id.next) { + if (sce->toolsettings->sculpt_paint_unified_alpha == 0) + sce->toolsettings->sculpt_paint_unified_alpha = 0.5f; + if (sce->toolsettings->sculpt_paint_unified_unprojected_radius == 0) + sce->toolsettings->sculpt_paint_unified_unprojected_radius = 0.125f; + + if (sce->toolsettings->sculpt_paint_unified_size == 0) + sce->toolsettings->sculpt_paint_unified_size = 35; + } + } + + { + Brush *brush; for (brush= main->brush.first; brush; brush= brush->id.next) { - /* Sanity Check */ + /* Sanity Check for Brushes 2.52 */ + /* These sanity checks apply to all versions */ // infinite number of dabs if (brush->spacing == 0) @@ -11268,6 +11288,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main) // bad radius if (brush->unprojected_radius == 0) brush->unprojected_radius = 0.125f; + brush->unprojected_radius = 0.125; // unusable size if (brush->size == 0) @@ -11295,11 +11316,26 @@ static void do_versions(FileData *fd, Library *lib, Main *main) // same as dots if (brush->rate == 0) - brush->rate = 0.1f; + brush->rate= 0.1f; + + /* Sanity Check for Brushes 2.53 */ + + // divide by zero + if (brush->adaptive_space_factor == 0) + brush->adaptive_space_factor= 1; + + if (brush->sculpt_plane_range == 0) + brush->sculpt_plane_range= 1; + + if (brush->frontface_angle== 0) + brush->frontface_angle= (float)(M_PI_2 * 80.0/90.0); + + if (brush->layer_distance== 0) + brush->layer_distance= 0.25f; - /* New Settings */ if (main->versionfile < 252 || (main->versionfile == 252 && main->subversionfile < 5)) { brush->flag |= BRUSH_SPACE_ATTEN; // explicitly enable adaptive space + brush->flag |= BRUSH_SPACE_ATTEN; // explicitly enable adaptive strength // spacing was originally in pixels, convert it to percentage for new version // size should not be zero due to sanity check above @@ -11323,21 +11359,25 @@ static void do_versions(FileData *fd, Library *lib, Main *main) brush->sub_col[2] = 1.00f; } } + + if (main->versionfile < 253) + brush->flag |= BRUSH_ADAPTIVE_SPACE; // explicitly enable adaptive space } } - /* GSOC Sculpt 2010 - Sanity check on Sculpt/Paint settings */ + /* GSOC Sculpt 2011 - MatCap */ if (main->versionfile < 253) { - Scene *sce; - for (sce= main->scene.first; sce; sce= sce->id.next) { - if (sce->toolsettings->sculpt_paint_unified_alpha == 0) - sce->toolsettings->sculpt_paint_unified_alpha = 0.5f; + Object *ob; - if (sce->toolsettings->sculpt_paint_unified_unprojected_radius == 0) - sce->toolsettings->sculpt_paint_unified_unprojected_radius = 0.125f; + /* MatCaps */ + for (ob=main->object.first; ob; ob=ob->id.next) { + Scene *sce; - if (sce->toolsettings->sculpt_paint_unified_size == 0) - sce->toolsettings->sculpt_paint_unified_size = 35; + for (sce= main->scene.first; sce; sce= sce->id.next) { + /* If max drawtype is textured then assume user won't mind if we bump it up to use MatCaps, */ + /* Otherwise, assume that if max drawtype is less than textured then user doesn't want to use MatCaps */ + if (ob->dt == OB_TEXTURE) + ob->dt = OB_MATCAP; } } @@ -11556,11 +11596,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main) sc->redraws_flag = TIME_ALL_3D_WIN|TIME_ALL_ANIM_WIN; } } - - for (brush= main->brush.first; brush; brush= brush->id.next) { - if(brush->height == 0) - brush->height= 0.4f; - } + } /* replace 'rim material' option for in offset*/ for(ob = main->object.first; ob; ob = ob->id.next) { @@ -12389,13 +12425,13 @@ static void expand_object_expandModifiers(void *userData, Object *UNUSED(ob), ID **idpoin) { struct { FileData *fd; Main *mainvar; } *data= userData; - + FileData *fd= data->fd; Main *mainvar= data->mainvar; - + expand_doit(fd, mainvar, *idpoin); -} - + } + static void expand_object(FileData *fd, Main *mainvar, Object *ob) { ParticleSystem *psys; diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h index 33d2dfcf4c5..3fd700aa48a 100644 --- a/source/blender/editors/include/ED_sculpt.h +++ b/source/blender/editors/include/ED_sculpt.h @@ -58,4 +58,8 @@ int ED_undo_paint_step(struct bContext *C, int type, int step, const char *name) void ED_undo_paint_free(void); int ED_undo_paint_valid(int type, const char *name); +void ED_draw_paint_overlay(const struct bContext *C, struct ARegion *ar); +void ED_draw_on_surface_cursor(float modelview[16], float projection[16], float col[3], float alpha, float size[3], int viewport[4], float location[3], float inner_radius, float outer_radius, int brush_size); +void ED_draw_fixed_overlay_on_surface(float modelview[16], float projection[16], float size[3], int viewport[4], float location[3], float outer_radius, struct Sculpt *sd, struct Brush *brush, struct ViewContext *vc, float t, float b, float l, float r, float angle); + #endif diff --git a/source/blender/editors/sculpt_paint/SConscript b/source/blender/editors/sculpt_paint/SConscript index 90b56ded2cd..f35250d4863 100644 --- a/source/blender/editors/sculpt_paint/SConscript +++ b/source/blender/editors/sculpt_paint/SConscript @@ -21,4 +21,7 @@ if env['OURPLATFORM'] == 'linuxcross': if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'): incs += ' ' + env['BF_PTHREADS_INC'] +if env['WITH_BF_ONSURFACEBRUSH']: + defs.append('WITH_ONSURFACEBRUSH') + env.BlenderLib ( 'bf_editors_sculpt_paint', sources, Split(incs), defines=defs, libtype=['core'], priority=[40] ) diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 58c3446673c..87f964a8928 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -30,7 +30,6 @@ * \ingroup edsculpt */ - #include "MEM_guardedalloc.h" #include "BLI_math.h" @@ -39,6 +38,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_brush_types.h" +#include "DNA_meshdata_types.h" #include "RNA_access.h" @@ -58,7 +58,9 @@ #include "paint_intern.h" /* still needed for sculpt_stroke_get_location, should be removed eventually (TODO) */ -#include "sculpt_intern.h" +#include "sculpt_intern.h" // XXX, for expedience in getting this working, refactor later (or this just shows that this needs unification) + +#include "BKE_DerivedMesh.h" #include <float.h> #include <math.h> @@ -81,7 +83,10 @@ typedef struct PaintStroke { int stroke_started; /* event that started stroke, for modal() return */ int event_type; - + + /* event that started stroke, for modal() return */ + int event_type; + StrokeGetLocation get_location; StrokeTestStart test_start; StrokeUpdateStep update_step; @@ -108,6 +113,107 @@ static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata glDisable(GL_LINE_SMOOTH); } +#if 0 + +// grid texture for testing + +#define GRID_WIDTH 8 +#define GRID_LENGTH 8 + +#define W (0xFFFFFFFF) +#define G (0x00888888) +#define E (0xE1E1E1E1) +#define C (0xC3C3C3C3) +#define O (0xB4B4B4B4) +#define Q (0xA9A9A9A9) + +static unsigned grid_texture0[256] = +{ + W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W, + W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W, +}; + +static unsigned grid_texture1[64] = +{ + C,C,C,C,C,C,C,C, + C,G,G,G,G,G,G,C, + C,G,G,G,G,G,G,C, + C,G,G,G,G,G,G,C, + C,G,G,G,G,G,G,C, + C,G,G,G,G,G,G,C, + C,G,G,G,G,G,G,C, + C,C,C,C,C,C,C,C, +}; + +static unsigned grid_texture2[16] = +{ + O,O,O,O, + O,G,G,O, + O,G,G,O, + O,O,O,O, +}; + +static unsigned grid_texture3[4] = +{ + Q,Q, + Q,Q, +}; + +static unsigned grid_texture4[1] = +{ + Q, +}; + +#undef W +#undef G +#undef E +#undef C +#undef O +#undef Q + +static void load_grid() +{ + static GLuint overlay_texture; + + if (!overlay_texture) { + //GLfloat largest_supported_anisotropy; + + glGenTextures(1, &overlay_texture); + glBindTexture(GL_TEXTURE_2D, overlay_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture0); + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGB, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture1); + glTexImage2D(GL_TEXTURE_2D, 2, GL_RGB, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture2); + glTexImage2D(GL_TEXTURE_2D, 3, GL_RGB, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture3); + glTexImage2D(GL_TEXTURE_2D, 4, GL_RGB, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture4); + glEnable(GL_TEXTURE_2D); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); + + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + //glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest_supported_anisotropy); + //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, largest_supported_anisotropy); + } +} + +#endif + typedef struct Snapshot { float size[3]; float ofs[3]; @@ -119,7 +225,7 @@ typedef struct Snapshot { int curve_changed_timestamp; } Snapshot; -static int same_snap(Snapshot* snap, Brush* brush, ViewContext* vc) +static int same_snap(Snapshot* snap, Brush* brush, ARegion *ar) { MTex* mtex = &brush->mtex; @@ -131,14 +237,15 @@ static int same_snap(Snapshot* snap, Brush* brush, ViewContext* vc) mtex->size[0] == snap->size[0] && mtex->size[1] == snap->size[1] && mtex->size[2] == snap->size[2] && - mtex->rot == snap->rot) && - ((mtex->brush_map_mode == MTEX_MAP_MODE_FIXED && brush_size(brush) <= snap->brush_size) || (brush_size(brush) == snap->brush_size)) && // make brush smaller shouldn't cause a resample - mtex->brush_map_mode == snap->brush_map_mode && - vc->ar->winx == snap->winx && - vc->ar->winy == snap->winy; + (ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_WRAP, MTEX_MAP_MODE_FIXED) || mtex->rot == snap->rot)) && // rotation does not cause fixed texture to be regenerated + ((ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_WRAP, MTEX_MAP_MODE_FIXED) && brush_size(brush) <= snap->brush_size) || (brush_size(brush) == snap->brush_size)) && // make brush smaller shouldn't cause a resample + (mtex->brush_map_mode == snap->brush_map_mode || + (ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_WRAP, MTEX_MAP_MODE_FIXED) && ELEM(snap->brush_map_mode, MTEX_MAP_MODE_WRAP, MTEX_MAP_MODE_FIXED))) && // texture doesn't have to be redone if switching from fixed to wrapped or visa versa + ar->winx == snap->winx && + ar->winy == snap->winy; } -static void make_snap(Snapshot* snap, Brush* brush, ViewContext* vc) +static void make_snap(Snapshot* snap, Brush* brush, ARegion *ar) { if (brush->mtex.tex) { snap->brush_map_mode = brush->mtex.brush_map_mode; @@ -148,17 +255,17 @@ static void make_snap(Snapshot* snap, Brush* brush, ViewContext* vc) } else { snap->brush_map_mode = -1; - snap->ofs[0]= snap->ofs[1]= snap->ofs[2]= -1; - snap->size[0]= snap->size[1]= snap->size[2]= -1; + snap->ofs[0]= snap->ofs[0]= snap->ofs[0]= -1; + snap->size[0]= snap->size[0]= snap->size[0]= -1; snap->rot = -1; } snap->brush_size = brush_size(brush); - snap->winx = vc->ar->winx; - snap->winy = vc->ar->winy; + snap->winx = ar->winx; + snap->winy = ar->winy; } -static int load_tex(Sculpt *sd, Brush* br, ViewContext* vc) +static int paint_load_overlay_tex(Sculpt *sd, Brush* br, ViewContext* vc) { static GLuint overlay_texture = 0; static int init = 0; @@ -178,7 +285,7 @@ static int load_tex(Sculpt *sd, Brush* br, ViewContext* vc) #endif if (br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED && !br->mtex.tex) return 0; - + refresh = !overlay_texture || (br->mtex.tex && @@ -186,7 +293,7 @@ static int load_tex(Sculpt *sd, Brush* br, ViewContext* vc) br->mtex.tex->preview->changed_timestamp[0] != tex_changed_timestamp)) || !br->curve || br->curve->changed_timestamp != curve_changed_timestamp || - !same_snap(&snap, br, vc); + !same_snap(&snap, br, ar); if (refresh) { if (br->mtex.tex && br->mtex.tex->preview) @@ -195,9 +302,9 @@ static int load_tex(Sculpt *sd, Brush* br, ViewContext* vc) if (br->curve) curve_changed_timestamp = br->curve->changed_timestamp; - make_snap(&snap, br, vc); + make_snap(&snap, br, ar); - if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { + if (ELEM(br->mtex.brush_map_mode, MTEX_MAP_MODE_WRAP, MTEX_MAP_MODE_FIXED)) { int s = brush_size(br); int r = 1; @@ -226,7 +333,7 @@ static int load_tex(Sculpt *sd, Brush* br, ViewContext* vc) old_size = size; } - buffer = MEM_mallocN(sizeof(GLubyte)*size*size, "load_tex"); + buffer = MEM_mallocN(sizeof(GLubyte)*size*size, "paint_load_overlay_tex"); #pragma omp parallel for schedule(static) if (sd->flags & SCULPT_USE_OPENMP) for (j= 0; j < size; j++) { @@ -239,7 +346,7 @@ static int load_tex(Sculpt *sd, Brush* br, ViewContext* vc) // largely duplicated from tex_strength const float rotation = -br->mtex.rot; - float radius = brush_size(br); + float diameter = brush_size(br); int index = j*size + i; float x; float avg; @@ -251,8 +358,8 @@ static int load_tex(Sculpt *sd, Brush* br, ViewContext* vc) y -= 0.5f; if (br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) { - x *= vc->ar->winx / radius; - y *= vc->ar->winy / radius; + x *= ar->winx / diameter; + y *= ar->winy / diameter; } else { x *= 2; @@ -265,8 +372,11 @@ static int load_tex(Sculpt *sd, Brush* br, ViewContext* vc) /* it is probably worth optimizing for those cases where the texture is not rotated by skipping the calls to atan2, sqrtf, sin, and cos. */ - if (br->mtex.tex && (rotation > 0.001f || rotation < -0.001f)) { - const float angle = atan2f(y, x) + rotation; + if ((br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) && + br->mtex.tex && + (rotation > 0.001f || rotation < -0.001f)) + { + const float angle= atan2(y, x) + rotation; x = len * cosf(angle); y = len * sinf(angle); @@ -282,7 +392,7 @@ static int load_tex(Sculpt *sd, Brush* br, ViewContext* vc) avg += br->texture_sample_bias; - if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) + if (ELEM(br->mtex.brush_map_mode, MTEX_MAP_MODE_WRAP, MTEX_MAP_MODE_FIXED)) avg *= brush_curve_strength(br, len, 1); /* Falloff curve */ buffer[index] = 255 - (GLubyte)(255*avg); @@ -321,14 +431,79 @@ static int load_tex(Sculpt *sd, Brush* br, ViewContext* vc) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + if (ELEM(br->mtex.brush_map_mode, MTEX_MAP_MODE_WRAP, MTEX_MAP_MODE_FIXED)) { + float clear[4]= {0,0,0,0}; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, clear); } return 1; } +void ED_draw_paint_overlay(const bContext* C, ARegion *ar) +{ + Paint *paint = paint_get_active(CTX_data_scene(C)); + Brush *brush = paint_brush(paint); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + + if (!sd->sculpting && + brush && + brush->mtex.brush_map_mode == MTEX_MAP_MODE_TILED && + brush->flag & BRUSH_TEXTURE_OVERLAY) + { + glPushAttrib( + GL_COLOR_BUFFER_BIT| + GL_CURRENT_BIT| + GL_DEPTH_BUFFER_BIT| + GL_ENABLE_BIT| + GL_LINE_BIT| + GL_POLYGON_BIT| + GL_STENCIL_BUFFER_BIT| + GL_TRANSFORM_BIT| + GL_VIEWPORT_BIT| + GL_TEXTURE_BIT); + + if (paint_load_overlay_tex(sd, brush, ar)) { + glEnable(GL_BLEND); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_FALSE); + glDepthFunc(GL_ALWAYS); + + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glLoadIdentity(); + + glColor4f( + U.sculpt_paint_overlay_col[0], + U.sculpt_paint_overlay_col[1], + U.sculpt_paint_overlay_col[2], + brush->texture_overlay_alpha / 100.0f); + + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 0.0f); + glVertex2f(0.0f, 0.0f); + + glTexCoord2f(1.0f, 0.0f); + glVertex2f(ar->winx, 0.0f); + + glTexCoord2f(1.0f, 1.0f); + glVertex2f(ar->winx, ar->winy); + + glTexCoord2f(0.0f, 1.0f); + glVertex2f(0.0f, ar->winy); + glEnd(); + + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + } + + glPopAttrib(); + } +} + static int project_brush_radius(RegionView3D* rv3d, float radius, float location[3], bglMats* mats) { float view[3], nonortho[3], ortho[3], offset[3], p1[2], p2[2]; @@ -369,8 +544,15 @@ static int project_brush_radius(RegionView3D* rv3d, float radius, float location return len_v2v2(p1, p2); } -static int sculpt_get_brush_geometry(bContext* C, int x, int y, int* pixel_radius, - float location[3]) +static int sculpt_get_brush_geometry( + bContext* C, + int x, + int y, + int* pixel_radius, + float location[3], + float modelview[16], + float projection[16], + int viewport[4]) { struct PaintStroke *stroke; float window[2]; @@ -381,11 +563,20 @@ static int sculpt_get_brush_geometry(bContext* C, int x, int y, int* pixel_radiu window[0] = x + stroke->vc.ar->winrct.xmin; window[1] = y + stroke->vc.ar->winrct.ymin; - if(stroke->vc.obact->sculpt && stroke->vc.obact->sculpt->pbvh && - sculpt_stroke_get_location(C, stroke, location, window)) { - *pixel_radius = project_brush_radius(stroke->vc.rv3d, - brush_unprojected_radius(stroke->brush), - location, &stroke->mats); + memcpy(modelview, stroke->vc.rv3d->viewmat, sizeof(float[16])); + memcpy(projection, stroke->vc.rv3d->winmat, sizeof(float[16])); + memcpy(viewport, stroke->mats.viewport, sizeof(int[4])); + + if (stroke->vc.obact->sculpt && + stroke->vc.obact->sculpt->pbvh && + sculpt_stroke_get_location(C, stroke, location, window)) + { + *pixel_radius = + project_brush_radius( + stroke->vc.rv3d, + brush_unprojected_radius(stroke->brush), + location, + &stroke->mats); if (*pixel_radius == 0) *pixel_radius = brush_size(stroke->brush); @@ -407,174 +598,689 @@ static int sculpt_get_brush_geometry(bContext* C, int x, int y, int* pixel_radiu return hit; } -/* Draw an overlay that shows what effect the brush's texture will - have on brush strength */ -/* TODO: sculpt only for now */ -static void paint_draw_alpha_overlay(Sculpt *sd, Brush *brush, - ViewContext *vc, int x, int y) -{ - rctf quad; - - /* check for overlay mode */ - if(!(brush->flag & BRUSH_TEXTURE_OVERLAY) || - !(ELEM(brush->mtex.brush_map_mode, MTEX_MAP_MODE_FIXED, MTEX_MAP_MODE_TILED))) - return; - - /* save lots of GL state - TODO: check on whether all of these are needed? */ - glPushAttrib(GL_COLOR_BUFFER_BIT| - GL_CURRENT_BIT| - GL_DEPTH_BUFFER_BIT| - GL_ENABLE_BIT| - GL_LINE_BIT| - GL_POLYGON_BIT| - GL_STENCIL_BUFFER_BIT| - GL_TRANSFORM_BIT| - GL_VIEWPORT_BIT| - GL_TEXTURE_BIT); - - if(load_tex(sd, brush, vc)) { - glEnable(GL_BLEND); +static void set_brush_dot_location(float *modelview, float location[3], const char symm, const char axis, float angle) +{ + glLoadMatrixf(modelview); + + angle *= (float)(180.0/M_PI); + switch (axis) { + case 'X': + glRotatef(angle, 1.0f, 0.0f, 0.0f); + break; + + case 'Y': + glRotatef(angle, 0.0f, 1.0f, 0.0f); + break; + + case 'Z': + glRotatef(angle, 0.0f, 0.0f, 1.0f); + break; + } + + glTranslatef( + !(symm & SCULPT_SYMM_X) ? location[0]: -location[0], + !(symm & SCULPT_SYMM_Y) ? location[1]: -location[1], + !(symm & SCULPT_SYMM_Z) ? location[2]: -location[2]); +} + +static void draw_brush_dot(GLUquadric *sphere, float radius) +{ + gluSphere(sphere, radius, 20, 20); +} + +static void draw_radial_symmetry_dot(float *modelview, float location[3], const char symm, int radial_symm[3], const int axis, GLUquadric *sphere, float radius) +{ + int i; + + for(i = 1; i < radial_symm[axis-'X']; ++i) { + const float angle = 2*M_PI*i/radial_symm[axis-'X']; + set_brush_dot_location(modelview, location, symm, axis, angle); + draw_brush_dot(sphere, radius); + } +} + +static void draw_symmetric_brush_dots(Sculpt *sd, float location[3], float col[3], float *modelview, float *projection, int viewport[4], float radius, int draw_first) +{ + int i; + short symm= sd->flags & 7; + + GLUquadric* sphere; + + sphere = gluNewQuadric(); + + glPushAttrib( + GL_COLOR_BUFFER_BIT| + GL_CURRENT_BIT| + GL_DEPTH_BUFFER_BIT| + GL_ENABLE_BIT| + GL_LINE_BIT| + GL_POLYGON_BIT| + GL_STENCIL_BUFFER_BIT| + GL_TRANSFORM_BIT| + GL_VIEWPORT_BIT| + GL_TEXTURE_BIT); + + glColor4f(col[0], col[1], col[2], 0.5f); + + glEnable(GL_BLEND); + + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixf(projection); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_FALSE); + + glEnable(GL_DEPTH_TEST); + + glEnable(GL_CULL_FACE); + + /* symm is a bit combination of XYZ - 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ + for(i = 0; i <= symm; ++i) { + if(i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + + if (draw_first) { + set_brush_dot_location(modelview, location, i, 0, 0); + draw_brush_dot(sphere, radius); + } + + draw_radial_symmetry_dot(modelview, location, i, sd->radial_symm, 'X', sphere, radius); + draw_radial_symmetry_dot(modelview, location, i, sd->radial_symm, 'Y', sphere, radius); + draw_radial_symmetry_dot(modelview, location, i, sd->radial_symm, 'Z', sphere, radius); + } + } + + glPopAttrib(); +} + +static void draw_fixed_overlay(Sculpt *sd, Brush *brush, ViewContext *vc, rctf* quad, float angle) +{ + glPushAttrib( + GL_COLOR_BUFFER_BIT| + GL_DEPTH_BUFFER_BIT| + GL_ENABLE_BIT| + GL_TRANSFORM_BIT| + GL_CURRENT_BIT| + GL_TEXTURE_BIT); + + if (paint_load_overlay_tex(sd, brush, vc->ar)) { glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_FALSE); glDepthFunc(GL_ALWAYS); + glEnable(GL_BLEND); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_TEXTURE); glPushMatrix(); glLoadIdentity(); - if(brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { - /* brush rotation */ - glTranslatef(0.5, 0.5, 0); - glRotatef((double)((brush->flag & BRUSH_RAKE) ? - sd->last_angle : sd->special_rotation) * (180.0/M_PI), - 0.0, 0.0, 1.0); - glTranslatef(-0.5f, -0.5f, 0); - - /* scale based on tablet pressure */ - if(sd->draw_pressure && brush_use_size_pressure(brush)) { - glTranslatef(0.5f, 0.5f, 0); - glScalef(1.0f/sd->pressure_value, 1.0f/sd->pressure_value, 1); - glTranslatef(-0.5f, -0.5f, 0); - } + glTranslatef(0.5f, 0.5f, 0.0f); - if(sd->draw_anchored) { - const float *aim = sd->anchored_initial_mouse; - const rcti *win = &vc->ar->winrct; - quad.xmin = aim[0]-sd->anchored_size - win->xmin; - quad.ymin = aim[1]-sd->anchored_size - win->ymin; - quad.xmax = aim[0]+sd->anchored_size - win->xmin; - quad.ymax = aim[1]+sd->anchored_size - win->ymin; - } - else { - const int radius= brush_size(brush); - quad.xmin = x - radius; - quad.ymin = y - radius; - quad.xmax = x + radius; - quad.ymax = y + radius; - } - } - else { - quad.xmin = 0; - quad.ymin = 0; - quad.xmax = vc->ar->winrct.xmax - vc->ar->winrct.xmin; - quad.ymax = vc->ar->winrct.ymax - vc->ar->winrct.ymin; + glRotatef(angle, 0.0f, 0.0f, 1.0f); + + glTranslatef(-0.5f, -0.5f, 0.0f); + + if (sd->draw_pressure && (brush->flag & BRUSH_SIZE_PRESSURE)) { + glTranslatef(0.5f, 0.5f, 0.0f); + glScalef(1.0f/sd->pressure_value, 1.0f/sd->pressure_value, 1); + glTranslatef(-0.5f, -0.5f, 0.0f); } - /* set quad color */ - glColor4f(U.sculpt_paint_overlay_col[0], - U.sculpt_paint_overlay_col[1], - U.sculpt_paint_overlay_col[2], - brush->texture_overlay_alpha / 100.0f); + glColor4f( + U.sculpt_paint_overlay_col[0], + U.sculpt_paint_overlay_col[1], + U.sculpt_paint_overlay_col[2], + brush->texture_overlay_alpha / 100.0f); - /* draw textured quad */ glBegin(GL_QUADS); - glTexCoord2f(0, 0); - glVertex2f(quad.xmin, quad.ymin); - glTexCoord2f(1, 0); - glVertex2f(quad.xmax, quad.ymin); - glTexCoord2f(1, 1); - glVertex2f(quad.xmax, quad.ymax); - glTexCoord2f(0, 1); - glVertex2f(quad.xmin, quad.ymax); + glTexCoord2f(0.0f, 0.0f); + glVertex2f(quad->xmin, quad->ymin); + + glTexCoord2f(1.0f, 0.0f); + glVertex2f(quad->xmax, quad->ymin); + + glTexCoord2f(1.0f, 1.0f); + glVertex2f(quad->xmax, quad->ymax); + + glTexCoord2f(0.0f, 1.0f); + glVertex2f(quad->xmin, quad->ymax); glEnd(); glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); } glPopAttrib(); } -/* Special actions taken when paint cursor goes over mesh */ -/* TODO: sculpt only for now */ -static void paint_cursor_on_hit(Sculpt *sd, Brush *brush, ViewContext *vc, - float location[3], float *visual_strength) +void ED_draw_on_surface_cursor(float modelview[16], float projection[16], float col[3], float alpha, float size[3], int viewport[4], float location[3], float inner_radius, float outer_radius, int brush_size) { - float unprojected_radius, projected_radius; + GLUquadric* sphere; + + glPushAttrib( + GL_COLOR_BUFFER_BIT| + GL_CURRENT_BIT| + GL_DEPTH_BUFFER_BIT| + GL_ENABLE_BIT| + GL_LINE_BIT| + GL_POLYGON_BIT| + GL_STENCIL_BUFFER_BIT| + GL_TRANSFORM_BIT| + GL_VIEWPORT_BIT| + GL_TEXTURE_BIT); + + glColor4f(col[0], col[1], col[2], alpha); - /* TODO: check whether this should really only be done when - brush is over mesh? */ - if(sd->draw_pressure && brush_use_alpha_pressure(brush)) - (*visual_strength) *= sd->pressure_value; + glEnable(GL_BLEND); - if(sd->draw_anchored) - projected_radius = sd->anchored_size; - else { - if(brush->flag & BRUSH_ANCHORED) - projected_radius = 8; - else - projected_radius = brush_size(brush); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixf(modelview); + + glTranslatef(location[0], location[1], location[2]); + + glScalef(size[0], size[1], size[2]); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixf(projection); + + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glDepthMask(GL_FALSE); + + glDisable(GL_CULL_FACE); + + glEnable(GL_DEPTH_TEST); + + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + glEnable(GL_STENCIL_TEST); + + glStencilFunc(GL_ALWAYS, 3, 0xFF); + glStencilOp(GL_KEEP, GL_REPLACE, GL_KEEP); + + sphere = gluNewQuadric(); + + gluSphere(sphere, outer_radius, 40, 40); + + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glStencilOp(GL_KEEP, GL_DECR, GL_KEEP); + + if (brush_size >= 8) + gluSphere(sphere, inner_radius, 40, 40); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + glStencilFunc(GL_EQUAL, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + gluSphere(sphere, outer_radius, 40, 40); + + glStencilFunc(GL_EQUAL, 3, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + gluSphere(sphere, outer_radius, 40, 40); + + gluDeleteQuadric(sphere); + + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + glPopAttrib(); +} + +static void stencil_brush_on_surface(float modelview[16], float projection[16], float size[3], int viewport[4], float location[3], float outer_radius) +{ + GLUquadric* sphere; + + glPushAttrib( + GL_COLOR_BUFFER_BIT| + GL_CURRENT_BIT| + GL_DEPTH_BUFFER_BIT| + GL_ENABLE_BIT| + GL_LINE_BIT| + GL_POLYGON_BIT| + GL_STENCIL_BUFFER_BIT| + GL_TRANSFORM_BIT| + GL_VIEWPORT_BIT| + GL_TEXTURE_BIT); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixf(modelview); + + glTranslatef(location[0], location[1], location[2]); + + glScalef(size[0], size[1], size[2]); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixf(projection); + + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glDepthMask(GL_FALSE); + + glDisable(GL_CULL_FACE); + + glEnable(GL_DEPTH_TEST); + + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + glEnable(GL_STENCIL_TEST); + + glStencilFunc(GL_ALWAYS, 3, 0xFF); + glStencilOp(GL_KEEP, GL_REPLACE, GL_KEEP); + + sphere = gluNewQuadric(); + + gluSphere(sphere, outer_radius, 40, 40); + + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glStencilOp(GL_KEEP, GL_DECR, GL_KEEP); + + gluSphere(sphere, outer_radius, 40, 40); + + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + glPopAttrib(); +} + +void ED_draw_fixed_overlay_on_surface(float modelview[16], float projection[16], float size[3], int viewport[4], float location[3], float outer_radius, Sculpt *sd, Brush *brush, ViewContext *vc, rctf* quad, float angle) +{ + stencil_brush_on_surface(modelview, projection, size, viewport, location, outer_radius); + + glPushAttrib(GL_STENCIL_BUFFER_BIT); + + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_EQUAL, 2, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + draw_fixed_overlay(sd, brush, vc, quad, angle); + + glPopAttrib(); +} + +typedef struct SphereTest { + float radius_squared; + float location[3]; + float dist; +} SphereTest; + +static void sphere_test_init(float radius_squared, float location[3], SphereTest *test) +{ + test->radius_squared= radius_squared; + copy_v3_v3(test->location, location); +} + +static int sphere_test_fast(SphereTest *test, float co[3]) +{ + return len_squared_v3v3(co, test->location) <= test->radius_squared; +} + +static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], float fno[3]) +{ + if(!view_vec || dot_v3v3(view_vec, fno) > 0) { + add_v3_v3(out, fno); + } else { + add_v3_v3(out_flip, fno); /* out_flip is used when out is {0,0,0} */ } - unprojected_radius = paint_calc_object_space_radius(vc, location, - projected_radius); +} + +static void PBVH_area_normal(float out[3], float view_vec[3], float radius, float location[3], PBVH* pbvh, PBVHNode **nodes, int totnode, int use_openmp) +{ + int n; + + float out_flip[3] = {0.0f, 0.0f, 0.0f}; + + zero_v3(out); + + #pragma omp parallel for schedule(guided) if (use_openmp) + for(n=0; n<totnode; n++) { + PBVHVertexIter vd; + SphereTest test; + float private_an[3] = {0.0f, 0.0f, 0.0f}; + float private_out_flip[3] = {0.0f, 0.0f, 0.0f}; + + sphere_test_init(radius, location, &test); + + BLI_pbvh_vertex_iter_begin(pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sphere_test_fast(&test, vd.co)) { + if(vd.no) { + float fno[3]; - if(sd->draw_pressure && brush_use_size_pressure(brush)) - unprojected_radius *= sd->pressure_value; + normal_short_to_float_v3(fno, vd.no); + add_norm_if(view_vec, private_an, private_out_flip, fno); + } + else { + add_norm_if(view_vec, private_an, private_out_flip, vd.fno); + } + } + } + BLI_pbvh_vertex_iter_end; - if(!brush_use_locked_size(brush)) - brush_set_unprojected_radius(brush, unprojected_radius); + #pragma omp critical + { + add_v3_v3(out, private_an); + add_v3_v3(out_flip, private_out_flip); + } + } + + if (is_zero_v3(out)) + copy_v3_v3(out, out_flip); + + normalize_v3(out); +} + +static void paint_sculpt_disp_vector(float out[3], int disp_type, float view_vec[3], float radius, float location[3], PBVH *pbvh, PBVHNode **nodes, int totnode, int use_openmp) +{ + switch (disp_type) { + case SCULPT_DISP_DIR_VIEW: + if (view_vec) + copy_v3_v3(out, view_vec); + else + zero_v3(out); + + break; + + case SCULPT_DISP_DIR_X: + out[1] = 0.0; + out[2] = 0.0; + out[0] = 1.0; + break; + + case SCULPT_DISP_DIR_Y: + out[0] = 0.0; + out[2] = 0.0; + out[1] = 1.0; + break; + + case SCULPT_DISP_DIR_Z: + out[0] = 0.0; + out[1] = 0.0; + out[2] = 1.0; + break; + + case SCULPT_DISP_DIR_AREA: + PBVH_area_normal(out, view_vec, radius, location, pbvh, nodes, totnode, use_openmp); + break; + + default: + break; + } +} + +static void calc_brush_local_mat(float out[4][4], const Brush *brush, float up_vec[3], float location[3], float disp_vec[3]) +{ + float mat[4][4]; + float scale[4][4]; + float tmat[4][4]; + + // first row, x axis is perpendicular to both the up vector and the displacement vector + cross_v3_v3v3(mat[0], up_vec, disp_vec); + mat[0][3] = 0; + + // second row, y axis is perpendicular to the x axis and displacement vector + cross_v3_v3v3(mat[1], disp_vec, mat[0]); + mat[1][3] = 0; + + // third row, z axis is simply the displacement vector + copy_v3_v3(mat[2], disp_vec); + mat[2][3] = 0; + + // fourth row, the origin of the coordinate system is the location of the brush + copy_v3_v3(mat[3], location); + mat[3][3] = 1; + + // normalize the coordinate system so that points inside the brush are within a unit sphere + normalize_m4(mat); + scale_m4_fl(scale, brush_unprojected_radius(brush)); + mul_m4_m4m4(tmat, scale, mat); + + // the inverse of the matrix describing the coordinate system of the + // brush is a matrix that will transform a point into 'local brush space' + invert_m4_m4(out, tmat); +} + +static void draw_wrapped_overlay(float modelview[16], float projection[16], float size[3], int viewport[4], float location[3], float unprojected_radius, Sculpt *sd, Brush *brush, ViewContext *vc, float angle, float up_vec[3]) +{ + glPushAttrib( + GL_COLOR_BUFFER_BIT| + GL_CURRENT_BIT| + GL_DEPTH_BUFFER_BIT| + GL_ENABLE_BIT| + GL_LINE_BIT| + GL_POLYGON_BIT| + GL_STENCIL_BUFFER_BIT| + GL_TRANSFORM_BIT| + GL_VIEWPORT_BIT| + GL_TEXTURE_BIT); + + if (paint_load_overlay_tex(sd, brush, vc->ar)) { + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixf(modelview); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixf(projection); + + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glLoadIdentity(); + glTranslatef(0.5f, 0.5f, 0.5f); + glScalef(0.5f, 0.5f, 0.5f); + + //glTranslatef(ob->orig[0], ob->orig[1], ob->orig[2]); + + glScalef(size[0], size[1], size[2]); + + glRotatef(angle, 0, 0, 1); + + { + float disp_vec[3]; + float view_vec[3]; + float brush_local_mat[4][4]; + + PBVHNode **nodes; + int totnode; + + viewvector(vc->rv3d, vc->rv3d->twmat[3], view_vec); + + BLI_pbvh_gather_nodes_in_sphere( + vc->obact->sculpt->pbvh, + location, + brush_unprojected_radius(brush), + &nodes, + &totnode); + + paint_sculpt_disp_vector( + disp_vec, + brush->sculpt_plane, + view_vec, + brush_unprojected_radius(brush), + location, + vc->obact->sculpt->pbvh, + nodes, + totnode, + sd->flags & SCULPT_USE_OPENMP); + + if (nodes) + MEM_freeN(nodes); + + calc_brush_local_mat( + brush_local_mat, + brush, + up_vec, + location, + disp_vec); + + glMultMatrixf(brush_local_mat); + } + + { + float plane_x[4]= {1,0,0,0}; + float plane_y[4]= {0,1,0,0}; + float plane_z[4]= {0,0,1,0}; + float plane_w[4]= {0,0,0,1}; + glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + glTexGenf(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + glTexGenf(GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + glTexGenfv(GL_S, GL_OBJECT_PLANE, plane_x); + glTexGenfv(GL_T, GL_OBJECT_PLANE, plane_y); + glTexGenfv(GL_R, GL_OBJECT_PLANE, plane_z); + glTexGenfv(GL_Q, GL_OBJECT_PLANE, plane_w); + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + glEnable(GL_TEXTURE_GEN_R); + glEnable(GL_TEXTURE_GEN_Q); + } + + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_FALSE); + + glColor4f( + U.sculpt_paint_overlay_col[0], + U.sculpt_paint_overlay_col[1], + U.sculpt_paint_overlay_col[2], + brush->texture_overlay_alpha / 100.0f); + + glEnable(GL_BLEND); + + glEnable(GL_CULL_FACE); + + glDepthFunc(GL_LEQUAL); + glEnable(GL_DEPTH_TEST); + + glPolygonOffset(-1, 1); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glEnable(GL_POLYGON_OFFSET_FILL); + + BLI_pbvh_draw_nodes_in_sphere(vc->obact->sculpt->pbvh, location, unprojected_radius); + + glPopMatrix(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + } + + glPopAttrib(); +} + +void ED_draw_wrapped_overlay_on_surface(float modelview[16], float projection[16], float size[3], int viewport[4], float location[3], float unprojected_radius, Sculpt *sd, Brush *brush, ViewContext *vc, float angle, float up_vec[3]) +{ + stencil_brush_on_surface(modelview, projection, size, viewport, location, unprojected_radius); + + glPushAttrib(GL_STENCIL_BUFFER_BIT); + + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_EQUAL, 2, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + draw_wrapped_overlay(modelview, projection, size, viewport, location, unprojected_radius, sd, brush, vc, angle, up_vec); + + glPopAttrib(); +} + +static void update_rake_delta(float out[3], float location[3], int x, int y, ViewContext *vc, float mat[4][4]) +{ + static float old_location[3], old_delta[3]; + + float delta[3], imat[4][4]; + + /* compute 3d coordinate at same z from original location + mouse */ + initgrabz(vc->rv3d, old_location[0], old_location[1], old_location[2]); + window_to_3d_delta(vc->ar, delta, x, y); + + /* compute delta to move verts by */ + sub_v3_v3v3(out, delta, old_delta); + invert_m4_m4(imat, mat); + mul_mat3_m4_v3(imat, out); + + copy_v3_v3(old_delta, delta); + + if (len_v3v3(old_location, location) > 1000) + copy_v3_v3(old_location, location); } +// XXX paint cursor now does a lot of the same work that is needed during a sculpt stroke +// problem: all this stuff was not intended to be used at this point, so things feel a +// bit hacked. I've put lots of stuff in Brush that probably better goes in Paint +// Functions should be refactored so that they can be used between sculpt.c and +// paint_stroke.c clearly and optimally and the lines of communication between the +// two modules should be more clearly defined. static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) { Paint *paint = paint_get_active(CTX_data_scene(C)); Brush *brush = paint_brush(paint); + ViewContext vc; - float final_radius; - float translation[2]; - float outline_alpha, *outline_col; - - /* set various defaults */ - translation[0] = x; - translation[1] = y; - outline_alpha = 0.5; - outline_col = brush->add_col; - final_radius = brush_size(brush); - /* check that brush drawing is enabled */ - if(!(paint->flags & PAINT_SHOW_BRUSH)) - return; + /* sd->last_angle, sd->last_y, sd->last_x, and set_brush_size need to be + updated even if we are not drawing the cursor, so we cannot return + until they are updated. */ /* can't use stroke vc here because this will be called during - mouse over too, not just during a stroke */ + mouse over too, not just during a stroke */ view3d_set_viewcontext(C, &vc); /* TODO: as sculpt and other paint modes are unified, this special mode of drawing will go away */ - if(vc.obact->sculpt) { + if (vc.obact->sculpt) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - float location[3]; - int pixel_radius, hit; + + int pixel_radius, viewport[4]; + float location[3], modelview[16], projection[16]; + + int hit; + + int flip; + int sign; + + float* col; + float alpha; + const float root_alpha = brush_alpha(brush); float visual_strength = root_alpha*root_alpha; - const float min_alpha = 0.20f; - const float max_alpha = 0.80f; - /* this is probably here so that rake takes into + const float min_alpha = sd->sculpting ? 0.10f : 0.20f; + const float max_alpha = sd->sculpting ? 0.40f : 0.80f; + + float angle; + + rctf quad; + + float rake_delta[3]; + + /* XXX: This is here so that rake takes into account the brush movements before the stroke - starts, but this doesn't really belong in draw code - (TODO) */ + starts. Is there a cleaner way? */ { const float u = 0.5f; const float v = 1 - u; @@ -583,7 +1289,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) const float dx = sd->last_x - x; const float dy = sd->last_y - y; - if(dx*dx + dy*dy >= r*r) { + if (dx*dx + dy*dy >= r*r) { sd->last_angle = atan2(dx, dy); sd->last_x = u*sd->last_x + v*x; @@ -591,106 +1297,262 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) } } - /* test if brush is over the mesh */ - hit = sculpt_get_brush_geometry(C, x, y, &pixel_radius, location); + /* can leave now if brush size is not locked and + drawing brush is not enabled */ + if(!brush_use_locked_size(brush) && !(paint->flags & PAINT_SHOW_BRUSH)) + return; - /* draw overlay */ - paint_draw_alpha_overlay(sd, brush, &vc, x, y); + hit = sculpt_get_brush_geometry(C, x, y, &pixel_radius, location, modelview, projection, viewport); - if(brush_use_locked_size(brush)) + if (brush_use_locked_size(brush)) brush_set_size(brush, pixel_radius); + /* XXX: need to determine if there is a condition that allows exiting + from this point */ + /* check if brush is subtracting, use different color then */ - /* TODO: no way currently to know state of pen flip or + /* XXX: no way currently to know state of pen flip or invert key modifier without starting a stroke */ - if((!(brush->flag & BRUSH_INVERTED) ^ - !(brush->flag & BRUSH_DIR_IN)) && - ELEM5(brush->sculpt_tool, SCULPT_TOOL_DRAW, - SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, - SCULPT_TOOL_PINCH, SCULPT_TOOL_CREASE)) - outline_col = brush->sub_col; + flip = brush->flag & BRUSH_INVERTED ? -1 : 1; + + sign = flip * ((brush->flag & BRUSH_DIR_IN)? -1 : 1); + + if (sign < 0 && + ELEM6(brush->sculpt_tool, + SCULPT_TOOL_DRAW, + SCULPT_TOOL_GRAVITY, + SCULPT_TOOL_INFLATE, + SCULPT_TOOL_CLAY, + SCULPT_TOOL_PINCH, + SCULPT_TOOL_CREASE)) + { + col = brush->sub_col; + } + else { + col = brush->add_col; + } - /* only do if brush is over the mesh */ - if(hit) - paint_cursor_on_hit(sd, brush, &vc, location, &visual_strength); + alpha = (paint->flags & PAINT_SHOW_BRUSH_ON_SURFACE) ? min_alpha + (visual_strength*(max_alpha-min_alpha)) : 0.50f; + + if (sd->draw_anchored) { + quad.xmin = sd->anchored_initial_mouse[0]-sd->anchored_size - vc.ar->winrct.xmin; + quad.ymin = sd->anchored_initial_mouse[1]-sd->anchored_size - vc.ar->winrct.ymin; + quad.xmax = sd->anchored_initial_mouse[0]+sd->anchored_size - vc.ar->winrct.xmin; + quad.ymax = sd->anchored_initial_mouse[1]+sd->anchored_size - vc.ar->winrct.ymin; + } + else { + const int size= brush_size(brush); + quad.xmin= (float)x-size; + quad.ymin= (float)y-size; + quad.xmax= (float)x+size; + quad.ymax= (float)y+size; + } + + if (brush->flag & BRUSH_RAKE) { + angle= sd->last_angle*(float)(180.0/M_PI); + } + else { + angle= sd->special_rotation*(float)(180.0/M_PI); + } - /* don't show effect of strength past the soft limit */ - if(visual_strength > 1) - visual_strength = 1; + angle -= brush->mtex.rot*(float)(180.0/M_PI); - outline_alpha = ((paint->flags & PAINT_SHOW_BRUSH_ON_SURFACE) ? - min_alpha + (visual_strength*(max_alpha-min_alpha)) : 0.50f); + update_rake_delta(rake_delta, location, x+vc.ar->winrct.xmin, y+vc.ar->winrct.ymin, &vc, CTX_data_active_object(C)->obmat); - if(sd->draw_anchored) { - final_radius = sd->anchored_size; - translation[0] = sd->anchored_initial_mouse[0] - vc.ar->winrct.xmin; - translation[1] = sd->anchored_initial_mouse[1] - vc.ar->winrct.ymin; + /* draw overlay */ + if (!sd->sculpting && + (!hit || !(paint->flags & PAINT_SHOW_BRUSH_ON_SURFACE)) && + brush->flag & BRUSH_TEXTURE_OVERLAY) + { + if (!hit || brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { + draw_fixed_overlay(sd, brush, &vc, &quad, angle); + } + else if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_WRAP) { + float up_vec[3]= {0,1,0}; + Object *ob= CTX_data_active_object(C); + + // if raking, up is the direction of the mouse (grab delta), + if (brush->flag & BRUSH_RAKE) { + negate_v3_v3(up_vec, rake_delta); + draw_wrapped_overlay(modelview, projection, ob->size, viewport, location, brush_unprojected_radius(brush), sd, brush, &vc, 0, up_vec); + } + // otherwise, it is the view up vector + else { + mul_mat3_m4_v3(vc.rv3d->viewinv, up_vec); + draw_wrapped_overlay(modelview, projection, ob->size, viewport, location, brush_unprojected_radius(brush), sd, brush, &vc, angle, up_vec); + } + } } - } - /* make lines pretty */ - glEnable(GL_BLEND); - glEnable(GL_LINE_SMOOTH); + /* only do if brush is over the mesh */ + if (hit) { + float projected_radius; + float unprojected_radius; - /* set brush color */ - glColor4f(outline_col[0], outline_col[1], outline_col[2], outline_alpha); + if (sd->draw_pressure && (brush->flag & BRUSH_ALPHA_PRESSURE)) + visual_strength *= sd->pressure_value; - /* draw brush outline */ - glTranslatef(translation[0], translation[1], 0); - glutil_draw_lined_arc(0.0, M_PI*2.0, final_radius, 40); - glTranslatef(-translation[0], -translation[1], 0); + // don't show effect of strength past the soft limit + if (visual_strength > 1) visual_strength = 1; - /* restore GL state */ - glDisable(GL_BLEND); - glDisable(GL_LINE_SMOOTH); -} + if(sd->draw_anchored) { + projected_radius = sd->anchored_size; + } + else { + if(brush->flag & BRUSH_ANCHORED) + projected_radius = 8; + else + projected_radius = brush_size(brush); + } -/* if this is a tablet event, return tablet pressure and set *pen_flip - to 1 if the eraser tool is being used, 0 otherwise */ -static float event_tablet_data(wmEvent *event, int *pen_flip) -{ - int erasor = 0; - float pressure = 1; + unprojected_radius = + paint_calc_object_space_radius(vc, location, projected_radius); + + if (sd->draw_pressure && (brush->flag & BRUSH_SIZE_PRESSURE)) + unprojected_radius *= sd->pressure_value; + + if (!brush_use_locked_size(brush)) + brush_set_unprojected_radius(brush, unprojected_radius); + + /* can leave now if brush drawing is off */ + if(!(paint->flags & PAINT_SHOW_BRUSH)) + return; + +#ifdef WITH_ONSURFACEBRUSH + if (paint->flags & PAINT_SHOW_BRUSH_ON_SURFACE) { + const float max_thickness= 0.12; + const float min_thickness= 0.06; + const float thickness= 1.0 - min_thickness - visual_strength*max_thickness; + const float inner_radius= sd->draw_anchored ? unprojected_radius : unprojected_radius*thickness; + const float outer_radius= sd->draw_anchored ? 1.0f/thickness * unprojected_radius : unprojected_radius; + Object *ob= CTX_data_active_object(C); + + float *location0 = sd->draw_anchored ? sd->anchored_location : location; + + if (!sd->sculpting && brush->flag & BRUSH_TEXTURE_OVERLAY) { + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) + ED_draw_fixed_overlay_on_surface(modelview, projection, ob->size, viewport, location0, outer_radius, sd, brush, &vc, &quad, angle); + else if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_WRAP) { + float up_vec[3]= {0,1,0}; + + // if raking, up is the direction of the mouse (grab delta), + if (brush->flag & BRUSH_RAKE) { + negate_v3_v3(up_vec, rake_delta); + ED_draw_wrapped_overlay_on_surface(modelview, projection, ob->size, viewport, location0, outer_radius, sd, brush, &vc, 0, up_vec); + } + // otherwise, it is the view up vector + else { + mul_mat3_m4_v3(vc.rv3d->viewinv, up_vec); + ED_draw_wrapped_overlay_on_surface(modelview, projection, ob->size, viewport, location0, outer_radius, sd, brush, &vc, angle, up_vec); + } + } + } - if(event->custom == EVT_DATA_TABLET) { - wmTabletData *wmtab= event->customdata; + ED_draw_on_surface_cursor(modelview, projection, col, alpha, ob->size, viewport, location0, inner_radius, outer_radius, brush_size(brush)); - erasor = (wmtab->Active == EVT_TABLET_ERASER); - pressure = (wmtab->Active != EVT_TABLET_NONE) ? wmtab->Pressure : 1; + { + float scale = 1.0f/(20.0f*(brush_size(brush)/100.0f)); + draw_symmetric_brush_dots(sd, sd->draw_anchored ? sd->anchored_location : location0, col, modelview, projection, viewport,scale*inner_radius, brush_size(brush) >= 8); + } + } +#endif + } + +#ifdef WITH_ONSURFACEBRUSH + if (!hit || !(paint->flags & PAINT_SHOW_BRUSH_ON_SURFACE)) { +#endif + glPushAttrib( + GL_COLOR_BUFFER_BIT| + GL_CURRENT_BIT| + GL_DEPTH_BUFFER_BIT| + GL_ENABLE_BIT| + GL_LINE_BIT| + GL_POLYGON_BIT| + GL_STENCIL_BUFFER_BIT| + GL_TRANSFORM_BIT| + GL_VIEWPORT_BIT| + GL_TEXTURE_BIT); + + glColor4f(col[0], col[1], col[2], alpha); + + glEnable(GL_BLEND); + + glEnable(GL_LINE_SMOOTH); + + if (sd->draw_anchored) { + glTranslatef(sd->anchored_initial_mouse[0] - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1] - vc.ar->winrct.ymin, 0.0f); + glutil_draw_lined_arc(0.0f, (float)(M_PI*2.0), sd->anchored_size, 40); + glTranslatef(-sd->anchored_initial_mouse[0] + vc.ar->winrct.xmin, -sd->anchored_initial_mouse[1] + vc.ar->winrct.xmin, 0.0f); + } + else { + glTranslatef((float)x, (float)y, 0.0f); + glutil_draw_lined_arc(0.0f, (float)(M_PI*2.0), brush_size(brush), 40); + glTranslatef(-(float)x, -(float)y, 0.0f); + } + + glPopAttrib(); +#ifdef WITH_ONSURFACEBRUSH + } +#endif } + else { + if(!(paint->flags & PAINT_SHOW_BRUSH)) + return; - if(pen_flip) - (*pen_flip) = erasor; + glColor4ubv(paint_get_active(CTX_data_scene(C))->paint_cursor_col); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); - return pressure; + glTranslatef((float)x, (float)y, 0.0f); + glutil_draw_lined_arc(0.0, M_PI*2.0, brush_size(brush), 40); // XXX: make sure this the right size to use in non-sculpt modes + glTranslatef((float)-x, (float)-y, 0.0f); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + } } /* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse_in[2]) { - Paint *paint = paint_get_active(CTX_data_scene(C)); - Brush *brush = paint_brush(paint); - PaintStroke *stroke = op->customdata; + Paint *paint = paint_get_active(CTX_data_scene(C)); // XXX + Brush *brush = paint_brush(paint); // XXX + float mouse[3]; + PointerRNA itemptr; + float location[3]; + float pressure; - int pen_flip; + int pen_flip; - /* see if tablet affects event */ - pressure = event_tablet_data(event, &pen_flip); + ViewContext vc; // XXX - /* TODO: as sculpt and other paint modes are unified, this - separation will go away */ - if(stroke->vc.obact->sculpt) { + PaintStroke *stroke = op->customdata; + + view3d_set_viewcontext(C, &vc); // XXX + + /* Tablet */ + if(event->custom == EVT_DATA_TABLET) { + wmTabletData *wmtab= event->customdata; + + pressure = (wmtab->Active != EVT_TABLET_NONE) ? wmtab->Pressure : 1; + pen_flip = (wmtab->Active == EVT_TABLET_ERASER); + } + else { + pressure = 1; + pen_flip = 0; + } + + // XXX: temporary check for sculpt mode until things are more unified + if (vc.obact->sculpt) { float delta[3]; brush_jitter_pos(brush, mouse_in, mouse); - /* XXX: meh, this is round about because - brush_jitter_pos isn't written in the best way to - be reused here */ - if(brush->flag & BRUSH_JITTER_PRESSURE) { + // XXX: meh, this is round about because brush_jitter_pos isn't written in the best way to be reused here + if (brush->flag & BRUSH_JITTER_PRESSURE) { sub_v3_v3v3(delta, mouse, mouse_in); mul_v3_fl(delta, pressure); add_v3_v3v3(mouse, mouse_in, delta); @@ -699,7 +1561,7 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *ev else copy_v3_v3(mouse, mouse_in); - /* TODO: can remove the if statement once all modes have this */ + /* XXX: can remove the if statement once all modes have this */ if(stroke->get_location) stroke->get_location(C, stroke, location, mouse); else @@ -708,10 +1570,10 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *ev /* Add to stroke */ RNA_collection_add(op->ptr, "stroke", &itemptr); - RNA_float_set_array(&itemptr, "location", location); - RNA_float_set_array(&itemptr, "mouse", mouse); - RNA_boolean_set(&itemptr, "pen_flip", pen_flip); - RNA_float_set(&itemptr, "pressure", pressure); + RNA_float_set_array(&itemptr, "location", location); + RNA_float_set_array(&itemptr, "mouse", mouse); + RNA_boolean_set (&itemptr, "pen_flip", pen_flip); + RNA_float_set (&itemptr, "pressure", pressure); stroke->last_mouse_position[0] = mouse[0]; stroke->last_mouse_position[1] = mouse[1]; @@ -753,35 +1615,67 @@ static int paint_space_stroke(bContext *C, wmOperator *op, wmEvent *event, const int cnt = 0; if(paint_space_stroke_enabled(stroke->brush)) { - float mouse[2]; float vec[2]; float length, scale; + float start_mouse[2]; - copy_v2_v2(mouse, stroke->last_mouse_position); - sub_v2_v2v2(vec, final_mouse, mouse); + copy_v2_v2(start_mouse, stroke->last_mouse_position); + sub_v2_v2v2(vec, final_mouse, start_mouse); - length = len_v2(vec); + length= len_v2(vec); if(length > FLT_EPSILON) { - int steps; - int i; - float pressure= 1.0f; - - /* XXX mysterious :) what has 'use size' do with this here... if you don't check for it, pressure fails */ - if(brush_use_size_pressure(stroke->brush)) - pressure = event_tablet_data(event, NULL); - - if(pressure > FLT_EPSILON) { - scale = (brush_size(stroke->brush)*pressure*stroke->brush->spacing/50.0f) / length; - if(scale > FLT_EPSILON) { - mul_v2_fl(vec, scale); - - steps = (int)(1.0f / scale); - - for(i = 0; i < steps; ++i, ++cnt) { - add_v2_v2(mouse, vec); - paint_brush_stroke_add_step(C, op, event, mouse); - } + float pressure = 1; + float dvec[2]; + float mouse[2]; + + pressure = brush_use_size_pressure(stroke->brush) ? event_tablet_data(event, NULL) : 1.0; + + scale = (brush_size(stroke->brush)*pressure*stroke->brush->spacing/50.0f) / length; + + // XXX: this code checks for very small numbers, which could lead to + // a large number of iterations. perhaps this code should just + // bail out or break if numbers are too small. Investigate this. + + if (scale < FLT_EPSILON) // make sure scale is big enough have an effect when added + scale= FLT_EPSILON; + + if (stroke->brush->flag & BRUSH_ADAPTIVE_SPACE) { + float t= 0; + + for(;;) { + float f; + float d; + + f= stroke->brush->adaptive_space_factor; + CLAMP(f, 0.1f, 1); // make sure that adaptive strength never sets spacing to zero + + d= f*scale; + CLAMP(d, FLT_EPSILON, scale); // d has to be big enough to increment t + + t += d; + + if (t > 1) break; + + mul_v2_v2fl(dvec, vec, t); + add_v2_v2v2(mouse, start_mouse, dvec); + + paint_brush_stroke_add_step(C, op, event, mouse); + + cnt++; + } + } + else { + int i, steps; + + copy_v2_v2(mouse, start_mouse); + mul_v2_fl(vec, scale); + + steps = (int)(1.0f / scale); + + for(i = 0; i < steps; ++i, ++cnt) { + add_v2_v2(mouse, vec); + paint_brush_stroke_add_step(C, op, event, mouse); } } } @@ -796,7 +1690,7 @@ PaintStroke *paint_stroke_new(bContext *C, StrokeGetLocation get_location, StrokeTestStart test_start, StrokeUpdateStep update_step, - StrokeDone done, int event_type) + StrokeDone done, int event_type) { PaintStroke *stroke = MEM_callocN(sizeof(PaintStroke), "PaintStroke"); @@ -809,7 +1703,7 @@ PaintStroke *paint_stroke_new(bContext *C, stroke->update_step = update_step; stroke->done = done; stroke->event_type= event_type; /* for modal, return event */ - + return stroke; } diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 09f5c32bea0..6cabed19ea7 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -286,7 +286,10 @@ void BRUSH_OT_curve_preset(wmOperatorType *ot) ot->exec= brush_curve_preset_exec; ot->poll= brush_curve_preset_poll; - ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + // XXX: check the meaning of these flags, removed OPTYPE_REGISTER becauase + // that seems to mean display a panel in the lower left, but this operator + // as no meaningful UI to present the user in that area + ot->flag= OPTYPE_UNDO; RNA_def_enum(ot->srna, "shape", prop_shape_items, CURVE_PRESET_SMOOTH, "Mode", ""); } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 20e74702067..2eec1bdbadb 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -34,7 +34,6 @@ * \ingroup edsculpt */ - #include "MEM_guardedalloc.h" #include "BLI_math.h" @@ -93,6 +92,8 @@ #include <omp.h> #endif +static void calc_sculpt_normal(const Sculpt *sd, const SculptSession *ss, float an[3], PBVHNode **nodes, int totnode, float range); + void ED_sculpt_force_update(bContext *C) { Object *ob= CTX_data_active_object(C); @@ -147,8 +148,10 @@ struct MultiresModifierData *sculpt_multires_active(Scene *scene, Object *ob) if(!modifier_isEnabled(scene, md, eModifierMode_Realtime)) continue; - if(mmd->sculptlvl > 0) return mmd; - else return NULL; + if (mmd->sculptlvl > 0) + return mmd; + else + return NULL; } } @@ -178,22 +181,29 @@ static int sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob) Mesh *me= (Mesh*)ob->data; MultiresModifierData *mmd= sculpt_multires_active(scene, ob); - if(mmd) return 0; + if (mmd) + return 0; - /* non-locked shaoe keys could be handled in the same way as deformed mesh */ - if((ob->shapeflag&OB_SHAPE_LOCK)==0 && me->key && ob->shapenr) + /* non-locked shape keys could be handled in the same way as deformed mesh */ + if ((ob->shapeflag&OB_SHAPE_LOCK)==0 && me->key && ob->shapenr) return 1; md= modifiers_getVirtualModifierList(ob); - + /* exception for shape keys because we can edit those */ for(; md; md= md->next) { ModifierTypeInfo *mti = modifierType_getInfo(md->type); - if(!modifier_isEnabled(scene, md, eModifierMode_Realtime)) continue; - if(md->type==eModifierType_ShapeKey) continue; + if (!modifier_isEnabled(scene, md, eModifierMode_Realtime)) + continue; + + if (md->type==eModifierType_ShapeKey) + continue; - if(mti->type==eModifierTypeType_OnlyDeform) return 1; - else if((sd->flags & SCULPT_ONLY_DEFORM)==0) return 1; + if (mti->type==eModifierTypeType_OnlyDeform) + return 1; + + if (!(sd->flags & SCULPT_ONLY_DEFORM)) + return 1; } return 0; @@ -261,6 +271,15 @@ typedef struct StrokeCache { float last_rake[2]; /* Last location of updating rake rotation */ int original; + /* used for orienting brush local space when raking is enabled */ + float orig_rake_location[3]; + float old_rake_location[3]; + float rake_delta[3]; + float rake_delta_symmetry[3]; + + float brush_local_mat[4][4]; + float symm_brush_local_mat[4][4]; + float vertex_rotation; char saved_active_brush_name[24]; @@ -268,14 +287,23 @@ typedef struct StrokeCache { float plane_trim_squared; + float frontface_start, frontface_range; + rcti previous_r; /* previous redraw rectangle */ } StrokeCache; +/* rotation direction is flipped in different symmetrical passes */ +static const int rotation_flip[8] = { 1, -1, -1, 1, -1, 1, 1, -1 }; + + /*** BVH Tree ***/ /* Get a screen-space rectangle of the modified area */ -static int sculpt_get_redraw_rect(ARegion *ar, RegionView3D *rv3d, - Object *ob, rcti *rect) +static int sculpt_get_redraw_rect( + ARegion *ar, + RegionView3D *rv3d, + Object *ob, + rcti *rect) { PBVH *pbvh= ob->sculpt->pbvh; float bb_min[3], bb_max[3], pmat[4][4]; @@ -309,22 +337,30 @@ static int sculpt_get_redraw_rect(ARegion *ar, RegionView3D *rv3d, } } } - + if (rect->xmin < rect->xmax && rect->ymin < rect->ymax) { /* expand redraw rect with redraw rect from previous step to prevent partial-redraw issues caused by fast strokes. This is needed here (not in sculpt_flush_update) as it was before because redraw rectangle should be the same in both of optimized PBVH draw function and 3d view redraw (if not -- some mesh parts could disapper from screen (sergey) */ - SculptSession *ss = ob->sculpt; - if (ss->cache) { - if (!BLI_rcti_is_empty(&ss->cache->previous_r)) - BLI_union_rcti(rect, &ss->cache->previous_r); + if (ob->sculpt->cache) { + rcti* previous_r = &(ob->sculpt->cache->previous_r); + + if (!BLI_rcti_is_empty(previous_r)) + BLI_union_rcti(rect, previous_r); } return 1; } + else { + return rect->xmin < rect->xmax && rect->ymin < rect->ymax; + } +} + + return 1; + } return 0; } @@ -367,17 +403,27 @@ void sculpt_get_redraw_planes(float planes[4][4], ARegion *ar, /************************ Brush Testing *******************/ +// XXX: consider rewriting not to use struct typedef struct SculptBrushTest { float radius_squared; float location[3]; float dist; } SculptBrushTest; -static void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test) +static void sculpt_brush_test_init(const SculptSession *ss, SculptBrushTest *test) { test->radius_squared= ss->cache->radius_squared; copy_v3_v3(test->location, ss->cache->location); - test->dist= 0.0f; /* just for initialize */ + + /* XXX: no need to initialize, consider not using struct at all and + trusting compiler to properly inline arguments */ + //test->dist = -1; +} + +static void sculpt_brush_range_test_init(const SculptSession *ss, SculptBrushTest *test, float range) +{ + test->radius_squared= ss->cache->radius_squared*range*range; + copy_v3_v3(test->location, ss->cache->location); } static int sculpt_brush_test(SculptBrushTest *test, float co[3]) @@ -411,7 +457,7 @@ static int sculpt_brush_test_fast(SculptBrushTest *test, float co[3]) return len_squared_v3v3(co, test->location) <= test->radius_squared; } -static int sculpt_brush_test_cube(SculptBrushTest *test, float co[3], float local[4][4]) +static int sculpt_brush_test_cube(SculptBrushTest *test, float *fade, float co[3], float local[4][4]) { static const float side = 0.70710678118654752440084436210485; // sqrt(.5); @@ -424,7 +470,18 @@ static int sculpt_brush_test_cube(SculptBrushTest *test, float co[3], float loca local_co[2] = fabs(local_co[2]); if (local_co[0] <= side && local_co[1] <= side && local_co[2] <= side) { - test->dist = MAX3(local_co[0], local_co[1], local_co[2]) / side; + float cmin, cmax; + int i; + + for (i= 0; i < 3; i++) { + cmin = MIN2(local_co[0]-0.1f, side); + cmax = MIN2(local_co[0]+0.1f, side); + + (*fade) *= (10*cmax)-(10*cmin); + } + + + test->dist = len_v3v3(co, test->location); return 1; } @@ -433,24 +490,42 @@ static int sculpt_brush_test_cube(SculptBrushTest *test, float co[3], float loca } } -static float frontface(Brush *brush, float sculpt_normal[3], short no[3], float fno[3]) +static float frontface(Brush *brush, float start, float range, float sculpt_normal[3], short no[3]) { if (brush->flag & BRUSH_FRONTFACE) { - float dot; + float angle; + float tmp[3]; - if (no) { - float tmp[3]; + normal_short_to_float_v3(tmp, no); + angle= angle_normalized_v3v3(tmp, sculpt_normal); - normal_short_to_float_v3(tmp, no); - dot= dot_v3v3(tmp, sculpt_normal); + if (angle >= start+range) { + return 0; } - else { - dot= dot_v3v3(fno, sculpt_normal); + else if (angle > start) { + return (start+range-angle) / range; } - return dot > 0 ? dot : 0; + } + + return 1; +} + +static void set_adaptive_space_factor(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float an_in[3]) +{ + Brush *brush = paint_brush(&(sd->paint)); + + if (brush->flag & BRUSH_ADAPTIVE_SPACE) { + float an[3]; + + if (an_in) + copy_v3_v3(an, an_in); + else + calc_sculpt_normal(sd, ss, an, nodes, totnode, brush->sculpt_plane_range); + + brush->adaptive_space_factor= dot_v3v3(an, ss->cache->view_normal); } else { - return 1; + brush->adaptive_space_factor= 1; } } @@ -486,7 +561,7 @@ static int sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float locat static float overlapped_curve(Brush* br, float x) { int i; - const int n = 100 / br->spacing; + const int n = 100 / ((br->flag&BRUSH_ADAPTIVE_SPACE ? br->adaptive_space_factor : 1.0f) * br->spacing); const float h = br->spacing / 50.0f; const float x0 = x-1; @@ -550,7 +625,7 @@ static float calc_overlap(StrokeCache *cache, const char symm, const char axis, //flip_coord(mirror, cache->traced_location, symm); flip_coord(mirror, cache->true_location, symm); - if(axis != 0) { + if(axis >= 'X' && axis <= 'Z') { float mat[4][4]= MAT4_UNITY; rotate_m4(mat, axis, angle); mul_m4_v3(mat, mirror); @@ -560,7 +635,7 @@ static float calc_overlap(StrokeCache *cache, const char symm, const char axis, distsq = len_squared_v3v3(mirror, cache->true_location); if (distsq <= 4.0f*(cache->radius_squared)) - return (2.0f*(cache->radius) - sqrtf(distsq)) / (2.0f*(cache->radius)); + return (2.0f*(cache->radius) - sqrt(distsq)) / (2.0f*(cache->radius)); else return 0; } @@ -581,7 +656,9 @@ static float calc_radial_symmetry_feather(Sculpt *sd, StrokeCache *cache, const static float calc_symmetry_feather(Sculpt *sd, StrokeCache* cache) { - if (sd->flags & SCULPT_SYMMETRY_FEATHER) { + Brush *brush= paint_brush(&(sd->paint)); + + if (brush->flag & BRUSH_SYMMETRY_FEATHER) { float overlap; int symm = cache->symmetry; int i; @@ -625,8 +702,9 @@ static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather) switch(brush->sculpt_tool){ case SCULPT_TOOL_CLAY: - case SCULPT_TOOL_CLAY_TUBES: + case SCULPT_TOOL_CLAY_STRIPS: case SCULPT_TOOL_DRAW: + case SCULPT_TOOL_GRAVITY: case SCULPT_TOOL_LAYER: return alpha * flip * pressure * overlap * feather; @@ -684,7 +762,98 @@ static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather) } } +/* Texture Sampling */ + +static void set_brush_local_mat(const Sculpt *sd, const SculptSession *ss, const Brush *brush, PBVHNode **nodes, int totnode, float *an_in) +{ + float tmat[4][4]; + + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_WRAP) { + if (ss->cache->mirror_symmetry_pass == 0 && ss->cache->radial_symmetry_pass == 0) { + float mat[4][4]; + float scale[4][4]; + float an[3]; + + if (an_in) + copy_v3_v3(an, an_in); + else + calc_sculpt_normal(sd, ss, an, nodes, totnode, brush->sculpt_plane_range); + + if (brush->flag & BRUSH_RAKE) { + cross_v3_v3v3(mat[0], an, ss->cache->rake_delta_symmetry); mat[0][3] = 0; + } + else { + float up[4]= {0,1,0,0}; // homogeneous up vector + + mul_m4_v4(ss->cache->vc->rv3d->viewinv, up); + cross_v3_v3v3(mat[0], up, an); mat[0][3] = 0; + } + + cross_v3_v3v3(mat[1], an, mat[0]); mat[1][3] = 0; + copy_v3_v3(mat[2], an); mat[2][3] = 0; + copy_v3_v3(mat[3], ss->cache->location); mat[3][3] = 1; + normalize_m4(mat); + scale_m4_fl(scale, ss->cache->radius); + mul_m4_m4m4(tmat, scale, mat); + invert_m4_m4(ss->cache->brush_local_mat, tmat); + + copy_m4_m4(ss->cache->symm_brush_local_mat, ss->cache->brush_local_mat); + } + else { + copy_m4_m4(tmat, ss->cache->symm_brush_local_mat); + mul_m4_m4m4(ss->cache->symm_brush_local_mat, tmat, ss->cache->symm_rot_mat); + + flip_coord(ss->cache->symm_brush_local_mat[0], ss->cache->brush_local_mat[0], ss->cache->mirror_symmetry_pass); + flip_coord(ss->cache->symm_brush_local_mat[1], ss->cache->brush_local_mat[1], ss->cache->mirror_symmetry_pass); + flip_coord(ss->cache->symm_brush_local_mat[2], ss->cache->brush_local_mat[2], ss->cache->mirror_symmetry_pass); + flip_coord(ss->cache->symm_brush_local_mat[3], ss->cache->brush_local_mat[3], ss->cache->mirror_symmetry_pass); + } + } +} + +#if 0 + +/* Get a pixel from the texcache at (px, py) */ +static unsigned char get_texcache_pixel(const SculptSession *ss, int px, int py) +{ + unsigned *p; + p = ss->texcache + py * ss->texcache_side + px; + return ((unsigned char*)(p))[0]; +} + +static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float v) +{ + unsigned x, y, x2, y2; + const int tc_max = ss->texcache_side - 1; + float urat, vrat, uopp; + + if(u < 0) u = 0; + else if(u >= ss->texcache_side) u = tc_max; + if(v < 0) v = 0; + else if(v >= ss->texcache_side) v = tc_max; + + x = floor(u); + y = floor(v); + x2 = x + 1; + y2 = y + 1; + + if(x2 > ss->texcache_side) x2 = tc_max; + if(y2 > ss->texcache_side) y2 = tc_max; + + urat = u - x; + vrat = v - y; + uopp = 1 - urat; + + return ((get_texcache_pixel(ss, x, y) * uopp + + get_texcache_pixel(ss, x2, y) * urat) * (1 - vrat) + + (get_texcache_pixel(ss, x, y2) * uopp + + get_texcache_pixel(ss, x2, y2) * urat) * vrat) / 255.0; +} + +#endif + /* Return a multiplier for brush strength on a particular vertex. */ +/* XXX: a lot of this code is the same as code in paint_stroke for loading the overlay */ static float tex_strength(SculptSession *ss, Brush *br, float *point, const float len) { MTex *mtex = &br->mtex; @@ -699,9 +868,9 @@ static float tex_strength(SculptSession *ss, Brush *br, float *point, const floa /* Get strength by feeding the vertex location directly into a texture */ externtex(mtex, point, &avg, - &jnk, &jnk, &jnk, &jnk, 0); + &jnk, &jnk, &jnk, &jnk, 0); // XXX: minor, is last argument a pointer? } - else if(ss->texcache) { + else if(ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_FIXED, MTEX_MAP_MODE_TILED)) { float rotation = -mtex->rot; float x, y, point_2d[3]; float radius; @@ -734,7 +903,7 @@ static float tex_strength(SculptSession *ss, Brush *br, float *point, const floa leave the coordinates relative to the screen */ { radius = brush_size(br); // use unadjusted size for tiled mode - + x = point_2d[0] - ss->cache->vc->ar->winrct.xmin; y = point_2d[1] - ss->cache->vc->ar->winrct.ymin; } @@ -746,7 +915,7 @@ static float tex_strength(SculptSession *ss, Brush *br, float *point, const floa x -= 0.5f; y -= 0.5f; } - + x *= ss->cache->vc->ar->winx / radius; y *= ss->cache->vc->ar->winy / radius; @@ -754,7 +923,7 @@ static float tex_strength(SculptSession *ss, Brush *br, float *point, const floa the texture is not rotated by skipping the calls to atan2, sqrtf, sin, and cos. */ if (rotation > 0.001f || rotation < -0.001f) { - const float angle = atan2f(y, x) + rotation; + const float angle = atan2(y, x) + rotation; const float flen = sqrtf(x*x + y*y); x = flen * cosf(angle); @@ -769,6 +938,49 @@ static float tex_strength(SculptSession *ss, Brush *br, float *point, const floa avg = paint_get_tex_pixel(br, x, y); } + // XXX: redunancy here can be refactored once this has been debugged + else /* mtex->brush_map_mode == MTEX_MAP_MODE_WRAP */{ + float rotation = -mtex->rot; + float x, y, point_2d[3]; + + /* if the active area is being applied for symmetry, flip it + across the symmetry axis and rotate it back to the orignal + position in order to project it. This insures that the + brush texture will be oriented correctly. */ + + flip_coord(point_2d, point, ss->cache->mirror_symmetry_pass); + + if (ss->cache->radial_symmetry_pass) + mul_m4_v3(ss->cache->symm_rot_mat_inv, point_2d); + + mul_m4_v3(ss->cache->symm_brush_local_mat, point_2d); + + x = point_2d[0]; + y = point_2d[1]; + + if (br->flag & BRUSH_RANDOM_ROTATION) + rotation += ss->cache->special_rotation; + + /* it is probably worth optimizing for those cases where + the texture is not rotated by skipping the calls to + atan2, sqrtf, sin, and cos. */ + + if (rotation > 0.001 || rotation < -0.001) { + const float angle = (atan2(y, x) + rotation) * rotation_flip[ss->cache->mirror_symmetry_pass]; + const float flen = sqrtf(x*x + y*y); + + x = flen * cos(angle); + y = flen * sin(angle); + } + + x *= br->mtex.size[0]; + y *= br->mtex.size[1]; + + x += br->mtex.ofs[0]; + y += br->mtex.ofs[1]; + + avg = paint_get_tex_pixel(br, x, y); + } avg += br->texture_sample_bias; @@ -836,7 +1048,7 @@ static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], floa } } -static void calc_area_normal(Sculpt *sd, Object *ob, float an[3], PBVHNode **nodes, int totnode) +static void calc_area_normal(const Sculpt *sd, const Object *ob, float an[3], PBVHNode **nodes, int totnode, float range) { SculptSession *ss = ob->sculpt; int n; @@ -844,7 +1056,7 @@ static void calc_area_normal(Sculpt *sd, Object *ob, float an[3], PBVHNode **nod float out_flip[3] = {0.0f, 0.0f, 0.0f}; (void)sd; /* unused w/o openmp */ - + zero_v3(an); #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) @@ -856,7 +1068,7 @@ static void calc_area_normal(Sculpt *sd, Object *ob, float an[3], PBVHNode **nod float private_out_flip[3] = {0.0f, 0.0f, 0.0f}; unode = sculpt_undo_push_node(ob, nodes[n]); - sculpt_brush_test_init(ss, &test); + sculpt_brush_range_test_init(ss, &test, range); if(ss->cache->original) { BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -901,7 +1113,7 @@ static void calc_area_normal(Sculpt *sd, Object *ob, float an[3], PBVHNode **nod /* This initializes the faces to be moved for this sculpt for draw/layer/flatten; then it finds average normal for all active vertices - note that this is called once for each mirroring direction */ -static void calc_sculpt_normal(Sculpt *sd, Object *ob, float an[3], PBVHNode **nodes, int totnode) +static void calc_sculpt_normal(const Sculpt *sd, const Object *ob, float an[3], PBVHNode **nodes, int totnode, float range) { SculptSession *ss = ob->sculpt; Brush *brush = paint_brush(&sd->paint); @@ -934,7 +1146,7 @@ static void calc_sculpt_normal(Sculpt *sd, Object *ob, float an[3], PBVHNode **n break; case SCULPT_DISP_DIR_AREA: - calc_area_normal(sd, ob, an, nodes, totnode); + calc_area_normal(sd, ob, an, nodes, totnode, range); default: break; @@ -963,8 +1175,10 @@ static void neighbor_average(SculptSession *ss, float avg[3], const unsigned ver /* Don't modify corner vertices */ if(ncount==1) { - if(ss->deform_cos) copy_v3_v3(avg, ss->deform_cos[vert]); - else copy_v3_v3(avg, ss->mvert[vert].co); + if (ss->deform_cos) + copy_v3_v3(avg, ss->deform_cos[vert]); + else + copy_v3_v3(avg, ss->mvert[vert].co); return; } @@ -981,8 +1195,11 @@ static void neighbor_average(SculptSession *ss, float avg[3], const unsigned ver for(i=0; i<(f->v4?4:3); ++i) { if(i != skip && (ncount!=2 || BLI_countlist(&ss->fmap[(&f->v1)[i]]) <= 2)) { - if(ss->deform_cos) add_v3_v3(avg, ss->deform_cos[(&f->v1)[i]]); - else add_v3_v3(avg, ss->mvert[(&f->v1)[i]].co); + if (ss->deform_cos) + add_v3_v3(avg, ss->deform_cos[(&f->v1)[i]]); + else + add_v3_v3(avg, ss->mvert[(&f->v1)[i]].co); + ++total; } } @@ -990,11 +1207,14 @@ static void neighbor_average(SculptSession *ss, float avg[3], const unsigned ver node= node->next; } - if(total>0) + if (total > 0) { mul_v3_fl(avg, 1.0f / total); + } else { - if(ss->deform_cos) copy_v3_v3(avg, ss->deform_cos[vert]); - else copy_v3_v3(avg, ss->mvert[vert].co); + if (ss->deform_cos) + copy_v3_v3(avg, ss->deform_cos[vert]); + else + copy_v3_v3(avg, ss->mvert[vert].co); } } @@ -1003,14 +1223,20 @@ static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, Brush *brush = paint_brush(&sd->paint); PBVHVertexIter vd; SculptBrushTest test; - + CLAMP(bstrength, 0.0f, 1.0f); sculpt_brush_test_init(ss, &test); BLI_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + short (*origno)[3]; + SculptUndoNode *unode; + + unode= sculpt_undo_push_node(ss, node); + origno= unode->no; + if(sculpt_brush_test(&test, vd.co)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno); + const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, ss->cache->view_normal, origno[vd.i]); float avg[3], val[3]; neighbor_average(ss, avg, vd.vert_indices[vd.i]); @@ -1036,7 +1262,13 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no DMGridAdjacency *gridadj, *adj; float (*tmpgrid)[3], (*tmprow)[3]; int v1, v2, v3, v4; - int *grid_indices, totgrid, gridsize, i, x, y; + int *grid_indices, totgrid, gridsize, i, k, x, y; + + short (*origno)[3]; + SculptUndoNode *unode; + + unode= sculpt_undo_push_node(ss, node); + origno= unode->no; sculpt_brush_test_init(ss, &test); @@ -1051,7 +1283,7 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no tmprow= MEM_mallocN(sizeof(float)*3*gridsize, "tmprow"); } - for(i = 0; i < totgrid; ++i) { + for(i = 0, k= 0; i < totgrid; ++i) { data = griddata[grid_indices[i]]; adj = &gridadj[grid_indices[i]]; @@ -1080,8 +1312,8 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no } /* blend with existing coordinates */ - for(y = 0; y < gridsize; ++y) { - for(x = 0; x < gridsize; ++x) { + for(y= 0; y < gridsize; ++y) { + for(x= 0; x < gridsize; ++x, ++k) { float *co; float *fno; int index; @@ -1103,7 +1335,7 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no fno= data[index].no; if(sculpt_brush_test(&test, co)) { - const float fade = bstrength*tex_strength(ss, brush, co, test.dist)*frontface(brush, ss->cache->view_normal, NULL, fno); + const float fade = bstrength*tex_strength(ss, brush, co, test.dist)*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, ss->cache->view_normal, origno[k]); float *avg, val[3]; float n; @@ -1144,15 +1376,21 @@ static void smooth(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float const float fract = 1.0f/max_iterations; int iteration, n, count; float last; + Brush *brush = paint_brush(&sd->paint); CLAMP(bstrength, 0, 1); count = (int)(bstrength*max_iterations); last = max_iterations*(bstrength - count*fract); + set_brush_local_mat(sd, ob, brush, nodes, totnode, NULL); + + set_adaptive_space_factor(sd, ob, nodes, totnode, NULL); + for(iteration = 0; iteration <= count; ++iteration) { #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for(n=0; n<totnode; n++) { + if(ss->multires) { do_multires_smooth_brush(sd, ss, nodes[n], iteration != count ? 1.0f : last); } @@ -1167,22 +1405,25 @@ static void smooth(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float static void do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - smooth(sd, ob, nodes, totnode, ss->cache->bstrength); + smooth(sd, ob, nodes, totnode, ob->sculpt->cache->bstrength); } static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; Brush *brush = paint_brush(&sd->paint); - float offset[3], area_normal[3]; + float offset[3], an[3]; float bstrength= ss->cache->bstrength; int n; - calc_sculpt_normal(sd, ob, area_normal, nodes, totnode); - + calc_sculpt_normal(sd, ob, an, nodes, totnode, brush->sculpt_plane_range); + + set_brush_local_mat(sd, ob, brush, NULL, 0, an); + + set_adaptive_space_factor(sd, ob, NULL, 0, an); + /* offset with as much as possible factored in already */ - mul_v3_v3fl(offset, area_normal, ss->cache->radius); + mul_v3_v3fl(offset, an, ss->cache->radius); mul_v3_v3(offset, ss->cache->scale); mul_v3_fl(offset, bstrength); @@ -1192,6 +1433,11 @@ static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) PBVHVertexIter vd; SculptBrushTest test; float (*proxy)[3]; + short (*origno)[3]; + SculptUndoNode *unode; + + unode= sculpt_undo_push_node(ob, nodes[n]); + origno= unode->no; proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; @@ -1199,9 +1445,9 @@ static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if (sculpt_brush_test(&test, vd.co)) { - //if(sculpt_brush_test_cyl(&test, vd.co, ss->cache->location, area_normal)) { + //if(sculpt_brush_test_cyl(&test, vd.co, ss->cache->location, an)) { /* offset vertex */ - float fade = tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, area_normal, vd.no, vd.fno); + float fade = tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, an, origno[vd.i]); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -1213,22 +1459,85 @@ static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) } } +static void gravity(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float bstrength) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = paint_brush(&sd->paint); + float offset[3], an[3]; + int n; + float gravity[3]; + + gravity[0]= gravity[1]= 0; + gravity[2]= -ss->cache->radius; + + calc_sculpt_normal(sd, ob, an, nodes, totnode, brush->sculpt_plane_range); + + set_brush_local_mat(sd, ob, brush, NULL, 0, an); + + set_adaptive_space_factor(sd, ob, NULL, 0, an); + + /* offset with as much as possible factored in already */ + mul_v3_v3fl(offset, gravity, ss->cache->radius); + mul_v3_v3(offset, ss->cache->scale); + mul_v3_fl(offset, bstrength); + + /* threaded loop over nodes */ + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for(n=0; n<totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + float (*proxy)[3]; + short (*origno)[3]; + SculptUndoNode *unode; + + unode= sculpt_undo_push_node(ob, nodes[n]); + origno= unode->no; + + proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if (sculpt_brush_test(&test, vd.co)) { + //if(sculpt_brush_test_cyl(&test, vd.co, ss->cache->location, an)) { + /* offset vertex */ + float fade = tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, an, origno[vd.i]); + + mul_v3_v3fl(proxy[vd.i], offset, fade); + + if(vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BLI_pbvh_vertex_iter_end; + } +} + +static void do_gravity_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +{ + gravity(sd, ss, nodes, totnode, ss->cache->bstrength); +} + static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; Brush *brush = paint_brush(&sd->paint); - float offset[3], area_normal[3]; + float offset[3], an[3]; float bstrength= ss->cache->bstrength; float flippedbstrength, crease_correction; int n; - calc_sculpt_normal(sd, ob, area_normal, nodes, totnode); - + calc_sculpt_normal(sd, ob, an, nodes, totnode, brush->sculpt_plane_range); + + set_brush_local_mat(sd, ob, brush, NULL, 0, an); + + set_adaptive_space_factor(sd, ob, NULL, 0, an); + /* offset with as much as possible factored in already */ - mul_v3_v3fl(offset, area_normal, ss->cache->radius); + mul_v3_v3fl(offset, an, ss->cache->radius); mul_v3_v3(offset, ss->cache->scale); mul_v3_fl(offset, bstrength); - + /* we divide out the squared alpha and multiply by the squared crease to give us the pinch strength */ if(brush_alpha(brush) > 0.0f) @@ -1247,6 +1556,11 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod PBVHVertexIter vd; SculptBrushTest test; float (*proxy)[3]; + short (*origno)[3]; + SculptUndoNode *unode; + + unode= sculpt_undo_push_node(ob, nodes[n]); + origno= unode->no; proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; @@ -1255,7 +1569,7 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if(sculpt_brush_test(&test, vd.co)) { /* offset vertex */ - const float fade = tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, area_normal, vd.no, vd.fno); + const float fade = tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_start, an, origno[vd.i]); float val1[3]; float val2[3]; @@ -1284,11 +1598,20 @@ static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode float bstrength= ss->cache->bstrength; int n; + set_brush_local_mat(sd, ob, brush, nodes, totnode, NULL); + + set_adaptive_space_factor(sd, ob, nodes, totnode, NULL); + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; float (*proxy)[3]; + short (*origno)[3]; + SculptUndoNode *unode; + + unode= sculpt_undo_push_node(ob, nodes[n]); + origno= unode->no; proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; @@ -1296,7 +1619,7 @@ static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if(sculpt_brush_test(&test, vd.co)) { - float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno); + float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, ss->cache->view_normal, origno[vd.i]); float val[3]; sub_v3_v3v3(val, test.location, vd.co); @@ -1304,7 +1627,7 @@ static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } + } } BLI_pbvh_vertex_iter_end; } @@ -1319,6 +1642,8 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) int n; float len; + // XXX: this probably needs to be done for thumb and rotate as well + // but not for nudge or snakehook if (brush->normal_weight > 0 || brush->flag & BRUSH_FRONTFACE) { int cache= 1; /* grab brush requires to test on original data */ @@ -1326,7 +1651,11 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) calc_sculpt_normal(sd, ob, an, nodes, totnode); SWAP(int, ss->cache->original, cache); } - + + set_brush_local_mat(sd, ob, brush, nodes, totnode, brush->normal_weight > 0 ? an : NULL); + + set_adaptive_space_factor(sd, ob, nodes, totnode, brush->normal_weight > 0 ? an : NULL); + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); len = len_v3(grab_delta); @@ -1356,7 +1685,7 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if(sculpt_brush_test(&test, origco[vd.i])) { - const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, an, origno[vd.i], NULL); + const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, an, origno[vd.i]); mul_v3_v3fl(proxy[vd.i], grab_delta, fade); @@ -1380,7 +1709,11 @@ static void do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - calc_sculpt_normal(sd, ob, an, nodes, totnode); + calc_sculpt_normal(sd, ob, an, nodes, totnode, brush->sculpt_plane_range); + + set_brush_local_mat(sd, ob, brush, NULL, 0, an); + + set_adaptive_space_factor(sd, ob, NULL, 0, an); cross_v3_v3v3(tmp, an, grab_delta); cross_v3_v3v3(cono, tmp, an); @@ -1390,6 +1723,11 @@ static void do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode PBVHVertexIter vd; SculptBrushTest test; float (*proxy)[3]; + short (*origno)[3]; + SculptUndoNode *unode; + + unode= sculpt_undo_push_node(ob, nodes[n]); + origno= unode->no; proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; @@ -1397,7 +1735,7 @@ static void do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if(sculpt_brush_test(&test, vd.co)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, an, vd.no, vd.fno); + const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, an, origno[vd.i]); mul_v3_v3fl(proxy[vd.i], cono, fade); @@ -1419,7 +1757,11 @@ static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to float len; if (brush->normal_weight > 0 || brush->flag & BRUSH_FRONTFACE) - calc_sculpt_normal(sd, ob, an, nodes, totnode); + calc_sculpt_normal(sd, ob, an, nodes, totnode, brush->sculpt_plane_range); + + set_brush_local_mat(sd, ob, brush, nodes, totnode, brush->normal_weight > 0 ? an : NULL); + + set_adaptive_space_factor(sd, ob, nodes, totnode, brush->normal_weight > 0 ? an : NULL); copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); @@ -1439,6 +1781,11 @@ static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to PBVHVertexIter vd; SculptBrushTest test; float (*proxy)[3]; + short (*origno)[3]; + SculptUndoNode *unode; + + unode= sculpt_undo_push_node(ob, nodes[n]); + origno= unode->no; proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; @@ -1446,7 +1793,7 @@ static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if(sculpt_brush_test(&test, vd.co)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, an, vd.no, vd.fno); + const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, an, origno[vd.i]); mul_v3_v3fl(proxy[vd.i], grab_delta, fade); @@ -1470,7 +1817,11 @@ static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - calc_sculpt_normal(sd, ob, an, nodes, totnode); + calc_sculpt_normal(sd, ob, an, nodes, totnode, brush->sculpt_plane_range); + + set_brush_local_mat(sd, ob, brush, NULL, 0, an); + + set_adaptive_space_factor(sd, ob, NULL, 0, an); cross_v3_v3v3(tmp, an, grab_delta); cross_v3_v3v3(cono, tmp, an); @@ -1494,7 +1845,7 @@ static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if(sculpt_brush_test(&test, origco[vd.i])) { - const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, an, origno[vd.i], NULL); + const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, an, origno[vd.i]); mul_v3_v3fl(proxy[vd.i], cono, fade); @@ -1514,10 +1865,13 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod float an[3]; int n; float m[3][3]; - static const int flip[8] = { 1, -1, -1, 1, -1, 1, 1, -1 }; - float angle = ss->cache->vertex_rotation * flip[ss->cache->mirror_symmetry_pass]; + float angle = ss->cache->vertex_rotation * rotation_flip[ss->cache->mirror_symmetry_pass]; + + calc_sculpt_normal(sd, ob, an, nodes, totnode, brush->sculpt_plane_range); - calc_sculpt_normal(sd, ob, an, nodes, totnode); + set_brush_local_mat(sd, ob, brush, NULL, 0, an); + + set_adaptive_space_factor(sd, ob, NULL, 0, an); axis_angle_to_mat3(m, an, angle); @@ -1540,7 +1894,7 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if(sculpt_brush_test(&test, origco[vd.i])) { - const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, an, origno[vd.i], NULL); + const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, an, origno[vd.i]); mul_v3_m3v3(proxy[vd.i], m, origco[vd.i]); sub_v3_v3(proxy[vd.i], origco[vd.i]); @@ -1554,21 +1908,26 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod } } +#if 0 static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; Brush *brush = paint_brush(&sd->paint); float bstrength= ss->cache->bstrength; - float area_normal[3], offset[3]; + float an[3], offset[3]; float lim= brush->height; int n; if(bstrength < 0) lim = -lim; - calc_sculpt_normal(sd, ob, area_normal, nodes, totnode); + calc_sculpt_normal(sd, ob, an, nodes, totnode, brush->sculpt_plane_range); + + set_brush_local_mat(sd, ob, brush, NULL, 0, an); - mul_v3_v3v3(offset, ss->cache->scale, area_normal); + set_adaptive_space_factor(sd, ob, NULL, 0, an); + + mul_v3_v3v3(offset, ss->cache->scale, an); #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for(n=0; n<totnode; n++) { @@ -1579,7 +1938,9 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode //float (*proxy)[3]; // XXX layer brush needs conversion to proxy but its more complicated //proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; - + + short (*origno)[3]; + unode= sculpt_undo_push_node(ob, nodes[n]); origco=unode->co; if(!unode->layer_disp) @@ -1590,11 +1951,13 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode layer_disp= unode->layer_disp; + origno= unode->no; + sculpt_brush_test_init(ss, &test); BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, origco[vd.i])) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, area_normal, vd.no, vd.fno); + if(sculpt_brush_test(&test, vd.co)) { + const float fade = bstrength*ss->cache->radius*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, an, origno[vd.i]); float *disp= &layer_disp[vd.i]; float val[3]; @@ -1625,6 +1988,7 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode BLI_pbvh_vertex_iter_end; } } +#endif static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { @@ -1633,11 +1997,20 @@ static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno float bstrength= ss->cache->bstrength; int n; + set_brush_local_mat(sd, ob, brush, nodes, totnode, NULL); + + set_adaptive_space_factor(sd, ob, nodes, totnode, NULL); + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; float (*proxy)[3]; + short (*origno)[3]; + SculptUndoNode* unode; + + unode= sculpt_undo_push_node(ob, nodes[n]); + origno= unode->no; proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; @@ -1645,7 +2018,7 @@ static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if(sculpt_brush_test(&test, vd.co)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno); + const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, ss->cache->view_normal, origno[vd.i]); float val[3]; if(vd.fno) copy_v3_v3(val, vd.fno); @@ -1715,7 +2088,7 @@ static void calc_flatten_center(Sculpt *sd, Object *ob, PBVHNode **nodes, int to /* this calculates flatten center and area normal together, amortizing the memory bandwidth and loop overhead to calculate both at the same time */ -static void calc_area_normal_and_flatten_center(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float an[3], float fc[3]) +static void calc_area_normal_and_flatten_center(Sculpt *sd, Ojbect *ob, PBVHNode **nodes, int totnode, float range, float an[3], float fc[3]) { SculptSession *ss = ob->sculpt; int n; @@ -1727,7 +2100,7 @@ static void calc_area_normal_and_flatten_center(Sculpt *sd, Object *ob, PBVHNode float count = 0; (void)sd; /* unused w/o openmp */ - + // an zero_v3(an); @@ -1745,7 +2118,7 @@ static void calc_area_normal_and_flatten_center(Sculpt *sd, Object *ob, PBVHNode int private_count = 0; unode = sculpt_undo_push_node(ob, nodes[n]); - sculpt_brush_test_init(ss, &test); + sculpt_brush_range_test_init(ss, &test, range); if(ss->cache->original) { BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -1812,7 +2185,7 @@ static void calc_area_normal_and_flatten_center(Sculpt *sd, Object *ob, PBVHNode } } -static void calc_sculpt_plane(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float an[3], float fc[3]) +static void calc_sculpt_plane(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float range, float an[3], float fc[3]) { SculptSession *ss = ob->sculpt; Brush *brush = paint_brush(&sd->paint); @@ -1845,7 +2218,7 @@ static void calc_sculpt_plane(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn break; case SCULPT_DISP_DIR_AREA: - calc_area_normal_and_flatten_center(sd, ob, nodes, totnode, an, fc); + calc_area_normal_and_flatten_center(sd, ob, nodes, totnode, range, an, fc); default: break; @@ -1886,35 +2259,53 @@ static void calc_sculpt_plane(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn /* Projects a point onto a plane along the plane's normal */ static void point_plane_project(float intr[3], float co[3], float plane_normal[3], float plane_center[3]) { - sub_v3_v3v3(intr, co, plane_center); - mul_v3_v3fl(intr, plane_normal, dot_v3v3(plane_normal, intr)); - sub_v3_v3v3(intr, co, intr); + sub_v3_v3v3(intr, co, plane_center); + mul_v3_v3fl(intr, plane_normal, dot_v3v3(plane_normal, intr)); + sub_v3_v3v3(intr, co, intr); } -static int plane_trim(StrokeCache *cache, Brush *brush, float val[3]) +static int plane_trim(StrokeCache *cache, Brush *brush, float val[3], float *fade) { - return !(brush->flag & BRUSH_PLANE_TRIM) || (dot_v3v3(val, val) <= cache->radius_squared*cache->plane_trim_squared); + if (brush->flag & BRUSH_PLANE_TRIM) { + float len_squared= dot_v3v3(val, val); + float trim_squared= cache->radius_squared*cache->plane_trim_squared; + + if (len_squared > trim_squared) { + return 0; + } + else { + *fade= 1 - sqrt(len_squared)/sqrt(trim_squared); + + return 1; + } + } + else { + *fade= 1; + + return 1; + } + } static int plane_point_side_flip(float co[3], float plane_normal[3], float plane_center[3], int flip) { - float delta[3]; - float d; + float delta[3]; + float d; - sub_v3_v3v3(delta, co, plane_center); - d = dot_v3v3(plane_normal, delta); + sub_v3_v3v3(delta, co, plane_center); + d = dot_v3v3(plane_normal, delta); - if (flip) d = -d; + if (flip) d = -d; - return d <= 0.0f; + return d <= 0.0f; } static int plane_point_side(float co[3], float plane_normal[3], float plane_center[3]) { - float delta[3]; + float delta[3]; - sub_v3_v3v3(delta, co, plane_center); - return dot_v3v3(plane_normal, delta) <= 0.0f; + sub_v3_v3v3(delta, co, plane_center); + return dot_v3v3(plane_normal, delta) <= 0.0f; } static float get_offset(Sculpt *sd, SculptSession *ss) @@ -1949,7 +2340,11 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, an, fc); + calc_sculpt_plane(sd, obs, nodes, totnode, brush->sculpt_plane_range, an, fc); + + set_brush_local_mat(sd, ob, brush, NULL, 0, an); + + set_adaptive_space_factor(sd, ob, NULL, 0, an); displace = radius*offset; @@ -1962,6 +2357,11 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno PBVHVertexIter vd; SculptBrushTest test; float (*proxy)[3]; + short (*origno)[3]; + SculptUndoNode* unode; + + unode= sculpt_undo_push_node(ob, nodes[n]); + origno= unode->no; proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; @@ -1971,13 +2371,14 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno if (sculpt_brush_test_sq(&test, vd.co)) { float intr[3]; float val[3]; + float fade; point_plane_project(intr, vd.co, an, fc); sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); + if (plane_trim(ss->cache, brush, val, &fade)) { + fade *= bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, an, origno[vd.i]); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -2011,7 +2412,11 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) int flip; - calc_sculpt_plane(sd, ob, nodes, totnode, an, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, brush->sculpt_plane_range, an, fc); + + set_brush_local_mat(sd, ob, brush, NULL, 0, an); + + set_adaptive_space_factor(sd, ob, NULL, 0, an); flip = bstrength < 0; @@ -2033,6 +2438,11 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) PBVHVertexIter vd; SculptBrushTest test; float (*proxy)[3]; + short (*origno)[3]; + SculptUndoNode* unode; + + unode= sculpt_undo_push_node(ob, nodes[n]); + origno= unode->no; proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; @@ -2044,13 +2454,14 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) //if (sculpt_brush_test_cyl(&test, vd.co, ss->cache->location, p)) { float intr[3]; float val[3]; + float fade; point_plane_project(intr, vd.co, an, fc); sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); + if (plane_trim(ss->cache, brush, val, &fade)) { + fade *= bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, an, origno[vd.i]); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -2064,7 +2475,7 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) } } -static void do_clay_tubes_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +static void do_clay_strips_brush(Sculpt *sd, Object* ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; Brush *brush = paint_brush(&sd->paint); @@ -2088,10 +2499,14 @@ static void do_clay_tubes_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to int flip; - calc_sculpt_plane(sd, ob, nodes, totnode, sn, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, brush->sculpt_plane_range, sn, fc); + + set_brush_local_mat(sd, ob, brush, NULL, 0, sn); + + set_adaptive_space_factor(sd, ob, NULL, 0, sn); if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) - calc_area_normal(sd, ob, an, nodes, totnode); + calc_area_normal(sd, ob, an, nodes, totnode, brush->sculpt_plane_range); else copy_v3_v3(an, sn); @@ -2111,7 +2526,7 @@ static void do_clay_tubes_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to mul_v3_fl(temp, displace); add_v3_v3(fc, temp); - cross_v3_v3v3(mat[0], an, ss->cache->grab_delta_symmetry); mat[0][3] = 0; + cross_v3_v3v3(mat[0], an, ss->cache->rake_delta_symmetry); mat[0][3] = 0; cross_v3_v3v3(mat[1], an, mat[0]); mat[1][3] = 0; copy_v3_v3(mat[2], an); mat[2][3] = 0; copy_v3_v3(mat[3], ss->cache->location); mat[3][3] = 1; @@ -2125,23 +2540,31 @@ static void do_clay_tubes_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to PBVHVertexIter vd; SculptBrushTest test; float (*proxy)[3]; + float cube_fade; + short (*origno)[3]; + SculptUndoNode* unode; + + unode= sculpt_undo_push_node(ob, nodes[n]); + origno= unode->no; proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; sculpt_brush_test_init(ss, &test); BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if (sculpt_brush_test_cube(&test, vd.co, mat)) { + cube_fade = 1; + if (sculpt_brush_test_cube(&test, &cube_fade, vd.co, mat)) { if (plane_point_side_flip(vd.co, sn, fc, flip)) { float intr[3]; float val[3]; + float fade; point_plane_project(intr, vd.co, sn, fc); sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, ss->cache->radius*test.dist)*frontface(brush, an, vd.no, vd.fno); + if (plane_trim(ss->cache, brush, val, &fade)) { + fade *= cube_fade*bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, an, origno[vd.i]); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -2173,7 +2596,11 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, an, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, brush->sculpt_plane_range, an, fc); + + set_brush_local_mat(sd, ob, brush, NULL, 0, an); + + set_adaptive_space_factor(sd, ob, NULL, 0, an); displace = radius*offset; @@ -2186,6 +2613,11 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) PBVHVertexIter vd; SculptBrushTest test; float (*proxy)[3]; + short (*origno)[3]; + SculptUndoNode* unode; + + unode= sculpt_undo_push_node(ob, nodes[n]); + origno= unode->no; proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; @@ -2196,13 +2628,14 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) if (plane_point_side(vd.co, an, fc)) { float intr[3]; float val[3]; + float fade; point_plane_project(intr, vd.co, an, fc); sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); + if (plane_trim(ss->cache, brush, val, &fade)) { + fade *= bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, an, origno[vd.i]); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -2216,7 +2649,7 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) } } -static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +static void do_scrape_brush(Sculpt *sd, Object* ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; Brush *brush = paint_brush(&sd->paint); @@ -2234,7 +2667,11 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, an, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, brush->sculpt_plane_range, an, fc); + + set_brush_local_mat(sd, ob, brush, NULL, 0, an); + + set_adaptive_space_factor(sd, ob, NULL, 0, an); displace = -radius*offset; @@ -2247,6 +2684,11 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod PBVHVertexIter vd; SculptBrushTest test; float (*proxy)[3]; + short (*origno)[3]; + SculptUndoNode* unode; + + unode= sculpt_undo_push_node(ob, nodes[n]); + origno= unode->no; proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; @@ -2257,13 +2699,14 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod if (!plane_point_side(vd.co, an, fc)) { float intr[3]; float val[3]; + float fade; point_plane_project(intr, vd.co, an, fc); sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); + if (plane_trim(ss->cache, brush, val, &fade)) { + fade *= bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, ss->cache->frontface_start, ss->cache->frontface_range, an, origno[vd.i]); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -2327,9 +2770,79 @@ void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]) vertcos_to_key(ob, kb, vertCos); } -static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush) +/* copy the modified vertices from bvh to the active key */ +static void sculpt_update_keyblock(Object *ob) { SculptSession *ss = ob->sculpt; + float (*vertCos)[3]; + + /* Keyblock update happens after hadning deformation caused by modifiers, + so ss->orig_cos would be updated with new stroke */ + if(ss->orig_cos) vertCos = ss->orig_cos; + else vertCos = BLI_pbvh_get_vertCos(ss->pbvh); + + if (vertCos) { + sculpt_vertcos_to_key(ob, ss->kb, vertCos); + + if(vertCos != ss->orig_cos) + MEM_freeN(vertCos); + } +} + +/* flush displacement from deformed PBVH to original layer */ +static void sculpt_flush_stroke_deform(Sculpt *sd, Object *ob) +{ + SculptSession *ss = ob->sculpt; + Brush *brush= paint_brush(&sd->paint); + + if(brush->sculpt_tool == SCULPT_TOOL_SMOOTH) { + /* smooth brushes does not use proxies, so sculpt_combine_proxies() + does not propagate needed deformation to original base */ + + int n, totnode; + Mesh *me= (Mesh*)ob->data; + PBVHNode** nodes; + float (*vertCos)[3]= NULL; + + if(ss->kb) + vertCos= MEM_callocN(sizeof(*vertCos)*me->totvert, "flushStrokeDeofrm keyVerts"); + + BLI_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for (n= 0; n < totnode; n++) { + PBVHVertexIter vd; + + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + sculpt_flush_pbvhvert_deform(ob, &vd); + + if(vertCos) { + int index= vd.vert_indices[vd.i]; + copy_v3_v3(vertCos[index], ss->orig_cos[index]); + } + } + BLI_pbvh_vertex_iter_end; + } + + if(vertCos) { + sculpt_vertcos_to_key(ob, ss->kb, vertCos); + MEM_freeN(vertCos); + } + + MEM_freeN(nodes); + + /* Modifiers could depend on mesh normals, so we should update them/ + Note, then if sculpting happens on locked key, normals should be re-calculated + after applying coords from keyblock on base mesh */ + mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL); + } + else if (ss->kb) { + sculpt_update_keyblock(ob); + } +} + +static void do_brush_action(Sculpt *sd, Object* ob, Brush *brush) +{ SculptSearchSphereData data; PBVHNode **nodes = NULL; int n, totnode; @@ -2343,6 +2856,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush) /* Only act if some verts are inside the brush area */ if (totnode) { + // XXX: is pushing unode here, then pushing again in brush redundant? #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) for (n= 0; n < totnode; n++) { sculpt_undo_push_node(ob, nodes[n]); @@ -2354,6 +2868,9 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush) case SCULPT_TOOL_DRAW: do_draw_brush(sd, ob, nodes, totnode); break; + case SCULPT_TOOL_GRAVITY: + do_gravity_brush(sd, ob, nodes, totnode); + break; case SCULPT_TOOL_SMOOTH: do_smooth_brush(sd, ob, nodes, totnode); break; @@ -2385,7 +2902,8 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush) do_thumb_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_LAYER: - do_layer_brush(sd, ob, nodes, totnode); + //do_layer_brush(sd, ob, nodes, totnode); + do_draw_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_FLATTEN: do_flatten_brush(sd, ob, nodes, totnode); @@ -2393,8 +2911,8 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush) case SCULPT_TOOL_CLAY: do_clay_brush(sd, ob, nodes, totnode); break; - case SCULPT_TOOL_CLAY_TUBES: - do_clay_tubes_brush(sd, ob, nodes, totnode); + case SCULPT_TOOL_CLAY_STRIPS: + do_clay_strips_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_FILL: do_fill_brush(sd, ob, nodes, totnode); @@ -2413,6 +2931,9 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush) } } + if (brush->sculpt_tool != SCULPT_TOOL_GRAVITY && brush->gravity_factor > 0) + gravity(sd, ob, nodes, totnode, brush->gravity_factor); + MEM_freeN(nodes); } } @@ -2441,172 +2962,183 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob) SculptSession *ss = ob->sculpt; Brush *brush= paint_brush(&sd->paint); PBVHNode** nodes; - int totnode, n; + int totnode; + int n; + + if (brush->sculpt_tool == SCULPT_TOOL_SMOOTH) + return; BLI_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); - if(!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER)) { - /* these brushes start from original coordinates */ - const int use_orco = (ELEM3(brush->sculpt_tool, SCULPT_TOOL_GRAB, - SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB)); + switch (brush->sculpt_tool) { + case SCULPT_TOOL_GRAB: + case SCULPT_TOOL_ROTATE: + case SCULPT_TOOL_THUMB: + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for (n= 0; n < totnode; n++) { + PBVHVertexIter vd; + PBVHProxyNode* proxies; + int proxy_count; + float (*origco)[3]; - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) - for (n= 0; n < totnode; n++) { - PBVHVertexIter vd; - PBVHProxyNode* proxies; - int proxy_count; - float (*orco)[3]; + origco= sculpt_undo_push_node(ob, nodes[n])->co; - if(use_orco) - orco= sculpt_undo_push_node(ob, nodes[n])->co; + BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count); - BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count); + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + float val[3]; + int p; - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - float val[3]; - int p; + copy_v3_v3(val, origco[vd.i]); - if(use_orco) - copy_v3_v3(val, orco[vd.i]); - else - copy_v3_v3(val, vd.co); + for (p= 0; p < proxy_count; p++) + add_v3_v3(val, proxies[p].co[vd.i]); - for (p= 0; p < proxy_count; p++) - add_v3_v3(val, proxies[p].co[vd.i]); + sculpt_clip(sd, ss, vd.co, val); - sculpt_clip(sd, ss, vd.co, val); + if (ss->modifiers_active) + sculpt_flush_pbvhvert_deform(ob, &vd); + } + BLI_pbvh_vertex_iter_end; - if(ss->modifiers_active) - sculpt_flush_pbvhvert_deform(ob, &vd); + BLI_pbvh_node_free_proxies(nodes[n]); } - BLI_pbvh_vertex_iter_end; - BLI_pbvh_node_free_proxies(nodes[n]); - } - } + break; - if (nodes) - MEM_freeN(nodes); -} + case SCULPT_TOOL_DRAW: + case SCULPT_TOOL_GRAVITY: + case SCULPT_TOOL_CLAY: + case SCULPT_TOOL_CLAY_STRIPS: + case SCULPT_TOOL_CREASE: + case SCULPT_TOOL_BLOB: + case SCULPT_TOOL_FILL: + case SCULPT_TOOL_FLATTEN: + case SCULPT_TOOL_INFLATE: + case SCULPT_TOOL_NUDGE: + case SCULPT_TOOL_PINCH: + case SCULPT_TOOL_SCRAPE: + case SCULPT_TOOL_SNAKE_HOOK: + case SCULPT_TOOL_LAYER: + #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + for (n= 0; n < totnode; n++) { + PBVHVertexIter vd; + PBVHProxyNode* proxies; + int proxy_count; -/* copy the modified vertices from bvh to the active key */ -static void sculpt_update_keyblock(Object *ob) -{ - SculptSession *ss = ob->sculpt; - float (*vertCos)[3]; + BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count); - /* Keyblock update happens after hadning deformation caused by modifiers, - so ss->orig_cos would be updated with new stroke */ - if(ss->orig_cos) vertCos = ss->orig_cos; - else vertCos = BLI_pbvh_get_vertCos(ss->pbvh); + BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + float val[3]; + int p; - if (vertCos) { - sculpt_vertcos_to_key(ob, ss->kb, vertCos); + copy_v3_v3(val, vd.co); + for (p= 0; p < proxy_count; p++) + add_v3_v3(val, proxies[p].co[vd.i]); + + if (brush->sculpt_tool == SCULPT_TOOL_LAYER || brush->flag & BRUSH_LAYER) { + float disp[3]; + float len; + float *base; + + if (ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) { + int index= vd.vert_indices[vd.i]; + + /* persistent base */ + base= ss->layer_co[index]; + } + else { + base= sculpt_undo_push_node(ob, nodes[n])->co[vd.i]; + } + + sub_v3_v3v3(disp, val, base); + len= len_v3(disp); + + if (len > ss->cache->radius*brush->layer_distance) { + normalize_v3(disp); + mul_v3_fl(disp, ss->cache->radius*brush->layer_distance); + add_v3_v3v3(val, disp, base); + } + } - if(vertCos != ss->orig_cos) - MEM_freeN(vertCos); - } -} + sculpt_clip(sd, ss, vd.co, val); -/* flush displacement from deformed PBVH to original layer */ -static void sculpt_flush_stroke_deform(Sculpt *sd, Object *ob) -{ - SculptSession *ss = ob->sculpt; - Brush *brush= paint_brush(&sd->paint); + if (ss->modifiers_active) + sculpt_flush_pbvhvert_deform(ob, &vd); + } + BLI_pbvh_vertex_iter_end; - if(ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER)) { - /* this brushes aren't using proxies, so sculpt_combine_proxies() wouldn't - propagate needed deformation to original base */ + BLI_pbvh_node_free_proxies(nodes[n]); + PBVHVertexIter vd; - int n, totnode; - Mesh *me= (Mesh*)ob->data; - PBVHNode** nodes; - float (*vertCos)[3]= NULL; + } - if(ss->kb) - vertCos= MEM_callocN(sizeof(*vertCos)*me->totvert, "flushStrokeDeofrm keyVerts"); + break; - BLI_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + case SCULPT_TOOL_SMOOTH: + default: + break; + } - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) - for (n= 0; n < totnode; n++) { - PBVHVertexIter vd; + if (nodes) + MEM_freeN(nodes); +} - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - sculpt_flush_pbvhvert_deform(ob, &vd); +#if 0 +static int max_overlap_count(Sculpt *sd) +{ + int count[3]; + int i, j; - if(vertCos) { - int index= vd.vert_indices[vd.i]; - copy_v3_v3(vertCos[index], ss->orig_cos[index]); - } - } - BLI_pbvh_vertex_iter_end; - } + for (i= 0; i < 3; i++) { + count[i] = sd->radial_symm[i]; - if(vertCos) { - sculpt_vertcos_to_key(ob, ss->kb, vertCos); - MEM_freeN(vertCos); + for (j= 0; j < 3; j++) { + if (i != j && sd->flags & (SCULPT_SYMM_X<<i)) + count[i] *= 2; } + } - MEM_freeN(nodes); - - /* Modifiers could depend on mesh normals, so we should update them/ - Note, then if sculpting happens on locked key, normals should be re-calculated - after applying coords from keyblock on base mesh */ - mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL); - } else if (ss->kb) - sculpt_update_keyblock(ob); + return MAX3(count[0], count[1], count[2]); } - -//static int max_overlap_count(Sculpt *sd) -//{ -// int count[3]; -// int i, j; -// -// for (i= 0; i < 3; i++) { -// count[i] = sd->radial_symm[i]; -// -// for (j= 0; j < 3; j++) { -// if (i != j && sd->flags & (SCULPT_SYMM_X<<i)) -// count[i] *= 2; -// } -// } -// -// return MAX3(count[0], count[1], count[2]); -//} +#endif /* Flip all the editdata across the axis/axes specified by symm. Used to calculate multiple modifications to the mesh when symmetry is enabled. */ static void calc_brushdata_symm(Sculpt *sd, StrokeCache *cache, const char symm, const char axis, const float angle, const float UNUSED(feather)) { - (void)sd; /* unused */ - flip_coord(cache->location, cache->true_location, symm); flip_coord(cache->grab_delta_symmetry, cache->grab_delta, symm); + flip_coord(cache->rake_delta_symmetry, cache->rake_delta, symm); flip_coord(cache->view_normal, cache->true_view_normal, symm); +#if 0 // XXX This reduces the length of the grab delta if it approaches the line of symmetry // XXX However, a different approach appears to be needed - //if (sd->flags & SCULPT_SYMMETRY_FEATHER) { - // float frac = 1.0f/max_overlap_count(sd); - // float reduce = (feather-frac)/(1-frac); + if (sd->flags & SCULPT_SYMMETRY_FEATHER) { + float frac = 1.0f/max_overlap_count(sd); + float reduce = (feather-frac)/(1-frac); - // printf("feather: %f frac: %f reduce: %f\n", feather, frac, reduce); + printf("feather: %f frac: %f reduce: %f\n", feather, frac, reduce); - // if (frac < 1) - // mul_v3_fl(cache->grab_delta_symmetry, reduce); - //} + if (frac < 1) + mul_v3_fl(cache->grab_delta_symmetry, reduce); + } +#else + (void)sd; /* XXX: unused as long as symmetry feather is disabled */ +#endif unit_m4(cache->symm_rot_mat); unit_m4(cache->symm_rot_mat_inv); - if(axis) { /* expects XYZ */ + if(axis >= 'X' && axis <= 'Z') { /* expects XYZ */ rotate_m4(cache->symm_rot_mat, axis, angle); rotate_m4(cache->symm_rot_mat_inv, axis, -angle); } mul_m4_v3(cache->symm_rot_mat, cache->location); mul_m4_v3(cache->symm_rot_mat, cache->grab_delta_symmetry); + mul_m4_v3(cache->symm_rot_mat, cache->rake_delta_symmetry); } static void do_radial_symmetry(Sculpt *sd, Object *ob, Brush *brush, const char symm, const int axis, const float feather) @@ -2637,6 +3169,7 @@ static void sculpt_fix_noise_tear(Sculpt *sd, Object *ob) static void do_symmetrical_brush_actions(Sculpt *sd, Object *ob) { + SculptSession *ss = ob->sculpt; Brush *brush = paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; StrokeCache *cache = ss->cache; @@ -2704,7 +3237,7 @@ void sculpt_free_deformMats(SculptSession *ss) ss->deform_imats = NULL; } -void sculpt_update_mesh_elements(Scene *scene, Sculpt *sd, Object *ob, int need_fmap) +void sculpt_update_mesh_elements(Scene *scene, Sculpt* sd, Object *ob, int need_fmap) { DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); SculptSession *ss = ob->sculpt; @@ -2712,8 +3245,10 @@ void sculpt_update_mesh_elements(Scene *scene, Sculpt *sd, Object *ob, int need_ ss->modifiers_active= sculpt_modifiers_active(scene, sd, ob); - if(!mmd) ss->kb= ob_get_keyblock(ob); - else ss->kb= NULL; + if (!mmd) + ss->kb= ob_get_keyblock(ob); + else + ss->kb= NULL; if(mmd) { ss->multires = mmd; @@ -2736,8 +3271,8 @@ void sculpt_update_mesh_elements(Scene *scene, Sculpt *sd, Object *ob, int need_ ss->pbvh = dm->getPBVH(ob, dm); ss->fmap = (need_fmap && dm->getFaceMap)? dm->getFaceMap(ob, dm): NULL; - if(ss->modifiers_active) { - if(!ss->orig_cos) { + if (ss->modifiers_active) { + if (!ss->orig_cos) { int a; sculpt_free_deformMats(ss); @@ -2751,7 +3286,10 @@ void sculpt_update_mesh_elements(Scene *scene, Sculpt *sd, Object *ob, int need_ for(a = 0; a < ((Mesh*)ob->data)->totvert; ++a) invert_m3(ss->deform_imats[a]); } - } else sculpt_free_deformMats(ss); + } + else { + sculpt_free_deformMats(ss); + } /* if pbvh is deformed, key block is already applied to it */ if (ss->kb && !BLI_pbvh_isDeformed(ss->pbvh)) { @@ -2782,40 +3320,107 @@ static const char *sculpt_tool_name(Sculpt *sd) switch(brush->sculpt_tool) { case SCULPT_TOOL_DRAW: - return "Draw Brush"; break; + return "Draw Brush"; + case SCULPT_TOOL_GRAVITY: + return "Gravity Brush"; case SCULPT_TOOL_SMOOTH: - return "Smooth Brush"; break; + return "Smooth Brush"; case SCULPT_TOOL_CREASE: - return "Crease Brush"; break; + return "Crease Brush"; case SCULPT_TOOL_BLOB: - return "Blob Brush"; break; + return "Blob Brush"; case SCULPT_TOOL_PINCH: - return "Pinch Brush"; break; + return "Pinch Brush"; case SCULPT_TOOL_INFLATE: - return "Inflate Brush"; break; + return "Inflate Brush"; case SCULPT_TOOL_GRAB: - return "Grab Brush"; break; + return "Grab Brush"; case SCULPT_TOOL_NUDGE: - return "Nudge Brush"; break; + return "Nudge Brush"; case SCULPT_TOOL_THUMB: - return "Thumb Brush"; break; + return "Thumb Brush"; case SCULPT_TOOL_LAYER: - return "Layer Brush"; break; + return "Layer Brush"; case SCULPT_TOOL_FLATTEN: - return "Flatten Brush"; break; + return "Flatten Brush"; case SCULPT_TOOL_CLAY: - return "Clay Brush"; break; - case SCULPT_TOOL_CLAY_TUBES: - return "Clay Tubes Brush"; break; + return "Clay Brush"; + case SCULPT_TOOL_CLAY_STRIPS: + return "Clay Strips Brush"; case SCULPT_TOOL_FILL: - return "Fill Brush"; break; + return "Fill Brush"; case SCULPT_TOOL_SCRAPE: - return "Scrape Brush"; break; + return "Scrape Brush"; default: - return "Sculpting"; break; + return "Sculpting"; } } +#if 0 +// XXX: before I had 'on-surface' working with the radial control +// with all the changes to the radial control I am probably doing to +// have to completely reimplement it +/**** Radial control ****/ +static int sculpt_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + Paint *p = paint_get_active(CTX_data_scene(C)); + Brush *brush = paint_brush(p); + float col[4], tex_col[4]; + + WM_paint_cursor_end(CTX_wm_manager(C), p->paint_cursor); + p->paint_cursor = NULL; + brush_radial_control_invoke(op, brush, 1); + + // XXX: add/sub color needs to be determined in one place so it's consistant. this same code is in paint_stroke + if((brush->flag & BRUSH_DIR_IN) && ELEM6(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, SCULPT_TOOL_CLAY_TUBES, SCULPT_TOOL_PINCH, SCULPT_TOOL_CREASE)) + copy_v3_v3(col, brush->sub_col); + else + copy_v3_v3(col, brush->add_col); + col[3]= 0.5f; + + copy_v3_v3(tex_col, U.sculpt_paint_overlay_col); + tex_col[3]= (brush->texture_overlay_alpha / 100.0f); + + RNA_float_set_array(op->ptr, "color", col); + RNA_float_set_array(op->ptr, "texture_color", tex_col); + + return WM_radial_control_invoke(C, op, event); +} + +static int sculpt_radial_control_modal(bContext *C, wmOperator *op, wmEvent *event) +{ + int ret = WM_radial_control_modal(C, op, event); + if(ret != OPERATOR_RUNNING_MODAL) + paint_cursor_start(C, sculpt_poll); + return ret; +} + +static int sculpt_radial_control_exec(bContext *C, wmOperator *op) +{ + Brush *brush = paint_brush(&CTX_data_tool_settings(C)->sculpt->paint); + int ret = brush_radial_control_exec(op, brush, 1); + + WM_event_add_notifier(C, NC_BRUSH|NA_EDITED, brush); + + return ret; +} + +static void SCULPT_OT_radial_control(wmOperatorType *ot) +{ + WM_OT_radial_control_partial(ot); + + ot->name= "Sculpt Radial Control"; + ot->idname= "SCULPT_OT_radial_control"; + + ot->invoke= sculpt_radial_control_invoke; + ot->modal= sculpt_radial_control_modal; + ot->exec= sculpt_radial_control_exec; + ot->poll= sculpt_poll; + + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING; +} +#endif + /**** Operator for applying a stroke (various attributes including mouse path) using the current brush. ****/ @@ -2897,8 +3502,10 @@ static void sculpt_update_cache_invariants(bContext* C, Sculpt *sd, SculptSessio /* not very nice, but with current events system implementation we can't handle brush appearance inversion hotkey separately (sergey) */ - if(cache->invert) brush->flag |= BRUSH_INVERTED; - else brush->flag &= ~BRUSH_INVERTED; + if (cache->invert) + brush->flag |= BRUSH_INVERTED; + else + brush->flag &= ~BRUSH_INVERTED; /* Alt-Smooth */ if (ss->cache->alt_smooth) { @@ -2909,10 +3516,10 @@ static void sculpt_update_cache_invariants(bContext* C, Sculpt *sd, SculptSessio br= (Brush *)find_id("BR", "Smooth"); if(br) { - paint_brush_set(p, br); - brush = br; + paint_brush_set(p, br); + brush = br; + } } - } copy_v2_v2(cache->mouse, cache->initial_mouse); copy_v2_v2(cache->tex_mouse, cache->initial_mouse); @@ -2935,11 +3542,12 @@ static void sculpt_update_cache_invariants(bContext* C, Sculpt *sd, SculptSessio ss->layer_co= MEM_mallocN(sizeof(float) * 3 * ss->totvert, "sculpt mesh vertices copy"); - if(ss->deform_cos) memcpy(ss->layer_co, ss->deform_cos, ss->totvert); + if (ss->deform_cos) { + memcpy(ss->layer_co, ss->deform_cos, ss->totvert); + } else { - for(i = 0; i < ss->totvert; ++i) { + for(i= 0; i < ss->totvert; ++i) copy_v3_v3(ss->layer_co[i], ss->mvert[i].co); - } } } } @@ -2956,7 +3564,7 @@ static void sculpt_update_cache_invariants(bContext* C, Sculpt *sd, SculptSessio cache->original = 1; } - if(ELEM8(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_CREASE, SCULPT_TOOL_BLOB, SCULPT_TOOL_LAYER, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, SCULPT_TOOL_CLAY_TUBES, SCULPT_TOOL_ROTATE)) + if(ELEM9(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_GRAVITY, SCULPT_TOOL_CREASE, SCULPT_TOOL_BLOB, SCULPT_TOOL_LAYER, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_ROTATE)) if(!(brush->flag & BRUSH_ACCUMULATE)) cache->original = 1; @@ -2967,8 +3575,12 @@ static void sculpt_update_cache_invariants(bContext* C, Sculpt *sd, SculptSessio cache->first_time= 1; cache->vertex_rotation= 0; + + cache->frontface_start= brush->frontface_angle; + cache->frontface_range= (brush->frontface_angle + M_PI_2) / 2 - cache->frontface_start; } +#if 0 static void sculpt_update_brush_delta(Sculpt *sd, Object *ob, Brush *brush) { SculptSession *ss = ob->sculpt; @@ -2977,7 +3589,7 @@ static void sculpt_update_brush_delta(Sculpt *sd, Object *ob, Brush *brush) if(ELEM5(tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_NUDGE, - SCULPT_TOOL_CLAY_TUBES, SCULPT_TOOL_SNAKE_HOOK, + SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_THUMB)) { float grab_location[3], imat[4][4], delta[3]; @@ -3006,7 +3618,7 @@ static void sculpt_update_brush_delta(Sculpt *sd, Object *ob, Brush *brush) mul_mat3_m4_v3(imat, delta); add_v3_v3(cache->grab_delta, delta); break; - case SCULPT_TOOL_CLAY_TUBES: + case SCULPT_TOOL_CLAY_STRIPS: case SCULPT_TOOL_NUDGE: sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); invert_m4_m4(imat, ob->obmat); @@ -3040,6 +3652,7 @@ static void sculpt_update_brush_delta(Sculpt *sd, Object *ob, Brush *brush) } } } +#endif /* Initialize the stroke cache variants from operator properties */ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, struct PaintStroke *stroke, PointerRNA *ptr) @@ -3069,6 +3682,10 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, st brush coord/pressure/etc. It's more an events design issue, which doesn't split coordinate/pressure/angle changing events. We should avoid this after events system re-design */ + /* XXX: jwilkins - I seem to remember pressure and thumb working just fine, + I need to investigate this further. Actually seems like grab/thumb should + totally ignore pressure, since grab should be based on pen position + and little else */ if(paint_space_stroke_enabled(brush) || cache->first_time) cache->pressure = RNA_float_get(ptr, "pressure"); @@ -3090,7 +3707,7 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, st } if (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK)) - cache->initial_radius *= 2.0f; + cache->initial_radius *= 1.5f; } if(brush_use_size_pressure(brush)) { @@ -3105,8 +3722,8 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, st if(!(brush->flag & BRUSH_ANCHORED || ELEM4(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE))) { copy_v2_v2(cache->tex_mouse, cache->mouse); - if ( (brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) && - (brush->flag & BRUSH_RANDOM_ROTATION) && + if (ELEM(brush->mtex.brush_map_mode, MTEX_MAP_MODE_FIXED, MTEX_MAP_MODE_WRAP) && + (brush->flag & BRUSH_RANDOM_ROTATION) && !(brush->flag & BRUSH_RAKE)) { cache->special_rotation = 2.0f*(float)M_PI*BLI_frand(); @@ -3127,8 +3744,8 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, st float halfway[2]; float out[3]; - halfway[0] = (float)dx * 0.5f + cache->initial_mouse[0]; - halfway[1] = (float)dy * 0.5f + cache->initial_mouse[1]; + halfway[0] = (float)dx * 0.5f + cache->initial_mouse[0]; + halfway[1] = (float)dy * 0.5f + cache->initial_mouse[1]; if (sculpt_stroke_get_location(C, stroke, out, halfway)) { copy_v3_v3(sd->anchored_location, out); @@ -3170,8 +3787,129 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, st } } - sculpt_update_brush_delta(sd, ob, brush); +#if 1 + /* Find the grab delta */ + if(brush->sculpt_tool == SCULPT_TOOL_GRAB) { + float grab_location[3], imat[4][4]; + + if(cache->first_time) + copy_v3_v3(cache->orig_grab_location, cache->true_location); + + /* compute 3d coordinate at same z from original location + mouse */ + initgrabz(cache->vc->rv3d, cache->orig_grab_location[0], + cache->orig_grab_location[1], cache->orig_grab_location[2]); + ED_view3d_win_to_delta(cache->vc->ar, cache->mouse, grab_location); + + /* compute delta to move verts by */ + if(!cache->first_time) { + float delta[3]; + sub_v3_v3v3(delta, grab_location, cache->old_grab_location); + invert_m4_m4(imat, ss->ob->obmat); + mul_mat3_m4_v3(imat, delta); + add_v3_v3(cache->grab_delta, delta); + } + else { + zero_v3(cache->grab_delta); + } + + copy_v3_v3(cache->old_grab_location, grab_location); + + /* location stays the same for finding vertices in brush radius */ + copy_v3_v3(cache->true_location, cache->orig_grab_location); + + sd->draw_anchored = 1; + copy_v3_v3(sd->anchored_location, cache->true_location); + copy_v3_v3(sd->anchored_initial_mouse, cache->initial_mouse); + sd->anchored_size = cache->pixel_radius; + } + /* Find the nudge/clay tubes delta */ + else if(brush->sculpt_tool == SCULPT_TOOL_NUDGE) { + float grab_location[3], imat[4][4]; + + if(cache->first_time) + copy_v3_v3(cache->orig_grab_location, cache->true_location); + + /* compute 3d coordinate at same z from original location + mouse */ + initgrabz(cache->vc->rv3d, cache->orig_grab_location[0], + cache->orig_grab_location[1], cache->orig_grab_location[2]); + ED_view3d_win_to_delta(cache->vc->ar, cache->mouse, grab_location); + + /* compute delta to move verts by */ + if (!cache->first_time) { + sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); + invert_m4_m4(imat, ss->ob->obmat); + mul_mat3_m4_v3(imat, cache->grab_delta); + } + else { + zero_v3(cache->grab_delta); + } + + copy_v3_v3(cache->old_grab_location, grab_location); + } + /* Find the snake hook delta */ + else if(brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK) { + float grab_location[3], imat[4][4]; + + if(cache->first_time) + copy_v3_v3(cache->orig_grab_location, cache->true_location); + else + add_v3_v3(cache->true_location, cache->grab_delta); + + /* compute 3d coordinate at same z from original location + mouse */ + initgrabz(cache->vc->rv3d, cache->orig_grab_location[0], + cache->orig_grab_location[1], cache->orig_grab_location[2]); + window_to_3d_delta(cache->vc->ar, grab_location, cache->mouse[0], cache->mouse[1]); + + /* compute delta to move verts by */ + if (!cache->first_time) { + sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); + invert_m4_m4(imat, ss->ob->obmat); + mul_mat3_m4_v3(imat, cache->grab_delta); + } + else { + zero_v3(cache->grab_delta); + } + + copy_v3_v3(cache->old_grab_location, grab_location); + } + /* Find the thumb delta */ + else if(brush->sculpt_tool == SCULPT_TOOL_THUMB) { + float grab_location[3], imat[4][4]; + + if(cache->first_time) + copy_v3_v3(cache->orig_grab_location, cache->true_location); + + /* compute 3d coordinate at same z from original location + mouse */ + initgrabz(cache->vc->rv3d, cache->orig_grab_location[0], + cache->orig_grab_location[1], cache->orig_grab_location[2]); + window_to_3d_delta(cache->vc->ar, grab_location, cache->mouse[0], cache->mouse[1]); + + /* compute delta to move verts by */ + if (!cache->first_time) { + float delta[3]; + sub_v3_v3v3(delta, grab_location, cache->old_grab_location); + invert_m4_m4(imat, ss->ob->obmat); + mul_mat3_m4_v3(imat, delta); + add_v3_v3(cache->grab_delta, delta); + } + else { + zero_v3(cache->grab_delta); + } + + copy_v3_v3(cache->old_grab_location, grab_location); + + /* location stays the same for finding vertices in brush radius */ + copy_v3_v3(cache->true_location, cache->orig_grab_location); + sd->draw_anchored = 1; + copy_v3_v3(sd->anchored_location, cache->orig_grab_location); + copy_v3_v3(sd->anchored_initial_mouse, cache->initial_mouse); + sd->anchored_size = cache->pixel_radius; + } + else +#else + sculpt_update_brush_delta(sd, ob, brush); +#endif if(brush->sculpt_tool == SCULPT_TOOL_ROTATE) { dx = cache->mouse[0] - cache->initial_mouse[0]; dy = cache->mouse[1] - cache->initial_mouse[1]; @@ -3185,6 +3923,30 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, st } sd->special_rotation = cache->special_rotation; + + if (brush->sculpt_tool == SCULPT_TOOL_CLAY_STRIPS || (brush->mtex.brush_map_mode == MTEX_MAP_MODE_WRAP && brush->flag & BRUSH_RAKE)) { + float rake_location[3], imat[4][4]; + + if(cache->first_time) + copy_v3_v3(cache->orig_rake_location, cache->true_location); + + /* compute 3d coordinate at same z from original location + mouse */ + initgrabz(cache->vc->rv3d, cache->orig_rake_location[0], + cache->orig_rake_location[1], cache->orig_rake_location[2]); + window_to_3d_delta(cache->vc->ar, rake_location, cache->mouse[0], cache->mouse[1]); + + /* compute delta to move verts by */ + if (!cache->first_time) { + sub_v3_v3v3(cache->rake_delta, rake_location, cache->old_rake_location); + invert_m4_m4(imat, ss->ob->obmat); + mul_mat3_m4_v3(imat, cache->rake_delta); + } + else { + zero_v3(cache->rake_delta); + } + + copy_v3_v3(cache->old_rake_location, rake_location); + } } static void sculpt_stroke_modifiers_check(bContext *C, Object *ob) @@ -3283,10 +4045,12 @@ static void sculpt_brush_init_tex(Sculpt *sd, SculptSession *ss) /* TODO: Shouldn't really have to do this at the start of every stroke, but sculpt would need some sort of notification when changes are made to the texture. */ + /* XXX: jwilkins - I recall adding a notification when the texture changes + that that I use for updating the overlay */ sculpt_update_tex(sd, ss); } -static int sculpt_brush_stroke_init(bContext *C, wmOperator *op) +static int sculpt_brush_stroke_init(bContext *C, ReportList wmOperator *op) { Scene *scene= CTX_data_scene(C); Object *ob= CTX_data_active_object(C); @@ -3294,15 +4058,15 @@ static int sculpt_brush_stroke_init(bContext *C, wmOperator *op) SculptSession *ss = CTX_data_active_object(C)->sculpt; Brush *brush = paint_brush(&sd->paint); int mode= RNA_enum_get(op->ptr, "mode"); - int is_smooth= 0; view3d_operator_needs_opengl(C); sculpt_brush_init_tex(sd, ss); - is_smooth|= mode == BRUSH_STROKE_SMOOTH; - is_smooth|= brush->sculpt_tool == SCULPT_TOOL_SMOOTH; - - sculpt_update_mesh_elements(scene, sd, ob, is_smooth); + sculpt_update_mesh_elements( + scene, + sd, + ob, + mode==BRUCH_STROKE_SMOOTH || brush->sculpt_tool==SCULPT_TOOL_SMOOTH); return 1; } @@ -3431,6 +4195,10 @@ static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op, omp_set_num_threads(2*num_procs); } #endif + sd->sculpting= 1; + + // redraw full screen at beginning of stroke so overlay can be erased + WM_main_add_notifier(NC_SPACE|ND_SPACE_VIEW3D, NULL); return 1; } @@ -3443,7 +4211,7 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, P Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; - + sculpt_stroke_modifiers_check(C, ob); sculpt_update_cache_variants(C, sd, ob, stroke, itemptr); sculpt_restore_mesh(sd, ss); @@ -3472,21 +4240,24 @@ static void sculpt_stroke_done(bContext *C, struct PaintStroke *UNUSED(stroke)) sd->draw_anchored= 0; sd->draw_pressure= 0; sd->special_rotation= 0; + sd->sculpting= 0; /* Finished */ if(ss->cache) { - Brush *brush= paint_brush(&sd->paint); - brush->flag &= ~BRUSH_INVERTED; + { + Brush *brush= paint_brush(&sd->paint); + brush->flag &= ~BRUSH_INVERTED; + } sculpt_stroke_modifiers_check(C, ob); /* Alt-Smooth */ if (ss->cache->alt_smooth) { Paint *p= &sd->paint; - brush= (Brush *)find_id("BR", ss->cache->saved_active_brush_name); - if(brush) { - paint_brush_set(p, brush); - } + Brush* brush= (Brush *)find_id("BR", ss->cache->saved_active_brush_name); + + if (brush) + paint_brush_set(p, br); } sculpt_cache_free(ss->cache); @@ -3521,10 +4292,13 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *even if(!sculpt_brush_stroke_init(C, op)) return OPERATOR_CANCELLED; - stroke = paint_stroke_new(C, sculpt_stroke_get_location, - sculpt_stroke_test_start, - sculpt_stroke_update_step, - sculpt_stroke_done, event->type); + stroke= paint_stroke_new( + C, + sculpt_stroke_get_location, + sculpt_stroke_test_start, + sculpt_stroke_update_step, + sculpt_stroke_done, + event->type); op->customdata = stroke; @@ -3550,8 +4324,14 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op) if(!sculpt_brush_stroke_init(C, op)) return OPERATOR_CANCELLED; - op->customdata = paint_stroke_new(C, sculpt_stroke_get_location, sculpt_stroke_test_start, - sculpt_stroke_update_step, sculpt_stroke_done, 0); + op->customdata= + paint_stroke_new( + C, + sculpt_stroke_get_location, + sculpt_stroke_test_start, + sculpt_stroke_update_step, + sculpt_stroke_done, + 0); /* frees op->customdata */ paint_stroke_exec(C, op); @@ -3562,10 +4342,10 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op) static void SCULPT_OT_brush_stroke(wmOperatorType *ot) { static EnumPropertyItem stroke_mode_items[] = { - {BRUSH_STROKE_NORMAL, "NORMAL", 0, "Normal", "Apply brush normally"}, - {BRUSH_STROKE_INVERT, "INVERT", 0, "Invert", "Invert action of brush for duration of stroke"}, - {BRUSH_STROKE_SMOOTH, "SMOOTH", 0, "Smooth", "Switch brush to smooth mode for duration of stroke"}, - {0} + { BRUSH_STROKE_NORMAL, "NORMAL", 0, "Normal", "Apply brush normally" }, + { BRUSH_STROKE_INVERT, "INVERT", 0, "Invert", "Invert action of brush for duration of stroke" }, + { BRUSH_STROKE_SMOOTH, "SMOOTH", 0, "Smooth", "Switch brush to smooth mode for duration of stroke" }, + { 0 } }; /* identifiers */ @@ -3697,12 +4477,16 @@ static void SCULPT_OT_sculptmode_toggle(wmOperatorType *ot) /* api callbacks */ ot->exec= sculpt_toggle_mode; ot->poll= ED_operator_object_active_editable_mesh; - - ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + // XXX: I don't think sculpt mode needs to register and + // it handles its own undo, but unsure how this should be set. + ot->flag= 0; + //ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } void ED_operatortypes_sculpt(void) { + //WM_operatortype_append(SCULPT_OT_radial_control); // XXX: need to reimplement on-surface radial control WM_operatortype_append(SCULPT_OT_brush_stroke); WM_operatortype_append(SCULPT_OT_sculptmode_toggle); WM_operatortype_append(SCULPT_OT_set_persistent_base); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index c4ea5c9478c..c58195b8d4a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: sculpt_undo.c 36485 2011-05-04 13:15:42Z nazgul $ * * ***** BEGIN GPL LICENSE BLOCK ***** * diff --git a/source/blender/editors/space_info/info_draw.c b/source/blender/editors/space_info/info_draw.c index 3121ddaa3b4..50a23568bfd 100644 --- a/source/blender/editors/space_info/info_draw.c +++ b/source/blender/editors/space_info/info_draw.c @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: info_draw.c 35242 2011-02-27 20:29:51Z jesterking $ * * ***** BEGIN GPL LICENSE BLOCK ***** * diff --git a/source/blender/editors/space_info/info_report.c b/source/blender/editors/space_info/info_report.c index 937b683e880..5a94e3bd148 100644 --- a/source/blender/editors/space_info/info_report.c +++ b/source/blender/editors/space_info/info_report.c @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: info_report.c 36644 2011-05-12 16:47:36Z campbellbarton $ * * ***** BEGIN GPL LICENSE BLOCK ***** * diff --git a/source/blender/editors/space_info/textview.c b/source/blender/editors/space_info/textview.c index afe8aa39c16..bf4e4cacc5f 100644 --- a/source/blender/editors/space_info/textview.c +++ b/source/blender/editors/space_info/textview.c @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: textview.c 35242 2011-02-27 20:29:51Z jesterking $ * * ***** BEGIN GPL LICENSE BLOCK ***** * diff --git a/source/blender/editors/space_info/textview.h b/source/blender/editors/space_info/textview.h index a30c082575a..a249bbd7be5 100644 --- a/source/blender/editors/space_info/textview.h +++ b/source/blender/editors/space_info/textview.h @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: textview.h 35242 2011-02-27 20:29:51Z jesterking $ * * ***** BEGIN GPL LICENSE BLOCK ***** * diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c index f070bae4e54..ce5fa4a518a 100644 --- a/source/blender/editors/space_view3d/drawmesh.c +++ b/source/blender/editors/space_view3d/drawmesh.c @@ -359,7 +359,7 @@ static void draw_textured_begin(Scene *scene, View3D *v3d, RegionView3D *rv3d, O int istex, solidtex= 0; // XXX scene->obedit warning - if(v3d->drawtype==OB_SOLID || ((ob->mode & OB_MODE_EDIT) && v3d->drawtype!=OB_TEXTURE)) { + if(v3d->drawtype==OB_SOLID || ((ob->mode & OB_MODE_EDIT) && !ELEM(v3d->drawtype, OB_TEXTURE, OB_MATCAP))) { /* draw with default lights in solid draw mode and edit mode */ solidtex= 1; Gtexdraw.islit= -1; @@ -375,7 +375,7 @@ static void draw_textured_begin(Scene *scene, View3D *v3d, RegionView3D *rv3d, O obcol[3]= CLAMPIS(ob->col[3]*255, 0, 255); glCullFace(GL_BACK); glEnable(GL_CULL_FACE); - if(solidtex || v3d->drawtype==OB_TEXTURE) istex= 1; + if(solidtex || ELEM(v3d->drawtype, OB_TEXTURE, OB_MATCAP)) istex= 1; else istex= 0; Gtexdraw.ob = ob; diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index d122f8f0642..bb49a9ea17f 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -104,7 +104,7 @@ /* this condition has been made more complex since editmode can draw textures */ #define CHECK_OB_DRAWTEXTURE(vd, dt) \ -((vd->drawtype==OB_TEXTURE && dt>OB_SOLID) || \ +((ELEM(vd->drawtype, OB_TEXTURE, OB_MATCAP) && dt>OB_SOLID) || \ (vd->drawtype==OB_SOLID && vd->flag2 & V3D_SOLID_TEX)) static void draw_bounding_volume(Scene *scene, Object *ob); @@ -126,7 +126,7 @@ static int check_ob_drawface_dot(Scene *sce, View3D *vd, char dt) return 1; /* if its drawing textures with zbuf sel, then dont draw dots */ - if(dt==OB_TEXTURE && vd->drawtype==OB_TEXTURE) + if(ELEM(dt, OB_TEXTURE, OB_MATCAP) && ELEM(vd->drawtype, OB_TEXTURE, OB_MATCAP)) return 0; if(vd->drawtype>=OB_SOLID && vd->flag2 & V3D_SOLID_TEX) @@ -215,7 +215,7 @@ int draw_glsl_material(Scene *scene, Object *ob, View3D *v3d, int dt) if(ob==OBACT && (ob && ob->mode & OB_MODE_WEIGHT_PAINT)) return 0; - return (scene->gm.matmode == GAME_MAT_GLSL) && (dt >= OB_SHADED); + return ((dt == OB_MATCAP) || (scene->gm.matmode == GAME_MAT_GLSL)) && (dt >= OB_SHADED); } static int check_material_alpha(Base *base, Mesh *me, int glsl) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index fb67e38cbf7..cfb19fce12f 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -313,6 +313,9 @@ static void view3d_free(SpaceLink *sl) } BLI_freelistN(&vd->bgpicbase); + if (vd->matcap_ima) + vd->matcap_ima->id.us--; + if(vd->localvd) MEM_freeN(vd->localvd); if(vd->properties_storage) MEM_freeN(vd->properties_storage); @@ -349,6 +352,11 @@ static SpaceLink *view3d_duplicate(SpaceLink *sl) if(bgpic->ima) bgpic->ima->id.us++; + v3dn->matcap_ima= v3do->matcap_ima; + + if (v3dn->matcap_ima) + v3dn->matcap_ima->id.us++; + v3dn->properties_storage= NULL; return (SpaceLink *)v3dn; diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 573951da4ca..67035247038 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -2104,6 +2104,11 @@ CustomDataMask ED_viewedit_datamask(bScreen *screen) if(sa->spacetype == SPACE_VIEW3D) { mask |= ED_view3d_datamask(scene, (View3D *)sa->spacedata.first); } + if(ELEM(view->drawtype, OB_TEXTURE, OB_MATCAP) || ((view->drawtype == OB_SOLID) && (view->flag2 & V3D_SOLID_TEX))) { + mask |= CD_MASK_MTFACE | CD_MASK_MCOL; + + if(scene->gm.matmode == GAME_MAT_GLSL) + mask |= CD_MASK_ORCO; } /* check if we need mcols due to vertex paint or weightpaint */ diff --git a/source/blender/editors/space_view3d/view3d_fly.c b/source/blender/editors/space_view3d/view3d_fly.c index fdee5c6f6d9..c61aa63f6e7 100644 --- a/source/blender/editors/space_view3d/view3d_fly.c +++ b/source/blender/editors/space_view3d/view3d_fly.c @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: view3d_fly.c 36824 2011-05-23 02:53:30Z campbellbarton $ * * ***** BEGIN GPL LICENSE BLOCK ***** * diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 470b99de00b..92ee80489bf 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -366,10 +366,14 @@ GPUFunction *GPU_lookup_function(const char *name) void GPU_extensions_exit(void) { extern Material defmaterial; // render module abuse... + extern Material matcap_ma; if(defmaterial.gpumaterial.first) GPU_material_free(&defmaterial); + if(matcap_ma.gpumaterial.first) + GPU_material_free(&matcap_ma); + if(FUNCTION_HASH) { BLI_ghash_free(FUNCTION_HASH, NULL, (GHashValFreeFP)MEM_freeN); FUNCTION_HASH = NULL; diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index 0e7df43bd34..8005e231e39 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -1,5 +1,5 @@ -/* * $Id$ +/* * * ***** BEGIN GPL LICENSE BLOCK ***** * @@ -213,7 +213,7 @@ static int is_pow2_limit(int num) /* XXX: texturepaint not global! if (G.f & G_TEXTUREPAINT) - return 1;*/ + return 1;*/ if (U.glreslimit != 0 && num > U.glreslimit) return 0; @@ -953,6 +953,7 @@ static Material *gpu_active_node_material(Material *ma) void GPU_begin_object_materials(View3D *v3d, RegionView3D *rv3d, Scene *scene, Object *ob, int glsl, int *do_alpha_pass) { + extern Material matcap_ma; Material *ma; GPUMaterial *gpumat; GPUBlendMode blendmode; @@ -987,51 +988,70 @@ void GPU_begin_object_materials(View3D *v3d, RegionView3D *rv3d, Scene *scene, O GMS.blendmode= GMS.blendmode_fixed; } - /* no materials assigned? */ - if(ob->totcol==0) { + if (MIN2(v3d->drawtype, ob->dt) != OB_MATCAP) { + /* no materials assigned? */ + if(ob->totcol==0) { gpu_material_to_fixed(&GMS.matbuf[0], &defmaterial, 0, ob); - /* do material 1 too, for displists! */ + /* do material 1 too, for displists! */ memcpy(&GMS.matbuf[1], &GMS.matbuf[0], sizeof(GPUMaterialFixed)); - if(glsl) { - GMS.gmatbuf[0]= &defmaterial; - GPU_material_from_blender(GMS.gscene, &defmaterial); - } + if(glsl) { + GMS.gmatbuf[0]= &defmaterial; + GPU_material_from_blender(GMS.gscene, &defmaterial); + } - GMS.blendmode[0]= GPU_BLEND_SOLID; - } - - /* setup materials */ - for(a=1; a<=ob->totcol; a++) { - /* find a suitable material */ - ma= give_current_material(ob, a); - if(!glsl) ma= gpu_active_node_material(ma); - if(ma==NULL) ma= &defmaterial; - - /* create glsl material if requested */ - gpumat = (glsl)? GPU_material_from_blender(GMS.gscene, ma): NULL; - - if(gpumat) { - /* do glsl only if creating it succeed, else fallback */ - GMS.gmatbuf[a]= ma; - blendmode = GPU_material_blend_mode(gpumat, ob->col); + GMS.blendmode[0]= GPU_BLEND_SOLID; } - else { - /* fixed function opengl materials */ + + /* setup materials */ + for(a=1; a<=ob->totcol; a++) { + /* find a suitable material */ + ma= give_current_material(ob, a); + if(!glsl) ma= gpu_active_node_material(ma); + if(ma==NULL) ma= &defmaterial; + + /* create glsl material if requested */ + gpumat = (glsl)? GPU_material_from_blender(GMS.gscene, ma): NULL; + + if(gpumat) { + /* do glsl only if creating it succeed, else fallback */ + GMS.gmatbuf[a]= ma; + blendmode = GPU_material_blend_mode(gpumat, ob->col); + } + else { + /* fixed function opengl materials */ gpu_material_to_fixed(&GMS.matbuf[a], ma, gamma, ob); - blendmode = (ma->alpha == 1.0f)? GPU_BLEND_SOLID: GPU_BLEND_ALPHA; - if(do_alpha_pass && GMS.alphapass) + blendmode = (ma->alpha == 1.0f)? GPU_BLEND_SOLID: GPU_BLEND_ALPHA; + if(do_alpha_pass && GMS.alphapass) GMS.matbuf[a].diff[3]= ma->alpha; - else + else GMS.matbuf[a].diff[3]= 1.0f; + } + + /* setting do_alpha_pass = 1 indicates this object needs to be + * drawn in a second alpha pass for improved blending */ + if(do_alpha_pass) { + GMS.blendmode[a]= blendmode; + if(ELEM(blendmode, GPU_BLEND_ALPHA, GPU_BLEND_ADD) && !GMS.alphapass) + *do_alpha_pass= 1; + } } + } + else /* MatCap */ { + a = ob->totcol ? 1 : 0; + + matcap_ma.mtex[0]->tex->ima= v3d->matcap_ima; + + gpumat= GPU_material_from_blender(GMS.gscene, &matcap_ma); + + GMS.gmatbuf[a]= &matcap_ma; + blendmode= GPU_material_blend_mode(gpumat, ob->col); - /* setting do_alpha_pass = 1 indicates this object needs to be - * drawn in a second alpha pass for improved blending */ if(do_alpha_pass) { GMS.blendmode[a]= blendmode; + if(ELEM(blendmode, GPU_BLEND_ALPHA, GPU_BLEND_ADD) && !GMS.alphapass) *do_alpha_pass= 1; } diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index e936f35574d..50e65b5ddbb 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -1049,7 +1049,7 @@ static void do_material_tex(GPUShadeInput *shi) GPU_link(mat, "mtex_negate_texnormal", tnor, &tnor); if(mtex->normapspace == MTEX_NSPACE_TANGENT) - { + { if(iFirstTimeNMap!=0) { // use unnormalized normal (this is how we bake it - closer to gamedev) @@ -1065,7 +1065,7 @@ static void do_material_tex(GPUShadeInput *shi) } else newnor = tnor; - + norfac = MIN2(fabsf(mtex->norfac), 1.0f); if(norfac == 1.0f && !GPU_link_changed(stencil)) { @@ -1104,7 +1104,7 @@ static void do_material_tex(GPUShadeInput *shi) iBumpSpacePrev = 0; init_done = 1; } - + // find current bump space if( mtex->texflag & MTEX_BUMP_OBJECTSPACE ) iBumpSpace = 1; @@ -1427,11 +1427,13 @@ void GPU_materials_free(void) Object *ob; Material *ma; extern Material defmaterial; + extern Material matcap_ma; for(ma=G.main->mat.first; ma; ma=ma->id.next) GPU_material_free(ma); GPU_material_free(&defmaterial); + GPU_material_free(&matcap_ma); for(ob=G.main->object.first; ob; ob=ob->id.next) GPU_lamp_free(ob); diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 3d143dc3d0f..9354d5fcdab 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -79,9 +79,12 @@ typedef struct Brush { float alpha; /* opacity */ int sculpt_plane; /* the direction of movement for sculpt vertices */ + float sculpt_plane_range; /* adjust the range for determining the sculpt plane normal and center */ float plane_offset; /* offset for plane brushes (clay, flatten, fill, scrape) */ + float frontface_angle; + char sculpt_tool; /* active sculpt tool */ char vertexpaint_tool; /* active vertex/weight paint tool/blend mode */ char imagepaint_tool; /* active image paint tool */ @@ -91,14 +94,20 @@ typedef struct Brush { float crease_pinch_factor; + float gravity_factor; + float plane_trim; float height; /* affectable height of brush (layer height for layer tool, i.e.) */ + float layer_distance, pad2; + float texture_sample_bias; int texture_overlay_alpha; float unprojected_radius; + float adaptive_space_factor; + float add_col[3]; float sub_col[3]; } Brush; @@ -132,30 +141,33 @@ typedef struct Brush { #define BRUSH_PLANE_TRIM (1<<26) #define BRUSH_FRONTFACE (1<<27) #define BRUSH_CUSTOM_ICON (1<<28) +#define BRUSH_SYMMETRY_FEATHER (1<<29) +#define BRUSH_LAYER (1<<30) /* temporary flag which sets up autmatically for correct brush drawing when inverted modal operator is running */ #define BRUSH_INVERTED (1<<29) /* Brush.sculpt_tool */ -#define SCULPT_TOOL_DRAW 1 -#define SCULPT_TOOL_SMOOTH 2 -#define SCULPT_TOOL_PINCH 3 -#define SCULPT_TOOL_INFLATE 4 -#define SCULPT_TOOL_GRAB 5 -#define SCULPT_TOOL_LAYER 6 -#define SCULPT_TOOL_FLATTEN 7 -#define SCULPT_TOOL_CLAY 8 -#define SCULPT_TOOL_FILL 9 -#define SCULPT_TOOL_SCRAPE 10 -#define SCULPT_TOOL_NUDGE 11 -#define SCULPT_TOOL_THUMB 12 -#define SCULPT_TOOL_SNAKE_HOOK 13 -#define SCULPT_TOOL_ROTATE 14 -//#define SCULPT_TOOL_WAX 15 // XXX: reuse this slot later -#define SCULPT_TOOL_CREASE 16 -#define SCULPT_TOOL_BLOB 17 -#define SCULPT_TOOL_CLAY_TUBES 18 +#define SCULPT_TOOL_DRAW 1 +#define SCULPT_TOOL_SMOOTH 2 +#define SCULPT_TOOL_PINCH 3 +#define SCULPT_TOOL_INFLATE 4 +#define SCULPT_TOOL_GRAB 5 +#define SCULPT_TOOL_LAYER 6 +#define SCULPT_TOOL_FLATTEN 7 +#define SCULPT_TOOL_CLAY 8 +#define SCULPT_TOOL_FILL 9 +#define SCULPT_TOOL_SCRAPE 10 +#define SCULPT_TOOL_NUDGE 11 +#define SCULPT_TOOL_THUMB 12 +#define SCULPT_TOOL_SNAKE_HOOK 13 +#define SCULPT_TOOL_ROTATE 14 +//#define SCULPT_TOOL_WAX 15 // XXX: reuse this slot later +#define SCULPT_TOOL_CREASE 16 +#define SCULPT_TOOL_BLOB 17 +#define SCULPT_TOOL_CLAY_STRIPS 18 +#define SCULPT_TOOL_GRAVITY 19 /* ImagePaintSettings.tool */ #define PAINT_TOOL_DRAW 0 diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 54a885a0860..66dc138d3ba 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -381,6 +381,7 @@ extern Object workob; #define OB_SOLID 3 #define OB_SHADED 4 #define OB_TEXTURE 5 +#define OB_MATCAP 6 /* dtx: flags, char! */ #define OB_AXIS 2 diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 657bfb1c884..ea990b1bc51 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -596,7 +596,7 @@ typedef struct Sculpt { float special_rotation; - int pad; + int sculpting; } Sculpt; typedef struct VPaint { @@ -1157,7 +1157,7 @@ typedef enum SculptFlags { SCULPT_LOCK_X = (1<<3), SCULPT_LOCK_Y = (1<<4), SCULPT_LOCK_Z = (1<<5), - SCULPT_SYMMETRY_FEATHER = (1<<6), +// SCULPT_SYMMETRY_FEATHER = (1<<6),// flag made per-brush, reuse flag later SCULPT_USE_OPENMP = (1<<7), SCULPT_ONLY_DEFORM = (1<<8), } SculptFlags; diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h index e81a9979c12..f6dd79c715f 100644 --- a/source/blender/makesdna/DNA_texture_types.h +++ b/source/blender/makesdna/DNA_texture_types.h @@ -489,6 +489,7 @@ typedef struct TexMapping { #define MTEX_MAP_MODE_FIXED 0 #define MTEX_MAP_MODE_TILED 1 #define MTEX_MAP_MODE_3D 2 +#define MTEX_MAP_MODE_WRAP 3 /* **************** EnvMap ********************* */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 1057eeae40f..2dd57e9628b 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -351,6 +351,7 @@ typedef struct UserDef { short gp_settings; short tb_leftmouse, tb_rightmouse; struct SolidLight light[3]; + short sculpt_paint_settings; /* user preferences for sculpt and paint */ short tw_hotspot, tw_flag, tw_handlesize, tw_size; short textimeout,texcollectrate; short wmdrawmethod; /* removed wmpad */ @@ -388,8 +389,7 @@ typedef struct UserDef { struct ColorBand coba_weight; /* from texture.h */ - float sculpt_paint_overlay_col[3]; - int pad3; + float sculpt_paint_overlay_col[4]; char author[80]; /* author name for file formats supporting it */ } UserDef; @@ -549,6 +549,11 @@ extern UserDef U; /* from blenkernel blender.c */ #define GP_PAINT_DOSMOOTH (1<<0) #define GP_PAINT_DOSIMPLIFY (1<<1) +/* sculpt_paint_settings */ +#define SCULPT_PAINT_USE_UNIFIED_SIZE (1<<0) +#define SCULPT_PAINT_USE_UNIFIED_ALPHA (1<<1) +#define SCULPT_PAINT_UNIFIED_LOCK_BRUSH_SIZE (1<<2) + /* color picker types */ #define USER_CP_CIRCLE 0 #define USER_CP_SQUARE_SV 1 diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 7379493003d..f90fbb24264 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -163,7 +163,7 @@ typedef struct View3D { /** * The drawing mode for the 3d display. Set to OB_WIRE, OB_SOLID, - * OB_SHADED or OB_TEXTURE */ + * OB_SHADED, OB_TEXTURE, or OB_MATCAP */ short drawtype; short ob_centre_cursor; /* optional bool for 3d cursor to define center */ short scenelock, around; @@ -198,6 +198,7 @@ typedef struct View3D { /* XXX depricated? */ struct bGPdata *gpd; /* Grease-Pencil Data (annotation layers) */ + struct Image *matcap_ima; } View3D; @@ -233,7 +234,7 @@ typedef struct View3D { #define RV3D_VIEW_FRONT 1 #define RV3D_VIEW_BACK 2 #define RV3D_VIEW_LEFT 3 -#define RV3D_VIEW_RIGHT 4 +#define RV3D_VIEW_RIGHT 4 #define RV3D_VIEW_TOP 5 #define RV3D_VIEW_BOTTOM 6 #define RV3D_VIEW_PERSPORTHO 7 diff --git a/source/blender/makesrna/SConscript b/source/blender/makesrna/SConscript index b706db5e64c..5ca67f929c5 100644 --- a/source/blender/makesrna/SConscript +++ b/source/blender/makesrna/SConscript @@ -54,6 +54,9 @@ if env['WITH_BF_PYTHON']: if env['WITH_BF_COLLADA']: defs.append('WITH_COLLADA') +if env['WITH_BF_ONSURFACEBRUSH']: + defs.append('WITH_ONSURFACEBRUSH') + if env['OURPLATFORM'] == 'linux2': cflags='-pthread' incs += ' ../../../extern/binreloc/include' diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index a6e26583a6a..d8eba424e94 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -49,11 +49,19 @@ static EnumPropertyItem prop_direction_items[]= { {BRUSH_DIR_IN, "SUBTRACT", 0, "Subtract", "Subtract effect of brush"}, {0, NULL, 0, NULL, NULL}}; +static EnumPropertyItem texture_angle_source_items[] = { + {0, "USER", 0, "User", ""}, + {BRUSH_RAKE, "RAKE", 0, "Rake", ""}, + {BRUSH_RANDOM_ROTATION, "RANDOM", 0, "Random", ""}, + {0, NULL, 0, NULL, NULL}}; + EnumPropertyItem brush_sculpt_tool_items[] = { {SCULPT_TOOL_BLOB, "BLOB", ICON_BRUSH_BLOB, "Blob", ""}, {SCULPT_TOOL_CLAY, "CLAY", ICON_BRUSH_CLAY, "Clay", ""}, + {SCULPT_TOOL_CLAY_STRIPS, "CLAY_STRIPS", ICON_BRUSH_CLAY, "Clay Strips", ""}, {SCULPT_TOOL_CREASE, "CREASE",ICON_BRUSH_CREASE, "Crease", ""}, {SCULPT_TOOL_DRAW, "DRAW", ICON_BRUSH_SCULPT_DRAW, "Draw", ""}, + {SCULPT_TOOL_GRAVITY, "GRAVITY", ICON_BRUSH_SCULPT_DRAW, "Gravity", ""}, {SCULPT_TOOL_FILL, "FILL", ICON_BRUSH_FILL, "Fill", ""}, {SCULPT_TOOL_FLATTEN, "FLATTEN", ICON_BRUSH_FLATTEN, "Flatten", ""}, {SCULPT_TOOL_GRAB, "GRAB", ICON_BRUSH_GRAB, "Grab", ""}, @@ -120,7 +128,13 @@ static void rna_Brush_update(Main *bmain, Scene *scene, PointerRNA *ptr) { Brush *br= (Brush*)ptr->data; WM_main_add_notifier(NC_BRUSH|NA_EDITED, br); - //WM_main_add_notifier(NC_SPACE|ND_SPACE_VIEW3D, NULL); +} + +static void rna_Brush_texture_overlay_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + Brush *br= (Brush*)ptr->data; + WM_main_add_notifier(NC_BRUSH|NA_EDITED, br); + WM_main_add_notifier(NC_SPACE|ND_SPACE_VIEW3D, NULL); } static void rna_Brush_sculpt_tool_update(Main *bmain, Scene *scene, PointerRNA *ptr) @@ -295,6 +309,26 @@ static EnumPropertyItem *rna_Brush_direction_itemf(bContext *C, PointerRNA *ptr, } } +static EnumPropertyItem *rna_Brush_texture_angle_source_itemf(bContext *C, PointerRNA *ptr, int *free) +{ + Brush *me= (Brush*)(ptr->data); + + static EnumPropertyItem texture_angle_source_no_random_items[] = { + {0, "USER", 0, "User", ""}, + {BRUSH_RAKE, "RAKE", 0, "Rake", ""}, + {0, NULL, 0, NULL, NULL}}; + + if (!(me->flag & BRUSH_ANCHORED) && + !ELEM4(me->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE) && + ELEM(me->mtex.brush_map_mode, MTEX_MAP_MODE_FIXED, MTEX_MAP_MODE_WRAP)) + { + return texture_angle_source_items; + } + else { + return texture_angle_source_no_random_items; + } +} + #else static void rna_def_brush_texture_slot(BlenderRNA *brna) @@ -303,6 +337,7 @@ static void rna_def_brush_texture_slot(BlenderRNA *brna) PropertyRNA *prop; static EnumPropertyItem prop_map_mode_items[] = { + {MTEX_MAP_MODE_WRAP, "WRAP", 0, "Wrap", ""}, {MTEX_MAP_MODE_FIXED, "FIXED", 0, "Fixed", ""}, {MTEX_MAP_MODE_TILED, "TILED", 0, "Tiled", ""}, {MTEX_MAP_MODE_3D, "3D", 0, "3D", ""}, @@ -349,17 +384,6 @@ static void rna_def_brush(BlenderRNA *brna) {BRUSH_AIRBRUSH, "AIRBRUSH", 0, "Airbrush", "Keep applying paint effect while holding mouse (spray)"}, {0, NULL, 0, NULL, NULL}}; - static EnumPropertyItem texture_angle_source_items[] = { - {0, "USER", 0, "User", "Rotate the brush texture by given angle"}, - {BRUSH_RAKE, "RAKE", 0, "Rake", "Rotate the brush texture to match the stroke direction"}, - {BRUSH_RANDOM_ROTATION, "RANDOM", 0, "Random", "Rotate the brush texture at random"}, - {0, NULL, 0, NULL, NULL}}; - - static EnumPropertyItem texture_angle_source_no_random_items[] = { - {0, "USER", 0, "User", "Rotate the brush texture by given angle"}, - {BRUSH_RAKE, "RAKE", 0, "Rake", "Rotate the brush texture to match the stroke direction"}, - {0, NULL, 0, NULL, NULL}}; - static EnumPropertyItem brush_sculpt_plane_items[] = { {SCULPT_DISP_DIR_AREA, "AREA", 0, "Area Plane", ""}, {SCULPT_DISP_DIR_VIEW, "VIEW", 0, "View Plane", ""}, @@ -411,12 +435,7 @@ static void rna_def_brush(BlenderRNA *brna) prop= RNA_def_property(srna, "texture_angle_source_random", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); RNA_def_property_enum_items(prop, texture_angle_source_items); - RNA_def_property_ui_text(prop, "Texture Angle Source", ""); - RNA_def_property_update(prop, 0, "rna_Brush_update"); - - prop= RNA_def_property(srna, "texture_angle_source_no_random", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); - RNA_def_property_enum_items(prop, texture_angle_source_no_random_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Brush_texture_angle_source_itemf"); RNA_def_property_ui_text(prop, "Texture Angle Source", ""); RNA_def_property_update(prop, 0, "rna_Brush_update"); @@ -450,7 +469,7 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "spacing"); RNA_def_property_range(prop, 1, 1000); RNA_def_property_ui_range(prop, 1, 500, 5, 0); - RNA_def_property_ui_text(prop, "Spacing", "Spacing between brush daubs as a percentage of brush diameter"); + RNA_def_property_ui_text(prop, "Spacing", "Spacing between brush dabs as a percentage of brush diameter"); RNA_def_property_update(prop, 0, "rna_Brush_update"); prop= RNA_def_property(srna, "smooth_stroke_radius", PROP_INT, PROP_DISTANCE); @@ -536,6 +555,37 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Autosmooth", "Amount of smoothing to automatically apply to each stroke"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop= RNA_def_property(srna, "gravity_factor", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "gravity_factor"); + RNA_def_property_float_default(prop, 0); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 0.001); + RNA_def_property_ui_text(prop, "Gravity", "Amount of gravity to apply to each stroke"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "sculpt_plane_range", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "sculpt_plane_range"); + RNA_def_property_range(prop, 0, 10); + RNA_def_property_float_default(prop, 1); + RNA_def_property_ui_range(prop, 0, 2, 0, 0); + RNA_def_property_ui_text(prop, "Sculpt Plane Range", "Determines the range, in brush radii, to sample vertexes when determining the area sculpt plane"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "frontface_angle", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "frontface_angle"); + RNA_def_property_range(prop, 0, 90); + RNA_def_property_float_default(prop, 80); + RNA_def_property_ui_text(prop, "Front-Face Angle", "Angle where effect of brush starts to be reduced to prevent it affecting back-faces"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "layer_distance", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "layer_distance"); + RNA_def_property_float_default(prop, 0); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 0.001); + RNA_def_property_ui_text(prop, "Layer Distance", "Limit to how far a vertex can travel in a single stroke as a fraction of brush radius"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + /* flag */ prop= RNA_def_property(srna, "use_airbrush", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_AIRBRUSH); @@ -610,6 +660,16 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Use Front-Face", "Brush only affects vertexes that face the viewer"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop= RNA_def_property(srna, "use_symmetry_feather", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_SYMMETRY_FEATHER); + RNA_def_property_ui_text(prop, "Symmetry Feathering", "Reduce the strength of the brush where it overlaps symmetrical dabs"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop= RNA_def_property(srna, "use_layer", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_LAYER); + RNA_def_property_ui_text(prop, "Use Layer", "Sets a limit on how far a vertex can travel during a single stroke"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop= RNA_def_property(srna, "use_anchor", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_ANCHORED); RNA_def_property_ui_text(prop, "Anchored", "Keep the brush anchored to the initial location"); @@ -640,10 +700,9 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Use Automatic Strength Adjustment", "Automatically adjusts strength to give consistent results for different spacings"); RNA_def_property_update(prop, 0, "rna_Brush_update"); - /* adaptive space is not implemented yet */ prop= RNA_def_property(srna, "use_adaptive_space", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_ADAPTIVE_SPACE); - RNA_def_property_ui_text(prop, "Adaptive Spacing", "Space daubs according to surface orientation instead of screen space"); + RNA_def_property_ui_text(prop, "Adaptive Spacing", "Space dabs according to surface orientation instead of screen space"); RNA_def_property_update(prop, 0, "rna_Brush_update"); prop= RNA_def_property(srna, "use_locked_size", PROP_BOOLEAN, PROP_NONE); @@ -655,7 +714,7 @@ static void rna_def_brush(BlenderRNA *brna) prop= RNA_def_property(srna, "use_texture_overlay", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_TEXTURE_OVERLAY); RNA_def_property_ui_text(prop, "Use Texture Overlay", "Show texture in viewport"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); + RNA_def_property_update(prop, 0, "rna_Brush_texture_overlay_update"); prop= RNA_def_property(srna, "use_edge_to_edge", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_EDGE_TO_EDGE); @@ -711,13 +770,13 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "mtex.tex"); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Texture", ""); - RNA_def_property_update(prop, NC_TEXTURE, "rna_Brush_update"); + RNA_def_property_update(prop, NC_TEXTURE, "rna_Brush_texture_overlay_update"); prop= RNA_def_property(srna, "texture_overlay_alpha", PROP_INT, PROP_PERCENTAGE); RNA_def_property_int_sdna(prop, NULL, "texture_overlay_alpha"); RNA_def_property_range(prop, 1, 100); RNA_def_property_ui_text(prop, "Texture Overlay Alpha", ""); - RNA_def_property_update(prop, 0, "rna_Brush_update"); + RNA_def_property_update(prop, 0, "rna_Brush_texture_overlay_update"); prop= RNA_def_property(srna, "cursor_color_add", PROP_FLOAT, PROP_COLOR); RNA_def_property_float_sdna(prop, NULL, "add_col"); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 5e1b22dbc9e..7bf53f74fd8 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -1720,6 +1720,7 @@ static void rna_def_object(BlenderRNA *brna) {OB_SOLID, "SOLID", 0, "Solid", "Draw the object as a solid (If solid drawing is enabled in the viewport)"}, // disabled {OB_SHADED, "SHADED", 0, "Shaded", ""}, {OB_TEXTURE, "TEXTURED", 0, "Textured", "Draw the object with textures (If textures are enabled in the viewport)"}, + {OB_MATCAP, "MATCAP", 0, "MatCap", ""}, {0, NULL, 0, NULL, NULL}}; static EnumPropertyItem boundtype_items[] = { diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 452131d829c..be5b41a7b64 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -194,6 +194,20 @@ static void rna_Sculpt_update(Main *bmain, Scene *scene, PointerRNA *ptr) } } +static int rna_Paint_is_on_surface_brush_capable(Paint* unused) +{ +#ifdef WITH_ONSURFACEBRUSH + int bits; + + (void)unused; + + glGetIntegerv(GL_STENCIL_BITS, &bits); + return bits > 0; +#else + return 0; +#endif +} + #else static void rna_def_paint(BlenderRNA *brna) @@ -201,6 +215,9 @@ static void rna_def_paint(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; + FunctionRNA *func; + PropertyRNA *parm; + srna= RNA_def_struct(brna, "Paint", NULL); RNA_def_struct_ui_text(srna, "Paint", ""); @@ -222,6 +239,12 @@ static void rna_def_paint(BlenderRNA *brna) prop= RNA_def_property(srna, "show_low_resolution", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_FAST_NAVIGATE); RNA_def_property_ui_text(prop, "Fast Navigate", "For multires, show low resolution while navigating the view"); + + /* functions */ + func= RNA_def_function(srna, "is_on_surface_brush_capable", "rna_Paint_is_on_surface_brush_capable"); + RNA_def_function_ui_description(func, "Returns true if on-surface brush is configured and all capabilities are present."); + parm= RNA_def_boolean(func, "ret", 0, "", ""); + RNA_def_function_return(func, parm); } static void rna_def_sculpt(BlenderRNA *brna) @@ -263,10 +286,6 @@ static void rna_def_sculpt(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_LOCK_Z); RNA_def_property_ui_text(prop, "Lock Z", "Disallow changes to the Z axis of vertices"); - prop= RNA_def_property(srna, "use_symmetry_feather", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_SYMMETRY_FEATHER); - RNA_def_property_ui_text(prop, "Symmetry Feathering", "Reduce the strength of the brush where it overlaps symmetrical daubs"); - prop= RNA_def_property(srna, "use_threaded", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_USE_OPENMP); RNA_def_property_ui_text(prop, "Use OpenMP", "Take advantage of multiple CPU cores to improve sculpting performance"); @@ -289,7 +308,7 @@ static void rna_def_vertex_paint(BlenderRNA *brna) prop= RNA_def_property(srna, "use_all_faces", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", VP_AREA); RNA_def_property_ui_text(prop, "All Faces", "Paint on all faces inside brush"); - + prop= RNA_def_property(srna, "use_normal", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", VP_NORMALS); RNA_def_property_ui_text(prop, "Normals", "Applies the vertex normal before painting"); diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index d2c2787784b..4ea6ae9f6a1 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: rna_sequencer_api.c 35238 2011-02-27 20:20:01Z jesterking $ * * ***** BEGIN GPL LICENSE BLOCK ***** * diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 463df7ae233..17e44c14b44 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -100,6 +100,7 @@ EnumPropertyItem viewport_shade_items[] = { {OB_SOLID, "SOLID", ICON_SOLID, "Solid", "Display the object solid, lit with default OpenGL lights"}, //{OB_SHADED, "SHADED", ICON_SMOOTH, "Shaded", "Display the object solid, with preview shading interpolated at vertices"}, {OB_TEXTURE, "TEXTURED", ICON_POTATO, "Textured", "Display the object solid, with face-assigned textures"}, + {OB_MATCAP, "MATCAP", ICON_SMOOTH, "MatCap", "Display the object solid, with MatCap texture"}, {0, NULL, 0, NULL, NULL}}; #ifdef RNA_RUNTIME @@ -834,6 +835,14 @@ static float rna_BackgroundImage_opacity_get(PointerRNA *ptr) return 1.0f-bgpic->blend; } +static void rna_View3D_matcap_image_changed(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + extern Material matcap_ma; + + if(matcap_ma.gpumaterial.first) + GPU_material_free(&matcap_ma); +} + static void rna_BackgroundImage_opacity_set(PointerRNA *ptr, float value) { BGpic *bgpic= (BGpic *)ptr->data; @@ -1392,6 +1401,13 @@ static void rna_def_space_view3d(BlenderRNA *brna) RNA_def_property_pointer_funcs(prop, "rna_SpaceView3D_region_quadview_get", NULL, NULL, NULL); RNA_def_property_ui_text(prop, "Quad View Region", "3D region that defines the quad view settings"); + prop= RNA_def_property(srna, "matcap_image", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "matcap_ima"); + RNA_def_property_struct_type(prop, "Image"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "MatCap Image", ""); + RNA_def_property_update(prop, NC_SPACE|ND_SPACE_VIEW3D, "rna_View3D_matcap_image_changed"); + /* region */ srna= RNA_def_struct(brna, "RegionView3D", NULL); diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index 3a80207ba15..9cf710976f8 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -191,6 +191,7 @@ void rna_TextureSlot_update(Main *bmain, Scene *scene, PointerRNA *ptr) break; case ID_BR: WM_main_add_notifier(NC_BRUSH, id); + WM_main_add_notifier(NC_SPACE|ND_SPACE_VIEW3D, NULL); break; case ID_PA: { diff --git a/source/blender/makesrna/rna_cleanup/rna_cleaner.py b/source/blender/makesrna/rna_cleanup/rna_cleaner.py deleted file mode 100755 index ae17ade36d7..00000000000 --- a/source/blender/makesrna/rna_cleanup/rna_cleaner.py +++ /dev/null @@ -1,321 +0,0 @@ -#! /usr/bin/env python3.1 - -""" -This script is used to help cleaning RNA api. - -Typical line in the input file (elements in [] are optional). - -[comment *] ToolSettings.snap_align_rotation -> use_snap_align_rotation: boolean [Align rotation with the snapping target] - -Geterate output format from blender run this: - ./blender.bin --background --python ./release/scripts/modules/rna_info.py 2> source/blender/makesrna/rna_cleanup/out.txt -""" - - -def font_bold(mystring): - """ - Formats the string as bold, to be used in printouts. - """ - font_bold = "\033[1m" - font_reset = "\033[0;0m" - return font_bold + mystring + font_reset - - -def usage(): - """ - Prints script usage. - """ - import sys - scriptname = sys.argv[0] - sort_choices_string = '|'.join(sort_choices) - message = "\nUSAGE:" - message += "\n%s input-file (.txt|.py) order-priority (%s).\n" % (font_bold(scriptname), sort_choices_string) - message += "%s -h for help\n" % font_bold(scriptname) - print(message) - exit() - - -def help(): - """ - Prints script' help. - """ - message = '\nHELP:' - message += '\nRun this script to re-format the edits you make in the input file.\n' - message += 'Do quick modification to important fields like \'to\' and don\'t care about fields like \'changed\' or \'description\' and save.\n' - message += 'The script outputs 3 files:\n' - message += ' 1) *_clean.txt: is formatted same as the .txt input, can be edited by user.\n' - message += ' 2) *_clean.py: is formatted same as the .py input, can be edited by user.\n' - message += ' 3) rna_api.py is not formatted for readability and go under complete check. Can be used for rna cleanup.\n' - print(message) - usage() - - -def check_commandline(): - """ - Takes parameters from the commandline. - """ - import sys - # Usage - if len(sys.argv)==1 or len(sys.argv)>3: - usage() - if sys.argv[1] == '-h': - help() - elif not (sys.argv[1].endswith(".txt") or sys.argv[1].endswith(".py")): - print ('\nBad input file extension... exiting.') - usage() - else: - inputfile = sys.argv[1] - if len(sys.argv) == 2: - sort_priority = default_sort_choice - print ('\nSecond parameter missing: choosing to order by %s.' % font_bold(sort_priority)) - elif len(sys.argv)==3: - sort_priority = sys.argv[2] - if sort_priority not in sort_choices: - print('\nWrong sort_priority... exiting.') - usage() - return (inputfile, sort_priority) - - -def check_prefix(prop, btype): - # reminder: props=[comment, changed, bclass, bfrom, bto, kwcheck, btype, description] - if btype == "boolean": - if '_' in prop: - prefix = prop.split('_')[0] - if prefix not in kw_prefixes: - return 'BAD-PREFIX: ' + prefix - else: - return prefix + '_' - elif prop in kw: - return 'SPECIAL-KEYWORD: ' + prop - else: - return 'BAD-KEYWORD: ' + prop - else: - return "" - - -def check_if_changed(a,b): - if a != b: return 'changed' - else: return 'same' - - -def get_props_from_txt(input_filename): - """ - If the file is *.txt, the script assumes it is formatted as outlined in this script docstring - """ - - file=open(input_filename,'r') - file_lines=file.readlines() - file.close() - - props_list=[] - props_length_max=[0,0,0,0,0,0,0,0] - - done_text = "+" - done = 0 - tot = 0 - - for iii, line in enumerate(file_lines): - - # debug - #print(line) - line_strip = line.strip() - # empty line or comment - if not line_strip: - continue - - if line_strip == "EOF": - break - - if line.startswith("#"): - line = line[1:] - - # class - [bclass, tail] = [x.strip() for x in line.split('.', 1)] - - # comment - if '*' in bclass: - [comment, bclass] = [x.strip() for x in bclass.split('*', 1)] - else: - comment= '' - - # skipping the header if we have one. - # the header is assumed to be "NOTE * CLASS.FROM -> TO: TYPE DESCRIPTION" - if comment == 'NOTE' and bclass == 'CLASS': - continue - - # from - [bfrom, tail] = [x.strip() for x in tail.split('->', 1)] - - # to - [bto, tail] = [x.strip() for x in tail.split(':', 1)] - - # type, description - try: - [btype, description] = tail.split(None, 1) - # make life easy and strip quotes - description = description.replace("'", "").replace('"', "").replace("\\", "").strip() - except ValueError: - [btype, description] = [tail,'NO DESCRIPTION'] - - # keyword-check - kwcheck = check_prefix(bto, btype) - - # changed - changed = check_if_changed(bfrom, bto) - - # lists formatting - props=[comment, changed, bclass, bfrom, bto, kwcheck, btype, description] - props_list.append(props) - props_length_max=list(map(max,zip(props_length_max,list(map(len,props))))) - - if done_text in comment: - done += 1 - tot += 1 - - print("Total done %.2f" % (done / tot * 100.0) ) - - return (props_list,props_length_max) - - -def get_props_from_py(input_filename): - """ - If the file is *.py, the script assumes it contains a python list (as "rna_api=[...]") - This means that this script executes the text in the py file with an exec(text). - """ - # adds the list "rna_api" to this function's scope - rna_api = __import__(input_filename[:-3]).rna_api - - props_length_max = [0 for i in rna_api[0]] # this way if the vector will take more elements we are safe - for index,props in enumerate(rna_api): - comment, changed, bclass, bfrom, bto, kwcheck, btype, description = props - kwcheck = check_prefix(bto, btype) # keyword-check - changed = check_if_changed(bfrom, bto) # changed? - description = repr(description) - description = description.replace("'", "").replace('"', "").replace("\\", "").strip() - rna_api[index] = [comment, changed, bclass, bfrom, bto, kwcheck, btype, description] - props_length = list(map(len,props)) # lengths - props_length_max = list(map(max,zip(props_length_max,props_length))) # max lengths - return (rna_api,props_length_max) - - -def get_props(input_filename): - if input_filename.endswith(".txt"): - props_list,props_length_max = get_props_from_txt(input_filename) - elif input_filename.endswith(".py"): - props_list,props_length_max = get_props_from_py(input_filename) - return (props_list,props_length_max) - - -def sort(props_list, sort_priority): - """ - reminder - props=[comment, changed, bclass, bfrom, bto, kwcheck, btype, description] - """ - - # order based on the i-th element in lists - if sort_priority == "class.to": - props_list = sorted(props_list, key=lambda p: (p[2], p[4])) - else: - i = sort_choices.index(sort_priority) - if i == 0: - props_list = sorted(props_list, key=lambda p: p[i], reverse=True) - else: - props_list = sorted(props_list, key=lambda p: p[i]) - - print ('\nSorted by %s.' % font_bold(sort_priority)) - return props_list - - -def file_basename(input_filename): - # if needed will use os.path - if input_filename.endswith(".txt"): - if input_filename.endswith("_work.txt"): - base_filename = input_filename.replace("_work.txt", "") - else: - base_filename = input_filename.replace(".txt", "") - elif input_filename.endswith(".py"): - if input_filename.endswith("_work.py"): - base_filename = input_filename.replace("_work.py", "") - else: - base_filename = input_filename.replace(".py", "") - - return base_filename - - -def write_files(basename, props_list, props_length_max): - """ - Writes in 3 files: - * output_filename_work.txt: formatted as txt input file (can be edited) - * output_filename_work.py: formatted for readability (can be edited) - * rna_api.py: unformatted, just as final output - """ - - f_rna = open("rna_api.py",'w') - f_txt = open(basename + '_work.txt','w') - f_py = open(basename + '_work.py','w') - - # reminder: props=[comment, changed, bclass, bfrom, bto, kwcheck, btype, description] - # [comment *] ToolSettings.snap_align_rotation -> use_snap_align_rotation: boolean [Align rotation with the snapping target] - rna = py = txt = '' - props_list = [['NOTE', 'CHANGED', 'CLASS', 'FROM', 'TO', 'KEYWORD-CHECK', 'TYPE', 'DESCRIPTION']] + props_list - for props in props_list: - #txt - - # quick way we can tell if it changed - if props[3] == props[4]: txt += "#" - else: txt += " " - - if props[0] != '': txt += '%s * ' % props[0] # comment - txt += '%s.%s -> %s: %s "%s"\n' % tuple(props[2:5] + props[6:]) # skipping keyword-check - # rna_api - if props[0] == 'NOTE': indent = '# ' - else: indent = ' ' - rna += indent + '("%s", "%s", "%s", "%s", "%s"),\n' % tuple(props[2:5] + props[6:]) # description is already string formatted - # py - blanks = [' '* (x[0]-x[1]) for x in zip(props_length_max,list(map(len,props)))] - props = [('"%s"%s' if props[-1] != x[0] else "%s%s") % (x[0],x[1]) for x in zip(props,blanks)] - py += indent + '(%s, %s, %s, %s, %s, %s, %s, "%s"),\n' % tuple(props) - - f_txt.write(txt) - f_py.write("rna_api = [\n%s]\n" % py) - f_rna.write("rna_api = [\n%s]\n" % rna) - - # write useful py script, wont hurt - f_py.write("\n'''\n") - f_py.write("for p_note, p_changed, p_class, p_from, p_to, p_check, p_type, p_desc in rna_api:\n") - f_py.write(" print(p_to)\n") - f_py.write("\n'''\n") - - f_txt.close() - f_py.close() - f_rna.close() - - print ('\nSaved %s, %s and %s.\n' % (font_bold(f_txt.name), font_bold(f_py.name), font_bold(f_rna.name) ) ) - - -def main(): - - global sort_choices, default_sort_choice - global kw_prefixes, kw - - sort_choices = ['note','changed','class','from','to','kw', 'class.to'] - default_sort_choice = sort_choices[-1] - kw_prefixes = [ 'active','apply','bl','exclude','has','invert','is','lock', \ - 'pressed','show','show_only','use','use_only','layers','states', 'select'] - kw = ['active','hide','invert','select','layers','mute','states','use','lock'] - - input_filename, sort_priority = check_commandline() - props_list,props_length_max = get_props(input_filename) - props_list = sort(props_list,sort_priority) - - output_basename = file_basename(input_filename) - write_files(output_basename, props_list,props_length_max) - - -if __name__=='__main__': - import sys - if not sys.version.startswith("3"): - print("Incorrect python version, use python 3!") - else: - main() - diff --git a/source/blender/makesrna/rna_cleanup/rna_cleaner_merge.py b/source/blender/makesrna/rna_cleanup/rna_cleaner_merge.py deleted file mode 100755 index 8d2fe07b774..00000000000 --- a/source/blender/makesrna/rna_cleanup/rna_cleaner_merge.py +++ /dev/null @@ -1,61 +0,0 @@ -#! /usr/bin/env python3.1 - -import sys - -''' -Example usage: - python3 rna_cleaner_merge.py out_work.py rna_booleans_work.py -''' -def main(): - - def work_line_id(line): - return line[2].split("|")[-1], line[3] # class/from - - - if not (sys.argv[-1].endswith(".py") and sys.argv[-2].endswith(".py")): - print("Only accepts 2 py files as arguments.") - - sys.path.insert(0, ".") - - mod_from = __import__(sys.argv[-1][:-3]) - mod_to = __import__(sys.argv[-2][:-3]) - - mod_to_dict = dict([(work_line_id(line), line) for line in mod_to.rna_api]) - mod_from_dict = dict([(work_line_id(line), line) for line in mod_from.rna_api]) - - rna_api_new = [] - - for key, val_orig in mod_to_dict.items(): - try: - val_new = mod_from_dict.pop(key) - except: - # print("not found", key) - val_new = val_orig - - # always take the class from the base - val = list(val_orig) - val[0] = val_new[0] # comment - val[4] = val_new[4] # -> to - val = tuple(val) - rna_api_new.append(val) - - def write_work_file(file_path, rna_api): - rna_api = list(rna_api) - rna_api.sort(key=work_line_id) - file_out = open(file_path, "w") - file_out.write("rna_api = [\n") - for line in rna_api: - file_out.write(" %s,\n" % (repr(line))) - file_out.write("]\n") - file_out.close() - - file_path = sys.argv[-2][:-3] + "_merged.py" - write_work_file(file_path, rna_api_new) - - if mod_from_dict: - file_path = sys.argv[-2][:-3] + "_lost.py" - write_work_file(file_path, list(mod_from_dict.values())) - print("Warning '%s' contains lost %d items from module %s.py" % (file_path, len(mod_from_dict), mod_from.__name__)) - -if __name__ == "__main__": - main() diff --git a/source/blender/makesrna/rna_cleanup/rna_properties.txt b/source/blender/makesrna/rna_cleanup/rna_properties.txt deleted file mode 100644 index 030fecec20e..00000000000 --- a/source/blender/makesrna/rna_cleanup/rna_properties.txt +++ /dev/null @@ -1 +0,0 @@ -# See svn history for example formatting for this file, currently this isnt in use. diff --git a/source/blender/makesrna/rna_cleanup/rna_update.sh b/source/blender/makesrna/rna_cleanup/rna_update.sh deleted file mode 100755 index a4d686cc482..00000000000 --- a/source/blender/makesrna/rna_cleanup/rna_update.sh +++ /dev/null @@ -1,13 +0,0 @@ -cd ../../../../ -./blender.bin --background --python ./release/scripts/modules/rna_info.py 2> source/blender/makesrna/rna_cleanup/out.txt -cd ./source/blender/makesrna/rna_cleanup/ -./rna_cleaner.py out.txt -./rna_cleaner.py rna_properties.txt -./rna_cleaner_merge.py out_work.py rna_properties_work.py -./rna_cleaner.py out_work_merged.py -./rna_cleaner.py out_work_lost.py -mv out_work_merged_work.txt rna_properties.txt # overwrite -mv out_work_lost_work.txt rna_properties_lost.txt -cat rna_properties.txt | grep -v "^#" > rna_properties_edits.txt -./rna_cleaner.py rna_properties.txt -echo "Updated: rna_properties.txt rna_properties_edits.txt rna_properties_lost.txt " diff --git a/source/blender/python/generic/mathutils_geometry.c b/source/blender/python/generic/mathutils_geometry.c index c4917199e59..e3ef790cac9 100644 --- a/source/blender/python/generic/mathutils_geometry.c +++ b/source/blender/python/generic/mathutils_geometry.c @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: mathutils_geometry.c 36871 2011-05-24 16:05:51Z campbellbarton $ * * ***** BEGIN GPL LICENSE BLOCK ***** * diff --git a/source/blender/python/generic/mathutils_geometry.h b/source/blender/python/generic/mathutils_geometry.h index 929b8cc8d75..2f9e3f20ef5 100644 --- a/source/blender/python/generic/mathutils_geometry.h +++ b/source/blender/python/generic/mathutils_geometry.h @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: mathutils_geometry.h 35236 2011-02-27 20:10:08Z jesterking $ * * ***** BEGIN GPL LICENSE BLOCK ***** * diff --git a/source/blender/windowmanager/SConscript b/source/blender/windowmanager/SConscript index f52ac8ba3cb..6b8adaf6e72 100644 --- a/source/blender/windowmanager/SConscript +++ b/source/blender/windowmanager/SConscript @@ -38,4 +38,7 @@ if env['WITH_GHOST_COCOA']: if env['BF_BUILDINFO']: defs.append('NAN_BUILDINFO') +if env['WITH_BF_ONSURFACEBRUSH']: + defs.append('WITH_ONSURFACEBRUSH') + env.BlenderLib ( 'bf_windowmanager', sources, Split(incs), defines=defs, libtype=['core'], priority=[5] ) diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 4fe82917705..59feb2c5327 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -340,6 +340,20 @@ typedef struct wmDrawTriple { int x[MAX_N_TEX], y[MAX_N_TEX]; int nx, ny; GLenum target; + +#ifdef WITH_ONSURFACEBRUSH + char* depth; + GLenum depth_type; + + GLuint depth_bind[MAX_N_TEX*MAX_N_TEX]; + int depth_x[MAX_N_TEX], depth_y[MAX_N_TEX]; + int depth_nx, depth_ny; + GLenum depth_target; + + GLenum depth_vertex_shader; + GLenum depth_fragment_shader; + GLenum depth_program; +#endif } wmDrawTriple; static int is_pow2(int n) @@ -408,6 +422,18 @@ static void wm_draw_triple_free(wmWindow *win) wmDrawTriple *triple= win->drawdata; glDeleteTextures(triple->nx*triple->ny, triple->bind); +#ifdef WITH_ONSURFACEBRUSH + glDeleteTextures(triple->depth_nx*triple->depth_ny, triple->depth_bind); + + if (GLEW_ARB_shader_objects) { + glDeleteObjectARB(triple->depth_vertex_shader); + glDeleteObjectARB(triple->depth_fragment_shader); + glDeleteObjectARB(triple->depth_program); + } + + if (triple->depth) + MEM_freeN(triple->depth); +#endif MEM_freeN(triple); @@ -558,6 +584,419 @@ static void wm_triple_copy_textures(wmWindow *win, wmDrawTriple *triple) glBindTexture(triple->target, 0); } +#ifdef WITH_ONSURFACEBRUSH +static void get_shading_language_version(int* major, int* minor) +{ + const char* version; + + version = glGetString(GL_SHADING_LANGUAGE_VERSION); + *major = atol(version); + version = strstr(version, "."); + *minor = atol(version+1); +} + +/* compile and link the shader program needed to write a depth texture to the depth buffer */ +static void load_depth_shader_program(wmDrawTriple* triple) +{ + int success; + GLsizei len; + GLbyte infoLog[1000]; + int major, minor; + + /* This vertex program just passes the texture coordinate through and transforms the vertex position */ + /* This program should be compatible up to OpenGL 4.0 with the fixed-function compatibility profile */ + static const GLcharARB* depth_vertex_shader_source_100[] = { + "void main()\n", + "{\n", + " gl_TexCoord[0] = gl_MultiTexCoord0;\n", + " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n", + "}\n", + }; + + /* This fragment program is used non-rectangle textures */ + /* Compatible with version 1.0-1.2 w/o GL_ARB_texture_rectangle */ + static const GLcharARB* depth_fragment_shader_source_100[] = { + "uniform sampler2D depth_texture;\n", + "void main()\n", + "{\n", + " gl_FragDepth = texture2D(depth_texture, gl_TexCoord[0].xy).x;\n", + "}\n", + }; + + /* This fragment program is used for rectangular textures */ + /* Compatible with version 1.0-1.2 w/ GL_ARB_texture_rectangle */ + static const GLcharARB* depth_fragment_shader_rect_source_100[] = { + "#extension GL_ARB_texture_rectangle : enable\n", + "uniform sampler2DRect depth_texture;\n", + "void main()\n", + "{\n", + " gl_FragDepth = texture2DRect(depth_texture, gl_TexCoord[0].xy).x;\n", + "}\n", + }; + + /* This fragment program is used non-rectangle textures */ + /* Compatible with version 1.3 w/o GL_ARB_texture_rectangle */ + static const GLcharARB* depth_fragment_shader_source_130[] = { + "#version 130\n", + "uniform sampler2D depth_texture;\n", + "void main()\n", + "{\n", + " gl_FragDepth = texture(depth_texture, gl_TexCoord[0].xy).x;\n", + "}\n", + }; + + /* This fragment program is used for rectangular textures */ + /* Compatible with version 1.3 w/ GL_ARB_texture_rectangle */ + static const GLcharARB* depth_fragment_shader_rect_source_130[] = { + "#version 130\n", + "#extension GL_ARB_texture_rectangle : enable\n", + "uniform sampler2DRect depth_texture;\n", + "void main()\n", + "{\n", + " gl_FragDepth = texture(depth_texture, gl_TexCoord[0].xy).x;\n", + "}\n", + }; + + const GLcharARB** source; + int source_lines; + + get_shading_language_version(&major, &minor); + + /* compile the vertex program */ + + triple->depth_vertex_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); + + glShaderSourceARB(triple->depth_vertex_shader, sizeof(depth_vertex_shader_source_100)/sizeof(GLcharARB*), depth_vertex_shader_source_100, NULL); + glCompileShaderARB(triple->depth_vertex_shader); + + /* print any errors/warnings gotten while compiling the vertex program */ + + glGetObjectParameterivARB(triple->depth_vertex_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success); + glGetInfoLogARB(triple->depth_vertex_shader, 1000, &len, infoLog); + + if (len > 0) + printf("triple depth buffer vertex program compilation messages:\n%s\n", infoLog); + + /* compile the appropriate fragment program depending on support for rectangular textures */ + + triple->depth_fragment_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); + + if (major == 1 && minor < 30) { + if (GLEW_ARB_texture_rectangle) { + source = depth_fragment_shader_rect_source_100; + source_lines = sizeof(depth_fragment_shader_rect_source_100)/sizeof(GLcharARB*); + } + else { + source = depth_fragment_shader_source_100; + source_lines = sizeof(depth_fragment_shader_source_100)/sizeof(GLcharARB*); + } + } + else { + if (GLEW_ARB_texture_rectangle) { + source = depth_fragment_shader_rect_source_130; + source_lines = sizeof(depth_fragment_shader_rect_source_130)/sizeof(GLcharARB*); + } + else { + source = depth_fragment_shader_source_130; + source_lines = sizeof(depth_fragment_shader_source_130)/sizeof(GLcharARB*); + } + } + + glShaderSourceARB(triple->depth_fragment_shader, source_lines, source, NULL); + glCompileShaderARB(triple->depth_fragment_shader); + + /* print any errors/warnings gotten while compiling the fragment program */ + + glGetObjectParameterivARB(triple->depth_fragment_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success); + glGetInfoLogARB(triple->depth_fragment_shader, 1000, &len, infoLog); + + if (len > 0) + printf("triple depth buffer fragment program compilation messages:\n%s\n", infoLog); + + /* link the shaders into a complete program */ + + triple->depth_program = glCreateProgramObjectARB(); + + glAttachObjectARB(triple->depth_program, triple->depth_vertex_shader); + glAttachObjectARB(triple->depth_program, triple->depth_fragment_shader); + + glLinkProgramARB(triple->depth_program); + + /* print any errors/warnings gotten while linking the final program */ + + glGetObjectParameterivARB(triple->depth_program, GL_OBJECT_LINK_STATUS_ARB, &success); + glGetInfoLogARB(triple->depth_program, 1000, &len, infoLog); + + if (len > 0) + printf("triple depth buffer program linker messages:\n%s\n", infoLog); +} + +static int wm_triple_gen_depth_buffer(wmWindow *win, wmDrawTriple *triple) +{ + /* To do this fast we need support for depth textures and GLSL */ + if (GLEW_ARB_depth_texture && + GLEW_ARB_shader_objects && + GLEW_ARB_vertex_shader && + GLEW_ARB_fragment_shader && + GLEW_ARB_shading_language_100) + { + GLint maxsize; + int x, y; + + /* XXX: this is copied from wm_triple_gen_textures. + can probably combine them together once this is accepted into trunk */ + + /* compute texture sizes */ + if(GLEW_ARB_texture_rectangle) { + triple->depth_target= GL_TEXTURE_RECTANGLE_ARB; + triple->depth_nx= 1; + triple->depth_ny= 1; + triple->depth_x[0]= win->sizex; + triple->depth_y[0]= win->sizey; + } + else if(GPU_non_power_of_two_support()) { + triple->depth_target= GL_TEXTURE_2D; + triple->depth_nx= 1; + triple->depth_ny= 1; + triple->depth_x[0]= win->sizex; + triple->depth_y[0]= win->sizey; + } + else { + triple->depth_target= GL_TEXTURE_2D; + triple->depth_nx= 0; + triple->depth_ny= 0; + split_width(win->sizex, MAX_N_TEX, triple->depth_x, &triple->depth_nx); + split_width(win->sizey, MAX_N_TEX, triple->depth_y, &triple->depth_ny); + } + + /* generate texture names */ + glGenTextures(triple->depth_nx*triple->depth_ny, triple->depth_bind); + + if(!triple->depth_bind[0]) { + /* not the typical failure case but we handle it anyway */ + printf("WM: failed to allocate depth texture for triple buffer drawing (glGenTextures).\n"); + return 0; + } + + for(y=0; y<triple->depth_ny; y++) { + for(x=0; x<triple->depth_nx; x++) { + /* proxy texture is only guaranteed to test for the cases that + * there is only one texture in use, which may not be the case */ + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxsize); + + if(triple->depth_x[x] > maxsize || triple->depth_y[y] > maxsize) { + glBindTexture(triple->depth_target, 0); + printf("WM: failed to allocate texture for triple buffer drawing (texture too large for graphics card).\n"); + return 0; + } + + /* setup actual texture */ + glBindTexture(triple->depth_target, triple->depth_bind[x + y*triple->depth_nx]); + + /* important difference from wm_triple_gen_textures! + use GL_DEPTH_COMPONENT as format and internalformat */ + glTexImage2D(triple->depth_target, 0, GL_DEPTH_COMPONENT, triple->depth_x[x], triple->depth_y[y], 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(triple->depth_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(triple->depth_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + /* important difference from wm_triple_gen_textures! + turn off depth comparison mode that would be used if this was a shadow map */ + glTexParameteri(triple->depth_target, GL_TEXTURE_COMPARE_MODE, GL_NONE); + + glBindTexture(triple->depth_target, 0); + + /* not sure if this works everywhere .. */ + if(glGetError() == GL_OUT_OF_MEMORY) { + printf("WM: failed to allocate depth texture for triple buffer drawing (out of memory).\n"); + return 0; + } + } + } + + load_depth_shader_program(triple); + } + /* otherwise, we have to fall back to the more compatible glReadBuffer/glDrawBuffer method */ + else { + const int count = win->sizex * win->sizey; + const int size = count*sizeof(GLfloat); + + triple->depth_type = GL_FLOAT; + triple->depth = MEM_mallocN(size, "wm_triple_gen_depth_buffer"); + } + + return 1; +} + +static void wm_triple_copy_depth_buffer(wmWindow *win, wmDrawTriple *triple) +{ + /* To do this fast we need support for depth textures and GLSL */ + if (GLEW_ARB_depth_texture && + GLEW_ARB_shader_objects && + GLEW_ARB_vertex_shader && + GLEW_ARB_fragment_shader && + GLEW_ARB_shading_language_100) + { + int x, y, sizex, sizey, offx, offy; + + /* XXX this is pretty much identical to wm_triple_copy_textures, + the fact that the textures are GL_DEPTH_COMPONENT format is what makes the difference */ + + for(y=0, offy=0; y<triple->depth_ny; offy+=triple->depth_y[y], y++) { + for(x=0, offx=0; x<triple->depth_nx; offx+=triple->depth_x[x], x++) { + sizex= (x == triple->depth_nx-1)? win->sizex-offx: triple->depth_x[x]; + sizey= (y == triple->depth_ny-1)? win->sizey-offy: triple->depth_y[y]; + + glBindTexture(triple->depth_target, triple->depth_bind[x + y*triple->depth_nx]); + glCopyTexSubImage2D(triple->depth_target, 0, 0, 0, offx, offy, sizex, sizey); + } + } + + glBindTexture(triple->depth_target, 0); + } + else if (triple->depth) { + /* For the compatibility fallback, we set the pixel store state to the defaults, + anything else is very unlikely to be even remotely fast (and it is already slow + using these defaults) */ + + glPushAttrib(GL_PIXEL_MODE_BIT); + + glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glPixelStorei(GL_PACK_SKIP_ROWS, 0); + glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + + glPixelTransferi(GL_DEPTH_SCALE, 1); + glPixelTransferi(GL_DEPTH_BIAS, 0); + + glReadPixels(0, 0, win->sizex, win->sizey, GL_DEPTH_COMPONENT, triple->depth_type, triple->depth); + + glPopAttrib(); + } +} + +static void wm_triple_draw_depth_buffer(wmWindow *win, wmDrawTriple *triple) +{ + /* To do this fast we need support for depth textures and GLSL */ + if (GLEW_ARB_depth_texture && + GLEW_ARB_shader_objects && + GLEW_ARB_vertex_shader && + GLEW_ARB_fragment_shader && + GLEW_ARB_shading_language_100) + { + float halfx, halfy, ratiox, ratioy; + int x, y, sizex, sizey, offx, offy; + GLint depth_texture; + + glPushAttrib( + GL_COLOR_BUFFER_BIT| + GL_DEPTH_BUFFER_BIT); + + /* depth test has to be enabled to write to depth buffer, + set GL_ALWAYS so that what is in the texture overwrites what + is there, and make sure the buffer is set to be writable */ + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_TRUE); + + /* since the fragment shader does not write gl_FragColor what it would + write to the color buffer is actually undefined. Regardless, do not + write to color buffer. */ + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + + /* load the shader and bind the sampler2D to the 0th texture unit */ + glUseProgramObjectARB(triple->depth_program); + depth_texture = glGetUniformLocationARB(triple->depth_program, "depth_texture"); + glActiveTextureARB(GL_TEXTURE0_ARB); + glUniform1iARB(depth_texture, 0); + + glEnable(triple->depth_target); + + for(y=0, offy=0; y<triple->depth_ny; offy+=triple->depth_y[y], y++) { + for(x=0, offx=0; x<triple->depth_nx; offx+=triple->depth_x[x], x++) { + sizex= (x == triple->depth_nx-1)? win->sizex-offx: triple->depth_x[x]; + sizey= (y == triple->depth_ny-1)? win->sizey-offy: triple->depth_y[y]; + + /* wmOrtho for the screen has this same offset */ + ratiox= sizex; + ratioy= sizey; + halfx= 0.375f; + halfy= 0.375f; + + /* texture rectangle has unnormalized coordinates */ + if(triple->depth_target == GL_TEXTURE_2D) { + ratiox /= triple->depth_x[x]; + ratioy /= triple->depth_y[y]; + halfx /= triple->depth_x[x]; + halfy /= triple->depth_y[y]; + } + + glBindTexture(triple->depth_target, triple->depth_bind[x + y*triple->depth_nx]); + + glBegin(GL_QUADS); + glTexCoord2f(halfx, halfy); + glVertex2f(offx, offy); + + glTexCoord2f(ratiox+halfx, halfy); + glVertex2f(offx+sizex, offy); + + glTexCoord2f(ratiox+halfx, ratioy+halfy); + glVertex2f(offx+sizex, offy+sizey); + + glTexCoord2f(halfx, ratioy+halfy); + glVertex2f(offx, offy+sizey); + glEnd(); + } + } + + glBindTexture(triple->depth_target, 0); + glDisable(triple->depth_target); + + /* go back to using the fixed function pipeline */ + glUseProgramObjectARB(0); + + glPopAttrib(); + } + else { + if (triple->depth) { + glPushAttrib( + GL_COLOR_BUFFER_BIT| + GL_DEPTH_BUFFER_BIT| + GL_ENABLE_BIT| + GL_PIXEL_MODE_BIT); + + /* About the only chance this will be remotely fast is if we use the default values */ + + glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + glPixelTransferi(GL_DEPTH_SCALE, 1); + glPixelTransferi(GL_DEPTH_BIAS, 0); + + glPixelZoom(1.0, 1.0); + + /* important, cannot write the depth buffer unless this is enabled */ + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glDepthFunc(GL_ALWAYS); + + /* make sure color buffer isn't overwritten */ + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + + /* send the saved depth buffer to the screen */ + glRasterPos2i(0, 0); + glDrawPixels(win->sizex, win->sizey, GL_DEPTH_COMPONENT, triple->depth_type, triple->depth); + + glPopAttrib(); + } + } +} +#endif + static void wm_method_draw_triple(bContext *C, wmWindow *win) { wmWindowManager *wm= CTX_wm_manager(C); @@ -574,11 +1013,20 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) wmSubWindowSet(win, screen->mainwin); wm_triple_draw_textures(win, win->drawdata); +#ifdef WITH_ONSURFACEBRUSH + wm_triple_draw_depth_buffer(win, win->drawdata); +#endif + + triple= win->drawdata; } else { win->drawdata= MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple"); - if(!wm_triple_gen_textures(win, win->drawdata)) + if(!wm_triple_gen_textures(win, win->drawdata) +#ifdef WITH_ONSURFACEBRUSH + || !wm_triple_gen_depth_buffer(win, win->drawdata) +#endif + ) { wm_draw_triple_fail(C, win); return; @@ -610,6 +1058,9 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) ED_area_overdraw(C); wm_triple_copy_textures(win, triple); +#ifdef WITH_ONSURFACEBRUSH + wm_triple_copy_depth_buffer(win, triple); +#endif } /* after area regions so we can do area 'overlay' drawing */ diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index f5a1b6b0298..e03d32ee7c8 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -2700,6 +2700,346 @@ void WM_OT_straightline_gesture(wmOperatorType *ot) /* *********************** radial control ****************** */ static const int WM_RADIAL_CONTROL_DISPLAY_SIZE = 200; +#if 0 +typedef struct wmRadialControl { + int mode; + float initial_value, value, max_value; + float col[4], tex_col[4]; + int initial_mouse[2]; + void *cursor; + GLuint tex; +} wmRadialControl; + +extern Paint *paint_get_active(Scene *sce); +extern struct Brush *paint_brush(struct Paint *paint); +#ifdef WITH_ONSURFACEBRUSH +extern int sculpt_get_brush_geometry(bContext* C, int x, int y, int* pixel_radius, float location[3], float modelview[16], float projection[16], int viewport[4]); +extern float unproject_brush_radius(Object *ob, ViewContext *vc, float center[3], float offset); +#endif + +static void wm_radial_control_paint(bContext *C, int x, int y, void *customdata) +{ + wmRadialControl *rc = (wmRadialControl*)customdata; + ARegion *ar = CTX_wm_region(C); + float r1=0.0f, r2=0.0f, r3=0.0f, angle=0.0f; + + // int hit = 0; + int hit = 0; + + if(rc->mode == WM_RADIALCONTROL_STRENGTH) + rc->tex_col[3]= (rc->value + 0.5f); + + if(rc->mode == WM_RADIALCONTROL_SIZE) { + r1= rc->value; + r2= rc->initial_value; + r3= r1; + } else if(rc->mode == WM_RADIALCONTROL_STRENGTH) { + r1= (1 - rc->value) * WM_RADIAL_CONTROL_DISPLAY_SIZE; + r2= r3= (float)WM_RADIAL_CONTROL_DISPLAY_SIZE; + } else if(rc->mode == WM_RADIALCONTROL_ANGLE) { + r1= r2= r3= (float)WM_RADIAL_CONTROL_DISPLAY_SIZE; + angle = rc->value; + } + + /* Keep cursor in the original place */ + x = rc->initial_mouse[0] - ar->winrct.xmin; + y = rc->initial_mouse[1] - ar->winrct.ymin; + +#ifdef WITH_ONSURFACEBRUSH + if ((paint->flags & PAINT_SHOW_BRUSH_ON_SURFACE) && vc.obact->sculpt) { + float alpha; + + int pixel_radius, viewport[4]; + float location[3], modelview[16], projection[16]; + + float visual_strength = rc->mode == WM_RADIALCONTROL_STRENGTH ? rc->value*rc->value : brush_alpha(brush)*brush_alpha(brush); + + const float min_alpha = 0.20f; + const float max_alpha = 0.80f; + + hit = sculpt_get_brush_geometry(C, x, y, &pixel_radius, location, modelview, projection, viewport); + + alpha = min_alpha + (visual_strength*(max_alpha-min_alpha)); + + if (hit) { + Object *ob= CTX_data_active_object(C); + + { + const float unprojected_radius= unproject_brush_radius(CTX_data_active_object(C), &vc, location, r1); + const float max_thickness= 0.12; + const float min_thickness= 0.06; + const float thickness= 1.0 - min_thickness - visual_strength*max_thickness; + const float inner_radius= unprojected_radius*thickness; + const float outer_radius= unprojected_radius; + + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + + ED_draw_fixed_overlay_on_surface(modelview, projection, ob->size, viewport, location, outer_radius, sd, brush, &vc, y+r3, y-r3, x-r3, x+r3, -angle); + + ED_draw_on_surface_cursor(modelview, projection, col, alpha, ob->size, viewport, location, inner_radius, outer_radius, brush_size(brush)); + + if(rc->mode == WM_RADIALCONTROL_ANGLE) { + glTranslatef((float)x, (float)y, 0.0f); + glEnable(GL_BLEND); + glColor4f(col[0], col[1], col[2], 0.5f); + glEnable(GL_LINE_SMOOTH); + fdrawline(0, 0, WM_RADIAL_CONTROL_DISPLAY_SIZE, 0); + glRotatef(angle, 0, 0, 1); + fdrawline(0, 0, WM_RADIAL_CONTROL_DISPLAY_SIZE, 0); + glDisable(GL_LINE_SMOOTH); + } + } + + { + const float unprojected_radius= unproject_brush_radius(CTX_data_active_object(C), &vc, location, r2); + const float max_thickness= 0.12; + const float min_thickness= 0.06; + const float thickness= 1.0 - min_thickness - visual_strength*max_thickness; + const float inner_radius= unprojected_radius*thickness; + const float outer_radius= unprojected_radius; + + ED_draw_on_surface_cursor(modelview, projection, col, alpha, ob->size, viewport, location, inner_radius, outer_radius, brush_size(brush)); + } + } + } + + if (!hit) { +#endif + glTranslatef((float)x, (float)y, 0.0f); + + glEnable(GL_BLEND); + + if(rc->mode == WM_RADIALCONTROL_ANGLE) { + glRotatef(angle, 0, 0, 1); + } + + if (rc->tex) { + glBindTexture(GL_TEXTURE_2D, rc->tex); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glEnable(GL_TEXTURE_2D); + glBegin(GL_QUADS); + glColor4fv(rc->tex_col); + glTexCoord2f(0,0); + glVertex2f(-r3, -r3); + glTexCoord2f(1,0); + glVertex2f(r3, -r3); + glTexCoord2f(1,1); + glVertex2f(r3, r3); + glTexCoord2f(0,1); + glVertex2f(-r3, r3); + glEnd(); + glDisable(GL_TEXTURE_2D); + } + + if(rc->mode == WM_RADIALCONTROL_ANGLE) { + glColor4fv(rc->col); + glEnable(GL_LINE_SMOOTH); + glRotatef(-angle, 0, 0, 1); + fdrawline(0.0f, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f); + glRotatef(angle, 0, 0, 1); + fdrawline(0.0f, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f); + glDisable(GL_LINE_SMOOTH); + } + + glColor4fv(rc->col); + glutil_draw_lined_arc(0.0, (float)(M_PI*2.0), r1, 40); + glutil_draw_lined_arc(0.0, (float)(M_PI*2.0), r2, 40); + glDisable(GL_BLEND); +#ifdef WITH_ONSURFACEBRUSH + } +#endif +} + +int WM_radial_control_modal(bContext *C, wmOperator *op, wmEvent *event) +{ + wmRadialControl *rc = (wmRadialControl*)op->customdata; + int mode, initial_mouse[2], delta[2]; + float dist; + double new_value = RNA_float_get(op->ptr, "new_value"); + int ret = OPERATOR_RUNNING_MODAL; + // float initial_value = RNA_float_get(op->ptr, "initial_value"); + + mode = RNA_enum_get(op->ptr, "mode"); + RNA_int_get_array(op->ptr, "initial_mouse", initial_mouse); + + switch(event->type) { + case MOUSEMOVE: + delta[0]= initial_mouse[0] - event->x; + delta[1]= initial_mouse[1] - event->y; + + //if (mode == WM_RADIALCONTROL_SIZE) + // delta[0]+= initial_value; + //else if(mode == WM_RADIALCONTROL_STRENGTH) + // delta[0]+= WM_RADIAL_CONTROL_DISPLAY_SIZE * (1 - initial_value); + //else if(mode == WM_RADIALCONTROL_ANGLE) { + // delta[0]+= WM_RADIAL_CONTROL_DISPLAY_SIZE * cos(initial_value*M_PI/180.0f); + // delta[1]+= WM_RADIAL_CONTROL_DISPLAY_SIZE * sin(initial_value*M_PI/180.0f); + //} + + dist= sqrtf(delta[0]*delta[0]+delta[1]*delta[1]); + + if(mode == WM_RADIALCONTROL_SIZE) + new_value = dist; + else if(mode == WM_RADIALCONTROL_STRENGTH) { + new_value = 1 - dist / WM_RADIAL_CONTROL_DISPLAY_SIZE; + } else if(mode == WM_RADIALCONTROL_ANGLE) + new_value = ((int)(atan2f(delta[1], delta[0]) * (float)(180.0 / M_PI)) + 180); + + if(event->ctrl) { + if(mode == WM_RADIALCONTROL_STRENGTH) + new_value = ((int)ceilf(new_value * 10.f) * 10.0f) / 100.f; + else + new_value = ((int)new_value + 5) / 10*10; + } + + break; + case ESCKEY: + case RIGHTMOUSE: + ret = OPERATOR_CANCELLED; + break; + case LEFTMOUSE: + case PADENTER: + op->type->exec(C, op); + ret = OPERATOR_FINISHED; + break; + } + + /* Clamp */ + if(new_value > rc->max_value) + new_value = rc->max_value; + else if(new_value < 0) + new_value = 0; + + /* Update paint data */ + rc->value = (float)new_value; + + RNA_float_set(op->ptr, "new_value", rc->value); + + if(ret != OPERATOR_RUNNING_MODAL) { + WM_paint_cursor_end(CTX_wm_manager(C), rc->cursor); + MEM_freeN(rc); + } + + ED_region_tag_redraw(CTX_wm_region(C)); + + //if (ret != OPERATOR_RUNNING_MODAL) { + // wmWindow *win = CTX_wm_window(C); + // WM_cursor_restore(win); + //} + + return ret; +} + +/* Expects the operator customdata to be an ImBuf (or NULL) */ +int WM_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + wmRadialControl *rc = MEM_callocN(sizeof(wmRadialControl), "radial control"); + // wmWindow *win = CTX_wm_window(C); + int mode = RNA_enum_get(op->ptr, "mode"); + float initial_value = RNA_float_get(op->ptr, "initial_value"); + //float initial_size = RNA_float_get(op->ptr, "initial_size"); + int mouse[2]; + + mouse[0]= event->x; + mouse[1]= event->y; + + //if (initial_size == 0) + // initial_size = WM_RADIAL_CONTROL_DISPLAY_SIZE; + + if(mode == WM_RADIALCONTROL_SIZE) { + rc->max_value = 200; + mouse[0]-= (int)initial_value; + } + else if(mode == WM_RADIALCONTROL_STRENGTH) { + rc->max_value = 1; + mouse[0]-= (int)(WM_RADIAL_CONTROL_DISPLAY_SIZE * (1.0f - initial_value)); + } + else if(mode == WM_RADIALCONTROL_ANGLE) { + rc->max_value = 360; + mouse[0]-= (int)(WM_RADIAL_CONTROL_DISPLAY_SIZE * cos(initial_value)); + mouse[1]-= (int)(WM_RADIAL_CONTROL_DISPLAY_SIZE * sin(initial_value)); + initial_value *= 180.0f/(float)M_PI; + } + + if(op->customdata) { + ImBuf *im = (ImBuf*)op->customdata; + /* Build GL texture */ + glGenTextures(1, &rc->tex); + glBindTexture(GL_TEXTURE_2D, rc->tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, im->x, im->y, 0, GL_ALPHA, GL_FLOAT, im->rect_float); + MEM_freeN(im->rect_float); + MEM_freeN(im); + } + + RNA_float_get_array(op->ptr, "color", rc->col); + RNA_float_get_array(op->ptr, "texture_color", rc->tex_col); + + RNA_int_set_array(op->ptr, "initial_mouse", mouse); + RNA_float_set(op->ptr, "new_value", initial_value); + + op->customdata = rc; + rc->mode = mode; + rc->initial_value = initial_value; + rc->initial_mouse[0] = mouse[0]; + rc->initial_mouse[1] = mouse[1]; + rc->cursor = WM_paint_cursor_activate(CTX_wm_manager(C), op->type->poll, + wm_radial_control_paint, op->customdata); + + //WM_cursor_modal(win, CURSOR_NONE); + + /* add modal handler */ + WM_event_add_modal_handler(C, op); + + WM_radial_control_modal(C, op, event); + + return OPERATOR_RUNNING_MODAL; +} + +/* Gets a descriptive string of the operation */ +void WM_radial_control_string(wmOperator *op, char str[], int maxlen) +{ + int mode = RNA_enum_get(op->ptr, "mode"); + float v = RNA_float_get(op->ptr, "new_value"); + + if(mode == WM_RADIALCONTROL_SIZE) + BLI_snprintf(str, maxlen, "Size: %d", (int)v); + else if(mode == WM_RADIALCONTROL_STRENGTH) + BLI_snprintf(str, maxlen, "Strength: %d", (int)v); + else if(mode == WM_RADIALCONTROL_ANGLE) + BLI_snprintf(str, maxlen, "Angle: %d", (int)(v * 180.0f/(float)M_PI)); +} + +/** Important: this doesn't define an actual operator, it + just sets up the common parts of the radial control op. **/ +void WM_OT_radial_control_partial(wmOperatorType *ot) +{ + static EnumPropertyItem radial_mode_items[] = { + {WM_RADIALCONTROL_SIZE, "SIZE", 0, "Size", ""}, + {WM_RADIALCONTROL_STRENGTH, "STRENGTH", 0, "Strength", ""}, + {WM_RADIALCONTROL_ANGLE, "ANGLE", 0, "Angle", ""}, + {0, NULL, 0, NULL, NULL}}; + static float color[4] = {1.0f, 1.0f, 1.0f, 0.5f}; + static float tex_color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + + /* Should be set in custom invoke() */ + RNA_def_float(ot->srna, "initial_value", 0, 0, FLT_MAX, "Initial Value", "", 0, FLT_MAX); + + /* Set internally, should be used in custom exec() to get final value */ + RNA_def_float(ot->srna, "new_value", 0, 0, FLT_MAX, "New Value", "", 0, FLT_MAX); + + /* Should be set before calling operator */ + RNA_def_enum(ot->srna, "mode", radial_mode_items, 0, "Mode", ""); + + /* Internal */ + RNA_def_int_vector(ot->srna, "initial_mouse", 2, NULL, INT_MIN, INT_MAX, "Initial Mouse", "", INT_MIN, INT_MAX); + + RNA_def_float_color(ot->srna, "color", 4, color, 0.0f, FLT_MAX, "Color", "Radial control color", 0.0f, 1.0f); + RNA_def_float_color(ot->srna, "texture_color", 4, tex_color, 0.0f, FLT_MAX, "Texture Color", "Radial control texture color", 0.0f, 1.0f); +} +#endif typedef struct { PropertyType type; diff --git a/source/blenderplayer/CMakeLists.txt b/source/blenderplayer/CMakeLists.txt index 901aaea3eca..34782ff9307 100644 --- a/source/blenderplayer/CMakeLists.txt +++ b/source/blenderplayer/CMakeLists.txt @@ -87,13 +87,12 @@ endif() ge_player_ghost ge_player_common bf_intern_string - bf_intern_ghost + bf_intern_ghost bf_rna bf_blenkernel bf_blenloader bf_blenpluginapi ge_blen_routines - bf_editor_datafiles ge_converter ge_logic_ketsji ge_phys_bullet @@ -104,10 +103,10 @@ endif() ge_oglrasterizer ge_logic_expressions ge_scenegraph - bf_ikplugin - bf_intern_itasc - bf_intern_ik - bf_intern_smoke + bf_ikplugin + bf_intern_itasc + bf_intern_ik + bf_intern_smoke bf_modifiers bf_intern_moto bf_nodes @@ -124,7 +123,7 @@ endif() bf_blenlib bf_imbuf_cineon bf_imbuf_openexr - extern_openjpeg + extern_openjpeg extern_redcode bf_imbuf_dds bf_dna @@ -134,11 +133,9 @@ endif() blenkernel_blc extern_binreloc extern_minilzo - bf_intern_ghost # duplicate for linking - bf_blenkernel # duplicate for linking bf_intern_mikktspace ) - + if(WITH_BUILTIN_GLEW) list(APPEND BLENDER_SORTED_LIBS extern_glew) endif() diff --git a/source/tests/CMakeLists.txt b/source/tests/CMakeLists.txt deleted file mode 100644 index 97e46bd44f2..00000000000 --- a/source/tests/CMakeLists.txt +++ /dev/null @@ -1,293 +0,0 @@ -# -*- mode: cmake; indent-tabs-mode: t; -*- -# $Id: CMakeLists.txt 34198 2011-01-09 15:12:08Z campbellbarton $ -# ***** 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. -# -# Contributor(s): Jacques Beaurain. -# -# ***** END GPL LICENSE BLOCK ***** - -# --env-system-scripts allows to run without the install target. - -# Use '--write-blend=/tmp/test.blend' to view output - - -set(TEST_SRC_DIR ${CMAKE_SOURCE_DIR}/../lib/tests) -set(TEST_OUT_DIR ${CMAKE_BINARY_DIR}/tests) - -# ugh, any better way to do this on testing only? -execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_OUT_DIR}) - -#~ if(NOT IS_DIRECTORY ${TEST_SRC_DIR}) -#~ message(FATAL_ERROR "CMake test directory not found!") -#~ endif() - -# all calls to blender use this -if(APPLE) - set(TEST_BLENDER_EXE ${EXECUTABLE_OUTPUT_PATH}/blender.app/Contents/MacOS/blender) -else() - set(TEST_BLENDER_EXE ${EXECUTABLE_OUTPUT_PATH}/blender) -endif() - -# for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no -set(TEST_BLENDER_EXE ${TEST_BLENDER_EXE} --background --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts) - - -# ------------------------------------------------------------------------------ -# GENERAL PYTHON CORRECTNESS TESTS -add_test(script_load_addons ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_load_addons.py -) - -add_test(script_load_modules ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_load_py_modules.py -) - -# test running operators doesn't segfault under various conditions -add_test(script_run_operators ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_run_operators.py -) - -# ------------------------------------------------------------------------------ -# IO TESTS - -# OBJ Import tests -add_test(import_obj_cube ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.import_scene.obj\(filepath='${TEST_SRC_DIR}/io_tests/obj/cube.obj'\) - --md5=4d090508b812b5e08168aa2614746bda --md5_method=SCENE - --write-blend=${TEST_OUT_DIR}/import_obj_cube.blend -) - -add_test(import_obj_nurbs_cyclic ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.import_scene.obj\(filepath='${TEST_SRC_DIR}/io_tests/obj/nurbs_cyclic.obj'\) - --md5=9e0da7b65b4c4f818a203d56af2d3a4b --md5_method=SCENE - --write-blend=${TEST_OUT_DIR}/import_obj_nurbs_cyclic.blend -) - -add_test(import_obj_makehuman ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.import_scene.obj\(filepath='${TEST_SRC_DIR}/io_tests/obj/makehuman.obj'\) - --md5=e0829dc078b0789e1d81f1071235bc4f --md5_method=SCENE - --write-blend=${TEST_OUT_DIR}/import_obj_makehuman.blend -) - -# OBJ Export tests -add_test(export_obj_cube ${TEST_BLENDER_EXE} - ${TEST_SRC_DIR}/io_tests/blend_geometry/all_quads.blend - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.export_scene.obj\(filepath='${TEST_OUT_DIR}/export_obj_cube.obj',use_selection=False\) - --md5_source=${TEST_OUT_DIR}/export_obj_cube.obj - --md5_source=${TEST_OUT_DIR}/export_obj_cube.mtl - --md5=f0580f0e5d7b379e0460f11d8fde244d --md5_method=FILE -) - -add_test(export_obj_nurbs ${TEST_BLENDER_EXE} - ${TEST_SRC_DIR}/io_tests/blend_geometry/nurbs.blend - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.export_scene.obj\(filepath='${TEST_OUT_DIR}/export_obj_nurbs.obj',use_selection=False,use_nurbs=True\) - --md5_source=${TEST_OUT_DIR}/export_obj_nurbs.obj - --md5_source=${TEST_OUT_DIR}/export_obj_nurbs.mtl - --md5=aa00875343e0feea449739146d26d7d0 --md5_method=FILE -) - -add_test(export_obj_all_objects ${TEST_BLENDER_EXE} - ${TEST_SRC_DIR}/io_tests/blend_scene/all_objects.blend - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.export_scene.obj\(filepath='${TEST_OUT_DIR}/export_obj_all_objects.obj',use_selection=False,use_nurbs=True\) - --md5_source=${TEST_OUT_DIR}/export_obj_all_objects.obj - --md5_source=${TEST_OUT_DIR}/export_obj_all_objects.mtl - --md5=4c93980ecfb7d02ca68d3da8e2fced69 --md5_method=FILE -) - - - -# PLY Import tests -add_test(import_ply_cube ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.import_mesh.ply\(filepath='${TEST_SRC_DIR}/io_tests/ply/cube_ascii.ply'\) - --md5=527134343c27fc0ea73115b85fbfd3ac --md5_method=SCENE - --write-blend=${TEST_OUT_DIR}/import_ply_cube.blend -) - -add_test(import_ply_bunny ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.import_mesh.ply\(filepath='${TEST_SRC_DIR}/io_tests/ply/bunny2.ply'\) - --md5=6ea5b8533400a17accf928b8fd024eaa --md5_method=SCENE - --write-blend=${TEST_OUT_DIR}/import_ply_bunny.blend -) - -add_test(import_ply_small_holes ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.import_mesh.ply\(filepath='${TEST_SRC_DIR}/io_tests/ply/many_small_holes.ply'\) - --md5=c3093e26ecae5b6d59fbbcf2a0d0b39f --md5_method=SCENE - --write-blend=${TEST_OUT_DIR}/import_ply_small_holes.blend -) - -# PLY Export tests (TODO) - - - -# STL Import tests -add_test(import_stl_cube ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.import_mesh.stl\(filepath='${TEST_SRC_DIR}/io_tests/stl/cube.stl'\) - --md5=8ceb5bb7e1cb5f4342fa1669988c66b4 --md5_method=SCENE - --write-blend=${TEST_OUT_DIR}/import_stl_cube.blend -) - -add_test(import_stl_conrod ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.import_mesh.stl\(filepath='${TEST_SRC_DIR}/io_tests/stl/conrod.stl'\) - --md5=690a4b8eb9002dcd8631c5a575ea7348 --md5_method=SCENE - --write-blend=${TEST_OUT_DIR}/import_stl_conrod.blend -) - -add_test(import_stl_knot_max_simplified ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.import_mesh.stl\(filepath='${TEST_SRC_DIR}/io_tests/stl/knot_max_simplified.stl'\) - --md5=baf82803f45a84ec4ddbad9cef57dd3e --md5_method=SCENE - --write-blend=${TEST_OUT_DIR}/import_stl_knot_max_simplified.blend -) - -# STL Export tests (TODO) - - - -# X3D Import -add_test(import_x3d_cube ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.import_scene.x3d\(filepath='${TEST_SRC_DIR}/io_tests/x3d/color_cube.x3d'\) - --md5=330c0cf6e8c44d5fe5b662d30b75be89 --md5_method=SCENE - --write-blend=${TEST_OUT_DIR}/import_x3d_cube.blend -) - -add_test(import_x3d_teapot ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.import_scene.x3d\(filepath='${TEST_SRC_DIR}/io_tests/x3d/teapot.x3d'\) - --md5=b2f02157bc918b54b835d1e6ece70423 --md5_method=SCENE - --write-blend=${TEST_OUT_DIR}/import_x3d_teapot.blend -) - -add_test(import_x3d_suzanne_material ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.import_scene.x3d\(filepath='${TEST_SRC_DIR}/io_tests/x3d/suzanne_material.x3d'\) - --md5=11837901cbbfabef52b6ab4f26fe97b3 --md5_method=SCENE - --write-blend=${TEST_OUT_DIR}/import_x3d_suzanne_material.blend -) - -# X3D Export -add_test(export_x3d_cube ${TEST_BLENDER_EXE} - ${TEST_SRC_DIR}/io_tests/blend_geometry/all_quads.blend - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.export_scene.x3d\(filepath='${TEST_OUT_DIR}/export_x3d_cube.x3d',use_selection=False\) - --md5_source=${TEST_OUT_DIR}/export_x3d_cube.x3d - --md5=9198ca86f19b68a4f1eb75bb48cb3dea --md5_method=FILE -) - -add_test(export_x3d_nurbs ${TEST_BLENDER_EXE} - ${TEST_SRC_DIR}/io_tests/blend_geometry/nurbs.blend - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.export_scene.x3d\(filepath='${TEST_OUT_DIR}/export_x3d_nurbs.x3d',use_selection=False\) - --md5_source=${TEST_OUT_DIR}/export_x3d_nurbs.x3d - --md5=078c0ca5a08f123cd2cdac48afb54853 --md5_method=FILE -) - -add_test(export_x3d_all_objects ${TEST_BLENDER_EXE} - ${TEST_SRC_DIR}/io_tests/blend_scene/all_objects.blend - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.export_scene.x3d\(filepath='${TEST_OUT_DIR}/export_x3d_all_objects.x3d',use_selection=False\) - --md5_source=${TEST_OUT_DIR}/export_x3d_all_objects.x3d - --md5=090f4f36c450826a043f9cd074d9cbb4 --md5_method=FILE -) - - - -# 3DS Import -add_test(import_3ds_cube ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.import_scene.autodesk_3ds\(filepath='${TEST_SRC_DIR}/io_tests/3ds/cube.3ds'\) - --md5=cb5a45c35a343c3f5beca2a918472951 --md5_method=SCENE - --write-blend=${TEST_OUT_DIR}/import_3ds_cube.blend -) - -add_test(import_3ds_hierarchy_lara ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.import_scene.autodesk_3ds\(filepath='${TEST_SRC_DIR}/io_tests/3ds/hierarchy_lara.3ds'\) - --md5=2e9812099b26ad607fdcf4c7be918c71 --md5_method=SCENE - --write-blend=${TEST_OUT_DIR}/import_3ds_hierarchy_lara.blend -) - -add_test(import_3ds_hierarchy_greek_trireme ${TEST_BLENDER_EXE} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.import_scene.autodesk_3ds\(filepath='${TEST_SRC_DIR}/io_tests/3ds/hierarchy_greek_trireme.3ds'\) - --md5=d05b922d7be20356d8409d1f768a3a9a --md5_method=SCENE - --write-blend=${TEST_OUT_DIR}/import_3ds_hierarchy_greek_trireme.blend -) - -# 3DS Export -add_test(export_3ds_cube ${TEST_BLENDER_EXE} - ${TEST_SRC_DIR}/io_tests/blend_geometry/all_quads.blend - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.export_scene.autodesk_3ds\(filepath='${TEST_OUT_DIR}/export_3ds_cube.3ds',use_selection=False\) - --md5_source=${TEST_OUT_DIR}/export_3ds_cube.3ds - --md5=0df6cfb130052d01e31ef77d391d4cc0 --md5_method=FILE -) - -add_test(export_3ds_nurbs ${TEST_BLENDER_EXE} - ${TEST_SRC_DIR}/io_tests/blend_geometry/nurbs.blend - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.export_scene.autodesk_3ds\(filepath='${TEST_OUT_DIR}/export_3ds_nurbs.3ds',use_selection=False\) - --md5_source=${TEST_OUT_DIR}/export_3ds_nurbs.3ds - --md5=ba1a6d43346fee3bcadc7e30e3c95935 --md5_method=FILE -) - -add_test(export_3ds_all_objects ${TEST_BLENDER_EXE} - ${TEST_SRC_DIR}/io_tests/blend_scene/all_objects.blend - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.export_scene.autodesk_3ds\(filepath='${TEST_OUT_DIR}/export_3ds_all_objects.3ds',use_selection=False\) - --md5_source=${TEST_OUT_DIR}/export_3ds_all_objects.3ds - --md5=87349a4699f1006e8194fb0ac05ac9c8 --md5_method=FILE -) - - - -# FBX Export -# 'use_metadata=False' for reliable md5's -add_test(export_fbx_cube ${TEST_BLENDER_EXE} - ${TEST_SRC_DIR}/io_tests/blend_geometry/all_quads.blend - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.export_scene.fbx\(filepath='${TEST_OUT_DIR}/export_fbx_cube.fbx',use_selection=False,use_metadata=False\) - --md5_source=${TEST_OUT_DIR}/export_fbx_cube.fbx - --md5=b2428e11b9ae650819f8d8b38cd869f7 --md5_method=FILE -) - -add_test(export_fbx_nurbs ${TEST_BLENDER_EXE} - ${TEST_SRC_DIR}/io_tests/blend_geometry/nurbs.blend - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.export_scene.fbx\(filepath='${TEST_OUT_DIR}/export_fbx_nurbs.fbx',use_selection=False,use_metadata=False\) - --md5_source=${TEST_OUT_DIR}/export_fbx_nurbs.fbx - --md5=741b536e98d6b105952766d0f290f270 --md5_method=FILE -) - -add_test(export_fbx_all_objects ${TEST_BLENDER_EXE} - ${TEST_SRC_DIR}/io_tests/blend_scene/all_objects.blend - --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- - --run={'FINISHED'}&bpy.ops.export_scene.fbx\(filepath='${TEST_OUT_DIR}/export_fbx_all_objects.fbx',use_selection=False,use_metadata=False\) - --md5_source=${TEST_OUT_DIR}/export_fbx_all_objects.fbx - --md5=d6b8b027cd2a0e99d88e5c3d77932748 --md5_method=FILE -) diff --git a/source/tests/batch_import.py b/source/tests/batch_import.py deleted file mode 100644 index 5c228c014ca..00000000000 --- a/source/tests/batch_import.py +++ /dev/null @@ -1,202 +0,0 @@ -# ##### 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 ##### - -# <pep8 compliant> - -""" -Example Usage: - -./blender.bin --background --python source/tests/batch_import.py -- \ - --operator="bpy.ops.import_scene.obj" \ - --path="/fe/obj" \ - --match="*.obj" \ - --start=0 --end=10 \ - --save_path=/tmp/test - -./blender.bin --background --python source/tests/batch_import.py -- \ - --operator="bpy.ops.import_scene.autodesk_3ds" \ - --path="/fe/" \ - --match="*.3ds" \ - --start=0 --end=1000 \ - --save_path=/tmp/test - -./blender.bin --background --addons io_curve_svg --python source/tests/batch_import.py -- \ - --operator="bpy.ops.import_curve.svg" \ - --path="/usr/" \ - --match="*.svg" \ - --start=0 --end=1000 \ - --save_path=/tmp/test - -""" - -import os -import sys - - -def clear_scene(): - import bpy - unique_obs = set() - for scene in bpy.data.scenes: - for obj in scene.objects[:]: - scene.objects.unlink(obj) - unique_obs.add(obj) - - # remove obdata, for now only worry about the startup scene - for bpy_data_iter in (bpy.data.objects, bpy.data.meshes, bpy.data.lamps, bpy.data.cameras): - for id_data in bpy_data_iter: - bpy_data_iter.remove(id_data) - - -def batch_import(operator="", - path="", - save_path="", - match="", - start=0, - end=sys.maxsize, - ): - import addon_utils - _reset_all = addon_utils.reset_all # XXX, hack - - import fnmatch - - path = os.path.normpath(path) - path = os.path.abspath(path) - - match_upper = match.upper() - pattern_match = lambda a: fnmatch.fnmatchcase(a.upper(), match_upper) - - def file_generator(path): - for dirpath, dirnames, filenames in os.walk(path): - - # skip '.svn' - if dirpath.startswith("."): - continue - - for filename in filenames: - if pattern_match(filename): - yield os.path.join(dirpath, filename) - - print("Collecting %r files in %s" % (match, path), end="") - - files = list(file_generator(path)) - files_len = len(files) - end = min(end, len(files)) - print(" found %d" % files_len, end="") - - files.sort() - files = files[start:end] - if len(files) != files_len: - print(" using a subset in (%d, %d), total %d" % (start, end, len(files)), end="") - - import bpy - op = eval(operator) - - tot_done = 0 - tot_fail = 0 - - for i, f in enumerate(files): - print(" %s(filepath=%r) # %d of %d" % (operator, f, i + start, len(files))) - - # hack so loading the new file doesnt undo our loaded addons - addon_utils.reset_all = lambda: None # XXX, hack - - bpy.ops.wm.read_factory_settings() - - addon_utils.reset_all = _reset_all # XXX, hack - clear_scene() - - result = op(filepath=f) - - if 'FINISHED' in result: - tot_done += 1 - else: - tot_fail += 1 - - if save_path: - fout = os.path.join(save_path, os.path.relpath(f, path)) - fout_blend = os.path.splitext(fout)[0] + ".blend" - - print("\tSaving: %r" % fout_blend) - - fout_dir = os.path.dirname(fout_blend) - if not os.path.exists(fout_dir): - os.makedirs(fout_dir) - - bpy.ops.wm.save_as_mainfile(filepath=fout_blend) - - print("finished, done:%d, fail:%d" % (tot_done, tot_fail)) - - -def main(): - import optparse - - # get the args passed to blender after "--", all of which are ignored by blender specifically - # so python may receive its own arguments - argv = sys.argv - - if "--" not in argv: - argv = [] # as if no args are passed - else: - argv = argv[argv.index("--") + 1:] # get all args after "--" - - # When --help or no args are given, print this help - usage_text = "Run blender in background mode with this script:" - usage_text += " blender --background --python " + __file__ + " -- [options]" - - parser = optparse.OptionParser(usage=usage_text) - - # Example background utility, add some text and renders or saves it (with options) - # Possible types are: string, int, long, choice, float and complex. - parser.add_option("-o", "--operator", dest="operator", help="This text will be used to render an image", type="string") - parser.add_option("-p", "--path", dest="path", help="Path to use for searching for files", type='string') - parser.add_option("-m", "--match", dest="match", help="Wildcard to match filename", type="string") - parser.add_option("-s", "--save_path", dest="save_path", help="Save the input file to a blend file in a new location", metavar='string') - parser.add_option("-S", "--start", dest="start", help="From collected files, start with this index", metavar='int') - parser.add_option("-E", "--end", dest="end", help="From collected files, end with this index", metavar='int') - - options, args = parser.parse_args(argv) # In this example we wont use the args - - if not argv: - parser.print_help() - return - - if not options.operator: - print("Error: --operator=\"some string\" argument not given, aborting.") - parser.print_help() - return - - if options.start is None: - options.start = 0 - - if options.end is None: - options.end = sys.maxsize - - # Run the example function - batch_import(operator=options.operator, - path=options.path, - save_path=options.save_path, - match=options.match, - start=int(options.start), - end=int(options.end), - ) - - print("batch job finished, exiting") - - -if __name__ == "__main__": - main() diff --git a/source/tests/bl_load_addons.py b/source/tests/bl_load_addons.py deleted file mode 100644 index 5bd83abbcb6..00000000000 --- a/source/tests/bl_load_addons.py +++ /dev/null @@ -1,78 +0,0 @@ -# ##### 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 ##### - -# <pep8 compliant> - -# simple script to enable all addons, and disable - -import bpy -import addon_utils - -import sys -import imp - - -def reload_addons(do_reload=True, do_reverse=True): - modules = addon_utils.modules({}) - modules.sort(key=lambda mod: mod.__name__) - addons = bpy.context.user_preferences.addons - - # first disable all - for mod_name in list(addons.keys()): - addon_utils.disable(mod_name) - - assert(bool(addons) == False) - - # Run twice each time. - for i in (0, 1): - for mod in modules: - mod_name = mod.__name__ - print("\tenabling:", mod_name) - addon_utils.enable(mod_name) - assert(mod_name in addons) - - for mod in addon_utils.modules({}): - mod_name = mod.__name__ - print("\tdisabling:", mod_name) - addon_utils.disable(mod_name) - assert(not (mod_name in addons)) - - # now test reloading - if do_reload: - imp.reload(sys.modules[mod_name]) - - if do_reverse: - # incase order matters when it shouldnt - modules.reverse() - - -def main(): - reload_addons(do_reload=False, do_reverse=False) - reload_addons(do_reload=False, do_reverse=True) - reload_addons(do_reload=True, do_reverse=True) - - -if __name__ == "__main__": - - # So a python error exits(1) - try: - main() - except: - import traceback - traceback.print_exc() - sys.exit(1) diff --git a/source/tests/bl_load_py_modules.py b/source/tests/bl_load_py_modules.py deleted file mode 100644 index 5a65578d8d3..00000000000 --- a/source/tests/bl_load_py_modules.py +++ /dev/null @@ -1,145 +0,0 @@ -# ##### 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 ##### - -# <pep8 compliant> - -# simple script to enable all addons, and disable - -import bpy -import addon_utils - -import sys -import os -import imp - - -def source_list(path, filename_check=None): - from os.path import join - for dirpath, dirnames, filenames in os.walk(path): - # skip '.svn' - if dirpath.startswith("."): - continue - - for filename in filenames: - filepath = join(dirpath, filename) - if filename_check is None or filename_check(filepath): - yield filepath - - -def load_addons(): - modules = addon_utils.modules({}) - modules.sort(key=lambda mod: mod.__name__) - addons = bpy.context.user_preferences.addons - - # first disable all - for mod_name in list(addons.keys()): - addon_utils.disable(mod_name) - - assert(bool(addons) == False) - - for mod in modules: - mod_name = mod.__name__ - addon_utils.enable(mod_name) - assert(mod_name in addons) - - -def load_modules(): - modules = [] - module_paths = [] - - # paths blender stores scripts in. - paths = bpy.utils.script_paths() - - # - # find all sys.path we added - for script_path in paths: - for mod_dir in sys.path: - if mod_dir.startswith(script_path): - module_paths.append(mod_dir) - - # - # collect modules from our paths. - for mod_dir in module_paths: - # print("mod_dir", mod_dir) - for mod, mod_full in bpy.path.module_names(mod_dir): - modules.append(__import__(mod)) - - # - # now submodules - for m in modules: - filepath = m.__file__ - if os.path.basename(filepath).startswith("__init__."): - mod_dir = os.path.dirname(filepath) - for submod, submod_full in bpy.path.module_names(mod_dir): - # fromlist is ignored, ugh. - mod_name_full = m.__name__ + "." + submod - __import__(mod_name_full) - mod_imp = sys.modules[mod_name_full] - - # check we load what we ask for. - assert(os.path.samefile(mod_imp.__file__, submod_full)) - - modules.append(mod_imp) - - # - # check which filepaths we didnt load - source_files = [] - for mod_dir in module_paths: - source_files.extend(source_list(mod_dir, filename_check=lambda f: f.endswith(".py"))) - - source_files = list(set(source_files)) - source_files.sort() - - # - # remove loaded files - loaded_files = list({m.__file__ for m in modules}) - loaded_files.sort() - - for f in loaded_files: - source_files.remove(f) - - # - # test we tested all files except for presets and templates - ignore_paths = [ - os.sep + "presets" + os.sep, - os.sep + "templates" + os.sep, - ] - - for f in source_files: - ok = False - for ignore in ignore_paths: - if ignore in f: - ok = True - if not ok: - raise Exception("Source file %r not loaded in test" % f) - - print("loaded %d modules" % len(loaded_files)) - - -def main(): - load_addons() - load_modules() - -if __name__ == "__main__": - # So a python error exits(1) - try: - main() - except: - import traceback - traceback.print_exc() - sys.exit(1) diff --git a/source/tests/bl_run_operators.py b/source/tests/bl_run_operators.py deleted file mode 100644 index 668b4e69228..00000000000 --- a/source/tests/bl_run_operators.py +++ /dev/null @@ -1,176 +0,0 @@ -# ##### 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 ##### - -# <pep8 compliant> - -# semi-useful script, runs all operators in a number of different -# contexts, cheap way to find misc small bugs but is in no way a complete test. -# -# only error checked for here is a segfault. - -import bpy -import sys - -op_blacklist = ( - "script.reload", - "export*.*", - "import*.*", - "*.save_*", - "*.read_*", - "*.open_*", - "*.link_append", - "render.render", - "*.*_export", - "*.*_import", - "wm.url_open", - "wm.doc_view", - "wm.path_open", - "help.operator_cheat_sheet", - ) - - -def filter_op_list(operators): - from fnmatch import fnmatchcase - - def is_op_ok(op): - for op_match in op_blacklist: - if fnmatchcase(op, op_match): - print(" skipping: %s (%s)" % (op, op_match)) - return False - return True - - operators[:] = [op for op in operators if is_op_ok(op[0])] - - -def run_ops(operators, setup_func=None): - print("\ncontext:", setup_func.__name__) - # first invoke - for op_id, op in operators: - if op.poll(): - print(" operator:", op_id) - sys.stdout.flush() # incase of crash - - # disable will get blender in a bad state and crash easy! - bpy.ops.wm.read_factory_settings() - - setup_func() - - for mode in ('EXEC_DEFAULT', 'INVOKE_DEFAULT'): - try: - op(mode) - except: - #import traceback - #traceback.print_exc() - pass - - -# contexts -def ctx_clear_scene(): # copied from batch_import.py - unique_obs = set() - for scene in bpy.data.scenes: - for obj in scene.objects[:]: - scene.objects.unlink(obj) - unique_obs.add(obj) - - # remove obdata, for now only worry about the startup scene - for bpy_data_iter in (bpy.data.objects, bpy.data.meshes, bpy.data.lamps, bpy.data.cameras): - for id_data in bpy_data_iter: - bpy_data_iter.remove(id_data) - - -def ctx_editmode_mesh(): - bpy.ops.object.mode_set(mode='EDIT') - bpy.ops.object.vertex_group_add() - - -def ctx_editmode_curves(): - bpy.ops.curve.primitive_nurbs_circle_add() - bpy.ops.object.mode_set(mode='EDIT') - - -def ctx_editmode_surface(): - bpy.ops.surface.primitive_nurbs_surface_torus_add() - bpy.ops.object.mode_set(mode='EDIT') - - -def ctx_editmode_mball(): - bpy.ops.object.metaball_add() - bpy.ops.object.mode_set(mode='EDIT') - - -def ctx_editmode_text(): - bpy.ops.object.text_add() - bpy.ops.object.mode_set(mode='EDIT') - - -def ctx_editmode_armature(): - bpy.ops.object.armature_add() - bpy.ops.object.mode_set(mode='EDIT') - - -def ctx_editmode_lattice(): - bpy.ops.object.add(type='LATTICE') - bpy.ops.object.mode_set(mode='EDIT') - # bpy.ops.object.vertex_group_add() - - -def ctx_object_empty(): - bpy.ops.object.add(type='EMPTY') - - -def ctx_weightpaint(): - bpy.ops.object.mode_set(mode='WEIGHT_PAINT') - - -def main(): - # bpy.ops.wm.read_factory_settings() - import bpy - operators = [] - for mod_name in dir(bpy.ops): - mod = getattr(bpy.ops, mod_name) - for submod_name in dir(mod): - op = getattr(mod, submod_name) - operators.append(("%s.%s" % (mod_name, submod_name), op)) - - operators.sort(key=lambda op: op[0]) - - filter_op_list(operators) - - # for testing, mix the list up. - #operators.reverse() - - #import random - #random.shuffle(operators) - - # Run the operator tests in different contexts - run_ops(operators, setup_func=lambda: None) - run_ops(operators, setup_func=ctx_editmode_surface) - run_ops(operators, setup_func=ctx_object_empty) - run_ops(operators, setup_func=ctx_editmode_armature) - run_ops(operators, setup_func=ctx_editmode_mesh) - run_ops(operators, setup_func=ctx_clear_scene) - run_ops(operators, setup_func=ctx_editmode_curves) - run_ops(operators, setup_func=ctx_editmode_mball) - run_ops(operators, setup_func=ctx_editmode_text) - run_ops(operators, setup_func=ctx_weightpaint) - run_ops(operators, setup_func=ctx_editmode_lattice) - - print("finished") - -if __name__ == "__main__": - main() diff --git a/source/tests/bl_test.py b/source/tests/bl_test.py deleted file mode 100644 index cfe91356444..00000000000 --- a/source/tests/bl_test.py +++ /dev/null @@ -1,197 +0,0 @@ -# ##### 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 ##### - -# <pep8 compliant> - -import sys -import os - - -# may split this out into a new file -def replace_bpy_app_version(): - """ So MD5's are predictable from output which uses blenders versions. - """ - - import bpy - - app = bpy.app - app_fake = type(bpy)("bpy.app") - - for attr in dir(app): - if not attr.startswith("_"): - setattr(app_fake, attr, getattr(app, attr)) - - app_fake.version = 0, 0, 0 - app_fake.version_string = "0.00 (sub 0)" - bpy.app = app_fake - - -def clear_startup_blend(): - import bpy - - for scene in bpy.data.scenes: - for obj in scene.objects: - scene.objects.unlink(obj) - - -def blend_to_md5(): - import bpy - scene = bpy.context.scene - ROUND = 4 - - def matrix2str(matrix): - return "".join([str(round(axis, ROUND)) for vector in matrix for axis in vector]).encode('ASCII') - - def coords2str(seq, attr): - return "".join([str(round(axis, ROUND)) for vertex in seq for axis in getattr(vertex, attr)]).encode('ASCII') - - import hashlib - - md5 = hashlib.new("md5") - md5_update = md5.update - - for obj in scene.objects: - md5_update(matrix2str(obj.matrix_world)) - data = obj.data - - if type(data) == bpy.types.Mesh: - md5_update(coords2str(data.vertices, "co")) - elif type(data) == bpy.types.Curve: - for spline in data.splines: - md5_update(coords2str(spline.bezier_points, "co")) - md5_update(coords2str(spline.points, "co")) - - return md5.hexdigest() - - -def main(): - argv = sys.argv - print(" args:", " ".join(argv)) - argv = argv[argv.index("--") + 1:] - - def arg_extract(arg, optional=True, array=False): - arg += "=" - if array: - value = [] - else: - value = None - - i = 0 - while i < len(argv): - if argv[i].startswith(arg): - item = argv[i][len(arg):] - del argv[i] - i -= 1 - - if array: - value.append(item) - else: - value = item - break - - i += 1 - - if (not value) and (not optional): - print(" '%s' not set" % arg) - sys.exit(1) - - return value - - run = arg_extract("--run", optional=False) - md5 = arg_extract("--md5", optional=False) - md5_method = arg_extract("--md5_method", optional=False) # 'SCENE' / 'FILE' - - # only when md5_method is 'FILE' - md5_source = arg_extract("--md5_source", optional=True, array=True) - - # save blend file, for testing - write_blend = arg_extract("--write-blend", optional=True) - - # ensure files are written anew - for f in md5_source: - if os.path.exists(f): - os.remove(f) - - import bpy - - replace_bpy_app_version() - if not bpy.data.filepath: - clear_startup_blend() - - print(" Running: '%s'" % run) - print(" MD5: '%s'!" % md5) - - try: - result = eval(run) - except: - import traceback - traceback.print_exc() - sys.exit(1) - - if write_blend is not None: - print(" Writing Blend: %s" % write_blend) - bpy.ops.wm.save_mainfile(filepath=write_blend, check_existing=False) - - print(" Result: '%s'" % str(result)) - if not result: - print(" Running: %s -> False" % run) - sys.exit(1) - - if md5_method == 'SCENE': - md5_new = blend_to_md5() - elif md5_method == 'FILE': - if not md5_source: - print(" Missing --md5_source argument") - sys.exit(1) - - for f in md5_source: - if not os.path.exists(f): - print(" Missing --md5_source=%r argument does not point to a file") - sys.exit(1) - - import hashlib - - md5_instance = hashlib.new("md5") - md5_update = md5_instance.update - - for f in md5_source: - filehandle = open(f, "rb") - md5_update(filehandle.read()) - filehandle.close() - - md5_new = md5_instance.hexdigest() - - else: - print(" Invalid --md5_method=%s argument is not a valid source") - sys.exit(1) - - if md5 != md5_new: - print(" Running: %s\n MD5 Recieved: %s\n MD5 Expected: %s" % (run, md5_new, md5)) - sys.exit(1) - - print(" Success: %s" % run) - - -if __name__ == "__main__": - # So a python error exits(1) - try: - main() - except: - import traceback - traceback.print_exc() - sys.exit(1) diff --git a/source/tests/pep8.py b/source/tests/pep8.py deleted file mode 100644 index f7c416553b2..00000000000 --- a/source/tests/pep8.py +++ /dev/null @@ -1,118 +0,0 @@ -# ##### 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 ##### - -# <pep8 compliant> - -import os - -# depends on pep8, pyflakes, pylint -# for ubuntu -# -# sudo apt-get install pylint pyflakes -# -# sudo apt-get install python-setuptools python-pip -# sudo pip install pep8 -# -# in debian install pylint pyflakes pep8 with apt-get/aptitude/etc -# -# on *nix run -# python source/tests/pep8.py > test_pep8.log 2>&1 - -# how many lines to read into the file, pep8 comment -# should be directly after the licence header, ~20 in most cases -PEP8_SEEK_COMMENT = 40 -SKIP_PREFIX = "./tools", "./config", "./scons", "./extern" - - -def file_list_py(path): - for dirpath, dirnames, filenames in os.walk(path): - for filename in filenames: - if filename.endswith(".py") or filename.endswith(".cfg"): - yield os.path.join(dirpath, filename) - - -def is_pep8(path): - print(path) - if open(path, 'rb').read(3) == b'\xef\xbb\xbf': - print("\nfile contains BOM, remove first 3 bytes: %r\n" % path) - - # templates dont have a header but should be pep8 - for d in ("presets", "templates", "examples"): - if ("%s%s%s" % (os.sep, d, os.sep)) in path: - return 1 - - f = open(path, 'r', encoding="utf8") - for i in range(PEP8_SEEK_COMMENT): - line = f.readline() - if line.startswith("# <pep8"): - if line.startswith("# <pep8 compliant>"): - return 1 - elif line.startswith("# <pep8-80 compliant>"): - return 2 - f.close() - return 0 - - -def main(): - files = [] - files_skip = [] - for f in file_list_py("."): - if [None for prefix in SKIP_PREFIX if f.startswith(prefix)]: - continue - - pep8_type = is_pep8(f) - - if pep8_type: - # so we can batch them for each tool. - files.append((os.path.abspath(f), pep8_type)) - else: - files_skip.append(f) - - print("\nSkipping...") - for f in files_skip: - print(" %s" % f) - - # strict imports - print("\n\n\n# running pep8...") - import re - import_check = re.compile(r"\s*from\s+[A-z\.]+\s+import \*\s*") - for f, pep8_type in files: - for i, l in enumerate(open(f, 'r', encoding='utf8')): - if import_check.match(l): - print("%s:%d:0: global import bad practice" % (f, i + 1)) - - print("\n\n\n# running pep8...") - for f, pep8_type in files: - if pep8_type == 1: - # E501:80 line length - os.system("pep8 --repeat --ignore=E501 '%s'" % (f)) - else: - os.system("pep8 --repeat '%s'" % (f)) - - # pyflakes - print("\n\n\n# running pyflakes...") - for f, pep8_type in files: - os.system("pyflakes '%s'" % f) - - print("\n\n\n# running pylint...") - for f, pep8_type in files: - # let pep8 complain about line length - os.system("pylint --reports=n --max-line-length=1000 '%s'" % f) - -if __name__ == "__main__": - main() diff --git a/source/tests/rna_array.py b/source/tests/rna_array.py deleted file mode 100644 index 06b4735cc0d..00000000000 --- a/source/tests/rna_array.py +++ /dev/null @@ -1,297 +0,0 @@ -# ##### 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 ##### - -import unittest -import random - -test= bpy.data.test - -# farr - 1-dimensional array of float -# fdarr - dynamic 1-dimensional array of float -# fmarr - 3-dimensional ([3][4][5]) array of float -# fdmarr - dynamic 3-dimensional (ditto size) array of float - -# same as above for other types except that the first letter is "i" for int and "b" for bool - -class TestArray(unittest.TestCase): - # test that assignment works by: assign -> test value - # - rvalue = list of float - # - rvalue = list of numbers - # test.object - # bpy.data.test.farr[3], iarr[3], barr[...], fmarr, imarr, bmarr - - def setUp(self): - test.farr= (1.0, 2.0, 3.0) - test.iarr= (7, 8, 9) - test.barr= (False, True, False) - - # test access - # test slice access, negative indices - def test_access(self): - rvals= ([1.0, 2.0, 3.0], [7, 8, 9], [False, True, False]) - for arr, rval in zip((test.farr, test.iarr, test.barr), rvals): - self.assertEqual(prop_to_list(arr), rval) - self.assertEqual(arr[0:3], rval) - self.assertEqual(arr[1:2], rval[1:2]) - self.assertEqual(arr[-1], arr[2]) - self.assertEqual(arr[-2], arr[1]) - self.assertEqual(arr[-3], arr[0]) - - # fail when index out of bounds - def test_access_fail(self): - for arr in (test.farr, test.iarr, test.barr): - self.assertRaises(IndexError, lambda : arr[4]) - - # test assignment of a whole array - def test_assign_array(self): - # should accept int as float - test.farr= (1, 2, 3) - - # fail when: unexpected no. of items, invalid item type - def test_assign_array_fail(self): - def assign_empty_list(arr): - setattr(test, arr, ()) - - for arr in ("farr", "iarr", "barr"): - self.assertRaises(ValueError, assign_empty_list, arr) - - def assign_invalid_float(): - test.farr= (1.0, 2.0, "3.0") - - def assign_invalid_int(): - test.iarr= ("1", 2, 3) - - def assign_invalid_bool(): - test.barr= (True, 0.123, False) - - for func in [assign_invalid_float, assign_invalid_int, assign_invalid_bool]: - self.assertRaises(TypeError, func) - - # shouldn't accept float as int - def assign_float_as_int(): - test.iarr= (1, 2, 3.0) - self.assertRaises(TypeError, assign_float_as_int) - - # non-dynamic arrays cannot change size - def assign_different_size(arr, val): - setattr(test, arr, val) - for arr, val in zip(("iarr", "farr", "barr"), ((1, 2), (1.0, 2.0), (True, False))): - self.assertRaises(ValueError, assign_different_size, arr, val) - - # test assignment of specific items - def test_assign_item(self): - for arr, rand_func in zip((test.farr, test.iarr, test.barr), (rand_float, rand_int, rand_bool)): - for i in range(len(arr)): - val= rand_func() - arr[i]= val - - self.assertEqual(arr[i], val) - - # float prop should accept also int - for i in range(len(test.farr)): - val= rand_int() - test.farr[i]= val - self.assertEqual(test.farr[i], float(val)) - - # - - def test_assign_item_fail(self): - def assign_bad_index(arr): - arr[4] = 1.0 - - def assign_bad_type(arr): - arr[1]= "123" - - for arr in [test.farr, test.iarr, test.barr]: - self.assertRaises(IndexError, assign_bad_index, arr) - - # not testing bool because bool allows not only (True|False) - for arr in [test.farr, test.iarr]: - self.assertRaises(TypeError, assign_bad_type, arr) - - def test_dynamic_assign_array(self): - # test various lengths here - for arr, rand_func in zip(("fdarr", "idarr", "bdarr"), (rand_float, rand_int, rand_bool)): - for length in range(1, 64): - rval= make_random_array(length, rand_func) - setattr(test, arr, rval) - self.assertEqual(prop_to_list(getattr(test, arr)), rval) - - def test_dynamic_assign_array_fail(self): - # could also test too big length here - - def assign_empty_list(arr): - setattr(test, arr, ()) - - for arr in ("fdarr", "idarr", "bdarr"): - self.assertRaises(ValueError, assign_empty_list, arr) - - -class TestMArray(unittest.TestCase): - def setUp(self): - # reset dynamic array sizes - for arr, func in zip(("fdmarr", "idmarr", "bdmarr"), (rand_float, rand_int, rand_bool)): - setattr(test, arr, make_random_3d_array((3, 4, 5), func)) - - # test assignment - def test_assign_array(self): - for arr, func in zip(("fmarr", "imarr", "bmarr"), (rand_float, rand_int, rand_bool)): - # assignment of [3][4][5] - rval= make_random_3d_array((3, 4, 5), func) - setattr(test, arr, rval) - self.assertEqual(prop_to_list(getattr(test, arr)), rval) - - # test assignment of [2][4][5], [1][4][5] should work on dynamic arrays - - def test_assign_array_fail(self): - def assign_empty_array(): - test.fmarr= () - self.assertRaises(ValueError, assign_empty_array) - - def assign_invalid_size(arr, rval): - setattr(test, arr, rval) - - # assignment of 3,4,4 or 3,3,5 should raise ex - for arr, func in zip(("fmarr", "imarr", "bmarr"), (rand_float, rand_int, rand_bool)): - rval= make_random_3d_array((3, 4, 4), func) - self.assertRaises(ValueError, assign_invalid_size, arr, rval) - - rval= make_random_3d_array((3, 3, 5), func) - self.assertRaises(ValueError, assign_invalid_size, arr, rval) - - rval= make_random_3d_array((3, 3, 3), func) - self.assertRaises(ValueError, assign_invalid_size, arr, rval) - - def test_assign_item(self): - # arr[i] = x - for arr, func in zip(("fmarr", "imarr", "bmarr", "fdmarr", "idmarr", "bdmarr"), (rand_float, rand_int, rand_bool) * 2): - rval= make_random_2d_array((4, 5), func) - - for i in range(3): - getattr(test, arr)[i]= rval - self.assertEqual(prop_to_list(getattr(test, arr)[i]), rval) - - # arr[i][j] = x - for arr, func in zip(("fmarr", "imarr", "bmarr", "fdmarr", "idmarr", "bdmarr"), (rand_float, rand_int, rand_bool) * 2): - - arr= getattr(test, arr) - rval= make_random_array(5, func) - - for i in range(3): - for j in range(4): - arr[i][j]= rval - self.assertEqual(prop_to_list(arr[i][j]), rval) - - - def test_assign_item_fail(self): - def assign_wrong_size(arr, i, rval): - getattr(test, arr)[i]= rval - - # assign wrong size at level 2 - for arr, func in zip(("fmarr", "imarr", "bmarr"), (rand_float, rand_int, rand_bool)): - rval1= make_random_2d_array((3, 5), func) - rval2= make_random_2d_array((4, 3), func) - - for i in range(3): - self.assertRaises(ValueError, assign_wrong_size, arr, i, rval1) - self.assertRaises(ValueError, assign_wrong_size, arr, i, rval2) - - def test_dynamic_assign_array(self): - for arr, func in zip(("fdmarr", "idmarr", "bdmarr"), (rand_float, rand_int, rand_bool)): - # assignment of [3][4][5] - rval= make_random_3d_array((3, 4, 5), func) - setattr(test, arr, rval) - self.assertEqual(prop_to_list(getattr(test, arr)), rval) - - # [2][4][5] - rval= make_random_3d_array((2, 4, 5), func) - setattr(test, arr, rval) - self.assertEqual(prop_to_list(getattr(test, arr)), rval) - - # [1][4][5] - rval= make_random_3d_array((1, 4, 5), func) - setattr(test, arr, rval) - self.assertEqual(prop_to_list(getattr(test, arr)), rval) - - - # test access - def test_access(self): - pass - - # test slice access, negative indices - def test_access_fail(self): - pass - -random.seed() - -def rand_int(): - return random.randint(-1000, 1000) - -def rand_float(): - return float(rand_int()) - -def rand_bool(): - return bool(random.randint(0, 1)) - -def make_random_array(len, rand_func): - arr= [] - for i in range(len): - arr.append(rand_func()) - - return arr - -def make_random_2d_array(dimsize, rand_func): - marr= [] - for i in range(dimsize[0]): - marr.append([]) - - for j in range(dimsize[1]): - marr[-1].append(rand_func()) - - return marr - -def make_random_3d_array(dimsize, rand_func): - marr= [] - for i in range(dimsize[0]): - marr.append([]) - - for j in range(dimsize[1]): - marr[-1].append([]) - - for k in range(dimsize[2]): - marr[-1][-1].append(rand_func()) - - return marr - -def prop_to_list(prop): - ret= [] - - for x in prop: - if type(x) not in (bool, int, float): - ret.append(prop_to_list(x)) - else: - ret.append(x) - - return ret - -def suite(): - return unittest.TestSuite([unittest.TestLoader().loadTestsFromTestCase(TestArray), unittest.TestLoader().loadTestsFromTestCase(TestMArray)]) - -if __name__ == "__main__": - unittest.TextTestRunner(verbosity=2).run(suite()) - diff --git a/source/tests/rna_info_dump.py b/source/tests/rna_info_dump.py deleted file mode 100644 index 62c1248d733..00000000000 --- a/source/tests/rna_info_dump.py +++ /dev/null @@ -1,131 +0,0 @@ -# ##### 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 ##### - -# <pep8 compliant> - -# Used for generating API diff's between releases -# ./blender.bin --background --python release/test/rna_info_dump.py - -import bpy - - -def api_dump(use_properties=True, use_functions=True): - - def prop_type(prop): - if prop.type == "pointer": - return prop.fixed_type.identifier - else: - return prop.type - - def func_to_str(struct_id_str, func_id, func): - - args = [] - for prop in func.args: - data_str = "%s %s" % (prop_type(prop), prop.identifier) - if prop.array_length: - data_str += "[%d]" % prop.array_length - if not prop.is_required: - data_str += "=%s" % prop.default_str - args.append(data_str) - - data_str = "%s.%s(%s)" % (struct_id_str, func_id, ", ".join(args)) - if func.return_values: - return_args = ", ".join(prop_type(arg) for arg in func.return_values) - if len(func.return_values) > 1: - data_str += " --> (%s)" % return_args - else: - data_str += " --> %s" % return_args - return data_str - - def prop_to_str(struct_id_str, prop_id, prop): - - prop_str = " <-- %s" % prop_type(prop) - if prop.array_length: - prop_str += "[%d]" % prop.array_length - - data_str = "%s.%s %s" % (struct_id_str, prop_id, prop_str) - return data_str - - def struct_full_id(v): - struct_id_str = v.identifier # "".join(sid for sid in struct_id if struct_id) - - for base in v.get_bases(): - struct_id_str = base.identifier + "|" + struct_id_str - - return struct_id_str - - def dump_funcs(): - data = [] - for struct_id, v in sorted(struct.items()): - struct_id_str = struct_full_id(v) - - funcs = [(func.identifier, func) for func in v.functions] - - for func_id, func in funcs: - data.append(func_to_str(struct_id_str, func_id, func)) - - for prop in v.properties: - if prop.collection_type: - funcs = [(prop.identifier + "." + func.identifier, func) for func in prop.collection_type.functions] - for func_id, func in funcs: - data.append(func_to_str(struct_id_str, func_id, func)) - data.sort() - data.append("# * functions *") - return data - - def dump_props(): - data = [] - for struct_id, v in sorted(struct.items()): - struct_id_str = struct_full_id(v) - - props = [(prop.identifier, prop) for prop in v.properties] - - for prop_id, prop in props: - data.append(prop_to_str(struct_id_str, prop_id, prop)) - - for prop in v.properties: - if prop.collection_type: - props = [(prop.identifier + "." + prop_sub.identifier, prop_sub) for prop_sub in prop.collection_type.properties] - for prop_sub_id, prop_sub in props: - data.append(prop_to_str(struct_id_str, prop_sub_id, prop_sub)) - data.sort() - data.insert(0, "# * properties *") - return data - - import rna_info - struct = rna_info.BuildRNAInfo()[0] - data = [] - - if use_functions: - data.extend(dump_funcs()) - - if use_properties: - data.extend(dump_props()) - - if bpy.app.background: - import sys - sys.stderr.write("\n".join(data)) - sys.stderr.write("\n\nEOF\n") - else: - text = bpy.data.texts.new(name="api.py") - text.from_string(data) - - print("END") - -if __name__ == "__main__": - api_dump() |