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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--extern/CMakeLists.txt2
-rw-r--r--extern/SConscript1
-rw-r--r--extern/ptex/CMakeLists.txt20
-rw-r--r--extern/ptex/SConscript15
-rw-r--r--extern/ptex/ptex.h52
-rw-r--r--extern/ptex/ptex_C_api.cpp184
-rw-r--r--extern/ptex/src/Makefile13
-rw-r--r--extern/ptex/src/ptex/Makefile89
-rw-r--r--extern/ptex/src/ptex/Makefile.deps25
-rw-r--r--extern/ptex/src/ptex/PtexCache.cpp419
-rw-r--r--extern/ptex/src/ptex/PtexCache.h300
-rw-r--r--extern/ptex/src/ptex/PtexDict.h595
-rw-r--r--extern/ptex/src/ptex/PtexFilters.cpp455
-rw-r--r--extern/ptex/src/ptex/PtexHalf.cpp105
-rw-r--r--extern/ptex/src/ptex/PtexHalf.h118
-rw-r--r--extern/ptex/src/ptex/PtexHashMap.h352
-rw-r--r--extern/ptex/src/ptex/PtexIO.h119
-rw-r--r--extern/ptex/src/ptex/PtexInt.h57
-rw-r--r--extern/ptex/src/ptex/PtexMutex.h79
-rw-r--r--extern/ptex/src/ptex/PtexPlatform.h160
-rw-r--r--extern/ptex/src/ptex/PtexReader.cpp1357
-rw-r--r--extern/ptex/src/ptex/PtexReader.h613
-rw-r--r--extern/ptex/src/ptex/PtexSeparableFilter.cpp389
-rw-r--r--extern/ptex/src/ptex/PtexSeparableFilter.h80
-rw-r--r--extern/ptex/src/ptex/PtexSeparableKernel.cpp149
-rw-r--r--extern/ptex/src/ptex/PtexSeparableKernel.h473
-rw-r--r--extern/ptex/src/ptex/PtexTriangleFilter.cpp261
-rw-r--r--extern/ptex/src/ptex/PtexTriangleFilter.h78
-rw-r--r--extern/ptex/src/ptex/PtexTriangleKernel.cpp179
-rw-r--r--extern/ptex/src/ptex/PtexTriangleKernel.h227
-rw-r--r--extern/ptex/src/ptex/PtexUtils.cpp677
-rw-r--r--extern/ptex/src/ptex/PtexUtils.h199
-rw-r--r--extern/ptex/src/ptex/PtexWriter.cpp1400
-rw-r--r--extern/ptex/src/ptex/PtexWriter.h193
-rw-r--r--extern/ptex/src/ptex/Ptexture.h984
-rw-r--r--release/scripts/ui/properties_data_mesh.py25
-rw-r--r--release/scripts/ui/properties_data_modifier.py5
-rw-r--r--release/scripts/ui/properties_texture.py1
-rw-r--r--release/scripts/ui/space_view3d.py23
-rw-r--r--release/scripts/ui/space_view3d_toolbar.py150
-rw-r--r--release/windows/installer/01.installer.bmpbin154542 -> 0 bytes
-rw-r--r--source/blender/blenkernel/BKE_DerivedMesh.h34
-rw-r--r--source/blender/blenkernel/BKE_customdata.h82
-rw-r--r--source/blender/blenkernel/BKE_dmgrid.h59
-rw-r--r--source/blender/blenkernel/BKE_multires.h5
-rw-r--r--source/blender/blenkernel/BKE_paint.h38
-rw-r--r--source/blender/blenkernel/BKE_ptex.h42
-rw-r--r--source/blender/blenkernel/BKE_subsurf.h3
-rw-r--r--source/blender/blenkernel/BKE_texture.h3
-rw-r--r--source/blender/blenkernel/CMakeLists.txt1
-rw-r--r--source/blender/blenkernel/SConscript1
-rw-r--r--source/blender/blenkernel/intern/CCGSubSurf.c509
-rw-r--r--source/blender/blenkernel/intern/CCGSubSurf.h11
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.c30
-rw-r--r--source/blender/blenkernel/intern/cdderivedmesh.c118
-rw-r--r--source/blender/blenkernel/intern/customdata.c400
-rw-r--r--source/blender/blenkernel/intern/multires.c502
-rw-r--r--source/blender/blenkernel/intern/object.c27
-rw-r--r--source/blender/blenkernel/intern/paint.c109
-rw-r--r--source/blender/blenkernel/intern/ptex.c445
-rw-r--r--source/blender/blenkernel/intern/shrinkwrap.c2
-rw-r--r--source/blender/blenkernel/intern/subsurf_ccg.c588
-rw-r--r--source/blender/blenkernel/intern/texture.c19
-rw-r--r--source/blender/blenlib/BLI_ghash.h2
-rw-r--r--source/blender/blenlib/BLI_math_geom.h1
-rw-r--r--source/blender/blenlib/BLI_pbvh.h136
-rw-r--r--source/blender/blenlib/intern/pbvh.c486
-rw-r--r--source/blender/blenloader/CMakeLists.txt1
-rw-r--r--source/blender/blenloader/SConscript1
-rw-r--r--source/blender/blenloader/intern/readfile.c57
-rw-r--r--source/blender/blenloader/intern/writefile.c49
-rw-r--r--source/blender/editors/include/ED_mesh.h7
-rw-r--r--source/blender/editors/include/ED_sculpt.h23
-rw-r--r--source/blender/editors/interface/interface_templates.c5
-rw-r--r--source/blender/editors/mesh/mesh_data.c82
-rw-r--r--source/blender/editors/mesh/mesh_intern.h1
-rw-r--r--source/blender/editors/mesh/mesh_ops.c1
-rw-r--r--source/blender/editors/object/object_intern.h1
-rw-r--r--source/blender/editors/object/object_modifier.c41
-rw-r--r--source/blender/editors/object/object_ops.c1
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt1
-rw-r--r--source/blender/editors/sculpt_paint/SConscript1
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h169
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c555
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c67
-rw-r--r--source/blender/editors/sculpt_paint/paint_overlay.c427
-rw-r--r--source/blender/editors/sculpt_paint/paint_ptex.c1414
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c609
-rw-r--r--source/blender/editors/sculpt_paint/paint_undo.c244
-rw-r--r--source/blender/editors/sculpt_paint/paint_utils.c331
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c588
-rw-r--r--source/blender/editors/sculpt_paint/pbvh_undo.c570
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c1855
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h69
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c302
-rw-r--r--source/blender/editors/space_view3d/drawmesh.c2
-rw-r--r--source/blender/editors/space_view3d/drawobject.c93
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c44
-rw-r--r--source/blender/editors/space_view3d/view3d_header.c2
-rw-r--r--source/blender/editors/util/undo.c2
-rw-r--r--source/blender/gpu/CMakeLists.txt1
-rw-r--r--source/blender/gpu/GPU_buffers.h49
-rw-r--r--source/blender/gpu/SConscript1
-rw-r--r--source/blender/gpu/intern/gpu_buffers.c1013
-rw-r--r--source/blender/makesdna/DNA_brush_types.h43
-rw-r--r--source/blender/makesdna/DNA_customdata_types.h33
-rw-r--r--source/blender/makesdna/DNA_mesh_types.h8
-rw-r--r--source/blender/makesdna/DNA_meshdata_types.h27
-rw-r--r--source/blender/makesdna/DNA_object_types.h4
-rw-r--r--source/blender/makesdna/DNA_scene_types.h74
-rw-r--r--source/blender/makesrna/RNA_access.h2
-rw-r--r--source/blender/makesrna/intern/rna_brush.c32
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c345
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c4
-rw-r--r--source/blender/makesrna/intern/rna_scene.c49
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c108
-rw-r--r--source/blender/modifiers/intern/MOD_multires.c10
-rw-r--r--source/blender/modifiers/intern/MOD_subsurf.c4
-rw-r--r--source/blender/modifiers/intern/MOD_util.c18
-rw-r--r--source/blender/modifiers/intern/MOD_util.h1
-rw-r--r--source/blender/modifiers/intern/MOD_wave.c1
-rw-r--r--source/blender/windowmanager/intern/wm_files.c2
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c1
-rw-r--r--source/creator/CMakeLists.txt2
124 files changed, 20393 insertions, 3330 deletions
diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt
index b15c8a31c73..7d4b03f3294 100644
--- a/extern/CMakeLists.txt
+++ b/extern/CMakeLists.txt
@@ -45,3 +45,5 @@ ENDIF(WITH_LZO)
IF(WITH_LZMA)
ADD_SUBDIRECTORY(lzma)
ENDIF(WITH_LZMA)
+
+ADD_SUBDIRECTORY(ptex) \ No newline at end of file
diff --git a/extern/SConscript b/extern/SConscript
index 22c7dc23dce..c2a2ead0982 100644
--- a/extern/SConscript
+++ b/extern/SConscript
@@ -3,6 +3,7 @@
Import('env')
SConscript(['glew/SConscript'])
+SConscript(['ptex/SConscript'])
if env['WITH_BF_BULLET']:
SConscript(['bullet2/src/SConscript'])
diff --git a/extern/ptex/CMakeLists.txt b/extern/ptex/CMakeLists.txt
new file mode 100644
index 00000000000..e6a21fa869e
--- /dev/null
+++ b/extern/ptex/CMakeLists.txt
@@ -0,0 +1,20 @@
+SET(INC ${ZLIB_INC} src .)
+
+SET(SRC
+ src/ptex/PtexCache.cpp
+ src/ptex/PtexFilters.cpp
+ src/ptex/PtexHalf.cpp
+ src/ptex/PtexReader.cpp
+ src/ptex/PtexSeparableFilter.cpp
+ src/ptex/PtexSeparableKernel.cpp
+ src/ptex/PtexTriangleFilter.cpp
+ src/ptex/PtexTriangleKernel.cpp
+ src/ptex/PtexUtils.cpp
+ src/ptex/PtexWriter.cpp
+ ptex_C_api.cpp
+)
+
+ADD_DEFINITIONS(-DPTEX_STATIC)
+
+BLENDERLIB(extern_ptex "${SRC}" "${INC}")
+
diff --git a/extern/ptex/SConscript b/extern/ptex/SConscript
new file mode 100644
index 00000000000..b189b5dd1fd
--- /dev/null
+++ b/extern/ptex/SConscript
@@ -0,0 +1,15 @@
+#!/usr/bin/python
+import sys
+import os
+
+Import('env')
+
+sources = ['src/glew.c']
+sources = env.Glob('src/ptex/*.cpp')
+sources += env.Glob('*.cpp')
+
+defs = 'PTEX_STATIC'
+incs = 'src/ptex'
+incs += ' ' + env['BF_ZLIB_INC']
+
+env.BlenderLib ( 'extern_ptex', sources, Split(incs), Split(defs), libtype=['extern','player'], priority=[50,230])
diff --git a/extern/ptex/ptex.h b/extern/ptex/ptex.h
new file mode 100644
index 00000000000..2fd7883093d
--- /dev/null
+++ b/extern/ptex/ptex.h
@@ -0,0 +1,52 @@
+#ifndef PTEX_H
+#define PTEX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct PtexTextureHandle PtexTextureHandle;
+typedef struct PtexFaceInfoHandle PtexFaceInfoHandle;
+typedef struct PtexResHandle PtexResHandle;
+typedef struct PtexWriterHandle PtexWriterHandle;
+
+typedef enum {
+ PTEX_DT_UINT8,
+ PTEX_DT_UINT16,
+ PTEX_DT_FLOAT,
+ PTEX_DT_UNSUPPORTED
+} PtexDataType;
+
+/* PtexTexture class */
+extern PtexTextureHandle *ptex_open(const char *path, int print_error, int premultiply);
+extern void ptex_texture_release(PtexTextureHandle *ptex_texture_handle);
+extern PtexDataType ptex_texture_data_type(PtexTextureHandle *ptex_texture_handle);
+extern int ptex_texture_num_channels(PtexTextureHandle *ptex_texture_handle);
+extern PtexFaceInfoHandle *ptex_texture_get_face_info(PtexTextureHandle* ptex_texture_handle, int faceid);
+extern void ptex_texture_get_data(PtexTextureHandle *ptex_texture_handle, int faceid, void *buffer, int stride, PtexResHandle *res_handle);
+extern void ptex_texture_get_pixel(PtexTextureHandle *ptex_texture_handle, int faceid, int u, int v, float *result, int firstchan, int nchannels, PtexResHandle res_handle);
+
+/* FaceInfo struct */
+PtexFaceInfoHandle *ptex_face_info_new(int u, int v, int adjfaces[4], int adjedges[4], int isSubface);
+extern PtexResHandle *ptex_face_info_get_res(PtexFaceInfoHandle *face_info_handle);
+extern int ptex_face_info_is_subface(PtexFaceInfoHandle *face_info_handle);
+
+/* Res struct */
+extern int ptex_res_u(PtexResHandle *ptex_res_handle);
+extern int ptex_res_v(PtexResHandle *ptex_res_handle);
+
+/* PtexWriter class */
+extern PtexWriterHandle *ptex_writer_open(const char *path, PtexDataType dt, int nchannels, int alphachan, int nfaces, int genmipmaps);
+extern void ptex_writer_write_face(PtexWriterHandle *ptex_writer_handle, int faceid, PtexFaceInfoHandle *info, const void *data, int stride);
+extern void ptex_writer_release(PtexWriterHandle *ptex_writer_handle);
+
+/* Utils */
+int ptex_data_size(PtexDataType type);
+int ptex_res_to_log2(int res);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/extern/ptex/ptex_C_api.cpp b/extern/ptex/ptex_C_api.cpp
new file mode 100644
index 00000000000..743401cee48
--- /dev/null
+++ b/extern/ptex/ptex_C_api.cpp
@@ -0,0 +1,184 @@
+#include "ptex.h"
+#include "src/ptex/Ptexture.h"
+#include <iostream>
+
+/**** PtexTexture class ****/
+PtexTextureHandle *ptex_open(const char *path, int print_error, int premultiply)
+{
+ PtexTextureHandle *ptex_texture_handle;
+
+ Ptex::String error_string;
+
+ ptex_texture_handle = (PtexTextureHandle*)PtexTexture::open(path, error_string, premultiply);
+
+ if(!ptex_texture_handle && print_error)
+ std::cout << "Ptex error: " << error_string << std::endl;
+
+ return ptex_texture_handle;
+}
+
+void ptex_texture_release(PtexTextureHandle *ptex_texture_handle)
+{
+ ((PtexTexture*)ptex_texture_handle)->release();
+}
+
+PtexDataType ptex_texture_data_type(PtexTextureHandle *ptex_texture_handle)
+{
+ Ptex::DataType type = ((PtexTexture*)ptex_texture_handle)->dataType();
+
+ switch(type) {
+ case Ptex::dt_uint8:
+ return PTEX_DT_UINT8;
+ case Ptex::dt_uint16:
+ return PTEX_DT_UINT16;
+ case Ptex::dt_float:
+ return PTEX_DT_FLOAT;
+ default:
+ return PTEX_DT_UNSUPPORTED;
+ }
+}
+
+int ptex_texture_num_channels(PtexTextureHandle *ptex_texture_handle)
+{
+ return ((PtexTexture*)ptex_texture_handle)->numChannels();
+}
+
+PtexFaceInfoHandle *ptex_texture_get_face_info(PtexTextureHandle *ptex_texture_handle, int faceid)
+{
+ return (PtexFaceInfoHandle*)(&((PtexTexture*)ptex_texture_handle)->getFaceInfo(faceid));
+}
+
+void ptex_texture_get_data(PtexTextureHandle *ptex_texture_handle, int faceid, void *buffer, int stride, PtexResHandle *res_handle)
+{
+ ((PtexTexture*)ptex_texture_handle)->getData(faceid, buffer, stride, *((Ptex::Res*)res_handle));
+}
+
+void ptex_texture_get_pixel(PtexTextureHandle *ptex_texture_handle, int faceid, int u, int v, float *result, int firstchan, int nchannels, PtexResHandle *res_handle)
+{
+ ((PtexTexture*)ptex_texture_handle)->getPixel(faceid, u, v, result, firstchan, nchannels, *((Ptex::Res*)res_handle));
+}
+
+
+
+/**** FaceInfo struct ****/
+PtexFaceInfoHandle *ptex_face_info_new(int u, int v, int adjfaces[4], int adjedges[4], int isSubface)
+{
+ int ulog2 = ptex_res_to_log2(u);
+ int vlog2 = ptex_res_to_log2(v);
+
+ return (PtexFaceInfoHandle*)(new Ptex::FaceInfo(Ptex::Res(ulog2, vlog2), adjfaces, adjedges, isSubface));
+}
+
+PtexResHandle *ptex_face_info_get_res(PtexFaceInfoHandle *face_info_handle)
+{
+ return (PtexResHandle*)(&((Ptex::FaceInfo*)face_info_handle)->res);
+}
+
+int ptex_face_info_is_subface(PtexFaceInfoHandle *face_info_handle)
+{
+ return ((Ptex::FaceInfo*)face_info_handle)->isSubface();
+}
+
+
+
+/**** Res struct ****/
+int ptex_res_u(PtexResHandle *ptex_res_handle)
+{
+ return ((Ptex::Res*)ptex_res_handle)->u();
+}
+
+int ptex_res_v(PtexResHandle *ptex_res_handle)
+{
+ return ((Ptex::Res*)ptex_res_handle)->v();
+}
+
+
+
+/**** PtexWriter class ****/
+PtexWriterHandle *ptex_writer_open(const char *path, PtexDataType dt, int nchannels, int alphachan, int nfaces, int genmipmaps)
+{
+ Ptex::DataType ptex_data_type;
+ Ptex::String error;
+
+ switch(dt) {
+ case PTEX_DT_UINT8:
+ ptex_data_type = Ptex::dt_uint8;
+ break;
+ case PTEX_DT_UINT16:
+ ptex_data_type = Ptex::dt_uint16;
+ break;
+ case PTEX_DT_FLOAT:
+ ptex_data_type = Ptex::dt_float;
+ break;
+ default:
+ return NULL;
+ }
+
+ return (PtexWriterHandle*)PtexWriter::open(path, Ptex::mt_quad, ptex_data_type, nchannels, alphachan, nfaces, error, genmipmaps);
+}
+
+void ptex_writer_write_face(PtexWriterHandle *ptex_writer_handle, int faceid, PtexFaceInfoHandle *info, const void *data, int stride)
+{
+ ((PtexWriter*)ptex_writer_handle)->writeFace(faceid, *(Ptex::FaceInfo*)info, data, stride);
+}
+
+void ptex_writer_release(PtexWriterHandle *ptex_writer_handle)
+{
+ ((PtexWriter*)ptex_writer_handle)->release();
+}
+
+
+
+/**** Utils ****/
+int ptex_data_size(PtexDataType type)
+{
+ switch(type) {
+ case PTEX_DT_UINT8:
+ return 1;
+ case PTEX_DT_UINT16:
+ return 2;
+ case PTEX_DT_FLOAT:
+ return 4;
+ default:
+ return 0;
+ }
+}
+
+int ptex_res_to_log2(int res)
+{
+ switch(res) {
+ case (1<<0): return 0;
+ case (1<<1): return 1;
+ case (1<<2): return 2;
+ case (1<<3): return 3;
+ case (1<<4): return 4;
+ case (1<<5): return 5;
+ case (1<<6): return 6;
+ case (1<<7): return 7;
+ case (1<<8): return 8;
+ case (1<<9): return 9;
+ case (1<<10): return 10;
+ case (1<<11): return 11;
+ case (1<<12): return 12;
+ case (1<<13): return 13;
+ case (1<<14): return 14;
+ case (1<<15): return 15;
+ case (1<<16): return 16;
+ case (1<<17): return 17;
+ case (1<<18): return 18;
+ case (1<<19): return 19;
+ case (1<<20): return 20;
+ case (1<<21): return 21;
+ case (1<<22): return 22;
+ case (1<<23): return 23;
+ case (1<<24): return 24;
+ case (1<<25): return 25;
+ case (1<<26): return 26;
+ case (1<<27): return 27;
+ case (1<<28): return 28;
+ case (1<<29): return 29;
+ case (1<<30): return 30;
+ case (1<<31): return 31;
+ default: return 0;
+ }
+}
diff --git a/extern/ptex/src/Makefile b/extern/ptex/src/Makefile
new file mode 100644
index 00000000000..7d6de6565c1
--- /dev/null
+++ b/extern/ptex/src/Makefile
@@ -0,0 +1,13 @@
+SUBDIRS = ptex utils tests
+
+.PHONY: subdirs $(SUBDIRS)
+
+subdirs: $(SUBDIRS)
+
+clean:
+ for s in $(SUBDIRS); do \
+ $(MAKE) -C $$s clean; \
+ done
+
+$(SUBDIRS):
+ $(MAKE) -C $@
diff --git a/extern/ptex/src/ptex/Makefile b/extern/ptex/src/ptex/Makefile
new file mode 100644
index 00000000000..97a2cde67a6
--- /dev/null
+++ b/extern/ptex/src/ptex/Makefile
@@ -0,0 +1,89 @@
+# use compiler from CXX env var if present, otherwise default to g++
+ifndef CXX
+CXX = g++
+LINK = g++
+else
+LINK = $(CXX)
+endif
+
+# DEBUG = -g -DDEBUG
+DEBUG = -O2 -DNDEBUG
+INCPATH = -I.
+DEFINES =
+
+ifdef PRMAN_15_COMPATIBLE_PTEX
+DEFINES += -DPTEX_NO_LARGE_METADATA_BLOCKS
+endif
+
+ifdef PTEX_STATIC
+DEFINES += -DPTEX_STATIC
+endif
+
+CXXFLAGS = -Wall -pedantic -W -std=c++98 $(DEBUG) $(INCPATH) $(DEFINES) -fPIC
+LFLAGS =
+LIBS = -lm -lz -lpthread
+
+SRCS = \
+ PtexCache.cpp \
+ PtexFilters.cpp \
+ PtexHalf.cpp \
+ PtexReader.cpp \
+ PtexSeparableFilter.cpp \
+ PtexSeparableKernel.cpp \
+ PtexTriangleFilter.cpp \
+ PtexTriangleKernel.cpp \
+ PtexUtils.cpp \
+ PtexWriter.cpp
+
+OBJECTS = $(patsubst %.cpp,%.o,$(SRCS))
+
+INSTALLDIR = ../../install
+
+INSTALL = \
+ lib/libPtex.a \
+ include/Ptexture.h \
+ include/PtexHalf.h \
+ include/PtexInt.h \
+ include/PtexUtils.h
+
+ALL = libPtex.a
+
+ifndef PTEX_STATIC
+ALL += libPtex.so
+INSTALL += lib/libPtex.so
+endif
+
+$(INSTALLDIR)/lib/% : %
+ @ mkdir -p $(@D)
+ cp $^ $@
+
+$(INSTALLDIR)/bin/% : %
+ @ mkdir -p $(@D)
+ cp $^ $@
+
+$(INSTALLDIR)/include/% : %
+ @ mkdir -p $(@D)
+ cp $^ $@
+
+install: all $(patsubst %,$(INSTALLDIR)/%,$(INSTALL))
+
+all: $(ALL)
+
+clean:
+ rm -f *.o $(ALL)
+
+ifdef PTEX_STATIC
+libPtex.a : libPtex.a($(OBJECTS))
+ ar -r $@
+
+else
+libPtex.a : libPtex.a($(OBJECTS))
+
+libPtex.so: $(OBJECTS)
+ $(LINK) $(LFLAGS) -shared -o $@ $(OBJECTS) $(LIBS)
+endif
+
+depend:
+ $(CXX) -MM $(SRCS) > Makefile.deps
+
+include Makefile.deps
diff --git a/extern/ptex/src/ptex/Makefile.deps b/extern/ptex/src/ptex/Makefile.deps
new file mode 100644
index 00000000000..2dbae12219c
--- /dev/null
+++ b/extern/ptex/src/ptex/Makefile.deps
@@ -0,0 +1,25 @@
+PtexCache.o: PtexCache.cpp PtexPlatform.h Ptexture.h PtexInt.h \
+ PtexReader.h PtexIO.h PtexCache.h PtexMutex.h PtexDict.h PtexUtils.h \
+ PtexHashMap.h
+PtexFilters.o: PtexFilters.cpp PtexPlatform.h Ptexture.h PtexInt.h \
+ PtexSeparableFilter.h PtexSeparableKernel.h PtexUtils.h \
+ PtexTriangleFilter.h
+PtexHalf.o: PtexHalf.cpp PtexHalf.h PtexInt.h
+PtexReader.o: PtexReader.cpp PtexPlatform.h Ptexture.h PtexInt.h \
+ PtexUtils.h PtexReader.h PtexIO.h PtexCache.h PtexMutex.h PtexDict.h \
+ PtexHashMap.h
+PtexSeparableFilter.o: PtexSeparableFilter.cpp PtexPlatform.h \
+ PtexSeparableFilter.h Ptexture.h PtexInt.h PtexSeparableKernel.h \
+ PtexUtils.h
+PtexSeparableKernel.o: PtexSeparableKernel.cpp PtexPlatform.h PtexUtils.h \
+ Ptexture.h PtexInt.h PtexHalf.h PtexSeparableKernel.h
+PtexTriangleFilter.o: PtexTriangleFilter.cpp PtexPlatform.h \
+ PtexTriangleFilter.h Ptexture.h PtexInt.h PtexTriangleKernel.h \
+ PtexUtils.h
+PtexTriangleKernel.o: PtexTriangleKernel.cpp PtexPlatform.h PtexUtils.h \
+ Ptexture.h PtexInt.h PtexHalf.h PtexTriangleKernel.h
+PtexUtils.o: PtexUtils.cpp PtexPlatform.h PtexHalf.h PtexInt.h \
+ PtexUtils.h Ptexture.h
+PtexWriter.o: PtexWriter.cpp PtexPlatform.h Ptexture.h PtexInt.h \
+ PtexUtils.h PtexWriter.h PtexIO.h PtexReader.h PtexCache.h PtexMutex.h \
+ PtexDict.h PtexHashMap.h
diff --git a/extern/ptex/src/ptex/PtexCache.cpp b/extern/ptex/src/ptex/PtexCache.cpp
new file mode 100644
index 00000000000..218ce9181a5
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexCache.cpp
@@ -0,0 +1,419 @@
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+/**
+ @file PtexCache.cpp
+ @brief LRU Cache Implementation
+
+ <b> Ownership.</b>
+ The cache owns all files and data. If an object is in use, the
+ cache will not delete it. When it is no longer in use, it may be
+ kept or may be deleted to keep resource usage under the set limits.
+ Deletions are done in lru order.
+
+ <b> Resource Tracking.</b>
+ All objects are created as part of the cache and have a ptr back to
+ the cache. Each object updates the cache's resource total when it is
+ created or deleted. Unused objects are kept in an lru list in the
+ cache. Only objects in the lru list can be deleted.
+
+ <b> Reference Counting.</b>
+ Every object has a ref count to track whether it is being used.
+ But objects don't generally ref their parent or children (otherwise
+ nothing would get freed).
+
+ A data handle must hold onto (and ref) all the objects it is using
+ or may need in the future. E.g. For a non-tiled face, this is just
+ the single face data block. For a tiled face, the file, the tiled
+ face, and the current tile must all be ref'd.
+
+ <b> Parents, Children, and Orphans.</b>
+ Every object must be reachable by some other object, generally the
+ object that created it, i.e. it's parent. Even though the parent
+ doesn't own it's children (the cache does), it must still track
+ them. Parentless objects (i.e. orphans) are not reachable and are
+ not retained in the cache.
+
+ When any object is deleted (file, tiled face, etc.), it must orphan
+ its children. If an orphaned child is not in use, then it is
+ immediately deleted. Otherwise, the child's parent ptr is set to
+ null and the child is deleted when it is no longer in use. A
+ parent may also orphan a child that it no longer needs; the
+ behavior is the same.
+
+ Each object stores a ptr to its own entry within its parent. When
+ the object is deleted by the cache, it clears this pointer so that
+ the parent no longer sees it.
+
+ <b> Cache LifeTime.</b>
+ When a cache is released from its owner, it will delete itself but
+ only after all objects it owns are no longer in use. To do this, a
+ ref count on the cache is used. The owner holds 1 ref (only one
+ owner allowed), and each object holds a ref (maintained internally).
+
+ <b> Threading.</b>
+ To fully support multi-threading, the following data structures
+ must be protected with a mutex: the cache lru lists, ref counts,
+ and parent/child ptrs. This is done with a single mutex per cache.
+ To avoid the need for recursive locks and to minimize the number of
+ lock points, this mutex is locked and unlocked primarily at the
+ external api boundary for methods that affect the cache state:
+ (e.g. getMetaData, getData, getTile, release, purge, and purgeAll).
+ Care must be taken to release the cache lock when calling any external
+ api from within the library.
+
+ Also, in order to prevent thread starvation, the cache lock is
+ released during file reads and significant computation such as
+ generating an image data reduction. Additional mutexes are used to
+ prevent contention in these cases:
+ - 1 mutex per cache to prevent concurrent file opens
+ - 1 mutex per file to prevent concurrent file reads
+ - 1 mutex per file to prevent concurrent (and possibly redundant)
+ reductions.
+ */
+
+#include "PtexPlatform.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <iostream>
+#include <ctype.h>
+#include "Ptexture.h"
+#include "PtexReader.h"
+#include "PtexCache.h"
+
+#ifdef GATHER_STATS
+namespace PtexInternal {
+ CacheStats::~CacheStats() {
+ if (getenv("PTEX_STATS"))
+ print();
+ }
+
+ void CacheStats::print()
+ {
+ if (nfilesOpened || ndataAllocated || nblocksRead) {
+ printf("Ptex Stats:\n");
+ printf(" nfilesOpened: %6d\n", nfilesOpened);
+ printf(" nfilesClosed: %6d\n", nfilesClosed);
+ printf(" ndataAllocated: %6d\n", ndataAllocated);
+ printf(" ndataFreed: %6d\n", ndataFreed);
+ printf(" nblocksRead: %6d\n", nblocksRead);
+ printf(" nseeks: %6d\n", nseeks);
+ if (nblocksRead)
+ printf(" avgReadSize: %6d\n", int(nbytesRead/nblocksRead));
+ if (nseeks)
+ printf(" avgSeqReadSize: %6d\n", int(nbytesRead/nseeks));
+ printf(" MbytesRead: %6.2f\n", nbytesRead/(1024.0*1024.0));
+ }
+ }
+
+ CacheStats stats;
+}
+#endif
+
+PtexCacheImpl::~PtexCacheImpl()
+{
+ // explicitly pop all unused items so that they are
+ // destroyed while cache is still valid
+ AutoLockCache locker(cachelock);
+ while (_unusedData.pop());
+ while (_unusedFiles.pop());
+}
+
+void PtexCacheImpl::setFileInUse(PtexLruItem* file)
+{
+ assert(cachelock.locked());
+ _unusedFiles.extract(file);
+ _unusedFileCount--;
+}
+
+void PtexCacheImpl::setFileUnused(PtexLruItem* file)
+{
+ assert(cachelock.locked());
+ _unusedFiles.push(file);
+ _unusedFileCount++;
+}
+
+void PtexCacheImpl::removeFile()
+{
+ // cachelock should be locked, but might not be if cache is being deleted
+ _unusedFileCount--;
+ STATS_INC(nfilesClosed);
+}
+
+void PtexCacheImpl::setDataInUse(PtexLruItem* data, int size)
+{
+ assert(cachelock.locked());
+ _unusedData.extract(data);
+ _unusedDataCount--;
+ _unusedDataSize -= size;
+}
+
+void PtexCacheImpl::setDataUnused(PtexLruItem* data, int size)
+{
+ assert(cachelock.locked());
+ _unusedData.push(data);
+ _unusedDataCount++;
+ _unusedDataSize += size;
+}
+
+void PtexCacheImpl::removeData(int size) {
+ // cachelock should be locked, but might not be if cache is being deleted
+ _unusedDataCount--;
+ _unusedDataSize -= size;
+ STATS_INC(ndataFreed);
+}
+
+
+/** Cache for reading Ptex texture files */
+class PtexReaderCache : public PtexCacheImpl
+{
+public:
+ PtexReaderCache(int maxFiles, int maxMem, bool premultiply, PtexInputHandler* handler)
+ : PtexCacheImpl(maxFiles, maxMem),
+ _io(handler), _cleanupCount(0), _premultiply(premultiply)
+ {}
+
+ ~PtexReaderCache()
+ {
+ // orphan all files since we're about to delete the file table
+ // and we don't want the base dtor to try to access it
+ purgeAll();
+ }
+
+ virtual void setSearchPath(const char* path)
+ {
+ // get the open lock since the path is used during open operations
+ AutoMutex locker(openlock);
+
+ // record path
+ _searchpath = path ? path : "";
+
+ // split into dirs
+ _searchdirs.clear();
+ char* buff = strdup(path);
+ char* pos = 0;
+ char* token = strtok_r(buff, ":", &pos);
+ while (token) {
+ if (token[0]) _searchdirs.push_back(token);
+ token = strtok_r(0, ":", &pos);
+ }
+ free(buff);
+ }
+
+ virtual const char* getSearchPath()
+ {
+ // get the open lock since the path is used during open operations
+ AutoMutex locker(openlock);
+ return _searchpath.c_str();
+ }
+
+ virtual PtexTexture* get(const char* path, Ptex::String& error);
+
+ virtual void purge(PtexTexture* texture)
+ {
+ PtexReader* reader = dynamic_cast<PtexReader*>(texture);
+ if (!reader) return;
+ purge(reader->path());
+ }
+
+ virtual void purge(const char* filename)
+ {
+ AutoLockCache locker(cachelock);
+ FileMap::iterator iter = _files.find(filename);
+ if (iter != _files.end()) {
+ PtexReader* reader = iter->second;
+ if (reader && intptr_t(reader) != -1) {
+ reader->orphan();
+ iter->second = 0;
+ }
+ _files.erase(iter);
+ }
+ }
+
+ virtual void purgeAll()
+ {
+ AutoLockCache locker(cachelock);
+ FileMap::iterator iter = _files.begin();
+ while (iter != _files.end()) {
+ PtexReader* reader = iter->second;
+ if (reader && intptr_t(reader) != -1) {
+ reader->orphan();
+ iter->second = 0;
+ }
+ iter = _files.erase(iter);
+ }
+ }
+
+
+ void removeBlankEntries()
+ {
+ // remove blank file entries to keep map size in check
+ for (FileMap::iterator i = _files.begin(); i != _files.end();) {
+ if (i->second == 0) i = _files.erase(i);
+ else i++;
+ }
+ }
+
+
+private:
+ PtexInputHandler* _io;
+ std::string _searchpath;
+ std::vector<std::string> _searchdirs;
+ typedef PtexDict<PtexReader*> FileMap;
+ FileMap _files;
+ int _cleanupCount;
+ bool _premultiply;
+};
+
+
+PtexTexture* PtexReaderCache::get(const char* filename, Ptex::String& error)
+{
+ AutoLockCache locker(cachelock);
+
+ // lookup reader in map
+ PtexReader* reader = _files[filename];
+ if (reader) {
+ // -1 means previous open attempt failed
+ if (intptr_t(reader) == -1) return 0;
+ reader->ref();
+ return reader;
+ }
+ else {
+ bool ok = true;
+
+ // get open lock and make sure we still need to open
+ // temporarily release cache lock while we open acquire open lock
+ cachelock.unlock();
+ AutoMutex openlocker(openlock);
+ cachelock.lock();
+
+ // lookup entry again (it might have changed in another thread)
+ PtexReader** entry = &_files[filename];
+
+ if (*entry) {
+ // another thread opened it while we were waiting
+ if (intptr_t(*entry) == -1) return 0;
+ (*entry)->ref();
+ return *entry;
+ }
+
+ // make a new reader
+ reader = new PtexReader((void**)entry, this, _premultiply, _io);
+
+ // temporarily release cache lock while we open the file
+ cachelock.unlock();
+ std::string tmppath;
+ const char* pathToOpen = filename;
+ if (!_io) {
+ bool isAbsolute = (filename[0] == '/'
+#ifdef WINDOWS
+ || filename[0] == '\\'
+ || (isalpha(filename[0]) && filename[1] == ':')
+#endif
+ );
+ if (!isAbsolute && !_searchdirs.empty()) {
+ // file is relative, search in searchpath
+ tmppath.reserve(256); // minimize reallocs (will grow automatically)
+ bool found = false;
+ struct stat statbuf;
+ for (size_t i = 0, size = _searchdirs.size(); i < size; i++) {
+ tmppath = _searchdirs[i];
+ tmppath += "/";
+ tmppath += filename;
+ if (stat(tmppath.c_str(), &statbuf) == 0) {
+ found = true;
+ pathToOpen = tmppath.c_str();
+ break;
+ }
+ }
+ if (!found) {
+ std::string errstr = "Can't find ptex file: ";
+ errstr += filename;
+ error = errstr.c_str();
+ ok = false;
+ }
+ }
+ }
+ if (ok) ok = reader->open(pathToOpen, error);
+
+ // reacquire cache lock
+ cachelock.lock();
+
+ if (!ok) {
+ // open failed, clear parent ptr and unref to delete
+ *entry = reader; // to pass parent check in orphan()
+ reader->orphan();
+ reader->unref();
+ *entry = (PtexReader*)-1; // flag for future lookups
+ return 0;
+ }
+
+ // successful open, record in _files map entry
+ *entry = reader;
+
+ // clean up unused files
+ purgeFiles();
+
+ // Cleanup map every so often so it doesn't get HUGE
+ // from being filled with blank entries from dead files.
+ // Note: this must be done while we still have the open lock!
+ if (++_cleanupCount >= 1000) {
+ _cleanupCount = 0;
+ removeBlankEntries();
+ }
+ }
+ return reader;
+}
+
+PtexCache* PtexCache::create(int maxFiles, int maxMem, bool premultiply,
+ PtexInputHandler* handler)
+{
+ // set default files to 100
+ if (maxFiles <= 0) maxFiles = 100;
+
+ // set default memory to 100 MB
+ const int MB = 1024*1024;
+ if (maxMem <= 0) maxMem = 100 * MB;
+
+ // if memory is < 1 MB, warn
+ if (maxMem < 1 * MB) {
+ std::cerr << "Warning, PtexCache created with < 1 MB" << std::endl;
+ }
+
+ return new PtexReaderCache(maxFiles, maxMem, premultiply, handler);
+}
+
+
diff --git a/extern/ptex/src/ptex/PtexCache.h b/extern/ptex/src/ptex/PtexCache.h
new file mode 100644
index 00000000000..a6ea316a8d1
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexCache.h
@@ -0,0 +1,300 @@
+#ifndef PtexCache_h
+#define PtexCache_h
+
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+#include "PtexPlatform.h"
+#include <assert.h>
+
+#include "PtexMutex.h"
+#include "Ptexture.h"
+#include "PtexDict.h"
+
+#define USE_SPIN // use spinlocks instead of mutex for main cache lock
+
+namespace PtexInternal {
+
+#ifdef USE_SPIN
+ typedef SpinLock CacheLock;
+#else
+ typedef Mutex CacheLock;
+#endif
+ typedef AutoLock<CacheLock> AutoLockCache;
+
+#ifndef NDEBUG
+#define GATHER_STATS
+#endif
+
+#ifdef GATHER_STATS
+ struct CacheStats{
+ int nfilesOpened;
+ int nfilesClosed;
+ int ndataAllocated;
+ int ndataFreed;
+ int nblocksRead;
+ long int nbytesRead;
+ int nseeks;
+
+ CacheStats()
+ : nfilesOpened(0),
+ nfilesClosed(0),
+ ndataAllocated(0),
+ ndataFreed(0),
+ nblocksRead(0),
+ nbytesRead(0),
+ nseeks(0) {}
+
+ ~CacheStats();
+ void print();
+ static void inc(int& val) {
+ static SpinLock spinlock;
+ AutoSpin lock(spinlock);
+ val++;
+ }
+ static void add(long int& val, int inc) {
+ static SpinLock spinlock;
+ AutoSpin lock(spinlock);
+ val+=inc;
+ }
+ };
+ extern CacheStats stats;
+#define STATS_INC(x) stats.inc(stats.x);
+#define STATS_ADD(x, y) stats.add(stats.x, y);
+#else
+#define STATS_INC(x)
+#define STATS_ADD(x, y)
+#endif
+}
+using namespace PtexInternal;
+
+/** One item in a cache, typically an open file or a block of memory */
+class PtexLruItem {
+public:
+ bool inuse() { return _prev == 0; }
+ void orphan()
+ {
+ // parent no longer wants me
+ void** p = _parent;
+ _parent = 0;
+ assert(p && *p == this);
+ if (!inuse()) delete this;
+ *p = 0;
+ }
+ template <typename T> static void orphanList(T& list)
+ {
+ for (typename T::iterator i=list.begin(); i != list.end(); i++) {
+ PtexLruItem* obj = *i;
+ if (obj) {
+ assert(obj->_parent == (void**)&*i);
+ obj->orphan();
+ }
+ }
+ }
+
+protected:
+ PtexLruItem(void** parent=0)
+ : _parent(parent), _prev(0), _next(0) {}
+ virtual ~PtexLruItem()
+ {
+ // detach from parent (if any)
+ if (_parent) { assert(*_parent == this); *_parent = 0; }
+ // unlink from lru list (if in list)
+ if (_prev) {
+ _prev->_next = _next;
+ _next->_prev = _prev;
+ }
+ }
+
+private:
+ friend class PtexLruList; // maintains prev/next, deletes
+ void** _parent; // pointer to this item within parent
+ PtexLruItem* _prev; // prev in lru list (0 if in-use)
+ PtexLruItem* _next; // next in lru list (0 if in-use)
+};
+
+
+
+/** A list of items kept in least-recently-used (LRU) order.
+ Only items not in use are kept in the list. */
+class PtexLruList {
+public:
+ PtexLruList() { _end._prev = _end._next = &_end; }
+ ~PtexLruList() { while (pop()); }
+
+ void extract(PtexLruItem* node)
+ {
+ // remove from list
+ node->_prev->_next = node->_next;
+ node->_next->_prev = node->_prev;
+ node->_next = node->_prev = 0;
+ }
+
+ void push(PtexLruItem* node)
+ {
+ // delete node if orphaned
+ if (!node->_parent) delete node;
+ else {
+ // add to end of list
+ node->_next = &_end;
+ node->_prev = _end._prev;
+ _end._prev->_next = node;
+ _end._prev = node;
+ }
+ }
+
+ bool pop()
+ {
+ if (_end._next == &_end) return 0;
+ delete _end._next; // item will unlink itself
+ return 1;
+ }
+
+private:
+ PtexLruItem _end;
+};
+
+
+/** Ptex cache implementation. Maintains a file and memory cache
+ within set limits */
+class PtexCacheImpl : public PtexCache {
+public:
+ PtexCacheImpl(int maxFiles, int maxMem)
+ : _pendingDelete(false),
+ _maxFiles(maxFiles), _unusedFileCount(0),
+ _maxDataSize(maxMem),
+ _unusedDataSize(0), _unusedDataCount(0)
+ {
+ /* Allow for a minimum number of data blocks so cache doesn't
+ thrash too much if there are any really big items in the
+ cache pushing over the limit. It's better to go over the
+ limit in this case and make sure there's room for at least
+ a modest number of objects in the cache.
+ */
+
+ // try to allow for at least 10 objects per file (up to 100 files)
+ _minDataCount = 10 * maxFiles;
+ // but no more than 1000
+ if (_minDataCount > 1000) _minDataCount = 1000;
+ }
+
+ virtual void release() { delete this; }
+
+ Mutex openlock;
+ CacheLock cachelock;
+
+ // internal use - only call from reader classes for deferred deletion
+ void setPendingDelete() { _pendingDelete = true; }
+ void handlePendingDelete() { if (_pendingDelete) delete this; }
+
+ // internal use - only call from PtexCachedFile, PtexCachedData
+ static void addFile() { STATS_INC(nfilesOpened); }
+ void setFileInUse(PtexLruItem* file);
+ void setFileUnused(PtexLruItem* file);
+ void removeFile();
+ static void addData() { STATS_INC(ndataAllocated); }
+ void setDataInUse(PtexLruItem* data, int size);
+ void setDataUnused(PtexLruItem* data, int size);
+ void removeData(int size);
+
+ void purgeFiles() {
+ while (_unusedFileCount > _maxFiles)
+ {
+ if (!_unusedFiles.pop()) break;
+ // note: pop will destroy item and item destructor will
+ // call removeFile which will decrement _unusedFileCount
+ }
+ }
+ void purgeData() {
+ while ((_unusedDataSize > _maxDataSize) &&
+ (_unusedDataCount > _minDataCount))
+ {
+ if (!_unusedData.pop()) break;
+ // note: pop will destroy item and item destructor will
+ // call removeData which will decrement _unusedDataSize
+ // and _unusedDataCount
+ }
+ }
+
+protected:
+ ~PtexCacheImpl();
+
+private:
+ bool _pendingDelete; // flag set if delete is pending
+
+ int _maxFiles, _unusedFileCount; // file limit, current unused file count
+ long int _maxDataSize, _unusedDataSize; // data limit (bytes), current size
+ int _minDataCount, _unusedDataCount; // min, current # of unused data blocks
+ PtexLruList _unusedFiles, _unusedData; // lists of unused items
+};
+
+
+/** Cache entry for open file handle */
+class PtexCachedFile : public PtexLruItem
+{
+public:
+ PtexCachedFile(void** parent, PtexCacheImpl* cache)
+ : PtexLruItem(parent), _cache(cache), _refcount(1)
+ { _cache->addFile(); }
+ void ref() { assert(_cache->cachelock.locked()); if (!_refcount++) _cache->setFileInUse(this); }
+ void unref() { assert(_cache->cachelock.locked()); if (!--_refcount) _cache->setFileUnused(this); }
+protected:
+ virtual ~PtexCachedFile() { _cache->removeFile(); }
+ PtexCacheImpl* _cache;
+private:
+ int _refcount;
+};
+
+
+/** Cache entry for allocated memory block */
+class PtexCachedData : public PtexLruItem
+{
+public:
+ PtexCachedData(void** parent, PtexCacheImpl* cache, int size)
+ : PtexLruItem(parent), _cache(cache), _refcount(1), _size(size)
+ { _cache->addData(); }
+ void ref() { assert(_cache->cachelock.locked()); if (!_refcount++) _cache->setDataInUse(this, _size); }
+ void unref() { assert(_cache->cachelock.locked()); if (!--_refcount) _cache->setDataUnused(this, _size); }
+protected:
+ void incSize(int size) { _size += size; }
+ virtual ~PtexCachedData() { _cache->removeData(_size); }
+ PtexCacheImpl* _cache;
+private:
+ int _refcount;
+ int _size;
+};
+
+
+#endif
diff --git a/extern/ptex/src/ptex/PtexDict.h b/extern/ptex/src/ptex/PtexDict.h
new file mode 100644
index 00000000000..a0ac85f85d4
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexDict.h
@@ -0,0 +1,595 @@
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+/**
+ @file PtexDict.h
+ @brief Contains PtexDict, a string-keyed hash table.
+*/
+
+#ifndef PtexDict_h
+#define PtexDict_h
+
+/**
+ @class PtexDict
+ @brief A string-keyed dictionary template, using a hash table.
+
+ <P>
+ An efficient string dictionary template. A hash table is used
+ that automatically doubles in size when it is more than 50%
+ full. It is hard-coded to use string keys for efficiency.
+
+ <P>
+ The interface is compatible with std::hash_map<>, though not all methods
+ are provided. Methods provided:
+
+ <UL>
+ <LI>iterator begin();
+ <LI>const_iterator begin() const;
+ <LI>iterator end();
+ <LI>const_iterator end() const;
+ <LI>T& operator[] (const char*);
+ <LI>iterator find(const char* key);
+ <LI>const_iterator find(const char* key) const;
+ <LI>bool erase(const char* key);
+ <LI>iterator erase(iterator);
+ <LI>void clear();
+ <LI>int size() const;
+ </UL>
+
+ <P>
+ Unlike std::hash_map<>, PtexDict doesn't have to construct a string
+ object to do a lookup. As a result, it is about 4-10 times faster
+ depending on the length of the keys. And when compiling
+ non-optimized, it is 6-10 times faster.
+
+ <P>
+ We have decided NOT to follow the coding standard's naming comvention for
+ this class, since it needed to remain generic, and compatible with the
+ STL std::hash_map<> class.
+
+ @author brentb
+ @author longson
+
+ @version <B>1.0 brentb 11/01/2000:</B> Initial version
+ @version <B>1.1 longson 06/26/2001:</B> Added file and class comment headers
+ @version <B>2.0 longson 01/16/2002:</B> Updated to support most of the coding standards, except for the naming conventions. Added const_iterator to provide const-safe access. Fixed problem with iterators and not allowing modification.
+
+*/
+
+/**
+ @struct PtexDict::value_type
+ @brief Internal class used to provide a return value for the value type
+
+ All data members and member functions are declared public by design
+ Default Copy and Assignment operators are sufficient..
+
+ <P>
+ We have decided NOT to follow the coding standard's naming comvention for
+ this class, since it needed to remain compatible with the STL std::pair<>
+ class.
+
+ @author brentb
+ @author longson
+
+ @version <B>1.0 brentb 11/01/2000:</B> Initial version
+ @version <B>1.1 longson 06/26/2001:</B> Added file and class comment headers
+
+*/
+
+template<class T>
+class PtexDict
+{
+ class Entry; ///< forward declared private class
+
+public: // Public Types
+ class iterator; ///< forward declared class
+ class const_iterator; ///< forward declared class
+ friend class iterator;
+ friend class const_iterator;
+
+ typedef const char* key_type; ///< This is the lookup type
+ typedef T mapped_type; ///< The data type stored
+
+ struct value_type {
+ public:
+ /// Default constructor references default_value, with a 0 for first
+ value_type(): first(0), second() {}
+ /// Creation constructor
+ value_type(key_type f, const T& s): first(f), second(s) {}
+
+ const key_type first; ///< Reference to the key type
+ T second; ///< Reference to the data
+ };
+
+public: // Public Member Interfce
+
+ /// Default contructor initializes the dictionary.
+ PtexDict() : _numEntries(0), _numBuckets(0), _bucketMask(0), _buckets(0) {}
+ /// clears the entries in the dictionary
+ virtual ~PtexDict() { clear(); }
+
+ /// Locates an entry, creating a new one if necessary.
+ /** operator[] will look up an entry and return the value. A new entry
+ will be created (using the default ctor for T) if one doesn't exist.
+ */
+ T& operator[](const char* key);
+
+ /// Returns an iterator referencing the beginning of the table
+ iterator begin()
+ {
+ iterator iter;
+ iter._d = this;
+ for (iter._b = 0; iter._b < _numBuckets; iter._b++) {
+ iter._e = &_buckets[iter._b];
+ if (*iter._e) return iter;
+ }
+ iter._e = 0;
+ return iter;
+ }
+
+ /// Returns an iterator referencing the end of the table.
+ inline iterator end() { return iterator( 0, this, 0 ); }
+
+ /// Const access to the beginning of the list
+ const_iterator begin() const
+ {
+ const_iterator iter;
+ iter._d = this;
+ for (iter._b = 0; iter._b < _numBuckets; iter._b++) {
+ iter._e = &_buckets[iter._b];
+ if (*iter._e) return iter;
+ }
+ iter._e = 0;
+ return iter;
+ }
+
+ /// Const access to the end of the list
+ inline const_iterator end() const { return const_iterator( 0, this, 0 ); }
+
+ /// Locates an entry, without creating a new one.
+ /** find will locate an entry, but won't create a new one. The result is
+ returned as a pair of key and value. The returned key points to the
+ internal key string and will remain valid until the entry is deleted.
+ If the key is not found, the returned iterator will be equal to the
+ value returned by end(), and the iterator will be equal to false. O(1)
+ */
+ iterator find(const char* key);
+ /// works the same as find above, but returns a constant iterator.
+ const_iterator find(const char* key) const;
+
+ /// Will remove an entry. It will return TRUE if an entry was found.
+ bool erase(const char* key);
+ /// Removes the entry referenced by the iterator, from the dictionary.
+ /** It will return a iterator to the next element, or will equal the
+ return value of end() if there is nothing else to erase. O(1) */
+ iterator erase(iterator iter);
+
+ /// clear will remove all entries from the dictionary. O(n)
+ void clear();
+
+ /// Returns the number of entries in the dictionary. O(1) time to call.
+ int size() const { return _numEntries; }
+
+private: // Private Member Interface
+
+ /// @brief This internal structure is used to store the dictionary elements
+ struct Entry {
+ public: // Public Member Interface
+ /// Default constructor initiaizes val with the defaul value
+ Entry() : _next(0), _hashval(0), _keylen(0),
+ _val(_key,T()), _pad(0) {}
+ private: // Private Member Interface
+ /// Copy constructor prohibited by design.
+ Entry(const Entry&);
+ /// Assignment operator prohibited by design.
+ Entry& operator=(const Entry&);
+
+ public:
+ Entry* _next; ///< Pointer to the next element in the structure
+ int _hashval; ///< cached hashval of key
+ int _keylen; ///< cached length of key
+ value_type _val; ///< The stored value of the hash table
+ union {
+ int _pad; ///< for integer align of _key, for fast compares
+ char _key[1];///< 1 is dummy length - actual size will be allocated
+ };
+ };
+
+ /// Copy constructor prohibited by design.
+ PtexDict(const PtexDict&);
+ /// Assignment operator prohibited by design.
+ PtexDict& operator=(const PtexDict&);
+
+ /// Returns the integer hash index for the key and length of the key.
+ int hash(const char* key, int& keylen) const
+ {
+ // this is similar to perl's hash function
+ int hashval = 0;
+ const char* cp = key;
+ char c;
+ while ((c = *cp++)) hashval = hashval * 33 + c;
+ keylen = int(cp-key)-1;
+ return hashval;
+ }
+
+ /// Returns a pointer to the desired entry, based on the key.
+ Entry** locate(const char* key, int& keylen, int& hashval) const
+ {
+ hashval = hash(key, keylen);
+ if (!_buckets) return 0;
+ for (Entry** e = &_buckets[hashval & _bucketMask]; *e; e=&(*e)->_next)
+ if ((*e)->_hashval == hashval && (*e)->_keylen == keylen &&
+ streq(key, (*e)->_key, keylen))
+ return e;
+ return 0;
+ }
+
+ /// Used for string compares, much faster then strcmp
+ /** This is MUCH faster than strcmp and even memcmp, partly because
+ it is inline and partly because it can do 4 chars at a time
+ */
+ static inline bool streq(const char* s1, const char* s2, int len)
+ {
+ // first make sure s1 is quad-aligned (s2 is always aligned)
+ if (((intptr_t)s1 & 3) == 0) {
+ int len4 = len >> 2;
+ while (len4--) {
+ if (*(int*)s1 != *(int*)s2) return 0;
+ s1 += 4; s2 += 4;
+ }
+ len &= 3;
+ }
+ while (len--) if (*s1++ != *s2++) return 0;
+ return 1;
+ }
+
+ /// Used to increase the size of the table if necessary
+ void grow();
+
+private: // Private Member data
+
+ int _numEntries; ///< The number of entries in the dictionary
+ int _numBuckets; ///< The number of buckets in use
+ int _bucketMask; ///< The mask for the buckets
+ Entry** _buckets; ///< The pointer to the bucket structure
+};
+
+
+/**
+ @class PtexDict::iterator
+ @brief Internal class used to provide iteration through the dictionary
+
+ This works on non-const types, and provides type safe modification access
+
+ @author brentb
+ @author longson
+
+ @version <B>1.0 brentb 11/01/2000:</B> Initial version
+ @version <B>1.1 longson 06/26/2001:</B> Added file and class comment headers
+ @version <B>1.2 longson 01/16/2002:</B> Made const-safe with const_iterator
+
+*/
+template<class T>
+class PtexDict<T>::iterator {
+public:
+ /// Default Constructor
+ iterator() : _d(0), _e(0), _b(0) {}
+
+ /// Proper copy constructor implementation
+ iterator(const iterator& iter) :
+ _d(iter._d), _e(iter._e), _b(iter._b) {}
+ /// Proper assignment operator
+ inline iterator& operator=(const iterator& iter)
+ { _e = iter._e; _d = iter._d; _b = iter._b; return *this; }
+
+ /// Operator for obtaining the value that the iterator references
+ inline value_type& operator*() const { return getValue(); }
+ /// Pointer reference operator
+ inline value_type* operator->() const { return &getValue(); }
+
+ /// For determining whether or not an iterator is valid
+ inline operator bool() { return _e != 0; }
+
+ /// For comparing equality of iterators
+ inline bool operator==(const iterator& iter) const
+ { return iter._e == _e; }
+ /// For comparing inequality of iterators
+ inline bool operator!=(const iterator& iter) const
+ { return iter._e != _e; }
+ /// For comparing equality of iterators
+ inline bool operator==(const const_iterator& iter) const
+ { return iter._e == _e; }
+ /// For comparing inequality of iterators
+ inline bool operator!=(const const_iterator& iter) const
+ { return iter._e != _e; }
+ /// For advancing the iterator to the next element
+ iterator& operator++(int);
+
+private: // Private interface
+
+ /// Constructor Helper for inline creation.
+ iterator( Entry** e, const PtexDict* d, int b): _d(d), _e(e), _b(b) {}
+
+ /// simple helper function for retrieving the value from the Entry
+ inline value_type& getValue() const{
+ if (_e) return (*_e)->_val;
+ else return _defaultVal;
+ }
+
+ friend class PtexDict;
+ friend class const_iterator;
+ const PtexDict* _d; ///< dictionary back reference
+ Entry** _e; ///< pointer to entry in table this iterator refs
+ int _b; ///< bucket number this references
+
+ static value_type _defaultVal; ///< Default value
+};
+
+// define the static type for the iterator
+template<class T> typename PtexDict<T>::value_type PtexDict<T>::iterator::_defaultVal;
+
+/**
+ @class PtexDict::const_iterator
+ @brief Internal class used to provide iteration through the dictionary
+
+ This works on const data types, and provides const safe access.
+ This class can also be created from a PtexDict::iterator class instance.
+
+ @author longson
+
+ @version <B>1.2 longson 01/16/2002:</B> Initial version based on iterator
+
+*/
+template<class T>
+class PtexDict<T>::const_iterator {
+public:
+ /// Default Constructor
+ const_iterator() : _d(0), _e(0), _b(0) {}
+
+ /// Proper copy constructor implementation for const_iterator
+ const_iterator(const const_iterator& iter) :
+ _d(iter._d), _e(iter._e), _b(iter._b) {}
+ /// Conversion constructor for iterator
+ const_iterator(const iterator& iter) :
+ _d(iter._d), _e(iter._e), _b(iter._b) {}
+ /// Proper assignment operator for const_iterator
+ inline const_iterator& operator=(const const_iterator& iter)
+ { _e = iter._e; _d = iter._d; _b = iter._b; return *this; }
+ /// Proper assignment operator for iterator
+ inline const_iterator& operator=(iterator& iter)
+ { _e = iter._e; _d = iter._d; _b = iter._b; return *this; }
+
+ /// Operator for obtaining the value that the const_iterator references
+ inline const value_type& operator*() const { return getValue(); }
+ /// Pointer reference operator
+ inline const value_type* operator->() const { return &getValue(); }
+
+ /// For determining whether or not an iterator is valid
+ inline operator bool() { return _e != 0; }
+
+ /// For comparing equality of iterators
+ inline bool operator==(const iterator& iter) const
+ { return iter._e == _e; }
+ /// For comparing inequality of iterators
+ inline bool operator!=(const iterator& iter) const
+ { return iter._e != _e; }
+ /// For comparing equality of const_iterators
+ inline bool operator==(const const_iterator& iter) const
+ { return iter._e == _e; }
+ /// For comparing inequality of iterators
+ inline bool operator!=(const const_iterator& iter) const
+ { return iter._e != _e; }
+ /// For advancing the iterator to the next element
+ const_iterator& operator++(int);
+
+private: // Private interface
+
+ /// Constructor Helper for inline creation.
+ const_iterator( Entry** e, const PtexDict* d, int b): _d(d),_e(e),_b(b) {}
+
+ /// simple helper function for retrieving the value from the Entry
+ inline const value_type& getValue() const{
+ if (_e) return (*_e)->_val;
+ else return _defaultVal;
+ }
+
+ friend class PtexDict;
+ friend class iterator;
+ const PtexDict* _d; ///< dictionary back reference
+ Entry** _e; ///< pointer to entry in table this iterator refs
+ int _b; ///< bucket number this references
+
+ static value_type _defaultVal; ///< Default value
+};
+
+// define the static type for the iterator
+template<class T> typename PtexDict<T>::value_type PtexDict<T>::const_iterator::_defaultVal;
+
+template<class T>
+typename PtexDict<T>::iterator& PtexDict<T>::iterator::operator++(int)
+{
+ if (_e) {
+ // move to next entry
+ _e = &(*_e)->_next;
+ if (!*_e) {
+ // move to next non-empty bucket
+ for (_b++; _b < _d->_numBuckets; _b++) {
+ _e = &_d->_buckets[_b];
+ if (*_e) return *this;
+ }
+ _e = 0;
+ }
+ }
+ return *this;
+}
+
+template<class T>
+typename PtexDict<T>::const_iterator& PtexDict<T>::const_iterator::operator++(int)
+{
+ if (_e) {
+ // move to next entry
+ _e = &(*_e)->_next;
+ if (!*_e) {
+ // move to next non-empty bucket
+ for (_b++; _b < _d->_numBuckets; _b++) {
+ _e = &_d->_buckets[_b];
+ if (*_e) return *this;
+ }
+ _e = 0;
+ }
+ }
+ return *this;
+}
+
+template<class T>
+typename PtexDict<T>::iterator PtexDict<T>::find(const char* key)
+{
+ int keylen, hashval;
+ Entry** e = locate(key, keylen, hashval);
+
+ /// return a valid iterator if we found an entry, else return end()
+ if (e) return iterator( e, this, hashval & _bucketMask );
+ else return end();
+}
+
+template<class T>
+typename PtexDict<T>::const_iterator PtexDict<T>::find(const char* key) const
+{
+ int keylen, hashval;
+ Entry** e = locate(key, keylen, hashval);
+
+ /// return a valid iterator if we found an entry, else return end()
+ if (e) return const_iterator( e, this, hashval & _bucketMask );
+ else return end();
+}
+
+template<class T>
+T& PtexDict<T>::operator[](const char* key)
+{
+ int keylen, hashval;
+ Entry** e = locate(key, keylen, hashval);
+ if (e) return (*e)->_val.second;
+
+ // create a new entry
+ _numEntries++;
+ if (_numEntries*2 >= _numBuckets) grow();
+
+ // allocate a buffer big enough to hold Entry + (the key length )
+ // Note: the NULL character is already accounted for by Entry::_key's size
+ void* ebuf = malloc( sizeof(Entry) + (keylen) * sizeof(char) );
+ Entry* ne = new(ebuf) Entry; // note: placement new
+
+ // Store the values in the Entry structure
+ Entry** slot = &_buckets[hashval & _bucketMask];
+ ne->_next = *slot; *slot = ne;
+ ne->_hashval = hashval;
+ ne->_keylen = keylen;
+
+ // copy the string given into the new location
+ memcpy(ne->_key, key, keylen);
+ ne->_key[keylen] = '\0';
+ return ne->_val.second;
+}
+
+
+template<class T>
+void PtexDict<T>::grow()
+{
+ if (!_buckets) {
+ _numBuckets = 16;
+ _bucketMask = _numBuckets - 1;
+ _buckets = (Entry**) calloc(_numBuckets, sizeof(Entry*));
+ } else {
+ int newsize = _numBuckets * 2;
+ _bucketMask = newsize - 1;
+ Entry** newbuckets = (Entry**) calloc(newsize, sizeof(Entry*));
+ for (int i = 0; i < _numBuckets; i++) {
+ for (Entry* e = _buckets[i]; e;) {
+ Entry* _next = e->_next;
+ Entry** slot = &newbuckets[e->_hashval & _bucketMask];
+ e->_next = *slot; *slot = e;
+ e = _next;
+ }
+ }
+ free(_buckets);
+ _buckets = newbuckets;
+ _numBuckets = newsize;
+ }
+}
+
+
+template<class T>
+bool PtexDict<T>::erase(const char* key)
+{
+ iterator iter = find(key);
+ if (!iter) return false;
+
+ erase(iter);
+ return true; // valid entry to remove
+}
+
+
+template<class T>
+typename PtexDict<T>::iterator PtexDict<T>::erase(iterator iter)
+{
+ Entry** eptr = iter._e;
+ if (!eptr) return iter;
+
+ // patch around deleted entry
+ Entry* e = *eptr;
+ Entry* next = e->_next;
+ if (!next) iter++; // advance iterator if at end of chain
+ *eptr = next;
+
+ // destroy entry. This is a strange destroy but is necessary because of
+ // the way Entry() is allocated by using malloc above.
+ e->~Entry(); // note: explicit dtor call
+ free(e); // free memory allocated.
+ _numEntries--;
+
+ return iter;
+}
+
+
+template<class T>
+void PtexDict<T>::clear()
+{
+ for (iterator i=begin(); i != end(); i = erase(i));
+ free(_buckets);
+ _buckets = 0;
+ _numEntries = 0;
+ _numBuckets = 0;
+}
+
+#endif //PtexDict_h
diff --git a/extern/ptex/src/ptex/PtexFilters.cpp b/extern/ptex/src/ptex/PtexFilters.cpp
new file mode 100644
index 00000000000..2ab6526a3cf
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexFilters.cpp
@@ -0,0 +1,455 @@
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+#include "PtexPlatform.h"
+#include "Ptexture.h"
+#include "PtexSeparableFilter.h"
+#include "PtexSeparableKernel.h"
+#include "PtexTriangleFilter.h"
+
+namespace {
+
+/** Point-sampling filter for rectangular textures */
+class PtexPointFilter : public PtexFilter, public Ptex
+{
+ public:
+ PtexPointFilter(PtexTexture* tx) : _tx(tx) {}
+ virtual void release() { delete this; }
+ virtual void eval(float* result, int firstchan, int nchannels,
+ int faceid, float u, float v,
+ float /*uw1*/, float /*vw1*/, float /*uw2*/, float /*vw2*/,
+ float /*width*/, float /*blur*/)
+ {
+ if (!_tx || nchannels <= 0) return;
+ if (faceid < 0 || faceid >= _tx->numFaces()) return;
+ const FaceInfo& f = _tx->getFaceInfo(faceid);
+ int resu = f.res.u(), resv = f.res.v();
+ int ui = PtexUtils::clamp(int(u*resu), 0, resu-1);
+ int vi = PtexUtils::clamp(int(v*resv), 0, resv-1);
+ _tx->getPixel(faceid, ui, vi, result, firstchan, nchannels);
+ }
+
+ private:
+ PtexTexture* _tx;
+};
+
+
+/** Point-sampling filter for triangular textures */
+class PtexPointFilterTri : public PtexFilter, public Ptex
+{
+ public:
+ PtexPointFilterTri(PtexTexture* tx) : _tx(tx) {}
+ virtual void release() { delete this; }
+ virtual void eval(float* result, int firstchan, int nchannels,
+ int faceid, float u, float v,
+ float /*uw1*/, float /*vw1*/, float /*uw2*/, float /*vw2*/,
+ float /*width*/, float /*blur*/)
+ {
+ if (!_tx || nchannels <= 0) return;
+ if (faceid < 0 || faceid >= _tx->numFaces()) return;
+ const FaceInfo& f = _tx->getFaceInfo(faceid);
+ int res = f.res.u();
+ int resm1 = res - 1;
+ float ut = u * res, vt = v * res;
+ int ui = PtexUtils::clamp(int(ut), 0, resm1);
+ int vi = PtexUtils::clamp(int(vt), 0, resm1);
+ float uf = ut - ui, vf = vt - vi;
+
+ if (uf + vf <= 1.0) {
+ // "even" triangles are stored in lower-left half-texture
+ _tx->getPixel(faceid, ui, vi, result, firstchan, nchannels);
+ }
+ else {
+ // "odd" triangles are stored in upper-right half-texture
+ _tx->getPixel(faceid, resm1-vi, resm1-ui, result, firstchan, nchannels);
+ }
+ }
+
+ private:
+ PtexTexture* _tx;
+};
+
+
+/** Separable filter with width=4 support.
+
+ The kernel width is calculated as a multiple of 4 times the filter
+ width and the texture resolution is chosen such that each kernel
+ axis has between 4 and 8.
+
+ For kernel widths too large to handle (because the kernel would
+ extend significantly beyond both sides of the face), a special
+ Hermite smoothstep is used to interpolate the two nearest 2 samples
+ along the affected axis (or axes).
+*/
+class PtexWidth4Filter : public PtexSeparableFilter
+{
+ public:
+ typedef double KernelFn(double x, const double* c);
+
+ PtexWidth4Filter(PtexTexture* tx, const PtexFilter::Options& opts, KernelFn k, const double* c = 0)
+ : PtexSeparableFilter(tx, opts), _k(k), _c(c) {}
+
+ virtual void buildKernel(PtexSeparableKernel& k, float u, float v, float uw, float vw,
+ Res faceRes)
+ {
+ buildKernelAxis(k.res.ulog2, k.u, k.uw, k.ku, u, uw, faceRes.ulog2);
+ buildKernelAxis(k.res.vlog2, k.v, k.vw, k.kv, v, vw, faceRes.vlog2);
+ }
+
+ private:
+
+ double blur(double x)
+ {
+ // 2-unit (x in -1..1) cubic hermite kernel
+ // this produces a blur roughly 1.5 times that of the 4-unit b-spline kernel
+ x = fabs(x);
+ return x < 1 ? (2*x-3)*x*x+1 : 0;
+ }
+
+ void buildKernelAxis(int8_t& k_ureslog2, int& k_u, int& k_uw, double* ku,
+ float u, float uw, int f_ureslog2)
+ {
+ // build 1 axis (note: "u" labels may repesent either u or v axis)
+
+ // clamp filter width to no smaller than a texel
+ uw = PtexUtils::max(uw, 1.0f/(1<<f_ureslog2));
+
+ // compute desired texture res based on filter width
+ k_ureslog2 = int(ceil(log2(1.0/uw)));
+ int resu = 1 << k_ureslog2;
+ double uwlo = 1.0/resu; // smallest filter width for this res
+
+ // compute lerp weights (amount to blend towards next-lower res)
+ double lerp2 = _options.lerp ? (uw-uwlo)/uwlo : 0;
+ double lerp1 = 1-lerp2;
+
+ // adjust for large filter widths
+ if (uw >= .25) {
+ if (uw < .5) {
+ k_ureslog2 = 2;
+ double upix = u * 4 - 0.5;
+ int u1 = int(ceil(upix - 2)), u2 = int(ceil(upix + 2));
+ u1 = u1 & ~1; // round down to even pair
+ u2 = (u2 + 1) & ~1; // round up to even pair
+ k_u = u1;
+ k_uw = u2-u1;
+ double x1 = u1-upix;
+ for (int i = 0; i < k_uw; i+=2) {
+ double xa = x1 + i, xb = xa + 1, xc = (xa+xb)*0.25;
+ // spread the filter gradually to approach the next-lower-res width
+ // at uw = .5, s = 1.0; at uw = 1, s = 0.8
+ double s = 1.0/(uw + .75);
+ double ka = _k(xa, _c), kb = _k(xb, _c), kc = blur(xc*s);
+ ku[i] = ka * lerp1 + kc * lerp2;
+ ku[i+1] = kb * lerp1 + kc * lerp2;
+ }
+ return;
+ }
+ else if (uw < 1) {
+ k_ureslog2 = 1;
+ double upix = u * 2 - 0.5;
+ k_u = int(floor(u - .5))*2;
+ k_uw = 4;
+ double x1 = k_u-upix;
+ for (int i = 0; i < k_uw; i+=2) {
+ double xa = x1 + i, xb = xa + 1, xc = (xa+xb)*0.5;
+ // spread the filter gradually to approach the next-lower-res width
+ // at uw = .5, s = .8; at uw = 1, s = 0.5
+ double s = 1.0/(uw*1.5 + .5);
+ double ka = blur(xa*s), kb = blur(xb*s), kc = blur(xc*s);
+ ku[i] = ka * lerp1 + kc * lerp2;
+ ku[i+1] = kb * lerp1 + kc * lerp2;
+ }
+ return;
+ }
+ else {
+ // use res 0 (1 texel per face) w/ no lerping
+ // (future: use face-blended values for filter > 2)
+ k_ureslog2 = 0;
+ double upix = u - .5;
+ k_uw = 2;
+ double ui = floor(upix);
+ k_u = int(ui);
+ ku[0] = blur(upix-ui);
+ ku[1] = 1-ku[0];
+ return;
+ }
+ }
+
+ // convert from normalized coords to pixel coords
+ double upix = u * resu - 0.5;
+ double uwpix = uw * resu;
+
+ // find integer pixel extent: [u,v] +/- [2*uw,2*vw]
+ // (kernel width is 4 times filter width)
+ double dupix = 2*uwpix;
+ int u1 = int(ceil(upix - dupix)), u2 = int(ceil(upix + dupix));
+
+ if (lerp2) {
+ // lerp kernel weights towards next-lower res
+ // extend kernel width to cover even pairs
+ u1 = u1 & ~1;
+ u2 = (u2 + 1) & ~1;
+ k_u = u1;
+ k_uw = u2-u1;
+
+ // compute kernel weights
+ double step = 1.0/uwpix, x1 = (u1-upix)*step;
+ for (int i = 0; i < k_uw; i+=2) {
+ double xa = x1 + i*step, xb = xa + step, xc = (xa+xb)*0.5;
+ double ka = _k(xa, _c), kb = _k(xb, _c), kc = _k(xc, _c);
+ ku[i] = ka * lerp1 + kc * lerp2;
+ ku[i+1] = kb * lerp1 + kc * lerp2;
+ }
+ }
+ else {
+ k_u = u1;
+ k_uw = u2-u1;
+ // compute kernel weights
+ double x1 = (u1-upix)/uwpix, step = 1.0/uwpix;
+ for (int i = 0; i < k_uw; i++) ku[i] = _k(x1 + i*step, _c);
+ }
+ }
+
+ KernelFn* _k; // kernel function
+ const double* _c; // kernel coefficients (if any)
+};
+
+
+/** Separable bicubic filter */
+class PtexBicubicFilter : public PtexWidth4Filter
+{
+ public:
+ PtexBicubicFilter(PtexTexture* tx, const PtexFilter::Options& opts, float sharpness)
+ : PtexWidth4Filter(tx, opts, kernelFn, _coeffs)
+ {
+ // compute Cubic filter coefficients:
+ // abs(x) < 1:
+ // 1/6 * ((12 - 9*B - 6*C)*x^3 + (-18 + 12*B + 6*C)*x^2 + (6 - 2*B))
+ // == c[0]*x^3 + c[1]*x^2 + c[2]
+ // abs(x) < 2:
+ // 1/6 * ((-B - 6*C)*x^3 + (6*B + 30*C)*x^2 + (-12*B - 48*C)*x + (8*B + 24*C))
+ // == c[3]*x^3 + c[4]*x^2 + c[5]*x + c[6]
+ // else: 0
+
+ float B = 1 - sharpness; // choose C = (1-B)/2
+ _coeffs[0] = 1.5 - B;
+ _coeffs[1] = 1.5 * B - 2.5;
+ _coeffs[2] = 1 - (1./3) * B;
+ _coeffs[3] = (1./3) * B - 0.5;
+ _coeffs[4] = 2.5 - 1.5 * B;
+ _coeffs[5] = 2 * B - 4;
+ _coeffs[6] = 2 - (2./3) * B;
+ }
+
+ private:
+ static double kernelFn(double x, const double* c)
+ {
+ x = fabs(x);
+ if (x < 1) return (c[0]*x + c[1])*x*x + c[2];
+ else if (x < 2) return ((c[3]*x + c[4])*x + c[5])*x + c[6];
+ else return 0;
+ }
+
+ double _coeffs[7]; // filter coefficients for current sharpness
+};
+
+
+
+/** Separable gaussian filter */
+class PtexGaussianFilter : public PtexWidth4Filter
+{
+ public:
+ PtexGaussianFilter(PtexTexture* tx, const PtexFilter::Options& opts)
+ : PtexWidth4Filter(tx, opts, kernelFn) {}
+
+ private:
+ static double kernelFn(double x, const double*)
+ {
+ return exp(-2*x*x);
+ }
+};
+
+
+
+/** Rectangular box filter.
+ The box is convolved with the texels as area samples and thus the kernel function is
+ actually trapezoidally shaped.
+ */
+class PtexBoxFilter : public PtexSeparableFilter
+{
+ public:
+ PtexBoxFilter(PtexTexture* tx, const PtexFilter::Options& opts)
+ : PtexSeparableFilter(tx, opts) {}
+
+ protected:
+ virtual void buildKernel(PtexSeparableKernel& k, float u, float v, float uw, float vw,
+ Res faceRes)
+ {
+ // clamp filter width to no larger than 1.0
+ uw = PtexUtils::min(uw, 1.0f);
+ vw = PtexUtils::min(vw, 1.0f);
+
+ // clamp filter width to no smaller than a texel
+ uw = PtexUtils::max(uw, 1.0f/(faceRes.u()));
+ vw = PtexUtils::max(vw, 1.0f/(faceRes.v()));
+
+ // compute desired texture res based on filter width
+ int ureslog2 = int(ceil(log2(1.0/uw))),
+ vreslog2 = int(ceil(log2(1.0/vw)));
+ Res res(ureslog2, vreslog2);
+ k.res = res;
+
+ // convert from normalized coords to pixel coords
+ u = u * k.res.u();
+ v = v * k.res.v();
+ uw *= k.res.u();
+ vw *= k.res.v();
+
+ // find integer pixel extent: [u,v] +/- [uw/2,vw/2]
+ // (box is 1 unit wide for a 1 unit filter period)
+ double u1 = u - 0.5*uw, u2 = u + 0.5*uw;
+ double v1 = v - 0.5*vw, v2 = v + 0.5*vw;
+ double u1floor = floor(u1), u2ceil = ceil(u2);
+ double v1floor = floor(v1), v2ceil = ceil(v2);
+ k.u = int(u1floor);
+ k.v = int(v1floor);
+ k.uw = int(u2ceil)-k.u;
+ k.vw = int(v2ceil)-k.v;
+
+ // compute kernel weights along u and v directions
+ computeWeights(k.ku, k.uw, 1-(u1-u1floor), 1-(u2ceil-u2));
+ computeWeights(k.kv, k.vw, 1-(v1-v1floor), 1-(v2ceil-v2));
+ }
+
+ private:
+ void computeWeights(double* kernel, int size, double f1, double f2)
+ {
+ assert(size >= 1 && size <= 3);
+
+ if (size == 1) {
+ kernel[0] = f1 + f2 - 1;
+ }
+ else {
+ kernel[0] = f1;
+ for (int i = 1; i < size-1; i++) kernel[i] = 1.0;
+ kernel[size-1] = f2;
+ }
+ }
+};
+
+
+/** Bilinear filter (for rectangular textures) */
+class PtexBilinearFilter : public PtexSeparableFilter
+{
+ public:
+ PtexBilinearFilter(PtexTexture* tx, const PtexFilter::Options& opts)
+ : PtexSeparableFilter(tx, opts) {}
+
+ protected:
+ virtual void buildKernel(PtexSeparableKernel& k, float u, float v, float uw, float vw,
+ Res faceRes)
+ {
+ // clamp filter width to no larger than 1.0
+ uw = PtexUtils::min(uw, 1.0f);
+ vw = PtexUtils::min(vw, 1.0f);
+
+ // clamp filter width to no smaller than a texel
+ uw = PtexUtils::max(uw, 1.0f/(faceRes.u()));
+ vw = PtexUtils::max(vw, 1.0f/(faceRes.v()));
+
+ // choose resolution closest to filter res
+ // there are three choices of "closest" that come to mind:
+ // 1) closest in terms of filter width, i.e. period of signal
+ // 2) closest in terms of texel resolution, (1 / filter width), i.e. freq of signal
+ // 3) closest in terms of resolution level (log2(1/filter width))
+ // Choice (1) probably makes the most sense. In log2 terms, that means you should
+ // use the next higher level when the fractional part of the log2 res is > log2(1/.75),
+ // and you should add 1-log2(1/.75) to round up.
+ const double roundWidth = 0.5849625007211563; // 1-log2(1/.75)
+ int ureslog2 = int(log2(1.0/uw) + roundWidth);
+ int vreslog2 = int(log2(1.0/vw) + roundWidth);
+ Res res(ureslog2, vreslog2);
+ k.res = res;
+
+ // convert from normalized coords to pixel coords
+ double upix = u * k.res.u() - 0.5;
+ double vpix = v * k.res.v() - 0.5;
+
+ float ufloor = floor(upix);
+ float vfloor = floor(vpix);
+ k.u = int(ufloor);
+ k.v = int(vfloor);
+ k.uw = 2;
+ k.vw = 2;
+
+ // compute kernel weights
+ float ufrac = upix-ufloor, vfrac = vpix-vfloor;
+ k.ku[0] = 1 - ufrac;
+ k.ku[1] = ufrac;
+ k.kv[0] = 1 - vfrac;
+ k.kv[1] = vfrac;
+ }
+};
+
+} // end local namespace
+
+
+PtexFilter* PtexFilter::getFilter(PtexTexture* tex, const PtexFilter::Options& opts)
+{
+ switch (tex->meshType()) {
+ case Ptex::mt_quad:
+ switch (opts.filter) {
+ case f_point: return new PtexPointFilter(tex);
+ case f_bilinear: return new PtexBilinearFilter(tex, opts);
+ default:
+ case f_box: return new PtexBoxFilter(tex, opts);
+ case f_gaussian: return new PtexGaussianFilter(tex, opts);
+ case f_bicubic: return new PtexBicubicFilter(tex, opts, opts.sharpness);
+ case f_bspline: return new PtexBicubicFilter(tex, opts, 0.0);
+ case f_catmullrom: return new PtexBicubicFilter(tex, opts, 1.0);
+ case f_mitchell: return new PtexBicubicFilter(tex, opts, 2.0/3.0);
+ }
+ break;
+
+ case Ptex::mt_triangle:
+ switch (opts.filter) {
+ case f_point: return new PtexPointFilterTri(tex);
+ default: return new PtexTriangleFilter(tex, opts);
+ }
+ break;
+ }
+ return 0;
+}
diff --git a/extern/ptex/src/ptex/PtexHalf.cpp b/extern/ptex/src/ptex/PtexHalf.cpp
new file mode 100644
index 00000000000..a8809715f9a
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexHalf.cpp
@@ -0,0 +1,105 @@
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+#include <math.h>
+#include "PtexHalf.h"
+
+uint16_t PtexHalf::f2hTable[512];
+uint32_t PtexHalf::h2fTable[65536];
+
+/** Table initializations. */
+static bool PtexHalfInit()
+{
+ union { int i; float f; } u;
+
+ for (int h = 0; h < 65536; h++) {
+ int s = (h & 0x8000)<<16;
+ int m = h & 0x03ff;
+ int e = h&0x7c00;
+
+ if (unsigned(e-1) < ((31<<10)-1)) {
+ // normal case
+ u.i = s|(((e+0x1c000)|m)<<13);
+ }
+ else if (e == 0) {
+ // denormalized
+ if (!(h&0x8000)) u.f = float(5.9604644775390625e-08*m);
+ else u.f = float(-5.9604644775390625e-08*m);
+ }
+ else {
+ // inf/nan, preserve low bits of m for nan code
+ u.i = s|0x7f800000|(m<<13);
+ }
+ PtexHalf::h2fTable[h] = u.i;
+ }
+
+ for (int i = 0; i < 512; i++) {
+ int f = i << 23;
+ int e = (f & 0x7f800000) - 0x38000000;
+ // normalized iff (0 < e < 31)
+ if (unsigned(e-1) < ((31<<23)-1)) {
+ int s = ((f>>16) & 0x8000);
+ int m = f & 0x7fe000;
+ // add bit 12 to round
+ PtexHalf::f2hTable[i] = (s|((e|m)>>13))+((f>>12)&1);
+ }
+ }
+
+ return 1;
+}
+
+static bool PtexHalfInitialized = PtexHalfInit();
+
+
+/** Handle exceptional cases for half-to-float conversion */
+uint16_t PtexHalf::fromFloat_except(uint32_t i)
+{
+ uint32_t s = ((i>>16) & 0x8000);
+ int32_t e = ((i>>13) & 0x3fc00) - 0x1c000;
+
+ if (e <= 0) {
+ // denormalized
+ union { uint32_t i; float f; } u;
+ u.i = i;
+ return s | int(fabs(u.f)*1.6777216e7 + .5);
+ }
+
+ if (e == 0x23c00)
+ // inf/nan, preserve msb bits of m for nan code
+ return s|0x7c00|((i&0x7fffff)>>13);
+ else
+ // overflow - convert to inf
+ return s|0x7c00;
+}
diff --git a/extern/ptex/src/ptex/PtexHalf.h b/extern/ptex/src/ptex/PtexHalf.h
new file mode 100644
index 00000000000..de59da3eac7
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexHalf.h
@@ -0,0 +1,118 @@
+#ifndef PtexHalf_h
+#define PtexHalf_h
+
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+/**
+ @file PtexHalf.h
+ @brief Half-precision floating-point type.
+*/
+
+#if defined(_WIN32) || defined(_WINDOWS) || defined(_MSC_VER)
+# ifndef PTEX_STATIC
+# ifdef PTEX_EXPORTS
+# define PTEXAPI __declspec(dllexport)
+# else
+# define PTEXAPI __declspec(dllimport)
+# endif
+# else
+# define PTEXAPI
+# endif
+#else
+# define PTEXAPI
+#endif
+
+#include "PtexInt.h"
+
+/**
+ @class PtexHalf
+ @brief Half-precision (16-bit) floating-point type.
+
+ This type should be compatible with opengl, openexr, and IEEE 754r.
+ The range is [-65504.0, 65504.0] and the precision is about 1 part
+ in 2000 (3.3 decimal places).
+
+ From OpenGL spec 2.1.2:
+
+ A 16-bit floating-point number has a 1-bit sign (S), a 5-bit
+ exponent (E), and a 10-bit mantissa (M). The value of a 16-bit
+ floating-point number is determined by the following:
+
+ \verbatim
+ (-1)^S * 0.0, if E == 0 and M == 0,
+ (-1)^S * 2^-14 * (M/2^10), if E == 0 and M != 0,
+ (-1)^S * 2^(E-15) * (1 + M/2^10), if 0 < E < 31,
+ (-1)^S * INF, if E == 31 and M == 0, or
+ NaN, if E == 31 and M != 0 \endverbatim
+*/
+
+struct PtexHalf {
+ uint16_t bits;
+
+ /// Default constructor, value is undefined
+ PtexHalf() {}
+ PtexHalf(float val) : bits(fromFloat(val)) {}
+ PtexHalf(double val) : bits(fromFloat(float(val))) {}
+ operator float() const { return toFloat(bits); }
+ PtexHalf& operator=(float val) { bits = fromFloat(val); return *this; }
+
+ static float toFloat(uint16_t h)
+ {
+ union { uint32_t i; float f; } u;
+ u.i = h2fTable[h];
+ return u.f;
+ }
+
+ static uint16_t fromFloat(float val)
+ {
+ if (val==0) return 0;
+ union { uint32_t i; float f; } u;
+ u.f = val;
+ int e = f2hTable[(u.i>>23)&0x1ff];
+ if (e) return e + (((u.i&0x7fffff) + 0x1000) >> 13);
+ return fromFloat_except(u.i);
+ }
+
+ private:
+ PTEXAPI static uint16_t fromFloat_except(uint32_t val);
+#ifndef DOXYGEN
+ /* internal */ public:
+#endif
+ PTEXAPI static uint32_t h2fTable[65536];
+ PTEXAPI static uint16_t f2hTable[512];
+};
+
+#endif
diff --git a/extern/ptex/src/ptex/PtexHashMap.h b/extern/ptex/src/ptex/PtexHashMap.h
new file mode 100644
index 00000000000..6bd338a793b
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexHashMap.h
@@ -0,0 +1,352 @@
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+/**
+ @file PtexHashMap.h
+ @brief Contains PtexHashMap, a lightweight hash table.
+*/
+
+#ifndef PtexHashMap_h
+#define PtexHashMap_h
+
+/**
+ @class PtexHashMap
+ @brief A lightweight hash table.
+
+ <P>
+ The hash table automatically doubles in size when it is more than 50% full.
+
+ <P>
+ The interface is mostely compatible with std::hash_map<>, though not all methods
+ are provided. Methods provided:
+
+ <UL>
+ <LI>iterator begin();
+ <LI>iterator end();
+ <LI>DataType& operator[] (KeyType key);
+ <LI>iterator find(KeyType key) const;
+ <LI>bool erase(KeyType key);
+ <LI>iterator erase(iterator);
+ <LI>void clear();
+ <LI>int size() const;
+ </UL>
+
+ @author brentb
+
+ @version <B>1.0 brentb 11/01/2000:</B> Initial version
+ @version <B>1.1 longson 06/26/2001:</B> Added file and class comment headers
+ @version <B>1.2 brentb 105/2006:</B> Generalized IntDict into general hash map
+
+*/
+
+/**
+ @class PtexHashMap::iterator
+ @brief Internal class used to provide iteration through the hash table
+
+ @author brentb
+
+ @version <B>1.0 brentb 11/01/2000:</B> Initial version
+ @version <B>1.1 longson 06/26/2001:</B> Added file and class comment headers
+
+*/
+
+template<typename KeyType, typename DataType, typename HashFn>
+class PtexHashMap
+{
+ struct Entry; // forward decl
+
+public:
+ typedef KeyType key_type;
+ typedef DataType data_type;
+ typedef HashFn hash_fn;
+
+ struct value_type {
+ key_type first;
+ data_type second;
+ value_type() : first(), second() {}
+ value_type(const KeyType& first, const DataType& second)
+ : first(first), second(second) {}
+ };
+
+ class iterator {
+ public:
+ /// Default Constructor
+ iterator() : e(0), h(0), b(0) {}
+
+ /// Proper copy constructor implementation
+ iterator(const iterator& iter) : e(iter.e), h(iter.h), b(iter.b) {}
+
+ /// Operator for obtaining the value that the iterator references
+ value_type operator*() const {
+ if (e) return value_type((*e)->key, (*e)->val);
+ return value_type();
+ }
+
+ /// Pointer reference operator
+ value_type* operator->() const {
+ static value_type v;
+ v = operator*();
+ return &v;
+ }
+
+ /// Proper assignment operator
+ iterator& operator=(const iterator& iter) {
+ e = iter.e; h = iter.h; b = iter.b; return *this;
+ }
+
+ /// For comparing equality of iterators
+ bool operator==(const iterator& iter) const { return iter.e == e; }
+ /// For comparing inequality of iterators
+ bool operator!=(const iterator& iter) const { return iter.e != e; }
+ /// For advancing the iterator to the next element
+ iterator& operator++(int);
+
+ private:
+ friend class PtexHashMap;
+ Entry** e; // pointer to prev entry or hash entry
+ const PtexHashMap* h;
+ int b; // bucket number
+ };
+
+ /// Returns an iterator referencing the beginning of the table
+ iterator begin() const
+ {
+ iterator iter;
+ iter.h = this;
+ for (iter.b = 0; iter.b < _numBuckets; iter.b++) {
+ iter.e = &_buckets[iter.b];
+ if (*iter.e) return iter;
+ }
+ iter.e = 0;
+ return iter;
+ }
+
+ /// Returns an iteraot referencing the location just beyond the table.
+ iterator end() const
+ {
+ iterator iter;
+ iter.h = this; iter.b = 0; iter.e = 0;
+ return iter;
+ }
+
+
+ /// Default contructor initializes the hash table.
+ PtexHashMap() : _numEntries(0), _numBuckets(0), _bucketMask(0), _buckets(0) {}
+ /// Non-Virtual destructor by design, clears the entries in the hash table
+ ~PtexHashMap() { clear(); }
+
+ /// Locates an entry, creating a new one if necessary.
+ /** operator[] will look up an entry and return the value. A new entry
+ will be created (using the default ctor for DataType) if one doesn't exist.
+ */
+ DataType& operator[](const KeyType& key);
+
+ /// Locates an entry, without creating a new one.
+ /** find will locate an entry, but won't create a new one. The result is
+ returned as a pair of key and value. The returned key points to the
+ internal key string and will remain valid until the entry is deleted.
+ If the key is not found, both the returned key and value pointers will
+ be NULL.
+ */
+ iterator find(const KeyType& key) const;
+
+ /// Will remove an entry. It will return TRUE if an entry was found.
+ bool erase(const KeyType& key);
+ /// Will remove an entry. It will return a iterator to the next element
+ iterator erase(iterator iter);
+
+ /// clear will remove all entries and free the table
+ void clear();
+
+ /// number of entries in the hash
+ int size() const { return _numEntries; }
+
+private:
+ /// Copy constructor prohibited by design.
+ PtexHashMap(const PtexHashMap&); // disallow
+ /// Assignment operator prohibited by design.
+ bool operator=(const PtexHashMap&); // disallow
+
+ friend class iterator;
+
+ struct Entry {
+ Entry() : val() {}
+ Entry* next;
+ KeyType key;
+ DataType val;
+ };
+
+ /// Returns a pointer to the desired entry, based on the key.
+ Entry** locate(const KeyType& key) const
+ {
+ if (!_buckets) return 0;
+ for (Entry** e = &_buckets[hash(key) & _bucketMask]; *e; e = &(*e)->next)
+ if ((*e)->key == key)
+ return e;
+ return 0;
+ }
+
+ /// Used to increase the size of the table if necessary
+ void grow();
+
+ int _numEntries;
+ int _numBuckets;
+ int _bucketMask;
+ Entry** _buckets;
+ HashFn hash;
+};
+
+
+template<class KeyType, class DataType, class HashFn>
+typename PtexHashMap<KeyType, DataType, HashFn>::iterator&
+PtexHashMap<KeyType, DataType, HashFn>::iterator::operator++(int)
+{
+ if (e) {
+ // move to next entry
+ e = &(*e)->next;
+ if (!*e) {
+ // move to next non-empty bucket
+ for (b++; b < h->_numBuckets; b++) {
+ e = &h->_buckets[b];
+ if (*e) return *this;
+ }
+ e = 0;
+ }
+ }
+ return *this;
+}
+
+
+template<class KeyType, class DataType, class HashFn>
+typename PtexHashMap<KeyType, DataType, HashFn>::iterator
+PtexHashMap<KeyType, DataType, HashFn>::find(const KeyType& key) const
+{
+ iterator iter;
+ Entry** e = locate(key);
+ if (e) {
+ iter.h = this;
+ iter.e = e;
+ iter.b = hash(key) & _bucketMask;
+ } else iter = end();
+ return iter;
+}
+
+template<class KeyType, class DataType, class HashFn>
+DataType& PtexHashMap<KeyType, DataType, HashFn>::operator[](const KeyType& key)
+{
+ Entry** e = locate(key);
+ if (e) return (*e)->val;
+
+ // create a new entry
+ _numEntries++;
+ if (_numEntries*2 >= _numBuckets) grow();
+ void* ebuf = malloc(sizeof(Entry));
+ Entry* ne = new(ebuf) Entry; // note: placement new
+ Entry** slot = &_buckets[hash(key) & _bucketMask];
+ ne->next = *slot; *slot = ne;
+ ne->key = key;
+ return ne->val;
+}
+
+
+template<class KeyType, class DataType, class HashFn>
+void PtexHashMap<KeyType, DataType, HashFn>::grow()
+{
+ if (!_buckets) {
+ _numBuckets = 16;
+ _bucketMask = _numBuckets - 1;
+ _buckets = (Entry**) calloc(_numBuckets, sizeof(Entry*));
+ } else {
+ int newsize = _numBuckets * 2;
+ _bucketMask = newsize - 1;
+ Entry** newbuckets = (Entry**) calloc(newsize, sizeof(Entry*));
+ for (int i = 0; i < _numBuckets; i++) {
+ for (Entry* e = _buckets[i]; e;) {
+ Entry* next = e->next;
+ Entry** slot = &newbuckets[hash(e->key) & _bucketMask];
+ e->next = *slot; *slot = e;
+ e = next;
+ }
+ }
+ free(_buckets);
+ _buckets = newbuckets;
+ _numBuckets = newsize;
+ }
+}
+
+
+template<class KeyType, class DataType, class HashFn>
+bool PtexHashMap<KeyType, DataType, HashFn>::erase(const KeyType& key)
+{
+ iterator iter = find(key);
+ if (iter == end()) return 0;
+ erase(iter);
+ return 1;
+}
+
+
+template<class KeyType, class DataType, class HashFn>
+typename PtexHashMap<KeyType, DataType, HashFn>::iterator
+PtexHashMap<KeyType, DataType, HashFn>::erase(iterator iter)
+{
+ Entry** eptr = iter.e;
+ if (!eptr) return iter;
+
+ // patch around deleted entry
+ Entry* e = *eptr;
+ Entry* next = e->next;
+ if (!next) iter++; // advance iterator if at end of chain
+ *eptr = next;
+
+
+ // destroy entry
+ e->~Entry(); // note: explicit dtor call
+ free(e);
+ _numEntries--;
+
+ return iter;
+}
+
+
+template<class KeyType, class DataType, class HashFn>
+void PtexHashMap<KeyType, DataType, HashFn>::clear()
+{
+ for (iterator i = begin(); i != end(); i = erase(i));
+ free(_buckets);
+ _buckets = 0;
+ _numEntries = 0;
+ _numBuckets = 0;
+}
+
+#endif
diff --git a/extern/ptex/src/ptex/PtexIO.h b/extern/ptex/src/ptex/PtexIO.h
new file mode 100644
index 00000000000..0748d863737
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexIO.h
@@ -0,0 +1,119 @@
+#ifndef PtexIO_h
+#define PtexIO_h
+
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+#include "Ptexture.h"
+
+struct PtexIO : public Ptex {
+ struct Header {
+ uint32_t magic;
+ uint32_t version;
+ MeshType meshtype:32;
+ DataType datatype:32;
+ int32_t alphachan;
+ uint16_t nchannels;
+ uint16_t nlevels;
+ uint32_t nfaces;
+ uint32_t extheadersize;
+ uint32_t faceinfosize;
+ uint32_t constdatasize;
+ uint32_t levelinfosize;
+ uint32_t minorversion;
+ uint64_t leveldatasize;
+ uint32_t metadatazipsize;
+ uint32_t metadatamemsize;
+ int pixelSize() const { return DataSize(datatype) * nchannels; }
+ bool hasAlpha() const { return alphachan >= 0 && alphachan < nchannels; }
+ };
+ struct ExtHeader {
+ BorderMode ubordermode:32;
+ BorderMode vbordermode:32;
+ uint32_t lmdheaderzipsize;
+ uint32_t lmdheadermemsize;
+ uint64_t lmddatasize;
+ uint64_t editdatasize;
+ uint64_t editdatapos;
+ };
+ struct LevelInfo {
+ uint64_t leveldatasize;
+ uint32_t levelheadersize;
+ uint32_t nfaces;
+ LevelInfo() : leveldatasize(0), levelheadersize(0), nfaces(0) {}
+ };
+ enum Encoding { enc_constant, enc_zipped, enc_diffzipped, enc_tiled };
+ struct FaceDataHeader {
+ uint32_t data; // bits 0..29 = blocksize, bits 30..31 = encoding
+ uint32_t blocksize() const { return data & 0x3fffffff; }
+ Encoding encoding() const { return Encoding((data >> 30) & 0x3); }
+ uint32_t& val() { return *(uint32_t*) this; }
+ const uint32_t& val() const { return *(uint32_t*) this; }
+ void set(uint32_t blocksize, Encoding encoding)
+ { data = (blocksize & 0x3fffffff) | ((encoding & 0x3) << 30); }
+ FaceDataHeader() : data(0) {}
+ };
+ enum EditType { et_editfacedata, et_editmetadata };
+ struct EditFaceDataHeader {
+ uint32_t faceid;
+ FaceInfo faceinfo;
+ FaceDataHeader fdh;
+ };
+ struct EditMetaDataHeader {
+ uint32_t metadatazipsize;
+ uint32_t metadatamemsize;
+ };
+
+ static const uint32_t Magic = 'P' | ('t'<<8) | ('e'<<16) | ('x'<<24);
+ static const int HeaderSize = sizeof(Header);
+ static const int ExtHeaderSize = sizeof(ExtHeader);
+ static const int LevelInfoSize = sizeof(LevelInfo);
+ static const int FaceDataHeaderSize = sizeof(FaceDataHeader);
+ static const int EditFaceDataHeaderSize = sizeof(EditFaceDataHeader);
+ static const int EditMetaDataHeaderSize = sizeof(EditMetaDataHeader);
+
+ // these constants can be tuned for performance
+ static const int BlockSize = 16384; // target block size for file I/O
+ static const int TileSize = 65536; // target tile size (uncompressed)
+ static const int AllocaMax = 16384; // max size for using alloca
+ static const int MetaDataThreshold = 1024; // cutoff for large meta data
+
+ static bool LittleEndian() {
+ short word = 0x0201;
+ return *(char*)&word == 1;
+ }
+};
+
+#endif
diff --git a/extern/ptex/src/ptex/PtexInt.h b/extern/ptex/src/ptex/PtexInt.h
new file mode 100644
index 00000000000..c3212dc5f4c
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexInt.h
@@ -0,0 +1,57 @@
+#ifndef PtexInt_h
+#define PtexInt_h
+
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+/**
+ @file PtexInt.h
+ @brief Portable fixed-width integer types
+*/
+
+#if defined(_WIN32) || defined(_WINDOWS) || defined(_MSC_VER)
+typedef __int8 int8_t;
+typedef __int16 int16_t;
+typedef __int32 int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#else
+#include <stdint.h>
+#endif
+
+#endif
diff --git a/extern/ptex/src/ptex/PtexMutex.h b/extern/ptex/src/ptex/PtexMutex.h
new file mode 100644
index 00000000000..a2ee343ca83
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexMutex.h
@@ -0,0 +1,79 @@
+#ifndef PtexMutex_h
+#define PtexMutex_h
+
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+// #define DEBUG_THREADING
+
+/** For internal use only */
+namespace PtexInternal {
+#ifndef NDEBUG
+ template <class T>
+ class DebugLock : public T {
+ public:
+ DebugLock() : _locked(0) {}
+ void lock() { T::lock(); _locked = 1; }
+ void unlock() { assert(_locked); _locked = 0; T::unlock(); }
+ bool locked() { return _locked != 0; }
+ private:
+ int _locked;
+ };
+#endif
+
+ /** Automatically acquire and release lock within enclosing scope. */
+ template <class T>
+ class AutoLock {
+ public:
+ AutoLock(T& m) : _m(m) { _m.lock(); }
+ ~AutoLock() { _m.unlock(); }
+ private:
+ T& _m;
+ };
+
+#ifndef NDEBUG
+ // add debug wrappers to mutex and spinlock
+ typedef DebugLock<_Mutex> Mutex;
+ typedef DebugLock<_SpinLock> SpinLock;
+#else
+ typedef _Mutex Mutex;
+ typedef _SpinLock SpinLock;
+#endif
+
+ typedef AutoLock<Mutex> AutoMutex;
+ typedef AutoLock<SpinLock> AutoSpin;
+}
+
+#endif
diff --git a/extern/ptex/src/ptex/PtexPlatform.h b/extern/ptex/src/ptex/PtexPlatform.h
new file mode 100644
index 00000000000..b2959f511c2
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexPlatform.h
@@ -0,0 +1,160 @@
+#ifndef PtexPlatform_h
+#define PtexPlatform_h
+#define PtexPlatform_h
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+/** @file PtexPlatform.h
+ @brief Platform-specific classes, functions, and includes.
+*/
+
+// platform-specific includes
+#if defined(_WIN32) || defined(_WINDOWS) || defined(_MSC_VER)
+#ifndef WINDOWS
+#define WINDOWS
+#endif
+#define _CRT_NONSTDC_NO_DEPRECATE 1
+#define _CRT_SECURE_NO_DEPRECATE 1
+#define NOMINMAX 1
+
+// windows - defined for both Win32 and Win64
+#include <Windows.h>
+#include <malloc.h>
+#include <io.h>
+#include <tchar.h>
+#include <process.h>
+
+#else
+
+// linux/unix/posix
+#include <stdlib.h>
+#include <alloca.h>
+#include <string.h>
+#include <pthread.h>
+// OS for spinlock
+#ifdef __APPLE__
+#include <libkern/OSAtomic.h>
+#include <sys/types.h>
+#endif
+#endif
+
+// general includes
+#include <stdio.h>
+#include <math.h>
+#include <assert.h>
+
+// missing functions on Windows
+#ifdef WINDOWS
+#define snprintf sprintf_s
+#define strtok_r strtok_s
+typedef __int64 FilePos;
+#define fseeko _fseeki64
+#define ftello _ftelli64
+
+inline double log2(double x) {
+ return log(x) * 1.4426950408889634;
+}
+
+#else
+typedef off_t FilePos;
+#endif
+
+
+namespace PtexInternal {
+
+ /*
+ * Mutex/SpinLock classes
+ */
+
+#ifdef WINDOWS
+
+ class _Mutex {
+ public:
+ _Mutex() { _mutex = CreateMutex(NULL, FALSE, NULL); }
+ ~_Mutex() { CloseHandle(_mutex); }
+ void lock() { WaitForSingleObject(_mutex, INFINITE); }
+ void unlock() { ReleaseMutex(_mutex); }
+ private:
+ HANDLE _mutex;
+ };
+
+ class _SpinLock {
+ public:
+ _SpinLock() { InitializeCriticalSection(&_spinlock); }
+ ~_SpinLock() { DeleteCriticalSection(&_spinlock); }
+ void lock() { EnterCriticalSection(&_spinlock); }
+ void unlock() { LeaveCriticalSection(&_spinlock); }
+ private:
+ CRITICAL_SECTION _spinlock;
+ };
+
+#else
+ // assume linux/unix/posix
+
+ class _Mutex {
+ public:
+ _Mutex() { pthread_mutex_init(&_mutex, 0); }
+ ~_Mutex() { pthread_mutex_destroy(&_mutex); }
+ void lock() { pthread_mutex_lock(&_mutex); }
+ void unlock() { pthread_mutex_unlock(&_mutex); }
+ private:
+ pthread_mutex_t _mutex;
+ };
+
+#ifdef __APPLE__
+ class _SpinLock {
+ public:
+ _SpinLock() { _spinlock = 0; }
+ ~_SpinLock() { }
+ void lock() { OSSpinLockLock(&_spinlock); }
+ void unlock() { OSSpinLockUnlock(&_spinlock); }
+ private:
+ OSSpinLock _spinlock;
+ };
+#else
+ class _SpinLock {
+ public:
+ _SpinLock() { pthread_spin_init(&_spinlock, PTHREAD_PROCESS_PRIVATE); }
+ ~_SpinLock() { pthread_spin_destroy(&_spinlock); }
+ void lock() { pthread_spin_lock(&_spinlock); }
+ void unlock() { pthread_spin_unlock(&_spinlock); }
+ private:
+ pthread_spinlock_t _spinlock;
+ };
+#endif // __APPLE__
+#endif
+}
+
+#endif // PtexPlatform_h
diff --git a/extern/ptex/src/ptex/PtexReader.cpp b/extern/ptex/src/ptex/PtexReader.cpp
new file mode 100644
index 00000000000..f86d349d672
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexReader.cpp
@@ -0,0 +1,1357 @@
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+#include "PtexPlatform.h"
+#include <iostream>
+#include <sstream>
+#include <errno.h>
+#include <stdio.h>
+
+#include "Ptexture.h"
+#include "PtexUtils.h"
+#include "PtexReader.h"
+
+
+namespace {
+ class DefaultInputHandler : public PtexInputHandler
+ {
+ public:
+ virtual Handle open(const char* path) { return (Handle) fopen(path, "rb"); }
+ virtual void seek(Handle handle, int64_t pos) { fseeko((FILE*)handle, pos, SEEK_SET); }
+ virtual size_t read(void* buffer, size_t size, Handle handle) {
+ return fread(buffer, size, 1, (FILE*)handle) == 1 ? size : 0;
+ }
+ virtual bool close(Handle handle) { return fclose((FILE*)handle); }
+ virtual const char* lastError() { return strerror(errno); }
+ } defaultInputHandler;
+}
+
+
+PtexTexture* PtexTexture::open(const char* path, Ptex::String& error, bool premultiply)
+{
+ // create a private cache and use it to open the file
+ PtexCache* cache = PtexCache::create(1, 1024*1024, premultiply);
+ PtexTexture* file = cache->get(path, error);
+
+ // make reader own the cache (so it will delete it later)
+ PtexReader* reader = dynamic_cast<PtexReader*> (file);
+ if (reader) reader->setOwnsCache();
+
+ // and purge cache so cache doesn't try to hold reader open
+ cache->purgeAll();
+ return file;
+}
+
+
+bool PtexReader::open(const char* path, Ptex::String& error)
+{
+ if (!LittleEndian()) {
+ error = "Ptex library doesn't currently support big-endian cpu's";
+ return 0;
+ }
+ _path = path;
+ _fp = _io->open(path);
+ if (!_fp) {
+ std::string errstr = "Can't open ptex file: ";
+ errstr += path; errstr += "\n"; errstr += _io->lastError();
+ error = errstr.c_str();
+ return 0;
+ }
+ readBlock(&_header, HeaderSize);
+ if (_header.magic != Magic) {
+ std::string errstr = "Not a ptex file: "; errstr += path;
+ error = errstr.c_str();
+ return 0;
+ }
+ if (_header.version != 1) {
+ char ver[21]; snprintf(ver, 20, "%d", _header.version);
+ std::string errstr = "Unsupported ptex file version (";
+ errstr += ver; errstr += "): "; errstr += path;
+ error = errstr.c_str();
+ return 0;
+ }
+ _pixelsize = _header.pixelSize();
+
+ // read extended header
+ memset(&_extheader, 0, sizeof(_extheader));
+ readBlock(&_extheader, PtexUtils::min(uint32_t(ExtHeaderSize), _header.extheadersize));
+
+ // compute offsets of various blocks
+ FilePos pos = tell();
+ _faceinfopos = pos; pos += _header.faceinfosize;
+ _constdatapos = pos; pos += _header.constdatasize;
+ _levelinfopos = pos; pos += _header.levelinfosize;
+ _leveldatapos = pos; pos += _header.leveldatasize;
+ _metadatapos = pos; pos += _header.metadatazipsize;
+ pos += sizeof(uint64_t); // compatibility barrier
+ _lmdheaderpos = pos; pos += _extheader.lmdheaderzipsize;
+ _lmddatapos = pos; pos += _extheader.lmddatasize;
+
+ // edit data may not start immediately if additional sections have been added
+ // use value from extheader if present (and > pos)
+ _editdatapos = PtexUtils::max(FilePos(_extheader.editdatapos), pos);
+
+ // read basic file info
+ readFaceInfo();
+ readConstData();
+ readLevelInfo();
+ readEditData();
+
+ // an error occurred while reading the file
+ if (!_ok) {
+ error = _error.c_str();
+ return 0;
+ }
+
+ return 1;
+}
+
+
+PtexReader::PtexReader(void** parent, PtexCacheImpl* cache, bool premultiply,
+ PtexInputHandler* io)
+ : PtexCachedFile(parent, cache),
+ _io(io ? io : &defaultInputHandler),
+ _premultiply(premultiply),
+ _ownsCache(false),
+ _ok(true),
+ _fp(0),
+ _pos(0),
+ _pixelsize(0),
+ _constdata(0),
+ _metadata(0),
+ _hasEdits(false)
+{
+ memset(&_header, 0, sizeof(_header));
+ memset(&_zstream, 0, sizeof(_zstream));
+ inflateInit(&_zstream);
+}
+
+
+PtexReader::~PtexReader()
+{
+ if (_fp) _io->close(_fp);
+
+ // we can free the const data directly since we own it (it doesn't go through the cache)
+ if (_constdata) free(_constdata);
+
+ // the rest must be orphaned since there may still be references outstanding
+ orphanList(_levels);
+ for (ReductionMap::iterator i = _reductions.begin(); i != _reductions.end(); i++) {
+ FaceData* f = i->second;
+ if (f) f->orphan();
+ }
+ if (_metadata) {
+ _metadata->orphan();
+ _metadata = 0;
+ }
+
+ inflateEnd(&_zstream);
+
+ if (_ownsCache) _cache->setPendingDelete();
+}
+
+
+void PtexReader::release()
+{
+ PtexCacheImpl* cache = _cache;
+ {
+ // create local scope for cache lock
+ AutoLockCache lock(cache->cachelock);
+ unref();
+ }
+ // If this reader owns the cache, then releasing it may cause deletion of the
+ // reader and thus flag the cache for pending deletion. Call the cache
+ // to handle the pending deletion.
+ cache->handlePendingDelete();
+}
+
+
+const Ptex::FaceInfo& PtexReader::getFaceInfo(int faceid)
+{
+ if (faceid >= 0 && uint32_t(faceid) < _faceinfo.size())
+ return _faceinfo[faceid];
+
+ static Ptex::FaceInfo dummy;
+ return dummy;
+}
+
+
+void PtexReader::readFaceInfo()
+{
+ if (_faceinfo.empty()) {
+ // read compressed face info block
+ seek(_faceinfopos);
+ int nfaces = _header.nfaces;
+ _faceinfo.resize(nfaces);
+ readZipBlock(&_faceinfo[0], _header.faceinfosize,
+ sizeof(FaceInfo)*nfaces);
+
+ // generate rfaceids
+ _rfaceids.resize(nfaces);
+ std::vector<uint32_t> faceids_r(nfaces);
+ PtexUtils::genRfaceids(&_faceinfo[0], nfaces,
+ &_rfaceids[0], &faceids_r[0]);
+
+ // store face res values indexed by rfaceid
+ _res_r.resize(nfaces);
+ for (int i = 0; i < nfaces; i++)
+ _res_r[i] = _faceinfo[faceids_r[i]].res;
+ }
+}
+
+
+
+void PtexReader::readLevelInfo()
+{
+ if (_levelinfo.empty()) {
+ // read level info block
+ seek(_levelinfopos);
+ _levelinfo.resize(_header.nlevels);
+ readBlock(&_levelinfo[0], LevelInfoSize*_header.nlevels);
+
+ // initialize related data
+ _levels.resize(_header.nlevels);
+ _levelpos.resize(_header.nlevels);
+ FilePos pos = _leveldatapos;
+ for (int i = 0; i < _header.nlevels; i++) {
+ _levelpos[i] = pos;
+ pos += _levelinfo[i].leveldatasize;
+ }
+ }
+}
+
+
+void PtexReader::readConstData()
+{
+ if (!_constdata) {
+ // read compressed constant data block
+ seek(_constdatapos);
+ int size = _pixelsize * _header.nfaces;
+ _constdata = (uint8_t*) malloc(size);
+ readZipBlock(_constdata, _header.constdatasize, size);
+ if (_premultiply && _header.hasAlpha())
+ PtexUtils::multalpha(_constdata, _header.nfaces, _header.datatype,
+ _header.nchannels, _header.alphachan);
+ }
+}
+
+
+PtexMetaData* PtexReader::getMetaData()
+{
+ AutoLockCache locker(_cache->cachelock);
+ if (_metadata) _metadata->ref();
+ else readMetaData();
+ return _metadata;
+}
+
+
+PtexReader::MetaData::Entry*
+PtexReader::MetaData::getEntry(const char* key)
+{
+ MetaMap::iterator iter = _map.find(key);
+ if (iter == _map.end()) {
+ // not found
+ return 0;
+ }
+
+ Entry* e = &iter->second;
+ if (!e->isLmd) {
+ // normal (small) meta data - just return directly
+ return e;
+ }
+
+ // large meta data - may not be read in yet
+ AutoLockCache lock(_cache->cachelock);
+ if (e->lmdData) {
+ // already in memory, add a ref
+ e->lmdData->ref();
+ _lmdRefs.push_back(e->lmdData);
+ return e;
+ }
+ else {
+ // not present, must read from file
+ // temporarily release cache lock so other threads can proceed
+ _cache->cachelock.unlock();
+
+ // get read lock and make sure we still need to read
+ AutoMutex locker(_reader->readlock);
+ if (e->lmdData) {
+ // another thread must have read it while we were waiting
+ _cache->cachelock.lock();
+
+ // make sure it's still there now that we have the lock
+ if (e->lmdData) {
+ e->data = e->lmdData->data();
+ _lmdRefs.push_back(e->lmdData);
+ e->lmdData->ref();
+ return e;
+ }
+ }
+ // go ahead and read, keep local until finished
+ LargeMetaData*& parent = e->lmdData;
+ LargeMetaData* volatile lmdData = new LargeMetaData((void**)&parent, _cache, e->datasize);
+ e->data = lmdData->data();
+ _reader->seek(e->lmdPos);
+ _reader->readZipBlock(e->data, e->lmdZipSize, e->datasize);
+ // reacquire cache lock and update entry
+ _cache->cachelock.lock();
+ e->lmdData = lmdData;
+ return e;
+ }
+}
+
+
+void PtexReader::readMetaData()
+{
+ // temporarily release cache lock so other threads can proceed
+ _cache->cachelock.unlock();
+
+ // get read lock and make sure we still need to read
+ AutoMutex locker(readlock);
+ if (_metadata) {
+ // another thread must have read it while we were waiting
+ _cache->cachelock.lock();
+ // make sure it's still there now that we have the lock
+ if (_metadata) {
+ _metadata->ref();
+ return;
+ }
+ _cache->cachelock.unlock();
+ }
+
+
+ // compute total size (including edit blocks) for cache tracking
+ int totalsize = _header.metadatamemsize;
+ for (size_t i = 0, size = _metaedits.size(); i < size; i++)
+ totalsize += _metaedits[i].memsize;
+
+ // allocate new meta data (keep local until fully initialized)
+ MetaData* volatile newmeta = new MetaData(&_metadata, _cache, totalsize, this);
+
+ // read primary meta data blocks
+ if (_header.metadatamemsize)
+ readMetaDataBlock(newmeta, _metadatapos,
+ _header.metadatazipsize, _header.metadatamemsize);
+
+ // read large meta data headers
+ if (_extheader.lmdheadermemsize)
+ readLargeMetaDataHeaders(newmeta, _lmdheaderpos,
+ _extheader.lmdheaderzipsize, _extheader.lmdheadermemsize);
+
+ // read meta data edits
+ for (size_t i = 0, size = _metaedits.size(); i < size; i++)
+ readMetaDataBlock(newmeta, _metaedits[i].pos,
+ _metaedits[i].zipsize, _metaedits[i].memsize);
+
+ // store meta data
+ _cache->cachelock.lock();
+ _metadata = newmeta;
+
+ // clean up unused data
+ _cache->purgeData();
+}
+
+
+void PtexReader::readMetaDataBlock(MetaData* metadata, FilePos pos, int zipsize, int memsize)
+{
+ seek(pos);
+ // read from file
+ bool useMalloc = memsize > AllocaMax;
+ char* buff = useMalloc ? (char*) malloc(memsize) : (char*)alloca(memsize);
+
+ if (readZipBlock(buff, zipsize, memsize)) {
+ // unpack data entries
+ char* ptr = buff;
+ char* end = ptr + memsize;
+ while (ptr < end) {
+ uint8_t keysize = *ptr++;
+ char* key = (char*)ptr; ptr += keysize;
+ key[keysize-1] = '\0';
+ uint8_t datatype = *ptr++;
+ uint32_t datasize; memcpy(&datasize, ptr, sizeof(datasize));
+ ptr += sizeof(datasize);
+ char* data = ptr; ptr += datasize;
+ metadata->addEntry(keysize-1, key, datatype, datasize, data);
+ }
+ }
+ if (useMalloc) free(buff);
+}
+
+
+void PtexReader::readLargeMetaDataHeaders(MetaData* metadata, FilePos pos, int zipsize, int memsize)
+{
+ seek(pos);
+ // read from file
+ bool useMalloc = memsize > AllocaMax;
+ char* buff = useMalloc ? (char*) malloc(memsize) : (char*)alloca(memsize);
+
+ if (readZipBlock(buff, zipsize, memsize)) {
+ pos += zipsize;
+
+ // unpack data entries
+ char* ptr = buff;
+ char* end = ptr + memsize;
+ while (ptr < end) {
+ uint8_t keysize = *ptr++;
+ char* key = (char*)ptr; ptr += keysize;
+ uint8_t datatype = *ptr++;
+ uint32_t datasize; memcpy(&datasize, ptr, sizeof(datasize));
+ ptr += sizeof(datasize);
+ uint32_t zipsize; memcpy(&zipsize, ptr, sizeof(zipsize));
+ ptr += sizeof(zipsize);
+ metadata->addLmdEntry(keysize-1, key, datatype, datasize, pos, zipsize);
+ pos += zipsize;
+ }
+ }
+ if (useMalloc) free(buff);
+}
+
+void PtexReader::readEditData()
+{
+ // determine file range to scan for edits
+ FilePos pos = FilePos(_editdatapos), endpos;
+ if (_extheader.editdatapos > 0) {
+ // newer files record the edit data position and size in the extheader
+ // note: position will be non-zero even if size is zero
+ endpos = FilePos(pos + _extheader.editdatasize);
+ }
+ else {
+ // must have an older file, just read until EOF
+ endpos = FilePos((uint64_t)-1);
+ }
+
+ while (pos < endpos) {
+ seek(pos);
+ // read the edit data header
+ uint8_t edittype = et_editmetadata;
+ uint32_t editsize;
+ if (!readBlock(&edittype, sizeof(edittype), /*reporterror*/ false)) break;
+ if (!readBlock(&editsize, sizeof(editsize), /*reporterror*/ false)) break;
+ if (!editsize) break;
+ _hasEdits = true;
+ pos = tell() + editsize;
+ switch (edittype) {
+ case et_editfacedata: readEditFaceData(); break;
+ case et_editmetadata: readEditMetaData(); break;
+ }
+ }
+}
+
+
+void PtexReader::readEditFaceData()
+{
+ // read header
+ EditFaceDataHeader efdh;
+ if (!readBlock(&efdh, EditFaceDataHeaderSize)) return;
+
+ // update face info
+ int faceid = efdh.faceid;
+ if (faceid < 0 || size_t(faceid) >= _header.nfaces) return;
+ FaceInfo& f = _faceinfo[faceid];
+ f = efdh.faceinfo;
+ f.flags |= FaceInfo::flag_hasedits;
+
+ // read const value now
+ uint8_t* constdata = _constdata + _pixelsize * faceid;
+ if (!readBlock(constdata, _pixelsize)) return;
+ if (_premultiply && _header.hasAlpha())
+ PtexUtils::multalpha(constdata, 1, _header.datatype,
+ _header.nchannels, _header.alphachan);
+
+ // update header info for remaining data
+ if (!f.isConstant()) {
+ _faceedits.push_back(FaceEdit());
+ FaceEdit& e = _faceedits.back();
+ e.pos = tell();
+ e.faceid = faceid;
+ e.fdh = efdh.fdh;
+ }
+}
+
+
+void PtexReader::readEditMetaData()
+{
+ // read header
+ EditMetaDataHeader emdh;
+ if (!readBlock(&emdh, EditMetaDataHeaderSize)) return;
+
+ // record header info for later
+ _metaedits.push_back(MetaEdit());
+ MetaEdit& e = _metaedits.back();
+ e.pos = tell();
+ e.zipsize = emdh.metadatazipsize;
+ e.memsize = emdh.metadatamemsize;
+}
+
+
+bool PtexReader::readBlock(void* data, int size, bool reporterror)
+{
+ int result = _io->read(data, size, _fp);
+ if (result == size) {
+ _pos += size;
+ STATS_INC(nblocksRead);
+ STATS_ADD(nbytesRead, size);
+ return 1;
+ }
+ if (reporterror)
+ setError("PtexReader error: read failed (EOF)");
+ return 0;
+}
+
+
+bool PtexReader::readZipBlock(void* data, int zipsize, int unzipsize)
+{
+ void* buff = alloca(BlockSize);
+ _zstream.next_out = (Bytef*) data;
+ _zstream.avail_out = unzipsize;
+
+ while (1) {
+ int size = (zipsize < BlockSize) ? zipsize : BlockSize;
+ zipsize -= size;
+ if (!readBlock(buff, size)) break;
+ _zstream.next_in = (Bytef*) buff;
+ _zstream.avail_in = size;
+ int zresult = inflate(&_zstream, zipsize ? Z_NO_FLUSH : Z_FINISH);
+ if (zresult == Z_STREAM_END) break;
+ if (zresult != Z_OK) {
+ setError("PtexReader error: unzip failed, file corrupt");
+ inflateReset(&_zstream);
+ return 0;
+ }
+ }
+
+ int total = _zstream.total_out;
+ inflateReset(&_zstream);
+ return total == unzipsize;
+}
+
+
+void PtexReader::readLevel(int levelid, Level*& level)
+{
+ // temporarily release cache lock so other threads can proceed
+ _cache->cachelock.unlock();
+
+ // get read lock and make sure we still need to read
+ AutoMutex locker(readlock);
+ if (level) {
+ // another thread must have read it while we were waiting
+ _cache->cachelock.lock();
+ // make sure it's still there now that we have the lock
+ if (level) {
+ level->ref();
+ return;
+ }
+ _cache->cachelock.unlock();
+ }
+
+ // go ahead and read the level
+ LevelInfo& l = _levelinfo[levelid];
+
+ // keep new level local until finished
+ Level* volatile newlevel = new Level((void**)&level, _cache, l.nfaces);
+ seek(_levelpos[levelid]);
+ readZipBlock(&newlevel->fdh[0], l.levelheadersize, FaceDataHeaderSize * l.nfaces);
+ computeOffsets(tell(), l.nfaces, &newlevel->fdh[0], &newlevel->offsets[0]);
+
+ // apply edits (if any) to level 0
+ if (levelid == 0) {
+ for (size_t i = 0, size = _faceedits.size(); i < size; i++) {
+ FaceEdit& e = _faceedits[i];
+ newlevel->fdh[e.faceid] = e.fdh;
+ newlevel->offsets[e.faceid] = e.pos;
+ }
+ }
+
+ // don't assign to result until level data is fully initialized
+ _cache->cachelock.lock();
+ level = newlevel;
+
+ // clean up unused data
+ _cache->purgeData();
+}
+
+
+void PtexReader::readFace(int levelid, Level* level, int faceid)
+{
+ // temporarily release cache lock so other threads can proceed
+ _cache->cachelock.unlock();
+
+ // get read lock and make sure we still need to read
+ FaceData*& face = level->faces[faceid];
+ AutoMutex locker(readlock);
+
+ if (face) {
+ // another thread must have read it while we were waiting
+ _cache->cachelock.lock();
+ // make sure it's still there now that we have the lock
+ if (face) {
+ face->ref();
+ return;
+ }
+ _cache->cachelock.unlock();
+ }
+
+ // Go ahead and read the face, and read nearby faces if
+ // possible. The goal is to coalesce small faces into single
+ // runs of consecutive reads to minimize seeking and take
+ // advantage of read-ahead buffering.
+
+ // Try to read as many faces as will fit in BlockSize. Use the
+ // in-memory size rather than the on-disk size to prevent flooding
+ // the memory cache. And don't coalesce w/ tiled faces as these
+ // are meant to be read individually.
+
+ // scan both backwards and forwards looking for unread faces
+ int first = faceid, last = faceid;
+ int totalsize = 0;
+
+ FaceDataHeader fdh = level->fdh[faceid];
+ if (fdh.encoding() != enc_tiled) {
+ totalsize += unpackedSize(fdh, levelid, faceid);
+
+ int nfaces = int(level->fdh.size());
+ while (1) {
+ int f = first-1;
+ if (f < 0 || level->faces[f]) break;
+ fdh = level->fdh[f];
+ if (fdh.encoding() == enc_tiled) break;
+ int size = totalsize + unpackedSize(fdh, levelid, f);
+ if (size > BlockSize) break;
+ first = f;
+ totalsize = size;
+ }
+ while (1) {
+ int f = last+1;
+ if (f >= nfaces || level->faces[f]) break;
+ fdh = level->fdh[f];
+ if (fdh.encoding() == enc_tiled) break;
+ int size = totalsize + unpackedSize(fdh, levelid, f);
+ if (size > BlockSize) break;
+ last = f;
+ totalsize = size;
+ }
+ }
+
+ // read all faces in range
+ // keep track of extra faces we read so we can add them to the cache later
+ std::vector<FaceData*> extraFaces;
+ extraFaces.reserve(last-first);
+
+ for (int i = first; i <= last; i++) {
+ fdh = level->fdh[i];
+ // skip faces with zero size (which is true for level-0 constant faces)
+ if (fdh.blocksize()) {
+ FaceData*& face = level->faces[i];
+ readFaceData(level->offsets[i], fdh, getRes(levelid, i), levelid, face);
+ if (i != faceid) extraFaces.push_back(face);
+ }
+ }
+
+ // reacquire cache lock, then unref extra faces to add them to the cache
+ _cache->cachelock.lock();
+ for (size_t i = 0, size = extraFaces.size(); i < size; i++)
+ extraFaces[i]->unref();
+}
+
+
+void PtexReader::TiledFace::readTile(int tile, FaceData*& data)
+{
+ // temporarily release cache lock so other threads can proceed
+ _cache->cachelock.unlock();
+
+ // get read lock and make sure we still need to read
+ AutoMutex locker(_reader->readlock);
+ if (data) {
+ // another thread must have read it while we were waiting
+ _cache->cachelock.lock();
+ // make sure it's still there now that we have the lock
+ if (data) {
+ data->ref();
+ return;
+ }
+ _cache->cachelock.unlock();
+ }
+
+ // go ahead and read the face data
+ _reader->readFaceData(_offsets[tile], _fdh[tile], _tileres, _levelid, data);
+ _cache->cachelock.lock();
+
+ // clean up unused data
+ _cache->purgeData();
+}
+
+
+void PtexReader::readFaceData(FilePos pos, FaceDataHeader fdh, Res res, int levelid,
+ FaceData*& face)
+{
+ // keep new face local until fully initialized
+ FaceData* volatile newface = 0;
+
+ seek(pos);
+ switch (fdh.encoding()) {
+ case enc_constant:
+ {
+ ConstantFace* pf = new ConstantFace((void**)&face, _cache, _pixelsize);
+ readBlock(pf->data(), _pixelsize);
+ if (levelid==0 && _premultiply && _header.hasAlpha())
+ PtexUtils::multalpha(pf->data(), 1, _header.datatype,
+ _header.nchannels, _header.alphachan);
+ newface = pf;
+ }
+ break;
+ case enc_tiled:
+ {
+ Res tileres;
+ readBlock(&tileres, sizeof(tileres));
+ uint32_t tileheadersize;
+ readBlock(&tileheadersize, sizeof(tileheadersize));
+ TiledFace* tf = new TiledFace((void**)&face, _cache, res, tileres, levelid, this);
+ readZipBlock(&tf->_fdh[0], tileheadersize, FaceDataHeaderSize * tf->_ntiles);
+ computeOffsets(tell(), tf->_ntiles, &tf->_fdh[0], &tf->_offsets[0]);
+ newface = tf;
+ }
+ break;
+ case enc_zipped:
+ case enc_diffzipped:
+ {
+ int uw = res.u(), vw = res.v();
+ int npixels = uw * vw;
+ int unpackedSize = _pixelsize * npixels;
+ PackedFace* pf = new PackedFace((void**)&face, _cache,
+ res, _pixelsize, unpackedSize);
+ void* tmp = alloca(unpackedSize);
+ readZipBlock(tmp, fdh.blocksize(), unpackedSize);
+ if (fdh.encoding() == enc_diffzipped)
+ PtexUtils::decodeDifference(tmp, unpackedSize, _header.datatype);
+ PtexUtils::interleave(tmp, uw * DataSize(_header.datatype), uw, vw,
+ pf->data(), uw * _pixelsize,
+ _header.datatype, _header.nchannels);
+ if (levelid==0 && _premultiply && _header.hasAlpha())
+ PtexUtils::multalpha(pf->data(), npixels, _header.datatype,
+ _header.nchannels, _header.alphachan);
+ newface = pf;
+ }
+ break;
+ }
+
+ face = newface;
+}
+
+
+void PtexReader::getData(int faceid, void* buffer, int stride)
+{
+ if (!_ok) return;
+ FaceInfo& f = _faceinfo[faceid];
+ getData(faceid, buffer, stride, f.res);
+}
+
+
+void PtexReader::getData(int faceid, void* buffer, int stride, Res res)
+{
+ if (!_ok) return;
+
+ // note - all locking is handled in called getData methods
+ int resu = res.u(), resv = res.v();
+ int rowlen = _pixelsize * resu;
+ if (stride == 0) stride = rowlen;
+
+ PtexPtr<PtexFaceData> d ( getData(faceid, res) );
+ if (!d) return;
+ if (d->isConstant()) {
+ // fill dest buffer with pixel value
+ PtexUtils::fill(d->getData(), buffer, stride,
+ resu, resv, _pixelsize);
+ }
+ else if (d->isTiled()) {
+ // loop over tiles
+ Res tileres = d->tileRes();
+ int ntilesu = res.ntilesu(tileres);
+ int ntilesv = res.ntilesv(tileres);
+ int tileures = tileres.u();
+ int tilevres = tileres.v();
+ int tilerowlen = _pixelsize * tileures;
+ int tile = 0;
+ char* dsttilerow = (char*) buffer;
+ for (int i = 0; i < ntilesv; i++) {
+ char* dsttile = dsttilerow;
+ for (int j = 0; j < ntilesu; j++) {
+ PtexPtr<PtexFaceData> t ( d->getTile(tile++) );
+ if (!t) { i = ntilesv; break; }
+ if (t->isConstant())
+ PtexUtils::fill(t->getData(), dsttile, stride,
+ tileures, tilevres, _pixelsize);
+ else
+ PtexUtils::copy(t->getData(), tilerowlen, dsttile, stride,
+ tilevres, tilerowlen);
+ dsttile += tilerowlen;
+ }
+ dsttilerow += stride * tilevres;
+ }
+ }
+ else {
+ PtexUtils::copy(d->getData(), rowlen, buffer, stride, resv, rowlen);
+ }
+}
+
+
+PtexFaceData* PtexReader::getData(int faceid)
+{
+ if (faceid < 0 || size_t(faceid) >= _header.nfaces) return 0;
+ if (!_ok) return 0;
+
+ FaceInfo& fi = _faceinfo[faceid];
+ if (fi.isConstant() || fi.res == 0) {
+ return new ConstDataPtr(getConstData() + faceid * _pixelsize, _pixelsize);
+ }
+
+ // get level zero (full) res face
+ AutoLockCache locker(_cache->cachelock);
+ Level* level = getLevel(0);
+ FaceData* face = getFace(0, level, faceid);
+ level->unref();
+ return face;
+}
+
+
+PtexFaceData* PtexReader::getData(int faceid, Res res)
+{
+ if (!_ok) return 0;
+ if (faceid < 0 || size_t(faceid) >= _header.nfaces) return 0;
+
+ FaceInfo& fi = _faceinfo[faceid];
+ if ((fi.isConstant() && res >= 0) || res == 0) {
+ return new ConstDataPtr(getConstData() + faceid * _pixelsize, _pixelsize);
+ }
+
+ // lock cache (can't autolock since we might need to unlock early)
+ _cache->cachelock.lock();
+
+
+ // determine how many reduction levels are needed
+ int redu = fi.res.ulog2 - res.ulog2, redv = fi.res.vlog2 - res.vlog2;
+
+ if (redu == 0 && redv == 0) {
+ // no reduction - get level zero (full) res face
+ Level* level = getLevel(0);
+ FaceData* face = getFace(0, level, faceid);
+ level->unref();
+ _cache->cachelock.unlock();
+ return face;
+ }
+
+ if (redu == redv && !fi.hasEdits() && res >= 0) {
+ // reduction is symmetric and non-negative
+ // and face has no edits => access data from reduction level (if present)
+ int levelid = redu;
+ if (size_t(levelid) < _levels.size()) {
+ Level* level = getLevel(levelid);
+
+ // get reduction face id
+ int rfaceid = _rfaceids[faceid];
+
+ // get the face data (if present)
+ FaceData* face = 0;
+ if (size_t(rfaceid) < level->faces.size())
+ face = getFace(levelid, level, rfaceid);
+ level->unref();
+ if (face) {
+ _cache->cachelock.unlock();
+ return face;
+ }
+ }
+ }
+
+ // dynamic reduction required - look in dynamic reduction cache
+ FaceData*& face = _reductions[ReductionKey(faceid, res)];
+ if (face) {
+ face->ref();
+ _cache->cachelock.unlock();
+ return face;
+ }
+
+ // not found, generate new reduction
+ // unlock cache - getData and reduce will handle their own locking
+ _cache->cachelock.unlock();
+
+ if (res.ulog2 < 0 || res.vlog2 < 0) {
+ std::cerr << "PtexReader::getData - reductions below 1 pixel not supported" << std::endl;
+ return 0;
+ }
+ if (redu < 0 || redv < 0) {
+ std::cerr << "PtexReader::getData - enlargements not supported" << std::endl;
+ return 0;
+ }
+ if (_header.meshtype == mt_triangle)
+ {
+ if (redu != redv) {
+ std::cerr << "PtexReader::getData - anisotropic reductions not supported for triangle mesh" << std::endl;
+ return 0;
+ }
+ PtexPtr<PtexFaceData> psrc ( getData(faceid, Res(res.ulog2+1, res.vlog2+1)) );
+ FaceData* src = dynamic_cast<FaceData*>(psrc.get());
+ assert(src);
+ if (src) src->reduce(face, this, res, PtexUtils::reduceTri);
+ return face;
+ }
+
+ // determine which direction to blend
+ bool blendu;
+ if (redu == redv) {
+ // for symmetric face blends, alternate u and v blending
+ blendu = (res.ulog2 & 1);
+ }
+ else blendu = redu > redv;
+
+ if (blendu) {
+ // get next-higher u-res and reduce in u
+ PtexPtr<PtexFaceData> psrc ( getData(faceid, Res(res.ulog2+1, res.vlog2)) );
+ FaceData* src = dynamic_cast<FaceData*>(psrc.get());
+ assert(src);
+ if (src) src->reduce(face, this, res, PtexUtils::reduceu);
+ }
+ else {
+ // get next-higher v-res and reduce in v
+ PtexPtr<PtexFaceData> psrc ( getData(faceid, Res(res.ulog2, res.vlog2+1)) );
+ FaceData* src = dynamic_cast<FaceData*>(psrc.get());
+ assert(src);
+ if (src) src->reduce(face, this, res, PtexUtils::reducev);
+ }
+
+ return face;
+}
+
+
+void PtexReader::blendFaces(FaceData*& face, int faceid, Res res, bool blendu)
+{
+ Res pres; // parent res, 1 higher in blend direction
+ int length; // length of blend edge (1xN or Nx1)
+ int e1, e2; // neighboring edge ids
+ if (blendu) {
+ assert(res.ulog2 < 0); // res >= 0 requires reduction, not blending
+ length = (res.vlog2 <= 0 ? 1 : res.v());
+ e1 = e_bottom; e2 = e_top;
+ pres = Res(res.ulog2+1, res.vlog2);
+ }
+ else {
+ assert(res.vlog2 < 0);
+ length = (res.ulog2 <= 0 ? 1 : res.u());
+ e1 = e_right; e2 = e_left;
+ pres = Res(res.ulog2, res.vlog2+1);
+ }
+
+ // get neighbor face ids
+ FaceInfo& f = _faceinfo[faceid];
+ int nf1 = f.adjfaces[e1], nf2 = f.adjfaces[e2];
+
+ // compute rotation of faces relative to current
+ int r1 = (f.adjedge(e1)-e1+2)&3;
+ int r2 = (f.adjedge(e2)-e2+2)&3;
+
+ // swap u and v res for faces rotated +/- 90 degrees
+ Res pres1 = pres, pres2 = pres;
+ if (r1 & 1) pres1.swapuv();
+ if (r2 & 1) pres2.swapuv();
+
+ // ignore faces that have insufficient res (unlikely, but possible)
+ if (nf1 >= 0 && !(_faceinfo[nf1].res >= pres)) nf1 = -1;
+ if (nf2 >= 0 && !(_faceinfo[nf2].res >= pres)) nf2 = -1;
+
+ // get parent face data
+ int nf = 1; // number of faces to blend (1 to 3)
+ bool flip[3]; // true if long dimension needs to be flipped
+ PtexFaceData* psrc[3]; // the face data
+ psrc[0] = getData(faceid, pres);
+ flip[0] = 0; // don't flip main face
+ if (nf1 >= 0) {
+ // face must be flipped if rot is 1 or 2 for blendu, or 2 or 3 for blendv
+ // thus, just add the blendu bool val to align the ranges and check bit 1
+ // also, no need to flip if length is zero
+ flip[nf] = length ? (r1 + blendu) & 1 : 0;
+ psrc[nf++] = getData(nf1, pres1);
+ }
+ if (nf2 >= 0) {
+ flip[nf] = length ? (r2 + blendu) & 1 : 0;
+ psrc[nf++] = getData(nf2, pres2);
+ }
+
+ // get reduce lock and make sure we still need to reduce
+ AutoMutex rlocker(reducelock);
+ if (face) {
+ // another thread must have generated it while we were waiting
+ AutoLockCache locker(_cache->cachelock);
+ // make sure it's still there now that we have the lock
+ if (face) {
+ face->ref();
+ // release parent data
+ for (int i = 0; i < nf; i++) psrc[i]->release();
+ return;
+ }
+ }
+
+ // allocate a new face data (1 x N or N x 1)
+ DataType dt = datatype();
+ int nchan = nchannels();
+ int size = _pixelsize * length;
+ PackedFace* pf = new PackedFace((void**)&face, _cache, res,
+ _pixelsize, size);
+ void* data = pf->getData();
+ if (nf == 1) {
+ // no neighbors - just copy face
+ memcpy(data, psrc[0]->getData(), size);
+ }
+ else {
+ float weight = 1.0f / nf;
+ memset(data, 0, size);
+ for (int i = 0; i < nf; i++)
+ PtexUtils::blend(psrc[i]->getData(), weight, data, flip[i],
+ length, dt, nchan);
+ }
+
+ {
+ AutoLockCache clocker(_cache->cachelock);
+ face = pf;
+
+ // clean up unused data
+ _cache->purgeData();
+ }
+
+ // release parent data
+ for (int i = 0; i < nf; i++) psrc[i]->release();
+}
+
+
+void PtexReader::getPixel(int faceid, int u, int v,
+ float* result, int firstchan, int nchannels)
+{
+ memset(result, 0, nchannels);
+
+ // clip nchannels against actual number available
+ nchannels = PtexUtils::min(nchannels,
+ _header.nchannels-firstchan);
+ if (nchannels <= 0) return;
+
+ // get raw pixel data
+ PtexPtr<PtexFaceData> data ( getData(faceid) );
+ if (!data) return;
+ void* pixel = alloca(_pixelsize);
+ data->getPixel(u, v, pixel);
+
+ // adjust for firstchan offset
+ int datasize = DataSize(_header.datatype);
+ if (firstchan)
+ pixel = (char*) pixel + datasize * firstchan;
+
+ // convert/copy to result as needed
+ if (_header.datatype == dt_float)
+ memcpy(result, pixel, datasize * nchannels);
+ else
+ ConvertToFloat(result, pixel, _header.datatype, nchannels);
+}
+
+
+void PtexReader::getPixel(int faceid, int u, int v,
+ float* result, int firstchan, int nchannels,
+ Ptex::Res res)
+{
+ memset(result, 0, nchannels);
+
+ // clip nchannels against actual number available
+ nchannels = PtexUtils::min(nchannels,
+ _header.nchannels-firstchan);
+ if (nchannels <= 0) return;
+
+ // get raw pixel data
+ PtexPtr<PtexFaceData> data ( getData(faceid, res) );
+ if (!data) return;
+ void* pixel = alloca(_pixelsize);
+ data->getPixel(u, v, pixel);
+
+ // adjust for firstchan offset
+ int datasize = DataSize(_header.datatype);
+ if (firstchan)
+ pixel = (char*) pixel + datasize * firstchan;
+
+ // convert/copy to result as needed
+ if (_header.datatype == dt_float)
+ memcpy(result, pixel, datasize * nchannels);
+ else
+ ConvertToFloat(result, pixel, _header.datatype, nchannels);
+}
+
+
+void PtexReader::PackedFace::reduce(FaceData*& face, PtexReader* r,
+ Res newres, PtexUtils::ReduceFn reducefn)
+{
+ // get reduce lock and make sure we still need to reduce
+ AutoMutex rlocker(r->reducelock);
+ if (face) {
+ // another thread must have generated it while we were waiting
+ AutoLockCache clocker(_cache->cachelock);
+ // make sure it's still there now that we have the lock
+ if (face) {
+ face->ref();
+ return;
+ }
+ }
+
+ // allocate a new face and reduce image
+ DataType dt = r->datatype();
+ int nchan = r->nchannels();
+ PackedFace* pf = new PackedFace((void**)&face, _cache, newres,
+ _pixelsize, _pixelsize * newres.size());
+ // reduce and copy into new face
+ reducefn(_data, _pixelsize * _res.u(), _res.u(), _res.v(),
+ pf->_data, _pixelsize * newres.u(), dt, nchan);
+ AutoLockCache clocker(_cache->cachelock);
+ face = pf;
+
+ // clean up unused data
+ _cache->purgeData();
+}
+
+
+
+void PtexReader::ConstantFace::reduce(FaceData*& face, PtexReader*,
+ Res, PtexUtils::ReduceFn)
+{
+ // get cache lock (just to protect the ref count)
+ AutoLockCache locker(_cache->cachelock);
+
+ // must make a new constant face (even though it's identical to this one)
+ // because it will be owned by a different reduction level
+ // and will therefore have a different parent
+ ConstantFace* pf = new ConstantFace((void**)&face, _cache, _pixelsize);
+ memcpy(pf->_data, _data, _pixelsize);
+ face = pf;
+}
+
+
+void PtexReader::TiledFaceBase::reduce(FaceData*& face, PtexReader* r,
+ Res newres, PtexUtils::ReduceFn reducefn)
+{
+ // get reduce lock and make sure we still need to reduce
+ AutoMutex rlocker(r->reducelock);
+ if (face) {
+ // another thread must have generated it while we were waiting
+ AutoLockCache clocker(_cache->cachelock);
+ // make sure it's still there now that we have the lock
+ if (face) {
+ face->ref();
+ return;
+ }
+ }
+
+ /* Tiled reductions should generally only be anisotropic (just u
+ or v, not both) since isotropic reductions are precomputed and
+ stored on disk. (This function should still work for isotropic
+ reductions though.)
+
+ In the anisotropic case, the number of tiles should be kept the
+ same along the direction not being reduced in order to preserve
+ the laziness of the file access. In contrast, if reductions
+ were not tiled, then any reduction would read all the tiles and
+ defeat the purpose of tiling.
+ */
+
+ // keep new face local until fully initialized
+ FaceData* volatile newface = 0;
+
+ // don't tile if either dimension is 1 (rare, would complicate blendFaces too much)
+ Res newtileres;
+ if (newres.ulog2 == 1 || newres.vlog2 == 1) {
+ newtileres = newres;
+ }
+ else {
+ // propagate the tile res to the reduction
+ newtileres = _tileres;
+ // but make sure tile isn't larger than the new face!
+ if (newtileres.ulog2 > newres.ulog2) newtileres.ulog2 = newres.ulog2;
+ if (newtileres.vlog2 > newres.vlog2) newtileres.vlog2 = newres.vlog2;
+ }
+
+
+ // determine how many tiles we will have on the reduction
+ int newntiles = newres.ntiles(newtileres);
+
+ if (newntiles == 1) {
+ // no need to keep tiling, reduce tiles into a single face
+ // first, get all tiles and check if they are constant (with the same value)
+ PtexFaceData** tiles = (PtexFaceData**) alloca(_ntiles * sizeof(PtexFaceData*));
+ bool allConstant = true;
+ for (int i = 0; i < _ntiles; i++) {
+ PtexFaceData* tile = tiles[i] = getTile(i);
+ allConstant = (allConstant && tile->isConstant() &&
+ (i == 0 || (0 == memcmp(tiles[0]->getData(), tile->getData(),
+ _pixelsize))));
+ }
+ if (allConstant) {
+ // allocate a new constant face
+ newface = new ConstantFace((void**)&face, _cache, _pixelsize);
+ memcpy(newface->getData(), tiles[0]->getData(), _pixelsize);
+ }
+ else {
+ // allocate a new packed face
+ newface = new PackedFace((void**)&face, _cache, newres,
+ _pixelsize, _pixelsize*newres.size());
+
+ int tileures = _tileres.u();
+ int tilevres = _tileres.v();
+ int sstride = _pixelsize * tileures;
+ int dstride = _pixelsize * newres.u();
+ int dstepu = dstride/_ntilesu;
+ int dstepv = dstride*newres.v()/_ntilesv - dstepu*(_ntilesu-1);
+
+ char* dst = (char*) newface->getData();
+ for (int i = 0; i < _ntiles;) {
+ PtexFaceData* tile = tiles[i];
+ if (tile->isConstant())
+ PtexUtils::fill(tile->getData(), dst, dstride,
+ newres.u()/_ntilesu, newres.v()/_ntilesv,
+ _pixelsize);
+ else
+ reducefn(tile->getData(), sstride, tileures, tilevres,
+ dst, dstride, _dt, _nchan);
+ i++;
+ dst += i%_ntilesu ? dstepu : dstepv;
+ }
+ }
+ // release the tiles
+ for (int i = 0; i < _ntiles; i++) tiles[i]->release();
+ }
+ else {
+ // otherwise, tile the reduced face
+ newface = new TiledReducedFace((void**)&face, _cache, newres, newtileres,
+ _dt, _nchan, this, reducefn);
+ }
+ AutoLockCache clocker(_cache->cachelock);
+ face = newface;
+
+ // clean up unused data
+ _cache->purgeData();
+}
+
+
+void PtexReader::TiledFaceBase::getPixel(int ui, int vi, void* result)
+{
+ int tileu = ui >> _tileres.ulog2;
+ int tilev = vi >> _tileres.vlog2;
+ PtexPtr<PtexFaceData> tile ( getTile(tilev * _ntilesu + tileu) );
+ tile->getPixel(ui - (tileu<<_tileres.ulog2),
+ vi - (tilev<<_tileres.vlog2), result);
+}
+
+
+
+PtexFaceData* PtexReader::TiledReducedFace::getTile(int tile)
+{
+ _cache->cachelock.lock();
+ FaceData*& face = _tiles[tile];
+ if (face) {
+ face->ref();
+ _cache->cachelock.unlock();
+ return face;
+ }
+
+ // first, get all parent tiles for this tile
+ // and check if they are constant (with the same value)
+ int pntilesu = _parentface->ntilesu();
+ int pntilesv = _parentface->ntilesv();
+ int nu = pntilesu / _ntilesu; // num parent tiles for this tile in u dir
+ int nv = pntilesv / _ntilesv; // num parent tiles for this tile in v dir
+
+ int ntiles = nu*nv; // num parent tiles for this tile
+ PtexFaceData** tiles = (PtexFaceData**) alloca(ntiles * sizeof(PtexFaceData*));
+ bool allConstant = true;
+ int ptile = (tile/_ntilesu) * nv * pntilesu + (tile%_ntilesu) * nu;
+ for (int i = 0; i < ntiles;) {
+ // temporarily release cache lock while we get parent tile
+ _cache->cachelock.unlock();
+ PtexFaceData* tile = tiles[i] = _parentface->getTile(ptile);
+ _cache->cachelock.lock();
+ allConstant = (allConstant && tile->isConstant() &&
+ (i==0 || (0 == memcmp(tiles[0]->getData(), tile->getData(),
+ _pixelsize))));
+ i++;
+ ptile += i%nu? 1 : pntilesu - nu + 1;
+ }
+
+ // make sure another thread didn't make the tile while we were checking
+ if (face) {
+ face->ref();
+ _cache->cachelock.unlock();
+ // release the tiles
+ for (int i = 0; i < ntiles; i++) tiles[i]->release();
+ return face;
+ }
+
+ if (allConstant) {
+ // allocate a new constant face
+ face = new ConstantFace((void**)&face, _cache, _pixelsize);
+ memcpy(face->getData(), tiles[0]->getData(), _pixelsize);
+ }
+ else {
+ // allocate a new packed face for the tile
+ face = new PackedFace((void**)&face, _cache, _tileres,
+ _pixelsize, _pixelsize*_tileres.size());
+
+ // generate reduction from parent tiles
+ int ptileures = _parentface->tileres().u();
+ int ptilevres = _parentface->tileres().v();
+ int sstride = ptileures * _pixelsize;
+ int dstride = _tileres.u() * _pixelsize;
+ int dstepu = dstride/nu;
+ int dstepv = dstride*_tileres.v()/nv - dstepu*(nu-1);
+
+ char* dst = (char*) face->getData();
+ for (int i = 0; i < ntiles;) {
+ PtexFaceData* tile = tiles[i];
+ if (tile->isConstant())
+ PtexUtils::fill(tile->getData(), dst, dstride,
+ _tileres.u()/nu, _tileres.v()/nv,
+ _pixelsize);
+ else
+ _reducefn(tile->getData(), sstride, ptileures, ptilevres,
+ dst, dstride, _dt, _nchan);
+ i++;
+ dst += i%nu ? dstepu : dstepv;
+ }
+ }
+
+ _cache->cachelock.unlock();
+
+ // release the tiles
+ for (int i = 0; i < ntiles; i++) tiles[i]->release();
+ return face;
+}
diff --git a/extern/ptex/src/ptex/PtexReader.h b/extern/ptex/src/ptex/PtexReader.h
new file mode 100644
index 00000000000..30bd2feded7
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexReader.h
@@ -0,0 +1,613 @@
+#ifndef PtexReader_h
+#define PtexReader_h
+
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+#include <stdio.h>
+#include <zlib.h>
+#include <vector>
+#include <string>
+#include <map>
+#include "Ptexture.h"
+#include "PtexIO.h"
+#include "PtexCache.h"
+#include "PtexUtils.h"
+
+#include "PtexHashMap.h"
+using namespace PtexInternal;
+
+#ifndef NDEBUG
+#include <assert.h>
+template<typename T> class safevector : public std::vector<T>
+{
+public:
+ safevector() : std::vector<T>() {}
+ safevector(size_t n, const T& val = T()) : std::vector<T>(n, val) {}
+ const T& operator[] (size_t n) const {
+ assert(n < std::vector<T>::size());
+ return std::vector<T>::operator[](n);
+ }
+ T& operator[] (size_t n) {
+ assert(n < std::vector<T>::size());
+ return std::vector<T>::operator[](n);
+ }
+};
+#else
+#define safevector std::vector
+#endif
+
+class PtexReader : public PtexCachedFile, public PtexTexture, public PtexIO {
+public:
+ PtexReader(void** parent, PtexCacheImpl* cache, bool premultiply,
+ PtexInputHandler* handler);
+ bool open(const char* path, Ptex::String& error);
+
+ void setOwnsCache() { _ownsCache = true; }
+ virtual void release();
+ virtual const char* path() { return _path.c_str(); }
+ virtual Ptex::MeshType meshType() { return MeshType(_header.meshtype); }
+ virtual Ptex::DataType dataType() { return DataType(_header.datatype); }
+ virtual Ptex::BorderMode uBorderMode() { return BorderMode(_extheader.ubordermode); };
+ virtual Ptex::BorderMode vBorderMode() { return BorderMode(_extheader.vbordermode); };
+ virtual int alphaChannel() { return _header.alphachan; }
+ virtual int numChannels() { return _header.nchannels; }
+ virtual int numFaces() { return _header.nfaces; }
+ virtual bool hasEdits() { return _hasEdits; }
+ virtual bool hasMipMaps() { return _header.nlevels > 1; }
+
+ virtual PtexMetaData* getMetaData();
+ virtual const Ptex::FaceInfo& getFaceInfo(int faceid);
+ virtual void getData(int faceid, void* buffer, int stride);
+ virtual void getData(int faceid, void* buffer, int stride, Res res);
+ virtual PtexFaceData* getData(int faceid);
+ virtual PtexFaceData* getData(int faceid, Res res);
+ virtual void getPixel(int faceid, int u, int v,
+ float* result, int firstchan, int nchannels);
+ virtual void getPixel(int faceid, int u, int v,
+ float* result, int firstchan, int nchannels,
+ Ptex::Res res);
+
+ DataType datatype() const { return _header.datatype; }
+ int nchannels() const { return _header.nchannels; }
+ int pixelsize() const { return _pixelsize; }
+ const Header& header() const { return _header; }
+ const ExtHeader& extheader() const { return _extheader; }
+ const LevelInfo& levelinfo(int level) const { return _levelinfo[level]; }
+
+ class MetaData : public PtexCachedData, public PtexMetaData {
+ public:
+ MetaData(MetaData** parent, PtexCacheImpl* cache, int size, PtexReader* reader)
+ : PtexCachedData((void**)parent, cache, sizeof(*this) + size),
+ _reader(reader) {}
+ virtual void release() {
+ AutoLockCache lock(_cache->cachelock);
+ // first, unref all lmdData refs
+ for (int i = 0, n = _lmdRefs.size(); i < n; i++)
+ _lmdRefs[i]->unref();
+ _lmdRefs.resize(0);
+
+ // finally, unref self
+ unref();
+ }
+
+ virtual int numKeys() { return int(_entries.size()); }
+ virtual void getKey(int n, const char*& key, MetaDataType& type)
+ {
+ Entry* e = _entries[n];
+ key = e->key;
+ type = e->type;
+ }
+
+ virtual void getValue(const char* key, const char*& value)
+ {
+ Entry* e = getEntry(key);
+ if (e) value = (const char*) e->data;
+ else value = 0;
+ }
+
+ virtual void getValue(const char* key, const int8_t*& value, int& count)
+ {
+ Entry* e = getEntry(key);
+ if (e) { value = (const int8_t*) e->data; count = e->datasize; }
+ else { value = 0; count = 0; }
+ }
+
+ virtual void getValue(const char* key, const int16_t*& value, int& count)
+ {
+ Entry* e = getEntry(key);
+ if (e) {
+ value = (const int16_t*) e->data;
+ count = int(e->datasize/sizeof(int16_t));
+ }
+ else { value = 0; count = 0; }
+ }
+
+ virtual void getValue(const char* key, const int32_t*& value, int& count)
+ {
+ Entry* e = getEntry(key);
+ if (e) {
+ value = (const int32_t*) e->data;
+ count = int(e->datasize/sizeof(int32_t));
+ }
+ else { value = 0; count = 0; }
+ }
+
+ virtual void getValue(const char* key, const float*& value, int& count)
+ {
+ Entry* e = getEntry(key);
+ if (e) {
+ value = (const float*) e->data;
+ count = int(e->datasize/sizeof(float));
+ }
+ else { value = 0; count = 0; }
+ }
+
+ virtual void getValue(const char* key, const double*& value, int& count)
+ {
+ Entry* e = getEntry(key);
+ if (e) {
+ value = (const double*) e->data;
+ count = int(e->datasize/sizeof(double));
+ }
+ else { value = 0; count = 0; }
+ }
+
+ void addEntry(uint8_t keysize, const char* key, uint8_t datatype,
+ uint32_t datasize, void* data)
+ {
+ Entry* e = newEntry(keysize, key, datatype, datasize);
+ e->data = malloc(datasize);
+ memcpy(e->data, data, datasize);
+ }
+
+ void addLmdEntry(uint8_t keysize, const char* key, uint8_t datatype,
+ uint32_t datasize, FilePos filepos, uint32_t zipsize)
+ {
+ Entry* e = newEntry(keysize, key, datatype, datasize);
+ e->isLmd = true;
+ e->lmdData = 0;
+ e->lmdPos = filepos;
+ e->lmdZipSize = zipsize;
+ }
+
+ protected:
+ class LargeMetaData : public PtexCachedData
+ {
+ public:
+ LargeMetaData(void** parent, PtexCacheImpl* cache, int size)
+ : PtexCachedData(parent, cache, size), _data(malloc(size)) {}
+ void* data() { return _data; }
+ private:
+ virtual ~LargeMetaData() { free(_data); }
+ void* _data;
+ };
+
+ struct Entry {
+ const char* key; // ptr to map key string
+ MetaDataType type; // meta data type
+ uint32_t datasize; // size of data in bytes
+ void* data; // if lmd, data only valid when lmd is loaded and ref'ed
+ bool isLmd; // true if data is a large meta data block
+ LargeMetaData* lmdData; // large meta data (lazy-loaded, lru-cached)
+ FilePos lmdPos; // large meta data file position
+ uint32_t lmdZipSize; // large meta data size on disk
+
+ Entry() :
+ key(0), type(MetaDataType(0)), datasize(0), data(0),
+ isLmd(0), lmdData(0), lmdPos(0), lmdZipSize(0) {}
+ ~Entry() { clear(); }
+ void clear() {
+ if (isLmd) {
+ isLmd = 0;
+ if (lmdData) { lmdData->orphan(); lmdData = 0; }
+ lmdPos = 0;
+ lmdZipSize = 0;
+ }
+ else {
+ free(data);
+ }
+ data = 0;
+ }
+ };
+
+ Entry* newEntry(uint8_t keysize, const char* key, uint8_t datatype, uint32_t datasize)
+ {
+ std::pair<MetaMap::iterator,bool> result =
+ _map.insert(std::make_pair(std::string(key, keysize), Entry()));
+ Entry* e = &result.first->second;
+ bool newEntry = result.second;
+ if (newEntry) _entries.push_back(e);
+ else e->clear();
+ e->key = result.first->first.c_str();
+ e->type = MetaDataType(datatype);
+ e->datasize = datasize;
+ return e;
+ }
+
+ Entry* getEntry(const char* key);
+
+ PtexReader* _reader;
+ typedef std::map<std::string, Entry> MetaMap;
+ MetaMap _map;
+ safevector<Entry*> _entries;
+ std::vector<LargeMetaData*> _lmdRefs;
+ };
+
+
+ class ConstDataPtr : public PtexFaceData {
+ public:
+ ConstDataPtr(void* data, int pixelsize)
+ : _data(data), _pixelsize(pixelsize) {}
+ virtual void release() { delete this; }
+ virtual Ptex::Res res() { return 0; }
+ virtual bool isConstant() { return true; }
+ virtual void getPixel(int, int, void* result)
+ { memcpy(result, _data, _pixelsize); }
+ virtual void* getData() { return _data; }
+ virtual bool isTiled() { return false; }
+ virtual Ptex::Res tileRes() { return 0; }
+ virtual PtexFaceData* getTile(int) { return 0; }
+
+ protected:
+ void* _data;
+ int _pixelsize;
+ };
+
+
+ class FaceData : public PtexCachedData, public PtexFaceData {
+ public:
+ FaceData(void** parent, PtexCacheImpl* cache, Res res, int size)
+ : PtexCachedData(parent, cache, size), _res(res) {}
+ virtual void release() { AutoLockCache lock(_cache->cachelock); unref(); }
+ virtual Ptex::Res res() { return _res; }
+ virtual void reduce(FaceData*&, PtexReader*,
+ Res newres, PtexUtils::ReduceFn) = 0;
+ protected:
+ Res _res;
+ };
+
+ class PackedFace : public FaceData {
+ public:
+ PackedFace(void** parent, PtexCacheImpl* cache, Res res, int pixelsize, int size)
+ : FaceData(parent, cache, res, sizeof(*this)+size),
+ _pixelsize(pixelsize), _data(malloc(size)) {}
+ void* data() { return _data; }
+ virtual bool isConstant() { return false; }
+ virtual void getPixel(int u, int v, void* result)
+ {
+ memcpy(result, (char*)_data + (v*_res.u() + u) * _pixelsize, _pixelsize);
+ }
+ virtual void* getData() { return _data; };
+ virtual bool isTiled() { return false; }
+ virtual Ptex::Res tileRes() { return _res; };
+ virtual PtexFaceData* getTile(int) { return 0; };
+ virtual void reduce(FaceData*&, PtexReader*,
+ Res newres, PtexUtils::ReduceFn);
+
+ protected:
+ virtual ~PackedFace() { free(_data); }
+
+ int _pixelsize;
+ void* _data;
+ };
+
+ class ConstantFace : public PackedFace {
+ public:
+ ConstantFace(void** parent, PtexCacheImpl* cache, int pixelsize)
+ : PackedFace(parent, cache, 0, pixelsize, pixelsize) {}
+ virtual bool isConstant() { return true; }
+ virtual void getPixel(int, int, void* result) { memcpy(result, _data, _pixelsize); }
+ virtual void reduce(FaceData*&, PtexReader*,
+ Res newres, PtexUtils::ReduceFn);
+ };
+
+
+ class TiledFaceBase : public FaceData {
+ public:
+ TiledFaceBase(void** parent, PtexCacheImpl* cache, Res res,
+ Res tileres, DataType dt, int nchan)
+ : FaceData(parent, cache, res, sizeof(*this)),
+ _tileres(tileres),
+ _dt(dt),
+ _nchan(nchan),
+ _pixelsize(DataSize(dt)*nchan)
+ {
+ _ntilesu = _res.ntilesu(tileres);
+ _ntilesv = _res.ntilesv(tileres);
+ _ntiles = _ntilesu*_ntilesv;
+ _tiles.resize(_ntiles);
+ incSize(sizeof(FaceData*)*_ntiles);
+ }
+
+ virtual void release() {
+ // Tiled faces ref the reader (directly or indirectly) and
+ // thus may trigger cache deletion on release. Call cache
+ // to check for pending delete.
+ // Note: release() may delete "this", so save _cache in
+ // local var.
+ PtexCacheImpl* cache = _cache;
+ FaceData::release();
+ cache->handlePendingDelete();
+ }
+ virtual bool isConstant() { return false; }
+ virtual void getPixel(int u, int v, void* result);
+ virtual void* getData() { return 0; }
+ virtual bool isTiled() { return true; }
+ virtual Ptex::Res tileRes() { return _tileres; };
+ virtual void reduce(FaceData*&, PtexReader*,
+ Res newres, PtexUtils::ReduceFn);
+ Res tileres() const { return _tileres; }
+ int ntilesu() const { return _ntilesu; }
+ int ntilesv() const { return _ntilesv; }
+ int ntiles() const { return _ntiles; }
+
+ protected:
+ virtual ~TiledFaceBase() { orphanList(_tiles); }
+
+ Res _tileres;
+ DataType _dt;
+ int _nchan;
+ int _ntilesu;
+ int _ntilesv;
+ int _ntiles;
+ int _pixelsize;
+ safevector<FaceData*> _tiles;
+ };
+
+
+ class TiledFace : public TiledFaceBase {
+ public:
+ TiledFace(void** parent, PtexCacheImpl* cache, Res res, Res tileres,
+ int levelid, PtexReader* reader)
+ : TiledFaceBase(parent, cache, res, tileres,
+ reader->datatype(), reader->nchannels()),
+ _reader(reader),
+ _levelid(levelid)
+ {
+ _fdh.resize(_ntiles),
+ _offsets.resize(_ntiles);
+ incSize((sizeof(FaceDataHeader)+sizeof(FilePos))*_ntiles);
+ }
+ virtual PtexFaceData* getTile(int tile)
+ {
+ AutoLockCache locker(_cache->cachelock);
+ FaceData*& f = _tiles[tile];
+ if (!f) readTile(tile, f);
+ else f->ref();
+ return f;
+ }
+ void readTile(int tile, FaceData*& data);
+
+ protected:
+ friend class PtexReader;
+ PtexReader* _reader;
+ int _levelid;
+ safevector<FaceDataHeader> _fdh;
+ safevector<FilePos> _offsets;
+ };
+
+
+ class TiledReducedFace : public TiledFaceBase {
+ public:
+ TiledReducedFace(void** parent, PtexCacheImpl* cache, Res res,
+ Res tileres, DataType dt, int nchan,
+ TiledFaceBase* parentface, PtexUtils::ReduceFn reducefn)
+ : TiledFaceBase(parent, cache, res, tileres, dt, nchan),
+ _parentface(parentface),
+ _reducefn(reducefn)
+ {
+ AutoLockCache locker(_cache->cachelock);
+ _parentface->ref();
+ }
+ ~TiledReducedFace()
+ {
+ _parentface->unref();
+ }
+ virtual PtexFaceData* getTile(int tile);
+
+ protected:
+ TiledFaceBase* _parentface;
+ PtexUtils::ReduceFn* _reducefn;
+ };
+
+
+ class Level : public PtexCachedData {
+ public:
+ safevector<FaceDataHeader> fdh;
+ safevector<FilePos> offsets;
+ safevector<FaceData*> faces;
+
+ Level(void** parent, PtexCacheImpl* cache, int nfaces)
+ : PtexCachedData(parent, cache,
+ sizeof(*this) + nfaces * (sizeof(FaceDataHeader) +
+ sizeof(FilePos) +
+ sizeof(FaceData*))),
+ fdh(nfaces),
+ offsets(nfaces),
+ faces(nfaces) {}
+ protected:
+ virtual ~Level() { orphanList(faces); }
+ };
+
+ Mutex readlock;
+ Mutex reducelock;
+
+protected:
+ virtual ~PtexReader();
+ void setError(const char* error)
+ {
+ _error = error; _error += " PtexFile: "; _error += _path;
+ _ok = 0;
+ }
+
+ FilePos tell() { return _pos; }
+ void seek(FilePos pos)
+ {
+ if (pos != _pos) {
+ _io->seek(_fp, pos);
+ _pos = pos;
+ STATS_INC(nseeks);
+ }
+ }
+
+ bool readBlock(void* data, int size, bool reportError=true);
+ bool readZipBlock(void* data, int zipsize, int unzipsize);
+ Level* getLevel(int levelid)
+ {
+ Level*& level = _levels[levelid];
+ if (!level) readLevel(levelid, level);
+ else level->ref();
+ return level;
+ }
+
+ uint8_t* getConstData() { if (!_constdata) readConstData(); return _constdata; }
+ FaceData* getFace(int levelid, Level* level, int faceid)
+ {
+ FaceData*& face = level->faces[faceid];
+ if (!face) readFace(levelid, level, faceid);
+ else face->ref();
+ return face;
+ }
+
+ Res getRes(int levelid, int faceid)
+ {
+ if (levelid == 0) return _faceinfo[faceid].res;
+ else {
+ // for reduction level, look up res via rfaceid
+ Res res = _res_r[faceid];
+ // and adjust for number of reductions
+ return Res(res.ulog2 - levelid, res.vlog2 - levelid);
+ }
+ }
+
+ int unpackedSize(FaceDataHeader fdh, int levelid, int faceid)
+ {
+ if (fdh.encoding() == enc_constant)
+ // level 0 constant faces are not stored
+ return levelid == 0 ? 0 : _pixelsize;
+ else
+ return getRes(levelid, faceid).size() * _pixelsize;
+ }
+
+ void readFaceInfo();
+ void readLevelInfo();
+ void readConstData();
+ void readLevel(int levelid, Level*& level);
+ void readFace(int levelid, Level* level, int faceid);
+ void readFaceData(FilePos pos, FaceDataHeader fdh, Res res, int levelid, FaceData*& face);
+ void readMetaData();
+ void readMetaDataBlock(MetaData* metadata, FilePos pos, int zipsize, int memsize);
+ void readLargeMetaDataHeaders(MetaData* metadata, FilePos pos, int zipsize, int memsize);
+ void readEditData();
+ void readEditFaceData();
+ void readEditMetaData();
+
+ void computeOffsets(FilePos pos, int noffsets, const FaceDataHeader* fdh, FilePos* offsets)
+ {
+ FilePos* end = offsets + noffsets;
+ while (offsets != end) { *offsets++ = pos; pos += fdh->blocksize(); fdh++; }
+ }
+ void blendFaces(FaceData*& face, int faceid, Res res, bool blendu);
+
+ PtexInputHandler* _io; // IO handler
+ bool _premultiply; // true if reader should premultiply the alpha chan
+ bool _ownsCache; // true if reader owns the cache
+ bool _ok; // flag set if read error occurred)
+ std::string _error; // error string (if !_ok)
+ PtexInputHandler::Handle _fp; // file pointer
+ FilePos _pos; // current seek position
+ std::string _path; // current file path
+ Header _header; // the header
+ ExtHeader _extheader; // extended header
+ FilePos _faceinfopos; // file positions of data sections
+ FilePos _constdatapos; // ...
+ FilePos _levelinfopos;
+ FilePos _leveldatapos;
+ FilePos _metadatapos;
+ FilePos _lmdheaderpos;
+ FilePos _lmddatapos;
+ FilePos _editdatapos;
+ int _pixelsize; // size of a pixel in bytes
+ uint8_t* _constdata; // constant pixel value per face
+ MetaData* _metadata; // meta data (read on demand)
+ bool _hasEdits; // has edit blocks
+
+ safevector<FaceInfo> _faceinfo; // per-face header info
+ safevector<uint32_t> _rfaceids; // faceids sorted in reduction order
+ safevector<Res> _res_r; // face res indexed by rfaceid
+ safevector<LevelInfo> _levelinfo; // per-level header info
+ safevector<FilePos> _levelpos; // file position of each level's data
+ safevector<Level*> _levels; // level data (read on demand)
+
+ struct MetaEdit
+ {
+ FilePos pos;
+ int zipsize;
+ int memsize;
+ };
+ safevector<MetaEdit> _metaedits;
+
+ struct FaceEdit
+ {
+ FilePos pos;
+ int faceid;
+ FaceDataHeader fdh;
+ };
+ safevector<FaceEdit> _faceedits;
+
+ struct ReductionKey {
+ int faceid;
+ Res res;
+ ReductionKey() : faceid(0), res(0,0) {}
+ ReductionKey(uint32_t faceid, Res res) : faceid(faceid), res(res) {}
+ bool operator==(const ReductionKey& k) const
+ { return k.faceid == faceid && k.res == res; }
+ struct Hasher {
+ uint32_t operator() (const ReductionKey& key) const
+ {
+ // constants from Knuth
+ static uint32_t M = 1664525, C = 1013904223;
+ uint32_t val = (key.res.ulog2 * M + key.res.vlog2 + C) * M + key.faceid;
+ return val;
+ }
+ };
+ };
+ typedef PtexHashMap<ReductionKey, FaceData*, ReductionKey::Hasher> ReductionMap;
+ ReductionMap _reductions;
+
+ z_stream_s _zstream;
+};
+
+#endif
diff --git a/extern/ptex/src/ptex/PtexSeparableFilter.cpp b/extern/ptex/src/ptex/PtexSeparableFilter.cpp
new file mode 100644
index 00000000000..20360287b18
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexSeparableFilter.cpp
@@ -0,0 +1,389 @@
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+#include "PtexPlatform.h"
+#include <math.h>
+#include <assert.h>
+
+#include "PtexSeparableFilter.h"
+#include "PtexSeparableKernel.h"
+#include "PtexUtils.h"
+
+
+//#define NOEDGEBLEND // uncomment to disable filtering across edges (for debugging)
+
+void PtexSeparableFilter::eval(float* result, int firstChan, int nChannels,
+ int faceid, float u, float v,
+ float uw1, float vw1, float uw2, float vw2,
+ float width, float blur)
+{
+ // init
+ if (!_tx || nChannels <= 0) return;
+ if (faceid < 0 || faceid >= _tx->numFaces()) return;
+ _ntxchan = _tx->numChannels();
+ _dt = _tx->dataType();
+ _firstChanOffset = firstChan*DataSize(_dt);
+ _nchan = PtexUtils::min(nChannels, _ntxchan-firstChan);
+
+ // get face info
+ const FaceInfo& f = _tx->getFaceInfo(faceid);
+
+ // if neighborhood is constant, just return constant value of face
+ if (f.isNeighborhoodConstant()) {
+ PtexPtr<PtexFaceData> data ( _tx->getData(faceid, 0) );
+ if (data) {
+ char* d = (char*) data->getData() + _firstChanOffset;
+ Ptex::ConvertToFloat(result, d, _dt, _nchan);
+ }
+ return;
+ }
+
+ // find filter width as bounding box of vectors w1 and w2
+ float uw = fabs(uw1) + fabs(uw2), vw = fabs(vw1) + fabs(vw2);
+
+ // handle border modes
+ switch (_uMode) {
+ case m_clamp: u = PtexUtils::clamp(u, 0.0f, 1.0f); break;
+ case m_periodic: u = u-floor(u); break;
+ case m_black: break; // do nothing
+ }
+
+ switch (_vMode) {
+ case m_clamp: v = PtexUtils::clamp(v, 0.0f, 1.0f); break;
+ case m_periodic: v = v-floor(v);
+ case m_black: break; // do nothing
+ }
+
+ // build kernel
+ PtexSeparableKernel k;
+ if (f.isSubface()) {
+ // for a subface, build the kernel as if it were on a main face and then downres
+ uw = uw * width + blur * 2;
+ vw = vw * width + blur * 2;
+ buildKernel(k, u*.5, v*.5, uw*.5, vw*.5, f.res);
+ if (k.res.ulog2 == 0) k.upresU();
+ if (k.res.vlog2 == 0) k.upresV();
+ k.res.ulog2--; k.res.vlog2--;
+ }
+ else {
+ uw = uw * width + blur;
+ vw = vw * width + blur;
+ buildKernel(k, u, v, uw, vw, f.res);
+ }
+ k.stripZeros();
+
+ // check kernel (debug only)
+ assert(k.uw > 0 && k.vw > 0);
+ assert(k.uw <= PtexSeparableKernel::kmax && k.vw <= PtexSeparableKernel::kmax);
+ _weight = k.weight();
+
+ // allocate temporary double-precision result
+ _result = (double*) alloca(sizeof(double)*_nchan);
+ memset(_result, 0, sizeof(double)*_nchan);
+
+ // apply to faces
+ splitAndApply(k, faceid, f);
+
+ // normalize (both for data type and cumulative kernel weight applied)
+ // and output result
+ double scale = 1.0 / (_weight * OneValue(_dt));
+ for (int i = 0; i < _nchan; i++) result[i] = float(_result[i] * scale);
+
+ // clear temp result
+ _result = 0;
+}
+
+
+void PtexSeparableFilter::splitAndApply(PtexSeparableKernel& k, int faceid, const Ptex::FaceInfo& f)
+{
+ // do we need to split? (i.e. does kernel span an edge?)
+ bool splitR = (k.u+k.uw > k.res.u()), splitL = (k.u < 0);
+ bool splitT = (k.v+k.vw > k.res.v()), splitB = (k.v < 0);
+
+#ifdef NOEDGEBLEND
+ // for debugging only
+ if (splitR) k.mergeR(_uMode);
+ if (splitL) k.mergeL(_uMode);
+ if (splitT) k.mergeT(_vMode);
+ if (splitB) k.mergeB(_vMode);
+#else
+ if (splitR || splitL || splitT || splitB) {
+ PtexSeparableKernel ka, kc;
+ if (splitR) {
+ if (f.adjface(e_right) >= 0) {
+ k.splitR(ka);
+ if (splitT) {
+ if (f.adjface(e_top) >= 0) {
+ ka.splitT(kc);
+ applyToCorner(kc, faceid, f, e_top);
+ }
+ else ka.mergeT(_vMode);
+ }
+ if (splitB) {
+ if (f.adjface(e_bottom) >= 0) {
+ ka.splitB(kc);
+ applyToCorner(kc, faceid, f, e_right);
+ }
+ else ka.mergeB(_vMode);
+ }
+ applyAcrossEdge(ka, faceid, f, e_right);
+ }
+ else k.mergeR(_uMode);
+ }
+ if (splitL) {
+ if (f.adjface(e_left) >= 0) {
+ k.splitL(ka);
+ if (splitT) {
+ if (f.adjface(e_top) >= 0) {
+ ka.splitT(kc);
+ applyToCorner(kc, faceid, f, e_left);
+ }
+ else ka.mergeT(_vMode);
+ }
+ if (splitB) {
+ if (f.adjface(e_bottom) >= 0) {
+ ka.splitB(kc);
+ applyToCorner(kc, faceid, f, e_bottom);
+ }
+ else ka.mergeB(_vMode);
+ }
+ applyAcrossEdge(ka, faceid, f, e_left);
+ }
+ else k.mergeL(_uMode);
+ }
+ if (splitT) {
+ if (f.adjface(e_top) >= 0) {
+ k.splitT(ka);
+ applyAcrossEdge(ka, faceid, f, e_top);
+ }
+ else k.mergeT(_vMode);
+ }
+ if (splitB) {
+ if (f.adjface(e_bottom) >= 0) {
+ k.splitB(ka);
+ applyAcrossEdge(ka, faceid, f, e_bottom);
+ }
+ else k.mergeB(_vMode);
+ }
+ }
+#endif
+
+ // do local face
+ apply(k, faceid, f);
+}
+
+
+void PtexSeparableFilter::applyAcrossEdge(PtexSeparableKernel& k,
+ int faceid, const Ptex::FaceInfo& f, int eid)
+{
+ int afid = f.adjface(eid), aeid = f.adjedge(eid);
+ const Ptex::FaceInfo* af = &_tx->getFaceInfo(afid);
+ int rot = eid - aeid + 2;
+
+ // adjust uv coord and res for face/subface boundary
+ bool fIsSubface = f.isSubface(), afIsSubface = af->isSubface();
+ if (fIsSubface != afIsSubface) {
+ if (afIsSubface) {
+ // main face to subface transition
+ // adjust res and offset uv coord for primary subface
+ bool primary = k.adjustMainToSubface(eid);
+ if (!primary) {
+ // advance ajacent face and edge id to secondary subface
+ int neid = (aeid + 3) % 4;
+ afid = af->adjface(neid);
+ aeid = af->adjedge(neid);
+ af = &_tx->getFaceInfo(afid);
+ rot += neid - aeid + 2;
+ }
+ }
+ else {
+ // subface to main face transition
+ // Note: the transform depends on which subface the kernel is
+ // coming from. The "primary" subface is the one the main
+ // face is pointing at. The secondary subface adjustment
+ // happens to be the same as for the primary subface for the
+ // next edge, so the cases can be combined.
+ bool primary = (af->adjface(aeid) == faceid);
+ k.adjustSubfaceToMain(eid - primary);
+ }
+ }
+
+ // rotate and apply (resplit if going to a subface)
+ k.rotate(rot);
+ if (afIsSubface) splitAndApply(k, afid, *af);
+ else apply(k, afid, *af);
+}
+
+
+void PtexSeparableFilter::applyToCorner(PtexSeparableKernel& k, int faceid,
+ const Ptex::FaceInfo& f, int eid)
+{
+ // traverse clockwise around corner vertex and gather corner faces
+ int afid = faceid, aeid = eid;
+ const FaceInfo* af = &f;
+ bool prevIsSubface = af->isSubface();
+
+ const int MaxValence = 10;
+ int cfaceId[MaxValence];
+ int cedgeId[MaxValence];
+ const FaceInfo* cface[MaxValence];
+
+ int numCorners = 0;
+ for (int i = 0; i < MaxValence; i++) {
+ // advance to next face
+ int prevFace = afid;
+ afid = af->adjface(aeid);
+ aeid = (af->adjedge(aeid) + 1) % 4;
+
+ // we hit a boundary or reached starting face
+ // note: we need to check edge id too because we might have
+ // a periodic texture (w/ toroidal topology) where all 4 corners
+ // are from the same face
+ if (afid < 0 || (afid == faceid && aeid == eid)) {
+ numCorners = i - 2;
+ break;
+ }
+
+ // record face info
+ af = &_tx->getFaceInfo(afid);
+ cfaceId[i] = afid;
+ cedgeId[i] = aeid;
+ cface[i] = af;
+
+ // check to see if corner is a subface "tee"
+ bool isSubface = af->isSubface();
+ if (prevIsSubface && !isSubface && af->adjface((aeid+3)%4) == prevFace)
+ {
+ // adjust the eid depending on whether we started from
+ // the primary or secondary subface.
+ bool primary = (i==1);
+ k.adjustSubfaceToMain(eid + primary * 2);
+ k.rotate(eid - aeid + 3 - primary);
+ splitAndApply(k, afid, *af);
+ return;
+ }
+ prevIsSubface = isSubface;
+ }
+
+ if (numCorners == 1) {
+ // regular case (valence 4)
+ applyToCornerFace(k, f, eid, cfaceId[1], *cface[1], cedgeId[1]);
+ }
+ else if (numCorners > 1) {
+ // valence 5+, make kernel symmetric and apply equally to each face
+ // first, rotate to standard orientation, u=v=0
+ k.rotate(eid + 2);
+ double initialWeight = k.weight();
+ double newWeight = k.makeSymmetric(initialWeight);
+ for (int i = 1; i <= numCorners; i++) {
+ PtexSeparableKernel kc = k;
+ applyToCornerFace(kc, f, 2, cfaceId[i], *cface[i], cedgeId[i]);
+ }
+ // adjust weight for symmetrification and for additional corners
+ _weight += newWeight * numCorners - initialWeight;
+ }
+ else {
+ // valence 2 or 3, ignore corner face (just adjust weight)
+ _weight -= k.weight();
+ }
+}
+
+
+void PtexSeparableFilter::applyToCornerFace(PtexSeparableKernel& k, const Ptex::FaceInfo& f, int eid,
+ int cfid, const Ptex::FaceInfo& cf, int ceid)
+{
+ // adjust uv coord and res for face/subface boundary
+ bool fIsSubface = f.isSubface(), cfIsSubface = cf.isSubface();
+ if (fIsSubface != cfIsSubface) {
+ if (cfIsSubface) k.adjustMainToSubface(eid + 3);
+ else k.adjustSubfaceToMain(eid + 3);
+ }
+
+ // rotate and apply (resplit if going to a subface)
+ k.rotate(eid - ceid + 2);
+ if (cfIsSubface) splitAndApply(k, cfid, cf);
+ else apply(k, cfid, cf);
+}
+
+
+void PtexSeparableFilter::apply(PtexSeparableKernel& k, int faceid, const Ptex::FaceInfo& f)
+{
+ assert(k.u >= 0 && k.u + k.uw <= k.res.u());
+ assert(k.v >= 0 && k.v + k.vw <= k.res.v());
+
+ if (k.uw <= 0 || k.vw <= 0) return;
+
+ // downres kernel if needed
+ while (k.res.u() > f.res.u()) k.downresU();
+ while (k.res.v() > f.res.v()) k.downresV();
+
+ // get face data, and apply
+ PtexPtr<PtexFaceData> dh ( _tx->getData(faceid, k.res) );
+ if (!dh) return;
+
+ if (dh->isConstant()) {
+ k.applyConst(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan);
+ }
+ else if (dh->isTiled()) {
+ Ptex::Res tileres = dh->tileRes();
+ PtexSeparableKernel kt;
+ kt.res = tileres;
+ int tileresu = tileres.u();
+ int tileresv = tileres.v();
+ int ntilesu = k.res.u() / tileresu;
+ for (int v = k.v, vw = k.vw; vw > 0; vw -= kt.vw, v += kt.vw) {
+ int tilev = v / tileresv;
+ kt.v = v % tileresv;
+ kt.vw = PtexUtils::min(vw, tileresv - kt.v);
+ kt.kv = k.kv + v - k.v;
+ for (int u = k.u, uw = k.uw; uw > 0; uw -= kt.uw, u += kt.uw) {
+ int tileu = u / tileresu;
+ kt.u = u % tileresu;
+ kt.uw = PtexUtils::min(uw, tileresu - kt.u);
+ kt.ku = k.ku + u - k.u;
+ PtexPtr<PtexFaceData> th ( dh->getTile(tilev * ntilesu + tileu) );
+ if (th) {
+ if (th->isConstant())
+ kt.applyConst(_result, (char*)th->getData()+_firstChanOffset, _dt, _nchan);
+ else
+ kt.apply(_result, (char*)th->getData()+_firstChanOffset, _dt, _nchan, _ntxchan);
+ }
+ }
+ }
+ }
+ else {
+ k.apply(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan, _ntxchan);
+ }
+}
diff --git a/extern/ptex/src/ptex/PtexSeparableFilter.h b/extern/ptex/src/ptex/PtexSeparableFilter.h
new file mode 100644
index 00000000000..da23f75566e
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexSeparableFilter.h
@@ -0,0 +1,80 @@
+#ifndef PtexSeparableFilter_h
+#define PtexSeparableFilter_h
+
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+#include "Ptexture.h"
+
+class PtexSeparableKernel;
+
+class PtexSeparableFilter : public PtexFilter, public Ptex
+{
+ public:
+ virtual void release() { delete this; }
+ virtual void eval(float* result, int firstchan, int nchannels,
+ int faceid, float u, float v,
+ float uw1, float vw1, float uw2, float vw2,
+ float width, float blur);
+
+ protected:
+ PtexSeparableFilter(PtexTexture* tx, const PtexFilter::Options& opts ) :
+ _tx(tx), _options(opts), _result(0), _weight(0),
+ _firstChanOffset(0), _nchan(0), _ntxchan(0),
+ _dt((DataType)0), _uMode(tx->uBorderMode()), _vMode(tx->vBorderMode()) {}
+ virtual ~PtexSeparableFilter() {}
+
+ virtual void buildKernel(PtexSeparableKernel& k, float u, float v, float uw, float vw,
+ Res faceRes) = 0;
+
+ void splitAndApply(PtexSeparableKernel& k, int faceid, const Ptex::FaceInfo& f);
+ void applyAcrossEdge(PtexSeparableKernel& k, int faceid, const Ptex::FaceInfo& f, int eid);
+ void applyToCorner(PtexSeparableKernel& k, int faceid, const Ptex::FaceInfo& f, int eid);
+ void applyToCornerFace(PtexSeparableKernel& k, const Ptex::FaceInfo& f, int eid,
+ int cfaceid, const Ptex::FaceInfo& cf, int ceid);
+ void apply(PtexSeparableKernel& k, int faceid, const Ptex::FaceInfo& f);
+
+ PtexTexture* _tx; // texture being evaluated
+ Options _options; // options
+ double* _result; // temp result
+ double _weight; // accumulated weight of data in _result
+ int _firstChanOffset; // byte offset of first channel to eval
+ int _nchan; // number of channels to eval
+ int _ntxchan; // number of channels in texture
+ DataType _dt; // data type of texture
+ BorderMode _uMode, _vMode; // border modes (clamp,black,periodic)
+};
+
+#endif
diff --git a/extern/ptex/src/ptex/PtexSeparableKernel.cpp b/extern/ptex/src/ptex/PtexSeparableKernel.cpp
new file mode 100644
index 00000000000..eea51109bbb
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexSeparableKernel.cpp
@@ -0,0 +1,149 @@
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+#include "PtexPlatform.h"
+#include "PtexUtils.h"
+#include "PtexHalf.h"
+#include "PtexSeparableKernel.h"
+
+namespace {
+ // apply to 1..4 channels (unrolled channel loop) of packed data (nTxChan==nChan)
+ template<class T, int nChan>
+ void Apply(PtexSeparableKernel& k, double* result, void* data, int /*nChan*/, int /*nTxChan*/)
+ {
+ double* rowResult = (double*) alloca(nChan*sizeof(double));
+ int rowlen = k.res.u() * nChan;
+ int datalen = k.uw * nChan;
+ int rowskip = rowlen - datalen;
+ double* kvp = k.kv;
+ T* p = (T*)data + (k.v * k.res.u() + k.u) * nChan;
+ T* pEnd = p + k.vw * rowlen;
+ while (p != pEnd)
+ {
+ double* kup = k.ku;
+ T* pRowEnd = p + datalen;
+ // just mult and copy first element
+ PtexUtils::VecMult<T,nChan>()(rowResult, p, *kup++);
+ p += nChan;
+ // accumulate remaining elements
+ while (p != pRowEnd) {
+ // rowResult[i] = p[i] * ku[u] for i in {0..n-1}
+ PtexUtils::VecAccum<T,nChan>()(rowResult, p, *kup++);
+ p += nChan;
+ }
+ // result[i] += rowResult[i] * kv[v] for i in {0..n-1}
+ PtexUtils::VecAccum<double,nChan>()(result, rowResult, *kvp++);
+ p += rowskip;
+ }
+ }
+
+ // apply to 1..4 channels (unrolled channel loop) w/ pixel stride
+ template<class T, int nChan>
+ void ApplyS(PtexSeparableKernel& k, double* result, void* data, int /*nChan*/, int nTxChan)
+ {
+ double* rowResult = (double*) alloca(nChan*sizeof(double));
+ int rowlen = k.res.u() * nTxChan;
+ int datalen = k.uw * nTxChan;
+ int rowskip = rowlen - datalen;
+ double* kvp = k.kv;
+ T* p = (T*)data + (k.v * k.res.u() + k.u) * nTxChan;
+ T* pEnd = p + k.vw * rowlen;
+ while (p != pEnd)
+ {
+ double* kup = k.ku;
+ T* pRowEnd = p + datalen;
+ // just mult and copy first element
+ PtexUtils::VecMult<T,nChan>()(rowResult, p, *kup++);
+ p += nTxChan;
+ // accumulate remaining elements
+ while (p != pRowEnd) {
+ // rowResult[i] = p[i] * ku[u] for i in {0..n-1}
+ PtexUtils::VecAccum<T,nChan>()(rowResult, p, *kup++);
+ p += nTxChan;
+ }
+ // result[i] += rowResult[i] * kv[v] for i in {0..n-1}
+ PtexUtils::VecAccum<double,nChan>()(result, rowResult, *kvp++);
+ p += rowskip;
+ }
+ }
+
+ // apply to N channels (general case)
+ template<class T>
+ void ApplyN(PtexSeparableKernel& k, double* result, void* data, int nChan, int nTxChan)
+ {
+ double* rowResult = (double*) alloca(nChan*sizeof(double));
+ int rowlen = k.res.u() * nTxChan;
+ int datalen = k.uw * nTxChan;
+ int rowskip = rowlen - datalen;
+ double* kvp = k.kv;
+ T* p = (T*)data + (k.v * k.res.u() + k.u) * nTxChan;
+ T* pEnd = p + k.vw * rowlen;
+ while (p != pEnd)
+ {
+ double* kup = k.ku;
+ T* pRowEnd = p + datalen;
+ // just mult and copy first element
+ PtexUtils::VecMultN<T>()(rowResult, p, nChan, *kup++);
+ p += nTxChan;
+ // accumulate remaining elements
+ while (p != pRowEnd) {
+ // rowResult[i] = p[i] * ku[u] for i in {0..n-1}
+ PtexUtils::VecAccumN<T>()(rowResult, p, nChan, *kup++);
+ p += nTxChan;
+ }
+ // result[i] += rowResult[i] * kv[v] for i in {0..n-1}
+ PtexUtils::VecAccumN<double>()(result, rowResult, nChan, *kvp++);
+ p += rowskip;
+ }
+ }
+}
+
+
+
+PtexSeparableKernel::ApplyFn
+PtexSeparableKernel::applyFunctions[] = {
+ // nChan == nTxChan
+ ApplyN<uint8_t>, ApplyN<uint16_t>, ApplyN<PtexHalf>, ApplyN<float>,
+ Apply<uint8_t,1>, Apply<uint16_t,1>, Apply<PtexHalf,1>, Apply<float,1>,
+ Apply<uint8_t,2>, Apply<uint16_t,2>, Apply<PtexHalf,2>, Apply<float,2>,
+ Apply<uint8_t,3>, Apply<uint16_t,3>, Apply<PtexHalf,3>, Apply<float,3>,
+ Apply<uint8_t,4>, Apply<uint16_t,4>, Apply<PtexHalf,4>, Apply<float,4>,
+
+ // nChan != nTxChan (need pixel stride)
+ ApplyN<uint8_t>, ApplyN<uint16_t>, ApplyN<PtexHalf>, ApplyN<float>,
+ ApplyS<uint8_t,1>, ApplyS<uint16_t,1>, ApplyS<PtexHalf,1>, ApplyS<float,1>,
+ ApplyS<uint8_t,2>, ApplyS<uint16_t,2>, ApplyS<PtexHalf,2>, ApplyS<float,2>,
+ ApplyS<uint8_t,3>, ApplyS<uint16_t,3>, ApplyS<PtexHalf,3>, ApplyS<float,3>,
+ ApplyS<uint8_t,4>, ApplyS<uint16_t,4>, ApplyS<PtexHalf,4>, ApplyS<float,4>,
+};
diff --git a/extern/ptex/src/ptex/PtexSeparableKernel.h b/extern/ptex/src/ptex/PtexSeparableKernel.h
new file mode 100644
index 00000000000..dcae2174ad4
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexSeparableKernel.h
@@ -0,0 +1,473 @@
+#ifndef PtexSeparableKernel_h
+#define PtexSeparableKernel_h
+
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+#include <assert.h>
+#include <algorithm>
+#include <numeric>
+#include "Ptexture.h"
+#include "PtexUtils.h"
+
+// Separable convolution kernel
+class PtexSeparableKernel : public Ptex {
+ public:
+ Res res; // resolution that kernel was built for
+ int u, v; // uv offset within face data
+ int uw, vw; // kernel width
+ double* ku; // kernel weights in u
+ double* kv; // kernel weights in v
+ static const int kmax = 10; // max kernel width
+ double kubuff[kmax];
+ double kvbuff[kmax];
+
+ PtexSeparableKernel()
+ : res(0), u(0), v(0), uw(0), vw(0), ku(kubuff), kv(kvbuff) {}
+
+ PtexSeparableKernel(const PtexSeparableKernel& k)
+ {
+ set(k.res, k.u, k.v, k.uw, k.vw, k.ku, k.kv);
+ }
+
+ PtexSeparableKernel& operator= (const PtexSeparableKernel& k)
+ {
+ set(k.res, k.u, k.v, k.uw, k.vw, k.ku, k.kv);
+ return *this;
+ }
+
+ void set(Res resVal,
+ int uVal, int vVal,
+ int uwVal, int vwVal,
+ const double* kuVal, const double* kvVal)
+ {
+ assert(uwVal <= kmax && vwVal <= kmax);
+ res = resVal;
+ u = uVal;
+ v = vVal;
+ uw = uwVal;
+ vw = vwVal;
+ memcpy(kubuff, kuVal, sizeof(*ku)*uw);
+ memcpy(kvbuff, kvVal, sizeof(*kv)*vw);
+ ku = kubuff;
+ kv = kvbuff;
+ }
+
+ void stripZeros()
+ {
+ while (ku[0] == 0) { ku++; u++; uw--; }
+ while (ku[uw-1] == 0) { uw--; }
+ while (kv[0] == 0) { kv++; v++; vw--; }
+ while (kv[vw-1] == 0) { vw--; }
+ assert(uw > 0 && vw > 0);
+ }
+
+ double weight() const
+ {
+ return accumulate(ku, uw) * accumulate(kv, vw);
+ }
+
+ void mergeL(BorderMode mode)
+ {
+ int w = -u;
+ if (mode != m_black)
+ ku[w] += accumulate(ku, w);
+ ku += w;
+ uw -= w;
+ u = 0;
+ }
+
+ void mergeR(BorderMode mode)
+ {
+ int w = uw + u - res.u();
+ double* kp = ku + uw - w;
+ if (mode != m_black)
+ kp[-1] += accumulate(kp, w);
+ uw -= w;
+ }
+
+ void mergeB(BorderMode mode)
+ {
+ int w = -v;
+ if (mode != m_black)
+ kv[w] += accumulate(kv, w);
+ kv += w;
+ vw -= w;
+ v = 0;
+ }
+
+ void mergeT(BorderMode mode)
+ {
+ int w = vw + v - res.v();
+ double* kp = kv + vw - w;
+ if (mode != m_black)
+ kp[-1] += accumulate(kp, w);
+ vw -= w;
+ }
+
+ void splitL(PtexSeparableKernel& k)
+ {
+ // split off left piece of width w into k
+ int w = -u;
+
+ if (w < uw) {
+ // normal case - split off a portion
+ // res u v uw vw ku kv
+ k.set(res, res.u()-w, v, w, vw, ku, kv);
+
+ // update local
+ u = 0;
+ uw -= w;
+ ku += w;
+ }
+ else {
+ // entire kernel is split off
+ k = *this;
+ k.u += res.u();
+ u = 0; uw = 0;
+ }
+ }
+
+ void splitR(PtexSeparableKernel& k)
+ {
+ // split off right piece of width w into k
+ int w = u + uw - res.u();
+
+ if (w < uw) {
+ // normal case - split off a portion
+ // res u v uw vw ku kv
+ k.set(res, 0, v, w, vw, ku + uw - w, kv);
+
+ // update local
+ uw -= w;
+ }
+ else {
+ // entire kernel is split off
+ k = *this;
+ k.u -= res.u();
+ u = 0; uw = 0;
+ }
+ }
+
+ void splitB(PtexSeparableKernel& k)
+ {
+ // split off bottom piece of width w into k
+ int w = -v;
+ if (w < vw) {
+ // normal case - split off a portion
+ // res u v uw vw ku kv
+ k.set(res, u, res.v()-w, uw, w, ku, kv);
+
+ // update local
+ v = 0;
+ vw -= w;
+ kv += w;
+ }
+ else {
+ // entire kernel is split off
+ k = *this;
+ k.v += res.v();
+ v = 0; vw = 0;
+ }
+ }
+
+ void splitT(PtexSeparableKernel& k)
+ {
+ // split off top piece of width w into k
+ int w = v + vw - res.v();
+ if (w < vw) {
+ // normal case - split off a portion
+ // res u v uw vw ku kv
+ k.set(res, u, 0, uw, w, ku, kv + vw - w);
+
+ // update local
+ vw -= w;
+ }
+ else {
+ // entire kernel is split off
+ k = *this;
+ k.v -= res.v();
+ v = 0; vw = 0;
+ }
+ }
+
+ void flipU()
+ {
+ u = res.u() - u - uw;
+ std::reverse(ku, ku+uw);
+ }
+
+ void flipV()
+ {
+ v = res.v() - v - vw;
+ std::reverse(kv, kv+vw);
+ }
+
+ void swapUV()
+ {
+ res.swapuv();
+ std::swap(u, v);
+ std::swap(uw, vw);
+ std::swap(ku, kv);
+ }
+
+ void rotate(int rot)
+ {
+ // rotate kernel 'rot' steps ccw
+ switch (rot & 3) {
+ default: return;
+ case 1: flipU(); swapUV(); break;
+ case 2: flipU(); flipV(); break;
+ case 3: flipV(); swapUV(); break;
+ }
+ }
+
+ bool adjustMainToSubface(int eid)
+ {
+ // to adjust the kernel for the subface, we must adjust the res down and offset the uv coords
+ // however, if the res is already zero, we must upres the kernel first
+ if (res.ulog2 == 0) upresU();
+ if (res.vlog2 == 0) upresV();
+
+ if (res.ulog2 > 0) res.ulog2--;
+ if (res.vlog2 > 0) res.vlog2--;
+
+ // offset uv coords and determine whether target subface is the primary one
+ bool primary = 0;
+ int resu = res.u(), resv = res.v();
+ switch (eid&3) {
+ case e_bottom:
+ primary = (u < resu);
+ v -= resv;
+ if (!primary) u -= resu;
+ break;
+ case e_right:
+ primary = (v < resv);
+ if (!primary) v -= resv;
+ break;
+ case e_top:
+ primary = (u >= resu);
+ if (primary) u -= resu;
+ break;
+ case e_left:
+ primary = (v >= resv);
+ u -= resu;
+ if (primary) v -= resv;
+ break;
+ }
+ return primary;
+ }
+
+ void adjustSubfaceToMain(int eid)
+ {
+ switch (eid&3) {
+ case e_bottom: v += res.v(); break;
+ case e_right: break;
+ case e_top: u += res.u(); break;
+ case e_left: u += res.u(); v += res.v(); break;
+ }
+ res.ulog2++; res.vlog2++;
+ }
+
+ void downresU()
+ {
+ double* src = ku;
+ double* dst = ku;
+
+ // skip odd leading sample (if any)
+ if (u & 1) {
+ dst++;
+ src++;
+ uw--;
+ }
+
+ // combine even pairs
+ for (int i = uw/2; i > 0; i--) {
+ *dst++ = src[0] + src[1];
+ src += 2;
+ }
+
+ // copy odd trailing sample (if any)
+ if (uw & 1) {
+ *dst++ = *src++;
+ }
+
+ // update state
+ u /= 2;
+ uw = int(dst - ku);
+ res.ulog2--;
+ }
+
+ void downresV()
+ {
+ double* src = kv;
+ double* dst = kv;
+
+ // skip odd leading sample (if any)
+ if (v & 1) {
+ dst++;
+ src++;
+ vw--;
+ }
+
+ // combine even pairs
+ for (int i = vw/2; i > 0; i--) {
+ *dst++ = src[0] + src[1];
+ src += 2;
+ }
+
+ // copy odd trailing sample (if any)
+ if (vw & 1) {
+ *dst++ = *src++;
+ }
+
+ // update state
+ v /= 2;
+ vw = int(dst - kv);
+ res.vlog2--;
+ }
+
+ void upresU()
+ {
+ double* src = ku + uw-1;
+ double* dst = ku + uw*2-2;
+ for (int i = uw; i > 0; i--) {
+ dst[0] = dst[1] = *src-- / 2;
+ dst -=2;
+ }
+ uw *= 2;
+ u *= 2;
+ res.ulog2++;
+ }
+
+ void upresV()
+ {
+ double* src = kv + vw-1;
+ double* dst = kv + vw*2-2;
+ for (int i = vw; i > 0; i--) {
+ dst[0] = dst[1] = *src-- / 2;
+ dst -=2;
+ }
+ vw *= 2;
+ v *= 2;
+ res.vlog2++;
+ }
+
+ double makeSymmetric(double initialWeight)
+ {
+ assert(u == 0 && v == 0);
+
+ // downres higher-res dimension until equal
+ if (res.ulog2 > res.vlog2) {
+ do { downresU(); } while(res.ulog2 > res.vlog2);
+ }
+ else if (res.vlog2 > res.ulog2) {
+ do { downresV(); } while (res.vlog2 > res.ulog2);
+ }
+
+ // truncate excess samples in longer dimension
+ uw = vw = PtexUtils::min(uw, vw);
+
+ // combine corresponding u and v samples and compute new kernel weight
+ double newWeight = 0;
+ for (int i = 0; i < uw; i++) {
+ double sum = ku[i] + kv[i];
+ ku[i] = kv[i] = sum;
+ newWeight += sum;
+ }
+ newWeight *= newWeight; // equivalent to k.weight() ( = sum(ku)*sum(kv) )
+
+ // compute scale factor to compensate for weight change
+ double scale = newWeight == 0 ? 1.0 : initialWeight / newWeight;
+
+ // Note: a sharpening kernel (like Mitchell) can produce
+ // negative weights which may cancel out when adding the two
+ // kernel axes together, and this can cause the compensation
+ // scale factor to spike up. We expect the scale factor to be
+ // less than one in "normal" cases (i.e. ku*kv <= (ku+kv)^2 if ku
+ // and kv are both positive), so clamping to -1..1 will have
+ // no effect on positive kernels. If there are negative
+ // weights, the clamping will just limit the amount of
+ // sharpening happening at the corners, and the result will
+ // still be smooth.
+
+ // clamp scale factor to -1..1 range
+ if (scale >= 1) {
+ // scale by 1 (i.e. do nothing)
+ }
+ else {
+ if (scale < -1) {
+ // a negative scale means the original kernel had an overall negative weight
+ // after making symmetric, the kernel will always be positive
+ // scale ku by -1
+ // note: choice of u is arbitrary; we could have scaled u or v (but not both)
+ for (int i = 0; i < uw; i++) ku[i] *= -1;
+ newWeight = -newWeight;
+ }
+ else {
+ // scale ku to restore initialWeight (again, choice of u instead of v is arbitrary)
+ for (int i = 0; i < uw; i++) ku[i] *= scale;
+ newWeight = initialWeight;
+ }
+ }
+ return newWeight;
+ }
+
+ void apply(double* dst, void* data, DataType dt, int nChan, int nTxChan)
+ {
+ // dispatch specialized apply function
+ ApplyFn fn = applyFunctions[(nChan!=nTxChan)*20 + ((unsigned)nChan<=4)*nChan*4 + dt];
+ fn(*this, dst, data, nChan, nTxChan);
+ }
+
+ void applyConst(double* dst, void* data, DataType dt, int nChan)
+ {
+ PtexUtils::applyConst(weight(), dst, data, dt, nChan);
+ }
+
+ private:
+ typedef void (*ApplyFn)(PtexSeparableKernel& k, double* dst, void* data, int nChan, int nTxChan);
+ typedef void (*ApplyConstFn)(double weight, double* dst, void* data, int nChan);
+ static ApplyFn applyFunctions[40];
+ static ApplyConstFn applyConstFunctions[20];
+ static inline double accumulate(const double* p, int n)
+ {
+ double result = 0;
+ for (const double* e = p + n; p != e; p++) result += *p;
+ return result;
+ }
+};
+
+#endif
diff --git a/extern/ptex/src/ptex/PtexTriangleFilter.cpp b/extern/ptex/src/ptex/PtexTriangleFilter.cpp
new file mode 100644
index 00000000000..42f12e0bc94
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexTriangleFilter.cpp
@@ -0,0 +1,261 @@
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+#include "PtexPlatform.h"
+#include <math.h>
+#include <assert.h>
+
+#include "PtexTriangleFilter.h"
+#include "PtexTriangleKernel.h"
+#include "PtexUtils.h"
+
+namespace {
+ inline double squared(double x) { return x*x; }
+}
+
+void PtexTriangleFilter::eval(float* result, int firstChan, int nChannels,
+ int faceid, float u, float v,
+ float uw1, float vw1, float uw2, float vw2,
+ float width, float blur)
+{
+ // init
+ if (!_tx || nChannels <= 0) return;
+ if (faceid < 0 || faceid >= _tx->numFaces()) return;
+ _ntxchan = _tx->numChannels();
+ _dt = _tx->dataType();
+ _firstChanOffset = firstChan*DataSize(_dt);
+ _nchan = PtexUtils::min(nChannels, _ntxchan-firstChan);
+
+ // get face info
+ const FaceInfo& f = _tx->getFaceInfo(faceid);
+
+ // if neighborhood is constant, just return constant value of face
+ if (f.isNeighborhoodConstant()) {
+ PtexPtr<PtexFaceData> data ( _tx->getData(faceid, 0) );
+ if (data) {
+ char* d = (char*) data->getData() + _firstChanOffset;
+ Ptex::ConvertToFloat(result, d, _dt, _nchan);
+ }
+ return;
+ }
+
+ // clamp u and v
+ u = PtexUtils::clamp(u, 0.0f, 1.0f);
+ v = PtexUtils::clamp(v, 0.0f, 1.0f);
+
+ // build kernel
+ PtexTriangleKernel k;
+ buildKernel(k, u, v, uw1, vw1, uw2, vw2, width, blur, f.res);
+
+ // accumulate the weight as we apply
+ _weight = 0;
+
+ // allocate temporary double-precision result
+ _result = (double*) alloca(sizeof(double)*_nchan);
+ memset(_result, 0, sizeof(double)*_nchan);
+
+ // apply to faces
+ splitAndApply(k, faceid, f);
+
+ // normalize (both for data type and cumulative kernel weight applied)
+ // and output result
+ double scale = 1.0 / (_weight * OneValue(_dt));
+ for (int i = 0; i < _nchan; i++) result[i] = float(_result[i] * scale);
+
+ // clear temp result
+ _result = 0;
+}
+
+
+
+void PtexTriangleFilter::buildKernel(PtexTriangleKernel& k, float u, float v,
+ float uw1, float vw1, float uw2, float vw2,
+ float width, float blur, Res faceRes)
+{
+ const double sqrt3 = 1.7320508075688772;
+
+ // compute ellipse coefficients, A*u^2 + B*u*v + C*v^2 == AC - B^2/4
+ double scaleAC = 0.25 * width*width;
+ double scaleB = -2 * scaleAC;
+ double A = (vw1*vw1 + vw2*vw2) * scaleAC;
+ double B = (uw1*vw1 + uw2*vw2) * scaleB;
+ double C = (uw1*uw1 + uw2*uw2) * scaleAC;
+
+ // convert to cartesian domain
+ double Ac = 0.75 * A;
+ double Bc = (sqrt3/2) * (B-A);
+ double Cc = 0.25 * A - 0.5 * B + C;
+
+ // compute min blur for eccentricity clamping
+ const double maxEcc = 15; // max eccentricity
+ const double eccRatio = (maxEcc*maxEcc + 1) / (maxEcc*maxEcc - 1);
+ double X = sqrt(squared(Ac - Cc) + squared(Bc));
+ double b_e = 0.5 * (eccRatio * X - (Ac + Cc));
+
+ // compute min blur for texel clamping
+ // (ensure that ellipse is no smaller than a texel)
+ double b_t = squared(0.5 / faceRes.u());
+
+ // add blur
+ double b_b = 0.25 * blur * blur;
+ double b = PtexUtils::max(b_b, PtexUtils::max(b_e, b_t));
+ Ac += b;
+ Cc += b;
+
+ // compute minor radius
+ double m = sqrt(2*(Ac*Cc - 0.25*Bc*Bc) / (Ac + Cc + X));
+
+ // choose desired resolution
+ int reslog2 = PtexUtils::max(0, int(ceil(log2(0.5/m))));
+
+ // convert back to triangular domain
+ A = (4/3.0) * Ac;
+ B = (2/sqrt3) * Bc + A;
+ C = -0.25 * A + 0.5 * B + Cc;
+
+ // scale by kernel width
+ double scale = PtexTriangleKernelWidth * PtexTriangleKernelWidth;
+ A *= scale;
+ B *= scale;
+ C *= scale;
+
+ // find u,v,w extents
+ double uw = PtexUtils::min(sqrt(C), 1.0);
+ double vw = PtexUtils::min(sqrt(A), 1.0);
+ double ww = PtexUtils::min(sqrt(A-B+C), 1.0);
+
+ // init kernel
+ double w = 1 - u - v;
+ k.set(Res(reslog2, reslog2), u, v, u-uw, v-vw, w-ww, u+uw, v+vw, w+ww, A, B, C);
+}
+
+
+void PtexTriangleFilter::splitAndApply(PtexTriangleKernel& k, int faceid, const Ptex::FaceInfo& f)
+{
+ // do we need to split? if so, split kernel and apply across edge(s)
+ if (k.u1 < 0 && f.adjface(2) >= 0) {
+ PtexTriangleKernel ka;
+ k.splitU(ka);
+ applyAcrossEdge(ka, f, 2);
+ }
+ if (k.v1 < 0 && f.adjface(0) >= 0) {
+ PtexTriangleKernel ka;
+ k.splitV(ka);
+ applyAcrossEdge(ka, f, 0);
+ }
+ if (k.w1 < 0 && f.adjface(1) >= 0) {
+ PtexTriangleKernel ka;
+ k.splitW(ka);
+ applyAcrossEdge(ka, f, 1);
+ }
+ // apply to local face
+ apply(k, faceid, f);
+}
+
+
+void PtexTriangleFilter::applyAcrossEdge(PtexTriangleKernel& k,
+ const Ptex::FaceInfo& f, int eid)
+{
+ int afid = f.adjface(eid), aeid = f.adjedge(eid);
+ const Ptex::FaceInfo& af = _tx->getFaceInfo(afid);
+ k.reorient(eid, aeid);
+ splitAndApply(k, afid, af);
+}
+
+
+void PtexTriangleFilter::apply(PtexTriangleKernel& k, int faceid, const Ptex::FaceInfo& f)
+{
+ // clamp kernel face (resolution and extent)
+ k.clampRes(f.res);
+ k.clampExtent();
+
+ // build kernel iterators
+ PtexTriangleKernelIter keven, kodd;
+ k.getIterators(keven, kodd);
+ if (!keven.valid && !kodd.valid) return;
+
+ // get face data, and apply
+ PtexPtr<PtexFaceData> dh ( _tx->getData(faceid, k.res) );
+ if (!dh) return;
+
+ if (keven.valid) applyIter(keven, dh);
+ if (kodd.valid) applyIter(kodd, dh);
+}
+
+
+void PtexTriangleFilter::applyIter(PtexTriangleKernelIter& k, PtexFaceData* dh)
+{
+ if (dh->isConstant()) {
+ k.applyConst(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan);
+ _weight += k.weight;
+ }
+ else if (dh->isTiled()) {
+ Ptex::Res tileres = dh->tileRes();
+ PtexTriangleKernelIter kt = k;
+ int tileresu = tileres.u();
+ int tileresv = tileres.v();
+ kt.rowlen = tileresu;
+ int ntilesu = k.rowlen / kt.rowlen;
+ int wOffsetBase = k.rowlen - tileresu;
+ for (int tilev = k.v1 / tileresv, tilevEnd = (k.v2-1) / tileresv; tilev <= tilevEnd; tilev++) {
+ int vOffset = tilev * tileresv;
+ kt.v = k.v - vOffset;
+ kt.v1 = PtexUtils::max(0, k.v1 - vOffset);
+ kt.v2 = PtexUtils::min(k.v2 - vOffset, tileresv);
+ for (int tileu = k.u1 / tileresu, tileuEnd = (k.u2-1) / tileresu; tileu <= tileuEnd; tileu++) {
+ int uOffset = tileu * tileresu;
+ int wOffset = wOffsetBase - uOffset - vOffset;
+ kt.u = k.u - uOffset;
+ kt.u1 = PtexUtils::max(0, k.u1 - uOffset);
+ kt.u2 = PtexUtils::min(k.u2 - uOffset, tileresu);
+ kt.w1 = k.w1 - wOffset;
+ kt.w2 = k.w2 - wOffset;
+ PtexPtr<PtexFaceData> th ( dh->getTile(tilev * ntilesu + tileu) );
+ if (th) {
+ kt.weight = 0;
+ if (th->isConstant())
+ kt.applyConst(_result, (char*)th->getData()+_firstChanOffset, _dt, _nchan);
+ else
+ kt.apply(_result, (char*)th->getData()+_firstChanOffset, _dt, _nchan, _ntxchan);
+ _weight += kt.weight;
+ }
+ }
+ }
+ }
+ else {
+ k.apply(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan, _ntxchan);
+ _weight += k.weight;
+ }
+}
diff --git a/extern/ptex/src/ptex/PtexTriangleFilter.h b/extern/ptex/src/ptex/PtexTriangleFilter.h
new file mode 100644
index 00000000000..c02161c1121
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexTriangleFilter.h
@@ -0,0 +1,78 @@
+#ifndef PtexTriangleFilter_h
+#define PtexTriangleFilter_h
+
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+#include "Ptexture.h"
+class PtexTriangleKernel;
+class PtexTriangleKernelIter;
+
+class PtexTriangleFilter : public PtexFilter, public Ptex
+{
+ public:
+ PtexTriangleFilter(PtexTexture* tx, const PtexFilter::Options& opts ) :
+ _tx(tx), _options(opts), _result(0), _weight(0),
+ _firstChanOffset(0), _nchan(0), _ntxchan(0),
+ _dt((DataType)0) {}
+ virtual void release() { delete this; }
+ virtual void eval(float* result, int firstchan, int nchannels,
+ int faceid, float u, float v,
+ float uw1, float vw1, float uw2, float vw2,
+ float width, float blur);
+
+ protected:
+ void buildKernel(PtexTriangleKernel& k, float u, float v,
+ float uw1, float vw1, float uw2, float vw2,
+ float width, float blur, Res faceRes);
+
+ void splitAndApply(PtexTriangleKernel& k, int faceid, const Ptex::FaceInfo& f);
+ void applyAcrossEdge(PtexTriangleKernel& k, const Ptex::FaceInfo& f, int eid);
+ void apply(PtexTriangleKernel& k, int faceid, const Ptex::FaceInfo& f);
+ void applyIter(PtexTriangleKernelIter& k, PtexFaceData* dh);
+
+ virtual ~PtexTriangleFilter() {}
+
+ PtexTexture* _tx; // texture being evaluated
+ Options _options; // options
+ double* _result; // temp result
+ double _weight; // accumulated weight of data in _result
+ int _firstChanOffset; // byte offset of first channel to eval
+ int _nchan; // number of channels to eval
+ int _ntxchan; // number of channels in texture
+ DataType _dt; // data type of texture
+};
+
+#endif
diff --git a/extern/ptex/src/ptex/PtexTriangleKernel.cpp b/extern/ptex/src/ptex/PtexTriangleKernel.cpp
new file mode 100644
index 00000000000..3e7c2ba091d
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexTriangleKernel.cpp
@@ -0,0 +1,179 @@
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+#include "PtexPlatform.h"
+#include "PtexUtils.h"
+#include "PtexHalf.h"
+#include "PtexTriangleKernel.h"
+
+
+namespace {
+ inline double gaussian(double x_squared)
+ {
+ static const double scale = -0.5 * (PtexTriangleKernelWidth * PtexTriangleKernelWidth);
+ return exp(scale * x_squared);
+ }
+}
+
+
+namespace {
+
+ // apply to 1..4 channels (unrolled channel loop) of packed data (nTxChan==nChan)
+ // the ellipse equation, Q, is calculated via finite differences (Heckbert '89 pg 57)
+ template<class T, int nChan>
+ void Apply(PtexTriangleKernelIter& k, double* result, void* data, int /*nChan*/, int /*nTxChan*/)
+ {
+ int nTxChan = nChan;
+ double DDQ = 2*k.A;
+ for (int vi = k.v1; vi != k.v2; vi++) {
+ int xw = k.rowlen - vi;
+ int x1 = PtexUtils::max(k.u1, xw-k.w2);
+ int x2 = PtexUtils::min(k.u2, xw-k.w1);
+ double U = x1 - k.u;
+ double V = vi - k.v;
+ double DQ = k.A*(2*U+1)+k.B*V;
+ double Q = k.A*U*U + (k.B*U + k.C*V)*V;
+ T* p = (T*)data + (vi * k.rowlen + x1) * nTxChan;
+ T* pEnd = p + (x2-x1)*nTxChan;
+ for (; p < pEnd; p += nTxChan) {
+ if (Q < 1) {
+ double weight = gaussian(Q)*k.wscale;
+ k.weight += weight;
+ PtexUtils::VecAccum<T,nChan>()(result, p, weight);
+ }
+ Q += DQ;
+ DQ += DDQ;
+ }
+ }
+ }
+
+ // apply to 1..4 channels (unrolled channel loop) w/ pixel stride
+ template<class T, int nChan>
+ void ApplyS(PtexTriangleKernelIter& k, double* result, void* data, int /*nChan*/, int nTxChan)
+ {
+ double DDQ = 2*k.A;
+ for (int vi = k.v1; vi != k.v2; vi++) {
+ int xw = k.rowlen - vi;
+ int x1 = PtexUtils::max(k.u1, xw-k.w2);
+ int x2 = PtexUtils::min(k.u2, xw-k.w1);
+ double U = x1 - k.u;
+ double V = vi - k.v;
+ double DQ = k.A*(2*U+1)+k.B*V;
+ double Q = k.A*U*U + (k.B*U + k.C*V)*V;
+ T* p = (T*)data + (vi * k.rowlen + x1) * nTxChan;
+ T* pEnd = p + (x2-x1)*nTxChan;
+ for (; p < pEnd; p += nTxChan) {
+ if (Q < 1) {
+ double weight = gaussian(Q)*k.wscale;
+ k.weight += weight;
+ PtexUtils::VecAccum<T,nChan>()(result, p, weight);
+ }
+ Q += DQ;
+ DQ += DDQ;
+ }
+ }
+ }
+
+ // apply to N channels (general case)
+ template<class T>
+ void ApplyN(PtexTriangleKernelIter& k, double* result, void* data, int nChan, int nTxChan)
+ {
+ double DDQ = 2*k.A;
+ for (int vi = k.v1; vi != k.v2; vi++) {
+ int xw = k.rowlen - vi;
+ int x1 = PtexUtils::max(k.u1, xw-k.w2);
+ int x2 = PtexUtils::min(k.u2, xw-k.w1);
+ double U = x1 - k.u;
+ double V = vi - k.v;
+ double DQ = k.A*(2*U+1)+k.B*V;
+ double Q = k.A*U*U + (k.B*U + k.C*V)*V;
+ T* p = (T*)data + (vi * k.rowlen + x1) * nTxChan;
+ T* pEnd = p + (x2-x1)*nTxChan;
+ for (; p < pEnd; p += nTxChan) {
+ if (Q < 1) {
+ double weight = gaussian(Q)*k.wscale;
+ k.weight += weight;
+ PtexUtils::VecAccumN<T>()(result, p, nChan, weight);
+ }
+ Q += DQ;
+ DQ += DDQ;
+ }
+ }
+ }
+}
+
+
+PtexTriangleKernelIter::ApplyFn
+PtexTriangleKernelIter::applyFunctions[] = {
+ // nChan == nTxChan
+ ApplyN<uint8_t>, ApplyN<uint16_t>, ApplyN<PtexHalf>, ApplyN<float>,
+ Apply<uint8_t,1>, Apply<uint16_t,1>, Apply<PtexHalf,1>, Apply<float,1>,
+ Apply<uint8_t,2>, Apply<uint16_t,2>, Apply<PtexHalf,2>, Apply<float,2>,
+ Apply<uint8_t,3>, Apply<uint16_t,3>, Apply<PtexHalf,3>, Apply<float,3>,
+ Apply<uint8_t,4>, Apply<uint16_t,4>, Apply<PtexHalf,4>, Apply<float,4>,
+
+ // nChan != nTxChan (need pixel stride)
+ ApplyN<uint8_t>, ApplyN<uint16_t>, ApplyN<PtexHalf>, ApplyN<float>,
+ ApplyS<uint8_t,1>, ApplyS<uint16_t,1>, ApplyS<PtexHalf,1>, ApplyS<float,1>,
+ ApplyS<uint8_t,2>, ApplyS<uint16_t,2>, ApplyS<PtexHalf,2>, ApplyS<float,2>,
+ ApplyS<uint8_t,3>, ApplyS<uint16_t,3>, ApplyS<PtexHalf,3>, ApplyS<float,3>,
+ ApplyS<uint8_t,4>, ApplyS<uint16_t,4>, ApplyS<PtexHalf,4>, ApplyS<float,4>,
+};
+
+
+void PtexTriangleKernelIter::applyConst(double* dst, void* data, DataType dt, int nChan)
+{
+ // iterate over texel locations and calculate weight as if texture weren't const
+ double DDQ = 2*A;
+ for (int vi = v1; vi != v2; vi++) {
+ int xw = rowlen - vi;
+ int x1 = PtexUtils::max(u1, xw-w2);
+ int x2 = PtexUtils::min(u2, xw-w1);
+ double U = x1 - u;
+ double V = vi - v;
+ double DQ = A*(2*U+1)+B*V;
+ double Q = A*U*U + (B*U + C*V)*V;
+ for (int x = x1; x < x2; x++) {
+ if (Q < 1) {
+ weight += gaussian(Q)*wscale;
+ }
+ Q += DQ;
+ DQ += DDQ;
+ }
+ }
+
+ // apply weight to single texel value
+ PtexUtils::applyConst(weight, dst, data, dt, nChan);
+}
diff --git a/extern/ptex/src/ptex/PtexTriangleKernel.h b/extern/ptex/src/ptex/PtexTriangleKernel.h
new file mode 100644
index 00000000000..696d993ee14
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexTriangleKernel.h
@@ -0,0 +1,227 @@
+#ifndef PtexTriangleKernel_h
+#define PtexTriangleKernel_h
+
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+#include <assert.h>
+#include <algorithm>
+#include <numeric>
+#include "Ptexture.h"
+#include "PtexUtils.h"
+
+// kernel width as a multiple of filter width (should be between 3 and 4)
+// for values below 3, the gaussian is not close to zero and a contour will be formed
+// larger values are more expensive (proportional to width-squared)
+static const float PtexTriangleKernelWidth = 3.5;
+
+
+/// Triangle filter kernel iterator (in texel coords)
+class PtexTriangleKernelIter : public Ptex {
+ public:
+ int rowlen; // row length (in u)
+ double u, v; // uv center in texels
+ int u1, v1, w1; // uvw lower bounds
+ int u2, v2, w2; // uvw upper bounds
+ double A,B,C; // ellipse coefficients (F = 1)
+ bool valid; // footprint is valid (non-empty)
+ double wscale; // amount to scale weights by (proportional to texel area)
+ double weight; // accumulated weight
+
+ void apply(double* dst, void* data, DataType dt, int nChan, int nTxChan)
+ {
+ // dispatch specialized apply function
+ ApplyFn fn = applyFunctions[(nChan!=nTxChan)*20 + ((unsigned)nChan<=4)*nChan*4 + dt];
+ fn(*this, dst, data, nChan, nTxChan);
+ }
+
+ void applyConst(double* dst, void* data, DataType dt, int nChan);
+
+ private:
+
+ typedef void (*ApplyFn)(PtexTriangleKernelIter& k, double* dst, void* data, int nChan, int nTxChan);
+ static ApplyFn applyFunctions[40];
+};
+
+
+/// Triangle filter kernel (in normalized triangle coords)
+class PtexTriangleKernel : public Ptex {
+ public:
+ Res res; // desired resolution
+ double u, v; // uv filter center
+ double u1, v1, w1; // uvw lower bounds
+ double u2, v2, w2; // uvw upper bounds
+ double A,B,C; // ellipse coefficients (F = A*C-B*B/4)
+
+ void set(Res resVal, double uVal, double vVal,
+ double u1Val, double v1Val, double w1Val,
+ double u2Val, double v2Val, double w2Val,
+ double AVal, double BVal, double CVal)
+ {
+ res = resVal;
+ u = uVal; v = vVal;
+ u1 = u1Val; v1 = v1Val; w1 = w1Val;
+ u2 = u2Val; v2 = v2Val; w2 = w2Val;
+ A = AVal; B = BVal; C = CVal;
+ }
+
+ void set(double uVal, double vVal,
+ double u1Val, double v1Val, double w1Val,
+ double u2Val, double v2Val, double w2Val)
+ {
+ u = uVal; v = vVal;
+ u1 = u1Val; v1 = v1Val; w1 = w1Val;
+ u2 = u2Val; v2 = v2Val; w2 = w2Val;
+ }
+
+ void setABC(double AVal, double BVal, double CVal)
+ {
+ A = AVal; B = BVal; C = CVal;
+ }
+
+ void splitU(PtexTriangleKernel& ka)
+ {
+ ka = *this;
+ u1 = 0;
+ ka.u2 = 0;
+ }
+
+ void splitV(PtexTriangleKernel& ka)
+ {
+ ka = *this;
+ v1 = 0;
+ ka.v2 = 0;
+ }
+
+ void splitW(PtexTriangleKernel& ka)
+ {
+ ka = *this;
+ w1 = 0;
+ ka.w2 = 0;
+ }
+
+ void rotate1()
+ {
+ // rotate ellipse where u'=w, v'=u, w'=v
+ // (derived by converting to Barycentric form, rotating, and converting back)
+ setABC(C, 2*C-B, A+C-B);
+ }
+
+ void rotate2()
+ {
+ // rotate ellipse where u'=v, v'=w, w'=u
+ // (derived by converting to Barycentric form, rotating, and converting back)
+ setABC(A+C-B, 2*A-B, A);
+ }
+
+ void reorient(int eid, int aeid)
+ {
+ double w = 1-u-v;
+
+#define C(eid, aeid) (eid*3 + aeid)
+ switch (C(eid, aeid)) {
+ case C(0, 0): set(1-u, -v, 1-u2, -v2, 1-w2, 1-u1, -v1, 1-w1); break;
+ case C(0, 1): set(1-w, 1-u, 1-w2, 1-u2, -v2, 1-w1, 1-u1, -v1); rotate1(); break;
+ case C(0, 2): set( -v, 1-w, -v2, 1-w2, 1-u2, -v1, 1-w1, 1-u1); rotate2(); break;
+
+ case C(1, 0): set(1-v, -w, 1-v2, -w2, 1-u2, 1-v1, -w1, 1-u1); rotate2(); break;
+ case C(1, 1): set(1-u, 1-v, 1-u2, 1-v2, -w2, 1-u1, 1-v1, -w1); break;
+ case C(1, 2): set( -w, 1-u, -w2, 1-u2, 1-v2, -w1, 1-u1, 1-v1); rotate1(); break;
+
+ case C(2, 0): set(1-w, -u, 1-w2, -u2, 1-v2, 1-w1, -u1, 1-v1); rotate1(); break;
+ case C(2, 1): set(1-v, 1-w, 1-v2, 1-w2, -u2, 1-v1, 1-w1, -u1); rotate2(); break;
+ case C(2, 2): set( -u, 1-v, -u2, 1-v2, 1-w2, -u1, 1-v1, 1-w1); break;
+#undef C
+ }
+ }
+
+ void clampRes(Res fres)
+ {
+ res.ulog2 = PtexUtils::min(res.ulog2, fres.ulog2);
+ res.vlog2 = res.ulog2;
+ }
+
+ void clampExtent()
+ {
+ u1 = PtexUtils::max(u1, 0.0);
+ v1 = PtexUtils::max(v1, 0.0);
+ w1 = PtexUtils::max(w1, 0.0);
+ u2 = PtexUtils::min(u2, 1-(v1+w1));
+ v2 = PtexUtils::min(v2, 1-(w1+u1));
+ w2 = PtexUtils::min(w2, 1-(u1+v1));
+ }
+
+ void getIterators(PtexTriangleKernelIter& ke, PtexTriangleKernelIter& ko)
+ {
+ int resu = res.u();
+
+ // normalize coefficients for texel units
+ double Finv = 1.0/(resu*resu*(A*C - 0.25 * B * B));
+ double Ak = A*Finv, Bk = B*Finv, Ck = C*Finv;
+
+ // build even iterator
+ ke.rowlen = resu;
+ ke.wscale = 1.0/(resu*resu);
+ double scale = ke.rowlen;
+ ke.u = u * scale - 1/3.0;
+ ke.v = v * scale - 1/3.0;
+ ke.u1 = int(ceil(u1 * scale - 1/3.0));
+ ke.v1 = int(ceil(v1 * scale - 1/3.0));
+ ke.w1 = int(ceil(w1 * scale - 1/3.0));
+ ke.u2 = int(ceil(u2 * scale - 1/3.0));
+ ke.v2 = int(ceil(v2 * scale - 1/3.0));
+ ke.w2 = int(ceil(w2 * scale - 1/3.0));
+ ke.A = Ak; ke.B = Bk; ke.C = Ck;
+ ke.valid = (ke.u2 > ke.u1 && ke.v2 > ke.v1 && ke.w2 > ke.w1);
+ ke.weight = 0;
+
+ // build odd iterator: flip kernel across diagonal (u = 1-v, v = 1-u, w = -w)
+ ko.rowlen = ke.rowlen;
+ ko.wscale = ke.wscale;
+ ko.u = (1-v) * scale - 1/3.0;
+ ko.v = (1-u) * scale - 1/3.0;
+ ko.u1 = int(ceil((1-v2) * scale - 1/3.0));
+ ko.v1 = int(ceil((1-u2) * scale - 1/3.0));
+ ko.w1 = int(ceil(( -w2) * scale - 1/3.0));
+ ko.u2 = int(ceil((1-v1) * scale - 1/3.0));
+ ko.v2 = int(ceil((1-u1) * scale - 1/3.0));
+ ko.w2 = int(ceil(( -w1) * scale - 1/3.0));
+ ko.A = Ck; ko.B = Bk; ko.C = Ak;
+ ko.valid = (ko.u2 > ko.u1 && ko.v2 > ko.v1 && ko.w2 > ko.w1);
+ ko.weight = 0;
+ }
+};
+
+#endif
diff --git a/extern/ptex/src/ptex/PtexUtils.cpp b/extern/ptex/src/ptex/PtexUtils.cpp
new file mode 100644
index 00000000000..59cc7b62021
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexUtils.cpp
@@ -0,0 +1,677 @@
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+#include "PtexPlatform.h"
+#include <algorithm>
+#include <vector>
+#include <stdlib.h>
+#include <string.h>
+
+#include "PtexHalf.h"
+#include "PtexUtils.h"
+
+const char* Ptex::MeshTypeName(MeshType mt)
+{
+ static const char* names[] = { "triangle", "quad" };
+ if (mt < 0 || mt >= int(sizeof(names)/sizeof(const char*)))
+ return "(invalid mesh type)";
+ return names[mt];
+}
+
+
+const char* Ptex::DataTypeName(DataType dt)
+{
+ static const char* names[] = { "uint8", "uint16", "float16", "float32" };
+ if (dt < 0 || dt >= int(sizeof(names)/sizeof(const char*)))
+ return "(invalid data type)";
+ return names[dt];
+}
+
+
+const char* Ptex::BorderModeName(BorderMode m)
+{
+ static const char* names[] = { "clamp", "black", "periodic" };
+ if (m < 0 || m >= int(sizeof(names)/sizeof(const char*)))
+ return "(invalid border mode)";
+ return names[m];
+}
+
+
+const char* Ptex::EdgeIdName(EdgeId eid)
+{
+ static const char* names[] = { "bottom", "right", "top", "left" };
+ if (eid < 0 || eid >= int(sizeof(names)/sizeof(const char*)))
+ return "(invalid edge id)";
+ return names[eid];
+}
+
+
+const char* Ptex::MetaDataTypeName(MetaDataType mdt)
+{
+ static const char* names[] = { "string", "int8", "int16", "int32", "float", "double" };
+ if (mdt < 0 || mdt >= int(sizeof(names)/sizeof(const char*)))
+ return "(invalid meta data type)";
+ return names[mdt];
+}
+
+
+
+namespace {
+ template<typename DST, typename SRC>
+ void ConvertArrayClamped(DST* dst, SRC* src, int numChannels, double scale, double round=0)
+ {
+ for (int i = 0; i < numChannels; i++)
+ dst[i] = DST(PtexUtils::clamp(src[i], 0.0f, 1.0f) * scale + round);
+ }
+
+ template<typename DST, typename SRC>
+ void ConvertArray(DST* dst, SRC* src, int numChannels, double scale, double round=0)
+ {
+ for (int i = 0; i < numChannels; i++) dst[i] = DST(src[i] * scale + round);
+ }
+}
+
+void Ptex::ConvertToFloat(float* dst, const void* src, Ptex::DataType dt, int numChannels)
+{
+ switch (dt) {
+ case dt_uint8: ConvertArray(dst, (uint8_t*)src, numChannels, 1/255.0); break;
+ case dt_uint16: ConvertArray(dst, (uint16_t*)src, numChannels, 1/65535.0); break;
+ case dt_half: ConvertArray(dst, (PtexHalf*)src, numChannels, 1.0); break;
+ case dt_float: memcpy(dst, src, sizeof(float)*numChannels); break;
+ }
+}
+
+
+void Ptex::ConvertFromFloat(void* dst, const float* src, Ptex::DataType dt, int numChannels)
+{
+ switch (dt) {
+ case dt_uint8: ConvertArrayClamped((uint8_t*)dst, src, numChannels, 255.0, 0.5); break;
+ case dt_uint16: ConvertArrayClamped((uint16_t*)dst, src, numChannels, 65535.0, 0.5); break;
+ case dt_half: ConvertArray((PtexHalf*)dst, src, numChannels, 1.0); break;
+ case dt_float: memcpy(dst, src, sizeof(float)*numChannels); break;
+ }
+}
+
+
+bool PtexUtils::isConstant(const void* data, int stride, int ures, int vres,
+ int pixelSize)
+{
+ int rowlen = pixelSize * ures;
+ const char* p = (const char*) data + stride;
+
+ // compare each row with the first
+ for (int i = 1; i < vres; i++, p += stride)
+ if (0 != memcmp(data, p, rowlen)) return 0;
+
+ // make sure first row is constant
+ p = (const char*) data + pixelSize;
+ for (int i = 1; i < ures; i++, p += pixelSize)
+ if (0 != memcmp(data, p, pixelSize)) return 0;
+
+ return 1;
+}
+
+
+namespace {
+ template<typename T>
+ inline void interleave(const T* src, int sstride, int uw, int vw,
+ T* dst, int dstride, int nchan)
+ {
+ sstride /= sizeof(T);
+ dstride /= sizeof(T);
+ // for each channel
+ for (T* dstend = dst + nchan; dst != dstend; dst++) {
+ // for each row
+ T* drow = dst;
+ for (const T* rowend = src + sstride*vw; src != rowend;
+ src += sstride, drow += dstride) {
+ // copy each pixel across the row
+ T* dp = drow;
+ for (const T* sp = src, * end = sp + uw; sp != end; dp += nchan)
+ *dp = *sp++;
+ }
+ }
+ }
+}
+
+
+void PtexUtils::interleave(const void* src, int sstride, int uw, int vw,
+ void* dst, int dstride, DataType dt, int nchan)
+{
+ switch (dt) {
+ case dt_uint8: ::interleave((const uint8_t*) src, sstride, uw, vw,
+ (uint8_t*) dst, dstride, nchan); break;
+ case dt_half:
+ case dt_uint16: ::interleave((const uint16_t*) src, sstride, uw, vw,
+ (uint16_t*) dst, dstride, nchan); break;
+ case dt_float: ::interleave((const float*) src, sstride, uw, vw,
+ (float*) dst, dstride, nchan); break;
+ }
+}
+
+namespace {
+ template<typename T>
+ inline void deinterleave(const T* src, int sstride, int uw, int vw,
+ T* dst, int dstride, int nchan)
+ {
+ sstride /= sizeof(T);
+ dstride /= sizeof(T);
+ // for each channel
+ for (const T* srcend = src + nchan; src != srcend; src++) {
+ // for each row
+ const T* srow = src;
+ for (const T* rowend = srow + sstride*vw; srow != rowend;
+ srow += sstride, dst += dstride) {
+ // copy each pixel across the row
+ const T* sp = srow;
+ for (T* dp = dst, * end = dp + uw; dp != end; sp += nchan)
+ *dp++ = *sp;
+ }
+ }
+ }
+}
+
+
+void PtexUtils::deinterleave(const void* src, int sstride, int uw, int vw,
+ void* dst, int dstride, DataType dt, int nchan)
+{
+ switch (dt) {
+ case dt_uint8: ::deinterleave((const uint8_t*) src, sstride, uw, vw,
+ (uint8_t*) dst, dstride, nchan); break;
+ case dt_half:
+ case dt_uint16: ::deinterleave((const uint16_t*) src, sstride, uw, vw,
+ (uint16_t*) dst, dstride, nchan); break;
+ case dt_float: ::deinterleave((const float*) src, sstride, uw, vw,
+ (float*) dst, dstride, nchan); break;
+ }
+}
+
+
+namespace {
+ template<typename T>
+ void encodeDifference(T* data, int size)
+ {
+ size /= sizeof(T);
+ T* p = (T*) data, * end = p + size, tmp, prev = 0;
+ while (p != end) { tmp = prev; prev = *p; *p++ -= tmp; }
+ }
+}
+
+void PtexUtils::encodeDifference(void* data, int size, DataType dt)
+{
+ switch (dt) {
+ case dt_uint8: ::encodeDifference((uint8_t*) data, size); break;
+ case dt_uint16: ::encodeDifference((uint16_t*) data, size); break;
+ default: break; // skip other types
+ }
+}
+
+
+namespace {
+ template<typename T>
+ void decodeDifference(T* data, int size)
+ {
+ size /= sizeof(T);
+ T* p = (T*) data, * end = p + size, prev = 0;
+ while (p != end) { *p += prev; prev = *p++; }
+ }
+}
+
+void PtexUtils::decodeDifference(void* data, int size, DataType dt)
+{
+ switch (dt) {
+ case dt_uint8: ::decodeDifference((uint8_t*) data, size); break;
+ case dt_uint16: ::decodeDifference((uint16_t*) data, size); break;
+ default: break; // skip other types
+ }
+}
+
+
+namespace {
+ template<typename T>
+ inline void reduce(const T* src, int sstride, int uw, int vw,
+ T* dst, int dstride, int nchan)
+ {
+ sstride /= sizeof(T);
+ dstride /= sizeof(T);
+ int rowlen = uw*nchan;
+ int srowskip = 2*sstride - rowlen;
+ int drowskip = dstride - rowlen/2;
+ for (const T* end = src + vw*sstride; src != end;
+ src += srowskip, dst += drowskip)
+ for (const T* rowend = src + rowlen; src != rowend; src += nchan)
+ for (const T* pixend = src+nchan; src != pixend; src++)
+ *dst++ = T(0.25 * (src[0] + src[nchan] +
+ src[sstride] + src[sstride+nchan]));
+ }
+}
+
+void PtexUtils::reduce(const void* src, int sstride, int uw, int vw,
+ void* dst, int dstride, DataType dt, int nchan)
+{
+ switch (dt) {
+ case dt_uint8: ::reduce((const uint8_t*) src, sstride, uw, vw,
+ (uint8_t*) dst, dstride, nchan); break;
+ case dt_half: ::reduce((const PtexHalf*) src, sstride, uw, vw,
+ (PtexHalf*) dst, dstride, nchan); break;
+ case dt_uint16: ::reduce((const uint16_t*) src, sstride, uw, vw,
+ (uint16_t*) dst, dstride, nchan); break;
+ case dt_float: ::reduce((const float*) src, sstride, uw, vw,
+ (float*) dst, dstride, nchan); break;
+ }
+}
+
+
+namespace {
+ template<typename T>
+ inline void reduceu(const T* src, int sstride, int uw, int vw,
+ T* dst, int dstride, int nchan)
+ {
+ sstride /= sizeof(T);
+ dstride /= sizeof(T);
+ int rowlen = uw*nchan;
+ int srowskip = sstride - rowlen;
+ int drowskip = dstride - rowlen/2;
+ for (const T* end = src + vw*sstride; src != end;
+ src += srowskip, dst += drowskip)
+ for (const T* rowend = src + rowlen; src != rowend; src += nchan)
+ for (const T* pixend = src+nchan; src != pixend; src++)
+ *dst++ = T(0.5 * (src[0] + src[nchan]));
+ }
+}
+
+void PtexUtils::reduceu(const void* src, int sstride, int uw, int vw,
+ void* dst, int dstride, DataType dt, int nchan)
+{
+ switch (dt) {
+ case dt_uint8: ::reduceu((const uint8_t*) src, sstride, uw, vw,
+ (uint8_t*) dst, dstride, nchan); break;
+ case dt_half: ::reduceu((const PtexHalf*) src, sstride, uw, vw,
+ (PtexHalf*) dst, dstride, nchan); break;
+ case dt_uint16: ::reduceu((const uint16_t*) src, sstride, uw, vw,
+ (uint16_t*) dst, dstride, nchan); break;
+ case dt_float: ::reduceu((const float*) src, sstride, uw, vw,
+ (float*) dst, dstride, nchan); break;
+ }
+}
+
+
+namespace {
+ template<typename T>
+ inline void reducev(const T* src, int sstride, int uw, int vw,
+ T* dst, int dstride, int nchan)
+ {
+ sstride /= sizeof(T);
+ dstride /= sizeof(T);
+ int rowlen = uw*nchan;
+ int srowskip = 2*sstride - rowlen;
+ int drowskip = dstride - rowlen;
+ for (const T* end = src + vw*sstride; src != end;
+ src += srowskip, dst += drowskip)
+ for (const T* rowend = src + rowlen; src != rowend; src++)
+ *dst++ = T(0.5 * (src[0] + src[sstride]));
+ }
+}
+
+void PtexUtils::reducev(const void* src, int sstride, int uw, int vw,
+ void* dst, int dstride, DataType dt, int nchan)
+{
+ switch (dt) {
+ case dt_uint8: ::reducev((const uint8_t*) src, sstride, uw, vw,
+ (uint8_t*) dst, dstride, nchan); break;
+ case dt_half: ::reducev((const PtexHalf*) src, sstride, uw, vw,
+ (PtexHalf*) dst, dstride, nchan); break;
+ case dt_uint16: ::reducev((const uint16_t*) src, sstride, uw, vw,
+ (uint16_t*) dst, dstride, nchan); break;
+ case dt_float: ::reducev((const float*) src, sstride, uw, vw,
+ (float*) dst, dstride, nchan); break;
+ }
+}
+
+
+
+namespace {
+ // generate a reduction of a packed-triangle texture
+ // note: this method won't work for tiled textures
+ template<typename T>
+ inline void reduceTri(const T* src, int sstride, int w, int /*vw*/,
+ T* dst, int dstride, int nchan)
+ {
+ sstride /= sizeof(T);
+ dstride /= sizeof(T);
+ int rowlen = w*nchan;
+ const T* src2 = src + (w-1) * sstride + rowlen - nchan;
+ int srowinc2 = -2*sstride - nchan;
+ int srowskip = 2*sstride - rowlen;
+ int srowskip2 = w*sstride - 2 * nchan;
+ int drowskip = dstride - rowlen/2;
+ for (const T* end = src + w*sstride; src != end;
+ src += srowskip, src2 += srowskip2, dst += drowskip)
+ for (const T* rowend = src + rowlen; src != rowend; src += nchan, src2 += srowinc2)
+ for (const T* pixend = src+nchan; src != pixend; src++, src2++)
+ *dst++ = T(0.25 * (src[0] + src[nchan] + src[sstride] + src2[0]));
+ }
+}
+
+void PtexUtils::reduceTri(const void* src, int sstride, int w, int /*vw*/,
+ void* dst, int dstride, DataType dt, int nchan)
+{
+ switch (dt) {
+ case dt_uint8: ::reduceTri((const uint8_t*) src, sstride, w, 0,
+ (uint8_t*) dst, dstride, nchan); break;
+ case dt_half: ::reduceTri((const PtexHalf*) src, sstride, w, 0,
+ (PtexHalf*) dst, dstride, nchan); break;
+ case dt_uint16: ::reduceTri((const uint16_t*) src, sstride, w, 0,
+ (uint16_t*) dst, dstride, nchan); break;
+ case dt_float: ::reduceTri((const float*) src, sstride, w, 0,
+ (float*) dst, dstride, nchan); break;
+ }
+}
+
+
+void PtexUtils::fill(const void* src, void* dst, int dstride,
+ int ures, int vres, int pixelsize)
+{
+ // fill first row
+ int rowlen = ures*pixelsize;
+ char* ptr = (char*) dst;
+ char* end = ptr + rowlen;
+ for (; ptr != end; ptr += pixelsize) memcpy(ptr, src, pixelsize);
+
+ // fill remaining rows from first row
+ ptr = (char*) dst + dstride;
+ end = (char*) dst + vres*dstride;
+ for (; ptr != end; ptr += dstride) memcpy(ptr, dst, rowlen);
+}
+
+
+void PtexUtils::copy(const void* src, int sstride, void* dst, int dstride,
+ int vres, int rowlen)
+{
+ // regular non-tiled case
+ if (sstride == rowlen && dstride == rowlen) {
+ // packed case - copy in single block
+ memcpy(dst, src, vres*rowlen);
+ } else {
+ // copy a row at a time
+ char* sptr = (char*) src;
+ char* dptr = (char*) dst;
+ for (char* end = sptr + vres*sstride; sptr != end;) {
+ memcpy(dptr, sptr, rowlen);
+ dptr += dstride;
+ sptr += sstride;
+ }
+ }
+}
+
+
+namespace {
+ template<typename T>
+ inline void blend(const T* src, float weight, T* dst, int rowlen, int nchan)
+ {
+ for (const T* end = src + rowlen * nchan; src != end; dst++)
+ *dst = *dst + T(weight * *src++);
+ }
+
+ template<typename T>
+ inline void blendflip(const T* src, float weight, T* dst, int rowlen, int nchan)
+ {
+ dst += (rowlen-1) * nchan;
+ for (const T* end = src + rowlen * nchan; src != end;) {
+ for (int i = 0; i < nchan; i++, dst++)
+ *dst = *dst + T(weight * *src++);
+ dst -= nchan*2;
+ }
+ }
+}
+
+
+void PtexUtils::blend(const void* src, float weight, void* dst, bool flip,
+ int rowlen, DataType dt, int nchan)
+{
+ switch ((dt<<1) | int(flip)) {
+ case (dt_uint8<<1): ::blend((const uint8_t*) src, weight,
+ (uint8_t*) dst, rowlen, nchan); break;
+ case (dt_uint8<<1 | 1): ::blendflip((const uint8_t*) src, weight,
+ (uint8_t*) dst, rowlen, nchan); break;
+ case (dt_half<<1): ::blend((const PtexHalf*) src, weight,
+ (PtexHalf*) dst, rowlen, nchan); break;
+ case (dt_half<<1 | 1): ::blendflip((const PtexHalf*) src, weight,
+ (PtexHalf*) dst, rowlen, nchan); break;
+ case (dt_uint16<<1): ::blend((const uint16_t*) src, weight,
+ (uint16_t*) dst, rowlen, nchan); break;
+ case (dt_uint16<<1 | 1): ::blendflip((const uint16_t*) src, weight,
+ (uint16_t*) dst, rowlen, nchan); break;
+ case (dt_float<<1): ::blend((const float*) src, weight,
+ (float*) dst, rowlen, nchan); break;
+ case (dt_float<<1 | 1): ::blendflip((const float*) src, weight,
+ (float*) dst, rowlen, nchan); break;
+ }
+}
+
+
+namespace {
+ template<typename T>
+ inline void average(const T* src, int sstride, int uw, int vw,
+ T* dst, int nchan)
+ {
+ float* buff = (float*) alloca(nchan*sizeof(float));
+ memset(buff, 0, nchan*sizeof(float));
+ sstride /= sizeof(T);
+ int rowlen = uw*nchan;
+ int rowskip = sstride - rowlen;
+ for (const T* end = src + vw*sstride; src != end; src += rowskip)
+ for (const T* rowend = src + rowlen; src != rowend;)
+ for (int i = 0; i < nchan; i++) buff[i] += *src++;
+ double scale = 1.0/(uw*vw);
+ for (int i = 0; i < nchan; i++) dst[i] = T(buff[i]*scale);
+ }
+}
+
+void PtexUtils::average(const void* src, int sstride, int uw, int vw,
+ void* dst, DataType dt, int nchan)
+{
+ switch (dt) {
+ case dt_uint8: ::average((const uint8_t*) src, sstride, uw, vw,
+ (uint8_t*) dst, nchan); break;
+ case dt_half: ::average((const PtexHalf*) src, sstride, uw, vw,
+ (PtexHalf*) dst, nchan); break;
+ case dt_uint16: ::average((const uint16_t*) src, sstride, uw, vw,
+ (uint16_t*) dst, nchan); break;
+ case dt_float: ::average((const float*) src, sstride, uw, vw,
+ (float*) dst, nchan); break;
+ }
+}
+
+
+namespace {
+ struct CompareRfaceIds {
+ const Ptex::FaceInfo* faces;
+ CompareRfaceIds(const Ptex::FaceInfo* faces) : faces(faces) {}
+ bool operator() (uint32_t faceid1, uint32_t faceid2)
+ {
+ const Ptex::FaceInfo& f1 = faces[faceid1];
+ const Ptex::FaceInfo& f2 = faces[faceid2];
+ int min1 = f1.isConstant() ? 1 : PtexUtils::min(f1.res.ulog2, f1.res.vlog2);
+ int min2 = f2.isConstant() ? 1 : PtexUtils::min(f2.res.ulog2, f2.res.vlog2);
+ return min1 > min2;
+ }
+ };
+}
+
+
+namespace {
+ template<typename T>
+ inline void multalpha(T* data, int npixels, int nchannels, int alphachan, double scale)
+ {
+ int alphaoffset; // offset to alpha chan from data ptr
+ int nchanmult; // number of channels to alpha-multiply
+ if (alphachan == 0) {
+ // first channel is alpha chan: mult the rest of the channels
+ data++;
+ alphaoffset = -1;
+ nchanmult = nchannels - 1;
+ }
+ else {
+ // mult all channels up to alpha chan
+ alphaoffset = alphachan;
+ nchanmult = alphachan;
+ }
+
+ for (T* end = data + npixels*nchannels; data != end; data += nchannels) {
+ double aval = scale * data[alphaoffset];
+ for (int i = 0; i < nchanmult; i++) data[i] = T(data[i] * aval);
+ }
+ }
+}
+
+void PtexUtils::multalpha(void* data, int npixels, DataType dt, int nchannels, int alphachan)
+{
+ double scale = OneValueInv(dt);
+ switch(dt) {
+ case dt_uint8: ::multalpha((uint8_t*) data, npixels, nchannels, alphachan, scale); break;
+ case dt_uint16: ::multalpha((uint16_t*) data, npixels, nchannels, alphachan, scale); break;
+ case dt_half: ::multalpha((PtexHalf*) data, npixels, nchannels, alphachan, scale); break;
+ case dt_float: ::multalpha((float*) data, npixels, nchannels, alphachan, scale); break;
+ }
+}
+
+
+namespace {
+ template<typename T>
+ inline void divalpha(T* data, int npixels, int nchannels, int alphachan, double scale)
+ {
+ int alphaoffset; // offset to alpha chan from data ptr
+ int nchandiv; // number of channels to alpha-divide
+ if (alphachan == 0) {
+ // first channel is alpha chan: div the rest of the channels
+ data++;
+ alphaoffset = -1;
+ nchandiv = nchannels - 1;
+ }
+ else {
+ // div all channels up to alpha chan
+ alphaoffset = alphachan;
+ nchandiv = alphachan;
+ }
+
+ for (T* end = data + npixels*nchannels; data != end; data += nchannels) {
+ T alpha = data[alphaoffset];
+ if (!alpha) continue; // don't divide by zero!
+ double aval = scale / alpha;
+ for (int i = 0; i < nchandiv; i++) data[i] = T(data[i] * aval);
+ }
+ }
+}
+
+void PtexUtils::divalpha(void* data, int npixels, DataType dt, int nchannels, int alphachan)
+{
+ double scale = OneValue(dt);
+ switch(dt) {
+ case dt_uint8: ::divalpha((uint8_t*) data, npixels, nchannels, alphachan, scale); break;
+ case dt_uint16: ::divalpha((uint16_t*) data, npixels, nchannels, alphachan, scale); break;
+ case dt_half: ::divalpha((PtexHalf*) data, npixels, nchannels, alphachan, scale); break;
+ case dt_float: ::divalpha((float*) data, npixels, nchannels, alphachan, scale); break;
+ }
+}
+
+
+void PtexUtils::genRfaceids(const FaceInfo* faces, int nfaces,
+ uint32_t* rfaceids, uint32_t* faceids)
+{
+ // stable_sort faceids by smaller dimension (u or v) in descending order
+ // treat const faces as having res of 1
+
+ // init faceids
+ for (int i = 0; i < nfaces; i++) faceids[i] = i;
+
+ // sort faceids by rfaceid
+ std::stable_sort(faceids, faceids + nfaces, CompareRfaceIds(faces));
+
+ // generate mapping from faceid to rfaceid
+ for (int i = 0; i < nfaces; i++) {
+ // note: i is the rfaceid
+ rfaceids[faceids[i]] = i;
+ }
+}
+
+namespace {
+ // apply to 1..4 channels, unrolled
+ template<class T, int nChan>
+ void ApplyConst(double weight, double* dst, void* data, int /*nChan*/)
+ {
+ // dst[i] += data[i] * weight for i in {0..n-1}
+ PtexUtils::VecAccum<T,nChan>()(dst, (T*) data, weight);
+ }
+
+ // apply to N channels (general case)
+ template<class T>
+ void ApplyConstN(double weight, double* dst, void* data, int nChan)
+ {
+ // dst[i] += data[i] * weight for i in {0..n-1}
+ PtexUtils::VecAccumN<T>()(dst, (T*) data, nChan, weight);
+ }
+}
+
+PtexUtils::ApplyConstFn
+PtexUtils::applyConstFunctions[20] = {
+ ApplyConstN<uint8_t>, ApplyConstN<uint16_t>, ApplyConstN<PtexHalf>, ApplyConstN<float>,
+ ApplyConst<uint8_t,1>, ApplyConst<uint16_t,1>, ApplyConst<PtexHalf,1>, ApplyConst<float,1>,
+ ApplyConst<uint8_t,2>, ApplyConst<uint16_t,2>, ApplyConst<PtexHalf,2>, ApplyConst<float,2>,
+ ApplyConst<uint8_t,3>, ApplyConst<uint16_t,3>, ApplyConst<PtexHalf,3>, ApplyConst<float,3>,
+ ApplyConst<uint8_t,4>, ApplyConst<uint16_t,4>, ApplyConst<PtexHalf,4>, ApplyConst<float,4>,
+};
+
+#ifndef PTEX_USE_STDSTRING
+Ptex::String::~String()
+{
+ if (_str) free(_str);
+}
+
+
+Ptex::String& Ptex::String::operator=(const char* str)
+{
+ if (_str) free(_str);
+ _str = str ? strdup(str) : 0;
+ return *this;
+}
+
+std::ostream& operator << (std::ostream& stream, const Ptex::String& str)
+{
+ stream << str.c_str();
+ return stream;
+}
+#endif
+
diff --git a/extern/ptex/src/ptex/PtexUtils.h b/extern/ptex/src/ptex/PtexUtils.h
new file mode 100644
index 00000000000..73293f06415
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexUtils.h
@@ -0,0 +1,199 @@
+#ifndef PtexUtils_h
+#define PtexUtils_h
+
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+#include "Ptexture.h"
+
+struct PtexUtils : public Ptex {
+
+ static bool isPowerOfTwo(int x)
+ {
+ return !(x&(x-1));
+ }
+
+ static uint32_t ones(uint32_t x)
+ {
+ // count number of ones
+ x = (x & 0x55555555) + ((x >> 1) & 0x55555555); // add pairs of bits
+ x = (x & 0x33333333) + ((x >> 2) & 0x33333333); // add bit pairs
+ x = (x & 0x0f0f0f0f) + ((x >> 4) & 0x0f0f0f0f); // add nybbles
+ x += (x >> 8); // add bytes
+ x += (x >> 16); // add words
+ return(x & 0xff);
+ }
+
+ static uint32_t floor_log2(uint32_t x)
+ {
+ // floor(log2(n))
+ x |= (x >> 1);
+ x |= (x >> 2);
+ x |= (x >> 4);
+ x |= (x >> 8);
+ x |= (x >> 16);
+ return ones(x>>1);
+ }
+
+ static uint32_t ceil_log2(uint32_t x)
+ {
+ // ceil(log2(n))
+ bool isPow2 = isPowerOfTwo(x);
+ x |= (x >> 1);
+ x |= (x >> 2);
+ x |= (x >> 4);
+ x |= (x >> 8);
+ x |= (x >> 16);
+ return ones(x>>1) + !isPow2;
+ }
+
+ static double smoothstep(double x, double a, double b)
+ {
+ if ( x < a ) return 0;
+ if ( x >= b ) return 1;
+ x = (x - a)/(b - a);
+ return x*x * (3 - 2*x);
+ }
+
+ static double qsmoothstep(double x, double a, double b)
+ {
+ // quintic smoothstep (cubic is only C1)
+ if ( x < a ) return 0;
+ if ( x >= b ) return 1;
+ x = (x - a)/(b - a);
+ return x*x*x * (10 + x * (-15 + x*6));
+ }
+
+ template<typename T>
+ static T cond(bool c, T a, T b) { return c * a + (!c)*b; }
+
+ template<typename T>
+ static T min(T a, T b) { return cond(a < b, a, b); }
+
+ template<typename T>
+ static T max(T a, T b) { return cond(a >= b, a, b); }
+
+ template<typename T>
+ static T clamp(T x, T lo, T hi) { return cond(x < lo, lo, cond(x > hi, hi, x)); }
+
+ static bool isConstant(const void* data, int stride, int ures, int vres,
+ int pixelSize);
+ static void interleave(const void* src, int sstride, int ures, int vres,
+ void* dst, int dstride, DataType dt, int nchannels);
+ static void deinterleave(const void* src, int sstride, int ures, int vres,
+ void* dst, int dstride, DataType dt, int nchannels);
+ static void encodeDifference(void* data, int size, DataType dt);
+ static void decodeDifference(void* data, int size, DataType dt);
+ typedef void ReduceFn(const void* src, int sstride, int ures, int vres,
+ void* dst, int dstride, DataType dt, int nchannels);
+ static void reduce(const void* src, int sstride, int ures, int vres,
+ void* dst, int dstride, DataType dt, int nchannels);
+ static void reduceu(const void* src, int sstride, int ures, int vres,
+ void* dst, int dstride, DataType dt, int nchannels);
+ static void reducev(const void* src, int sstride, int ures, int vres,
+ void* dst, int dstride, DataType dt, int nchannels);
+ static void reduceTri(const void* src, int sstride, int ures, int vres,
+ void* dst, int dstride, DataType dt, int nchannels);
+ static void average(const void* src, int sstride, int ures, int vres,
+ void* dst, DataType dt, int nchannels);
+ static void fill(const void* src, void* dst, int dstride,
+ int ures, int vres, int pixelsize);
+ static void copy(const void* src, int sstride, void* dst, int dstride,
+ int nrows, int rowlen);
+ static void blend(const void* src, float weight, void* dst, bool flip,
+ int rowlen, DataType dt, int nchannels);
+ static void multalpha(void* data, int npixels, DataType dt, int nchannels, int alphachan);
+ static void divalpha(void* data, int npixels, DataType dt, int nchannels, int alphachan);
+
+ static void genRfaceids(const FaceInfo* faces, int nfaces,
+ uint32_t* rfaceids, uint32_t* faceids);
+
+ // fixed length vector accumulator: dst[i] += val[i] * weight
+ template<typename T, int n>
+ struct VecAccum {
+ VecAccum() {}
+ void operator()(double* dst, const T* val, double weight)
+ {
+ *dst += *val * weight;
+ // use template to unroll loop
+ VecAccum<T,n-1>()(dst+1, val+1, weight);
+ }
+ };
+ template<typename T>
+ struct VecAccum<T,0> { void operator()(double*, const T*, double) {} };
+
+ // variable length vector accumulator: dst[i] += val[i] * weight
+ template<typename T>
+ struct VecAccumN {
+ void operator()(double* dst, const T* val, int nchan, double weight)
+ {
+ for (int i = 0; i < nchan; i++) dst[i] += val[i] * weight;
+ }
+ };
+
+ // fixed length vector multiplier: dst[i] += val[i] * weight
+ template<typename T, int n>
+ struct VecMult {
+ VecMult() {}
+ void operator()(double* dst, const T* val, double weight)
+ {
+ *dst = *val * weight;
+ // use template to unroll loop
+ VecMult<T,n-1>()(dst+1, val+1, weight);
+ }
+ };
+ template<typename T>
+ struct VecMult<T,0> { void operator()(double*, const T*, double) {} };
+
+ // variable length vector multiplier: dst[i] = val[i] * weight
+ template<typename T>
+ struct VecMultN {
+ void operator()(double* dst, const T* val, int nchan, double weight)
+ {
+ for (int i = 0; i < nchan; i++) dst[i] = val[i] * weight;
+ }
+ };
+
+ typedef void (*ApplyConstFn)(double weight, double* dst, void* data, int nChan);
+ static ApplyConstFn applyConstFunctions[20];
+ static void applyConst(double weight, double* dst, void* data, Ptex::DataType dt, int nChan)
+ {
+ // dispatch specialized apply function
+ ApplyConstFn fn = applyConstFunctions[((unsigned)nChan<=4)*nChan*4 + dt];
+ fn(weight, dst, data, nChan);
+ }
+};
+
+#endif
diff --git a/extern/ptex/src/ptex/PtexWriter.cpp b/extern/ptex/src/ptex/PtexWriter.cpp
new file mode 100644
index 00000000000..0d5a7a3bfed
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexWriter.cpp
@@ -0,0 +1,1400 @@
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+/* Ptex writer classes:
+
+ PtexIncrWriter implements "incremental" mode and simply appends
+ "edit" blocks to the end of the file.
+
+ PtexMainWriter implements both writing from scratch and updating
+ an existing file, either to add data or to "roll up" previous
+ incremental edits.
+
+ Because the various headers (faceinfo, levelinfo, etc.) are
+ variable-length and precede the data, and because the data size
+ is not known until it is compressed and written, all data
+ are written to a temp file and then copied at the end to the
+ final location. This happens during the "finish" phase.
+
+ Each time a texture is written to the file, a reduction of the
+ texture is also generated and stored. These reductions are stored
+ in a temporary form and recalled later as the resolution levels are
+ generated.
+
+ The final reduction for each face is averaged and stored in the
+ const data block.
+*/
+
+#include "PtexPlatform.h"
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+
+#include "Ptexture.h"
+#include "PtexUtils.h"
+#include "PtexWriter.h"
+
+
+namespace {
+
+ FILE* OpenTempFile(std::string& tmppath)
+ {
+ static Mutex lock;
+ AutoMutex locker(lock);
+
+ // choose temp dir
+ static std::string tmpdir;
+ static int initialized = 0;
+ if (!initialized) {
+ initialized = 1;
+#ifdef WINDOWS
+ // use GetTempPath API (first call determines length of result)
+ DWORD result = ::GetTempPath(0, (LPSTR) L"");
+ if (result > 0) {
+ std::vector<TCHAR> tempPath(result + 1);
+ result = ::GetTempPath(static_cast<DWORD>(tempPath.size()), &tempPath[0]);
+ if (result > 0 && result <= tempPath.size())
+ tmpdir = std::string(tempPath.begin(),
+ tempPath.begin() + static_cast<std::size_t>(result));
+ else
+ tmpdir = ".";
+ }
+#else
+ // try $TEMP or $TMP, use /tmp as last resort
+ const char* t = getenv("TEMP");
+ if (!t) t = getenv("TMP");
+ if (!t) t = "/tmp";
+ tmpdir = t;
+#endif
+ }
+
+ // build temp path
+
+#ifdef WINDOWS
+ // use process id and counter to make unique filename
+ std::stringstream s;
+ static int count = 0;
+ s << tmpdir << "/" << "PtexTmp" << _getpid() << "_" << ++count;
+ tmppath = s.str();
+ return fopen((char*) tmppath.c_str(), "wb+");
+#else
+ // use mkstemp to open unique file
+ tmppath = tmpdir + "/PtexTmpXXXXXX";
+ int fd = mkstemp((char*) tmppath.c_str());
+ return fdopen(fd, "w+");
+#endif
+ }
+
+ std::string fileError(const char* message, const char* path)
+ {
+ std::stringstream str;
+ str << message << path << "\n" << strerror(errno);
+ return str.str();
+ }
+
+ bool checkFormat(Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan,
+ Ptex::String& error)
+ {
+ // check to see if given file attributes are valid
+ if (!PtexIO::LittleEndian()) {
+ error = "PtexWriter doesn't currently support big-endian cpu's";
+ return 0;
+ }
+
+ if (mt < Ptex::mt_triangle || mt > Ptex::mt_quad) {
+ error = "PtexWriter error: Invalid mesh type";
+ return 0;
+ }
+
+ if (dt < Ptex::dt_uint8 || dt > Ptex::dt_float) {
+ error = "PtexWriter error: Invalid data type";
+ return 0;
+ }
+
+ if (nchannels <= 0) {
+ error = "PtexWriter error: Invalid number of channels";
+ return 0;
+ }
+
+ if (alphachan != -1 && (alphachan < 0 || alphachan >= nchannels)) {
+ error = "PtexWriter error: Invalid alpha channel";
+ return 0;
+ }
+
+ return 1;
+ }
+}
+
+
+PtexWriter* PtexWriter::open(const char* path,
+ Ptex::MeshType mt, Ptex::DataType dt,
+ int nchannels, int alphachan, int nfaces,
+ Ptex::String& error, bool genmipmaps)
+{
+ if (!checkFormat(mt, dt, nchannels, alphachan, error))
+ return 0;
+
+ PtexMainWriter* w = new PtexMainWriter(path, 0,
+ mt, dt, nchannels, alphachan, nfaces,
+ genmipmaps);
+ std::string errstr;
+ if (!w->ok(error)) {
+ w->release();
+ return 0;
+ }
+ return w;
+}
+
+
+PtexWriter* PtexWriter::edit(const char* path, bool incremental,
+ Ptex::MeshType mt, Ptex::DataType dt,
+ int nchannels, int alphachan, int nfaces,
+ Ptex::String& error, bool genmipmaps)
+{
+ if (!checkFormat(mt, dt, nchannels, alphachan, error))
+ return 0;
+
+ // try to open existing file (it might not exist)
+ FILE* fp = fopen(path, "rb+");
+ if (!fp && errno != ENOENT) {
+ error = fileError("Can't open ptex file for update: ", path).c_str();
+ }
+
+ PtexWriterBase* w = 0;
+ // use incremental writer iff incremental mode requested and file exists
+ if (incremental && fp) {
+ w = new PtexIncrWriter(path, fp, mt, dt, nchannels, alphachan, nfaces);
+ }
+ // otherwise use main writer
+ else {
+ PtexTexture* tex = 0;
+ if (fp) {
+ // got an existing file, close and reopen with PtexReader
+ fclose(fp);
+
+ // open reader for existing file
+ tex = PtexTexture::open(path, error);
+ if (!tex) return 0;
+
+ // make sure header matches
+ bool headerMatch = (mt == tex->meshType() &&
+ dt == tex->dataType() &&
+ nchannels == tex->numChannels() &&
+ alphachan == tex->alphaChannel() &&
+ nfaces == tex->numFaces());
+ if (!headerMatch) {
+ std::stringstream str;
+ str << "PtexWriter::edit error: header doesn't match existing file, "
+ << "conversions not currently supported";
+ error = str.str().c_str();
+ return 0;
+ }
+ }
+ w = new PtexMainWriter(path, tex, mt, dt, nchannels, alphachan,
+ nfaces, genmipmaps);
+ }
+
+ if (!w->ok(error)) {
+ w->release();
+ return 0;
+ }
+ return w;
+}
+
+
+bool PtexWriter::applyEdits(const char* path, Ptex::String& error)
+{
+ // open reader for existing file
+ PtexTexture* tex = PtexTexture::open(path, error);
+ if (!tex) return 0;
+
+ // see if we have any edits to apply
+ if (tex->hasEdits()) {
+ // create non-incremental writer
+ PtexWriter* w = new PtexMainWriter(path, tex, tex->meshType(), tex->dataType(),
+ tex->numChannels(), tex->alphaChannel(), tex->numFaces(),
+ tex->hasMipMaps());
+ // close to rebuild file
+ if (!w->close(error)) return 0;
+ w->release();
+ }
+ return 1;
+}
+
+
+PtexWriterBase::PtexWriterBase(const char* path,
+ Ptex::MeshType mt, Ptex::DataType dt,
+ int nchannels, int alphachan, int nfaces,
+ bool compress)
+ : _ok(true),
+ _path(path),
+ _tilefp(0)
+{
+ memset(&_header, 0, sizeof(_header));
+ _header.magic = Magic;
+ _header.version = PtexFileMajorVersion;
+ _header.minorversion = PtexFileMinorVersion;
+ _header.meshtype = mt;
+ _header.datatype = dt;
+ _header.alphachan = alphachan;
+ _header.nchannels = nchannels;
+ _header.nfaces = nfaces;
+ _header.nlevels = 0;
+ _header.extheadersize = sizeof(_extheader);
+ _pixelSize = _header.pixelSize();
+
+ memset(&_extheader, 0, sizeof(_extheader));
+
+ if (mt == mt_triangle)
+ _reduceFn = &PtexUtils::reduceTri;
+ else
+ _reduceFn = &PtexUtils::reduce;
+
+ memset(&_zstream, 0, sizeof(_zstream));
+ deflateInit(&_zstream, compress ? Z_DEFAULT_COMPRESSION : 0);
+
+ // create temp file for writing tiles
+ // (must compress each tile before assembling a tiled face)
+ std::string error;
+ _tilefp = OpenTempFile(_tilepath);
+ if (!_tilefp) {
+ setError(fileError("Error creating temp file: ", _tilepath.c_str()));
+ }
+}
+
+
+void PtexWriterBase::release()
+{
+ Ptex::String error;
+ // close writer if app didn't, and report error if any
+ if (_tilefp && !close(error))
+ std::cerr << error.c_str() << std::endl;
+ delete this;
+}
+
+PtexWriterBase::~PtexWriterBase()
+{
+ deflateEnd(&_zstream);
+}
+
+
+bool PtexWriterBase::close(Ptex::String& error)
+{
+ if (_ok) finish();
+ if (!_ok) getError(error);
+ if (_tilefp) {
+ fclose(_tilefp);
+ unlink(_tilepath.c_str());
+ _tilefp = 0;
+ }
+ return _ok;
+}
+
+
+bool PtexWriterBase::storeFaceInfo(int faceid, FaceInfo& f, const FaceInfo& src, int flags)
+{
+ if (faceid < 0 || size_t(faceid) >= _header.nfaces) {
+ setError("PtexWriter error: faceid out of range");
+ return 0;
+ }
+
+ if (_header.meshtype == mt_triangle && (f.res.ulog2 != f.res.vlog2)) {
+ setError("PtexWriter error: asymmetric face res not supported for triangle textures");
+ return 0;
+ }
+
+ // copy all values
+ f = src;
+
+ // and clear extraneous ones
+ if (_header.meshtype == mt_triangle) {
+ f.flags = 0; // no user-settable flags on triangles
+ f.adjfaces[3] = -1;
+ f.adjedges &= 0x3f; // clear all but bottom six bits
+ }
+ else {
+ // clear non-user-settable flags
+ f.flags &= FaceInfo::flag_subface;
+ }
+
+ // set new flags
+ f.flags |= flags;
+ return 1;
+}
+
+
+void PtexWriterBase::writeMeta(const char* key, const char* value)
+{
+ addMetaData(key, mdt_string, value, int(strlen(value)+1));
+}
+
+
+void PtexWriterBase::writeMeta(const char* key, const int8_t* value, int count)
+{
+ addMetaData(key, mdt_int8, value, count);
+}
+
+
+void PtexWriterBase::writeMeta(const char* key, const int16_t* value, int count)
+{
+ addMetaData(key, mdt_int16, value, count*sizeof(int16_t));
+}
+
+
+void PtexWriterBase::writeMeta(const char* key, const int32_t* value, int count)
+{
+ addMetaData(key, mdt_int32, value, count*sizeof(int32_t));
+}
+
+
+void PtexWriterBase::writeMeta(const char* key, const float* value, int count)
+{
+ addMetaData(key, mdt_float, value, count*sizeof(float));
+}
+
+
+void PtexWriterBase::writeMeta(const char* key, const double* value, int count)
+{
+ addMetaData(key, mdt_double, value, count*sizeof(double));
+}
+
+
+void PtexWriterBase::writeMeta(PtexMetaData* data)
+{
+ int nkeys = data->numKeys();
+ for (int i = 0; i < nkeys; i++) {
+ const char* key = 0;
+ MetaDataType type;
+ data->getKey(i, key, type);
+ int count;
+ switch (type) {
+ case mdt_string:
+ {
+ const char* val=0;
+ data->getValue(key, val);
+ writeMeta(key, val);
+ }
+ break;
+ case mdt_int8:
+ {
+ const int8_t* val=0;
+ data->getValue(key, val, count);
+ writeMeta(key, val, count);
+ }
+ break;
+ case mdt_int16:
+ {
+ const int16_t* val=0;
+ data->getValue(key, val, count);
+ writeMeta(key, val, count);
+ }
+ break;
+ case mdt_int32:
+ {
+ const int32_t* val=0;
+ data->getValue(key, val, count);
+ writeMeta(key, val, count);
+ }
+ break;
+ case mdt_float:
+ {
+ const float* val=0;
+ data->getValue(key, val, count);
+ writeMeta(key, val, count);
+ }
+ break;
+ case mdt_double:
+ {
+ const double* val=0;
+ data->getValue(key, val, count);
+ writeMeta(key, val, count);
+ }
+ break;
+ }
+ }
+}
+
+
+void PtexWriterBase::addMetaData(const char* key, MetaDataType t,
+ const void* value, int size)
+{
+ if (strlen(key) > 255) {
+ std::stringstream str;
+ str << "PtexWriter error: meta data key too long (max=255) \"" << key << "\"";
+ setError(str.str());
+ return;
+ }
+ if (size <= 0) {
+ std::stringstream str;
+ str << "PtexWriter error: meta data size <= 0 for \"" << key << "\"";
+ setError(str.str());
+ }
+ std::map<std::string,int>::iterator iter = _metamap.find(key);
+ int index;
+ if (iter != _metamap.end()) {
+ // see if we already have this entry - if so, overwrite it
+ index = iter->second;
+ }
+ else {
+ // allocate a new entry
+ index = _metadata.size();
+ _metadata.resize(index+1);
+ _metamap[key] = index;
+ }
+ MetaEntry& m = _metadata[index];
+ m.key = key;
+ m.datatype = t;
+ m.data.resize(size);
+ memcpy(&m.data[0], value, size);
+}
+
+
+int PtexWriterBase::writeBlank(FILE* fp, int size)
+{
+ if (!_ok) return 0;
+ static char zeros[BlockSize] = {0};
+ int remain = size;
+ while (remain > 0) {
+ remain -= writeBlock(fp, zeros, remain < BlockSize ? remain : BlockSize);
+ }
+ return size;
+}
+
+
+int PtexWriterBase::writeBlock(FILE* fp, const void* data, int size)
+{
+ if (!_ok) return 0;
+ if (!fwrite(data, size, 1, fp)) {
+ setError("PtexWriter error: file write failed");
+ return 0;
+ }
+ return size;
+}
+
+
+int PtexWriterBase::writeZipBlock(FILE* fp, const void* data, int size, bool finish)
+{
+ if (!_ok) return 0;
+ void* buff = alloca(BlockSize);
+ _zstream.next_in = (Bytef*)data;
+ _zstream.avail_in = size;
+
+ while (1) {
+ _zstream.next_out = (Bytef*)buff;
+ _zstream.avail_out = BlockSize;
+ int zresult = deflate(&_zstream, finish ? Z_FINISH : Z_NO_FLUSH);
+ int size = BlockSize - _zstream.avail_out;
+ if (size > 0) writeBlock(fp, buff, size);
+ if (zresult == Z_STREAM_END) break;
+ if (zresult != Z_OK) {
+ setError("PtexWriter error: data compression internal error");
+ break;
+ }
+ if (!finish && _zstream.avail_out != 0)
+ // waiting for more input
+ break;
+ }
+
+ if (!finish) return 0;
+
+ int total = _zstream.total_out;
+ deflateReset(&_zstream);
+ return total;
+}
+
+
+int PtexWriterBase::readBlock(FILE* fp, void* data, int size)
+{
+ if (!fread(data, size, 1, fp)) {
+ setError("PtexWriter error: temp file read failed");
+ return 0;
+ }
+ return size;
+}
+
+
+int PtexWriterBase::copyBlock(FILE* dst, FILE* src, FilePos pos, int size)
+{
+ if (size <= 0) return 0;
+ fseeko(src, pos, SEEK_SET);
+ int remain = size;
+ void* buff = alloca(BlockSize);
+ while (remain) {
+ int nbytes = remain < BlockSize ? remain : BlockSize;
+ if (!fread(buff, nbytes, 1, src)) {
+ setError("PtexWriter error: temp file read failed");
+ return 0;
+ }
+ if (!writeBlock(dst, buff, nbytes)) break;
+ remain -= nbytes;
+ }
+ return size;
+}
+
+
+Ptex::Res PtexWriterBase::calcTileRes(Res faceres)
+{
+ // desired number of tiles = floor(log2(facesize / tilesize))
+ int facesize = faceres.size() * _pixelSize;
+ int ntileslog2 = PtexUtils::floor_log2(facesize/TileSize);
+ if (ntileslog2 == 0) return faceres;
+
+ // number of tiles is defined as:
+ // ntileslog2 = ureslog2 + vreslog2 - (tile_ureslog2 + tile_vreslog2)
+ // rearranging to solve for the tile res:
+ // tile_ureslog2 + tile_vreslog2 = ureslog2 + vreslog2 - ntileslog2
+ int n = faceres.ulog2 + faceres.vlog2 - ntileslog2;
+
+ // choose u and v sizes for roughly square result (u ~= v ~= n/2)
+ // and make sure tile isn't larger than face
+ Res tileres;
+ tileres.ulog2 = PtexUtils::min((n+1)/2, int(faceres.ulog2));
+ tileres.vlog2 = PtexUtils::min(n - tileres.ulog2, int(faceres.vlog2));
+ return tileres;
+}
+
+
+void PtexWriterBase::writeConstFaceBlock(FILE* fp, const void* data,
+ FaceDataHeader& fdh)
+{
+ // write a single const face data block
+ // record level data for face and output the one pixel value
+ fdh.set(_pixelSize, enc_constant);
+ writeBlock(fp, data, _pixelSize);
+}
+
+
+void PtexWriterBase::writeFaceBlock(FILE* fp, const void* data, int stride,
+ Res res, FaceDataHeader& fdh)
+{
+ // write a single face data block
+ // copy to temp buffer, and deinterleave
+ int ures = res.u(), vres = res.v();
+ int blockSize = ures*vres*_pixelSize;
+ bool useMalloc = blockSize > AllocaMax;
+ char* buff = useMalloc ? (char*) malloc(blockSize) : (char*)alloca(blockSize);
+ PtexUtils::deinterleave(data, stride, ures, vres, buff,
+ ures*DataSize(_header.datatype),
+ _header.datatype, _header.nchannels);
+
+ // difference if needed
+ bool diff = (_header.datatype == dt_uint8 ||
+ _header.datatype == dt_uint16);
+ if (diff) PtexUtils::encodeDifference(buff, blockSize, _header.datatype);
+
+ // compress and stream data to file, and record size in header
+ int zippedsize = writeZipBlock(fp, buff, blockSize);
+
+ // record compressed size and encoding in data header
+ fdh.set(zippedsize, diff ? enc_diffzipped : enc_zipped);
+ if (useMalloc) free(buff);
+}
+
+
+void PtexWriterBase::writeFaceData(FILE* fp, const void* data, int stride,
+ Res res, FaceDataHeader& fdh)
+{
+ // determine whether to break into tiles
+ Res tileres = calcTileRes(res);
+ int ntilesu = res.ntilesu(tileres);
+ int ntilesv = res.ntilesv(tileres);
+ int ntiles = ntilesu * ntilesv;
+ if (ntiles == 1) {
+ // write single block
+ writeFaceBlock(fp, data, stride, res, fdh);
+ } else {
+ // write tiles to tilefp temp file
+ rewind(_tilefp);
+
+ // alloc tile header
+ std::vector<FaceDataHeader> tileHeader(ntiles);
+ int tileures = tileres.u();
+ int tilevres = tileres.v();
+ int tileustride = tileures*_pixelSize;
+ int tilevstride = tilevres*stride;
+
+ // output tiles
+ FaceDataHeader* tdh = &tileHeader[0];
+ int datasize = 0;
+ const char* rowp = (const char*) data;
+ const char* rowpend = rowp + ntilesv * tilevstride;
+ for (; rowp != rowpend; rowp += tilevstride) {
+ const char* p = rowp;
+ const char* pend = p + ntilesu * tileustride;
+ for (; p != pend; tdh++, p += tileustride) {
+ // determine if tile is constant
+ if (PtexUtils::isConstant(p, stride, tileures, tilevres, _pixelSize))
+ writeConstFaceBlock(_tilefp, p, *tdh);
+ else
+ writeFaceBlock(_tilefp, p, stride, tileres, *tdh);
+ datasize += tdh->blocksize();
+ }
+ }
+
+ // output compressed tile header
+ uint32_t tileheadersize = writeZipBlock(_tilefp, &tileHeader[0],
+ int(sizeof(FaceDataHeader)*tileHeader.size()));
+
+
+ // output tile data pre-header
+ int totalsize = 0;
+ totalsize += writeBlock(fp, &tileres, sizeof(Res));
+ totalsize += writeBlock(fp, &tileheadersize, sizeof(tileheadersize));
+
+ // copy compressed tile header from temp file
+ totalsize += copyBlock(fp, _tilefp, datasize, tileheadersize);
+
+ // copy tile data from temp file
+ totalsize += copyBlock(fp, _tilefp, 0, datasize);
+
+ fdh.set(totalsize, enc_tiled);
+ }
+}
+
+
+void PtexWriterBase::writeReduction(FILE* fp, const void* data, int stride, Res res)
+{
+ // reduce and write to file
+ Ptex::Res newres(res.ulog2-1, res.vlog2-1);
+ int buffsize = newres.size() * _pixelSize;
+ bool useMalloc = buffsize > AllocaMax;
+ char* buff = useMalloc ? (char*) malloc(buffsize) : (char*)alloca(buffsize);
+
+ int dstride = newres.u() * _pixelSize;
+ _reduceFn(data, stride, res.u(), res.v(), buff, dstride, _header.datatype, _header.nchannels);
+ writeBlock(fp, buff, buffsize);
+
+ if (useMalloc) free(buff);
+}
+
+
+
+int PtexWriterBase::writeMetaDataBlock(FILE* fp, MetaEntry& val)
+{
+ uint8_t keysize = uint8_t(val.key.size()+1);
+ uint8_t datatype = val.datatype;
+ uint32_t datasize = uint32_t(val.data.size());
+ writeZipBlock(fp, &keysize, sizeof(keysize), false);
+ writeZipBlock(fp, val.key.c_str(), keysize, false);
+ writeZipBlock(fp, &datatype, sizeof(datatype), false);
+ writeZipBlock(fp, &datasize, sizeof(datasize), false);
+ writeZipBlock(fp, &val.data[0], datasize, false);
+ int memsize = (sizeof(keysize) + keysize + sizeof(datatype)
+ + sizeof(datasize) + datasize);
+ return memsize;
+}
+
+
+PtexMainWriter::PtexMainWriter(const char* path, PtexTexture* tex,
+ Ptex::MeshType mt, Ptex::DataType dt,
+ int nchannels, int alphachan, int nfaces, bool genmipmaps)
+ : PtexWriterBase(path, mt, dt, nchannels, alphachan, nfaces,
+ /* compress */ true),
+ _hasNewData(false),
+ _genmipmaps(genmipmaps),
+ _reader(0)
+{
+ _tmpfp = OpenTempFile(_tmppath);
+ if (!_tmpfp) {
+ setError(fileError("Error creating temp file: ", _tmppath.c_str()));
+ return;
+ }
+
+ // data will be written to a ".new" path and then renamed to final location
+ _newpath = path; _newpath += ".new";
+
+ _levels.reserve(20);
+ _levels.resize(1);
+
+ // init faceinfo and set flags to -1 to mark as uninitialized
+ _faceinfo.resize(nfaces);
+ for (int i = 0; i < nfaces; i++) _faceinfo[i].flags = uint8_t(-1);
+
+ _levels.front().pos.resize(nfaces);
+ _levels.front().fdh.resize(nfaces);
+ _rpos.resize(nfaces);
+ _constdata.resize(nfaces*_pixelSize);
+
+ if (tex) {
+ // access reader implementation
+ _reader = dynamic_cast<PtexReader*>(tex);
+ if (!_reader) {
+ setError("Internal error: dynamic_cast<PtexReader*> failed");
+ tex->release();
+ return;
+ }
+
+ // copy border modes
+ setBorderModes(tex->uBorderMode(), tex->vBorderMode());
+
+ // copy meta data from existing file
+ PtexPtr<PtexMetaData> meta ( _reader->getMetaData() );
+ writeMeta(meta);
+
+ // see if we have any edits
+ _hasNewData = _reader->hasEdits();
+ }
+}
+
+
+PtexMainWriter::~PtexMainWriter()
+{
+ if (_reader) _reader->release();
+}
+
+
+bool PtexMainWriter::close(Ptex::String& error)
+{
+ // closing base writer will write all pending data via finish() method
+ // and will close _fp (which in this case is on the temp disk)
+ bool result = PtexWriterBase::close(error);
+ if (_reader) {
+ _reader->release();
+ _reader = 0;
+ }
+ if (_tmpfp) {
+ fclose(_tmpfp);
+ unlink(_tmppath.c_str());
+ _tmpfp = 0;
+ }
+ if (result && _hasNewData) {
+ // rename temppath into final location
+ unlink(_path.c_str());
+ if (rename(_newpath.c_str(), _path.c_str()) == -1) {
+ error = fileError("Can't write to ptex file: ", _path.c_str()).c_str();
+ unlink(_newpath.c_str());
+ result = false;
+ }
+ }
+ return result;
+}
+
+bool PtexMainWriter::writeFace(int faceid, const FaceInfo& f, const void* data, int stride)
+{
+ if (!_ok) return 0;
+
+ // auto-compute stride
+ if (stride == 0) stride = f.res.u()*_pixelSize;
+
+ // handle constant case
+ if (PtexUtils::isConstant(data, stride, f.res.u(), f.res.v(), _pixelSize))
+ return writeConstantFace(faceid, f, data);
+
+ // non-constant case, ...
+
+ // check and store face info
+ if (!storeFaceInfo(faceid, _faceinfo[faceid], f)) return 0;
+
+ // record position of current face
+ _levels.front().pos[faceid] = ftello(_tmpfp);
+
+ // write face data
+ writeFaceData(_tmpfp, data, stride, f.res, _levels.front().fdh[faceid]);
+ if (!_ok) return 0;
+
+ // premultiply (if needed) before making reductions; use temp copy of data
+ uint8_t* temp = 0;
+ if (_header.hasAlpha()) {
+ // first copy to temp buffer
+ int rowlen = f.res.u() * _pixelSize, nrows = f.res.v();
+ temp = (uint8_t*) malloc(rowlen * nrows);
+ PtexUtils::copy(data, stride, temp, rowlen, nrows, rowlen);
+
+ // multiply alpha
+ PtexUtils::multalpha(temp, f.res.size(), _header.datatype, _header.nchannels,
+ _header.alphachan);
+
+ // override source buffer
+ data = temp;
+ stride = rowlen;
+ }
+
+ // generate first reduction (if needed)
+ if (_genmipmaps &&
+ (f.res.ulog2 > MinReductionLog2 && f.res.vlog2 > MinReductionLog2))
+ {
+ _rpos[faceid] = ftello(_tmpfp);
+ writeReduction(_tmpfp, data, stride, f.res);
+ }
+ else {
+ storeConstValue(faceid, data, stride, f.res);
+ }
+
+ if (temp) free(temp);
+ _hasNewData = true;
+ return 1;
+}
+
+
+bool PtexMainWriter::writeConstantFace(int faceid, const FaceInfo& f, const void* data)
+{
+ if (!_ok) return 0;
+
+ // check and store face info
+ if (!storeFaceInfo(faceid, _faceinfo[faceid], f, FaceInfo::flag_constant)) return 0;
+
+ // store face value in constant block
+ memcpy(&_constdata[faceid*_pixelSize], data, _pixelSize);
+ _hasNewData = true;
+ return 1;
+}
+
+
+
+void PtexMainWriter::storeConstValue(int faceid, const void* data, int stride, Res res)
+{
+ // compute average value and store in _constdata block
+ uint8_t* constdata = &_constdata[faceid*_pixelSize];
+ PtexUtils::average(data, stride, res.u(), res.v(), constdata,
+ _header.datatype, _header.nchannels);
+ if (_header.hasAlpha())
+ PtexUtils::divalpha(constdata, 1, _header.datatype, _header.nchannels, _header.alphachan);
+}
+
+
+
+void PtexMainWriter::finish()
+{
+ // do nothing if there's no new data to write
+ if (!_hasNewData) return;
+
+ // copy missing faces from _reader
+ if (_reader) {
+ for (int i = 0, nfaces = _header.nfaces; i < nfaces; i++) {
+ if (_faceinfo[i].flags == uint8_t(-1)) {
+ // copy face data
+ const Ptex::FaceInfo& info = _reader->getFaceInfo(i);
+ int size = _pixelSize * info.res.size();
+ if (info.isConstant()) {
+ PtexPtr<PtexFaceData> data ( _reader->getData(i) );
+ if (data) {
+ writeConstantFace(i, info, data->getData());
+ }
+ } else {
+ void* data = malloc(size);
+ _reader->getData(i, data, 0);
+ writeFace(i, info, data, 0);
+ free(data);
+ }
+ }
+ }
+ }
+ else {
+ // just flag missing faces as constant (black)
+ for (int i = 0, nfaces = _header.nfaces; i < nfaces; i++) {
+ if (_faceinfo[i].flags == uint8_t(-1))
+ _faceinfo[i].flags = FaceInfo::flag_constant;
+ }
+ }
+
+ // write reductions to tmp file
+ if (_genmipmaps)
+ generateReductions();
+
+ // flag faces w/ constant neighborhoods
+ flagConstantNeighorhoods();
+
+ // update header
+ _header.nlevels = uint16_t(_levels.size());
+ _header.nfaces = uint32_t(_faceinfo.size());
+
+ // create new file
+ FILE* newfp = fopen(_newpath.c_str(), "wb+");
+ if (!newfp) {
+ setError(fileError("Can't write to ptex file: ", _newpath.c_str()));
+ return;
+ }
+
+ // write blank header (to fill in later)
+ writeBlank(newfp, HeaderSize);
+ writeBlank(newfp, ExtHeaderSize);
+
+ // write compressed face info block
+ _header.faceinfosize = writeZipBlock(newfp, &_faceinfo[0],
+ sizeof(FaceInfo)*_header.nfaces);
+
+ // write compressed const data block
+ _header.constdatasize = writeZipBlock(newfp, &_constdata[0], int(_constdata.size()));
+
+ // write blank level info block (to fill in later)
+ FilePos levelInfoPos = ftello(newfp);
+ writeBlank(newfp, LevelInfoSize * _header.nlevels);
+
+ // write level data blocks (and record level info)
+ std::vector<LevelInfo> levelinfo(_header.nlevels);
+ for (int li = 0; li < _header.nlevels; li++)
+ {
+ LevelInfo& info = levelinfo[li];
+ LevelRec& level = _levels[li];
+ int nfaces = int(level.fdh.size());
+ info.nfaces = nfaces;
+ // output compressed level data header
+ info.levelheadersize = writeZipBlock(newfp, &level.fdh[0],
+ sizeof(FaceDataHeader)*nfaces);
+ info.leveldatasize = info.levelheadersize;
+ // copy level data from tmp file
+ for (int fi = 0; fi < nfaces; fi++)
+ info.leveldatasize += copyBlock(newfp, _tmpfp, level.pos[fi],
+ level.fdh[fi].blocksize());
+ _header.leveldatasize += info.leveldatasize;
+ }
+ rewind(_tmpfp);
+
+ // write meta data (if any)
+ if (!_metadata.empty())
+ writeMetaData(newfp);
+
+ // update extheader for edit data position
+ _extheader.editdatapos = ftello(newfp);
+
+ // rewrite level info block
+ fseeko(newfp, levelInfoPos, SEEK_SET);
+ _header.levelinfosize = writeBlock(newfp, &levelinfo[0], LevelInfoSize*_header.nlevels);
+
+ // rewrite header
+ fseeko(newfp, 0, SEEK_SET);
+ writeBlock(newfp, &_header, HeaderSize);
+ writeBlock(newfp, &_extheader, ExtHeaderSize);
+ fclose(newfp);
+}
+
+
+void PtexMainWriter::flagConstantNeighorhoods()
+{
+ // for each constant face
+ for (int faceid = 0, n = int(_faceinfo.size()); faceid < n; faceid++) {
+ FaceInfo& f = _faceinfo[faceid];
+ if (!f.isConstant()) continue;
+ uint8_t* constdata = &_constdata[faceid*_pixelSize];
+
+ // check to see if neighborhood is constant
+ bool isConst = true;
+ bool isTriangle = _header.meshtype == mt_triangle;
+ int nedges = isTriangle ? 3 : 4;
+ for (int eid = 0; eid < nedges; eid++) {
+ bool prevWasSubface = f.isSubface();
+ int prevFid = faceid;
+ // traverse across edge
+ int afid = f.adjface(eid);
+ int aeid = f.adjedge(eid);
+ int count = 0;
+ const int maxcount = 10; // max valence (as safety valve)
+ while (afid != faceid) {
+ // if we hit a boundary, assume non-const (not worth
+ // the trouble to redo traversal from CCW direction;
+ // also, boundary might want to be "black")
+ // assume const if we hit max valence too
+ if (afid < 0 || ++count == maxcount)
+ { isConst = false; break; }
+
+ // check if neighor is constant, and has the same value as face
+ FaceInfo& af = _faceinfo[afid];
+ if (!af.isConstant() ||
+ 0 != memcmp(constdata, &_constdata[afid*_pixelSize], _pixelSize))
+ { isConst = false; break; }
+
+ // traverse around vertex in CW direction
+ // handle T junction between subfaces and main face
+ bool isSubface = af.isSubface();
+ bool isT = !isTriangle && prevWasSubface && !isSubface && af.adjface(aeid) == prevFid;
+ std::swap(prevFid, afid);
+ prevWasSubface = isSubface;
+
+ if (isT) {
+ // traverse to secondary subface across T junction
+ FaceInfo& pf = _faceinfo[afid];
+ int peid = af.adjedge(aeid);
+ peid = (peid + 3) % 4;
+ afid = pf.adjface(peid);
+ aeid = pf.adjedge(peid);
+ aeid = (aeid + 3) % 4;
+ }
+ else {
+ // traverse around vertex
+ aeid = (aeid + 1) % nedges;
+ afid = af.adjface(aeid);
+ aeid = af.adjedge(aeid);
+ }
+ }
+ if (!isConst) break;
+ }
+ if (isConst) f.flags |= FaceInfo::flag_nbconstant;
+ }
+}
+
+
+void PtexMainWriter::generateReductions()
+{
+ // first generate "rfaceids", reduction faceids,
+ // which are faceids reordered by decreasing smaller dimension
+ int nfaces = _header.nfaces;
+ _rfaceids.resize(nfaces);
+ _faceids_r.resize(nfaces);
+ PtexUtils::genRfaceids(&_faceinfo[0], nfaces, &_rfaceids[0], &_faceids_r[0]);
+
+ // determine how many faces in each level, and resize _levels
+ // traverse in reverse rfaceid order to find number of faces
+ // larger than cutoff size of each level
+ for (int rfaceid = nfaces-1, cutoffres = MinReductionLog2; rfaceid >= 0; rfaceid--) {
+ int faceid = _faceids_r[rfaceid];
+ FaceInfo& face = _faceinfo[faceid];
+ Res res = face.res;
+ int min = face.isConstant() ? 1 : PtexUtils::min(res.ulog2, res.vlog2);
+ while (min > cutoffres) {
+ // i == last face for current level
+ int size = rfaceid+1;
+ _levels.push_back(LevelRec());
+ LevelRec& level = _levels.back();
+ level.pos.resize(size);
+ level.fdh.resize(size);
+ cutoffres++;
+ }
+ }
+
+ // generate and cache reductions (including const data)
+ // first, find largest face and allocate tmp buffer
+ int buffsize = 0;
+ for (int i = 0; i < nfaces; i++)
+ buffsize = PtexUtils::max(buffsize, _faceinfo[i].res.size());
+ buffsize *= _pixelSize;
+ char* buff = (char*) malloc(buffsize);
+
+ int nlevels = int(_levels.size());
+ for (int i = 1; i < nlevels; i++) {
+ LevelRec& level = _levels[i];
+ int nextsize = (i+1 < nlevels)? int(_levels[i+1].fdh.size()) : 0;
+ for (int rfaceid = 0, size = int(level.fdh.size()); rfaceid < size; rfaceid++) {
+ // output current reduction for face (previously generated)
+ int faceid = _faceids_r[rfaceid];
+ Res res = _faceinfo[faceid].res;
+ res.ulog2 -= i; res.vlog2 -= i;
+ int stride = res.u() * _pixelSize;
+ int blocksize = res.size() * _pixelSize;
+ fseeko(_tmpfp, _rpos[faceid], SEEK_SET);
+ readBlock(_tmpfp, buff, blocksize);
+ fseeko(_tmpfp, 0, SEEK_END);
+ level.pos[rfaceid] = ftello(_tmpfp);
+ writeFaceData(_tmpfp, buff, stride, res, level.fdh[rfaceid]);
+ if (!_ok) return;
+
+ // write a new reduction if needed for next level
+ if (rfaceid < nextsize) {
+ fseeko(_tmpfp, _rpos[faceid], SEEK_SET);
+ writeReduction(_tmpfp, buff, stride, res);
+ }
+ else {
+ // the last reduction for each face is its constant value
+ storeConstValue(faceid, buff, stride, res);
+ }
+ }
+ }
+ fseeko(_tmpfp, 0, SEEK_END);
+ free(buff);
+}
+
+
+void PtexMainWriter::writeMetaData(FILE* fp)
+{
+ std::vector<MetaEntry*> lmdEntries; // large meta data items
+
+ // write small meta data items in a single zip block
+ for (int i = 0, n = _metadata.size(); i < n; i++) {
+ MetaEntry& e = _metadata[i];
+#ifndef PTEX_NO_LARGE_METADATA_BLOCKS
+ if (int(e.data.size()) > MetaDataThreshold) {
+ // skip large items, but record for later
+ lmdEntries.push_back(&e);
+ }
+ else
+#endif
+ {
+ // add small item to zip block
+ _header.metadatamemsize += writeMetaDataBlock(fp, e);
+ }
+ }
+ if (_header.metadatamemsize) {
+ // finish zip block
+ _header.metadatazipsize = writeZipBlock(fp, 0, 0, /*finish*/ true);
+ }
+
+ // write compatibility barrier
+ writeBlank(fp, sizeof(uint64_t));
+
+ // write large items as separate blocks
+ int nLmd = lmdEntries.size();
+ if (nLmd > 0) {
+ // write data records to tmp file and accumulate zip sizes for lmd header
+ std::vector<FilePos> lmdoffset(nLmd);
+ std::vector<uint32_t> lmdzipsize(nLmd);
+ for (int i = 0; i < nLmd; i++) {
+ MetaEntry* e= lmdEntries[i];
+ lmdoffset[i] = ftello(_tmpfp);
+ lmdzipsize[i] = writeZipBlock(_tmpfp, &e->data[0], e->data.size());
+ }
+
+ // write lmd header records as single zip block
+ for (int i = 0; i < nLmd; i++) {
+ MetaEntry* e = lmdEntries[i];
+ uint8_t keysize = uint8_t(e->key.size()+1);
+ uint8_t datatype = e->datatype;
+ uint32_t datasize = e->data.size();
+ uint32_t zipsize = lmdzipsize[i];
+
+ writeZipBlock(fp, &keysize, sizeof(keysize), false);
+ writeZipBlock(fp, e->key.c_str(), keysize, false);
+ writeZipBlock(fp, &datatype, sizeof(datatype), false);
+ writeZipBlock(fp, &datasize, sizeof(datasize), false);
+ writeZipBlock(fp, &zipsize, sizeof(zipsize), false);
+ _extheader.lmdheadermemsize +=
+ sizeof(keysize) + keysize + sizeof(datatype) + sizeof(datasize) + sizeof(zipsize);
+ }
+ _extheader.lmdheaderzipsize = writeZipBlock(fp, 0, 0, /*finish*/ true);
+
+ // copy data records
+ for (int i = 0; i < nLmd; i++) {
+ _extheader.lmddatasize +=
+ copyBlock(fp, _tmpfp, lmdoffset[i], lmdzipsize[i]);
+ }
+ }
+}
+
+
+PtexIncrWriter::PtexIncrWriter(const char* path, FILE* fp,
+ Ptex::MeshType mt, Ptex::DataType dt,
+ int nchannels, int alphachan, int nfaces)
+ : PtexWriterBase(path, mt, dt, nchannels, alphachan, nfaces,
+ /* compress */ false),
+ _fp(fp)
+{
+ // note: incremental saves are not compressed (see compress flag above)
+ // to improve save time in the case where in incremental save is followed by
+ // a full save (which ultimately it always should be). With a compressed
+ // incremental save, the data would be compressed twice and decompressed once
+ // on every save vs. just compressing once.
+
+ // make sure existing header matches
+ if (!fread(&_header, PtexIO::HeaderSize, 1, fp) || _header.magic != Magic) {
+ std::stringstream str;
+ str << "Not a ptex file: " << path;
+ setError(str.str());
+ return;
+ }
+
+ bool headerMatch = (mt == _header.meshtype &&
+ dt == _header.datatype &&
+ nchannels == _header.nchannels &&
+ alphachan == int(_header.alphachan) &&
+ nfaces == int(_header.nfaces));
+ if (!headerMatch) {
+ std::stringstream str;
+ str << "PtexWriter::edit error: header doesn't match existing file, "
+ << "conversions not currently supported";
+ setError(str.str());
+ return;
+ }
+
+ // read extended header
+ memset(&_extheader, 0, sizeof(_extheader));
+ if (!fread(&_extheader, PtexUtils::min(uint32_t(ExtHeaderSize), _header.extheadersize), 1, fp)) {
+ std::stringstream str;
+ str << "Error reading extended header: " << path;
+ setError(str.str());
+ return;
+ }
+
+ // seek to end of file to append
+ fseeko(_fp, 0, SEEK_END);
+}
+
+
+PtexIncrWriter::~PtexIncrWriter()
+{
+}
+
+
+bool PtexIncrWriter::writeFace(int faceid, const FaceInfo& f, const void* data, int stride)
+{
+ if (stride == 0) stride = f.res.u()*_pixelSize;
+
+ // handle constant case
+ if (PtexUtils::isConstant(data, stride, f.res.u(), f.res.v(), _pixelSize))
+ return writeConstantFace(faceid, f, data);
+
+ // init headers
+ uint8_t edittype = et_editfacedata;
+ uint32_t editsize;
+ EditFaceDataHeader efdh;
+ efdh.faceid = faceid;
+
+ // check and store face info
+ if (!storeFaceInfo(faceid, efdh.faceinfo, f))
+ return 0;
+
+ // record position and skip headers
+ FilePos pos = ftello(_fp);
+ writeBlank(_fp, sizeof(edittype) + sizeof(editsize) + sizeof(efdh));
+
+ // must compute constant (average) val first
+ uint8_t* constval = (uint8_t*) malloc(_pixelSize);
+
+ if (_header.hasAlpha()) {
+ // must premult alpha before averaging
+ // first copy to temp buffer
+ int rowlen = f.res.u() * _pixelSize, nrows = f.res.v();
+ uint8_t* temp = (uint8_t*) malloc(rowlen * nrows);
+ PtexUtils::copy(data, stride, temp, rowlen, nrows, rowlen);
+
+ // multiply alpha
+ PtexUtils::multalpha(temp, f.res.size(), _header.datatype, _header.nchannels,
+ _header.alphachan);
+ // average
+ PtexUtils::average(temp, rowlen, f.res.u(), f.res.v(), constval,
+ _header.datatype, _header.nchannels);
+ // unmult alpha
+ PtexUtils::divalpha(constval, 1, _header.datatype, _header.nchannels,
+ _header.alphachan);
+ free(temp);
+ }
+ else {
+ // average
+ PtexUtils::average(data, stride, f.res.u(), f.res.v(), constval,
+ _header.datatype, _header.nchannels);
+ }
+ // write const val
+ writeBlock(_fp, constval, _pixelSize);
+ free(constval);
+
+ // write face data
+ writeFaceData(_fp, data, stride, f.res, efdh.fdh);
+
+ // update editsize in header
+ editsize = sizeof(efdh) + _pixelSize + efdh.fdh.blocksize();
+
+ // rewind and write headers
+ fseeko(_fp, pos, SEEK_SET);
+ writeBlock(_fp, &edittype, sizeof(edittype));
+ writeBlock(_fp, &editsize, sizeof(editsize));
+ writeBlock(_fp, &efdh, sizeof(efdh));
+ fseeko(_fp, 0, SEEK_END);
+ return 1;
+}
+
+
+bool PtexIncrWriter::writeConstantFace(int faceid, const FaceInfo& f, const void* data)
+{
+ // init headers
+ uint8_t edittype = et_editfacedata;
+ uint32_t editsize;
+ EditFaceDataHeader efdh;
+ efdh.faceid = faceid;
+ efdh.fdh.set(0, enc_constant);
+ editsize = sizeof(efdh) + _pixelSize;
+
+ // check and store face info
+ if (!storeFaceInfo(faceid, efdh.faceinfo, f, FaceInfo::flag_constant))
+ return 0;
+
+ // write headers
+ writeBlock(_fp, &edittype, sizeof(edittype));
+ writeBlock(_fp, &editsize, sizeof(editsize));
+ writeBlock(_fp, &efdh, sizeof(efdh));
+ // write data
+ writeBlock(_fp, data, _pixelSize);
+ return 1;
+}
+
+
+void PtexIncrWriter::writeMetaDataEdit()
+{
+ // init headers
+ uint8_t edittype = et_editmetadata;
+ uint32_t editsize;
+ EditMetaDataHeader emdh;
+ emdh.metadatazipsize = 0;
+ emdh.metadatamemsize = 0;
+
+ // record position and skip headers
+ FilePos pos = ftello(_fp);
+ writeBlank(_fp, sizeof(edittype) + sizeof(editsize) + sizeof(emdh));
+
+ // write meta data
+ for (int i = 0, n = _metadata.size(); i < n; i++) {
+ MetaEntry& e = _metadata[i];
+ emdh.metadatamemsize += writeMetaDataBlock(_fp, e);
+ }
+ // finish zip block
+ emdh.metadatazipsize = writeZipBlock(_fp, 0, 0, /*finish*/ true);
+
+ // update headers
+ editsize = sizeof(emdh) + emdh.metadatazipsize;
+
+ // rewind and write headers
+ fseeko(_fp, pos, SEEK_SET);
+ writeBlock(_fp, &edittype, sizeof(edittype));
+ writeBlock(_fp, &editsize, sizeof(editsize));
+ writeBlock(_fp, &emdh, sizeof(emdh));
+ fseeko(_fp, 0, SEEK_END);
+}
+
+
+bool PtexIncrWriter::close(Ptex::String& error)
+{
+ // closing base writer will write all pending data via finish() method
+ bool result = PtexWriterBase::close(error);
+ if (_fp) {
+ fclose(_fp);
+ _fp = 0;
+ }
+ return result;
+}
+
+
+void PtexIncrWriter::finish()
+{
+ // write meta data edit block (if any)
+ if (!_metadata.empty()) writeMetaDataEdit();
+
+ // rewrite extheader for updated editdatasize
+ if (_extheader.editdatapos) {
+ _extheader.editdatasize = uint64_t(ftello(_fp)) - _extheader.editdatapos;
+ fseeko(_fp, HeaderSize, SEEK_SET);
+ fwrite(&_extheader, PtexUtils::min(uint32_t(ExtHeaderSize), _header.extheadersize), 1, _fp);
+ }
+}
diff --git a/extern/ptex/src/ptex/PtexWriter.h b/extern/ptex/src/ptex/PtexWriter.h
new file mode 100644
index 00000000000..af2ee03e24a
--- /dev/null
+++ b/extern/ptex/src/ptex/PtexWriter.h
@@ -0,0 +1,193 @@
+#ifndef PtexWriter_h
+#define PtexWriter_h
+
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+#include "PtexPlatform.h"
+#include <zlib.h>
+#include <map>
+#include <vector>
+#include <stdio.h>
+#include "Ptexture.h"
+#include "PtexIO.h"
+#include "PtexReader.h"
+
+
+class PtexWriterBase : public PtexWriter, public PtexIO {
+public:
+ virtual void setBorderModes(Ptex::BorderMode uBorderMode, Ptex::BorderMode vBorderMode)
+ {
+ _extheader.ubordermode = uBorderMode;
+ _extheader.vbordermode = vBorderMode;
+ }
+ virtual void writeMeta(const char* key, const char* value);
+ virtual void writeMeta(const char* key, const int8_t* value, int count);
+ virtual void writeMeta(const char* key, const int16_t* value, int count);
+ virtual void writeMeta(const char* key, const int32_t* value, int count);
+ virtual void writeMeta(const char* key, const float* value, int count);
+ virtual void writeMeta(const char* key, const double* value, int count);
+ virtual void writeMeta(PtexMetaData* data);
+ virtual bool close(Ptex::String& error);
+ virtual void release();
+
+ bool ok(Ptex::String& error) {
+ if (!_ok) getError(error);
+ return _ok;
+ }
+ void getError(Ptex::String& error) {
+ error = (_error + "\nPtex file: " + _path).c_str();
+ }
+
+protected:
+ struct MetaEntry {
+ std::string key;
+ MetaDataType datatype;
+ std::vector<uint8_t> data;
+ MetaEntry() : datatype(MetaDataType(0)), data() {}
+ };
+
+ virtual void finish() = 0;
+ PtexWriterBase(const char* path,
+ Ptex::MeshType mt, Ptex::DataType dt,
+ int nchannels, int alphachan, int nfaces,
+ bool compress);
+ virtual ~PtexWriterBase();
+
+ int writeBlank(FILE* fp, int size);
+ int writeBlock(FILE* fp, const void* data, int size);
+ int writeZipBlock(FILE* fp, const void* data, int size, bool finish=true);
+ int readBlock(FILE* fp, void* data, int size);
+ int copyBlock(FILE* dst, FILE* src, FilePos pos, int size);
+ Res calcTileRes(Res faceres);
+ virtual void addMetaData(const char* key, MetaDataType t, const void* value, int size);
+ void writeConstFaceBlock(FILE* fp, const void* data, FaceDataHeader& fdh);
+ void writeFaceBlock(FILE* fp, const void* data, int stride, Res res,
+ FaceDataHeader& fdh);
+ void writeFaceData(FILE* fp, const void* data, int stride, Res res,
+ FaceDataHeader& fdh);
+ void writeReduction(FILE* fp, const void* data, int stride, Res res);
+ int writeMetaDataBlock(FILE* fp, MetaEntry& val);
+ void setError(const std::string& error) { _error = error; _ok = false; }
+ bool storeFaceInfo(int faceid, FaceInfo& dest, const FaceInfo& src, int flags=0);
+
+ bool _ok; // true if no error has occurred
+ std::string _error; // the error text (if any)
+ std::string _path; // file path
+ std::string _tilepath; // temp tile file path ("<path>.tiles.tmp")
+ FILE* _tilefp; // temp tile file handle
+ Header _header; // the file header
+ ExtHeader _extheader; // extended header
+ int _pixelSize; // size of a pixel in bytes
+ std::vector<MetaEntry> _metadata; // meta data waiting to be written
+ std::map<std::string,int> _metamap; // for preventing duplicate keys
+ z_stream_s _zstream; // libzip compression stream
+
+ PtexUtils::ReduceFn* _reduceFn;
+};
+
+
+class PtexMainWriter : public PtexWriterBase {
+public:
+ PtexMainWriter(const char* path, PtexTexture* tex,
+ Ptex::MeshType mt, Ptex::DataType dt,
+ int nchannels, int alphachan, int nfaces, bool genmipmaps);
+
+ virtual bool close(Ptex::String& error);
+ virtual bool writeFace(int faceid, const FaceInfo& f, const void* data, int stride);
+ virtual bool writeConstantFace(int faceid, const FaceInfo& f, const void* data);
+
+protected:
+ virtual ~PtexMainWriter();
+ virtual void addMetaData(const char* key, MetaDataType t, const void* value, int size)
+ {
+ PtexWriterBase::addMetaData(key, t, value, size);
+ _hasNewData = true;
+ }
+
+private:
+ virtual void finish();
+ void generateReductions();
+ void flagConstantNeighorhoods();
+ void storeConstValue(int faceid, const void* data, int stride, Res res);
+ void writeMetaData(FILE* fp);
+
+ std::string _newpath; // path to ".new" file
+ std::string _tmppath; // temp file path ("<path>.tmp")
+ FILE* _tmpfp; // temp file handle
+ bool _hasNewData; // true if data has been written
+ bool _genmipmaps; // true if mipmaps should be generated
+ std::vector<FaceInfo> _faceinfo; // info about each face
+ std::vector<uint8_t> _constdata; // constant data for each face
+ std::vector<uint32_t> _rfaceids; // faceid reordering for reduction levels
+ std::vector<uint32_t> _faceids_r; // faceid indexed by rfaceid
+
+ static const int MinReductionLog2 =2; // log2(minimum reduction size) - can tune
+ struct LevelRec {
+ // note: level 0 is ordered by faceid
+ // levels 1+ are reduction levels (half res in both u and v) and
+ // are ordered by rfaceid[faceid]. Also, faces with a minimum
+ // dimension (the smaller of u or v) smaller than MinReductionLog2
+ // are omitted from subsequent levels.
+ std::vector<FilePos> pos; // position of data blocks within _tmp file
+ std::vector<FaceDataHeader> fdh; // face data headers
+ };
+ std::vector<LevelRec> _levels; // info about each level
+ std::vector<FilePos> _rpos; // reduction file positions
+
+ PtexReader* _reader; // reader for accessing existing data in file
+};
+
+
+class PtexIncrWriter : public PtexWriterBase {
+ public:
+ PtexIncrWriter(const char* path, FILE* fp,
+ Ptex::MeshType mt, Ptex::DataType dt,
+ int nchannels, int alphachan, int nfaces);
+
+ virtual bool close(Ptex::String& error);
+ virtual bool writeFace(int faceid, const FaceInfo& f, const void* data, int stride);
+ virtual bool writeConstantFace(int faceid, const FaceInfo& f, const void* data);
+
+ protected:
+ void writeMetaDataEdit();
+ virtual void finish();
+ virtual ~PtexIncrWriter();
+
+ private:
+ FILE* _fp; // the file being edited
+};
+
+#endif
diff --git a/extern/ptex/src/ptex/Ptexture.h b/extern/ptex/src/ptex/Ptexture.h
new file mode 100644
index 00000000000..2b798681b8f
--- /dev/null
+++ b/extern/ptex/src/ptex/Ptexture.h
@@ -0,0 +1,984 @@
+#ifndef Ptexture_h
+#define Ptexture_h
+
+/*
+PTEX SOFTWARE
+Copyright 2009 Disney Enterprises, Inc. All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
+ Studios" or the names of its contributors may NOT be used to
+ endorse or promote products derived from this software without
+ specific prior written permission from Walt Disney Pictures.
+
+Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
+IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+/**
+ @file Ptexture.h
+ @brief Public API classes for reading, writing, caching, and filtering Ptex files.
+*/
+
+#if defined(_WIN32) || defined(_WINDOWS) || defined(_MSC_VER)
+# ifndef PTEX_STATIC
+# ifdef PTEX_EXPORTS
+# define PTEXAPI __declspec(dllexport)
+# else
+# define PTEXAPI __declspec(dllimport)
+# endif
+# else
+# define PTEXAPI
+# endif
+#else
+# define PTEXAPI
+# ifndef DOXYGEN
+# define PTEX_USE_STDSTRING
+# endif
+#endif
+
+#include "PtexInt.h"
+#include <ostream>
+
+#define PtexAPIVersion 2
+#define PtexFileMajorVersion 1
+#define PtexFileMinorVersion 3
+
+/** Common data structures and enums used throughout the API. */
+struct Ptex {
+ /** Type of base mesh for which the textures are defined. A mesh
+ can be triangle-based (with triangular textures) or quad-based
+ (with rectangular textures). */
+ enum MeshType {
+ mt_triangle, ///< Mesh is triangle-based.
+ mt_quad ///< Mesh is quad-based.
+ };
+
+ /** Type of data stored in texture file. */
+ enum DataType {
+ dt_uint8, ///< Unsigned, 8-bit integer.
+ dt_uint16, ///< Unsigned, 16-bit integer.
+ dt_half, ///< Half-precision (16-bit) floating point.
+ dt_float ///< Single-precision (32-bit) floating point.
+ };
+
+ /** How to handle mesh border when filtering. */
+ enum BorderMode {
+ m_clamp, ///< texel access is clamped to border
+ m_black, ///< texel beyond border are assumed to be black
+ m_periodic ///< texel access wraps to other side of face
+ };
+
+ /** Edge IDs used in adjacency data in the Ptex::FaceInfo struct.
+ Edge ID usage for triangle meshes is TBD. */
+ enum EdgeId {
+ e_bottom, ///< Bottom edge, from UV (0,0) to (1,0)
+ e_right, ///< Right edge, from UV (1,0) to (1,1)
+ e_top, ///< Top edge, from UV (1,1) to (0,1)
+ e_left ///< Left edge, from UV (0,1) to (0,0)
+ };
+
+ /** Type of meta data entry. */
+ enum MetaDataType {
+ mdt_string, ///< Null-terminated string.
+ mdt_int8, ///< Signed 8-bit integer.
+ mdt_int16, ///< Signed 16-bit integer.
+ mdt_int32, ///< Signed 32-bit integer.
+ mdt_float, ///< Single-precision (32-bit) floating point.
+ mdt_double ///< Double-precision (32-bit) floating point.
+ };
+
+ /** Look up name of given mesh type. */
+ PTEXAPI static const char* MeshTypeName(MeshType mt);
+
+ /** Look up name of given data type. */
+ PTEXAPI static const char* DataTypeName(DataType dt);
+
+ /** Look up name of given border mode. */
+ PTEXAPI static const char* BorderModeName(BorderMode m);
+
+ /** Look up name of given edge ID. */
+ PTEXAPI static const char* EdgeIdName(EdgeId eid);
+
+ /** Look up name of given meta data type. */
+ PTEXAPI static const char* MetaDataTypeName(MetaDataType mdt);
+
+ /** Look up size of given data type (in bytes). */
+ static int DataSize(DataType dt) {
+ static const int sizes[] = { 1,2,2,4 };
+ return sizes[dt];
+ }
+
+ /** Look up value of given data type that corresponds to the normalized value of 1.0. */
+ static double OneValue(DataType dt) {
+ static const double one[] = { 255.0, 65535.0, 1.0, 1.0 };
+ return one[dt];
+ }
+
+ /** Lookup up inverse value of given data type that corresponds to the normalized value of 1.0. */
+ static double OneValueInv(DataType dt) {
+ static const double one[] = { 1.0/255.0, 1.0/65535.0, 1.0, 1.0 };
+ return one[dt];
+ }
+
+ /** Convert a number of data values from the given data type to float. */
+ PTEXAPI static void ConvertToFloat(float* dst, const void* src,
+ Ptex::DataType dt, int numChannels);
+
+ /** Convert a number of data values from float to the given data type. */
+ PTEXAPI static void ConvertFromFloat(void* dst, const float* src,
+ Ptex::DataType dt, int numChannels);
+
+ /** Pixel resolution of a given texture.
+ The resolution is stored in log form: ulog2 = log2(ures), vlog2 = log2(vres)).
+ Note: negative ulog2 or vlog2 values are reserved for internal use.
+ */
+ struct Res {
+ int8_t ulog2; ///< log base 2 of u resolution, in texels
+ int8_t vlog2; ///< log base 2 of v resolution, in texels
+
+ /// Default constructor, sets res to 0 (1x1 texel).
+ Res() : ulog2(0), vlog2(0) {}
+
+ /// Constructor.
+ Res(int8_t ulog2, int8_t vlog2) : ulog2(ulog2), vlog2(vlog2) {}
+
+ /// Constructor from 16-bit integer
+ Res(uint16_t value) {
+ ulog2 = *(uint8_t *)&value;
+ vlog2 = *((uint8_t *)&value + 1);
+ }
+
+ /// U resolution in texels.
+ int u() const { return 1<<(unsigned)ulog2; }
+
+ /// V resolution in texels.
+ int v() const { return 1<<(unsigned)vlog2; }
+
+ /// Resolution as a single 16-bit integer value.
+ uint16_t& val() { return *(uint16_t*)this; }
+
+ /// Resolution as a single 16-bit integer value.
+ const uint16_t& val() const { return *(uint16_t*)this; }
+
+ /// Total size of specified texture in texels (u * v).
+ int size() const { return u() * v(); }
+
+ /// Comparison operator.
+ bool operator==(const Res& r) const { return val() == r.val(); }
+
+ /// Comparison operator.
+ bool operator!=(const Res& r) const { return val() != r.val(); }
+
+ /// True if res is >= given res in both u and v directions.
+ bool operator>=(const Res& r) const { return ulog2 >= r.ulog2 && vlog2 >= r.vlog2; }
+
+ /// Get value of resolution with u and v swapped.
+ Res swappeduv() const { return Res(vlog2, ulog2); }
+
+ /// Swap the u and v resolution values in place.
+ void swapuv() { *this = swappeduv(); }
+
+ /// Clamp the resolution value against the given value.
+ void clamp(const Res& r) {
+ if (ulog2 > r.ulog2) ulog2 = r.ulog2;
+ if (vlog2 > r.vlog2) vlog2 = r.vlog2;
+ }
+
+ /// Determine the number of tiles in the u direction for the given tile res.
+ int ntilesu(Res tileres) const { return 1<<(ulog2-tileres.ulog2); }
+
+ /// Determine the number of tiles in the v direction for the given tile res.
+ int ntilesv(Res tileres) const { return 1<<(vlog2-tileres.vlog2); }
+
+ /// Determine the total number of tiles for the given tile res.
+ int ntiles(Res tileres) const { return ntilesu(tileres) * ntilesv(tileres); }
+ };
+
+ /** Information about a face, as stored in the Ptex file header.
+ The FaceInfo data contains the face resolution and neighboring face
+ adjacency information as well as a set of flags describing the face.
+
+ The adjfaces data member contains the face ids of the four neighboring faces.
+ The neighbors are accessed in EdgeId order, CCW, starting with the bottom edge.
+ The adjedges data member contains the corresponding edge id for each neighboring face.
+
+ If a face has no neighbor for a given edge, the adjface id should be -1, and the
+ adjedge id doesn't matter (but is typically zero).
+
+ If an adjacent face is a pair of subfaces, the id of the first subface as encountered
+ in a CCW traversal should be stored as the adjface id.
+ */
+ struct FaceInfo {
+ Res res; ///< Resolution of face.
+ uint8_t adjedges; ///< Adjacent edges, 2 bits per edge.
+ uint8_t flags; ///< Flags.
+ int32_t adjfaces[4]; ///< Adjacent faces (-1 == no adjacent face).
+
+ /// Default constructor, sets all members to zero.
+ FaceInfo() : res(), adjedges(0), flags(0)
+ {
+ adjfaces[0] = adjfaces[1] = adjfaces[2] = adjfaces[3] = -1;
+ }
+
+ /// Constructor.
+ FaceInfo(Res res) : res(res), adjedges(0), flags(0)
+ {
+ adjfaces[0] = adjfaces[1] = adjfaces[2] = adjfaces[3] = -1;
+ }
+
+ /// Constructor.
+ FaceInfo(Res res, int adjfaces[4], int adjedges[4], bool isSubface=false)
+ : res(res), flags(isSubface ? flag_subface : 0)
+ {
+ setadjfaces(adjfaces[0], adjfaces[1], adjfaces[2], adjfaces[3]);
+ setadjedges(adjedges[0], adjedges[1], adjedges[2], adjedges[3]);
+ }
+
+ /// Access an adjacent edge id. The eid value must be 0..3.
+ EdgeId adjedge(int eid) const { return EdgeId((adjedges >> (2*eid)) & 3); }
+
+ /// Access an adjacent face id. The eid value must be 0..3.
+ int adjface(int eid) const { return adjfaces[eid]; }
+
+ /// Determine if face is constant (by checking a flag).
+ bool isConstant() const { return (flags & flag_constant) != 0; }
+
+ /// Determine if neighborhood of face is constant (by checking a flag).
+ bool isNeighborhoodConstant() const { return (flags & flag_nbconstant) != 0; }
+
+ /// Determine if face has edits in the file (by checking a flag).
+ bool hasEdits() const { return (flags & flag_hasedits) != 0; }
+
+ /// Determine if face is a subface (by checking a flag).
+ bool isSubface() const { return (flags & flag_subface) != 0; }
+
+ /// Set the adjfaces data.
+ void setadjfaces(int f0, int f1, int f2, int f3)
+ { adjfaces[0] = f0, adjfaces[1] = f1, adjfaces[2] = f2; adjfaces[3] = f3; }
+
+ /// Set the adjedges data.
+ void setadjedges(int e0, int e1, int e2, int e3)
+ { adjedges = (e0&3) | ((e1&3)<<2) | ((e2&3)<<4) | ((e3&3)<<6); }
+
+ /// Flag bit values (for internal use).
+ enum { flag_constant = 1, flag_hasedits = 2, flag_nbconstant = 4, flag_subface = 8 };
+ };
+
+
+ /** Memory-managed string. Used for returning error messages from
+ API functions. On most platforms, this is a typedef to
+ std::string. For Windows, this is a custom class that
+ implements a subset of std::string. (Note: std::string cannot
+ be passed through a Windows DLL interface).
+ */
+#ifdef PTEX_USE_STDSTRING
+ typedef std::string String;
+#else
+ class String
+ {
+ public:
+ String() : _str(0) {}
+ String(const String& str) : _str(0) { *this = str; }
+ PTEXAPI ~String();
+ PTEXAPI String& operator=(const char* str);
+ String& operator=(const String& str) { *this = str._str; return *this; }
+ const char* c_str() const { return _str ? _str : ""; }
+ bool empty() const { return _str == 0; }
+
+ private:
+ char* _str;
+ };
+#endif
+
+}
+#ifndef DOXYGEN
+;
+#endif
+
+/// std::stream output operator. \relates Ptex::String
+#ifndef PTEX_USE_STDSTRING
+std::ostream& operator << (std::ostream& stream, const Ptex::String& str);
+#endif
+
+
+/**
+ @class PtexMetaData
+ @brief Meta data accessor
+
+ Meta data is acquired from PtexTexture and accessed through this interface.
+ */
+class PtexMetaData {
+ protected:
+ /// Destructor not for public use. Use release() instead.
+ virtual ~PtexMetaData() {}
+
+ public:
+ /// Release resources held by this pointer (pointer becomes invalid).
+ virtual void release() = 0;
+
+ /// Query number of meta data entries stored in file.
+ virtual int numKeys() = 0;
+
+ /// Query the name and type of a meta data entry.
+ virtual void getKey(int n, const char*& key, Ptex::MetaDataType& type) = 0;
+
+ /** Query the value of a given meta data entry.
+ If the key doesn't exist or the type doesn't match, value is set to null */
+ virtual void getValue(const char* key, const char*& value) = 0;
+
+ /** Query the value of a given meta data entry.
+ If the key doesn't exist or the type doesn't match, value is set to null */
+ virtual void getValue(const char* key, const int8_t*& value, int& count) = 0;
+
+ /** Query the value of a given meta data entry.
+ If the key doesn't exist or the type doesn't match, value is set to null */
+ virtual void getValue(const char* key, const int16_t*& value, int& count) = 0;
+
+ /** Query the value of a given meta data entry.
+ If the key doesn't exist or the type doesn't match, value is set to null */
+ virtual void getValue(const char* key, const int32_t*& value, int& count) = 0;
+
+ /** Query the value of a given meta data entry.
+ If the key doesn't exist or the type doesn't match, value is set to null */
+ virtual void getValue(const char* key, const float*& value, int& count) = 0;
+
+ /** Query the value of a given meta data entry.
+ If the key doesn't exist or the type doesn't match, value is set to null */
+ virtual void getValue(const char* key, const double*& value, int& count) = 0;
+};
+
+
+/**
+ @class PtexFaceData
+ @brief Per-face texture data accessor
+
+ Per-face texture data is acquired from PtexTexture and accessed
+ through this interface. This interface provides low-level access
+ to the data as stored on disk for maximum efficiency. If this
+ isn't needed, face data can be more conveniently read directly
+ from PtexTexture.
+ */
+class PtexFaceData {
+ protected:
+ /// Destructor not for public use. Use release() instead.
+ virtual ~PtexFaceData() {}
+
+ public:
+ /// Release resources held by this pointer (pointer becomes invalid).
+ virtual void release() = 0;
+
+ /** True if this data block is constant. */
+ virtual bool isConstant() = 0;
+
+ /** Resolution of the texture held by this data block. Note: the
+ indicated texture res may be larger than 1x1 even if the
+ texture data is constant. */
+ virtual Ptex::Res res() = 0;
+
+ /** Read a single texel from the data block. The texel coordinates, u and v, have
+ a range of [0..ures-1, 0..vres-1]. Note: this method will work correctly even if
+ the face is constant or tiled. */
+ virtual void getPixel(int u, int v, void* result) = 0;
+
+ /** Access the data from this data block.
+
+ If the data block is constant, getData will return a pointer to a single texel's data value.
+
+ If the data block is tiled, then getData will return null and
+ the data must be accessed per-tile via the getTile() function. */
+ virtual void* getData() = 0;
+
+ /** True if this data block is tiled.
+ If tiled, the data must be access per-tile via getTile(). */
+ virtual bool isTiled() = 0;
+
+ /** Resolution of each tile in this data block. */
+ virtual Ptex::Res tileRes() = 0;
+
+ /** Access a tile from the data block. Tiles are accessed in v-major order. */
+ virtual PtexFaceData* getTile(int tile) = 0;
+};
+
+
+/**
+ @class PtexTexture
+ @brief Interface for reading data from a ptex file
+
+ PtexTexture instances can be acquired via the static open() method, or via
+ the PtexCache interface.
+
+ Data access through this interface is returned in v-major order with all data channels interleaved per texel.
+ */
+class PtexTexture {
+ protected:
+ /// Destructor not for public use. Use release() instead.
+ virtual ~PtexTexture() {}
+
+ public:
+ /** Open a ptex file for reading.
+
+ If an error occurs, an error message will be stored in the
+ error string param and the a pointer will be returned.
+
+ If the premultiply param is set to true and the texture file has a specified alpha channel,
+ then all data stored in the file will be multiplied by alpha when read from disk. If premultiply
+ is false, then the full-resolution textures will be returned as stored on disk which is assumed
+ to be unmultiplied. Reductions (both stored mip-maps and dynamically generated reductions) are
+ always premultiplied with alpha. See PtexWriter for more information about alpha channels.
+ */
+ PTEXAPI static PtexTexture* open(const char* path, Ptex::String& error, bool premultiply=0);
+
+
+ /// Release resources held by this pointer (pointer becomes invalid).
+ virtual void release() = 0;
+
+ /** Path that file was opened with. If the file was opened using a search path (via PtexCache),
+ the the path will be the path as found in the search path. Otherwise, the path will be
+ the path as supplied to open. */
+ virtual const char* path() = 0;
+
+ /** Type of mesh for which texture data is defined. */
+ virtual Ptex::MeshType meshType() = 0;
+
+ /** Type of data stored in file. */
+ virtual Ptex::DataType dataType() = 0;
+
+ /** Mode for filtering texture access beyond mesh border. */
+ virtual Ptex::BorderMode uBorderMode() = 0;
+
+ /** Mode for filtering texture access beyond mesh border. */
+ virtual Ptex::BorderMode vBorderMode() = 0;
+
+ /** Index of alpha channel (if any). One channel in the file can be flagged to be the alpha channel.
+ If no channel is acting as the alpha channel, -1 is returned.
+ See PtexWriter for more details. */
+ virtual int alphaChannel() = 0;
+
+ /** Number of channels stored in file. */
+ virtual int numChannels() = 0;
+
+ /** Number of faces stored in file. */
+ virtual int numFaces() = 0;
+
+ /** True if the file has edit blocks. See PtexWriter for more details. */
+ virtual bool hasEdits() = 0;
+
+ /** True if the file has mipmaps. See PtexWriter for more details. */
+ virtual bool hasMipMaps() = 0;
+
+ /** Access meta data. */
+ virtual PtexMetaData* getMetaData() = 0;
+
+ /** Access resolution and adjacency information about a face. */
+ virtual const Ptex::FaceInfo& getFaceInfo(int faceid) = 0;
+
+ /** Access texture data for a face at highest-resolution.
+
+ The texture data is copied into the user-supplied buffer.
+ The buffer must be at least this size (in bytes):
+ DataSize(dataType()) * numChannels() * getFaceInfo(faceid).res.size().
+
+ If a stride is given, then (stride-row_length) bytes will be
+ skipped after each row. If stride is zero, then no bytes will
+ be skipped. Note: the image can be flipped vertically by using
+ an appropriate negative stride value.
+
+ @param faceid Face index [0..numFaces-1]
+ @param buffer User-supplied buffer
+ @param stride Size of each row in user buffer (in bytes)
+ */
+ virtual void getData(int faceid, void* buffer, int stride) = 0;
+
+ /** Access texture data for a face at a specific resolution.
+
+ The specified resolution may be lower than the full resolution
+ for the face. If it is lower, then the texture data is
+ accessed from the stored mip-maps. If the requested
+ resolution doesn't match a stored resolution, the desired
+ resolution will be generated from the nearest available
+ resolution.
+
+ See previous getData() method for interface details.
+ */
+ virtual void getData(int faceid, void* buffer, int stride, Ptex::Res res) = 0;
+
+ /** Access texture data for a face at highest-resolution as stored on disk. */
+ virtual PtexFaceData* getData(int faceid) = 0;
+
+ /** Access texture data for a face at a specific resolution as stored on disk.
+
+ The specified resolution may be lower (but not higher) than
+ the full resolution for the face. If it is lower, then the
+ texture data is accessed from the stored mip-maps. If the
+ requested resolution doesn't match a stored resolution, the
+ desired resolution will be generated from the nearest
+ available resolution.
+ */
+ virtual PtexFaceData* getData(int faceid, Ptex::Res res) = 0;
+
+ /** Access a single texel from the highest resolution texture .
+ The texel data is converted to floating point (integer types
+ are normalized 0.0 to 1.0). A subset of the available
+ channels may be accessed.
+
+ @param faceid Face index [0..numFaces-1]
+ @param u U coordinate [0..ures-1]
+ @param v V coordinate [0..vres-1]
+ @param result Result data
+ @param firstchan First channel to access [0..numChannels-1]
+ @param nchannels Number of channels to access.
+ */
+ virtual void getPixel(int faceid, int u, int v,
+ float* result, int firstchan, int nchannels) = 0;
+
+ /** Access a single texel for a face at a particular resolution.
+
+ The specified resolution may be lower (but not higher) than
+ the full resolution for the face. If it is lower, then the
+ texture data is accessed from the stored mip-maps. If the
+ requested resolution doesn't match a stored resolution, the
+ desired resolution will be generated from the nearest
+ available resolution.
+
+ See previous getPixel() method for details.
+ */
+ virtual void getPixel(int faceid, int u, int v,
+ float* result, int firstchan, int nchannels,
+ Ptex::Res res) = 0;
+};
+
+
+/** @class PtexInputHandler
+ @brief Custom handler interface for intercepting and redirecting Ptex input stream calls
+
+ A custom instance of this class can be defined and supplied to the PtexCache class.
+ Files accessed through the cache will have their input streams redirected through this
+ interface.
+ */
+class PtexInputHandler {
+ protected:
+ virtual ~PtexInputHandler() {}
+
+ public:
+ typedef void* Handle;
+
+ /** Open a file in read mode.
+ Returns null if there was an error.
+ If an error occurs, the error string is available via lastError().
+ */
+ virtual Handle open(const char* path) = 0;
+
+ /** Seek to an absolute byte position in the input stream. */
+ virtual void seek(Handle handle, int64_t pos) = 0;
+
+ /** Read a number of bytes from the file.
+ Returns the number of bytes successfully read.
+ If less than the requested number of bytes is read, the error string
+ is available via lastError().
+ */
+ virtual size_t read(void* buffer, size_t size, Handle handle) = 0;
+
+ /** Close a file. Returns false if an error occurs, and the error
+ string is available via lastError(). */
+ virtual bool close(Handle handle) = 0;
+
+ /** Return the last error message encountered. */
+ virtual const char* lastError() = 0;
+};
+
+
+/**
+ @class PtexCache
+ @brief File-handle and memory cache for reading ptex files
+
+ The PtexCache class allows cached read access to multiple ptex
+ files while constraining the open file count and memory usage to
+ specified limits. File and data objects accessed via the cache
+ are added back to the cache when their release method is called.
+ Released objects are maintained in an LRU list and only destroyed
+ when the specified resource limits are exceeded.
+
+ The cache is fully multi-threaded. Cached data will be shared among
+ all threads that have access to the cache, and the data are protected
+ with internal locks. See PtexCache.cpp for details about the caching
+ and locking implementation.
+ */
+
+class PtexCache {
+ protected:
+ /// Destructor not for public use. Use release() instead.
+ virtual ~PtexCache() {}
+
+ public:
+ /** Create a cache with the specified limits.
+
+ @param maxFiles Maximum open file handles. If unspecified,
+ limit is set to 100 open files.
+
+ @param maxMem Maximum allocated memory, in bytes. If unspecified,
+ limit is set to 100MB.
+
+ @param premultiply If true, textures will be premultiplied by the alpha
+ channel (if any) when read from disk. See PtexTexture and PtexWriter
+ for more details.
+
+ @param handler If specified, all input calls made through this cache will
+ be directed through the handler.
+ */
+ PTEXAPI static PtexCache* create(int maxFiles=0,
+ int maxMem=0,
+ bool premultiply=false,
+ PtexInputHandler* handler=0);
+
+ /// Release resources held by this pointer (pointer becomes invalid).
+ virtual void release() = 0;
+
+ /** Set a search path for finding textures.
+ Note: if an input handler is installed the search path will be ignored.
+
+ @param path colon-delimited search path.
+ */
+ virtual void setSearchPath(const char* path) = 0;
+
+ /** Query the search path. Returns string set via setSearchPath. */
+ virtual const char* getSearchPath() = 0;
+
+ /** Open a texture. If the specified path was previously opened, and the
+ open file limit hasn't been exceeded, then a pointer to the already
+ open file will be returned.
+
+ If the specified path hasn't been opened yet or was closed,
+ either to maintain the open file limit or because the file was
+ explicitly purged from the cache, then the file will be newly
+ opened. If the path is relative (i.e. doesn't begin with a
+ '/') then the search path will be used to locate the file.
+
+ The texture file will stay open until the PtexTexture::release
+ method is called, at which point the texture will be returned
+ to the cache. Once released, the file will be closed when
+ either the file handle limit is reached, the cached is
+ released, or when the texture is purged (see purge methods
+ below).
+
+ If texture could not be opened, null will be returned and
+ an error string will be set.
+
+ @param path File path. If path is relative, search path will
+ be used to find the file.
+
+ @param error Error string set if texture could not be
+ opened.
+ */
+ virtual PtexTexture* get(const char* path, Ptex::String& error) = 0;
+
+ /** Remove a texture file from the cache. The texture file will remain
+ open and accessible until the file handle is released, but any
+ future cache accesses for that file will open the file anew.
+ */
+ virtual void purge(PtexTexture* texture) = 0;
+
+ /** Remove a texture file from the cache by pathname. The path must
+ match the full path as opened. This function will not search
+ for the file, but if a search path was used, the path must
+ match the path as found by the search path.
+ */
+ virtual void purge(const char* path) = 0;
+
+ /** Remove all texture files from the cache. Open files with
+ active PtexTexture* handles remain open until released.
+ */
+ virtual void purgeAll() = 0;
+};
+
+
+/**
+ @class PtexWriter
+ @brief Interface for writing data to a ptex file.
+
+ Note: if an alpha channel is specified, then the textures being
+ written to the file are expected to have unmultiplied-alpha data.
+ Generated mipmaps will be premultiplied by the Ptex library. On
+ read, PtexTexture will (if requested) premultiply all textures by
+ alpha when getData is called; by default only reductions are
+ premultiplied. If the source textures are already premultiplied,
+ then alphachan can be set to -1 and the library will just leave all
+ the data as-is. The only reason to store unmultiplied-alpha
+ textures in the file is to preserve the original texture data for
+ later editing.
+*/
+
+class PtexWriter {
+ protected:
+ /// Destructor not for public use. Use release() instead.
+ virtual ~PtexWriter() {}
+
+ public:
+ /** Open a new texture file for writing.
+ @param path Path to file.
+ @param mt Type of mesh for which the textures are defined.
+ @param dt Type of data stored within file.
+ @param nchannels Number of data channels.
+ @param alphachan Index of alpha channel, [0..nchannels-1] or -1 if no alpha channel is present.
+ @param nfaces Number of faces in mesh.
+ @param error String containing error message if open failed.
+ @param genmipmaps Specify true if mipmaps should be generated.
+ */
+ PTEXAPI
+ static PtexWriter* open(const char* path,
+ Ptex::MeshType mt, Ptex::DataType dt,
+ int nchannels, int alphachan, int nfaces,
+ Ptex::String& error, bool genmipmaps=true);
+
+ /** Open an existing texture file for writing.
+
+ If the incremental param is specified as true, then data
+ values written to the file are appended to the file as "edit
+ blocks". This is the fastest way to write data to the file, but
+ edit blocks are slower to read back, and they have no mipmaps so
+ filtering can be inefficient.
+
+ If incremental is false, then the edits are applied to the
+ file and the entire file is regenerated on close as if it were
+ written all at once with open().
+
+ If the file doesn't exist it will be created and written as if
+ open() were used. If the file exists, the mesh type, data
+ type, number of channels, alpha channel, and number of faces
+ must agree with those stored in the file.
+ */
+ PTEXAPI
+ static PtexWriter* edit(const char* path, bool incremental,
+ Ptex::MeshType mt, Ptex::DataType dt,
+ int nchannels, int alphachan, int nfaces,
+ Ptex::String& error, bool genmipmaps=true);
+
+ /** Apply edits to a file.
+
+ If a file has pending edits, the edits will be applied and the
+ file will be regenerated with no edits. This is equivalent to
+ calling edit() with incremental set to false. The advantage
+ is that the file attributes such as mesh type, data type,
+ etc., don't need to be known in advance.
+ */
+ PTEXAPI
+ static bool applyEdits(const char* path, Ptex::String& error);
+
+ /** Release resources held by this pointer (pointer becomes invalid). */
+ virtual void release() = 0;
+
+ /** Set border modes */
+ virtual void setBorderModes(Ptex::BorderMode uBorderMode, Ptex::BorderMode vBorderMode) = 0;
+
+ /** Write a string as meta data. Both the key and string params must be null-terminated strings. */
+ virtual void writeMeta(const char* key, const char* string) = 0;
+
+ /** Write an array of signed 8-bit integers as meta data. The key must be a null-terminated string. */
+ virtual void writeMeta(const char* key, const int8_t* value, int count) = 0;
+
+ /** Write an array of signed 16-bit integers as meta data. The key must be a null-terminated string. */
+ virtual void writeMeta(const char* key, const int16_t* value, int count) = 0;
+
+ /** Write an array of signed 32-bit integers as meta data. The key must be a null-terminated string. */
+ virtual void writeMeta(const char* key, const int32_t* value, int count) = 0;
+
+ /** Write an array of signed 32-bit floats as meta data. The key must be a null-terminated string. */
+ virtual void writeMeta(const char* key, const float* value, int count) = 0;
+
+ /** Write an array of signed 32-bit doubles as meta data. The key must be a null-terminated string. */
+ virtual void writeMeta(const char* key, const double* value, int count) = 0;
+
+ /** Copy meta data from an existing meta data block. */
+ virtual void writeMeta(PtexMetaData* data) = 0;
+
+ /** Write texture data for a face.
+ The data is assumed to be channel-interleaved per texel and stored in v-major order.
+
+ @param faceid Face index [0..nfaces-1].
+ @param info Face resolution and adjacency information.
+ @param data Texel data.
+ @param stride Distance between rows, in bytes (if zero, data is assumed packed).
+
+ If an error is encountered while writing, false is returned and an error message can be
+ when close is called.
+ */
+ virtual bool writeFace(int faceid, const Ptex::FaceInfo& info, const void* data, int stride=0) = 0;
+
+ /** Write constant texture data for a face.
+ The data is written as a single constant texel value. Note: the resolution specified in the
+ info param may indicate a resolution greater than 1x1 and the value will be preserved when
+ reading. This is useful to indicate a texture's logical resolution even when the data is
+ constant. */
+ virtual bool writeConstantFace(int faceid, const Ptex::FaceInfo& info, const void* data) = 0;
+
+ /** Close the file. This operation can take some time if mipmaps are being generated or if there
+ are many edit blocks. If an error occurs while writing, false is returned and an error string
+ is written into the error parameter. */
+ virtual bool close(Ptex::String& error) = 0;
+
+#if NEW_API
+ virtual bool writeFaceReduction(int faceid, const Ptex::Res& res, const void* data, int stride=0) = 0;
+ virtual bool writeConstantFaceReduction(int faceid, const Ptex::Res& res, const void* data) = 0;
+#endif
+};
+
+
+/**
+ @class PtexFilter
+ @brief Interface for filtered sampling of ptex data files.
+
+ PtexFilter instances are obtained by calling one of the particular static methods. When finished using
+ the filter, it must be returned to the library using release().
+
+ To apply the filter to a ptex data file, use the eval() method.
+ */
+class PtexFilter {
+ protected:
+ /// Destructor not for public use. Use release() instead.
+ virtual ~PtexFilter() {};
+
+ public:
+ /// Filter types
+ enum FilterType {
+ f_point, ///< Point-sampled (no filtering)
+ f_bilinear, ///< Bi-linear interpolation
+ f_box, ///< Box filter
+ f_gaussian, ///< Gaussian filter
+ f_bicubic, ///< General bi-cubic filter (uses sharpness option)
+ f_bspline, ///< BSpline (equivalent to bi-cubic w/ sharpness=0)
+ f_catmullrom, ///< Catmull-Rom (equivalent to bi-cubic w/ sharpness=1)
+ f_mitchell ///< Mitchell (equivalent to bi-cubic w/ sharpness=2/3)
+ };
+
+ /// Choose filter options
+ struct Options {
+ int __structSize; ///< (for internal use only)
+ FilterType filter; ///< Filter type.
+ bool lerp; ///< Interpolate between mipmap levels.
+ float sharpness; ///< Filter sharpness, 0..1 (for general bi-cubic filter only).
+
+ /// Constructor - sets defaults
+ Options(FilterType filter=f_box, bool lerp=0, float sharpness=0) :
+ __structSize(sizeof(Options)),
+ filter(filter), lerp(lerp), sharpness(sharpness) {}
+ };
+
+ /* Construct a filter for the given texture.
+ */
+ PTEXAPI static PtexFilter* getFilter(PtexTexture* tx, const Options& opts);
+
+ /** Release resources held by this pointer (pointer becomes invalid). */
+ virtual void release() = 0;
+
+ /** Apply filter to a ptex data file.
+
+ The filter region is a parallelogram centered at the given
+ (u,v) coordinate with sides defined by two vectors [uw1, vw1]
+ and [uw2, vw2]. For an axis-aligned rectangle, the vectors
+ are [uw, 0] and [0, vw]. See \link filterfootprint Filter
+ Footprint \endlink for details.
+
+ @param result Buffer to hold filter result. Must be large enough to hold nchannels worth of data.
+ @param firstchan First channel to evaluate [0..tx->numChannels()-1]
+ @param nchannels Number of channels to evaluate
+ @param faceid Face index [0..tx->numFaces()-1]
+ @param u U coordinate, normalized [0..1]
+ @param v V coordinate, normalized [0..1]
+ @param uw1 U filter width 1, normalized [0..1]
+ @param vw1 V filter width 1, normalized [0..1]
+ @param uw2 U filter width 2, normalized [0..1]
+ @param vw2 V filter width 2, normalized [0..1]
+ @param width scale factor for filter width
+ @param blur amount to add to filter width [0..1]
+ */
+ virtual void eval(float* result, int firstchan, int nchannels,
+ int faceid, float u, float v, float uw1, float vw1, float uw2, float vw2,
+ float width=1, float blur=0) = 0;
+};
+
+
+/**
+ @class PtexPtr
+ @brief Smart-pointer for acquiring and releasing API objects
+
+ All public API objects must be released back to the Ptex library
+ via the release() method. This smart-pointer class can wrap any of
+ the Ptex API objects and will automatically release the object when
+ the pointer goes out of scope. Usage of PtexPtr is optional, but
+ recommended.
+
+ Note: for efficiency and safety, PtexPtr is noncopyable. However,
+ ownership can be transferred between PtexPtr instances via the
+ PtexPtr::swap member function.
+
+ Example:
+ \code
+ {
+ Ptex::String error;
+ PtexPtr<PtexTexture> inptx(PtexTexture::open(inptxname, error));
+ if (!inptx) {
+ std::cerr << error << std::endl;
+ }
+ else {
+ // read some data
+ inptx->getData(faceid, buffer, stride);
+ }
+ }
+ \endcode
+ */
+template <class T> class PtexPtr {
+ T* _ptr;
+ public:
+ /// Constructor.
+ PtexPtr(T* ptr=0) : _ptr(ptr) {}
+
+ /// Destructor, calls ptr->release().
+ ~PtexPtr() { if (_ptr) _ptr->release(); }
+
+ /// Use as pointer value.
+ operator T* () { return _ptr; }
+
+ /// Access members of pointer.
+ T* operator-> () { return _ptr; }
+
+ /// Get pointer value.
+ T* get() { return _ptr; }
+
+ /// Swap pointer values.
+ void swap(PtexPtr& p)
+ {
+ T* tmp = p._ptr;
+ p._ptr = _ptr;
+ _ptr = tmp;
+ }
+
+ private:
+ /// Copying prohibited
+ PtexPtr(const PtexPtr& p);
+
+ /// Assignment prohibited
+ void operator= (PtexPtr& p);
+};
+
+#endif
diff --git a/release/scripts/ui/properties_data_mesh.py b/release/scripts/ui/properties_data_mesh.py
index 38364139147..3af0d6a33ac 100644
--- a/release/scripts/ui/properties_data_mesh.py
+++ b/release/scripts/ui/properties_data_mesh.py
@@ -254,6 +254,27 @@ class DATA_PT_shape_keys(MeshButtonsPanel, bpy.types.Panel):
row.prop(key, "slurph")
+class DATA_PT_ptex(MeshButtonsPanel, bpy.types.Panel):
+ bl_label = "PTex"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ me = context.mesh
+
+ row = layout.row()
+ row.template_list(me, "ptex_layers", me, "active_ptex_index", rows=2)
+ col = row.column(align=True)
+ col.operator("ptex.layer_remove", icon='ZOOMOUT', text="")
+
+ layout.operator_menu_enum("ptex.layer_convert", "type")
+ layout.operator_menu_enum("ptex.layer_add", "type")
+ row = layout.row()
+ row.operator("ptex.open")
+ row.operator("ptex.layer_save")
+
+
class DATA_PT_uv_texture(MeshButtonsPanel, bpy.types.Panel):
bl_label = "UV Texture"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
@@ -344,6 +365,10 @@ class DATA_PT_vertex_colors(MeshButtonsPanel, bpy.types.Panel):
lay = me.vertex_colors.active
if lay:
layout.prop(lay, "name")
+ if lay.multiresolution:
+ layout.operator("mesh.vertex_color_multiresolution_toggle", text="Remove Multires")
+ else:
+ layout.operator("mesh.vertex_color_multiresolution_toggle", text="Add Multires")
class DATA_PT_custom_props_mesh(MeshButtonsPanel, PropertyPanel, bpy.types.Panel):
diff --git a/release/scripts/ui/properties_data_modifier.py b/release/scripts/ui/properties_data_modifier.py
index 8950a017022..e1710f20a5d 100644
--- a/release/scripts/ui/properties_data_modifier.py
+++ b/release/scripts/ui/properties_data_modifier.py
@@ -394,8 +394,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
split = layout.split()
col = split.column()
col.prop(md, "levels", text="Preview")
- col.prop(md, "sculpt_levels", text="Sculpt")
+ col.prop(md, "edit_levels", text="Edit")
col.prop(md, "render_levels", text="Render")
+ col.prop(md, "show_only_control_edges")
col = split.column()
@@ -403,7 +404,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
col.operator("object.multires_subdivide", text="Subdivide")
col.operator("object.multires_higher_levels_delete", text="Delete Higher")
col.operator("object.multires_reshape", text="Reshape")
- col.prop(md, "show_only_control_edges")
+ col.operator("object.multires_base_apply", text="Apply Base")
layout.separator()
diff --git a/release/scripts/ui/properties_texture.py b/release/scripts/ui/properties_texture.py
index bf0c5d56e4c..295078dc1a5 100644
--- a/release/scripts/ui/properties_texture.py
+++ b/release/scripts/ui/properties_texture.py
@@ -30,6 +30,7 @@ class TEXTURE_MT_specials(bpy.types.Menu):
layout.operator("texture.slot_copy", icon='COPYDOWN')
layout.operator("texture.slot_paste", icon='PASTEDOWN')
+ layout.operator("paint.mask_from_texture")
class TEXTURE_MT_envmap_specials(bpy.types.Menu):
diff --git a/release/scripts/ui/space_view3d.py b/release/scripts/ui/space_view3d.py
index 02f29c7632c..7df932f6800 100644
--- a/release/scripts/ui/space_view3d.py
+++ b/release/scripts/ui/space_view3d.py
@@ -957,6 +957,23 @@ class VIEW3D_MT_paint_vertex(bpy.types.Menu):
layout.operator("paint.vertex_color_set")
layout.operator("paint.vertex_color_dirt")
+ # hiding
+ prop = layout.operator("ptex.subface_flag_set", text="Hide Selected")
+ prop.flag = 'HIDDEN'
+ prop = layout.operator("ptex.subface_flag_set", text="Show All")
+ prop.flag = 'HIDDEN'
+ prop.set = 0
+ prop.ignore_hidden = 0
+ prop.ignore_unselected = 0
+
+ # masking
+ prop = layout.operator("ptex.subface_flag_set", text="Mask Selected")
+ prop.flag = 'MASKED'
+ prop = layout.operator("ptex.subface_flag_set", text="Unmask Selected")
+ prop.flag = 'MASKED'
+ prop.set = 0
+
+
class VIEW3D_MT_hook(bpy.types.Menu):
bl_label = "Hooks"
@@ -1037,6 +1054,11 @@ class VIEW3D_MT_sculpt(bpy.types.Menu):
sculpt = tool_settings.sculpt
brush = tool_settings.sculpt.brush
+ layout.operator("sculpt.area_hide", text="Show Hidden").show_all = True
+ layout.operator("sculpt.area_hide", text="Hide Exterior Area")
+ layout.operator("sculpt.area_hide", text="Hide Interior Area").hide_inside = True
+ layout.separator()
+
layout.operator("ed.undo")
layout.operator("ed.redo")
@@ -1045,6 +1067,7 @@ class VIEW3D_MT_sculpt(bpy.types.Menu):
layout.prop(sculpt, "use_symmetry_x")
layout.prop(sculpt, "use_symmetry_y")
layout.prop(sculpt, "use_symmetry_z")
+
layout.separator()
layout.prop(sculpt, "lock_x")
layout.prop(sculpt, "lock_y")
diff --git a/release/scripts/ui/space_view3d_toolbar.py b/release/scripts/ui/space_view3d_toolbar.py
index a6f3f7e34c2..ec6f8ed1f3f 100644
--- a/release/scripts/ui/space_view3d_toolbar.py
+++ b/release/scripts/ui/space_view3d_toolbar.py
@@ -475,7 +475,7 @@ class PaintPanel():
if context.sculpt_object:
return ts.sculpt
- elif context.vertex_paint_object:
+ elif context.vertex_paint_object and (not context.vertex_paint_object.data.ptex_edit_mode):
return ts.vertex_paint
elif context.weight_paint_object:
return ts.weight_paint
@@ -487,6 +487,39 @@ class PaintPanel():
return None
+class VIEW3D_PT_tools_masking(PaintPanel, bpy.types.Panel):
+ bl_label = "Masking"
+ bl_default_closed = False
+
+ @classmethod
+ def poll(cls, context):
+ return cls.paint_settings(context) and (context.sculpt_object or context.vertex_paint_object)
+
+ def draw(self, context):
+ layout = self.layout
+
+ settings = self.paint_settings(context)
+ mesh = context.object.data
+
+ row = layout.row()
+
+ col = row.column()
+ col.template_list(mesh, "paint_mask_layers", mesh, "active_paint_mask_index", rows=2)
+
+ col = row.column(align=True)
+ col.operator("paint.mask_layer_add", icon='ZOOMIN', text="")
+ col.operator("paint.mask_layer_remove", icon='ZOOMOUT', text="")
+
+ row = layout.row(align=True)
+ row.active = mesh.active_paint_mask_index != -1
+ row.operator("paint.mask_set", text="Clear").mode = 'CLEAR'
+ row.operator("paint.mask_set", text="Fill").mode = 'FILL'
+ row.operator("paint.mask_set", text="Invert").mode = 'INVERT'
+
+ if row.active:
+ layout.prop(mesh.paint_mask_layers[mesh.active_paint_mask_index], "strength", slider=True)
+
+
class VIEW3D_PT_tools_brush(PaintPanel, bpy.types.Panel):
bl_label = "Brush"
@@ -622,6 +655,8 @@ class VIEW3D_PT_tools_brush(PaintPanel, bpy.types.Panel):
col.prop(brush, "use_accumulate")
+ col.prop(brush, "mask")
+
if brush.sculpt_tool == 'LAYER':
col.separator()
@@ -695,11 +730,17 @@ class VIEW3D_PT_tools_brush(PaintPanel, bpy.types.Panel):
row.prop(brush, "strength", text="Strength", slider=True)
row.prop(brush, "use_pressure_strength", toggle=True, text="")
+ if brush.vertexpaint_tool == 'ALPHA':
+ row = col.row(align=True)
+ row.prop(brush, "direction", expand=True)
+
# XXX - TODO
#row = col.row(align=True)
#row.prop(brush, "jitter", slider=True)
#row.prop(brush, "use_pressure_jitter", toggle=True, text="")
+ col.prop(brush, "mask")
+
class VIEW3D_PT_tools_brush_texture(PaintPanel, bpy.types.Panel):
bl_label = "Texture"
@@ -709,6 +750,7 @@ class VIEW3D_PT_tools_brush_texture(PaintPanel, bpy.types.Panel):
def poll(cls, context):
settings = cls.paint_settings(context)
return (settings and settings.brush and (context.sculpt_object or
+ context.vertex_paint_object or
context.texture_paint_object))
def draw(self, context):
@@ -722,7 +764,7 @@ class VIEW3D_PT_tools_brush_texture(PaintPanel, bpy.types.Panel):
col.template_ID_preview(brush, "texture", new="texture.new", rows=3, cols=8)
- if context.sculpt_object:
+ if context.sculpt_object or context.vertex_paint_object:
#XXX duplicated from properties_texture.py
col.separator()
@@ -854,7 +896,7 @@ class VIEW3D_PT_tools_brush_stroke(PaintPanel, bpy.types.Panel):
col = layout.column()
- if context.sculpt_object:
+ if context.sculpt_object or context.vertex_paint_object:
col.label(text="Stroke Method:")
col.prop(brush, "stroke_method", text="")
@@ -944,7 +986,6 @@ class VIEW3D_PT_tools_brush_curve(PaintPanel, bpy.types.Panel):
layout = self.layout
settings = self.paint_settings(context)
-
brush = settings.brush
layout.template_curve_mapping(brush, "curve", brush=True)
@@ -994,20 +1035,23 @@ class VIEW3D_PT_sculpt_options(PaintPanel, bpy.types.Panel):
row.prop(sculpt, "lock_y", text="Y", toggle=True)
row.prop(sculpt, "lock_z", text="Z", toggle=True)
-
-class VIEW3D_PT_sculpt_symmetry(PaintPanel, bpy.types.Panel):
+class VIEW3D_PT_tools_paint_symmetry(PaintPanel, bpy.types.Panel):
bl_label = "Symmetry"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
- return (context.sculpt_object and context.tool_settings.sculpt)
+ return cls.paint_settings(context) and (context.sculpt_object or context.vertex_paint_object)
def draw(self, context):
layout = self.layout
- sculpt = context.tool_settings.sculpt
+ if context.sculpt_object:
+ paint = context.tool_settings.sculpt
+ elif context.vertex_paint_object:
+ paint = context.tool_settings.vertex_paint
+
settings = __class__.paint_settings(context)
brush = settings.brush
@@ -1016,19 +1060,19 @@ class VIEW3D_PT_sculpt_symmetry(PaintPanel, bpy.types.Panel):
col = split.column()
col.label(text="Mirror:")
- col.prop(sculpt, "use_symmetry_x", text="X")
- col.prop(sculpt, "use_symmetry_y", text="Y")
- col.prop(sculpt, "use_symmetry_z", text="Z")
+ col.prop(paint, "use_symmetry_x", text="X")
+ col.prop(paint, "use_symmetry_y", text="Y")
+ col.prop(paint, "use_symmetry_z", text="Z")
col = split.column()
- col.prop(sculpt, "radial_symmetry", text="Radial")
+ col.prop(paint, "radial_symmetry", text="Radial")
col = layout.column()
col.separator()
- col.prop(sculpt, "use_symmetry_feather", text="Feather")
+ col.prop(paint, "use_symmetry_feather", text="Feather")
class VIEW3D_PT_tools_brush_appearance(PaintPanel, bpy.types.Panel):
@@ -1037,7 +1081,7 @@ class VIEW3D_PT_tools_brush_appearance(PaintPanel, bpy.types.Panel):
@classmethod
def poll(cls, context):
- return (context.sculpt_object and context.tool_settings.sculpt) or (context.vertex_paint_object and context.tool_settings.vertex_paint) or (context.weight_paint_object and context.tool_settings.weight_paint) or (context.texture_paint_object and context.tool_settings.image_paint)
+ return cls.paint_settings(context) and ((context.sculpt_object and context.tool_settings.sculpt) or (context.vertex_paint_object and context.tool_settings.vertex_paint) or (context.weight_paint_object and context.tool_settings.weight_paint) or (context.texture_paint_object and context.tool_settings.image_paint))
def draw(self, context):
layout = self.layout
@@ -1122,10 +1166,14 @@ class VIEW3D_PT_tools_weightpaint_options(View3DPanel, bpy.types.Panel):
# ********** default tools for vertexpaint ****************
-class VIEW3D_PT_tools_vertexpaint(View3DPanel, bpy.types.Panel):
- bl_context = "vertexpaint"
+class VIEW3D_PT_tools_vertexpaint(PaintPanel, bpy.types.Panel):
bl_label = "Options"
+ @classmethod
+ def poll(cls, context):
+ settings = cls.paint_settings(context)
+ return settings and context.vertex_paint_object
+
def draw(self, context):
layout = self.layout
@@ -1138,6 +1186,8 @@ class VIEW3D_PT_tools_vertexpaint(View3DPanel, bpy.types.Panel):
col.prop(vpaint, "use_normal")
col.prop(vpaint, "use_spray")
+ col.prop(vpaint, "fast_navigate")
+
col.label(text="Unified Settings:")
col.prop(tool_settings, "sculpt_paint_use_unified_size", text="Size")
col.prop(tool_settings, "sculpt_paint_use_unified_strength", text="Strength")
@@ -1329,6 +1379,74 @@ class VIEW3D_PT_tools_particlemode(View3DPanel, bpy.types.Panel):
sub.prop(pe, "fade_frames", slider=True)
+class VIEW3D_PT_paint_overlay(PaintPanel, bpy.types.Panel):
+ bl_label = "Source Image"
+ bl_default_closed = True
+
+ @classmethod
+ def poll(cls, context):
+ settings = cls.paint_settings(context)
+ return settings and context.vertex_paint_object
+
+ def draw(self, context):
+ layout = self.layout
+
+ overlay = context.tool_settings.paint_overlay
+
+ layout.prop(overlay, "enabled")
+ layout.template_ID(overlay, "image", open="image.open")
+ layout.prop(overlay, "transparency_color")
+ layout.prop(overlay, "transparency_tolerance")
+
+class VIEW3D_PT_ptex_edit(View3DPanel, bpy.types.Panel):
+ bl_label = "Ptex"
+ bl_default_closed = False
+
+ @classmethod
+ def poll(cls, context):
+ ob = context.vertex_paint_object
+ return ob and ob.data.ptex_edit_mode
+
+ def draw(self, context):
+ layout = self.layout
+
+ mesh = context.active_object.data
+ active_ptex = mesh.active_ptex_index
+
+ if active_ptex != -1:
+ ts = context.tool_settings
+
+ layout.operator("ptex.face_resolution_set", text="Double Selected").operation = 'DOUBLE'
+ layout.operator("ptex.face_resolution_set", text="Half Selected").operation = 'HALF'
+
+ prop = layout.operator("ptex.face_resolution_set")
+ prop.operation = 'NUMERIC'
+ layout.prop(ts, "ptex_u_resolution", text="U")
+ layout.prop(ts, "ptex_v_resolution", text="V")
+
+ active_face = mesh.faces.active
+ active_subface = mesh.faces.active_subface
+
+ # display active [sub]face's resolution
+ if active_face >= 0 and active_subface >= 0:
+ box = layout.box()
+
+ box.label("Current Resolution:")
+
+ ptex_layer = mesh.ptex_layers[active_ptex]
+ ptex = ptex_layer.data[active_face]
+ subface = ptex.subfaces[active_subface]
+
+ ures = subface.resolution[0];
+ vres = subface.resolution[1];
+ if len(ptex.subfaces) == 4:
+ ures *= 2
+ vres *= 2
+
+ box.label(str(ures) + " x " + str(vres))
+
+
+
def register():
pass
diff --git a/release/windows/installer/01.installer.bmp b/release/windows/installer/01.installer.bmp
deleted file mode 100644
index 10fb01454a4..00000000000
--- a/release/windows/installer/01.installer.bmp
+++ /dev/null
Binary files differ
diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h
index 33852a1b923..5b3d2d7a2ed 100644
--- a/source/blender/blenkernel/BKE_DerivedMesh.h
+++ b/source/blender/blenkernel/BKE_DerivedMesh.h
@@ -68,10 +68,7 @@ struct PBVH;
#define SUB_ELEMS_EDGE 2
#define SUB_ELEMS_FACE 4
-typedef struct DMGridData {
- float co[3];
- float no[3];
-} DMGridData;
+typedef struct DMGridData DMGridData;
typedef struct DMGridAdjacency {
int index[4];
@@ -84,6 +81,23 @@ typedef enum DerivedMeshType {
DM_TYPE_CCGDM
} DerivedMeshType;
+typedef enum DMDrawFlags {
+ /* fast navigation for multires */
+ DM_DRAW_LOWEST_SUBDIVISION_LEVEL = 1,
+ /* draw mask for sculpt mode */
+ DM_DRAW_PAINT_MASK = 2,
+ /* draw ptex textures */
+ DM_DRAW_PTEX = 4,
+ /* mcol, weight colors, etc. */
+ DM_DRAW_VERTEX_COLORS = 8,
+ /* visually show ptex texel density */
+ DM_DRAW_PTEX_TEXELS = 16,
+ /* mainly for sculpting, use smooth normals */
+ DM_DRAW_FULLY_SMOOTH = 32,
+ /* back-buffer selection mode */
+ DM_DRAW_BACKBUF_SELECTION = 64,
+} DMDrawFlags;
+
typedef struct DerivedMesh DerivedMesh;
struct DerivedMesh {
/* Private DerivedMesh data, only for internal DerivedMesh use */
@@ -158,6 +172,8 @@ struct DerivedMesh {
DMGridData **(*getGridData)(DerivedMesh *dm);
DMGridAdjacency *(*getGridAdjacency)(DerivedMesh *dm);
int *(*getGridOffset)(DerivedMesh *dm);
+ struct GridKey *(*getGridKey)(DerivedMesh *dm);
+ struct GridToFace *(*getGridFaceMap)(DerivedMesh *dm);
/* Iterate over each mapped vertex in the derived mesh, calling the
* given function with the original vert and the mapped vert's new
@@ -240,7 +256,8 @@ struct DerivedMesh {
* Also called for *final* editmode DerivedMeshes
*/
void (*drawFacesSolid)(DerivedMesh *dm, float (*partial_redraw_planes)[4],
- int fast, int (*setMaterial)(int, void *attribs));
+ int (*setMaterial)(int, void *attribs),
+ DMDrawFlags flags);
/* Draw all faces
* o If useTwoSided, draw front and back using col arrays
@@ -278,9 +295,10 @@ struct DerivedMesh {
* smooth shaded.
*/
void (*drawMappedFaces)(DerivedMesh *dm,
- int (*setDrawOptions)(void *userData, int index,
- int *drawSmooth_r),
- void *userData, int useColors);
+ float (*partial_redraw_planes)[4],
+ int (*setDrawOptions)(void *userData, int index,
+ int *drawSmooth_r),
+ void *userData, DMDrawFlags flags);
/* Draw mapped faces using MTFace
* o Drawing options too complicated to enumerate, look at code.
diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h
index 84eb8ef5300..0c9d4a42fdc 100644
--- a/source/blender/blenkernel/BKE_customdata.h
+++ b/source/blender/blenkernel/BKE_customdata.h
@@ -35,6 +35,7 @@
struct ID;
struct CustomData;
struct CustomDataLayer;
+struct CustomDataMultires;
typedef unsigned int CustomDataMask;
extern const CustomDataMask CD_MASK_BAREMESH;
@@ -90,14 +91,18 @@ void *CustomData_add_layer(struct CustomData *data, int type, int alloctype,
void *CustomData_add_layer_named(struct CustomData *data, int type, int alloctype,
void *layer, int totelem, const char *name);
-/* frees the active or first data layer with the give type.
+/* adds a layer and puts it at the specified offset */
+void *CustomData_add_layer_at_offset(struct CustomData *data, int type, int alloctype,
+ void *layerdata, int totelem, int offset);
+
+/* frees the layer at index.
* returns 1 on succes, 0 if no layer with the given type is found
*
* in editmode, use EM_free_data_layer instead of this function
*/
int CustomData_free_layer(struct CustomData *data, int type, int totelem, int index);
-/* frees the layer index with the give type.
+/* frees the active layer of the give type.
* returns 1 on succes, 0 if no layer with the given type is found
*
* in editmode, use EM_free_data_layer instead of this function
@@ -185,20 +190,46 @@ void *CustomData_em_get_n(const struct CustomData *data, void *block, int type,
void *CustomData_bmesh_get(const struct CustomData *data, void *block, int type);
void *CustomData_bmesh_get_n(const struct CustomData *data, void *block, int type, int n);
-/* gets a pointer to the active or first layer of type
- * returns NULL if there is no layer of type
- */
+/************************** Get layer data ***********************/
+/* return the data from the first layer matching the parameters */
+/* these all return NULL if no such layer is found */
+
+/* returns the data from the active layer matching type */
void *CustomData_get_layer(const struct CustomData *data, int type);
+
+/* returns the data from the nth layer matching type */
void *CustomData_get_layer_n(const struct CustomData *data, int type, int n);
+
+/* returns the data from the first layer matching type and name */
void *CustomData_get_layer_named(const struct CustomData *data, int type,
char *name);
+/********************************* Get layer index *********************************/
+/* return the index within data.layers of the first layer matching the parameters */
+/* these all return -1 if no such layer is found */
+
+/* returns the index of the first layer matching type */
int CustomData_get_layer_index(const struct CustomData *data, int type);
+
+/* returns the index of the first layer matching type and name */
int CustomData_get_named_layer_index(const struct CustomData *data, int type, char *name);
+
+/* returns the index of the active layer */
int CustomData_get_active_layer_index(const struct CustomData *data, int type);
+
+/* returns the index of the render layer */
int CustomData_get_render_layer_index(const struct CustomData *data, int type);
+
+/* returns the index of the clone layer */
int CustomData_get_clone_layer_index(const struct CustomData *data, int type);
+
+/* returns the index of the stencil/mask layer */
int CustomData_get_stencil_layer_index(const struct CustomData *data, int type);
+
+/************************************ Get layer offset ***********************************/
+/* return the offset of the layer from the first layer of the specified type */
+/* to find the index of that layer, add it to the index of the first layer of that type */
+/* these all return -1 if no layer of the specified type is found */
int CustomData_get_active_layer(const struct CustomData *data, int type);
int CustomData_get_render_layer(const struct CustomData *data, int type);
int CustomData_get_clone_layer(const struct CustomData *data, int type);
@@ -241,6 +272,7 @@ void CustomData_set_layer_stencil_index(struct CustomData *data, int type, int n
/* adds flag to the layer flags */
void CustomData_set_layer_flag(struct CustomData *data, int type, int flag);
+void CustomData_set_layer_offset_flag(struct CustomData *data, int type, int offset, int flag);
/* alloc/free a block of custom data attached to one element in editmode */
void CustomData_em_set_default(struct CustomData *data, void **block);
@@ -280,6 +312,45 @@ void CustomData_to_bmeshpoly(struct CustomData *fdata, struct CustomData *pdata,
void CustomData_from_bmeshpoly(struct CustomData *fdata, struct CustomData *pdata, struct CustomData *ldata, int total);
void CustomData_bmesh_init_pool(struct CustomData *data, int allocsize);
+/* Subsurf grids */
+
+/* return the number of layers of type that have multires data */
+int CustomData_get_multires_count(struct CustomData *cd, int type);
+
+/* allocates a list of names of layers that have multires data */
+void *CustomData_get_multires_names(struct CustomData *cd, int type);
+
+/* number of floats used per-element for the multires of a customdata type */
+int CustomData_multires_type_totfloat(int type);
+
+/* returns the multires data for a layer matching name and type,
+ or NULL if no such layer found */
+float *CustomData_multires_get_data(struct CustomDataMultires *cdm, int type,
+ char *name);
+
+/* if layer matching type and name exists, free and replace its griddata
+ otherwise create the layer and set its griddata */
+void CustomData_multires_sync_layer(struct CustomDataMultires *cdm, int type,
+ char *name);
+
+/* insert a multires layer of the specified type and assign griddata */
+void CustomData_multires_add_layer_data(struct CustomDataMultires *cdm, int type,
+ char *name, float *griddata);
+
+/* insert a multires layer of the specified type int each
+ cdm in the array of length count */
+void CustomData_multires_add_layers(struct CustomDataMultires *cdm, int count,
+ int type, char *name);
+
+/* remove the multires layer with matching source name from the cdm
+ array of length count, returns 1 if succesful, 0 otherwise */
+int CustomData_multires_remove_layers(struct CustomDataMultires *cdm, int count,
+ int type, char *name);
+
+/* rename a layer matching type and old_name */
+void CustomData_multires_rename(struct CustomDataMultires *cdm, int type,
+ char *old_name, char *name);
+
/* External file storage */
void CustomData_external_add(struct CustomData *data,
@@ -296,4 +367,3 @@ void CustomData_external_reload(struct CustomData *data,
struct ID *id, CustomDataMask mask, int totelem);
#endif
-
diff --git a/source/blender/blenkernel/BKE_dmgrid.h b/source/blender/blenkernel/BKE_dmgrid.h
new file mode 100644
index 00000000000..fc1aa04f34b
--- /dev/null
+++ b/source/blender/blenkernel/BKE_dmgrid.h
@@ -0,0 +1,59 @@
+#ifndef BKE_DMGRID_H
+#define BKE_DMGRID_H
+
+struct CustomData;
+
+/* Each grid element can contain zero or more layers of coordinates,
+ paint masks, and normals; these numbers are stored in the GridKey
+
+ The name arrays are the unique names of the source customdata layer
+*/
+typedef struct GridKey {
+ int co;
+ int color;
+ int mask;
+ int no;
+
+ /* key to identify the source layer */
+ char (*color_names)[32];
+ char (*mask_names)[32];
+} GridKey;
+
+#define GRIDELEM_KEY_INIT(_key, _totco, _totcolor, _totmask, _totno) \
+ ((_key)->co = _totco, (_key)->color = _totcolor, \
+ (_key)->mask = _totmask, (_key)->no = _totno, \
+ (_key)->color_names = NULL, (_key)->mask_names = NULL)
+
+#define GRIDELEM_SIZE(_key) ((3*(_key)->co + 4*(_key)->color + (_key)->mask + 3*(_key)->no) * sizeof(float))
+#define GRIDELEM_INTERP_COUNT(_key) (3*(_key)->co + 4*(_key)->color + (_key)->mask)
+
+#define GRIDELEM_COLOR_OFFSET(_key) (3*(_key)->co*sizeof(float))
+#define GRIDELEM_MASK_OFFSET(_key) (GRIDELEM_COLOR_OFFSET(_key) + 4*(_key)->color*sizeof(float))
+#define GRIDELEM_NO_OFFSET(_key) (GRIDELEM_MASK_OFFSET(_key) + (_key)->mask*sizeof(float))
+
+#define GRIDELEM_AT(_grid, _elem, _key) ((struct DMGridData*)(((char*)(_grid)) + (_elem) * GRIDELEM_SIZE(_key)))
+#define GRIDELEM_INC(_grid, _inc, _key) ((_grid) = GRIDELEM_AT(_grid, _inc, _key))
+
+ /* I can't figure out how to cast this type without a typedef,
+ having the array length is useful to directly index layers */
+typedef float (*gridelem_f4)[4];
+#define GRIDELEM_CO(_grid, _key) ((float*)(_grid))
+#define GRIDELEM_COLOR(_grid, _key) ((gridelem_f4)((char*)(_grid) + GRIDELEM_COLOR_OFFSET(_key)))
+#define GRIDELEM_MASK(_grid, _key) ((float*)((char*)(_grid) + GRIDELEM_MASK_OFFSET(_key)))
+#define GRIDELEM_NO(_grid, _key) ((float*)((char*)(_grid) + GRIDELEM_NO_OFFSET(_key)))
+
+#define GRIDELEM_CO_AT(_grid, _elem, _key) GRIDELEM_CO(GRIDELEM_AT(_grid, _elem, _key), _key)
+#define GRIDELEM_COLOR_AT(_grid, _elem, _key) GRIDELEM_COLOR(GRIDELEM_AT(_grid, _elem, _key), _key)
+#define GRIDELEM_MASK_AT(_grid, _elem, _key) GRIDELEM_MASK(GRIDELEM_AT(_grid, _elem, _key), _key)
+#define GRIDELEM_NO_AT(_grid, _elem, _key) GRIDELEM_NO(GRIDELEM_AT(_grid, _elem, _key), _key)
+
+/* returns the active gridelem layer offset for either colors
+ or masks, -1 if not found */
+int gridelem_active_offset(struct CustomData *data, GridKey *gridkey, int type);
+
+typedef struct GridToFace {
+ int face;
+ char offset;
+} GridToFace;
+
+#endif
diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h
index fc0ab2eea1e..6e76f2d0924 100644
--- a/source/blender/blenkernel/BKE_multires.h
+++ b/source/blender/blenkernel/BKE_multires.h
@@ -31,6 +31,7 @@
#define BKE_MULTIRES_H
struct DerivedMesh;
+struct GridKey;
struct Mesh;
struct MFace;
struct Multires;
@@ -46,8 +47,9 @@ void multires_force_external_reload(struct Object *ob);
void multiresModifier_set_levels_from_disps(struct MultiresModifierData *mmd, struct Object *ob);
+/* note: gridkey can be NULL, will provide a context-sensitive default */
struct DerivedMesh *multires_dm_create_from_derived(struct MultiresModifierData*,
- int local_mmd, struct DerivedMesh*, struct Object *, int, int);
+ int local_mmd, struct DerivedMesh*, struct Object *, struct GridKey *, int, int);
struct MultiresModifierData *find_multires_modifier_before(struct Scene *scene,
struct ModifierData *lastmd);
@@ -63,6 +65,7 @@ int multiresModifier_reshapeFromDM(struct Scene *scene, struct MultiresModifierD
struct Object *ob, struct DerivedMesh *srcdm);
int multiresModifier_reshapeFromDeformMod(struct Scene *scene, struct MultiresModifierData *mmd,
struct Object *ob, struct ModifierData *md);
+void multiresModifier_base_apply(struct MultiresModifierData *mmd, struct Object *ob);
void multires_stitch_grids(struct Object *);
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 20742033a2e..6e8cb769be8 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -31,6 +31,9 @@
#include "DNA_vec_types.h"
struct Brush;
+struct CustomData;
+struct DMGridData;
+struct GridKey;
struct MFace;
struct MultireModifierData;
struct MVert;
@@ -58,31 +61,26 @@ void paint_brush_set(struct Paint *paint, struct Brush *br);
* however hiding faces is useful */
int paint_facesel_test(struct Object *ob);
-/* Session data (mode-specific) */
+void paint_refresh_mask_display(struct Object *ob);
+float paint_mask_from_gridelem(struct DMGridData *elem, struct GridKey *gridkey,
+ struct CustomData *vdata);
+float paint_mask_from_vertex(struct CustomData *vdata, int vertex_index,
+ int pmask_totlayer, int pmask_first_layer);
typedef struct SculptSession {
- struct ProjVert *projverts;
-
/* Mesh data (not copied) can come either directly from a Mesh, or from a MultiresDM */
struct MultiresModifierData *multires; /* Special handling for multires meshes */
struct MVert *mvert;
struct MFace *mface;
int totvert, totface;
float *face_normals;
- struct Object *ob;
struct KeyBlock *kb;
/* Mesh connectivity */
struct ListBase *fmap;
- /* PBVH acceleration structure */
- struct PBVH *pbvh;
-
- /* Used temporarily per-stroke */
- float *vertexcosnos;
-
- /* Partial redraw */
- int partial_redraw;
+ /* Area hiding */
+ ListBase hidden_areas;
/* Used to cache the render of the active texture */
unsigned int texcache_side, *texcache, texcache_actual;
@@ -93,13 +91,23 @@ typedef struct SculptSession {
struct SculptStroke *stroke;
struct StrokeCache *cache;
- struct GPUDrawObject *drawobject;
-
int modifiers_active;
rcti previous_r;
} SculptSession;
-void free_sculptsession(struct Object *ob);
+typedef struct PaintSession {
+ /* mode-specific data (just sculpt for now */
+ SculptSession *sculpt;
+
+ /* PBVH acceleration structure */
+ struct PBVH *pbvh;
+
+ /* Partial redraw */
+ int partial_redraw;
+} PaintSession;
+
+void create_paintsession(struct Object *ob);
+void free_paintsession(struct Object *ob);
#endif
diff --git a/source/blender/blenkernel/BKE_ptex.h b/source/blender/blenkernel/BKE_ptex.h
new file mode 100644
index 00000000000..6c3b197f202
--- /dev/null
+++ b/source/blender/blenkernel/BKE_ptex.h
@@ -0,0 +1,42 @@
+/*
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2010 by Nicholas Bishop
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+struct DerivedMesh;
+struct Mesh;
+struct MPtex;
+struct MPtexSubface;
+struct PtexTextureHandle;
+
+void ptex_elem_to_floats(int type, int channels, void *data, float *out);
+void ptex_elem_from_floats(int type, int channels, void *data, float *in);
+
+struct DerivedMesh *quad_dm_create_from_derived(struct DerivedMesh *dm);
+void ptex_subface_scale(struct MPtex *pt, struct MPtexSubface *subface, int ures, int vres);
+void ptex_layer_from_file(struct Mesh *me, struct PtexTextureHandle *ptex_texture);
+int ptex_layer_save_file(struct Mesh *me, const char *filename);
diff --git a/source/blender/blenkernel/BKE_subsurf.h b/source/blender/blenkernel/BKE_subsurf.h
index 940a0027d0b..6eede8643c1 100644
--- a/source/blender/blenkernel/BKE_subsurf.h
+++ b/source/blender/blenkernel/BKE_subsurf.h
@@ -32,6 +32,7 @@ struct DMGridAdjacency;
struct DMGridData;
struct DerivedMesh;
struct EditMesh;
+struct GridKey;
struct IndexNode;
struct ListBase;
struct Mesh;
@@ -49,6 +50,7 @@ struct _CCGVert;
struct DerivedMesh *subsurf_make_derived_from_derived(
struct DerivedMesh *dm,
struct SubsurfModifierData *smd,
+ struct GridKey *gridkey,
int useRenderParams, float (*vertCos)[3],
int isFinalCalc, int editMode);
@@ -80,6 +82,7 @@ typedef struct CCGDerivedMesh {
struct DMGridAdjacency *gridAdjacency;
int *gridOffset;
struct _CCGFace **gridFaces;
+ struct GridToFace *gridFaceMap;
struct {
struct MultiresModifierData *mmd;
diff --git a/source/blender/blenkernel/BKE_texture.h b/source/blender/blenkernel/BKE_texture.h
index 99bb8db44ed..269e65f508b 100644
--- a/source/blender/blenkernel/BKE_texture.h
+++ b/source/blender/blenkernel/BKE_texture.h
@@ -44,6 +44,7 @@ struct PluginTex;
struct PointDensity;
struct Tex;
struct TexMapping;
+struct TexResult;
struct VoxelData;
struct World;
@@ -112,5 +113,7 @@ struct VoxelData *BKE_copy_voxeldata(struct VoxelData *vd);
int BKE_texture_dependsOnTime(const struct Tex *texture);
+void get_texture_value(struct Tex *texture, float *tex_co, struct TexResult *texres);
+
#endif
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 1b6411e4f64..6e4c831575d 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -50,6 +50,7 @@ SET(INC
../../../intern/memutil
../../../intern/opennl/extern
../../../intern/smoke/extern
+ ../../../extern/ptex
../../../source/blender/windowmanager # XXX - BAD LEVEL CALL WM_api.h
${ZLIB_INC}
)
diff --git a/source/blender/blenkernel/SConscript b/source/blender/blenkernel/SConscript
index bbc66ac14ea..f9bc36dff1a 100644
--- a/source/blender/blenkernel/SConscript
+++ b/source/blender/blenkernel/SConscript
@@ -11,6 +11,7 @@ incs += ' #/intern/iksolver/extern ../blenloader'
incs += ' #/extern/bullet2/src'
incs += ' #/intern/opennl/extern #/intern/bsp/extern'
incs += ' ../gpu #/extern/glew/include'
+incs += ' #/extern/ptex'
incs += ' #/intern/smoke/extern'
incs += ' #/intern/audaspace/intern'
diff --git a/source/blender/blenkernel/intern/CCGSubSurf.c b/source/blender/blenkernel/intern/CCGSubSurf.c
index bbd68fb797b..131db2b843a 100644
--- a/source/blender/blenkernel/intern/CCGSubSurf.c
+++ b/source/blender/blenkernel/intern/CCGSubSurf.c
@@ -211,6 +211,8 @@ static CCGAllocatorIFC *_getStandardAllocatorIFC(void) {
static int VertDataEqual(float *a, float *b) {
return a[0]==b[0] && a[1]==b[1] && a[2]==b[2];
}
+
+#if 0
#define VertDataZero(av) { float *_a = (float*) av; _a[0] = _a[1] = _a[2] = 0.0f; }
#define VertDataCopy(av, bv) { float *_a = (float*) av, *_b = (float*) bv; _a[0] =_b[0]; _a[1] =_b[1]; _a[2] =_b[2]; }
#define VertDataAdd(av, bv) { float *_a = (float*) av, *_b = (float*) bv; _a[0]+=_b[0]; _a[1]+=_b[1]; _a[2]+=_b[2]; }
@@ -223,6 +225,51 @@ static int VertDataEqual(float *a, float *b) {
_t[1] = (_a[1]+_b[1]+_c[1]+_d[1])*.25; \
_t[2] = (_a[2]+_b[2]+_c[2]+_d[2])*.25; \
}
+#else
+
+CCG_INLINE void VertDataZero(void *a, const int n)
+{
+ int i;
+ for(i = 0; i < n; ++i)
+ ((float*)a)[i] = 0;
+}
+
+CCG_INLINE void VertDataCopy(void *a, void *b, int n)
+{
+ int i;
+ for(i = 0; i < n; ++i)
+ ((float*)a)[i] = ((float*)b)[i];
+}
+
+CCG_INLINE void VertDataAdd(void *a, void *b, int n)
+{
+ int i;
+ for(i = 0; i < n; ++i)
+ ((float*)a)[i] += ((float*)b)[i];
+}
+
+CCG_INLINE void VertDataSub(void *a, void *b, int n)
+{
+ int i;
+ for(i = 0; i < n; ++i)
+ ((float*)a)[i] -= ((float*)b)[i];
+}
+
+CCG_INLINE void VertDataMulN(void *a, float b, int n)
+{
+ int i;
+ for(i = 0; i < n; ++i)
+ ((float*)a)[i] *= b;
+}
+
+CCG_INLINE void VertDataAvg4(void *t, void *a, void *b, void *c, void *d, int n)
+{
+ int i;
+ for(i = 0; i < n; ++i)
+ ((float*)t)[i] = (((float*)a)[i] + ((float*)b)[i] + ((float*)c)[i] + ((float*)d)[i]) * 0.25f;
+}
+#endif
+
#define NormZero(av) { float *_a = (float*) av; _a[0] = _a[1] = _a[2] = 0.0f; }
#define NormCopy(av, bv) { float *_a = (float*) av, *_b = (float*) bv; _a[0] =_b[0]; _a[1] =_b[1]; _a[2] =_b[2]; }
#define NormAdd(av, bv) { float *_a = (float*) av, *_b = (float*) bv; _a[0]+=_b[0]; _a[1]+=_b[1]; _a[2]+=_b[2]; }
@@ -314,7 +361,7 @@ struct _CCGSubSurf {
void *q, *r;
- // data for calc vert normals
+ // data for calc vert normals
int calcVertNormals;
int normalDataOffset;
@@ -702,6 +749,15 @@ void ccgSubSurf_free(CCGSubSurf *ss) {
_ehash_free(ss->eMap, (EHEntryFreeFP) _edge_free, ss);
_ehash_free(ss->vMap, (EHEntryFreeFP) _vert_free, ss);
+ /* free gridkey */
+ if(ss->meshIFC.gridkey.color_names)
+ MEM_freeN(ss->meshIFC.gridkey.color_names);
+ if(ss->meshIFC.gridkey.mask_names)
+ MEM_freeN(ss->meshIFC.gridkey.mask_names);
+
+ ss->meshIFC.gridkey.color_names = NULL;
+ ss->meshIFC.gridkey.mask_names = NULL;
+
CCGSUBSURF_free(ss, ss);
if (allocatorIFC.release) {
@@ -899,13 +955,13 @@ CCGError ccgSubSurf_syncVert(CCGSubSurf *ss, CCGVertHDL vHDL, void *vertData, in
v = _ehash_lookupWithPrev(ss->vMap, vHDL, &prevp);
if (!v) {
v = _vert_new(vHDL, ss);
- VertDataCopy(_vert_getCo(v,0,ss->meshIFC.vertDataSize), vertData);
+ VertDataCopy(_vert_getCo(v,0,ss->meshIFC.vertDataSize), vertData, ss->meshIFC.finterpCount);
_ehash_insert(ss->vMap, (EHEntry*) v);
v->flags = Vert_eEffected|seamflag;
} else if (!VertDataEqual(vertData, _vert_getCo(v, 0, ss->meshIFC.vertDataSize)) || ((v->flags & Vert_eSeam) != seamflag)) {
int i, j;
- VertDataCopy(_vert_getCo(v,0,ss->meshIFC.vertDataSize), vertData);
+ VertDataCopy(_vert_getCo(v,0,ss->meshIFC.vertDataSize), vertData, ss->meshIFC.finterpCount);
v->flags = Vert_eEffected|seamflag;
for (i=0; i<v->numEdges; i++) {
@@ -928,13 +984,13 @@ CCGError ccgSubSurf_syncVert(CCGSubSurf *ss, CCGVertHDL vHDL, void *vertData, in
v = _ehash_lookupWithPrev(ss->oldVMap, vHDL, &prevp);
if (!v) {
v = _vert_new(vHDL, ss);
- VertDataCopy(_vert_getCo(v,0,ss->meshIFC.vertDataSize), vertData);
+ VertDataCopy(_vert_getCo(v,0,ss->meshIFC.vertDataSize), vertData, ss->meshIFC.finterpCount);
_ehash_insert(ss->vMap, (EHEntry*) v);
v->flags = Vert_eEffected|seamflag;
} else if (!VertDataEqual(vertData, _vert_getCo(v, 0, ss->meshIFC.vertDataSize)) || ((v->flags & Vert_eSeam) != seamflag)) {
*prevp = v->next;
_ehash_insert(ss->vMap, (EHEntry*) v);
- VertDataCopy(_vert_getCo(v,0,ss->meshIFC.vertDataSize), vertData);
+ VertDataCopy(_vert_getCo(v,0,ss->meshIFC.vertDataSize), vertData, ss->meshIFC.finterpCount);
v->flags = Vert_eEffected|Vert_eChanged|seamflag;
} else {
*prevp = v->next;
@@ -1313,7 +1369,7 @@ static void ccgSubSurf__calcVertNormals(CCGSubSurf *ss,
}
VertDataCopy((float*)((byte*)FACE_getCenterData(f) + normalDataOffset),
- FACE_getIFNo(f, lvl, S, 0, 0));
+ FACE_getIFNo(f, lvl, S, 0, 0), 3);
for (x=1; x<gridSize-1; x++)
NormCopy(FACE_getIENo(f, lvl, S, x),
@@ -1349,6 +1405,7 @@ static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
int nextLvl = curLvl+1;
int ptrIdx, cornerIdx, i;
int vertDataSize = ss->meshIFC.vertDataSize;
+ int finterpCount = ss->meshIFC.finterpCount;
void *q = ss->q, *r = ss->r;
#pragma omp parallel for private(ptrIdx) if(numEffectedF*edgeSize*edgeSize*4 >= CCG_OMP_LIMIT)
@@ -1370,7 +1427,7 @@ static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
void *co3 = FACE_getIFCo(f, curLvl, S, x+0, y+1);
void *co = FACE_getIFCo(f, nextLvl, S, fx, fy);
- VertDataAvg4(co, co0, co1, co2, co3);
+ VertDataAvg4(co, co0, co1, co2, co3, finterpCount);
}
}
}
@@ -1388,7 +1445,7 @@ static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
void *co3 = FACE_getIFCo(f, nextLvl, S, fx, 1);
void *co = FACE_getIECo(f, nextLvl, S, fx);
- VertDataAvg4(co, co0, co1, co2, co3);
+ VertDataAvg4(co, co0, co1, co2, co3, finterpCount);
}
/* interior face interior edge midpoints
@@ -1407,7 +1464,7 @@ static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
void *co3 = FACE_getIFCo(f, nextLvl, S, fx+1, fy);
void *co = FACE_getIFCo(f, nextLvl, S, fx, fy);
- VertDataAvg4(co, co0, co1, co2, co3);
+ VertDataAvg4(co, co0, co1, co2, co3, finterpCount);
}
}
@@ -1422,7 +1479,7 @@ static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
void *co3 = FACE_getIFCo(f, nextLvl, S, fx, fy+1);
void *co = FACE_getIFCo(f, nextLvl, S, fx, fy);
- VertDataAvg4(co, co0, co1, co2, co3);
+ VertDataAvg4(co, co0, co1, co2, co3, finterpCount);
}
}
}
@@ -1444,9 +1501,9 @@ static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
void *co1 = EDGE_getCo(e, curLvl, x+1);
void *co = EDGE_getCo(e, nextLvl, fx);
- VertDataCopy(co, co0);
- VertDataAdd(co, co1);
- VertDataMulN(co, 0.5);
+ VertDataCopy(co, co0, finterpCount);
+ VertDataAdd(co, co1, finterpCount);
+ VertDataMulN(co, 0.5, finterpCount);
}
} else {
for (x=0; x<edgeSize-1; x++) {
@@ -1456,25 +1513,25 @@ static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
void *co = EDGE_getCo(e, nextLvl, fx);
int numFaces = 0;
- VertDataCopy(q, co0);
- VertDataAdd(q, co1);
+ VertDataCopy(q, co0, finterpCount);
+ VertDataAdd(q, co1, finterpCount);
for (j=0; j<e->numFaces; j++) {
CCGFace *f = e->faces[j];
- VertDataAdd(q, _face_getIFCoEdge(f, e, nextLvl, fx, 1, subdivLevels, vertDataSize));
+ VertDataAdd(q, _face_getIFCoEdge(f, e, nextLvl, fx, 1, subdivLevels, vertDataSize), finterpCount);
numFaces++;
}
- VertDataMulN(q, 1.0f/(2.0f+numFaces));
+ VertDataMulN(q, 1.0f/(2.0f+numFaces), finterpCount);
- VertDataCopy(r, co0);
- VertDataAdd(r, co1);
- VertDataMulN(r, 0.5);
+ VertDataCopy(r, co0, finterpCount);
+ VertDataAdd(r, co1, finterpCount);
+ VertDataMulN(r, 0.5, finterpCount);
- VertDataCopy(co, q);
- VertDataSub(r, q);
- VertDataMulN(r, sharpness);
- VertDataAdd(co, r);
+ VertDataCopy(co, q, finterpCount);
+ VertDataSub(r, q, finterpCount);
+ VertDataMulN(r, sharpness, finterpCount);
+ VertDataAdd(co, r, finterpCount);
}
}
}
@@ -1518,51 +1575,51 @@ static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
seam = 0;
if (!v->numEdges) {
- VertDataCopy(nCo, co);
+ VertDataCopy(nCo, co, finterpCount);
} else if (_vert_isBoundary(v)) {
int numBoundary = 0;
- VertDataZero(r);
+ VertDataZero(r, finterpCount);
for (j=0; j<v->numEdges; j++) {
CCGEdge *e = v->edges[j];
if (_edge_isBoundary(e)) {
- VertDataAdd(r, _edge_getCoVert(e, v, curLvl, 1, vertDataSize));
+ VertDataAdd(r, _edge_getCoVert(e, v, curLvl, 1, vertDataSize), finterpCount);
numBoundary++;
}
}
- VertDataCopy(nCo, co);
- VertDataMulN(nCo, 0.75);
- VertDataMulN(r, 0.25f/numBoundary);
- VertDataAdd(nCo, r);
+ VertDataCopy(nCo, co, finterpCount);
+ VertDataMulN(nCo, 0.75, finterpCount);
+ VertDataMulN(r, 0.25f/numBoundary, finterpCount);
+ VertDataAdd(nCo, r, finterpCount);
} else {
int cornerIdx = (1 + (1<<(curLvl))) - 2;
int numEdges = 0, numFaces = 0;
- VertDataZero(q);
+ VertDataZero(q, finterpCount);
for (j=0; j<v->numFaces; j++) {
CCGFace *f = v->faces[j];
- VertDataAdd(q, FACE_getIFCo(f, nextLvl, _face_getVertIndex(f,v), cornerIdx, cornerIdx));
+ VertDataAdd(q, FACE_getIFCo(f, nextLvl, _face_getVertIndex(f,v), cornerIdx, cornerIdx), finterpCount);
numFaces++;
}
- VertDataMulN(q, 1.0f/numFaces);
- VertDataZero(r);
+ VertDataMulN(q, 1.0f/numFaces, finterpCount);
+ VertDataZero(r, finterpCount);
for (j=0; j<v->numEdges; j++) {
CCGEdge *e = v->edges[j];
- VertDataAdd(r, _edge_getCoVert(e, v, curLvl, 1,vertDataSize));
+ VertDataAdd(r, _edge_getCoVert(e, v, curLvl, 1,vertDataSize), finterpCount);
numEdges++;
}
- VertDataMulN(r, 1.0f/numEdges);
+ VertDataMulN(r, 1.0f/numEdges, finterpCount);
- VertDataCopy(nCo, co);
- VertDataMulN(nCo, numEdges-2.0f);
- VertDataAdd(nCo, q);
- VertDataAdd(nCo, r);
- VertDataMulN(nCo, 1.0f/numEdges);
+ VertDataCopy(nCo, co, finterpCount);
+ VertDataMulN(nCo, numEdges-2.0f, finterpCount);
+ VertDataAdd(nCo, q, finterpCount);
+ VertDataAdd(nCo, r, finterpCount);
+ VertDataMulN(nCo, 1.0f/numEdges, finterpCount);
}
if ((sharpCount>1 && v->numFaces) || seam) {
- VertDataZero(q);
+ VertDataZero(q, finterpCount);
if (seam) {
avgSharpness = 1.0f;
@@ -1576,32 +1633,32 @@ static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
if (seam) {
if (_edge_isBoundary(e))
- VertDataAdd(q, _edge_getCoVert(e, v, curLvl, 1, vertDataSize));
+ VertDataAdd(q, _edge_getCoVert(e, v, curLvl, 1, vertDataSize), finterpCount);
} else if (sharpness != 0.0) {
- VertDataAdd(q, _edge_getCoVert(e, v, curLvl, 1, vertDataSize));
+ VertDataAdd(q, _edge_getCoVert(e, v, curLvl, 1, vertDataSize), finterpCount);
}
}
- VertDataMulN(q, (float) 1/sharpCount);
+ VertDataMulN(q, (float) 1/sharpCount, finterpCount);
if (sharpCount!=2 || allSharp) {
// q = q + (co-q)*avgSharpness
- VertDataCopy(r, co);
- VertDataSub(r, q);
- VertDataMulN(r, avgSharpness);
- VertDataAdd(q, r);
+ VertDataCopy(r, co, finterpCount);
+ VertDataSub(r, q, finterpCount);
+ VertDataMulN(r, avgSharpness, finterpCount);
+ VertDataAdd(q, r, finterpCount);
}
// r = co*.75 + q*.25
- VertDataCopy(r, co);
- VertDataMulN(r, .75);
- VertDataMulN(q, .25);
- VertDataAdd(r, q);
+ VertDataCopy(r, co, finterpCount);
+ VertDataMulN(r, .75, finterpCount);
+ VertDataMulN(q, .25, finterpCount);
+ VertDataAdd(r, q, finterpCount);
// nCo = nCo + (r-nCo)*avgSharpness
- VertDataSub(r, nCo);
- VertDataMulN(r, avgSharpness);
- VertDataAdd(nCo, r);
+ VertDataSub(r, nCo, finterpCount);
+ VertDataMulN(r, avgSharpness, finterpCount);
+ VertDataAdd(nCo, r, finterpCount);
}
}
@@ -1634,13 +1691,13 @@ static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
int fx = x*2;
void *co = EDGE_getCo(e, curLvl, x);
void *nCo = EDGE_getCo(e, nextLvl, fx);
- VertDataCopy(r, EDGE_getCo(e, curLvl, x-1));
- VertDataAdd(r, EDGE_getCo(e, curLvl, x+1));
- VertDataMulN(r, 0.5);
- VertDataCopy(nCo, co);
- VertDataMulN(nCo, 0.75);
- VertDataMulN(r, 0.25);
- VertDataAdd(nCo, r);
+ VertDataCopy(r, EDGE_getCo(e, curLvl, x-1), finterpCount);
+ VertDataAdd(r, EDGE_getCo(e, curLvl, x+1), finterpCount);
+ VertDataMulN(r, 0.5, finterpCount);
+ VertDataCopy(nCo, co, finterpCount);
+ VertDataMulN(nCo, 0.75, finterpCount);
+ VertDataMulN(r, 0.25, finterpCount);
+ VertDataAdd(nCo, r, finterpCount);
}
} else {
for (x=1; x<edgeSize-1; x++) {
@@ -1649,37 +1706,37 @@ static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
void *nCo = EDGE_getCo(e, nextLvl, fx);
int numFaces = 0;
- VertDataZero(q);
- VertDataZero(r);
- VertDataAdd(r, EDGE_getCo(e, curLvl, x-1));
- VertDataAdd(r, EDGE_getCo(e, curLvl, x+1));
+ VertDataZero(q, finterpCount);
+ VertDataZero(r, finterpCount);
+ VertDataAdd(r, EDGE_getCo(e, curLvl, x-1), finterpCount);
+ VertDataAdd(r, EDGE_getCo(e, curLvl, x+1), finterpCount);
for (j=0; j<e->numFaces; j++) {
CCGFace *f = e->faces[j];
- VertDataAdd(q, _face_getIFCoEdge(f, e, nextLvl, fx-1, 1, subdivLevels, vertDataSize));
- VertDataAdd(q, _face_getIFCoEdge(f, e, nextLvl, fx+1, 1, subdivLevels, vertDataSize));
+ VertDataAdd(q, _face_getIFCoEdge(f, e, nextLvl, fx-1, 1, subdivLevels, vertDataSize), finterpCount);
+ VertDataAdd(q, _face_getIFCoEdge(f, e, nextLvl, fx+1, 1, subdivLevels, vertDataSize), finterpCount);
- VertDataAdd(r, _face_getIFCoEdge(f, e, curLvl, x, 1, subdivLevels, vertDataSize));
+ VertDataAdd(r, _face_getIFCoEdge(f, e, curLvl, x, 1, subdivLevels, vertDataSize), finterpCount);
numFaces++;
}
- VertDataMulN(q, 1.0/(numFaces*2.0f));
- VertDataMulN(r, 1.0/(2.0f + numFaces));
+ VertDataMulN(q, 1.0/(numFaces*2.0f), finterpCount);
+ VertDataMulN(r, 1.0/(2.0f + numFaces), finterpCount);
- VertDataCopy(nCo, co);
- VertDataMulN(nCo, (float) numFaces);
- VertDataAdd(nCo, q);
- VertDataAdd(nCo, r);
- VertDataMulN(nCo, 1.0f/(2+numFaces));
+ VertDataCopy(nCo, co, finterpCount);
+ VertDataMulN(nCo, (float) numFaces, finterpCount);
+ VertDataAdd(nCo, q, finterpCount);
+ VertDataAdd(nCo, r, finterpCount);
+ VertDataMulN(nCo, 1.0f/(2+numFaces), finterpCount);
if (sharpCount==2) {
- VertDataCopy(q, co);
- VertDataMulN(q, 6.0f);
- VertDataAdd(q, EDGE_getCo(e, curLvl, x-1));
- VertDataAdd(q, EDGE_getCo(e, curLvl, x+1));
- VertDataMulN(q, 1/8.0f);
-
- VertDataSub(q, nCo);
- VertDataMulN(q, avgSharpness);
- VertDataAdd(nCo, q);
+ VertDataCopy(q, co, finterpCount);
+ VertDataMulN(q, 6.0f, finterpCount);
+ VertDataAdd(q, EDGE_getCo(e, curLvl, x-1), finterpCount);
+ VertDataAdd(q, EDGE_getCo(e, curLvl, x+1), finterpCount);
+ VertDataMulN(q, 1/8.0f, finterpCount);
+
+ VertDataSub(q, nCo, finterpCount);
+ VertDataMulN(q, avgSharpness, finterpCount);
+ VertDataAdd(nCo, q, finterpCount);
}
}
}
@@ -1705,21 +1762,21 @@ static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
* o old interior edge points
* o new interior face midpoints
*/
- VertDataZero(q);
+ VertDataZero(q, finterpCount);
for (S=0; S<f->numVerts; S++) {
- VertDataAdd(q, FACE_getIFCo(f, nextLvl, S, 1, 1));
+ VertDataAdd(q, FACE_getIFCo(f, nextLvl, S, 1, 1), finterpCount);
}
- VertDataMulN(q, 1.0f/f->numVerts);
- VertDataZero(r);
+ VertDataMulN(q, 1.0f/f->numVerts, finterpCount);
+ VertDataZero(r, finterpCount);
for (S=0; S<f->numVerts; S++) {
- VertDataAdd(r, FACE_getIECo(f, curLvl, S, 1));
+ VertDataAdd(r, FACE_getIECo(f, curLvl, S, 1), finterpCount);
}
- VertDataMulN(r, 1.0f/f->numVerts);
+ VertDataMulN(r, 1.0f/f->numVerts, finterpCount);
- VertDataMulN(FACE_getCenterData(f), f->numVerts-2.0f);
- VertDataAdd(FACE_getCenterData(f), q);
- VertDataAdd(FACE_getCenterData(f), r);
- VertDataMulN(FACE_getCenterData(f), 1.0f/f->numVerts);
+ VertDataMulN(FACE_getCenterData(f), f->numVerts-2.0f, finterpCount);
+ VertDataAdd(FACE_getCenterData(f), q, finterpCount);
+ VertDataAdd(FACE_getCenterData(f), r, finterpCount);
+ VertDataMulN(FACE_getCenterData(f), 1.0f/f->numVerts, finterpCount);
for (S=0; S<f->numVerts; S++) {
/* interior face shift
@@ -1735,19 +1792,19 @@ static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
void *nCo = FACE_getIFCo(f, nextLvl, S, fx, fy);
VertDataAvg4(q, FACE_getIFCo(f, nextLvl, S, fx-1, fy-1),
- FACE_getIFCo(f, nextLvl, S, fx+1, fy-1),
- FACE_getIFCo(f, nextLvl, S, fx+1, fy+1),
- FACE_getIFCo(f, nextLvl, S, fx-1, fy+1));
+ FACE_getIFCo(f, nextLvl, S, fx+1, fy-1),
+ FACE_getIFCo(f, nextLvl, S, fx+1, fy+1),
+ FACE_getIFCo(f, nextLvl, S, fx-1, fy+1), finterpCount);
VertDataAvg4(r, FACE_getIFCo(f, nextLvl, S, fx-1, fy+0),
- FACE_getIFCo(f, nextLvl, S, fx+1, fy+0),
- FACE_getIFCo(f, nextLvl, S, fx+0, fy-1),
- FACE_getIFCo(f, nextLvl, S, fx+0, fy+1));
-
- VertDataCopy(nCo, co);
- VertDataSub(nCo, q);
- VertDataMulN(nCo, 0.25f);
- VertDataAdd(nCo, r);
+ FACE_getIFCo(f, nextLvl, S, fx+1, fy+0),
+ FACE_getIFCo(f, nextLvl, S, fx+0, fy-1),
+ FACE_getIFCo(f, nextLvl, S, fx+0, fy+1), finterpCount);
+
+ VertDataCopy(nCo, co, finterpCount);
+ VertDataSub(nCo, q, finterpCount);
+ VertDataMulN(nCo, 0.25f, finterpCount);
+ VertDataAdd(nCo, r, finterpCount);
}
}
@@ -1762,19 +1819,19 @@ static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
void *nCo = FACE_getIECo(f, nextLvl, S, fx);
VertDataAvg4(q, FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 1, fx-1),
- FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 1, fx+1),
- FACE_getIFCo(f, nextLvl, S, fx+1, +1),
- FACE_getIFCo(f, nextLvl, S, fx-1, +1));
+ FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 1, fx+1),
+ FACE_getIFCo(f, nextLvl, S, fx+1, +1),
+ FACE_getIFCo(f, nextLvl, S, fx-1, +1), finterpCount);
VertDataAvg4(r, FACE_getIECo(f, nextLvl, S, fx-1),
- FACE_getIECo(f, nextLvl, S, fx+1),
- FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 1, fx),
- FACE_getIFCo(f, nextLvl, S, fx, 1));
-
- VertDataCopy(nCo, co);
- VertDataSub(nCo, q);
- VertDataMulN(nCo, 0.25f);
- VertDataAdd(nCo, r);
+ FACE_getIECo(f, nextLvl, S, fx+1),
+ FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 1, fx),
+ FACE_getIFCo(f, nextLvl, S, fx, 1), finterpCount);
+
+ VertDataCopy(nCo, co, finterpCount);
+ VertDataSub(nCo, q, finterpCount);
+ VertDataMulN(nCo, 0.25f, finterpCount);
+ VertDataAdd(nCo, r, finterpCount);
}
}
}
@@ -1794,8 +1851,8 @@ static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
#pragma omp parallel for private(i) if(numEffectedF*edgeSize*edgeSize*4 >= CCG_OMP_LIMIT)
for (i=0; i<numEffectedE; i++) {
CCGEdge *e = effectedE[i];
- VertDataCopy(EDGE_getCo(e, nextLvl, 0), VERT_getCo(e->v0, nextLvl));
- VertDataCopy(EDGE_getCo(e, nextLvl, edgeSize-1), VERT_getCo(e->v1, nextLvl));
+ VertDataCopy(EDGE_getCo(e, nextLvl, 0), VERT_getCo(e->v0, nextLvl), finterpCount);
+ VertDataCopy(EDGE_getCo(e, nextLvl, edgeSize-1), VERT_getCo(e->v1, nextLvl), finterpCount);
}
#pragma omp parallel for private(i) if(numEffectedF*edgeSize*edgeSize*4 >= CCG_OMP_LIMIT)
@@ -1807,19 +1864,19 @@ static void ccgSubSurf__calcSubdivLevel(CCGSubSurf *ss,
CCGEdge *e = FACE_getEdges(f)[S];
CCGEdge *prevE = FACE_getEdges(f)[(S+f->numVerts-1)%f->numVerts];
- VertDataCopy(FACE_getIFCo(f, nextLvl, S, 0, 0), FACE_getCenterData(f));
- VertDataCopy(FACE_getIECo(f, nextLvl, S, 0), FACE_getCenterData(f));
- VertDataCopy(FACE_getIFCo(f, nextLvl, S, cornerIdx, cornerIdx), VERT_getCo(FACE_getVerts(f)[S], nextLvl));
- VertDataCopy(FACE_getIECo(f, nextLvl, S, cornerIdx), EDGE_getCo(FACE_getEdges(f)[S], nextLvl, cornerIdx));
+ VertDataCopy(FACE_getIFCo(f, nextLvl, S, 0, 0), FACE_getCenterData(f), finterpCount);
+ VertDataCopy(FACE_getIECo(f, nextLvl, S, 0), FACE_getCenterData(f), finterpCount);
+ VertDataCopy(FACE_getIFCo(f, nextLvl, S, cornerIdx, cornerIdx), VERT_getCo(FACE_getVerts(f)[S], nextLvl), finterpCount);
+ VertDataCopy(FACE_getIECo(f, nextLvl, S, cornerIdx), EDGE_getCo(FACE_getEdges(f)[S], nextLvl, cornerIdx), finterpCount);
for (x=1; x<gridSize-1; x++) {
void *co = FACE_getIECo(f, nextLvl, S, x);
- VertDataCopy(FACE_getIFCo(f, nextLvl, S, x, 0), co);
- VertDataCopy(FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 0, x), co);
+ VertDataCopy(FACE_getIFCo(f, nextLvl, S, x, 0), co, finterpCount);
+ VertDataCopy(FACE_getIFCo(f, nextLvl, (S+1)%f->numVerts, 0, x), co, finterpCount);
}
for (x=0; x<gridSize-1; x++) {
int eI = gridSize-1-x;
- VertDataCopy(FACE_getIFCo(f, nextLvl, S, cornerIdx, x), _edge_getCoVert(e, FACE_getVerts(f)[S], nextLvl, eI,vertDataSize));
- VertDataCopy(FACE_getIFCo(f, nextLvl, S, x, cornerIdx), _edge_getCoVert(prevE, FACE_getVerts(f)[S], nextLvl, eI,vertDataSize));
+ VertDataCopy(FACE_getIFCo(f, nextLvl, S, cornerIdx, x), _edge_getCoVert(e, FACE_getVerts(f)[S], nextLvl, eI,vertDataSize), finterpCount);
+ VertDataCopy(FACE_getIFCo(f, nextLvl, S, x, cornerIdx), _edge_getCoVert(prevE, FACE_getVerts(f)[S], nextLvl, eI,vertDataSize), finterpCount);
}
}
}
@@ -1833,6 +1890,7 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
int numEffectedV, numEffectedE, numEffectedF;
int subdivLevels = ss->subdivLevels;
int vertDataSize = ss->meshIFC.vertDataSize;
+ int finterpCount = ss->meshIFC.finterpCount;
int i, j, ptrIdx, S;
int curLvl, nextLvl;
void *q = ss->q, *r = ss->r;
@@ -1872,11 +1930,11 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
for (ptrIdx=0; ptrIdx<numEffectedF; ptrIdx++) {
CCGFace *f = effectedF[ptrIdx];
void *co = FACE_getCenterData(f);
- VertDataZero(co);
+ VertDataZero(co, finterpCount);
for (i=0; i<f->numVerts; i++) {
- VertDataAdd(co, VERT_getCo(FACE_getVerts(f)[i], curLvl));
+ VertDataAdd(co, VERT_getCo(FACE_getVerts(f)[i], curLvl), finterpCount);
}
- VertDataMulN(co, 1.0f/f->numVerts);
+ VertDataMulN(co, 1.0f/f->numVerts, finterpCount);
f->flags = 0;
}
@@ -1886,28 +1944,28 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
float sharpness = EDGE_getSharpness(e, curLvl);
if (_edge_isBoundary(e) || sharpness>=1.0) {
- VertDataCopy(co, VERT_getCo(e->v0, curLvl));
- VertDataAdd(co, VERT_getCo(e->v1, curLvl));
- VertDataMulN(co, 0.5f);
+ VertDataCopy(co, VERT_getCo(e->v0, curLvl), finterpCount);
+ VertDataAdd(co, VERT_getCo(e->v1, curLvl), finterpCount);
+ VertDataMulN(co, 0.5f, finterpCount);
} else {
int numFaces = 0;
- VertDataCopy(q, VERT_getCo(e->v0, curLvl));
- VertDataAdd(q, VERT_getCo(e->v1, curLvl));
+ VertDataCopy(q, VERT_getCo(e->v0, curLvl), finterpCount);
+ VertDataAdd(q, VERT_getCo(e->v1, curLvl), finterpCount);
for (i=0; i<e->numFaces; i++) {
CCGFace *f = e->faces[i];
- VertDataAdd(q, FACE_getCenterData(f));
+ VertDataAdd(q, FACE_getCenterData(f), finterpCount);
numFaces++;
}
- VertDataMulN(q, 1.0f/(2.0f+numFaces));
+ VertDataMulN(q, 1.0f/(2.0f+numFaces), finterpCount);
- VertDataCopy(r, VERT_getCo(e->v0, curLvl));
- VertDataAdd(r, VERT_getCo(e->v1, curLvl));
- VertDataMulN(r, 0.5f);
+ VertDataCopy(r, VERT_getCo(e->v0, curLvl), finterpCount);
+ VertDataAdd(r, VERT_getCo(e->v1, curLvl), finterpCount);
+ VertDataMulN(r, 0.5f, finterpCount);
- VertDataCopy(co, q);
- VertDataSub(r, q);
- VertDataMulN(r, sharpness);
- VertDataAdd(co, r);
+ VertDataCopy(co, q, finterpCount);
+ VertDataSub(r, q, finterpCount);
+ VertDataMulN(r, sharpness, finterpCount);
+ VertDataAdd(co, r, finterpCount);
}
// edge flags cleared later
@@ -1946,49 +2004,49 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
seam = 0;
if (!v->numEdges) {
- VertDataCopy(nCo, co);
+ VertDataCopy(nCo, co, finterpCount);
} else if (_vert_isBoundary(v)) {
int numBoundary = 0;
- VertDataZero(r);
+ VertDataZero(r, finterpCount);
for (i=0; i<v->numEdges; i++) {
CCGEdge *e = v->edges[i];
if (_edge_isBoundary(e)) {
- VertDataAdd(r, VERT_getCo(_edge_getOtherVert(e, v), curLvl));
+ VertDataAdd(r, VERT_getCo(_edge_getOtherVert(e, v), curLvl), finterpCount);
numBoundary++;
}
}
- VertDataCopy(nCo, co);
- VertDataMulN(nCo, 0.75);
- VertDataMulN(r, 0.25f/numBoundary);
- VertDataAdd(nCo, r);
+ VertDataCopy(nCo, co, finterpCount);
+ VertDataMulN(nCo, 0.75, finterpCount);
+ VertDataMulN(r, 0.25f/numBoundary, finterpCount);
+ VertDataAdd(nCo, r, finterpCount);
} else {
int numEdges = 0, numFaces = 0;
- VertDataZero(q);
+ VertDataZero(q, finterpCount);
for (i=0; i<v->numFaces; i++) {
CCGFace *f = v->faces[i];
- VertDataAdd(q, FACE_getCenterData(f));
+ VertDataAdd(q, FACE_getCenterData(f), finterpCount);
numFaces++;
}
- VertDataMulN(q, 1.0f/numFaces);
- VertDataZero(r);
+ VertDataMulN(q, 1.0f/numFaces, finterpCount);
+ VertDataZero(r, finterpCount);
for (i=0; i<v->numEdges; i++) {
CCGEdge *e = v->edges[i];
- VertDataAdd(r, VERT_getCo(_edge_getOtherVert(e, v), curLvl));
+ VertDataAdd(r, VERT_getCo(_edge_getOtherVert(e, v), curLvl), finterpCount);
numEdges++;
}
- VertDataMulN(r, 1.0f/numEdges);
+ VertDataMulN(r, 1.0f/numEdges, finterpCount);
- VertDataCopy(nCo, co);
- VertDataMulN(nCo, numEdges-2.0f);
- VertDataAdd(nCo, q);
- VertDataAdd(nCo, r);
- VertDataMulN(nCo, 1.0f/numEdges);
+ VertDataCopy(nCo, co, finterpCount);
+ VertDataMulN(nCo, numEdges-2.0f, finterpCount);
+ VertDataAdd(nCo, q, finterpCount);
+ VertDataAdd(nCo, r, finterpCount);
+ VertDataMulN(nCo, 1.0f/numEdges, finterpCount);
}
if (sharpCount>1 || seam) {
- VertDataZero(q);
+ VertDataZero(q, finterpCount);
if (seam) {
avgSharpness = 1.0f;
@@ -2003,34 +2061,34 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
if (seam) {
if (_edge_isBoundary(e)) {
CCGVert *oV = _edge_getOtherVert(e, v);
- VertDataAdd(q, VERT_getCo(oV, curLvl));
+ VertDataAdd(q, VERT_getCo(oV, curLvl), finterpCount);
}
} else if (sharpness != 0.0) {
CCGVert *oV = _edge_getOtherVert(e, v);
- VertDataAdd(q, VERT_getCo(oV, curLvl));
+ VertDataAdd(q, VERT_getCo(oV, curLvl), finterpCount);
}
}
- VertDataMulN(q, (float) 1/sharpCount);
+ VertDataMulN(q, (float) 1/sharpCount, finterpCount);
if (sharpCount!=2 || allSharp) {
// q = q + (co-q)*avgSharpness
- VertDataCopy(r, co);
- VertDataSub(r, q);
- VertDataMulN(r, avgSharpness);
- VertDataAdd(q, r);
+ VertDataCopy(r, co, finterpCount);
+ VertDataSub(r, q, finterpCount);
+ VertDataMulN(r, avgSharpness, finterpCount);
+ VertDataAdd(q, r, finterpCount);
}
// r = co*.75 + q*.25
- VertDataCopy(r, co);
- VertDataMulN(r, .75);
- VertDataMulN(q, .25);
- VertDataAdd(r, q);
+ VertDataCopy(r, co, finterpCount);
+ VertDataMulN(r, .75, finterpCount);
+ VertDataMulN(q, .25, finterpCount);
+ VertDataAdd(r, q, finterpCount);
// nCo = nCo + (r-nCo)*avgSharpness
- VertDataSub(r, nCo);
- VertDataMulN(r, avgSharpness);
- VertDataAdd(nCo, r);
+ VertDataSub(r, nCo, finterpCount);
+ VertDataMulN(r, avgSharpness, finterpCount);
+ VertDataAdd(nCo, r, finterpCount);
}
// vert flags cleared later
@@ -2058,8 +2116,8 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
for (i=0; i<numEffectedE; i++) {
CCGEdge *e = effectedE[i];
- VertDataCopy(EDGE_getCo(e, nextLvl, 0), VERT_getCo(e->v0, nextLvl));
- VertDataCopy(EDGE_getCo(e, nextLvl, 2), VERT_getCo(e->v1, nextLvl));
+ VertDataCopy(EDGE_getCo(e, nextLvl, 0), VERT_getCo(e->v0, nextLvl), finterpCount);
+ VertDataCopy(EDGE_getCo(e, nextLvl, 2), VERT_getCo(e->v1, nextLvl), finterpCount);
}
for (i=0; i<numEffectedF; i++) {
CCGFace *f = effectedF[i];
@@ -2067,13 +2125,13 @@ static void ccgSubSurf__sync(CCGSubSurf *ss) {
CCGEdge *e = FACE_getEdges(f)[S];
CCGEdge *prevE = FACE_getEdges(f)[(S+f->numVerts-1)%f->numVerts];
- VertDataCopy(FACE_getIFCo(f, nextLvl, S, 0, 0), FACE_getCenterData(f));
- VertDataCopy(FACE_getIECo(f, nextLvl, S, 0), FACE_getCenterData(f));
- VertDataCopy(FACE_getIFCo(f, nextLvl, S, 1, 1), VERT_getCo(FACE_getVerts(f)[S], nextLvl));
- VertDataCopy(FACE_getIECo(f, nextLvl, S, 1), EDGE_getCo(FACE_getEdges(f)[S], nextLvl, 1));
+ VertDataCopy(FACE_getIFCo(f, nextLvl, S, 0, 0), FACE_getCenterData(f), finterpCount);
+ VertDataCopy(FACE_getIECo(f, nextLvl, S, 0), FACE_getCenterData(f), finterpCount);
+ VertDataCopy(FACE_getIFCo(f, nextLvl, S, 1, 1), VERT_getCo(FACE_getVerts(f)[S], nextLvl), finterpCount);
+ VertDataCopy(FACE_getIECo(f, nextLvl, S, 1), EDGE_getCo(FACE_getEdges(f)[S], nextLvl, 1), finterpCount);
- VertDataCopy(FACE_getIFCo(f, nextLvl, S, 1, 0), _edge_getCoVert(e, FACE_getVerts(f)[S], nextLvl, 1, vertDataSize));
- VertDataCopy(FACE_getIFCo(f, nextLvl, S, 0, 1), _edge_getCoVert(prevE, FACE_getVerts(f)[S], nextLvl, 1, vertDataSize));
+ VertDataCopy(FACE_getIFCo(f, nextLvl, S, 1, 0), _edge_getCoVert(e, FACE_getVerts(f)[S], nextLvl, 1, vertDataSize), finterpCount);
+ VertDataCopy(FACE_getIFCo(f, nextLvl, S, 0, 1), _edge_getCoVert(prevE, FACE_getVerts(f)[S], nextLvl, 1, vertDataSize), finterpCount);
}
}
@@ -2181,6 +2239,7 @@ CCGError ccgSubSurf_updateFromFaces(CCGSubSurf *ss, int lvl, CCGFace **effectedF
{
int i, S, x, gridSize, cornerIdx, subdivLevels;
int vertDataSize = ss->meshIFC.vertDataSize, freeF;
+ int finterpCount = ss->meshIFC.finterpCount;
subdivLevels = ss->subdivLevels;
lvl = (lvl)? lvl: subdivLevels;
@@ -2196,16 +2255,16 @@ CCGError ccgSubSurf_updateFromFaces(CCGSubSurf *ss, int lvl, CCGFace **effectedF
CCGEdge *e = FACE_getEdges(f)[S];
CCGEdge *prevE = FACE_getEdges(f)[(S+f->numVerts-1)%f->numVerts];
- VertDataCopy(FACE_getCenterData(f), FACE_getIFCo(f, lvl, S, 0, 0));
- VertDataCopy(VERT_getCo(FACE_getVerts(f)[S], lvl), FACE_getIFCo(f, lvl, S, cornerIdx, cornerIdx));
+ VertDataCopy(FACE_getCenterData(f), FACE_getIFCo(f, lvl, S, 0, 0), finterpCount);
+ VertDataCopy(VERT_getCo(FACE_getVerts(f)[S], lvl), FACE_getIFCo(f, lvl, S, cornerIdx, cornerIdx), finterpCount);
for (x=0; x<gridSize; x++)
- VertDataCopy(FACE_getIECo(f, lvl, S, x), FACE_getIFCo(f, lvl, S, x, 0));
+ VertDataCopy(FACE_getIECo(f, lvl, S, x), FACE_getIFCo(f, lvl, S, x, 0), finterpCount);
for (x=0; x<gridSize; x++) {
int eI = gridSize-1-x;
- VertDataCopy(_edge_getCoVert(e, FACE_getVerts(f)[S], lvl, eI,vertDataSize), FACE_getIFCo(f, lvl, S, cornerIdx, x));
- VertDataCopy(_edge_getCoVert(prevE, FACE_getVerts(f)[S], lvl, eI,vertDataSize), FACE_getIFCo(f, lvl, S, x, cornerIdx));
+ VertDataCopy(_edge_getCoVert(e, FACE_getVerts(f)[S], lvl, eI,vertDataSize), FACE_getIFCo(f, lvl, S, cornerIdx, x), finterpCount);
+ VertDataCopy(_edge_getCoVert(prevE, FACE_getVerts(f)[S], lvl, eI,vertDataSize), FACE_getIFCo(f, lvl, S, x, cornerIdx), finterpCount);
}
}
}
@@ -2220,6 +2279,7 @@ CCGError ccgSubSurf_updateToFaces(CCGSubSurf *ss, int lvl, CCGFace **effectedF,
{
int i, S, x, gridSize, cornerIdx, subdivLevels;
int vertDataSize = ss->meshIFC.vertDataSize, freeF;
+ int finterpCount = ss->meshIFC.finterpCount;
subdivLevels = ss->subdivLevels;
lvl = (lvl)? lvl: subdivLevels;
@@ -2238,17 +2298,17 @@ CCGError ccgSubSurf_updateToFaces(CCGSubSurf *ss, int lvl, CCGFace **effectedF,
for (x=0; x<gridSize; x++) {
int eI = gridSize-1-x;
- VertDataCopy(FACE_getIFCo(f, lvl, S, cornerIdx, x), _edge_getCoVert(e, FACE_getVerts(f)[S], lvl, eI,vertDataSize));
- VertDataCopy(FACE_getIFCo(f, lvl, S, x, cornerIdx), _edge_getCoVert(prevE, FACE_getVerts(f)[S], lvl, eI,vertDataSize));
+ VertDataCopy(FACE_getIFCo(f, lvl, S, cornerIdx, x), _edge_getCoVert(e, FACE_getVerts(f)[S], lvl, eI,vertDataSize), finterpCount);
+ VertDataCopy(FACE_getIFCo(f, lvl, S, x, cornerIdx), _edge_getCoVert(prevE, FACE_getVerts(f)[S], lvl, eI,vertDataSize), finterpCount);
}
for (x=1; x<gridSize-1; x++) {
- VertDataCopy(FACE_getIFCo(f, lvl, S, 0, x), FACE_getIECo(f, lvl, prevS, x));
- VertDataCopy(FACE_getIFCo(f, lvl, S, x, 0), FACE_getIECo(f, lvl, S, x));
+ VertDataCopy(FACE_getIFCo(f, lvl, S, 0, x), FACE_getIECo(f, lvl, prevS, x), finterpCount);
+ VertDataCopy(FACE_getIFCo(f, lvl, S, x, 0), FACE_getIECo(f, lvl, S, x), finterpCount);
}
- VertDataCopy(FACE_getIFCo(f, lvl, S, 0, 0), FACE_getCenterData(f));
- VertDataCopy(FACE_getIFCo(f, lvl, S, cornerIdx, cornerIdx), VERT_getCo(FACE_getVerts(f)[S], lvl));
+ VertDataCopy(FACE_getIFCo(f, lvl, S, 0, 0), FACE_getCenterData(f), finterpCount);
+ VertDataCopy(FACE_getIFCo(f, lvl, S, cornerIdx, cornerIdx), VERT_getCo(FACE_getVerts(f)[S], lvl), finterpCount);
}
}
@@ -2266,6 +2326,7 @@ CCGError ccgSubSurf_stitchFaces(CCGSubSurf *ss, int lvl, CCGFace **effectedF, in
int numEffectedV, numEffectedE, freeF;
int i, S, x, gridSize, cornerIdx, subdivLevels, edgeSize;
int vertDataSize = ss->meshIFC.vertDataSize;
+ int finterpCount = ss->meshIFC.finterpCount;
subdivLevels = ss->subdivLevels;
lvl = (lvl)? lvl: subdivLevels;
@@ -2280,47 +2341,47 @@ CCGError ccgSubSurf_stitchFaces(CCGSubSurf *ss, int lvl, CCGFace **effectedF, in
/* zero */
for (i=0; i<numEffectedV; i++) {
CCGVert *v = effectedV[i];
- VertDataZero(VERT_getCo(v, lvl));
+ VertDataZero(VERT_getCo(v, lvl), finterpCount);
}
for (i=0; i<numEffectedE; i++) {
CCGEdge *e = effectedE[i];
for (x=0; x<edgeSize; x++)
- VertDataZero(EDGE_getCo(e, lvl, x));
+ VertDataZero(EDGE_getCo(e, lvl, x), finterpCount);
}
/* add */
for (i=0; i<numEffectedF; i++) {
CCGFace *f = effectedF[i];
- VertDataZero(FACE_getCenterData(f));
+ VertDataZero(FACE_getCenterData(f), finterpCount);
for (S=0; S<f->numVerts; S++)
for (x=0; x<gridSize; x++)
- VertDataZero(FACE_getIECo(f, lvl, S, x));
+ VertDataZero(FACE_getIECo(f, lvl, S, x), finterpCount);
for (S=0; S<f->numVerts; S++) {
int prevS = (S+f->numVerts-1)%f->numVerts;
CCGEdge *e = FACE_getEdges(f)[S];
CCGEdge *prevE = FACE_getEdges(f)[prevS];
- VertDataAdd(FACE_getCenterData(f), FACE_getIFCo(f, lvl, S, 0, 0));
+ VertDataAdd(FACE_getCenterData(f), FACE_getIFCo(f, lvl, S, 0, 0), finterpCount);
if (FACE_getVerts(f)[S]->flags&Vert_eEffected)
- VertDataAdd(VERT_getCo(FACE_getVerts(f)[S], lvl), FACE_getIFCo(f, lvl, S, cornerIdx, cornerIdx));
+ VertDataAdd(VERT_getCo(FACE_getVerts(f)[S], lvl), FACE_getIFCo(f, lvl, S, cornerIdx, cornerIdx), finterpCount);
for (x=1; x<gridSize-1; x++) {
- VertDataAdd(FACE_getIECo(f, lvl, S, x), FACE_getIFCo(f, lvl, S, x, 0));
- VertDataAdd(FACE_getIECo(f, lvl, prevS, x), FACE_getIFCo(f, lvl, S, 0, x));
+ VertDataAdd(FACE_getIECo(f, lvl, S, x), FACE_getIFCo(f, lvl, S, x, 0), finterpCount);
+ VertDataAdd(FACE_getIECo(f, lvl, prevS, x), FACE_getIFCo(f, lvl, S, 0, x), finterpCount);
}
for (x=0; x<gridSize-1; x++) {
int eI = gridSize-1-x;
if (FACE_getEdges(f)[S]->flags&Edge_eEffected)
- VertDataAdd(_edge_getCoVert(e, FACE_getVerts(f)[S], lvl, eI,vertDataSize), FACE_getIFCo(f, lvl, S, cornerIdx, x));
+ VertDataAdd(_edge_getCoVert(e, FACE_getVerts(f)[S], lvl, eI,vertDataSize), FACE_getIFCo(f, lvl, S, cornerIdx, x), finterpCount);
if (FACE_getEdges(f)[prevS]->flags&Edge_eEffected)
if(x != 0)
- VertDataAdd(_edge_getCoVert(prevE, FACE_getVerts(f)[S], lvl, eI,vertDataSize), FACE_getIFCo(f, lvl, S, x, cornerIdx));
+ VertDataAdd(_edge_getCoVert(prevE, FACE_getVerts(f)[S], lvl, eI,vertDataSize), FACE_getIFCo(f, lvl, S, x, cornerIdx), finterpCount);
}
}
}
@@ -2328,50 +2389,50 @@ CCGError ccgSubSurf_stitchFaces(CCGSubSurf *ss, int lvl, CCGFace **effectedF, in
/* average */
for (i=0; i<numEffectedV; i++) {
CCGVert *v = effectedV[i];
- VertDataMulN(VERT_getCo(v, lvl), 1.0f/v->numFaces);
+ VertDataMulN(VERT_getCo(v, lvl), 1.0f/v->numFaces, finterpCount);
}
for (i=0; i<numEffectedE; i++) {
CCGEdge *e = effectedE[i];
- VertDataCopy(EDGE_getCo(e, lvl, 0), VERT_getCo(e->v0, lvl));
- VertDataCopy(EDGE_getCo(e, lvl, edgeSize-1), VERT_getCo(e->v1, lvl));
+ VertDataCopy(EDGE_getCo(e, lvl, 0), VERT_getCo(e->v0, lvl), finterpCount);
+ VertDataCopy(EDGE_getCo(e, lvl, edgeSize-1), VERT_getCo(e->v1, lvl), finterpCount);
for (x=1; x<edgeSize-1; x++)
- VertDataMulN(EDGE_getCo(e, lvl, x), 1.0f/e->numFaces);
+ VertDataMulN(EDGE_getCo(e, lvl, x), 1.0f/e->numFaces, finterpCount);
}
/* copy */
for (i=0; i<numEffectedF; i++) {
CCGFace *f = effectedF[i];
- VertDataMulN(FACE_getCenterData(f), 1.0f/f->numVerts);
+ VertDataMulN(FACE_getCenterData(f), 1.0f/f->numVerts, finterpCount);
for (S=0; S<f->numVerts; S++)
for (x=1; x<gridSize-1; x++)
- VertDataMulN(FACE_getIECo(f, lvl, S, x), 0.5f);
+ VertDataMulN(FACE_getIECo(f, lvl, S, x), 0.5f, finterpCount);
for (S=0; S<f->numVerts; S++) {
int prevS = (S+f->numVerts-1)%f->numVerts;
CCGEdge *e = FACE_getEdges(f)[S];
CCGEdge *prevE = FACE_getEdges(f)[prevS];
- VertDataCopy(FACE_getIFCo(f, lvl, S, 0, 0), FACE_getCenterData(f));
- VertDataCopy(FACE_getIFCo(f, lvl, S, cornerIdx, cornerIdx), VERT_getCo(FACE_getVerts(f)[S], lvl));
+ VertDataCopy(FACE_getIFCo(f, lvl, S, 0, 0), FACE_getCenterData(f), finterpCount);
+ VertDataCopy(FACE_getIFCo(f, lvl, S, cornerIdx, cornerIdx), VERT_getCo(FACE_getVerts(f)[S], lvl), finterpCount);
for (x=1; x<gridSize-1; x++) {
- VertDataCopy(FACE_getIFCo(f, lvl, S, x, 0), FACE_getIECo(f, lvl, S, x));
- VertDataCopy(FACE_getIFCo(f, lvl, S, 0, x), FACE_getIECo(f, lvl, prevS, x));
+ VertDataCopy(FACE_getIFCo(f, lvl, S, x, 0), FACE_getIECo(f, lvl, S, x), finterpCount);
+ VertDataCopy(FACE_getIFCo(f, lvl, S, 0, x), FACE_getIECo(f, lvl, prevS, x), finterpCount);
}
for (x=0; x<gridSize-1; x++) {
int eI = gridSize-1-x;
- VertDataCopy(FACE_getIFCo(f, lvl, S, cornerIdx, x), _edge_getCoVert(e, FACE_getVerts(f)[S], lvl, eI,vertDataSize));
- VertDataCopy(FACE_getIFCo(f, lvl, S, x, cornerIdx), _edge_getCoVert(prevE, FACE_getVerts(f)[S], lvl, eI,vertDataSize));
+ VertDataCopy(FACE_getIFCo(f, lvl, S, cornerIdx, x), _edge_getCoVert(e, FACE_getVerts(f)[S], lvl, eI,vertDataSize), finterpCount);
+ VertDataCopy(FACE_getIFCo(f, lvl, S, x, cornerIdx), _edge_getCoVert(prevE, FACE_getVerts(f)[S], lvl, eI,vertDataSize), finterpCount);
}
- VertDataCopy(FACE_getIECo(f, lvl, S, 0), FACE_getCenterData(f));
- VertDataCopy(FACE_getIECo(f, lvl, S, gridSize-1), FACE_getIFCo(f, lvl, S, gridSize-1, 0));
+ VertDataCopy(FACE_getIECo(f, lvl, S, 0), FACE_getCenterData(f), finterpCount);
+ VertDataCopy(FACE_getIECo(f, lvl, S, gridSize-1), FACE_getIFCo(f, lvl, S, gridSize-1, 0), finterpCount);
}
}
@@ -2503,6 +2564,10 @@ int ccgSubSurf_getGridLevelSize(CCGSubSurf *ss, int level) {
}
}
+struct GridKey *ccgSubSurf_getGridKey(CCGSubSurf *ss) {
+ return &ss->meshIFC.gridkey;
+}
+
/* Vert accessors */
CCGVertHDL ccgSubSurf_getVertVertHandle(CCGVert *v) {
diff --git a/source/blender/blenkernel/intern/CCGSubSurf.h b/source/blender/blenkernel/intern/CCGSubSurf.h
index 34d684221b0..b1238849404 100644
--- a/source/blender/blenkernel/intern/CCGSubSurf.h
+++ b/source/blender/blenkernel/intern/CCGSubSurf.h
@@ -1,5 +1,7 @@
/* $Id$ */
+#include "BKE_dmgrid.h"
+
typedef void* CCGMeshHDL;
typedef void* CCGVertHDL;
typedef void* CCGEdgeHDL;
@@ -12,8 +14,14 @@ typedef struct _CCGFace CCGFace;
typedef struct _CCGMeshIFC CCGMeshIFC;
struct _CCGMeshIFC {
int vertUserSize, edgeUserSize, faceUserSize;
-
int vertDataSize;
+
+ /* How many floats to interpolate per-vertex.
+ example: if interpolating coordinates and paint masks,
+ that would be (3+1) floats, so finterpCount would be 4. */
+ int finterpCount;
+
+ struct GridKey gridkey;
};
/***/
@@ -90,6 +98,7 @@ int ccgSubSurf_getEdgeSize (CCGSubSurf *ss);
int ccgSubSurf_getEdgeLevelSize (CCGSubSurf *ss, int level);
int ccgSubSurf_getGridSize (CCGSubSurf *ss);
int ccgSubSurf_getGridLevelSize (CCGSubSurf *ss, int level);
+struct GridKey* ccgSubSurf_getGridKey (CCGSubSurf *ss);
CCGVert* ccgSubSurf_getVert (CCGSubSurf *ss, CCGVertHDL v);
CCGVertHDL ccgSubSurf_getVertVertHandle (CCGVert *v);
diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c
index 01b02653a51..8dd71a60bbc 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.c
+++ b/source/blender/blenkernel/intern/DerivedMesh.c
@@ -46,11 +46,14 @@
#include "BKE_cdderivedmesh.h"
#include "BKE_displist.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_dmgrid.h"
#include "BKE_key.h"
#include "BKE_modifier.h"
#include "BKE_mesh.h"
#include "BKE_object.h"
#include "BKE_paint.h"
+#include "BKE_ptex.h"
#include "BKE_texture.h"
#include "BKE_utildefines.h"
@@ -626,7 +629,9 @@ static void emDM_foreachMappedFaceCenter(DerivedMesh *dm, void (*func)(void *use
func(userData, i, cent, emdm->vertexCos?emdm->faceNos[i]:efa->n);
}
}
-static void emDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *userData, int index, int *drawSmooth_r), void *userData, int useColors)
+static void emDM_drawMappedFaces(DerivedMesh *dm, float (*partial_redraw_planes)[4],
+ int (*setDrawOptions)(void *userData, int index, int *drawSmooth_r),
+ void *userData, DMDrawFlags flags)
{
EditMeshDerivedMesh *emdm= (EditMeshDerivedMesh*) dm;
EditFace *efa;
@@ -1957,6 +1962,13 @@ static void mesh_calc_modifiers(Scene *scene, Object *ob, float (*inputVertexCos
add_weight_mcol_dm(ob, finaldm);
}
+ /* hack: for ptex paint use all quads */
+ if((ob->mode & OB_MODE_VERTEX_PAINT) && finaldm->type != DM_TYPE_CCGDM) {
+ dm = finaldm;
+ finaldm = quad_dm_create_from_derived(finaldm);
+ dm->release(dm);
+ }
+
/* add an orco layer if needed */
if(dataMask & CD_MASK_ORCO) {
add_orco_dm(ob, NULL, finaldm, orcodm, CD_ORCO);
@@ -2219,13 +2231,13 @@ static void clear_mesh_caches(Object *ob)
ob->derivedDeform->release(ob->derivedDeform);
ob->derivedDeform= NULL;
}
- /* we free pbvh on changes, except during sculpt since it can't deal with
+ /* we free pbvh on changes, except during paint since it can't deal with
changing PVBH node organization, we hope topology does not change in
the meantime .. weak */
- if(ob->sculpt && ob->sculpt->pbvh) {
- if(!ob->sculpt->cache) {
- BLI_pbvh_free(ob->sculpt->pbvh);
- ob->sculpt->pbvh= NULL;
+ if(ob->paint && ob->paint->pbvh) {
+ if(!ob->paint->sculpt || !ob->paint->sculpt->cache) {
+ BLI_pbvh_free(ob->paint->pbvh);
+ ob->paint->pbvh= NULL;
}
}
}
@@ -2235,7 +2247,7 @@ static void mesh_build_data(Scene *scene, Object *ob, CustomDataMask dataMask)
Object *obact = scene->basact?scene->basact->object:NULL;
int editing = paint_facesel_test(ob);
/* weight paint and face select need original indicies because of selection buffer drawing */
- int needMapping = (ob==obact) && (editing || (ob->mode & (OB_MODE_WEIGHT_PAINT|OB_MODE_VERTEX_PAINT)) || editing);
+ int needMapping = (ob==obact) && (editing || (ob->mode & OB_MODE_WEIGHT_PAINT));
clear_mesh_caches(ob);
@@ -2248,6 +2260,10 @@ static void mesh_build_data(Scene *scene, Object *ob, CustomDataMask dataMask)
ob->derivedFinal->needsFree = 0;
ob->derivedDeform->needsFree = 0;
ob->lastDataMask = dataMask;
+
+ /* update the pbvh for paint */
+ if((ob->mode & (OB_MODE_SCULPT|OB_MODE_VERTEX_PAINT)) && ob->paint)
+ ob->paint->pbvh= ob->derivedFinal->getPBVH(ob, ob->derivedFinal);
}
static void editmesh_build_data(Scene *scene, Object *obedit, EditMesh *em, CustomDataMask dataMask)
diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c
index ca81e216006..6441ce84d70 100644
--- a/source/blender/blenkernel/intern/cdderivedmesh.c
+++ b/source/blender/blenkernel/intern/cdderivedmesh.c
@@ -37,6 +37,7 @@
#include "BIF_gl.h"
#include "BKE_cdderivedmesh.h"
+#include "BKE_DerivedMesh.h"
#include "BKE_global.h"
#include "BKE_mesh.h"
#include "BKE_paint.h"
@@ -191,13 +192,15 @@ static int can_pbvh_draw(Object *ob, DerivedMesh *dm)
CDDerivedMesh *cddm = (CDDerivedMesh*) dm;
Mesh *me= (ob)? ob->data: NULL;
- if(ob->sculpt->modifiers_active) return 0;
+ if(ob->paint->sculpt && ob->paint->sculpt->modifiers_active) return 0;
+ //if(paint_facesel_test(ob)) return 0;
- return (cddm->mvert == me->mvert) || ob->sculpt->kb;
+ return (cddm->mvert == me->mvert) || (ob->paint->sculpt && ob->paint->sculpt->kb) || (ob->mode & OB_MODE_VERTEX_PAINT);
}
static struct PBVH *cdDM_getPBVH(Object *ob, DerivedMesh *dm)
{
+ SculptSession *ss;
CDDerivedMesh *cddm = (CDDerivedMesh*) dm;
Mesh *me= (ob)? ob->data: NULL;
@@ -206,10 +209,12 @@ static struct PBVH *cdDM_getPBVH(Object *ob, DerivedMesh *dm)
return NULL;
}
- if(!ob->sculpt)
+ if(!ob->paint)
return NULL;
- if(ob->sculpt->pbvh) {
- cddm->pbvh= ob->sculpt->pbvh;
+ ss = ob->paint->sculpt;
+
+ if(ob->paint->pbvh) {
+ cddm->pbvh= ob->paint->pbvh;
cddm->pbvh_draw = can_pbvh_draw(ob, dm);
}
@@ -217,10 +222,35 @@ static struct PBVH *cdDM_getPBVH(Object *ob, DerivedMesh *dm)
this derivedmesh is just original mesh. it's the multires subsurf dm
that this is actually for, to support a pbvh on a modified mesh */
if(!cddm->pbvh && ob->type == OB_MESH) {
- cddm->pbvh = BLI_pbvh_new();
+ MFace *mface;
+ MVert *mvert;
+ int totvert, totface;
+ int leaf_limit = PBVH_DEFAULT_LEAF_LIMIT;
+
+ /* TODO: set leaf limit more intelligently */
+ if(ob->mode & OB_MODE_VERTEX_PAINT)
+ leaf_limit = 1;
+
+ cddm->pbvh = BLI_pbvh_new(leaf_limit);
cddm->pbvh_draw = can_pbvh_draw(ob, dm);
- BLI_pbvh_build_mesh(cddm->pbvh, me->mface, me->mvert,
- me->totface, me->totvert);
+
+ if(ob->mode & OB_MODE_VERTEX_PAINT) {
+ mface = cddm->mface;
+ mvert = cddm->mvert;
+ totvert = cdDM_getNumVerts(&cddm->dm);
+ totface = cdDM_getNumFaces(&cddm->dm);
+ }
+ else {
+ mface = me->mface;
+ mvert = me->mvert;
+ totvert = me->totvert;
+ totface = me->totface;
+ }
+
+ BLI_pbvh_build_mesh(cddm->pbvh, mface, mvert,
+ &me->vdata, &me->fdata,
+ totface, totvert,
+ ss ? &ss->hidden_areas : NULL);
}
return cddm->pbvh;
@@ -416,9 +446,39 @@ static void cdDM_drawLooseEdges(DerivedMesh *dm)
}
}
+/* draw using the PBVH; returns true if drawing is done,
+ false if regular drawing should be used */
+static int cddm_draw_pbvh(DerivedMesh *dm, float (*partial_redraw_planes)[4],
+ int (*setMaterial)(int, void *attribs),
+ DMDrawFlags flags)
+{
+ CDDerivedMesh *cddm = (CDDerivedMesh*)dm;
+ MFace *mface = cddm->mface;
+ float (*face_nors)[3];
+
+ if(!(flags & DM_DRAW_BACKBUF_SELECTION) && cddm->pbvh && cddm->pbvh_draw) {
+ if(dm->numFaceData) {
+ face_nors = CustomData_get_layer(&dm->faceData, CD_NORMAL);
+
+ /* should be per face */
+ if(setMaterial && !setMaterial(mface->mat_nr+1, NULL))
+ return 1;
+ if(mface->flag & ME_SMOOTH)
+ flags |= DM_DRAW_FULLY_SMOOTH;
+
+ BLI_pbvh_draw(cddm->pbvh, partial_redraw_planes, face_nors, flags);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
static void cdDM_drawFacesSolid(DerivedMesh *dm,
float (*partial_redraw_planes)[4],
- int fast, int (*setMaterial)(int, void *attribs))
+ int (*setMaterial)(int, void *attribs),
+ DMDrawFlags flags)
{
CDDerivedMesh *cddm = (CDDerivedMesh*) dm;
MVert *mvert = cddm->mvert;
@@ -426,29 +486,16 @@ static void cdDM_drawFacesSolid(DerivedMesh *dm,
float *nors= dm->getFaceDataArray(dm, CD_NORMAL);
int a, glmode = -1, shademodel = -1, matnr = -1, drawCurrentMat = 1;
-#define PASSVERT(index) { \
- if(shademodel == GL_SMOOTH) { \
- short *no = mvert[index].no; \
- glNormal3sv(no); \
- } \
- glVertex3fv(mvert[index].co); \
-}
-
- if(cddm->pbvh && cddm->pbvh_draw) {
- if(dm->numFaceData) {
- float (*face_nors)[3] = CustomData_get_layer(&dm->faceData, CD_NORMAL);
-
- /* should be per face */
- if(!setMaterial(mface->mat_nr+1, NULL))
- return;
-
- glShadeModel((mface->flag & ME_SMOOTH)? GL_SMOOTH: GL_FLAT);
- BLI_pbvh_draw(cddm->pbvh, partial_redraw_planes, face_nors, (mface->flag & ME_SMOOTH));
- glShadeModel(GL_FLAT);
- }
+#define PASSVERT(index) { \
+ if(shademodel == GL_SMOOTH) { \
+ short *no = mvert[index].no; \
+ glNormal3sv(no); \
+ } \
+ glVertex3fv(mvert[index].co); \
+ }
+ if(cddm_draw_pbvh(dm, partial_redraw_planes, setMaterial, flags))
return;
- }
if( GPU_buffer_legacy(dm) ) {
DEBUG_VBO( "Using legacy code. cdDM_drawFacesSolid\n" );
@@ -777,7 +824,9 @@ static void cdDM_drawFacesTex(DerivedMesh *dm, int (*setDrawOptions)(MTFace *tfa
cdDM_drawFacesTex_common(dm, setDrawOptions, NULL, NULL);
}
-static void cdDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *userData, int index, int *drawSmooth_r), void *userData, int useColors)
+static void cdDM_drawMappedFaces(DerivedMesh *dm, float (*partial_redraw_planes)[4],
+ int (*setDrawOptions)(void *userData, int index, int *drawSmooth_r),
+ void *userData, DMDrawFlags flags)
{
CDDerivedMesh *cddm = (CDDerivedMesh*) dm;
MVert *mv = cddm->mvert;
@@ -786,6 +835,9 @@ static void cdDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *us
float *nors= dm->getFaceDataArray(dm, CD_NORMAL);
int i, orig, *index = DM_get_face_data_layer(dm, CD_ORIGINDEX);
+ if(cddm_draw_pbvh(dm, partial_redraw_planes, NULL, flags))
+ return;
+
mc = DM_get_face_data_layer(dm, CD_ID_MCOL);
if(!mc)
mc = DM_get_face_data_layer(dm, CD_WEIGHT_MCOL);
@@ -810,7 +862,7 @@ static void cdDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *us
if(!setDrawOptions || setDrawOptions(userData, orig, &drawSmooth)) {
unsigned char *cp = NULL;
- if(useColors && mc)
+ if((flags & DM_DRAW_VERTEX_COLORS) && mc)
cp = (unsigned char *)&mc[i * 4];
glShadeModel(drawSmooth?GL_SMOOTH:GL_FLAT);
@@ -867,7 +919,7 @@ static void cdDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *us
int prevstart = 0;
GPU_vertex_setup(dm);
GPU_normal_setup(dm);
- if( useColors && mc )
+ if((flags & DM_DRAW_VERTEX_COLORS) && mc )
GPU_color_setup(dm);
if( !GPU_buffer_legacy(dm) ) {
int tottri = dm->drawObject->nelements/3;
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index 1f867a615b2..7e4de5e36d6 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -50,6 +50,8 @@
#include "BKE_global.h"
#include "BKE_utildefines.h"
+#include "ptex.h"
+
/* number of layers to add when growing a CustomData object */
#define CUSTOMDATA_GROW 5
@@ -760,7 +762,7 @@ static void layerSwap_mcol(void *data, const int *corner_indices)
static void layerDefault_mcol(void *data, int count)
{
- static MCol default_mcol = {255, 255, 255, 255};
+ static MCol default_mcol = {0, 255, 255, 255}; /* abgr */
MCol *mcol = (MCol*)data;
int i;
@@ -768,9 +770,74 @@ static void layerDefault_mcol(void *data, int count)
mcol[i] = default_mcol;
}
+/* Grid */
+
+static void layerCopy_grid(const void *source_v, void *dest_v, int count)
+{
+ const CustomDataMultires *source = source_v;
+ CustomDataMultires *dest = dest_v;
+ int i, j;
+
+ for(i = 0; i < count; ++i) {
+ dest[i].totlayer = source[i].totlayer;
+ dest[i].layers = MEM_callocN(sizeof(CustomDataMultiresLayer) *
+ dest[i].totlayer,
+ "CustomDataMultiresLayers");
+
+ for(j = 0; j < source[i].totlayer; ++j) {
+ CustomDataMultiresLayer *dl = dest[i].layers + j;
+ CustomDataMultiresLayer *sl = source[i].layers + j;
+
+ dl->type = sl->type;
+ dl->griddata = MEM_dupallocN(sl->griddata);
+ BLI_strncpy(dl->name, sl->name, sizeof(dl->name));
+ }
+ }
+}
+
+static void layerFree_grid(void *data_v, int count, int size)
+{
+ CustomDataMultires *data = data_v;
+ int i, j;
+
+ for(i = 0; i < count; ++i) {
+ for(j = 0; j < data[i].totlayer; ++j) {
+ if(data[i].layers[j].griddata)
+ MEM_freeN(data[i].layers[j].griddata);
+ }
+ if(data[i].layers)
+ MEM_freeN(data[i].layers);
+ }
+}
+
+static void layerCopy_mptex(const void *source_v, void *dest_v, int count)
+{
+ const MPtex *source = source_v;
+ MPtex *dest = dest_v;
+ int i, j;
+
+ for(i = 0; i < count; ++i) {
+ dest[i] = source[i];
+ for(j = 0; j < source[i].totsubface; ++j)
+ dest[i].subfaces[j].data = MEM_dupallocN(source[i].subfaces[j].data);
+ }
+}
+
+static void layerFree_mptex(void *data_v, int count, int size)
+{
+ MPtex *data = data_v;
+ int i, j;
+ for(i = 0; i < count; ++i) {
+ for(j = 0; j < data[i].totsubface; ++j) {
+ if(data[i].subfaces[j].data)
+ MEM_freeN(data[i].subfaces[j].data);
+ }
+ }
+}
const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
+ /* 0 */
{sizeof(MVert), "MVert", 1, NULL, NULL, NULL, NULL, NULL, NULL},
{sizeof(MSticky), "MSticky", 1, NULL, NULL, NULL, layerInterp_msticky, NULL,
NULL},
@@ -787,6 +854,8 @@ const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
/* 3 floats per normal vector */
{sizeof(float)*3, "", 0, NULL, NULL, NULL, NULL, NULL, NULL},
{sizeof(int), "", 0, NULL, NULL, NULL, NULL, NULL, NULL},
+
+ /* 10 */
{sizeof(MFloatProperty), "MFloatProperty",1,"Float",NULL,NULL,NULL,NULL},
{sizeof(MIntProperty), "MIntProperty",1,"Int",NULL,NULL,NULL,NULL},
{sizeof(MStringProperty), "MStringProperty",1,"String",NULL,NULL,NULL,NULL},
@@ -799,13 +868,22 @@ const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
{sizeof(float)*3*4, "", 0, NULL, NULL, NULL, NULL, NULL, NULL},
{sizeof(MDisps), "MDisps", 1, NULL, layerCopy_mdisps,
layerFree_mdisps, layerInterp_mdisps, layerSwap_mdisps, NULL, layerRead_mdisps, layerWrite_mdisps, layerFilesize_mdisps},
+
+ /* 20 */
{sizeof(MCol)*4, "MCol", 4, "WeightCol", NULL, NULL, layerInterp_mcol,
layerSwap_mcol, layerDefault_mcol},
- {sizeof(MCol)*4, "MCol", 4, "IDCol", NULL, NULL, layerInterp_mcol,
+ {sizeof(MCol)*4, "MCol", 4, "IDCol", NULL, NULL, layerInterp_mcol,
layerSwap_mcol, layerDefault_mcol},
- {sizeof(MCol)*4, "MCol", 4, "TexturedCol", NULL, NULL, layerInterp_mcol,
+ {sizeof(MCol)*4, "MCol", 4, "TexturedCol", NULL, NULL, layerInterp_mcol,
layerSwap_mcol, layerDefault_mcol},
- {sizeof(float)*3, "", 0, NULL, NULL, NULL, NULL, NULL, NULL}
+ /* CD_CLOTH_ORCO */
+ {sizeof(float)*3, "", 0, NULL, NULL, NULL, NULL, NULL, NULL},
+ /* CD_GRID */
+ {sizeof(CustomDataMultires), "CustomDataMultires", 1, "Grid", layerCopy_grid, layerFree_grid, NULL, NULL, NULL},
+ /* CD_PAINTMASK */
+ {sizeof(float), "", 0, "Mask", NULL, NULL, NULL, NULL, NULL},
+ /* CD_MPTEX */
+ {sizeof(MPtex), "MPtex", 1, "Ptex", layerCopy_mptex, layerFree_mptex, NULL, NULL, NULL},
};
const char *LAYERTYPENAMES[CD_NUMTYPES] = {
@@ -813,7 +891,8 @@ const char *LAYERTYPENAMES[CD_NUMTYPES] = {
/* 5-9 */ "CDMTFace", "CDMCol", "CDOrigIndex", "CDNormal", "CDFlags",
/* 10-14 */ "CDMFloatProperty", "CDMIntProperty","CDMStringProperty", "CDOrigSpace", "CDOrco",
/* 15-19 */ "CDMTexPoly", "CDMLoopUV", "CDMloopCol", "CDTangent", "CDMDisps",
- /* 20-23 */"CDWeightMCol", "CDIDMCol", "CDTextureMCol", "CDClothOrco"
+ /* 20-24 */ "CDWeightMCol", "CDIDMCol", "CDTextureMCol", "CDClothOrco", "CDGrid",
+ /* 25-26 */ "CDPaintMask", "CD_PTEX"
};
const CustomDataMask CD_MASK_BAREMESH =
@@ -821,14 +900,17 @@ const CustomDataMask CD_MASK_BAREMESH =
const CustomDataMask CD_MASK_MESH =
CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MFACE |
CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE | CD_MASK_MCOL |
- CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR | CD_MASK_MDISPS;
+ CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR | CD_MASK_MDISPS |
+ CD_MASK_GRIDS | CD_MASK_PAINTMASK | CD_MASK_MPTEX;
const CustomDataMask CD_MASK_EDITMESH =
CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE |
- CD_MASK_MCOL|CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR | CD_MASK_MDISPS;
+ CD_MASK_MCOL|CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR |
+ CD_MASK_MDISPS | CD_MASK_GRIDS | CD_MASK_PAINTMASK | CD_MASK_MPTEX;
const CustomDataMask CD_MASK_DERIVEDMESH =
CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE |
CD_MASK_MCOL | CD_MASK_ORIGINDEX | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_CLOTH_ORCO |
- CD_MASK_PROP_STR | CD_MASK_ORIGSPACE | CD_MASK_ORCO | CD_MASK_TANGENT | CD_MASK_WEIGHT_MCOL;
+ CD_MASK_PROP_STR | CD_MASK_ORIGSPACE | CD_MASK_ORCO | CD_MASK_TANGENT | CD_MASK_WEIGHT_MCOL |
+ CD_MASK_PAINTMASK;
const CustomDataMask CD_MASK_BMESH =
CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR;
const CustomDataMask CD_MASK_FACECORNERS =
@@ -898,6 +980,8 @@ void CustomData_merge(const struct CustomData *source, struct CustomData *dest,
newlayer->active_clone = lastclone;
newlayer->active_mask = lastmask;
newlayer->flag |= lastflag & (CD_FLAG_EXTERNAL|CD_FLAG_IN_MEMORY);
+ if(layer->flag & CD_FLAG_MULTIRES)
+ newlayer->flag |= CD_FLAG_MULTIRES;
}
}
}
@@ -1076,6 +1160,20 @@ int CustomData_get_stencil_layer(const CustomData *data, int type)
return -1;
}
+char *CustomData_get_layer_name_at_offset(const CustomData *data, int type, int offset)
+{
+ int first;
+
+ first = CustomData_get_layer_index(data, type);
+
+ if((first != -1) &&
+ (first + offset < data->totlayer) &&
+ (data->layers[first + offset].type == type))
+ return data->layers[first + offset].name;
+ else
+ return NULL;
+}
+
void CustomData_set_layer_active(CustomData *data, int type, int n)
{
int i;
@@ -1158,6 +1256,17 @@ void CustomData_set_layer_flag(struct CustomData *data, int type, int flag)
data->layers[i].flag |= flag;
}
+void CustomData_set_layer_offset_flag(struct CustomData *data, int type, int offset, int flag)
+{
+ int first = CustomData_get_layer_index(data, type);
+
+ if((first != -1) &&
+ (first+offset < data->totlayer) &&
+ (data->layers[first+offset].type == type)) {
+ data->layers[first+offset].flag |= flag;
+ }
+}
+
static int customData_resize(CustomData *data, int amount)
{
CustomDataLayer *tmp = MEM_callocN(sizeof(*tmp)*(data->maxlayer + amount),
@@ -1221,8 +1330,9 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data,
data->layers[index] = data->layers[index - 1];
data->layers[index].type = type;
- data->layers[index].flag = flag;
+ data->layers[index].flag = flag | CD_FLAG_ENABLED;
data->layers[index].data = newlayerdata;
+ data->layers[index].strength = 1;
if(name || (name=typeInfo->defaultname)) {
strcpy(data->layers[index].name, name);
@@ -1255,7 +1365,7 @@ void *CustomData_add_layer(CustomData *data, int type, int alloctype,
const LayerTypeInfo *typeInfo= layerType_getInfo(type);
layer = customData_add_layer__internal(data, type, alloctype, layerdata,
- totelem, typeInfo->defaultname);
+ totelem, typeInfo->defaultname);
if(layer)
return layer->data;
@@ -1263,6 +1373,32 @@ void *CustomData_add_layer(CustomData *data, int type, int alloctype,
return NULL;
}
+void *CustomData_add_layer_at_offset(CustomData *cd, int type, int alloctype,
+ void *layerdata, int totelem, int offset)
+{
+ int type_first_layer, type_totlayer, i;
+ CustomDataLayer copy_of_new;
+
+ /* add the new layer as normal */
+ CustomData_add_layer(cd, type, alloctype, layerdata, totelem);
+
+ type_first_layer = CustomData_get_layer_index(cd, type);
+ type_totlayer = CustomData_number_of_layers(cd, type);
+
+ /* make a copy of the new layer */
+ copy_of_new = cd->layers[type_first_layer + type_totlayer - 1];
+
+ /* move the old layers up to make room for the new layer */
+ for(i = type_first_layer + type_totlayer - 1;
+ i > type_first_layer + offset; --i)
+ cd->layers[i] = cd->layers[i-1];
+
+ /* copy the new layer into the correct offset */
+ cd->layers[type_first_layer + offset] = copy_of_new;
+
+ return copy_of_new.data;
+}
+
/*same as above but accepts a name*/
void *CustomData_add_layer_named(CustomData *data, int type, int alloctype,
void *layerdata, int totelem, const char *name)
@@ -2323,6 +2459,250 @@ int CustomData_verify_versions(struct CustomData *data, int index)
return keeplayer;
}
+/* Multires */
+
+int CustomData_multires_type_totfloat(int type)
+{
+ switch(type) {
+ case CD_MVERT:
+ return 3;
+ case CD_MCOL:
+ return 4;
+ case CD_PAINTMASK:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int customdata_multires_find_first_layer_index(CustomDataMultires *cdm, int type)
+{
+ int i;
+ for(i = 0; i < cdm->totlayer; ++i) {
+ if(cdm->layers[i].type == type)
+ return i;
+ }
+ return -1;
+}
+
+static int customdata_multires_find_named_layer_index(CustomDataMultires *cdm,
+ int type, char *name)
+{
+ int first, i;
+
+ first = customdata_multires_find_first_layer_index(cdm, type);
+
+ if(first != -1) {
+ for(i = first; first < cdm->totlayer; ++i) {
+ if(cdm->layers[i].type != type)
+ break;
+ else if(!strcmp(name, cdm->layers[i].name))
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+int CustomData_get_multires_count(CustomData *cd, int type)
+{
+ int first, tot, i;
+
+ first = CustomData_get_layer_index(cd, type);
+ tot = 0;
+
+ if(first != -1) {
+ for(i = first; i < cd->totlayer; ++i) {
+ if(cd->layers[i].type != type)
+ break;
+ if(cd->layers[i].flag & CD_FLAG_MULTIRES)
+ ++tot;
+ }
+ }
+
+ return tot;
+}
+
+void *CustomData_get_multires_names(CustomData *cd, int type)
+{
+ char (*names)[32] = NULL;
+ int count, first;
+
+ count = CustomData_get_multires_count(cd, type);
+
+ if(count) {
+ int layer_ndx, names_ndx;
+
+ names = MEM_callocN(32*count, "CustomData_get_multires_names");
+ first = CustomData_get_layer_index(cd, type);
+ names_ndx = 0;
+
+ for(layer_ndx = first; layer_ndx < cd->totlayer; ++layer_ndx) {
+ CustomDataLayer *cdl = cd->layers + layer_ndx;
+
+ if(cdl->type != type)
+ break;
+ else if(cdl->flag & CD_FLAG_MULTIRES) {
+ BLI_strncpy(names[names_ndx], cdl->name, 32);
+ ++names_ndx;
+ }
+ }
+ }
+
+ return names;
+}
+
+float *CustomData_multires_get_data(CustomDataMultires *cdm, int type,
+ char *name)
+{
+ int layer;
+
+ layer = customdata_multires_find_named_layer_index(cdm, type, name);
+
+ if(layer == -1)
+ return NULL;
+ else
+ return cdm->layers[layer].griddata;
+}
+
+static float *customdata_multires_alloc_griddata(int type, int totelem)
+{
+ return MEM_callocN(sizeof(float) * totelem *
+ CustomData_multires_type_totfloat(type),
+ "CustomDataMultiresLayer.griddata");
+}
+
+void CustomData_multires_sync_layer(CustomDataMultires *cdm, int type,
+ char *name)
+{
+ int layer;
+
+ layer = customdata_multires_find_named_layer_index(cdm, type, name);
+
+ if(layer == -1)
+ CustomData_multires_add_layer_data(cdm, type, name, NULL);
+ else {
+ if(cdm->layers[layer].griddata)
+ MEM_freeN(cdm->layers[layer].griddata);
+ cdm->layers[layer].griddata =
+ customdata_multires_alloc_griddata(type, cdm->totelem);
+ }
+}
+
+void CustomData_multires_add_layer_data(CustomDataMultires *cdm, int type,
+ char *name, float *griddata)
+{
+ CustomDataMultiresLayer *old = cdm->layers;
+ int first, layer, i;
+
+ cdm->layers = MEM_callocN(sizeof(CustomDataMultiresLayer) *
+ (cdm->totlayer + 1),
+ "customdata multires add layer");
+
+ first = customdata_multires_find_first_layer_index(cdm, type);
+
+ /* if no layers of type yet, add new layer at end
+ otherwise add layer at the beginning of the type's segment */
+ if(first == -1)
+ layer = cdm->totlayer;
+ else
+ layer = first;
+
+ /* create griddata if none given */
+ if(!griddata) {
+ griddata =
+ customdata_multires_alloc_griddata(type, cdm->totelem);
+ }
+
+ for(i = 0; i <= cdm->totlayer; ++i) {
+ if(i < layer)
+ cdm->layers[i] = old[i];
+ else if(i == layer) {
+ cdm->layers[i].griddata = griddata;
+ cdm->layers[i].type = type;
+ BLI_strncpy(cdm->layers[i].name, name,
+ sizeof(cdm->layers[i].name));
+ }
+ else if(i > layer)
+ cdm->layers[i] = old[i-1];
+ }
+
+ ++cdm->totlayer;
+ if(old) MEM_freeN(old);
+}
+
+void CustomData_multires_add_layers(CustomDataMultires *cdm, int count,
+ int type, char *name)
+{
+ int i;
+
+ if(!cdm)
+ return;
+
+ for(i = 0; i < count; ++i, ++cdm)
+ CustomData_multires_add_layer_data(cdm, type, name, NULL);
+}
+
+static int CustomData_multires_layer_delete(CustomDataMultires *cdm, int type,
+ char *name)
+{
+ CustomDataMultiresLayer *old = cdm->layers;
+ int layer, i;
+
+ layer = customdata_multires_find_named_layer_index(cdm, type, name);
+
+ if(layer == -1)
+ return 0;
+
+ cdm->layers = MEM_callocN(sizeof(CustomDataMultiresLayer) *
+ (cdm->totlayer - 1),
+ "customdata multires remove layer");
+
+ /* copy over layer data, skipping the removed layer */
+ for(i = 0; i < cdm->totlayer; ++i) {
+ if(i < layer)
+ cdm->layers[i] = old[i];
+ else if(i == layer && old[i].griddata)
+ MEM_freeN(old[i].griddata);
+ else if(i > layer)
+ cdm->layers[i - 1] = old[i];
+ }
+
+ --cdm->totlayer;
+ MEM_freeN(old);
+
+ return 1;
+}
+
+int CustomData_multires_remove_layers(CustomDataMultires *cdm, int count,
+ int type, char *name)
+{
+ int i;
+
+ if(!cdm)
+ return 0;
+
+ for(i = 0; i < count; ++i, ++cdm) {
+ if(!CustomData_multires_layer_delete(cdm, type, name))
+ return 0;
+ }
+
+ return 1;
+}
+
+void CustomData_multires_rename(CustomDataMultires *cdm, int type,
+ char *old_name, char *name)
+{
+ int layer;
+
+ layer = customdata_multires_find_named_layer_index(cdm, type, old_name);
+
+ if(layer != -1) {
+ BLI_strncpy(cdm->layers[layer].name, name,
+ sizeof(cdm->layers[layer].name));
+ }
+}
+
/****************************** External Files *******************************/
static void customdata_external_filename(char filename[FILE_MAX], ID *id, CustomDataExternal *external)
diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c
index 57f568e6094..686c2223774 100644
--- a/source/blender/blenkernel/intern/multires.c
+++ b/source/blender/blenkernel/intern/multires.c
@@ -57,8 +57,14 @@ static const int multires_max_levels = 13;
static const int multires_grid_tot[] = {0, 4, 9, 25, 81, 289, 1089, 4225, 16641, 66049, 263169, 1050625, 4198401, 16785409};
static const int multires_side_tot[] = {0, 2, 3, 5, 9, 17, 33, 65, 129, 257, 513, 1025, 2049, 4097};
+typedef enum {
+ APPLY_DISPS,
+ CALC_DISPS,
+ ADD_DISPS,
+} DispOp;
+
static void multires_mvert_to_ss(DerivedMesh *dm, MVert *mvert);
-static void multiresModifier_disp_run(DerivedMesh *dm, Mesh *me, int invert, int add, DMGridData **oldGridData, int totlvl);
+static void multiresModifier_disp_run(DerivedMesh *dm, Mesh *me, DispOp op, DMGridData **oldGridData, int totlvl);
DerivedMesh *get_multires_dm(Scene *scene, MultiresModifierData *mmd, Object *ob)
{
@@ -93,7 +99,7 @@ static int multires_get_level(Object *ob, MultiresModifierData *mmd, int render)
{
if(render)
return (mmd->modifier.scene)? get_render_subsurf_level(&mmd->modifier.scene->r, mmd->renderlvl): mmd->renderlvl;
- else if(ob->mode == OB_MODE_SCULPT)
+ else if(ob->mode & OB_MODE_SCULPT)
return mmd->sculptlvl;
else
return (mmd->modifier.scene)? get_render_subsurf_level(&mmd->modifier.scene->r, mmd->lvl): mmd->lvl;
@@ -118,8 +124,10 @@ static void multires_dm_mark_as_modified(DerivedMesh *dm)
void multires_mark_as_modified(Object *ob)
{
- if(ob && ob->derivedFinal)
- multires_dm_mark_as_modified(ob->derivedFinal);
+ DerivedMesh *dm = ob->derivedFinal;
+
+ if(ob && dm && dm->type == DM_TYPE_CCGDM)
+ multires_dm_mark_as_modified(dm);
}
void multires_force_update(Object *ob)
@@ -130,9 +138,9 @@ void multires_force_update(Object *ob)
ob->derivedFinal->release(ob->derivedFinal);
ob->derivedFinal = NULL;
}
- if(ob->sculpt && ob->sculpt->pbvh) {
- BLI_pbvh_free(ob->sculpt->pbvh);
- ob->sculpt->pbvh= NULL;
+ if(ob->paint && ob->paint->pbvh) {
+ BLI_pbvh_free(ob->paint->pbvh);
+ ob->paint->pbvh= NULL;
}
}
}
@@ -324,10 +332,43 @@ static void multires_set_tot_mdisps(Mesh *me, int lvl)
}
}
+/* ensure all the layers needed by the gridkey have
+ a matching multires layer
+
+ clear and resize the all griddata to match the level
+*/
+static void multires_sync_customdata(Mesh *me, GridKey *gridkey,
+ int lvl)
+{
+ CustomDataMultires *cd_grids;
+ int i, j;
+
+ cd_grids = CustomData_get_layer(&me->fdata, CD_GRIDS);
+ if(!cd_grids)
+ cd_grids = CustomData_add_layer(&me->fdata, CD_GRIDS,
+ CD_CALLOC, NULL, me->totface);
+
+ for(i = 0; i < me->totface; ++i) {
+ int nvert = (me->mface[i].v4)? 4: 3;
+ int totelem = multires_grid_tot[lvl]*nvert;
+
+ cd_grids[i].totelem = totelem;
+
+ for(j = 0; j < gridkey->mask; ++j) {
+ CustomData_multires_sync_layer(cd_grids+i,
+ CD_PAINTMASK,
+ gridkey->mask_names[j]);
+ }
+ }
+}
+
+/* TODO: removed this in favor of sync_customdata */
static void multires_reallocate_mdisps(Mesh *me, MDisps *mdisps, int lvl)
{
int i;
+ /* This will be replaced when we do CD_DISPS */
+
/* reallocate displacements to be filled in */
for(i = 0; i < me->totface; ++i) {
int nvert = (me->mface[i].v4)? 4: 3;
@@ -369,23 +410,24 @@ static void multires_copy_grid(float (*gridA)[3], float (*gridB)[3], int sizeA,
}
}
-static void multires_copy_dm_grid(DMGridData *gridA, DMGridData *gridB, int sizeA, int sizeB)
+static void multires_copy_dm_grid(DMGridData *gridA, DMGridData *gridB, GridKey *gridkey, int sizeA, int sizeB)
{
int x, y, j, skip;
+ int size = sizeof(float)*GRIDELEM_INTERP_COUNT(gridkey);
if(sizeA > sizeB) {
skip = (sizeA-1)/(sizeB-1);
for(j = 0, y = 0; y < sizeB; y++)
for(x = 0; x < sizeB; x++, j++)
- copy_v3_v3(gridA[y*skip*sizeA + x*skip].co, gridB[j].co);
+ memcpy(GRIDELEM_AT(gridA, y*skip*sizeA + x*skip, gridkey), GRIDELEM_AT(gridB, j, gridkey), size);
}
else {
skip = (sizeB-1)/(sizeA-1);
for(j = 0, y = 0; y < sizeA; y++)
for(x = 0; x < sizeA; x++, j++)
- copy_v3_v3(gridA[j].co, gridB[y*skip*sizeB + x*skip].co);
+ memcpy(GRIDELEM_AT(gridA, j, gridkey), GRIDELEM_AT(gridB, y*skip*sizeB + x*skip, gridkey), size);
}
}
@@ -442,7 +484,9 @@ void multiresModifier_del_levels(MultiresModifierData *mmd, Object *ob, int dire
multires_set_tot_level(ob, mmd, lvl);
}
-static DerivedMesh *multires_dm_create_local(Object *ob, DerivedMesh *dm, int lvl, int totlvl, int simple)
+static DerivedMesh *multires_dm_create_local(Object *ob, DerivedMesh *dm,
+ GridKey *gridkey, int lvl,
+ int totlvl, int simple)
{
MultiresModifierData mmd;
@@ -453,13 +497,32 @@ static DerivedMesh *multires_dm_create_local(Object *ob, DerivedMesh *dm, int lv
mmd.totlvl = totlvl;
mmd.simple = simple;
- return multires_dm_create_from_derived(&mmd, 1, dm, ob, 0, 0);
+ return multires_dm_create_from_derived(&mmd, 1, dm, ob, gridkey, 0, 0);
}
-static DerivedMesh *subsurf_dm_create_local(Object *ob, DerivedMesh *dm, int lvl, int simple, int optimal)
+static void init_gridkey_from_customdata(GridKey *gridkey,
+ CustomData *vdata,
+ CustomData *fdata)
{
+ GRIDELEM_KEY_INIT(gridkey, 1, 0,
+ CustomData_get_multires_count(vdata, CD_PAINTMASK),
+ 1);
+
+ gridkey->mask_names = CustomData_get_multires_names(vdata,
+ CD_PAINTMASK);
+
+}
+
+static DerivedMesh *subsurf_dm_create_local(Object *ob, DerivedMesh *dm,
+ GridKey *gridkey, int lvl,
+ int simple, int optimal)
+{
+ DerivedMesh *result;
+ Mesh *me = get_mesh(ob);
SubsurfModifierData smd;
+ GridKey default_gridkey;
+ memset(&default_gridkey, 0, sizeof(GridKey));
memset(&smd, 0, sizeof(SubsurfModifierData));
smd.levels = smd.renderLevels = lvl;
smd.flags |= eSubsurfModifierFlag_SubsurfUv;
@@ -468,7 +531,149 @@ static DerivedMesh *subsurf_dm_create_local(Object *ob, DerivedMesh *dm, int lvl
if(optimal)
smd.flags |= eSubsurfModifierFlag_ControlEdges;
- return subsurf_make_derived_from_derived(dm, &smd, 0, NULL, 0, 0);
+ if(!gridkey) {
+ init_gridkey_from_customdata(&default_gridkey,
+ &me->vdata, &me->fdata);
+ gridkey = &default_gridkey;
+ }
+
+ result = subsurf_make_derived_from_derived(dm, &smd, gridkey, 0, NULL, 0, 0);
+
+ if(default_gridkey.color_names) MEM_freeN(default_gridkey.color_names);
+ if(default_gridkey.mask_names) MEM_freeN(default_gridkey.mask_names);
+
+ return result;
+}
+
+/* assumes no is normalized; return value's sign is negative if v is on
+ the other side of the plane */
+static float v3_dist_from_plane(float v[3], float center[3], float no[3])
+{
+ float s[3];
+ sub_v3_v3v3(s, v, center);
+ return dot_v3v3(s, no);
+}
+
+void multiresModifier_base_apply(MultiresModifierData *mmd, Object *ob)
+{
+ DerivedMesh *cddm, *dispdm, *origdm;
+ GridKey gridkey;
+ Mesh *me;
+ ListBase *fmap;
+ float (*origco)[3];
+ int i, j, offset, totlvl;
+
+ multires_force_update(ob);
+
+ me = get_mesh(ob);
+ totlvl = mmd->totlvl;
+
+ /* only need vert/norm grid data */
+ GRIDELEM_KEY_INIT(&gridkey, 1, 0, 0, 1);
+
+ /* XXX - probably not necessary to regenerate the cddm so much? */
+
+ /* generate highest level with displacements */
+ cddm = CDDM_from_mesh(me, NULL);
+ DM_set_only_copy(cddm, CD_MASK_BAREMESH);
+ dispdm = multires_dm_create_local(ob, cddm, &gridkey, totlvl, totlvl, 0);
+ cddm->release(cddm);
+
+ /* copy the new locations of the base verts into the mesh */
+ offset = dispdm->getNumVerts(dispdm) - me->totvert;
+ for(i = 0; i < me->totvert; ++i) {
+ dispdm->getVertCo(dispdm, offset + i, me->mvert[i].co);
+ }
+
+ /* heuristic to produce a better-fitting base mesh */
+ cddm = CDDM_from_mesh(me, NULL);
+ fmap = cddm->getFaceMap(ob, cddm);
+ origco = MEM_callocN(sizeof(float)*3*me->totvert, "multires apply base origco");
+ for(i = 0; i < me->totvert ;++i)
+ copy_v3_v3(origco[i], me->mvert[i].co);
+
+ for(i = 0; i < me->totvert; ++i) {
+ IndexNode *n;
+ float avg_no[3] = {0,0,0}, center[3] = {0,0,0}, push[3];
+ float dist;
+ int tot;
+
+ /* Don't adjust verts not used by at least one face */
+ if(!fmap[i].first)
+ continue;
+
+ /* find center */
+ for(n = fmap[i].first, tot = 0; n; n = n->next) {
+ MFace *f = &me->mface[n->index];
+ int S = f->v4 ? 4 : 3;
+
+ /* this double counts, not sure if that's bad or good */
+ for(j = 0; j < S; ++j) {
+ int vndx = (&f->v1)[j];
+ if(vndx != i) {
+ add_v3_v3(center, origco[vndx]);
+ ++tot;
+ }
+ }
+ }
+ mul_v3_fl(center, 1.0f / tot);
+
+ /* find normal */
+ for(n = fmap[i].first; n; n = n->next) {
+ MFace *f = &me->mface[n->index];
+ int S = f->v4 ? 4 : 3;
+ float v[4][3], no[3];
+
+ for(j = 0; j < S; ++j) {
+ int vndx = (&f->v1)[j];
+ if(vndx == i)
+ copy_v3_v3(v[j], center);
+ else
+ copy_v3_v3(v[j], origco[vndx]);
+ }
+
+ if(S == 4)
+ normal_quad_v3(no, v[0], v[1], v[2], v[3]);
+ else
+ normal_tri_v3(no, v[0], v[1], v[2]);
+ add_v3_v3(avg_no, no);
+ }
+ normalize_v3(avg_no);
+
+ /* push vertex away from the plane */
+ dist = v3_dist_from_plane(me->mvert[i].co, center, avg_no);
+ copy_v3_v3(push, avg_no);
+ mul_v3_fl(push, dist);
+ add_v3_v3(me->mvert[i].co, push);
+
+ }
+
+ MEM_freeN(origco);
+ cddm->release(cddm);
+
+ /* subdivide the mesh to highest level without displacements */
+ cddm = CDDM_from_mesh(me, NULL);
+ DM_set_only_copy(cddm, CD_MASK_BAREMESH);
+ origdm = subsurf_dm_create_local(ob, cddm, &gridkey, totlvl, 0, 0);
+ cddm->release(cddm);
+
+ multiresModifier_disp_run(dispdm, me, CALC_DISPS, origdm->getGridData(origdm), totlvl);
+
+ origdm->release(origdm);
+ dispdm->release(dispdm);
+}
+
+static DMGridData **copy_grids(DMGridData **grids, int totgrid, int gridsize, GridKey *gridkey)
+{
+ DMGridData **grids_copy = MEM_callocN(sizeof(DMGridData*) * totgrid, "subgrids");
+ int i;
+
+ for(i = 0; i < totgrid; ++i) {
+ grids_copy[i] = MEM_callocN(GRIDELEM_SIZE(gridkey)*gridsize*gridsize, "subgrid");
+ memcpy(grids_copy[i], grids[i], GRIDELEM_SIZE(gridkey)*gridsize*gridsize);
+ }
+
+ return grids_copy;
}
void multiresModifier_subdivide(MultiresModifierData *mmd, Object *ob, int updateblock, int simple)
@@ -493,14 +698,16 @@ void multiresModifier_subdivide(MultiresModifierData *mmd, Object *ob, int updat
DMGridData **highGridData, **lowGridData, **subGridData;
CCGSubSurf *ss;
int i, numGrids, highGridSize, lowGridSize;
+ GridKey *gridkey;
/* create subsurf DM from original mesh at high level */
cddm = CDDM_from_mesh(me, NULL);
DM_set_only_copy(cddm, CD_MASK_BAREMESH);
- highdm = subsurf_dm_create_local(ob, cddm, totlvl, simple, 0);
+ highdm = subsurf_dm_create_local(ob, cddm, NULL, totlvl, simple, 0);
+ gridkey = highdm->getGridKey(highdm);
/* create multires DM from original mesh at low level */
- lowdm = multires_dm_create_local(ob, cddm, lvl, lvl, simple);
+ lowdm = multires_dm_create_local(ob, cddm, NULL, lvl, lvl, simple);
cddm->release(cddm);
/* copy subsurf grids and replace them with low displaced grids */
@@ -510,16 +717,12 @@ void multiresModifier_subdivide(MultiresModifierData *mmd, Object *ob, int updat
lowGridSize = lowdm->getGridSize(lowdm);
lowGridData = lowdm->getGridData(lowdm);
- subGridData = MEM_callocN(sizeof(float*)*numGrids, "subGridData*");
-
- for(i = 0; i < numGrids; ++i) {
- /* backup subsurf grids */
- subGridData[i] = MEM_callocN(sizeof(DMGridData)*highGridSize*highGridSize, "subGridData");
- memcpy(subGridData[i], highGridData[i], sizeof(DMGridData)*highGridSize*highGridSize);
+ /* backup subsurf grids */
+ subGridData = copy_grids(highGridData, numGrids, highGridSize, gridkey);
- /* overwrite with current displaced grids */
- multires_copy_dm_grid(highGridData[i], lowGridData[i], highGridSize, lowGridSize);
- }
+ /* overwrite with current displaced grids */
+ for(i = 0; i < numGrids; ++i)
+ multires_copy_dm_grid(highGridData[i], lowGridData[i], gridkey, highGridSize, lowGridSize);
/* low lower level dm no longer needed at this point */
lowdm->release(lowdm);
@@ -531,9 +734,10 @@ void multiresModifier_subdivide(MultiresModifierData *mmd, Object *ob, int updat
/* reallocate displacements */
multires_reallocate_mdisps(me, mdisps, totlvl);
+ multires_sync_customdata(me, gridkey, totlvl);
/* compute displacements */
- multiresModifier_disp_run(highdm, me, 1, 0, subGridData, totlvl);
+ multiresModifier_disp_run(highdm, me, CALC_DISPS, subGridData, totlvl);
/* free */
highdm->release(highdm);
@@ -542,48 +746,107 @@ void multiresModifier_subdivide(MultiresModifierData *mmd, Object *ob, int updat
MEM_freeN(subGridData);
}
else {
+ /* XXX: I think this can be safely removed, we already check in
+ disp run for unallocated disps -- nicholas */
+
/* only reallocate, nothing to upsample */
- multires_reallocate_mdisps(me, mdisps, totlvl);
+ //multires_reallocate_mdisps(me, mdisps, totlvl);
}
multires_set_tot_level(ob, mmd, totlvl);
}
-static void grid_tangent(int gridSize, int index, int x, int y, int axis, DMGridData **gridData, float t[3])
+static void grid_tangent(int gridSize, int index, int x, int y, int axis, DMGridData **gridData, GridKey *gridkey, float t[3])
{
+ DMGridData *grid = gridData[index];
+
if(axis == 0) {
if(x == gridSize - 1) {
if(y == gridSize - 1)
- sub_v3_v3v3(t, gridData[index][x + gridSize*(y - 1)].co, gridData[index][x - 1 + gridSize*(y - 1)].co);
+ sub_v3_v3v3(t, GRIDELEM_CO_AT(grid, x + gridSize*(y - 1), gridkey), GRIDELEM_CO_AT(grid, x - 1 + gridSize*(y - 1), gridkey));
else
- sub_v3_v3v3(t, gridData[index][x + gridSize*y].co, gridData[index][x - 1 + gridSize*y].co);
+ sub_v3_v3v3(t, GRIDELEM_CO_AT(grid, x + gridSize*y, gridkey), GRIDELEM_CO_AT(grid, x - 1 + gridSize*y, gridkey));
}
else
- sub_v3_v3v3(t, gridData[index][x + 1 + gridSize*y].co, gridData[index][x + gridSize*y].co);
+ sub_v3_v3v3(t, GRIDELEM_CO_AT(grid, x + 1 + gridSize*y, gridkey), GRIDELEM_CO_AT(grid, x + gridSize*y, gridkey));
}
else if(axis == 1) {
if(y == gridSize - 1) {
if(x == gridSize - 1)
- sub_v3_v3v3(t, gridData[index][x - 1 + gridSize*y].co, gridData[index][x - 1 + gridSize*(y - 1)].co);
+ sub_v3_v3v3(t, GRIDELEM_CO_AT(grid, x - 1 + gridSize*y, gridkey), GRIDELEM_CO_AT(grid, x - 1 + gridSize*(y - 1), gridkey));
else
- sub_v3_v3v3(t, gridData[index][x + gridSize*y].co, gridData[index][x + gridSize*(y - 1)].co);
+ sub_v3_v3v3(t, GRIDELEM_CO_AT(grid, x + gridSize*y, gridkey), GRIDELEM_CO_AT(grid, x + gridSize*(y - 1), gridkey));
}
else
- sub_v3_v3v3(t, gridData[index][x + gridSize*(y + 1)].co, gridData[index][x + gridSize*y].co);
+ sub_v3_v3v3(t, GRIDELEM_CO_AT(grid, x + gridSize*(y + 1), gridkey), GRIDELEM_CO_AT(grid, x + gridSize*y, gridkey));
+ }
+}
+
+#if 0
+static void debug_print_paintmask_grids(CustomData *grids, int gridsize)
+{
+ float *pmask;
+ int x, y;
+
+ printf("debug_print_paintmask_grids:\n");
+ pmask = CustomData_get_layer(grids, CD_PAINTMASK);
+
+ for(y = 0; y < gridsize; ++y) {
+ for(x = 0; x < gridsize; ++x, ++pmask)
+ printf("%.2f ", *pmask);
+ printf("\n");
}
+
+ printf("\n");
+}
+#endif
+
+/* XXX - move these to blenlib? */
+void add_v4_v4v4(float v[4], float a[4], float b[4])
+{
+ v[0] = a[0] + b[0];
+ v[1] = a[1] + b[1];
+ v[2] = a[2] + b[2];
+ v[3] = a[3] + b[3];
+}
+
+void add_v4_v4(float v[4], float a[4])
+{
+ v[0] += a[0];
+ v[1] += a[1];
+ v[2] += a[2];
+ v[3] += a[3];
}
-static void multiresModifier_disp_run(DerivedMesh *dm, Mesh *me, int invert, int add, DMGridData **oldGridData, int totlvl)
+void sub_v4_v4v4(float v[4], float a[4], float b[4])
+{
+ v[0] = a[0] - b[0];
+ v[1] = a[1] - b[1];
+ v[2] = a[2] - b[2];
+ v[3] = a[3] - b[3];
+}
+
+void clamp_v4_fl(float v[4], float min, float max)
+{
+ CLAMP(v[0], min, max);
+ CLAMP(v[1], min, max);
+ CLAMP(v[2], min, max);
+ CLAMP(v[3], min, max);
+}
+
+static void multiresModifier_disp_run(DerivedMesh *dm, Mesh *me, DispOp op, DMGridData **oldGridData, int totlvl)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh*)dm;
DMGridData **gridData, **subGridData;
MFace *mface = me->mface;
MDisps *mdisps = CustomData_get_layer(&me->fdata, CD_MDISPS);
+ CustomDataMultires *stored_grids;
int *gridOffset;
+ GridKey *gridkey;
int i, numGrids, gridSize, dGridSize, dSkip;
if(!mdisps) {
- if(invert)
+ if(op == CALC_DISPS)
mdisps = CustomData_add_layer(&me->fdata, CD_MDISPS, CD_DEFAULT, NULL, me->totface);
else
return;
@@ -594,15 +857,18 @@ static void multiresModifier_disp_run(DerivedMesh *dm, Mesh *me, int invert, int
gridData = dm->getGridData(dm);
gridOffset = dm->getGridOffset(dm);
subGridData = (oldGridData)? oldGridData: gridData;
+ gridkey = dm->getGridKey(dm);
dGridSize = multires_side_tot[totlvl];
dSkip = (dGridSize-1)/(gridSize-1);
+ stored_grids = CustomData_get_layer(&me->fdata, CD_GRIDS);
+
#pragma omp parallel for private(i) if(me->totface*gridSize*gridSize*4 >= CCG_OMP_LIMIT)
for(i = 0; i < me->totface; ++i) {
const int numVerts = mface[i].v4 ? 4 : 3;
MDisps *mdisp = &mdisps[i];
- int S, x, y, gIndex = gridOffset[i];
+ int S, x, y, j, gIndex = gridOffset[i];
/* when adding new faces in edit mode, need to allocate disps */
if(!mdisp->disps)
@@ -610,56 +876,99 @@ static void multiresModifier_disp_run(DerivedMesh *dm, Mesh *me, int invert, int
{
multires_reallocate_mdisps(me, mdisps, totlvl);
}
+
+ /* XXX: this doesn't cover all the cases */
+ if((gridkey->mask || gridkey->color) && !stored_grids)
+ #pragma omp critical
+ {
+ multires_sync_customdata(me, gridkey, totlvl);
+ stored_grids = CustomData_get_layer(&me->fdata, CD_GRIDS);
+ }
for(S = 0; S < numVerts; ++S, ++gIndex) {
DMGridData *grid = gridData[gIndex];
DMGridData *subgrid = subGridData[gIndex];
- float (*dispgrid)[3] = &mdisp->disps[S*dGridSize*dGridSize];
+ int stored_index = S*dGridSize*dGridSize;
+ float (*dispgrid)[3] = &mdisp->disps[stored_index];
for(y = 0; y < gridSize; y++) {
for(x = 0; x < gridSize; x++) {
- float *co = grid[x + y*gridSize].co;
- float *sco = subgrid[x + y*gridSize].co;
- float *no = subgrid[x + y*gridSize].no;
- float *data = dispgrid[dGridSize*y*dSkip + x*dSkip];
- float mat[3][3], tx[3], ty[3], disp[3], d[3];
+ int ccgdm_offset = x + y*gridSize;
+ int stored_offset = dGridSize*y*dSkip + x*dSkip;
+
+ float *co = GRIDELEM_CO_AT(grid, ccgdm_offset, gridkey);
+ float *sco = GRIDELEM_CO_AT(subgrid, ccgdm_offset, gridkey);
+ float *no = GRIDELEM_NO_AT(subgrid, ccgdm_offset, gridkey);
+ float *data = dispgrid[stored_offset];
+
+ float tan_to_ob_mat[3][3], tx[3], ty[3], disp[3], d[3];
/* construct tangent space matrix */
- grid_tangent(gridSize, gIndex, x, y, 0, subGridData, tx);
+ grid_tangent(gridSize, gIndex, x, y, 0, subGridData, gridkey, tx);
normalize_v3(tx);
- grid_tangent(gridSize, gIndex, x, y, 1, subGridData, ty);
+ grid_tangent(gridSize, gIndex, x, y, 1, subGridData, gridkey, ty);
normalize_v3(ty);
//mul_v3_fl(tx, 1.0f/(gridSize-1));
//mul_v3_fl(ty, 1.0f/(gridSize-1));
//cross_v3_v3v3(no, tx, ty);
- column_vectors_to_mat3(mat, tx, ty, no);
+ column_vectors_to_mat3(tan_to_ob_mat, tx, ty, no);
- if(!invert) {
- /* convert to object space and add */
- mul_v3_m3v3(disp, mat, data);
+ switch(op) {
+ case APPLY_DISPS:
+ /* Convert displacement to object space
+ and add to grid points */
+ mul_v3_m3v3(disp, tan_to_ob_mat, data);
add_v3_v3v3(co, sco, disp);
- }
- else if(!add) {
- /* convert difference to tangent space */
+ break;
+ case CALC_DISPS:
+ /* Calculate displacement between new and old
+ grid points and convert to tangent space. */
sub_v3_v3v3(disp, co, sco);
- invert_m3(mat);
- mul_v3_m3v3(data, mat, disp);
- }
- else {
- /* convert difference to tangent space */
- invert_m3(mat);
- mul_v3_m3v3(d, mat, co);
+ invert_m3(tan_to_ob_mat);
+ mul_v3_m3v3(data, tan_to_ob_mat, disp);
+ break;
+ case ADD_DISPS:
+ /* Convert subdivided displacements to tangent
+ space and add to the original displacements */
+ invert_m3(tan_to_ob_mat);
+ mul_v3_m3v3(d, tan_to_ob_mat, co);
add_v3_v3(data, d);
+ break;
+ }
+
+ /* Paint Masks */
+ for(j = 0; j < gridkey->mask; ++j) {
+ float *mask = &GRIDELEM_MASK_AT(grid, ccgdm_offset, gridkey)[j];
+ float *smask = &GRIDELEM_MASK_AT(subgrid, ccgdm_offset, gridkey)[j];
+ float *stored_mask_layer =
+ CustomData_multires_get_data(&stored_grids[i],
+ CD_PAINTMASK,
+ gridkey->mask_names[j]);
+ float *stored_mask = &stored_mask_layer[stored_index];
+
+ switch(op) {
+ case APPLY_DISPS:
+ *mask = *smask + stored_mask[stored_offset];
+ break;
+ case CALC_DISPS:
+ stored_mask[stored_offset] = *mask - *smask;
+ CLAMP(stored_mask[stored_offset], 0, 1);
+ break;
+ case ADD_DISPS:
+ stored_mask[stored_offset] += *mask;
+ CLAMP(stored_mask[stored_offset], 0, 1);
+ break;
+ }
}
}
}
}
}
- if(!invert) {
+ if(op == APPLY_DISPS) {
ccgSubSurf_stitchFaces(ccgdm->ss, 0, NULL, 0);
ccgSubSurf_updateNormals(ccgdm->ss, NULL, 0);
}
@@ -672,6 +981,7 @@ static void multiresModifier_update(DerivedMesh *dm)
Mesh *me;
MDisps *mdisps;
MultiresModifierData *mmd;
+ GridKey *gridkey;
ob = ccgdm->multires.ob;
me = ccgdm->multires.ob->data;
@@ -680,6 +990,10 @@ static void multiresModifier_update(DerivedMesh *dm)
CustomData_external_read(&me->fdata, &me->id, CD_MASK_MDISPS, me->totface);
mdisps = CustomData_get_layer(&me->fdata, CD_MDISPS);
+ /* use the same gridkey as the dm so that we don't try
+ to update layers that didn't exist before */
+ gridkey = dm->getGridKey(dm);
+
if(mdisps) {
int lvl = ccgdm->multires.lvl;
int totlvl = ccgdm->multires.totlvl;
@@ -696,10 +1010,10 @@ static void multiresModifier_update(DerivedMesh *dm)
else cddm = CDDM_from_mesh(me, NULL);
DM_set_only_copy(cddm, CD_MASK_BAREMESH);
- highdm = subsurf_dm_create_local(ob, cddm, totlvl, mmd->simple, 0);
+ highdm = subsurf_dm_create_local(ob, cddm, gridkey, totlvl, mmd->simple, 0);
/* create multires DM from original mesh and displacements */
- lowdm = multires_dm_create_local(ob, cddm, lvl, totlvl, mmd->simple);
+ lowdm = multires_dm_create_local(ob, cddm, gridkey, lvl, totlvl, mmd->simple);
cddm->release(cddm);
/* gather grid data */
@@ -710,19 +1024,20 @@ static void multiresModifier_update(DerivedMesh *dm)
lowGridData = lowdm->getGridData(lowdm);
gridData = dm->getGridData(dm);
- subGridData = MEM_callocN(sizeof(DMGridData*)*numGrids, "subGridData*");
- diffGrid = MEM_callocN(sizeof(DMGridData)*lowGridSize*lowGridSize, "diff");
+ /* backup subsurf grids */
+ subGridData = copy_grids(highGridData, numGrids, highGridSize, gridkey);
- for(i = 0; i < numGrids; ++i) {
- /* backup subsurf grids */
- subGridData[i] = MEM_callocN(sizeof(DMGridData)*highGridSize*highGridSize, "subGridData");
- memcpy(subGridData[i], highGridData[i], sizeof(DMGridData)*highGridSize*highGridSize);
+ diffGrid = MEM_callocN(GRIDELEM_SIZE(gridkey)*lowGridSize*lowGridSize, "diff");
- /* write difference of subsurf and displaced low level into high subsurf */
- for(j = 0; j < lowGridSize*lowGridSize; ++j)
- sub_v3_v3v3(diffGrid[j].co, gridData[i][j].co, lowGridData[i][j].co);
+ /* write difference of subsurf and displaced low level into high subsurf */
+ for(i = 0; i < numGrids; ++i) {
+ for(j = 0; j < lowGridSize*lowGridSize; ++j) {
+ int k;
+ for(k = 0; k < GRIDELEM_INTERP_COUNT(gridkey); ++k)
+ ((float*)GRIDELEM_AT(diffGrid, j, gridkey))[k] = ((float*)GRIDELEM_AT(gridData[i], j, gridkey))[k] - ((float*)GRIDELEM_AT(lowGridData[i], j, gridkey))[k];
+ }
- multires_copy_dm_grid(highGridData[i], diffGrid, highGridSize, lowGridSize);
+ multires_copy_dm_grid(highGridData[i], diffGrid, gridkey, highGridSize, lowGridSize);
}
/* lower level dm no longer needed at this point */
@@ -735,7 +1050,7 @@ static void multiresModifier_update(DerivedMesh *dm)
ccgSubSurf_updateLevels(ss, lvl, NULL, 0);
/* add to displacements */
- multiresModifier_disp_run(highdm, me, 1, 1, subGridData, mmd->totlvl);
+ multiresModifier_disp_run(highdm, me, ADD_DISPS, subGridData, mmd->totlvl);
/* free */
highdm->release(highdm);
@@ -750,10 +1065,10 @@ static void multiresModifier_update(DerivedMesh *dm)
else cddm = CDDM_from_mesh(me, NULL);
DM_set_only_copy(cddm, CD_MASK_BAREMESH);
- subdm = subsurf_dm_create_local(ob, cddm, mmd->totlvl, mmd->simple, 0);
+ subdm = subsurf_dm_create_local(ob, cddm, gridkey, mmd->totlvl, mmd->simple, 0);
cddm->release(cddm);
- multiresModifier_disp_run(dm, me, 1, 0, subdm->getGridData(subdm), mmd->totlvl);
+ multiresModifier_disp_run(dm, me, CALC_DISPS, subdm->getGridData(subdm), mmd->totlvl);
subdm->release(subdm);
}
@@ -762,8 +1077,15 @@ static void multiresModifier_update(DerivedMesh *dm)
void multires_stitch_grids(Object *ob)
{
- /* utility for smooth brush */
- if(ob && ob->derivedFinal) {
+ DerivedMesh *dm;
+
+ if(!ob)
+ return;
+
+ dm = ob->derivedFinal;
+
+ /* utility for smooth/blur brushes */
+ if(dm && dm->type == DM_TYPE_CCGDM) {
CCGDerivedMesh *ccgdm = (CCGDerivedMesh*)ob->derivedFinal;
CCGFace **faces;
int totface;
@@ -772,6 +1094,7 @@ void multires_stitch_grids(Object *ob)
BLI_pbvh_get_grid_updates(ccgdm->pbvh, 0, (void***)&faces, &totface);
if(totface) {
+ /* TODO: could improve performance by limiting to e.g. just coords or just colors */
ccgSubSurf_stitchFaces(ccgdm->ss, 0, faces, totface);
MEM_freeN(faces);
}
@@ -780,7 +1103,7 @@ void multires_stitch_grids(Object *ob)
}
DerivedMesh *multires_dm_create_from_derived(MultiresModifierData *mmd, int local_mmd, DerivedMesh *dm, Object *ob,
- int useRenderParams, int isFinalCalc)
+ GridKey *gridkey, int useRenderParams, int isFinalCalc)
{
Mesh *me= ob->data;
DerivedMesh *result;
@@ -792,7 +1115,7 @@ DerivedMesh *multires_dm_create_from_derived(MultiresModifierData *mmd, int loca
if(lvl == 0)
return dm;
- result = subsurf_dm_create_local(ob, dm, lvl,
+ result = subsurf_dm_create_local(ob, dm, gridkey, lvl,
mmd->simple, mmd->flags & eMultiresModifierFlag_ControlEdges);
if(!local_mmd) {
@@ -810,17 +1133,19 @@ DerivedMesh *multires_dm_create_from_derived(MultiresModifierData *mmd, int loca
numGrids = result->getNumGrids(result);
gridSize = result->getGridSize(result);
gridData = result->getGridData(result);
+ /* null gridkey can be passed in, so update it here */
+ gridkey = result->getGridKey(result);
subGridData = MEM_callocN(sizeof(DMGridData*)*numGrids, "subGridData*");
for(i = 0; i < numGrids; i++) {
- subGridData[i] = MEM_callocN(sizeof(DMGridData)*gridSize*gridSize, "subGridData");
- memcpy(subGridData[i], gridData[i], sizeof(DMGridData)*gridSize*gridSize);
+ subGridData[i] = MEM_callocN(GRIDELEM_SIZE(gridkey)*gridSize*gridSize, "subGridData");
+ memcpy(subGridData[i], gridData[i], GRIDELEM_SIZE(gridkey)*gridSize*gridSize);
}
multires_set_tot_mdisps(me, mmd->totlvl);
CustomData_external_read(&me->fdata, &me->id, CD_MASK_MDISPS, me->totface);
- multiresModifier_disp_run(result, ob->data, 0, 0, subGridData, mmd->totlvl);
+ multiresModifier_disp_run(result, ob->data, APPLY_DISPS, subGridData, mmd->totlvl);
for(i = 0; i < numGrids; i++)
MEM_freeN(subGridData[i]);
@@ -829,6 +1154,7 @@ DerivedMesh *multires_dm_create_from_derived(MultiresModifierData *mmd, int loca
return result;
}
+
/**** Old Multires code ****
***************************/
@@ -1109,13 +1435,13 @@ static void multires_mvert_to_ss(DerivedMesh *dm, MVert *mvert)
int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(f);
vd= ccgSubSurf_getFaceCenterData(f);
- copy_v3_v3(vd->co, mvert[i].co);
+ copy_v3_v3(GRIDELEM_CO(vd, gridkey), mvert[i].co);
i++;
for(S = 0; S < numVerts; S++) {
for(x = 1; x < gridSize - 1; x++, i++) {
vd= ccgSubSurf_getFaceGridEdgeData(ss, f, S, x);
- copy_v3_v3(vd->co, mvert[i].co);
+ copy_v3_v3(GRIDELEM_CO(vd, gridkey), mvert[i].co);
}
}
@@ -1123,7 +1449,7 @@ static void multires_mvert_to_ss(DerivedMesh *dm, MVert *mvert)
for(y = 1; y < gridSize - 1; y++) {
for(x = 1; x < gridSize - 1; x++, i++) {
vd= ccgSubSurf_getFaceGridData(ss, f, S, x, y);
- copy_v3_v3(vd->co, mvert[i].co);
+ copy_v3_v3(GRIDELEM_CO(vd, gridkey), mvert[i].co);
}
}
}
@@ -1136,7 +1462,7 @@ static void multires_mvert_to_ss(DerivedMesh *dm, MVert *mvert)
for(x = 1; x < edgeSize - 1; x++, i++) {
vd= ccgSubSurf_getEdgeData(ss, e, x);
- copy_v3_v3(vd->co, mvert[i].co);
+ copy_v3_v3(GRIDELEM_CO(vd, gridkey), mvert[i].co);
}
}
@@ -1145,7 +1471,7 @@ static void multires_mvert_to_ss(DerivedMesh *dm, MVert *mvert)
CCGVert *v = ccgdm->vertMap[index].vert;
vd= ccgSubSurf_getVertData(ss, v);
- copy_v3_v3(vd->co, mvert[i].co);
+ copy_v3_v3(GRIDELEM_CO(vd, gridkey), mvert[i].co);
i++;
}
@@ -1364,7 +1690,7 @@ void multires_load_old(Object *ob, Mesh *me)
mmd->lvl = mmd->totlvl;
orig = CDDM_from_mesh(me, NULL);
- dm = multires_dm_create_from_derived(mmd, 0, orig, ob, 0, 0);
+ dm = multires_dm_create_from_derived(mmd, 0, orig, ob, NULL, 0, 0);
multires_load_old_dm(dm, me, mmd->totlvl+1);
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index d9ee67920eb..c5a12d1fb4e 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -228,29 +228,6 @@ void object_free_display(Object *ob)
freedisplist(&ob->disp);
}
-void free_sculptsession(Object *ob)
-{
- if(ob && ob->sculpt) {
- SculptSession *ss = ob->sculpt;
- DerivedMesh *dm= ob->derivedFinal;
-
- if(ss->pbvh)
- BLI_pbvh_free(ss->pbvh);
- if(dm && dm->getPBVH)
- dm->getPBVH(NULL, dm); /* signal to clear */
-
- if(ss->texcache)
- MEM_freeN(ss->texcache);
-
- if(ss->layer_co)
- MEM_freeN(ss->layer_co);
-
- MEM_freeN(ss);
-
- ob->sculpt = NULL;
- }
-}
-
/* do not free object itself */
void free_object(Object *ob)
{
@@ -305,7 +282,7 @@ void free_object(Object *ob)
if(ob->bsoft) bsbFree(ob->bsoft);
if(ob->gpulamp.first) GPU_lamp_free(ob);
- free_sculptsession(ob);
+ free_paintsession(ob);
if(ob->pc_ids.first) BLI_freelistN(&ob->pc_ids);
}
@@ -1308,7 +1285,7 @@ Object *copy_object(Object *ob)
copy_constraints(&obn->constraints, &ob->constraints, TRUE);
obn->mode = 0;
- obn->sculpt = NULL;
+ obn->paint = NULL;
/* increase user numbers */
id_us_plus((ID *)obn->data);
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index ffb99c10c40..f2b64a3068b 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -25,6 +25,7 @@
* ***** END GPL LICENSE BLOCK *****
*/
+#include "MEM_guardedalloc.h"
#include "DNA_object_types.h"
#include "DNA_mesh_types.h"
@@ -32,9 +33,16 @@
#include "DNA_brush_types.h"
#include "BKE_brush.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_dmgrid.h"
#include "BKE_library.h"
#include "BKE_paint.h"
+#include "BKE_utildefines.h"
+#include "BLI_listbase.h"
+#include "BLI_pbvh.h"
+
+#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -109,3 +117,104 @@ void copy_paint(Paint *src, Paint *tar)
{
tar->brush= src->brush;
}
+
+/* Update the mask without doing a full object recalc */
+void paint_refresh_mask_display(Object *ob)
+{
+ if(ob && ob->paint && ob->paint->pbvh) {
+ BLI_pbvh_search_callback(ob->paint->pbvh, NULL, NULL,
+ BLI_pbvh_node_set_flags,
+ SET_INT_IN_POINTER(PBVH_UpdateColorBuffers));
+ }
+}
+
+
+float paint_mask_from_gridelem(DMGridData *elem, GridKey *gridkey,
+ CustomData *vdata)
+{
+ CustomDataLayer *cdl;
+ float mask = 0;
+ int i, ndx;
+
+ for(i=0; i < gridkey->mask; ++i) {
+ ndx = CustomData_get_named_layer_index(vdata,
+ CD_PAINTMASK,
+ gridkey->mask_names[i]);
+ cdl = &vdata->layers[ndx];
+
+ if(!(cdl->flag & CD_FLAG_ENABLED))
+ continue;
+
+ mask += GRIDELEM_MASK(elem, gridkey)[i] * cdl->strength;
+ }
+
+ CLAMP(mask, 0, 1);
+
+ return mask;
+}
+
+float paint_mask_from_vertex(CustomData *vdata, int vertex_index,
+ int pmask_totlayer, int pmask_first_layer)
+{
+ float mask = 0;
+ int i;
+
+ for(i = 0; i < pmask_totlayer; ++i) {
+ CustomDataLayer *cdl= vdata->layers + pmask_first_layer + i;
+
+ if(!(cdl->flag & CD_FLAG_ENABLED))
+ continue;
+
+ mask += ((float*)cdl->data)[vertex_index] * cdl->strength;
+ }
+
+ CLAMP(mask, 0, 1);
+
+ return mask;
+}
+
+void create_paintsession(Object *ob)
+{
+ if(ob->paint)
+ free_paintsession(ob);
+
+ ob->paint = MEM_callocN(sizeof(PaintSession), "PaintSession");
+}
+
+static void free_sculptsession(PaintSession *ps)
+{
+ if(ps && ps->sculpt) {
+ SculptSession *ss = ps->sculpt;
+
+ BLI_freelistN(&ss->hidden_areas);
+
+ if(ss->texcache)
+ MEM_freeN(ss->texcache);
+
+ if(ss->layer_co)
+ MEM_freeN(ss->layer_co);
+
+ MEM_freeN(ss);
+
+ ps->sculpt = NULL;
+ }
+}
+
+void free_paintsession(Object *ob)
+{
+ if(ob && ob->paint) {
+ PaintSession *ps = ob->paint;
+ DerivedMesh *dm= ob->derivedFinal;
+
+ free_sculptsession(ps);
+
+ if(ps->pbvh)
+ BLI_pbvh_free(ps->pbvh);
+
+ if(dm && dm->getPBVH)
+ dm->getPBVH(NULL, dm); /* signal to clear PBVH */
+
+ MEM_freeN(ps);
+ ob->paint = NULL;
+ }
+}
diff --git a/source/blender/blenkernel/intern/ptex.c b/source/blender/blenkernel/intern/ptex.c
new file mode 100644
index 00000000000..72218830446
--- /dev/null
+++ b/source/blender/blenkernel/intern/ptex.c
@@ -0,0 +1,445 @@
+/*
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2010 by Nicholas Bishop
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+
+#include "BKE_DerivedMesh.h"
+#include "BKE_dmgrid.h"
+#include "BKE_mesh.h"
+#include "BKE_subsurf.h"
+
+#include "BLI_math.h"
+
+#include "ptex.h"
+
+#include <assert.h>
+
+DerivedMesh *quad_dm_create_from_derived(DerivedMesh *dm)
+{
+ DerivedMesh *ccgdm;
+ SubsurfModifierData smd;
+ GridKey gridkey;
+
+ memset(&smd, 0, sizeof(SubsurfModifierData));
+ smd.levels = 1;
+ smd.subdivType = ME_SIMPLE_SUBSURF;
+ GRIDELEM_KEY_INIT(&gridkey, 1, 0, 0, 1);
+ ccgdm = subsurf_make_derived_from_derived(dm, &smd, &gridkey,
+ 0, NULL, 0, 0);
+
+ return ccgdm;
+}
+
+void ptex_elem_to_floats(int type, int channels, void *data, float *out)
+{
+ int i;
+
+ switch(type) {
+ case PTEX_DT_UINT8:
+ for(i = 0; i < channels; ++i)
+ out[i] = ((unsigned char*)data)[i] / 255.0;
+ break;
+ case PTEX_DT_UINT16:
+ for(i = 0; i < channels; ++i)
+ out[i] = ((unsigned char*)data)[i] / 65535.0;
+ break;
+ case PTEX_DT_FLOAT:
+ for(i = 0; i < channels; ++i)
+ out[i] = ((float*)data)[i];
+ break;
+ default:
+ break;
+ }
+}
+
+void ptex_elem_from_floats(int type, int channels, void *data, float *in)
+{
+ int i;
+
+ switch(type) {
+ case PTEX_DT_UINT8:
+ for(i = 0; i < channels; ++i)
+ ((unsigned char*)data)[i] = in[i] * 255;
+ break;
+ case PTEX_DT_UINT16:
+ for(i = 0; i < channels; ++i)
+ ((unsigned short*)data)[i] = in[i] * 65535;
+ break;
+ case PTEX_DT_FLOAT:
+ for(i = 0; i < channels; ++i)
+ ((float*)data)[i] = in[i];
+ break;
+ default:
+ break;
+ }
+}
+
+static void ptex_elem_to_floats_mul_add(MPtex *pt, void *data, float *out, float fac)
+{
+ int i;
+
+ switch(pt->type) {
+ case PTEX_DT_UINT8:
+ for(i = 0; i < pt->channels; ++i)
+ out[i] += (((unsigned char*)data)[i] / 255.0) * fac;
+ break;
+ case PTEX_DT_UINT16:
+ for(i = 0; i < pt->channels; ++i)
+ out[i] += (((unsigned char*)data)[i] / 65535.0) * fac;
+ break;
+ case PTEX_DT_FLOAT:
+ for(i = 0; i < pt->channels; ++i)
+ out[i] += ((float*)data)[i] * fac;
+ break;
+ default:
+ break;
+ }
+}
+
+/* get interpolated value for one texel */
+static void ptex_bilinear_interp(MPtex *pt, MPtexSubface *subface,
+ void *out, int layersize,
+ float x, float y, float *tmp)
+{
+ char *input_start = subface->data;
+ int rowlen = subface->res[0];
+ int xi = (int)x;
+ int yi = (int)y;
+ int xt = xi+1, yt = yi+1;
+ float s = x - xi;
+ float t = y - yi;
+ float u = 1 - s;
+ float v = 1 - t;
+
+ if(xt == subface->res[0])
+ --xt;
+ if(yt == subface->res[1])
+ --yt;
+
+ memset(tmp, 0, sizeof(float)*pt->channels);
+ ptex_elem_to_floats_mul_add(pt, input_start + layersize * (yi*rowlen+xi), tmp, u*v);
+ ptex_elem_to_floats_mul_add(pt, input_start + layersize * (yi*rowlen+xt), tmp, s*v);
+ ptex_elem_to_floats_mul_add(pt, input_start + layersize * (yt*rowlen+xt), tmp, s*t);
+ ptex_elem_to_floats_mul_add(pt, input_start + layersize * (yt*rowlen+xi), tmp, u*t);
+ ptex_elem_from_floats(pt->type, pt->channels, out, tmp);
+}
+
+/* interpolate subface to new resolution */
+void ptex_subface_scale(MPtex *pt, MPtexSubface *subface, int ures, int vres)
+{
+ float ui, vi, ui_step, vi_step;
+ float *tmp;
+ char *new_data, *new_data_start;
+ int u, v, layersize;
+
+ layersize = pt->channels * ptex_data_size(pt->type);
+
+ new_data_start = new_data =
+ MEM_callocN(layersize * ures * vres, "ptex_subface_scale.new_data");
+
+ /* tmp buffer used in interpolation */
+ tmp = MEM_callocN(sizeof(float) * pt->channels, "ptex_subface_scale.tmp");
+
+ ui_step = subface->res[0] / (float)ures;
+ vi_step = subface->res[1] / (float)vres;
+ for(v = 0, vi = 0; v < vres; ++v, vi += vi_step) {
+ for(u = 0, ui = 0; u < ures; ++u, ui += ui_step, new_data += layersize) {
+ ptex_bilinear_interp(pt, subface, new_data, layersize, ui, vi, tmp);
+ }
+ }
+
+ MEM_freeN(subface->data);
+ subface->data = new_data_start;
+
+ subface->res[0] = ures;
+ subface->res[1] = vres;
+
+ MEM_freeN(tmp);
+}
+
+/* copy data to/from ptex file format and internal MPtex format */
+static void ptex_transfer_filedata(MPtex *pt, int offset, char *file_data_start, int from_file)
+{
+ char *mptex_data, *file_data;
+ char **src, **dest;
+ int file_res[2], file_half_res[2];
+ int i, u, v, layersize;
+
+ layersize = pt->channels * ptex_data_size(pt->type);
+
+ if(pt->totsubface == 4) {
+ file_res[0] = pt->subfaces[1].res[0] << 1;
+ file_res[1] = pt->subfaces[1].res[1] << 1;
+ }
+ else {
+ file_res[0] = pt->subfaces[offset].res[1];
+ file_res[1] = pt->subfaces[offset].res[0];
+ }
+
+ file_half_res[0] = file_res[0] >> 1;
+ file_half_res[1] = file_res[1] >> 1;
+
+ if(from_file) {
+ src = &file_data;
+ dest = &mptex_data;
+ }
+ else {
+ src = &mptex_data;
+ dest = &file_data;
+ }
+
+ if(pt->totsubface == 4) {
+ /* save quad subfaces as one face */
+
+ for(i = 0; i < 4; ++i) {
+ MPtexSubface *subface = &pt->subfaces[i];
+ int file_center_offset[2], file_step, file_row_step;
+
+ switch(i) {
+ case 0:
+ file_center_offset[0] = -1;
+ file_center_offset[1] = -1;
+ file_step = -file_res[0];
+ file_row_step = file_res[0] * file_half_res[1] - 1;
+ break;
+ case 1:
+ file_center_offset[0] = 0;
+ file_center_offset[1] = -1;
+ file_step = 1;
+ file_row_step = -file_res[0] - file_half_res[0];
+ break;
+ case 2:
+ file_center_offset[0] = 0;
+ file_center_offset[1] = 0;
+ file_step = file_res[0];
+ file_row_step = -file_res[0] * file_half_res[1] + 1;
+ break;
+ case 3:
+ file_center_offset[0] = -1;
+ file_center_offset[1] = 0;
+ file_step = -1;
+ file_row_step = file_res[0] + file_half_res[0];
+ break;
+ }
+
+ mptex_data = subface->data;
+ file_data = file_data_start +
+ layersize * (file_res[0] * (file_half_res[1]+file_center_offset[1]) +
+ file_half_res[0]+file_center_offset[0]);
+
+ for(v = 0; v < subface->res[1]; ++v) {
+ for(u = 0; u < subface->res[0]; ++u) {
+ memcpy(*dest, *src, layersize);
+ mptex_data += layersize;
+ file_data += layersize * file_step;
+ }
+ file_data += layersize * file_row_step;
+ }
+ }
+ }
+ else {
+ mptex_data = pt->subfaces[offset].data;
+ file_data = file_data_start;
+
+ for(v = 0; v < file_res[1]; ++v) {
+ for(u = 0; u < file_res[0]; ++u) {
+ mptex_data = (char*)pt->subfaces[offset].data +
+ layersize * ((file_res[0] - u - 1) * file_res[1] +
+ (file_res[1] - v - 1));
+ file_data = file_data_start + layersize * (v*file_res[0]+u);
+
+ memcpy(*dest, *src, layersize);
+ }
+ }
+ }
+}
+
+/* creates a new CD_MPTEX layer and loads ptex_texture into it */
+void ptex_layer_from_file(Mesh *me, PtexTextureHandle *ptex_texture)
+{
+ MPtex *mptex;
+ PtexDataType ptex_data_type;
+ int channels;
+ int i, j, layersize, active_offset;
+
+ channels = ptex_texture_num_channels(ptex_texture);
+ ptex_data_type = ptex_texture_data_type(ptex_texture);
+
+ /* number of bytes for one ptex element */
+ layersize = ptex_data_size(ptex_data_type) * channels;
+
+ active_offset = CustomData_number_of_layers(&me->fdata, CD_MPTEX);
+ mptex = CustomData_add_layer(&me->fdata, CD_MPTEX, CD_CALLOC,
+ NULL, me->totface);
+ CustomData_set_layer_active(&me->fdata, CD_MPTEX, active_offset);
+
+ for(i = 0, j = 0; i < me->totface; ++i) {
+ int S = me->mface[i].v4 ? 4 : 3;
+ int k, file_totsubface;
+
+ mptex[i].type = ptex_data_type;
+ mptex[i].channels = channels;
+ mptex[i].totsubface = S;
+
+ /* quads don't have subfaces in ptex files */
+ file_totsubface = (S==4)? 1 : S;
+
+ for(k = 0; k < file_totsubface; ++k) {
+ PtexFaceInfoHandle *ptex_face;
+ PtexResHandle *ptex_res;
+ int l, file_res[2], file_half_res[2], faceid;
+ char *filedata;
+
+ faceid = j+k;
+
+ ptex_face = ptex_texture_get_face_info(ptex_texture, faceid);
+ ptex_res = ptex_face_info_get_res(ptex_face);
+
+ file_res[0] = ptex_res_u(ptex_res);
+ file_res[1] = ptex_res_v(ptex_res);
+ file_half_res[0] = file_res[0] >> 1;
+ file_half_res[1] = file_res[1] >> 1;
+
+ filedata = MEM_callocN(layersize * file_res[0] * file_res[1], "Ptex data from file");
+ ptex_texture_get_data(ptex_texture, faceid, filedata, 0, ptex_res);
+
+ /* allocate mem for ptex data, set subface resolutions */
+ if(S==4) {
+ int ures, vres;
+
+ /* use quarter resolution for quad subfaces */
+ ures = file_half_res[0];
+ vres = file_half_res[1];
+
+ /* TODO: handle 1xV and Ux1 inputs */
+ assert(ures > 0 && vres > 0);
+
+ for(l = 0; l < 4; ++l) {
+ SWAP(int, ures, vres);
+
+ mptex[i].subfaces[l].res[0] = ures;
+ mptex[i].subfaces[l].res[1] = vres;
+ mptex[i].subfaces[l].data =
+ MEM_callocN(layersize * ures * vres,
+ "Ptex quad data from file");
+ }
+
+
+ }
+ else {
+ mptex[i].subfaces[k].res[0] = file_res[1];
+ mptex[i].subfaces[k].res[1] = file_res[0];
+ mptex[i].subfaces[k].data =
+ MEM_callocN(layersize * file_res[0] * file_res[1],
+ "Ptex tri data from file");
+ }
+
+ ptex_transfer_filedata(&mptex[i], k, filedata, 1);
+ MEM_freeN(filedata);
+ }
+
+ j += file_totsubface;
+ }
+
+ /* data is all copied, can release ptex file */
+ ptex_texture_release(ptex_texture);
+}
+
+int ptex_layer_save_file(struct Mesh *me, const char *filename)
+{
+ MPtex *mptex;
+ PtexWriterHandle *ptex_writer;
+ char *file_data;
+ int i, j, totface, faceid;
+
+ mptex = CustomData_get_layer(&me->fdata, CD_MPTEX);
+
+ for(i = 0, totface = 0; i < me->totface; ++i)
+ totface += (me->mface[i].v4 ? 4 : 3);
+
+ ptex_writer = ptex_writer_open(filename, mptex->type, mptex->channels, 0, totface, 1);
+ if(!ptex_writer)
+ return -1;
+
+ for(i = 0, faceid = 0; i < me->totface; ++i, ++mptex) {
+ PtexFaceInfoHandle *face_info;
+ int adjfaces[4] = {0,0,0,0}, adjedges[4] = {0,0,0,0};
+ int layersize;
+
+ layersize = ptex_data_size(mptex->type) * mptex->channels;
+
+ /* TODO: adjacency data (needed for filtering) */
+
+ if(mptex->totsubface == 4) {
+ int res[2];
+
+ res[0] = mptex->subfaces[1].res[0]*2;
+ res[1] = mptex->subfaces[1].res[1]*2;
+
+ file_data = MEM_callocN(res[0] * res[1] * layersize,
+ "mptex save quad data");
+
+ ptex_transfer_filedata(mptex, 0, file_data, 0);
+
+ face_info = ptex_face_info_new(res[0], res[1],
+ adjfaces, adjedges, 0);
+ ptex_writer_write_face(ptex_writer, faceid, face_info, file_data, 0);
+ faceid += 1;
+
+ MEM_freeN(file_data);
+ }
+ else if(mptex->totsubface == 3) {
+ for(j = 0; j < mptex->totsubface; ++j, ++faceid) {
+ MPtexSubface *subface = &mptex->subfaces[j];
+
+ file_data =
+ MEM_callocN(subface->res[0] * subface->res[1] * layersize,
+ "mptex save subface data");
+
+ ptex_transfer_filedata(mptex, j, file_data, 0);
+
+ face_info = ptex_face_info_new(subface->res[1],
+ subface->res[0],
+ adjfaces, adjedges, 1);
+ ptex_writer_write_face(ptex_writer, faceid, face_info,
+ file_data, 0);
+
+ MEM_freeN(file_data);
+ }
+ }
+ }
+
+ ptex_writer_release(ptex_writer);
+
+ return 0;
+}
diff --git a/source/blender/blenkernel/intern/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c
index f64854f90de..ceb693c4ecc 100644
--- a/source/blender/blenkernel/intern/shrinkwrap.c
+++ b/source/blender/blenkernel/intern/shrinkwrap.c
@@ -564,7 +564,7 @@ void shrinkwrapModifier_deform(ShrinkwrapModifierData *smd, Scene *scene, Object
ssmd.subdivType = ME_CC_SUBSURF; //catmull clark
ssmd.levels = smd->subsurfLevels; //levels
- ss_mesh = subsurf_make_derived_from_derived(dm, &ssmd, FALSE, NULL, 0, 0);
+ ss_mesh = subsurf_make_derived_from_derived(dm, &ssmd, NULL, FALSE, NULL, 0, 0);
if(ss_mesh)
{
diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c
index d6486c3ee4d..90a8ca03079 100644
--- a/source/blender/blenkernel/intern/subsurf_ccg.c
+++ b/source/blender/blenkernel/intern/subsurf_ccg.c
@@ -27,6 +27,7 @@
* ***** END GPL LICENSE BLOCK *****
*/
+#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
@@ -59,12 +60,45 @@
#include "BIF_gl.h"
#include "BIF_glutil.h"
+#include "GPU_buffers.h"
#include "GPU_draw.h"
#include "GPU_extensions.h"
#include "GPU_material.h"
#include "CCGSubSurf.h"
+/* From BKE_dmgrid.h */
+int gridelem_active_offset(CustomData *data, GridKey *gridkey, int type)
+{
+ char (*names)[32] = NULL;
+ int active, tot, i;
+
+ active = CustomData_get_active_layer_index(data, type);
+
+ if(active == -1)
+ return -1;
+
+ switch(type) {
+ case CD_MCOL:
+ tot = gridkey->color;
+ names = gridkey->color_names;
+ break;
+ case CD_PAINTMASK:
+ tot = gridkey->mask;
+ names = gridkey->mask_names;
+ break;
+ default:
+ return -1;
+ }
+
+ for(i = 0; i < tot; ++i) {
+ if(!strcmp(names[i], data->layers[active].name))
+ return i;
+ }
+
+ return -1;
+}
+
static int ccgDM_getVertMapIndex(CCGSubSurf *ss, CCGVert *v);
static int ccgDM_getEdgeMapIndex(CCGSubSurf *ss, CCGEdge *e);
static int ccgDM_getFaceMapIndex(CCGSubSurf *ss, CCGFace *f);
@@ -89,7 +123,7 @@ static void arena_release(CCGAllocatorHDL a) {
BLI_memarena_free(a);
}
-static CCGSubSurf *_getSubSurf(CCGSubSurf *prevSS, int subdivLevels, int useAging, int useArena, int useFlatSubdiv) {
+static CCGSubSurf *_getSubSurf(CCGSubSurf *prevSS, GridKey *gridkey, int subdivLevels, int useAging, int useArena, int useFlatSubdiv) {
CCGMeshIFC ifc;
CCGSubSurf *ccgSS;
@@ -116,7 +150,11 @@ static CCGSubSurf *_getSubSurf(CCGSubSurf *prevSS, int subdivLevels, int useAgin
} else {
ifc.vertUserSize = ifc.edgeUserSize = ifc.faceUserSize = 8;
}
- ifc.vertDataSize = sizeof(DMGridData);
+ ifc.vertDataSize = GRIDELEM_SIZE(gridkey);
+ ifc.finterpCount = GRIDELEM_INTERP_COUNT(gridkey);
+ ifc.gridkey = *gridkey;
+ ifc.gridkey.color_names = MEM_dupallocN(gridkey->color_names);
+ ifc.gridkey.mask_names = MEM_dupallocN(gridkey->mask_names);
if (useArena) {
CCGAllocatorIFC allocatorIFC;
@@ -136,7 +174,7 @@ static CCGSubSurf *_getSubSurf(CCGSubSurf *prevSS, int subdivLevels, int useAgin
ccgSubSurf_setUseAgeCounts(ccgSS, 1, 8, 8, 8);
}
- ccgSubSurf_setCalcVertexNormals(ccgSS, 1, BLI_STRUCT_OFFSET(DMGridData, no));
+ ccgSubSurf_setCalcVertexNormals(ccgSS, 1, GRIDELEM_NO_OFFSET(gridkey));
return ccgSS;
}
@@ -316,12 +354,15 @@ static void set_subsurf_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh *result,
int index, gridSize, gridFaces, edgeSize, totface, x, y, S;
MTFace *dmtface = CustomData_get_layer_n(&dm->faceData, CD_MTFACE, n);
MTFace *tface = CustomData_get_layer_n(&result->faceData, CD_MTFACE, n);
+ GridKey gridkey;
if(!dmtface || !tface)
return;
+ GRIDELEM_KEY_INIT(&gridkey, 1, 0, 0, 0); /* TODO */
+
/* create a CCGSubSurf from uv's */
- uvss = _getSubSurf(NULL, ccgSubSurf_getSubdivisionLevels(ss), 0, 1, 0);
+ uvss = _getSubSurf(NULL, &gridkey, ccgSubSurf_getSubdivisionLevels(ss), 0, 1, 0);
if(!ss_sync_from_uv(uvss, ss, dm, dmtface)) {
ccgSubSurf_free(uvss);
@@ -356,10 +397,10 @@ static void set_subsurf_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh *result,
for(y = 0; y < gridFaces; y++) {
for(x = 0; x < gridFaces; x++) {
- float *a = faceGridData[(y + 0)*gridSize + x + 0].co;
- float *b = faceGridData[(y + 0)*gridSize + x + 1].co;
- float *c = faceGridData[(y + 1)*gridSize + x + 1].co;
- float *d = faceGridData[(y + 1)*gridSize + x + 0].co;
+ float *a = GRIDELEM_CO_AT(faceGridData, (y + 0)*gridSize + x + 0, &gridkey);
+ float *b = GRIDELEM_CO_AT(faceGridData, (y + 0)*gridSize + x + 1, &gridkey);
+ float *c = GRIDELEM_CO_AT(faceGridData, (y + 1)*gridSize + x + 1, &gridkey);
+ float *d = GRIDELEM_CO_AT(faceGridData, (y + 1)*gridSize + x + 0, &gridkey);
tf->uv[0][0] = a[0]; tf->uv[0][1] = a[1];
tf->uv[1][0] = d[0]; tf->uv[1][1] = d[1];
@@ -421,14 +462,14 @@ static void calc_ss_weights(int gridFaces,
}
static void ss_sync_from_derivedmesh(CCGSubSurf *ss, DerivedMesh *dm,
- float (*vertexCos)[3], int useFlatSubdiv)
+ float (*vertexCos)[3], int useFlatSubdiv)
{
float creaseFactor = (float) ccgSubSurf_getSubdivisionLevels(ss);
CCGVertHDL fVerts[4];
int totvert = dm->getNumVerts(dm);
int totedge = dm->getNumEdges(dm);
int totface = dm->getNumFaces(dm);
- int i;
+ int i, j;
int *index;
MVert *mvert = dm->getVertArray(dm);
MEdge *medge = dm->getEdgeArray(dm);
@@ -436,23 +477,92 @@ static void ss_sync_from_derivedmesh(CCGSubSurf *ss, DerivedMesh *dm,
MVert *mv;
MEdge *me;
MFace *mf;
+ float *vertData;
+ GridKey *gridkey = ccgSubSurf_getGridKey(ss);
+ float (*colors)[4] = NULL;
+ int pmask_layer_count, pmask_first_layer;
ccgSubSurf_initFullSync(ss);
mv = mvert;
index = (int *)dm->getVertDataArray(dm, CD_ORIGINDEX);
+
+ pmask_layer_count = CustomData_number_of_layers(&dm->vertData, CD_PAINTMASK);
+ pmask_first_layer = CustomData_get_layer_index(&dm->vertData, CD_PAINTMASK);
+
+ assert(gridkey->mask == 0 || gridkey->mask == pmask_layer_count);
+
+ vertData = MEM_callocN(GRIDELEM_SIZE(gridkey), "vertData");
+
+ /* for editable subdivided colors, find the average mcol for each vert */
+ if(gridkey->color) {
+ int *users;
+ int k;
+
+ colors = MEM_callocN(sizeof(float)*4*gridkey->color*totvert,
+ "ss_sync_from_derivedmesh.colors");
+ users = MEM_callocN(sizeof(int)*totvert,
+ "ss_sync_from_derivedmesh.users");
+
+ for(i = 0; i < totface; ++i) {
+ MFace *f = mface + i;
+ int S = f->v4 ? 4 : 3;
+
+ for(j = 0; j < S; ++j) {
+ int vndx = (&f->v1)[j];
+
+ ++users[vndx];
+
+ for(k = 0; k < gridkey->color; ++k) {
+ MCol *mcol = CustomData_get_layer_named(&dm->faceData,
+ CD_MCOL,
+ gridkey->color_names[k]);
+
+ colors[vndx*gridkey->color + k][0] += mcol[i*4+j].b;
+ colors[vndx*gridkey->color + k][1] += mcol[i*4+j].g;
+ colors[vndx*gridkey->color + k][2] += mcol[i*4+j].r;
+ colors[vndx*gridkey->color + k][3] += mcol[i*4+j].a;
+
+ }
+ }
+ }
+
+ /* divide by number of faces sharing the corner
+ also convert from [0,255] to [0,1] */
+ for(i = 0; i < totvert; ++i) {
+ float inv = 1.0f / (users[i] * 255);
+ for(j = 0; j < gridkey->color; ++j) {
+ colors[i*gridkey->color + j][0] *= inv;
+ colors[i*gridkey->color + j][1] *= inv;
+ colors[i*gridkey->color + j][2] *= inv;
+ colors[i*gridkey->color + j][3] *= inv;
+ }
+ }
+
+ MEM_freeN(users);
+ }
+
for(i = 0; i < totvert; i++, mv++) {
CCGVert *v;
- if(vertexCos) {
- ccgSubSurf_syncVert(ss, SET_INT_IN_POINTER(i), vertexCos[i], 0, &v);
- } else {
- ccgSubSurf_syncVert(ss, SET_INT_IN_POINTER(i), mv->co, 0, &v);
- }
+ copy_v3_v3(vertData, vertexCos ? vertexCos[i] : mv->co);
+
+ /* copy color data */
+ for(j = 0; j < gridkey->color; ++j)
+ memcpy(&vertData[3 + 4*j], colors[i*gridkey->color + j], sizeof(float)*4);
+
+ /* copy paint mask data */
+ for(j = 0; j < gridkey->mask; ++j)
+ vertData[3 + gridkey->color*4 + j] = ((float*)dm->vertData.layers[pmask_first_layer+j].data)[i];
+
+ ccgSubSurf_syncVert(ss, SET_INT_IN_POINTER(i), vertData, 0, &v);
((int*)ccgSubSurf_getVertUserData(ss, v))[1] = (index)? *index++: i;
}
+ MEM_freeN(vertData);
+ if(colors) MEM_freeN(colors);
+
me = medge;
index = (int *)dm->getEdgeDataArray(dm, CD_ORIGINDEX);
for(i = 0; i < totedge; i++, me++) {
@@ -478,7 +588,7 @@ static void ss_sync_from_derivedmesh(CCGSubSurf *ss, DerivedMesh *dm,
fVerts[2] = SET_INT_IN_POINTER(mf->v3);
fVerts[3] = SET_INT_IN_POINTER(mf->v4);
- // this is very bad, means mesh is internally consistent.
+ // this is very bad, means mesh is internally inconsistent.
// it is not really possible to continue without modifying
// other parts of code significantly to handle missing faces.
// since this really shouldn't even be possible we just bail.
@@ -524,6 +634,7 @@ static void ccgDM_getMinMax(DerivedMesh *dm, float min_r[3], float max_r[3]) {
CCGFaceIterator *fi = ccgSubSurf_getFaceIterator(ss);
int i, edgeSize = ccgSubSurf_getEdgeSize(ss);
int gridSize = ccgSubSurf_getGridSize(ss);
+ GridKey *gridkey = ccgSubSurf_getGridKey(ss);
if (!ccgSubSurf_getNumVerts(ss))
min_r[0] = min_r[1] = min_r[2] = max_r[0] = max_r[1] = max_r[2] = 0.0;
@@ -540,7 +651,7 @@ static void ccgDM_getMinMax(DerivedMesh *dm, float min_r[3], float max_r[3]) {
DMGridData *edgeData = ccgSubSurf_getEdgeDataArray(ss, e);
for (i=0; i<edgeSize; i++)
- DO_MINMAX(edgeData[i].co, min_r, max_r);
+ DO_MINMAX(GRIDELEM_CO_AT(edgeData, i, gridkey), min_r, max_r);
}
for (; !ccgFaceIterator_isStopped(fi); ccgFaceIterator_next(fi)) {
@@ -552,7 +663,7 @@ static void ccgDM_getMinMax(DerivedMesh *dm, float min_r[3], float max_r[3]) {
for (y=0; y<gridSize; y++)
for (x=0; x<gridSize; x++)
- DO_MINMAX(faceGridData[y*gridSize + x].co, min_r, max_r);
+ DO_MINMAX(GRIDELEM_CO_AT(faceGridData, y*gridSize + x, gridkey), min_r, max_r);
}
}
@@ -580,6 +691,7 @@ static void ccgDM_getFinalVert(DerivedMesh *dm, int vertNum, MVert *mv)
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh*) dm;
CCGSubSurf *ss = ccgdm->ss;
+ GridKey *gridkey = ccgSubSurf_getGridKey(ss);
DMGridData *vd;
int i;
@@ -613,15 +725,15 @@ static void ccgDM_getFinalVert(DerivedMesh *dm, int vertNum, MVert *mv)
offset = vertNum - ccgdm->faceMap[i].startVert;
if(offset < 1) {
vd = ccgSubSurf_getFaceCenterData(f);
- copy_v3_v3(mv->co, vd->co);
- normal_float_to_short_v3(mv->no, vd->no);
+ copy_v3_v3(mv->co, GRIDELEM_CO(vd, gridkey));
+ normal_float_to_short_v3(mv->no, GRIDELEM_NO(vd, gridkey));
} else if(offset < gridSideEnd) {
offset -= 1;
grid = offset / gridSideVerts;
x = offset % gridSideVerts + 1;
vd = ccgSubSurf_getFaceGridEdgeData(ss, f, grid, x);
- copy_v3_v3(mv->co, vd->co);
- normal_float_to_short_v3(mv->no, vd->no);
+ copy_v3_v3(mv->co, GRIDELEM_CO(vd, gridkey));
+ normal_float_to_short_v3(mv->no, GRIDELEM_NO(vd, gridkey));
} else if(offset < gridInternalEnd) {
offset -= gridSideEnd;
grid = offset / gridInternalVerts;
@@ -629,8 +741,8 @@ static void ccgDM_getFinalVert(DerivedMesh *dm, int vertNum, MVert *mv)
y = offset / gridSideVerts + 1;
x = offset % gridSideVerts + 1;
vd = ccgSubSurf_getFaceGridData(ss, f, grid, x, y);
- copy_v3_v3(mv->co, vd->co);
- normal_float_to_short_v3(mv->no, vd->no);
+ copy_v3_v3(mv->co, GRIDELEM_CO(vd, gridkey));
+ normal_float_to_short_v3(mv->no, GRIDELEM_NO(vd, gridkey));
}
} else if((vertNum < ccgdm->vertMap[0].startVert) && (ccgSubSurf_getNumEdges(ss) > 0)) {
/* this vert comes from edge data */
@@ -646,8 +758,8 @@ static void ccgDM_getFinalVert(DerivedMesh *dm, int vertNum, MVert *mv)
x = vertNum - ccgdm->edgeMap[i].startVert + 1;
vd = ccgSubSurf_getEdgeData(ss, e, x);
- copy_v3_v3(mv->co, vd->co);
- normal_float_to_short_v3(mv->no, vd->no);
+ copy_v3_v3(mv->co, GRIDELEM_CO(vd, gridkey));
+ normal_float_to_short_v3(mv->no, GRIDELEM_NO(vd, gridkey));
} else {
/* this vert comes from vert data */
CCGVert *v;
@@ -655,8 +767,8 @@ static void ccgDM_getFinalVert(DerivedMesh *dm, int vertNum, MVert *mv)
v = ccgdm->vertMap[i].vert;
vd = ccgSubSurf_getVertData(ss, v);
- copy_v3_v3(mv->co, vd->co);
- normal_float_to_short_v3(mv->no, vd->no);
+ copy_v3_v3(mv->co, GRIDELEM_CO(vd, gridkey));
+ normal_float_to_short_v3(mv->no, GRIDELEM_NO(vd, gridkey));
}
}
@@ -808,6 +920,7 @@ static void ccgDM_copyFinalVertArray(DerivedMesh *dm, MVert *mvert)
int totvert, totedge, totface;
int gridSize = ccgSubSurf_getGridSize(ss);
int edgeSize = ccgSubSurf_getEdgeSize(ss);
+ GridKey *gridkey = ccgSubSurf_getGridKey(ss);
int i = 0;
totface = ccgSubSurf_getNumFaces(ss);
@@ -816,15 +929,15 @@ static void ccgDM_copyFinalVertArray(DerivedMesh *dm, MVert *mvert)
int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(f);
vd= ccgSubSurf_getFaceCenterData(f);
- copy_v3_v3(mvert[i].co, vd->co);
- normal_float_to_short_v3(mvert[i].no, vd->no);
+ copy_v3_v3(mvert[i].co, GRIDELEM_CO(vd, gridkey));
+ normal_float_to_short_v3(mvert[i].no, GRIDELEM_NO(vd, gridkey));
i++;
for(S = 0; S < numVerts; S++) {
for(x = 1; x < gridSize - 1; x++, i++) {
vd= ccgSubSurf_getFaceGridEdgeData(ss, f, S, x);
- copy_v3_v3(mvert[i].co, vd->co);
- normal_float_to_short_v3(mvert[i].no, vd->no);
+ copy_v3_v3(mvert[i].co, GRIDELEM_CO(vd, gridkey));
+ normal_float_to_short_v3(mvert[i].no, GRIDELEM_NO(vd, gridkey));
}
}
@@ -832,8 +945,8 @@ static void ccgDM_copyFinalVertArray(DerivedMesh *dm, MVert *mvert)
for(y = 1; y < gridSize - 1; y++) {
for(x = 1; x < gridSize - 1; x++, i++) {
vd= ccgSubSurf_getFaceGridData(ss, f, S, x, y);
- copy_v3_v3(mvert[i].co, vd->co);
- normal_float_to_short_v3(mvert[i].no, vd->no);
+ copy_v3_v3(mvert[i].co, GRIDELEM_CO(vd, gridkey));
+ normal_float_to_short_v3(mvert[i].no, GRIDELEM_NO(vd, gridkey));
}
}
}
@@ -846,9 +959,9 @@ static void ccgDM_copyFinalVertArray(DerivedMesh *dm, MVert *mvert)
for(x = 1; x < edgeSize - 1; x++, i++) {
vd= ccgSubSurf_getEdgeData(ss, e, x);
- copy_v3_v3(mvert[i].co, vd->co);
+ copy_v3_v3(mvert[i].co, GRIDELEM_CO(vd, gridkey));
/* XXX, This gives errors with -fpe, the normals dont seem to be unit length - campbell */
- normal_float_to_short_v3(mvert[i].no, vd->no);
+ normal_float_to_short_v3(mvert[i].no, GRIDELEM_NO(vd, gridkey));
}
}
@@ -857,8 +970,8 @@ static void ccgDM_copyFinalVertArray(DerivedMesh *dm, MVert *mvert)
CCGVert *v = ccgdm->vertMap[index].vert;
vd= ccgSubSurf_getVertData(ss, v);
- copy_v3_v3(mvert[i].co, vd->co);
- normal_float_to_short_v3(mvert[i].no, vd->no);
+ copy_v3_v3(mvert[i].co, GRIDELEM_CO(vd, gridkey));
+ normal_float_to_short_v3(mvert[i].no, GRIDELEM_NO(vd, gridkey));
i++;
}
}
@@ -1070,6 +1183,7 @@ static void ccgdm_getVertCos(DerivedMesh *dm, float (*cos)[3]) {
static void ccgDM_foreachMappedVert(DerivedMesh *dm, void (*func)(void *userData, int index, float *co, float *no_f, short *no_s), void *userData) {
CCGDerivedMesh *ccgdm = (CCGDerivedMesh*) dm;
CCGVertIterator *vi = ccgSubSurf_getVertIterator(ccgdm->ss);
+ GridKey *gridkey = ccgSubSurf_getGridKey(ccgdm->ss);
for (; !ccgVertIterator_isStopped(vi); ccgVertIterator_next(vi)) {
CCGVert *v = ccgVertIterator_getCurrent(vi);
@@ -1077,7 +1191,7 @@ static void ccgDM_foreachMappedVert(DerivedMesh *dm, void (*func)(void *userData
int index = ccgDM_getVertMapIndex(ccgdm->ss, v);
if (index!=-1)
- func(userData, index, vd->co, vd->no, NULL);
+ func(userData, index, GRIDELEM_CO(vd, gridkey), GRIDELEM_NO(vd, gridkey), NULL);
}
ccgVertIterator_free(vi);
@@ -1087,6 +1201,7 @@ static void ccgDM_foreachMappedEdge(DerivedMesh *dm, void (*func)(void *userData
CCGSubSurf *ss = ccgdm->ss;
CCGEdgeIterator *ei = ccgSubSurf_getEdgeIterator(ss);
int i, edgeSize = ccgSubSurf_getEdgeSize(ss);
+ GridKey *gridkey = ccgSubSurf_getGridKey(ss);
for (; !ccgEdgeIterator_isStopped(ei); ccgEdgeIterator_next(ei)) {
CCGEdge *e = ccgEdgeIterator_getCurrent(ei);
@@ -1095,7 +1210,7 @@ static void ccgDM_foreachMappedEdge(DerivedMesh *dm, void (*func)(void *userData
if (index!=-1) {
for (i=0; i<edgeSize-1; i++)
- func(userData, index, edgeData[i].co, edgeData[i+1].co);
+ func(userData, index, GRIDELEM_CO_AT(edgeData, i, gridkey), GRIDELEM_CO_AT(edgeData, i+1, gridkey));
}
}
@@ -1153,6 +1268,7 @@ static void ccgDM_drawEdges(DerivedMesh *dm, int drawLooseEdges, int drawAllEdge
CCGFaceIterator *fi = ccgSubSurf_getFaceIterator(ss);
int i, edgeSize = ccgSubSurf_getEdgeSize(ss);
int gridSize = ccgSubSurf_getGridSize(ss);
+ GridKey *gridkey = ccgSubSurf_getGridKey(ss);
int useAging;
ccgSubSurf_getUseAgeCounts(ss, &useAging, NULL, NULL, NULL);
@@ -1171,8 +1287,8 @@ static void ccgDM_drawEdges(DerivedMesh *dm, int drawLooseEdges, int drawAllEdge
glBegin(GL_LINE_STRIP);
for (i=0; i<edgeSize-1; i++) {
- glVertex3fv(edgeData[i].co);
- glVertex3fv(edgeData[i+1].co);
+ glVertex3fv(GRIDELEM_CO_AT(edgeData, i, gridkey));
+ glVertex3fv(GRIDELEM_CO_AT(edgeData, i+1, gridkey));
}
glEnd();
}
@@ -1191,18 +1307,18 @@ static void ccgDM_drawEdges(DerivedMesh *dm, int drawLooseEdges, int drawAllEdge
glBegin(GL_LINE_STRIP);
for (x=0; x<gridSize; x++)
- glVertex3fv(faceGridData[x].co);
+ glVertex3fv(GRIDELEM_CO_AT(faceGridData, x, gridkey));
glEnd();
for (y=1; y<gridSize-1; y++) {
glBegin(GL_LINE_STRIP);
for (x=0; x<gridSize; x++)
- glVertex3fv(faceGridData[y*gridSize + x].co);
+ glVertex3fv(GRIDELEM_CO_AT(faceGridData, y*gridSize + x, gridkey));
glEnd();
}
for (x=1; x<gridSize-1; x++) {
glBegin(GL_LINE_STRIP);
for (y=0; y<gridSize; y++)
- glVertex3fv(faceGridData[y*gridSize + x].co);
+ glVertex3fv(GRIDELEM_CO_AT(faceGridData, y*gridSize + x, gridkey));
glEnd();
}
}
@@ -1217,6 +1333,7 @@ static void ccgDM_drawLooseEdges(DerivedMesh *dm) {
CCGSubSurf *ss = ccgdm->ss;
CCGEdgeIterator *ei = ccgSubSurf_getEdgeIterator(ss);
int i, edgeSize = ccgSubSurf_getEdgeSize(ss);
+ GridKey *gridkey = ccgSubSurf_getGridKey(ss);
for (; !ccgEdgeIterator_isStopped(ei); ccgEdgeIterator_next(ei)) {
CCGEdge *e = ccgEdgeIterator_getCurrent(ei);
@@ -1225,8 +1342,8 @@ static void ccgDM_drawLooseEdges(DerivedMesh *dm) {
if (!ccgSubSurf_getEdgeNumFaces(e)) {
glBegin(GL_LINE_STRIP);
for (i=0; i<edgeSize-1; i++) {
- glVertex3fv(edgeData[i].co);
- glVertex3fv(edgeData[i+1].co);
+ glVertex3fv(GRIDELEM_CO_AT(edgeData, i, gridkey));
+ glVertex3fv(GRIDELEM_CO_AT(edgeData, i+1, gridkey));
}
glEnd();
}
@@ -1264,31 +1381,49 @@ static void ccgdm_pbvh_update(CCGDerivedMesh *ccgdm)
}
}
- /* Only used by non-editmesh types */
-static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes)[4], int fast, int (*setMaterial)(int, void *attribs)) {
- CCGDerivedMesh *ccgdm = (CCGDerivedMesh*) dm;
- CCGSubSurf *ss = ccgdm->ss;
- CCGFaceIterator *fi;
- int gridSize = ccgSubSurf_getGridSize(ss);
+static int ccgdm_draw_pbvh(DerivedMesh *dm, float (*partial_redraw_planes)[4],
+ int (*setMaterial)(int, void *attribs),
+ DMDrawFlags flags)
+{
+ CCGDerivedMesh *ccgdm = (CCGDerivedMesh*)dm;
char *faceFlags = ccgdm->faceFlags;
- int step = (fast)? gridSize-1: 1;
ccgdm_pbvh_update(ccgdm);
- if(ccgdm->pbvh && ccgdm->multires.mmd && !fast) {
+ if(ccgdm->pbvh && (ccgdm->multires.mmd || DM_DRAW_PTEX) && !(flags & DM_DRAW_LOWEST_SUBDIVISION_LEVEL)) {
if(dm->numFaceData) {
/* should be per face */
- if(!setMaterial(faceFlags[1]+1, NULL))
- return;
+ if(setMaterial && !setMaterial(faceFlags[1]+1, NULL))
+ return 1;
+ if(faceFlags[0] & ME_SMOOTH)
+ flags |= DM_DRAW_FULLY_SMOOTH;
- glShadeModel((faceFlags[0] & ME_SMOOTH)? GL_SMOOTH: GL_FLAT);
- BLI_pbvh_draw(ccgdm->pbvh, partial_redraw_planes, NULL, (faceFlags[0] & ME_SMOOTH));
- glShadeModel(GL_FLAT);
+ BLI_pbvh_draw(ccgdm->pbvh, partial_redraw_planes, NULL, flags);
}
- return;
+ return 1;
}
+ return 0;
+}
+
+/* Only used by non-editmesh types */
+static void ccgDM_drawFacesSolid(DerivedMesh *dm,
+ float (*partial_redraw_planes)[4],
+ int (*setMaterial)(int, void *attribs),
+ DMDrawFlags flags) {
+ CCGDerivedMesh *ccgdm = (CCGDerivedMesh*) dm;
+ CCGSubSurf *ss = ccgdm->ss;
+ CCGFaceIterator *fi;
+ int gridSize = ccgSubSurf_getGridSize(ss);
+ GridKey *gridkey = ccgSubSurf_getGridKey(ss);
+ char *faceFlags = ccgdm->faceFlags;
+ int step = (flags & DM_DRAW_LOWEST_SUBDIVISION_LEVEL)? gridSize-1: 1;
+
+ if(!(flags & DM_DRAW_BACKBUF_SELECTION) &&
+ ccgdm_draw_pbvh(dm, partial_redraw_planes, setMaterial, flags))
+ return;
+
fi = ccgSubSurf_getFaceIterator(ss);
for (; !ccgFaceIterator_isStopped(fi); ccgFaceIterator_next(fi)) {
CCGFace *f = ccgFaceIterator_getCurrent(fi);
@@ -1316,13 +1451,13 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes)
for (y=0; y<gridSize-1; y+=step) {
glBegin(GL_QUAD_STRIP);
for (x=0; x<gridSize; x+=step) {
- DMGridData *a = &faceGridData[(y+0)*gridSize + x];
- DMGridData *b = &faceGridData[(y+step)*gridSize + x];
+ DMGridData *a = GRIDELEM_AT(faceGridData, (y+0)*gridSize + x, gridkey);
+ DMGridData *b = GRIDELEM_AT(faceGridData, (y+step)*gridSize + x, gridkey);
- glNormal3fv(a->no);
- glVertex3fv(a->co);
- glNormal3fv(b->no);
- glVertex3fv(b->co);
+ glNormal3fv(GRIDELEM_NO(a, gridkey));
+ glVertex3fv(GRIDELEM_CO(a, gridkey));
+ glNormal3fv(GRIDELEM_NO(b, gridkey));
+ glVertex3fv(GRIDELEM_CO(b, gridkey));
}
glEnd();
}
@@ -1330,10 +1465,10 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes)
glBegin(GL_QUADS);
for (y=0; y<gridSize-1; y+=step) {
for (x=0; x<gridSize-1; x+=step) {
- float *a = faceGridData[(y+0)*gridSize + x].co;
- float *b = faceGridData[(y+0)*gridSize + x + step].co;
- float *c = faceGridData[(y+step)*gridSize + x + step].co;
- float *d = faceGridData[(y+step)*gridSize + x].co;
+ float *a = GRIDELEM_CO_AT(faceGridData, (y+0)*gridSize + x, gridkey);
+ float *b = GRIDELEM_CO_AT(faceGridData, (y+0)*gridSize + x + step, gridkey);
+ float *c = GRIDELEM_CO_AT(faceGridData, (y+step)*gridSize + x + step, gridkey);
+ float *d = GRIDELEM_CO_AT(faceGridData, (y+step)*gridSize + x, gridkey);
ccgDM_glNormalFast(a, b, c, d);
@@ -1362,6 +1497,7 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, int (*setMaterial)(int, v
int gridSize = ccgSubSurf_getGridSize(ss);
int gridFaces = gridSize - 1;
int edgeSize = ccgSubSurf_getEdgeSize(ss);
+ GridKey *gridkey = ccgSubSurf_getGridKey(ss);
int transp, orig_transp, new_transp;
char *faceFlags = ccgdm->faceFlags;
int a, b, i, doDraw, numVerts, matnr, new_matnr, totface;
@@ -1447,31 +1583,31 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, int (*setMaterial)(int, v
for (y=0; y<gridFaces; y++) {
glBegin(GL_QUAD_STRIP);
for (x=0; x<gridFaces; x++) {
- vda = &faceGridData[(y+0)*gridSize + x];
- vdb = &faceGridData[(y+1)*gridSize + x];
+ vda = GRIDELEM_AT(faceGridData, (y+0)*gridSize + x, gridkey);
+ vdb = GRIDELEM_AT(faceGridData, (y+1)*gridSize + x, gridkey);
PASSATTRIB(0, 0, 0);
- glNormal3fv(vda->no);
- glVertex3fv(vda->co);
+ glNormal3fv(GRIDELEM_NO(vda, gridkey));
+ glVertex3fv(GRIDELEM_CO(vda, gridkey));
PASSATTRIB(0, 1, 1);
- glNormal3fv(vdb->no);
- glVertex3fv(vdb->co);
+ glNormal3fv(GRIDELEM_NO(vdb, gridkey));
+ glVertex3fv(GRIDELEM_CO(vdb, gridkey));
if(x != gridFaces-1)
a++;
}
- vda = &faceGridData[(y+0)*gridSize + x];
- vdb = &faceGridData[(y+1)*gridSize + x];
+ vda = GRIDELEM_AT(faceGridData, (y+0)*gridSize + x, gridkey);
+ vdb = GRIDELEM_AT(faceGridData, (y+1)*gridSize + x, gridkey);
PASSATTRIB(0, 0, 3);
- glNormal3fv(vda->no);
- glVertex3fv(vda->co);
+ glNormal3fv(GRIDELEM_NO(vda, gridkey));
+ glVertex3fv(GRIDELEM_CO(vda, gridkey));
PASSATTRIB(0, 1, 2);
- glNormal3fv(vdb->no);
- glVertex3fv(vdb->co);
+ glNormal3fv(GRIDELEM_NO(vdb, gridkey));
+ glVertex3fv(GRIDELEM_CO(vdb, gridkey));
glEnd();
@@ -1481,10 +1617,10 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, int (*setMaterial)(int, v
glBegin(GL_QUADS);
for (y=0; y<gridFaces; y++) {
for (x=0; x<gridFaces; x++) {
- float *aco = faceGridData[(y+0)*gridSize + x].co;
- float *bco = faceGridData[(y+0)*gridSize + x + 1].co;
- float *cco = faceGridData[(y+1)*gridSize + x + 1].co;
- float *dco = faceGridData[(y+1)*gridSize + x].co;
+ float *aco = GRIDELEM_CO_AT(faceGridData, (y+0)*gridSize + x, gridkey);
+ float *bco = GRIDELEM_CO_AT(faceGridData, (y+0)*gridSize + x + 1, gridkey);
+ float *cco = GRIDELEM_CO_AT(faceGridData, (y+1)*gridSize + x + 1, gridkey);
+ float *dco = GRIDELEM_CO_AT(faceGridData, (y+1)*gridSize + x, gridkey);
ccgDM_glNormalFast(aco, bco, cco, dco);
@@ -1519,6 +1655,7 @@ static void ccgDM_drawFacesColored(DerivedMesh *dm, int useTwoSided, unsigned ch
CCGSubSurf *ss = ccgdm->ss;
CCGFaceIterator *fi = ccgSubSurf_getFaceIterator(ss);
int gridSize = ccgSubSurf_getGridSize(ss);
+ GridKey *gridkey = ccgSubSurf_getGridKey(ss);
unsigned char *cp1, *cp2;
int useTwoSide=1;
@@ -1545,10 +1682,10 @@ static void ccgDM_drawFacesColored(DerivedMesh *dm, int useTwoSided, unsigned ch
DMGridData *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S);
for (y=0; y<gridSize-1; y++) {
for (x=0; x<gridSize-1; x++) {
- float *a = faceGridData[(y+0)*gridSize + x].co;
- float *b = faceGridData[(y+0)*gridSize + x + 1].co;
- float *c = faceGridData[(y+1)*gridSize + x + 1].co;
- float *d = faceGridData[(y+1)*gridSize + x].co;
+ float *a = GRIDELEM_CO_AT(faceGridData, (y+0)*gridSize + x, gridkey);
+ float *b = GRIDELEM_CO_AT(faceGridData, (y+0)*gridSize + x + 1, gridkey);
+ float *c = GRIDELEM_CO_AT(faceGridData, (y+1)*gridSize + x + 1, gridkey);
+ float *d = GRIDELEM_CO_AT(faceGridData, (y+1)*gridSize + x, gridkey);
glColor3ub(cp1[3], cp1[2], cp1[1]);
glVertex3fv(d);
@@ -1592,6 +1729,7 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm,
MTFace *tf = DM_get_face_data_layer(dm, CD_MTFACE);
char *faceFlags = ccgdm->faceFlags;
int i, totface, flag, gridSize = ccgSubSurf_getGridSize(ss);
+ GridKey *gridkey = ccgSubSurf_getGridKey(ss);
int gridFaces = gridSize - 1;
ccgdm_pbvh_update(ccgdm);
@@ -1643,18 +1781,18 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm,
for (y=0; y<gridFaces; y++) {
glBegin(GL_QUAD_STRIP);
for (x=0; x<gridFaces; x++) {
- a = &faceGridData[(y+0)*gridSize + x];
- b = &faceGridData[(y+1)*gridSize + x];
+ a = GRIDELEM_AT(faceGridData, (y+0)*gridSize + x, gridkey);
+ b = GRIDELEM_AT(faceGridData, (y+1)*gridSize + x, gridkey);
if(tf) glTexCoord2fv(tf->uv[0]);
if(cp) glColor3ub(cp[3], cp[2], cp[1]);
- glNormal3fv(a->no);
- glVertex3fv(a->co);
+ glNormal3fv(GRIDELEM_NO(a, gridkey));
+ glVertex3fv(GRIDELEM_CO(a, gridkey));
if(tf) glTexCoord2fv(tf->uv[1]);
if(cp) glColor3ub(cp[7], cp[6], cp[5]);
- glNormal3fv(b->no);
- glVertex3fv(b->co);
+ glNormal3fv(GRIDELEM_NO(b, gridkey));
+ glVertex3fv(GRIDELEM_CO(b, gridkey));
if(x != gridFaces-1) {
if(tf) tf++;
@@ -1662,18 +1800,18 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm,
}
}
- a = &faceGridData[(y+0)*gridSize + x];
- b = &faceGridData[(y+1)*gridSize + x];
+ a = GRIDELEM_AT(faceGridData, (y+0)*gridSize + x, gridkey);
+ b = GRIDELEM_AT(faceGridData, (y+1)*gridSize + x, gridkey);
if(tf) glTexCoord2fv(tf->uv[3]);
if(cp) glColor3ub(cp[15], cp[14], cp[13]);
- glNormal3fv(a->no);
- glVertex3fv(a->co);
+ glNormal3fv(GRIDELEM_NO(a, gridkey));
+ glVertex3fv(GRIDELEM_CO(a, gridkey));
if(tf) glTexCoord2fv(tf->uv[2]);
if(cp) glColor3ub(cp[11], cp[10], cp[9]);
- glNormal3fv(b->no);
- glVertex3fv(b->co);
+ glNormal3fv(GRIDELEM_NO(b, gridkey));
+ glVertex3fv(GRIDELEM_CO(b, gridkey));
if(tf) tf++;
if(cp) cp += 16;
@@ -1685,10 +1823,10 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm,
glBegin(GL_QUADS);
for (y=0; y<gridFaces; y++) {
for (x=0; x<gridFaces; x++) {
- float *a_co = faceGridData[(y+0)*gridSize + x].co;
- float *b_co = faceGridData[(y+0)*gridSize + x + 1].co;
- float *c_co = faceGridData[(y+1)*gridSize + x + 1].co;
- float *d_co = faceGridData[(y+1)*gridSize + x].co;
+ float *a_co = GRIDELEM_CO_AT(faceGridData, (y+0)*gridSize + x, gridkey);
+ float *b_co = GRIDELEM_CO_AT(faceGridData, (y+0)*gridSize + x + 1, gridkey);
+ float *c_co = GRIDELEM_CO_AT(faceGridData, (y+1)*gridSize + x + 1, gridkey);
+ float *d_co = GRIDELEM_CO_AT(faceGridData, (y+1)*gridSize + x, gridkey);
ccgDM_glNormalFast(a_co, b_co, c_co, d_co);
@@ -1761,18 +1899,45 @@ static void ccgDM_drawUVEdges(DerivedMesh *dm)
}
}
-static void ccgDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *userData, int index, int *drawSmooth_r), void *userData, int useColors) {
+/* TODO: unify with gpu_buffers.c */
+static void gl_color_from_grid(DMGridData *elem, GridKey *gridkey)
+{
+ float v[3] = {1, 1, 1};
+ int i;
+
+ for(i = 0; i < gridkey->color; ++i) {
+ float *col = GRIDELEM_COLOR(elem, gridkey)[i];
+ interp_v3_v3v3(v, v, col, col[3]);
+ }
+ glColor3fv(v);
+}
+
+static void ccgDM_drawMappedFaces(DerivedMesh *dm,
+ float (*partial_redraw_planes)[4],
+ int (*setDrawOptions)(void *userData, int index, int *drawSmooth_r),
+ void *userData, DMDrawFlags flags) {
CCGDerivedMesh *ccgdm = (CCGDerivedMesh*) dm;
CCGSubSurf *ss = ccgdm->ss;
MCol *mcol= NULL;
int i, gridSize = ccgSubSurf_getGridSize(ss);
+ GridKey *gridkey = ccgSubSurf_getGridKey(ss);
char *faceFlags = ccgdm->faceFlags;
int gridFaces = gridSize - 1, totface;
+ int step = (flags & DM_DRAW_LOWEST_SUBDIVISION_LEVEL)? gridSize-1: 1;
- if(useColors) {
- mcol = dm->getFaceDataArray(dm, CD_WEIGHT_MCOL);
- if(!mcol)
- mcol = dm->getFaceDataArray(dm, CD_MCOL);
+ if(ccgdm_draw_pbvh(dm, partial_redraw_planes, NULL, flags))
+ return;
+
+ if(flags & DM_DRAW_VERTEX_COLORS) {
+ if(flags & DM_DRAW_LOWEST_SUBDIVISION_LEVEL) {
+ glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
+ glEnable(GL_COLOR_MATERIAL);
+ }
+ else {
+ mcol = dm->getFaceDataArray(dm, CD_WEIGHT_MCOL);
+ if(!mcol)
+ mcol = dm->getFaceDataArray(dm, CD_MCOL);
+ }
}
totface = ccgSubSurf_getNumFaces(ss);
@@ -1805,36 +1970,41 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *u
for (S=0; S<numVerts; S++) {
DMGridData *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S);
+ DMGridData *a, *b, *c, *d;
+
if (drawSmooth) {
glShadeModel(GL_SMOOTH);
- for (y=0; y<gridFaces; y++) {
- DMGridData *a, *b;
+ for (y=0; y<gridFaces; y+=step) {
glBegin(GL_QUAD_STRIP);
- for (x=0; x<gridFaces; x++) {
- a = &faceGridData[(y+0)*gridSize + x];
- b = &faceGridData[(y+1)*gridSize + x];
+ for (x=0; x<gridFaces; x+=step) {
+ a = GRIDELEM_AT(faceGridData, (y+0)*gridSize + x, gridkey);
+ b = GRIDELEM_AT(faceGridData, (y+step)*gridSize + x, gridkey);
if(cp) glColor3ub(cp[3], cp[2], cp[1]);
- glNormal3fv(a->no);
- glVertex3fv(a->co);
+ else gl_color_from_grid(a, gridkey);
+ glNormal3fv(GRIDELEM_NO(a, gridkey));
+ glVertex3fv(GRIDELEM_CO(a, gridkey));
if(cp) glColor3ub(cp[7], cp[6], cp[5]);
- glNormal3fv(b->no);
- glVertex3fv(b->co);
+ else gl_color_from_grid(b, gridkey);
+ glNormal3fv(GRIDELEM_NO(b, gridkey));
+ glVertex3fv(GRIDELEM_CO(b, gridkey));
if(x != gridFaces-1) {
if(cp) cp += 16;
}
}
- a = &faceGridData[(y+0)*gridSize + x];
- b = &faceGridData[(y+1)*gridSize + x];
+ a = GRIDELEM_AT(faceGridData, (y+0)*gridSize + x, gridkey);
+ b = GRIDELEM_AT(faceGridData, (y+step)*gridSize + x, gridkey);
if(cp) glColor3ub(cp[15], cp[14], cp[13]);
- glNormal3fv(a->no);
- glVertex3fv(a->co);
+ else gl_color_from_grid(a, gridkey);
+ glNormal3fv(GRIDELEM_NO(a, gridkey));
+ glVertex3fv(GRIDELEM_CO(a, gridkey));
if(cp) glColor3ub(cp[11], cp[10], cp[9]);
- glNormal3fv(b->no);
- glVertex3fv(b->co);
+ else gl_color_from_grid(b, gridkey);
+ glNormal3fv(GRIDELEM_NO(b, gridkey));
+ glVertex3fv(GRIDELEM_CO(b, gridkey));
if(cp) cp += 16;
@@ -1843,23 +2013,37 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *u
} else {
glShadeModel(GL_FLAT);
glBegin(GL_QUADS);
- for (y=0; y<gridFaces; y++) {
- for (x=0; x<gridFaces; x++) {
- float *a = faceGridData[(y+0)*gridSize + x].co;
- float *b = faceGridData[(y+0)*gridSize + x + 1].co;
- float *c = faceGridData[(y+1)*gridSize + x + 1].co;
- float *d = faceGridData[(y+1)*gridSize + x].co;
-
- ccgDM_glNormalFast(a, b, c, d);
+ for (y=0; y<gridFaces; y+=step) {
+ for (x=0; x<gridFaces; x+=step) {
+ float *a_co, *b_co, *c_co, *d_co;
+
+ a = GRIDELEM_AT(faceGridData, (y+0)*gridSize + x, gridkey);
+ b = GRIDELEM_AT(faceGridData, (y+0)*gridSize + x + step, gridkey);
+ c = GRIDELEM_AT(faceGridData, (y+step)*gridSize + x + step, gridkey);
+ d = GRIDELEM_AT(faceGridData, (y+step)*gridSize + x, gridkey);
+
+ a_co = GRIDELEM_CO(a, gridkey);
+ b_co = GRIDELEM_CO(b, gridkey);
+ c_co = GRIDELEM_CO(c, gridkey);
+ d_co = GRIDELEM_CO(d, gridkey);
+
+ ccgDM_glNormalFast(a_co, b_co, c_co, d_co);
if(cp) glColor3ub(cp[7], cp[6], cp[5]);
- glVertex3fv(d);
+ else gl_color_from_grid(d, gridkey);
+ glVertex3fv(a_co);
+
if(cp) glColor3ub(cp[11], cp[10], cp[9]);
- glVertex3fv(c);
+ else gl_color_from_grid(c, gridkey);
+ glVertex3fv(b_co);
+
if(cp) glColor3ub(cp[15], cp[14], cp[13]);
- glVertex3fv(b);
+ else gl_color_from_grid(b, gridkey);
+ glVertex3fv(c_co);
+
if(cp) glColor3ub(cp[3], cp[2], cp[1]);
- glVertex3fv(a);
+ else gl_color_from_grid(a, gridkey);
+ glVertex3fv(d_co);
if(cp) cp += 16;
}
@@ -1872,12 +2056,16 @@ static void ccgDM_drawMappedFaces(DerivedMesh *dm, int (*setDrawOptions)(void *u
}
}
}
+
+ if(flags & DM_DRAW_LOWEST_SUBDIVISION_LEVEL)
+ glDisable(GL_COLOR_MATERIAL);
}
static void ccgDM_drawMappedEdges(DerivedMesh *dm, int (*setDrawOptions)(void *userData, int index), void *userData) {
CCGDerivedMesh *ccgdm = (CCGDerivedMesh*) dm;
CCGSubSurf *ss = ccgdm->ss;
CCGEdgeIterator *ei = ccgSubSurf_getEdgeIterator(ss);
int i, useAging, edgeSize = ccgSubSurf_getEdgeSize(ss);
+ GridKey *gridkey = ccgSubSurf_getGridKey(ss);
ccgSubSurf_getUseAgeCounts(ss, &useAging, NULL, NULL, NULL);
@@ -1894,8 +2082,8 @@ static void ccgDM_drawMappedEdges(DerivedMesh *dm, int (*setDrawOptions)(void *u
}
for (i=0; i<edgeSize-1; i++) {
- glVertex3fv(edgeData[i].co);
- glVertex3fv(edgeData[i+1].co);
+ glVertex3fv(GRIDELEM_CO_AT(edgeData, i, gridkey));
+ glVertex3fv(GRIDELEM_CO_AT(edgeData, i+1, gridkey));
}
}
glEnd();
@@ -1908,6 +2096,7 @@ static void ccgDM_drawMappedEdgesInterp(DerivedMesh *dm, int (*setDrawOptions)(v
CCGSubSurf *ss = ccgdm->ss;
CCGEdgeIterator *ei = ccgSubSurf_getEdgeIterator(ss);
int i, useAging, edgeSize = ccgSubSurf_getEdgeSize(ss);
+ GridKey *gridkey = ccgSubSurf_getGridKey(ss);
ccgSubSurf_getUseAgeCounts(ss, &useAging, NULL, NULL, NULL);
@@ -1926,7 +2115,7 @@ static void ccgDM_drawMappedEdgesInterp(DerivedMesh *dm, int (*setDrawOptions)(v
glColor3ub(0, ageCol>0?ageCol:0, 0);
}
- glVertex3fv(edgeData[i].co);
+ glVertex3fv(GRIDELEM_CO_AT(edgeData, i, gridkey));
}
}
glEnd();
@@ -1938,6 +2127,7 @@ static void ccgDM_foreachMappedFaceCenter(DerivedMesh *dm, void (*func)(void *us
CCGDerivedMesh *ccgdm = (CCGDerivedMesh*) dm;
CCGSubSurf *ss = ccgdm->ss;
CCGFaceIterator *fi = ccgSubSurf_getFaceIterator(ss);
+ GridKey *gridkey = ccgSubSurf_getGridKey(ss);
for (; !ccgFaceIterator_isStopped(fi); ccgFaceIterator_next(fi)) {
CCGFace *f = ccgFaceIterator_getCurrent(fi);
@@ -1947,7 +2137,7 @@ static void ccgDM_foreachMappedFaceCenter(DerivedMesh *dm, void (*func)(void *us
/* Face center data normal isn't updated atm. */
DMGridData *vd = ccgSubSurf_getFaceGridData(ss, f, 0, 0, 0);
- func(userData, index, vd->co, vd->no);
+ func(userData, index, GRIDELEM_CO(vd, gridkey), GRIDELEM_NO(vd, gridkey));
}
}
@@ -1971,6 +2161,7 @@ static void ccgDM_release(DerivedMesh *dm) {
if(ccgdm->gridData) MEM_freeN(ccgdm->gridData);
if(ccgdm->gridAdjacency) MEM_freeN(ccgdm->gridAdjacency);
if(ccgdm->gridOffset) MEM_freeN(ccgdm->gridOffset);
+ if(ccgdm->gridFaceMap) MEM_freeN(ccgdm->gridFaceMap);
if(ccgdm->freeSS) ccgSubSurf_free(ccgdm->ss);
if(ccgdm->fmap) MEM_freeN(ccgdm->fmap);
if(ccgdm->fmap_mem) MEM_freeN(ccgdm->fmap_mem);
@@ -2116,6 +2307,7 @@ static int ccgdm_adjacent_grid(CCGSubSurf *ss, int *gridOffset, CCGFace *f, int
adjf = ccgSubSurf_getEdgeFace(e, i);
if(adjf != f) {
+ /* within the adjacent face, find the side [0-3] */
numEdges = ccgSubSurf_getFaceNumVerts(adjf);
for(j = 0; j < numEdges; j++)
if(ccgSubSurf_getFaceEdge(ss, adjf, j) == e)
@@ -2128,6 +2320,7 @@ static int ccgdm_adjacent_grid(CCGSubSurf *ss, int *gridOffset, CCGFace *f, int
fIndex = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(ss, adjf));
+ /* offset to choose the subface (there are two subfaces per edge) */
return gridOffset[fIndex] + (j + offset)%numEdges;
}
@@ -2179,12 +2372,12 @@ static void ccgdm_create_grids(DerivedMesh *dm)
adj->index[0] = gIndex - S + nextS;
adj->rotation[0] = 3;
- adj->index[1] = ccgdm_adjacent_grid(ss, gridOffset, f, prevS, 0);
- adj->rotation[1] = 1;
- adj->index[2] = ccgdm_adjacent_grid(ss, gridOffset, f, S, 1);
- adj->rotation[2] = 3;
+ adj->index[1] = ccgdm_adjacent_grid(ss, gridOffset, f, S, 1);
+ adj->rotation[1] = 2;
+ adj->index[2] = ccgdm_adjacent_grid(ss, gridOffset, f, prevS, 0);
+ adj->rotation[2] = 1;
adj->index[3] = gIndex - S + prevS;
- adj->rotation[3] = 1;
+ adj->rotation[3] = 0;
}
}
@@ -2218,6 +2411,13 @@ static int *ccgDM_getGridOffset(DerivedMesh *dm)
return ccgdm->gridOffset;
}
+static GridKey *ccgDM_getGridKey(DerivedMesh *dm)
+{
+ CCGDerivedMesh *ccgdm= (CCGDerivedMesh*)dm;
+
+ return ccgSubSurf_getGridKey(ccgdm->ss);
+}
+
static ListBase *ccgDM_getFaceMap(Object *ob, DerivedMesh *dm)
{
CCGDerivedMesh *ccgdm= (CCGDerivedMesh*)dm;
@@ -2232,6 +2432,36 @@ static ListBase *ccgDM_getFaceMap(Object *ob, DerivedMesh *dm)
return ccgdm->fmap;
}
+static GridToFace *ccgDM_getGridFaceMap(DerivedMesh *dm)
+{
+ CCGDerivedMesh *ccgdm = (CCGDerivedMesh*)dm;
+ CCGSubSurf *ss = ccgdm->ss;
+ GridToFace *gtf = ccgdm->gridFaceMap;
+
+ if(!gtf) {
+ int totgrid, totface, i, j;
+
+ totgrid = dm->getNumGrids(dm);
+ totface = ccgSubSurf_getNumFaces(ss);
+
+ gtf = ccgdm->gridFaceMap =
+ MEM_callocN(sizeof(GridToFace) * totgrid,
+ "ccgdm.gridFaceMap");
+
+ for(i = 0; i < totface; ++i) {
+ CCGFace *f = ccgdm->faceMap[i].face;
+ int S = ccgSubSurf_getFaceNumVerts(f);
+
+ for(j = 0; j < S; ++j, ++gtf) {
+ gtf->face = i;
+ gtf->offset = j;
+ }
+ }
+ }
+
+ return ccgdm->gridFaceMap;
+}
+
static int ccgDM_use_grid_pbvh(CCGDerivedMesh *ccgdm)
{
ModifierData *md;
@@ -2252,56 +2482,73 @@ static int ccgDM_use_grid_pbvh(CCGDerivedMesh *ccgdm)
static struct PBVH *ccgDM_getPBVH(Object *ob, DerivedMesh *dm)
{
+ SculptSession *ss;
CCGDerivedMesh *ccgdm= (CCGDerivedMesh*)dm;
int gridSize, numGrids, grid_pbvh;
+ GridKey *gridkey;
+ Mesh *me;
if(!ob) {
ccgdm->pbvh= NULL;
return NULL;
}
- if(!ob->sculpt)
+ if(!ob->paint)
return NULL;
+ ss = ob->paint->sculpt;
- grid_pbvh = ccgDM_use_grid_pbvh(ccgdm);
+ grid_pbvh = (ob->mode & OB_MODE_VERTEX_PAINT) || ccgDM_use_grid_pbvh(ccgdm);
- if(ob->sculpt->pbvh) {
+ if(ob->paint->pbvh) {
if(grid_pbvh) {
/* pbvh's grids, gridadj and gridfaces points to data inside ccgdm
but this can be freed on ccgdm release, this updates the pointers
when the ccgdm gets remade, the assumption is that the topology
does not change. */
ccgdm_create_grids(dm);
- BLI_pbvh_grids_update(ob->sculpt->pbvh, ccgdm->gridData, ccgdm->gridAdjacency, (void**)ccgdm->gridFaces);
+ BLI_pbvh_grids_update(ob->paint->pbvh, ccgdm->gridData,
+ ccgdm->gridAdjacency, (void**)ccgdm->gridFaces,
+ ccgDM_getGridKey(&ccgdm->dm));
}
- ccgdm->pbvh = ob->sculpt->pbvh;
+ ccgdm->pbvh = ob->paint->pbvh;
ccgdm->pbvh_draw = grid_pbvh;
}
if(ccgdm->pbvh)
return ccgdm->pbvh;
+ me = ob->data;
+
/* no pbvh exists yet, we need to create one. only in case of multires
we build a pbvh over the modified mesh, in other cases the base mesh
is being sculpted, so we build a pbvh from that. */
if(grid_pbvh) {
+ int leaf_limit = PBVH_DEFAULT_LEAF_LIMIT;
+
+ /* TODO: set leaf limit more intelligently */
+ if(ob->mode & OB_MODE_VERTEX_PAINT)
+ leaf_limit = 1;
+
ccgdm_create_grids(dm);
gridSize = ccgDM_getGridSize(dm);
numGrids = ccgDM_getNumGrids(dm);
+ gridkey = ccgDM_getGridKey(dm);
- ob->sculpt->pbvh= ccgdm->pbvh = BLI_pbvh_new();
+ ob->paint->pbvh= ccgdm->pbvh = BLI_pbvh_new(leaf_limit);
BLI_pbvh_build_grids(ccgdm->pbvh, ccgdm->gridData, ccgdm->gridAdjacency,
- numGrids, gridSize, (void**)ccgdm->gridFaces);
+ numGrids, gridSize, gridkey, (void**)ccgdm->gridFaces,
+ dm->getGridFaceMap(dm),
+ &me->vdata, &me->fdata,
+ ss ? &ss->hidden_areas : NULL);
ccgdm->pbvh_draw = 1;
}
else if(ob->type == OB_MESH) {
- Mesh *me= ob->data;
-
- ob->sculpt->pbvh= ccgdm->pbvh = BLI_pbvh_new();
+ ob->paint->pbvh= ccgdm->pbvh = BLI_pbvh_new(PBVH_DEFAULT_LEAF_LIMIT);
BLI_pbvh_build_mesh(ccgdm->pbvh, me->mface, me->mvert,
- me->totface, me->totvert);
+ &me->vdata, &me->fdata, me->totface, me->totvert,
+ ss ? &ss->hidden_areas : NULL);
ccgdm->pbvh_draw = 0;
}
@@ -2362,7 +2609,9 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss,
ccgdm->dm.getGridData = ccgDM_getGridData;
ccgdm->dm.getGridAdjacency = ccgDM_getGridAdjacency;
ccgdm->dm.getGridOffset = ccgDM_getGridOffset;
+ ccgdm->dm.getGridKey = ccgDM_getGridKey;
ccgdm->dm.getFaceMap = ccgDM_getFaceMap;
+ ccgdm->dm.getGridFaceMap = ccgDM_getGridFaceMap;
ccgdm->dm.getPBVH = ccgDM_getPBVH;
ccgdm->dm.getVertCos = ccgdm_getVertCos;
@@ -2609,6 +2858,7 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss,
struct DerivedMesh *subsurf_make_derived_from_derived(
struct DerivedMesh *dm,
struct SubsurfModifierData *smd,
+ struct GridKey *gridkey,
int useRenderParams, float (*vertCos)[3],
int isFinalCalc, int editMode)
{
@@ -2617,11 +2867,17 @@ struct DerivedMesh *subsurf_make_derived_from_derived(
int useSubsurfUv = smd->flags & eSubsurfModifierFlag_SubsurfUv;
int drawInteriorEdges = !(smd->flags & eSubsurfModifierFlag_ControlEdges);
CCGDerivedMesh *result;
+ GridKey default_gridkey;
+
+ if(!gridkey) {
+ GRIDELEM_KEY_INIT(&default_gridkey, 1, 0, 0, 1);
+ gridkey = &default_gridkey;
+ }
if(editMode) {
int levels= (smd->modifier.scene)? get_render_subsurf_level(&smd->modifier.scene->r, smd->levels): smd->levels;
- smd->emCache = _getSubSurf(smd->emCache, levels, useAging, 0,
+ smd->emCache = _getSubSurf(smd->emCache, gridkey, levels, useAging, 0,
useSimple);
ss_sync_from_derivedmesh(smd->emCache, dm, vertCos, useSimple);
@@ -2636,7 +2892,7 @@ struct DerivedMesh *subsurf_make_derived_from_derived(
if(levels == 0)
return dm;
- ss = _getSubSurf(NULL, levels, 0, 1, useSimple);
+ ss = _getSubSurf(NULL, gridkey, levels, 0, 1, useSimple);
ss_sync_from_derivedmesh(ss, dm, vertCos, useSimple);
@@ -2664,7 +2920,7 @@ struct DerivedMesh *subsurf_make_derived_from_derived(
}
if(useIncremental && isFinalCalc) {
- smd->mCache = ss = _getSubSurf(smd->mCache, levels,
+ smd->mCache = ss = _getSubSurf(smd->mCache, gridkey, levels,
useAging, 0, useSimple);
ss_sync_from_derivedmesh(ss, dm, vertCos, useSimple);
@@ -2678,7 +2934,7 @@ struct DerivedMesh *subsurf_make_derived_from_derived(
smd->mCache = NULL;
}
- ss = _getSubSurf(NULL, levels, 0, 1, useSimple);
+ ss = _getSubSurf(NULL, gridkey, levels, 0, 1, useSimple);
ss_sync_from_derivedmesh(ss, dm, vertCos, useSimple);
result = getCCGDerivedMesh(ss, drawInteriorEdges, useSubsurfUv, dm);
@@ -2700,11 +2956,15 @@ void subsurf_calculate_limit_positions(Mesh *me, float (*positions_r)[3])
* calculated vert positions is incorrect for the verts
* on the boundary of the mesh.
*/
- CCGSubSurf *ss = _getSubSurf(NULL, 1, 0, 1, 0);
+ CCGSubSurf *ss;
float edge_sum[3], face_sum[3];
CCGVertIterator *vi;
DerivedMesh *dm = CDDM_from_mesh(me, NULL);
+ GridKey gridkey;
+
+ GRIDELEM_KEY_INIT(&gridkey, 1, 0, 0, 1);
+ ss = _getSubSurf(NULL, &gridkey, 1, 0, 1, 0);
ss_sync_from_derivedmesh(ss, dm, NULL, 0);
vi = ccgSubSurf_getVertIterator(ss);
diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c
index 77416f4dd12..2c5912bb122 100644
--- a/source/blender/blenkernel/intern/texture.c
+++ b/source/blender/blenkernel/intern/texture.c
@@ -69,6 +69,8 @@
#include "BKE_node.h"
#include "BKE_animsys.h"
+#include "RE_shader_ext.h"
+
/* ------------------------------------------------------------------------- */
@@ -1348,3 +1350,20 @@ int BKE_texture_dependsOnTime(const struct Tex *texture)
}
/* ------------------------------------------------------------------------- */
+
+void get_texture_value(Tex *texture, float *tex_co, TexResult *texres)
+{
+ int result_type;
+
+ result_type = multitex_ext(texture, tex_co, NULL, NULL, 0, texres);
+
+ /* if the texture gave an RGB value, we assume it didn't give a valid
+ * intensity, so calculate one (formula from do_material_tex).
+ * if the texture didn't give an RGB value, copy the intensity across
+ */
+ if(result_type & TEX_RGB)
+ texres->tin = (0.35f * texres->tr + 0.45f * texres->tg
+ + 0.2f * texres->tb);
+ else
+ texres->tr = texres->tg = texres->tb = texres->tin;
+}
diff --git a/source/blender/blenlib/BLI_ghash.h b/source/blender/blenlib/BLI_ghash.h
index 0e985184f7a..7c9433405ac 100644
--- a/source/blender/blenlib/BLI_ghash.h
+++ b/source/blender/blenlib/BLI_ghash.h
@@ -40,6 +40,8 @@ extern "C" {
#include <stdlib.h>
#include <string.h>
+#include "MEM_guardedalloc.h"
+
#include "BKE_utildefines.h"
#include "BLI_mempool.h"
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index d2b9e6caf96..5144939b619 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -96,7 +96,6 @@ int isect_ray_tri_epsilon_v3(float p1[3], float d[3],
float v0[3], float v1[3], float v2[3], float *lambda, float *uv, float epsilon);
/* point in polygon */
-int isect_point_tri_v2(float p[2], float a[2], float b[2], float c[2]);
int isect_point_quad_v2(float p[2], float a[2], float b[2], float c[2], float d[2]);
int isect_point_tri_v2(float v1[2], float v2[2], float v3[2], float pt[2]);
diff --git a/source/blender/blenlib/BLI_pbvh.h b/source/blender/blenlib/BLI_pbvh.h
index 4797aeb2364..fed1c2fafee 100644
--- a/source/blender/blenlib/BLI_pbvh.h
+++ b/source/blender/blenlib/BLI_pbvh.h
@@ -25,17 +25,29 @@
#ifndef BLI_PBVH_H
#define BLI_PBVH_H
+#include <assert.h>
+
+struct BoundBox;
+struct CustomData;
struct MFace;
struct MVert;
+struct GridKey;
struct DMGridAdjacency;
struct DMGridData;
struct PBVH;
struct PBVHNode;
+struct GridToFace;
struct ListBase;
typedef struct PBVH PBVH;
typedef struct PBVHNode PBVHNode;
+typedef struct PBVHHiddenArea {
+ struct PBVHHiddenArea *next, *prev;
+ float clip_planes[4][4];
+ int hide_inside;
+} HiddenArea;
+
typedef struct {
float (*co)[3];
} PBVHProxyNode;
@@ -48,14 +60,26 @@ typedef int (*BLI_pbvh_SearchCallback)(PBVHNode *node, void *data);
typedef void (*BLI_pbvh_HitCallback)(PBVHNode *node, void *data);
typedef void (*BLI_pbvh_HitOccludedCallback)(PBVHNode *node, void *data, float* tmin);
-/* Building */
+/* test AABB against sphere */
+typedef struct {
+ float *center;
+ float radius_squared;
+ int original;
+} PBVHSearchSphereData;
+int BLI_pbvh_search_sphere_cb(PBVHNode *node, void *data);
-PBVH *BLI_pbvh_new(void);
+/* Building */
+#define PBVH_DEFAULT_LEAF_LIMIT 10000
+PBVH *BLI_pbvh_new(int leaf_limit);
void BLI_pbvh_build_mesh(PBVH *bvh, struct MFace *faces, struct MVert *verts,
- int totface, int totvert);
+ struct CustomData *vdata, struct CustomData *fdata,
+ int totface, int totvert, ListBase *hidden_areas);
void BLI_pbvh_build_grids(PBVH *bvh, struct DMGridData **grids,
- struct DMGridAdjacency *gridadj, int totgrid,
- int gridsize, void **gridfaces);
+ struct DMGridAdjacency *gridadj, int totgrid,
+ int gridsize, struct GridKey *gridkey, void **gridfaces,
+ struct GridToFace *grid_face_map,
+ struct CustomData *vdata, struct CustomData *fdata,
+ ListBase *hidden_areas);
void BLI_pbvh_free(PBVH *bvh);
/* Hierarchical Search in the BVH, two methods:
@@ -78,13 +102,14 @@ void BLI_pbvh_search_gather(PBVH *bvh,
void BLI_pbvh_raycast(PBVH *bvh, BLI_pbvh_HitOccludedCallback cb, void *data,
float ray_start[3], float ray_normal[3], int original);
int BLI_pbvh_node_raycast(PBVH *bvh, PBVHNode *node, float (*origco)[3],
- float ray_start[3], float ray_normal[3], float *dist);
+ float ray_start[3], float ray_normal[3], float *dist,
+ int *hit_index, int *grid_hit_index);
/* Drawing */
void BLI_pbvh_node_draw(PBVHNode *node, void *data);
int BLI_pbvh_node_planes_contain_AABB(PBVHNode *node, void *data);
-void BLI_pbvh_draw(PBVH *bvh, float (*planes)[4], float (*face_nors)[3], int smooth);
+void BLI_pbvh_draw(PBVH *bvh, float (*planes)[4], float (*face_nors)[3], int flags);
/* Node Access */
@@ -94,19 +119,44 @@ typedef enum {
PBVH_UpdateNormals = 2,
PBVH_UpdateBB = 4,
PBVH_UpdateOriginalBB = 8,
- PBVH_UpdateDrawBuffers = 16,
- PBVH_UpdateRedraw = 32
+
+ /* Update vertex data (coord + normal */
+ PBVH_UpdateVertBuffers = 16,
+
+ /* Update color data (used for masks) */
+ PBVH_UpdateColorBuffers = 32,
+
+ PBVH_UpdateRedraw = 64,
+
+ PBVH_UpdateAll = PBVH_UpdateNormals |
+ PBVH_UpdateBB |
+ PBVH_UpdateOriginalBB |
+ PBVH_UpdateVertBuffers |
+ PBVH_UpdateColorBuffers |
+ PBVH_UpdateRedraw,
} PBVHNodeFlags;
void BLI_pbvh_node_mark_update(PBVHNode *node);
+void BLI_pbvh_node_set_flags(PBVHNode *node, void *data);
+
+/* returns true if the pbvh is using grids rather than faces */
+int BLI_pbvh_uses_grids(PBVH *bvh);
+
+void BLI_pbvh_get_customdata(PBVH *pbvh, struct CustomData **vdata, struct CustomData **fdata);
+struct GridToFace *BLI_pbvh_get_grid_face_map(PBVH *pbvh);
+void BLI_pbvh_node_get_faces(PBVH *bvh, PBVHNode *node,
+ struct MFace **faces,
+ int **face_indices, int **face_vert_indices,
+ int *totface);
void BLI_pbvh_node_get_grids(PBVH *bvh, PBVHNode *node,
int **grid_indices, int *totgrid, int *maxgrid, int *gridsize,
- struct DMGridData ***griddata, struct DMGridAdjacency **gridadj);
+ struct DMGridData ***griddata, struct DMGridAdjacency **gridadj,
+ struct GridKey **gridkey);
void BLI_pbvh_node_num_verts(PBVH *bvh, PBVHNode *node,
int *uniquevert, int *totvert);
void BLI_pbvh_node_get_verts(PBVH *bvh, PBVHNode *node,
- int **vert_indices, struct MVert **verts);
+ int **vert_indices, struct MVert **verts);
void BLI_pbvh_node_get_BB(PBVHNode *node, float bb_min[3], float bb_max[3]);
void BLI_pbvh_node_get_original_BB(PBVHNode *node, float bb_min[3], float bb_max[3]);
@@ -119,7 +169,7 @@ void BLI_pbvh_update(PBVH *bvh, int flags, float (*face_nors)[3]);
void BLI_pbvh_redraw_BB(PBVH *bvh, float bb_min[3], float bb_max[3]);
void BLI_pbvh_get_grid_updates(PBVH *bvh, int clear, void ***gridfaces, int *totface);
void BLI_pbvh_grids_update(PBVH *bvh, struct DMGridData **grids,
- struct DMGridAdjacency *gridadj, void **gridfaces);
+ struct DMGridAdjacency *gridadj, void **gridfaces, struct GridKey *gridkey);
/* vertex deformer */
float (*BLI_pbvh_get_vertCos(struct PBVH *pbvh))[3];
@@ -148,22 +198,30 @@ typedef struct PBVHVertexIter {
/* grid */
struct DMGridData **grids;
- struct DMGridData *grid;
+ struct DMGridData *grid, *elem;
int *grid_indices;
int totgrid;
int gridsize;
+ struct GridKey *gridkey;
/* mesh */
struct MVert *mverts;
int totvert;
int *vert_indices;
+ /* mask layers */
+ struct CustomData *vdata;
+ int pmask_first_layer, pmask_layer_count, pmask_active_layer;
+
/* result: these are all computed in the macro, but we assume
that compiler optimizations will skip the ones we don't use */
struct MVert *mvert;
float *co;
short *no;
float *fno;
+ float *mask_active;
+
+ float mask_combined; /* not editable */
} PBVHVertexIter;
#ifdef _MSC_VER
@@ -175,21 +233,20 @@ typedef struct PBVHVertexIter {
struct DMGridData **grids; \
struct MVert *verts; \
int *grid_indices, totgrid, gridsize, *vert_indices, uniq_verts, totvert; \
+ struct GridKey *gridkey; \
\
- vi.grid= 0; \
- vi.no= 0; \
- vi.fno= 0; \
- vi.mvert= 0; \
- vi.skip= 0; \
+ memset(&vi, 0, sizeof(PBVHVertexIter)); \
\
- BLI_pbvh_node_get_grids(bvh, node, &grid_indices, &totgrid, NULL, &gridsize, &grids, NULL); \
+ BLI_pbvh_node_get_grids(bvh, node, &grid_indices, &totgrid, NULL, &gridsize, &grids, NULL, &gridkey); \
BLI_pbvh_node_num_verts(bvh, node, &uniq_verts, &totvert); \
BLI_pbvh_node_get_verts(bvh, node, &vert_indices, &verts); \
+ BLI_pbvh_get_customdata(bvh, &vi.vdata, NULL); \
\
vi.grids= grids; \
vi.grid_indices= grid_indices; \
vi.totgrid= (grids)? totgrid: 1; \
vi.gridsize= gridsize; \
+ vi.gridkey= gridkey; \
\
if(mode == PBVH_ITER_ALL) \
vi.totvert = totvert; \
@@ -197,6 +254,16 @@ typedef struct PBVHVertexIter {
vi.totvert= uniq_verts; \
vi.vert_indices= vert_indices; \
vi.mverts= verts; \
+ vi.mask_active= NULL; \
+ assert(!gridkey || gridkey->mask == 0 || vi.vdata); \
+ vi.pmask_layer_count = CustomData_number_of_layers(vi.vdata, CD_PAINTMASK); \
+ assert(!gridkey || gridkey->mask == 0 || gridkey->mask == vi.pmask_layer_count); \
+ if(vi.pmask_layer_count) { \
+ vi.pmask_first_layer = CustomData_get_layer_index(vi.vdata, CD_PAINTMASK); \
+ vi.pmask_active_layer = CustomData_get_active_layer_index(vi.vdata, CD_PAINTMASK); \
+ if(vi.pmask_active_layer != -1 && !(vi.vdata->layers[vi.pmask_active_layer].flag & CD_FLAG_ENABLED)) \
+ vi.pmask_active_layer = -1; \
+ } \
}\
\
for(vi.i=0, vi.g=0; vi.g<vi.totgrid; vi.g++) { \
@@ -218,24 +285,45 @@ typedef struct PBVHVertexIter {
} \
\
for(vi.gy=0; vi.gy<vi.height; vi.gy++) { \
- if(vi.grid) vi.grid += vi.skip; \
+ if(vi.grid) GRIDELEM_INC(vi.grid, vi.skip, vi.gridkey); \
\
for(vi.gx=0; vi.gx<vi.width; vi.gx++, vi.i++) { \
if(vi.grid) { \
- vi.co= vi.grid->co; \
- vi.fno= vi.grid->no; \
- vi.grid++; \
+ vi.co= GRIDELEM_CO(vi.grid, vi.gridkey); \
+ vi.fno= GRIDELEM_NO(vi.grid, vi.gridkey); \
+ \
+ if(vi.gridkey->mask) { \
+ vi.mask_combined = \
+ paint_mask_from_gridelem(vi.grid, vi.gridkey, vi.vdata); \
+ \
+ if(vi.pmask_active_layer != -1) \
+ vi.mask_active= &GRIDELEM_MASK(vi.grid, \
+ vi.gridkey)[vi.pmask_active_layer - \
+ vi.pmask_first_layer]; \
+ } \
+ \
+ vi.elem= vi.grid; \
+ GRIDELEM_INC(vi.grid, 1, vi.gridkey); \
} \
else { \
vi.mvert= &vi.mverts[vi.vert_indices[vi.gx]]; \
vi.co= vi.mvert->co; \
vi.no= vi.mvert->no; \
+ if(vi.pmask_layer_count) { \
+ vi.mask_combined = \
+ paint_mask_from_vertex(vi.vdata, vi.vert_indices[vi.gx], \
+ vi.pmask_layer_count, \
+ vi.pmask_first_layer); \
+ \
+ if(vi.pmask_active_layer != -1) \
+ vi.mask_active = &((float*)vi.vdata->layers[vi.pmask_active_layer].data)[vi.vert_indices[vi.gx]]; \
+ } \
} \
#define BLI_pbvh_vertex_iter_end \
} \
} \
- }
+ } \
void BLI_pbvh_node_get_proxies(PBVHNode* node, PBVHProxyNode** proxies, int* proxy_count);
void BLI_pbvh_node_free_proxies(PBVHNode* node);
diff --git a/source/blender/blenlib/intern/pbvh.c b/source/blender/blenlib/intern/pbvh.c
index bd721871f0a..b8c229080d5 100644
--- a/source/blender/blenlib/intern/pbvh.c
+++ b/source/blender/blenlib/intern/pbvh.c
@@ -20,9 +20,9 @@
* ***** END GPL LICENSE BLOCK *****
*/
-
-
#include "DNA_meshdata_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_object_types.h"
#include "MEM_guardedalloc.h"
@@ -31,12 +31,14 @@
#include "BLI_pbvh.h"
#include "BKE_DerivedMesh.h"
+#include "BKE_dmgrid.h"
#include "BKE_mesh.h" /* for mesh_calc_normals */
#include "BKE_global.h" /* for mesh_calc_normals */
+#include "BKE_paint.h"
#include "GPU_buffers.h"
-#define LEAF_LIMIT 10000
+static void pbvh_free_nodes(PBVH *bvh);
//#define PERFCNTRS
@@ -121,6 +123,14 @@ struct PBVH {
void **gridfaces;
int totgrid;
int gridsize;
+ struct GridKey *gridkey;
+ struct GridToFace *grid_face_map;
+
+ /* Used by both mesh and grid type */
+ CustomData *vdata;
+
+ /* For vertex paint */
+ CustomData *fdata;
/* Only used during BVH build and update,
don't need to remain valid after */
@@ -153,6 +163,62 @@ typedef struct PBVHIter {
int stackspace;
} PBVHIter;
+/* Test AABB against sphere */
+int BLI_pbvh_search_sphere_cb(PBVHNode *node, void *data_v)
+{
+ PBVHSearchSphereData *data = data_v;
+ float nearest[3];
+ float t[3], bb_min[3], bb_max[3];
+ int i;
+
+ if(data->original)
+ BLI_pbvh_node_get_original_BB(node, bb_min, bb_max);
+ else
+ BLI_pbvh_node_get_BB(node, bb_min, bb_max);
+
+ for(i = 0; i < 3; ++i) {
+ if(bb_min[i] > data->center[i])
+ nearest[i] = bb_min[i];
+ else if(bb_max[i] < data->center[i])
+ nearest[i] = bb_max[i];
+ else
+ nearest[i] = data->center[i];
+ }
+
+ sub_v3_v3v3(t, data->center, nearest);
+
+ return t[0] * t[0] + t[1] * t[1] + t[2] * t[2] < data->radius_squared;
+}
+
+/* Adapted from:
+ http://www.gamedev.net/community/forums/topic.asp?topic_id=512123
+ Returns true if the AABB is at least partially within the frustum
+ (ok, not a real frustum), false otherwise.
+*/
+static int pbvh_planes_contain_AABB(float bb_min[3], float bb_max[3], float (*planes)[4])
+{
+ int i, axis;
+ float vmin[3], vmax[3];
+
+ for(i = 0; i < 4; ++i) {
+ for(axis = 0; axis < 3; ++axis) {
+ if(planes[i][axis] > 0) {
+ vmin[axis] = bb_min[axis];
+ vmax[axis] = bb_max[axis];
+ }
+ else {
+ vmin[axis] = bb_max[axis];
+ vmax[axis] = bb_min[axis];
+ }
+ }
+
+ if(dot_v3v3(planes[i], vmin) + planes[i][3] > 0)
+ return 0;
+ }
+
+ return 1;
+}
+
static void BB_reset(BB *bb)
{
bb->bmin[0] = bb->bmin[1] = bb->bmin[2] = FLT_MAX;
@@ -372,13 +438,15 @@ static void build_mesh_leaf_node(PBVH *bvh, PBVHNode *node)
if(!G.background) {
node->draw_buffers =
GPU_build_mesh_buffers(map, bvh->verts, bvh->faces,
- node->prim_indices,
- node->totprim, node->vert_indices,
- node->uniq_verts,
- node->uniq_verts + node->face_verts);
+ bvh->vdata,
+ bvh->fdata,
+ node->prim_indices,
+ node->totprim, node->vert_indices,
+ node->uniq_verts,
+ node->uniq_verts + node->face_verts);
}
- node->flag |= PBVH_UpdateDrawBuffers;
+ node->flag |= PBVH_UpdateVertBuffers|PBVH_UpdateColorBuffers;
BLI_ghash_free(map, NULL, NULL);
}
@@ -387,10 +455,9 @@ static void build_grids_leaf_node(PBVH *bvh, PBVHNode *node)
{
if(!G.background) {
node->draw_buffers =
- GPU_build_grid_buffers(bvh->grids, node->prim_indices,
- node->totprim, bvh->gridsize);
+ GPU_build_grid_buffers(bvh->gridsize);
}
- node->flag |= PBVH_UpdateDrawBuffers;
+ node->flag |= PBVH_UpdateVertBuffers|PBVH_UpdateColorBuffers;
}
/* Recursively build a node in the tree
@@ -472,119 +539,175 @@ void build_sub(PBVH *bvh, int node_index, BB *cb, BBC *prim_bbc,
prim_bbc, end, offset + count - end);
}
-static void pbvh_build(PBVH *bvh, BB *cb, BBC *prim_bbc, int totprim)
+/* Returns 0 if the primitive should be hidden, 1 otherwise */
+static int test_prim_against_hidden_areas(BBC *prim_bbc, ListBase *hidden_areas)
{
- int i;
+ HiddenArea *area;
+
+ for(area = hidden_areas->first; area; area = area->next) {
+ int prim_inside_planes = pbvh_planes_contain_AABB(prim_bbc->bmin, prim_bbc->bmax, area->clip_planes);
+ if((prim_inside_planes && area->hide_inside) || (!prim_inside_planes && !area->hide_inside))
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Initially, the root node contains all primitives in
+ their original order.
+
+ If we are clipping, exclude primitives outside the
+ clip planes from the primitive list
+*/
+static int pbvh_initialize_prim_indices(PBVH *bvh, BBC *prim_bbc, int totprim, ListBase *hidden_areas)
+{
+ int prim, index;
+ int *prim_indices;
+
+ prim_indices = MEM_callocN(sizeof(int) * totprim, "bvh prim indices");
+
+ for(prim= 0, index = 0; prim < totprim; ++prim) {
+ if(!hidden_areas || test_prim_against_hidden_areas(&prim_bbc[prim], hidden_areas)) {
+ prim_indices[index] = prim;
+ ++index;
+ }
+ }
+
+ if(index == prim) {
+ bvh->prim_indices = prim_indices;
+ return totprim;
+ }
+ else {
+ bvh->prim_indices = MEM_callocN(sizeof(int) * index, "bvh prim indices");
+ memcpy(bvh->prim_indices, prim_indices, sizeof(int) * index);
+ MEM_freeN(prim_indices);
+ return index;
+ }
+}
+
+static void pbvh_build(PBVH *bvh, BB *cb, BBC *prim_bbc, int totprim, ListBase *hidden_areas)
+{
+ int max_prim_index;
if(totprim != bvh->totprim) {
+ /* Initialize the nodes */
bvh->totprim = totprim;
- if(bvh->nodes) MEM_freeN(bvh->nodes);
+ if(bvh->nodes)
+ pbvh_free_nodes(bvh);
if(bvh->prim_indices) MEM_freeN(bvh->prim_indices);
- bvh->prim_indices = MEM_callocN(sizeof(int) * totprim,
- "bvh prim indices");
- for(i = 0; i < totprim; ++i)
- bvh->prim_indices[i] = i;
+
+ max_prim_index = pbvh_initialize_prim_indices(bvh, prim_bbc, totprim, hidden_areas);
+
bvh->totnode = 0;
- if(bvh->node_mem_count < 100) {
+ if(bvh->node_mem_count < 100)
bvh->node_mem_count = 100;
- bvh->nodes = MEM_callocN(sizeof(PBVHNode) *
- bvh->node_mem_count,
- "bvh initial nodes");
- }
+
+ bvh->nodes = MEM_callocN(sizeof(PBVHNode) *
+ bvh->node_mem_count,
+ "bvh initial nodes");
}
bvh->totnode = 1;
- build_sub(bvh, 0, cb, prim_bbc, 0, totprim);
+ build_sub(bvh, 0, cb, prim_bbc, 0, max_prim_index);
}
-/* Do a full rebuild with on Mesh data structure */
-void BLI_pbvh_build_mesh(PBVH *bvh, MFace *faces, MVert *verts, int totface, int totvert)
+void pbvh_begin_build(PBVH *bvh, int totprim, ListBase *hidden_areas)
{
- BBC *prim_bbc = NULL;
- BB cb;
int i, j;
+ int totgridelem;
+ BBC *prim_bbc;
+ BB cb;
- bvh->faces = faces;
- bvh->verts = verts;
- bvh->vert_bitmap = BLI_bitmap_new(totvert);
- bvh->totvert = totvert;
- bvh->leaf_limit = LEAF_LIMIT;
-
+ /* cb will be the bounding box around all primitives' centroids */
BB_reset(&cb);
- /* For each face, store the AABB and the AABB centroid */
- prim_bbc = MEM_mallocN(sizeof(BBC) * totface, "prim_bbc");
+ if(bvh->faces)
+ bvh->vert_bitmap = BLI_bitmap_new(bvh->totvert);
+ else
+ totgridelem = bvh->gridsize*bvh->gridsize;
+
+ /* For each primitive, store the AABB and the AABB centroid */
+ prim_bbc = MEM_mallocN(sizeof(BBC) * totprim, "prim_bbc");
- for(i = 0; i < totface; ++i) {
- MFace *f = faces + i;
- const int sides = f->v4 ? 4 : 3;
+ for(i = 0; i < totprim; ++i) {
BBC *bbc = prim_bbc + i;
BB_reset((BB*)bbc);
- for(j = 0; j < sides; ++j)
- BB_expand((BB*)bbc, verts[(&f->v1)[j]].co);
+ if(bvh->faces) {
+ /* For regular mesh */
+ MFace *f = bvh->faces + i;
+ const int sides = f->v4 ? 4 : 3;
+ for(j = 0; j < sides; ++j)
+ BB_expand((BB*)bbc, bvh->verts[(&f->v1)[j]].co);
+ }
+ else {
+ /* For multires */
+ DMGridData *grid= bvh->grids[i];
+ for(j = 0; j < totgridelem; ++j)
+ BB_expand((BB*)bbc, GRIDELEM_CO_AT(grid, j, bvh->gridkey));
+ }
BBC_update_centroid(bbc);
-
BB_expand(&cb, bbc->bcentroid);
}
- if(totface)
- pbvh_build(bvh, &cb, prim_bbc, totface);
+ pbvh_build(bvh, &cb, prim_bbc, totprim, hidden_areas);
MEM_freeN(prim_bbc);
- MEM_freeN(bvh->vert_bitmap);
+ if(bvh->faces)
+ MEM_freeN(bvh->vert_bitmap);
}
-/* Do a full rebuild with on Grids data structure */
-void BLI_pbvh_build_grids(PBVH *bvh, DMGridData **grids, DMGridAdjacency *gridadj,
- int totgrid, int gridsize, void **gridfaces)
+/* Do a full rebuild with on Mesh data structure */
+void BLI_pbvh_build_mesh(PBVH *bvh, MFace *faces, MVert *verts,
+ CustomData *vdata, CustomData *fdata,
+ int totface, int totvert,
+ ListBase *hidden_areas)
{
- BBC *prim_bbc = NULL;
- BB cb;
- int i, j;
+ bvh->faces = faces;
+ bvh->verts = verts;
+ bvh->vdata = vdata;
+ bvh->fdata = fdata;
+ bvh->totvert = totvert;
+ if(totface)
+ pbvh_begin_build(bvh, totface, hidden_areas);
+}
+
+/* Do a full rebuild with on Grids data structure */
+void BLI_pbvh_build_grids(PBVH *bvh, DMGridData **grids,
+ DMGridAdjacency *gridadj,
+ int totgrid, int gridsize, GridKey *gridkey,
+ void **gridfaces, GridToFace *grid_face_map,
+ CustomData *vdata, CustomData *fdata,
+ ListBase *hidden_areas)
+{
bvh->grids= grids;
bvh->gridadj= gridadj;
bvh->gridfaces= gridfaces;
bvh->totgrid= totgrid;
bvh->gridsize= gridsize;
- bvh->leaf_limit = MAX2(LEAF_LIMIT/((gridsize-1)*(gridsize-1)), 1);
-
- BB_reset(&cb);
-
- /* For each grid, store the AABB and the AABB centroid */
- prim_bbc = MEM_mallocN(sizeof(BBC) * totgrid, "prim_bbc");
-
- for(i = 0; i < totgrid; ++i) {
- DMGridData *grid= grids[i];
- BBC *bbc = prim_bbc + i;
-
- BB_reset((BB*)bbc);
-
- for(j = 0; j < gridsize*gridsize; ++j)
- BB_expand((BB*)bbc, grid[j].co);
-
- BBC_update_centroid(bbc);
-
- BB_expand(&cb, bbc->bcentroid);
- }
+ bvh->gridkey= gridkey;
+ bvh->vdata= vdata;
+ bvh->fdata= fdata;
+ bvh->leaf_limit = MAX2(bvh->leaf_limit/((gridsize-1)*(gridsize-1)), 1);
+ bvh->grid_face_map = grid_face_map;
if(totgrid)
- pbvh_build(bvh, &cb, prim_bbc, totgrid);
-
- MEM_freeN(prim_bbc);
+ pbvh_begin_build(bvh, totgrid, hidden_areas);
}
-PBVH *BLI_pbvh_new(void)
+PBVH *BLI_pbvh_new(int leaf_limit)
{
PBVH *bvh = MEM_callocN(sizeof(PBVH), "pbvh");
+ bvh->leaf_limit = leaf_limit;
+
return bvh;
}
-void BLI_pbvh_free(PBVH *bvh)
+static void pbvh_free_nodes(PBVH *bvh)
{
PBVHNode *node;
int i;
@@ -612,6 +735,12 @@ void BLI_pbvh_free(PBVH *bvh)
}
MEM_freeN(bvh->nodes);
+}
+
+void BLI_pbvh_free(PBVH *bvh)
+{
+ pbvh_free_nodes(bvh);
+
MEM_freeN(bvh->prim_indices);
MEM_freeN(bvh);
}
@@ -1011,7 +1140,7 @@ static void pbvh_update_BB_redraw(PBVH *bvh, PBVHNode **nodes,
}
}
-static void pbvh_update_draw_buffers(PBVH *bvh, PBVHNode **nodes, int totnode, int smooth)
+static void pbvh_update_draw_buffers(PBVH *bvh, PBVHNode **nodes, int totnode, DMDrawFlags flags)
{
PBVHNode *node;
int n;
@@ -1020,24 +1149,55 @@ static void pbvh_update_draw_buffers(PBVH *bvh, PBVHNode **nodes, int totnode, i
for(n = 0; n < totnode; n++) {
node= nodes[n];
- if(node->flag & PBVH_UpdateDrawBuffers) {
+ if(node->flag & PBVH_UpdateVertBuffers) {
if(bvh->grids) {
- GPU_update_grid_buffers(node->draw_buffers,
- bvh->grids,
- node->prim_indices,
- node->totprim,
- bvh->gridsize,
- smooth);
+ GPU_update_grid_vert_buffers(node->draw_buffers,
+ bvh->grids,
+ node->prim_indices,
+ node->totprim,
+ bvh->gridsize,
+ bvh->gridkey,
+ flags & DM_DRAW_FULLY_SMOOTH);
}
else {
- GPU_update_mesh_buffers(node->draw_buffers,
- bvh->verts,
- node->vert_indices,
- node->uniq_verts +
- node->face_verts);
+ GPU_update_mesh_vert_buffers(node->draw_buffers,
+ bvh->verts,
+ node->vert_indices,
+ node->uniq_verts +
+ node->face_verts);
}
- node->flag &= ~PBVH_UpdateDrawBuffers;
+ node->flag &= ~PBVH_UpdateVertBuffers;
+ }
+
+ if(node->flag & PBVH_UpdateColorBuffers) {
+ if(bvh->grids) {
+ if(flags & DM_DRAW_PTEX) {
+ GPU_update_ptex(node->draw_buffers, bvh, node);
+ /* TODO: should only do this after ptex
+ res change */
+ GPU_update_grid_uv_buffer(node->draw_buffers,
+ bvh, node, flags);
+ }
+ else if(flags & DM_DRAW_PAINT_MASK) {
+ GPU_update_grid_color_buffers(node->draw_buffers,
+ bvh->grids,
+ node->prim_indices,
+ node->totprim,
+ bvh->gridsize,
+ bvh->gridkey,
+ bvh->vdata,
+ flags);
+ }
+ }
+ else {
+ if(flags & DM_DRAW_PAINT_MASK) {
+ GPU_update_mesh_color_buffers(node->draw_buffers,
+ bvh, node, flags);
+ }
+ }
+
+ node->flag &= ~PBVH_UpdateColorBuffers;
}
}
}
@@ -1093,6 +1253,8 @@ void BLI_pbvh_update(PBVH *bvh, int flag, float (*face_nors)[3])
if(nodes) MEM_freeN(nodes);
}
+/* get the object-space bounding box containing all the nodes that
+ have been marked with PBVH_UpdateRedraw */
void BLI_pbvh_redraw_BB(PBVH *bvh, float bb_min[3], float bb_max[3])
{
PBVHIter iter;
@@ -1128,7 +1290,7 @@ void BLI_pbvh_get_grid_updates(PBVH *bvh, int clear, void ***gridfaces, int *tot
pbvh_iter_begin(&iter, bvh, NULL, NULL);
while((node=pbvh_iter_next(&iter))) {
- if(node->flag & PBVH_UpdateNormals) {
+ if(node->flag & (PBVH_UpdateNormals)) {
for(i = 0; i < node->totprim; ++i) {
face= bvh->gridfaces[node->prim_indices[i]];
if(!BLI_ghash_lookup(map, face))
@@ -1165,11 +1327,34 @@ void BLI_pbvh_get_grid_updates(PBVH *bvh, int clear, void ***gridfaces, int *tot
*gridfaces= faces;
}
+/**** Access to mesh/grid data ****/
+int BLI_pbvh_uses_grids(PBVH *bvh)
+{
+ return !!bvh->grids;
+}
+
+void BLI_pbvh_get_customdata(PBVH *bvh, CustomData **vdata, CustomData **fdata)
+{
+ if(vdata) *vdata = bvh->vdata;
+ if(fdata) *fdata = bvh->fdata;
+}
+
+GridToFace *BLI_pbvh_get_grid_face_map(PBVH *pbvh)
+{
+ return pbvh->grid_face_map;
+}
+
+
/***************************** Node Access ***********************************/
void BLI_pbvh_node_mark_update(PBVHNode *node)
{
- node->flag |= PBVH_UpdateNormals|PBVH_UpdateBB|PBVH_UpdateOriginalBB|PBVH_UpdateDrawBuffers|PBVH_UpdateRedraw;
+ node->flag |= PBVH_UpdateNormals|PBVH_UpdateBB|PBVH_UpdateOriginalBB|PBVH_UpdateVertBuffers|PBVH_UpdateRedraw;
+}
+
+void BLI_pbvh_node_set_flags(PBVHNode *node, void *data)
+{
+ node->flag |= GET_INT_FROM_POINTER(data);
}
void BLI_pbvh_node_get_verts(PBVH *bvh, PBVHNode *node, int **vert_indices, MVert **verts)
@@ -1182,7 +1367,7 @@ void BLI_pbvh_node_num_verts(PBVH *bvh, PBVHNode *node, int *uniquevert, int *to
{
if(bvh->grids) {
if(totvert) *totvert= node->totprim*bvh->gridsize*bvh->gridsize;
- if(uniquevert) *uniquevert= *totvert;
+ if(uniquevert) *uniquevert= node->totprim*bvh->gridsize*bvh->gridsize;
}
else {
if(totvert) *totvert= node->uniq_verts + node->face_verts;
@@ -1190,7 +1375,26 @@ void BLI_pbvh_node_num_verts(PBVH *bvh, PBVHNode *node, int *uniquevert, int *to
}
}
-void BLI_pbvh_node_get_grids(PBVH *bvh, PBVHNode *node, int **grid_indices, int *totgrid, int *maxgrid, int *gridsize, DMGridData ***griddata, DMGridAdjacency **gridadj)
+void BLI_pbvh_node_get_faces(PBVH *bvh, PBVHNode *node,
+ MFace **mface,
+ int **face_indices, int **face_vert_indices,
+ int *totnode)
+{
+ if(bvh->grids) {
+ if(mface) *mface= NULL;
+ if(face_indices) *face_indices= NULL;
+ if(face_vert_indices) *face_vert_indices= NULL;
+ if(totnode) *totnode= 0;
+ }
+ else {
+ if(mface) *mface= bvh->faces;
+ if(face_indices) *face_indices= node->prim_indices;
+ if(face_vert_indices) *face_vert_indices= node->face_vert_indices;
+ if(totnode) *totnode= node->totprim;
+ }
+}
+
+void BLI_pbvh_node_get_grids(PBVH *bvh, PBVHNode *node, int **grid_indices, int *totgrid, int *maxgrid, int *gridsize, DMGridData ***griddata, DMGridAdjacency **gridadj, GridKey **gridkey)
{
if(bvh->grids) {
if(grid_indices) *grid_indices= node->prim_indices;
@@ -1199,6 +1403,7 @@ void BLI_pbvh_node_get_grids(PBVH *bvh, PBVHNode *node, int **grid_indices, int
if(gridsize) *gridsize= bvh->gridsize;
if(griddata) *griddata= bvh->grids;
if(gridadj) *gridadj= bvh->gridadj;
+ if(gridkey) *gridkey= bvh->gridkey;
}
else {
if(grid_indices) *grid_indices= NULL;
@@ -1207,6 +1412,7 @@ void BLI_pbvh_node_get_grids(PBVH *bvh, PBVHNode *node, int **grid_indices, int
if(gridsize) *gridsize= 0;
if(griddata) *griddata= NULL;
if(gridadj) *gridadj= NULL;
+ if(gridkey) *gridkey= NULL;
}
}
@@ -1324,10 +1530,14 @@ static int ray_face_intersection(float ray_start[3], float ray_normal[3],
}
int BLI_pbvh_node_raycast(PBVH *bvh, PBVHNode *node, float (*origco)[3],
- float ray_start[3], float ray_normal[3], float *dist)
+ float ray_start[3], float ray_normal[3], float *dist,
+ int *hit_index, int *grid_hit_index)
{
int hit= 0;
+ if(hit_index) *hit_index = -1;
+ if(grid_hit_index) *grid_hit_index = -1;
+
if(bvh->faces) {
MVert *vert = bvh->verts;
int *faces= node->prim_indices;
@@ -1337,10 +1547,11 @@ int BLI_pbvh_node_raycast(PBVH *bvh, PBVHNode *node, float (*origco)[3],
for(i = 0; i < totface; ++i) {
MFace *f = bvh->faces + faces[i];
+ int lhit = 0;
if(origco) {
/* intersect with backuped original coordinates */
- hit |= ray_face_intersection(ray_start, ray_normal,
+ lhit = ray_face_intersection(ray_start, ray_normal,
origco[face_verts[i*4+0]],
origco[face_verts[i*4+1]],
origco[face_verts[i*4+2]],
@@ -1349,13 +1560,18 @@ int BLI_pbvh_node_raycast(PBVH *bvh, PBVHNode *node, float (*origco)[3],
}
else {
/* intersect with current coordinates */
- hit |= ray_face_intersection(ray_start, ray_normal,
+ lhit = ray_face_intersection(ray_start, ray_normal,
vert[f->v1].co,
vert[f->v2].co,
vert[f->v3].co,
f->v4 ? vert[f->v4].co : NULL,
dist);
}
+
+ if(lhit && hit_index)
+ *hit_index = i;
+
+ hit |= lhit;
}
}
else {
@@ -1365,13 +1581,16 @@ int BLI_pbvh_node_raycast(PBVH *bvh, PBVHNode *node, float (*origco)[3],
for(i = 0; i < totgrid; ++i) {
DMGridData *grid= bvh->grids[node->prim_indices[i]];
+
if (!grid)
continue;
for(y = 0; y < gridsize-1; ++y) {
for(x = 0; x < gridsize-1; ++x) {
+ int lhit = 0;
+
if(origco) {
- hit |= ray_face_intersection(ray_start, ray_normal,
+ lhit |= ray_face_intersection(ray_start, ray_normal,
origco[y*gridsize + x],
origco[y*gridsize + x+1],
origco[(y+1)*gridsize + x+1],
@@ -1379,13 +1598,20 @@ int BLI_pbvh_node_raycast(PBVH *bvh, PBVHNode *node, float (*origco)[3],
dist);
}
else {
- hit |= ray_face_intersection(ray_start, ray_normal,
- grid[y*gridsize + x].co,
- grid[y*gridsize + x+1].co,
- grid[(y+1)*gridsize + x+1].co,
- grid[(y+1)*gridsize + x].co,
+ lhit |= ray_face_intersection(ray_start, ray_normal,
+ GRIDELEM_CO_AT(grid, y*gridsize + x, bvh->gridkey),
+ GRIDELEM_CO_AT(grid, y*gridsize + x+1, bvh->gridkey),
+ GRIDELEM_CO_AT(grid, (y+1)*gridsize + x+1, bvh->gridkey),
+ GRIDELEM_CO_AT(grid, (y+1)*gridsize + x, bvh->gridkey),
dist);
}
+
+ if(lhit) {
+ if(hit_index) *hit_index = i;
+ if(grid_hit_index) *grid_hit_index = y*gridsize + x;
+ }
+
+ hit |= lhit;
}
}
@@ -1399,7 +1625,11 @@ int BLI_pbvh_node_raycast(PBVH *bvh, PBVHNode *node, float (*origco)[3],
//#include <GL/glew.h>
-void BLI_pbvh_node_draw(PBVHNode *node, void *data)
+typedef struct {
+ PBVH *pbvh;
+ DMDrawFlags flags;
+} PBVHDrawData;
+void BLI_pbvh_node_draw(PBVHNode *node, void *data_v)
{
#if 0
/* XXX: Just some quick code to show leaf nodes in different colors */
@@ -1417,68 +1647,52 @@ void BLI_pbvh_node_draw(PBVHNode *node, void *data)
glColor3f(1, 0, 0);
#endif
- GPU_draw_buffers(node->draw_buffers);
+
+ PBVHDrawData *data = data_v;
+ GPU_draw_buffers(node->draw_buffers, data->pbvh, node, data->flags);
}
-/* Adapted from:
- http://www.gamedev.net/community/forums/topic.asp?topic_id=512123
- Returns true if the AABB is at least partially within the frustum
- (ok, not a real frustum), false otherwise.
-*/
int BLI_pbvh_node_planes_contain_AABB(PBVHNode *node, void *data)
{
- float (*planes)[4] = data;
- int i, axis;
- float vmin[3], vmax[3], bb_min[3], bb_max[3];
+ float bb_min[3], bb_max[3];
BLI_pbvh_node_get_BB(node, bb_min, bb_max);
- for(i = 0; i < 4; ++i) {
- for(axis = 0; axis < 3; ++axis) {
- if(planes[i][axis] > 0) {
- vmin[axis] = bb_min[axis];
- vmax[axis] = bb_max[axis];
- }
- else {
- vmin[axis] = bb_max[axis];
- vmax[axis] = bb_min[axis];
- }
- }
-
- if(dot_v3v3(planes[i], vmin) + planes[i][3] > 0)
- return 0;
- }
-
- return 1;
+ return pbvh_planes_contain_AABB(bb_min, bb_max, data);
}
-void BLI_pbvh_draw(PBVH *bvh, float (*planes)[4], float (*face_nors)[3], int smooth)
+void BLI_pbvh_draw(PBVH *bvh, float (*planes)[4], float (*face_nors)[3], int flags)
{
PBVHNode **nodes;
+ PBVHDrawData draw_data = {bvh, flags};
int totnode;
- BLI_pbvh_search_gather(bvh, update_search_cb, SET_INT_IN_POINTER(PBVH_UpdateNormals|PBVH_UpdateDrawBuffers),
+ BLI_pbvh_search_gather(bvh, update_search_cb,
+ SET_INT_IN_POINTER(PBVH_UpdateNormals|PBVH_UpdateVertBuffers|PBVH_UpdateColorBuffers),
&nodes, &totnode);
pbvh_update_normals(bvh, nodes, totnode, face_nors);
- pbvh_update_draw_buffers(bvh, nodes, totnode, smooth);
+ pbvh_update_draw_buffers(bvh, nodes, totnode, flags);
if(nodes) MEM_freeN(nodes);
if(planes) {
BLI_pbvh_search_callback(bvh, BLI_pbvh_node_planes_contain_AABB,
- planes, BLI_pbvh_node_draw, NULL);
+ planes, BLI_pbvh_node_draw, &draw_data);
}
else {
- BLI_pbvh_search_callback(bvh, NULL, NULL, BLI_pbvh_node_draw, NULL);
+ BLI_pbvh_search_callback(bvh, NULL, NULL, BLI_pbvh_node_draw, &draw_data);
}
}
-void BLI_pbvh_grids_update(PBVH *bvh, DMGridData **grids, DMGridAdjacency *gridadj, void **gridfaces)
+void BLI_pbvh_grids_update(PBVH *bvh, DMGridData **grids,
+ DMGridAdjacency *gridadj, void **gridfaces,
+ struct GridKey *gridkey)
{
bvh->grids= grids;
bvh->gridadj= gridadj;
bvh->gridfaces= gridfaces;
+ bvh->gridkey= gridkey;
}
float (*BLI_pbvh_get_vertCos(PBVH *pbvh))[3]
diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt
index cc70d3355f2..511b3f5e408 100644
--- a/source/blender/blenloader/CMakeLists.txt
+++ b/source/blender/blenloader/CMakeLists.txt
@@ -35,6 +35,7 @@ SET(INC
../include
../makesrna
../render/extern/include
+ ../../../extern/ptex
../../../intern/guardedalloc
${ZLIB_INC}
)
diff --git a/source/blender/blenloader/SConscript b/source/blender/blenloader/SConscript
index 88d345290e5..a68e773478a 100644
--- a/source/blender/blenloader/SConscript
+++ b/source/blender/blenloader/SConscript
@@ -6,6 +6,7 @@ sources = env.Glob('intern/*.c')
incs = '. #/intern/guardedalloc ../blenlib ../blenkernel'
incs += ' ../makesdna ../readblenfile ../editors/include'
incs += ' ../render/extern/include ../makesrna'
+incs += ' #/extern/ptex'
incs += ' ' + env['BF_ZLIB_INC']
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index f24de9c605d..acb565cda97 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -3257,6 +3257,34 @@ static void direct_link_mdisps(FileData *fd, int count, MDisps *mdisps, int exte
}
}
+static void direct_link_customdata_multires(FileData *fd, int count,
+ CustomDataMultires *cdm)
+{
+ if(cdm) {
+ int i, j;
+
+ for(i = 0; i < count; ++i, ++cdm) {
+ cdm->layers = newdataadr(fd, cdm->layers);
+ for(j = 0; j < cdm->totlayer; ++j) {
+ CustomDataMultiresLayer *l = cdm->layers + j;
+ l->griddata = newdataadr(fd, l->griddata);
+ }
+ }
+ }
+}
+
+static void direct_link_customdata_mptex(FileData *fd, int count, MPtex *mptex)
+{
+ if(mptex) {
+ int i, j;
+
+ for(i = 0; i < count; ++i, ++mptex) {
+ for(j = 0; j < mptex->totsubface; ++j)
+ mptex->subfaces[j].data = newdataadr(fd, mptex->subfaces[j].data);
+ }
+ }
+}
+
static void direct_link_customdata(FileData *fd, CustomData *data, int count)
{
int i = 0;
@@ -3274,6 +3302,10 @@ static void direct_link_customdata(FileData *fd, CustomData *data, int count)
layer->data = newdataadr(fd, layer->data);
if(layer->type == CD_MDISPS)
direct_link_mdisps(fd, count, layer->data, layer->flag & CD_FLAG_EXTERNAL);
+ if(layer->type == CD_GRIDS)
+ direct_link_customdata_multires(fd, count, layer->data);
+ if(layer->type == CD_MPTEX)
+ direct_link_customdata_mptex(fd, count, layer->data);
i++;
}
}
@@ -4100,9 +4132,10 @@ static void direct_link_object(FileData *fd, Object *ob)
ob->gpulamp.first= ob->gpulamp.last= NULL;
link_list(fd, &ob->pc_ids);
- if(ob->sculpt) {
- ob->sculpt= MEM_callocN(sizeof(SculptSession), "reload sculpt session");
- ob->sculpt->ob= ob;
+ if(ob->paint) {
+ ob->paint = MEM_callocN(sizeof(PaintSession), "PaintSession");
+ if(ob->mode & OB_MODE_SCULPT)
+ ob->paint->sculpt= MEM_callocN(sizeof(SculptSession), "reload sculpt session");
}
}
@@ -4137,6 +4170,8 @@ static void lib_link_scene(FileData *fd, Main *main)
sce= main->scene.first;
while(sce) {
if(sce->id.flag & LIB_NEEDLINK) {
+ ToolSettings *ts;
+
/*Link ID Properties -- and copy this comment EXACTLY for easy finding
of library blocks that implement this.*/
if (sce->id.properties) IDP_LibLinkProperty(sce->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
@@ -4150,10 +4185,18 @@ static void lib_link_scene(FileData *fd, Main *main)
sce->ima= newlibadr_us(fd, sce->id.lib, sce->ima);
sce->gpd= newlibadr_us(fd, sce->id.lib, sce->gpd);
- link_paint(fd, sce, &sce->toolsettings->sculpt->paint);
- link_paint(fd, sce, &sce->toolsettings->vpaint->paint);
- link_paint(fd, sce, &sce->toolsettings->wpaint->paint);
- link_paint(fd, sce, &sce->toolsettings->imapaint.paint);
+ ts = sce->toolsettings;
+ link_paint(fd, sce, &ts->sculpt->paint);
+ link_paint(fd, sce, &ts->vpaint->paint);
+ link_paint(fd, sce, &ts->wpaint->paint);
+ link_paint(fd, sce, &ts->imapaint.paint);
+ if(ts) {
+ if(ts->paint_overlay.img)
+ ts->paint_overlay.img = newlibadr_us(fd, sce->id.lib, ts->paint_overlay.img);
+ ts->paint_overlay.gltex = 0;
+ if(!ts->ptex_ures) ts->ptex_ures = 128;
+ if(!ts->ptex_vres) ts->ptex_vres = 128;
+ }
sce->toolsettings->skgen_template = newlibadr(fd, sce->id.lib, sce->toolsettings->skgen_template);
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index dc5a0dbeafa..c101eaed4bb 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -150,6 +150,8 @@ Any case: direct data is ALWAYS after the lib block
#include "readfile.h"
+#include "ptex.h"
+
#include <errno.h>
/* ********* my write, buffered writing with minimum size chunks ************ */
@@ -1463,6 +1465,44 @@ static void write_mdisps(WriteData *wd, int count, MDisps *mdlist, int external)
}
}
+static void write_customdata_multires(WriteData *wd, int count,
+ CustomDataMultires *grids)
+{
+ int i, j;
+
+ writestruct(wd, DATA, "CustomDataMultires", count, grids);
+
+ for(i = 0; i < count; ++i) {
+ writestruct(wd, DATA, "CustomDataMultiresLayer",
+ grids[i].totlayer, grids[i].layers);
+
+ for(j = 0; j < grids[i].totlayer; ++j) {
+ CustomDataMultiresLayer *l = &grids[i].layers[j];
+
+ writedata(wd, DATA, sizeof(float) * grids[i].totelem *
+ CustomData_multires_type_totfloat(l->type),
+ l->griddata);
+ }
+ }
+}
+
+static void write_customdata_mptex(WriteData *wd, int count,
+ MPtex *mptex)
+{
+ int i, j;
+
+ writestruct(wd, DATA, "MPtex", count, mptex);
+
+ for(i = 0; i < count; ++i, ++mptex) {
+ int layersize = mptex->channels * ptex_data_size(mptex->type);
+
+ for(j = 0; j < mptex->totsubface; ++j)
+ writedata(wd, DATA,
+ layersize * (mptex->subfaces[j].res[0] * mptex->subfaces[j].res[1]),
+ mptex->subfaces[j].data);
+ }
+}
+
static void write_customdata(WriteData *wd, ID *id, int count, CustomData *data, int partial_type, int partial_count)
{
int i;
@@ -1485,6 +1525,15 @@ static void write_customdata(WriteData *wd, ID *id, int count, CustomData *data,
else if (layer->type == CD_MDISPS) {
write_mdisps(wd, count, layer->data, layer->flag & CD_FLAG_EXTERNAL);
}
+ else if (layer->type == CD_PAINTMASK) {
+ writedata(wd, DATA, sizeof(float)*count, layer->data);
+ }
+ else if (layer->type == CD_GRIDS) {
+ write_customdata_multires(wd, count, layer->data);
+ }
+ else if (layer->type == CD_MPTEX) {
+ write_customdata_mptex(wd, count, layer->data);
+ }
else {
CustomData_file_write_info(layer->type, &structname, &structnum);
if (structnum) {
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index 564fe04ca4d..26ed5201e08 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -54,6 +54,7 @@ struct CustomData;
struct Material;
struct Object;
struct rcti;
+struct CustomDataLayer;
#define EM_FGON_DRAW 1 // face flag
#define EM_FGON 2 // edge and face flag both
@@ -214,6 +215,10 @@ void ED_mesh_faces_add(struct Mesh *mesh, struct ReportList *reports, int count)
void ED_mesh_edges_add(struct Mesh *mesh, struct ReportList *reports, int count);
void ED_mesh_vertices_add(struct Mesh *mesh, struct ReportList *reports, int count);
+void ED_mesh_delete_customdata_layer(struct bContext *C, struct Object *ob, struct CustomDataLayer *layer);
+
+void ED_mesh_geometry_add(struct Mesh *mesh, struct ReportList *reports, int verts, int edges, int faces);
+
void ED_mesh_transform(struct Mesh *me, float *mat);
void ED_mesh_calc_normals(struct Mesh *me);
void ED_mesh_material_link(struct Mesh *me, struct Material *ma);
@@ -224,5 +229,7 @@ int ED_mesh_uv_texture_remove(struct bContext *C, struct Object *ob, struct Mesh
int ED_mesh_color_add(struct bContext *C, struct Scene *scene, struct Object *ob, struct Mesh *me, const char *name, int active_set);
int ED_mesh_color_remove(struct bContext *C, struct Object *ob, struct Mesh *me);
+int ED_mesh_layers_poll(struct bContext *C);
+
#endif /* ED_MESH_H */
diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h
index 506813ce626..67e65409e6c 100644
--- a/source/blender/editors/include/ED_sculpt.h
+++ b/source/blender/editors/include/ED_sculpt.h
@@ -30,16 +30,16 @@
struct ARegion;
struct bContext;
+struct MultiresModifierData;
struct Object;
+struct PaintOverlay;
struct RegionView3D;
+struct Scene;
struct wmKeyConfig;
struct wmWindowManager;
/* sculpt.c */
void ED_operatortypes_sculpt(void);
-void sculpt_get_redraw_planes(float planes[4][4], struct ARegion *ar,
- struct RegionView3D *rv3d, struct Object *ob);
-void ED_sculpt_force_update(struct bContext *C);
/* paint_ops.c */
void ED_operatortypes_paint(void);
@@ -52,4 +52,21 @@ void ED_keymap_paint(struct wmKeyConfig *keyconf);
int ED_undo_paint_step(struct bContext *C, int type, int step, const char *name);
void ED_undo_paint_free(void);
+typedef struct PaintLayerUndoNode PaintLayerUndoNode;
+PaintLayerUndoNode *paint_layer_undo_push(int type, char *description);
+void paint_layer_undo_set_add(PaintLayerUndoNode *unode, char *name);
+void paint_layer_undo_set_remove(PaintLayerUndoNode *unode, char *name,
+ struct CustomData *data, struct CustomData *fdata,
+ int totvert, int totface);
+
+/* paint_util.c */
+struct MultiresModifierData *ED_paint_multires_active(struct Scene *scene, struct Object *ob);
+void paint_get_redraw_planes(float planes[4][4], struct ARegion *ar,
+ struct RegionView3D *rv3d, struct Object *ob);
+void ED_paint_force_update(struct bContext *C);
+
+/* paint_vertex.c */
+void ED_paint_overlay_draw(const struct bContext *C, struct ARegion *ar);
+void ED_paint_update_overlay(struct PaintOverlay *overlay);
+
#endif
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index e0f57a259b5..93424f365ea 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -2013,6 +2013,11 @@ static void list_item_row(bContext *C, uiLayout *layout, PointerRNA *ptr, Pointe
//uiItemR(row, itemptr, "mute", 0, "", ICON_MUTE_IPO_OFF);
uiBlockSetEmboss(block, UI_EMBOSS);
}
+ else if(itemptr->type == &RNA_MeshPaintMaskLayer) {
+ uiItemL(sub, name, icon);
+ uiBlockSetEmboss(block, UI_EMBOSS);
+ uiDefButR(block, OPTION, 0, "", 0, 0, UI_UNIT_X, UI_UNIT_Y, itemptr, "enabled", 0, 0, 0, 0, 0, NULL);
+ }
else
uiItemL(sub, name, icon); /* fails, backdrop LISTROW... */
diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c
index d1c6ff4c4ae..14934bf8127 100644
--- a/source/blender/editors/mesh/mesh_data.c
+++ b/source/blender/editors/mesh/mesh_data.c
@@ -45,6 +45,7 @@
#include "BKE_library.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
+#include "BKE_multires.h"
#include "BKE_report.h"
#include "BLI_math.h"
@@ -59,6 +60,7 @@
#include "ED_mesh.h"
#include "ED_object.h"
+#include "ED_sculpt.h"
#include "ED_uvedit.h"
#include "ED_view3d.h"
@@ -66,7 +68,7 @@
#include "mesh_intern.h"
-static void delete_customdata_layer(bContext *C, Object *ob, CustomDataLayer *layer)
+void ED_mesh_delete_customdata_layer(bContext *C, Object *ob, CustomDataLayer *layer)
{
Mesh *me = ob->data;
CustomData *data= (me->edit_mesh)? &me->edit_mesh->fdata: &me->fdata;
@@ -207,7 +209,7 @@ int ED_mesh_uv_texture_remove(bContext *C, Object *ob, Mesh *me)
if(!cdl)
return 0;
- delete_customdata_layer(C, ob, cdl);
+ ED_mesh_delete_customdata_layer(C, ob, cdl);
DAG_id_flush_update(&me->id, OB_RECALC_DATA);
WM_event_add_notifier(C, NC_GEOM|ND_DATA, me);
@@ -270,7 +272,7 @@ int ED_mesh_color_remove(bContext *C, Object *ob, Mesh *me)
if(!cdl)
return 0;
- delete_customdata_layer(C, ob, cdl);
+ ED_mesh_delete_customdata_layer(C, ob, cdl);
DAG_id_flush_update(&me->id, OB_RECALC_DATA);
WM_event_add_notifier(C, NC_GEOM|ND_DATA, me);
@@ -279,7 +281,7 @@ int ED_mesh_color_remove(bContext *C, Object *ob, Mesh *me)
/*********************** UV texture operators ************************/
-static int layers_poll(bContext *C)
+int ED_mesh_layers_poll(bContext *C)
{
Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data;
ID *data= (ob)? ob->data: NULL;
@@ -306,7 +308,7 @@ void MESH_OT_uv_texture_add(wmOperatorType *ot)
ot->idname= "MESH_OT_uv_texture_add";
/* api callbacks */
- ot->poll= layers_poll;
+ ot->poll= ED_mesh_layers_poll;
ot->exec= uv_texture_add_exec;
/* flags */
@@ -381,7 +383,7 @@ void MESH_OT_drop_named_image(wmOperatorType *ot)
ot->idname= "MESH_OT_drop_named_image";
/* api callbacks */
- ot->poll= layers_poll;
+ ot->poll= ED_mesh_layers_poll;
ot->invoke= drop_named_image_invoke;
/* flags */
@@ -411,7 +413,7 @@ void MESH_OT_uv_texture_remove(wmOperatorType *ot)
ot->idname= "MESH_OT_uv_texture_remove";
/* api callbacks */
- ot->poll= layers_poll;
+ ot->poll= ED_mesh_layers_poll;
ot->exec= uv_texture_remove_exec;
/* flags */
@@ -420,6 +422,37 @@ void MESH_OT_uv_texture_remove(wmOperatorType *ot)
/*********************** vertex color operators ************************/
+static int vertex_color_multires_toggle(Object *ob)
+{
+ Mesh *me= ob->data;
+ CustomDataMultires *cdm;
+ CustomDataLayer *cdl;
+ int active;
+
+ /* so that dm is recalculated correctly after */
+ multires_force_update(ob);
+
+ active = CustomData_get_active_layer_index(&me->fdata, CD_MCOL);
+ cdm = CustomData_get_layer(&me->fdata, CD_GRIDS);
+
+ if(active == -1)
+ return 1;
+
+ cdl = &me->fdata.layers[active];
+
+ if(cdm) {
+ if(cdl->flag & CD_FLAG_MULTIRES)
+ CustomData_multires_remove_layers(cdm, me->totface, CD_MCOL, cdl->name);
+ else
+ CustomData_multires_add_layers(cdm, me->totface, CD_MCOL, cdl->name);
+ }
+
+ /* note - if there's no griddata, it can still be synced up later */
+ cdl->flag ^= CD_FLAG_MULTIRES;
+
+ return 1;
+}
+
static int vertex_color_add_exec(bContext *C, wmOperator *op)
{
Scene *scene= CTX_data_scene(C);
@@ -429,6 +462,10 @@ static int vertex_color_add_exec(bContext *C, wmOperator *op)
if(!ED_mesh_color_add(C, scene, ob, me, NULL, TRUE))
return OPERATOR_CANCELLED;
+ if((ob->mode & OB_MODE_VERTEX_PAINT) &&
+ ED_paint_multires_active(scene, ob))
+ vertex_color_multires_toggle(ob);
+
return OPERATOR_FINISHED;
}
@@ -440,7 +477,7 @@ void MESH_OT_vertex_color_add(wmOperatorType *ot)
ot->idname= "MESH_OT_vertex_color_add";
/* api callbacks */
- ot->poll= layers_poll;
+ ot->poll= ED_mesh_layers_poll;
ot->exec= vertex_color_add_exec;
/* flags */
@@ -467,7 +504,30 @@ void MESH_OT_vertex_color_remove(wmOperatorType *ot)
/* api callbacks */
ot->exec= vertex_color_remove_exec;
- ot->poll= layers_poll;
+ ot->poll= ED_mesh_layers_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+static int vertex_color_multires_toggle_exec(bContext *C, wmOperator *op)
+{
+ if(vertex_color_multires_toggle(CTX_data_active_object(C)))
+ return OPERATOR_FINISHED;
+ else
+ return OPERATOR_CANCELLED;
+}
+
+void MESH_OT_vertex_color_multiresolution_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Vertex Color Multiresolution Toggle";
+ ot->description= "Add or remove multiresolution data from this layer";
+ ot->idname= "MESH_OT_vertex_color_multiresolution_toggle";
+
+ /* api callbacks */
+ ot->exec= vertex_color_multires_toggle_exec;
+ ot->poll= ED_mesh_layers_poll;
/* flags */
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
@@ -501,7 +561,7 @@ void MESH_OT_sticky_add(wmOperatorType *ot)
ot->idname= "MESH_OT_sticky_add";
/* api callbacks */
- ot->poll= layers_poll;
+ ot->poll= ED_mesh_layers_poll;
ot->exec= sticky_add_exec;
/* flags */
@@ -533,7 +593,7 @@ void MESH_OT_sticky_remove(wmOperatorType *ot)
ot->idname= "MESH_OT_sticky_remove";
/* api callbacks */
- ot->poll= layers_poll;
+ ot->poll= ED_mesh_layers_poll;
ot->exec= sticky_remove_exec;
/* flags */
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index fcd1eb40a02..89186f2d1f6 100644
--- a/source/blender/editors/mesh/mesh_intern.h
+++ b/source/blender/editors/mesh/mesh_intern.h
@@ -249,6 +249,7 @@ void MESH_OT_uv_texture_add(struct wmOperatorType *ot);
void MESH_OT_uv_texture_remove(struct wmOperatorType *ot);
void MESH_OT_vertex_color_add(struct wmOperatorType *ot);
void MESH_OT_vertex_color_remove(struct wmOperatorType *ot);
+void MESH_OT_vertex_color_multiresolution_toggle(struct wmOperatorType *ot);
void MESH_OT_sticky_add(struct wmOperatorType *ot);
void MESH_OT_sticky_remove(struct wmOperatorType *ot);
void MESH_OT_drop_named_image(struct wmOperatorType *ot);
diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c
index 9e071aa4a21..71baffca31d 100644
--- a/source/blender/editors/mesh/mesh_ops.c
+++ b/source/blender/editors/mesh/mesh_ops.c
@@ -133,6 +133,7 @@ void ED_operatortypes_mesh(void)
WM_operatortype_append(MESH_OT_uv_texture_remove);
WM_operatortype_append(MESH_OT_vertex_color_add);
WM_operatortype_append(MESH_OT_vertex_color_remove);
+ WM_operatortype_append(MESH_OT_vertex_color_multiresolution_toggle);
WM_operatortype_append(MESH_OT_sticky_add);
WM_operatortype_append(MESH_OT_sticky_remove);
WM_operatortype_append(MESH_OT_drop_named_image);
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index ed75c4060a2..9222b33aa8f 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -153,6 +153,7 @@ void OBJECT_OT_multires_reshape(struct wmOperatorType *ot);
void OBJECT_OT_multires_higher_levels_delete(struct wmOperatorType *ot);
void OBJECT_OT_multires_external_save(struct wmOperatorType *ot);
void OBJECT_OT_multires_external_pack(struct wmOperatorType *ot);
+void OBJECT_OT_multires_base_apply(struct wmOperatorType *ot);
void OBJECT_OT_meshdeform_bind(struct wmOperatorType *ot);
void OBJECT_OT_explode_refresh(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index aac5129c6a9..517040d47b7 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -1147,6 +1147,47 @@ void OBJECT_OT_multires_external_pack(wmOperatorType *ot)
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
}
+/********************* multires apply base ***********************/
+static int multires_base_apply_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = ED_object_active_context(C);
+ MultiresModifierData *mmd = (MultiresModifierData *)edit_modifier_property_get(C, op, ob, eModifierType_Multires);
+
+ if (!mmd)
+ return OPERATOR_CANCELLED;
+
+ multiresModifier_base_apply(mmd, ob);
+
+ DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+static int multires_base_apply_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ if (edit_modifier_invoke_properties(C, op))
+ return multires_base_apply_exec(C, op);
+ else
+ return OPERATOR_CANCELLED;
+}
+
+
+void OBJECT_OT_multires_base_apply(wmOperatorType *ot)
+{
+ ot->name= "Multires Apply Base";
+ ot->description= "Modify the base mesh to conform to the displaced mesh";
+ ot->idname= "OBJECT_OT_multires_base_apply";
+
+ ot->poll= multires_poll;
+ ot->invoke= multires_base_apply_invoke;
+ ot->exec= multires_base_apply_exec;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+ edit_modifier_properties(ot);
+}
+
/************************ mdef bind operator *********************/
static int meshdeform_poll(bContext *C)
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 6e9e8eeb5e5..692b7302cc5 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -136,6 +136,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_multires_higher_levels_delete);
WM_operatortype_append(OBJECT_OT_multires_external_save);
WM_operatortype_append(OBJECT_OT_multires_external_pack);
+ WM_operatortype_append(OBJECT_OT_multires_base_apply);
WM_operatortype_append(OBJECT_OT_meshdeform_bind);
WM_operatortype_append(OBJECT_OT_explode_refresh);
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index 3211763b619..96bfab97c5f 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -27,6 +27,7 @@ SET(INC
../../imbuf
../../gpu
../../blenlib
+ ../../../../extern/ptex
../../makesdna
../../makesrna
../../windowmanager
diff --git a/source/blender/editors/sculpt_paint/SConscript b/source/blender/editors/sculpt_paint/SConscript
index 2902b21fff1..4c49b19d307 100644
--- a/source/blender/editors/sculpt_paint/SConscript
+++ b/source/blender/editors/sculpt_paint/SConscript
@@ -9,6 +9,7 @@ incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf'
incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include'
incs += ' ../../render/extern/include'
incs += ' ../../gpu ../../makesrna'
+incs += ' #/extern/ptex'
if env['OURPLATFORM'] == 'linux2':
cflags='-pthread'
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index 3ed314095ef..6c1e933f9e8 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -29,10 +29,17 @@
#ifndef ED_PAINT_INTERN_H
#define ED_PAINT_INTERN_H
+#include "BLI_pbvh.h"
+
struct bContext;
+struct Brush;
struct Scene;
struct Object;
struct Mesh;
+struct Multires;
+struct Object;
+struct Paint;
+struct PaintOverlay;
struct PaintStroke;
struct PointerRNA;
struct ViewContext;
@@ -44,24 +51,83 @@ struct VPaint;
struct ListBase;
/* paint_stroke.c */
-typedef int (*StrokeGetLocation)(struct bContext *C, struct PaintStroke *stroke, float location[3], float mouse[2]);
+typedef struct PaintStroke PaintStroke;
+typedef int (*StrokeGetLocation)(struct bContext *C, PaintStroke *stroke, float location[3], float mouse[2]);
typedef int (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, struct wmEvent *event);
-typedef void (*StrokeUpdateStep)(struct bContext *C, struct PaintStroke *stroke, struct PointerRNA *itemptr);
-typedef void (*StrokeDone)(struct bContext *C, struct PaintStroke *stroke);
+typedef void (*StrokeUpdateStep)(struct bContext *C, PaintStroke *stroke, struct PointerRNA *itemptr);
+typedef void (*StrokeUpdateSymmetry)(struct bContext *C, PaintStroke *stroke,
+ char symmetry, char axis, float angle,
+ int mirror_symmetry_pass, int radial_symmetry_pass,
+ float (*symmetry_rot_mat)[4]);
+typedef void (*StrokeBrushAction)(struct bContext *C, PaintStroke *stroke);
+typedef void (*StrokeDone)(struct bContext *C, PaintStroke *stroke);
-struct PaintStroke *paint_stroke_new(struct bContext *C,
- StrokeGetLocation get_location, StrokeTestStart test_start,
- StrokeUpdateStep update_step, StrokeDone done);
-void paint_stroke_free(struct PaintStroke *stroke);
+PaintStroke *paint_stroke_new(struct bContext *C,
+ StrokeGetLocation get_location,
+ StrokeTestStart test_start,
+ StrokeUpdateStep update_step,
+ StrokeUpdateSymmetry update_symmetry,
+ StrokeBrushAction brush_action,
+ StrokeDone done);
+void paint_stroke_free(PaintStroke *stroke);
int paint_stroke_modal(struct bContext *C, struct wmOperator *op, struct wmEvent *event);
+void paint_stroke_apply_brush(struct bContext *C, PaintStroke *stroke, struct Paint *paint);
+float paint_stroke_combined_strength(PaintStroke *stroke,
+ float dist, float co[3], float mask);
int paint_stroke_exec(struct bContext *C, struct wmOperator *op);
-struct ViewContext *paint_stroke_view_context(struct PaintStroke *stroke);
-void *paint_stroke_mode_data(struct PaintStroke *stroke);
-void paint_stroke_set_mode_data(struct PaintStroke *stroke, void *mode_data);
+void *paint_stroke_mode_data(PaintStroke *stroke);
+void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data);
+
+/* paint stroke cache access */
+struct ViewContext *paint_stroke_view_context(PaintStroke *stroke);
+float paint_stroke_feather(PaintStroke *stroke);
+void paint_stroke_mouse_location(PaintStroke *stroke, float mouse[2]);
+void paint_stroke_initial_mouse_location(PaintStroke *stroke, float initial_mouse[2]);
+void paint_stroke_location(PaintStroke *stroke, float location[3]);
+float paint_stroke_pressure(PaintStroke *stroke);
+float paint_stroke_radius(PaintStroke *stroke);
+float paint_stroke_radius_squared(PaintStroke *stroke);
+void paint_stroke_symmetry_location(PaintStroke *stroke, float loc[3]);
+int paint_stroke_first_dab(PaintStroke *stroke);
+void paint_stroke_project(PaintStroke *stroke, float loc[3], float out[2]);
+void paint_stroke_symmetry_unflip(PaintStroke *stroke, float out[3], float vec[3]);
+
+/* paint stroke modifiers */
+void paint_stroke_set_modifier_use_original_location(PaintStroke *stroke);
+void paint_stroke_set_modifier_initial_radius_factor(PaintStroke *stroke, float initial_radius_factor);
+void paint_stroke_set_modifier_use_original_texture_coords(PaintStroke *stroke);
+
+typedef struct {
+ void *mode_data;
+ struct Object *ob;
+ float *ray_start, *ray_normal;
+ int hit;
+ float dist;
+ int original;
+} PaintStrokeRaycastData;
+
+int paint_stroke_over_mesh(struct bContext *C, PaintStroke *stroke, int x, int y);
+int paint_stroke_get_location(struct bContext *C, PaintStroke *stroke,
+ BLI_pbvh_HitOccludedCallback hit_cb, void *mode_data,
+ float out[3], float mouse[2], int original);
+
int paint_poll(struct bContext *C);
void paint_cursor_start(struct bContext *C, int (*poll)(struct bContext *C));
+typedef struct PaintStrokeTest {
+ float radius_squared;
+ float location[3];
+ float dist;
+} PaintStrokeTest;
+void paint_stroke_test_init(PaintStrokeTest *test, PaintStroke *stroke);
+int paint_stroke_test(PaintStrokeTest *test, float co[3]);
+int paint_stroke_test_sq(PaintStrokeTest *test, float co[3]);
+int paint_stroke_test_fast(PaintStrokeTest *test, float co[3]);
+int paint_stroke_test_cube(PaintStrokeTest *test, float co[3],
+ float local[4][4]);
+float paint_stroke_test_dist(PaintStrokeTest *test);
+
/* paint_vertex.c */
int weight_paint_poll(struct bContext *C);
int weight_paint_mode_poll(struct bContext *C);
@@ -80,7 +146,6 @@ void PAINT_OT_weight_from_bones(struct wmOperatorType *ot);
void PAINT_OT_vertex_paint_radial_control(struct wmOperatorType *ot);
void PAINT_OT_vertex_paint_toggle(struct wmOperatorType *ot);
void PAINT_OT_vertex_paint(struct wmOperatorType *ot);
-
unsigned int vpaint_get_current_col(struct VPaint *vp);
/* paint_image.c */
@@ -110,6 +175,24 @@ void PAINT_OT_face_select_all(struct wmOperatorType *ot);
int facemask_paint_poll(struct bContext *C);
+float paint_calc_object_space_radius(struct ViewContext *vc,
+ float center[3],
+ float pixel_radius);
+
+void paint_tag_partial_redraw(struct bContext *C, struct Object *ob);
+
+void paint_flip_coord(float out[3], float in[3], const char symm);
+
+float brush_tex_strength(struct ViewContext *vc,
+ float pmat[4][4], struct Brush *br,
+ float co[3], float mask, const float len,
+ float pixel_radius, float radius3d,
+ float special_rotation, float tex_mouse[2]);
+
+int paint_util_raycast(struct ViewContext *vc,
+ BLI_pbvh_HitOccludedCallback hit_cb, void *mode_data,
+ float out[3], float mouse[2], int original);
+
/* stroke operator */
typedef enum wmBrushStrokeMode {
WM_BRUSHSTROKE_NORMAL,
@@ -126,5 +209,69 @@ struct ListBase *undo_paint_push_get_list(int type);
void undo_paint_push_count_alloc(int type, int size);
void undo_paint_push_end(int type);
+/* paint_mask.c */
+void paintmask_brush_apply(struct Paint *paint, PaintStroke *stroke,
+ struct PBVHNode **nodes,
+ int totnode, float bstrength);
+
+void PAINT_OT_mask_layer_add(struct wmOperatorType *ot);
+void PAINT_OT_mask_layer_remove(struct wmOperatorType *ot);
+
+typedef enum {
+ MASKING_CLEAR,
+ MASKING_FILL,
+ MASKING_INVERT,
+ MASKING_RANDOM,
+} MaskSetMode;
+void PAINT_OT_mask_set(struct wmOperatorType *ot);
+void PAINT_OT_mask_from_texture(struct wmOperatorType *ot);
+
+/* pbvh_undo.c */
+typedef enum {
+ PBVH_UNDO_CO_NO = (1<<0),
+ PBVH_UNDO_PMASK = (1<<1),
+ PBVH_UNDO_PTEX = (1<<2)
+} PBVHUndoFlag;
+
+typedef struct PBVHUndoNode PBVHUndoNode;
+
+PBVHUndoNode *pbvh_undo_push_node(PBVHNode *node, PBVHUndoFlag flag,
+ struct Object *ob, struct Scene *scene);
+void pbvh_undo_push_begin(char *name);
+void pbvh_undo_push_end(void);
+/* undo node access */
+PBVHUndoNode *pbvh_undo_get_node(struct PBVHNode *node);
+typedef float (*pbvh_undo_f3)[3];
+typedef short (*pbvh_undo_s3)[3];
+int pbvh_undo_node_totvert(PBVHUndoNode *unode);
+pbvh_undo_f3 pbvh_undo_node_co(PBVHUndoNode *unode);
+pbvh_undo_s3 pbvh_undo_node_no(PBVHUndoNode *unode);
+float *pbvh_undo_node_layer_disp(PBVHUndoNode *unode);
+void pbvh_undo_node_set_layer_disp(PBVHUndoNode *unode, float *layer_disp);
+const char *pbvh_undo_node_mptex_name(PBVHUndoNode *unode);
+void *pbvh_undo_node_mptex_data(PBVHUndoNode *unode, int ndx);
+
+/* paint_ptex.c */
+void PTEX_OT_layer_add(struct wmOperatorType *ot);
+void PTEX_OT_layer_remove(struct wmOperatorType *ot);
+void PTEX_OT_layer_save(struct wmOperatorType *ot);
+void PTEX_OT_layer_convert(struct wmOperatorType *ot);
+void PTEX_OT_open(struct wmOperatorType *ot);
+void PTEX_OT_face_resolution_set(struct wmOperatorType *ot);
+void PTEX_OT_subface_select(struct wmOperatorType *ot);
+void PTEX_OT_select_all(struct wmOperatorType *ot);
+void PTEX_OT_subface_flag_set(struct wmOperatorType *ot);
+
+/* paint_overlay.c */
+int paint_sample_overlay(PaintStroke *stroke, float col[3], float co[2]);
+typedef enum {
+ PAINT_MANIP_GRAB,
+ PAINT_MANIP_SCALE,
+ PAINT_MANIP_ROTATE
+} PaintManipAction;
+void paint_overlay_transform(struct PaintOverlay *overlay, struct ARegion *ar, struct ImBuf *ibuf,
+ int out[2], float vec[2], int scale, int rotate);
+void PAINT_OT_overlay_manipulate(struct wmOperatorType *ot);
+
#endif /* ED_PAINT_INTERN_H */
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
new file mode 100644
index 00000000000..fb9624b8f60
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -0,0 +1,555 @@
+#include "MEM_guardedalloc.h"
+
+#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_context.h"
+#include "BKE_customdata.h"
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_dmgrid.h"
+#include "BKE_mesh.h"
+#include "BKE_multires.h"
+#include "BKE_paint.h"
+#include "BKE_texture.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_pbvh.h"
+#include "BLI_string.h"
+
+#include "ED_mesh.h"
+#include "ED_sculpt.h"
+#include "ED_view3d.h"
+#include "paint_intern.h"
+#include "sculpt_intern.h"
+
+#include "RE_render_ext.h"
+#include "RE_shader_ext.h"
+
+/* for redraw, just need to update the pbvh's vbo buffers */
+static void paintmask_redraw(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+
+ paint_refresh_mask_display(ob);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+}
+
+static int mask_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+
+ return ob && get_mesh(ob) && ob->paint &&
+ (ob->mode & (OB_MODE_SCULPT|OB_MODE_VERTEX_PAINT));
+}
+
+static int mask_active_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+
+ if(mask_poll(C)) {
+ Mesh *me = get_mesh(ob);
+ return CustomData_get_active_layer_index(&me->vdata, CD_PAINTMASK) != -1;
+ }
+
+ return 0;
+}
+
+void paintmask_brush_apply(Paint *paint, PaintStroke *stroke, PBVHNode **nodes, int totnode,
+ float bstrength)
+{
+ Object *ob = paint_stroke_view_context(stroke)->obact;
+ int n;
+
+ for(n=0; n<totnode; n++) {
+ PBVHVertexIter vd;
+ PaintStrokeTest test;
+
+ pbvh_undo_push_node(nodes[n], PBVH_UNDO_PMASK, ob, NULL);
+ paint_stroke_test_init(&test, stroke);
+
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ /* TODO: should add a mask layer if needed */
+ if(vd.mask_active) {
+ if(paint_stroke_test(&test, vd.co)) {
+ float fade;
+
+ fade = paint_stroke_combined_strength(stroke,
+ test.dist,
+ vd.co,
+ 0) *
+ bstrength;
+
+ *vd.mask_active += fade;
+ CLAMP(*vd.mask_active, 0, 1);
+
+ if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ BLI_pbvh_vertex_iter_end;
+
+ BLI_pbvh_node_set_flags(nodes[n], SET_INT_IN_POINTER(PBVH_UpdateColorBuffers|PBVH_UpdateRedraw));
+ }
+}
+
+static float get_tex_mask_strength(MTex *tex_slot, float *uv, float vco[3])
+{
+ float texvec[3] = {0, 0, 0};
+ TexResult texres;
+ int mapping = tex_slot->texco;
+
+ if(mapping == TEXCO_UV && !uv)
+ mapping = TEXCO_ORCO;
+
+ switch(tex_slot->texco) {
+ case TEXCO_UV:
+ texvec[0] = uv[0] * 2 - 1;
+ texvec[1] = uv[1] * 2 - 1;
+ break;
+ default:
+ copy_v3_v3(texvec, vco);
+ }
+
+ memset(&texres, 0, sizeof(TexResult));
+ get_texture_value(tex_slot->tex, texvec, &texres);
+
+ return texres.tin;
+}
+
+/* Set the value of a single mask element from either a UV or a coord */
+#define SET_MASK(elem, uv) \
+ GRIDELEM_MASK(elem, gridkey)[active] = \
+ get_tex_mask_strength(tex_slot, uv, \
+ GRIDELEM_CO(elem, gridkey)) \
+
+/* Fill active mask layer of entire grid from either MTFaces or coords */
+static void mask_grid_from_tex(DMGridData *grid, int gridsize,
+ GridKey *gridkey, MTFace *mtface,
+ MTex *tex_slot, int active)
+{
+ int x, y, boundary;
+
+ boundary = gridsize - 2;
+
+ for(y = 0; y <= boundary; ++y) {
+ for(x = 0; x <= boundary; ++x) {
+ SET_MASK(GRIDELEM_AT(grid, y*gridsize+x, gridkey),
+ mtface ? mtface->uv[0] : NULL);
+
+ /* Do the edge of the grid separately because the UV
+ grid is one element smaller on each side compared
+ to the vert-data grid */
+ if(x == boundary && y == boundary) {
+ SET_MASK(GRIDELEM_AT(grid, (y+1)*gridsize+x+1, gridkey),
+ mtface ? mtface->uv[2] : NULL);
+ }
+ if(x == boundary) {
+ SET_MASK(GRIDELEM_AT(grid, y*gridsize+x+1, gridkey),
+ mtface ? mtface->uv[3] : NULL);
+ }
+ if(y == boundary) {
+ SET_MASK(GRIDELEM_AT(grid, (y+1)*gridsize+x, gridkey),
+ mtface ? mtface->uv[1] : NULL);
+ }
+
+ if(mtface) ++mtface;
+ }
+ }
+}
+
+static void mask_face_from_tex(MFace *f, MVert *mvert, MTFace *mtface,
+ float *pmask, MTex *tex_slot, int active)
+{
+ int S = f->v4 ? 4 : 3;
+ int i;
+
+ /* Masks are per-vertex, not per-face-corner, so the mask
+ value at each vertex comes from one arbitrary face
+ corner; not averaged or otherwise combined yet */
+ for(i = 0; i < S; ++i) {
+ int vndx = (&f->v1)[i];
+ float *vco = mvert[vndx].co;
+
+ pmask[vndx] = get_tex_mask_strength(tex_slot,
+ mtface ? mtface->uv[i] : NULL, vco);
+ }
+}
+
+static int paint_mask_from_texture_exec(bContext *C, wmOperator *op)
+{
+ struct Scene *scene;
+ Object *ob;
+ Mesh *me;
+ struct MultiresModifierData *mmd;
+ PaintSession *ps;
+ MTex *tex_slot;
+ DerivedMesh *dm = NULL;
+ PBVH *pbvh;
+ MTFace *mtfaces = NULL;
+ PBVHNode **nodes;
+ int totnode, n, i, active;
+
+ tex_slot = CTX_data_pointer_get_type(C, "texture_slot",
+ &RNA_TextureSlot).data;
+
+ scene = CTX_data_scene(C);
+ ob = CTX_data_active_object(C);
+ ps = ob->paint;
+ me = get_mesh(ob);
+ mmd = ED_paint_multires_active(scene, ob);
+
+ pbvh_undo_push_begin("Paint mask from texture");
+
+ active = CustomData_get_active_layer(&me->vdata, CD_PAINTMASK);
+
+ /* if using UV mapping, check for a matching MTFace layer */
+ if(tex_slot->texco == TEXCO_UV) {
+ mtfaces = CustomData_get_layer_named(&me->fdata, CD_MTFACE,
+ tex_slot->uvname);
+ }
+
+ /* the MTFace mask is needed only for multires+UV */
+ dm = mesh_get_derived_final(scene, ob,
+ (mtfaces && mmd) ? CD_MASK_MTFACE : 0);
+
+ /* use the subdivided UVs for multires */
+ if(mtfaces && mmd) {
+ mtfaces = CustomData_get_layer_named(&dm->faceData,
+ CD_MTFACE,
+ tex_slot->uvname);
+ }
+
+ /* update the pbvh */
+ ps->pbvh = pbvh = dm->getPBVH(ob, dm);
+
+ /* get all nodes in the pbvh */
+ BLI_pbvh_search_gather(pbvh,
+ NULL, NULL,
+ &nodes, &totnode);
+
+ if(mmd) {
+ /* For all grids, find offset into mtfaces and apply
+ the texture to the grid */
+ for(n = 0; n < totnode; ++n) {
+ DMGridData **grids;
+ GridKey *gridkey;
+ int *grid_indices, totgrid, gridsize;
+
+ pbvh_undo_push_node(nodes[n], PBVH_UNDO_PMASK, ob, scene);
+
+ BLI_pbvh_node_get_grids(pbvh, nodes[n], &grid_indices,
+ &totgrid, NULL, &gridsize,
+ &grids, NULL, &gridkey);
+
+ for(i = 0; i < totgrid; ++i) {
+ int grid_index = grid_indices[i];
+ MTFace *mtface = NULL;
+
+ if(mtfaces) {
+ mtface = &mtfaces[grid_index *
+ ((gridsize-1) *
+ (gridsize-1))];
+ }
+
+ mask_grid_from_tex(grids[grid_index],
+ gridsize, gridkey,
+ mtface, tex_slot, active);
+ }
+
+ BLI_pbvh_node_set_flags(nodes[n],
+ SET_INT_IN_POINTER(PBVH_UpdateColorBuffers));
+ }
+
+ multires_mark_as_modified(ob);
+ }
+ else {
+ float *pmask = CustomData_get_layer(&me->vdata, CD_PAINTMASK);
+
+ for(n = 0; n < totnode; ++n) {
+ MFace *mface;
+ int *face_indices, totface;
+
+ pbvh_undo_push_node(nodes[n], PBVH_UNDO_PMASK, ob, scene);
+
+ BLI_pbvh_node_get_faces(pbvh, nodes[n], &mface,
+ &face_indices, NULL, &totface);
+
+ for(i = 0; i < totface; ++i) {
+ int face_index = face_indices[i];
+ MTFace *mtface = NULL;
+
+ if(mtfaces)
+ mtface = mtfaces + face_index;
+
+ mask_face_from_tex(me->mface + face_index,
+ me->mvert, mtface,
+ pmask, tex_slot, active);
+ }
+
+ BLI_pbvh_node_set_flags(nodes[n],
+ SET_INT_IN_POINTER(PBVH_UpdateColorBuffers));
+ }
+ }
+
+ MEM_freeN(nodes);
+
+ pbvh_undo_push_end();
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+/* fills a mask with intensity values from a texture, using an
+ mtex to provide mapping */
+void PAINT_OT_mask_from_texture(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Mask From Texture";
+ ot->idname= "PAINT_OT_mask_from_texture";
+
+ /* api callbacks */
+ ot->exec= paint_mask_from_texture_exec;
+ ot->poll= mask_active_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER;
+}
+
+static void set_mask_value(MaskSetMode mode, float *m)
+{
+ *m = (mode == MASKING_CLEAR ? 0 :
+ mode == MASKING_FILL ? 1 :
+ mode == MASKING_INVERT ? (1 - *m) :
+ mode == MASKING_RANDOM ? (float)rand() / RAND_MAX : 0);
+}
+
+static int paint_mask_set_exec(bContext *C, wmOperator *op)
+{
+ MaskSetMode mode = RNA_enum_get(op->ptr, "mode");
+ struct Scene *scene;
+ Object *ob;
+ DerivedMesh *dm;
+ struct MultiresModifierData *mmd;
+ PaintSession *ps;
+ Mesh *me;
+ PBVH *pbvh;
+
+ scene = CTX_data_scene(C);
+ ob = CTX_data_active_object(C);
+ ps = ob->paint;
+ me = get_mesh(ob);
+ mmd = ED_paint_multires_active(scene, ob);
+
+ dm = mesh_get_derived_final(scene, ob, 0);
+ ps->pbvh = pbvh = dm->getPBVH(ob, dm);
+
+ if(pbvh) {
+ PBVHNode **nodes;
+ int n, totnode;
+
+ BLI_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
+
+ pbvh_undo_push_begin("Paint mask fill");
+
+ for(n=0; n<totnode; n++) {
+ PBVHVertexIter vd;
+
+ pbvh_undo_push_node(nodes[n], PBVH_UNDO_PMASK, ob, scene);
+
+ BLI_pbvh_vertex_iter_begin(pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(vd.mask_active)
+ set_mask_value(mode, vd.mask_active);
+ }
+ BLI_pbvh_vertex_iter_end;
+
+ BLI_pbvh_node_set_flags(nodes[n], SET_INT_IN_POINTER(PBVH_UpdateColorBuffers|PBVH_UpdateRedraw));
+ }
+
+ if(nodes)
+ MEM_freeN(nodes);
+
+ if(mmd)
+ multires_mark_as_modified(ob);
+
+ pbvh_undo_push_end();
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+/* fills up a mask for the entire object, setting each vertex to
+ either 0, 1, or a random value */
+void PAINT_OT_mask_set(wmOperatorType *ot)
+{
+ static EnumPropertyItem mask_items[] = {
+ {MASKING_CLEAR, "CLEAR", 0, "Clear", ""},
+ {MASKING_FILL, "FILL", 0, "Fill", ""},
+ {MASKING_INVERT, "INVERT", 0, "Invert", ""},
+ {MASKING_RANDOM, "RANDOM", 0, "Random", ""},
+ {0, NULL, 0, NULL, NULL}};
+
+ /* identifiers */
+ ot->name= "Set Mask";
+ ot->idname= "PAINT_OT_mask_set";
+
+ /* api callbacks */
+ ot->exec= paint_mask_set_exec;
+ ot->poll= mask_active_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "mode", mask_items, MASKING_CLEAR, "Mode", "");
+}
+
+/* if this is a multires mesh, update it and free the DM.
+ returns 1 if this is a multires mesh, 0 otherwise */
+static int paintmask_check_multires(bContext *C)
+{
+ struct Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+
+ if(ED_paint_multires_active(scene, ob)) {
+ multires_force_update(ob);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int mask_layer_poll(bContext *C)
+{
+ return mask_poll(C) && ED_mesh_layers_poll(C);
+}
+
+static int mask_layer_add_exec(bContext *C, wmOperator *op)
+{
+ Object *ob= CTX_data_active_object(C);
+ Mesh *me= ob->data;
+ int multires, top, first;
+ char *layer_name;
+ PaintLayerUndoNode *unode;
+
+ unode = paint_layer_undo_push(CD_PAINTMASK, "Add mask layer");
+
+ multires = paintmask_check_multires(C);
+
+ top= CustomData_number_of_layers(&me->vdata, CD_PAINTMASK);
+ CustomData_add_layer(&me->vdata, CD_PAINTMASK, CD_DEFAULT,
+ NULL, me->totvert);
+
+ first = CustomData_get_layer_index(&me->vdata, CD_PAINTMASK);
+ assert(first >= 0);
+
+ me->vdata.layers[first + top].flag |= CD_FLAG_MULTIRES;
+ CustomData_set_layer_active(&me->vdata, CD_PAINTMASK, top);
+ layer_name = me->vdata.layers[first + top].name;
+
+ /* now that we have correct name, update multires and finish undo push */
+
+ if(multires) {
+ CustomDataMultires *cdm;
+
+ cdm = CustomData_get_layer(&me->fdata, CD_GRIDS);
+
+ CustomData_multires_add_layers(cdm, me->totface,
+ CD_PAINTMASK, layer_name);
+ }
+
+ paint_layer_undo_set_add(unode, layer_name);
+
+ paintmask_redraw(C);
+
+ return OPERATOR_FINISHED;
+}
+
+static int mask_layer_remove_exec(bContext *C, wmOperator *op)
+{
+ Object *ob= CTX_data_active_object(C);
+ Mesh *me= ob->data;
+ int active_offset, first;
+
+ active_offset = CustomData_get_active_layer(&me->vdata, CD_PAINTMASK);
+
+ if(active_offset >= 0) {
+ PaintLayerUndoNode *unode;
+ int multires = paintmask_check_multires(C);
+ char *layer_name;
+
+ unode = paint_layer_undo_push(CD_PAINTMASK,
+ "Remove mask layer");
+
+ first = CustomData_get_layer_index(&me->vdata, CD_PAINTMASK);
+ layer_name = me->vdata.layers[first + active_offset].name;
+
+ paint_layer_undo_set_remove(unode, layer_name,
+ &me->vdata, &me->fdata,
+ me->totvert, me->totface);
+
+ if(multires) {
+ CustomDataMultires *cdm;
+
+ cdm = CustomData_get_layer(&me->fdata, CD_GRIDS);
+
+ CustomData_multires_remove_layers(cdm, me->totface,
+ CD_PAINTMASK,
+ layer_name);
+ }
+
+ CustomData_free_layer_active(&me->vdata, CD_PAINTMASK,
+ me->totvert);
+
+ paintmask_redraw(C);
+ }
+ else
+ return OPERATOR_CANCELLED;
+
+ return OPERATOR_FINISHED;
+}
+
+void PAINT_OT_mask_layer_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Add Mask Layer";
+ ot->description= "Add a paint mask layer";
+ ot->idname= "PAINT_OT_mask_layer_add";
+
+ /* api callbacks */
+ ot->poll= mask_layer_poll;
+ ot->exec= mask_layer_add_exec;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+void PAINT_OT_mask_layer_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Remove Mask Layer";
+ ot->description= "Remove the active paint mask layer";
+ ot->idname= "PAINT_OT_mask_layer_remove";
+
+ /* api callbacks */
+ ot->poll= mask_layer_poll;
+ ot->exec= mask_layer_remove_exec;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index b5cadb9c484..39bae3c1153 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -19,6 +19,7 @@
* ***** END GPL LICENSE BLOCK *****
*/
+#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@@ -216,11 +217,29 @@ void ED_operatortypes_paint(void)
WM_operatortype_append(PAINT_OT_vertex_paint_toggle);
WM_operatortype_append(PAINT_OT_vertex_paint);
WM_operatortype_append(PAINT_OT_vertex_color_set);
+ WM_operatortype_append(PAINT_OT_overlay_manipulate);
+
+ /* ptex */
+ WM_operatortype_append(PTEX_OT_layer_add);
+ WM_operatortype_append(PTEX_OT_layer_remove);
+ WM_operatortype_append(PTEX_OT_layer_save);
+ WM_operatortype_append(PTEX_OT_layer_convert);
+ WM_operatortype_append(PTEX_OT_open);
+ WM_operatortype_append(PTEX_OT_face_resolution_set);
+ WM_operatortype_append(PTEX_OT_subface_select);
+ WM_operatortype_append(PTEX_OT_select_all);
+ WM_operatortype_append(PTEX_OT_subface_flag_set);
/* face-select */
WM_operatortype_append(PAINT_OT_face_select_linked);
WM_operatortype_append(PAINT_OT_face_select_linked_pick);
WM_operatortype_append(PAINT_OT_face_select_all);
+
+ /* mask */
+ WM_operatortype_append(PAINT_OT_mask_layer_add);
+ WM_operatortype_append(PAINT_OT_mask_layer_remove);
+ WM_operatortype_append(PAINT_OT_mask_set);
+ WM_operatortype_append(PAINT_OT_mask_from_texture);
}
@@ -301,6 +320,19 @@ static void ed_keymap_paint_brush_size(wmKeyMap *keymap, const char *path)
RNA_float_set(kmi->ptr, "scalar", 10.0/9.0); // 1.1111....
}
+static void ed_keymap_paint_overlay(wmKeyMap *keymap)
+{
+ wmKeyMapItem *kmi;
+ kmi = WM_keymap_add_item(keymap, "PAINT_OT_overlay_manipulate", GKEY, KM_PRESS, 0, 0);
+ RNA_enum_set(kmi->ptr, "action", PAINT_MANIP_GRAB);
+ kmi = WM_keymap_add_item(keymap, "PAINT_OT_overlay_manipulate", SKEY, KM_PRESS, 0, 0);
+ RNA_enum_set(kmi->ptr, "action", PAINT_MANIP_SCALE);
+ kmi = WM_keymap_add_item(keymap, "PAINT_OT_overlay_manipulate", RKEY, KM_PRESS, 0, 0);
+ RNA_enum_set(kmi->ptr, "action", PAINT_MANIP_ROTATE);
+ kmi= WM_keymap_add_item(keymap, "WM_OT_context_toggle", IKEY, KM_PRESS, 0, 0);
+ RNA_string_set(kmi->ptr, "data_path", "tool_settings.paint_overlay.enabled");
+}
+
void ED_keymap_paint(wmKeyConfig *keyconf)
{
wmKeyMap *keymap;
@@ -318,6 +350,12 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, 0, 0)->ptr, "mode", WM_BRUSHSTROKE_NORMAL);
RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_CTRL, 0)->ptr, "mode", WM_BRUSHSTROKE_INVERT);
RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_BRUSHSTROKE_SMOOTH);
+
+ WM_keymap_add_item(keymap, "SCULPT_OT_area_hide", EVT_TWEAK_L, KM_ANY, KM_CTRL|KM_SHIFT, 0);
+ RNA_boolean_set(WM_keymap_add_item(keymap, "SCULPT_OT_area_hide", EVT_TWEAK_L, KM_ANY, KM_CTRL|KM_ALT, 0)->ptr, "hide_inside", 1);
+ RNA_boolean_set(WM_keymap_add_item(keymap, "SCULPT_OT_area_hide", LEFTMOUSE, KM_RELEASE, KM_CTRL|KM_SHIFT, 0)->ptr, "show_all", 1);
+ RNA_boolean_set(WM_keymap_add_item(keymap, "SCULPT_OT_area_hide", LEFTMOUSE, KM_RELEASE, KM_CTRL|KM_ALT, 0)->ptr, "show_all", 1);
+ RNA_boolean_set(WM_keymap_add_item(keymap, "SCULPT_OT_area_hide", HKEY, KM_PRESS, KM_ALT, 0)->ptr, "show_all", 1);
//stroke_mode_modal_keymap(keyconf);
@@ -394,16 +432,41 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
RNA_enum_set(WM_keymap_add_item(keymap, "PAINT_OT_vertex_paint_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_RADIALCONTROL_STRENGTH);
WM_keymap_verify_item(keymap, "PAINT_OT_vertex_paint", LEFTMOUSE, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "PAINT_OT_sample_color", RIGHTMOUSE, KM_PRESS, 0, 0);
-
WM_keymap_add_item(keymap,
"PAINT_OT_vertex_color_set",KKEY, KM_PRESS, KM_SHIFT, 0);
+ /* ptex edit mode */
+ WM_keymap_add_item(keymap, "PTEX_OT_subface_select", RIGHTMOUSE, KM_PRESS, 0, 0);
+ kmi = WM_keymap_add_item(keymap, "PTEX_OT_subface_select", RIGHTMOUSE, KM_PRESS, KM_SHIFT, 0);
+ RNA_boolean_set(kmi->ptr, "extend", 1);
+ WM_keymap_add_item(keymap, "PTEX_OT_select_all", AKEY, KM_PRESS, 0, 0);
+
+ /* ptex subface hiding */
+ kmi = WM_keymap_add_item(keymap, "PTEX_OT_subface_flag_set", HKEY, KM_PRESS, 0, 0);
+ RNA_enum_set(kmi->ptr, "flag", MPTEX_SUBFACE_HIDDEN);
+ kmi = WM_keymap_add_item(keymap, "PTEX_OT_subface_flag_set", HKEY, KM_PRESS, KM_ALT, 0);
+ RNA_enum_set(kmi->ptr, "flag", MPTEX_SUBFACE_HIDDEN);
+ RNA_boolean_set(kmi->ptr, "ignore_unselected", 0);
+ RNA_boolean_set(kmi->ptr, "ignore_hidden", 0);
+ RNA_boolean_set(kmi->ptr, "set", 0);
+
+ /* ptex subface mask */
+ kmi = WM_keymap_add_item(keymap, "PTEX_OT_subface_flag_set", MKEY, KM_PRESS, 0, 0);
+ RNA_enum_set(kmi->ptr, "flag", MPTEX_SUBFACE_MASKED);
+ kmi = WM_keymap_add_item(keymap, "PTEX_OT_subface_flag_set", MKEY, KM_PRESS, KM_ALT, 0);
+ RNA_enum_set(kmi->ptr, "flag", MPTEX_SUBFACE_MASKED);
+ RNA_boolean_set(kmi->ptr, "ignore_unselected", 0);
+ RNA_boolean_set(kmi->ptr, "ignore_hidden", 0);
+ RNA_boolean_set(kmi->ptr, "set", 0);
+
ed_keymap_paint_brush_switch(keymap, "tool_settings.vertex_paint.active_brush_index");
ed_keymap_paint_brush_size(keymap, "tool_settings.vertex_paint.brush.size");
kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", MKEY, KM_PRESS, 0, 0); /* mask toggle */
RNA_string_set(kmi->ptr, "data_path", "vertex_paint_object.data.use_paint_mask");
+ ed_keymap_paint_overlay(keymap);
+
/* Weight Paint mode */
keymap= WM_keymap_find(keyconf, "Weight Paint", 0, 0);
keymap->poll= weight_paint_mode_poll;
@@ -442,6 +505,8 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", MKEY, KM_PRESS, 0, 0); /* mask toggle */
RNA_string_set(kmi->ptr, "data_path", "texture_paint_object.data.use_paint_mask");
+ ed_keymap_paint_overlay(keymap);
+
/* face-mask mode */
keymap= WM_keymap_find(keyconf, "Face Mask", 0, 0);
keymap->poll= facemask_paint_poll;
diff --git a/source/blender/editors/sculpt_paint/paint_overlay.c b/source/blender/editors/sculpt_paint/paint_overlay.c
new file mode 100644
index 00000000000..c02e2c1fcd6
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/paint_overlay.c
@@ -0,0 +1,427 @@
+/*
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2010 by Nicholas Bishop
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): None
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "BKE_context.h"
+#include "BKE_image.h"
+#include "BKE_paint.h"
+
+#include "BLI_math.h"
+
+#include "IMB_imbuf_types.h"
+#include "IMB_imbuf.h"
+
+#include "ED_screen.h"
+#include "ED_space_api.h"
+#include "ED_view3d.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+#include "UI_resources.h"
+
+#include "paint_intern.h"
+
+#include <math.h>
+
+int paint_sample_overlay(PaintStroke *stroke, float col[3], float co[2])
+{
+ ViewContext *vc = paint_stroke_view_context(stroke);
+ PaintOverlay *overlay = &vc->scene->toolsettings->paint_overlay;
+
+ col[0] = col[1] = col[2] = col[3] = 0;
+
+ if(overlay->use && overlay->img) {
+ ImBuf *ibuf = BKE_image_get_ibuf(overlay->img, NULL);
+
+ if(ibuf) {
+ int x, y;
+ int offset, trans[2];
+ float uco[3], proj[2];
+
+ paint_stroke_symmetry_unflip(stroke, uco, co);
+ paint_stroke_project(stroke, uco, proj);
+
+
+ paint_overlay_transform(overlay, vc->ar, ibuf,
+ trans, proj, 1, 1);
+ x = trans[0];
+ y = trans[1];
+
+ if(x >= 0 && x < ibuf->x && y >= 0 && y < ibuf->y) {
+ offset = y*ibuf->x + x;
+
+ if(ibuf->rect) {
+ char *ccol = ((char*)ibuf->rect) + offset*4;
+
+ col[0] = ccol[0] / 255.0;
+ col[1] = ccol[1] / 255.0;
+ col[2] = ccol[2] / 255.0;
+ col[3] = ccol[3] / 255.0;
+ }
+ }
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int paint_overlay_poll(bContext *C)
+{
+ PaintOverlay *overlay = &CTX_data_tool_settings(C)->paint_overlay;
+
+ if(vertex_paint_poll(C) || image_texture_paint_poll(C))
+ return overlay->img && overlay->use;
+
+ return 0;
+}
+
+void ED_paint_update_overlay(PaintOverlay *overlay)
+{
+ if(overlay->gltex) {
+ glDeleteTextures(1, &overlay->gltex);
+ overlay->gltex = 0;
+ }
+}
+
+void ED_paint_overlay_draw(const bContext *C, ARegion *ar)
+{
+ PaintOverlay *overlay = &CTX_data_tool_settings(C)->paint_overlay;
+ ImBuf *ibuf;
+ int center[2];
+ int sx, sy;
+
+ if(!paint_overlay_poll((bContext*)C))
+ return;
+
+ ibuf = BKE_image_get_ibuf(overlay->img, NULL);
+
+ if(!ibuf)
+ return;
+
+ glEnable(GL_TEXTURE_2D);
+ glEnable(GL_BLEND);
+
+ if(!overlay->gltex) {
+ /* update gl texture */
+
+ /* doing this manually because it looks like the GPU
+ Image stuff is customized on mesh tex? */
+
+ glGenTextures(1, (GLuint*)&overlay->gltex);
+ glBindTexture(GL_TEXTURE_2D, overlay->gltex);
+
+ if ((ibuf->rect==NULL) && ibuf->rect_float)
+ IMB_rect_from_float(ibuf);
+
+ {
+ char *bc = (char*)ibuf->rect;
+ char transp[3] = {overlay->transp_col[0] * 255,
+ overlay->transp_col[1] * 255,
+ overlay->transp_col[2] * 255};
+ int i;
+
+ for(i = 0; i < ibuf->y*ibuf->x; ++i, bc+=4) {
+ float d[3] = {fabs(bc[2]-transp[0]),
+ fabs(bc[1]-transp[1]),
+ fabs(bc[0]-transp[2])};
+
+ if(d[0] < overlay->transp_tol &&
+ d[1] < overlay->transp_tol &&
+ d[2] < overlay->transp_tol)
+ bc[3] = 0;
+ else
+ bc[3] = 255;
+ }
+ }
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ibuf->x, ibuf->y,
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, overlay->gltex);
+ glColor4f(1, 1, 1, 0.5);
+
+ glPushMatrix();
+ center[0] = ar->winx/2 + overlay->offset[0];
+ center[1] = ar->winy/2 + overlay->offset[1];
+ sx = overlay->size[0] / 2;
+ sy = overlay->size[1] / 2;
+
+ glTranslatef(center[0], center[1], 0);
+ glRotatef(overlay->angle * 180/M_PI, 0, 0, 1);
+
+ /* draw textured quad */
+ glBegin(GL_QUADS);
+ glTexCoord2f(0, 0);
+ glVertex2f(-sx, -sy);
+ glTexCoord2f(1, 0);
+ glVertex2f(+sx, -sy);
+ glTexCoord2f(1, 1);
+ glVertex2f(+sx, +sy);
+ glTexCoord2f(0, 1);
+ glVertex2f(-sx, +sy);
+ glEnd();
+ glPopMatrix();
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+}
+
+
+/* convert screen-space coords to ibuf coords */
+void paint_overlay_transform(PaintOverlay *overlay, ARegion *ar, ImBuf *ibuf,
+ int out[2], float vec[2], int scale, int rotate)
+{
+ float center[2], size[2], org[2], t1[2], t2[2];
+ float sina, cosa;
+
+ center[0] = ar->winx/2 + overlay->offset[0];
+ center[1] = ar->winy/2 + overlay->offset[1];
+ size[0] = overlay->size[0];
+ size[1] = overlay->size[1];
+ org[0] = center[0] - size[0]/2;
+ org[1] = center[1] - size[1]/2;
+ sina = sin(overlay->angle);
+ cosa = cos(overlay->angle);
+
+ /* make overlay center origin */
+ sub_v2_v2v2(t1, vec, center);
+
+ /* apply rotation */
+ if(rotate) {
+ t2[0] = cosa*t1[0] + sina*t1[1];
+ t2[1] = -sina*t1[0] + cosa*t1[1];
+ }
+ else {
+ out[0] = t1[0];
+ out[1] = t1[1];
+ t2[0] = t1[0];
+ t2[1] = t1[1];
+ }
+
+ /* translation */
+ if(scale) {
+ out[0] = t2[0] + size[0]/2;
+ out[1] = t2[1] + size[1]/2;
+
+ /* scale */
+ out[0] *= (ibuf->x / size[0]);
+ out[1] *= (ibuf->y / size[1]);
+ }
+ else {
+ out[0] = t2[0];
+ out[1] = t2[1];
+ }
+}
+
+typedef struct {
+ int x, y;
+ int offset[2];
+ int size[2];
+ float orig_angle, start_angle;
+
+ ImBuf *ibuf;
+ int ibuf_mouse[2];
+
+ void *draw_cb_handle;
+ struct ARegionType *draw_cb_type;
+ /* data needed for manip draw */
+ PaintManipAction action;
+ int cur_mouse[2];
+} VPaintManipData;
+
+/* when rotating or scaling, draw hashed line to center */
+static void paint_overlay_manip_draw(const bContext *C, ARegion *ar, void *data_v)
+{
+ PaintOverlay *overlay = &CTX_data_tool_settings(C)->paint_overlay;
+ VPaintManipData *data = data_v;
+
+ if(data->action != PAINT_MANIP_GRAB) {
+ UI_ThemeColor(TH_WIRE);
+ setlinestyle(3);
+
+ glBegin(GL_LINES);
+ glVertex2i(data->cur_mouse[0] - ar->winrct.xmin,
+ data->cur_mouse[1] - ar->winrct.ymin);
+ glVertex2i(ar->winx/2 + overlay->offset[0],
+ ar->winy/2 + overlay->offset[1]);
+ glEnd();
+
+ setlinestyle(0);
+ }
+}
+
+static int paint_overlay_manip_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ PaintOverlay *overlay = &CTX_data_tool_settings(C)->paint_overlay;
+ ARegion *ar = CTX_wm_region(C);
+ VPaintManipData *data;
+ float mouse[2] = {event->x - ar->winrct.xmin,
+ mouse[1] = event->y - ar->winrct.ymin};
+ int angle_mouse[2];
+
+ op->customdata = data = MEM_callocN(sizeof(VPaintManipData), "VPaintManipData");
+
+ data->x = event->x;
+ data->y = event->y;
+ data->offset[0] = overlay->offset[0];
+ data->offset[1] = overlay->offset[1];
+ data->size[0] = overlay->size[0];
+ data->size[1] = overlay->size[1];
+ data->orig_angle = overlay->angle;
+ data->ibuf = BKE_image_get_ibuf(overlay->img, NULL);
+ data->action = RNA_enum_get(op->ptr, "action");
+ data->cur_mouse[0] = event->x;
+ data->cur_mouse[1] = event->y;
+
+ paint_overlay_transform(overlay, ar, data->ibuf, data->ibuf_mouse, mouse, 0, 1);
+
+ paint_overlay_transform(overlay, ar, data->ibuf, angle_mouse, mouse, 0, 0);
+ data->start_angle = atan2(angle_mouse[0], angle_mouse[1]);
+
+ data->draw_cb_type = ar->type;
+ data->draw_cb_handle = ED_region_draw_cb_activate(ar->type,
+ paint_overlay_manip_draw,
+ data,
+ REGION_DRAW_POST_PIXEL);
+
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int paint_overlay_manip_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+ PaintOverlay *overlay = &CTX_data_tool_settings(C)->paint_overlay;
+ ARegion *ar = CTX_wm_region(C);
+ float mouse[2] = {event->x - ar->winrct.xmin,
+ event->y - ar->winrct.ymin};
+ int ibuf_mouse[2];
+ int *co = overlay->offset;
+ int *size = overlay->size;
+ float *angle = &overlay->angle;
+ VPaintManipData *data = op->customdata;
+ int dx, dy;
+
+ dx = event->x - data->x;
+ dy = event->y - data->y;
+ data->cur_mouse[0] = event->x;
+ data->cur_mouse[1] = event->y;
+
+ switch(data->action) {
+ case PAINT_MANIP_GRAB:
+ co[0] = data->offset[0] + dx;
+ co[1] = data->offset[1] + dy;
+ break;
+ case PAINT_MANIP_SCALE:
+ {
+ float d[2];
+
+ paint_overlay_transform(overlay, ar, data->ibuf, ibuf_mouse, mouse, 0, 1);
+
+ d[0] = fabs(ibuf_mouse[0]) - fabs(data->ibuf_mouse[0]);
+ d[1] = fabs(ibuf_mouse[1]) - fabs(data->ibuf_mouse[1]);
+
+ size[0] = data->size[0] + d[0];
+ size[1] = data->size[1] + d[1];
+ }
+ break;
+ case PAINT_MANIP_ROTATE:
+ paint_overlay_transform(overlay, ar, data->ibuf, ibuf_mouse, mouse, 0, 0);
+ *angle = data->orig_angle + data->start_angle - atan2(ibuf_mouse[0], ibuf_mouse[1]);
+
+ break;
+ }
+
+ ED_region_tag_redraw(CTX_wm_region(C));
+
+ if(event->type == LEFTMOUSE) {
+ ED_region_draw_cb_exit(data->draw_cb_type, data->draw_cb_handle);
+ MEM_freeN(data);
+ return OPERATOR_FINISHED;
+ }
+ else if(event->type == RIGHTMOUSE) {
+ co[0] = data->offset[0];
+ co[1] = data->offset[1];
+ size[0] = data->size[0];
+ size[1] = data->size[1];
+ *angle = data->orig_angle;
+ ED_region_draw_cb_exit(data->draw_cb_type, data->draw_cb_handle);
+ MEM_freeN(data);
+ return OPERATOR_CANCELLED;
+ }
+ else
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void PAINT_OT_overlay_manipulate(wmOperatorType *ot)
+{
+ static EnumPropertyItem action_items[]= {
+ {PAINT_MANIP_GRAB, "GRAB", 0, "Grab", ""},
+ {PAINT_MANIP_SCALE, "SCALE", 0, "Scale", ""},
+ {PAINT_MANIP_ROTATE, "Rotate", 0, "Rotate", ""},
+
+ {0, NULL, 0, NULL, NULL}};
+
+ /* identifiers */
+ ot->name= "Paint Overlay Manipulate";
+ ot->idname= "PAINT_OT_overlay_manipulate";
+
+ /* api callbacks */
+ ot->invoke= paint_overlay_manip_invoke;
+ ot->modal= paint_overlay_manip_modal;
+ ot->poll= paint_overlay_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_BLOCKING;
+
+ /* properties */
+ ot->prop= RNA_def_enum(ot->srna, "action", action_items, 0, "Action", "");
+}
diff --git a/source/blender/editors/sculpt_paint/paint_ptex.c b/source/blender/editors/sculpt_paint/paint_ptex.c
new file mode 100644
index 00000000000..be669f4b911
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/paint_ptex.c
@@ -0,0 +1,1414 @@
+#include "MEM_guardedalloc.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_object_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_space_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "BKE_context.h"
+#include "BKE_customdata.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_dmgrid.h"
+#include "BKE_mesh.h"
+#include "BKE_paint.h"
+#include "BKE_ptex.h"
+#include "BKE_report.h"
+#include "BKE_subsurf.h"
+
+#include "BLI_math.h"
+#include "BLI_string.h"
+
+#include "IMB_imbuf.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "paint_intern.h"
+#include "ptex.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+static void paint_raycast_cb(PBVHNode *node, void *data_v, float *tmin)
+{
+ if(BLI_pbvh_node_get_tmin(node) < *tmin) {
+ PaintStrokeRaycastData *data = data_v;
+
+ if(BLI_pbvh_node_raycast(data->ob->paint->pbvh, node, NULL,
+ data->ray_start, data->ray_normal,
+ &data->dist, NULL, NULL)) {
+ data->hit |= 1;
+ *tmin = data->dist;
+ }
+ }
+}
+
+static int ptex_paint_stroke_get_location(bContext *C, struct PaintStroke *stroke, float out[3], float mouse[2])
+{
+ // XXX: sculpt_stroke_modifiers_check(C, ss);
+ return paint_stroke_get_location(C, stroke, paint_raycast_cb, NULL, out, mouse, 0);
+}
+
+static int ptex_paint_stroke_test_start(bContext *C, struct wmOperator *op, wmEvent *event)
+{
+ if(paint_stroke_over_mesh(C, op->customdata, event->x, event->y)) {
+ Object *ob= CTX_data_active_object(C);
+ Scene *scene = CTX_data_scene(C);
+ DerivedMesh *dm;
+ Mesh *me;
+
+ /* context checks could be a poll() */
+ me= get_mesh(ob);
+
+ dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH|CD_MASK_MCOL);
+ ob->paint->pbvh = dm->getPBVH(ob, dm);
+
+ pbvh_undo_push_begin("Vertex paint");
+
+ return 1;
+ }
+ return 0;
+}
+
+static void ptex_paint_blend(Brush *brush, PaintStroke *stroke, float col[4], float alpha, float co[2])
+{
+ float src_img[4], *src;
+ int tool = brush->vertexpaint_tool;
+
+ if(tool == IMB_BLEND_ADD_ALPHA &&
+ (brush->flag & BRUSH_DIR_IN))
+ tool = IMB_BLEND_ERASE_ALPHA;
+
+ if(paint_sample_overlay(stroke, src_img, co)) {
+ src = src_img;
+ alpha *= src_img[3];
+ }
+ else
+ src = brush->rgb;
+
+ IMB_blend_color_float(col, col, src, alpha, tool);
+}
+
+static void ptex_elem_to_float4(PtexDataType type, int channels, void *data, float fcol[4])
+{
+ int i;
+
+ /* default alpha */
+ fcol[3] = 1;
+
+ switch(type) {
+ case PTEX_DT_UINT8:
+ for(i = 0; i < channels; ++i)
+ fcol[i] = ((unsigned char*)data)[i] / 255.0;
+ break;
+ case PTEX_DT_UINT16:
+ for(i = 0; i < channels; ++i)
+ fcol[i] = ((unsigned char*)data)[i] / 65535.0;
+ break;
+ case PTEX_DT_FLOAT:
+ for(i = 0; i < channels; ++i)
+ fcol[i] = ((float*)data)[i];
+ break;
+ default:
+ break;
+ }
+
+ if(channels == 1) {
+ for(i = 1; i < 4; ++i)
+ fcol[i] = fcol[0];
+ }
+}
+
+static void ptex_elem_from_float4(PtexDataType type, int channels, void *data, float fcol[4])
+{
+ int i;
+
+ if(channels == 1) {
+ float avg = (fcol[0]+fcol[1]+fcol[2]) / 3.0f;
+ switch(type) {
+ case PTEX_DT_UINT8:
+ ((unsigned char*)data)[0] = avg * 255;
+ break;
+ case PTEX_DT_UINT16:
+ ((unsigned short*)data)[0] = avg * 65535;
+ break;
+ case PTEX_DT_FLOAT:
+ ((float*)data)[0] = avg;
+ break;
+ default:
+ break;
+ }
+ }
+ else {
+ switch(type) {
+ case PTEX_DT_UINT8:
+ for(i = 0; i < channels; ++i)
+ ((unsigned char*)data)[i] = fcol[i] * 255;
+ break;
+ case PTEX_DT_UINT16:
+ for(i = 0; i < channels; ++i)
+ ((unsigned short*)data)[i] = fcol[i] * 65535;
+ break;
+ case PTEX_DT_FLOAT:
+ for(i = 0; i < channels; ++i)
+ ((float*)data)[i] = fcol[i];
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void ptex_get_edge_iter(MPtex *mptex, GridToFace *gtf,
+ char *data,
+ int layersize, int edge, int border,
+ char **start, int *len, int *step)
+{
+ MPtexSubface *subface = &mptex[gtf->face].subfaces[gtf->offset];
+ int *res = subface->res;
+ int start_offset;
+ int x, y;
+
+ if(!data)
+ data = subface->data;
+
+ switch(edge) {
+ case 0:
+ x = border;
+ y = 0;
+ *len = res[0];
+ *step = layersize;
+ break;
+ case 2:
+ x = border;
+ y = res[1] + border + border - 1;
+ *len = res[0];
+ *step = layersize;
+ break;
+ case 1:
+ x = -1;
+ y = border + 1;
+ *len = res[1];
+ *step = layersize * (res[0] + border + border);
+ break;
+ case 3:
+ x = 0;
+ y = border;
+ *len = res[1];
+ *step = layersize * (res[0] + border + border);
+ break;
+ }
+
+ start_offset = y * (res[0] + border + border) + x;
+ *start = data + layersize * start_offset;
+}
+
+
+
+/* build a ptex grid that includes borders filled with neighbor data,
+ or repeated data if at a mesh boundary. this could be done more efficient
+ by not allocating a full grid here, but simpler to get this working first */
+static void *ptex_paint_build_blur_input(DMGridAdjacency *grid_adj, MPtex *mptex,
+ int grid_index, GridToFace *grid_face_map,
+ int layersize)
+{
+ GridToFace *gtf;
+ MPtexSubface *subface;
+ char *out;
+ int i, v;
+
+ gtf = &grid_face_map[grid_index];
+ subface = &mptex[gtf->face].subfaces[gtf->offset];
+
+ out = MEM_mallocN(layersize * (subface->res[0]+2) * (subface->res[1]+2), "blurred_data_input");
+
+ /* copy center */
+ for(v = 0; v < subface->res[1]; ++v) {
+ memcpy(out + layersize * ((v+1) * (subface->res[0]+2) + 1),
+ (char*)subface->data + layersize * v * subface->res[0],
+ layersize * subface->res[0]);
+ }
+
+ /* fill in borders with adjacent data */
+ for(i = 0; i < 4; ++i) {
+ DMGridAdjacency *adj = &grid_adj[grid_index];
+ char *t1, *t2;
+ float step2_fac;
+ int j, len1, len2, step1, step2;
+
+ ptex_get_edge_iter(mptex, &grid_face_map[grid_index], out,
+ layersize, i, 1, &t1, &len1, &step1);
+
+ if(adj->index[i] == -1) {
+ /* mesh boundary, just repeat existing border */
+ ptex_get_edge_iter(mptex, &grid_face_map[grid_index],
+ NULL, layersize, i, 0,
+ &t2, &len2, &step2);
+ }
+ else {
+ ptex_get_edge_iter(mptex, &grid_face_map[adj->index[i]],
+ NULL, layersize, adj->rotation[i], 0,
+ &t2, &len2, &step2);
+ }
+
+ step2_fac = (float)len2 / (float)len1;
+
+ for(j = 0; j < len1; ++j) {
+ memcpy(t1 + step1 * j,
+ t2 + step2*(int)(step2_fac*j),
+ layersize);
+ }
+ }
+
+ return out;
+}
+
+static void ptex_blur_texel(MPtex *pt, MPtexSubface *subface,
+ char *blur_input, int layersize,
+ int u_offset, int v_offset,
+ float avg[4])
+{
+ int i;
+
+ zero_v4(avg);
+
+ for(i = 0; i < 4; ++i) {
+ float col[4];
+ int u = u_offset;
+ int v = v_offset;
+
+ if(i == 0) u--;
+ else if(i == 1) v--;
+ else if(i == 2) u++;
+ else if(i == 3) v++;
+
+ ptex_elem_to_float4(pt->type,
+ pt->channels,
+ blur_input + layersize*((v+1)*(subface->res[0]+2) + u+1),
+ col);
+ avg[0] += col[0];
+ avg[1] += col[1];
+ avg[2] += col[2];
+ avg[3] += col[3];
+ }
+
+ mul_v4_fl(avg, 1.0f / i);
+}
+
+static void ptex_paint_ptex_from_quad(Brush *brush, PaintStroke *stroke, PaintStrokeTest *test,
+ MPtex *pt, MPtexSubface *subface, int res[2],
+ int u_offset, int v_offset, int layersize,
+ char *blur_input,
+ float v1[3], float v2[3], float v3[3], float v4[3])
+
+{
+ char *data = (char*)subface->data + layersize * (v_offset * subface->res[0] + u_offset);
+ float dtop[3], dbot[3], xoffset, yinterp, ustep, vstep;
+ float co_bot[3], co_top[3], start_top[3], start_bot[3];
+ int u, v;
+
+ /* start of top and bottom "rails" */
+ copy_v3_v3(start_top, v4);
+ copy_v3_v3(start_bot, v1);
+
+ /* direction of "rails" */
+ sub_v3_v3v3(dtop, v3, v4);
+ sub_v3_v3v3(dbot, v2, v1);
+
+ /* offset to use center of texel rather than corner */
+ xoffset = 1.0f / (2 * res[0]);
+ yinterp = 1.0f / (2 * res[1]);
+ madd_v3_v3fl(start_top, dtop, xoffset);
+ madd_v3_v3fl(start_bot, dbot, xoffset);
+
+ ustep = 1.0f / res[0];
+ vstep = 1.0f / res[1];
+
+ /* precalculate interpolation along "rails" */
+ mul_v3_fl(dtop, ustep);
+ mul_v3_fl(dbot, ustep);
+
+ for(v = 0; v < res[1]; ++v) {
+ copy_v3_v3(co_top, start_top);
+ copy_v3_v3(co_bot, start_bot);
+
+ for(u = 0; u < res[0]; ++u) {
+ float co[3];
+
+ interp_v3_v3v3(co, co_bot, co_top, yinterp);
+
+ if(paint_stroke_test(test, co)) {
+ float strength;
+ float fcol[4];
+ char *elem = data + layersize*(v*subface->res[0] + u);
+
+ strength = brush->alpha *
+ paint_stroke_combined_strength(stroke, test->dist, co, 0);
+
+ ptex_elem_to_float4(pt->type, pt->channels, elem, fcol);
+
+ if(blur_input) {
+ float blurcol[4];
+
+ ptex_blur_texel(pt, subface,
+ blur_input, layersize,
+ u_offset + u, v_offset + v,
+ blurcol);
+
+ interp_v4_v4v4(fcol, fcol, blurcol, strength);
+ }
+ else
+ ptex_paint_blend(brush, stroke, fcol, strength, co);
+
+ ptex_elem_from_float4(pt->type, pt->channels, elem, fcol);
+ }
+
+ add_v3_v3(co_bot, dbot);
+ add_v3_v3(co_top, dtop);
+ }
+
+ yinterp += vstep;
+ }
+}
+
+static void ptex_paint_node_grids(Brush *brush, PaintStroke *stroke,
+ DMGridData **grids, GridKey *gridkey,
+ GridToFace *grid_face_map,
+ DMGridAdjacency *grid_adj,
+ CustomData *fdata,
+ int *grid_indices,
+ int totgrid, int gridsize)
+{
+ PaintStrokeTest test;
+ MPtex *mptex;
+ int i;
+
+ mptex = CustomData_get_layer(fdata, CD_MPTEX);
+
+ paint_stroke_test_init(&test, stroke);
+
+ for(i = 0; i < totgrid; ++i) {
+ int g = grid_indices[i];
+ DMGridData *grid = grids[g];
+ GridToFace *gtf = &grid_face_map[g];
+ MPtex *pt = &mptex[gtf->face];
+ MPtexSubface *subface = &pt->subfaces[gtf->offset];
+ char *blur_input = NULL;
+ int u, v, x, y, layersize, res[2];
+
+ /* ignore hidden and masked subfaces */
+ if(subface->flag & (MPTEX_SUBFACE_HIDDEN|MPTEX_SUBFACE_MASKED))
+ continue;
+
+ layersize = pt->channels * ptex_data_size(pt->type);
+
+ if(brush->vertexpaint_tool == VERTEX_PAINT_BLUR) {
+ blur_input = ptex_paint_build_blur_input(grid_adj, mptex,
+ g, grid_face_map,
+ layersize);
+ }
+
+ res[0] = MAX2(subface->res[0] / (gridsize - 1), 1);
+ res[1] = MAX2(subface->res[1] / (gridsize - 1), 1);
+
+ for(v = 0, y = 0; v < subface->res[1]; v += res[1], ++y) {
+ for(u = 0, x = 0; u < subface->res[0]; u += res[0], ++x) {
+ float *co[4] = {
+ GRIDELEM_CO_AT(grid, y*gridsize+x, gridkey),
+ GRIDELEM_CO_AT(grid, y*gridsize+(x+1), gridkey),
+
+ GRIDELEM_CO_AT(grid, (y+1)*gridsize+(x+1), gridkey),
+ GRIDELEM_CO_AT(grid, (y+1)*gridsize+x, gridkey),
+ };
+
+ ptex_paint_ptex_from_quad(brush, stroke, &test,
+ pt, subface, res, u, v,
+ layersize, blur_input,
+ co[0], co[1], co[2], co[3]);
+ }
+ }
+
+ if(blur_input)
+ MEM_freeN(blur_input);
+ }
+}
+
+static void ptex_paint_nodes(VPaint *vp, PaintStroke *stroke,
+ Scene *scene, Object *ob,
+ PBVHNode **nodes, int totnode)
+{
+ PBVH *pbvh = ob->paint->pbvh;
+ Brush *brush = paint_brush(&vp->paint);
+ CustomData *vdata = NULL;
+ CustomData *fdata = NULL;
+ GridToFace *grid_face_map;
+ int n;
+
+ assert(BLI_pbvh_uses_grids(pbvh));
+
+ BLI_pbvh_get_customdata(pbvh, &vdata, &fdata);
+ grid_face_map = BLI_pbvh_get_grid_face_map(pbvh);
+
+ for(n = 0; n < totnode; ++n) {
+ DMGridData **grids;
+ DMGridAdjacency *grid_adj;
+ GridKey *gridkey;
+ int *grid_indices;
+ int totgrid, gridsize;
+
+ pbvh_undo_push_node(nodes[n], PBVH_UNDO_PTEX, ob, scene);
+
+ BLI_pbvh_node_get_grids(pbvh, nodes[n],
+ &grid_indices, &totgrid, NULL,
+ &gridsize, &grids, &grid_adj, &gridkey);
+
+ ptex_paint_node_grids(brush, stroke,
+ grids, gridkey,
+ grid_face_map,
+ grid_adj, fdata,
+ grid_indices,
+ totgrid, gridsize);
+
+ BLI_pbvh_node_set_flags(nodes[n],
+ SET_INT_IN_POINTER(PBVH_UpdateColorBuffers|
+ PBVH_UpdateRedraw));
+ }
+}
+
+static void ptex_paint_restore_node(PBVH *pbvh, PBVHNode *node, PBVHUndoNode *unode,
+ CustomData *fdata, GridToFace *grid_face_map)
+{
+ MPtex *mptex;
+ int *grid_indices, totgrid, i;
+
+ mptex = CustomData_get_layer_named(fdata, CD_MPTEX,
+ (char*)pbvh_undo_node_mptex_name(unode));
+
+ grid_face_map = BLI_pbvh_get_grid_face_map(pbvh);
+
+ BLI_pbvh_node_get_grids(pbvh, node,
+ &grid_indices, &totgrid,
+ NULL, NULL, NULL, NULL, NULL);
+
+ for(i = 0; i < totgrid; i++) {
+ GridToFace *gtf = &grid_face_map[grid_indices[i]];
+ MPtex *pt = &mptex[gtf->face];
+ MPtexSubface *subface = &pt->subfaces[gtf->offset];
+ int layersize;
+
+ layersize = pt->channels * ptex_data_size(pt->type);
+
+ memcpy(subface->data, pbvh_undo_node_mptex_data(unode, i),
+ layersize * subface->res[0] * subface->res[1]);
+ }
+
+ BLI_pbvh_node_set_flags(node, SET_INT_IN_POINTER(PBVH_UpdateColorBuffers|
+ PBVH_UpdateRedraw));
+}
+
+static void ptex_paint_restore(VPaint *vp, Object *ob)
+{
+ Brush *brush = paint_brush(&vp->paint);
+ PBVH *pbvh = ob->paint->pbvh;
+
+ /* Restore the mesh before continuing with anchored stroke */
+ if((brush->flag & BRUSH_ANCHORED) ||
+ (brush->flag & BRUSH_RESTORE_MESH))
+ {
+ PBVHNode **nodes;
+ CustomData *fdata;
+ GridToFace *grid_face_map;
+ int n, totnode;
+
+ BLI_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
+
+ grid_face_map = BLI_pbvh_get_grid_face_map(pbvh);
+ BLI_pbvh_get_customdata(pbvh, NULL, &fdata);
+
+ for(n = 0; n < totnode; n++) {
+ PBVHUndoNode *unode;
+
+ unode= pbvh_undo_get_node(nodes[n]);
+ if(unode) {
+ ptex_paint_restore_node(pbvh, nodes[n], unode, fdata, grid_face_map);
+ }
+ }
+
+ if(nodes)
+ MEM_freeN(nodes);
+ }
+}
+
+static void ptex_stitch_subfaces()
+{
+
+}
+
+static void ptex_paint_stroke_update_step(bContext *C, PaintStroke *stroke,
+ PointerRNA *itemptr)
+{
+ VPaint *vp= CTX_data_tool_settings(C)->vpaint;
+ Object *ob = CTX_data_active_object(C);
+
+ ptex_paint_restore(vp, ob);
+
+ paint_stroke_apply_brush(C, stroke, &vp->paint);
+
+ if(paint_brush(&vp->paint)->vertexpaint_tool == VERTEX_PAINT_BLUR)
+ ptex_stitch_subfaces();
+
+ /* partial redraw */
+ paint_tag_partial_redraw(C, ob);
+}
+
+static void ptex_paint_stroke_brush_action(bContext *C, PaintStroke *stroke)
+{
+
+ VPaint *vp= CTX_data_tool_settings(C)->vpaint;
+ ViewContext *vc = paint_stroke_view_context(stroke);
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = vc->obact;
+ PBVHSearchSphereData search_data;
+ PBVHNode **nodes;
+ int totnode;
+ float center[3], radius;
+
+ paint_stroke_symmetry_location(stroke, center);
+
+ search_data.center = center;
+
+ radius = paint_stroke_radius(stroke);
+ search_data.radius_squared = radius*radius;
+ search_data.original = 0;
+
+ BLI_pbvh_search_gather(ob->paint->pbvh, BLI_pbvh_search_sphere_cb,
+ &search_data, &nodes, &totnode);
+
+ ptex_paint_nodes(vp, stroke, scene, ob, nodes, totnode);
+
+ if(nodes)
+ MEM_freeN(nodes);
+}
+
+static void ptex_paint_stroke_done(bContext *C, struct PaintStroke *stroke)
+{
+ pbvh_undo_push_end();
+}
+
+static int ptex_paint_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ op->customdata = paint_stroke_new(C,
+ ptex_paint_stroke_get_location,
+ ptex_paint_stroke_test_start,
+ ptex_paint_stroke_update_step,
+ NULL,
+ ptex_paint_stroke_brush_action,
+ ptex_paint_stroke_done);
+
+ /* add modal handler */
+ WM_event_add_modal_handler(C, op);
+
+ op->type->modal(C, op, event);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void PAINT_OT_vertex_paint(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Vertex Paint";
+ ot->idname= "PAINT_OT_vertex_paint";
+
+ /* api callbacks */
+ ot->invoke= ptex_paint_invoke;
+ ot->modal= paint_stroke_modal;
+ ot->exec= paint_stroke_exec;
+ ot->poll= vertex_paint_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_BLOCKING;
+
+ RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
+}
+
+static EnumPropertyItem ptex_layer_type_items[]= {
+ {PTEX_DT_UINT8, "PTEX_DT_UINT8", 0, "8-bit channels", ""},
+ {PTEX_DT_UINT16, "PTEX_DT_UINT16", 0, "16-bit channels", ""},
+ {PTEX_DT_FLOAT, "PTEX_DT_FLOAT", 0, "32-bit floating-point channels", ""},
+
+ {0, NULL, 0, NULL, NULL}};
+
+static int ptex_active_layer_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ if(ob) {
+ Mesh *me = get_mesh(ob);
+ if(me)
+ return !!CustomData_get_layer(&me->fdata, CD_MPTEX);
+ }
+ return 0;
+}
+
+static int ptex_layer_convert_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ Mesh *me = ob->data;
+ MPtex *mptex;
+ PtexDataType new_type;
+ int i, j, x, y;
+
+ new_type = RNA_enum_get(op->ptr, "type");
+ mptex = CustomData_get_layer(&me->fdata, CD_MPTEX);
+
+ for(i = 0; i < me->totface; ++i) {
+ MPtex *pt = &mptex[i];
+
+ for(j = 0; j < mptex[i].totsubface; ++j) {
+ MPtexSubface *subface = &pt->subfaces[j];
+ int orig_layersize, new_layersize;
+ float *f;
+ char *orig_data, *new_data, *new_data_start;
+
+ orig_layersize = pt->channels * ptex_data_size(pt->type);
+ new_layersize = pt->channels * ptex_data_size(new_type);
+
+ f = MEM_callocN(sizeof(float) * pt->channels, "tmp ptex elem");
+ new_data_start = new_data =
+ MEM_callocN(new_layersize * subface->res[0] * subface->res[1],
+ "mptex converted data");
+ orig_data = subface->data;
+
+ for(y = 0; y < subface->res[1]; ++y) {
+ for(x = 0; x < subface->res[0]; ++x,
+ orig_data += orig_layersize,
+ new_data += new_layersize) {
+ ptex_elem_to_floats(pt->type, pt->channels, orig_data, f);
+ ptex_elem_from_floats(new_type, pt->channels, new_data, f);
+ }
+ }
+
+ MEM_freeN(subface->data);
+ subface->data = new_data_start;
+ MEM_freeN(f);
+ }
+
+ pt->type = new_type;
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void PTEX_OT_layer_convert(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Convert Layer";
+ ot->description= "Convert Ptex data to another type";
+ ot->idname= "PTEX_OT_layer_convert";
+
+ /* api callbacks */
+ ot->exec= ptex_layer_convert_exec;
+ ot->poll= ptex_active_layer_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "type", ptex_layer_type_items, PTEX_DT_FLOAT, "Type", "Layer channels and data type");
+}
+
+static int next_power_of_two(int n)
+{
+ n--;
+ n = (n >> 1) | n;
+ n = (n >> 2) | n;
+ n = (n >> 4) | n;
+ n = (n >> 8) | n;
+ n = (n >> 16) | n;
+ n++;
+
+ return n;
+}
+
+static const void *ptex_default_data(PtexDataType type) {
+ static const unsigned char ptex_def_val_uc[] = {255, 255, 255, 255};
+ static const unsigned short ptex_def_val_us[] = {65535, 65535, 65535, 65535};
+ static const float ptex_def_val_f[] = {1, 1, 1, 1};
+
+ switch(type) {
+ case PTEX_DT_UINT8:
+ return ptex_def_val_uc;
+ case PTEX_DT_UINT16:
+ return ptex_def_val_us;
+ case PTEX_DT_FLOAT:
+ return ptex_def_val_f;
+ default:
+ return NULL;
+ };
+}
+
+/* add a new ptex layer
+ automatically sets resolution based on face area */
+static int ptex_layer_add_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ Mesh *me = ob->data;
+ MPtex *mptex;
+ float (*limit_pos)[3], *face_area, totarea;
+ float density;
+ float largest_face_area = 0;
+ const void *def_val;
+ PtexDataType type;
+ int totchannel;
+ int layer_size;
+ int tottexel = 0;
+ int active_offset;
+ int i, j;
+
+ type = RNA_enum_get(op->ptr, "type");
+ totchannel = RNA_int_get(op->ptr, "channels");
+ layer_size = ptex_data_size(type) * totchannel;
+ def_val = ptex_default_data(type);
+
+ active_offset = CustomData_number_of_layers(&me->fdata, CD_MPTEX);
+ mptex = CustomData_add_layer(&me->fdata, CD_MPTEX, CD_CALLOC,
+ NULL, me->totface);
+ CustomData_set_layer_active(&me->fdata, CD_MPTEX, active_offset);
+
+ /* TODO: for now i'm allocating texels based on limit surface area;
+ according to ptex paper it's better to use surface derivatives */
+
+ limit_pos = MEM_callocN(sizeof(float)*3*me->totvert, "limit_pos");
+ face_area = MEM_callocN(sizeof(float)*me->totface, "face_area");
+ subsurf_calculate_limit_positions(me, limit_pos);
+ for(i = 0, totarea = 0; i < me->totface; ++i) {
+ MFace *f = &me->mface[i];
+ if(f->v4) {
+ face_area[i] = area_quad_v3(limit_pos[f->v1], limit_pos[f->v2],
+ limit_pos[f->v3], limit_pos[f->v4]);
+ }
+ else {
+ face_area[i] = area_tri_v3(limit_pos[f->v1], limit_pos[f->v2],
+ limit_pos[f->v3]);
+ }
+ largest_face_area = MAX2(largest_face_area, face_area[i]);
+ totarea += face_area[i];
+ }
+
+ /* try to make the density factor less dependent on mesh size */
+ density = RNA_float_get(op->ptr, "density") * 1000 / largest_face_area;
+
+ for(i = 0; i < me->totface; ++i) {
+ int S = me->mface[i].v4 ? 4 : 3;
+ int ures;
+ int vres;
+ int gridsize;
+ char *data;
+
+ if(S == 4) {
+ /* adjust u and v resolution by the ration
+ between the average edge size in u and v
+ directions */
+ float len1 = (len_v3v3(limit_pos[me->mface[i].v1],
+ limit_pos[me->mface[i].v2]) +
+ len_v3v3(limit_pos[me->mface[i].v3],
+ limit_pos[me->mface[i].v4])) * 0.5f;
+ float len2 = (len_v3v3(limit_pos[me->mface[i].v2],
+ limit_pos[me->mface[i].v3]) +
+ len_v3v3(limit_pos[me->mface[i].v4],
+ limit_pos[me->mface[i].v1])) * 0.5f;
+ float r = len2/len1;
+
+ ures = next_power_of_two(sqrtf((face_area[i] * density) * r)) / 2;
+ vres = next_power_of_two(sqrtf((face_area[i] * density) / r)) / 2;
+ }
+ else {
+ /* do triangles uniform (subfaces) */
+ ures = sqrtf(face_area[i] * (density / 3.0f));
+ vres = ures = next_power_of_two(ures);
+ }
+
+ ures = MAX2(ures, 1);
+ vres = MAX2(vres, 1);
+ gridsize = ures * vres;
+
+ mptex[i].totsubface = S;
+ mptex[i].type = type;
+ mptex[i].channels = totchannel;
+
+ for(j = 0; j < S; ++j) {
+ int texels, k;
+
+ mptex[i].subfaces[j].res[0] = ures;
+ mptex[i].subfaces[j].res[1] = vres;
+
+ texels = ures*vres;
+ data = mptex[i].subfaces[j].data =
+ MEM_callocN(layer_size * texels, "MptexSubface.data");
+ tottexel += texels;
+
+ for(k = 0; k < texels; ++k) {
+ memcpy(data, def_val, layer_size);
+ data += layer_size;
+ }
+ }
+ }
+
+ printf("total texels = %d, sqrt(texels)=%.1f\n", tottexel, sqrtf(tottexel));
+
+ MEM_freeN(face_area);
+ MEM_freeN(limit_pos);
+
+ return OPERATOR_FINISHED;
+}
+
+void PTEX_OT_layer_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Add Layer";
+ ot->description= "Add a new ptex layer";
+ ot->idname= "PTEX_OT_layer_add";
+
+ /* api callbacks */
+ ot->exec= ptex_layer_add_exec;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_float(ot->srna, "density", 10, 0, 6000, "Density", "Density of texels to generate", 0, 6000);
+ RNA_def_int(ot->srna, "channels", 3, 1, 4, "Channels", "", 1, 4);
+ RNA_def_enum(ot->srna, "type", ptex_layer_type_items, PTEX_DT_FLOAT, "Type", "Layer channels and data type");
+}
+
+static int ptex_layer_remove_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ Mesh *me = ob->data;
+
+ CustomData_free_layer_active(&me->fdata, CD_MPTEX,
+ me->totface);
+
+ if((ob->mode & OB_MODE_VERTEX_PAINT) &&
+ !CustomData_number_of_layers(&me->fdata, CD_MPTEX))
+ ED_object_toggle_modes(C, OB_MODE_VERTEX_PAINT);
+
+ return OPERATOR_FINISHED;
+}
+
+void PTEX_OT_layer_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Remove Layer";
+ ot->description= "Remove active ptex layer";
+ ot->idname= "PTEX_OT_layer_remove";
+
+ /* api callbacks */
+ ot->exec= ptex_layer_remove_exec;
+ ot->poll= ptex_active_layer_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+static int ptex_layer_save_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ Mesh *me = ob->data;
+ char str[FILE_MAX];
+
+ if(!me->totface)
+ return OPERATOR_CANCELLED;
+
+ RNA_string_get(op->ptr, "filepath", str);
+ if(!ptex_layer_save_file(me, str))
+ return OPERATOR_FINISHED;
+ else
+ return OPERATOR_CANCELLED;
+}
+
+static int ptex_layer_save_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ /*Object *ob = CTX_data_active_object(C);
+ Mesh *me = ob->data;
+ const char *name;
+ char buf[FILE_MAX];*/
+
+ if(RNA_property_is_set(op->ptr, "filepath"))
+ return ptex_layer_save_exec(C, op);
+
+ /*name = me->fdata.layers[CustomData_get_active_layer_index(&me->fdata, CD_MPTEX)].name;
+ BLI_snprintf(buf, FILE_MAX, "%s.ptx", name);
+
+ RNA_string_set(op->ptr, "filepath", buf);*/
+
+ WM_event_add_fileselect(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void PTEX_OT_layer_save(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Save Layer";
+ ot->description= "Save active ptex layer";
+ ot->idname= "PTEX_OT_layer_save";
+
+ /* api callbacks */
+ ot->invoke= ptex_layer_save_invoke;
+ ot->exec= ptex_layer_save_exec;
+ ot->poll= ptex_active_layer_poll;
+
+ WM_operator_properties_filesel(ot, 0, FILE_SPECIAL, FILE_SAVE, WM_FILESEL_FILEPATH);
+}
+
+/* loads a .ptx file
+ makes some assumptions that could be relaxed
+ later as our ptex implementation is refined
+
+ on the other hand, some unsupported ptex features
+ are not checked for yet
+*/
+int ptex_open_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ Mesh *me = ob->data;
+
+ PtexTextureHandle *ptex_texture;
+ PtexDataType ptex_data_type;
+ int totchannel;
+
+ char *path;
+ int i, j;
+
+ path = RNA_string_get_alloc(op->ptr, "filepath", NULL, 0);
+
+ ptex_texture = ptex_open(path, 1, 0);
+ MEM_freeN(path);
+
+ /* check if loader worked */
+ if(!ptex_texture) {
+ BKE_report(op->reports, RPT_ERROR, "Error loading ptex file (see stdout for now, TODO)");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* data type */
+ ptex_data_type = ptex_texture_data_type(ptex_texture);
+ if(ptex_data_type == PTEX_DT_UNSUPPORTED) {
+ BKE_report(op->reports, RPT_ERROR, "Ptex format unsupported");
+ ptex_texture_release(ptex_texture);
+ return OPERATOR_CANCELLED;
+ }
+
+ /* data channels */
+ totchannel = ptex_texture_num_channels(ptex_texture);
+ if(totchannel == 2 || totchannel > 4) {
+ BKE_report(op->reports, RPT_ERROR, "Ptex channel count unsupported");
+ ptex_texture_release(ptex_texture);
+ return OPERATOR_CANCELLED;
+ }
+
+ /* check that ptex file matches mesh topology */
+ for(i = 0, j = 0; i < me->totface; ++i) {
+ MFace *f = &me->mface[i];
+ PtexFaceInfoHandle *ptex_face = ptex_texture_get_face_info(ptex_texture, j);
+ int subface;
+
+ if(!ptex_face) {
+ BKE_report(op->reports, RPT_ERROR, "Ptex/mesh topology mismatch");
+ ptex_texture_release(ptex_texture);
+ return OPERATOR_CANCELLED;
+ }
+
+ subface = ptex_face_info_is_subface(ptex_face);
+
+ if(subface != (f->v4 == 0)) {
+ BKE_report(op->reports, RPT_ERROR, "Ptex/mesh topology mismatch");
+ ptex_texture_release(ptex_texture);
+ return OPERATOR_CANCELLED;
+ }
+
+ j += (f->v4 ? 1 : 3);
+ }
+
+ ptex_layer_from_file(me, ptex_texture);
+
+ return OPERATOR_FINISHED;
+}
+
+static int ptex_open_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ WM_event_add_fileselect(C, op);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void PTEX_OT_open(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Open";
+ ot->idname= "PTEX_OT_open";
+
+ /* api callbacks */
+ ot->exec= ptex_open_exec;
+ ot->invoke= ptex_open_invoke;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ WM_operator_properties_filesel(ot, 0, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH|WM_FILESEL_RELPATH);
+}
+
+typedef enum {
+ RES_OP_NUMERIC,
+ RES_OP_DOUBLE,
+ RES_OP_HALF
+} PtexResOp;
+
+static void ptex_face_resolution_set(MPtex *pt, int offset, ToolSettings *ts, PtexResOp op)
+{
+ int i;
+
+ for(i = 0; i < pt->totsubface; ++i) {
+ int ures, vres;
+
+ if(i == offset || pt->totsubface == 4) {
+ MPtexSubface *subface = &pt->subfaces[i];
+
+ switch(op) {
+ case RES_OP_NUMERIC:
+ ures = ts->ptex_ures;
+ vres = ts->ptex_vres;
+ if(pt->totsubface == 4) {
+ ures >>= 1;
+ vres >>= 1;
+ }
+ break;
+ case RES_OP_DOUBLE:
+ ures = subface->res[0] << 1;
+ vres = subface->res[1] << 1;
+ break;
+ case RES_OP_HALF:
+ ures = subface->res[0] >> 1;
+ vres = subface->res[1] >> 1;
+ break;
+ }
+
+ if(ures < 1) ures = 1;
+ if(vres < 1) vres = 1;
+
+ ptex_subface_scale(pt, subface, ures, vres);
+ }
+ }
+}
+
+static void ptex_redraw_selected(PBVHNode *node, void *data)
+{
+ PBVH *pbvh = data;
+ MPtex *mptex;
+ GridToFace *grid_face_map;
+ CustomData *fdata;
+ int totgrid, *grid_indices, i;
+
+ BLI_pbvh_get_customdata(pbvh, NULL, &fdata);
+ mptex = CustomData_get_layer(fdata, CD_MPTEX);
+ grid_face_map = BLI_pbvh_get_grid_face_map(pbvh);
+ BLI_pbvh_node_get_grids(pbvh, node,
+ &grid_indices, &totgrid, NULL, NULL,
+ NULL, NULL, NULL);
+
+ for(i = 0; i < totgrid; ++i) {
+ GridToFace *gtf = &grid_face_map[grid_indices[i]];
+ if(mptex[gtf->face].subfaces[gtf->offset].flag & MPTEX_SUBFACE_SELECTED) {
+ BLI_pbvh_node_set_flags(node,
+ SET_INT_IN_POINTER(PBVH_UpdateColorBuffers|
+ PBVH_UpdateRedraw));
+ }
+ }
+}
+
+static int ptex_face_resolution_set_exec(bContext *C, wmOperator *op)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ Object *ob = CTX_data_active_object(C);
+ Mesh *me = ob->data;
+ MPtex *mptex = CustomData_get_layer(&me->fdata, CD_MPTEX);
+ PtexResOp operation = RNA_enum_get(op->ptr, "operation");
+ int i, j;
+
+ for(i = 0; i < me->totface; ++i) {
+ for(j = 0; j < mptex[i].totsubface; ++j) {
+ if(mptex[i].subfaces[j].flag & MPTEX_SUBFACE_SELECTED) {
+ ptex_face_resolution_set(mptex + i, j, ts, operation);
+ if(mptex[i].totsubface == 4)
+ break;
+ }
+ }
+ }
+
+ BLI_pbvh_search_callback(ob->paint->pbvh, NULL, NULL, ptex_redraw_selected, ob->paint->pbvh);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+static int ptex_face_resolution_set_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ if(ob) {
+ Mesh *me = get_mesh(ob);
+ if(me && CustomData_get_layer(&me->fdata, CD_MPTEX))
+ return 1;
+ }
+ return 0;
+}
+
+void PTEX_OT_face_resolution_set(wmOperatorType *ot)
+{
+ static EnumPropertyItem op_items[] = {
+ {RES_OP_NUMERIC, "NUMERIC", 0, "Numeric", ""},
+ {RES_OP_DOUBLE, "DOUBLE", 0, "Double", ""},
+ {RES_OP_HALF, "HALF", 0, "Half", ""},
+ {0, NULL, 0, NULL, NULL}};
+
+ /* identifiers */
+ ot->name= "Set Face Resolution";
+ ot->idname= "PTEX_OT_face_resolution_set";
+
+ /* api callbacks */
+ ot->exec= ptex_face_resolution_set_exec;
+ ot->poll= ptex_face_resolution_set_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ RNA_def_enum(ot->srna, "operation", op_items, RES_OP_NUMERIC, "Operation", "How to modify the resolution");
+}
+
+typedef struct {
+ int grid_index;
+ PBVHNode *node;
+} PtexSelectData;
+
+static void select_raycast_cb(PBVHNode *node, void *data_v, float *tmin)
+{
+ if(BLI_pbvh_node_get_tmin(node) < *tmin) {
+ PaintStrokeRaycastData *data = data_v;
+ PtexSelectData *mode_data = data->mode_data;
+
+ if(BLI_pbvh_node_raycast(data->ob->paint->pbvh, node, NULL,
+ data->ray_start, data->ray_normal,
+ &data->dist, &mode_data->grid_index, NULL)) {
+ data->hit |= 1;
+ *tmin = data->dist;
+ mode_data->node = node;
+ }
+ }
+}
+
+static int ptex_subface_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ Object *ob = CTX_data_active_object(C);
+ Mesh *me = ob->data;
+ ViewContext vc;
+ float out[3], mouse[2] = {event->x, event->y};
+ PtexSelectData mode_data;
+
+ view3d_set_viewcontext(C, &vc);
+ if(paint_util_raycast(&vc, select_raycast_cb, &mode_data, out, mouse, 0)) {
+ PBVH *pbvh = ob->paint->pbvh;
+ GridToFace *grid_face_map, *gtf;
+ CustomData *fdata;
+ MPtex *mptex, *pt;
+ int *grid_indices;
+ int i, j;
+
+ grid_face_map = BLI_pbvh_get_grid_face_map(pbvh);
+ BLI_pbvh_get_customdata(pbvh, NULL, &fdata);
+ BLI_pbvh_node_get_grids(pbvh, mode_data.node,
+ &grid_indices, NULL, NULL, NULL,
+ NULL, NULL, NULL);
+
+ mptex = CustomData_get_layer(fdata, CD_MPTEX);
+
+ /* deselect everything */
+ if(!RNA_boolean_get(op->ptr, "extend")) {
+ for(i = 0; i < me->totface; ++i) {
+ for(j = 0; j < mptex[i].totsubface; ++j)
+ mptex[i].subfaces[j].flag &= ~MPTEX_SUBFACE_SELECTED;
+ }
+ }
+
+ gtf = &grid_face_map[grid_indices[mode_data.grid_index]];
+ pt = &mptex[gtf->face];
+
+ if(pt->totsubface == 4) {
+ for(i = 0; i < 4; ++i)
+ pt->subfaces[i].flag ^= MPTEX_SUBFACE_SELECTED;
+ }
+ else
+ pt->subfaces[gtf->offset].flag ^= MPTEX_SUBFACE_SELECTED;
+
+ me->act_face = gtf->face;
+ me->act_subface = gtf->offset;
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static int ptex_edit_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+
+ if(ob && (ob->mode & OB_MODE_VERTEX_PAINT)) {
+ Mesh *me = get_mesh(ob);
+ return me && (me->editflag & ME_EDIT_PTEX);
+ }
+
+ return 0;
+}
+
+void PTEX_OT_subface_select(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select Subface";
+ ot->idname= "PTEX_OT_subface_select";
+
+ /* api callbacks */
+ ot->invoke= ptex_subface_select_invoke;
+ ot->poll= ptex_edit_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ RNA_def_boolean(ot->srna, "extend", 0, "Extend", "");
+}
+
+static int ptex_select_all_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ Mesh *me = ob->data;
+ MPtex *mptex;
+ int i, j, action = RNA_enum_get(op->ptr, "action");
+
+ mptex = CustomData_get_layer(&me->fdata, CD_MPTEX);
+
+ if(action == SEL_TOGGLE) {
+ for(i = 0; i < me->totface; ++i) {
+ for(j = 0; j < mptex[i].totsubface; ++j) {
+ if(mptex[i].subfaces[j].flag & MPTEX_SUBFACE_SELECTED) {
+ action = SEL_DESELECT;
+ break;
+ }
+ }
+ }
+ }
+
+ if(action == SEL_TOGGLE)
+ action = SEL_SELECT;
+
+ for(i = 0; i < me->totface; ++i) {
+ for(j = 0; j < mptex[i].totsubface; ++j) {
+ MPtexSubface *subface = &mptex[i].subfaces[j];
+ switch(action) {
+ case SEL_SELECT:
+ subface->flag |= MPTEX_SUBFACE_SELECTED;
+ break;
+ case SEL_DESELECT:
+ subface->flag &= ~MPTEX_SUBFACE_SELECTED;
+ break;
+ case SEL_INVERT:
+ subface->flag ^= MPTEX_SUBFACE_SELECTED;
+ break;
+ }
+ }
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PTEX_OT_select_all(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select or Deselect All";
+ ot->description= "Change selection of all ptex faces";
+ ot->idname= "PTEX_OT_select_all";
+
+ /* api callbacks */
+ ot->exec= ptex_select_all_exec;
+ ot->poll= ptex_edit_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ WM_operator_properties_select_all(ot);
+}
+
+int ptex_subface_flag_set_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ Mesh *me = ob->data;
+ MPtex *mptex = CustomData_get_layer(&me->fdata, CD_MPTEX);
+ int flag = RNA_enum_get(op->ptr, "flag");
+ int set = RNA_boolean_get(op->ptr, "set");
+ int ignore_unselected = RNA_boolean_get(op->ptr, "ignore_unselected");
+ int ignore_hidden = RNA_boolean_get(op->ptr, "ignore_hidden");
+ int i, j;
+
+ for(i = 0; i < me->totface; ++i) {
+ for(j = 0; j < mptex[i].totsubface; ++j) {
+ MPtexSubface *subface = &mptex[i].subfaces[j];
+
+ if((!ignore_unselected || (subface->flag & MPTEX_SUBFACE_SELECTED)) &&
+ (!ignore_hidden || !(subface->flag & MPTEX_SUBFACE_HIDDEN))) {
+ if(set)
+ subface->flag |= flag;
+ else
+ subface->flag &= ~flag;
+ }
+ }
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PTEX_OT_subface_flag_set(wmOperatorType *ot)
+{
+ static EnumPropertyItem flag_items[] = {
+ {MPTEX_SUBFACE_HIDDEN, "HIDDEN", 0, "Hidden", ""},
+ {MPTEX_SUBFACE_MASKED, "MASKED", 0, "Masked", ""},
+ {0, NULL, 0, NULL, NULL}};
+
+ /* identifiers */
+ ot->name= "Set Subface Flags";
+ ot->description= "Set or clear a flag from ptex subfaces";
+ ot->idname= "PTEX_OT_subface_flag_set";
+
+ /* api callbacks */
+ ot->exec= ptex_subface_flag_set_exec;
+ ot->poll= ptex_active_layer_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ RNA_def_enum(ot->srna, "flag", flag_items, 0, "Flag", "");
+ RNA_def_boolean(ot->srna, "set", 1, "Set", "Set the flag if true, otherwise clear the flag");
+ RNA_def_boolean(ot->srna, "ignore_unselected", 1, "Ignore Unselected", "Don't change the flags of unselected faces");
+ RNA_def_boolean(ot->srna, "ignore_hidden", 1, "Ignore Hidden", "Don't change the flags of hidden faces");
+}
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 6d58731d79c..576d4b64b05 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -34,15 +34,19 @@
#include "RNA_access.h"
+#include "BKE_brush.h"
#include "BKE_context.h"
+#include "BKE_image.h"
#include "BKE_paint.h"
-#include "BKE_brush.h"
#include "WM_api.h"
#include "WM_types.h"
#include "BLI_math.h"
+#include "BLI_rand.h"
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
#include "BIF_gl.h"
#include "BIF_glutil.h"
@@ -57,28 +61,62 @@
#include <float.h>
#include <math.h>
-typedef struct PaintStroke {
+struct PaintStroke {
void *mode_data;
void *smooth_stroke_cursor;
wmTimer *timer;
/* Cached values */
ViewContext vc;
+ float project_mat[4][4];
bglMats mats;
Brush *brush;
-
- float last_mouse_position[2];
+ /* brush location (object space) */
+ float location[3], symmetry_location[3];
+ /* screen-space brush location */
+ float mouse[2], initial_mouse[2], last_mouse_position[2];
+ /* not always the same as brush_size() */
+ int pixel_radius;
+ /* tablet pressure, or 1 if using mouse */
+ float pressure;
+ /* 3d brush radius */
+ float radius, radius_squared, initial_radius;
+ /* previous location of updating rake rotation */
+ float last_rake[2];
+ /* this value is added to the brush's rotation in calculations */
+ float rotation;
+ /* mouse location used for texturing */
+ float tex_mouse[2];
+ /* symmetry */
+ /* current symmetry pass (0-7) */
+ int mirror_symmetry_pass;
+ int radial_symmetry_pass;
+ float symm_rot_mat[4][4];
+ float symm_rot_mat_inv[4][4];
+ /* decrease brush strength if symmetry overlaps */
+ float feather;
+
+ /* anything special that sculpt or other paint modes need
+ to do should go through these modifiers */
+ float modifier_initial_radius_factor;
+ int modifier_use_original_texture_coords;
+ int modifier_use_original_location;
/* Set whether any stroke step has yet occurred
e.g. in sculpt mode, stroke doesn't start until cursor
passes over the mesh */
int stroke_started;
+ /* 1 if this is the first paint dab, 0 otherwise */
+ int first_dab;
+ /* callbacks */
StrokeGetLocation get_location;
StrokeTestStart test_start;
StrokeUpdateStep update_step;
+ StrokeUpdateSymmetry update_symmetry;
+ StrokeBrushAction brush_action;
StrokeDone done;
-} PaintStroke;
+};
/*** Cursor ***/
static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata)
@@ -319,7 +357,7 @@ int load_tex(Sculpt *sd, Brush* br, ViewContext* vc)
buffer = MEM_mallocN(sizeof(GLubyte)*size*size, "load_tex");
- #pragma omp parallel for schedule(static) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(static) if (sd->paint.flags & PAINT_USE_OPENMP)
for (j= 0; j < size; j++) {
int i;
float y;
@@ -432,7 +470,7 @@ static void projectf(bglMats *mats, const float v[3], float p[2])
p[1]= uy;
}
-static int project_brush_radius(RegionView3D* rv3d, float radius, float location[3], bglMats* mats)
+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];
@@ -472,8 +510,10 @@ static int project_brush_radius(RegionView3D* rv3d, float radius, float location
return len_v2v2(p1, p2);
}
+#if 0
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])
{
+ Object *ob = CTX_data_active_object(C);
struct PaintStroke *stroke;
float window[2];
int hit;
@@ -487,13 +527,13 @@ int sculpt_get_brush_geometry(bContext* C, int x, int y, int* pixel_radius, floa
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)) {
+ if (ob && ob->paint && ob->paint->sculpt && ob->paint->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);
- mul_m4_v3(stroke->vc.obact->sculpt->ob->obmat, location);
+ mul_m4_v3(ob->obmat, location);
hit = 1;
}
@@ -509,6 +549,7 @@ int sculpt_get_brush_geometry(bContext* C, int x, int y, int* pixel_radius, floa
return hit;
}
+#endif
// XXX duplicated from sculpt.c
float unproject_brush_radius(Object *ob, ViewContext *vc, float center[3], float offset)
@@ -539,14 +580,16 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *unused)
(void)unused;
view3d_set_viewcontext(C, &vc);
-
- if (vc.obact->sculpt) {
+
+// XXX
+#if 0
+ if (vc.obact->paint && vc.obact->paint->sculpt) {
Paint *paint = paint_get_active(CTX_data_scene(C));
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Brush *brush = paint_brush(paint);
- int pixel_radius, viewport[4];
- float location[3], modelview[16], projection[16];
+ int pixel_radius = 0, viewport[4];
+ float location[3]; // XXX: modelview[16], projection[16];
int hit;
@@ -581,7 +624,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *unused)
if(!brush_use_locked_size(brush) && !(paint->flags & PAINT_SHOW_BRUSH))
return;
- hit = sculpt_get_brush_geometry(C, x, y, &pixel_radius, location, modelview, projection, viewport);
+ hit = 0;
if (brush_use_locked_size(brush))
brush_set_size(brush, pixel_radius);
@@ -621,7 +664,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *unused)
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glLoadIdentity();
-
+
if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) {
glTranslatef(0.5f, 0.5f, 0);
@@ -763,7 +806,11 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *unused)
glPopAttrib();
}
- else {
+ //else
+#endif
+
+
+ {
Paint *paint = paint_get_active(CTX_data_scene(C));
Brush *brush = paint_brush(paint);
@@ -783,73 +830,271 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *unused)
}
}
-/* 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])
+/**** Symmetry ****/
+
+float calc_overlap(PaintStroke *stroke, float location[3], char symm, char axis, float angle)
{
- Paint *paint = paint_get_active(CTX_data_scene(C)); // XXX
- Brush *brush = paint_brush(paint); // XXX
+ float mirror[3];
+ float distsq;
+ float mat[4][4];
+
+ //paint_flip_coord(mirror, cache->traced_location, symm);
+ paint_flip_coord(mirror, location, symm);
- float mouse[3];
+ unit_m4(mat);
+ rotate_m4(mat, axis, angle);
- PointerRNA itemptr;
+ mul_m4_v3(mat, mirror);
- float location[3];
+ //distsq = len_squared_v3v3(mirror, cache->traced_location);
+ distsq = len_squared_v3v3(mirror, location);
- float pressure;
- int pen_flip;
+ if (distsq <= 4*stroke->radius_squared)
+ return (2*stroke->radius - sqrt(distsq)) / (2*stroke->radius);
+ else
+ return 0;
+}
+
+static float calc_radial_symmetry_feather(PaintStroke *stroke, Paint *paint,
+ float location[3], char symm, char axis)
+{
+ int i;
+ float overlap;
+
+ overlap = 0;
+ for(i = 1; i < paint->radial_symm[axis-'X']; ++i) {
+ const float angle = 2*M_PI*i / paint->radial_symm[axis-'X'];
+ overlap += calc_overlap(stroke, location, symm, axis, angle);
+ }
+
+ return overlap;
+}
+
+static float calc_symmetry_feather(PaintStroke *stroke, Paint *paint, float location[3], char symm)
+{
+ if(paint->flags & PAINT_SYMMETRY_FEATHER) {
+ float overlap;
+ int i;
+
+ overlap = 0;
+ for (i = 0; i <= symm; i++) {
+ if(i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) {
+
+ overlap += calc_overlap(stroke, location, i, 0, 0);
+
+ overlap += calc_radial_symmetry_feather(stroke, paint, location, i, 'X');
+ overlap += calc_radial_symmetry_feather(stroke, paint, location, i, 'Y');
+ overlap += calc_radial_symmetry_feather(stroke, paint, location, i, 'Z');
+ }
+ }
+
+ return 1/overlap;
+ }
+ else {
+ return 1;
+ }
+}
+
+/* flip data across the axes specified by symm */
+static void calc_symm(bContext *C, PaintStroke *stroke, float location[3], char symm,
+ char axis, float angle)
+{
+ /* radial symmetry */
+ unit_m4(stroke->symm_rot_mat);
+ rotate_m4(stroke->symm_rot_mat, axis, angle);
+
+ unit_m4(stroke->symm_rot_mat_inv);
+ rotate_m4(stroke->symm_rot_mat_inv, axis, -angle);
+
+ /* symmetry_location */
+ paint_flip_coord(location, location, symm);
+ mul_m4_v3(stroke->symm_rot_mat, location);
+ copy_v3_v3(stroke->symmetry_location, location);
+
+ /* callback */
+ if(stroke->update_symmetry) {
+ stroke->update_symmetry(C, stroke, symm, axis,
+ angle,
+ stroke->mirror_symmetry_pass,
+ stroke->radial_symmetry_pass,
+ stroke->symm_rot_mat);
+ }
+}
+
+static void do_radial_symmetry(bContext *C, PaintStroke *stroke, Paint *paint, char symm, int axis, float feather)
+{
+ int i;
+
+ for(i = 1; i < paint->radial_symm[axis-'X']; ++i) {
+ const float angle = 2*M_PI*i / paint->radial_symm[axis-'X'];
+ float location[3];
+
+ stroke->radial_symmetry_pass= i;
+ copy_v3_v3(location, stroke->location);
+ calc_symm(C, stroke, location, symm, axis, angle);
+
+ stroke->brush_action(C, stroke);
+ }
+}
+
+static void paint_stroke_update_cache(bContext *C, PaintStroke *stroke, Paint *paint)
+{
+ ViewContext *vc = &stroke->vc;
+
+ if(stroke->first_dab) {
+ copy_v2_v2(stroke->initial_mouse, stroke->mouse);
+ copy_v2_v2(stroke->tex_mouse, stroke->initial_mouse);
+
+ if(!brush_use_locked_size(stroke->brush)) {
+ stroke->initial_radius =
+ paint_calc_object_space_radius(vc,
+ stroke->location,
+ brush_size(stroke->brush));
+ brush_set_unprojected_radius(stroke->brush, stroke->initial_radius);
+ }
+ else
+ stroke->initial_radius= brush_unprojected_radius(stroke->brush);
+
+ stroke->initial_radius *= stroke->modifier_initial_radius_factor;
+ }
+
+ stroke->pixel_radius = brush_size(stroke->brush);
+ stroke->radius = stroke->initial_radius;
+ stroke->feather = calc_symmetry_feather(stroke, paint, stroke->location, paint->flags & 7);
+
+ if(brush_use_size_pressure(stroke->brush)) {
+ stroke->pixel_radius *= stroke->pressure;
+ stroke->radius *= stroke->pressure;
+ }
+
+ if(!((stroke->brush->flag & BRUSH_ANCHORED) ||
+ stroke->modifier_use_original_texture_coords)) {
+ copy_v2_v2(stroke->tex_mouse, stroke->mouse);
+
+ if((stroke->brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) &&
+ (stroke->brush->flag & BRUSH_RANDOM_ROTATION) &&
+ !(stroke->brush->flag & BRUSH_RAKE)) {
+ stroke->rotation = 2*M_PI*BLI_frand();
+ }
+ }
- ViewContext vc; // XXX
+ if(stroke->brush->flag & BRUSH_ANCHORED) {
+ int dx, dy;
+
+ dx = stroke->mouse[0] - stroke->initial_mouse[0];
+ dy = stroke->mouse[1] - stroke->initial_mouse[1];
+
+ stroke->pixel_radius = sqrt(dx*dx + dy*dy);
+
+ stroke->rotation = atan2(dx, dy) + M_PI;
+
+ if(stroke->brush->flag & BRUSH_EDGE_TO_EDGE) {
+ float d[3];
+ float halfway[3];
+ float out[3];
+
+ d[0] = dx;
+ d[1] = dy;
+ d[2] = 0;
+
+ mul_v3_v3fl(halfway, d, 0.5f);
+ add_v3_v3(halfway, stroke->initial_mouse);
+
+ if(stroke->get_location(C, stroke, out, halfway)) {
+ copy_v2_v2(stroke->tex_mouse, halfway);
+ copy_v3_v3(stroke->location, out);
+ stroke->pixel_radius /= 2.0f;
+ }
+ }
+
+ stroke->radius= paint_calc_object_space_radius(&stroke->vc,
+ stroke->location,
+ stroke->pixel_radius);
+ }
+ else if(stroke->brush->flag & BRUSH_RAKE) {
+ const float u = 0.5f;
+ const float r = 20;
+
+ const float dx = stroke->last_rake[0] - stroke->mouse[0];
+ const float dy = stroke->last_rake[1] - stroke->mouse[1];
+
+ if(stroke->first_dab) {
+ copy_v2_v2(stroke->last_rake, stroke->mouse);
+ }
+ else if (dx*dx + dy*dy >= r*r) {
+ stroke->rotation = atan2(dx, dy);
+ interp_v2_v2v2(stroke->last_rake, stroke->mouse, stroke->last_rake, u);
+ }
+ }
+
+ stroke->radius_squared = stroke->radius*stroke->radius;
+}
+
+/* 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));
PaintStroke *stroke = op->customdata;
+ PointerRNA itemptr;
+ int pen_flip;
- view3d_set_viewcontext(C, &vc); // XXX
+ view3d_get_object_project_mat(stroke->vc.rv3d, stroke->vc.obact, stroke->project_mat);
/* Tablet */
if(event->custom == EVT_DATA_TABLET) {
wmTabletData *wmtab= event->customdata;
- pressure = (wmtab->Active != EVT_TABLET_NONE) ? wmtab->Pressure : 1;
+ stroke->pressure = (wmtab->Active != EVT_TABLET_NONE) ? wmtab->Pressure : 1;
pen_flip = (wmtab->Active == EVT_TABLET_ERASER);
}
else {
- pressure = 1;
+ stroke->pressure = 1;
pen_flip = 0;
}
// XXX: temporary check for sculpt mode until things are more unified
- if (vc.obact->sculpt) {
+ if(stroke->vc.obact->paint && stroke->vc.obact->paint->sculpt) {
float delta[3];
- brush_jitter_pos(brush, mouse_in, mouse);
+ brush_jitter_pos(stroke->brush, mouse_in, stroke->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) {
- sub_v3_v3v3(delta, mouse, mouse_in);
- mul_v3_fl(delta, pressure);
- add_v3_v3v3(mouse, mouse_in, delta);
+ if(stroke->brush->flag & BRUSH_JITTER_PRESSURE) {
+ sub_v3_v3v3(delta, stroke->mouse, mouse_in);
+ mul_v3_fl(delta, stroke->pressure);
+ add_v3_v3v3(stroke->mouse, mouse_in, delta);
}
}
else
- copy_v3_v3(mouse, mouse_in);
+ copy_v2_v2(stroke->mouse, mouse_in);
- /* XXX: can remove the if statement once all modes have this */
- if(stroke->get_location)
- stroke->get_location(C, stroke, location, mouse);
- else
- zero_v3(location);
+ if(stroke->first_dab ||
+ !((stroke->brush->flag & BRUSH_ANCHORED) ||
+ stroke->modifier_use_original_location)) {
+
+ /* XXX: can remove the following if statement once all modes have this */
+ if(stroke->get_location)
+ stroke->get_location(C, stroke, stroke->location, stroke->mouse);
+ else
+ zero_v3(stroke->location);
+ }
/* 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_float_set_array(&itemptr, "location", stroke->location);
+ RNA_float_set_array(&itemptr, "mouse", stroke->mouse);
RNA_boolean_set (&itemptr, "pen_flip", pen_flip);
- RNA_float_set (&itemptr, "pressure", pressure);
+ RNA_float_set(&itemptr, "pressure", stroke->pressure);
+
+ copy_v2_v2(stroke->last_mouse_position, stroke->mouse);
- stroke->last_mouse_position[0] = mouse[0];
- stroke->last_mouse_position[1] = mouse[1];
+ paint_stroke_update_cache(C, stroke, paint);
stroke->update_step(C, stroke, &itemptr);
+
+ stroke->first_dab = 0;
}
/* Returns zero if no sculpt changes should be made, non-zero otherwise */
@@ -858,7 +1103,7 @@ static int paint_smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *ev
output[0] = event->x;
output[1] = event->y;
- if ((stroke->brush->flag & BRUSH_SMOOTH_STROKE) &&
+ if((stroke->brush->flag & BRUSH_SMOOTH_STROKE) &&
!ELEM4(stroke->brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK) &&
!(stroke->brush->flag & BRUSH_ANCHORED) &&
!(stroke->brush->flag & BRUSH_RESTORE_MESH))
@@ -915,7 +1160,7 @@ static int paint_space_stroke(bContext *C, wmOperator *op, wmEvent *event, const
pressure = brush_use_size_pressure(stroke->brush) ? wmtab->Pressure : 1;
}
- scale = (brush_size(stroke->brush)*pressure*stroke->brush->spacing/50.0f) / length;
+ scale = (brush_size(stroke->brush) * pressure * stroke->brush->spacing/50.0f) / length;
mul_v2_fl(vec, scale);
steps = (int)(1.0f / scale);
@@ -933,20 +1178,25 @@ static int paint_space_stroke(bContext *C, wmOperator *op, wmEvent *event, const
/**** Public API ****/
PaintStroke *paint_stroke_new(bContext *C,
- StrokeGetLocation get_location,
- StrokeTestStart test_start,
- StrokeUpdateStep update_step,
- StrokeDone done)
+ StrokeGetLocation get_location,
+ StrokeTestStart test_start,
+ StrokeUpdateStep update_step,
+ StrokeUpdateSymmetry update_symmetry,
+ StrokeBrushAction brush_action,
+ StrokeDone done)
{
PaintStroke *stroke = MEM_callocN(sizeof(PaintStroke), "PaintStroke");
stroke->brush = paint_brush(paint_get_active(CTX_data_scene(C)));
view3d_set_viewcontext(C, &stroke->vc);
view3d_get_transformation(stroke->vc.ar, stroke->vc.rv3d, stroke->vc.obact, &stroke->mats);
+ stroke->modifier_initial_radius_factor = 1;
stroke->get_location = get_location;
stroke->test_start = test_start;
stroke->update_step = update_step;
+ stroke->update_symmetry = update_symmetry;
+ stroke->brush_action = brush_action;
stroke->done = done;
return stroke;
@@ -961,7 +1211,6 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
{
PaintStroke *stroke = op->customdata;
float mouse[2];
- int first= 0;
if(!stroke->stroke_started) {
stroke->last_mouse_position[0] = event->x;
@@ -974,9 +1223,10 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
if(stroke->brush->flag & BRUSH_AIRBRUSH)
stroke->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, stroke->brush->rate);
+
+ stroke->first_dab = 1;
}
- first= 1;
//ED_region_tag_redraw(ar);
}
@@ -989,11 +1239,12 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
if(stroke->timer)
WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), stroke->timer);
- stroke->done(C, stroke);
+ if(stroke->stroke_started)
+ stroke->done(C, stroke);
MEM_freeN(stroke);
return OPERATOR_FINISHED;
}
- else if(first || ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (event->type == TIMER && (event->customdata == stroke->timer))) {
+ else if(stroke->first_dab || ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (event->type == TIMER && (event->customdata == stroke->timer))) {
if(stroke->stroke_started) {
if(paint_smooth_stroke(stroke, mouse, event)) {
if(paint_space_stroke_enabled(stroke->brush)) {
@@ -1012,7 +1263,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
}
/* we want the stroke to have the first daub at the start location instead of waiting till we have moved the space distance */
- if(first &&
+ if(stroke->first_dab &&
stroke->stroke_started &&
paint_space_stroke_enabled(stroke->brush) &&
!(stroke->brush->flag & BRUSH_ANCHORED) &&
@@ -1024,12 +1275,66 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
return OPERATOR_RUNNING_MODAL;
}
+void paint_stroke_apply_brush(bContext *C, PaintStroke *stroke, Paint *paint)
+{
+ char symm = paint->flags & 7;
+ float location[3];
+ int i;
+
+ /* symm is a bitwise 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)))) {
+ stroke->mirror_symmetry_pass= i;
+ stroke->radial_symmetry_pass= 0;
+
+ copy_v3_v3(location, stroke->location);
+ calc_symm(C, stroke, location, i, 0, 0);
+
+ stroke->brush_action(C, stroke);
+
+ do_radial_symmetry(C, stroke, paint, i, 'X', stroke->feather);
+ do_radial_symmetry(C, stroke, paint, i, 'Y', stroke->feather);
+ do_radial_symmetry(C, stroke, paint, i, 'Z', stroke->feather);
+ }
+ }
+}
+
+/* combines mask, curve, and texture strengths */
+float paint_stroke_combined_strength(PaintStroke *stroke, float dist, float co[3], float mask)
+{
+ float mco[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. */
+ if(stroke->brush->mtex.tex) {
+ paint_stroke_symmetry_unflip(stroke, mco, co);
+ co = mco;
+ }
+
+ return brush_tex_strength(&stroke->vc,
+ stroke->project_mat, stroke->brush, co, mask, dist,
+ stroke->pixel_radius, stroke->radius,
+ stroke->rotation,
+ stroke->tex_mouse);
+}
+
int paint_stroke_exec(bContext *C, wmOperator *op)
{
+ Paint *paint = paint_get_active(CTX_data_scene(C));
PaintStroke *stroke = op->customdata;
RNA_BEGIN(op->ptr, itemptr, "stroke") {
+ RNA_float_get_array(&itemptr, "location", stroke->location);
+ RNA_float_get_array(&itemptr, "mouse", stroke->mouse);
+ stroke->pressure = RNA_float_get(&itemptr, "pressure");
+ paint_stroke_update_cache(C, stroke, paint);
+
stroke->update_step(C, stroke, &itemptr);
+
+ stroke->first_dab = 0;
}
RNA_END;
@@ -1039,10 +1344,7 @@ int paint_stroke_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-ViewContext *paint_stroke_view_context(PaintStroke *stroke)
-{
- return &stroke->vc;
-}
+/**** mode data ****/
void *paint_stroke_mode_data(struct PaintStroke *stroke)
{
@@ -1054,6 +1356,106 @@ void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data)
stroke->mode_data = mode_data;
}
+/**** cache access ***/
+
+ViewContext *paint_stroke_view_context(PaintStroke *stroke)
+{
+ return &stroke->vc;
+}
+
+float paint_stroke_feather(struct PaintStroke *stroke)
+{
+ return stroke->feather;
+}
+
+void paint_stroke_mouse_location(PaintStroke *stroke, float mouse[2])
+{
+ copy_v2_v2(mouse, stroke->mouse);
+}
+void paint_stroke_initial_mouse_location(PaintStroke *stroke, float initial_mouse[2])
+{
+ copy_v2_v2(initial_mouse, stroke->initial_mouse);
+}
+
+void paint_stroke_location(PaintStroke *stroke, float location[3])
+{
+ copy_v3_v3(location, stroke->location);
+}
+
+float paint_stroke_pressure(struct PaintStroke *stroke)
+{
+ return stroke->pressure;
+}
+
+void paint_stroke_symmetry_location(struct PaintStroke *stroke, float loc[3])
+{
+ copy_v3_v3(loc, stroke->symmetry_location);
+}
+
+float paint_stroke_radius(struct PaintStroke *stroke)
+{
+ return stroke->radius;
+}
+
+float paint_stroke_radius_squared(struct PaintStroke *stroke)
+{
+ return stroke->radius_squared;
+}
+
+int paint_stroke_first_dab(PaintStroke *stroke)
+{
+ return stroke->first_dab;
+}
+
+void paint_stroke_project(PaintStroke *stroke, float loc[3], float out[2])
+{
+ view3d_project_float(stroke->vc.ar, loc, out, stroke->project_mat);
+}
+
+void paint_stroke_symmetry_unflip(PaintStroke *stroke, float out[3], float vec[3])
+{
+ paint_flip_coord(out, vec, stroke->mirror_symmetry_pass);
+
+ if(stroke->radial_symmetry_pass)
+ mul_m4_v3(stroke->symm_rot_mat_inv, out);
+}
+
+/**** stroke modifiers ****/
+void paint_stroke_set_modifier_use_original_location(PaintStroke *stroke)
+{
+ stroke->modifier_use_original_location = 1;
+}
+
+void paint_stroke_set_modifier_initial_radius_factor(PaintStroke *stroke,
+ float initial_radius_factor)
+{
+ stroke->modifier_initial_radius_factor = initial_radius_factor;
+}
+
+void paint_stroke_set_modifier_use_original_texture_coords(PaintStroke *stroke)
+{
+ stroke->modifier_use_original_texture_coords = 1;
+}
+
+/* returns 1 if the mouse is over the mesh, 0 otherwise */
+int paint_stroke_over_mesh(bContext *C, PaintStroke *stroke, int x, int y)
+{
+ float mouse[2] = {x, y}, co[3];
+ return stroke->get_location(C, stroke, co, mouse);
+}
+
+/* Do a raycast in the tree to find the 3d brush location
+ (This allows us to ignore the GL depth buffer)
+ Returns 0 if the ray doesn't hit the mesh, non-zero otherwise
+ */
+int paint_stroke_get_location(bContext *C, PaintStroke *stroke,
+ BLI_pbvh_HitOccludedCallback hit_cb, void *mode_data,
+ float out[3], float mouse[2], int original)
+{
+ ViewContext *vc = paint_stroke_view_context(stroke);
+ return paint_util_raycast(vc, hit_cb, mode_data, out, mouse, original);
+}
+
int paint_poll(bContext *C)
{
Paint *p = paint_get_active(CTX_data_scene(C));
@@ -1072,3 +1474,88 @@ void paint_cursor_start(bContext *C, int (*poll)(bContext *C))
p->paint_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), poll, paint_draw_cursor, NULL);
}
+/* Optimization for testing if a coord is within the brush area */
+
+void paint_stroke_test_init(PaintStrokeTest *test, PaintStroke *stroke)
+{
+ test->radius_squared= stroke->radius_squared;
+ copy_v3_v3(test->location, stroke->symmetry_location);
+}
+
+int paint_stroke_test(PaintStrokeTest *test, float co[3])
+{
+ float distsq = len_squared_v3v3(co, test->location);
+
+ if(distsq <= test->radius_squared) {
+ test->dist = sqrt(distsq);
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+int paint_stroke_test_sq(PaintStrokeTest *test, float co[3])
+{
+ float distsq = len_squared_v3v3(co, test->location);
+
+ if(distsq <= test->radius_squared) {
+ test->dist = distsq;
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+int paint_stroke_test_fast(PaintStrokeTest *test, float co[3])
+{
+ return len_squared_v3v3(co, test->location) <= test->radius_squared;
+}
+
+int paint_stroke_test_cube(PaintStrokeTest *test, float co[3], float local[4][4])
+{
+ const static float side = 0.70710678118654752440084436210485; // sqrt(.5);
+
+ float local_co[3];
+
+ mul_v3_m4v3(local_co, local, co);
+
+ local_co[0] = fabs(local_co[0]);
+ local_co[1] = fabs(local_co[1]);
+ 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;
+
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+
+#if 0
+
+static int paint_stroke_test_cyl(SculptBrushTest *test, float co[3], float location[3], float an[3])
+{
+ if (paint_stroke_test_fast(test, co)) {
+ float t1[3], t2[3], t3[3], dist;
+
+ sub_v3_v3v3(t1, location, co);
+ sub_v3_v3v3(t2, x2, location);
+
+ cross_v3_v3v3(t3, an, t1);
+
+ dist = len_v3(t3)/len_v3(t2);
+
+ test->dist = dist;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/source/blender/editors/sculpt_paint/paint_undo.c b/source/blender/editors/sculpt_paint/paint_undo.c
index c2f82b8e2e0..6dc406005f9 100644
--- a/source/blender/editors/sculpt_paint/paint_undo.c
+++ b/source/blender/editors/sculpt_paint/paint_undo.c
@@ -27,12 +27,19 @@
#include "MEM_guardedalloc.h"
+#include "DNA_customdata_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_object_types.h"
#include "DNA_userdef_types.h"
#include "BLI_listbase.h"
+#include "BKE_mesh.h"
+#include "BLI_string.h"
#include "BKE_context.h"
+#include "BKE_customdata.h"
#include "BKE_global.h"
+#include "BKE_multires.h"
#include "ED_sculpt.h"
@@ -187,6 +194,242 @@ static void undo_stack_free(UndoStack *stack)
stack->current= NULL;
}
+/**** paint undo for layers (mcol, paintmask) ****/
+
+typedef enum {
+ LAYER_ADDED,
+ LAYER_REMOVED
+} PaintLayerUndoOp;
+
+struct PaintLayerUndoNode {
+ struct PaintLayerUndoNode *next, *prev;
+
+ /* customdata type */
+ int type;
+ /* add/remove */
+ PaintLayerUndoOp op;
+ /* only for restoring into its original location */
+ int layer_offset;
+ /* for identifying layer, don't use layer_offset for that */
+ char layer_name[32];
+ /* copy of a removed layer's data */
+ void *layer_data;
+ void **multires_layer_data;
+ float strength;
+ /* length of multires_layer_data array */
+ int totface;
+ /* whether layer has multires data */
+ int flag_multires;
+};
+
+void paint_layer_undo_set_add(PaintLayerUndoNode *unode, char *name)
+{
+ unode->op = LAYER_ADDED;
+
+ /* check for restore */
+ if(unode->layer_name != name) {
+ BLI_strncpy(unode->layer_name, name,
+ sizeof(unode->layer_name));
+ }
+
+ unode->totface = 0;
+}
+
+void paint_layer_undo_set_remove(PaintLayerUndoNode *unode, char *name,
+ CustomData *data, CustomData *fdata,
+ int totvert, int totface)
+{
+ CustomDataMultires *cdm;
+ int ndx;
+
+ unode->op = LAYER_REMOVED;
+ /* check for restore */
+ if(unode->layer_name != name) {
+ BLI_strncpy(unode->layer_name, name,
+ sizeof(unode->layer_name));
+ }
+
+ unode->totface = totface;
+
+ ndx = CustomData_get_named_layer_index(data, unode->type, name);
+ assert(ndx >= 0);
+
+ /* store the layer offset so we can re-insert layer at the
+ same location on undo */
+ unode->layer_offset =
+ ndx - CustomData_get_layer_index(data, unode->type);
+
+ /* backup layer data */
+ unode->layer_data = MEM_dupallocN(data->layers[ndx].data);
+
+ unode->strength = data->layers[ndx].strength;
+
+ unode->flag_multires = data->layers[ndx].flag & CD_FLAG_MULTIRES;
+ if(!unode->flag_multires)
+ return;
+
+ /* back multires data */
+ cdm = CustomData_get_layer(fdata, CD_GRIDS);
+ if(cdm && totface) {
+ int i;
+
+ /* check first cdm to see if this layer has multires data */
+ if(!CustomData_multires_get_data(cdm, unode->type, name))
+ return;
+
+ unode->multires_layer_data =
+ MEM_callocN(sizeof(void*) * totface,
+ "PaintLayerUndoNode.multires_layer_data");
+
+ for(i = 0; i < totface; ++i, ++cdm) {
+ float *f;
+
+ f = CustomData_multires_get_data(cdm, unode->type,
+ name);
+ assert(f);
+
+ unode->multires_layer_data[i] = MEM_dupallocN(f);
+ }
+ }
+}
+
+void paint_layer_undo_restore(bContext *C, ListBase *lb)
+{
+ PaintLayerUndoNode *unode = lb->first; /* only one undo node */
+ Object *ob;
+ Mesh *me;
+ CustomData *data, *fdata;
+ CustomDataMultires *cdm;
+ int i, ndx, offset, active, totelem;
+
+ ob = CTX_data_active_object(C);
+ me = get_mesh(ob);
+ fdata = &me->fdata;
+
+ switch(unode->type) {
+ case CD_MCOL:
+ data = &me->fdata;
+ totelem = me->totface;
+ break;
+ case CD_PAINTMASK:
+ data = &me->vdata;
+ totelem = me->totface;
+ break;
+ default:
+ assert(0);
+ }
+
+ /* update multires before making changes */
+ if(ED_paint_multires_active(CTX_data_scene(C), ob))
+ multires_force_update(ob);
+
+ switch(unode->op) {
+ case LAYER_ADDED:
+ /* backup current layer data for redo */
+ paint_layer_undo_set_remove(unode, unode->layer_name, data,
+ fdata, me->totvert, me->totface);
+
+ active = CustomData_get_active_layer(data, unode->type);
+
+ /* remove layer */
+ ndx = CustomData_get_named_layer_index(data, unode->type,
+ unode->layer_name);
+ CustomData_free_layer(data, unode->type, totelem, ndx);
+
+ /* set active layer */
+ offset = CustomData_number_of_layers(data, unode->type) - 1;
+ if(active > offset)
+ active = offset;
+ CustomData_set_layer_active(data, unode->type, active);
+
+ /* remove layer's multires data */
+ cdm = CustomData_get_layer(fdata, CD_GRIDS);
+ if(!cdm)
+ break;
+
+ CustomData_multires_remove_layers(cdm, me->totface,
+ unode->type,
+ unode->layer_name);
+
+ break;
+ case LAYER_REMOVED:
+ paint_layer_undo_set_add(unode, unode->layer_name);
+
+ /* add layer */
+ CustomData_add_layer_at_offset(data, unode->type, CD_ASSIGN,
+ unode->layer_data, totelem,
+ unode->layer_offset);
+
+ ndx = CustomData_get_named_layer_index(data, unode->type,
+ unode->layer_name);
+ offset = ndx - CustomData_get_layer_index(data, unode->type);
+
+ CustomData_set_layer_active(data, unode->type, offset);
+ BLI_strncpy(data->layers[ndx].name, unode->layer_name,
+ sizeof(data->layers[ndx].name));
+ data->layers[ndx].strength = unode->strength;
+
+ if(!unode->flag_multires)
+ break;
+
+ /* add multires layer */
+ CustomData_set_layer_offset_flag(data, unode->type,
+ offset, CD_FLAG_MULTIRES);
+
+ cdm = CustomData_get_layer(fdata, CD_GRIDS);
+ if(!cdm)
+ break;
+
+ for(i = 0; i < me->totface; ++i, ++cdm) {
+ void *griddata = unode->multires_layer_data[i];
+
+ CustomData_multires_add_layer_data(cdm, unode->type,
+ unode->layer_name,
+ griddata);
+ }
+
+ unode->layer_data = NULL;
+ if(unode->multires_layer_data)
+ MEM_freeN(unode->multires_layer_data);
+ unode->multires_layer_data = NULL;
+
+ break;
+ }
+}
+
+static void paint_layer_undo_node_free(ListBase *lb)
+{
+ PaintLayerUndoNode *unode = lb->first;
+
+ if(unode->layer_data)
+ MEM_freeN(unode->layer_data);
+
+ if(unode->multires_layer_data) {
+ int i;
+
+ for(i = 0; i < unode->totface; ++i)
+ MEM_freeN(unode->multires_layer_data[i]);
+ MEM_freeN(unode->multires_layer_data);
+ }
+}
+
+PaintLayerUndoNode *paint_layer_undo_push(int type, char *description)
+{
+ PaintLayerUndoNode *unode;
+
+ undo_paint_push_begin(UNDO_PAINT_MESH, description,
+ paint_layer_undo_restore,
+ paint_layer_undo_node_free);
+
+ unode = MEM_callocN(sizeof(PaintLayerUndoNode), "PaintLayerUndoNode");
+ unode->type = type;
+
+ BLI_addtail(undo_paint_push_get_list(UNDO_PAINT_MESH), unode);
+ undo_paint_push_end(UNDO_PAINT_MESH);
+
+ return unode;
+}
+
/* Exported Functions */
void undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free)
@@ -242,4 +485,3 @@ void ED_undo_paint_free(void)
undo_stack_free(&ImageUndoStack);
undo_stack_free(&MeshUndoStack);
}
-
diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c
index 0098b8ca12c..d8bef04f82a 100644
--- a/source/blender/editors/sculpt_paint/paint_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_utils.c
@@ -4,6 +4,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@@ -17,10 +18,13 @@
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_DerivedMesh.h"
+#include "BKE_modifier.h"
+#include "BKE_multires.h"
#include "BKE_paint.h"
#include "BIF_gl.h"
+#include "BIF_glutil.h"
#include "ED_view3d.h"
#include "ED_screen.h"
@@ -31,10 +35,21 @@
#include "WM_api.h"
#include "WM_types.h"
+#include "RE_render_ext.h"
+#include "RE_shader_ext.h"
+
#include "paint_intern.h"
/* 3D Paint */
+void ED_paint_force_update(bContext *C)
+{
+ Object *ob= CTX_data_active_object(C);
+
+ if(ob && (ob->mode & (OB_MODE_SCULPT|OB_MODE_VERTEX_PAINT)))
+ multires_force_update(ob);
+}
+
static void imapaint_project(Object *ob, float *model, float *proj, float *co, float *pco)
{
VECCOPY(pco, co);
@@ -296,3 +311,319 @@ void PAINT_OT_face_select_all(wmOperatorType *ot)
WM_operator_properties_select_all(ot);
}
+
+float paint_calc_object_space_radius(ViewContext *vc, float center[3],
+ float pixel_radius)
+{
+ Object *ob = vc->obact;
+ float delta[3], scale, loc[3];
+
+ mul_v3_m4v3(loc, ob->obmat, center);
+
+ initgrabz(vc->rv3d, loc[0], loc[1], loc[2]);
+ window_to_3d_delta(vc->ar, delta, pixel_radius, 0);
+
+ scale= fabsf(mat4_to_scale(ob->obmat));
+ scale= (scale == 0.0f)? 1.0f: scale;
+
+ return len_v3(delta)/scale;
+}
+
+/* Paint modes can handle multires differently from regular meshes, but only
+ if it's the last modifier on the stack and it is not on level zero */
+struct MultiresModifierData *ED_paint_multires_active(Scene *scene, Object *ob)
+{
+ Mesh *me= (Mesh*)ob->data;
+ ModifierData *md, *nmd;
+
+ if(!CustomData_get_layer(&me->fdata, CD_MDISPS)) {
+ /* multires can't work without displacement layer */
+ return NULL;
+ }
+
+ for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) {
+ if(md->type == eModifierType_Multires) {
+ MultiresModifierData *mmd= (MultiresModifierData*)md;
+
+ /* Check if any of the modifiers after multires are active
+ * if not it can use the multires struct */
+ for(nmd= md->next; nmd; nmd= nmd->next)
+ if(modifier_isEnabled(scene, nmd, eModifierMode_Realtime))
+ break;
+
+ if(!nmd && mmd->sculptlvl > 0)
+ return mmd;
+ }
+ }
+
+ return NULL;
+}
+
+/*** BVH Tree ***/
+
+/* Get a screen-space rectangle of the modified area */
+static int paint_get_redraw_rect(ARegion *ar, RegionView3D *rv3d,
+ Object *ob, rcti *rect)
+{
+ PBVH *pbvh= ob->paint->pbvh;
+ float bb_min[3], bb_max[3], pmat[4][4];
+ int i, j, k;
+
+ view3d_get_object_project_mat(rv3d, ob, pmat);
+
+ if(!pbvh)
+ return 0;
+
+ BLI_pbvh_redraw_BB(pbvh, bb_min, bb_max);
+
+ rect->xmin = rect->ymin = INT_MAX;
+ rect->xmax = rect->ymax = INT_MIN;
+
+ if(bb_min[0] > bb_max[0] || bb_min[1] > bb_max[1] || bb_min[2] > bb_max[2])
+ return 0;
+
+ for(i = 0; i < 2; ++i) {
+ for(j = 0; j < 2; ++j) {
+ for(k = 0; k < 2; ++k) {
+ float vec[3], proj[2];
+ vec[0] = i ? bb_min[0] : bb_max[0];
+ vec[1] = j ? bb_min[1] : bb_max[1];
+ vec[2] = k ? bb_min[2] : bb_max[2];
+ view3d_project_float(ar, vec, proj, pmat);
+ rect->xmin = MIN2(rect->xmin, proj[0]);
+ rect->xmax = MAX2(rect->xmax, proj[0]);
+ rect->ymin = MIN2(rect->ymin, proj[1]);
+ rect->ymax = MAX2(rect->ymax, proj[1]);
+ }
+ }
+ }
+
+ return rect->xmin < rect->xmax && rect->ymin < rect->ymax;
+}
+
+void paint_tag_partial_redraw(bContext *C, Object *ob)
+{
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ ARegion *ar = CTX_wm_region(C);
+ rcti r;
+
+ if(paint_get_redraw_rect(ar, rv3d, ob, &r)) {
+ //rcti tmp;
+
+ r.xmin += ar->winrct.xmin + 1;
+ r.xmax += ar->winrct.xmin - 1;
+ r.ymin += ar->winrct.ymin + 1;
+ r.ymax += ar->winrct.ymin - 1;
+
+ //tmp = r;
+
+ //if (!BLI_rcti_is_empty(&ss->previous_r))
+ // BLI_union_rcti(&r, &ss->previous_r);
+
+ //ss->previous_r= tmp;
+
+ ob->paint->partial_redraw = 1;
+ ED_region_tag_redraw_partial(ar, &r);
+ }
+}
+
+void paint_get_redraw_planes(float planes[4][4], ARegion *ar,
+ RegionView3D *rv3d, Object *ob)
+{
+ PBVH *pbvh= ob->paint->pbvh;
+ BoundBox bb;
+ bglMats mats;
+ rcti rect;
+
+ memset(&bb, 0, sizeof(BoundBox));
+
+ view3d_get_transformation(ar, rv3d, ob, &mats);
+ paint_get_redraw_rect(ar, rv3d,ob, &rect);
+
+#if 1
+ /* use some extra space just in case */
+ rect.xmin -= 2;
+ rect.xmax += 2;
+ rect.ymin -= 2;
+ rect.ymax += 2;
+#else
+ /* it was doing this before, allows to redraw a smaller
+ part of the screen but also gives artifaces .. */
+ rect.xmin += 2;
+ rect.xmax -= 2;
+ rect.ymin += 2;
+ rect.ymax -= 2;
+#endif
+
+ view3d_calculate_clipping(&bb, planes, &mats, &rect);
+ mul_m4_fl(planes, -1.0f);
+
+ /* clear redraw flag from nodes */
+ if(pbvh)
+ BLI_pbvh_update(pbvh, PBVH_UpdateRedraw, NULL);
+}
+
+float get_tex_pixel(Brush* br, float u, float v)
+{
+ TexResult texres;
+ float co[3];
+ int hasrgb;
+
+ co[0] = u;
+ co[1] = v;
+ co[2] = 0;
+
+ memset(&texres, 0, sizeof(TexResult));
+ hasrgb = multitex_ext(br->mtex.tex, co, NULL, NULL, 1, &texres);
+
+ if (hasrgb & TEX_RGB)
+ texres.tin = (0.35*texres.tr + 0.45*texres.tg + 0.2*texres.tb)*texres.ta;
+
+ return texres.tin;
+}
+
+/* selectively flip any axis of a coordinate */
+void paint_flip_coord(float out[3], float in[3], const char symm)
+{
+ if(symm & PAINT_SYMM_X)
+ out[0]= -in[0];
+ else
+ out[0]= in[0];
+ if(symm & PAINT_SYMM_Y)
+ out[1]= -in[1];
+ else
+ out[1]= in[1];
+ if(symm & PAINT_SYMM_Z)
+ out[2]= -in[2];
+ else
+ out[2]= in[2];
+}
+
+/* return a multiplier for brush strength at a coordinate,
+ incorporating texture, curve control, and masking
+
+ TODO: pulled almost directly from sculpt, still needs
+ to be prettied up
+*/
+float brush_tex_strength(ViewContext *vc,
+ float pmat[4][4], Brush *br,
+ float co[3], float mask, const float len,
+ float pixel_radius, float radius3d,
+ float special_rotation, float tex_mouse[2])
+{
+ MTex *mtex = &br->mtex;
+ float avg= 1;
+
+ if(!mtex->tex) {
+ avg= 1;
+ }
+ else if(mtex->brush_map_mode == MTEX_MAP_MODE_3D) {
+ float jnk;
+
+ /* Get strength by feeding the vertex
+ location directly into a texture */
+ externtex(mtex, co, &avg,
+ &jnk, &jnk, &jnk, &jnk, 0);
+ }
+ else {
+ float rotation = -mtex->rot;
+ float x, y, point_2d[3];
+ float radius;
+
+ view3d_project_float(vc->ar, co, point_2d, pmat);
+
+ /* if fixed mode, keep coordinates relative to mouse */
+ if(mtex->brush_map_mode == MTEX_MAP_MODE_FIXED) {
+ rotation += special_rotation;
+
+ point_2d[0] -= tex_mouse[0];
+ point_2d[1] -= tex_mouse[1];
+
+ radius = pixel_radius; // use pressure adjusted size for fixed mode
+
+ x = point_2d[0];
+ y = point_2d[1];
+ }
+ else /* else (mtex->brush_map_mode == MTEX_MAP_MODE_TILED),
+ leave the coordinates relative to the screen */
+ {
+ radius = brush_size(br); // use unadjusted size for tiled mode
+
+ x = point_2d[0] - vc->ar->winrct.xmin;
+ y = point_2d[1] - vc->ar->winrct.ymin;
+ }
+
+ x /= vc->ar->winx;
+ y /= vc->ar->winy;
+
+ if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) {
+ x -= 0.5f;
+ y -= 0.5f;
+ }
+
+ x *= vc->ar->winx / radius;
+ y *= vc->ar->winy / radius;
+
+ /* 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;
+ 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 = get_tex_pixel(br, x, y);
+ }
+
+ avg += br->texture_sample_bias;
+
+ avg *= brush_curve_strength(br, len, radius3d); /* Falloff curve */
+ avg*= 1 - mask;
+
+ return avg;
+}
+
+int paint_util_raycast(ViewContext *vc,
+ BLI_pbvh_HitOccludedCallback hit_cb, void *mode_data,
+ float out[3], float mouse[2], int original)
+{
+ float ray_start[3], ray_end[3], ray_normal[3], dist;
+ float obimat[4][4];
+ float mval[2] = {mouse[0] - vc->ar->winrct.xmin,
+ mouse[1] - vc->ar->winrct.ymin};
+ PaintStrokeRaycastData hit_data;
+
+ viewline(vc->ar, vc->v3d, mval, ray_start, ray_end);
+
+ invert_m4_m4(obimat, vc->obact->obmat);
+ mul_m4_v3(obimat, ray_start);
+ mul_m4_v3(obimat, ray_end);
+
+ sub_v3_v3v3(ray_normal, ray_end, ray_start);
+ dist= normalize_v3(ray_normal);
+
+ hit_data.mode_data = mode_data;
+ hit_data.ob = vc->obact;
+ hit_data.ray_start = ray_start;
+ hit_data.ray_normal = ray_normal;
+ hit_data.dist = dist;
+ hit_data.hit = 0;
+ hit_data.original = original;
+ BLI_pbvh_raycast(vc->obact->paint->pbvh, hit_cb, &hit_data,
+ ray_start, ray_normal, original);
+
+ copy_v3_v3(out, ray_normal);
+ mul_v3_fl(out, hit_data.dist);
+ add_v3_v3(out, ray_start);
+
+ return hit_data.hit;
+}
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index 399ba535e57..426cd09dcde 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -22,7 +22,7 @@
*
* The Original Code is: all of this file.
*
- * Contributor(s): none yet.
+ * Contributor(s): Nicholas Bishop
*
* ***** END GPL LICENSE BLOCK *****
*/
@@ -62,30 +62,29 @@
#include "BKE_context.h"
#include "BKE_depsgraph.h"
#include "BKE_deform.h"
+#include "BKE_displist.h"
+#include "BKE_dmgrid.h"
+#include "BKE_global.h"
+#include "BKE_image.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
+#include "BKE_multires.h"
#include "BKE_object.h"
#include "BKE_paint.h"
+#include "BKE_subsurf.h"
+#include "BKE_utildefines.h"
#include "WM_api.h"
#include "WM_types.h"
-
#include "ED_armature.h"
#include "ED_mesh.h"
#include "ED_screen.h"
#include "ED_view3d.h"
-#include "paint_intern.h"
+#include "ptex.h"
-/* brush->vertexpaint_tool */
-#define VP_MIX 0
-#define VP_ADD 1
-#define VP_SUB 2
-#define VP_MUL 3
-#define VP_BLUR 4
-#define VP_LIGHTEN 5
-#define VP_DARKEN 6
+#include "paint_intern.h"
/* polling - retrieve whether cursor should be set or operator should be done */
@@ -102,8 +101,10 @@ int vertex_paint_poll(bContext *C)
{
if(vertex_paint_mode_poll(C) &&
paint_brush(&CTX_data_tool_settings(C)->vpaint->paint)) {
+ Object *ob = CTX_data_active_object(C);
ScrArea *sa= CTX_wm_area(C);
- if(sa->spacetype==SPACE_VIEW3D) {
+ if(!(get_mesh(ob)->editflag & ME_EDIT_PTEX) &&
+ sa->spacetype==SPACE_VIEW3D) {
ARegion *ar= CTX_wm_region(C);
if(ar->regiontype==RGN_TYPE_WINDOW)
return 1;
@@ -187,72 +188,6 @@ unsigned int vpaint_get_current_col(VPaint *vp)
return rgba_to_mcol(brush->rgb[0], brush->rgb[1], brush->rgb[2], 1.0f);
}
-static void do_shared_vertexcol(Mesh *me)
-{
- /* if no mcol: do not do */
- /* if tface: only the involved faces, otherwise all */
- MFace *mface;
- MTFace *tface;
- int a;
- short *scolmain, *scol;
- char *mcol;
-
- if(me->mcol==0 || me->totvert==0 || me->totface==0) return;
-
- scolmain= MEM_callocN(4*sizeof(short)*me->totvert, "colmain");
-
- tface= me->mtface;
- mface= me->mface;
- mcol= (char *)me->mcol;
- for(a=me->totface; a>0; a--, mface++, mcol+=16) {
- if((tface && tface->mode & TF_SHAREDCOL) || (me->editflag & ME_EDIT_PAINT_MASK)==0) {
- scol= scolmain+4*mface->v1;
- scol[0]++; scol[1]+= mcol[1]; scol[2]+= mcol[2]; scol[3]+= mcol[3];
- scol= scolmain+4*mface->v2;
- scol[0]++; scol[1]+= mcol[5]; scol[2]+= mcol[6]; scol[3]+= mcol[7];
- scol= scolmain+4*mface->v3;
- scol[0]++; scol[1]+= mcol[9]; scol[2]+= mcol[10]; scol[3]+= mcol[11];
- if(mface->v4) {
- scol= scolmain+4*mface->v4;
- scol[0]++; scol[1]+= mcol[13]; scol[2]+= mcol[14]; scol[3]+= mcol[15];
- }
- }
- if(tface) tface++;
- }
-
- a= me->totvert;
- scol= scolmain;
- while(a--) {
- if(scol[0]>1) {
- scol[1]/= scol[0];
- scol[2]/= scol[0];
- scol[3]/= scol[0];
- }
- scol+= 4;
- }
-
- tface= me->mtface;
- mface= me->mface;
- mcol= (char *)me->mcol;
- for(a=me->totface; a>0; a--, mface++, mcol+=16) {
- if((tface && tface->mode & TF_SHAREDCOL) || (me->editflag & ME_EDIT_PAINT_MASK)==0) {
- scol= scolmain+4*mface->v1;
- mcol[1]= scol[1]; mcol[2]= scol[2]; mcol[3]= scol[3];
- scol= scolmain+4*mface->v2;
- mcol[5]= scol[1]; mcol[6]= scol[2]; mcol[7]= scol[3];
- scol= scolmain+4*mface->v3;
- mcol[9]= scol[1]; mcol[10]= scol[2]; mcol[11]= scol[3];
- if(mface->v4) {
- scol= scolmain+4*mface->v4;
- mcol[13]= scol[1]; mcol[14]= scol[2]; mcol[15]= scol[3];
- }
- }
- if(tface) tface++;
- }
-
- MEM_freeN(scolmain);
-}
-
static void make_vertexcol(Object *ob) /* single ob */
{
Mesh *me;
@@ -272,24 +207,6 @@ static void make_vertexcol(Object *ob) /* single ob */
//else
memset(me->mcol, 255, 4*sizeof(MCol)*me->totface);
-
- DAG_id_flush_update(&me->id, OB_RECALC_DATA);
-
-}
-
-static void copy_vpaint_prev(VPaint *vp, unsigned int *mcol, int tot)
-{
- if(vp->vpaint_prev) {
- MEM_freeN(vp->vpaint_prev);
- vp->vpaint_prev= NULL;
- }
- vp->tot= tot;
-
- if(mcol==NULL || tot==0) return;
-
- vp->vpaint_prev= MEM_mallocN(4*sizeof(int)*tot, "vpaint_prev");
- memcpy(vp->vpaint_prev, mcol, 4*sizeof(int)*tot);
-
}
static void copy_wpaint_prev (VPaint *wp, MDeformVert *dverts, int dcount)
@@ -494,197 +411,6 @@ void vpaint_dogamma(Scene *scene)
}
*/
-static unsigned int mcol_blend(unsigned int col1, unsigned int col2, int fac)
-{
- char *cp1, *cp2, *cp;
- int mfac;
- unsigned int col=0;
-
- if(fac==0) return col1;
- if(fac>=255) return col2;
-
- mfac= 255-fac;
-
- cp1= (char *)&col1;
- cp2= (char *)&col2;
- cp= (char *)&col;
-
- cp[0]= 255;
- cp[1]= (mfac*cp1[1]+fac*cp2[1])/255;
- cp[2]= (mfac*cp1[2]+fac*cp2[2])/255;
- cp[3]= (mfac*cp1[3]+fac*cp2[3])/255;
-
- return col;
-}
-
-static unsigned int mcol_add(unsigned int col1, unsigned int col2, int fac)
-{
- char *cp1, *cp2, *cp;
- int temp;
- unsigned int col=0;
-
- if(fac==0) return col1;
-
- cp1= (char *)&col1;
- cp2= (char *)&col2;
- cp= (char *)&col;
-
- cp[0]= 255;
- temp= cp1[1] + ((fac*cp2[1])/255);
- if(temp>254) cp[1]= 255; else cp[1]= temp;
- temp= cp1[2] + ((fac*cp2[2])/255);
- if(temp>254) cp[2]= 255; else cp[2]= temp;
- temp= cp1[3] + ((fac*cp2[3])/255);
- if(temp>254) cp[3]= 255; else cp[3]= temp;
-
- return col;
-}
-
-static unsigned int mcol_sub(unsigned int col1, unsigned int col2, int fac)
-{
- char *cp1, *cp2, *cp;
- int temp;
- unsigned int col=0;
-
- if(fac==0) return col1;
-
- cp1= (char *)&col1;
- cp2= (char *)&col2;
- cp= (char *)&col;
-
- cp[0]= 255;
- temp= cp1[1] - ((fac*cp2[1])/255);
- if(temp<0) cp[1]= 0; else cp[1]= temp;
- temp= cp1[2] - ((fac*cp2[2])/255);
- if(temp<0) cp[2]= 0; else cp[2]= temp;
- temp= cp1[3] - ((fac*cp2[3])/255);
- if(temp<0) cp[3]= 0; else cp[3]= temp;
-
- return col;
-}
-
-static unsigned int mcol_mul(unsigned int col1, unsigned int col2, int fac)
-{
- char *cp1, *cp2, *cp;
- int mfac;
- unsigned int col=0;
-
- if(fac==0) return col1;
-
- mfac= 255-fac;
-
- cp1= (char *)&col1;
- cp2= (char *)&col2;
- cp= (char *)&col;
-
- /* first mul, then blend the fac */
- cp[0]= 255;
- cp[1]= (mfac*cp1[1] + fac*((cp2[1]*cp1[1])/255) )/255;
- cp[2]= (mfac*cp1[2] + fac*((cp2[2]*cp1[2])/255) )/255;
- cp[3]= (mfac*cp1[3] + fac*((cp2[3]*cp1[3])/255) )/255;
-
-
- return col;
-}
-
-static unsigned int mcol_lighten(unsigned int col1, unsigned int col2, int fac)
-{
- char *cp1, *cp2, *cp;
- int mfac;
- unsigned int col=0;
-
- if(fac==0) return col1;
- if(fac>=255) return col2;
-
- mfac= 255-fac;
-
- cp1= (char *)&col1;
- cp2= (char *)&col2;
- cp= (char *)&col;
-
- /* See if are lighter, if so mix, else dont do anything.
- if the paint col is darker then the original, then ignore */
- if (cp1[1]+cp1[2]+cp1[3] > cp2[1]+cp2[2]+cp2[3])
- return col1;
-
- cp[0]= 255;
- cp[1]= (mfac*cp1[1]+fac*cp2[1])/255;
- cp[2]= (mfac*cp1[2]+fac*cp2[2])/255;
- cp[3]= (mfac*cp1[3]+fac*cp2[3])/255;
-
- return col;
-}
-
-static unsigned int mcol_darken(unsigned int col1, unsigned int col2, int fac)
-{
- char *cp1, *cp2, *cp;
- int mfac;
- unsigned int col=0;
-
- if(fac==0) return col1;
- if(fac>=255) return col2;
-
- mfac= 255-fac;
-
- cp1= (char *)&col1;
- cp2= (char *)&col2;
- cp= (char *)&col;
-
- /* See if were darker, if so mix, else dont do anything.
- if the paint col is brighter then the original, then ignore */
- if (cp1[1]+cp1[2]+cp1[3] < cp2[1]+cp2[2]+cp2[3])
- return col1;
-
- cp[0]= 255;
- cp[1]= (mfac*cp1[1]+fac*cp2[1])/255;
- cp[2]= (mfac*cp1[2]+fac*cp2[2])/255;
- cp[3]= (mfac*cp1[3]+fac*cp2[3])/255;
- return col;
-}
-
-static void vpaint_blend(VPaint *vp, unsigned int *col, unsigned int *colorig, unsigned int paintcol, int alpha)
-{
- Brush *brush = paint_brush(&vp->paint);
-
- if(brush->vertexpaint_tool==VP_MIX || brush->vertexpaint_tool==VP_BLUR) *col= mcol_blend( *col, paintcol, alpha);
- else if(brush->vertexpaint_tool==VP_ADD) *col= mcol_add( *col, paintcol, alpha);
- else if(brush->vertexpaint_tool==VP_SUB) *col= mcol_sub( *col, paintcol, alpha);
- else if(brush->vertexpaint_tool==VP_MUL) *col= mcol_mul( *col, paintcol, alpha);
- else if(brush->vertexpaint_tool==VP_LIGHTEN) *col= mcol_lighten( *col, paintcol, alpha);
- else if(brush->vertexpaint_tool==VP_DARKEN) *col= mcol_darken( *col, paintcol, alpha);
-
- /* if no spray, clip color adding with colorig & orig alpha */
- if((vp->flag & VP_SPRAY)==0) {
- unsigned int testcol=0, a;
- char *cp, *ct, *co;
-
- alpha= (int)(255.0*brush_alpha(brush));
-
- if(brush->vertexpaint_tool==VP_MIX || brush->vertexpaint_tool==VP_BLUR) testcol= mcol_blend( *colorig, paintcol, alpha);
- else if(brush->vertexpaint_tool==VP_ADD) testcol= mcol_add( *colorig, paintcol, alpha);
- else if(brush->vertexpaint_tool==VP_SUB) testcol= mcol_sub( *colorig, paintcol, alpha);
- else if(brush->vertexpaint_tool==VP_MUL) testcol= mcol_mul( *colorig, paintcol, alpha);
- else if(brush->vertexpaint_tool==VP_LIGHTEN) testcol= mcol_lighten( *colorig, paintcol, alpha);
- else if(brush->vertexpaint_tool==VP_DARKEN) testcol= mcol_darken( *colorig, paintcol, alpha);
-
- cp= (char *)col;
- ct= (char *)&testcol;
- co= (char *)colorig;
-
- for(a=0; a<4; a++) {
- if( ct[a]<co[a] ) {
- if( cp[a]<ct[a] ) cp[a]= ct[a];
- else if( cp[a]>co[a] ) cp[a]= co[a];
- }
- else {
- if( cp[a]<co[a] ) cp[a]= co[a];
- else if( cp[a]>ct[a] ) cp[a]= ct[a];
- }
- }
- }
-}
-
-
static int sample_backbuf_area(ViewContext *vc, int *indexar, int totface, int x, int y, float size)
{
struct ImBuf *ibuf;
@@ -774,32 +500,32 @@ static void wpaint_blend(VPaint *wp, MDeformWeight *dw, MDeformWeight *uw, float
if (flip) {
switch(tool) {
- case VP_MIX:
+ case IMB_BLEND_MIX:
paintval = 1.f - paintval; break;
- case VP_ADD:
- tool= VP_SUB; break;
- case VP_SUB:
- tool= VP_ADD; break;
- case VP_LIGHTEN:
- tool= VP_DARKEN; break;
- case VP_DARKEN:
- tool= VP_LIGHTEN; break;
+ case IMB_BLEND_ADD:
+ tool= IMB_BLEND_SUB; break;
+ case IMB_BLEND_SUB:
+ tool= IMB_BLEND_ADD; break;
+ case IMB_BLEND_LIGHTEN:
+ tool= IMB_BLEND_DARKEN; break;
+ case IMB_BLEND_DARKEN:
+ tool= IMB_BLEND_LIGHTEN; break;
}
}
- if(tool==VP_MIX || tool==VP_BLUR)
+ if(tool==IMB_BLEND_MIX || tool==VERTEX_PAINT_BLUR)
dw->weight = paintval*alpha + dw->weight*(1.0-alpha);
- else if(tool==VP_ADD)
+ else if(tool==IMB_BLEND_ADD)
dw->weight += paintval*alpha;
- else if(tool==VP_SUB)
+ else if(tool==IMB_BLEND_SUB)
dw->weight -= paintval*alpha;
- else if(tool==VP_MUL)
+ else if(tool==IMB_BLEND_MUL)
/* first mul, then blend the fac */
dw->weight = ((1.0-alpha) + alpha*paintval)*dw->weight;
- else if(tool==VP_LIGHTEN) {
+ else if(tool==IMB_BLEND_LIGHTEN) {
if (dw->weight < paintval)
dw->weight = paintval*alpha + dw->weight*(1.0-alpha);
- } else if(tool==VP_DARKEN) {
+ } else if(tool==IMB_BLEND_DARKEN) {
if (dw->weight > paintval)
dw->weight = paintval*alpha + dw->weight*(1.0-alpha);
}
@@ -810,21 +536,21 @@ static void wpaint_blend(VPaint *wp, MDeformWeight *dw, MDeformWeight *uw, float
float testw=0.0f;
alpha= brush_alpha(brush);
- if(tool==VP_MIX || tool==VP_BLUR)
+ if(tool==IMB_BLEND_MIX || tool==VERTEX_PAINT_BLUR)
testw = paintval*alpha + uw->weight*(1.0-alpha);
- else if(tool==VP_ADD)
+ else if(tool==IMB_BLEND_ADD)
testw = uw->weight + paintval*alpha;
- else if(tool==VP_SUB)
+ else if(tool==IMB_BLEND_SUB)
testw = uw->weight - paintval*alpha;
- else if(tool==VP_MUL)
+ else if(tool==IMB_BLEND_MUL)
/* first mul, then blend the fac */
testw = ((1.0-alpha) + alpha*paintval)*uw->weight;
- else if(tool==VP_LIGHTEN) {
+ else if(tool==IMB_BLEND_LIGHTEN) {
if (uw->weight < paintval)
testw = paintval*alpha + uw->weight*(1.0-alpha);
else
testw = uw->weight;
- } else if(tool==VP_DARKEN) {
+ } else if(tool==IMB_BLEND_DARKEN) {
if (uw->weight > paintval)
testw = paintval*alpha + uw->weight*(1.0-alpha);
else
@@ -1413,7 +1139,7 @@ static int wpaint_stroke_test_start(bContext *C, wmOperator *op, wmEvent *event)
wpd->vgroup_mirror= actdef;
}
}
-
+
return 1;
}
@@ -1496,7 +1222,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
/* make sure each vertex gets treated only once */
/* and calculate filter weight */
totw= 0;
- if(brush->vertexpaint_tool==VP_BLUR)
+ if(brush->vertexpaint_tool==VERTEX_PAINT_BLUR)
paintweight= 0.0f;
else
paintweight= ts->vgroup_weight;
@@ -1510,7 +1236,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
(me->dvert+mface->v3)->flag= 1;
if(mface->v4) (me->dvert+mface->v4)->flag= 1;
- if(brush->vertexpaint_tool==VP_BLUR) {
+ if(brush->vertexpaint_tool==VERTEX_PAINT_BLUR) {
MDeformWeight *dw, *(*dw_func)(MDeformVert *, int);
if(wp->flag & VP_ONLYVGROUP)
@@ -1532,7 +1258,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
}
}
- if(brush->vertexpaint_tool==VP_BLUR)
+ if(brush->vertexpaint_tool==VERTEX_PAINT_BLUR)
paintweight/= (float)totw;
for(index=0; index<totindex; index++) {
@@ -1633,7 +1359,7 @@ static int wpaint_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
op->customdata = paint_stroke_new(C, NULL, wpaint_stroke_test_start,
- wpaint_stroke_update_step,
+ wpaint_stroke_update_step, NULL, NULL,
wpaint_stroke_done);
/* add modal handler */
@@ -1689,6 +1415,10 @@ void PAINT_OT_weight_set(wmOperatorType *ot)
/* ************ set / clear vertex paint mode ********** */
+struct VPaintSession {
+ unsigned int src_image_gltex;
+};
+
static int set_vpaint(bContext *C, wmOperator *op) /* toggle */
{
@@ -1704,11 +1434,10 @@ static int set_vpaint(bContext *C, wmOperator *op) /* toggle */
return OPERATOR_PASS_THROUGH;
}
- if(me && me->mcol==NULL) make_vertexcol(ob);
-
/* toggle: end vpaint */
if(ob->mode & OB_MODE_VERTEX_PAINT) {
-
+ free_paintsession(ob);
+
ob->mode &= ~OB_MODE_VERTEX_PAINT;
}
else {
@@ -1716,18 +1445,25 @@ static int set_vpaint(bContext *C, wmOperator *op) /* toggle */
/* Turn off weight painting */
if (ob->mode & OB_MODE_WEIGHT_PAINT)
set_wpaint(C, op);
-
- if(vp==NULL)
- vp= scene->toolsettings->vpaint= new_vpaint(0);
-
+
+ if(me->mcol==NULL)
+ make_vertexcol(ob);
+
+ if(!CustomData_get_layer(&me->fdata, CD_MPTEX))
+ WM_operator_name_call(C, "PTEX_OT_layer_add", WM_OP_INVOKE_REGION_WIN, NULL);
+
+ create_paintsession(ob);
+
paint_cursor_start(C, vertex_paint_poll);
paint_init(&vp->paint, PAINT_CURSOR_VERTEX_PAINT);
}
- if (me)
- /* update modifier stack for mapping requirements */
- DAG_id_flush_update(&me->id, OB_RECALC_DATA);
+ /* create pbvh */
+ if(ob->mode & OB_MODE_VERTEX_PAINT) {
+ DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH|CD_MASK_MCOL);
+ ob->paint->pbvh = dm->getPBVH(ob, dm);
+ }
WM_event_add_notifier(C, NC_SCENE|ND_MODE, scene);
@@ -1749,211 +1485,6 @@ void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot)
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
}
-
-
-/* ********************** vertex paint operator ******************* */
-
-/* Implementation notes:
-
-Operator->invoke()
- - validate context (add mcol)
- - create customdata storage
- - call paint once (mouse click)
- - add modal handler
-
-Operator->modal()
- - for every mousemove, apply vertex paint
- - exit on mouse release, free customdata
- (return OPERATOR_FINISHED also removes handler and operator)
-
-For future:
- - implement a stroke event (or mousemove with past positons)
- - revise whether op->customdata should be added in object, in set_vpaint
-
-*/
-
-typedef struct VPaintData {
- ViewContext vc;
- unsigned int paintcol;
- int *indexar;
- float *vertexcosnos;
- float vpimat[3][3];
-} VPaintData;
-
-static int vpaint_stroke_test_start(bContext *C, struct wmOperator *op, wmEvent *event)
-{
- ToolSettings *ts= CTX_data_tool_settings(C);
- struct PaintStroke *stroke = op->customdata;
- VPaint *vp= ts->vpaint;
- struct VPaintData *vpd;
- Object *ob= CTX_data_active_object(C);
- Mesh *me;
- float mat[4][4], imat[4][4];
-
- /* context checks could be a poll() */
- me= get_mesh(ob);
- if(me==NULL || me->totface==0) return OPERATOR_PASS_THROUGH;
-
- if(me->mcol==NULL) make_vertexcol(ob);
- if(me->mcol==NULL) return OPERATOR_CANCELLED;
-
- /* make mode data storage */
- vpd= MEM_callocN(sizeof(struct VPaintData), "VPaintData");
- paint_stroke_set_mode_data(stroke, vpd);
- view3d_set_viewcontext(C, &vpd->vc);
-
- vpd->vertexcosnos= mesh_get_mapped_verts_nors(vpd->vc.scene, ob);
- vpd->indexar= get_indexarray(me);
- vpd->paintcol= vpaint_get_current_col(vp);
-
- /* for filtering */
- copy_vpaint_prev(vp, (unsigned int *)me->mcol, me->totface);
-
- /* some old cruft to sort out later */
- mul_m4_m4m4(mat, ob->obmat, vpd->vc.rv3d->viewmat);
- invert_m4_m4(imat, mat);
- copy_m3_m4(vpd->vpimat, imat);
-
- return 1;
-}
-
-static void vpaint_paint_face(VPaint *vp, VPaintData *vpd, Object *ob, int index, float mval[2], float pressure, int flip)
-{
- ViewContext *vc = &vpd->vc;
- Brush *brush = paint_brush(&vp->paint);
- Mesh *me = get_mesh(ob);
- MFace *mface= ((MFace*)me->mface) + index;
- unsigned int *mcol= ((unsigned int*)me->mcol) + 4*index;
- unsigned int *mcolorig= ((unsigned int*)vp->vpaint_prev) + 4*index;
- float alpha;
- int i;
-
- if((vp->flag & VP_COLINDEX && mface->mat_nr!=ob->actcol-1) ||
- ((me->editflag & ME_EDIT_PAINT_MASK) && !(mface->flag & ME_FACE_SEL)))
- return;
-
- if(brush->vertexpaint_tool==VP_BLUR) {
- unsigned int fcol1= mcol_blend( mcol[0], mcol[1], 128);
- if(mface->v4) {
- unsigned int fcol2= mcol_blend( mcol[2], mcol[3], 128);
- vpd->paintcol= mcol_blend( fcol1, fcol2, 128);
- }
- else {
- vpd->paintcol= mcol_blend( mcol[2], fcol1, 170);
- }
-
- }
-
- for(i = 0; i < (mface->v4 ? 4 : 3); ++i) {
- alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*(&mface->v1)[i], mval, pressure);
- if(alpha)
- vpaint_blend(vp, mcol+i, mcolorig+i, vpd->paintcol, (int)(alpha*255.0));
- }
-}
-
-static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
-{
- ToolSettings *ts= CTX_data_tool_settings(C);
- struct VPaintData *vpd = paint_stroke_mode_data(stroke);
- VPaint *vp= ts->vpaint;
- Brush *brush = paint_brush(&vp->paint);
- ViewContext *vc= &vpd->vc;
- Object *ob= vc->obact;
- Mesh *me= ob->data;
- float mat[4][4];
- int *indexar= vpd->indexar;
- int totindex, index, flip;
- float pressure, mval[2];
-
- RNA_float_get_array(itemptr, "mouse", mval);
- flip = RNA_boolean_get(itemptr, "pen_flip");
- pressure = RNA_float_get(itemptr, "pressure");
-
- view3d_operator_needs_opengl(C);
-
- /* load projection matrix */
- mul_m4_m4m4(mat, ob->obmat, vc->rv3d->persmat);
-
- mval[0]-= vc->ar->winrct.xmin;
- mval[1]-= vc->ar->winrct.ymin;
-
-
- /* which faces are involved */
- if(vp->flag & VP_AREA) {
- totindex= sample_backbuf_area(vc, indexar, me->totface, mval[0], mval[1], brush_size(brush));
- }
- else {
- indexar[0]= view3d_sample_backbuf(vc, mval[0], mval[1]);
- if(indexar[0]) totindex= 1;
- else totindex= 0;
- }
-
- swap_m4m4(vc->rv3d->persmat, mat);
-
- for(index=0; index<totindex; index++) {
- if(indexar[index] && indexar[index]<=me->totface)
- vpaint_paint_face(vp, vpd, ob, indexar[index]-1, mval, pressure, flip);
- }
-
- swap_m4m4(vc->rv3d->persmat, mat);
-
- /* was disabled because it is slow, but necessary for blur */
- if(brush->vertexpaint_tool == VP_BLUR)
- do_shared_vertexcol(me);
-
- ED_region_tag_redraw(vc->ar);
-
- DAG_id_flush_update(ob->data, OB_RECALC_DATA);
-}
-
-static void vpaint_stroke_done(bContext *C, struct PaintStroke *stroke)
-{
- ToolSettings *ts= CTX_data_tool_settings(C);
- struct VPaintData *vpd= paint_stroke_mode_data(stroke);
-
- if(vpd->vertexcosnos)
- MEM_freeN(vpd->vertexcosnos);
- MEM_freeN(vpd->indexar);
-
- /* frees prev buffer */
- copy_vpaint_prev(ts->vpaint, NULL, 0);
-
- MEM_freeN(vpd);
-}
-
-static int vpaint_invoke(bContext *C, wmOperator *op, wmEvent *event)
-{
-
- op->customdata = paint_stroke_new(C, NULL, vpaint_stroke_test_start,
- vpaint_stroke_update_step,
- vpaint_stroke_done);
-
- /* add modal handler */
- WM_event_add_modal_handler(C, op);
-
- op->type->modal(C, op, event);
-
- return OPERATOR_RUNNING_MODAL;
-}
-
-void PAINT_OT_vertex_paint(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name= "Vertex Paint";
- ot->idname= "PAINT_OT_vertex_paint";
-
- /* api callbacks */
- ot->invoke= vpaint_invoke;
- ot->modal= paint_stroke_modal;
- /* ot->exec= vpaint_exec; <-- needs stroke property */
- ot->poll= vertex_paint_poll;
-
- /* flags */
- ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
-
- RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
-}
-
/* ********************** weight from bones operator ******************* */
static int weight_from_bones_poll(bContext *C)
@@ -2001,4 +1532,3 @@ void PAINT_OT_weight_from_bones(wmOperatorType *ot)
/* properties */
ot->prop= RNA_def_enum(ot->srna, "type", type_items, 0, "Type", "Method to use for assigning weights.");
}
-
diff --git a/source/blender/editors/sculpt_paint/pbvh_undo.c b/source/blender/editors/sculpt_paint/pbvh_undo.c
new file mode 100644
index 00000000000..793f5e79676
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/pbvh_undo.c
@@ -0,0 +1,570 @@
+/*
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2006 by Nicholas Bishop
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#include "BLI_math.h"
+#include "BLI_ghash.h"
+#include "BLI_threads.h"
+
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_key_types.h"
+
+#include "BKE_cdderivedmesh.h"
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_dmgrid.h"
+#include "BKE_modifier.h"
+#include "BKE_multires.h"
+#include "BKE_paint.h"
+#include "BKE_mesh.h"
+#include "BKE_key.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_sculpt.h"
+#include "paint_intern.h"
+#include "sculpt_intern.h"
+#include "ptex.h"
+
+/************************** Undo *************************/
+
+struct PBVHUndoNode {
+ struct PBVHUndoNode *next, *prev;
+
+ /* object id name */
+ char idname[MAX_ID_NAME];
+ /* only during push, not valid afterwards! */
+ struct PBVHNode *node;
+
+ /* total unique verts in node */
+ int totvert;
+
+ /* actual undo data */
+ float (*co)[3];
+ /* paint mask */
+ float *pmask;
+ char pmask_name[32];
+ /* ptex */
+ void **mptex_data;
+ char mptex_name[32];
+
+ /* non-multires */
+ /* to verify if me->totvert it still the same */
+ int maxvert;
+ /* to restore into the right location */
+ int *vert_indices;
+ int *face_indices;
+ /* multires */
+ /* to verify total number of grids is still the same */
+ int maxgrid;
+ int gridsize;
+ int totgrid;
+ /* to restore into the right location */
+ int *grid_indices;
+
+ /* shape keys */
+ char *shapeName[32]; /* keep size in sync with keyblock dna */
+
+ /* only during push, not stored */
+ short (*no)[3];
+ /* layer brush */
+ float *layer_disp;
+};
+
+static void pbvh_undo_restore_mesh_co(PBVHUndoNode *unode, bContext *C, Scene *scene, Object *ob)
+{
+ SculptSession *ss = ob->paint->sculpt;
+ Mesh *me = ob->data;
+ MVert *mvert;
+ char *shapeName= (char*)unode->shapeName;
+ int *index, i;
+
+ if(ss && ss->kb && strcmp(ss->kb->name, shapeName)) {
+ /* shape key has been changed before calling undo operator */
+
+ Key *key= ob_get_key(ob);
+ KeyBlock *kb= key_get_named_keyblock(key, shapeName);
+
+ if (kb) {
+ ob->shapenr= BLI_findindex(&key->block, kb) + 1;
+ ob->shapeflag|= OB_SHAPE_LOCK;
+
+ sculpt_update_mesh_elements(scene, ob, 0);
+ WM_event_add_notifier(C, NC_OBJECT|ND_DATA, ob);
+ } else {
+ /* key has been removed -- skip this undo node */
+ return;
+ }
+ }
+
+ mvert= me->mvert;
+ index= unode->vert_indices;
+
+ if(ss && ss->kb) {
+ float (*vertCos)[3];
+ vertCos= key_to_vertcos(ob, ss->kb);
+
+ for(i=0; i<unode->totvert; i++)
+ swap_v3_v3(vertCos[index[i]], unode->co[i]);
+
+ /* propagate new coords to keyblock */
+ sculpt_vertcos_to_key(ob, ss->kb, vertCos);
+
+ /* pbvh uses it's own mvert array, so coords should be */
+ /* propagated to pbvh here */
+ BLI_pbvh_apply_vertCos(ob->paint->pbvh, vertCos);
+
+ MEM_freeN(vertCos);
+ }
+ else {
+ for(i=0; i<unode->totvert; i++) {
+ swap_v3_v3(mvert[index[i]].co, unode->co[i]);
+ mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+ }
+
+ }
+}
+
+static void pbvh_undo_restore(bContext *C, ListBase *lb)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ DerivedMesh *dm = mesh_get_derived_final(scene, ob, 0);
+ PBVH *pbvh;
+ PBVHUndoNode *unode;
+ MultiresModifierData *mmd;
+ GridToFace *grid_face_map;
+ int i, j, update_co= 0, update_ptex= 0, update_mask= 0;
+
+ // XXX: sculpt_update_mesh_elements(scene, ob, 0);
+
+ pbvh = dm->getPBVH(ob, dm);
+ grid_face_map = dm->getGridFaceMap(dm);
+
+ for(unode=lb->first; unode; unode=unode->next) {
+ CustomData *vdata, *fdata;
+
+ if(!(strcmp(unode->idname, ob->id.name)==0))
+ continue;
+
+ BLI_pbvh_get_customdata(pbvh, &vdata, &fdata);
+
+ if(unode->maxvert) {
+ /* regular mesh restore */
+ if(dm->getNumVerts(dm) != unode->maxvert)
+ continue;
+
+ update_co |= !!unode->co;
+ update_ptex |= !!unode->mptex_data;
+ update_mask |= !!unode->pmask;
+
+ if(unode->co) {
+ pbvh_undo_restore_mesh_co(unode, C, scene, ob);
+ }
+ if(unode->pmask) {
+ float *pmask;
+
+ pmask = CustomData_get_layer_named(vdata,
+ CD_PAINTMASK,
+ unode->pmask_name);
+
+ for(i=0; i<unode->totvert; i++)
+ SWAP(float, pmask[unode->vert_indices[i]],
+ unode->pmask[i]);
+ }
+ }
+ else if(unode->maxgrid && dm->getGridData) {
+ /* multires restore */
+ DMGridData **grids, *grid;
+ GridKey *gridkey;
+ float (*co)[3] = NULL, *pmask = NULL;
+ int gridsize, active_pmask;
+
+ if(dm->getNumGrids(dm) != unode->maxgrid)
+ continue;
+
+ /* do ptex restore before checking gridsize */
+ for(j=0; j<unode->totgrid; j++) {
+ if(unode->mptex_data) {
+ GridToFace *gtf = &grid_face_map[unode->grid_indices[j]];
+ MPtex *mptex;
+
+ mptex = CustomData_get_layer_named(fdata,
+ CD_MPTEX,
+ unode->mptex_name);
+ SWAP(void*, unode->mptex_data[j],
+ mptex[gtf->face].subfaces[gtf->offset].data);
+ }
+ }
+
+ update_ptex |= !!unode->mptex_data;
+
+ if(dm->getGridSize(dm) != unode->gridsize)
+ continue;
+
+ update_co |= !!unode->co;
+ update_mask |= !!unode->pmask;
+
+ grids= dm->getGridData(dm);
+ gridsize= dm->getGridSize(dm);
+ gridkey= dm->getGridKey(dm);
+
+ if(unode->co) {
+ co = unode->co;
+ }
+ if(unode->pmask) {
+ pmask = unode->pmask;
+ active_pmask = gridelem_active_offset(vdata, gridkey, CD_PAINTMASK);
+ }
+
+ for(j=0; j<unode->totgrid; j++) {
+ grid= grids[unode->grid_indices[j]];
+
+ for(i=0; i<gridsize*gridsize; i++) {
+ DMGridData *elem = GRIDELEM_AT(grid, i, gridkey);
+
+ if(co) {
+ swap_v3_v3(GRIDELEM_CO(elem, gridkey), co[0]);
+ ++co;
+ }
+ if(pmask) {
+ SWAP(float, GRIDELEM_MASK(elem, gridkey)[active_pmask],
+ *pmask);
+ ++pmask;
+ }
+ }
+ }
+ }
+ }
+
+ if(update_co || update_ptex || update_mask) {
+ SculptSession *ss = ob->paint->sculpt;
+ int update_flags = PBVH_UpdateRedraw;
+
+ /* we update all nodes still, should be more clever, but also
+ needs to work correct when exiting/entering sculpt mode and
+ the nodes get recreated, though in that case it could do all */
+ BLI_pbvh_search_callback(ob->paint->pbvh, NULL, NULL, BLI_pbvh_node_set_flags,
+ SET_INT_IN_POINTER(PBVH_UpdateAll));
+
+ if(update_co)
+ update_flags |= PBVH_UpdateBB|PBVH_UpdateOriginalBB;
+ if(update_ptex || update_mask)
+ update_flags |= PBVH_UpdateColorBuffers|PBVH_UpdateRedraw;
+
+ BLI_pbvh_update(ob->paint->pbvh, update_flags, NULL);
+
+ if((mmd=ED_paint_multires_active(scene, ob)))
+ multires_mark_as_modified(ob);
+
+ /* TODO: should work with other paint modes too */
+ if(ss && (ss->modifiers_active || ((Mesh*)ob->data)->id.us > 1))
+ DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
+ }
+}
+
+static void pbvh_undo_free(ListBase *lb)
+{
+ PBVHUndoNode *unode;
+
+ for(unode=lb->first; unode; unode=unode->next) {
+ if(unode->co)
+ MEM_freeN(unode->co);
+ if(unode->no)
+ MEM_freeN(unode->no);
+ if(unode->pmask)
+ MEM_freeN(unode->pmask);
+ if(unode->mptex_data) {
+ int i;
+ for(i = 0; i < unode->totgrid; ++i)
+ MEM_freeN(unode->mptex_data[i]);
+ MEM_freeN(unode->mptex_data);
+ }
+ if(unode->vert_indices)
+ MEM_freeN(unode->vert_indices);
+ if(unode->face_indices)
+ MEM_freeN(unode->face_indices);
+ if(unode->grid_indices)
+ MEM_freeN(unode->grid_indices);
+ if(unode->layer_disp)
+ MEM_freeN(unode->layer_disp);
+ }
+}
+
+PBVHUndoNode *pbvh_undo_get_node(PBVHNode *node)
+{
+ ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH);
+ PBVHUndoNode *unode;
+
+ if(!lb)
+ return NULL;
+
+ for(unode=lb->first; unode; unode=unode->next)
+ if(unode->node == node)
+ return unode;
+
+ return NULL;
+}
+
+PBVHUndoNode *pbvh_undo_push_node(PBVHNode *node, PBVHUndoFlag flag,
+ Object *ob, Scene *scene)
+{
+ ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH);
+ PBVHUndoNode *unode;
+ PBVH *pbvh = ob->paint->pbvh;
+ PBVHVertexIter vd;
+ int totbytes= 0;
+
+ CustomData *vdata, *fdata;
+ SculptSession *ss = NULL; /* for shapekey */
+
+ GridKey *gridkey;
+ int uses_grids, totgrid, *grid_indices;
+ GridToFace *grid_face_map;
+
+ /* list is manipulated by multiple threads, so we lock */
+ BLI_lock_thread(LOCK_CUSTOM1);
+
+ if((unode= pbvh_undo_get_node(node))) {
+ BLI_unlock_thread(LOCK_CUSTOM1);
+ return unode;
+ }
+
+ unode= MEM_callocN(sizeof(PBVHUndoNode), "PBVHUndoNode");
+ strcpy(unode->idname, ob->id.name);
+ unode->node= node;
+
+ /* XXX: changed this to just use unique verts rather than all,
+ seems like only unique are restored from anyway? -nicholas */
+ BLI_pbvh_node_num_verts(pbvh, node, &unode->totvert, NULL);
+
+ BLI_pbvh_get_customdata(pbvh, &vdata, &fdata);
+ uses_grids = BLI_pbvh_uses_grids(pbvh);
+
+ if(uses_grids) {
+ /* multires */
+ BLI_pbvh_node_get_grids(pbvh, node, &grid_indices,
+ &totgrid, &unode->maxgrid, &unode->gridsize,
+ NULL, NULL, &gridkey);
+
+ unode->totgrid= totgrid;
+ unode->grid_indices= MEM_mapallocN(sizeof(int)*totgrid, "PBVHUndoNode.grid_indices");
+ totbytes += sizeof(int) * totgrid;
+ }
+ else {
+ /* regular mesh */
+
+ if(scene) {
+ DerivedMesh *dm = mesh_get_derived_final(scene, ob, 0);
+ unode->maxvert= dm->getNumVerts(dm);
+ }
+ else
+ unode->maxvert= get_mesh(ob)->totvert;
+
+ if(flag & (PBVH_UNDO_CO_NO|PBVH_UNDO_PMASK)) {
+ unode->vert_indices= MEM_mapallocN(sizeof(int)*unode->totvert,
+ "PBVHUndoNode.vert_indices");
+ totbytes += sizeof(int) * unode->totvert;
+ }
+ }
+
+ /* allocate only the necessary undo data
+ XXX: we will use this while sculpting, is mapalloc slow to access then? */
+
+ if(flag & PBVH_UNDO_CO_NO) {
+ unode->co= MEM_mapallocN(sizeof(float)*3*unode->totvert, "PBVHUndoNode.co");
+ unode->no= MEM_mapallocN(sizeof(short)*3*unode->totvert, "PBVHUndoNode.no");
+ totbytes += (sizeof(float)*3 + sizeof(short)*3) * unode->totvert;
+ }
+ if(flag & PBVH_UNDO_PMASK) {
+ int active;
+ active= CustomData_get_active_layer_index(vdata, CD_PAINTMASK);
+
+ if(active == -1)
+ flag &= ~PBVH_UNDO_PMASK;
+ else {
+ BLI_strncpy(unode->pmask_name,
+ vdata->layers[active].name,
+ sizeof(unode->pmask_name));
+ unode->pmask= MEM_mapallocN(sizeof(float)*unode->totvert, "PBVHUndoNode.pmask");
+ totbytes += sizeof(float) * unode->totvert;
+ }
+ }
+ if(flag & PBVH_UNDO_PTEX) {
+ int i, active;
+
+ active= CustomData_get_active_layer_index(fdata, CD_MPTEX);
+
+ if(active == -1)
+ flag &= ~PBVH_UNDO_PTEX;
+
+ assert(uses_grids);
+
+ BLI_strncpy(unode->mptex_name,
+ fdata->layers[active].name,
+ sizeof(unode->mptex_name));
+
+ grid_face_map= BLI_pbvh_get_grid_face_map(pbvh);
+
+ for(i = 0; i < unode->totgrid; ++i) {
+ GridToFace *gtf= &grid_face_map[grid_indices[i]];
+ MPtex *pt= ((MPtex*)fdata->layers[active].data) + gtf->face;
+ MPtexSubface *subface= &pt->subfaces[gtf->offset];
+
+ totbytes+= (pt->channels * ptex_data_size(pt->type) *
+ subface->res[0] * subface->res[1]);
+ }
+
+ unode->mptex_data= MEM_mapallocN(sizeof(void*)*unode->totgrid, "PBVHUndoNode.mptex_data");
+ totbytes+= sizeof(void*)*unode->totgrid;
+ }
+
+ /* push undo node onto paint undo list */
+ undo_paint_push_count_alloc(UNDO_PAINT_MESH, totbytes);
+ BLI_addtail(lb, unode);
+
+ BLI_unlock_thread(LOCK_CUSTOM1);
+
+ /* the rest is threaded, hopefully this is the performance critical part */
+
+ if(uses_grids || (flag & (PBVH_UNDO_CO_NO|PBVH_UNDO_PMASK))) {
+ BLI_pbvh_vertex_iter_begin(pbvh, node, vd, PBVH_ITER_UNIQUE) {
+ if(flag & PBVH_UNDO_CO_NO) {
+ copy_v3_v3(unode->co[vd.i], vd.co);
+ if(vd.no) VECCOPY(unode->no[vd.i], vd.no)
+ else normal_float_to_short_v3(unode->no[vd.i], vd.fno);
+ }
+ if(flag & PBVH_UNDO_PMASK) {
+ if(vd.mask_active)
+ unode->pmask[vd.i]= *vd.mask_active;
+ }
+
+ if(vd.vert_indices)
+ unode->vert_indices[vd.i]= vd.vert_indices[vd.i];
+ }
+ BLI_pbvh_vertex_iter_end;
+ }
+
+ if(unode->grid_indices)
+ memcpy(unode->grid_indices, grid_indices, sizeof(int)*totgrid);
+
+ /* copy ptex data (doesn't handle res changes yet) */
+ if(flag & PBVH_UNDO_PTEX) {
+ MPtex *mptex;
+ int i;
+
+ mptex= CustomData_get_layer(fdata, CD_MPTEX);
+
+ for(i = 0; i < unode->totgrid; ++i) {
+ GridToFace *gtf= &grid_face_map[grid_indices[i]];
+ MPtex *pt= &mptex[gtf->face];
+ MPtexSubface *subface= &pt->subfaces[gtf->offset];
+
+ unode->mptex_data[i]= MEM_dupallocN(subface->data);
+ }
+ }
+
+ /* store active shape key */
+ ss= ob->paint->sculpt;
+ if(ss && ss->kb)
+ BLI_strncpy((char*)unode->shapeName, ss->kb->name, sizeof(ss->kb->name));
+ else
+ unode->shapeName[0]= '\0';
+
+ return unode;
+}
+
+void pbvh_undo_push_begin(char *name)
+{
+ undo_paint_push_begin(UNDO_PAINT_MESH, name,
+ pbvh_undo_restore, pbvh_undo_free);
+}
+
+void pbvh_undo_push_end(void)
+{
+ ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH);
+ PBVHUndoNode *unode;
+
+ if(!lb || !lb->first)
+ return;
+
+ /* remove data that's only used during stroke */
+ for(unode=lb->first; unode; unode=unode->next) {
+ if(unode->no) {
+ MEM_freeN(unode->no);
+ unode->no= NULL;
+ }
+
+ if(unode->layer_disp) {
+ MEM_freeN(unode->layer_disp);
+ unode->layer_disp= NULL;
+ }
+ }
+
+ undo_paint_push_end(UNDO_PAINT_MESH);
+}
+
+int pbvh_undo_node_totvert(PBVHUndoNode *unode)
+{
+ return unode->totvert;
+}
+
+pbvh_undo_f3 pbvh_undo_node_co(PBVHUndoNode *unode)
+{
+ return unode->co;
+}
+
+pbvh_undo_s3 pbvh_undo_node_no(PBVHUndoNode *unode)
+{
+ return unode->no;
+}
+
+float *pbvh_undo_node_layer_disp(PBVHUndoNode *unode)
+{
+ return unode->layer_disp;
+}
+
+void pbvh_undo_node_set_layer_disp(PBVHUndoNode *unode, float *layer_disp)
+{
+ unode->layer_disp = layer_disp;
+}
+
+const char *pbvh_undo_node_mptex_name(PBVHUndoNode *unode)
+{
+ return unode->mptex_name;
+}
+
+void *pbvh_undo_node_mptex_data(PBVHUndoNode *unode, int ndx)
+{
+ return unode->mptex_data[ndx];
+}
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 2e192a08ca5..ed2251cea38 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -50,6 +50,9 @@
#include "BKE_cdderivedmesh.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
+#include "BKE_dmgrid.h"
+#include "BKE_global.h"
+#include "BKE_image.h"
#include "BKE_key.h"
#include "BKE_library.h"
#include "BKE_mesh.h"
@@ -64,6 +67,7 @@
#include "WM_api.h"
#include "WM_types.h"
#include "ED_screen.h"
+#include "ED_sculpt.h"
#include "ED_view3d.h"
#include "paint_intern.h"
#include "sculpt_intern.h"
@@ -71,10 +75,6 @@
#include "RNA_access.h"
#include "RNA_define.h"
-
-#include "RE_render_ext.h"
-#include "RE_shader_ext.h"
-
#include "GPU_buffers.h"
#include <math.h>
@@ -89,49 +89,13 @@
*
*/
-void ED_sculpt_force_update(bContext *C)
-{
- Object *ob= CTX_data_active_object(C);
-
- if(ob && (ob->mode & OB_MODE_SCULPT))
- multires_force_update(ob);
-}
-
-/* Sculpt mode handles multires differently from regular meshes, but only if
- it's the last modifier on the stack and it is not on the first level */
-struct MultiresModifierData *sculpt_multires_active(Scene *scene, Object *ob)
-{
- Mesh *me= (Mesh*)ob->data;
- ModifierData *md, *nmd;
-
- if(!CustomData_get_layer(&me->fdata, CD_MDISPS)) {
- /* multires can't work without displacement layer */
- return NULL;
- }
-
- for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) {
- if(md->type == eModifierType_Multires) {
- MultiresModifierData *mmd= (MultiresModifierData*)md;
-
- /* Check if any of the modifiers after multires are active
- * if not it can use the multires struct */
- for(nmd= md->next; nmd; nmd= nmd->next)
- if(modifier_isEnabled(scene, nmd, eModifierMode_Realtime))
- break;
-
- if(!nmd && mmd->sculptlvl > 0)
- return mmd;
- }
- }
-
- return NULL;
-}
+static int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float out[3], float mouse[2]);
/* Checks whether full update mode (slower) needs to be used to work with modifiers */
int sculpt_modifiers_active(Scene *scene, Object *ob)
{
ModifierData *md;
- MultiresModifierData *mmd= sculpt_multires_active(scene, ob);
+ MultiresModifierData *mmd= ED_paint_multires_active(scene, ob);
/* check if there are any modifiers after what we are sculpting,
for a multires modifier with a deform modifier in front, we
@@ -142,11 +106,22 @@ int sculpt_modifiers_active(Scene *scene, Object *ob)
else
md= modifiers_getVirtualModifierList(ob);
- /* exception for shape keys because we can edit those */
for(; md; md= md->next) {
- if(modifier_isEnabled(scene, md, eModifierMode_Realtime))
- if(md->type != eModifierType_ShapeKey)
+ if(modifier_isEnabled(scene, md, eModifierMode_Realtime)) {
+
+ /* exception for shape keys because we can edit those */
+ if(md->type == eModifierType_ShapeKey)
+ continue;
+
+ /*exception for multires on level zero, it's
+ not caught by the earlier multires check */
+ else if(md->type == eModifierType_Multires &&
+ ((MultiresModifierData*)md)->sculptlvl == 0)
+ continue;
+
+ else
return 1;
+ }
}
return 0;
@@ -169,44 +144,26 @@ typedef enum StrokeFlags {
*/
typedef struct StrokeCache {
/* Invariants */
- float initial_radius;
float scale[3];
int flag;
float clip_tolerance[3];
- float initial_mouse[2];
/* Variants */
- float radius;
- float radius_squared;
- //float traced_location[3];
- float true_location[3];
- float location[3];
-
float pen_flip;
float invert;
- float pressure;
- float mouse[2];
float bstrength;
- float tex_mouse[2];
/* The rest is temporary storage that isn't saved as a property */
- int first_time; /* Beginning of stroke may do some things special */
-
- bglMats *mats;
-
/* Clean this up! */
+ PaintStroke *stroke;
ViewContext *vc;
Brush *brush;
float (*face_norms)[3]; /* Copy of the mesh faces' normals */
- float special_rotation; /* Texture rotation (radians) for anchored and rake modes */
- int pixel_radius, previous_pixel_radius;
float grab_delta[3], grab_delta_symmetry[3];
float old_grab_location[3], orig_grab_location[3];
- int symmetry; /* Symmetry index between 0 and 7 bit combo 0 is Brush only;
- 1 is X mirror; 2 is Y mirror; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
int mirror_symmetry_pass; /* the symmetry pass we are currently on between 0 and 7*/
float true_view_normal[3];
float view_normal[3];
@@ -214,8 +171,6 @@ typedef struct StrokeCache {
float last_center[3];
int radial_symmetry_pass;
float symm_rot_mat[4][4];
- float symm_rot_mat_inv[4][4];
- float last_rake[2]; /* Last location of updating rake rotation */
int original;
float vertex_rotation;
@@ -226,177 +181,6 @@ typedef struct StrokeCache {
float plane_trim_squared;
} StrokeCache;
-/* ===== OPENGL =====
- *
- * Simple functions to get data from the GL
- */
-
-/* Convert a point in model coordinates to 2D screen coordinates. */
-static void projectf(bglMats *mats, const float v[3], float p[2])
-{
- double ux, uy, uz;
-
- gluProject(v[0],v[1],v[2], mats->modelview, mats->projection,
- (GLint *)mats->viewport, &ux, &uy, &uz);
- p[0]= ux;
- p[1]= uy;
-}
-
-/*XXX: static void project(bglMats *mats, const float v[3], short p[2])
-{
- float f[2];
- projectf(mats, v, f);
-
- p[0]= f[0];
- p[1]= f[1];
-}
-*/
-
-/*** BVH Tree ***/
-
-/* Get a screen-space rectangle of the modified area */
-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];
- int i, j, k;
-
- view3d_get_object_project_mat(rv3d, ob, pmat);
-
- if(!pbvh)
- return 0;
-
- BLI_pbvh_redraw_BB(pbvh, bb_min, bb_max);
-
- rect->xmin = rect->ymin = INT_MAX;
- rect->xmax = rect->ymax = INT_MIN;
-
- if(bb_min[0] > bb_max[0] || bb_min[1] > bb_max[1] || bb_min[2] > bb_max[2])
- return 0;
-
- for(i = 0; i < 2; ++i) {
- for(j = 0; j < 2; ++j) {
- for(k = 0; k < 2; ++k) {
- float vec[3], proj[2];
- vec[0] = i ? bb_min[0] : bb_max[0];
- vec[1] = j ? bb_min[1] : bb_max[1];
- vec[2] = k ? bb_min[2] : bb_max[2];
- view3d_project_float(ar, vec, proj, pmat);
- rect->xmin = MIN2(rect->xmin, proj[0]);
- rect->xmax = MAX2(rect->xmax, proj[0]);
- rect->ymin = MIN2(rect->ymin, proj[1]);
- rect->ymax = MAX2(rect->ymax, proj[1]);
- }
- }
- }
-
- return rect->xmin < rect->xmax && rect->ymin < rect->ymax;
-}
-
-void sculpt_get_redraw_planes(float planes[4][4], ARegion *ar,
- RegionView3D *rv3d, Object *ob)
-{
- PBVH *pbvh= ob->sculpt->pbvh;
- BoundBox bb;
- bglMats mats;
- rcti rect;
-
- memset(&bb, 0, sizeof(BoundBox));
-
- view3d_get_transformation(ar, rv3d, ob, &mats);
- sculpt_get_redraw_rect(ar, rv3d,ob, &rect);
-
-#if 1
- /* use some extra space just in case */
- rect.xmin -= 2;
- rect.xmax += 2;
- rect.ymin -= 2;
- rect.ymax += 2;
-#else
- /* it was doing this before, allows to redraw a smaller
- part of the screen but also gives artifaces .. */
- rect.xmin += 2;
- rect.xmax -= 2;
- rect.ymin += 2;
- rect.ymax -= 2;
-#endif
-
- view3d_calculate_clipping(&bb, planes, &mats, &rect);
- mul_m4_fl(planes, -1.0f);
-
- /* clear redraw flag from nodes */
- if(pbvh)
- BLI_pbvh_update(pbvh, PBVH_UpdateRedraw, NULL);
-}
-
-/************************ Brush Testing *******************/
-
-typedef struct SculptBrushTest {
- float radius_squared;
- float location[3];
- float dist;
-} SculptBrushTest;
-
-static void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test)
-{
- test->radius_squared= ss->cache->radius_squared;
- copy_v3_v3(test->location, ss->cache->location);
-}
-
-static int sculpt_brush_test(SculptBrushTest *test, float co[3])
-{
- float distsq = len_squared_v3v3(co, test->location);
-
- if(distsq <= test->radius_squared) {
- test->dist = sqrt(distsq);
- return 1;
- }
- else {
- return 0;
- }
-}
-
-static int sculpt_brush_test_sq(SculptBrushTest *test, float co[3])
-{
- float distsq = len_squared_v3v3(co, test->location);
-
- if(distsq <= test->radius_squared) {
- test->dist = distsq;
- return 1;
- }
- else {
- return 0;
- }
-}
-
-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 const float side = 0.70710678118654752440084436210485; // sqrt(.5);
-
- float local_co[3];
-
- mul_v3_m4v3(local_co, local, co);
-
- local_co[0] = fabs(local_co[0]);
- local_co[1] = fabs(local_co[1]);
- 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;
-
- return 1;
- }
- else {
- return 0;
- }
-}
-
static float frontface(Brush *brush, float sculpt_normal[3], short no[3], float fno[3])
{
if (brush->flag & BRUSH_FRONTFACE) {
@@ -419,30 +203,6 @@ static float frontface(Brush *brush, float sculpt_normal[3], short no[3], float
}
}
-#if 0
-
-static int sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float location[3], float an[3])
-{
- if (sculpt_brush_test_fast(test, co)) {
- float t1[3], t2[3], t3[3], dist;
-
- sub_v3_v3v3(t1, location, co);
- sub_v3_v3v3(t2, x2, location);
-
- cross_v3_v3v3(t3, an, t1);
-
- dist = len_v3(t3)/len_v3(t2);
-
- test->dist = dist;
-
- return 1;
- }
-
- return 0;
-}
-
-#endif
-
/* ===== Sculpting =====
*
*/
@@ -490,86 +250,6 @@ static float integrate_overlap(Brush* br)
return max;
}
-/* Uses symm to selectively flip any axis of a coordinate. */
-static void flip_coord(float out[3], float in[3], const char symm)
-{
- if(symm & SCULPT_SYMM_X)
- out[0]= -in[0];
- else
- out[0]= in[0];
- if(symm & SCULPT_SYMM_Y)
- out[1]= -in[1];
- else
- out[1]= in[1];
- if(symm & SCULPT_SYMM_Z)
- out[2]= -in[2];
- else
- out[2]= in[2];
-}
-
-float calc_overlap(StrokeCache *cache, const char symm, const char axis, const float angle)
-{
- float mirror[3];
- float distsq;
- float mat[4][4];
-
- //flip_coord(mirror, cache->traced_location, symm);
- flip_coord(mirror, cache->true_location, symm);
-
- unit_m4(mat);
- rotate_m4(mat, axis, angle);
-
- mul_m4_v3(mat, mirror);
-
- //distsq = len_squared_v3v3(mirror, cache->traced_location);
- distsq = len_squared_v3v3(mirror, cache->true_location);
-
- if (distsq <= 4*(cache->radius_squared))
- return (2*(cache->radius) - sqrt(distsq)) / (2*(cache->radius));
- else
- return 0;
-}
-
-static float calc_radial_symmetry_feather(Sculpt *sd, StrokeCache *cache, const char symm, const char axis)
-{
- int i;
- float overlap;
-
- overlap = 0;
- for(i = 1; i < sd->radial_symm[axis-'X']; ++i) {
- const float angle = 2*M_PI*i/sd->radial_symm[axis-'X'];
- overlap += calc_overlap(cache, symm, axis, angle);
- }
-
- return overlap;
-}
-
-static float calc_symmetry_feather(Sculpt *sd, StrokeCache* cache)
-{
- if (sd->flags & SCULPT_SYMMETRY_FEATHER) {
- float overlap;
- int symm = cache->symmetry;
- int i;
-
- overlap = 0;
- for (i = 0; i <= symm; i++) {
- if(i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) {
-
- overlap += calc_overlap(cache, i, 0, 0);
-
- overlap += calc_radial_symmetry_feather(sd, cache, i, 'X');
- overlap += calc_radial_symmetry_feather(sd, cache, i, 'Y');
- overlap += calc_radial_symmetry_feather(sd, cache, i, 'Z');
- }
- }
-
- return 1/overlap;
- }
- else {
- return 1;
- }
-}
-
/* Return modified brush strength. Includes the direction of the brush, positive
values pull vertices, negative values push. Uses tablet pressure and a
special multiplier found experimentally to scale the strength factor. */
@@ -581,7 +261,7 @@ static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather)
const float root_alpha = brush_alpha(brush);
float alpha = root_alpha*root_alpha;
float dir = brush->flag & BRUSH_DIR_IN ? -1 : 1;
- float pressure = brush_use_alpha_pressure(brush) ? cache->pressure : 1;
+ float pressure = brush_use_alpha_pressure(brush) ? paint_stroke_pressure(cache->stroke) : 1;
float pen_flip = cache->pen_flip ? -1 : 1;
float invert = cache->invert ? -1 : 1;
float accum = integrate_overlap(brush);
@@ -649,25 +329,6 @@ static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather)
}
}
-float get_tex_pixel(Brush* br, float u, float v)
-{
- TexResult texres;
- float co[3];
- int hasrgb;
-
- co[0] = u;
- co[1] = v;
- co[2] = 0;
-
- memset(&texres, 0, sizeof(TexResult));
- hasrgb = multitex_ext(br->mtex.tex, co, NULL, NULL, 1, &texres);
-
- if (hasrgb & TEX_RGB)
- texres.tin = (0.35*texres.tr + 0.45*texres.tg + 0.2*texres.tb)*texres.ta;
-
- return texres.tin;
-}
-
#if 0
/* Get a pixel from the texcache at (px, py) */
@@ -709,131 +370,10 @@ static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float
#endif
-/* Return a multiplier for brush strength on a particular vertex. */
-static float tex_strength(SculptSession *ss, Brush *br, float *point, const float len)
+static float tex_strength(SculptSession *ss, Brush *br, float *co,
+ float mask, float dist)
{
- MTex *mtex = &br->mtex;
- float avg= 1;
-
- if(!mtex->tex) {
- avg= 1;
- }
- else if(mtex->brush_map_mode == MTEX_MAP_MODE_3D) {
- float jnk;
-
- /* Get strength by feeding the vertex
- location directly into a texture */
- externtex(mtex, point, &avg,
- &jnk, &jnk, &jnk, &jnk, 0);
- }
- else if(ss->texcache) {
- float rotation = -mtex->rot;
- float x, y, point_2d[3];
- float radius;
-
- /* 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);
-
- projectf(ss->cache->mats, point_2d, point_2d);
-
- /* if fixed mode, keep coordinates relative to mouse */
- if(mtex->brush_map_mode == MTEX_MAP_MODE_FIXED) {
- rotation += ss->cache->special_rotation;
-
- point_2d[0] -= ss->cache->tex_mouse[0];
- point_2d[1] -= ss->cache->tex_mouse[1];
-
- radius = ss->cache->pixel_radius; // use pressure adjusted size for fixed mode
-
- x = point_2d[0];
- y = point_2d[1];
- }
- else /* else (mtex->brush_map_mode == MTEX_MAP_MODE_TILED),
- 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;
- }
-
- x /= ss->cache->vc->ar->winx;
- y /= ss->cache->vc->ar->winy;
-
- if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) {
- x -= 0.5f;
- y -= 0.5f;
- }
-
- x *= ss->cache->vc->ar->winx / radius;
- y *= ss->cache->vc->ar->winy / radius;
-
- /* 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;
- 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 = get_tex_pixel(br, x, y);
- }
-
- avg += br->texture_sample_bias;
-
- avg *= brush_curve_strength(br, len, ss->cache->radius); /* Falloff curve */
-
- return avg;
-}
-
-typedef struct {
- Sculpt *sd;
- SculptSession *ss;
- float radius_squared;
- int original;
-} SculptSearchSphereData;
-
-/* Test AABB against sphere */
-static int sculpt_search_sphere_cb(PBVHNode *node, void *data_v)
-{
- SculptSearchSphereData *data = data_v;
- float *center = data->ss->cache->location, nearest[3];
- float t[3], bb_min[3], bb_max[3];
- int i;
-
- if(data->original)
- BLI_pbvh_node_get_original_BB(node, bb_min, bb_max);
- else
- 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;
+ return paint_stroke_combined_strength(ss->cache->stroke, dist, co, mask);
}
/* Handles clipping against a mirror modifier and SCULPT_LOCK axis flags */
@@ -842,7 +382,7 @@ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float *co, const float va
int i;
for(i=0; i<3; ++i) {
- if(sd->flags & (SCULPT_LOCK_X << i))
+ if(sd->paint.flags & (PAINT_LOCK_X << i))
continue;
if((ss->cache->flag & (CLIP_X << i)) && (fabs(co[i]) <= ss->cache->clip_tolerance[i]))
@@ -861,39 +401,40 @@ static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], floa
}
}
-static void calc_area_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHNode **nodes, int totnode)
+static void calc_area_normal(Sculpt *sd, Object *ob, float an[3], PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
int n;
float out_flip[3] = {0.0f, 0.0f, 0.0f};
zero_v3(an);
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for(n=0; n<totnode; n++) {
PBVHVertexIter vd;
- SculptBrushTest test;
- SculptUndoNode *unode;
+ PaintStrokeTest test;
+ PBVHUndoNode *unode;
float private_an[3] = {0.0f, 0.0f, 0.0f};
float private_out_flip[3] = {0.0f, 0.0f, 0.0f};
- unode = sculpt_undo_push_node(ss, nodes[n]);
- sculpt_brush_test_init(ss, &test);
+ unode = pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL);
+ paint_stroke_test_init(&test, ss->cache->stroke);
if(ss->cache->original) {
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if(sculpt_brush_test_fast(&test, unode->co[vd.i])) {
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(paint_stroke_test_fast(&test, pbvh_undo_node_co(unode)[vd.i])) {
float fno[3];
- normal_short_to_float_v3(fno, unode->no[vd.i]);
+ normal_short_to_float_v3(fno, pbvh_undo_node_no(unode)[vd.i]);
add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno);
}
}
BLI_pbvh_vertex_iter_end;
}
else {
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if(sculpt_brush_test_fast(&test, vd.co)) {
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(paint_stroke_test_fast(&test, vd.co)) {
if(vd.no) {
float fno[3];
@@ -923,13 +464,14 @@ static void calc_area_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHNod
/* 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, SculptSession *ss, float an[3], PBVHNode **nodes, int totnode)
+static void calc_sculpt_normal(Sculpt *sd, Object *ob, float an[3], PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
if (ss->cache->mirror_symmetry_pass == 0 &&
ss->cache->radial_symmetry_pass == 0 &&
- (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL)))
+ (paint_stroke_first_dab(ss->cache->stroke) || !(brush->flag & BRUSH_ORIGINAL_NORMAL)))
{
switch (brush->sculpt_plane) {
case SCULPT_DISP_DIR_VIEW:
@@ -955,7 +497,7 @@ static void calc_sculpt_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHN
break;
case SCULPT_DISP_DIR_AREA:
- calc_area_normal(sd, ss, an, nodes, totnode);
+ calc_area_normal(sd, ob, an, nodes, totnode);
default:
break;
@@ -965,7 +507,7 @@ static void calc_sculpt_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHN
}
else {
copy_v3_v3(an, ss->cache->last_area_normal);
- flip_coord(an, an, ss->cache->mirror_symmetry_pass);
+ paint_flip_coord(an, an, ss->cache->mirror_symmetry_pass);
mul_m4_v3(ss->cache->symm_rot_mat, an);
}
}
@@ -973,8 +515,9 @@ static void calc_sculpt_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHN
/* For the smooth brush, uses the neighboring vertices around vert to calculate
a smoothed location for vert. Skips corner vertices (used by only one
polygon.) */
-static void neighbor_average(SculptSession *ss, float avg[3], const unsigned vert)
+static void neighbor_average(Object *ob, float avg[3], const unsigned vert)
{
+ SculptSession *ss = ob->paint->sculpt;
int i, skip= -1, total=0;
IndexNode *node= ss->fmap[vert].first;
char ncount= BLI_countlist(&ss->fmap[vert]);
@@ -1014,22 +557,23 @@ static void neighbor_average(SculptSession *ss, float avg[3], const unsigned ver
copy_v3_v3(avg, ss->mvert[vert].co);
}
-static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, float bstrength)
+static void do_mesh_smooth_brush(Sculpt *sd, Object *ob, PBVHNode *node, float bstrength)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
PBVHVertexIter vd;
- SculptBrushTest test;
+ PaintStrokeTest test;
CLAMP(bstrength, 0.0f, 1.0f);
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
- BLI_pbvh_vertex_iter_begin(ss->pbvh, node, 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);
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, node, vd, PBVH_ITER_UNIQUE) {
+ if(paint_stroke_test(&test, vd.co)) {
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno);
float avg[3], val[3];
- neighbor_average(ss, avg, vd.vert_indices[vd.i]);
+ neighbor_average(ob, avg, vd.vert_indices[vd.i]);
sub_v3_v3v3(val, avg, vd.co);
mul_v3_fl(val, fade);
@@ -1044,22 +588,24 @@ static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node,
BLI_pbvh_vertex_iter_end;
}
-static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, float bstrength)
+static void do_multires_smooth_brush(Sculpt *sd, Object *ob, PBVHNode *node, float bstrength)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
- SculptBrushTest test;
+ PaintStrokeTest test;
DMGridData **griddata, *data;
DMGridAdjacency *gridadj, *adj;
+ GridKey *gridkey;
float (*tmpgrid)[3], (*tmprow)[3];
int v1, v2, v3, v4;
int *grid_indices, totgrid, gridsize, i, x, y;
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
CLAMP(bstrength, 0.0f, 1.0f);
- BLI_pbvh_node_get_grids(ss->pbvh, node, &grid_indices, &totgrid,
- NULL, &gridsize, &griddata, &gridadj);
+ BLI_pbvh_node_get_grids(ob->paint->pbvh, node, &grid_indices, &totgrid,
+ NULL, &gridsize, &griddata, &gridadj, &gridkey);
#pragma omp critical
{
@@ -1077,7 +623,8 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no
float tmp[3];
v1 = y*gridsize;
- add_v3_v3v3(tmprow[0], data[v1].co, data[v1+gridsize].co);
+ add_v3_v3v3(tmprow[0], GRIDELEM_CO_AT(data, v1, gridkey),
+ GRIDELEM_CO_AT(data, v1+gridsize, gridkey));
for (x= 0; x < gridsize-1; x++) {
v1 = x + y*gridsize;
@@ -1085,7 +632,7 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no
v3 = v1 + gridsize;
v4 = v3 + 1;
- add_v3_v3v3(tmprow[x+1], data[v2].co, data[v4].co);
+ add_v3_v3v3(tmprow[x+1], GRIDELEM_CO_AT(data, v2, gridkey), GRIDELEM_CO_AT(data, v4, gridkey));
add_v3_v3v3(tmp, tmprow[x+1], tmprow[x]);
add_v3_v3(tmpgrid[v1], tmp);
@@ -1115,11 +662,13 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no
continue;
index = x + y*gridsize;
- co= data[index].co;
- fno= data[index].no;
+ co= GRIDELEM_CO_AT(data, index, gridkey);
+ fno= GRIDELEM_NO_AT(data, index, gridkey);
- 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);
+ if(paint_stroke_test(&test, co)) {
+ float mask = (gridkey->mask ?
+ *GRIDELEM_MASK_AT(data, x + y*gridsize, gridkey) : 0);
+ const float fade = bstrength*tex_strength(ss, brush, co, mask, test.dist)*frontface(brush, ss->cache->view_normal, NULL, fno);
float *avg, val[3];
float n;
@@ -1153,8 +702,9 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no
}
}
-static void smooth(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float bstrength)
+static void smooth(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float bstrength)
{
+ SculptSession *ss = ob->paint->sculpt;
const int max_iterations = 4;
const float fract = 1.0f/max_iterations;
int iteration, n, count;
@@ -1166,55 +716,57 @@ static void smooth(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode,
last = max_iterations*(bstrength - count*fract);
for(iteration = 0; iteration <= count; ++iteration) {
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for(n=0; n<totnode; n++) {
if(ss->multires) {
- do_multires_smooth_brush(sd, ss, nodes[n], iteration != count ? 1.0f : last);
+ do_multires_smooth_brush(sd, ob, nodes[n], iteration != count ? 1.0f : last);
}
else if(ss->fmap)
- do_mesh_smooth_brush(sd, ss, nodes[n], iteration != count ? 1.0f : last);
+ do_mesh_smooth_brush(sd, ob, nodes[n], iteration != count ? 1.0f : last);
}
if(ss->multires)
- multires_stitch_grids(ss->ob);
+ multires_stitch_grids(ob);
}
}
-static void do_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
- smooth(sd, ss, nodes, totnode, ss->cache->bstrength);
+ SculptSession *ss = ob->paint->sculpt;
+ smooth(sd, ob, nodes, totnode, ss->cache->bstrength);
}
-static void do_draw_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
float offset[3], area_normal[3];
float bstrength= ss->cache->bstrength;
int n;
- calc_sculpt_normal(sd, ss, area_normal, nodes, totnode);
+ calc_sculpt_normal(sd, ob, area_normal, nodes, totnode);
/* offset with as much as possible factored in already */
- mul_v3_v3fl(offset, area_normal, ss->cache->radius);
+ mul_v3_v3fl(offset, area_normal, paint_stroke_radius(ss->cache->stroke));
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)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for(n=0; n<totnode; n++) {
PBVHVertexIter vd;
- SculptBrushTest test;
+ PaintStrokeTest test;
float (*proxy)[3];
- proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+ proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co;
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
- 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)) {
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (paint_stroke_test(&test, vd.co)) {
+ //if(paint_stroke_test_cyl(&test, vd.co, ss->cache->location, area_normal)) {
/* 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, vd.mask_combined, test.dist)*frontface(brush, area_normal, vd.no, vd.fno);
mul_v3_v3fl(proxy[vd.i], offset, fade);
@@ -1226,18 +778,19 @@ static void do_draw_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t
}
}
-static void do_crease_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
float offset[3], area_normal[3];
float bstrength= ss->cache->bstrength;
float flippedbstrength, crease_correction;
int n;
- calc_sculpt_normal(sd, ss, area_normal, nodes, totnode);
+ calc_sculpt_normal(sd, ob, area_normal, nodes, totnode);
/* offset with as much as possible factored in already */
- mul_v3_v3fl(offset, area_normal, ss->cache->radius);
+ mul_v3_v3fl(offset, area_normal, paint_stroke_radius(ss->cache->stroke));
mul_v3_v3(offset, ss->cache->scale);
mul_v3_fl(offset, bstrength);
@@ -1254,20 +807,20 @@ static void do_crease_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
if(brush->sculpt_tool == SCULPT_TOOL_BLOB) flippedbstrength *= -1.0f;
/* threaded loop over nodes */
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for(n=0; n<totnode; n++) {
PBVHVertexIter vd;
- SculptBrushTest test;
+ PaintStrokeTest test;
float (*proxy)[3];
- proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+ proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co;
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if(sculpt_brush_test(&test, vd.co)) {
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(paint_stroke_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, vd.mask_combined, test.dist)*frontface(brush, area_normal, vd.no, vd.fno);
float val1[3];
float val2[3];
@@ -1289,25 +842,26 @@ static void do_crease_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
}
}
-static void do_pinch_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength= ss->cache->bstrength;
int n;
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for(n=0; n<totnode; n++) {
PBVHVertexIter vd;
- SculptBrushTest test;
+ PaintStrokeTest test;
float (*proxy)[3];
- proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+ proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co;
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
- 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);
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(paint_stroke_test(&test, vd.co)) {
+ float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno);
float val[3];
sub_v3_v3v3(val, test.location, vd.co);
@@ -1321,8 +875,9 @@ static void do_pinch_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
}
}
-static void do_grab_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush= paint_brush(&sd->paint);
float bstrength= ss->cache->bstrength;
float grab_delta[3], an[3];
@@ -1330,7 +885,7 @@ static void do_grab_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t
float len;
if (brush->normal_weight > 0)
- calc_sculpt_normal(sd, ss, an, nodes, totnode);
+ calc_sculpt_normal(sd, ob, an, nodes, totnode);
copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
@@ -1342,26 +897,26 @@ static void do_grab_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t
add_v3_v3(grab_delta, an);
}
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for(n=0; n<totnode; n++) {
PBVHVertexIter vd;
- SculptUndoNode* unode;
- SculptBrushTest test;
+ PBVHUndoNode* unode;
+ PaintStrokeTest test;
float (*origco)[3];
short (*origno)[3];
float (*proxy)[3];
- unode= sculpt_undo_push_node(ss, nodes[n]);
- origco= unode->co;
- origno= unode->no;
+ unode= pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL);
+ origco= pbvh_undo_node_co(unode);
+ origno= pbvh_undo_node_no(unode);
- proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+ proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co;
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
- 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);
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(paint_stroke_test(&test, origco[vd.i])) {
+ const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], vd.mask_combined, test.dist)*frontface(brush, an, origno[vd.i], NULL);
mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
@@ -1373,8 +928,9 @@ static void do_grab_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t
}
}
-static void do_nudge_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
float grab_delta[3];
@@ -1384,24 +940,24 @@ static void do_nudge_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
- calc_sculpt_normal(sd, ss, an, nodes, totnode);
+ calc_sculpt_normal(sd, ob, an, nodes, totnode);
cross_v3_v3v3(tmp, an, grab_delta);
cross_v3_v3v3(cono, tmp, an);
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for(n = 0; n < totnode; n++) {
PBVHVertexIter vd;
- SculptBrushTest test;
+ PaintStrokeTest test;
float (*proxy)[3];
- proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+ proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co;
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
- 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);
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(paint_stroke_test(&test, vd.co)) {
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, test.dist)*frontface(brush, an, vd.no, vd.fno);
mul_v3_v3fl(proxy[vd.i], cono, fade);
@@ -1413,8 +969,9 @@ static void do_nudge_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
}
}
-static void do_snake_hook_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
float grab_delta[3], an[3];
@@ -1422,7 +979,7 @@ static void do_snake_hook_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes,
float len;
if (brush->normal_weight > 0)
- calc_sculpt_normal(sd, ss, an, nodes, totnode);
+ calc_sculpt_normal(sd, ob, an, nodes, totnode);
copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
@@ -1437,19 +994,19 @@ static void do_snake_hook_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes,
add_v3_v3(grab_delta, an);
}
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for(n = 0; n < totnode; n++) {
PBVHVertexIter vd;
- SculptBrushTest test;
+ PaintStrokeTest test;
float (*proxy)[3];
- proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+ proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co;
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
- 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);
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(paint_stroke_test(&test, vd.co)) {
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, test.dist)*frontface(brush, an, vd.no, vd.fno);
mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
@@ -1461,8 +1018,9 @@ static void do_snake_hook_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes,
}
}
-static void do_thumb_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
float grab_delta[3];
@@ -1472,31 +1030,31 @@ static void do_thumb_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
- calc_sculpt_normal(sd, ss, an, nodes, totnode);
+ calc_sculpt_normal(sd, ob, an, nodes, totnode);
cross_v3_v3v3(tmp, an, grab_delta);
cross_v3_v3v3(cono, tmp, an);
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for(n = 0; n < totnode; n++) {
PBVHVertexIter vd;
- SculptUndoNode* unode;
- SculptBrushTest test;
+ PBVHUndoNode* unode;
+ PaintStrokeTest test;
float (*origco)[3];
short (*origno)[3];
float (*proxy)[3];
- unode= sculpt_undo_push_node(ss, nodes[n]);
- origco= unode->co;
- origno= unode->no;
+ unode= pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL);
+ origco= pbvh_undo_node_co(unode);
+ origno= pbvh_undo_node_no(unode);
- proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+ proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co;
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
- 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);
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(paint_stroke_test(&test, origco[vd.i])) {
+ const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], vd.mask_combined, test.dist)*frontface(brush, an, origno[vd.i], NULL);
mul_v3_v3fl(proxy[vd.i], cono, fade);
@@ -1508,8 +1066,9 @@ static void do_thumb_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
}
}
-static void do_rotate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush= paint_brush(&sd->paint);
float bstrength= ss->cache->bstrength;
float an[3];
@@ -1518,30 +1077,30 @@ static void do_rotate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
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];
- calc_sculpt_normal(sd, ss, an, nodes, totnode);
+ calc_sculpt_normal(sd, ob, an, nodes, totnode);
axis_angle_to_mat3(m, an, angle);
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for(n=0; n<totnode; n++) {
PBVHVertexIter vd;
- SculptUndoNode* unode;
- SculptBrushTest test;
+ PBVHUndoNode* unode;
+ PaintStrokeTest test;
float (*origco)[3];
short (*origno)[3];
float (*proxy)[3];
- unode= sculpt_undo_push_node(ss, nodes[n]);
- origco= unode->co;
- origno= unode->no;
+ unode= pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL);
+ origco= pbvh_undo_node_co(unode);
+ origno= pbvh_undo_node_no(unode);
- proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+ proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co;
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
- 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);
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(paint_stroke_test(&test, origco[vd.i])) {
+ const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], vd.mask_combined, test.dist)*frontface(brush, an, origno[vd.i], NULL);
mul_v3_m3v3(proxy[vd.i], m, origco[vd.i]);
sub_v3_v3(proxy[vd.i], origco[vd.i]);
@@ -1555,46 +1114,47 @@ static void do_rotate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
}
}
-static void do_layer_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength= ss->cache->bstrength;
float area_normal[3], offset[3];
- float lim= ss->cache->radius / 4;
+ float lim= paint_stroke_radius(ss->cache->stroke) / 4;
int n;
if(bstrength < 0)
lim = -lim;
- calc_sculpt_normal(sd, ss, area_normal, nodes, totnode);
+ calc_sculpt_normal(sd, ob, area_normal, nodes, totnode);
mul_v3_v3v3(offset, ss->cache->scale, area_normal);
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for(n=0; n<totnode; n++) {
PBVHVertexIter vd;
- SculptBrushTest test;
- SculptUndoNode *unode;
+ PaintStrokeTest test;
+ PBVHUndoNode *unode;
float (*origco)[3], *layer_disp;
//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;
+ //proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co;
- unode= sculpt_undo_push_node(ss, nodes[n]);
- origco=unode->co;
- if(!unode->layer_disp)
- {
- #pragma omp critical
- unode->layer_disp= MEM_callocN(sizeof(float)*unode->totvert, "layer disp");
- }
+ unode= pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL);
+ origco= pbvh_undo_node_co(unode);
+ if(!pbvh_undo_node_layer_disp(unode)) {
+ #pragma omp critical
+ pbvh_undo_node_set_layer_disp(unode, MEM_callocN(sizeof(float)*
+ pbvh_undo_node_totvert(unode), "layer disp"));
+ }
- layer_disp= unode->layer_disp;
+ layer_disp= pbvh_undo_node_layer_disp(unode);
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if(sculpt_brush_test(&test, vd.co)) {
- const float fade = bstrength*ss->cache->radius*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, area_normal, vd.no, vd.fno);
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(paint_stroke_test(&test, vd.co)) {
+ const float fade = bstrength*paint_stroke_radius(ss->cache->stroke)*tex_strength(ss, brush, vd.co, vd.mask_combined, test.dist)*frontface(brush, area_normal, vd.no, vd.fno);
float *disp= &layer_disp[vd.i];
float val[3];
@@ -1626,31 +1186,32 @@ static void do_layer_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
}
}
-static void do_inflate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength= ss->cache->bstrength;
int n;
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for(n=0; n<totnode; n++) {
PBVHVertexIter vd;
- SculptBrushTest test;
+ PaintStrokeTest test;
float (*proxy)[3];
- proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+ proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co;
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
- 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);
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(paint_stroke_test(&test, vd.co)) {
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno);
float val[3];
if(vd.fno) copy_v3_v3(val, vd.fno);
else normal_short_to_float_v3(val, vd.no);
- mul_v3_fl(val, fade * ss->cache->radius);
+ mul_v3_fl(val, fade * paint_stroke_radius(ss->cache->stroke));
mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale);
if(vd.mvert)
@@ -1661,28 +1222,29 @@ static void do_inflate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, in
}
}
-static void calc_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float fc[3])
+static void calc_flatten_center(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float fc[3])
{
+ SculptSession *ss = ob->paint->sculpt;
int n;
float count = 0;
zero_v3(fc);
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for(n=0; n<totnode; n++) {
PBVHVertexIter vd;
- SculptBrushTest test;
- SculptUndoNode *unode;
+ PaintStrokeTest test;
+ PBVHUndoNode *unode;
float private_fc[3] = {0.0f, 0.0f, 0.0f};
int private_count = 0;
- unode = sculpt_undo_push_node(ss, nodes[n]);
- sculpt_brush_test_init(ss, &test);
+ unode = pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL);
+ paint_stroke_test_init(&test, ss->cache->stroke);
if(ss->cache->original) {
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if(sculpt_brush_test_fast(&test, unode->co[vd.i])) {
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(paint_stroke_test_fast(&test, pbvh_undo_node_co(unode)[vd.i])) {
add_v3_v3(private_fc, vd.co);
private_count++;
}
@@ -1690,8 +1252,8 @@ static void calc_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes,
BLI_pbvh_vertex_iter_end;
}
else {
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if(sculpt_brush_test_fast(&test, vd.co)) {
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(paint_stroke_test_fast(&test, vd.co)) {
add_v3_v3(private_fc, vd.co);
private_count++;
}
@@ -1711,8 +1273,9 @@ static void calc_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes,
/* 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, SculptSession *ss, PBVHNode **nodes, int totnode, float an[3], float fc[3])
+static void calc_area_normal_and_flatten_center(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float an[3], float fc[3])
{
+ SculptSession *ss = ob->paint->sculpt;
int n;
// an
@@ -1727,26 +1290,26 @@ static void calc_area_normal_and_flatten_center(Sculpt *sd, SculptSession *ss, P
// fc
zero_v3(fc);
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for(n=0; n<totnode; n++) {
PBVHVertexIter vd;
- SculptBrushTest test;
- SculptUndoNode *unode;
+ PaintStrokeTest test;
+ PBVHUndoNode *unode;
float private_an[3] = {0.0f, 0.0f, 0.0f};
float private_out_flip[3] = {0.0f, 0.0f, 0.0f};
float private_fc[3] = {0.0f, 0.0f, 0.0f};
int private_count = 0;
- unode = sculpt_undo_push_node(ss, nodes[n]);
- sculpt_brush_test_init(ss, &test);
+ unode = pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL);
+ paint_stroke_test_init(&test, ss->cache->stroke);
if(ss->cache->original) {
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if(sculpt_brush_test_fast(&test, unode->co[vd.i])) {
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(paint_stroke_test_fast(&test, pbvh_undo_node_co(unode)[vd.i])) {
// an
float fno[3];
- normal_short_to_float_v3(fno, unode->no[vd.i]);
+ normal_short_to_float_v3(fno, pbvh_undo_node_no(unode)[vd.i]);
add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno);
// fc
@@ -1757,8 +1320,8 @@ static void calc_area_normal_and_flatten_center(Sculpt *sd, SculptSession *ss, P
BLI_pbvh_vertex_iter_end;
}
else {
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if(sculpt_brush_test_fast(&test, vd.co)) {
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if(paint_stroke_test_fast(&test, vd.co)) {
// an
if(vd.no) {
float fno[3];
@@ -1805,13 +1368,14 @@ static void calc_area_normal_and_flatten_center(Sculpt *sd, SculptSession *ss, P
}
}
-static void calc_sculpt_plane(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float an[3], float fc[3])
+static void calc_sculpt_plane(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float an[3], float fc[3])
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
if (ss->cache->mirror_symmetry_pass == 0 &&
ss->cache->radial_symmetry_pass == 0 &&
- (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL)))
+ (paint_stroke_first_dab(ss->cache->stroke) || !(brush->flag & BRUSH_ORIGINAL_NORMAL)))
{
switch (brush->sculpt_plane) {
case SCULPT_DISP_DIR_VIEW:
@@ -1837,7 +1401,7 @@ static void calc_sculpt_plane(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, i
break;
case SCULPT_DISP_DIR_AREA:
- calc_area_normal_and_flatten_center(sd, ss, nodes, totnode, an, fc);
+ calc_area_normal_and_flatten_center(sd, ob, nodes, totnode, an, fc);
default:
break;
@@ -1846,7 +1410,7 @@ static void calc_sculpt_plane(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, i
// fc
/* flatten center has not been calculated yet if we are not using the area normal */
if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA)
- calc_flatten_center(sd, ss, nodes, totnode, fc);
+ calc_flatten_center(sd, ob, nodes, totnode, fc);
// an
copy_v3_v3(ss->cache->last_area_normal, an);
@@ -1862,10 +1426,10 @@ static void calc_sculpt_plane(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, i
copy_v3_v3(fc, ss->cache->last_center);
// an
- flip_coord(an, an, ss->cache->mirror_symmetry_pass);
+ paint_flip_coord(an, an, ss->cache->mirror_symmetry_pass);
// fc
- flip_coord(fc, fc, ss->cache->mirror_symmetry_pass);
+ paint_flip_coord(fc, fc, ss->cache->mirror_symmetry_pass);
// an
mul_m4_v3(ss->cache->symm_rot_mat, an);
@@ -1885,7 +1449,8 @@ static void point_plane_project(float intr[3], float co[3], float plane_normal[3
static int plane_trim(StrokeCache *cache, Brush *brush, float val[3])
{
- return !(brush->flag & BRUSH_PLANE_TRIM) || (dot_v3v3(val, val) <= cache->radius_squared*cache->plane_trim_squared);
+ return !(brush->flag & BRUSH_PLANE_TRIM) ||
+ (dot_v3v3(val, val) <= paint_stroke_radius_squared(cache->stroke)*cache->plane_trim_squared);
}
static int plane_point_side_flip(float co[3], float plane_normal[3], float plane_center[3], int flip)
@@ -1916,18 +1481,19 @@ static float get_offset(Sculpt *sd, SculptSession *ss)
float rv = brush->plane_offset;
if (brush->flag & BRUSH_OFFSET_PRESSURE) {
- rv *= ss->cache->pressure;
+ rv *= paint_stroke_pressure(ss->cache->stroke);
}
return rv;
}
-static void do_flatten_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
- const float radius = ss->cache->radius;
+ const float radius = paint_stroke_radius(ss->cache->stroke);
float an[3];
float fc[3];
@@ -1940,7 +1506,7 @@ static void do_flatten_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, in
float temp[3];
- calc_sculpt_plane(sd, ss, nodes, totnode, an, fc);
+ calc_sculpt_plane(sd, ob, nodes, totnode, an, fc);
displace = radius*offset;
@@ -1948,18 +1514,18 @@ static void do_flatten_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, in
mul_v3_fl(temp, displace);
add_v3_v3(fc, temp);
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for(n = 0; n < totnode; n++) {
PBVHVertexIter vd;
- SculptBrushTest test;
+ PaintStrokeTest test;
float (*proxy)[3];
- proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+ proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co;
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (sculpt_brush_test_sq(&test, vd.co)) {
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (paint_stroke_test_sq(&test, vd.co)) {
float intr[3];
float val[3];
@@ -1968,7 +1534,7 @@ static void do_flatten_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, in
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);
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -1981,12 +1547,13 @@ static void do_flatten_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, in
}
}
-static void do_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
- float radius = ss->cache->radius;
+ float radius = paint_stroke_radius(ss->cache->stroke);
float offset = get_offset(sd, ss);
float displace;
@@ -2001,7 +1568,7 @@ static void do_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t
int flip;
- calc_sculpt_plane(sd, ss, nodes, totnode, an, fc);
+ calc_sculpt_plane(sd, ob, nodes, totnode, an, fc);
flip = bstrength < 0;
@@ -2018,20 +1585,20 @@ static void do_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t
//add_v3_v3v3(p, ss->cache->location, an);
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
- SculptBrushTest test;
+ PaintStrokeTest test;
float (*proxy)[3];
- proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+ proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co;
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (sculpt_brush_test_sq(&test, vd.co)) {
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (paint_stroke_test_sq(&test, vd.co)) {
if (plane_point_side_flip(vd.co, an, fc, flip)) {
- //if (sculpt_brush_test_cyl(&test, vd.co, ss->cache->location, p)) {
+ //if (paint_stroke_test_cyl(&test, vd.co, ss->cache->location, p)) {
float intr[3];
float val[3];
@@ -2040,7 +1607,7 @@ static void do_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t
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);
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -2054,12 +1621,13 @@ static void do_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t
}
}
-static void do_clay_tubes_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_clay_tubes_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
- float radius = ss->cache->radius;
+ float radius = paint_stroke_radius(ss->cache->stroke);
float offset = get_offset(sd, ss);
float displace;
@@ -2077,15 +1645,15 @@ static void do_clay_tubes_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes,
int flip;
- calc_sculpt_plane(sd, ss, nodes, totnode, sn, fc);
+ calc_sculpt_plane(sd, ob, nodes, totnode, sn, fc);
if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL))
- calc_area_normal(sd, ss, an, nodes, totnode);
+ calc_area_normal(sd, ob, an, nodes, totnode);
else
copy_v3_v3(an, sn);
- if (ss->cache->first_time)
- return; // delay the first daub because grab delta is not setup
+ if(paint_stroke_first_dab(ss->cache->stroke))
+ return; // delay the first dab because grab delta is not setup
flip = bstrength < 0;
@@ -2103,24 +1671,25 @@ static void do_clay_tubes_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes,
cross_v3_v3v3(mat[0], an, ss->cache->grab_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;
+ paint_stroke_symmetry_location(ss->cache->stroke, mat[3]);
+ mat[3][3] = 1;
normalize_m4(mat);
- scale_m4_fl(scale, ss->cache->radius);
+ scale_m4_fl(scale, paint_stroke_radius(ss->cache->stroke));
mul_m4_m4m4(tmat, scale, mat);
invert_m4_m4(mat, tmat);
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
- SculptBrushTest test;
+ PaintStrokeTest test;
float (*proxy)[3];
- proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+ proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co;
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (sculpt_brush_test_cube(&test, vd.co, mat)) {
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (paint_stroke_test_cube(&test, vd.co, mat)) {
if (plane_point_side_flip(vd.co, sn, fc, flip)) {
float intr[3];
float val[3];
@@ -2130,7 +1699,7 @@ static void do_clay_tubes_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes,
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);
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, paint_stroke_radius(ss->cache->stroke)*test.dist)*frontface(brush, an, vd.no, vd.fno);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -2144,12 +1713,13 @@ static void do_clay_tubes_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes,
}
}
-static void do_fill_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
- const float radius = ss->cache->radius;
+ const float radius = paint_stroke_radius(ss->cache->stroke);
float an[3];
float fc[3];
@@ -2161,7 +1731,7 @@ static void do_fill_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t
float temp[3];
- calc_sculpt_plane(sd, ss, nodes, totnode, an, fc);
+ calc_sculpt_plane(sd, ob, nodes, totnode, an, fc);
displace = radius*offset;
@@ -2169,18 +1739,18 @@ static void do_fill_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t
mul_v3_fl(temp, displace);
add_v3_v3(fc, temp);
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
- SculptBrushTest test;
+ PaintStrokeTest test;
float (*proxy)[3];
- proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+ proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co;
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (sculpt_brush_test_sq(&test, vd.co)) {
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (paint_stroke_test_sq(&test, vd.co)) {
if (plane_point_side(vd.co, an, fc)) {
float intr[3];
float val[3];
@@ -2190,7 +1760,7 @@ static void do_fill_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t
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);
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -2204,12 +1774,13 @@ static void do_fill_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t
}
}
-static void do_scrape_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
float bstrength = ss->cache->bstrength;
- const float radius = ss->cache->radius;
+ const float radius = paint_stroke_radius(ss->cache->stroke);
float an[3];
float fc[3];
@@ -2221,7 +1792,7 @@ static void do_scrape_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
float temp[3];
- calc_sculpt_plane(sd, ss, nodes, totnode, an, fc);
+ calc_sculpt_plane(sd, ob, nodes, totnode, an, fc);
displace = -radius*offset;
@@ -2229,18 +1800,18 @@ static void do_scrape_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
mul_v3_fl(temp, displace);
add_v3_v3(fc, temp);
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for (n = 0; n < totnode; n++) {
PBVHVertexIter vd;
- SculptBrushTest test;
+ PaintStrokeTest test;
float (*proxy)[3];
- proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+ proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co;
- sculpt_brush_test_init(ss, &test);
+ paint_stroke_test_init(&test, ss->cache->stroke);
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (sculpt_brush_test_sq(&test, vd.co)) {
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (paint_stroke_test_sq(&test, vd.co)) {
if (!plane_point_side(vd.co, an, fc)) {
float intr[3];
float val[3];
@@ -2250,7 +1821,7 @@ static void do_scrape_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
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);
+ const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -2316,139 +1887,155 @@ void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3])
}
/* copy the modified vertices from bvh to the active key */
-static void sculpt_update_keyblock(SculptSession *ss)
+static void sculpt_update_keyblock(Object *ob)
{
- float (*vertCos)[3]= BLI_pbvh_get_vertCos(ss->pbvh);
+ SculptSession *ss = ob->paint->sculpt;
+ float (*vertCos)[3]= BLI_pbvh_get_vertCos(ob->paint->pbvh);
if (vertCos) {
- sculpt_vertcos_to_key(ss->ob, ss->kb, vertCos);
+ sculpt_vertcos_to_key(ob, ss->kb, vertCos);
MEM_freeN(vertCos);
}
}
-static void do_brush_action(Sculpt *sd, SculptSession *ss, Brush *brush)
+static void sculpt_stroke_brush_action(bContext *C, PaintStroke *stroke)
{
- SculptSearchSphereData data;
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->paint->sculpt;
+ Brush *brush = paint_brush(&sd->paint);
+ PBVHSearchSphereData data;
PBVHNode **nodes = NULL;
+ float location[3];
int n, totnode;
/* Build a list of all nodes that are potentially within the brush's area of influence */
- data.ss = ss;
- data.sd = sd;
- data.radius_squared = ss->cache->radius_squared;
+ paint_stroke_symmetry_location(ss->cache->stroke, location);
+ data.center = location;
+ data.radius_squared = paint_stroke_radius_squared(ss->cache->stroke);
data.original = ELEM4(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB, SCULPT_TOOL_LAYER);
- BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
+ BLI_pbvh_search_gather(ob->paint->pbvh, BLI_pbvh_search_sphere_cb, &data, &nodes, &totnode);
/* Only act if some verts are inside the brush area */
if (totnode) {
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
- for (n= 0; n < totnode; n++) {
- sculpt_undo_push_node(ss, nodes[n]);
- BLI_pbvh_node_mark_update(nodes[n]);
- }
-
/* Apply one type of brush action */
- switch(brush->sculpt_tool){
- case SCULPT_TOOL_DRAW:
- do_draw_brush(sd, ss, nodes, totnode);
- break;
- case SCULPT_TOOL_SMOOTH:
- do_smooth_brush(sd, ss, nodes, totnode);
- break;
- case SCULPT_TOOL_CREASE:
- do_crease_brush(sd, ss, nodes, totnode);
- break;
- case SCULPT_TOOL_BLOB:
- do_crease_brush(sd, ss, nodes, totnode);
- break;
- case SCULPT_TOOL_PINCH:
- do_pinch_brush(sd, ss, nodes, totnode);
- break;
- case SCULPT_TOOL_INFLATE:
- do_inflate_brush(sd, ss, nodes, totnode);
- break;
- case SCULPT_TOOL_GRAB:
- do_grab_brush(sd, ss, nodes, totnode);
- break;
- case SCULPT_TOOL_ROTATE:
- do_rotate_brush(sd, ss, nodes, totnode);
- break;
- case SCULPT_TOOL_SNAKE_HOOK:
- do_snake_hook_brush(sd, ss, nodes, totnode);
- break;
- case SCULPT_TOOL_NUDGE:
- do_nudge_brush(sd, ss, nodes, totnode);
- break;
- case SCULPT_TOOL_THUMB:
- do_thumb_brush(sd, ss, nodes, totnode);
- break;
- case SCULPT_TOOL_LAYER:
- do_layer_brush(sd, ss, nodes, totnode);
- break;
- case SCULPT_TOOL_FLATTEN:
- do_flatten_brush(sd, ss, nodes, totnode);
- break;
- case SCULPT_TOOL_CLAY:
- do_clay_brush(sd, ss, nodes, totnode);
- break;
- case SCULPT_TOOL_CLAY_TUBES:
- do_clay_tubes_brush(sd, ss, nodes, totnode);
- break;
- case SCULPT_TOOL_FILL:
- do_fill_brush(sd, ss, nodes, totnode);
- break;
- case SCULPT_TOOL_SCRAPE:
- do_scrape_brush(sd, ss, nodes, totnode);
- break;
+ if(brush->flag & BRUSH_MASK)
+ paintmask_brush_apply(&sd->paint, stroke, nodes,
+ totnode,
+ ss->cache->bstrength);
+ else {
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
+ for (n= 0; n < totnode; n++) {
+ pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL);
+ BLI_pbvh_node_mark_update(nodes[n]);
+ }
+
+ switch(brush->sculpt_tool){
+ case SCULPT_TOOL_DRAW:
+ do_draw_brush(sd, ob, nodes, totnode);
+ break;
+ case SCULPT_TOOL_SMOOTH:
+ do_smooth_brush(sd, ob, nodes, totnode);
+ break;
+ case SCULPT_TOOL_CREASE:
+ do_crease_brush(sd, ob, nodes, totnode);
+ break;
+ case SCULPT_TOOL_BLOB:
+ do_crease_brush(sd, ob, nodes, totnode);
+ break;
+ case SCULPT_TOOL_PINCH:
+ do_pinch_brush(sd, ob, nodes, totnode);
+ break;
+ case SCULPT_TOOL_INFLATE:
+ do_inflate_brush(sd, ob, nodes, totnode);
+ break;
+ case SCULPT_TOOL_GRAB:
+ do_grab_brush(sd, ob, nodes, totnode);
+ break;
+ case SCULPT_TOOL_ROTATE:
+ do_rotate_brush(sd, ob, nodes, totnode);
+ break;
+ case SCULPT_TOOL_SNAKE_HOOK:
+ do_snake_hook_brush(sd, ob, nodes, totnode);
+ break;
+ case SCULPT_TOOL_NUDGE:
+ do_nudge_brush(sd, ob, nodes, totnode);
+ break;
+ case SCULPT_TOOL_THUMB:
+ do_thumb_brush(sd, ob, nodes, totnode);
+ break;
+ case SCULPT_TOOL_LAYER:
+ do_layer_brush(sd, ob, nodes, totnode);
+ break;
+ case SCULPT_TOOL_FLATTEN:
+ do_flatten_brush(sd, ob, nodes, totnode);
+ break;
+ 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);
+ break;
+ case SCULPT_TOOL_FILL:
+ do_fill_brush(sd, ob, nodes, totnode);
+ break;
+ case SCULPT_TOOL_SCRAPE:
+ do_scrape_brush(sd, ob, nodes, totnode);
+ break;
+ }
}
if (brush->sculpt_tool != SCULPT_TOOL_SMOOTH && brush->autosmooth_factor > 0) {
if (brush->flag & BRUSH_INVERSE_SMOOTH_PRESSURE) {
- smooth(sd, ss, nodes, totnode, brush->autosmooth_factor*(1-ss->cache->pressure));
+ smooth(sd, ob, nodes, totnode, brush->autosmooth_factor *
+ (1 - paint_stroke_pressure(ss->cache->stroke)));
}
else {
- smooth(sd, ss, nodes, totnode, brush->autosmooth_factor);
+ smooth(sd, ob, nodes, totnode, brush->autosmooth_factor);
}
}
/* copy the modified vertices from mesh to the active key */
if(ss->kb)
- mesh_to_key(ss->ob->data, ss->kb);
+ mesh_to_key(ob->data, ss->kb);
/* optimization: we could avoid copying new coords to keyblock at each */
/* stroke step if there are no modifiers due to pbvh is used for displaying */
/* so to increase speed we'll copy new coords to keyblock when stroke is done */
- if(ss->kb && ss->modifiers_active) sculpt_update_keyblock(ss);
+ if(ss->kb && ss->modifiers_active) sculpt_update_keyblock(ob);
MEM_freeN(nodes);
}
}
-static void sculpt_combine_proxies(Sculpt *sd, SculptSession *ss)
+static void sculpt_combine_proxies(Sculpt *sd, Object *ob)
{
+ SculptSession *ss = ob->paint->sculpt;
Brush *brush= paint_brush(&sd->paint);
PBVHNode** nodes;
int totnode;
int n;
- BLI_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode);
+ BLI_pbvh_gather_proxies(ob->paint->pbvh, &nodes, &totnode);
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)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for (n= 0; n < totnode; n++) {
PBVHVertexIter vd;
PBVHProxyNode* proxies;
+ PBVHUndoNode *unode;
int proxy_count;
float (*origco)[3];
- origco= sculpt_undo_push_node(ss, nodes[n])->co;
+ unode= pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL);
+ origco= pbvh_undo_node_co(unode);
BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count);
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
float val[3];
int p;
@@ -2478,7 +2065,7 @@ static void sculpt_combine_proxies(Sculpt *sd, SculptSession *ss)
case SCULPT_TOOL_PINCH:
case SCULPT_TOOL_SCRAPE:
case SCULPT_TOOL_SNAKE_HOOK:
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for (n= 0; n < totnode; n++) {
PBVHVertexIter vd;
PBVHProxyNode* proxies;
@@ -2486,7 +2073,7 @@ static void sculpt_combine_proxies(Sculpt *sd, SculptSession *ss)
BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count);
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
float val[3];
int p;
@@ -2515,97 +2102,6 @@ static void sculpt_combine_proxies(Sculpt *sd, SculptSession *ss)
MEM_freeN(nodes);
}
-//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]);
-//}
-
-/* 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 feather)
-{
- flip_coord(cache->location, cache->true_location, symm);
- flip_coord(cache->grab_delta_symmetry, cache->grab_delta, symm);
- flip_coord(cache->view_normal, cache->true_view_normal, symm);
-
- // 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);
-
- // printf("feather: %f frac: %f reduce: %f\n", feather, frac, reduce);
-
- // if (frac < 1)
- // mul_v3_fl(cache->grab_delta_symmetry, reduce);
- //}
-
- unit_m4(cache->symm_rot_mat);
- unit_m4(cache->symm_rot_mat_inv);
- 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);
-}
-
-static void do_radial_symmetry(Sculpt *sd, SculptSession *ss, Brush *brush, const char symm, const int axis, const float feather)
-{
- int i;
-
- for(i = 1; i < sd->radial_symm[axis-'X']; ++i) {
- const float angle = 2*M_PI*i/sd->radial_symm[axis-'X'];
- ss->cache->radial_symmetry_pass= i;
- calc_brushdata_symm(sd, ss->cache, symm, axis, angle, feather);
- do_brush_action(sd, ss, brush);
- }
-}
-
-static void do_symmetrical_brush_actions(Sculpt *sd, SculptSession *ss)
-{
- Brush *brush = paint_brush(&sd->paint);
- StrokeCache *cache = ss->cache;
- const char symm = sd->flags & 7;
- int i;
-
- float feather = calc_symmetry_feather(sd, ss->cache);
-
- cache->bstrength= brush_strength(sd, cache, feather);
-
- cache->symmetry= symm;
-
- /* 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)))) {
- cache->mirror_symmetry_pass= i;
- cache->radial_symmetry_pass= 0;
-
- calc_brushdata_symm(sd, cache, i, 0, 0, feather);
- do_brush_action(sd, ss, brush);
-
- do_radial_symmetry(sd, ss, brush, i, 'X', feather);
- do_radial_symmetry(sd, ss, brush, i, 'Y', feather);
- do_radial_symmetry(sd, ss, brush, i, 'Z', feather);
- }
- }
-
- sculpt_combine_proxies(sd, ss);
-
- cache->first_time= 0;
-}
-
static void sculpt_update_tex(Sculpt *sd, SculptSession *ss)
{
Brush *brush = paint_brush(&sd->paint);
@@ -2627,10 +2123,10 @@ static void sculpt_update_tex(Sculpt *sd, SculptSession *ss)
void sculpt_update_mesh_elements(Scene *scene, Object *ob, int need_fmap)
{
DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
- SculptSession *ss = ob->sculpt;
- MultiresModifierData *mmd= sculpt_multires_active(scene, ob);
+ SculptSession *ss = ob->paint->sculpt;
+ MultiresModifierData *mmd= ED_paint_multires_active(scene, ob);
- ss->ob= ob;
+ ob= ob;
ss->modifiers_active= sculpt_modifiers_active(scene, ob);
@@ -2655,16 +2151,16 @@ void sculpt_update_mesh_elements(Scene *scene, Object *ob, int need_fmap)
ss->multires = NULL;
}
- ss->pbvh = dm->getPBVH(ob, dm);
+ ob->paint->pbvh = dm->getPBVH(ob, dm);
ss->fmap = (need_fmap && dm->getFaceMap)? dm->getFaceMap(ob, dm): NULL;
/* if pbvh is deformed, key block is already applied to it */
- if (ss->kb && !BLI_pbvh_isDeformed(ss->pbvh)) {
+ if (ss->kb && !BLI_pbvh_isDeformed(ob->paint->pbvh)) {
float (*vertCos)[3]= key_to_vertcos(ob, ss->kb);
if (vertCos) {
/* apply shape keys coordinates to PBVH */
- BLI_pbvh_apply_vertCos(ss->pbvh, vertCos);
+ BLI_pbvh_apply_vertCos(ob->paint->pbvh, vertCos);
MEM_freeN(vertCos);
}
}
@@ -2783,27 +2279,10 @@ static void SCULPT_OT_radial_control(wmOperatorType *ot)
/**** Operator for applying a stroke (various attributes including mouse path)
using the current brush. ****/
-static float unproject_brush_radius(Object *ob, ViewContext *vc, float center[3], float offset)
-{
- float delta[3], scale, loc[3];
-
- mul_v3_m4v3(loc, ob->obmat, center);
-
- initgrabz(vc->rv3d, loc[0], loc[1], loc[2]);
- window_to_3d_delta(vc->ar, delta, offset, 0);
-
- scale= fabsf(mat4_to_scale(ob->obmat));
- scale= (scale == 0.0f)? 1.0f: scale;
-
- return len_v3(delta)/scale;
-}
-
static void sculpt_cache_free(StrokeCache *cache)
{
if(cache->face_norms)
MEM_freeN(cache->face_norms);
- if(cache->mats)
- MEM_freeN(cache->mats);
MEM_freeN(cache);
}
@@ -2819,6 +2298,7 @@ static void sculpt_update_cache_invariants(bContext* C, Sculpt *sd, SculptSessio
int mode;
ss->cache = cache;
+ ss->cache->stroke = op->customdata;
/* Set scaling adjustment */
ss->cache->scale[0] = 1.0f / ob->size[0];
@@ -2844,16 +2324,6 @@ static void sculpt_update_cache_invariants(bContext* C, Sculpt *sd, SculptSessio
}
}
- /* Initial mouse location */
- if (event) {
- ss->cache->initial_mouse[0] = event->x;
- ss->cache->initial_mouse[1] = event->y;
- }
- else {
- ss->cache->initial_mouse[0] = 0;
- ss->cache->initial_mouse[1] = 0;
- }
-
mode = RNA_int_get(op->ptr, "mode");
cache->invert = mode == WM_BRUSHSTROKE_INVERT;
cache->alt_smooth = mode == WM_BRUSHSTROKE_SMOOTH;
@@ -2872,18 +2342,12 @@ static void sculpt_update_cache_invariants(bContext* C, Sculpt *sd, SculptSessio
}
}
- copy_v2_v2(cache->mouse, cache->initial_mouse);
- copy_v2_v2(cache->tex_mouse, cache->initial_mouse);
-
/* Truly temporary data that isn't stored in properties */
cache->vc = vc;
cache->brush = brush;
- cache->mats = MEM_callocN(sizeof(bglMats), "sculpt bglMats");
- view3d_get_transformation(vc->ar, vc->rv3d, vc->obact, cache->mats);
-
viewvector(cache->vc->rv3d, cache->vc->rv3d->twmat[3], cache->true_view_normal);
/* Initialize layer brush displacements and persistent coords */
if(brush->sculpt_tool == SCULPT_TOOL_LAYER) {
@@ -2914,17 +2378,15 @@ static void sculpt_update_cache_invariants(bContext* C, Sculpt *sd, SculptSessio
if(!(brush->flag & BRUSH_ACCUMULATE))
cache->original = 1;
- cache->special_rotation = (brush->flag & BRUSH_RAKE) ? sd->last_angle : 0;
//cache->last_rake[0] = sd->last_x;
//cache->last_rake[1] = sd->last_y;
- cache->first_time= 1;
-
cache->vertex_rotation= 0;
}
-static void sculpt_update_brush_delta(Sculpt *sd, SculptSession *ss, Brush *brush)
+static void sculpt_update_brush_delta(Sculpt *sd, Object *ob, Brush *brush)
{
+ SculptSession *ss = ob->paint->sculpt;
StrokeCache *cache = ss->cache;
int tool = brush->sculpt_tool;
@@ -2933,13 +2395,17 @@ static void sculpt_update_brush_delta(Sculpt *sd, SculptSession *ss, Brush *brus
SCULPT_TOOL_CLAY_TUBES, SCULPT_TOOL_SNAKE_HOOK,
SCULPT_TOOL_THUMB)) {
float grab_location[3], imat[4][4], delta[3];
+ float mouse[2], location[3];
+
+ paint_stroke_mouse_location(cache->stroke, mouse);
+ paint_stroke_location(cache->stroke, location);
- if(cache->first_time) {
+ if(paint_stroke_first_dab(cache->stroke)) {
copy_v3_v3(cache->orig_grab_location,
- cache->true_location);
+ location);
}
else if(tool == SCULPT_TOOL_SNAKE_HOOK)
- add_v3_v3(cache->true_location, cache->grab_delta);
+ add_v3_v3(location, cache->grab_delta);
/* compute 3d coordinate at same z from original location + mouse */
initgrabz(cache->vc->rv3d,
@@ -2948,27 +2414,27 @@ static void sculpt_update_brush_delta(Sculpt *sd, SculptSession *ss, Brush *brus
cache->orig_grab_location[2]);
window_to_3d_delta(cache->vc->ar, grab_location,
- cache->mouse[0], cache->mouse[1]);
+ mouse[0], mouse[1]);
/* compute delta to move verts by */
- if(!cache->first_time) {
+ if(!paint_stroke_first_dab(cache->stroke)) {
switch(tool) {
case SCULPT_TOOL_GRAB:
case SCULPT_TOOL_THUMB:
sub_v3_v3v3(delta, grab_location, cache->old_grab_location);
- invert_m4_m4(imat, ss->ob->obmat);
+ invert_m4_m4(imat, ob->obmat);
mul_mat3_m4_v3(imat, delta);
add_v3_v3(cache->grab_delta, delta);
break;
case SCULPT_TOOL_CLAY_TUBES:
case SCULPT_TOOL_NUDGE:
sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location);
- invert_m4_m4(imat, ss->ob->obmat);
+ invert_m4_m4(imat, ob->obmat);
mul_mat3_m4_v3(imat, cache->grab_delta);
break;
case SCULPT_TOOL_SNAKE_HOOK:
sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location);
- invert_m4_m4(imat, ss->ob->obmat);
+ invert_m4_m4(imat, ob->obmat);
mul_mat3_m4_v3(imat, cache->grab_delta);
break;
}
@@ -2979,18 +2445,9 @@ static void sculpt_update_brush_delta(Sculpt *sd, SculptSession *ss, Brush *brus
copy_v3_v3(cache->old_grab_location, grab_location);
- if(tool == SCULPT_TOOL_GRAB)
- copy_v3_v3(sd->anchored_location, cache->true_location);
- else if(tool == SCULPT_TOOL_THUMB)
- copy_v3_v3(sd->anchored_location, cache->orig_grab_location);
-
if(ELEM(tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) {
/* 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_initial_mouse, cache->initial_mouse);
- sd->anchored_size = cache->pixel_radius;
+ copy_v3_v3(location, cache->orig_grab_location);
}
}
}
@@ -2998,221 +2455,74 @@ static void sculpt_update_brush_delta(Sculpt *sd, SculptSession *ss, Brush *brus
/* Initialize the stroke cache variants from operator properties */
static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, SculptSession *ss, struct PaintStroke *stroke, PointerRNA *ptr)
{
+ Object *ob = CTX_data_active_object(C);
StrokeCache *cache = ss->cache;
Brush *brush = paint_brush(&sd->paint);
- int dx, dy;
-
- //RNA_float_get_array(ptr, "location", cache->traced_location);
-
- if (cache->first_time ||
- !((brush->flag & BRUSH_ANCHORED)||
- (brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK)||
- (brush->sculpt_tool == SCULPT_TOOL_ROTATE))
- )
- {
- RNA_float_get_array(ptr, "location", cache->true_location);
- }
-
cache->pen_flip = RNA_boolean_get(ptr, "pen_flip");
- RNA_float_get_array(ptr, "mouse", cache->mouse);
-
- cache->pressure = RNA_float_get(ptr, "pressure");
/* Truly temporary data that isn't stored in properties */
- sd->draw_pressure= 1;
- sd->pressure_value= cache->pressure;
-
- cache->previous_pixel_radius = cache->pixel_radius;
- cache->pixel_radius = brush_size(brush);
-
- if(cache->first_time) {
- if (!brush_use_locked_size(brush)) {
- cache->initial_radius= unproject_brush_radius(ss->ob, cache->vc, cache->true_location, brush_size(brush));
- brush_set_unprojected_radius(brush, cache->initial_radius);
- }
- else {
- cache->initial_radius= brush_unprojected_radius(brush);
- }
-
- if (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK))
- cache->initial_radius *= 2.0f;
- }
-
- if(brush_use_size_pressure(brush)) {
- cache->pixel_radius *= cache->pressure;
- cache->radius= cache->initial_radius * cache->pressure;
- }
- else
- cache->radius= cache->initial_radius;
-
- cache->radius_squared = cache->radius*cache->radius;
-
- 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) &&
- !(brush->flag & BRUSH_RAKE))
- {
- cache->special_rotation = 2*M_PI*BLI_frand();
- }
- }
-
- if(brush->flag & BRUSH_ANCHORED) {
- int hit = 0;
-
- dx = cache->mouse[0] - cache->initial_mouse[0];
- dy = cache->mouse[1] - cache->initial_mouse[1];
-
- sd->anchored_size = cache->pixel_radius = sqrt(dx*dx + dy*dy);
-
- cache->special_rotation = atan2(dx, dy) + M_PI;
-
- if (brush->flag & BRUSH_EDGE_TO_EDGE) {
- float halfway[2];
- float out[3];
-
- halfway[0] = dx*0.5 + cache->initial_mouse[0];
- halfway[1] = dy*0.5 + cache->initial_mouse[1];
-
- if (sculpt_stroke_get_location(C, stroke, out, halfway)) {
- copy_v3_v3(sd->anchored_location, out);
- copy_v2_v2(sd->anchored_initial_mouse, halfway);
- copy_v2_v2(cache->tex_mouse, halfway);
- copy_v3_v3(cache->true_location, sd->anchored_location);
- sd->anchored_size /= 2.0f;
- cache->pixel_radius /= 2.0f;
- hit = 1;
- }
- }
-
- if (!hit)
- copy_v2_v2(sd->anchored_initial_mouse, cache->initial_mouse);
-
- cache->radius= unproject_brush_radius(ss->ob, paint_stroke_view_context(stroke), cache->true_location, cache->pixel_radius);
- cache->radius_squared = cache->radius*cache->radius;
+ sculpt_update_brush_delta(sd, ob, brush);
- copy_v3_v3(sd->anchored_location, cache->true_location);
-
- sd->draw_anchored = 1;
- }
- else if(brush->flag & BRUSH_RAKE) {
- const float u = 0.5f;
- const float v = 1 - u;
- const float r = 20;
-
- const float dx = cache->last_rake[0] - cache->mouse[0];
- const float dy = cache->last_rake[1] - cache->mouse[1];
-
- if (cache->first_time) {
- copy_v2_v2(cache->last_rake, cache->mouse);
- }
- else if (dx*dx + dy*dy >= r*r) {
- cache->special_rotation = atan2(dx, dy);
-
- cache->last_rake[0] = u*cache->last_rake[0] + v*cache->mouse[0];
- cache->last_rake[1] = u*cache->last_rake[1] + v*cache->mouse[1];
- }
- }
+ if(brush->sculpt_tool == SCULPT_TOOL_ROTATE) {
+ float mouse[2], initial_mouse[2];
+ float dx, dy;
- sculpt_update_brush_delta(sd, ss, brush);
+ paint_stroke_mouse_location(cache->stroke, mouse);
+ paint_stroke_initial_mouse_location(cache->stroke, initial_mouse);
- if(brush->sculpt_tool == SCULPT_TOOL_ROTATE) {
- dx = cache->mouse[0] - cache->initial_mouse[0];
- dy = cache->mouse[1] - cache->initial_mouse[1];
+ dx = mouse[0] - initial_mouse[0];
+ dy = mouse[1] - initial_mouse[1];
cache->vertex_rotation = -atan2(dx, dy);
-
- sd->draw_anchored = 1;
- copy_v2_v2(sd->anchored_initial_mouse, cache->initial_mouse);
- copy_v3_v3(sd->anchored_location, cache->true_location);
- sd->anchored_size = cache->pixel_radius;
}
-
- sd->special_rotation = cache->special_rotation;
}
-static void sculpt_stroke_modifiers_check(bContext *C, SculptSession *ss)
+static void sculpt_stroke_modifiers_check(bContext *C)
{
- if(ss->modifiers_active) {
+ Object *ob = CTX_data_active_object(C);
+
+ if(ob->paint->sculpt->modifiers_active) {
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Brush *brush = paint_brush(&sd->paint);
- sculpt_update_mesh_elements(CTX_data_scene(C), ss->ob, brush->sculpt_tool == SCULPT_TOOL_SMOOTH);
+ sculpt_update_mesh_elements(CTX_data_scene(C), ob, brush->sculpt_tool == SCULPT_TOOL_SMOOTH);
}
}
-typedef struct {
- SculptSession *ss;
- float *ray_start, *ray_normal;
- int hit;
- float dist;
- int original;
-} SculptRaycastData;
-
-void sculpt_raycast_cb(PBVHNode *node, void *data_v, float* tmin)
+static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float* tmin)
{
if (BLI_pbvh_node_get_tmin(node) < *tmin) {
- SculptRaycastData *srd = data_v;
+ PaintStrokeRaycastData *data = data_v;
+ SculptSession *ss = data->mode_data;
float (*origco)[3]= NULL;
- if(srd->original && srd->ss->cache) {
+ if(data->original && ss->cache) {
/* intersect with coordinates from before we started stroke */
- SculptUndoNode *unode= sculpt_undo_get_node(node);
- origco= (unode)? unode->co: NULL;
+ PBVHUndoNode *unode= pbvh_undo_get_node(node);
+ origco= (unode)? pbvh_undo_node_co(unode): NULL;
}
- if (BLI_pbvh_node_raycast(srd->ss->pbvh, node, origco, srd->ray_start, srd->ray_normal, &srd->dist)) {
- srd->hit = 1;
- *tmin = srd->dist;
+ if(BLI_pbvh_node_raycast(data->ob->paint->pbvh, node, origco, data->ray_start, data->ray_normal, &data->dist, NULL, NULL)) {
+ data->hit = 1;
+ *tmin = data->dist;
}
}
}
-/* Do a raycast in the tree to find the 3d brush location
- (This allows us to ignore the GL depth buffer)
- Returns 0 if the ray doesn't hit the mesh, non-zero otherwise
- */
-int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float out[3], float mouse[2])
-{
- ViewContext *vc = paint_stroke_view_context(stroke);
- SculptSession *ss= vc->obact->sculpt;
- StrokeCache *cache= ss->cache;
- float ray_start[3], ray_end[3], ray_normal[3], dist;
- float obimat[4][4];
- float mval[2];
- SculptRaycastData srd;
-
- mval[0] = mouse[0] - vc->ar->winrct.xmin;
- mval[1] = mouse[1] - vc->ar->winrct.ymin;
-
- sculpt_stroke_modifiers_check(C, ss);
-
- viewline(vc->ar, vc->v3d, mval, ray_start, ray_end);
-
- invert_m4_m4(obimat, ss->ob->obmat);
- mul_m4_v3(obimat, ray_start);
- mul_m4_v3(obimat, ray_end);
-
- sub_v3_v3v3(ray_normal, ray_end, ray_start);
- dist= normalize_v3(ray_normal);
-
- srd.ss = vc->obact->sculpt;
- srd.ray_start = ray_start;
- srd.ray_normal = ray_normal;
- srd.dist = dist;
- srd.hit = 0;
- srd.original = (cache)? cache->original: 0;
- BLI_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd,
- ray_start, ray_normal, srd.original);
-
- copy_v3_v3(out, ray_normal);
- mul_v3_fl(out, srd.dist);
- add_v3_v3(out, ray_start);
+static int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float out[3], float mouse[2])
+{
+ ViewContext *vc = paint_stroke_view_context(stroke);
+ SculptSession *ss= vc->obact->paint->sculpt;
+ StrokeCache *cache= ss->cache;
+ int original;
- return srd.hit;
+ sculpt_stroke_modifiers_check(C);
+ original = (cache)? cache->original: 0;
+
+ return paint_stroke_get_location(C, stroke, sculpt_raycast_cb, ss,
+ out, mouse, original);
}
static int sculpt_brush_stroke_init(bContext *C, ReportList *reports)
@@ -3220,7 +2530,7 @@ static int sculpt_brush_stroke_init(bContext *C, ReportList *reports)
Scene *scene= CTX_data_scene(C);
Object *ob= CTX_data_active_object(C);
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
- SculptSession *ss = CTX_data_active_object(C)->sculpt;
+ SculptSession *ss = CTX_data_active_object(C)->paint->sculpt;
Brush *brush = paint_brush(&sd->paint);
if(ob_get_key(ob) && !(ob->shapeflag & OB_SHAPE_LOCK)) {
@@ -3240,7 +2550,7 @@ static int sculpt_brush_stroke_init(bContext *C, ReportList *reports)
return 1;
}
-static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss)
+static void sculpt_restore_mesh(Sculpt *sd, Object *ob)
{
Brush *brush = paint_brush(&sd->paint);
@@ -3249,26 +2559,27 @@ static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss)
(brush->sculpt_tool == SCULPT_TOOL_GRAB && brush_use_size_pressure(brush)) ||
(brush->flag & BRUSH_RESTORE_MESH))
{
+ SculptSession *ss = ob->paint->sculpt;
StrokeCache *cache = ss->cache;
int i;
PBVHNode **nodes;
int n, totnode;
- BLI_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
+ BLI_pbvh_search_gather(ob->paint->pbvh, NULL, NULL, &nodes, &totnode);
- #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+ #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP)
for(n=0; n<totnode; n++) {
- SculptUndoNode *unode;
+ PBVHUndoNode *unode;
- unode= sculpt_undo_get_node(nodes[n]);
+ unode= pbvh_undo_get_node(nodes[n]);
if(unode) {
PBVHVertexIter vd;
- BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
- copy_v3_v3(vd.co, unode->co[vd.i]);
- if(vd.no) VECCOPY(vd.no, unode->no[vd.i])
- else normal_short_to_float_v3(vd.fno, unode->no[vd.i]);
+ BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+ copy_v3_v3(vd.co, pbvh_undo_node_co(unode)[vd.i]);
+ if(vd.no) VECCOPY(vd.no, pbvh_undo_node_no(unode)[vd.i])
+ else normal_short_to_float_v3(vd.fno, pbvh_undo_node_no(unode)[vd.i]);
if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
@@ -3292,7 +2603,7 @@ static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss)
static void sculpt_flush_update(bContext *C)
{
Object *ob = CTX_data_active_object(C);
- SculptSession *ss = ob->sculpt;
+ SculptSession *ss = ob->paint->sculpt;
ARegion *ar = CTX_wm_region(C);
MultiresModifierData *mmd = ss->multires;
@@ -3306,64 +2617,31 @@ static void sculpt_flush_update(bContext *C)
ED_region_tag_redraw(ar);
}
else {
- rcti r;
+ BLI_pbvh_update(ob->paint->pbvh, PBVH_UpdateBB, NULL);
- BLI_pbvh_update(ss->pbvh, PBVH_UpdateBB, NULL);
-
- if (sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r)) {
- //rcti tmp;
-
- r.xmin += ar->winrct.xmin + 1;
- r.xmax += ar->winrct.xmin - 1;
- r.ymin += ar->winrct.ymin + 1;
- r.ymax += ar->winrct.ymin - 1;
-
- //tmp = r;
-
- //if (!BLI_rcti_is_empty(&ss->previous_r))
- // BLI_union_rcti(&r, &ss->previous_r);
-
- //ss->previous_r= tmp;
-
- ss->partial_redraw = 1;
- ED_region_tag_redraw_partial(ar, &r);
- }
+ paint_tag_partial_redraw(C, ob);
}
}
-/* Returns whether the mouse/stylus is over the mesh (1)
- or over the background (0) */
-static int over_mesh(bContext *C, struct wmOperator *op, float x, float y)
+static int sculpt_stroke_test_start(bContext *C, wmOperator *op, wmEvent *event)
{
- float mouse[2], co[3];
-
- mouse[0] = x;
- mouse[1] = y;
-
- return sculpt_stroke_get_location(C, op->customdata, co, mouse);
-}
-
-static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op,
- wmEvent *event)
-{
- /* Don't start the stroke until mouse goes over the mesh */
- if(over_mesh(C, op, event->x, event->y)) {
+ if(paint_stroke_over_mesh(C, op->customdata, event->x, event->y)) {
Object *ob = CTX_data_active_object(C);
- SculptSession *ss = ob->sculpt;
+ SculptSession *ss = ob->paint->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C));
sculpt_update_cache_invariants(C, sd, ss, op, event);
- sculpt_undo_push_begin(sculpt_tool_name(sd));
+ pbvh_undo_push_begin(sculpt_tool_name(sd));
#ifdef _OPENMP
/* If using OpenMP then create a number of threads two times the
number of processor cores.
Justification: Empirically I've found that two threads per
processor gives higher throughput. */
- if (sd->flags & SCULPT_USE_OPENMP) {
+ if (sd->paint.flags & PAINT_USE_OPENMP) {
int num_procs;
num_procs = omp_get_num_procs();
@@ -3377,15 +2655,37 @@ static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op,
return 0;
}
+static void sculpt_stroke_update_symmetry(bContext *C, struct PaintStroke *stroke,
+ char symm, char axis, float angle,
+ int mirror_symmetry_pass, int radial_symmetry_pass,
+ float (*symmetry_rot_mat)[4])
+{
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->paint->sculpt;
+ StrokeCache *cache = ss->cache;
+
+ paint_flip_coord(cache->grab_delta_symmetry, cache->grab_delta, symm);
+ paint_flip_coord(cache->view_normal, cache->true_view_normal, symm);
+
+ copy_m4_m4(cache->symm_rot_mat, symmetry_rot_mat);
+
+ mul_m4_v3(symmetry_rot_mat, cache->grab_delta_symmetry);
+}
+
static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
- SculptSession *ss = CTX_data_active_object(C)->sculpt;
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->paint->sculpt;
+ StrokeCache *cache = ss->cache;
- sculpt_stroke_modifiers_check(C, ss);
+ sculpt_stroke_modifiers_check(C);
sculpt_update_cache_variants(C, sd, ss, stroke, itemptr);
- sculpt_restore_mesh(sd, ss);
- do_symmetrical_brush_actions(sd, ss);
+ sculpt_restore_mesh(sd, ob);
+
+ cache->bstrength= brush_strength(sd, cache, paint_stroke_feather(stroke));
+ paint_stroke_apply_brush(C, stroke, &sd->paint);
+ sculpt_combine_proxies(sd, ob);
/* Cleanup */
sculpt_flush_update(C);
@@ -3394,19 +2694,14 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
static void sculpt_stroke_done(bContext *C, struct PaintStroke *unused)
{
Object *ob= CTX_data_active_object(C);
- SculptSession *ss = ob->sculpt;
+ SculptSession *ss = ob->paint->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
(void)unused;
- // reset values used to draw brush after completing the stroke
- sd->draw_anchored= 0;
- sd->draw_pressure= 0;
- sd->special_rotation= 0;
-
/* Finished */
if(ss->cache) {
- sculpt_stroke_modifiers_check(C, ss);
+ sculpt_stroke_modifiers_check(C);
/* Alt-Smooth */
if (ss->cache->alt_smooth) {
@@ -3420,16 +2715,16 @@ static void sculpt_stroke_done(bContext *C, struct PaintStroke *unused)
sculpt_cache_free(ss->cache);
ss->cache = NULL;
- sculpt_undo_push_end();
+ pbvh_undo_push_end();
- BLI_pbvh_update(ss->pbvh, PBVH_UpdateOriginalBB, NULL);
+ BLI_pbvh_update(ob->paint->pbvh, PBVH_UpdateOriginalBB, NULL);
/* optimization: if there is locked key and active modifiers present in */
/* the stack, keyblock is updating at each step. otherwise we could update */
/* keyblock only when stroke is finished */
- if(ss->kb && !ss->modifiers_active) sculpt_update_keyblock(ss);
+ if(ss->kb && !ss->modifiers_active) sculpt_update_keyblock(ob);
- ss->partial_redraw = 0;
+ ob->paint->partial_redraw = 0;
/* try to avoid calling this, only for e.g. linked duplicates now */
if(((Mesh*)ob->data)->id.us > 1)
@@ -3439,8 +2734,22 @@ static void sculpt_stroke_done(bContext *C, struct PaintStroke *unused)
}
}
+static void sculpt_stroke_set_modifiers(PaintStroke *stroke, Brush *brush)
+{
+ if(ELEM(brush->sculpt_tool, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_ROTATE))
+ paint_stroke_set_modifier_use_original_location(stroke);
+
+ if(ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK))
+ paint_stroke_set_modifier_initial_radius_factor(stroke, 2.0f);
+
+ if(ELEM4(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK,
+ SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE))
+ paint_stroke_set_modifier_use_original_texture_coords(stroke);
+}
+
static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
struct PaintStroke *stroke;
int ignore_background_click;
@@ -3450,15 +2759,18 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *even
stroke = paint_stroke_new(C, sculpt_stroke_get_location,
sculpt_stroke_test_start,
sculpt_stroke_update_step,
+ sculpt_stroke_update_symmetry,
+ sculpt_stroke_brush_action,
sculpt_stroke_done);
op->customdata = stroke;
+ sculpt_stroke_set_modifiers(stroke, paint_brush(&sd->paint));
/* For tablet rotation */
ignore_background_click = RNA_boolean_get(op->ptr,
"ignore_background_click");
- if(ignore_background_click && !over_mesh(C, op, event->x, event->y)) {
+ if(ignore_background_click && !paint_stroke_over_mesh(C, stroke, event->x, event->y)) {
paint_stroke_free(stroke);
return OPERATOR_PASS_THROUGH;
}
@@ -3474,13 +2786,19 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *even
static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
- SculptSession *ss = CTX_data_active_object(C)->sculpt;
+ SculptSession *ss = CTX_data_active_object(C)->paint->sculpt;
if(!sculpt_brush_stroke_init(C, op->reports))
return OPERATOR_CANCELLED;
- op->customdata = paint_stroke_new(C, sculpt_stroke_get_location, sculpt_stroke_test_start,
- sculpt_stroke_update_step, sculpt_stroke_done);
+ op->customdata = paint_stroke_new(C, sculpt_stroke_get_location,
+ sculpt_stroke_test_start,
+ sculpt_stroke_update_step,
+ sculpt_stroke_update_symmetry,
+ sculpt_stroke_brush_action,
+ sculpt_stroke_done);
+
+ sculpt_stroke_set_modifiers(op->customdata, paint_brush(&sd->paint));
sculpt_update_cache_invariants(C, sd, ss, op, NULL);
@@ -3511,7 +2829,7 @@ static void SCULPT_OT_brush_stroke(wmOperatorType *ot)
ot->exec= sculpt_brush_stroke_exec;
ot->poll= sculpt_poll;
- /* flags (sculpt does own undo? (ton) */
+ /* flags */
ot->flag= OPTYPE_BLOCKING;
/* properties */
@@ -3528,11 +2846,114 @@ static void SCULPT_OT_brush_stroke(wmOperatorType *ot)
"Clicks on the background do not start the stroke");
}
+static void sculpt_area_hide_update(bContext *C)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ DerivedMesh *dm;
+
+ dm = mesh_get_derived_final(scene, ob, 0);
+ /* Force the removal of the old pbvh */
+ if(ob->paint->pbvh) {
+ BLI_pbvh_free(ob->paint->pbvh);
+ ob->paint->pbvh = NULL;
+ }
+ dm->getPBVH(NULL, dm);
+
+ /* Update */
+ sculpt_update_mesh_elements(scene, ob, 0);
+}
+
+static int sculpt_area_hide_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ int show_all = RNA_boolean_get(op->ptr, "show_all");
+
+ if(show_all) {
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->paint->sculpt;
+ ARegion *ar= CTX_wm_region(C);
+
+ if(ss->hidden_areas.first) {
+ /* Free all hidden areas */
+ BLI_freelistN(&ss->hidden_areas);
+ sculpt_area_hide_update(C);
+
+ /* Avoid cracks in multires */
+ if(ss->multires) {
+ BLI_pbvh_search_callback(ob->paint->pbvh, NULL, NULL,
+ BLI_pbvh_node_set_flags,
+ SET_INT_IN_POINTER(PBVH_UpdateAll));
+ multires_stitch_grids(ob);
+ }
+
+ ED_region_tag_redraw(ar);
+ }
+ return OPERATOR_FINISHED;
+ }
+ else
+ return WM_border_select_invoke(C, op, event);
+}
+
+static int sculpt_area_hide_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->paint->sculpt;
+ ViewContext vc;
+ bglMats mats;
+ BoundBox bb;
+ rcti rect;
+ HiddenArea *area;
+
+ memset(&mats, 0, sizeof(bglMats));
+ rect.xmin= RNA_int_get(op->ptr, "xmin");
+ rect.ymin= RNA_int_get(op->ptr, "ymin");
+ rect.xmax= RNA_int_get(op->ptr, "xmax");
+ rect.ymax= RNA_int_get(op->ptr, "ymax");
+
+ area = MEM_callocN(sizeof(HiddenArea), "hidden_area");
+ area->hide_inside = RNA_boolean_get(op->ptr, "hide_inside");
+
+ view3d_operator_needs_opengl(C);
+ view3d_set_viewcontext(C, &vc);
+ view3d_get_transformation(vc.ar, vc.rv3d, ob, &mats);
+ view3d_calculate_clipping(&bb, area->clip_planes, &mats, &rect);
+ mul_m4_fl(area->clip_planes, -1.0f);
+
+ BLI_addtail(&ss->hidden_areas, area);
+
+ sculpt_area_hide_update(C);
+
+ return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_area_hide(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Hide Area";
+ ot->idname= "SCULPT_OT_area_hide";
+
+ /* api callbacks */
+ ot->invoke= sculpt_area_hide_invoke;
+ ot->modal= WM_border_select_modal;
+ ot->exec= sculpt_area_hide_exec;
+ ot->poll= sculpt_mode_poll;
+
+ ot->flag= OPTYPE_REGISTER;
+
+ /* rna */
+ RNA_def_boolean(ot->srna, "show_all", 0, "Show All", "");
+ RNA_def_boolean(ot->srna, "hide_inside", 0, "Hide Inside", "");
+ RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
+}
+
/**** Reset the copy of the mesh that is being sculpted on (currently just for the layer brush) ****/
static int sculpt_set_persistent_base(bContext *C, wmOperator *unused)
{
- SculptSession *ss = CTX_data_active_object(C)->sculpt;
+ SculptSession *ss = CTX_data_active_object(C)->paint->sculpt;
(void)unused;
@@ -3562,8 +2983,9 @@ static void SCULPT_OT_set_persistent_base(wmOperatorType *ot)
static void sculpt_init_session(Scene *scene, Object *ob)
{
- ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
- ob->sculpt->ob = ob;
+ create_paintsession(ob);
+
+ ob->paint->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
sculpt_update_mesh_elements(scene, ob, 0);
}
@@ -3573,7 +2995,7 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *unused)
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = CTX_data_tool_settings(C);
Object *ob = CTX_data_active_object(C);
- MultiresModifierData *mmd= sculpt_multires_active(scene, ob);
+ MultiresModifierData *mmd= ED_paint_multires_active(scene, ob);
int flush_recalc= 0;
(void)unused;
@@ -3593,7 +3015,7 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *unused)
/* Leave sculptmode */
ob->mode &= ~OB_MODE_SCULPT;
- free_sculptsession(ob);
+ free_paintsession(ob);
}
else {
/* Enter sculptmode */
@@ -3607,13 +3029,9 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *unused)
ts->sculpt = MEM_callocN(sizeof(Sculpt), "sculpt mode data");
/* Turn on X plane mirror symmetry by default */
- ts->sculpt->flags |= SCULPT_SYMM_X;
+ ts->sculpt->paint.flags |= PAINT_SYMM_X;
}
- /* Create sculpt mode session data */
- if(ob->sculpt)
- free_sculptsession(ob);
-
sculpt_init_session(scene, ob);
paint_init(&ts->sculpt->paint, PAINT_CURSOR_SCULPT);
@@ -3645,5 +3063,6 @@ void ED_operatortypes_sculpt()
WM_operatortype_append(SCULPT_OT_brush_stroke);
WM_operatortype_append(SCULPT_OT_sculptmode_toggle);
WM_operatortype_append(SCULPT_OT_set_persistent_base);
+ WM_operatortype_append(SCULPT_OT_area_hide);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 254876d9b68..f8704f9c69b 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -37,79 +37,18 @@
#include "BLI_pbvh.h"
struct bContext;
-struct Brush;
struct KeyBlock;
-struct Mesh;
-struct MultiresModifierData;
struct Object;
struct Scene;
-struct Sculpt;
-struct SculptStroke;
-
-/* Interface */
-void sculptmode_selectbrush_menu(void);
-void sculptmode_draw_mesh(int);
-void sculpt_paint_brush(char clear);
-void sculpt_stroke_draw(struct SculptStroke *);
-void sculpt_radialcontrol_start(int mode);
-struct MultiresModifierData *sculpt_multires_active(struct Scene *scene, struct Object *ob);
-
-struct Brush *sculptmode_brush(void);
-//void do_symmetrical_brush_actions(struct Sculpt *sd, struct wmOperator *wm, struct BrushAction *a, short *, short *);
-
-void sculpt(Sculpt *sd);
+struct PBVHNode;
+struct SculptUndoNode;
int sculpt_poll(struct bContext *C);
void sculpt_update_mesh_elements(struct Scene *scene, struct Object *ob, int need_fmap);
-/* Stroke */
-struct SculptStroke *sculpt_stroke_new(const int max);
-void sculpt_stroke_free(struct SculptStroke *);
-void sculpt_stroke_add_point(struct SculptStroke *, const short x, const short y);
-void sculpt_stroke_apply(struct Sculpt *sd, struct SculptStroke *);
-void sculpt_stroke_apply_all(struct Sculpt *sd, struct SculptStroke *);
-int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float out[3], float mouse[2]);
-
-/* Partial Mesh Visibility */
-void sculptmode_pmv(int mode);
-
-/* Undo */
-
-typedef struct SculptUndoNode {
- struct SculptUndoNode *next, *prev;
-
- char idname[MAX_ID_NAME]; /* name instead of pointer*/
- void *node; /* only during push, not valid afterwards! */
-
- float (*co)[3];
- short (*no)[3];
- int totvert;
-
- /* non-multires */
- int maxvert; /* to verify if totvert it still the same */
- int *index; /* to restore into right location */
-
- /* multires */
- int maxgrid; /* same for grid */
- int gridsize; /* same for grid */
- int totgrid; /* to restore into right location */
- int *grids; /* to restore into right location */
-
- /* layer brush */
- float *layer_disp;
-
- /* shape keys */
- char *shapeName[32]; /* keep size in sync with keyblock dna */
-} SculptUndoNode;
-
-SculptUndoNode *sculpt_undo_push_node(SculptSession *ss, PBVHNode *node);
-SculptUndoNode *sculpt_undo_get_node(PBVHNode *node);
-void sculpt_undo_push_begin(char *name);
-void sculpt_undo_push_end(void);
-
struct MultiresModifierData *sculpt_multires_active(struct Scene *scene, struct Object *ob);
-int sculpt_modifiers_active(Scene *scene, Object *ob);
-void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]);
+int sculpt_modifiers_active(struct Scene *scene, struct Object *ob);
+void sculpt_vertcos_to_key(struct Object *ob, struct KeyBlock *kb, float (*vertCos)[3]);
void brush_jitter_pos(struct Brush *brush, float *pos, float *jitterpos);
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
deleted file mode 100644
index e92740678fd..00000000000
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * $Id$
- *
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2006 by Nicholas Bishop
- * All rights reserved.
- *
- * The Original Code is: all of this file.
- *
- * Contributor(s): none yet.
- *
- * ***** END GPL LICENSE BLOCK *****
- *
- * Implements the Sculpt Mode tools
- *
- */
-
-#include "MEM_guardedalloc.h"
-
-#include "BLI_math.h"
-#include "BLI_ghash.h"
-#include "BLI_threads.h"
-
-#include "DNA_meshdata_types.h"
-#include "DNA_object_types.h"
-#include "DNA_scene_types.h"
-#include "DNA_mesh_types.h"
-
-#include "BKE_cdderivedmesh.h"
-#include "BKE_context.h"
-#include "BKE_depsgraph.h"
-#include "BKE_modifier.h"
-#include "BKE_multires.h"
-#include "BKE_paint.h"
-#include "BKE_key.h"
-
-#include "WM_api.h"
-#include "WM_types.h"
-
-#include "ED_sculpt.h"
-#include "paint_intern.h"
-#include "sculpt_intern.h"
-
-/************************** Undo *************************/
-
-static void update_cb(PBVHNode *node, void *unused)
-{
- (void)unused;
- BLI_pbvh_node_mark_update(node);
-}
-
-static void sculpt_undo_restore(bContext *C, ListBase *lb)
-{
- Scene *scene = CTX_data_scene(C);
- Object *ob = CTX_data_active_object(C);
- DerivedMesh *dm = mesh_get_derived_final(scene, ob, 0);
- SculptSession *ss = ob->sculpt;
- SculptUndoNode *unode;
- MVert *mvert;
- MultiresModifierData *mmd;
- int *index;
- int i, j, update= 0;
-
- sculpt_update_mesh_elements(scene, ob, 0);
-
- for(unode=lb->first; unode; unode=unode->next) {
- if(!(strcmp(unode->idname, ob->id.name)==0))
- continue;
-
- if(unode->maxvert) {
- char *shapeName= (char*)unode->shapeName;
-
- /* regular mesh restore */
- if(ss->totvert != unode->maxvert)
- continue;
-
- if (ss->kb && strcmp(ss->kb->name, shapeName)) {
- /* shape key has been changed before calling undo operator */
-
- Key *key= ob_get_key(ob);
- KeyBlock *kb= key_get_named_keyblock(key, shapeName);
-
- if (kb) {
- ob->shapenr= BLI_findindex(&key->block, kb) + 1;
- ob->shapeflag|= OB_SHAPE_LOCK;
-
- sculpt_update_mesh_elements(scene, ob, 0);
- WM_event_add_notifier(C, NC_OBJECT|ND_DATA, ob);
- } else {
- /* key has been removed -- skip this undo node */
- continue;
- }
- }
-
- index= unode->index;
- mvert= ss->mvert;
-
- if (ss->kb) {
- float (*vertCos)[3];
- vertCos= key_to_vertcos(ob, ss->kb);
-
- for(i=0; i<unode->totvert; i++)
- swap_v3_v3(vertCos[index[i]], unode->co[i]);
-
- /* propagate new coords to keyblock */
- sculpt_vertcos_to_key(ob, ss->kb, vertCos);
-
- /* pbvh uses it's own mvert array, so coords should be */
- /* propagated to pbvh here */
- BLI_pbvh_apply_vertCos(ss->pbvh, vertCos);
-
- MEM_freeN(vertCos);
- } else {
- for(i=0; i<unode->totvert; i++) {
- swap_v3_v3(mvert[index[i]].co, unode->co[i]);
- mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
- }
- }
- }
- else if(unode->maxgrid && dm->getGridData) {
- /* multires restore */
- DMGridData **grids, *grid;
- float (*co)[3];
- int gridsize;
-
- if(dm->getNumGrids(dm) != unode->maxgrid)
- continue;
- if(dm->getGridSize(dm) != unode->gridsize)
- continue;
-
- grids= dm->getGridData(dm);
- gridsize= dm->getGridSize(dm);
-
- co = unode->co;
- for(j=0; j<unode->totgrid; j++) {
- grid= grids[unode->grids[j]];
-
- for(i=0; i<gridsize*gridsize; i++, co++)
- swap_v3_v3(grid[i].co, co[0]);
- }
- }
-
- update= 1;
- }
-
- if(update) {
- /* we update all nodes still, should be more clever, but also
- needs to work correct when exiting/entering sculpt mode and
- the nodes get recreated, though in that case it could do all */
- BLI_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, NULL);
- BLI_pbvh_update(ss->pbvh, PBVH_UpdateBB|PBVH_UpdateOriginalBB|PBVH_UpdateRedraw, NULL);
-
- if((mmd=sculpt_multires_active(scene, ob)))
- multires_mark_as_modified(ob);
-
- if(ss->modifiers_active || ((Mesh*)ob->data)->id.us > 1)
- DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
- }
-}
-
-static void sculpt_undo_free(ListBase *lb)
-{
- SculptUndoNode *unode;
-
- for(unode=lb->first; unode; unode=unode->next) {
- if(unode->co)
- MEM_freeN(unode->co);
- if(unode->no)
- MEM_freeN(unode->no);
- if(unode->index)
- MEM_freeN(unode->index);
- if(unode->grids)
- MEM_freeN(unode->grids);
- if(unode->layer_disp)
- MEM_freeN(unode->layer_disp);
- }
-}
-
-SculptUndoNode *sculpt_undo_get_node(PBVHNode *node)
-{
- ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH);
- SculptUndoNode *unode;
-
- if(!lb)
- return NULL;
-
- for(unode=lb->first; unode; unode=unode->next)
- if(unode->node == node)
- return unode;
-
- return NULL;
-}
-
-SculptUndoNode *sculpt_undo_push_node(SculptSession *ss, PBVHNode *node)
-{
- ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH);
- Object *ob= ss->ob;
- SculptUndoNode *unode;
- int totvert, allvert, totgrid, maxgrid, gridsize, *grids;
-
- /* list is manipulated by multiple threads, so we lock */
- BLI_lock_thread(LOCK_CUSTOM1);
-
- if((unode= sculpt_undo_get_node(node))) {
- BLI_unlock_thread(LOCK_CUSTOM1);
- return unode;
- }
-
- unode= MEM_callocN(sizeof(SculptUndoNode), "SculptUndoNode");
- strcpy(unode->idname, ob->id.name);
- unode->node= node;
-
- BLI_pbvh_node_num_verts(ss->pbvh, node, &totvert, &allvert);
- BLI_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid,
- &maxgrid, &gridsize, NULL, NULL);
-
- unode->totvert= totvert;
- /* we will use this while sculpting, is mapalloc slow to access then? */
- unode->co= MEM_mapallocN(sizeof(float)*3*allvert, "SculptUndoNode.co");
- unode->no= MEM_mapallocN(sizeof(short)*3*allvert, "SculptUndoNode.no");
- undo_paint_push_count_alloc(UNDO_PAINT_MESH, (sizeof(float)*3 + sizeof(short)*3 + sizeof(int))*allvert);
- BLI_addtail(lb, unode);
-
- if(maxgrid) {
- /* multires */
- unode->maxgrid= maxgrid;
- unode->totgrid= totgrid;
- unode->gridsize= gridsize;
- unode->grids= MEM_mapallocN(sizeof(int)*totgrid, "SculptUndoNode.grids");
- }
- else {
- /* regular mesh */
- unode->maxvert= ss->totvert;
- unode->index= MEM_mapallocN(sizeof(int)*allvert, "SculptUndoNode.index");
- }
-
- BLI_unlock_thread(LOCK_CUSTOM1);
-
- /* copy threaded, hopefully this is the performance critical part */
- {
- PBVHVertexIter vd;
-
- BLI_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) {
- copy_v3_v3(unode->co[vd.i], vd.co);
- if(vd.no) VECCOPY(unode->no[vd.i], vd.no)
- else normal_float_to_short_v3(unode->no[vd.i], vd.fno);
- if(vd.vert_indices) unode->index[vd.i]= vd.vert_indices[vd.i];
- }
- BLI_pbvh_vertex_iter_end;
- }
-
- if(unode->grids)
- memcpy(unode->grids, grids, sizeof(int)*totgrid);
-
- /* store active shape key */
- if(ss->kb) BLI_strncpy((char*)unode->shapeName, ss->kb->name, sizeof(ss->kb->name));
- else unode->shapeName[0]= '\0';
-
- return unode;
-}
-
-void sculpt_undo_push_begin(char *name)
-{
- undo_paint_push_begin(UNDO_PAINT_MESH, name,
- sculpt_undo_restore, sculpt_undo_free);
-}
-
-void sculpt_undo_push_end(void)
-{
- ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH);
- SculptUndoNode *unode;
-
- /* we don't need normals in the undo stack */
- for(unode=lb->first; unode; unode=unode->next) {
- if(unode->no) {
- MEM_freeN(unode->no);
- unode->no= NULL;
- }
-
- if(unode->layer_disp) {
- MEM_freeN(unode->layer_disp);
- unode->layer_disp= NULL;
- }
- }
-
- undo_paint_push_end(UNDO_PAINT_MESH);
-}
diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c
index 372ac976342..2da2b39ff19 100644
--- a/source/blender/editors/space_view3d/drawmesh.c
+++ b/source/blender/editors/space_view3d/drawmesh.c
@@ -647,7 +647,7 @@ void draw_mesh_textured(Scene *scene, View3D *v3d, RegionView3D *rv3d, Object *o
dm->drawMappedFacesTex(dm, draw_em_tf_mapped__set_draw, me->edit_mesh);
} else if(faceselect) {
if(ob->mode & OB_MODE_WEIGHT_PAINT)
- dm->drawMappedFaces(dm, wpaint__setSolidDrawOptions, me, 1);
+ dm->drawMappedFaces(dm, NULL, wpaint__setSolidDrawOptions, me, DM_DRAW_VERTEX_COLORS);
else
dm->drawMappedFacesTex(dm, draw_tface_mapped__set_draw, me);
}
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index 30b12b96ed5..615f5969a52 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -1867,7 +1867,7 @@ static void draw_dm_faces_sel(DerivedMesh *dm, unsigned char *baseCol, unsigned
data.cols[2] = actCol;
data.efa_act = efa_act;
- dm->drawMappedFaces(dm, draw_dm_faces_sel__setDrawOptions, &data, 0);
+ dm->drawMappedFaces(dm, NULL, draw_dm_faces_sel__setDrawOptions, &data, 0);
}
static int draw_dm_creases__setDrawOptions(void *userData, int index)
@@ -2287,7 +2287,7 @@ static void draw_em_fancy(Scene *scene, View3D *v3d, RegionView3D *rv3d, Object
glEnable(GL_LIGHTING);
glFrontFace((ob->transflag&OB_NEG_SCALE)?GL_CW:GL_CCW);
- finalDM->drawMappedFaces(finalDM, draw_em_fancy__setFaceOpts, 0, 0);
+ finalDM->drawMappedFaces(finalDM, NULL, draw_em_fancy__setFaceOpts, NULL, 0);
glFrontFace(GL_CCW);
glDisable(GL_LIGHTING);
@@ -2420,7 +2420,7 @@ static void draw_mesh_object_outline(View3D *v3d, Object *ob, DerivedMesh *dm)
drawFacesSolid() doesn't draw the transparent faces */
if(ob->dtx & OB_DRAWTRANSP) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- dm->drawFacesSolid(dm, NULL, 0, GPU_enable_material);
+ dm->drawFacesSolid(dm, NULL, GPU_enable_material, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
GPU_disable_material();
}
@@ -2449,6 +2449,9 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D
int totvert, totedge, totface;
DispList *dl;
DerivedMesh *dm= mesh_get_derived_final(scene, ob, v3d->customdata_mask);
+ Paint *p;
+ float planes[4][4], (*paint_redraw_planes)[4] = NULL;
+ DMDrawFlags fast_navigate = 0;
if(!dm)
return;
@@ -2460,6 +2463,24 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D
totvert = dm->getNumVerts(dm);
totedge = dm->getNumEdges(dm);
totface = dm->getNumFaces(dm);
+
+ /* setup for fast paint/sculpt drawing */
+ if((ob->mode & (OB_MODE_SCULPT|OB_MODE_VERTEX_PAINT)) &&
+ (p=paint_get_active(scene))) {
+ /* drop down to a low multires level during navigation */
+ fast_navigate = ((p->flags & PAINT_FAST_NAVIGATE) &&
+ (rv3d->rflag & RV3D_NAVIGATING))? DM_DRAW_LOWEST_SUBDIVISION_LEVEL : 0;
+
+ if(ob->paint && ob->paint->partial_redraw) {
+ if(ar->do_draw & RGN_DRAW_PARTIAL) {
+ paint_get_redraw_planes(planes, ar, rv3d, ob);
+ paint_redraw_planes = planes;
+ ob->paint->partial_redraw = 0;
+ }
+
+ }
+ }
+
/* vertexpaint, faceselect wants this, but it doesnt work for shaded? */
if(dt!=OB_SHADED)
@@ -2480,7 +2501,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D
else if(dt==OB_WIRE || totface==0) {
draw_wire = 1; /* draw wire only, no depth buffer stuff */
}
- else if( (ob==OBACT && (ob->mode & OB_MODE_TEXTURE_PAINT || paint_facesel_test(ob))) ||
+ else if( (ob==OBACT && (ob->mode & OB_MODE_TEXTURE_PAINT)) ||
CHECK_OB_DRAWTEXTURE(v3d, dt))
{
int faceselect= (ob==OBACT && paint_facesel_test(ob));
@@ -2517,7 +2538,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D
/* weight paint in solid mode, special case. focus on making the weights clear
* rather then the shading, this is also forced in wire view */
GPU_enable_material(0, NULL);
- dm->drawMappedFaces(dm, wpaint__setSolidDrawOptions, me->mface, 1);
+ dm->drawMappedFaces(dm, NULL, wpaint__setSolidDrawOptions, me->mface, DM_DRAW_VERTEX_COLORS);
bglPolygonOffset(rv3d->dist, 1.0);
glDepthMask(0); // disable write in zbuffer, selected edge wires show better
@@ -2538,9 +2559,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D
}
else {
- Paint *p;
-
- if((v3d->flag&V3D_SELECT_OUTLINE) && ((v3d->flag2 & V3D_RENDER_OVERRIDE)==0) && (base->flag&SELECT) && !draw_wire && !ob->sculpt)
+ if((v3d->flag&V3D_SELECT_OUTLINE) && ((v3d->flag2 & V3D_RENDER_OVERRIDE)==0) && (base->flag&SELECT) && !draw_wire && !ob->paint)
draw_mesh_object_outline(v3d, ob, dm);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, me->flag & ME_TWOSIDED );
@@ -2548,23 +2567,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D
glEnable(GL_LIGHTING);
glFrontFace((ob->transflag&OB_NEG_SCALE)?GL_CW:GL_CCW);
- if(ob->sculpt && (p=paint_get_active(scene))) {
- float planes[4][4];
- float (*fpl)[4] = NULL;
- int fast= (p->flags & PAINT_FAST_NAVIGATE) && (rv3d->rflag & RV3D_NAVIGATING);
-
- if(ob->sculpt->partial_redraw) {
- if(ar->do_draw & RGN_DRAW_PARTIAL) {
- sculpt_get_redraw_planes(planes, ar, rv3d, ob);
- fpl = planes;
- ob->sculpt->partial_redraw = 0;
- }
- }
-
- dm->drawFacesSolid(dm, fpl, fast, GPU_enable_material);
- }
- else
- dm->drawFacesSolid(dm, NULL, 0, GPU_enable_material);
+ dm->drawFacesSolid(dm, paint_redraw_planes, GPU_enable_material, fast_navigate|DM_DRAW_PAINT_MASK);
GPU_disable_material();
@@ -2576,7 +2579,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D
} else {
UI_ThemeColor(TH_WIRE);
}
- if(!ob->sculpt && (v3d->flag2 & V3D_RENDER_OVERRIDE)==0)
+ if(!ob->paint && (v3d->flag2 & V3D_RENDER_OVERRIDE)==0)
dm->drawLooseEdges(dm);
}
}
@@ -2599,18 +2602,32 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
- dm->drawMappedFaces(dm, wpaint__setSolidDrawOptions, me->mface, 1);
+ dm->drawMappedFaces(dm, NULL, wpaint__setSolidDrawOptions, me->mface, DM_DRAW_VERTEX_COLORS);
glDisable(GL_COLOR_MATERIAL);
glDisable(GL_LIGHTING);
GPU_disable_material();
}
else if(ob->mode & (OB_MODE_VERTEX_PAINT|OB_MODE_TEXTURE_PAINT)) {
- if(me->mcol)
- dm->drawMappedFaces(dm, wpaint__setSolidDrawOptions, NULL, 1);
+ if(me->mcol) {
+ MFace *mface = get_mesh(ob)->mface;
+ DMDrawFlags dm_flags = DM_DRAW_PTEX;
+
+ /* XXX - temporary - set up nicer drawing for new vpaint */
+ GPU_enable_material(mface->mat_nr+1, NULL);
+ glEnable(GL_LIGHTING);
+ dm_flags |= fast_navigate;
+ if(me->editflag & ME_EDIT_PTEX)
+ dm_flags |= DM_DRAW_PTEX_TEXELS;
+
+ dm->drawMappedFaces(dm, paint_redraw_planes,
+ wpaint__setSolidDrawOptions, NULL,
+ dm_flags);
+ glDisable(GL_LIGHTING);
+ }
else {
glColor3f(1.0f, 1.0f, 1.0f);
- dm->drawMappedFaces(dm, wpaint__setSolidDrawOptions, NULL, 0);
+ dm->drawMappedFaces(dm, NULL, wpaint__setSolidDrawOptions, NULL, 0);
}
}
else do_draw= 1;
@@ -3076,7 +3093,7 @@ static int drawCurveDerivedMesh(Scene *scene, View3D *v3d, RegionView3D *rv3d, B
if(!glsl) {
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
glEnable(GL_LIGHTING);
- dm->drawFacesSolid(dm, NULL, 0, GPU_enable_material);
+ dm->drawFacesSolid(dm, NULL, GPU_enable_material, 0);
glDisable(GL_LIGHTING);
}
else
@@ -6328,7 +6345,7 @@ static void bbs_mesh_solid_EM(Scene *scene, View3D *v3d, Object *ob, DerivedMesh
cpack(0);
if (facecol) {
- dm->drawMappedFaces(dm, bbs_mesh_solid__setSolidDrawOptions, (void*)(intptr_t) 1, 0);
+ dm->drawMappedFaces(dm, NULL, bbs_mesh_solid__setSolidDrawOptions, (void*)(intptr_t) 1, 0);
if(check_ob_drawface_dot(scene, v3d, ob->dt)) {
glPointSize(UI_GetThemeValuef(TH_FACEDOT_SIZE));
@@ -6339,7 +6356,7 @@ static void bbs_mesh_solid_EM(Scene *scene, View3D *v3d, Object *ob, DerivedMesh
}
} else {
- dm->drawMappedFaces(dm, bbs_mesh_solid__setSolidDrawOptions, (void*) 0, 0);
+ dm->drawMappedFaces(dm, NULL, bbs_mesh_solid__setSolidDrawOptions, (void*) 0, 0);
}
}
@@ -6369,8 +6386,10 @@ static void bbs_mesh_solid(Scene *scene, View3D *v3d, Object *ob)
glColor3ub(0, 0, 0);
- if(face_sel_mode) dm->drawMappedFaces(dm, bbs_mesh_solid_hide__setDrawOpts, me, 0);
- else dm->drawMappedFaces(dm, bbs_mesh_solid__setDrawOpts, me, 0);
+ if(face_sel_mode)
+ dm->drawMappedFaces(dm, NULL, bbs_mesh_solid_hide__setDrawOpts, me, DM_DRAW_BACKBUF_SELECTION);
+ else
+ dm->drawMappedFaces(dm, NULL, bbs_mesh_solid__setDrawOpts, me, DM_DRAW_BACKBUF_SELECTION);
dm->release(dm);
}
@@ -6473,11 +6492,11 @@ static void draw_object_mesh_instance(Scene *scene, View3D *v3d, RegionView3D *r
glEnable(GL_LIGHTING);
if(dm) {
- dm->drawFacesSolid(dm, NULL, 0, GPU_enable_material);
+ dm->drawFacesSolid(dm, NULL, GPU_enable_material, 0);
GPU_end_object_materials();
}
else if(edm)
- edm->drawMappedFaces(edm, NULL, NULL, 0);
+ edm->drawMappedFaces(edm, NULL, NULL, NULL, 0);
glDisable(GL_LIGHTING);
}
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index 24f71b8b1df..c643b47f5d2 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -74,6 +74,7 @@
#include "ED_screen.h"
#include "ED_space_api.h"
#include "ED_screen_types.h"
+#include "ED_sculpt.h"
#include "ED_transform.h"
#include "UI_interface.h"
@@ -2205,6 +2206,37 @@ static void draw_viewport_fps(Scene *scene, ARegion *ar)
BLF_draw_default(22, ar->winy-17, 0.0f, printable);
}
+
+void debug_draw_redraw_area(ARegion *ar)
+{
+ rcti winrct;
+
+ if(G.rt != 444)
+ return;
+
+ /* do nothing if it looks like this isn't a partial redraw */
+ region_scissor_winrct(ar, &winrct);
+ if(ar->drawrct.xmin == winrct.xmin &&
+ ar->drawrct.xmax == winrct.xmax &&
+ ar->drawrct.ymin == winrct.ymin &&
+ ar->drawrct.ymax == winrct.ymax)
+ return;
+
+ /* choose a nice pastel color so that debugging is kept cheerful */
+ glColor3f((rand() / (float)RAND_MAX) * 0.3 + 0.7,
+ (rand() / (float)RAND_MAX) * 0.3 + 0.7,
+ (rand() / (float)RAND_MAX) * 0.3 + 0.7);
+
+ /* draw the redraw area, pull in by 2px so it doesn't get
+ clipped by the scissor */
+ glBegin(GL_LINE_LOOP);
+ glVertex2i(ar->drawrct.xmin-winrct.xmin+2, ar->drawrct.ymin-winrct.ymin+2);
+ glVertex2i(ar->drawrct.xmax-winrct.xmin-2, ar->drawrct.ymin-winrct.ymin+2);
+ glVertex2i(ar->drawrct.xmax-winrct.xmin-2, ar->drawrct.ymax-winrct.ymin-2);
+ glVertex2i(ar->drawrct.xmin-winrct.xmin+2, ar->drawrct.ymax-winrct.ymin-2);
+ glEnd();
+}
+
void view3d_main_area_draw(const bContext *C, ARegion *ar)
{
Scene *scene= CTX_data_scene(C);
@@ -2404,12 +2436,12 @@ void view3d_main_area_draw(const bContext *C, ARegion *ar)
}
ED_region_pixelspace(ar);
-
-// retopo_paint_view_update(v3d);
-// retopo_draw_paint_lines();
-
- /* Draw particle edit brush XXX (removed) */
-
+
+ debug_draw_redraw_area(ar);
+
+ /* would be nicer to have as a region callback, but
+ can't get the right context then? -nicholas */
+ ED_paint_overlay_draw(C, ar);
if(rv3d->persp==RV3D_CAMOB)
drawviewborder(scene, ar, v3d);
diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c
index d7ab8034286..f9b8c6a5d62 100644
--- a/source/blender/editors/space_view3d/view3d_header.c
+++ b/source/blender/editors/space_view3d/view3d_header.c
@@ -471,7 +471,7 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C)
PointerRNA meshptr;
RNA_pointer_create(&ob->id, &RNA_Mesh, ob->data, &meshptr);
- uiItemR(layout, &meshptr, "use_paint_mask", UI_ITEM_R_ICON_ONLY, "", 0);
+ uiItemR(layout, &meshptr, "ptex_edit_mode", UI_ITEM_R_ICON_ONLY, "", 0);
} else {
char *str_menu;
diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c
index e5128ea784a..1d81db70811 100644
--- a/source/blender/editors/util/undo.c
+++ b/source/blender/editors/util/undo.c
@@ -140,7 +140,7 @@ static int ed_undo_step(bContext *C, int step, const char *undoname)
if(!ED_undo_paint_step(C, UNDO_PAINT_IMAGE, step, undoname) && undoname)
do_glob_undo= 1;
}
- else if(obact && obact->mode & OB_MODE_SCULPT) {
+ else if(obact && obact->mode & (OB_MODE_SCULPT|OB_MODE_VERTEX_PAINT)) {
if(!ED_undo_paint_step(C, UNDO_PAINT_MESH, step, undoname) && undoname)
do_glob_undo= 1;
}
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 5de7b0c5281..c3fff768486 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -35,6 +35,7 @@ SET(INC
../makesdna
../makesrna
../../../extern/glew/include
+ ../../../extern/ptex
../../../intern/guardedalloc
../../../intern/smoke/extern
)
diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h
index 6f552e087d7..dcda5ccf391 100644
--- a/source/blender/gpu/GPU_buffers.h
+++ b/source/blender/gpu/GPU_buffers.h
@@ -51,7 +51,9 @@
struct DerivedMesh;
struct DMGridData;
struct GHash;
-struct DMGridData;
+struct GridKey;
+struct PBVH;
+struct PBVHNode;
/* V - vertex, N - normal, T - uv, C - color
F - float, UB - unsigned byte */
@@ -131,19 +133,38 @@ void GPU_buffer_free( GPUBuffer *buffer, GPUBufferPool *pool );
GPUDrawObject *GPU_drawobject_new( struct DerivedMesh *dm );
void GPU_drawobject_free( struct DerivedMesh *dm );
-/* Buffers for non-DerivedMesh drawing */
-void *GPU_build_mesh_buffers(struct GHash *map, struct MVert *mvert,
- struct MFace *mface, int *face_indices,
- int totface, int *vert_indices, int uniq_verts,
- int totvert);
-void GPU_update_mesh_buffers(void *buffers, struct MVert *mvert,
- int *vert_indices, int totvert);
-void *GPU_build_grid_buffers(struct DMGridData **grids,
- int *grid_indices, int totgrid, int gridsize);
-void GPU_update_grid_buffers(void *buffers_v, struct DMGridData **grids,
- int *grid_indices, int totgrid, int gridsize, int smooth);
-void GPU_draw_buffers(void *buffers);
-void GPU_free_buffers(void *buffers);
+/* Buffers for PBVH drawing */
+typedef struct GPU_Buffers GPU_Buffers;
+enum DMDrawFlags;
+GPU_Buffers *GPU_build_mesh_buffers(struct GHash *map, struct MVert *mvert,
+ struct MFace *mface, struct CustomData *vdata,
+ struct CustomData *fdata, int *face_indices,
+ int totface, int *vert_indices, int uniq_verts,
+ int totvert);
+void GPU_update_mesh_vert_buffers(GPU_Buffers *buffers, struct MVert *mvert,
+ int *vert_indices, int totvert);
+void GPU_update_mesh_color_buffers(GPU_Buffers *buffers,
+ struct PBVH *bvh,
+ struct PBVHNode *node,
+ enum DMDrawFlags flags);
+GPU_Buffers *GPU_build_grid_buffers(int gridsize);
+void GPU_update_grid_vert_buffers(GPU_Buffers *buffersb, struct DMGridData **grids,
+ int *grid_indices, int totgrid, int gridsize,
+ struct GridKey *gridkey, int smooth);
+void GPU_update_grid_color_buffers(GPU_Buffers *buffers,
+ struct DMGridData **grids,
+ int *grid_indices, int totgrid,
+ int gridsize, struct GridKey *gridkey,
+ struct CustomData *vdata, enum DMDrawFlags flags);
+void GPU_update_grid_uv_buffer(GPU_Buffers *buffers,
+ struct PBVH *pbvh,
+ struct PBVHNode *node,
+ enum DMDrawFlags flags);
+
+void GPU_update_ptex(GPU_Buffers *buffers, struct PBVH *bvh, struct PBVHNode *node);
+void GPU_draw_buffers(GPU_Buffers *buffers, struct PBVH *bvh,
+ struct PBVHNode *node, enum DMDrawFlags flags);
+void GPU_free_buffers(GPU_Buffers *buffers);
/* called before drawing */
void GPU_vertex_setup( struct DerivedMesh *dm );
diff --git a/source/blender/gpu/SConscript b/source/blender/gpu/SConscript
index 4f1cdc2ce76..e68fd852929 100644
--- a/source/blender/gpu/SConscript
+++ b/source/blender/gpu/SConscript
@@ -7,6 +7,7 @@ defs = [ 'GLEW_STATIC' ]
incs = '../blenlib ../blenkernel ../makesdna ../include'
incs += ' #/extern/glew/include #intern/guardedalloc #intern/smoke/extern ../imbuf .'
+incs += ' #/extern/ptex'
if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'):
incs += ' ' + env['BF_PTHREADS_INC']
diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c
index 5bddb42add2..f2be686d2d4 100644
--- a/source/blender/gpu/intern/gpu_buffers.c
+++ b/source/blender/gpu/intern/gpu_buffers.c
@@ -35,16 +35,21 @@
#include <string.h>
#include "GL/glew.h"
+#include "ptex.h"
#include "MEM_guardedalloc.h"
#include "BLI_ghash.h"
#include "BLI_math.h"
#include "BLI_threads.h"
+#include "BLI_pbvh.h"
#include "DNA_meshdata_types.h"
#include "BKE_DerivedMesh.h"
+#include "BKE_dmgrid.h"
+#include "BKE_paint.h"
+#include "BKE_utildefines.h"
#include "DNA_userdef_types.h"
@@ -398,36 +403,187 @@ void GPU_drawobject_free( DerivedMesh *dm )
dm->drawObject = 0;
}
+/* XXX: don't merge this to trunk
+
+ I'm having graphics problems with GL_SHORT normals;
+ this is on experimental drivers that I'm sure not
+ very many other people are using, so not worth really
+ fixing.
+*/
+#define VBO_FLOATS 1
+
/* Convenience struct for building the VBO. */
typedef struct {
float co[3];
+#ifdef VBO_FLOATS
+ float no[3];
+#else
short no[3];
+#endif
} VertexBufferFormat;
-typedef struct {
+struct GPU_Buffers {
/* opengl buffer handles */
- GLuint vert_buf, index_buf;
+ GLuint vert_buf, index_buf, color_buf, uv_buf;
GLenum index_type;
- /* mesh pointers in case buffer allocation fails */
- MFace *mface;
- MVert *mvert;
- int *face_indices;
- int totface;
+ GLuint *ptex;
+ int totptex;
- /* grid pointers */
- DMGridData **grids;
- int *grid_indices;
- int totgrid;
+ int use_grids;
+ unsigned int tot_tri;
int gridsize;
+};
+
+static void delete_buffer(GLuint *buf)
+{
+ glDeleteBuffersARB(1, buf);
+ *buf = 0;
+}
+
+static void gpu_colors_from_floats(unsigned char out[3],
+ float fcol[3],
+ float mask_strength)
+{
+ CLAMP(mask_strength, 0, 1);
+ /* avoid making the mask output completely black */
+ mask_strength = (1 - mask_strength) * 0.75 + 0.25;
+
+ out[0] = fcol[0] * mask_strength * 255;
+ out[1] = fcol[1] * mask_strength * 255;
+ out[2] = fcol[2] * mask_strength * 255;
+}
+
+/* create or destroy a buffer as needed, return a pointer to the buffer data.
+ if the return value is not null, it must be freed with glUnmapBuffer */
+static void *map_buffer(GPU_Buffers *buffers, GLuint *id, int needed, int totelem, int elemsize)
+{
+ void *data = NULL;
- unsigned int tot_tri, tot_quad;
-} GPU_Buffers;
+ if(needed && !(*id))
+ glGenBuffersARB(1, id);
+ else if(!needed && (*id))
+ delete_buffer(id);
+
+ if(needed && (*id)) {
+ glBindBufferARB(GL_ARRAY_BUFFER_ARB, *id);
+ glBufferDataARB(GL_ARRAY_BUFFER_ARB,
+ elemsize * totelem,
+ NULL, GL_STATIC_DRAW_ARB);
+ data = glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+ if(!data)
+ delete_buffer(id);
+ }
+
+ return data;
+}
-void GPU_update_mesh_buffers(void *buffers_v, MVert *mvert,
- int *vert_indices, int totvert)
+static unsigned char *map_color_buffer(GPU_Buffers *buffers, int have_colors, int totelem)
+{
+ return map_buffer(buffers, &buffers->color_buf, have_colors, totelem, sizeof(char) * 3);
+}
+
+static void *map_uv_buffer(GPU_Buffers *buffers, int need_uvs, int totelem)
+{
+ return map_buffer(buffers, &buffers->uv_buf, need_uvs, totelem, sizeof(float) * 2);
+}
+
+static void color_from_face_corner(CustomData *fdata, int mcol_first_layer,
+ int mcol_totlayer, int cndx, float v[3])
+{
+ int i;
+
+ v[0] = v[1] = v[2] = 1;
+
+ for(i = mcol_first_layer; i < mcol_first_layer+mcol_totlayer; ++i) {
+ MCol *mcol;
+ float col[3];
+
+ mcol = fdata->layers[i].data;
+ mcol += cndx;
+
+ col[0] = mcol->b / 255.0f;
+ col[1] = mcol->g / 255.0f;
+ col[2] = mcol->r / 255.0f;
+
+ interp_v3_v3v3(v, v, col,
+ mcol->a / 255.0f);
+ }
+}
+
+void GPU_update_mesh_color_buffers(GPU_Buffers *buffers, PBVH *bvh,
+ PBVHNode *node, DMDrawFlags flags)
+{
+ unsigned char *color_data;
+ CustomData *vdata, *fdata;
+ MFace *mface;
+ int totvert, *vert_indices;
+ int totface, *face_indices, *face_vert_indices;
+ int mcol_totlayer, pmask_totlayer;
+ int color_needed;
+
+ if(!buffers->vert_buf)
+ return;
+
+ BLI_pbvh_get_customdata(bvh, &vdata, &fdata);
+ BLI_pbvh_node_num_verts(bvh, node, NULL, &totvert);
+ BLI_pbvh_node_get_verts(bvh, node, &vert_indices, NULL);
+ BLI_pbvh_node_get_faces(bvh, node, &mface, &face_indices,
+ &face_vert_indices, &totface);
+
+ mcol_totlayer = CustomData_number_of_layers(fdata, CD_MCOL);
+ pmask_totlayer = CustomData_number_of_layers(vdata, CD_PAINTMASK);
+
+ /* avoid creating color buffer if not needed */
+ color_needed = (flags & DM_DRAW_PAINT_MASK) && pmask_totlayer;
+
+ /* Make a color buffer if there's a mask layer and
+ get rid of any color buffer if there's no mask layer */
+ color_data = map_color_buffer(buffers, color_needed, totvert);
+
+ if(color_data) {
+ int i, j, mcol_first_layer, pmask_first_layer;
+
+ mcol_first_layer = CustomData_get_layer_index(fdata, CD_MCOL);
+ pmask_first_layer = CustomData_get_layer_index(vdata, CD_PAINTMASK);
+
+
+ for(i = 0; i < totface; ++i) {
+ int face_index = face_indices[i];
+ int S = mface[face_index].v4 ? 4 : 3;
+
+ /* for now this arbitrarily chooses one face's corner's
+ mcol to be assigned to a vertex; alternatives would
+ be to combine multiple colors through averaging or
+ draw separate quads so that mcols can abruptly
+ transition from one face to another */
+ for(j = 0; j < S; ++j) {
+ int node_vert_index = face_vert_indices[i*4 + j];
+ float col[3], mask;
+
+ color_from_face_corner(fdata,
+ mcol_first_layer,
+ mcol_totlayer,
+ face_index*4+j, col);
+
+ mask = paint_mask_from_vertex(vdata,
+ vert_indices[node_vert_index],
+ pmask_totlayer,
+ pmask_first_layer);
+
+ gpu_colors_from_floats(color_data +
+ node_vert_index*3,
+ col, mask);
+ }
+ }
+
+ glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
+ }
+}
+
+void GPU_update_mesh_vert_buffers(GPU_Buffers *buffers, MVert *mvert,
+ int *vert_indices, int totvert)
{
- GPU_Buffers *buffers = buffers_v;
VertexBufferFormat *vert_data;
int i;
@@ -445,26 +601,27 @@ void GPU_update_mesh_buffers(void *buffers_v, MVert *mvert,
VertexBufferFormat *out = vert_data + i;
copy_v3_v3(out->co, v->co);
+#ifdef VBO_FLOATS
+ normal_short_to_float_v3(out->no, v->no);
+#else
memcpy(out->no, v->no, sizeof(short) * 3);
+#endif
}
glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
}
- else {
- glDeleteBuffersARB(1, &buffers->vert_buf);
- buffers->vert_buf = 0;
- }
+ else
+ delete_buffer(&buffers->vert_buf);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
}
-
- buffers->mvert = mvert;
}
-void *GPU_build_mesh_buffers(GHash *map, MVert *mvert, MFace *mface,
- int *face_indices, int totface,
- int *vert_indices, int tot_uniq_verts,
- int totvert)
+GPU_Buffers *GPU_build_mesh_buffers(GHash *map, MVert *mvert, MFace *mface,
+ CustomData *vdata, CustomData *fdata,
+ int *face_indices, int totface,
+ int *vert_indices, int tot_uniq_verts,
+ int totvert)
{
GPU_Buffers *buffers;
unsigned short *tri_data;
@@ -516,32 +673,238 @@ void *GPU_build_mesh_buffers(GHash *map, MVert *mvert, MFace *mface,
}
glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB);
}
- else {
- glDeleteBuffersARB(1, &buffers->index_buf);
- buffers->index_buf = 0;
- }
+ else
+ delete_buffer(&buffers->index_buf);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
}
if(buffers->index_buf)
glGenBuffersARB(1, &buffers->vert_buf);
- GPU_update_mesh_buffers(buffers, mvert, vert_indices, totvert);
buffers->tot_tri = tottri;
- buffers->mface = mface;
- buffers->face_indices = face_indices;
- buffers->totface = totface;
-
return buffers;
}
-void GPU_update_grid_buffers(void *buffers_v, DMGridData **grids,
- int *grid_indices, int totgrid, int gridsize, int smooth)
+static void color_from_gridelem(DMGridData *elem, GridKey *gridkey, float col[3])
+{
+ int i;
+
+ col[0] = col[1] = col[2] = 1;
+
+ /* combine colors */
+ for(i = 0; i < gridkey->color; ++i) {
+ float *c = GRIDELEM_COLOR(elem, gridkey)[i];
+
+ /* TODO: check layer enabled/strength */
+
+ /* for now we just combine layers in order
+ interpolating using the alpha component
+ ("order" is ill-defined here since we
+ don't guarantee the order of cdm data) */
+ interp_v3_v3v3(col, col, c, c[3]);
+ }
+}
+
+void GPU_update_grid_color_buffers(GPU_Buffers *buffers, DMGridData **grids, int *grid_indices,
+ int totgrid, int gridsize, GridKey *gridkey, CustomData *vdata,
+ DMDrawFlags flags)
+{
+ unsigned char *color_data;
+ int totvert;
+ int color_needed;
+
+ if(!buffers->vert_buf)
+ return;
+
+ /* avoid creating color buffer if not needed */
+ color_needed = (flags & DM_DRAW_PAINT_MASK) && gridkey->mask;
+
+ totvert= gridsize*gridsize*totgrid;
+ color_data= map_color_buffer(buffers, color_needed, totvert);
+
+ if(color_data) {
+ int i, j;
+
+ for(i = 0; i < totgrid; ++i) {
+ DMGridData *grid= grids[grid_indices[i]];
+
+ for(j = 0; j < gridsize*gridsize; ++j, color_data += 3) {
+ DMGridData *elem = GRIDELEM_AT(grid, j, gridkey);
+ float col[3], mask;
+
+ color_from_gridelem(elem, gridkey, col);
+ mask = paint_mask_from_gridelem(elem, gridkey, vdata);
+
+ gpu_colors_from_floats(color_data, col, mask);
+ }
+ }
+
+ glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
+ }
+}
+
+void GPU_update_grid_uv_buffer(GPU_Buffers *buffers, PBVH *pbvh, PBVHNode *node, DMDrawFlags flags)
+{
+ CustomData *fdata;
+ GridToFace *grid_face_map;
+ MPtex *mptex;
+ float (*uv_data)[2];
+ int *grid_indices, totgrid, gridsize, totvert;
+
+ if(!buffers->vert_buf)
+ return;
+
+ BLI_pbvh_get_customdata(pbvh, NULL, &fdata);
+ mptex = CustomData_get_layer(fdata, CD_MPTEX);
+ grid_face_map = BLI_pbvh_get_grid_face_map(pbvh);
+ BLI_pbvh_node_get_grids(pbvh, node,
+ &grid_indices, &totgrid, NULL, &gridsize,
+ NULL, NULL, NULL);
+
+ /* for now, pbvh is required to give one node per subface in ptex mode */
+ assert(totgrid == 1);
+
+ totvert= gridsize*gridsize*totgrid;
+ uv_data= map_uv_buffer(buffers, (flags & DM_DRAW_PTEX), totvert);
+
+ if(uv_data) {
+ GridToFace *gtf = &grid_face_map[grid_indices[0]];
+ MPtex *pt = &mptex[gtf->face];
+ MPtexSubface *subface = &pt->subfaces[gtf->offset];
+ float u, v, ustep, vstep, vstart = 0;
+ int x, y;
+
+ if(flags & DM_DRAW_PTEX_TEXELS) {
+ ustep = subface->res[0] >> 1;
+ vstep = subface->res[1] >> 1;
+ /* make quad texel pattern appear uniform across all four subfaces */
+ if(gtf->offset % 2)
+ vstart = 0.5;
+ }
+ else {
+ ustep = 1;
+ vstep = 1;
+ }
+
+ ustep /= gridsize - 1.0f;
+ vstep /= gridsize - 1.0f;
+
+ for(y = 0, v = vstart; y < gridsize; ++y, v += vstep) {
+ for(x = 0, u = 0; x < gridsize; ++x, u += ustep, ++uv_data) {
+ uv_data[0][0] = u;
+ uv_data[0][1] = v;
+ }
+ }
+
+ glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
+ }
+}
+
+GLenum gl_type_from_ptex(MPtex *pt)
+{
+ switch(pt->type) {
+ case PTEX_DT_UINT8:
+ return GL_UNSIGNED_BYTE;
+ case PTEX_DT_UINT16:
+ return GL_UNSIGNED_SHORT;
+ case PTEX_DT_FLOAT:
+ return GL_FLOAT;
+ default:
+ return 0;
+ }
+}
+
+GLenum gl_format_from_ptex(MPtex *pt)
{
- GPU_Buffers *buffers = buffers_v;
- DMGridData *vert_data;
+ switch(pt->channels) {
+ case 1:
+ return GL_LUMINANCE;
+ case 2:
+ return GL_LUMINANCE_ALPHA;
+ case 3:
+ return GL_RGB;
+ case 4:
+ return GL_RGBA;
+ default:
+ return 0;
+ }
+}
+
+static void gpu_create_ptex_textures(GPU_Buffers *buffers)
+{
+ buffers->ptex = MEM_callocN(sizeof(GLuint) * buffers->totptex, "PTex IDs");
+ glGenTextures(buffers->totptex, buffers->ptex);
+}
+
+static void gpu_init_ptex_texture(GLuint id, GLenum glformat, GLenum gltype,
+ int ures, int vres, void *data)
+{
+ glBindTexture(GL_TEXTURE_2D, id);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, glformat, ures, vres,
+ 0, glformat, gltype, data);
+}
+
+void GPU_update_ptex(GPU_Buffers *buffers, PBVH *pbvh, PBVHNode *node)
+{
+ int *grid_indices, totgrid;
+ GridToFace *grid_face_map;
+ CustomData *fdata;
+ MPtex *mptex;
+ int i;
+
+ BLI_pbvh_get_customdata(pbvh, NULL, &fdata);
+ grid_face_map = BLI_pbvh_get_grid_face_map(pbvh);
+
+ assert(BLI_pbvh_uses_grids(pbvh));
+
+ BLI_pbvh_node_get_grids(pbvh, node,
+ &grid_indices, &totgrid,
+ NULL, NULL, NULL, NULL, NULL);
+
+ /* TODO: composite multiple layers */
+ mptex = CustomData_get_layer(fdata, CD_MPTEX);
+
+ /* one texture per grid
+ TODO: pack multiple textures together? */
+ if(!buffers->ptex) {
+ buffers->totptex = totgrid;
+ gpu_create_ptex_textures(buffers);
+ }
+
+ for(i = 0; i < totgrid; ++i) {
+ GridToFace *gtf = &grid_face_map[grid_indices[i]];
+ MPtex *pt = &mptex[gtf->face];
+ MPtexSubface *subface = &pt->subfaces[gtf->offset];
+
+ gpu_init_ptex_texture(buffers->ptex[i],
+ gl_format_from_ptex(pt),
+ gl_type_from_ptex(pt),
+ subface->res[0], subface->res[1],
+ subface->data);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+}
+
+typedef struct {
+ float co[3];
+ float no[3];
+} GridVBO;
+
+void GPU_update_grid_vert_buffers(GPU_Buffers *buffers, DMGridData **grids,
+ int *grid_indices, int totgrid, int gridsize, GridKey *gridkey, int smooth)
+{
+ GridVBO *vert_data;
int i, j, k, totvert;
totvert= gridsize*gridsize*totgrid;
@@ -550,13 +913,19 @@ void GPU_update_grid_buffers(void *buffers_v, DMGridData **grids,
if(buffers->vert_buf) {
glBindBufferARB(GL_ARRAY_BUFFER_ARB, buffers->vert_buf);
glBufferDataARB(GL_ARRAY_BUFFER_ARB,
- sizeof(DMGridData) * totvert,
+ sizeof(GridVBO) * totvert,
NULL, GL_STATIC_DRAW_ARB);
vert_data = glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
if(vert_data) {
for(i = 0; i < totgrid; ++i) {
DMGridData *grid= grids[grid_indices[i]];
- memcpy(vert_data, grid, sizeof(DMGridData)*gridsize*gridsize);
+
+ for(j = 0; j < gridsize; ++j) {
+ for(k = 0; k < gridsize; ++k) {
+ copy_v3_v3(vert_data[k + j*gridsize].co, GRIDELEM_CO_AT(grid, k + j*gridsize, gridkey));
+ copy_v3_v3(vert_data[k + j*gridsize].no, GRIDELEM_NO_AT(grid, k + j*gridsize, gridkey));
+ }
+ }
if(!smooth) {
/* for flat shading, recalc normals and set the last vertex of
@@ -564,11 +933,13 @@ void GPU_update_grid_buffers(void *buffers_v, DMGridData **grids,
that is what opengl will use */
for(j = 0; j < gridsize-1; ++j) {
for(k = 0; k < gridsize-1; ++k) {
- normal_quad_v3(vert_data[(j+1)*gridsize + (k+1)].no,
- vert_data[(j+1)*gridsize + k].co,
- vert_data[(j+1)*gridsize + k+1].co,
- vert_data[j*gridsize + k+1].co,
- vert_data[j*gridsize + k].co);
+ float norm[3];
+ normal_quad_v3(norm,
+ GRIDELEM_CO_AT(grid, (j+1)*gridsize + k, gridkey),
+ GRIDELEM_CO_AT(grid, (j+1)*gridsize + k+1, gridkey),
+ GRIDELEM_CO_AT(grid, j*gridsize + k+1, gridkey),
+ GRIDELEM_CO_AT(grid, j*gridsize + k, gridkey));
+ copy_v3_v3(vert_data[(j+1)*gridsize + (k+1)].no, norm);
}
}
}
@@ -577,194 +948,506 @@ void GPU_update_grid_buffers(void *buffers_v, DMGridData **grids,
}
glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
}
- else {
- glDeleteBuffersARB(1, &buffers->vert_buf);
- buffers->vert_buf = 0;
- }
+ else
+ delete_buffer(&buffers->vert_buf);
+
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
}
- buffers->grids = grids;
- buffers->grid_indices = grid_indices;
- buffers->totgrid = totgrid;
- buffers->gridsize = gridsize;
-
//printf("node updated %p\n", buffers_v);
}
-void *GPU_build_grid_buffers(DMGridData **grids,
- int *grid_indices, int totgrid, int gridsize)
+static int gpu_build_grid_ibo(int gridsize)
+{
+ GLuint index_buf;
+ int totndx, use_ushorts, i, j;
+ unsigned short *quads_ushort;
+ unsigned int *quads_uint;
+
+ /* count the number of quads */
+ totndx = (gridsize-1)*(gridsize-1) * 4;
+
+ /* generate index buffer object */
+ if(GL_ARB_vertex_buffer_object && !(U.gameflags & USER_DISABLE_VBO))
+ glGenBuffersARB(1, &index_buf);
+
+ /* bad failure */
+ if(!index_buf)
+ return 0;
+
+ glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, index_buf);
+
+ /* if possible, restrict indices to unsigned shorts to save space */
+ use_ushorts = totndx < USHRT_MAX;
+
+ /* allocate empty buffer data */
+ glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB,
+ (use_ushorts ? sizeof(unsigned short) :
+ sizeof(unsigned int)) * totndx,
+ NULL, GL_STATIC_DRAW_ARB);
+
+ /* map the buffer into memory */
+ quads_ushort = (void*)(quads_uint =
+ glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB,
+ GL_WRITE_ONLY_ARB));
+ if(!quads_ushort) {
+ delete_buffer(&index_buf);
+ glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
+ return 0;
+ }
+
+ /* fill the quad buffer */
+ for(i = 0; i < gridsize-1; ++i) {
+ for(j = 0; j < gridsize-1; ++j) {
+#define IBO_ASSIGN(val) do { \
+ if(use_ushorts) \
+ *(quads_ushort++) = val; \
+ else \
+ *(quads_uint++) = val; \
+ } while(0)
+
+ IBO_ASSIGN(i*gridsize + j+1);
+ IBO_ASSIGN(i*gridsize + j);
+ IBO_ASSIGN((i+1)*gridsize + j);
+ IBO_ASSIGN((i+1)*gridsize + j+1);
+
+#undef IBO_ASSIGN
+ }
+ }
+
+ glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB);
+ glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
+
+ return index_buf;
+}
+
+/* cache grid IBOs, uses reference counting to free them */
+static int gpu_grid_ibo(int gridsize, int release)
+{
+ /* {reference count, buffer id} */
+ static unsigned int grid_buffers[32][2], inited = 0;
+ int lvl, i;
+
+ if(!inited) {
+ memset(grid_buffers, 0, sizeof(int)*32*2);
+ inited = 1;
+ }
+
+ for(i = 0, --gridsize; i < 32; ++i) {
+ if(gridsize & (1 << i)) {
+ lvl = i;
+ break;
+ }
+ }
+
+ if(release) {
+ if(grid_buffers[lvl][0] > 0) {
+ --grid_buffers[lvl][0];
+ if(!grid_buffers[lvl][0])
+ delete_buffer(&grid_buffers[lvl][1]);
+ }
+ else
+ fprintf(stderr, "gpu_grid_ibo: bad reference count\n");
+ }
+ else {
+ ++grid_buffers[lvl][0];
+
+ if(!grid_buffers[lvl][1])
+ grid_buffers[lvl][1] = gpu_build_grid_ibo(gridsize + 1);
+ }
+
+ return grid_buffers[lvl][1];
+}
+
+GPU_Buffers *GPU_build_grid_buffers(int gridsize)
{
GPU_Buffers *buffers;
- int i, j, k, totquad, offset= 0;
buffers = MEM_callocN(sizeof(GPU_Buffers), "GPU_Buffers");
- /* Count the number of quads */
- totquad= (gridsize-1)*(gridsize-1)*totgrid;
+ buffers->index_buf = gpu_grid_ibo(gridsize, 0);
+ buffers->gridsize = gridsize;
+ buffers->use_grids = 1;
- /* Generate index buffer object */
- if(GL_ARB_vertex_buffer_object && !(U.gameflags & USER_DISABLE_VBO))
- glGenBuffersARB(1, &buffers->index_buf);
+ /* Build VBO */
+ if(buffers->index_buf)
+ glGenBuffersARB(1, &buffers->vert_buf);
- if(buffers->index_buf) {
- glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, buffers->index_buf);
+ return buffers;
+}
- if(totquad < USHRT_MAX) {
- unsigned short *quad_data;
+/* create a global texture for visualizing ptex texels */
+static void gpu_bind_ptex_pattern()
+{
+ static int inited = 0;
+ static GLuint ptex_pattern_gltex = 0;
- buffers->index_type = GL_UNSIGNED_SHORT;
- glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB,
- sizeof(unsigned short) * totquad * 4, NULL, GL_STATIC_DRAW_ARB);
+ if(!inited) {
+ #define color1 64, 255, 255
+ #define color2 255, 128, 255
+ unsigned char pattern[2*2*3] = {
+ color1, color2,
+ color2, color1
+ };
+ unsigned char avg[3] = {160, 192, 255};
- /* Fill the quad buffer */
- quad_data = glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
- if(quad_data) {
- for(i = 0; i < totgrid; ++i) {
- for(j = 0; j < gridsize-1; ++j) {
- for(k = 0; k < gridsize-1; ++k) {
- *(quad_data++)= offset + j*gridsize + k+1;
- *(quad_data++)= offset + j*gridsize + k;
- *(quad_data++)= offset + (j+1)*gridsize + k;
- *(quad_data++)= offset + (j+1)*gridsize + k+1;
- }
- }
+ glGenTextures(1, &ptex_pattern_gltex);
- offset += gridsize*gridsize;
- }
- glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB);
+ glBindTexture(GL_TEXTURE_2D, ptex_pattern_gltex);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2,
+ 0, GL_RGB, GL_UNSIGNED_BYTE, pattern);
+
+ glTexImage2D(GL_TEXTURE_2D, 1, GL_RGB, 1, 1,
+ 0, GL_RGB, GL_UNSIGNED_BYTE, avg);
+
+ inited = 1;
+ }
+
+ glBindTexture(GL_TEXTURE_2D, ptex_pattern_gltex);
+}
+
+static void gpu_draw_node_without_vb(GPU_Buffers *buffers, PBVH *pbvh, PBVHNode *node, DMDrawFlags flags)
+{
+ DMGridData **grids;
+ GridKey *gridkey;
+ int *grid_indices, totgrid, gridsize;
+ CustomData *vdata = NULL, *fdata = NULL;
+ MPtex *mptex = NULL;
+ int mcol_first_layer, pmask_first_layer;
+ int i, use_grids, use_color, use_ptex, ptex_edit = 0;
+
+ use_grids = BLI_pbvh_uses_grids(pbvh);
+ BLI_pbvh_get_customdata(pbvh, &vdata, &fdata);
+
+ /* see if color data is needed */
+ if(use_grids) {
+ BLI_pbvh_node_get_grids(pbvh, node, &grid_indices,
+ &totgrid, NULL, &gridsize,
+ &grids, NULL, &gridkey);
+ use_color = gridkey->color || gridkey->mask;
+ if(use_color)
+ BLI_pbvh_get_customdata(pbvh, &vdata, NULL);
+ }
+ else {
+ mcol_first_layer = CustomData_get_layer_index(fdata, CD_MCOL);
+ pmask_first_layer = CustomData_get_layer_index(vdata, CD_PAINTMASK);
+
+ use_color = (flags & DM_DRAW_PAINT_MASK) && pmask_first_layer != -1;
+ }
+
+ if((use_ptex = (buffers->ptex && (flags & DM_DRAW_PTEX)))) {
+ mptex = CustomData_get_layer(fdata, CD_MPTEX);
+ glEnable(GL_TEXTURE_2D);
+ ptex_edit = flags & DM_DRAW_PTEX_TEXELS;
+ }
+
+ if(use_color || use_ptex) {
+ glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
+ glEnable(GL_COLOR_MATERIAL);
+ }
+ if(use_grids) {
+ GridToFace *grid_face_map = BLI_pbvh_get_grid_face_map(pbvh);
+ int x, y;
+
+ BLI_pbvh_node_get_grids(pbvh, node, &grid_indices,
+ &totgrid, NULL, &gridsize,
+ &grids, NULL, &gridkey);
+
+ if(ptex_edit)
+ gpu_bind_ptex_pattern();
+
+ for(i = 0; i < totgrid; ++i) {
+ DMGridData *grid = grids[grid_indices[i]];
+ GridToFace *gtf = &grid_face_map[grid_indices[i]];
+ MPtex *pt;
+ MPtexSubface *subface;
+ float u, v, ustep, vstep, vstart = 0;
+
+ if(mptex) {
+ pt = &mptex[gtf->face];
+ subface = &pt->subfaces[gtf->offset];
+
+ if(subface->flag & MPTEX_SUBFACE_HIDDEN)
+ continue;
+ }
+
+ if(ptex_edit) {
+ ustep = subface->res[0] >> 1;
+ vstep = subface->res[1] >> 1;
}
else {
- glDeleteBuffersARB(1, &buffers->index_buf);
- buffers->index_buf = 0;
+ ustep = 1;
+ vstep = 1;
}
- }
- else {
- unsigned int *quad_data;
- buffers->index_type = GL_UNSIGNED_INT;
- glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB,
- sizeof(unsigned int) * totquad * 4, NULL, GL_STATIC_DRAW_ARB);
+ if(gridsize > 1) {
+ ustep /= gridsize - 1;
+ vstep /= gridsize - 1;
+ }
- /* Fill the quad buffer */
- quad_data = glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+ /* make quad texel pattern appear uniform across all four subfaces */
+ if(ptex_edit && (gtf->offset % 2))
+ vstart = 0.5;
+
+ if(ptex_edit) {
+ if(subface->flag & MPTEX_SUBFACE_SELECTED)
+ glColor3ub(255, 255, 255);
+ else if(subface->flag & MPTEX_SUBFACE_MASKED)
+ glColor3ub(96, 96, 96);
+ else
+ glColor3ub(128, 128, 128);
+ }
+ else if(use_ptex) {
+ if(subface->flag & MPTEX_SUBFACE_MASKED)
+ glColor3ub(128, 128, 128);
+ else
+ glColor3ub(255, 255, 255);
+ glBindTexture(GL_TEXTURE_2D, buffers->ptex[i]);
+ }
- if(quad_data) {
- for(i = 0; i < totgrid; ++i) {
- for(j = 0; j < gridsize-1; ++j) {
- for(k = 0; k < gridsize-1; ++k) {
- *(quad_data++)= offset + j*gridsize + k+1;
- *(quad_data++)= offset + j*gridsize + k;
- *(quad_data++)= offset + (j+1)*gridsize + k;
- *(quad_data++)= offset + (j+1)*gridsize + k+1;
- }
+ for(y = 0, v = vstart; y < gridsize-1; y++, v += vstep) {
+ glBegin(GL_QUAD_STRIP);
+ for(x = 0, u = 0; x < gridsize; x++, u += ustep) {
+ DMGridData *a = GRIDELEM_AT(grid, y*gridsize + x, gridkey);
+ DMGridData *b = GRIDELEM_AT(grid, (y+1)*gridsize + x, gridkey);
+ float acol[3], bcol[3], amask, bmask;
+ unsigned char aglc[3], bglc[3];
+
+ color_from_gridelem(a, gridkey, acol);
+ color_from_gridelem(b, gridkey, bcol);
+ amask = paint_mask_from_gridelem(a, gridkey, vdata);
+ bmask = paint_mask_from_gridelem(b, gridkey, vdata);
+
+ if(use_color) {
+ gpu_colors_from_floats(aglc, acol, amask);
+ gpu_colors_from_floats(bglc, bcol, bmask);
}
- offset += gridsize*gridsize;
+ if(use_color)
+ glColor3ubv(aglc);
+ if(use_ptex)
+ glTexCoord2f(u, v);
+ glNormal3fv(GRIDELEM_NO(a, gridkey));
+ glVertex3fv(GRIDELEM_CO(a, gridkey));
+ if(use_color)
+ glColor3ubv(bglc);
+ if(use_ptex)
+ glTexCoord2f(u, v + vstep);
+ glNormal3fv(GRIDELEM_NO(b, gridkey));
+ glVertex3fv(GRIDELEM_CO(b, gridkey));
}
- glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB);
- }
- else {
- glDeleteBuffersARB(1, &buffers->index_buf);
- buffers->index_buf = 0;
+ glEnd();
}
}
+ }
+ else {
+ MFace *mface;
+ MVert *mvert;
+ int totface, *face_indices;
+ int j, mcol_totlayer, pmask_totlayer;
- glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
+ BLI_pbvh_node_get_verts(pbvh, node, NULL, &mvert);
+ BLI_pbvh_node_get_faces(pbvh, node, &mface, &face_indices, NULL, &totface);
+
+ if(mcol_first_layer)
+ mcol_totlayer = CustomData_number_of_layers(fdata, CD_MCOL);
+
+ if(pmask_first_layer)
+ pmask_totlayer = CustomData_number_of_layers(vdata, CD_PAINTMASK);
+
+ for(i = 0; i < totface; ++i) {
+ int face_index = face_indices[i];
+ MFace *f = mface + face_index;
+ int S = f->v4 ? 4 : 3;
+
+ glBegin((f->v4)? GL_QUADS: GL_TRIANGLES);
+
+ for(j = 0; j < S; ++j) {
+ int vndx = (&f->v1)[j];
+ float col[3], mask;
+ unsigned char glc[3];
+
+ color_from_face_corner(fdata,
+ mcol_first_layer,
+ mcol_totlayer,
+ face_index*4+j, col);
+
+ mask = paint_mask_from_vertex(vdata, vndx,
+ pmask_totlayer,
+ pmask_first_layer);
+
+ gpu_colors_from_floats(glc, col, mask);
+
+ glColor3ubv(glc);
+ glNormal3sv(mvert[vndx].no);
+ glVertex3fv(mvert[vndx].co);
+ }
+
+ glEnd();
+ }
}
- /* Build VBO */
- if(buffers->index_buf)
- glGenBuffersARB(1, &buffers->vert_buf);
+ if(use_color || use_ptex)
+ glDisable(GL_COLOR_MATERIAL);
+}
+
+static void gpu_draw_grids(GPU_Buffers *buffers, PBVH *pbvh, PBVHNode *node, DMDrawFlags flags)
+{
+ int g, totgrid, *grid_indices, gridsize, offset, totndx;
- buffers->tot_quad = totquad;
+ BLI_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid,
+ NULL, &gridsize, NULL, NULL, NULL);
- return buffers;
+ totndx = (gridsize-1)*(gridsize-1) * 4;
+
+ for(g = 0; g < totgrid; ++g) {
+ offset = gridsize * gridsize * g;
+
+ glVertexPointer(3, GL_FLOAT, sizeof(GridVBO),
+ (char*)0 + offset*sizeof(GridVBO) + offsetof(GridVBO, co));
+ glNormalPointer(GL_FLOAT, sizeof(GridVBO),
+ (char*)0 + offset*sizeof(GridVBO) + offsetof(GridVBO, no));
+
+ if(buffers->color_buf) {
+ glBindBufferARB(GL_ARRAY_BUFFER_ARB, buffers->color_buf);
+ glColorPointer(3, GL_UNSIGNED_BYTE, 0, (char*)0 + (offset * 3));
+ }
+
+ if(buffers->uv_buf) {
+ CustomData *fdata;
+ GridToFace *gtf;
+ MPtex *mptex, *pt;
+ MPtexSubface *subface;
+ GridToFace *grid_face_map;
+
+ /* note: code here assumes there's only one
+ ptex subface per node */
+
+ BLI_pbvh_get_customdata(pbvh, NULL, &fdata);
+ mptex = CustomData_get_layer(fdata, CD_MPTEX);
+ grid_face_map = BLI_pbvh_get_grid_face_map(pbvh);
+
+ gtf = &grid_face_map[grid_indices[0]];
+ pt = &mptex[gtf->face];
+ subface = &pt->subfaces[gtf->offset];
+
+ glEnable(GL_TEXTURE_2D);
+ if(flags & DM_DRAW_PTEX_TEXELS) {
+ gpu_bind_ptex_pattern();
+ if(subface->flag & MPTEX_SUBFACE_SELECTED)
+ glColor3ub(255, 255, 255);
+ else if(subface->flag & MPTEX_SUBFACE_MASKED)
+ glColor3ub(96, 96, 96);
+ else
+ glColor3ub(128, 128, 128);
+ }
+ else {
+ glBindTexture(GL_TEXTURE_2D, buffers->ptex[0]);
+
+ if(subface->flag & MPTEX_SUBFACE_MASKED)
+ glColor3ub(128, 128, 128);
+ else
+ glColor3ub(255, 255, 255);
+ }
+
+ if(subface->flag & MPTEX_SUBFACE_HIDDEN)
+ continue;
+
+ glBindBufferARB(GL_ARRAY_BUFFER_ARB, buffers->uv_buf);
+ glTexCoordPointer(2, GL_FLOAT, 0, (void*)0);
+ }
+
+ glDrawElements(GL_QUADS, totndx,
+ (totndx < USHRT_MAX ?
+ GL_UNSIGNED_SHORT : GL_UNSIGNED_INT), 0);
+ }
}
-void GPU_draw_buffers(void *buffers_v)
+void GPU_draw_buffers(GPU_Buffers *buffers, PBVH *pbvh, PBVHNode *node, DMDrawFlags flags)
{
- GPU_Buffers *buffers = buffers_v;
+ glShadeModel((flags & DM_DRAW_FULLY_SMOOTH) ? GL_SMOOTH: GL_FLAT);
if(buffers->vert_buf && buffers->index_buf) {
+ GLboolean use_colmat, colmat;
+
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
+ if(buffers->color_buf)
+ glEnableClientState(GL_COLOR_ARRAY);
+ if(buffers->uv_buf)
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, buffers->vert_buf);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, buffers->index_buf);
- if(buffers->tot_quad) {
- glVertexPointer(3, GL_FLOAT, sizeof(DMGridData), (void*)offsetof(DMGridData, co));
- glNormalPointer(GL_FLOAT, sizeof(DMGridData), (void*)offsetof(DMGridData, no));
+ use_colmat = buffers->color_buf || (flags & DM_DRAW_PTEX);
+ if(use_colmat) {
+ glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
+ glGetBooleanv(GL_COLOR_MATERIAL, &colmat);
+ glEnable(GL_COLOR_MATERIAL);
+ }
- glDrawElements(GL_QUADS, buffers->tot_quad * 4, buffers->index_type, 0);
+ if(buffers->use_grids) {
+ gpu_draw_grids(buffers, pbvh, node, flags);
}
else {
glVertexPointer(3, GL_FLOAT, sizeof(VertexBufferFormat), (void*)offsetof(VertexBufferFormat, co));
+#ifdef VBO_FLOATS
+ glNormalPointer(GL_FLOAT, sizeof(VertexBufferFormat), (void*)offsetof(VertexBufferFormat, no));
+#else
glNormalPointer(GL_SHORT, sizeof(VertexBufferFormat), (void*)offsetof(VertexBufferFormat, no));
+#endif
+ if(buffers->color_buf) {
+ glBindBufferARB(GL_ARRAY_BUFFER_ARB, buffers->color_buf);
+ glColorPointer(3, GL_UNSIGNED_BYTE, 0, (void*)0);
+ }
glDrawElements(GL_TRIANGLES, buffers->tot_tri * 3, buffers->index_type, 0);
}
+ if(use_colmat && !colmat)
+ glDisable(GL_COLOR_MATERIAL);
+
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
- else if(buffers->totface) {
- /* fallback if we are out of memory */
- int i;
-
- for(i = 0; i < buffers->totface; ++i) {
- MFace *f = buffers->mface + buffers->face_indices[i];
-
- glBegin((f->v4)? GL_QUADS: GL_TRIANGLES);
- glNormal3sv(buffers->mvert[f->v1].no);
- glVertex3fv(buffers->mvert[f->v1].co);
- glNormal3sv(buffers->mvert[f->v2].no);
- glVertex3fv(buffers->mvert[f->v2].co);
- glNormal3sv(buffers->mvert[f->v3].no);
- glVertex3fv(buffers->mvert[f->v3].co);
- if(f->v4) {
- glNormal3sv(buffers->mvert[f->v4].no);
- glVertex3fv(buffers->mvert[f->v4].co);
- }
- glEnd();
- }
+ else {
+ /* fallback to regular drawing if out of memory or if VBO is switched off */
+ gpu_draw_node_without_vb(buffers, pbvh, node, flags);
}
- else if(buffers->totgrid) {
- int i, x, y, gridsize = buffers->gridsize;
- for(i = 0; i < buffers->totgrid; ++i) {
- DMGridData *grid = buffers->grids[buffers->grid_indices[i]];
-
- for(y = 0; y < gridsize-1; y++) {
- glBegin(GL_QUAD_STRIP);
- for(x = 0; x < gridsize; x++) {
- DMGridData *a = &grid[y*gridsize + x];
- DMGridData *b = &grid[(y+1)*gridsize + x];
-
- glNormal3fv(a->no);
- glVertex3fv(a->co);
- glNormal3fv(b->no);
- glVertex3fv(b->co);
- }
- glEnd();
- }
- }
- }
+ glShadeModel(GL_FLAT);
}
-void GPU_free_buffers(void *buffers_v)
+void GPU_free_buffers(GPU_Buffers *buffers_v)
{
if(buffers_v) {
GPU_Buffers *buffers = buffers_v;
if(buffers->vert_buf)
glDeleteBuffersARB(1, &buffers->vert_buf);
- if(buffers->index_buf)
- glDeleteBuffersARB(1, &buffers->index_buf);
+ if(buffers->index_buf) {
+ if(buffers->use_grids)
+ gpu_grid_ibo(buffers->gridsize, 1);
+ else
+ glDeleteBuffersARB(1, &buffers->index_buf);
+ }
+ if(buffers->ptex) {
+ glDeleteTextures(buffers->totptex, buffers->ptex);
+ MEM_freeN(buffers->ptex);
+ }
MEM_freeN(buffers);
}
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index 1492319fe22..c34804ad16d 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -126,26 +126,33 @@ typedef struct Brush {
#define BRUSH_PLANE_TRIM (1<<26)
#define BRUSH_FRONTFACE (1<<27)
#define BRUSH_CUSTOM_ICON (1<<28)
+#define BRUSH_MASK (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
+typedef enum {
+ SCULPT_TOOL_DRAW = 1,
+ SCULPT_TOOL_SMOOTH,
+ SCULPT_TOOL_PINCH,
+ SCULPT_TOOL_INFLATE,
+ SCULPT_TOOL_GRAB,
+ SCULPT_TOOL_LAYER,
+ SCULPT_TOOL_FLATTEN,
+ SCULPT_TOOL_CLAY,
+ SCULPT_TOOL_FILL,
+ SCULPT_TOOL_SCRAPE,
+ SCULPT_TOOL_NUDGE,
+ SCULPT_TOOL_THUMB,
+ SCULPT_TOOL_SNAKE_HOOK,
+ SCULPT_TOOL_ROTATE,
+ SCULPT_TOOL_WAX, // XXX: reuse this slot later
+ SCULPT_TOOL_CREASE,
+ SCULPT_TOOL_BLOB,
+ SCULPT_TOOL_CLAY_TUBES
+} SculptTool;
+
+/* Brush.vertexpaint_tool */
+#define VERTEX_PAINT_BLUR 99
+/* The other vpaint tools are in IMB_BlendMode */
/* ImagePaintSettings.tool */
#define PAINT_TOOL_DRAW 0
diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h
index 8908143946a..407efc53cdc 100644
--- a/source/blender/makesdna/DNA_customdata_types.h
+++ b/source/blender/makesdna/DNA_customdata_types.h
@@ -39,7 +39,7 @@ typedef struct CustomDataLayer {
int active_rnd; /* number of the layer to render*/
int active_clone; /* number of the layer to render*/
int active_mask; /* number of the layer to render*/
- char pad[4];
+ float strength; /* for mixing a layer, strength between 0 and 1 */
char name[32]; /* layer name */
void *data; /* layer data */
} CustomDataLayer;
@@ -54,11 +54,28 @@ typedef struct CustomDataExternal {
typedef struct CustomData {
CustomDataLayer *layers; /* CustomDataLayers, ordered by type */
int totlayer, maxlayer; /* number of layers, size of layers array */
- int totsize, pad; /* in editmode, total size of all data layers */
+ int totsize; /* in editmode, total size of all data layers */
+ int pad;
void *pool; /* Bmesh: Memory pool for allocation of blocks */
CustomDataExternal *external; /* external file storing customdata layers */
} CustomData;
+typedef struct CustomDataMultiresLayer {
+ float *griddata; /* storage for n grids, where n is face's number of corners */
+ char name[32]; /* source layer's name, should be kept in sync */
+ int type; /* customdata type */
+ int pad;
+} CustomDataMultiresLayer;
+
+/* some CustomData layers can be subdivided to store multires data, but since
+ multires data is always related to faces and the source layers might be
+ vertex data, the subdivided data is stored separately
+*/
+typedef struct CustomDataMultires {
+ CustomDataMultiresLayer *layers;
+ int totlayer, totelem;
+} CustomDataMultires;
+
/* CustomData.type */
#define CD_MVERT 0
#define CD_MSTICKY 1
@@ -84,7 +101,10 @@ typedef struct CustomData {
#define CD_ID_MCOL 21
#define CD_TEXTURE_MCOL 22
#define CD_CLOTH_ORCO 23
-#define CD_NUMTYPES 24
+#define CD_GRIDS 24
+#define CD_PAINTMASK 25
+#define CD_MPTEX 26
+#define CD_NUMTYPES 27
/* Bits for CustomDataMask */
#define CD_MASK_MVERT (1 << CD_MVERT)
@@ -109,6 +129,9 @@ typedef struct CustomData {
#define CD_MASK_MDISPS (1 << CD_MDISPS)
#define CD_MASK_WEIGHT_MCOL (1 << CD_WEIGHT_MCOL)
#define CD_MASK_CLOTH_ORCO (1 << CD_CLOTH_ORCO)
+#define CD_MASK_GRIDS (1 << CD_GRIDS)
+#define CD_MASK_PAINTMASK (1 << CD_PAINTMASK)
+#define CD_MASK_MPTEX (1 << CD_MPTEX)
/* CustomData.flag */
@@ -123,6 +146,10 @@ typedef struct CustomData {
#define CD_FLAG_EXTERNAL (1<<3)
/* indicates external data is read into memory */
#define CD_FLAG_IN_MEMORY (1<<4)
+/* used to enable/disable a layer */
+#define CD_FLAG_ENABLED (1<<5)
+/* indicates the layer is linked to subdivided data */
+#define CD_FLAG_MULTIRES (1<<6)
/* Limits */
#define MAX_MTFACE 8
diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h
index 4e41309cff4..6843b0aff50 100644
--- a/source/blender/makesdna/DNA_mesh_types.h
+++ b/source/blender/makesdna/DNA_mesh_types.h
@@ -82,7 +82,12 @@ typedef struct Mesh {
* this means the active face must always be selected, this is to keep track
* of the last selected face and is similar to the old active face flag where
* the face does not need to be selected, -1 is inactive */
- int act_face;
+ int act_face;
+
+ /* used for ptex selection */
+ int act_subface;
+
+ int pad;
short texflag, editflag;
@@ -125,6 +130,7 @@ typedef struct TFace {
#define ME_EDIT_PAINT_MASK (1 << 3)
#define ME_EDIT_MIRROR_TOPO (1 << 4) // unused so far
+#define ME_EDIT_PTEX (1 << 5)
/* me->flag */
diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h
index 450c56b0dec..47daa8abea4 100644
--- a/source/blender/makesdna/DNA_meshdata_types.h
+++ b/source/blender/makesdna/DNA_meshdata_types.h
@@ -125,6 +125,33 @@ typedef struct MDisps {
float (*disps)[3];
} MDisps;
+typedef struct MPtexSubface {
+ void *data;
+
+ /* resolution in U and V directions, always a power of two */
+ int res[2];
+
+ int flag, pad;
+} MPtexSubface;
+
+#define MPTEX_SUBFACE_SELECTED 1
+#define MPTEX_SUBFACE_HIDDEN 2
+#define MPTEX_SUBFACE_MASKED 4
+
+typedef struct MPtex {
+ struct MPtexSubface subfaces[4];
+
+ /* equal to face's number of vertices */
+ int totsubface;
+
+ /* enum PtexDataType */
+ char type;
+
+ char channels;
+
+ char pad[2];
+} MPtex;
+
/** Multires structs kept for compatibility with old files **/
typedef struct MultiresCol {
float a, r, g, b;
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 60b4f6a51b6..92ba63b086a 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -53,7 +53,7 @@ struct SoftBody;
struct FluidsimSettings;
struct ParticleSystem;
struct DerivedMesh;
-struct SculptSession;
+struct PaintSession;
struct bGPdata;
@@ -97,7 +97,7 @@ typedef struct Object {
ID id;
struct AnimData *adt; /* animation data (must be immediately after id for utilities to use it) */
- struct SculptSession *sculpt;
+ struct PaintSession *paint;
short type, partype;
int par1, par2, par3; /* can be vertexnrs */
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index cc5d256615e..3d23ed15b85 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -508,12 +508,15 @@ typedef struct TimeMarker {
typedef struct Paint {
struct Brush *brush;
-
+
/* WM Paint cursor */
void *paint_cursor;
unsigned char paint_cursor_col[4];
int flags;
+ int radial_symm[3];
+
+ int pad;
} Paint;
typedef struct ImagePaintSettings {
@@ -566,33 +569,26 @@ typedef struct TransformOrientation {
typedef struct Sculpt {
Paint paint;
- /* For rotating around a pivot point */
- //float pivot[3]; XXX not used?
- int flags;
-
- /* Control tablet input */
- //char tablet_size, tablet_strength; XXX not used?
- int radial_symm[3];
-
- // all this below is used to communicate with the cursor drawing routine
-
/* record movement of mouse so that rake can start at an intuitive angle */
float last_x, last_y;
float last_angle;
- int draw_anchored;
- int anchored_size;
- float anchored_location[3];
- float anchored_initial_mouse[2];
-
- int draw_pressure;
- float pressure_value;
-
- float special_rotation;
-
int pad;
} Sculpt;
+typedef struct PaintOverlay {
+ struct Image *img;
+ int offset[2];
+ /* width, height in pixels */
+ int size[2];
+ float angle;
+ float transp_col[3];
+ float transp_tol;
+ int use;
+ unsigned int gltex;
+ int pad;
+} PaintOverlay;
+
typedef struct VPaint {
Paint paint;
@@ -613,11 +609,12 @@ typedef struct VPaint {
// #define VP_MIRROR_X 32 // depricated in 2.5x use (me->editflag & ME_EDIT_MIRROR_X)
#define VP_ONLYVGROUP 128
-
typedef struct ToolSettings {
VPaint *vpaint; /* vertex paint */
VPaint *wpaint; /* weight paint */
Sculpt *sculpt;
+
+ struct PaintOverlay paint_overlay;
/* Vertex groups */
float vgroup_weight;
@@ -727,11 +724,15 @@ typedef struct ToolSettings {
int auto_normalize; /*auto normalizing mode in wpaint*/
+ /* Sculpt */
short sculpt_paint_settings; /* user preferences for sculpt and paint */
short pad1;
int sculpt_paint_unified_size; /* unified radius of brush in pixels */
float sculpt_paint_unified_unprojected_radius;/* unified radius of brush in Blender units */
float sculpt_paint_unified_alpha; /* unified strength of brush */
+
+ /* Paint */
+ int ptex_ures, ptex_vres;
} ToolSettings;
typedef struct bStats {
@@ -1130,24 +1131,21 @@ typedef struct Scene {
/* Paint.flags */
typedef enum {
- PAINT_SHOW_BRUSH = (1<<0),
- PAINT_FAST_NAVIGATE = (1<<1),
- PAINT_SHOW_BRUSH_ON_SURFACE = (1<<2),
+ PAINT_SYMM_X = (1<<0),
+ PAINT_SYMM_Y = (1<<1),
+ PAINT_SYMM_Z = (1<<2),
+ PAINT_SYMMETRY_FEATHER = (1<<3),
+
+ PAINT_SHOW_BRUSH = (1<<4),
+ PAINT_FAST_NAVIGATE = (1<<5),
+ PAINT_SHOW_BRUSH_ON_SURFACE = (1<<6),
+
+ PAINT_LOCK_X = (1<<7),
+ PAINT_LOCK_Y = (1<<8),
+ PAINT_LOCK_Z = (1<<9),
+ PAINT_USE_OPENMP = (1<<10),
} PaintFlags;
-/* Sculpt.flags */
-/* These can eventually be moved to paint flags? */
-typedef enum SculptFlags {
- SCULPT_SYMM_X = (1<<0),
- SCULPT_SYMM_Y = (1<<1),
- SCULPT_SYMM_Z = (1<<2),
- SCULPT_LOCK_X = (1<<3),
- SCULPT_LOCK_Y = (1<<4),
- SCULPT_LOCK_Z = (1<<5),
- SCULPT_SYMMETRY_FEATHER = (1<<6),
- SCULPT_USE_OPENMP = (1<<7),
-} SculptFlags;
-
/* sculpt_paint_settings */
#define SCULPT_PAINT_USE_UNIFIED_SIZE (1<<0)
#define SCULPT_PAINT_USE_UNIFIED_ALPHA (1<<1)
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index e454dda0ae6..ecad022fb8f 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -300,6 +300,8 @@ extern StructRNA RNA_MeshFloatProperty;
extern StructRNA RNA_MeshFloatPropertyLayer;
extern StructRNA RNA_MeshIntProperty;
extern StructRNA RNA_MeshIntPropertyLayer;
+extern StructRNA RNA_MeshPaintMaskLayer;
+extern StructRNA RNA_MeshPtexLayer;
extern StructRNA RNA_MeshSticky;
extern StructRNA RNA_MeshStringProperty;
extern StructRNA RNA_MeshStringPropertyLayer;
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index 83d40a3802b..1a814c33a33 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -66,13 +66,14 @@ EnumPropertyItem brush_sculpt_tool_items[] = {
EnumPropertyItem brush_vertexpaint_tool_items[] = {
- {0, "MIX", ICON_BRUSH_MIX, "Mix", "Use mix blending mode while painting"},
- {1, "ADD", ICON_BRUSH_ADD, "Add", "Use add blending mode while painting"},
- {2, "SUB", ICON_BRUSH_SUBTRACT, "Subtract", "Use subtract blending mode while painting"},
- {3, "MUL", ICON_BRUSH_MULTIPLY, "Multiply", "Use multiply blending mode while painting"},
- {4, "BLUR", ICON_BRUSH_BLUR, "Blur", "Blur the color with surrounding values"},
- {5, "LIGHTEN", ICON_BRUSH_LIGHTEN, "Lighten", "Use lighten blending mode while painting"},
- {6, "DARKEN", ICON_BRUSH_DARKEN, "Darken", "Use darken blending mode while painting"},
+ {IMB_BLEND_MIX, "MIX", ICON_BRUSH_MIX, "Mix", "Use mix blending mode while painting"},
+ {IMB_BLEND_ADD, "ADD", ICON_BRUSH_ADD, "Add", "Use add blending mode while painting"},
+ {IMB_BLEND_SUB, "SUB", ICON_BRUSH_SUBTRACT, "Subtract", "Use subtract blending mode while painting"},
+ {IMB_BLEND_MUL, "MUL", ICON_BRUSH_MULTIPLY, "Multiply", "Use multiply blending mode while painting"},
+ {IMB_BLEND_LIGHTEN, "LIGHTEN", ICON_BRUSH_LIGHTEN, "Lighten", "Use lighten blending mode while painting"},
+ {IMB_BLEND_DARKEN, "DARKEN", ICON_BRUSH_DARKEN, "Darken", "Use darken blending mode while painting"},
+ {IMB_BLEND_ADD_ALPHA, "ALPHA", ICON_BRUSH_LIGHTEN /* TODO */, "Alpha", "Add or remove alpha from the active layer"},
+ {VERTEX_PAINT_BLUR, "BLUR", ICON_BRUSH_BLUR, "Blur", "Blur the color with surrounding values"},
{0, NULL, 0, NULL, NULL}};
EnumPropertyItem brush_imagepaint_tool_items[] = {
@@ -262,9 +263,10 @@ static EnumPropertyItem *rna_Brush_direction_itemf(bContext *C, PointerRNA *ptr,
{BRUSH_DIR_IN, "DEFLATE", 0, "Deflate", "Subtract effect of brush"},
{0, NULL, 0, NULL, NULL}};
- Brush *me= (Brush*)(ptr->data);
+ Brush *br= (Brush*)(ptr->data);
- switch (me->sculpt_tool) {
+ if(br->ob_mode & OB_MODE_SCULPT) {
+ switch (br->sculpt_tool) {
case SCULPT_TOOL_DRAW:
case SCULPT_TOOL_CREASE:
case SCULPT_TOOL_BLOB:
@@ -289,7 +291,14 @@ static EnumPropertyItem *rna_Brush_direction_itemf(bContext *C, PointerRNA *ptr,
default:
return prop_default_items;
+ }
}
+ else if(br->ob_mode & OB_MODE_VERTEX_PAINT) {
+ if(br->vertexpaint_tool == IMB_BLEND_ADD_ALPHA)
+ return prop_direction_items;
+ }
+
+ return prop_default_items;
}
#else
@@ -622,6 +631,11 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_ACCUMULATE);
RNA_def_property_ui_text(prop, "Accumulate", "Accumulate stroke dabs on top of each other");
RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+ prop= RNA_def_property(srna, "mask", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_MASK);
+ RNA_def_property_ui_text(prop, "Mask", "Mark areas as protected from brush editing");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
prop= RNA_def_property(srna, "use_space_atten", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_SPACE_ATTEN);
diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index db5a2062359..d13e4a7d3a4 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -46,6 +46,7 @@
#include "BKE_depsgraph.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
+#include "BKE_paint.h"
#include "BKE_utildefines.h"
#include "ED_mesh.h" /* XXX Bad level call */
@@ -295,18 +296,21 @@ static CustomData *rna_mesh_fdata(Mesh *me)
return (me->edit_mesh)? &me->edit_mesh->fdata: &me->fdata;
}
-static int rna_CustomDataLayer_length(PointerRNA *ptr, int type)
+static CustomData *rna_mesh_vdata(Mesh *me)
{
- Mesh *me= (Mesh*)ptr->id.data;
- CustomData *fdata= rna_mesh_fdata(me);
- CustomDataLayer *layer;
- int i, length= 0;
+ return (me->edit_mesh)? &me->edit_mesh->vdata: &me->vdata;
+}
- for(layer=fdata->layers, i=0; i<fdata->totlayer; layer++, i++)
- if(layer->type == type)
- length++;
+static int rna_face_CustomDataLayer_count(PointerRNA *ptr, int type)
+{
+ CustomData *fdata= rna_mesh_fdata(ptr->id.data);
+ return CustomData_number_of_layers(fdata, type);
+}
- return length;
+static int rna_vert_CustomDataLayer_count(PointerRNA *ptr, int type)
+{
+ CustomData *vdata= rna_mesh_vdata(ptr->id.data);
+ return CustomData_number_of_layers(vdata, type);
}
static int rna_CustomDataLayer_active_get(PointerRNA *ptr, int type, int render)
@@ -368,7 +372,7 @@ static void rna_Mesh_uv_textures_begin(CollectionPropertyIterator *iter, Pointer
static int rna_Mesh_uv_textures_length(PointerRNA *ptr)
{
- return rna_CustomDataLayer_length(ptr, CD_MTFACE);
+ return rna_face_CustomDataLayer_count(ptr, CD_MTFACE);
}
static PointerRNA rna_Mesh_active_uv_texture_get(PointerRNA *ptr)
@@ -691,7 +695,7 @@ static void rna_Mesh_vertex_colors_begin(CollectionPropertyIterator *iter, Point
static int rna_Mesh_vertex_colors_length(PointerRNA *ptr)
{
- return rna_CustomDataLayer_length(ptr, CD_MCOL);
+ return rna_face_CustomDataLayer_count(ptr, CD_MCOL);
}
static PointerRNA rna_Mesh_active_vertex_color_get(PointerRNA *ptr)
@@ -788,6 +792,190 @@ static void rna_MeshColorLayer_name_set(PointerRNA *ptr, const char *value)
CustomData_set_layer_unique_name(fdata, cdl - fdata->layers);
}
+/* Paint mask layers */
+
+static void rna_MeshPaintMask_update_data(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ /* Hack: sculpt mesh is the active object, so find the active object
+ and compare it's mesh to see if the update is on the sculpt mesh,
+ only then pass the object to the real update function */
+ Object *ob = OBACT;
+
+ if(get_mesh(ob) == ptr->id.data)
+ paint_refresh_mask_display(ob);
+}
+
+static int rna_MeshPaintMaskLayer_data_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION])
+{
+ Mesh *me= (Mesh*)ptr->id.data;
+
+ length[0] = me->totvert;
+
+ return length[0];
+}
+
+static void rna_MeshPaintMaskLayer_data_get(PointerRNA *ptr, float *values)
+{
+ Mesh *me= (Mesh*)ptr->id.data;
+ CustomDataLayer *layer= (CustomDataLayer*)ptr->data;
+
+ memcpy(values, layer->data, me->totvert * sizeof(float));
+}
+
+static void rna_MeshPaintMaskLayer_data_set(PointerRNA *ptr, const float *values)
+{
+ Mesh *me= (Mesh*)ptr->id.data;
+ CustomDataLayer *layer= (CustomDataLayer*)ptr->data;
+
+ memcpy(layer->data, values, me->totvert * sizeof(float));
+}
+
+static char *rna_MeshPaintMask_path(PointerRNA *ptr)
+{
+ CustomDataLayer *layer= (CustomDataLayer*)ptr->data;
+
+ return BLI_sprintfN("paintmask[%d]", (float*)ptr->data - (float*)layer->data);
+}
+
+static int rna_mask_layer_check(CollectionPropertyIterator *iter, void *data)
+{
+ CustomDataLayer *layer= (CustomDataLayer*)data;
+ return (layer->type != CD_PAINTMASK);
+}
+
+
+static void rna_Mesh_mask_layers_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Mesh *me= (Mesh*)ptr->data;
+ CustomData *vdata= rna_mesh_vdata(me);
+ rna_iterator_array_begin(iter, (void*)vdata->layers,
+ sizeof(CustomDataLayer),
+ vdata->totlayer, 0,
+ rna_mask_layer_check);
+}
+
+static int rna_Mesh_mask_layers_length(PointerRNA *ptr)
+{
+ return rna_vert_CustomDataLayer_count(ptr, CD_PAINTMASK);
+}
+
+static int rna_Mesh_active_paint_mask_index_get(PointerRNA *ptr)
+{
+ Mesh *me= (Mesh*)ptr->data;
+ CustomData *vdata= rna_mesh_vdata(me);
+ return CustomData_get_active_layer(vdata, CD_PAINTMASK);
+}
+
+static void rna_Mesh_active_paint_mask_index_set(PointerRNA *ptr, int value)
+{
+ Mesh *me= (Mesh*)ptr->data;
+ CustomData *vdata= rna_mesh_vdata(me);
+
+ CustomData_set_layer_active(vdata, CD_PAINTMASK, value);
+}
+
+static void rna_Mesh_active_paint_mask_index_range(PointerRNA *ptr, int *min, int *max)
+{
+ Mesh *me= (Mesh*)ptr->data;
+ CustomData *vdata= rna_mesh_vdata(me);
+
+ *min= 0;
+ *max= CustomData_number_of_layers(vdata, CD_PAINTMASK)-1;
+ *max= MAX2(0, *max);
+}
+
+/* Ptex layers */
+
+static char *rna_MeshPtexLayer_path(PointerRNA *ptr)
+{
+ CustomDataLayer *layer= (CustomDataLayer*)ptr->data;
+
+ return BLI_sprintfN("ptex[%d]", (MPtex*)ptr->data - (MPtex*)layer->data);
+}
+
+static int rna_ptex_layer_check(CollectionPropertyIterator *iter, void *data)
+{
+ CustomDataLayer *layer= (CustomDataLayer*)data;
+ return (layer->type != CD_MPTEX);
+}
+
+static void rna_Mesh_ptex_layers_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Mesh *me= (Mesh*)ptr->data;
+ CustomData *fdata= rna_mesh_fdata(me);
+ rna_iterator_array_begin(iter, (void*)fdata->layers,
+ sizeof(CustomDataLayer),
+ fdata->totlayer, 0,
+ rna_ptex_layer_check);
+}
+
+static int rna_Mesh_ptex_layers_length(PointerRNA *ptr)
+{
+ return rna_face_CustomDataLayer_count(ptr, CD_MPTEX);
+}
+
+
+static int rna_Mesh_active_ptex_index_get(PointerRNA *ptr)
+{
+ Mesh *me= (Mesh*)ptr->data;
+ CustomData *fdata= rna_mesh_fdata(me);
+ return CustomData_get_active_layer(fdata, CD_MPTEX);
+}
+
+static void rna_Mesh_active_ptex_index_set(PointerRNA *ptr, int value)
+{
+ Mesh *me= (Mesh*)ptr->data;
+ CustomData *fdata= rna_mesh_fdata(me);
+
+ CustomData_set_layer_active(fdata, CD_MPTEX, value);
+}
+
+static void rna_Mesh_active_ptex_index_range(PointerRNA *ptr, int *min, int *max)
+{
+ Mesh *me= (Mesh*)ptr->data;
+ CustomData *fdata= rna_mesh_fdata(me);
+
+ *min= 0;
+ *max= CustomData_number_of_layers(fdata, CD_MPTEX)-1;
+ *max= MAX2(0, *max);
+}
+
+static void rna_MeshPtexLayer_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Mesh *me= (Mesh*)ptr->id.data;
+ CustomDataLayer *layer= (CustomDataLayer*)ptr->data;
+ rna_iterator_array_begin(iter, layer->data, sizeof(MPtex), (me->edit_mesh)? 0: me->totface, 0, NULL);
+}
+
+static int rna_MeshPtexLayer_data_length(PointerRNA *ptr)
+{
+ Mesh *me= (Mesh*)ptr->id.data;
+ return (me->edit_mesh)? 0: me->totface;
+}
+
+static void rna_MeshPtexSubface_resolution_get(PointerRNA *ptr, int *values)
+{
+ MPtexSubface *subface= (MPtexSubface*)ptr->data;
+ values[0] = subface->res[0];
+ values[1] = subface->res[1];
+}
+
+static void rna_MeshPtex_subfaces_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ MPtex *pt= (MPtex*)ptr->data;
+ rna_iterator_array_begin(iter, pt->subfaces,
+ sizeof(MPtexSubface),
+ pt->totsubface, 0, NULL);
+}
+
+static int rna_MeshPtex_subfaces_length(PointerRNA *ptr)
+{
+ MPtex *pt= (MPtex*)ptr->data;
+ return pt->totsubface;
+}
+
+/* Custom property layers */
+
static void rna_MeshFloatPropertyLayer_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
Mesh *me= (Mesh*)ptr->id.data;
@@ -816,7 +1004,7 @@ static void rna_Mesh_float_layers_begin(CollectionPropertyIterator *iter, Pointe
static int rna_Mesh_float_layers_length(PointerRNA *ptr)
{
- return rna_CustomDataLayer_length(ptr, CD_PROP_FLT);
+ return rna_face_CustomDataLayer_count(ptr, CD_PROP_FLT);
}
static int rna_int_layer_check(CollectionPropertyIterator *iter, void *data)
@@ -847,7 +1035,7 @@ static void rna_Mesh_int_layers_begin(CollectionPropertyIterator *iter, PointerR
static int rna_Mesh_int_layers_length(PointerRNA *ptr)
{
- return rna_CustomDataLayer_length(ptr, CD_PROP_INT);
+ return rna_face_CustomDataLayer_count(ptr, CD_PROP_INT);
}
static int rna_string_layer_check(CollectionPropertyIterator *iter, void *data)
@@ -878,7 +1066,7 @@ static void rna_Mesh_string_layers_begin(CollectionPropertyIterator *iter, Point
static int rna_Mesh_string_layers_length(PointerRNA *ptr)
{
- return rna_CustomDataLayer_length(ptr, CD_PROP_STR);
+ return rna_face_CustomDataLayer_count(ptr, CD_PROP_STR);
}
static void rna_TextureFace_image_set(PointerRNA *ptr, PointerRNA value)
@@ -1013,6 +1201,11 @@ static char *rna_MeshColor_path(PointerRNA *ptr)
return rna_CustomDataData_path(ptr, "vertex_colors", CD_MCOL);
}
+static char *rna_MeshPtex_path(PointerRNA *ptr)
+{
+ return rna_CustomDataData_path(ptr, "mesh_ptex", CD_MPTEX);
+}
+
static char *rna_MeshSticky_path(PointerRNA *ptr)
{
return BLI_sprintfN("sticky[%d]", (MSticky*)ptr->data - ((Mesh*)ptr->id.data)->msticky);
@@ -1509,6 +1702,11 @@ static void rna_def_mcol(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Active Render", "Sets the layer as active for rendering");
RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
+ prop= RNA_def_property(srna, "multiresolution", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", CD_FLAG_MULTIRES);
+ RNA_def_property_ui_text(prop, "Multiresolution", "Sets whether the layer can be edited at multiple resolutions");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
prop= RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "MeshColor");
RNA_def_property_ui_text(prop, "Data", "");
@@ -1548,6 +1746,83 @@ static void rna_def_mcol(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
}
+static void rna_def_ptex(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna= RNA_def_struct(brna, "MeshPtexLayer", NULL);
+ RNA_def_struct_sdna(srna, "CustomDataLayer");
+ RNA_def_struct_ui_text(srna, "Mesh Ptex Layer", "");
+ RNA_def_struct_path_func(srna, "rna_MeshPtexLayer_path");
+
+ prop= RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_struct_name_property(srna, prop);
+ RNA_def_property_ui_text(prop, "Name", "");
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
+
+ prop= RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "MeshPtex");
+ RNA_def_property_ui_text(prop, "Data", "");
+ RNA_def_property_collection_funcs(prop, "rna_MeshPtexLayer_data_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", "rna_MeshPtexLayer_data_length", 0, 0);
+
+ srna= RNA_def_struct(brna, "MeshPtex", NULL);
+ RNA_def_struct_sdna(srna, "MPtex");
+ RNA_def_struct_ui_text(srna, "Mesh Ptex", "Variable-resolution multi-channel data");
+ RNA_def_struct_path_func(srna, "rna_MeshPtex_path");
+
+ prop= RNA_def_property(srna, "subfaces", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "MeshPtexSubface");
+ RNA_def_property_ui_text(prop, "Subfaces", "");
+ RNA_def_property_collection_funcs(prop, "rna_MeshPtex_subfaces_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get", "rna_MeshPtex_subfaces_length", 0, 0);
+
+ srna= RNA_def_struct(brna, "MeshPtexSubface", NULL);
+ RNA_def_struct_sdna(srna, "MPtexSubface");
+ RNA_def_struct_ui_text(srna, "Mesh Ptex Subface", "");
+ RNA_def_struct_path_func(srna, "rna_MeshPtex_path");
+
+ prop= RNA_def_property(srna, "resolution", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "res");
+ RNA_def_property_array(prop, 2);
+ RNA_def_property_range(prop, 0, 31);
+ RNA_def_property_int_funcs(prop, "rna_MeshPtexSubface_resolution_get", NULL, NULL);
+ RNA_def_property_ui_text(prop, "Resolution", "");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+}
+
+static void rna_def_paintmask(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna= RNA_def_struct(brna, "MeshPaintMaskLayer", NULL);
+ RNA_def_struct_sdna(srna, "CustomDataLayer");
+ RNA_def_struct_ui_text(srna, "Mesh Paint Mask Layer", "");
+ RNA_def_struct_path_func(srna, "rna_MeshPaintMaskLayer_path");
+
+ prop= RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_struct_name_property(srna, prop);
+ RNA_def_property_ui_text(prop, "Name", "");
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
+
+ prop= RNA_def_float_array(srna, "data", 1, NULL, 0.0f, 1.0f, "Vertex Paint Masks", "", 0.0f, 1.0f);
+ RNA_def_property_flag(prop, PROP_DYNAMIC);
+ RNA_def_property_dynamic_array_funcs(prop, "rna_MeshPaintMaskLayer_data_get_length");
+ RNA_def_property_float_funcs(prop, "rna_MeshPaintMaskLayer_data_get",
+ "rna_MeshPaintMaskLayer_data_set", NULL);
+ RNA_def_struct_path_func(srna, "rna_MeshPaintMask_path");
+
+ prop= RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Strength", "Opacity of the paint mask");
+ RNA_def_property_update(prop, NC_OBJECT|ND_DRAW, "rna_MeshPaintMask_update_data");
+
+ prop= RNA_def_property(srna, "enabled", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", CD_FLAG_ENABLED);
+ RNA_def_property_ui_text(prop, "Enabled", "Enable the mask for display and affecting brushes.");
+ RNA_def_property_update(prop, NC_OBJECT|ND_DRAW, "rna_MeshPaintMask_update_data");
+}
+
static void rna_def_mproperties(BlenderRNA *brna)
{
StructRNA *srna;
@@ -1688,6 +1963,10 @@ static void rna_def_mesh_faces(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_property_int_sdna(prop, NULL, "act_face");
RNA_def_property_ui_text(prop, "Active Face", "The active face for this mesh");
+ prop= RNA_def_property(srna, "active_subface", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "act_subface");
+ RNA_def_property_ui_text(prop, "Active Subface", "The active subface of the active face");
+
prop= RNA_def_property(srna, "active_tface", PROP_POINTER, PROP_UNSIGNED);
RNA_def_property_struct_type(prop, "MeshTextureFace");
RNA_def_property_pointer_funcs(prop, "rna_Mesh_active_mtface_get", NULL, NULL, NULL);
@@ -1856,6 +2135,34 @@ static void rna_def_mesh(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Vertex Colors", "");
rna_def_vertex_colors(brna, prop);
+ prop= RNA_def_property(srna, "paint_mask_layers", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "vdata.layers", "vdata.totlayer");
+ RNA_def_property_collection_funcs(prop, "rna_Mesh_mask_layers_begin",
+ 0, 0, 0, "rna_Mesh_mask_layers_length", 0, 0);
+ RNA_def_property_struct_type(prop, "MeshPaintMaskLayer");
+ RNA_def_property_ui_text(prop, "Paint Mask Layers", "Masked areas are not affected by paint brushes.");
+
+ prop= RNA_def_property(srna, "active_paint_mask_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_funcs(prop, "rna_Mesh_active_paint_mask_index_get",
+ "rna_Mesh_active_paint_mask_index_set",
+ "rna_Mesh_active_paint_mask_index_range");
+ RNA_def_property_ui_text(prop, "Active Paint Mask Index", "Active paint mask layer index");
+
+ /* Ptex */
+ prop= RNA_def_property(srna, "ptex_layers", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "fdata.layers", "fdata.totlayer");
+ RNA_def_property_collection_funcs(prop, "rna_Mesh_ptex_layers_begin",
+ 0, 0, 0, "rna_Mesh_ptex_layers_length", 0, 0);
+ RNA_def_property_struct_type(prop, "MeshPtexLayer");
+ RNA_def_property_ui_text(prop, "Ptex Layers", "Variable-resolution mesh colors");
+
+ prop= RNA_def_property(srna, "active_ptex_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_funcs(prop, "rna_Mesh_active_ptex_index_get",
+ "rna_Mesh_active_ptex_index_set",
+ "rna_Mesh_active_ptex_index_range");
+ RNA_def_property_ui_text(prop, "Active Ptex Index", "Active ptex layer index");
+
+ /* float layers */
prop= RNA_def_property(srna, "layers_float", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "fdata.layers", "fdata.totlayer");
RNA_def_property_collection_funcs(prop, "rna_Mesh_float_layers_begin", 0, 0, 0, "rna_Mesh_float_layers_length", 0, 0);
@@ -2015,7 +2322,13 @@ static void rna_def_mesh(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "editflag", ME_EDIT_PAINT_MASK);
RNA_def_property_ui_text(prop, "Paint Mask", "Face selection masking for painting");
RNA_def_property_ui_icon(prop, ICON_FACESEL_HLT, 0);
- RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
+
+ prop= RNA_def_property(srna, "ptex_edit_mode", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "editflag", ME_EDIT_PTEX);
+ RNA_def_property_ui_text(prop, "Ptex Edit Mode", "Edit faces' ptex resolution");
+ RNA_def_property_ui_icon(prop, ICON_FACESEL_HLT, 0);
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
/* readonly editmesh info - use for extrude menu */
@@ -2050,6 +2363,8 @@ void RNA_def_mesh(BlenderRNA *brna)
rna_def_mtface(brna);
rna_def_msticky(brna);
rna_def_mcol(brna);
+ rna_def_ptex(brna);
+ rna_def_paintmask(brna);
rna_def_mproperties(brna);
}
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index e83a17cc277..a31cbdd20c7 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -616,9 +616,9 @@ static void rna_def_modifier_multires(BlenderRNA *brna)
RNA_def_property_int_funcs(prop, NULL, NULL, "rna_MultiresModifier_level_range");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
- prop= RNA_def_property(srna, "sculpt_levels", PROP_INT, PROP_UNSIGNED);
+ prop= RNA_def_property(srna, "edit_levels", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "sculptlvl");
- RNA_def_property_ui_text(prop, "Sculpt Levels", "Number of subdivisions to use in sculpt mode");
+ RNA_def_property_ui_text(prop, "Edit Levels", "Number of subdivisions to use in sculpt and paint modes");
RNA_def_property_int_funcs(prop, NULL, NULL, "rna_MultiresModifier_level_range");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 1bcaf490c8d..b21653d95b1 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -35,6 +35,11 @@
#include "DNA_scene_types.h"
#include "DNA_userdef_types.h"
+#include "BLI_winstuff.h"
+
+#include <stdio.h>
+#include <string.h>
+
/* Include for Bake Options */
#include "RE_pipeline.h"
@@ -169,6 +174,7 @@ EnumPropertyItem image_type_items[] = {
#include "BLI_threads.h"
#include "BLI_editVert.h"
#include "BLI_blenlib.h"
+#include "BLI_winstuff.h"
#include "WM_api.h"
@@ -978,6 +984,33 @@ static void rna_def_transform_orientation(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SPACE|ND_SPACE_VIEW3D, NULL);
}
+static EnumPropertyItem *ptex_resolution_items()
+{
+#define PTEX_TOP_RES 20
+ static EnumPropertyItem items[PTEX_TOP_RES+1];
+ static char identifiers[PTEX_TOP_RES][14];
+ static int inited = 0;
+
+ if(!inited) {
+ int i;
+
+ memset(items, 0, sizeof(EnumPropertyItem) * (PTEX_TOP_RES+1));
+
+ for(i = 0; i < PTEX_TOP_RES; ++i) {
+ int x = 1 << i;
+ items[i].value = x;
+ items[i].identifier = identifiers[i];
+ items[i].name = identifiers[i] + 3;
+ snprintf(identifiers[i], 14, "RES%d", x);
+ }
+
+ inited = 1;
+ }
+
+ return items;
+#undef PTEX_TOP_RES
+}
+
static void rna_def_tool_settings(BlenderRNA *brna)
{
StructRNA *srna;
@@ -1045,6 +1078,10 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "particle");
RNA_def_property_ui_text(prop, "Particle Edit", "");
+ prop= RNA_def_property(srna, "paint_overlay", PROP_POINTER, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Paint Overlay", "Image overlay for paint");
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+
/* Transform */
prop= RNA_def_property(srna, "proportional_edit", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "proportional");
@@ -1235,6 +1272,18 @@ static void rna_def_tool_settings(BlenderRNA *brna)
prop= RNA_def_property(srna, "sculpt_paint_use_unified_strength", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "sculpt_paint_settings", SCULPT_PAINT_USE_UNIFIED_ALPHA);
RNA_def_property_ui_text(prop, "Sculpt/Paint Use Unified Strength", "Instead of per brush strength, the strength is shared across brushes");
+
+ /* Ptex */
+
+ prop= RNA_def_property(srna, "ptex_u_resolution", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "ptex_ures");
+ RNA_def_property_enum_items(prop, ptex_resolution_items());
+ RNA_def_property_ui_text(prop, "Ptex U Resolution", "");
+
+ prop= RNA_def_property(srna, "ptex_v_resolution", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "ptex_vres");
+ RNA_def_property_enum_items(prop, ptex_resolution_items());
+ RNA_def_property_ui_text(prop, "Ptex V Resolution", "");
}
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index 3b83ec2d3ad..6cd39cf78a9 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -52,11 +52,15 @@ static EnumPropertyItem particle_edit_hair_brush_items[] = {
#include "MEM_guardedalloc.h"
#include "BKE_context.h"
+#include "BKE_image.h"
#include "BKE_pointcache.h"
#include "BKE_particle.h"
#include "BKE_depsgraph.h"
+#include "IMB_imbuf_types.h"
+
#include "ED_particle.h"
+#include "ED_sculpt.h"
static EnumPropertyItem particle_edit_disconnected_hair_brush_items[] = {
{PE_BRUSH_NONE, "NONE", 0, "None", "Don't use any brush"},
@@ -74,6 +78,29 @@ static EnumPropertyItem particle_edit_cache_brush_items[] = {
{PE_BRUSH_LENGTH, "LENGTH", 0, "Length", "Make paths longer or shorter"},
{0, NULL, 0, NULL, NULL}};
+static void rna_PaintOverlay_image_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ PaintOverlay *overlay = &scene->toolsettings->paint_overlay;
+ ED_paint_update_overlay(overlay);
+}
+
+static void rna_PaintOverlay_image_update_new(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ PaintOverlay *overlay = &scene->toolsettings->paint_overlay;
+
+ /* make sure size is reasonable */
+ if(overlay->size[0] < 4 || overlay->size[1] < 4) {
+ ImBuf *ibuf;
+
+ ibuf = BKE_image_get_ibuf(overlay->img, NULL);
+
+ overlay->size[0] = ibuf->x;
+ overlay->size[1] = ibuf->y;
+ }
+
+ ED_paint_update_overlay(overlay);
+}
+
static PointerRNA rna_ParticleEdit_brush_get(PointerRNA *ptr)
{
ParticleEditSettings *pset= (ParticleEditSettings*)ptr->data;
@@ -181,6 +208,18 @@ static void rna_def_paint(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Brush", "Active Brush");
RNA_def_property_update(prop, NC_BRUSH|NA_EDITED, NULL);
+ prop= RNA_def_property(srna, "symmetry_x", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_SYMM_X);
+ RNA_def_property_ui_text(prop, "Symmetry X", "Mirror brush across the X axis");
+
+ prop= RNA_def_property(srna, "symmetry_y", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_SYMM_Y);
+ RNA_def_property_ui_text(prop, "Symmetry Y", "Mirror brush across the Y axis");
+
+ prop= RNA_def_property(srna, "symmetry_z", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_SYMM_Z);
+ RNA_def_property_ui_text(prop, "Symmetry Z", "Mirror brush across the Z axis");
+
prop= RNA_def_property(srna, "show_brush", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_SHOW_BRUSH);
RNA_def_property_ui_text(prop, "Show Brush", "");
@@ -192,15 +231,6 @@ 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");
-}
-
-static void rna_def_sculpt(BlenderRNA *brna)
-{
- StructRNA *srna;
- PropertyRNA *prop;
-
- srna= RNA_def_struct(brna, "Sculpt", "Paint");
- RNA_def_struct_ui_text(srna, "Sculpt", "");
prop= RNA_def_property(srna, "radial_symmetry", PROP_INT, PROP_XYZ);
RNA_def_property_int_sdna(prop, NULL, "radial_symm");
@@ -210,38 +240,46 @@ static void rna_def_sculpt(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Radial Symmetry Count X Axis", "Number of times to copy strokes across the surface");
prop= RNA_def_property(srna, "use_symmetry_x", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_SYMM_X);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_SYMM_X);
RNA_def_property_ui_text(prop, "Symmetry X", "Mirror brush across the X axis");
prop= RNA_def_property(srna, "use_symmetry_y", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_SYMM_Y);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_SYMM_Y);
RNA_def_property_ui_text(prop, "Symmetry Y", "Mirror brush across the Y axis");
prop= RNA_def_property(srna, "use_symmetry_z", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_SYMM_Z);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_SYMM_Z);
RNA_def_property_ui_text(prop, "Symmetry Z", "Mirror brush across the Z axis");
prop= RNA_def_property(srna, "lock_x", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_LOCK_X);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_LOCK_X);
RNA_def_property_ui_text(prop, "Lock X", "Disallow changes to the X axis of vertices");
prop= RNA_def_property(srna, "lock_y", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_LOCK_Y);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_LOCK_Y);
RNA_def_property_ui_text(prop, "Lock Y", "Disallow changes to the Y axis of vertices");
prop= RNA_def_property(srna, "lock_z", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_LOCK_Z);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_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");
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_SYMMETRY_FEATHER);
+ RNA_def_property_ui_text(prop, "Symmetry Feathering", "Reduce the strength of the brush where it overlaps symmetrical dabs");
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_boolean_sdna(prop, NULL, "flags", PAINT_USE_OPENMP);
RNA_def_property_ui_text(prop, "Use OpenMP", "Take advantage of multiple CPU cores to improve sculpting performance");
}
+static void rna_def_sculpt(BlenderRNA *brna)
+{
+ StructRNA *srna;
+
+ srna= RNA_def_struct(brna, "Sculpt", "Paint");
+ RNA_def_struct_ui_text(srna, "Sculpt", "");
+}
+
static void rna_def_vertex_paint(BlenderRNA *brna)
{
StructRNA *srna;
@@ -496,6 +534,39 @@ static void rna_def_particle_edit(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Curve", "");
}
+static void rna_def_paint_overlay(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna= RNA_def_struct(brna, "PaintOverlay", NULL);
+ RNA_def_struct_sdna(srna, "PaintOverlay");
+ RNA_def_struct_ui_text(srna, "Paint Overlay", "Properties of paint overlay");
+
+ prop= RNA_def_property(srna, "enabled", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use", 1);
+ RNA_def_property_ui_text(prop, "Enable Overlay", "Overlay source image in viewport and sample brush color from it");
+ RNA_def_property_update(prop, NC_SPACE|ND_SPACE_VIEW3D|NA_EDITED, NULL);
+
+ prop= RNA_def_property(srna, "image", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "img");
+ RNA_def_property_ui_text(prop, "Image", "Sample image for brush color");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, ND_SPACE_VIEW3D|NA_EDITED, "rna_PaintOverlay_image_update_new");
+
+ prop= RNA_def_property(srna, "transparency_color", PROP_FLOAT, PROP_COLOR);
+ RNA_def_property_float_sdna(prop, NULL, "transp_col");
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_ui_text(prop, "Transparency Color", "");
+ RNA_def_property_update(prop, NC_SPACE|ND_SPACE_VIEW3D|NA_EDITED, "rna_PaintOverlay_image_update");
+
+ prop= RNA_def_property(srna, "transparency_tolerance", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "transp_tol");
+ RNA_def_property_ui_range(prop, 0, 255, 100, 1);
+ RNA_def_property_ui_text(prop, "Tolerance", "");
+ RNA_def_property_update(prop, NC_SPACE|ND_SPACE_VIEW3D|NA_EDITED, "rna_PaintOverlay_image_update");
+}
+
void RNA_def_sculpt_paint(BlenderRNA *brna)
{
rna_def_paint(brna);
@@ -503,6 +574,7 @@ void RNA_def_sculpt_paint(BlenderRNA *brna)
rna_def_vertex_paint(brna);
rna_def_image_paint(brna);
rna_def_particle_edit(brna);
+ rna_def_paint_overlay(brna);
}
#endif
diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c
index 758809ff7ec..4c05f139b71 100644
--- a/source/blender/modifiers/intern/MOD_multires.c
+++ b/source/blender/modifiers/intern/MOD_multires.c
@@ -64,8 +64,6 @@ static void copyData(ModifierData *md, ModifierData *target)
static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *dm,
int useRenderParams, int isFinalCalc)
{
- SculptSession *ss= ob->sculpt;
- int sculpting= (ob->mode & OB_MODE_SCULPT) && ss;
MultiresModifierData *mmd = (MultiresModifierData*)md;
DerivedMesh *result;
Mesh *me= (Mesh*)ob->data;
@@ -77,21 +75,17 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *dm,
}
}
- result = multires_dm_create_from_derived(mmd, 0, dm, ob, useRenderParams, isFinalCalc);
+ result = multires_dm_create_from_derived(mmd, 0, dm, ob, NULL, useRenderParams, isFinalCalc);
if(result == dm)
return dm;
if(useRenderParams || !isFinalCalc) {
DerivedMesh *cddm= CDDM_copy(result);
+
result->release(result);
result= cddm;
}
- else if(sculpting) {
- /* would be created on the fly too, just nicer this
- way on first stroke after e.g. switching levels */
- ss->pbvh= result->getPBVH(ob, result);
- }
return result;
}
diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c
index cd3657b9674..c314e6b173c 100644
--- a/source/blender/modifiers/intern/MOD_subsurf.c
+++ b/source/blender/modifiers/intern/MOD_subsurf.c
@@ -90,7 +90,7 @@ static DerivedMesh *applyModifier(
SubsurfModifierData *smd = (SubsurfModifierData*) md;
DerivedMesh *result;
- result = subsurf_make_derived_from_derived(derivedData, smd,
+ result = subsurf_make_derived_from_derived(derivedData, smd, NULL,
useRenderParams, NULL, isFinalCalc, 0);
if(useRenderParams || !isFinalCalc) {
@@ -109,7 +109,7 @@ static DerivedMesh *applyModifierEM(
SubsurfModifierData *smd = (SubsurfModifierData*) md;
DerivedMesh *result;
- result = subsurf_make_derived_from_derived(derivedData, smd, 0,
+ result = subsurf_make_derived_from_derived(derivedData, smd, NULL, 0,
NULL, 0, 1);
return result;
diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c
index 754b18bc282..a33c80bc6ae 100644
--- a/source/blender/modifiers/intern/MOD_util.c
+++ b/source/blender/modifiers/intern/MOD_util.c
@@ -39,6 +39,7 @@
#include "BKE_cdderivedmesh.h"
#include "BKE_mesh.h"
#include "BKE_displist.h"
+#include "BKE_texture.h"
#include "BKE_utildefines.h"
#include "BKE_modifier.h"
@@ -49,23 +50,6 @@
#include "RE_shader_ext.h"
-void get_texture_value(Tex *texture, float *tex_co, TexResult *texres)
-{
- int result_type;
-
- result_type = multitex_ext(texture, tex_co, NULL, NULL, 0, texres);
-
- /* if the texture gave an RGB value, we assume it didn't give a valid
- * intensity, so calculate one (formula from do_material_tex).
- * if the texture didn't give an RGB value, copy the intensity across
- */
- if(result_type & TEX_RGB)
- texres->tin = (0.35f * texres->tr + 0.45f * texres->tg
- + 0.2f * texres->tb);
- else
- texres->tr = texres->tg = texres->tb = texres->tin;
-}
-
void modifier_vgroup_cache(ModifierData *md, float (*vertexCos)[3])
{
while((md=md->next) && md->type==eModifierType_Armature) {
diff --git a/source/blender/modifiers/intern/MOD_util.h b/source/blender/modifiers/intern/MOD_util.h
index 5750e042199..c064f776897 100644
--- a/source/blender/modifiers/intern/MOD_util.h
+++ b/source/blender/modifiers/intern/MOD_util.h
@@ -37,7 +37,6 @@ struct Scene;
struct EditMesh;
struct ModifierData;
-void get_texture_value(struct Tex *texture, float *tex_co, struct TexResult *texres);
void modifier_vgroup_cache(struct ModifierData *md, float (*vertexCos)[3]);
void validate_layer_name(const struct CustomData *data, int type, char *name, char *outname);
struct DerivedMesh *get_cddm(struct Scene *scene, struct Object *ob, struct EditMesh *em, struct DerivedMesh *dm, float (*vertexCos)[3]);
diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c
index 42cbddce233..b4f20eed832 100644
--- a/source/blender/modifiers/intern/MOD_wave.c
+++ b/source/blender/modifiers/intern/MOD_wave.c
@@ -40,6 +40,7 @@
#include "BKE_object.h"
#include "BKE_deform.h"
#include "BKE_scene.h"
+#include "BKE_texture.h"
#include "depsgraph_private.h"
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 67decd6292e..2888cf9b8d0 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -625,7 +625,7 @@ int WM_write_file(bContext *C, const char *target, int fileflags, ReportList *re
}
ED_object_exit_editmode(C, EM_DO_UNDO);
- ED_sculpt_force_update(C);
+ ED_paint_force_update(C);
/* blend file thumbnail */
ibuf_thumb= blend_file_thumb(di, CTX_data_scene(C), &thumb);
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 5b7d952de77..121d2d3d21a 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -3210,6 +3210,7 @@ static void gesture_border_modal_keymap(wmKeyConfig *keyconf)
WM_modalkeymap_assign(keymap, "MARKER_OT_select_border");
WM_modalkeymap_assign(keymap, "NLA_OT_select_border");
WM_modalkeymap_assign(keymap, "NODE_OT_select_border");
+ WM_modalkeymap_assign(keymap, "SCULPT_OT_area_hide");
// WM_modalkeymap_assign(keymap, "SCREEN_OT_border_select"); // template
WM_modalkeymap_assign(keymap, "SEQUENCER_OT_select_border");
WM_modalkeymap_assign(keymap, "SEQUENCER_OT_view_ghost_border");
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index 526c1e3f13c..fec10222b02 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -559,7 +559,7 @@ ENDIF(CMAKE_SYSTEM_NAME MATCHES "Linux")
bf_python_ext
extern_binreloc
extern_glew
- extern_openjpeg
+ extern_ptex
bf_videotex
bf_rna
bf_dna