diff --git a/dust3d.pro b/dust3d.pro index 700144fc..330a5644 100644 --- a/dust3d.pro +++ b/dust3d.pro @@ -274,6 +274,8 @@ HEADERS += thirdparty/thekla_atlas/extern/poshlib/posh.h SOURCES += thirdparty/thekla_atlas/src/thekla/thekla_atlas.cpp HEADERS += thirdparty/thekla_atlas/src/thekla/thekla_atlas.h +HEADERS += thirdparty/thekla_atlas/src/nvcore/nvcore.h + HEADERS += thirdparty/thekla_atlas/src/nvcore/Stream.h SOURCES += thirdparty/thekla_atlas/src/nvcore/Debug.cpp diff --git a/thirdparty/thekla_atlas/CMakeLists.txt b/thirdparty/thekla_atlas/CMakeLists.txt index b74d29b5..3f0bd99b 100755 --- a/thirdparty/thekla_atlas/CMakeLists.txt +++ b/thirdparty/thekla_atlas/CMakeLists.txt @@ -23,3 +23,32 @@ add_executable( target_link_libraries( thekla_atlas_test nvmesh) + +set(CMAKE_DEBUG_POSTFIX d) + +set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--export-all-symbols") + +add_library(libthekla_atlas + src/thekla/thekla_atlas.cpp +) + +install(TARGETS libthekla_atlas DESTINATION lib/static) + +get_property(HEADER_DIRS DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) + +configure_file(theklaAtlasConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/theklaAtlasConfig.cmake @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/theklaAtlasConfig.cmake DESTINATION .) + +file(GLOB HEADERS src/*.h) +install(FILES ${HEADERS} DESTINATION include) +file(GLOB HEADERS src/thekla/*.h) +install(FILES ${HEADERS} DESTINATION include/thekla) +file(GLOB HEADERS src/nvcore/*.h) +install(FILES ${HEADERS} DESTINATION include/nvcore) +file(GLOB HEADERS src/nvimage/*.h) +install(FILES ${HEADERS} DESTINATION include/nvimage) +file(GLOB HEADERS src/nvmesh/*.h) +install(FILES ${HEADERS} DESTINATION include/nvmesh) +file(GLOB HEADERS src/nvmath/*.h) +install(FILES ${HEADERS} DESTINATION include/nvmath) diff --git a/thirdparty/thekla_atlas/src/nvcore/Array.h b/thirdparty/thekla_atlas/src/nvcore/Array.h index d3fab26a..b295cb2b 100755 --- a/thirdparty/thekla_atlas/src/nvcore/Array.h +++ b/thirdparty/thekla_atlas/src/nvcore/Array.h @@ -179,6 +179,4 @@ namespace nv } // nv namespace -#include "Array.inl" - #endif // NV_CORE_ARRAY_H diff --git a/thirdparty/thekla_atlas/src/nvcore/Array.inl b/thirdparty/thekla_atlas/src/nvcore/Array.inl index 0b4de28b..67c00296 100755 --- a/thirdparty/thekla_atlas/src/nvcore/Array.inl +++ b/thirdparty/thekla_atlas/src/nvcore/Array.inl @@ -12,6 +12,9 @@ #include // memmove #include // for placement new +/////// Changes in Dust3D Begin ///////////// +#include "Vector.inl" +/////// Changes in Dust3D End ///////////// namespace nv diff --git a/thirdparty/thekla_atlas/src/nvcore/BitArray.h b/thirdparty/thekla_atlas/src/nvcore/BitArray.h index 23cf8806..80626973 100755 --- a/thirdparty/thekla_atlas/src/nvcore/BitArray.h +++ b/thirdparty/thekla_atlas/src/nvcore/BitArray.h @@ -40,7 +40,7 @@ namespace nv { public: - BitArray() {} + BitArray() : m_size(0){} BitArray(uint sz) { resize(sz); } @@ -237,7 +237,7 @@ namespace nv private: // Number of bits stored. - uint m_size; + uint m_size = 0; // Array of bits. Array m_wordArray; diff --git a/thirdparty/thekla_atlas/src/nvcore/CMakeLists.txt b/thirdparty/thekla_atlas/src/nvcore/CMakeLists.txt index a84a9e99..879be2d9 100755 --- a/thirdparty/thekla_atlas/src/nvcore/CMakeLists.txt +++ b/thirdparty/thekla_atlas/src/nvcore/CMakeLists.txt @@ -1,4 +1,5 @@ PROJECT(nvcore) +set(CMAKE_DEBUG_POSTFIX d) SET(CORE_SRCS nvcore.h diff --git a/thirdparty/thekla_atlas/src/nvcore/Debug.cpp b/thirdparty/thekla_atlas/src/nvcore/Debug.cpp index 75ac6beb..8edb9ec7 100755 --- a/thirdparty/thekla_atlas/src/nvcore/Debug.cpp +++ b/thirdparty/thekla_atlas/src/nvcore/Debug.cpp @@ -106,7 +106,8 @@ namespace struct sigaction s_old_sigtrap; struct sigaction s_old_sigfpe; struct sigaction s_old_sigbus; - + struct sigaction s_old_sigill; + #endif @@ -431,9 +432,9 @@ namespace pSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); pSymbol->MaxNameLength = MAX_STRING_LEN; - DWORD64 dwDisplacement; + DWORD64 dwDisplacement64; - if (SymGetSymFromAddr64(hProcess, ip, &dwDisplacement, pSymbol)) + if (SymGetSymFromAddr64(hProcess, ip, &dwDisplacement64, pSymbol)) { pSymbol->Name[MAX_STRING_LEN-1] = 0; @@ -455,8 +456,8 @@ namespace IMAGEHLP_LINE64 theLine = { 0 }; theLine.SizeOfStruct = sizeof(theLine); - DWORD dwDisplacement; - if (!SymGetLineFromAddr64(hProcess, ip, &dwDisplacement, &theLine)) + DWORD dwDisplacement32; + if (!SymGetLineFromAddr64(hProcess, ip, &dwDisplacement32, &theLine)) { // Do not print unknown symbols anymore. //break; @@ -679,6 +680,9 @@ namespace # elif NV_CPU_ARM ucontext_t * ucp = (ucontext_t *)secret; return (void *) ucp->uc_mcontext->__ss.__pc; +# elif NV_CPU_ARM_64 + ucontext_t * ucp = (ucontext_t *)secret; + return (void *) ucp->uc_mcontext->__ss.__pc; # else # error "Unknown CPU" # endif @@ -784,8 +788,8 @@ namespace printStackTrace(trace, size, 1); } #endif // defined(NV_HAVE_EXECINFO_H) - - exit(0); + + exit(EXIT_FAILURE + 2); } #endif // defined(NV_HAVE_SIGNAL_H) @@ -933,15 +937,24 @@ namespace // Assert handler method. virtual int assertion(const char * exp, const char * file, int line, const char * func, const char * msg, va_list arg) { - int ret = NV_ABORT_EXIT; + int ret = NV_ABORT_IGNORE; - if( func != NULL ) { - nvDebug( "*** Assertion failed: %s\n On file: %s\n On function: %s\n On line: %d\n ", exp, file, func, line ); + StringBuilder error_string; + error_string.format("*** Assertion failed: %s\n On file: %s\n On line: %d\n", exp, file, line ); + if (func != NULL) { + error_string.appendFormat(" On function: %s\n", func); } - else { - nvDebug( "*** Assertion failed: %s\n On file: %s\n On line: %d\n ", exp, file, line ); + if (msg != NULL) { + error_string.append(" Message: "); + va_list tmp; + va_copy(tmp, arg); + error_string.appendFormatList(msg, tmp); + va_end(tmp); + error_string.append("\n"); } - + nvDebug("%s", error_string.str()); + + #if _DEBUG if (debug::isDebuggerPresent()) { return NV_ABORT_DEBUG; @@ -1173,7 +1186,7 @@ void debug::enableSigHandler(bool interactive) s_interactive = interactive; #if (NV_OS_WIN32 && NV_CC_MSVC) || NV_OS_DURANGO - if (interactive) { + if (!interactive) { #if NV_OS_WIN32 // Do not display message boxes on error. // http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621(v=vs.85).aspx @@ -1224,6 +1237,8 @@ void debug::enableSigHandler(bool interactive) sigaction(SIGTRAP, &sa, &s_old_sigtrap); sigaction(SIGFPE, &sa, &s_old_sigfpe); sigaction(SIGBUS, &sa, &s_old_sigbus); + sigaction(SIGILL, &sa, &s_old_sigill); + #endif } @@ -1249,7 +1264,8 @@ void debug::disableSigHandler() sigaction(SIGTRAP, &s_old_sigtrap, NULL); sigaction(SIGFPE, &s_old_sigfpe, NULL); sigaction(SIGBUS, &s_old_sigbus, NULL); - + sigaction(SIGILL, &s_old_sigill, NULL); + #endif } diff --git a/thirdparty/thekla_atlas/src/nvcore/Debug.h b/thirdparty/thekla_atlas/src/nvcore/Debug.h index 3804ed47..98a32fd4 100755 --- a/thirdparty/thekla_atlas/src/nvcore/Debug.h +++ b/thirdparty/thekla_atlas/src/nvcore/Debug.h @@ -12,6 +12,8 @@ # ifdef __APPLE__ # include # include +# include // getpid +# include # endif #endif @@ -43,10 +45,22 @@ //# define nvDebugBreak() __asm { int 3 } # elif NV_OS_ORBIS # define nvDebugBreak() __debugbreak() -# elif NV_OS_IOS && TARGET_OS_IPHONE -# define nvDebugBreak() raise(SIGINT) # elif NV_CC_CLANG -# define nvDebugBreak() __builtin_debugtrap() +# if NV_OS_IOS +# if NV_CPU_ARM_64 +# if __clang_major__ >= 9 +# define nvDebugBreak() asm volatile("svc #0x80") // IC: "svc 0" doesn't seem to work anymore. +# else +# define nvDebugBreak() asm volatile("svc 0") +# endif +# elif NV_CPU_ARM +# define nvDebugBreak() asm("trap") +# else // simulator? +# define nvDebugBreak() __builtin_debugtrap() +# endif +# else +# define nvDebugBreak() __builtin_debugtrap() +# endif # elif NV_CC_GNUC //# define nvDebugBreak() __builtin_debugtrap() // Does GCC have debugtrap? # define nvDebugBreak() __builtin_trap() @@ -98,12 +112,14 @@ } \ NV_MULTI_LINE_MACRO_END +// IC: When using this macro in llvm/ios you can ignore future hits typing 'expr ignoreAll=true' in the console. // GCC, LLVM need "##" before the __VA_ARGS__, MSVC doesn't care #define nvAssertMacroWithIgnoreAll(exp,...) \ NV_MULTI_LINE_MACRO_BEGIN \ static bool ignoreAll = false; \ if (!ignoreAll && !nvExpect(exp)) { \ int _result = nvAbort(#exp, __FILE__, __LINE__, __FUNC__, ##__VA_ARGS__); \ + /*nvDebug("Type 'expr ignoreAll=true' to ignore future hits\n");*/ \ if (_result == NV_ABORT_DEBUG) { \ nvDebugBreak(); \ } else if (_result == NV_ABORT_IGNORE) { \ @@ -124,7 +140,7 @@ #define nvCheckMacro(exp) \ (\ (exp) ? true : ( \ - (nvAbort(#exp, __FILE__, __LINE__, __FUNC__) == NV_ABORT_DEBUG) ? (nvDebugBreak(), true) : ( false ) \ + (nvAbort(#exp, __FILE__, __LINE__, __FUNC__) == NV_ABORT_DEBUG) ? ([](){nvDebugBreak();}(), true) : ( false ) \ ) \ ) @@ -199,11 +215,15 @@ namespace nv if (ptr == NULL) return true; if (reinterpret_cast(ptr) < 0x10000ULL) return false; if (reinterpret_cast(ptr) >= 0x000007FFFFFEFFFFULL) return false; + #elif NV_CPU_ARM_64 + if (ptr == NULL) return true; + if (reinterpret_cast(ptr) < 0x10000ULL) return false; + if (reinterpret_cast(ptr) >= 0x000007FFFFFEFFFFULL) return false; #else - if (reinterpret_cast(ptr) == 0xcccccccc) return false; - if (reinterpret_cast(ptr) == 0xcdcdcdcd) return false; - if (reinterpret_cast(ptr) == 0xdddddddd) return false; - if (reinterpret_cast(ptr) == 0xffffffff) return false; + if (reinterpret_cast(ptr) == 0xcccccccc) return false; + if (reinterpret_cast(ptr) == 0xcdcdcdcd) return false; + if (reinterpret_cast(ptr) == 0xdddddddd) return false; + if (reinterpret_cast(ptr) == 0xffffffff) return false; #endif return true; } diff --git a/thirdparty/thekla_atlas/src/nvcore/DefsGnucDarwin.h b/thirdparty/thekla_atlas/src/nvcore/DefsGnucDarwin.h index afb21c3d..14974878 100755 --- a/thirdparty/thekla_atlas/src/nvcore/DefsGnucDarwin.h +++ b/thirdparty/thekla_atlas/src/nvcore/DefsGnucDarwin.h @@ -27,8 +27,8 @@ #define NV_FASTCALL __attribute__((fastcall)) #define NV_FORCEINLINE __attribute__((always_inline)) inline #define NV_DEPRECATED __attribute__((deprecated)) -#if NV_OS_IOS -#define NV_THREAD_LOCAL // @@ IC: Looks like iOS does not have support for TLS declarations. +#if 0 //Apple finally added TLS support to iOS!// NV_OS_IOS +#define NV_THREAD_LOCAL #else #define NV_THREAD_LOCAL __thread #endif diff --git a/thirdparty/thekla_atlas/src/nvcore/FileSystem.cpp b/thirdparty/thekla_atlas/src/nvcore/FileSystem.cpp index 5ed0ca07..881ade25 100755 --- a/thirdparty/thekla_atlas/src/nvcore/FileSystem.cpp +++ b/thirdparty/thekla_atlas/src/nvcore/FileSystem.cpp @@ -73,3 +73,31 @@ bool FileSystem::removeFile(const char * path) // @@ Use unlink or remove? return remove(path) == 0; } + + +#include "StdStream.h" // for fileOpen + +bool FileSystem::copyFile(const char * src, const char * dst) { + + FILE * fsrc = fileOpen(src, "rb"); + if (fsrc == NULL) return false; + NV_ON_RETURN(fclose(fsrc)); + + FILE * fdst = fileOpen(dst, "wb"); + if (fdst == NULL) return false; + NV_ON_RETURN(fclose(fdst)); + + char buffer[1024]; + size_t n; + + while ((n = fread(buffer, sizeof(char), sizeof(buffer), fsrc)) > 0) { + if (fwrite(buffer, sizeof(char), n, fdst) != n) { + return false; + } + } + + return true; +} + + + diff --git a/thirdparty/thekla_atlas/src/nvcore/FileSystem.h b/thirdparty/thekla_atlas/src/nvcore/FileSystem.h index afd0f449..30661c2b 100755 --- a/thirdparty/thekla_atlas/src/nvcore/FileSystem.h +++ b/thirdparty/thekla_atlas/src/nvcore/FileSystem.h @@ -15,7 +15,7 @@ namespace nv NVCORE_API bool createDirectory(const char * path); NVCORE_API bool changeDirectory(const char * path); NVCORE_API bool removeFile(const char * path); - + NVCORE_API bool copyFile(const char * src, const char * dst); } // FileSystem namespace } // nv namespace diff --git a/thirdparty/thekla_atlas/src/nvcore/Memory.cpp b/thirdparty/thekla_atlas/src/nvcore/Memory.cpp index 03b05dfc..fb4a04f5 100755 --- a/thirdparty/thekla_atlas/src/nvcore/Memory.cpp +++ b/thirdparty/thekla_atlas/src/nvcore/Memory.cpp @@ -5,10 +5,12 @@ #include "Utils.h" #include +/////// Changes in Dust3D Begin ///////////// #if defined(__APPLE__) #else #include #endif +/////// Changes in Dust3D End ///////////// #define USE_EFENCE 0 diff --git a/thirdparty/thekla_atlas/src/nvcore/StrLib.cpp b/thirdparty/thekla_atlas/src/nvcore/StrLib.cpp index 7ec6c701..8896a543 100755 --- a/thirdparty/thekla_atlas/src/nvcore/StrLib.cpp +++ b/thirdparty/thekla_atlas/src/nvcore/StrLib.cpp @@ -526,6 +526,28 @@ StringBuilder & StringBuilder::copy( const StringBuilder & s ) return *this; } +void StringBuilder::removeChar(char c) +{ + char * src = strchr(m_str, c); + if (src) { + char * dst = src; + src++; + while (*src) { + *dst++ = *src++; + } + *dst = '\0'; + } +} + +void StringBuilder::replaceChars(char c, char x) +{ + char * src = m_str; + while (*src) { + if (*src == c) *src = x; + src++; + } +} + bool StringBuilder::endsWith(const char * str) const { uint l = uint(strlen(str)); @@ -540,7 +562,7 @@ bool StringBuilder::beginsWith(const char * str) const return strncmp(m_str, str, l) == 0; } -// Find given char starting from the end. +// Find given char starting from the end. Why not use strrchr!? char * StringBuilder::reverseFind(char c) { int length = (int)strlen(m_str) - 1; diff --git a/thirdparty/thekla_atlas/src/nvcore/StrLib.h b/thirdparty/thekla_atlas/src/nvcore/StrLib.h index ae4b5d12..9634d575 100755 --- a/thirdparty/thekla_atlas/src/nvcore/StrLib.h +++ b/thirdparty/thekla_atlas/src/nvcore/StrLib.h @@ -124,6 +124,9 @@ namespace nv StringBuilder & toLower(); StringBuilder & toUpper(); + + void removeChar(char c); + void replaceChars(char c, char x); bool endsWith(const char * str) const; bool beginsWith(const char * str) const; @@ -131,13 +134,13 @@ namespace nv char * reverseFind(char c); void reset(); - bool isNull() const { return m_size == 0; } + NV_FORCEINLINE bool isNull() const { return m_size == 0; } // const char * accessors //operator const char * () const { return m_str; } //operator char * () { return m_str; } - const char * str() const { return m_str; } - char * str() { return m_str; } + NV_FORCEINLINE const char * str() const { return m_str; } + NV_FORCEINLINE char * str() { return m_str; } char * release(); // Release ownership of string. void acquire(char *); // Take ownership of string. @@ -283,25 +286,25 @@ namespace nv /// Equal operator. bool operator==( const String & str ) const { - return strMatch(str.data, data); + return strEqual(str.data, data); } /// Equal operator. bool operator==( const char * str ) const { - return strMatch(str, data); + return strEqual(str, data); } /// Not equal operator. bool operator!=( const String & str ) const { - return !strMatch(str.data, data); + return !strEqual(str.data, data); } /// Not equal operator. bool operator!=( const char * str ) const { - return !strMatch(str, data); + return !strEqual(str, data); } /// Returns true if this string is the null string. diff --git a/thirdparty/thekla_atlas/src/nvcore/Utils.h b/thirdparty/thekla_atlas/src/nvcore/Utils.h index f20e42cd..44e17e63 100755 --- a/thirdparty/thekla_atlas/src/nvcore/Utils.h +++ b/thirdparty/thekla_atlas/src/nvcore/Utils.h @@ -72,6 +72,11 @@ namespace nv //template <> inline uint32 U32(uint8 x) { return x; } template <> inline uint32 U32(int8 x) { nvDebugCheck(x >= 0); return (uint32)x; } +#if NV_OS_DARWIN + template <> inline uint32 U32(unsigned long x) { nvDebugCheck(x <= NV_UINT32_MAX); return (uint32)x; } + template <> inline uint32 U32(long x) { nvDebugCheck(x >= 0 && x <= NV_UINT32_MAX); return (uint32)x; } +#endif + // int32 casts: template inline int32 I32(T x) { return x; } template <> inline int32 I32(uint64 x) { nvDebugCheck(x <= NV_INT32_MAX); return (int32)x; } @@ -120,11 +125,11 @@ namespace nv // int8 casts: template inline int8 I8(T x) { return x; } template <> inline int8 I8(uint64 x) { nvDebugCheck(x <= NV_INT8_MAX); return (int8)x; } - template <> inline int8 I8(int64 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_UINT8_MAX); return (int8)x; } + template <> inline int8 I8(int64 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_INT8_MAX); return (int8)x; } template <> inline int8 I8(uint32 x) { nvDebugCheck(x <= NV_INT8_MAX); return (int8)x; } - template <> inline int8 I8(int32 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_UINT8_MAX); return (int8)x; } + template <> inline int8 I8(int32 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_INT8_MAX); return (int8)x; } template <> inline int8 I8(uint16 x) { nvDebugCheck(x <= NV_INT8_MAX); return (int8)x; } - template <> inline int8 I8(int16 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_UINT8_MAX); return (int8)x; } + template <> inline int8 I8(int16 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_INT8_MAX); return (int8)x; } template <> inline int8 I8(uint8 x) { nvDebugCheck(x <= NV_INT8_MAX); return (int8)x; } //template <> inline int8 I8(int8 x) { return x; } diff --git a/thirdparty/thekla_atlas/src/nvcore/nvcore.h b/thirdparty/thekla_atlas/src/nvcore/nvcore.h index a3deb66b..9af7ddd6 100755 --- a/thirdparty/thekla_atlas/src/nvcore/nvcore.h +++ b/thirdparty/thekla_atlas/src/nvcore/nvcore.h @@ -84,9 +84,9 @@ // Threading: // some platforms don't implement __thread or similar for thread-local-storage -#if NV_OS_UNIX || NV_OS_ORBIS || NV_OS_IOS //ACStodoIOS darwin instead of ios? +#if NV_OS_UNIX || NV_OS_ORBIS || NV_OS_IOS # define NV_OS_USE_PTHREAD 1 -# if NV_OS_IOS +# if 0 //Apple finally added TLS support to iOS!// NV_OS_IOS # define NV_OS_HAS_TLS_QUALIFIER 0 # else # define NV_OS_HAS_TLS_QUALIFIER 1 @@ -114,6 +114,8 @@ # define NV_CPU_PPC 1 #elif defined POSH_CPU_STRONGARM # define NV_CPU_ARM 1 +#elif defined POSH_CPU_AARCH64 +# define NV_CPU_ARM_64 1 #else # error "Unsupported CPU" #endif @@ -151,10 +153,16 @@ #endif // Endiannes: -#define NV_LITTLE_ENDIAN POSH_LITTLE_ENDIAN -#define NV_BIG_ENDIAN POSH_BIG_ENDIAN -#define NV_ENDIAN_STRING POSH_ENDIAN_STRING - +// @@ POSH endian detection is broken for arm64 on iOS. They are bi-endian and iOS sets all their processors to little endian by default. +#if NV_OS_IOS +# define NV_LITTLE_ENDIAN 1 +# define NV_BIG_ENDIAN 0 +# define NV_ENDIAN_STRING "little" +#else +# define NV_LITTLE_ENDIAN POSH_LITTLE_ENDIAN +# define NV_BIG_ENDIAN POSH_BIG_ENDIAN +# define NV_ENDIAN_STRING POSH_ENDIAN_STRING +#endif // Define the right printf prefix for size_t arguments: #if POSH_64BIT_POINTER @@ -311,6 +319,24 @@ template char (&ArraySizeHelper(T (&array)[N]))[N]; NV_STRING_JOIN3(AtStartup_, __LINE__, Instance); \ } +namespace nv { + template + struct ScopeExit { + ScopeExit(F f) : f(f) {} + ~ScopeExit() { f(); } + F f; + }; + + template + ScopeExit MakeScopeExit(F f) { + return ScopeExit(f); + }; +} + +#define NV_ON_RETURN(code) \ + auto NV_STRING_JOIN2(scope_exit_, __LINE__) = nv::MakeScopeExit([=](){code;}) + + // Indicate the compiler that the parameter is not used to suppress compier warnings. #if NV_CC_MSVC #define NV_UNUSED(a) ((a)=(a)) @@ -318,6 +344,14 @@ template char (&ArraySizeHelper(T (&array)[N]))[N]; #define NV_UNUSED(a) _Pragma(NV_STRING(unused(a))) #endif +#if NV_CC_GNUC || NV_CC_CLANG +#define NV_LIKELY(x) __builtin_expect(!!(x), 1) +#define NV_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define NV_LIKELY(x) x +#define NV_UNLIKELY(x) x +#endif + // Null index. @@ Move this somewhere else... it's only used by nvmesh. //const unsigned int NIL = unsigned int(~0); #define NIL uint(~0) diff --git a/thirdparty/thekla_atlas/src/nvimage/CMakeLists.txt b/thirdparty/thekla_atlas/src/nvimage/CMakeLists.txt index 5bc3cf43..c368af24 100755 --- a/thirdparty/thekla_atlas/src/nvimage/CMakeLists.txt +++ b/thirdparty/thekla_atlas/src/nvimage/CMakeLists.txt @@ -1,4 +1,5 @@ PROJECT(nvimage) +set(CMAKE_DEBUG_POSTFIX d) SET(IMAGE_SRCS nvimage.h diff --git a/thirdparty/thekla_atlas/src/nvimage/Image.h b/thirdparty/thekla_atlas/src/nvimage/Image.h index 4c5748cb..bf54940b 100755 --- a/thirdparty/thekla_atlas/src/nvimage/Image.h +++ b/thirdparty/thekla_atlas/src/nvimage/Image.h @@ -7,6 +7,10 @@ #include "nvimage.h" #include "nvcore/Debug.h" +#if NV_USE_ALTIVEC +#undef pixel +#endif + namespace nv { class Color32; diff --git a/thirdparty/thekla_atlas/src/nvmath/Box.h b/thirdparty/thekla_atlas/src/nvmath/Box.h index bec04784..19b5f2a3 100755 --- a/thirdparty/thekla_atlas/src/nvmath/Box.h +++ b/thirdparty/thekla_atlas/src/nvmath/Box.h @@ -99,7 +99,5 @@ namespace nv } // nv namespace -#include "Box.inl" - #endif // NV_MATH_BOX_H diff --git a/thirdparty/thekla_atlas/src/nvmath/CMakeLists.txt b/thirdparty/thekla_atlas/src/nvmath/CMakeLists.txt index 49f50496..45e508e4 100755 --- a/thirdparty/thekla_atlas/src/nvmath/CMakeLists.txt +++ b/thirdparty/thekla_atlas/src/nvmath/CMakeLists.txt @@ -1,4 +1,5 @@ PROJECT(nvmath) +set(CMAKE_DEBUG_POSTFIX d) SET(MATH_SRCS nvmath.h diff --git a/thirdparty/thekla_atlas/src/nvmath/ConvexHull.cpp b/thirdparty/thekla_atlas/src/nvmath/ConvexHull.cpp index a4a95dac..52cfdff5 100755 --- a/thirdparty/thekla_atlas/src/nvmath/ConvexHull.cpp +++ b/thirdparty/thekla_atlas/src/nvmath/ConvexHull.cpp @@ -7,12 +7,9 @@ #include "nvcore/RadixSort.h" #include "nvcore/Array.inl" -using namespace nv; +#include -inline static float triangleArea(Vector2::Arg v1, Vector2::Arg v2, Vector2::Arg v3) -{ - return 0.5f * (v3.x * v1.y + v1.x * v2.y + v2.x * v3.y - v2.x * v1.y - v3.x * v2.y - v1.x * v3.y); -} +using namespace nv; // Compute the convex hull using Graham Scan. @@ -61,7 +58,7 @@ void nv::convexHull(const Array & input, Array & output, float Vector2 b = output[output.count()-1]; Vector2 c = top[i]; - float area = triangleArea(a, b, c); + float area = triangleArea(a, b, c); // * 0.5 if (area >= -epsilon) { output.popBack(); @@ -82,7 +79,7 @@ void nv::convexHull(const Array & input, Array & output, float Vector2 b = output[output.count()-1]; Vector2 c = bottom[i]; - float area = triangleArea(a, b, c); + float area = triangleArea(a, b, c); // * 0.5 if (area >= -epsilon) { output.popBack(); @@ -99,6 +96,63 @@ void nv::convexHull(const Array & input, Array & output, float output.popBack(); } + +void nv::reduceConvexHullToNSides(Array *input, uint num_sides) { + + while (input->size() > num_sides) { + // find the shortest side and merge it with its shorter neighbor. + // we'll see how this goes... if it's not satisfactory, we could also merge + // based partially on the "flatness" of adjacent edges. + int shortest_index = 0; + float shortest_length = nv::length((*input)[1] - (*input)[0]); + + for (uint i = 1; i < input->size(); i++) { + nv::Vector2 p1 = (*input)[i]; + nv::Vector2 p2 = (*input)[(i + 1) % input->size()]; + float length = nv::length(p2 - p1); + + if (length < shortest_length) { + shortest_index = i; + shortest_length = length; + } + } + + int prev_index = (shortest_index + input->size() - 1) % input->size(); + int next_index = (shortest_index + 1) % input->size(); + int after_index = (next_index + 1) % input->size(); + float prev_len = nv::length((*input)[prev_index] - (*input)[shortest_index]); + float next_len = nv::length((*input)[next_index] - (*input)[after_index]); + + if (prev_len < next_len) { + input->removeAt(shortest_index); + } else { + input->removeAt(next_index); + } + } +} + +bool nv::isClockwise(const Array &input) { + nv::Vector2 d1 = input[1] - input[0]; + nv::Vector2 d2 = input[2] - input[0]; + nv::Vector3 normal = cross(Vector3(d1.x, d1.y, 0), Vector3(d2.x, d2.y, 0)); + return (normal.z < 0.0f); +} + +void nv::flipWinding(Array *input) { + int start = 0; + int end = input->size() - 1; + + while (end > start) { + nv::Vector2 swap = (*input)[start]; + (*input)[start] = (*input)[end]; + (*input)[end] = swap; + start++; + end--; + } +} + + + /* void testConvexHull() { @@ -118,3 +172,280 @@ void testConvexHull() { } */ + + +struct Line { + Vector2 v; + Vector2 d; +}; + + +bool intersect(const Line &line0, const Line &line1, Vector2 * point) +{ + float d = triangleArea(line0.d, line1.d); + if (fabsf(d) < NV_EPSILON) // Parallel lines + return false; + + float t = triangleArea(line1.d, line0.v - line1.v) / d; + + if (t < 0.5f) // Intersects on the wrong side + return false; + + *point = line0.v + t * line0.d; + return true; +} + +static inline Line buildLine(const Array & vertices, uint i0, uint i1) { + Line line; + line.v = vertices[i0]; + line.d = vertices[i1] - vertices[i0]; // @@ Normalize? + return line; +} + + +static bool buildPolygon(const Array & vertices, const Array & edges, Array & output) { + + const uint vertexCount = vertices.count(); + const uint edgeCount = edges.count(); + + output.clear(); + output.reserve(edgeCount); + + // Intersect each edge against the next. + Line first, prev, current; + Vector2 point; + + first = prev = buildLine(vertices, 0, 1); + + for (uint i = 1; i < edgeCount; i++) { + current = buildLine(vertices, edges[i], (edges[i]+1) % vertexCount); + + if (!intersect(prev, current, &point)) { + return false; + } + output.append(point); + + prev = current; + } + + if (!intersect(current, first, &point)) { + return false; + } + output.append(point); + + return true; +} + +// Triangulate polygon and sum triangle areas. +static float polygonArea(const Array & vertices) { + + float area = 0; + Vector2 v0 = vertices[0]; + + for (uint i = 2; i < vertices.count(); i++) + { + Vector2 v1 = vertices[i]; + Vector2 v2 = vertices[i-1]; + + area += triangleArea(v0, v1, v2); + } + + return area * 0.5f; +} + +// @@ Test this! +static bool next(Array & edges, uint vertexCount) { + int count = edges.count(); + + uint previous = vertexCount; + + for (int i = count-1; i >= 0; i--) { + uint e = edges[i]; + if (e + 1 < previous) { + // increment current and reset remainder of the sequence. + for (; i < count; i++) { + edges[i] = ++e; + } + return true; + } + previous = e; + } + + return false; +} + + + +// Given a convex hull, finds best fit polygon with the given number of edges. +// Brute force implementation, tries all possible edges, but may not arrive to best result, because edges of best fit polygon are not necessarily aligned to convex hull edges. +bool nv::bestFitPolygon(const Array & hullVertices, uint edgeCount, Array * bestPolygon) { + uint vertexCount = hullVertices.count(); + + // Not a valid polygon. + if (edgeCount <= 2) return false; + + // Hull has same or less vertices that desired polygon. + if (edgeCount >= vertexCount) return false; + + bestPolygon->reserve(edgeCount); + float bestArea = FLT_MAX; + + if (edgeCount == 4) { + // Try with axis aligned box first. + Vector2 box_min(FLT_MAX); + Vector2 box_max(-FLT_MAX); + + for (uint i = 0; i < hullVertices.count(); i++) { + box_min.x = min(box_min.x, hullVertices[i].x); + box_min.y = min(box_min.y, hullVertices[i].y); + box_max.x = max(box_max.x, hullVertices[i].x); + box_max.y = max(box_max.y, hullVertices[i].y); + } + + bestArea = (box_max.x - box_min.x) * (box_max.y - box_min.y); + + bestPolygon->append(Vector2(box_min.x, box_min.y)); + bestPolygon->append(Vector2(box_min.x, box_max.y)); + bestPolygon->append(Vector2(box_max.x, box_max.y)); + bestPolygon->append(Vector2(box_max.x, box_min.y)); + } + + + // Select all possible combinations of consecutive edges. + + // For example, for groups of 3 out of 5 we want: + + // 012 + // 013 + // 014 + // 023 + // 024 + // 034 + // 123 + // 124 + // 134 + + + Array edges(edgeCount); + + // Init edge sequence. + for (uint i = 0; i < edgeCount; i++) { + edges.append(i); + } + + // Traverse sequence testing all + Array polygon(edgeCount); + do { + if (buildPolygon(hullVertices, edges, polygon)) { + float area = polygonArea(polygon); + if (area < bestArea) { + bestArea = area; + *bestPolygon = polygon; + } + } + } while (next(edges, vertexCount)); + + return bestArea < FLT_MAX; +} + + +static bool pointInTriangle(const Vector2 & p, const Vector2 & a, const Vector2 & b, const Vector2 & c) +{ + return triangleArea(a, b, p) >= 0.00001f && + triangleArea(b, c, p) >= 0.00001f && + triangleArea(c, a, p) >= 0.00001f; +} + +void nv::triangulate(const Array & input, Array * output) { + + const uint edgeCount = input.count(); + nvDebugCheck(edgeCount >= 3); + + output->clear(); // @@ Do we want to clear? + output->reserve(edgeCount); + + if (edgeCount == 3) { + // Simple case for triangles. + output->append(0); + output->append(1); + output->append(2); + } + else { + Array polygonVertices; + Array polygonAngles; + + polygonVertices.resize(edgeCount); + polygonAngles.resize(edgeCount); + + for (uint i = 0; i < edgeCount; ++i) { + polygonVertices[i] = i; + } + + while (polygonVertices.size() > 2) { + uint size = polygonVertices.size(); + + // Update polygon angles. @@ Update only those that have changed. + float minAngle = 2 * PI; + uint bestEar = 0; // Use first one if none of them is valid. + bool bestIsValid = false; + for (uint i = 0; i < size; i++) { + uint i0 = i; + uint i1 = (i+1) % size; // @@ Use Sean's polygon interation trick. + uint i2 = (i+2) % size; + + Vector2 p0 = input[polygonVertices[i0]]; + Vector2 p1 = input[polygonVertices[i1]]; + Vector2 p2 = input[polygonVertices[i2]]; + + float d = clamp(dot(p0-p1, p2-p1) / (length(p0-p1) * length(p2-p1)), -1.0f, 1.0f); + float angle = acosf(d); + + float area = triangleArea(p0, p1, p2); + if (area < 0.0f) angle = 2.0f * PI - angle; + + polygonAngles[i1] = angle; + + if (angle < minAngle || !bestIsValid) { + + // Make sure this is a valid ear, if not, skip this point. + bool valid = true; + for (uint j = 0; j < size; j++) { + if (j == i0 || j == i1 || j == i2) continue; + Vector2 p = input[polygonVertices[j]]; + + if (pointInTriangle(p, p0, p1, p2)) { + valid = false; + break; + } + } + + if (valid || !bestIsValid) { + minAngle = angle; + bestEar = i1; + bestIsValid = valid; + } + } + } + + nvDebugCheck(minAngle <= 2 * PI); + + // Clip best ear: + + uint i0 = (bestEar+size-1) % size; + uint i1 = (bestEar+0) % size; + uint i2 = (bestEar+1) % size; + + int v0 = polygonVertices[i0]; + int v1 = polygonVertices[i1]; + int v2 = polygonVertices[i2]; + + output->append(v0); + output->append(v1); + output->append(v2); + + polygonVertices.removeAt(i1); + polygonAngles.removeAt(i1); + } + } + +} diff --git a/thirdparty/thekla_atlas/src/nvmath/ConvexHull.h b/thirdparty/thekla_atlas/src/nvmath/ConvexHull.h index 6c2db5d7..ea5a7a23 100755 --- a/thirdparty/thekla_atlas/src/nvmath/ConvexHull.h +++ b/thirdparty/thekla_atlas/src/nvmath/ConvexHull.h @@ -12,6 +12,16 @@ namespace nv { void convexHull(const Array & input, Array & output, float epsilon = 0); + //ACS: moved these down from collision_volume.cpp + bool isClockwise(const Array & input); + void reduceConvexHullToNSides(Array *input, uint num_sides); + void flipWinding(Array *input); + + bool bestFitPolygon(const Array & input, uint vertexCount, Array * output); + + // Basic ear-clipping algorithm. + void triangulate(const Array & input, Array * output); + } // namespace nv #endif // NV_MATH_CONVEXHULL_H diff --git a/thirdparty/thekla_atlas/src/nvmath/Matrix.cpp b/thirdparty/thekla_atlas/src/nvmath/Matrix.cpp index 29bd19f5..d3d55711 100755 --- a/thirdparty/thekla_atlas/src/nvmath/Matrix.cpp +++ b/thirdparty/thekla_atlas/src/nvmath/Matrix.cpp @@ -57,7 +57,7 @@ static bool ludcmp(float **a, int n, int *indx, float *d) } a[i][j]=sum; - float dum = vv[i]*fabs(sum); + float dum = vv[i]*fabsf(sum); if (dum >= big) { // Is the figure of merit for the pivot better than the best so far? big = dum; @@ -197,6 +197,36 @@ bool nv::solveLU(const Matrix3 & A, const Vector3 & b, Vector3 * x) return true; } +bool nv::solveLU(const Matrix2 & A, const Vector2 & b, Vector2 * x) +{ + nvDebugCheck(x != NULL); + + float m[2][2]; + float *a[2] = {m[0], m[1]}; + int idx[2]; + float d; + + for (int y = 0; y < 2; y++) { + for (int x = 0; x < 2; x++) { + a[x][y] = A(x, y); + } + } + + // Create LU decomposition. + if (!ludcmp(a, 2, idx, &d)) { + // Singular matrix. + return false; + } + + // Init solution. + *x = b; + + // Do back substitution. + lubksb(a, 2, idx, x->component); + + return true; +} + bool nv::solveCramer(const Matrix & A, const Vector4 & b, Vector4 * x) { @@ -223,6 +253,22 @@ bool nv::solveCramer(const Matrix3 & A, const Vector3 & b, Vector3 * x) return true; } +bool nv::solveCramer(const Matrix2 & A, const Vector2 & b, Vector2 * x) +{ + nvDebugCheck(x != NULL); + + const float det = A.determinant(); + if (equal(det, 0.0f)) { // @@ Use input epsilon. + return false; + } + + Matrix2 Ai = inverseCramer(A); + + *x = transform(Ai, b); + + return true; +} + // Inverse using gaussian elimination. From Jon's code. @@ -238,8 +284,8 @@ Matrix nv::inverse(const Matrix & m) { for (i=0; i<4; i++) { /* eliminate in column i, below diag */ max = -1.; for (k=i; k<4; k++) /* find pivot for column i */ - if (fabs(A(k, i)) > max) { - max = fabs(A(k, i)); + if (fabsf(A(k, i)) > max) { + max = fabsf(A(k, i)); j = k; } if (max<=0.) return B; /* if no nonzero pivot, PUNT */ @@ -293,8 +339,8 @@ Matrix3 nv::inverse(const Matrix3 & m) { for (i=0; i<3; i++) { /* eliminate in column i, below diag */ max = -1.; for (k=i; k<3; k++) /* find pivot for column i */ - if (fabs(A(k, i)) > max) { - max = fabs(A(k, i)); + if (fabsf(A(k, i)) > max) { + max = fabsf(A(k, i)); j = k; } if (max<=0.) return B; /* if no nonzero pivot, PUNT */ diff --git a/thirdparty/thekla_atlas/src/nvmath/Matrix.h b/thirdparty/thekla_atlas/src/nvmath/Matrix.h index 506bdad1..e48f1992 100755 --- a/thirdparty/thekla_atlas/src/nvmath/Matrix.h +++ b/thirdparty/thekla_atlas/src/nvmath/Matrix.h @@ -14,6 +14,46 @@ namespace nv { enum identity_t { identity }; + // 2x2 matrix. + class NVMATH_CLASS Matrix2 + { + public: + Matrix2(); + explicit Matrix2(float f); + explicit Matrix2(identity_t); + Matrix2(const Matrix2 & m); + Matrix2(Vector2::Arg v0, Vector2::Arg v1); + Matrix2(float a, float b, float c, float d); + + float data(uint idx) const; + float & data(uint idx); + float get(uint row, uint col) const; + float operator()(uint row, uint col) const; + float & operator()(uint row, uint col); + + Vector2 row(uint i) const; + Vector2 column(uint i) const; + + void operator*=(float s); + void operator/=(float s); + void operator+=(const Matrix2 & m); + void operator-=(const Matrix2 & m); + + void scale(float s); + void scale(Vector2::Arg s); + float determinant() const; + + private: + float m_data[4]; + }; + + // Solve equation system using LU decomposition and back-substitution. + extern bool solveLU(const Matrix2 & m, const Vector2 & b, Vector2 * x); + + // Solve equation system using Cramer's inverse. + extern bool solveCramer(const Matrix2 & A, const Vector2 & b, Vector2 * x); + + // 3x3 matrix. class NVMATH_CLASS Matrix3 { @@ -52,6 +92,8 @@ namespace nv // Solve equation system using Cramer's inverse. extern bool solveCramer(const Matrix3 & A, const Vector3 & b, Vector3 * x); + extern Matrix3 inverse(const Matrix3 & m); + // 4x4 matrix. class NVMATH_CLASS Matrix @@ -106,7 +148,6 @@ namespace nv // Compute inverse using Gaussian elimination and partial pivoting. extern Matrix inverse(const Matrix & m); - extern Matrix3 inverse(const Matrix3 & m); } // nv namespace diff --git a/thirdparty/thekla_atlas/src/nvmath/Matrix.inl b/thirdparty/thekla_atlas/src/nvmath/Matrix.inl index c0d99d9f..b7319848 100755 --- a/thirdparty/thekla_atlas/src/nvmath/Matrix.inl +++ b/thirdparty/thekla_atlas/src/nvmath/Matrix.inl @@ -8,6 +8,199 @@ namespace nv { + inline Matrix2::Matrix2() {} + + inline Matrix2::Matrix2(float f) + { + for(int i = 0; i < 4; i++) { + m_data[i] = f; + } + } + + inline Matrix2::Matrix2(identity_t) + { + for(int i = 0; i < 2; i++) { + for(int j = 0; j < 2; j++) { + m_data[2*j+i] = (i == j) ? 1.0f : 0.0f; + } + } + } + + inline Matrix2::Matrix2(const Matrix2 & m) + { + for(int i = 0; i < 4; i++) { + m_data[i] = m.m_data[i]; + } + } + + inline Matrix2::Matrix2(Vector2::Arg v0, Vector2::Arg v1) + { + m_data[0] = v0.x; m_data[1] = v0.y; + m_data[2] = v1.x; m_data[3] = v1.y; + } + + inline Matrix2::Matrix2(float a, float b, float c, float d) + { + m_data[0] = a; m_data[1] = b; + m_data[2] = c; m_data[3] = d; + } + + inline float Matrix2::data(uint idx) const + { + nvDebugCheck(idx < 4); + return m_data[idx]; + } + inline float & Matrix2::data(uint idx) + { + nvDebugCheck(idx < 4); + return m_data[idx]; + } + inline float Matrix2::get(uint row, uint col) const + { + nvDebugCheck(row < 2 && col < 2); + return m_data[col * 2 + row]; + } + inline float Matrix2::operator()(uint row, uint col) const + { + nvDebugCheck(row < 2 && col < 2); + return m_data[col * 2 + row]; + } + inline float & Matrix2::operator()(uint row, uint col) + { + nvDebugCheck(row < 2 && col < 2); + return m_data[col * 2 + row]; + } + + inline Vector2 Matrix2::row(uint i) const + { + nvDebugCheck(i < 2); + return Vector2(get(i, 0), get(i, 1)); + } + inline Vector2 Matrix2::column(uint i) const + { + nvDebugCheck(i < 2); + return Vector2(get(0, i), get(1, i)); + } + + inline void Matrix2::operator*=(float s) + { + for(int i = 0; i < 4; i++) { + m_data[i] *= s; + } + } + + inline void Matrix2::operator/=(float s) + { + float is = 1.0f /s; + for(int i = 0; i < 4; i++) { + m_data[i] *= is; + } + } + + inline void Matrix2::operator+=(const Matrix2 & m) + { + for(int i = 0; i < 4; i++) { + m_data[i] += m.m_data[i]; + } + } + + inline void Matrix2::operator-=(const Matrix2 & m) + { + for(int i = 0; i < 4; i++) { + m_data[i] -= m.m_data[i]; + } + } + + inline Matrix2 operator+(const Matrix2 & a, const Matrix2 & b) + { + Matrix2 m = a; + m += b; + return m; + } + + inline Matrix2 operator-(const Matrix2 & a, const Matrix2 & b) + { + Matrix2 m = a; + m -= b; + return m; + } + + inline Matrix2 operator*(const Matrix2 & a, float s) + { + Matrix2 m = a; + m *= s; + return m; + } + + inline Matrix2 operator*(float s, const Matrix2 & a) + { + Matrix2 m = a; + m *= s; + return m; + } + + inline Matrix2 operator/(const Matrix2 & a, float s) + { + Matrix2 m = a; + m /= s; + return m; + } + + inline Matrix2 mul(const Matrix2 & a, const Matrix2 & b) + { + Matrix2 m; + + for(int i = 0; i < 2; i++) { + const float ai0 = a(i,0), ai1 = a(i,1); + m(i, 0) = ai0 * b(0,0) + ai1 * b(1,0); + m(i, 1) = ai0 * b(0,1) + ai1 * b(1,1); + } + + return m; + } + + inline Matrix2 operator*(const Matrix2 & a, const Matrix2 & b) + { + return mul(a, b); + } + + // Transform the given 3d vector with the given matrix. + inline Vector2 transform(const Matrix2 & m, const Vector2 & p) + { + return Vector2(p.x * m(0,0) + p.y * m(0,1), + p.x * m(1,0) + p.y * m(1,1)); + } + + inline void Matrix2::scale(float s) + { + for (int i = 0; i < 4; i++) { + m_data[i] *= s; + } + } + + inline void Matrix2::scale(Vector2::Arg s) + { + m_data[0] *= s.x; m_data[1] *= s.x; + m_data[2] *= s.y; m_data[3] *= s.y; + } + + inline float Matrix2::determinant() const + { + return get(0,0) * get(1,1) - get(0,1) * get(1,0); + } + + // Inverse using Cramer's rule. + inline Matrix2 inverseCramer(const Matrix2 & m) + { + const float det = m.determinant(); + if (equal(det, 0.0f, 0.0f)) { + return Matrix2(0); + } + + return m * (1/det); + } + + inline Matrix3::Matrix3() {} inline Matrix3::Matrix3(float f) @@ -16,7 +209,7 @@ namespace nv m_data[i] = f; } } - + inline Matrix3::Matrix3(identity_t) { for(int i = 0; i < 3; i++) { @@ -536,7 +729,7 @@ namespace nv // Get perspective matrix. inline Matrix perspective(float fovy, float aspect, float zNear, float zFar) { - float xmax = zNear * tan(fovy / 2); + float xmax = zNear * tanf(fovy / 2); float xmin = -xmax; float ymax = xmax / aspect; @@ -548,7 +741,7 @@ namespace nv // Get inverse perspective matrix. inline Matrix perspectiveInverse(float fovy, float aspect, float zNear, float zFar) { - float xmax = zNear * tan(fovy / 2); + float xmax = zNear * tanf(fovy / 2); float xmin = -xmax; float ymax = xmax / aspect; @@ -560,7 +753,7 @@ namespace nv // Get infinite perspective matrix. inline Matrix perspective(float fovy, float aspect, float zNear) { - float x = zNear * tan(fovy / 2); + float x = zNear * tanf(fovy / 2); float y = x / aspect; return frustum( -x, x, -y, y, zNear ); } diff --git a/thirdparty/thekla_atlas/src/nvmath/ProximityGrid.cpp b/thirdparty/thekla_atlas/src/nvmath/ProximityGrid.cpp index 3553e48f..d5ff0b9f 100755 --- a/thirdparty/thekla_atlas/src/nvmath/ProximityGrid.cpp +++ b/thirdparty/thekla_atlas/src/nvmath/ProximityGrid.cpp @@ -65,7 +65,7 @@ void ProximityGrid::init(const Box & box, uint count) { else { // Ideally we want one cell per point. float cellVolume = volume / count; - cellWidth = pow(cellVolume, 1.0f / 3.0f); + cellWidth = powf(cellVolume, 1.0f / 3.0f); } nvDebugCheck(cellWidth != 0); diff --git a/thirdparty/thekla_atlas/src/nvmath/Quaternion.h b/thirdparty/thekla_atlas/src/nvmath/Quaternion.h index dc5219e5..c2890a51 100755 --- a/thirdparty/thekla_atlas/src/nvmath/Quaternion.h +++ b/thirdparty/thekla_atlas/src/nvmath/Quaternion.h @@ -22,6 +22,7 @@ namespace nv Quaternion(Vector4::Arg v); const Quaternion & operator=(Quaternion::Arg v); + const Quaternion & operator*=(float s); Vector4 asVector() const; @@ -47,6 +48,13 @@ namespace nv w = v.w; return *this; } + inline const Quaternion & Quaternion::operator*=(float s) { + x *= s; + y *= s; + z *= s; + w *= s; + return *this; + } inline Vector4 Quaternion::asVector() const { return Vector4(x, y, z, w); } @@ -105,7 +113,7 @@ namespace nv { return scale(q, s); } - + inline Quaternion scale(Quaternion::Arg q, Vector4::Arg s) { return scale(q.asVector(), s); @@ -170,15 +178,15 @@ namespace nv /// Transform vector. inline Vector3 transform(Quaternion::Arg q, Vector3::Arg v) { - //Quaternion t = q * v * conjugate(q); - //return imag(t); + Quaternion t = q * v * conjugate(q); + return imag(t); // Faster method by Fabian Giesen and others: // http://molecularmusings.wordpress.com/2013/05/24/a-faster-quaternion-vector-multiplication/ // http://mollyrocket.com/forums/viewtopic.php?t=833&sid=3a84e00a70ccb046cfc87ac39881a3d0 - Vector3 t = 2 * cross(imag(q), v); - return v + q.w * t + cross(imag(q), t); + //Vector3 t = 2 * cross(imag(q), v); + //return v + q.w * t + cross(imag(q), t); } // @@ Not tested. @@ -207,7 +215,73 @@ namespace nv } } + inline Quaternion fromMatrix(const Matrix3 & m) { + #if 0 // IC: There must be a bug in this code: + if (m(2, 2) < 0) { + if (m(0, 0) < m(1,1)) { + float t = 1 - m(0, 0) - m(1, 1) - m(2, 2); + return Quaternion(t, m(0,1)+m(1,0), m(2,0)+m(0,2), m(1,2)-m(2,1)); + } + else { + float t = 1 - m(0, 0) + m(1, 1) - m(2, 2); + return Quaternion(t, m(0,1) + m(1,0), m(1,2) + m(2,1), m(2,0) - m(0,2)); + } + } + else { + if (m(0, 0) < -m(1, 1)) { + float t = 1 - m(0, 0) - m(1, 1) + m(2, 2); + return Quaternion(t, m(2,0) + m(0,2), m(1,2) + m(2,1), m(0,1) - m(1,0)); + } + else { + float t = 1 + m(0, 0) + m(1, 1) + m(2, 2); + return Quaternion(t, m(1,2) - m(2,1), m(2,0) - m(0,2), m(0,1) - m(1,0)); + } + } + #else + Quaternion q; + float tr = m(0,0) + m(1,1) + m(2,2); + if (tr > 0) { + float s = sqrtf(tr + 1.0f); + float p = 0.5f / s; + q.x = (m(2,1) - m(1,2)) * p; + q.y = (m(0,2) - m(2,0)) * p; + q.z = (m(1,0) - m(0,1)) * p; + q.w = s * 0.5f; + } + else if ((m(0,0) >= m(1,1)) && (m(0,0) >= m(2,2))) { + float s = sqrtf(m(0,0) - m(1,1) - m(2,2) + 1.0f); + float p = 0.5f / s; + q.x = s * 0.5f; + q.y = (m(1,0) + m(0,1)) * p; + q.z = (m(2,0) + m(0,2)) * p; + q.w = (m(2,1) - m(1,2)) * p; + } + else if ((m(1,1) >= m(0,0)) && (m(1,1) >= m(2,2))) { + float s = sqrtf(m(1,1) - m(2,2) - m(0,0) + 1.0f); + float p = 0.5f / s; + q.x = (m(0,1) + m(1,0)) * p; + q.y = s * 0.5f; + q.z = (m(2,1) + m(1,2)) * p; + q.w = (m(0,2) - m(2,0)) * p; + } + else if ((m(2,2) >= m(0,0)) && (m(2,2) >= m(1,1))) { + float s = sqrtf(m(2,2) - m(0,0) - m(1,1) + 1.0f); + float p = 0.5f / s; + q.x = (m(0,2) + m(2,0)) * p; + q.y = (m(1,2) + m(2,1)) * p; + q.z = s * 0.5f; + q.w = (m(1,0) - m(0,1)) * p; + } + return q; + #endif + } + + + + + + } // nv namespace #endif // NV_MATH_QUATERNION_H diff --git a/thirdparty/thekla_atlas/src/nvmath/Vector.h b/thirdparty/thekla_atlas/src/nvmath/Vector.h index 9236f2a5..ad18672a 100755 --- a/thirdparty/thekla_atlas/src/nvmath/Vector.h +++ b/thirdparty/thekla_atlas/src/nvmath/Vector.h @@ -146,6 +146,4 @@ template T to(const nv::Vector2 & v) { NV_COMPILER_CHECK(sizeof(T) template T to(const nv::Vector3 & v) { NV_COMPILER_CHECK(sizeof(T) == sizeof(nv::Vector3)); return T(v.x, v.y, v.z); } template T to(const nv::Vector4 & v) { NV_COMPILER_CHECK(sizeof(T) == sizeof(nv::Vector4)); return T(v.x, v.y, v.z, v.w); } -#include "Vector.inl" - #endif // NV_MATH_VECTOR_H diff --git a/thirdparty/thekla_atlas/src/nvmath/ftoi.h b/thirdparty/thekla_atlas/src/nvmath/ftoi.h index 5a1c8c58..bee15c09 100755 --- a/thirdparty/thekla_atlas/src/nvmath/ftoi.h +++ b/thirdparty/thekla_atlas/src/nvmath/ftoi.h @@ -53,8 +53,7 @@ namespace nv return (val<0) ? ftoi_ceil_xs(val) : ftoi_floor_xs(val); } - -#if NV_USE_SSE +#if NV_CPU_X86 || NV_CPU_X86_64 NV_FORCEINLINE int ftoi_round_sse(float f) { return _mm_cvt_ss2si(_mm_set_ss(f)); @@ -64,6 +63,12 @@ namespace nv return _mm_cvtt_ss2si(_mm_set_ss(f)); } +#endif + + + +#if NV_USE_SSE + NV_FORCEINLINE int ftoi_round(float val) { return ftoi_round_sse(val); } diff --git a/thirdparty/thekla_atlas/src/nvmath/nvmath.h b/thirdparty/thekla_atlas/src/nvmath/nvmath.h index d07300ee..eaf85f42 100755 --- a/thirdparty/thekla_atlas/src/nvmath/nvmath.h +++ b/thirdparty/thekla_atlas/src/nvmath/nvmath.h @@ -14,6 +14,12 @@ #include // finite, isnan #endif +#if NV_CPU_X86 || NV_CPU_X86_64 + //#include + #include +#endif + + // Function linkage #if NVMATH_SHARED @@ -36,33 +42,36 @@ #endif #ifndef NV_USE_SSE - // 1=SSE, 2=SSE2 # if NV_CPU_X86_64 // x64 always supports at least SSE2 # define NV_USE_SSE 2 # elif NV_CC_MSVC && defined(_M_IX86_FP) // Also on x86 with the /arch:SSE flag in MSVC. -# define NV_USE_SSE _M_IX86_FP -# elif defined(__SSE2__) -# define NV_USE_SSE 2 +# define NV_USE_SSE _M_IX86_FP // 1=SSE, 2=SS2 # elif defined(__SSE__) # define NV_USE_SSE 1 +# elif defined(__SSE2__) +# define NV_USE_SSE 2 # else // Otherwise we assume no SSE. # define NV_USE_SSE 0 # endif #endif -#if NV_USE_SSE - #include +// Temporarily disable SSE to make things compile for now +#if TARGET_OS_TV +#undef NV_USE_SSE +#define NV_USE_SSE 0 #endif + // Internally set NV_USE_SIMD when either altivec or sse is available. #if NV_USE_ALTIVEC && NV_USE_SSE # error "Cannot enable both altivec and sse!" #endif + #ifndef PI #define PI float(3.1415926535897932384626433833) #endif @@ -169,10 +178,8 @@ namespace nv { #if NV_OS_WIN32 || NV_OS_XBOX || NV_OS_DURANGO return _finite(f) != 0; -#elif NV_OS_DARWIN || NV_OS_FREEBSD || NV_OS_OPENBSD || NV_OS_ORBIS +#elif NV_OS_DARWIN || NV_OS_FREEBSD || NV_OS_OPENBSD || NV_OS_ORBIS || NV_OS_LINUX return isfinite(f); -#elif NV_OS_LINUX - return finitef(f); #else # error "isFinite not supported" #endif @@ -184,10 +191,8 @@ namespace nv { #if NV_OS_WIN32 || NV_OS_XBOX || NV_OS_DURANGO return _isnan(f) != 0; -#elif NV_OS_DARWIN || NV_OS_FREEBSD || NV_OS_OPENBSD || NV_OS_ORBIS +#elif NV_OS_DARWIN || NV_OS_FREEBSD || NV_OS_OPENBSD || NV_OS_ORBIS || NV_OS_LINUX return isnan(f); -#elif NV_OS_LINUX - return isnanf(f); #else # error "isNan not supported" #endif @@ -221,7 +226,7 @@ namespace nv inline float frac(float f) { - return f - floor(f); + return f - floorf(f); } inline float floatRound(float f) diff --git a/thirdparty/thekla_atlas/src/nvmesh/BaseMesh.cpp b/thirdparty/thekla_atlas/src/nvmesh/BaseMesh.cpp index f17d3b46..d6cdef77 100755 --- a/thirdparty/thekla_atlas/src/nvmesh/BaseMesh.cpp +++ b/thirdparty/thekla_atlas/src/nvmesh/BaseMesh.cpp @@ -1,9 +1,11 @@ // This code is in the public domain -- Ignacio Castaņo #include "BaseMesh.h" -#include "Stream.h" +#include "nvcore/Stream.h" #include "nvmath/TypeSerialization.h" - +/////// Changes in Dust3D Begin ///////////// +#include "Array.inl" +/////// Changes in Dust3D End ///////////// namespace nv { diff --git a/thirdparty/thekla_atlas/src/nvmesh/CMakeLists.txt b/thirdparty/thekla_atlas/src/nvmesh/CMakeLists.txt index b2b1fc06..ae40805f 100755 --- a/thirdparty/thekla_atlas/src/nvmesh/CMakeLists.txt +++ b/thirdparty/thekla_atlas/src/nvmesh/CMakeLists.txt @@ -1,4 +1,5 @@ PROJECT(nvmesh) +set(CMAKE_DEBUG_POSTFIX d) SET(MESH_SRCS nvmesh.h diff --git a/thirdparty/thekla_atlas/src/nvmesh/MeshBuilder.cpp b/thirdparty/thekla_atlas/src/nvmesh/MeshBuilder.cpp index 24d8ddff..cfae58f3 100755 --- a/thirdparty/thekla_atlas/src/nvmesh/MeshBuilder.cpp +++ b/thirdparty/thekla_atlas/src/nvmesh/MeshBuilder.cpp @@ -77,6 +77,7 @@ namespace String name; uint faceCount; + bool doubleSided = false; }; struct Vertex @@ -141,6 +142,7 @@ struct MeshBuilder::PrivateData Array faceArray; uint maxFaceIndexCount; + bool detect_duplicate_faces = true; }; @@ -221,7 +223,7 @@ void MeshBuilder::endGroup() } // Add named material, check for uniquenes. -uint MeshBuilder::addMaterial(const char * name) +uint MeshBuilder::addMaterial(const char * name, bool doubleSided) { uint index; if (d->materialMap.get(name, &index)) { @@ -232,6 +234,7 @@ uint MeshBuilder::addMaterial(const char * name) d->materialMap.add(name, index); Material material(name); + material.doubleSided = doubleSided; d->materialArray.append(material); } return index; @@ -297,7 +300,7 @@ uint MeshBuilder::addVertex(const Vector3 & pos, const Vector3 & nor, const Vect #endif // Return true if the face is valid and was added to the mesh. -bool MeshBuilder::endPolygon() +bool MeshBuilder::endPolygon(bool check_duplicates/*=true*/) { const Face & face = d->faceArray.back(); const uint count = face.indexCount; @@ -353,6 +356,61 @@ bool MeshBuilder::endPolygon() } + if (!invalid) { + + Array faceVertices; + faceVertices.reserve(4); + + for (uint i = 0; i < face.indexCount; i++) { + uint v = d->indexArray[face.firstIndex + i]; + uint p = d->vertexArray[v].pos; + faceVertices.append(d->posArray[p]); + } + + if (check_duplicates) { + + uint faceCount = d->faceArray.count() - 1; + for (uint f = 0; f < faceCount; f++) { + const Face & other = d->faceArray[f]; + + // Skip faces with different vertex count. + if (other.indexCount != face.indexCount) { + continue; + } + + const uint orientationCount = (face.material != NIL && d->materialArray[face.material].doubleSided) ? 2 : 1; + + // Compare face vertices in all valid permutations. + bool duplicateFace; + for (uint orientation = 0; orientation < orientationCount; orientation++) { + for (uint order = 0; order < face.indexCount; order++) { + duplicateFace = true; + for (uint i = 0; i < other.indexCount; i++) { + uint v = d->indexArray[other.firstIndex + i]; + uint p = d->vertexArray[v].pos; + + uint idx = orientation ? (i + order) % face.indexCount : (face.indexCount - 1 - i + order) % face.indexCount; + + if (!equal(faceVertices[idx], d->posArray[p], 0.0f)) { + duplicateFace = false; + break; + } + } + if (duplicateFace) { + goto duplicate; + } + } + } + + duplicate: + if (duplicateFace) { + invalid = true; + break; + } + } + } + } + if (invalid) { d->indexArray.resize(d->indexArray.size() - count); diff --git a/thirdparty/thekla_atlas/src/nvmesh/MeshBuilder.h b/thirdparty/thekla_atlas/src/nvmesh/MeshBuilder.h index 5b3af3fc..196a1933 100755 --- a/thirdparty/thekla_atlas/src/nvmesh/MeshBuilder.h +++ b/thirdparty/thekla_atlas/src/nvmesh/MeshBuilder.h @@ -36,7 +36,7 @@ namespace nv void beginGroup(uint id); void endGroup(); - uint addMaterial(const char * name); + uint addMaterial(const char * name, bool deleteDuplicateFaces); void beginMaterial(uint id); void endMaterial(); @@ -44,7 +44,7 @@ namespace nv uint addVertex(uint p, uint n = NIL, uint t0 = NIL, uint t1 = NIL, uint c0 = NIL, uint c1 = NIL, uint c2 = NIL); uint addVertex(const Vector3 & p); //uint addVertex(const Vector3 & p, const Vector3 & n, const Vector2 & t0 = Vector2(0), const Vector2 & t1 = Vector2(0), const Vector4 & c0 = Vector4(0), const Vector4 & c1 = Vector4(0)); - bool endPolygon(); + bool endPolygon(bool check_duplicates = true); uint weldPositions(); uint weldNormals(); diff --git a/thirdparty/thekla_atlas/src/nvmesh/QuadTriMesh.cpp b/thirdparty/thekla_atlas/src/nvmesh/QuadTriMesh.cpp index 64a071ab..48ea50a5 100755 --- a/thirdparty/thekla_atlas/src/nvmesh/QuadTriMesh.cpp +++ b/thirdparty/thekla_atlas/src/nvmesh/QuadTriMesh.cpp @@ -1,7 +1,10 @@ // This code is in the public domain -- Ignacio Castaņo #include "QuadTriMesh.h" -#include "Stream.h" +#include "nvcore/Stream.h" +/////// Changes in Dust3D Begin ///////////// +#include "Array.inl" +/////// Changes in Dust3D End ///////////// using namespace nv; diff --git a/thirdparty/thekla_atlas/src/nvmesh/param/Atlas.h b/thirdparty/thekla_atlas/src/nvmesh/param/Atlas.h index 8b478207..856ae60c 100755 --- a/thirdparty/thekla_atlas/src/nvmesh/param/Atlas.h +++ b/thirdparty/thekla_atlas/src/nvmesh/param/Atlas.h @@ -157,6 +157,7 @@ namespace nv float scale = 1.0f; uint vertexMapWidth; uint vertexMapHeight; + bool blockAligned = true; private: diff --git a/thirdparty/thekla_atlas/src/nvmesh/param/AtlasPacker.cpp b/thirdparty/thekla_atlas/src/nvmesh/param/AtlasPacker.cpp index f2156899..fd074bd3 100755 --- a/thirdparty/thekla_atlas/src/nvmesh/param/AtlasPacker.cpp +++ b/thirdparty/thekla_atlas/src/nvmesh/param/AtlasPacker.cpp @@ -114,9 +114,12 @@ namespace for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { Color32 color = bitmap.pixel(i, j); - fwrite(&color.r, 1, 1, fp); - fwrite(&color.g, 1, 1, fp); - fwrite(&color.b, 1, 1, fp); + uint8 r = color.r; // current version of apple's llvm compiling for arm64 doesn't like taking the address of a bit-field. Workaround by using the stack + uint8 g = color.g; + uint8 b = color.b; + fwrite(&r, 1, 1, fp); + fwrite(&g, 1, 1, fp); + fwrite(&b, 1, 1, fp); } } @@ -378,6 +381,8 @@ static void computeBoundingBox(Chart * chart, Vector2 * majorAxis, Vector2 * min void AtlasPacker::packCharts(int quality, float texelsPerUnit, bool blockAligned, bool conservative) { + nvDebugCheck(texelsPerUnit > 0.0f); + const uint chartCount = m_atlas->chartCount(); if (chartCount == 0) return; @@ -416,7 +421,7 @@ void AtlasPacker::packCharts(int quality, float texelsPerUnit, bool blockAligned //chartOrderArray[c] = chartArea; // Compute chart scale - float parametricArea = fabs(chart->computeParametricArea()); // @@ There doesn't seem to be anything preventing parametric area to be negative. + float parametricArea = fabsf(chart->computeParametricArea()); // @@ There doesn't seem to be anything preventing parametric area to be negative. if (parametricArea < NV_EPSILON) { // When the parametric area is too small we use a rough approximation to prevent divisions by very small numbers. Vector2 bounds = chart->computeParametricBounds(); @@ -499,7 +504,7 @@ void AtlasPacker::packCharts(int quality, float texelsPerUnit, bool blockAligned if (extents.x > 0) { int cw = ftoi_ceil(extents.x); - if (blockAligned) { + if (blockAligned && chart->blockAligned) { // Align all chart extents to 4x4 blocks, but taking padding into account. if (conservative) { cw = align(cw + 2, 4) - 2; @@ -517,7 +522,7 @@ void AtlasPacker::packCharts(int quality, float texelsPerUnit, bool blockAligned if (extents.y > 0) { int ch = ftoi_ceil(extents.y); - if (blockAligned) { + if (blockAligned && chart->blockAligned) { // Align all chart extents to 4x4 blocks, but taking padding into account. if (conservative) { ch = align(ch + 2, 4) - 2; @@ -613,6 +618,8 @@ void AtlasPacker::packCharts(int quality, float texelsPerUnit, bool blockAligned BitMap chart_bitmap; if (chart->isVertexMapped()) { + chart->blockAligned = false; + // Init all bits to 1. chart_bitmap.resize(ftoi_ceil(chartExtents[c].x), ftoi_ceil(chartExtents[c].y), /*initValue=*/true); @@ -650,10 +657,14 @@ void AtlasPacker::packCharts(int quality, float texelsPerUnit, bool blockAligned } } + if (!chart->blockAligned) { + int k = 1; + } + int best_x, best_y; int best_cw, best_ch; // Includes padding now. int best_r; - findChartLocation(quality, &chart_bitmap, chartExtents[c], w, h, &best_x, &best_y, &best_cw, &best_ch, &best_r); + findChartLocation(quality, &chart_bitmap, chartExtents[c], w, h, &best_x, &best_y, &best_cw, &best_ch, &best_r, chart->blockAligned); /*if (w < best_x + best_cw || h < best_y + best_ch) { @@ -849,7 +860,7 @@ void AtlasPacker::packCharts(int quality, float texelsPerUnit, bool blockAligned // is occupied at this point. At the end we have many small charts and a large atlas with sparse holes. Finding those holes randomly is slow. A better approach would be to // start stacking large charts as if they were tetris pieces. Once charts get small try to place them randomly. It may be interesting to try a intermediate strategy, first try // along one axis and then try exhaustively along that axis. -void AtlasPacker::findChartLocation(int quality, const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r) +void AtlasPacker::findChartLocation(int quality, const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r, bool blockAligned) { int attempts = 256; if (quality == 1) attempts = 4096; @@ -859,20 +870,23 @@ void AtlasPacker::findChartLocation(int quality, const BitMap * bitmap, Vector2: if (quality == 0 || w*h < attempts) { - findChartLocation_bruteForce(bitmap, extents, w, h, best_x, best_y, best_w, best_h, best_r); + findChartLocation_bruteForce(bitmap, extents, w, h, best_x, best_y, best_w, best_h, best_r, blockAligned); } else { - findChartLocation_random(bitmap, extents, w, h, best_x, best_y, best_w, best_h, best_r, attempts); + findChartLocation_random(bitmap, extents, w, h, best_x, best_y, best_w, best_h, best_r, attempts, blockAligned); } } #define BLOCK_SIZE 4 -void AtlasPacker::findChartLocation_bruteForce(const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r) +void AtlasPacker::findChartLocation_bruteForce(const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r, bool blockAligned) { int best_metric = INT_MAX; + int step_size = blockAligned ? BLOCK_SIZE : 1; + + // Try two different orientations. for (int r = 0; r < 2; r++) { @@ -880,9 +894,9 @@ void AtlasPacker::findChartLocation_bruteForce(const BitMap * bitmap, Vector2::A int ch = bitmap->height(); if (r & 1) swap(cw, ch); - for (int y = 0; y <= h + 1; y += BLOCK_SIZE) // + 1 to extend atlas in case atlas full. + for (int y = 0; y <= h + 1; y += step_size) // + 1 to extend atlas in case atlas full. { - for (int x = 0; x <= w + 1; x += BLOCK_SIZE) // + 1 not really necessary here. + for (int x = 0; x <= w + 1; x += step_size) // + 1 not really necessary here. { // Early out. int area = max(w, x+cw) * max(h, y+ch); @@ -923,7 +937,7 @@ done: } -void AtlasPacker::findChartLocation_random(const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r, int minTrialCount) +void AtlasPacker::findChartLocation_random(const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r, int minTrialCount, bool blockAligned) { int best_metric = INT_MAX; @@ -933,8 +947,10 @@ void AtlasPacker::findChartLocation_random(const BitMap * bitmap, Vector2::Arg e int x = m_rand.getRange(w + 1); // + 1 to extend atlas in case atlas full. We may want to use a higher number to increase probability of extending atlas. int y = m_rand.getRange(h + 1); // + 1 to extend atlas in case atlas full. - x = align(x, BLOCK_SIZE); - y = align(y, BLOCK_SIZE); + if (blockAligned) { + x = align(x, BLOCK_SIZE); + y = align(y, BLOCK_SIZE); + } int cw = bitmap->width(); int ch = bitmap->height(); diff --git a/thirdparty/thekla_atlas/src/nvmesh/param/AtlasPacker.h b/thirdparty/thekla_atlas/src/nvmesh/param/AtlasPacker.h index 2d305f38..08aede94 100755 --- a/thirdparty/thekla_atlas/src/nvmesh/param/AtlasPacker.h +++ b/thirdparty/thekla_atlas/src/nvmesh/param/AtlasPacker.h @@ -28,9 +28,9 @@ namespace nv private: - void findChartLocation(int quality, const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r); - void findChartLocation_bruteForce(const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r); - void findChartLocation_random(const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r, int minTrialCount); + void findChartLocation(int quality, const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r, bool blockAligned); + void findChartLocation_bruteForce(const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r, bool blockAligned); + void findChartLocation_random(const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r, int minTrialCount, bool blockAligned); void drawChartBitmapDilate(const Chart * chart, BitMap * bitmap, int padding); void drawChartBitmap(const Chart * chart, BitMap * bitmap, const Vector2 & scale, const Vector2 & offset); diff --git a/thirdparty/thekla_atlas/src/nvmesh/param/ConformalMap.cpp b/thirdparty/thekla_atlas/src/nvmesh/param/ConformalMap.cpp index 81ab6455..15431f53 100755 --- a/thirdparty/thekla_atlas/src/nvmesh/param/ConformalMap.cpp +++ b/thirdparty/thekla_atlas/src/nvmesh/param/ConformalMap.cpp @@ -2,7 +2,9 @@ #include "ConformalMap.h" #include "Util.h" - +/////// Changes in Dust3D Begin ///////////// +#include "Array.inl" +/////// Changes in Dust3D End ///////////// #include #include #include diff --git a/thirdparty/thekla_atlas/src/nvmesh/raster/ClippedTriangle.h b/thirdparty/thekla_atlas/src/nvmesh/raster/ClippedTriangle.h index 0947d485..ecb40c97 100755 --- a/thirdparty/thekla_atlas/src/nvmesh/raster/ClippedTriangle.h +++ b/thirdparty/thekla_atlas/src/nvmesh/raster/ClippedTriangle.h @@ -116,7 +116,7 @@ namespace nv centroidx += f * (v[k].x + v[k+1].x); centroidy += f * (v[k].y + v[k+1].y); } - m_area = 0.5f * fabs(m_area); + m_area = 0.5f * fabsf(m_area); if (m_area==0) { m_centroid = Vector2(0.0f); } else { diff --git a/thirdparty/thekla_atlas/src/nvmesh/weld/Snap.cpp b/thirdparty/thekla_atlas/src/nvmesh/weld/Snap.cpp index b6bff4d8..4ef83992 100755 --- a/thirdparty/thekla_atlas/src/nvmesh/weld/Snap.cpp +++ b/thirdparty/thekla_atlas/src/nvmesh/weld/Snap.cpp @@ -1,7 +1,10 @@ // This code is in the public domain -- castanyo@yahoo.es #include - +/////// Changes in Dust3D Begin ///////////// +#include "Array.inl" +#include "nvmath/Box.inl" +/////// Changes in Dust3D End ///////////// #include #include #include diff --git a/thirdparty/thekla_atlas/src/nvmesh/weld/VertexWeld.cpp b/thirdparty/thekla_atlas/src/nvmesh/weld/VertexWeld.cpp index 2ba4dcae..917fee29 100755 --- a/thirdparty/thekla_atlas/src/nvmesh/weld/VertexWeld.cpp +++ b/thirdparty/thekla_atlas/src/nvmesh/weld/VertexWeld.cpp @@ -2,7 +2,9 @@ #include #include - +/////// Changes in Dust3D Begin ///////////// +#include "Array.inl" +/////// Changes in Dust3D End ///////////// #include #include diff --git a/thirdparty/thekla_atlas/src/nvmesh/weld/Weld.h b/thirdparty/thekla_atlas/src/nvmesh/weld/Weld.h index e6155394..d9815bf8 100755 --- a/thirdparty/thekla_atlas/src/nvmesh/weld/Weld.h +++ b/thirdparty/thekla_atlas/src/nvmesh/weld/Weld.h @@ -111,7 +111,7 @@ inline void reverseXRefs(Array & xrefs, uint count) // struct WeldN { - uint vertexSize; + const uint vertexSize; WeldN(uint n) : vertexSize(n) {} diff --git a/thirdparty/thekla_atlas/src/thekla/thekla_atlas.h b/thirdparty/thekla_atlas/src/thekla/thekla_atlas.h index 1d0716e7..239dfcf8 100755 --- a/thirdparty/thekla_atlas/src/thekla/thekla_atlas.h +++ b/thirdparty/thekla_atlas/src/thekla/thekla_atlas.h @@ -1,21 +1,39 @@ // Thekla Atlas Generator +#if THEKLA_ATLAS_SHARED +#if _WIN32 +#define DLL_EXPORT __declspec(dllexport) +#define DLL_IMPORT __declspec(dllimport) +#else +#define DLL_EXPORT +#define DLL_IMPORT +#endif +#ifdef THEKLA_ATLAS_EXPORTS +#define THEKLA_ATLAS_API DLL_EXPORT +#else +#define THEKLA_ATLAS_API DLL_IMPORT +#endif +#else +#define THEKLA_ATLAS_API +#endif + + namespace Thekla { enum Atlas_Charter { - Atlas_Charter_Witness, // Options: threshold - Atlas_Charter_Extract, // Options: --- + Atlas_Charter_Witness, + Atlas_Charter_Extract, Atlas_Charter_Default = Atlas_Charter_Witness }; enum Atlas_Mapper { - Atlas_Mapper_LSCM, // Options: --- + Atlas_Mapper_LSCM, Atlas_Mapper_Default = Atlas_Mapper_LSCM }; enum Atlas_Packer { - Atlas_Packer_Witness, // Options: texel_area + Atlas_Packer_Witness, Atlas_Packer_Default = Atlas_Packer_Witness }; @@ -92,11 +110,11 @@ enum Atlas_Error { Atlas_Error_Not_Implemented, }; -void atlas_set_default_options(Atlas_Options * options); +THEKLA_ATLAS_API void atlas_set_default_options(Atlas_Options * options); -Atlas_Output_Mesh * atlas_generate(const Atlas_Input_Mesh * input, const Atlas_Options * options, Atlas_Error * error); +THEKLA_ATLAS_API Atlas_Output_Mesh * atlas_generate(const Atlas_Input_Mesh * input, const Atlas_Options * options, Atlas_Error * error); -void atlas_free(Atlas_Output_Mesh * output); +THEKLA_ATLAS_API void atlas_free(Atlas_Output_Mesh * output); /* diff --git a/thirdparty/thekla_atlas/src/thekla/thekla_atlas_test.cpp b/thirdparty/thekla_atlas/src/thekla/thekla_atlas_test.cpp index 02fbfe6e..296fc390 100755 --- a/thirdparty/thekla_atlas/src/thekla/thekla_atlas_test.cpp +++ b/thirdparty/thekla_atlas/src/thekla/thekla_atlas_test.cpp @@ -54,5 +54,4 @@ int main(int argc, char * argv[]) { atlas_free(output_mesh); return 0; - } diff --git a/thirdparty/thekla_atlas/src/thekla/thekla_mesh_load.h b/thirdparty/thekla_atlas/src/thekla/thekla_mesh_load.h index 6dcf769d..4b721152 100755 --- a/thirdparty/thekla_atlas/src/thekla/thekla_mesh_load.h +++ b/thirdparty/thekla_atlas/src/thekla/thekla_mesh_load.h @@ -58,36 +58,63 @@ Obj_Mesh * obj_mesh_load(const char * filename, const Obj_Load_Options * options return NULL; } - printf("%lu shapes\n", shapes.size()); - printf("%lu materials\n", materials.size()); + printf("%zu shapes\n", shapes.size()); + printf("%zu materials\n", materials.size()); assert(shapes.size() > 0); Obj_Mesh* mesh = new Obj_Mesh(); - mesh->vertex_count = shapes[0].mesh.positions.size() / 3; - mesh->vertex_array = new Obj_Vertex[mesh->vertex_count]; - for (int nvert = 0; nvert < mesh->vertex_count; nvert++) { - mesh->vertex_array[nvert].position[0] = shapes[0].mesh.positions[nvert * 3]; - mesh->vertex_array[nvert].position[1] = shapes[0].mesh.positions[nvert * 3 + 1]; - mesh->vertex_array[nvert].position[2] = shapes[0].mesh.positions[nvert * 3 + 2]; - mesh->vertex_array[nvert].normal[0] = shapes[0].mesh.normals[nvert * 3]; - mesh->vertex_array[nvert].normal[1] = shapes[0].mesh.normals[nvert * 3 + 1]; - mesh->vertex_array[nvert].normal[2] = shapes[0].mesh.normals[nvert * 3 + 2]; - mesh->vertex_array[nvert].uv[0] = 0; - mesh->vertex_array[nvert].uv[1] = 0; - mesh->vertex_array[nvert].first_colocal = nvert; + // Count vertices and faces in all shapes. + mesh->vertex_count = 0; + mesh->face_count = 0; + for (int s = 0; s < shapes.size(); s++) { + mesh->vertex_count += (int)shapes[s].mesh.positions.size() / 3; + mesh->face_count += (int)shapes[s].mesh.indices.size() / 3; } - mesh->face_count = shapes[0].mesh.indices.size() / 3; - mesh->face_array = new Obj_Face[mesh->face_count]; - for (int nface = 0; nface < mesh->face_count; nface++) { - mesh->face_array[nface].material_index = 0; - mesh->face_array[nface].vertex_index[0] = shapes[0].mesh.indices[nface * 3]; - mesh->face_array[nface].vertex_index[1] = shapes[0].mesh.indices[nface * 3 + 1]; - mesh->face_array[nface].vertex_index[2] = shapes[0].mesh.indices[nface * 3 + 2]; + mesh->vertex_array = new Obj_Vertex[mesh->vertex_count]; + for (int s = 0, nv = 0; s < shapes.size(); s++) { + const int shape_vertex_count = (int)shapes[s].mesh.positions.size() / 3; + + for (int v = 0; v < shape_vertex_count; v++) { + mesh->vertex_array[nv+v].position[0] = shapes[s].mesh.positions[v * 3 + 0]; + mesh->vertex_array[nv+v].position[1] = shapes[s].mesh.positions[v * 3 + 1]; + mesh->vertex_array[nv+v].position[2] = shapes[s].mesh.positions[v * 3 + 2]; + mesh->vertex_array[nv+v].normal[0] = shapes[s].mesh.normals[v * 3 + 0]; + mesh->vertex_array[nv+v].normal[1] = shapes[s].mesh.normals[v * 3 + 1]; + mesh->vertex_array[nv+v].normal[2] = shapes[s].mesh.normals[v * 3 + 2]; + mesh->vertex_array[nv+v].uv[0] = shapes[s].mesh.texcoords[v * 3 + 0]; // The input UVs are provided as a hint to the chart generator. + mesh->vertex_array[nv+v].uv[1] = shapes[s].mesh.texcoords[v * 3 + 1]; + mesh->vertex_array[nv+v].first_colocal = nv+v; + + // Link colocals. You probably want to do this more efficiently! Sort by one axis or use a hash or grid. + for (int vv = 0; vv < v; vv++) { + if (mesh->vertex_array[nv+v].position[0] == mesh->vertex_array[nv+vv].position[0] && + mesh->vertex_array[nv+v].position[1] == mesh->vertex_array[nv+vv].position[1] && + mesh->vertex_array[nv+v].position[2] == mesh->vertex_array[nv+vv].position[2]) + { + mesh->vertex_array[nv+v].first_colocal = nv+vv; + } + } + } + nv += shape_vertex_count; } + mesh->face_array = new Obj_Face[mesh->face_count]; + for (int s = 0, nf = 0; s < shapes.size(); s++) { + const int shape_face_count = (int)shapes[s].mesh.indices.size() / 3; + for (int f = 0; f < shape_face_count; f++) { + mesh->face_array[nf+f].material_index = 0; + mesh->face_array[nf+f].vertex_index[0] = shapes[s].mesh.indices[f * 3 + 0]; + mesh->face_array[nf+f].vertex_index[1] = shapes[s].mesh.indices[f * 3 + 1]; + mesh->face_array[nf+f].vertex_index[2] = shapes[s].mesh.indices[f * 3 + 2]; + } + nf += shape_face_count; + } + + // @@ Add support for obj materials! Charter also uses material boundaries as a hint to cut charts. + printf("Reading %d verts\n", mesh->vertex_count); printf("Reading %d triangles\n", mesh->face_count); diff --git a/thirdparty/thekla_atlas/theklaAtlasConfig.cmake.in b/thirdparty/thekla_atlas/theklaAtlasConfig.cmake.in new file mode 100755 index 00000000..3c4b7653 --- /dev/null +++ b/thirdparty/thekla_atlas/theklaAtlasConfig.cmake.in @@ -0,0 +1,21 @@ +set(THEKLA_ATLAS_INCLUDE_DIRS "@CMAKE_INSTALL_PREFIX@/include") +set(THEKLA_ATLAS_LIBRARIES @CMAKE_INSTALL_PREFIX@/lib/static/libthekla_atlas.lib + @CMAKE_INSTALL_PREFIX@/lib/static/nvcore.lib + @CMAKE_INSTALL_PREFIX@/lib/static/nvimage.lib + @CMAKE_INSTALL_PREFIX@/lib/static/nvmath.lib + @CMAKE_INSTALL_PREFIX@/lib/static/nvmesh.lib +) +set(THEKLA_ATLAS_LIBRARIES_DEBUG @CMAKE_INSTALL_PREFIX@/lib/static/libTHEKLA_ATLAS@CMAKE_DEBUG_POSTFIX@.lib + @CMAKE_INSTALL_PREFIX@/lib/static/nvcore@CMAKE_DEBUG_POSTFIX@.lib + @CMAKE_INSTALL_PREFIX@/lib/static/nvimage@CMAKE_DEBUG_POSTFIX@.lib + @CMAKE_INSTALL_PREFIX@/lib/static/nvmath@CMAKE_DEBUG_POSTFIX@.lib + @CMAKE_INSTALL_PREFIX@/lib/static/nvmesh@CMAKE_DEBUG_POSTFIX@.lib +) +set(THEKLA_ATLAS_LIBRARIES_RELEASE @CMAKE_INSTALL_PREFIX@/lib/static/libthekla_atlas.lib + @CMAKE_INSTALL_PREFIX@/lib/static/nvcore.lib + @CMAKE_INSTALL_PREFIX@/lib/static/nvimage.lib + @CMAKE_INSTALL_PREFIX@/lib/static/nvmath.lib + @CMAKE_INSTALL_PREFIX@/lib/static/nvmesh.lib +) +set(THEKLA_ATLAS_COMPILE_DEFINITIONS @COMPILE_DEFINITIONS@) +set(THEKLA_ATLAS_FOUND 1)