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/ptex/CMakeLists.txt18
-rw-r--r--extern/ptex/ptex.h45
-rw-r--r--extern/ptex/ptex_C_api.cpp100
-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.py16
-rw-r--r--source/blender/blenkernel/intern/cdderivedmesh.c8
-rw-r--r--source/blender/blenkernel/intern/customdata.c31
-rw-r--r--source/blender/blenkernel/intern/subsurf_ccg.c9
-rw-r--r--source/blender/blenlib/BLI_pbvh.h4
-rw-r--r--source/blender/blenlib/intern/pbvh.c17
-rw-r--r--source/blender/blenloader/CMakeLists.txt1
-rw-r--r--source/blender/blenloader/intern/readfile.c13
-rw-r--r--source/blender/blenloader/intern/writefile.c24
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt1
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h4
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c4
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c299
-rw-r--r--source/blender/editors/sculpt_paint/pbvh_undo.c3
-rw-r--r--source/blender/editors/sculpt_paint/ptex.c331
-rw-r--r--source/blender/gpu/CMakeLists.txt3
-rw-r--r--source/blender/gpu/GPU_buffers.h6
-rw-r--r--source/blender/gpu/intern/gpu_buffers.c199
-rw-r--r--source/blender/makesdna/DNA_customdata_types.h4
-rw-r--r--source/blender/makesdna/DNA_meshdata_types.h20
-rw-r--r--source/blender/makesrna/RNA_access.h1
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c89
-rw-r--r--source/creator/CMakeLists.txt1
56 files changed, 11303 insertions, 95 deletions
diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt
index 35271d24a2d..83febd9d5ca 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/ptex/CMakeLists.txt b/extern/ptex/CMakeLists.txt
new file mode 100644
index 00000000000..499046dc41f
--- /dev/null
+++ b/extern/ptex/CMakeLists.txt
@@ -0,0 +1,18 @@
+SET(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
+)
+
+BLENDERLIB(extern_ptex "${SRC}" "${INC}")
+
diff --git a/extern/ptex/ptex.h b/extern/ptex/ptex.h
new file mode 100644
index 00000000000..28d95c299b4
--- /dev/null
+++ b/extern/ptex/ptex.h
@@ -0,0 +1,45 @@
+#ifndef PTEX_H
+#define PTEX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef void* PtexTextureHandle;
+typedef void* PtexFaceInfoHandle;
+typedef void* PtexResHandle;
+
+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 */
+extern PtexResHandle ptex_face_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);
+
+/* Utils */
+int ptex_data_size(PtexDataType type);
+
+
+#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..2a9fd7fed7e
--- /dev/null
+++ b/extern/ptex/ptex_C_api.cpp
@@ -0,0 +1,100 @@
+#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 ****/
+PtexResHandle ptex_face_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();
+}
+
+/**** 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;
+ }
+}
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 2e5fbe7e37d..3947ea4943c 100644
--- a/release/scripts/ui/properties_data_mesh.py
+++ b/release/scripts/ui/properties_data_mesh.py
@@ -372,6 +372,21 @@ class DATA_PT_vertex_colors(DataButtonsPanel):
layout.operator("mesh.vertex_color_multiresolution_toggle", text="Add Multires")
+class DATA_PT_ptex(DataButtonsPanel):
+ bl_label = "PTex"
+ COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ me = context.mesh
+
+ layout.template_list(me, "ptex_layers", me, "active_ptex_index", rows=2)
+ row = layout.row()
+ row.operator_menu_enum("ptex.layer_add", "type")
+ row.operator("ptex.open")
+
+
classes = [
MESH_MT_vertex_group_specials,
MESH_MT_shape_key_specials,
@@ -381,6 +396,7 @@ classes = [
DATA_PT_settings,
DATA_PT_vertex_groups,
DATA_PT_shape_keys,
+ DATA_PT_ptex,
DATA_PT_uv_texture,
DATA_PT_texface,
DATA_PT_vertex_colors,
diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c
index 402609947ec..576b76edf4b 100644
--- a/source/blender/blenkernel/intern/cdderivedmesh.c
+++ b/source/blender/blenkernel/intern/cdderivedmesh.c
@@ -220,7 +220,13 @@ 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();
+ 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->vdata, &me->fdata, me->totface,
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index ce3cb1284ef..5e3802443d9 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -797,6 +797,29 @@ static void layerFree_grid(void *data_v, int count, int size)
}
}
+static void layerCopy_mptex(const void *source_v, void *dest_v, int count)
+{
+ const MPtex *source = source_v;
+ MPtex *dest = dest_v;
+ int i;
+
+ for(i = 0; i < count; ++i) {
+ dest[i] = source[i];
+ dest[i].data = MEM_dupallocN(source[i].data);
+ }
+}
+
+static void layerFree_mptex(void *data_v, int count, int size)
+{
+ MPtex *data = data_v;
+ int i;
+
+ for(i = 0; i < count; ++i) {
+ if(data[i].data)
+ MEM_freeN(data[i].data);
+ }
+}
+
const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
/* 0 */
{sizeof(MVert), "MVert", 1, NULL, NULL, NULL, NULL, NULL, NULL},
@@ -843,6 +866,8 @@ const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
{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] = {
@@ -851,7 +876,7 @@ const char *LAYERTYPENAMES[CD_NUMTYPES] = {
/* 10-14 */ "CDMFloatProperty", "CDMIntProperty","CDMStringProperty", "CDOrigSpace", "CDOrco",
/* 15-19 */ "CDMTexPoly", "CDMLoopUV", "CDMloopCol", "CDTangent", "CDMDisps",
/* 20-24 */ "CDWeightMCol", "CDIDMCol", "CDTextureMCol", "CDClothOrco", "CDGrid",
- /* 25-26 */ "CDPaintMask"
+ /* 25-26 */ "CDPaintMask", "CD_PTEX"
};
const CustomDataMask CD_MASK_BAREMESH =
@@ -860,11 +885,11 @@ 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_GRIDS | CD_MASK_PAINTMASK;
+ 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_GRIDS | CD_MASK_PAINTMASK;
+ 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 |
diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c
index 554f94c8d15..65f4e07d1f1 100644
--- a/source/blender/blenkernel/intern/subsurf_ccg.c
+++ b/source/blender/blenkernel/intern/subsurf_ccg.c
@@ -2496,7 +2496,7 @@ static struct PBVH *ccgDM_getPBVH(Object *ob, DerivedMesh *dm)
numGrids = ccgDM_getNumGrids(dm);
gridkey = ccgDM_getGridKey(dm);
- ob->paint->pbvh= ccgdm->pbvh = BLI_pbvh_new();
+ ob->paint->pbvh= ccgdm->pbvh = BLI_pbvh_new(PBVH_DEFAULT_LEAF_LIMIT);
BLI_pbvh_build_grids(ccgdm->pbvh, ccgdm->gridData, ccgdm->gridAdjacency,
numGrids, gridSize, gridkey, (void**)ccgdm->gridFaces,
&me->vdata, &me->fdata,
@@ -2504,7 +2504,7 @@ static struct PBVH *ccgDM_getPBVH(Object *ob, DerivedMesh *dm)
ccgdm->pbvh_draw = 1;
}
else if(ob->type == OB_MESH) {
- ob->paint->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->vdata, &me->fdata, me->totface, me->totvert,
ss ? &ss->hidden_areas : NULL);
@@ -2918,8 +2918,11 @@ void subsurf_calculate_limit_positions(Mesh *me, float (*positions_r)[3])
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, NULL, 1, 0, 1, 0);
+ 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/blenlib/BLI_pbvh.h b/source/blender/blenlib/BLI_pbvh.h
index d2645fd21c0..e7fc42e8210 100644
--- a/source/blender/blenlib/BLI_pbvh.h
+++ b/source/blender/blenlib/BLI_pbvh.h
@@ -68,8 +68,8 @@ typedef struct {
int BLI_pbvh_search_sphere_cb(PBVHNode *node, void *data);
/* Building */
-
-PBVH *BLI_pbvh_new(void);
+#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,
struct CustomData *vdata, struct CustomData *fdata,
int totface, int totvert, ListBase *hidden_areas);
diff --git a/source/blender/blenlib/intern/pbvh.c b/source/blender/blenlib/intern/pbvh.c
index b344eb21764..27e51b930c2 100644
--- a/source/blender/blenlib/intern/pbvh.c
+++ b/source/blender/blenlib/intern/pbvh.c
@@ -21,6 +21,7 @@
*/
#include "DNA_meshdata_types.h"
+#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
#include "BLI_math.h"
@@ -37,8 +38,6 @@
static void pbvh_free_nodes(PBVH *bvh);
-#define LEAF_LIMIT 10000
-
//#define PERFCNTRS
/* Bitmap */
@@ -669,7 +668,6 @@ void BLI_pbvh_build_mesh(PBVH *bvh, MFace *faces, MVert *verts,
bvh->vdata = vdata;
bvh->fdata = fdata;
bvh->totvert = totvert;
- bvh->leaf_limit = LEAF_LIMIT;
if(totface)
pbvh_begin_build(bvh, totface, hidden_areas);
@@ -690,16 +688,18 @@ void BLI_pbvh_build_grids(PBVH *bvh, DMGridData **grids,
bvh->gridkey= gridkey;
bvh->vdata= vdata;
bvh->fdata= fdata;
- bvh->leaf_limit = MAX2(LEAF_LIMIT/((gridsize-1)*(gridsize-1)), 1);
+ bvh->leaf_limit = MAX2(PBVH_DEFAULT_LEAF_LIMIT/((gridsize-1)*(gridsize-1)), 1);
if(totgrid)
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;
}
@@ -1178,8 +1178,11 @@ static void pbvh_update_draw_buffers(PBVH *bvh, PBVHNode **nodes, int totnode, G
flags);
}
else {
- GPU_update_mesh_color_buffers(node->draw_buffers,
- bvh, node, flags);
+ if(flags & GPU_DRAW_ACTIVE_MCOL)
+ GPU_update_mesh_ptex(node->draw_buffers, bvh, node);
+ else
+ GPU_update_mesh_color_buffers(node->draw_buffers,
+ bvh, node, flags);
}
node->flag &= ~PBVH_UpdateColorBuffers;
diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt
index cae0fccd12a..fd4f697409f 100644
--- a/source/blender/blenloader/CMakeLists.txt
+++ b/source/blender/blenloader/CMakeLists.txt
@@ -31,6 +31,7 @@ SET(INC
../makesdna ../readblenfile ../include ../makesrna
../python ../../kernel/gen_messaging
../render/extern/include
+ ../../../extern/ptex
${ZLIB_INC}
)
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index cc9fe1e57fe..0ecb1168b29 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -3278,6 +3278,17 @@ static void direct_link_customdata_multires(FileData *fd, int count,
}
}
+static void direct_link_customdata_mptex(FileData *fd, int count, MPtex *mptex)
+{
+ if(mptex) {
+ int i;
+
+ for(i = 0; i < count; ++i, ++mptex) {
+ mptex->data = newdataadr(fd, mptex->data);
+ }
+ }
+}
+
static void direct_link_customdata(FileData *fd, CustomData *data, int count)
{
int i = 0;
@@ -3297,6 +3308,8 @@ static void direct_link_customdata(FileData *fd, CustomData *data, int count)
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++;
}
}
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index e49c39117a2..b19838f9869 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -171,6 +171,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 ************ */
@@ -1505,6 +1507,25 @@ static void write_customdata_multires(WriteData *wd, int count,
}
}
+static void write_customdata_mptex(WriteData *wd, int count,
+ MPtex *mptex)
+{
+ int i;
+
+ writestruct(wd, DATA, "MPtex", count, mptex);
+
+ for(i = 0; i < count; ++i, ++mptex) {
+ int layersize = mptex->channels * ptex_data_size(mptex->type);
+ float texels = mptex->ures*mptex->vres;
+
+ if(mptex->subfaces)
+ texels *= mptex->subfaces;
+
+ writedata(wd, DATA, layersize * texels,
+ mptex->data);
+ }
+}
+
static void write_customdata(WriteData *wd, ID *id, int count, CustomData *data, int partial_type, int partial_count)
{
int i;
@@ -1533,6 +1554,9 @@ static void write_customdata(WriteData *wd, ID *id, int count, CustomData *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/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index f0493d8e2d8..08fab38826f 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -28,6 +28,7 @@ SET(INC
../../blenlib
../include
../../../../intern/guardedalloc
+ ../../../../extern/ptex
../../makesdna
../../makesrna
../../windowmanager
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index 3c92089a258..d57bc346387 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -243,5 +243,9 @@ 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);
+/* ptex.c */
+void PTEX_OT_layer_add(struct wmOperatorType *ot);
+void PTEX_OT_open(struct wmOperatorType *ot);
+
#endif /* ED_PAINT_INTERN_H */
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index c60c81dad11..fc88aa15ebe 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -218,6 +218,10 @@ void ED_operatortypes_paint(void)
WM_operatortype_append(PAINT_OT_vertex_color_set);
WM_operatortype_append(PAINT_OT_vertex_colors_to_texture);
+ /* ptex */
+ WM_operatortype_append(PTEX_OT_layer_add);
+ WM_operatortype_append(PTEX_OT_open);
+
/* face-select */
WM_operatortype_append(PAINT_OT_face_select_linked);
WM_operatortype_append(PAINT_OT_face_select_linked_pick);
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index 05c53831f8f..c7dc76230b3 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -70,6 +70,7 @@
#include "BKE_multires.h"
#include "BKE_object.h"
#include "BKE_paint.h"
+#include "BKE_subsurf.h"
#include "BKE_utildefines.h"
#include "WM_api.h"
@@ -83,6 +84,8 @@
#include "ED_screen.h"
#include "ED_view3d.h"
+#include "ptex.h"
+
#include "paint_intern.h"
/* polling - retrieve whether cursor should be set or operator should be done */
@@ -1459,7 +1462,6 @@ void PAINT_OT_weight_set(wmOperatorType *ot)
/* ************ set / clear vertex paint mode ********** */
-
static int set_vpaint(bContext *C, wmOperator *op) /* toggle */
{
Object *ob= CTX_data_active_object(C);
@@ -1485,13 +1487,13 @@ 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);
@@ -1587,43 +1589,200 @@ static void vpaint_blend(Brush *brush, float col[4], float alpha)
IMB_blend_color_float(col, col, brush->rgb, alpha, tool);
}
+/* TODO */
+#if 0
static void vpaint_nodes_grids(Brush *brush, PaintStroke *stroke,
+ MPtex *ptexes, GridToFace *grid_face_map,
DMGridData **grids, CustomData *vdata,
GridKey *gridkey,
int *grid_indices, int totgrid,
- int gridsize, int active)
+ int gridsize)
{
PaintStrokeTest test;
- int i, x, y;
+ int i;
paint_stroke_test_init(&test, stroke);
for(i = 0; i < totgrid; ++i) {
DMGridData *grid = grids[grid_indices[i]];
+ PTex *ptex = &ptexes[grid_face_map[grid_indices[i]].face];
+ int offset = grid_face_map[grid_indices[i]].offset * ptex->ures*ptex->vres;
+ int u, v;
+ float x, y, xfac, yfac;
- for(y = 0; y < gridsize; ++y) {
- for(x = 0; x < gridsize; ++x) {
- DMGridData *elem = GRIDELEM_AT(grid,
- y*gridsize+x,
- gridkey);
- float *co = GRIDELEM_CO(elem, gridkey);
- float *gridcol = GRIDELEM_COLOR(elem, gridkey)[active];
- float mask = paint_mask_from_gridelem(elem,
- gridkey,
- vdata);
+ xfac = (gridsize-1);
+ if(ptex->ures > 1) xfac /= (float)(ptex->ures - 1);
+ yfac = (gridsize-1);
+ if(ptex->vres > 1) yfac /= (float)(ptex->vres - 1);
+
+ for(v = 0; v < ptex->vres; ++v) {
+ for(u = 0; u < ptex->ures; ++u) {
+ DMGridData *elem[4];
+ float *color, co[3], co_top[3], co_bot[3];
+ int xi, yi;
+
+ color = ptex->colors[offset + v*ptex->ures+ u];
+
+ /* do a naive interpolation to get gridco,
+ can probably do this much better */
+
+ x = u*xfac;
+ y = v*yfac;
+ xi = (int)x;
+ yi = (int)y;
+
+ elem[0] = GRIDELEM_AT(grid, yi*gridsize+xi, gridkey);
+ elem[1] = GRIDELEM_AT(grid, (yi+1)*gridsize+xi, gridkey);
+ elem[2] = GRIDELEM_AT(grid, (yi+1)*gridsize+xi+1, gridkey);
+ elem[3] = GRIDELEM_AT(grid, yi*gridsize+xi+1, gridkey);
+
+ interp_v3_v3v3(co_top, GRIDELEM_CO(elem[0], gridkey),
+ GRIDELEM_CO(elem[3], gridkey), x - xi);
+
+ interp_v3_v3v3(co_bot, GRIDELEM_CO(elem[1], gridkey),
+ GRIDELEM_CO(elem[2], gridkey), x - xi);
+
+ interp_v3_v3v3(co, co_top, co_bot, y - yi);
if(paint_stroke_test(&test, co)) {
float strength;
strength = brush->alpha *
- paint_stroke_combined_strength(stroke, test.dist, co, mask);
+ paint_stroke_combined_strength(stroke, test.dist, co, 0);
- vpaint_blend(brush, gridcol, strength);
+ vpaint_blend(brush, color, strength);
}
}
}
}
}
+#endif
+
+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 vpaint_ptex_from_quad(Brush *brush, PaintStroke *stroke, PaintStrokeTest *test,
+ MPtex *pt, char *data,
+ float v1[3], float v2[3], float v3[3], float v4[3])
+
+{
+ float dtop[3], dbot[3], yinterp, ustep, vstep;
+ float co_bot[3], co_top[3];
+ int u, v, layersize;
+
+ layersize = pt->channels * ptex_data_size(pt->type);
+
+ sub_v3_v3v3(dtop, v1, v2);
+ sub_v3_v3v3(dbot, v4, v3);;
+ ustep = 1;
+ if(pt->ures != 1)
+ ustep /= (pt->ures - 1);
+ mul_v3_fl(dtop, ustep);
+ mul_v3_fl(dbot, ustep);
+
+ vstep = 1;
+ if(pt->vres != 1)
+ vstep /= (pt->vres - 1);
+
+ for(v = 0, yinterp = 0; v < pt->vres; ++v) {
+ copy_v3_v3(co_top, v2);
+ copy_v3_v3(co_bot, v3);
+
+ for(u = 0; u < pt->ures; ++u, data += layersize) {
+ float co[3];
+
+ interp_v3_v3v3(co, co_bot, co_top, yinterp);
+
+ if(paint_stroke_test(test, co)) {
+ float strength;
+ float fcol[4];
+
+ strength = brush->alpha *
+ paint_stroke_combined_strength(stroke, test->dist, co, 0);
+
+ ptex_elem_to_float4(pt->type, pt->channels, data, fcol);
+ vpaint_blend(brush, fcol, strength);
+ ptex_elem_from_float4(pt->type, pt->channels, data, fcol);
+ }
+
+ /*(*color)[0] = u / (pt->ures-1.0f);
+ (*color)[1] = v / (pt->vres-1.0f);
+ (*color)[2] = 1;*/
+
+ add_v3_v3(co_bot, dbot);
+ add_v3_v3(co_top, dtop);
+ }
+
+ yinterp += vstep;
+ }
+}
static void vpaint_nodes_faces(Brush *brush, PaintStroke *stroke,
MFace *mface, MVert *mvert,
@@ -1631,6 +1790,7 @@ static void vpaint_nodes_faces(Brush *brush, PaintStroke *stroke,
int *face_indices, int totface)
{
PaintStrokeTest test;
+ MPtex *mptex;
MCol *mcol;
int pmask_totlayer, pmask_first_layer;
int i, j;
@@ -1641,39 +1801,46 @@ static void vpaint_nodes_faces(Brush *brush, PaintStroke *stroke,
paint_stroke_test_init(&test, stroke);
+ mptex = CustomData_get_layer(fdata, CD_MPTEX);
+
for(i = 0; i < totface; ++i) {
int face_index = face_indices[i];
MFace *f = mface + face_index;
+ MPtex *pt = &mptex[face_index];
int S = f->v4 ? 4 : 3;
- for(j = 0; j < S; ++j) {
- int vndx = (&f->v1)[j];
- int cndx = face_index*4 + j;
- float *co = mvert[vndx].co;
- float fcol[4];
- float mask;
-
- fcol[0] = mcol[cndx].b / 255.0f;
- fcol[1] = mcol[cndx].g / 255.0f;
- fcol[2] = mcol[cndx].r / 255.0f;
- fcol[3] = mcol[cndx].a / 255.0f;
-
- mask = paint_mask_from_vertex(vdata, vndx,
- pmask_totlayer,
- pmask_first_layer);
- if(paint_stroke_test(&test, co)) {
- float strength;
-
- strength = brush->alpha *
- paint_stroke_combined_strength(stroke, test.dist, co, mask);
-
- vpaint_blend(brush, fcol, strength);
+ if(S == 4) {
+ /* fast case */
+ vpaint_ptex_from_quad(brush, stroke, &test, pt, pt->data,
+ mvert[f->v3].co, mvert[f->v4].co,
+ mvert[f->v1].co, mvert[f->v2].co);
+ }
+ else {
+ /* subfaces */
+
+ /* for now this is just tris, can be updated for ngons too */
+
+ float half[4][3], center[3];
+ int layersize = pt->channels * ptex_data_size(pt->type);
+
+ for(j = 0; j < 3; ++j) {
+ int next = (j == S-1) ? 0 : j+1;
+ mid_v3_v3v3(half[j], mvert[(&f->v1)[j]].co, mvert[(&f->v1)[next]].co);
}
- mcol[cndx].b = fcol[0] * 255.0f;
- mcol[cndx].g = fcol[1] * 255.0f;
- mcol[cndx].r = fcol[2] * 255.0f;
- mcol[cndx].a = fcol[3] * 255.0f;
+ cent_tri_v3(center, mvert[f->v1].co, mvert[f->v2].co, mvert[f->v3].co);
+
+ for(j = 0; j < 3; ++j) {
+ int vndx = (&f->v1)[j];
+ int prev = j==0 ? S-1 : j-1;
+ char *data = pt->data;
+ data += layersize*pt->ures*pt->vres * j;
+
+ vpaint_ptex_from_quad(brush, stroke, &test, pt,
+ data,
+ mvert[vndx].co, half[j],
+ center, half[prev]);
+ }
}
}
}
@@ -1823,50 +1990,34 @@ static void vpaint_nodes(VPaint *vp, PaintStroke *stroke,
PBVH *pbvh = ob->paint->pbvh;
Brush *brush = paint_brush(&vp->paint);
int blur = brush->vertexpaint_tool == VERTEX_PAINT_BLUR;
+ CustomData *vdata = NULL;
+ CustomData *fdata = NULL;
int n;
- for(n = 0; n < totnode; ++n) {
- CustomData *vdata = NULL;
- CustomData *fdata = NULL;
+ BLI_pbvh_get_customdata(pbvh, &vdata, &fdata);
+ for(n = 0; n < totnode; ++n) {
pbvh_undo_push_node(nodes[n], PBVH_UNDO_COLOR, ob);
- BLI_pbvh_get_customdata(pbvh, &vdata, &fdata);
-
if(BLI_pbvh_uses_grids(pbvh)) {
+#if 0 /* TODO */
DMGridData **grids;
GridKey *gridkey;
int *grid_indices, totgrid, gridsize;
- int active;
BLI_pbvh_node_get_grids(pbvh, nodes[n], &grid_indices,
&totgrid, NULL, &gridsize, &grids,
NULL, &gridkey);
- active = gridelem_active_offset(fdata, gridkey, CD_MCOL);
-
- if(active != -1) {
- if(blur) {
- vpaint_nodes_grids_smooth(brush, stroke,
- grids, vdata,
- gridkey,
- grid_indices,
- totgrid,
- gridsize,
- active);
- BLI_pbvh_node_set_flags(nodes[n],
- SET_INT_IN_POINTER(PBVH_NeedsColorStitch));
- }
- else {
- vpaint_nodes_grids(brush, stroke,
- grids, vdata,
- gridkey,
- grid_indices,
- totgrid,
- gridsize,
- active);
- }
- }
+ vpaint_nodes_grids(brush, stroke,
+ NULL,
+ grid_face_map,
+ grids, vdata,
+ gridkey,
+ grid_indices,
+ totgrid,
+ gridsize);
+#endif
}
else {
MVert *mvert;
diff --git a/source/blender/editors/sculpt_paint/pbvh_undo.c b/source/blender/editors/sculpt_paint/pbvh_undo.c
index bd883ac1507..88033ce9fd8 100644
--- a/source/blender/editors/sculpt_paint/pbvh_undo.c
+++ b/source/blender/editors/sculpt_paint/pbvh_undo.c
@@ -550,6 +550,9 @@ 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) {
diff --git a/source/blender/editors/sculpt_paint/ptex.c b/source/blender/editors/sculpt_paint/ptex.c
new file mode 100644
index 00000000000..b51c51b2b3d
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/ptex.c
@@ -0,0 +1,331 @@
+#include "MEM_guardedalloc.h"
+
+#include "DNA_object_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_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_report.h"
+#include "BKE_subsurf.h"
+
+#include "BLI_math.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ptex.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+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 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);
+
+ mptex = CustomData_add_layer(&me->fdata, CD_MPTEX, CD_CALLOC,
+ NULL, me->totface);
+
+ /* 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; 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;
+ int texels;
+ 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));
+ vres = next_power_of_two(sqrtf((face_area[i] * density) / r));
+ }
+ 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].ures = ures;
+ mptex[i].vres = vres;
+ mptex[i].type = type;
+ mptex[i].channels = totchannel;
+
+ if(S == 4) {
+ mptex[i].subfaces = 0;
+ texels = gridsize;
+ }
+ else {
+ mptex[i].subfaces = S;
+ texels = S*gridsize;
+ }
+
+ data = mptex[i].data = MEM_callocN(layer_size * texels, "PTex colors");
+ tottexel += texels;
+
+ for(j = 0; j < texels; ++j) {
+ 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)
+{
+ static EnumPropertyItem 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}};
+
+ /* 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", type_items, PTEX_DT_FLOAT, "Type", "Layer channels and data type");
+}
+
+/* loads a .ptx file
+ makes some fairly strict 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;
+ MPtex *mptex;
+
+ 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, j += (me->mface[i].v4 ? 1 : 3)) {
+ 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;
+ }
+
+ /* TODO: check that all subfaces of a particular face have the same resolution */
+ }
+
+ mptex = CustomData_add_layer(&me->fdata, CD_MPTEX, CD_CALLOC,
+ NULL, me->totface);
+
+ /* TODO: for now, just convert to customdata ptex */
+ for(i = 0, j = 0; i < me->totface; ++i, j += (me->mface[i].v4 ? 1 : 3)) {
+ int S = me->mface[i].v4 ? 4 : 3;
+ PtexFaceInfoHandle *ptex_face;
+ PtexResHandle *ptex_res;
+ int texels;
+
+ ptex_face = ptex_texture_get_face_info(ptex_texture, j);
+ ptex_res = ptex_face_get_res(ptex_face);
+
+ mptex[i].ures = ptex_res_u(ptex_res);
+ mptex[i].vres = ptex_res_v(ptex_res);
+ mptex[i].type = ptex_data_type;
+ mptex[i].channels = totchannel;
+
+ if(S == 4) {
+ mptex[i].subfaces = 0;
+ texels = mptex[i].ures * mptex[i].vres;
+ }
+ else {
+ mptex[i].subfaces = S;
+ mptex[i].ures = ptex_res_u(ptex_res);
+ mptex[i].vres = ptex_res_v(ptex_res);
+ texels = mptex[i].ures * mptex[i].vres * S;
+ }
+
+ mptex[i].data = MEM_callocN(ptex_data_size(ptex_data_type) *
+ totchannel * texels, "Ptex file data");
+
+ ptex_texture_get_data(ptex_texture, j, mptex[i].data, 0, ptex_res);
+
+ /* TODO: interleave data? */
+ }
+
+ /* data is all copied, can release ptex file */
+ ptex_texture_release(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);
+}
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 126cddf852f..4401600a317 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -28,7 +28,8 @@ FILE(GLOB SRC intern/*.c)
SET(INC
. ../blenlib ../blenkernel ../makesdna ../makesrna ../include
- ../../../extern/glew/include ../../../intern/guardedalloc ../../../intern/smoke/extern ../imbuf)
+ ../../../extern/glew/include ../../../intern/guardedalloc ../../../intern/smoke/extern ../imbuf
+ ../../../extern/ptex)
IF(WIN32)
INCLUDE_DIRECTORIES(${PTHREADS_INC})
diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h
index 79feb5cf6eb..62151f1e338 100644
--- a/source/blender/gpu/GPU_buffers.h
+++ b/source/blender/gpu/GPU_buffers.h
@@ -162,6 +162,12 @@ void GPU_update_grid_color_buffers(GPU_Buffers *buffers,
int *grid_indices, int totgrid,
int gridsize, struct GridKey *gridkey,
struct CustomData *vdata, GPUDrawFlags flags);
+typedef struct {
+ float (*colors)[4];
+ int ures, vres;
+} PTexHandle;
+void GPU_update_mesh_ptex(GPU_Buffers *buffers, struct PBVH *bvh, struct PBVHNode *node);
+void GPU_update_grid_ptex(GPU_Buffers *buffers, PTexHandle *grids, int totgrid);
void GPU_draw_buffers(GPU_Buffers *buffers, struct PBVH *bvh,
struct PBVHNode *node, GPUDrawFlags flags);
void GPU_free_buffers(GPU_Buffers *buffers);
diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c
index a2ff9b40b9f..6c12f981897 100644
--- a/source/blender/gpu/intern/gpu_buffers.c
+++ b/source/blender/gpu/intern/gpu_buffers.c
@@ -35,6 +35,7 @@
#include <string.h>
#include "GL/glew.h"
+#include "ptex.h"
#include "MEM_guardedalloc.h"
@@ -423,6 +424,9 @@ struct GPU_Buffers {
GLuint vert_buf, index_buf, color_buf;
GLenum index_type;
+ GLuint *ptex;
+ int totptex;
+
unsigned int tot_tri, tot_quad;
};
@@ -730,6 +734,120 @@ void GPU_update_grid_color_buffers(GPU_Buffers *buffers, DMGridData **grids, int
}
}
+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)
+{
+ 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;
+ }
+}
+
+void GPU_update_mesh_ptex(GPU_Buffers *buffers, PBVH *pbvh, PBVHNode *node)
+{
+ CustomData *fdata;
+ MFace *mface;
+ MPtex *mptex;
+ int *face_indices, totface;
+ int i, j;
+
+ BLI_pbvh_get_customdata(pbvh, NULL, &fdata);
+ BLI_pbvh_node_get_faces(pbvh, node, &mface, &face_indices, NULL, &totface);
+
+ /* TODO: composite multiple layers */
+ mptex = CustomData_get_layer(fdata, CD_MPTEX);
+
+ /* pack all ptex for one face into one texture */
+ if(!buffers->ptex) {
+ buffers->totptex = totface;
+ buffers->ptex = MEM_callocN(sizeof(GLuint) * buffers->totptex, "PTex IDs");
+ glGenTextures(buffers->totptex, buffers->ptex);
+ }
+
+ for(i = 0; i < totface; ++i) {
+ int face_ndx = face_indices[i];
+ MPtex *pt = &mptex[face_ndx];
+ int S = mface[i].v4 ? 4 : 3;
+
+ GLenum gltype = gl_type_from_ptex(pt);
+ GLenum glformat = gl_format_from_ptex(pt);
+ int layersize = pt->channels * ptex_data_size(pt->type);
+
+ glBindTexture(GL_TEXTURE_2D, buffers->ptex[i]);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ if(S == 4) {
+ /* load quad ptex whole */
+ glTexImage2D(GL_TEXTURE_2D, 0, glformat, pt->ures, pt->vres,
+ 0, glformat, gltype, pt->data);
+ }
+ else {
+ /* pack non-quads ptex in side by side */
+ glTexImage2D(GL_TEXTURE_2D, 0, glformat, S * pt->ures, pt->vres,
+ 0, glformat, gltype, NULL);
+
+ for(j = 0; j < S; ++j) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, pt->ures*j, 0, pt->ures,
+ pt->vres, glformat, gltype,
+ ((char*)pt->data) + pt->ures*pt->vres*j*layersize);
+ }
+ }
+ }
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+void GPU_update_grid_ptex(GPU_Buffers *buffers, PTexHandle *grids, int totgrid)
+{
+ int i;
+
+ if(buffers->totptex != totgrid) {
+ buffers->totptex = totgrid;
+ buffers->ptex = MEM_callocN(sizeof(GLuint) * totgrid, "PTex IDs");
+ glGenTextures(totgrid, buffers->ptex);
+ }
+
+
+ for(i = 0; i < totgrid; ++i) {
+ glBindTexture(GL_TEXTURE_2D, buffers->ptex[i]);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, grids[i].ures, grids[i].vres,
+ 0, GL_RGBA, GL_FLOAT, grids[i].colors);
+ }
+}
+
typedef struct {
float co[3];
float no[3];
@@ -878,6 +996,71 @@ GPU_Buffers *GPU_build_grid_buffers(DMGridData **grids,
return buffers;
}
+static void gpu_draw_ptex_node_without_vb(GPU_Buffers *buffers, MFace *mface, MVert *mvert,
+ int *face_indices, int totface, GPUDrawFlags flags)
+{
+ int i, j;
+
+ for(i = 0; i < totface; ++i) {
+ int face_index = face_indices[i];
+ MFace *f = mface + face_index;
+ int S = f->v4 ? 4 : 3;
+
+ glBindTexture(GL_TEXTURE_2D, buffers->ptex[i]);
+
+ glBegin(GL_QUADS);
+
+ if(S == 4) {
+ /* fast case */
+
+ float uv[4][2] = {{0,0}, {1,0}, {1,1}, {0,1}};
+
+ for(j = 0; j < 4; ++j) {
+ int vndx = (&f->v1)[j];
+
+ glTexCoord2fv(uv[j]);
+ glNormal3sv(mvert[vndx].no);
+ glVertex3fv(mvert[vndx].co);
+ }
+ }
+ else {
+ /* split ngons into subfaces (still quads) */
+
+ /* for now this is just tris, can be updated for ngons too */
+ float center[3];
+ float half[4][3];
+ float texoffs = 1.0f / S;
+
+ cent_tri_v3(center, mvert[f->v1].co, mvert[f->v2].co, mvert[f->v3].co);
+
+ for(j = 0; j < 3; ++j) {
+ int next = (j == S-1) ? 0 : j+1;
+ mid_v3_v3v3(half[j], mvert[(&f->v1)[j]].co, mvert[(&f->v1)[next]].co);
+ }
+
+ for(j = 0; j < 3; ++j) {
+ int prev = j == 0 ? S-1 : j-1;
+ int vndx = (&f->v1)[j];
+
+ glTexCoord2f(texoffs * (j+1), 1);
+ glNormal3sv(mvert[vndx].no);
+ glVertex3fv(mvert[vndx].co);
+
+ glTexCoord2f(texoffs * j, 1);
+ glVertex3fv(half[j]);
+
+ glTexCoord2f(texoffs * j, 0);
+ glVertex3fv(center);
+
+ glTexCoord2f(texoffs * (j+1), 0);
+ glVertex3fv(half[prev]);
+ }
+ }
+
+ glEnd();
+ }
+}
+
static void gpu_draw_node_without_vb(GPU_Buffers *buffers, PBVH *pbvh, PBVHNode *node, GPUDrawFlags flags)
{
DMGridData **grids;
@@ -904,14 +1087,17 @@ static void gpu_draw_node_without_vb(GPU_Buffers *buffers, PBVH *pbvh, PBVHNode
mcol_first_layer = CustomData_get_layer_index(fdata, CD_MCOL);
pmask_first_layer = CustomData_get_layer_index(vdata, CD_PAINTMASK);
- use_color = mcol_first_layer != -1 || pmask_first_layer != -1;
+ use_color = !(flags & GPU_DRAW_ACTIVE_MCOL) && pmask_first_layer != -1;
}
-
+
if(use_color) {
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
}
+ if(buffers->ptex && GPU_DRAW_ACTIVE_MCOL)
+ glEnable(GL_TEXTURE_2D);
+
if(use_grids) {
int x, y;
@@ -963,6 +1149,11 @@ static void gpu_draw_node_without_vb(GPU_Buffers *buffers, PBVH *pbvh, PBVHNode
BLI_pbvh_node_get_verts(pbvh, node, NULL, &mvert);
BLI_pbvh_node_get_faces(pbvh, node, &mface, &face_indices, NULL, &totface);
+ if(flags & GPU_DRAW_ACTIVE_MCOL && buffers->ptex) {
+ gpu_draw_ptex_node_without_vb(buffers, mface, mvert, face_indices, totface, flags);
+ return;
+ }
+
if(mcol_first_layer)
mcol_totlayer = CustomData_number_of_layers(fdata, CD_MCOL);
@@ -1078,6 +1269,10 @@ void GPU_free_buffers(GPU_Buffers *buffers_v)
glDeleteBuffersARB(1, &buffers->vert_buf);
if(buffers->index_buf)
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_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h
index 33c536d8df6..407efc53cdc 100644
--- a/source/blender/makesdna/DNA_customdata_types.h
+++ b/source/blender/makesdna/DNA_customdata_types.h
@@ -103,7 +103,8 @@ typedef struct CustomDataMultires {
#define CD_CLOTH_ORCO 23
#define CD_GRIDS 24
#define CD_PAINTMASK 25
-#define CD_NUMTYPES 26
+#define CD_MPTEX 26
+#define CD_NUMTYPES 27
/* Bits for CustomDataMask */
#define CD_MASK_MVERT (1 << CD_MVERT)
@@ -130,6 +131,7 @@ typedef struct CustomDataMultires {
#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 */
diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h
index 450c56b0dec..0eff26fb6b7 100644
--- a/source/blender/makesdna/DNA_meshdata_types.h
+++ b/source/blender/makesdna/DNA_meshdata_types.h
@@ -125,6 +125,26 @@ typedef struct MDisps {
float (*disps)[3];
} MDisps;
+typedef struct MPtex {
+ void *data;
+
+ /* for non-quads, ures and vres are the
+ resolution of the subfaces */
+ int ures;
+ int vres;
+
+ /* for quads, subfaces=0; anything else
+ is set to the face's number of vertices */
+ int subfaces;
+
+ /* 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/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index b9bc1d626ac..cb1d1ca62cf 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -301,6 +301,7 @@ 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_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index d463df7b0d3..c9bff267c24 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -786,7 +786,7 @@ static void rna_MeshColorLayer_name_set(PointerRNA *ptr, const char *value)
CustomData_set_layer_unique_name(fdata, cdl - fdata->layers);
}
-/* Paint mask layer */
+/* Paint mask layers */
static void rna_MeshPaintMask_update_data(Main *bmain, Scene *scene, PointerRNA *ptr)
{
@@ -878,6 +878,62 @@ static void rna_Mesh_active_paint_mask_index_range(PointerRNA *ptr, int *min, in
*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);
+}
+
/* Custom property layers */
static void rna_MeshFloatPropertyLayer_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
@@ -1619,6 +1675,22 @@ 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");
+}
+
static void rna_def_paintmask(BlenderRNA *brna)
{
StructRNA *srna;
@@ -1870,6 +1942,20 @@ static void rna_def_mesh(BlenderRNA *brna)
"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");
+
prop= RNA_def_property(srna, "float_layers", 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);
@@ -2067,6 +2153,7 @@ 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/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index a653d45bd14..8b451474f38 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -489,6 +489,7 @@ ENDIF(CMAKE_SYSTEM_NAME MATCHES "Linux")
bf_gen_python
extern_binreloc
extern_glew
+ extern_ptex
extern_libopenjpeg
bf_videotex
bf_rna