Update to the latest thekla_atlas

master
Jeremy Hu 2018-10-11 08:56:09 +08:00
parent a2770ecd8e
commit 1623d7d782
47 changed files with 1172 additions and 149 deletions

View File

@ -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

View File

@ -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)

View File

@ -179,6 +179,4 @@ namespace nv
} // nv namespace
#include "Array.inl"
#endif // NV_CORE_ARRAY_H

View File

@ -12,6 +12,9 @@
#include <string.h> // memmove
#include <new> // for placement new
/////// Changes in Dust3D Begin /////////////
#include "Vector.inl"
/////// Changes in Dust3D End /////////////
namespace nv

View File

@ -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<uint> m_wordArray;

View File

@ -1,4 +1,5 @@
PROJECT(nvcore)
set(CMAKE_DEBUG_POSTFIX d)
SET(CORE_SRCS
nvcore.h

View File

@ -106,6 +106,7 @@ 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
@ -785,7 +789,7 @@ namespace
}
#endif // defined(NV_HAVE_EXECINFO_H)
exit(0);
exit(EXIT_FAILURE + 2);
}
#endif // defined(NV_HAVE_SIGNAL_H)
@ -933,14 +937,23 @@ 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()) {
@ -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,6 +1264,7 @@ 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
}

View File

@ -12,6 +12,8 @@
# ifdef __APPLE__
# include <TargetConditionals.h>
# include <signal.h>
# include <sys/types.h> // getpid
# include <unistd.h>
# 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<uint64>(ptr) < 0x10000ULL) return false;
if (reinterpret_cast<uint64>(ptr) >= 0x000007FFFFFEFFFFULL) return false;
#elif NV_CPU_ARM_64
if (ptr == NULL) return true;
if (reinterpret_cast<uint64>(ptr) < 0x10000ULL) return false;
if (reinterpret_cast<uint64>(ptr) >= 0x000007FFFFFEFFFFULL) return false;
#else
if (reinterpret_cast<uintptr_t>(ptr) == 0xcccccccc) return false;
if (reinterpret_cast<uintptr_t>(ptr) == 0xcdcdcdcd) return false;
if (reinterpret_cast<uintptr_t>(ptr) == 0xdddddddd) return false;
if (reinterpret_cast<uintptr_t>(ptr) == 0xffffffff) return false;
if (reinterpret_cast<uint32>(ptr) == 0xcccccccc) return false;
if (reinterpret_cast<uint32>(ptr) == 0xcdcdcdcd) return false;
if (reinterpret_cast<uint32>(ptr) == 0xdddddddd) return false;
if (reinterpret_cast<uint32>(ptr) == 0xffffffff) return false;
#endif
return true;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -5,10 +5,12 @@
#include "Utils.h"
#include <stdlib.h>
/////// Changes in Dust3D Begin /////////////
#if defined(__APPLE__)
#else
#include <malloc.h>
#endif
/////// Changes in Dust3D End /////////////
#define USE_EFENCE 0

View File

@ -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;

View File

@ -125,19 +125,22 @@ 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;
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.

View File

@ -72,6 +72,11 @@ namespace nv
//template <> inline uint32 U32<uint8>(uint8 x) { return x; }
template <> inline uint32 U32<int8>(int8 x) { nvDebugCheck(x >= 0); return (uint32)x; }
#if NV_OS_DARWIN
template <> inline uint32 U32<unsigned long>(unsigned long x) { nvDebugCheck(x <= NV_UINT32_MAX); return (uint32)x; }
template <> inline uint32 U32<long>(long x) { nvDebugCheck(x >= 0 && x <= NV_UINT32_MAX); return (uint32)x; }
#endif
// int32 casts:
template <typename T> inline int32 I32(T x) { return x; }
template <> inline int32 I32<uint64>(uint64 x) { nvDebugCheck(x <= NV_INT32_MAX); return (int32)x; }
@ -120,11 +125,11 @@ namespace nv
// int8 casts:
template <typename T> inline int8 I8(T x) { return x; }
template <> inline int8 I8<uint64>(uint64 x) { nvDebugCheck(x <= NV_INT8_MAX); return (int8)x; }
template <> inline int8 I8<int64>(int64 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_UINT8_MAX); return (int8)x; }
template <> inline int8 I8<int64>(int64 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_INT8_MAX); return (int8)x; }
template <> inline int8 I8<uint32>(uint32 x) { nvDebugCheck(x <= NV_INT8_MAX); return (int8)x; }
template <> inline int8 I8<int32>(int32 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_UINT8_MAX); return (int8)x; }
template <> inline int8 I8<int32>(int32 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_INT8_MAX); return (int8)x; }
template <> inline int8 I8<uint16>(uint16 x) { nvDebugCheck(x <= NV_INT8_MAX); return (int8)x; }
template <> inline int8 I8<int16>(int16 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_UINT8_MAX); return (int8)x; }
template <> inline int8 I8<int16>(int16 x) { nvDebugCheck(x >= NV_INT8_MIN && x <= NV_INT8_MAX); return (int8)x; }
template <> inline int8 I8<uint8>(uint8 x) { nvDebugCheck(x <= NV_INT8_MAX); return (int8)x; }
//template <> inline int8 I8<int8>(int8 x) { return x; }

View File

@ -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 <typename T, size_t N> char (&ArraySizeHelper(T (&array)[N]))[N];
NV_STRING_JOIN3(AtStartup_, __LINE__, Instance); \
}
namespace nv {
template <typename F>
struct ScopeExit {
ScopeExit(F f) : f(f) {}
~ScopeExit() { f(); }
F f;
};
template <typename F>
ScopeExit<F> MakeScopeExit(F f) {
return ScopeExit<F>(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 <typename T, size_t N> 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)

View File

@ -1,4 +1,5 @@
PROJECT(nvimage)
set(CMAKE_DEBUG_POSTFIX d)
SET(IMAGE_SRCS
nvimage.h

View File

@ -7,6 +7,10 @@
#include "nvimage.h"
#include "nvcore/Debug.h"
#if NV_USE_ALTIVEC
#undef pixel
#endif
namespace nv
{
class Color32;

View File

@ -99,7 +99,5 @@ namespace nv
} // nv namespace
#include "Box.inl"
#endif // NV_MATH_BOX_H

View File

@ -1,4 +1,5 @@
PROJECT(nvmath)
set(CMAKE_DEBUG_POSTFIX d)
SET(MATH_SRCS
nvmath.h

View File

@ -7,12 +7,9 @@
#include "nvcore/RadixSort.h"
#include "nvcore/Array.inl"
using namespace nv;
#include <float.h>
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<Vector2> & input, Array<Vector2> & 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<Vector2> & input, Array<Vector2> & 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<Vector2> & input, Array<Vector2> & output, float
output.popBack();
}
void nv::reduceConvexHullToNSides(Array<Vector2> *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<Vector2> &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<Vector2> *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<Vector2> & 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<Vector2> & vertices, const Array<uint> & edges, Array<Vector2> & 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<Vector2> & 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<uint> & 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<Vector2> & hullVertices, uint edgeCount, Array<Vector2> * 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<uint> edges(edgeCount);
// Init edge sequence.
for (uint i = 0; i < edgeCount; i++) {
edges.append(i);
}
// Traverse sequence testing all
Array<Vector2> 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<Vector2> & input, Array<uint> * 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<int> polygonVertices;
Array<float> 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);
}
}
}

View File

@ -12,6 +12,16 @@ namespace nv {
void convexHull(const Array<Vector2> & input, Array<Vector2> & output, float epsilon = 0);
//ACS: moved these down from collision_volume.cpp
bool isClockwise(const Array<Vector2> & input);
void reduceConvexHullToNSides(Array<Vector2> *input, uint num_sides);
void flipWinding(Array<Vector2> *input);
bool bestFitPolygon(const Array<Vector2> & input, uint vertexCount, Array<Vector2> * output);
// Basic ear-clipping algorithm.
void triangulate(const Array<Vector2> & input, Array<uint> * output);
} // namespace nv
#endif // NV_MATH_CONVEXHULL_H

View File

@ -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 */

View File

@ -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

View File

@ -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)
@ -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 );
}

View File

@ -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);

View File

@ -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); }
@ -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,6 +215,72 @@ 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

View File

@ -146,6 +146,4 @@ template <typename T> T to(const nv::Vector2 & v) { NV_COMPILER_CHECK(sizeof(T)
template <typename T> T to(const nv::Vector3 & v) { NV_COMPILER_CHECK(sizeof(T) == sizeof(nv::Vector3)); return T(v.x, v.y, v.z); }
template <typename T> 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

View File

@ -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);
}

View File

@ -14,6 +14,12 @@
#include <float.h> // finite, isnan
#endif
#if NV_CPU_X86 || NV_CPU_X86_64
//#include <intrin.h>
#include <xmmintrin.h>
#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 <xmmintrin.h>
// 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)

View File

@ -1,9 +1,11 @@
// This code is in the public domain -- Ignacio Castaño <castano@gmail.com>
#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
{

View File

@ -1,4 +1,5 @@
PROJECT(nvmesh)
set(CMAKE_DEBUG_POSTFIX d)
SET(MESH_SRCS
nvmesh.h

View File

@ -77,6 +77,7 @@ namespace
String name;
uint faceCount;
bool doubleSided = false;
};
struct Vertex
@ -141,6 +142,7 @@ struct MeshBuilder::PrivateData
Array<Face> 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<Vector3> 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);

View File

@ -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();

View File

@ -1,7 +1,10 @@
// This code is in the public domain -- Ignacio Castaño <castano@gmail.com>
#include "QuadTriMesh.h"
#include "Stream.h"
#include "nvcore/Stream.h"
/////// Changes in Dust3D Begin /////////////
#include "Array.inl"
/////// Changes in Dust3D End /////////////
using namespace nv;

View File

@ -157,6 +157,7 @@ namespace nv
float scale = 1.0f;
uint vertexMapWidth;
uint vertexMapHeight;
bool blockAligned = true;
private:

View File

@ -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();

View File

@ -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);

View File

@ -2,7 +2,9 @@
#include "ConformalMap.h"
#include "Util.h"
/////// Changes in Dust3D Begin /////////////
#include "Array.inl"
/////// Changes in Dust3D End /////////////
#include <nvmath/Sparse.h>
#include <nvmath/Vector.inl>
#include <nvmath/Solver.h>

View File

@ -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 {

View File

@ -1,7 +1,10 @@
// This code is in the public domain -- castanyo@yahoo.es
#include <nvcore/RadixSort.h>
/////// Changes in Dust3D Begin /////////////
#include "Array.inl"
#include "nvmath/Box.inl"
/////// Changes in Dust3D End /////////////
#include <nvmesh/weld/Snap.h>
#include <nvmesh/TriMesh.h>
#include <nvmesh/geometry/Bounds.h>

View File

@ -2,7 +2,9 @@
#include <nvmesh/TriMesh.h>
#include <nvmesh/QuadTriMesh.h>
/////// Changes in Dust3D Begin /////////////
#include "Array.inl"
/////// Changes in Dust3D End /////////////
#include <nvmesh/weld/VertexWeld.h>
#include <nvmesh/weld/Weld.h>

View File

@ -111,7 +111,7 @@ inline void reverseXRefs(Array<uint> & xrefs, uint count)
//
struct WeldN
{
uint vertexSize;
const uint vertexSize;
WeldN(uint n) : vertexSize(n) {}

View File

@ -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);
/*

View File

@ -54,5 +54,4 @@ int main(int argc, char * argv[]) {
atlas_free(output_mesh);
return 0;
}

View File

@ -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);

View File

@ -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)