diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 5cceff72..b96f18ad 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -2,6 +2,10 @@ Changes for PolyVox version 0.3 =============================== This release has focused on... (some introduction here). +Error Handling +-------------- +PolyVox now has it's own POLYVOX_ASSERT() macro rather than using the standard assert(). This has some advantages such as allowing a message to be printed and providing file/line information, and it is also possible to enable/disable it independantly of whether you are making a debug or release build + Volume wrap modes ----------------- This release has seen a overhaul of the way PolyVox handles voxel positions which are outside of the volume. It used to be the case that you could specify a border value which would be returned whenever an out-of-volume access was performed, but this was not flexible enough for all use cases. You can now choose between different wrapping modes (border, clamp, etc) and apply them to both the volume itself or to samplers. If you have multiple samplers pointing at the same volume then you can choose to have different wrap modes for each of them. diff --git a/CMakeLists.txt b/CMakeLists.txt index fd5883b6..8af58ba6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ PROJECT(PolyVox) SET(POLYVOX_VERSION_MAJOR "0") SET(POLYVOX_VERSION_MINOR "2") -SET(POLYVOX_VERSION_PATCH "0") +SET(POLYVOX_VERSION_PATCH "1") SET(POLYVOX_VERSION "${POLYVOX_VERSION_MAJOR}.${POLYVOX_VERSION_MINOR}.${POLYVOX_VERSION_PATCH}" CACHE STRING "PolyVox version") MARK_AS_ADVANCED(FORCE POLYVOX_VERSION) @@ -69,6 +69,10 @@ if(CMAKE_CXX_COMPILER MATCHES "clang") ADD_DEFINITIONS(-std=c++0x) #Enable C++0x mode endif() +if(NOT MSVC) #This is causing problems in Visual Studio so disable it for now + INCLUDE(cmake/Modules/CheckCXX11Features.cmake) +endif() + ADD_SUBDIRECTORY(library) OPTION(ENABLE_EXAMPLES "Should the examples be built" ON) diff --git a/cmake/Modules/CheckCXX11Features.cmake b/cmake/Modules/CheckCXX11Features.cmake new file mode 100644 index 00000000..b3158f4c --- /dev/null +++ b/cmake/Modules/CheckCXX11Features.cmake @@ -0,0 +1,133 @@ +# - Check which parts of the C++11 standard the compiler supports +# +# When found it will set the following variables +# +# CXX11_COMPILER_FLAGS - the compiler flags needed to get C++11 features +# +# HAS_CXX11_AUTO - auto keyword +# HAS_CXX11_AUTO_RET_TYPE - function declaration with deduced return types +# HAS_CXX11_CLASS_OVERRIDE - override and final keywords for classes and methods +# HAS_CXX11_CONSTEXPR - constexpr keyword +# HAS_CXX11_CSTDINT_H - cstdint header +# HAS_CXX11_DECLTYPE - decltype keyword +# HAS_CXX11_FUNC - __func__ preprocessor constant +# HAS_CXX11_INITIALIZER_LIST - initializer list +# HAS_CXX11_LAMBDA - lambdas +# HAS_CXX11_LIB_REGEX - regex library +# HAS_CXX11_LONG_LONG - long long signed & unsigned types +# HAS_CXX11_NULLPTR - nullptr +# HAS_CXX11_RVALUE_REFERENCES - rvalue references +# HAS_CXX11_SIZEOF_MEMBER - sizeof() non-static members +# HAS_CXX11_STATIC_ASSERT - static_assert() +# HAS_CXX11_VARIADIC_TEMPLATES - variadic templates + +#============================================================================= +# Copyright 2011,2012 Rolf Eike Beer +# Copyright 2012 Andreas Weis +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +if (NOT CMAKE_CXX_COMPILER_LOADED) + message(FATAL_ERROR "CheckCXX11Features modules only works if language CXX is enabled") +endif () + +cmake_minimum_required(VERSION 2.8.3) + +# +### Check for needed compiler flags +# +include(CheckCXXCompilerFlag) +check_cxx_compiler_flag("-std=c++11" _HAS_CXX11_FLAG) +if (NOT _HAS_CXX11_FLAG) + check_cxx_compiler_flag("-std=c++0x" _HAS_CXX0X_FLAG) +endif () + +if (_HAS_CXX11_FLAG) + set(CXX11_COMPILER_FLAGS "-std=c++11") +elseif (_HAS_CXX0X_FLAG) + set(CXX11_COMPILER_FLAGS "-std=c++0x") +endif () + +function(cxx11_check_feature FEATURE_NAME RESULT_VAR) + if (NOT DEFINED ${RESULT_VAR}) + set(_bindir "${CMAKE_CURRENT_BINARY_DIR}/cxx11_${FEATURE_NAME}") + + set(_SRCFILE_BASE ${CMAKE_CURRENT_LIST_DIR}/CheckCXX11Features/cxx11-test-${FEATURE_NAME}) + set(_LOG_NAME "\"${FEATURE_NAME}\"") + message(STATUS "Checking C++11 support for ${_LOG_NAME}") + + set(_SRCFILE "${_SRCFILE_BASE}.cpp") + set(_SRCFILE_FAIL "${_SRCFILE_BASE}_fail.cpp") + set(_SRCFILE_FAIL_COMPILE "${_SRCFILE_BASE}_fail_compile.cpp") + + if (CROSS_COMPILING) + try_compile(${RESULT_VAR} "${_bindir}" "${_SRCFILE}" + COMPILE_DEFINITIONS "${CXX11_COMPILER_FLAGS}") + if (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL}) + try_compile(${RESULT_VAR} "${_bindir}_fail" "${_SRCFILE_FAIL}" + COMPILE_DEFINITIONS "${CXX11_COMPILER_FLAGS}") + endif (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL}) + else (CROSS_COMPILING) + try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR + "${_bindir}" "${_SRCFILE}" + COMPILE_DEFINITIONS "${CXX11_COMPILER_FLAGS}") + if (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR) + set(${RESULT_VAR} TRUE) + else (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR) + set(${RESULT_VAR} FALSE) + endif (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR) + if (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL}) + try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR + "${_bindir}_fail" "${_SRCFILE_FAIL}" + COMPILE_DEFINITIONS "${CXX11_COMPILER_FLAGS}") + if (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR) + set(${RESULT_VAR} TRUE) + else (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR) + set(${RESULT_VAR} FALSE) + endif (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR) + endif (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL}) + endif (CROSS_COMPILING) + if (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE}) + try_compile(_TMP_RESULT "${_bindir}_fail_compile" "${_SRCFILE_FAIL_COMPILE}" + COMPILE_DEFINITIONS "${CXX11_COMPILER_FLAGS}") + if (_TMP_RESULT) + set(${RESULT_VAR} FALSE) + else (_TMP_RESULT) + set(${RESULT_VAR} TRUE) + endif (_TMP_RESULT) + endif (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE}) + + if (${RESULT_VAR}) + message(STATUS "Checking C++11 support for ${_LOG_NAME}: works") + else (${RESULT_VAR}) + message(STATUS "Checking C++11 support for ${_LOG_NAME}: not supported") + endif (${RESULT_VAR}) + set(${RESULT_VAR} ${${RESULT_VAR}} CACHE INTERNAL "C++11 support for ${_LOG_NAME}") + endif (NOT DEFINED ${RESULT_VAR}) +endfunction(cxx11_check_feature) + +#cxx11_check_feature("__func__" HAS_CXX11_FUNC) +#cxx11_check_feature("auto" HAS_CXX11_AUTO) +#cxx11_check_feature("auto_ret_type" HAS_CXX11_AUTO_RET_TYPE) +#cxx11_check_feature("class_override_final" HAS_CXX11_CLASS_OVERRIDE) +cxx11_check_feature("constexpr" HAS_CXX11_CONSTEXPR) +cxx11_check_feature("cstdint" HAS_CXX11_CSTDINT_H) +#cxx11_check_feature("decltype" HAS_CXX11_DECLTYPE) +#cxx11_check_feature("initializer_list" HAS_CXX11_INITIALIZER_LIST) +#cxx11_check_feature("lambda" HAS_CXX11_LAMBDA) +#cxx11_check_feature("long_long" HAS_CXX11_LONG_LONG) +#cxx11_check_feature("nullptr" HAS_CXX11_NULLPTR) +#cxx11_check_feature("regex" HAS_CXX11_LIB_REGEX) +#cxx11_check_feature("rvalue-references" HAS_CXX11_RVALUE_REFERENCES) +#cxx11_check_feature("sizeof_member" HAS_CXX11_SIZEOF_MEMBER) +cxx11_check_feature("static_assert" HAS_CXX11_STATIC_ASSERT) +#cxx11_check_feature("variadic_templates" HAS_CXX11_VARIADIC_TEMPLATES) +cxx11_check_feature("shared_ptr" HAS_CXX11_SHARED_PTR) diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-__func__.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-__func__.cpp new file mode 100644 index 00000000..3bfd8a89 --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-__func__.cpp @@ -0,0 +1,8 @@ +int main(void) +{ + if (!__func__) + return 1; + if (!(*__func__)) + return 1; + return 0; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-auto.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-auto.cpp new file mode 100644 index 00000000..948648e9 --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-auto.cpp @@ -0,0 +1,12 @@ + +int main() +{ + auto i = 5; + auto f = 3.14159f; + auto d = 3.14159; + bool ret = ( + (sizeof(f) < sizeof(d)) && + (sizeof(i) == sizeof(int)) + ); + return ret ? 0 : 1; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-auto_fail_compile.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-auto_fail_compile.cpp new file mode 100644 index 00000000..3c0e3f2e --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-auto_fail_compile.cpp @@ -0,0 +1,7 @@ +int main(void) +{ + // must fail because there is no initializer + auto i; + + return 0; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-auto_ret_type.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-auto_ret_type.cpp new file mode 100644 index 00000000..937b6835 --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-auto_ret_type.cpp @@ -0,0 +1,8 @@ +auto foo(int i) -> int { + return i - 1; +} + +int main() +{ + return foo(1); +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-class_override_final.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-class_override_final.cpp new file mode 100644 index 00000000..f5870b19 --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-class_override_final.cpp @@ -0,0 +1,21 @@ +class base { +public: + virtual int foo(int a) + { return 4 + a; } + int bar(int a) final + { return a - 2; } +}; + +class sub final : public base { +public: + virtual int foo(int a) override + { return 8 + 2 * a; }; +}; + +int main(void) +{ + base b; + sub s; + + return (b.foo(2) * 2 == s.foo(2)) ? 0 : 1; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-class_override_final_fail_compile.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-class_override_final_fail_compile.cpp new file mode 100644 index 00000000..bc00b278 --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-class_override_final_fail_compile.cpp @@ -0,0 +1,25 @@ +class base { +public: + virtual int foo(int a) + { return 4 + a; } + virtual int bar(int a) final + { return a - 2; } +}; + +class sub final : public base { +public: + virtual int foo(int a) override + { return 8 + 2 * a; }; + virtual int bar(int a) + { return a; } +}; + +class impossible : public sub { }; + +int main(void) +{ + base b; + sub s; + + return 1; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-constexpr.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-constexpr.cpp new file mode 100644 index 00000000..ed624512 --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-constexpr.cpp @@ -0,0 +1,19 @@ +constexpr int square(int x) +{ + return x*x; +} + +constexpr int the_answer() +{ + return 42; +} + +int main() +{ + int test_arr[square(3)]; + bool ret = ( + (square(the_answer()) == 1764) && + (sizeof(test_arr)/sizeof(test_arr[0]) == 9) + ); + return ret ? 0 : 1; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-cstdint.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-cstdint.cpp new file mode 100644 index 00000000..ca2c72dd --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-cstdint.cpp @@ -0,0 +1,11 @@ +#include + +int main() +{ + bool test = + (sizeof(int8_t) == 1) && + (sizeof(int16_t) == 2) && + (sizeof(int32_t) == 4) && + (sizeof(int64_t) == 8); + return test ? 0 : 1; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-decltype.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-decltype.cpp new file mode 100644 index 00000000..0dbb1cc5 --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-decltype.cpp @@ -0,0 +1,10 @@ +bool check_size(int i) +{ + return sizeof(int) == sizeof(decltype(i)); +} + +int main() +{ + bool ret = check_size(42); + return ret ? 0 : 1; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-initializer_list.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-initializer_list.cpp new file mode 100644 index 00000000..35e6c384 --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-initializer_list.cpp @@ -0,0 +1,27 @@ +#include + +class seq { +public: + seq(std::initializer_list list); + + int length() const; +private: + std::vector m_v; +}; + +seq::seq(std::initializer_list list) + : m_v(list) +{ +} + +int seq::length() const +{ + return m_v.size(); +} + +int main(void) +{ + seq a = {18, 20, 2, 0, 4, 7}; + + return (a.length() == 6) ? 0 : 1; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-lambda.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-lambda.cpp new file mode 100644 index 00000000..4c33ed58 --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-lambda.cpp @@ -0,0 +1,5 @@ +int main() +{ + int ret = 0; + return ([&ret]() -> int { return ret; })(); +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-long_long.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-long_long.cpp new file mode 100644 index 00000000..09111275 --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-long_long.cpp @@ -0,0 +1,7 @@ +int main(void) +{ + long long l; + unsigned long long ul; + + return ((sizeof(l) >= 8) && (sizeof(ul) >= 8)) ? 0 : 1; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-nullptr.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-nullptr.cpp new file mode 100644 index 00000000..9f410715 --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-nullptr.cpp @@ -0,0 +1,6 @@ +int main(void) +{ + void *v = nullptr; + + return v ? 1 : 0; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-nullptr_fail_compile.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-nullptr_fail_compile.cpp new file mode 100644 index 00000000..6a002bcb --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-nullptr_fail_compile.cpp @@ -0,0 +1,6 @@ +int main(void) +{ + int i = nullptr; + + return 1; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-regex.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-regex.cpp new file mode 100644 index 00000000..2fe01c4f --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-regex.cpp @@ -0,0 +1,26 @@ +#include +#include + +int parse_line(std::string const& line) +{ + std::string tmp; + if(std::regex_search(line, std::regex("(\\s)+(-)?(\\d)+//(-)?(\\d)+(\\s)+"))) { + tmp = std::regex_replace(line, std::regex("(-)?(\\d)+//(-)?(\\d)+"), std::string("V")); + } else if(std::regex_search(line, std::regex("(\\s)+(-)?(\\d)+/(-)?(\\d)+(\\s)+"))) { + tmp = std::regex_replace(line, std::regex("(-)?(\\d)+/(-)?(\\d)+"), std::string("V")); + } else if(std::regex_search(line, std::regex("(\\s)+(-)?(\\d)+/(-)?(\\d)+/(-)?(\\d)+(\\s)+"))) { + tmp = std::regex_replace(line, std::regex("(-)?(\\d)+/(-)?(\\d)+/(-)?(\\d)+"), std::string("V")); + } else { + tmp = std::regex_replace(line, std::regex("(-)?(\\d)+"), std::string("V")); + } + return static_cast(std::count(tmp.begin(), tmp.end(), 'V')); +} + +int main() +{ + bool test = (parse_line("f 7/7/7 -3/3/-3 2/-2/2") == 3) && + (parse_line("f 7//7 3//-3 -2//2") == 3) && + (parse_line("f 7/7 3/-3 -2/2") == 3) && + (parse_line("f 7 3 -2") == 3); + return test ? 0 : 1; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-rvalue-references.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-rvalue-references.cpp new file mode 100644 index 00000000..e6e7e5a9 --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-rvalue-references.cpp @@ -0,0 +1,57 @@ +#include + +class rvmove { +public: + void *ptr; + char *array; + + rvmove() + : ptr(0), + array(new char[10]) + { + ptr = this; + } + + rvmove(rvmove &&other) + : ptr(other.ptr), + array(other.array) + { + other.array = 0; + other.ptr = 0; + } + + ~rvmove() + { + assert(((ptr != 0) && (array != 0)) || ((ptr == 0) && (array == 0))); + delete[] array; + } + + rvmove &operator=(rvmove &&other) + { + delete[] array; + ptr = other.ptr; + array = other.array; + other.array = 0; + other.ptr = 0; + return *this; + } + + static rvmove create() + { + return rvmove(); + } +private: + rvmove(const rvmove &); + rvmove &operator=(const rvmove &); +}; + +int main() +{ + rvmove mine; + if (mine.ptr != &mine) + return 1; + mine = rvmove::create(); + if (mine.ptr == &mine) + return 1; + return 0; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-shared_ptr.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-shared_ptr.cpp new file mode 100644 index 00000000..2d9c6bd1 --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-shared_ptr.cpp @@ -0,0 +1,7 @@ +#include + +int main() +{ + std::shared_ptr test; + return 0; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-sizeof_member.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-sizeof_member.cpp new file mode 100644 index 00000000..4902fc73 --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-sizeof_member.cpp @@ -0,0 +1,14 @@ +struct foo { + char bar; + int baz; +}; + +int main(void) +{ + bool ret = ( + (sizeof(foo::bar) == 1) && + (sizeof(foo::baz) >= sizeof(foo::bar)) && + (sizeof(foo) >= sizeof(foo::bar) + sizeof(foo::baz)) + ); + return ret ? 0 : 1; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-sizeof_member_fail.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-sizeof_member_fail.cpp new file mode 100644 index 00000000..0348c2ce --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-sizeof_member_fail.cpp @@ -0,0 +1,9 @@ +struct foo { + int baz; + double bar; +}; + +int main(void) +{ + return (sizeof(foo::bar) == 4) ? 0 : 1; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-static_assert.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-static_assert.cpp new file mode 100644 index 00000000..47c2fefb --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-static_assert.cpp @@ -0,0 +1,5 @@ +int main(void) +{ + static_assert(0 < 1, "your ordering of integers is screwed"); + return 0; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-static_assert_fail_compile.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-static_assert_fail_compile.cpp new file mode 100644 index 00000000..362fcdde --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-static_assert_fail_compile.cpp @@ -0,0 +1,5 @@ +int main(void) +{ + static_assert(1 < 0, "your ordering of integers is screwed"); + return 0; +} diff --git a/cmake/Modules/CheckCXX11Features/cxx11-test-variadic_templates.cpp b/cmake/Modules/CheckCXX11Features/cxx11-test-variadic_templates.cpp new file mode 100644 index 00000000..4518e886 --- /dev/null +++ b/cmake/Modules/CheckCXX11Features/cxx11-test-variadic_templates.cpp @@ -0,0 +1,23 @@ +int Accumulate() +{ + return 0; +} + +template +int Accumulate(T v, Ts... vs) +{ + return v + Accumulate(vs...); +} + +template +int CountElements() +{ + return sizeof...(Is); +} + +int main() +{ + int acc = Accumulate(1, 2, 3, 4, -5); + int count = CountElements<1,2,3,4,5>(); + return ((acc == 5) && (count == 5)) ? 0 : 1; +} diff --git a/examples/Basic/CMakeLists.txt b/examples/Basic/CMakeLists.txt index 9f0d531f..02e203d2 100644 --- a/examples/Basic/CMakeLists.txt +++ b/examples/Basic/CMakeLists.txt @@ -44,7 +44,7 @@ SOURCE_GROUP("Headers" FILES ${INC_FILES}) FIND_PACKAGE(OpenGL REQUIRED) #Tell CMake the paths for OpenGL and for PolyVox (which is just relative to our current location) -INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR} ${PolyVoxCore_SOURCE_DIR}/include ${GLEW_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR} ${PolyVoxCore_BINARY_DIR}/include ${PolyVoxCore_SOURCE_DIR}/include ${GLEW_SOURCE_DIR}) LINK_DIRECTORIES(${PolyVoxCore_BINARY_DIR}) #Build diff --git a/examples/OpenGL/CMakeLists.txt b/examples/OpenGL/CMakeLists.txt index ce92c693..5cd1ed55 100644 --- a/examples/OpenGL/CMakeLists.txt +++ b/examples/OpenGL/CMakeLists.txt @@ -52,7 +52,7 @@ SOURCE_GROUP("Headers" FILES ${INC_FILES}) FIND_PACKAGE(OpenGL REQUIRED) #Tell CMake the paths for OpenGL and for PolyVox (which is just relative to our current location) -INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR} ${PolyVoxCore_SOURCE_DIR}/include ${GLEW_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR} ${PolyVoxCore_BINARY_DIR}/include ${PolyVoxCore_SOURCE_DIR}/include ${GLEW_SOURCE_DIR}) LINK_DIRECTORIES(${PolyVoxCore_BINARY_DIR}) #Build diff --git a/examples/Paging/CMakeLists.txt b/examples/Paging/CMakeLists.txt index ef5ddc0c..e8020b2d 100644 --- a/examples/Paging/CMakeLists.txt +++ b/examples/Paging/CMakeLists.txt @@ -46,7 +46,7 @@ SOURCE_GROUP("Headers" FILES ${INC_FILES}) FIND_PACKAGE(OpenGL REQUIRED) #Tell CMake the paths for OpenGL and for PolyVox (which is just relative to our current location) -INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR} ${PolyVoxCore_SOURCE_DIR}/include ${GLEW_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR} ${PolyVoxCore_BINARY_DIR}/include ${PolyVoxCore_SOURCE_DIR}/include ${GLEW_SOURCE_DIR}) LINK_DIRECTORIES(${PolyVoxCore_BINARY_DIR}) #Build diff --git a/examples/SmoothLOD/CMakeLists.txt b/examples/SmoothLOD/CMakeLists.txt index db1fefec..570cda55 100644 --- a/examples/SmoothLOD/CMakeLists.txt +++ b/examples/SmoothLOD/CMakeLists.txt @@ -44,7 +44,7 @@ SOURCE_GROUP("Headers" FILES ${INC_FILES}) FIND_PACKAGE(OpenGL REQUIRED) #Tell CMake the paths for OpenGL and for PolyVox (which is just relative to our current location) -INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR} ${PolyVoxCore_SOURCE_DIR}/include ${GLEW_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR} ${PolyVoxCore_BINARY_DIR}/include ${PolyVoxCore_SOURCE_DIR}/include ${GLEW_SOURCE_DIR}) LINK_DIRECTORIES(${PolyVoxCore_BINARY_DIR}) #Build diff --git a/library/PolyVoxCore/CMakeLists.txt b/library/PolyVoxCore/CMakeLists.txt index a8bae984..f5cb490c 100644 --- a/library/PolyVoxCore/CMakeLists.txt +++ b/library/PolyVoxCore/CMakeLists.txt @@ -20,10 +20,13 @@ # 3. This notice may not be removed or altered from any source # distribution. -CMAKE_MINIMUM_REQUIRED(VERSION 2.6) - PROJECT(PolyVoxCore) +if(NOT MSVC) + #Set up the C++11 feature header file based on the CheckCXX11Features script + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/include/PolyVoxCore/Impl/CompilerCapabilities.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/PolyVoxCore/Impl/CompilerCapabilities.h) +endif() + #Projects source files SET(CORE_SRC_FILES source/ArraySizes.cpp @@ -91,6 +94,7 @@ SET(CORE_INC_FILES ) SET(IMPL_SRC_FILES + source/Impl/ErrorHandling.cpp source/Impl/MarchingCubesTables.cpp source/Impl/RandomUnitVectors.cpp source/Impl/RandomVectors.cpp @@ -103,6 +107,7 @@ SET(IMPL_INC_FILES include/PolyVoxCore/Impl/AStarPathfinderImpl.h include/PolyVoxCore/Impl/Block.h include/PolyVoxCore/Impl/Block.inl + include/PolyVoxCore/Impl/ErrorHandling.h include/PolyVoxCore/Impl/MarchingCubesTables.h include/PolyVoxCore/Impl/RandomUnitVectors.h include/PolyVoxCore/Impl/RandomVectors.h @@ -123,7 +128,7 @@ SOURCE_GROUP("Sources\\Impl" FILES ${IMPL_SRC_FILES}) SOURCE_GROUP("Headers\\Impl" FILES ${IMPL_INC_FILES}) #Tell CMake the paths -INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/include) #Core #Build diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h index 1c9f6cdd..089915de 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.h @@ -29,6 +29,7 @@ freely, subject to the following restrictions: #include "PolyVoxForwardDeclarations.h" #include "PolyVoxCore/Array.h" +#include "PolyVoxCore/BaseVolume.h" //For wrap modes... should move these? #include "PolyVoxCore/DefaultIsQuadNeeded.h" #include "PolyVoxCore/SurfaceMesh.h" @@ -109,7 +110,13 @@ namespace PolyVox }; public: - CubicSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh >* result, bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded()); + // This is a bit ugly - it seems that the C++03 syntax is different from the C++11 syntax? See this thread: http://stackoverflow.com/questions/6076015/typename-outside-of-template + // Long term we should probably come back to this and if the #ifdef is still needed then maybe it should check for C++11 mode instead of MSVC? +#if defined(_MSC_VER) + CubicSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh >* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = VolumeType::VoxelType(0), bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded()); +#else + CubicSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh >* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(0), bool bMergeQuads = true, IsQuadNeeded isQuadNeeded = IsQuadNeeded()); +#endif void execute(); @@ -144,6 +151,10 @@ namespace PolyVox //This constant defines the maximum number of quads which can share a //vertex in a cubic style mesh. See the initialisation for more details. static const uint32_t MaxVerticesPerPosition; + + //The wrap mode + WrapMode m_eWrapMode; + typename VolumeType::VoxelType m_tBorderValue; }; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl index 8dbe3c4a..caa17261 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl @@ -35,11 +35,13 @@ namespace PolyVox const uint32_t CubicSurfaceExtractor::MaxVerticesPerPosition = 6; template - CubicSurfaceExtractor::CubicSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh >* result, bool bMergeQuads, IsQuadNeeded isQuadNeeded) + CubicSurfaceExtractor::CubicSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh >* result, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, bool bMergeQuads, IsQuadNeeded isQuadNeeded) :m_volData(volData) ,m_regSizeInVoxels(region) ,m_meshCurrent(result) ,m_bMergeQuads(bMergeQuads) + ,m_eWrapMode(eWrapMode) + ,m_tBorderValue(tBorderValue) { m_funcIsQuadNeededCallback = isQuadNeeded; } @@ -72,6 +74,7 @@ namespace PolyVox m_vecQuads[PositiveZ].resize(m_regSizeInVoxels.getUpperCorner().getZ() - m_regSizeInVoxels.getLowerCorner().getZ() + 2); typename VolumeType::Sampler volumeSampler(m_volData); + volumeSampler.setWrapMode(m_eWrapMode, m_tBorderValue); for(int32_t z = m_regSizeInVoxels.getLowerCorner().getZ(); z <= m_regSizeInVoxels.getUpperCorner().getZ(); z++) { diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractorWithNormals.h b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractorWithNormals.h index cf5bd70e..138887cb 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractorWithNormals.h +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractorWithNormals.h @@ -27,6 +27,7 @@ freely, subject to the following restrictions: #include "PolyVoxCore/DefaultIsQuadNeeded.h" #include "PolyVoxCore/Array.h" +#include "PolyVoxCore/BaseVolume.h" //For wrap modes... should move these? #include "PolyVoxCore/SurfaceMesh.h" namespace PolyVox @@ -35,7 +36,13 @@ namespace PolyVox class CubicSurfaceExtractorWithNormals { public: - CubicSurfaceExtractorWithNormals(VolumeType* volData, Region region, SurfaceMesh* result, IsQuadNeeded isQuadNeeded = IsQuadNeeded()); + // This is a bit ugly - it seems that the C++03 syntax is different from the C++11 syntax? See this thread: http://stackoverflow.com/questions/6076015/typename-outside-of-template + // Long term we should probably come back to this and if the #ifdef is still needed then maybe it should check for C++11 mode instead of MSVC? +#if defined(_MSC_VER) + CubicSurfaceExtractorWithNormals(VolumeType* volData, Region region, SurfaceMesh* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = VolumeType::VoxelType(0), IsQuadNeeded isQuadNeeded = IsQuadNeeded()); +#else + CubicSurfaceExtractorWithNormals(VolumeType* volData, Region region, SurfaceMesh* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(0), IsQuadNeeded isQuadNeeded = IsQuadNeeded()); +#endif void execute(); @@ -51,6 +58,10 @@ namespace PolyVox //Information about the region we are currently processing Region m_regSizeInVoxels; + + //The wrap mode + WrapMode m_eWrapMode; + typename VolumeType::VoxelType m_tBorderValue; }; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractorWithNormals.inl b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractorWithNormals.inl index e3c5ca34..5e16d3b8 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractorWithNormals.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractorWithNormals.inl @@ -24,11 +24,13 @@ freely, subject to the following restrictions: namespace PolyVox { template - CubicSurfaceExtractorWithNormals::CubicSurfaceExtractorWithNormals(VolumeType* volData, Region region, SurfaceMesh* result, IsQuadNeeded isQuadNeeded) + CubicSurfaceExtractorWithNormals::CubicSurfaceExtractorWithNormals(VolumeType* volData, Region region, SurfaceMesh* result, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, IsQuadNeeded isQuadNeeded) :m_volData(volData) ,m_sampVolume(volData) ,m_meshCurrent(result) ,m_regSizeInVoxels(region) + ,m_eWrapMode(eWrapMode) + ,m_tBorderValue(tBorderValue) { m_funcIsQuadNeededCallback = isQuadNeeded; } @@ -51,7 +53,7 @@ namespace PolyVox uint32_t material = 0; - if(m_funcIsQuadNeededCallback(m_volData->getVoxelAt(x,y,z), m_volData->getVoxelAt(x+1,y,z), material)) + if(m_funcIsQuadNeededCallback(m_volData->getVoxelWithWrapping(x,y,z,m_eWrapMode,m_tBorderValue), m_volData->getVoxelWithWrapping(x+1,y,z,m_eWrapMode,m_tBorderValue), material)) { uint32_t v0 = m_meshCurrent->addVertex(PositionMaterialNormal(Vector3DFloat(regX + 0.5f, regY - 0.5f, regZ - 0.5f), Vector3DFloat(1.0f, 0.0f, 0.0f), static_cast(material))); uint32_t v1 = m_meshCurrent->addVertex(PositionMaterialNormal(Vector3DFloat(regX + 0.5f, regY - 0.5f, regZ + 0.5f), Vector3DFloat(1.0f, 0.0f, 0.0f), static_cast(material))); @@ -61,7 +63,7 @@ namespace PolyVox m_meshCurrent->addTriangleCubic(v0,v2,v1); m_meshCurrent->addTriangleCubic(v1,v2,v3); } - if(m_funcIsQuadNeededCallback(m_volData->getVoxelAt(x+1,y,z), m_volData->getVoxelAt(x,y,z), material)) + if(m_funcIsQuadNeededCallback(m_volData->getVoxelWithWrapping(x+1,y,z,m_eWrapMode,m_tBorderValue), m_volData->getVoxelWithWrapping(x,y,z,m_eWrapMode,m_tBorderValue), material)) { uint32_t v0 = m_meshCurrent->addVertex(PositionMaterialNormal(Vector3DFloat(regX + 0.5f, regY - 0.5f, regZ - 0.5f), Vector3DFloat(-1.0f, 0.0f, 0.0f), static_cast(material))); uint32_t v1 = m_meshCurrent->addVertex(PositionMaterialNormal(Vector3DFloat(regX + 0.5f, regY - 0.5f, regZ + 0.5f), Vector3DFloat(-1.0f, 0.0f, 0.0f), static_cast(material))); @@ -72,7 +74,7 @@ namespace PolyVox m_meshCurrent->addTriangleCubic(v1,v3,v2); } - if(m_funcIsQuadNeededCallback(m_volData->getVoxelAt(x,y,z), m_volData->getVoxelAt(x,y+1,z), material)) + if(m_funcIsQuadNeededCallback(m_volData->getVoxelWithWrapping(x,y,z,m_eWrapMode,m_tBorderValue), m_volData->getVoxelWithWrapping(x,y+1,z,m_eWrapMode,m_tBorderValue), material)) { uint32_t v0 = m_meshCurrent->addVertex(PositionMaterialNormal(Vector3DFloat(regX - 0.5f, regY + 0.5f, regZ - 0.5f), Vector3DFloat(0.0f, 1.0f, 0.0f), static_cast(material))); uint32_t v1 = m_meshCurrent->addVertex(PositionMaterialNormal(Vector3DFloat(regX - 0.5f, regY + 0.5f, regZ + 0.5f), Vector3DFloat(0.0f, 1.0f, 0.0f), static_cast(material))); @@ -82,7 +84,7 @@ namespace PolyVox m_meshCurrent->addTriangleCubic(v0,v1,v2); m_meshCurrent->addTriangleCubic(v1,v3,v2); } - if(m_funcIsQuadNeededCallback(m_volData->getVoxelAt(x,y+1,z), m_volData->getVoxelAt(x,y,z), material)) + if(m_funcIsQuadNeededCallback(m_volData->getVoxelWithWrapping(x,y+1,z,m_eWrapMode,m_tBorderValue), m_volData->getVoxelWithWrapping(x,y,z,m_eWrapMode,m_tBorderValue), material)) { uint32_t v0 = m_meshCurrent->addVertex(PositionMaterialNormal(Vector3DFloat(regX - 0.5f, regY + 0.5f, regZ - 0.5f), Vector3DFloat(0.0f, -1.0f, 0.0f), static_cast(material))); uint32_t v1 = m_meshCurrent->addVertex(PositionMaterialNormal(Vector3DFloat(regX - 0.5f, regY + 0.5f, regZ + 0.5f), Vector3DFloat(0.0f, -1.0f, 0.0f), static_cast(material))); @@ -93,7 +95,7 @@ namespace PolyVox m_meshCurrent->addTriangleCubic(v1,v2,v3); } - if(m_funcIsQuadNeededCallback(m_volData->getVoxelAt(x,y,z), m_volData->getVoxelAt(x,y,z+1), material)) + if(m_funcIsQuadNeededCallback(m_volData->getVoxelWithWrapping(x,y,z,m_eWrapMode,m_tBorderValue), m_volData->getVoxelWithWrapping(x,y,z+1,m_eWrapMode,m_tBorderValue), material)) { uint32_t v0 = m_meshCurrent->addVertex(PositionMaterialNormal(Vector3DFloat(regX - 0.5f, regY - 0.5f, regZ + 0.5f), Vector3DFloat(0.0f, 0.0f, 1.0f), static_cast(material))); uint32_t v1 = m_meshCurrent->addVertex(PositionMaterialNormal(Vector3DFloat(regX - 0.5f, regY + 0.5f, regZ + 0.5f), Vector3DFloat(0.0f, 0.0f, 1.0f), static_cast(material))); @@ -103,7 +105,7 @@ namespace PolyVox m_meshCurrent->addTriangleCubic(v0,v2,v1); m_meshCurrent->addTriangleCubic(v1,v2,v3); } - if(m_funcIsQuadNeededCallback(m_volData->getVoxelAt(x,y,z+1), m_volData->getVoxelAt(x,y,z), material)) + if(m_funcIsQuadNeededCallback(m_volData->getVoxelWithWrapping(x,y,z+1,m_eWrapMode,m_tBorderValue), m_volData->getVoxelWithWrapping(x,y,z,m_eWrapMode,m_tBorderValue), material)) { uint32_t v0 = m_meshCurrent->addVertex(PositionMaterialNormal(Vector3DFloat(regX - 0.5f, regY - 0.5f, regZ + 0.5f), Vector3DFloat(0.0f, 0.0f, -1.0f), static_cast(material))); uint32_t v1 = m_meshCurrent->addVertex(PositionMaterialNormal(Vector3DFloat(regX - 0.5f, regY + 0.5f, regZ + 0.5f), Vector3DFloat(0.0f, 0.0f, -1.0f), static_cast(material))); diff --git a/library/PolyVoxCore/include/PolyVoxCore/DefaultMarchingCubesController.h b/library/PolyVoxCore/include/PolyVoxCore/DefaultMarchingCubesController.h index fd0d00ef..7be8dc9e 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/DefaultMarchingCubesController.h +++ b/library/PolyVoxCore/include/PolyVoxCore/DefaultMarchingCubesController.h @@ -1,148 +1,128 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -#ifndef __PolyVox_MarchingCubesController_H__ -#define __PolyVox_MarchingCubesController_H__ - -#include "PolyVoxCore/BaseVolume.h" - -#include - -namespace PolyVox -{ - /** - * This class provides a default implementation of a controller for the MarchingCubesSurfaceExtractor. It controls the behaviour of the - * MarchingCubesSurfaceExtractor and provides the required properties from the underlying voxel type. - * - * PolyVox does not enforce any requirements regarding what data must be present in a voxel, and instead allows any primitive or user-defined - * type to be used. However, the Marching Cubes algorithm does have some requirents about the underlying data in that conceptually it operates - * on a density field. In addition, the PolyVox implementation of the Marching Cubes algorithm also understands the idea of each voxel - * having a material which is copied into the vertex data. - * - * Because we want the MarchingCubesSurfaceExtractor to work on any voxel type, we use a Marching Cubes controller (passed as - * a parameter of the MarchingCubesSurfaceExtractor) to expose the required properties. This parameter defaults to the DefaultMarchingCubesController. - * The main implementation of this class is designed to work with primitives data types, and the class is also specialised for the Material, - * Density and MaterialdensityPair classes. - * - * If you create a custom class for your voxel data then you probably want to include a specialisation of DefaultMarchingCubesController, - * though you don't have to if you don't want to use the Marching Cubes algorithm or if you prefer to define a seperate Marching Cubes controller - * and pass it as an explicit parameter (rather than relying on the default). - * - * For primitive types, the DefaultMarchingCubesController considers the value of the voxel to represent it's density and just returns a constant - * for the material. So you can, for example, run the MarchingCubesSurfaceExtractor on a volume of floats or ints. - * - * It is possible to customise the behaviour of the controller by providing a threshold value through the constructor. The extracted surface - * will pass through the density value specified by the threshold, and so you should make sure that the threshold value you choose is between - * the minimum and maximum values found in your volume data. By default it is in the middle of the representable range of the underlying type. - * - * \sa MarchingCubesSurfaceExtractor - * - */ - template - class DefaultMarchingCubesController - { - public: - /// Used to inform the MarchingCubesSurfaceExtractor about which type it should use for representing densities. - typedef VoxelType DensityType; - /// Used to inform the MarchingCubesSurfaceExtractor about which type it should use for representing materials. We're using a float here - /// because this implementation always returns a constant value off 1.0f. PolyVox also uses floats to store the materials in the mesh vertices - /// but this is not really desirable on modern hardware. We'll probably come back to material representation in the future. - typedef float MaterialType; - - /** - * Constructor - * - * This version of the constructor takes no parameters and sets the threshold to the middle of the representable range of the underlying type. - * For example, if the voxel type is 'uint8_t' then the representable range is 0-255, and the threshold will be set to 127. On the other hand, - * if the voxel type is 'float' then the representable range is -FLT_MAX to FLT_MAX and the threshold will be set to zero. - */ - DefaultMarchingCubesController(void) - :m_tThreshold(((std::numeric_limits::min)() + (std::numeric_limits::max)()) / 2) - ,m_eWrapMode(WrapModes::Border) - ,m_tBorder(VoxelType(0)) - { - } - - /** - * Converts the underlying voxel type into a density value. - * - * The default implementation of this function just returns the voxel type directly and is suitable for primitives types. Specialisations of - * this class can modify this behaviour. - */ - DensityType convertToDensity(VoxelType voxel) - { - return voxel; - } - - /** - * Converts the underlying voxel type into a material value. - * - * The default implementation of this function just returns the constant '1'. There's not much else it can do, as it needs to work with primitive - * types and the actual value of the type is already being considered to be the density. Specialisations of this class can modify this behaviour. - */ - MaterialType convertToMaterial(VoxelType /*voxel*/) - { - return 1; - } - - VoxelType getBorderValue(void) - { - return m_tBorder; - } - - /** - * Returns the density value which was passed to the constructor. - * - * As mentioned in the class description, the extracted surface will pass through the density value specified by the threshold, and so you - * should make sure that the threshold value you choose is between the minimum and maximum values found in your volume data. By default it - * is in the middle of the representable range of the underlying type. - */ - DensityType getThreshold(void) - { - return m_tThreshold; - } - - WrapMode getWrapMode(void) - { - return m_eWrapMode; - } - - void setThreshold(DensityType tThreshold) - { - m_tThreshold = tThreshold; - } - - void setWrapMode(WrapMode eWrapMode, VoxelType tBorder = VoxelType(0)) - { - m_eWrapMode = eWrapMode; - m_tBorder = tBorder; - } - - private: - DensityType m_tThreshold; - WrapMode m_eWrapMode; - VoxelType m_tBorder; - }; -} - -#endif +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +#ifndef __PolyVox_MarchingCubesController_H__ +#define __PolyVox_MarchingCubesController_H__ + +#include "PolyVoxCore/BaseVolume.h" + +#include + +namespace PolyVox +{ + /** + * This class provides a default implementation of a controller for the MarchingCubesSurfaceExtractor. It controls the behaviour of the + * MarchingCubesSurfaceExtractor and provides the required properties from the underlying voxel type. + * + * PolyVox does not enforce any requirements regarding what data must be present in a voxel, and instead allows any primitive or user-defined + * type to be used. However, the Marching Cubes algorithm does have some requirents about the underlying data in that conceptually it operates + * on a density field. In addition, the PolyVox implementation of the Marching Cubes algorithm also understands the idea of each voxel + * having a material which is copied into the vertex data. + * + * Because we want the MarchingCubesSurfaceExtractor to work on any voxel type, we use a Marching Cubes controller (passed as + * a parameter of the MarchingCubesSurfaceExtractor) to expose the required properties. This parameter defaults to the DefaultMarchingCubesController. + * The main implementation of this class is designed to work with primitives data types, and the class is also specialised for the Material, + * Density and MaterialdensityPair classes. + * + * If you create a custom class for your voxel data then you probably want to include a specialisation of DefaultMarchingCubesController, + * though you don't have to if you don't want to use the Marching Cubes algorithm or if you prefer to define a seperate Marching Cubes controller + * and pass it as an explicit parameter (rather than relying on the default). + * + * For primitive types, the DefaultMarchingCubesController considers the value of the voxel to represent it's density and just returns a constant + * for the material. So you can, for example, run the MarchingCubesSurfaceExtractor on a volume of floats or ints. + * + * It is possible to customise the behaviour of the controller by providing a threshold value through the constructor. The extracted surface + * will pass through the density value specified by the threshold, and so you should make sure that the threshold value you choose is between + * the minimum and maximum values found in your volume data. By default it is in the middle of the representable range of the underlying type. + * + * \sa MarchingCubesSurfaceExtractor + * + */ + template + class DefaultMarchingCubesController + { + public: + /// Used to inform the MarchingCubesSurfaceExtractor about which type it should use for representing densities. + typedef VoxelType DensityType; + /// Used to inform the MarchingCubesSurfaceExtractor about which type it should use for representing materials. We're using a float here + /// because this implementation always returns a constant value off 1.0f. PolyVox also uses floats to store the materials in the mesh vertices + /// but this is not really desirable on modern hardware. We'll probably come back to material representation in the future. + typedef float MaterialType; + + /** + * Constructor + * + * This version of the constructor takes no parameters and sets the threshold to the middle of the representable range of the underlying type. + * For example, if the voxel type is 'uint8_t' then the representable range is 0-255, and the threshold will be set to 127. On the other hand, + * if the voxel type is 'float' then the representable range is -FLT_MAX to FLT_MAX and the threshold will be set to zero. + */ + DefaultMarchingCubesController(void) + :m_tThreshold(((std::numeric_limits::min)() + (std::numeric_limits::max)()) / 2) + { + } + + /** + * Converts the underlying voxel type into a density value. + * + * The default implementation of this function just returns the voxel type directly and is suitable for primitives types. Specialisations of + * this class can modify this behaviour. + */ + DensityType convertToDensity(VoxelType voxel) + { + return voxel; + } + + /** + * Converts the underlying voxel type into a material value. + * + * The default implementation of this function just returns the constant '1'. There's not much else it can do, as it needs to work with primitive + * types and the actual value of the type is already being considered to be the density. Specialisations of this class can modify this behaviour. + */ + MaterialType convertToMaterial(VoxelType /*voxel*/) + { + return 1; + } + + /** + * Returns the density value which was passed to the constructor. + * + * As mentioned in the class description, the extracted surface will pass through the density value specified by the threshold, and so you + * should make sure that the threshold value you choose is between the minimum and maximum values found in your volume data. By default it + * is in the middle of the representable range of the underlying type. + */ + DensityType getThreshold(void) + { + return m_tThreshold; + } + + void setThreshold(DensityType tThreshold) + { + m_tThreshold = tThreshold; + } + + private: + DensityType m_tThreshold; + }; +} + +#endif diff --git a/library/PolyVoxCore/include/PolyVoxCore/Density.h b/library/PolyVoxCore/include/PolyVoxCore/Density.h index a92d23b9..4d60ad53 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Density.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Density.h @@ -155,13 +155,11 @@ namespace PolyVox { // Default to a threshold value halfway between the min and max possible values. m_tThreshold = (Density::getMinDensity() + Density::getMaxDensity()) / 2; - m_eWrapMode = WrapModes::Border; } DefaultMarchingCubesController(DensityType tThreshold) { m_tThreshold = tThreshold; - m_eWrapMode = WrapModes::Border; } DensityType convertToDensity(Density voxel) @@ -174,35 +172,18 @@ namespace PolyVox return 1; } - Density getBorderValue(void) - { - return m_tBorder; - } - DensityType getThreshold(void) { return m_tThreshold; } - WrapMode getWrapMode(void) - { - return m_eWrapMode; - } - void setThreshold(DensityType tThreshold) { m_tThreshold = tThreshold; } - void setWrapMode(WrapMode eWrapMode) - { - m_eWrapMode = eWrapMode; - } - private: DensityType m_tThreshold; - WrapMode m_eWrapMode; - Density m_tBorder; }; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/CompilerCapabilities.h b/library/PolyVoxCore/include/PolyVoxCore/Impl/CompilerCapabilities.h new file mode 100644 index 00000000..9afbf506 --- /dev/null +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/CompilerCapabilities.h @@ -0,0 +1,20 @@ +/* + * This file provides the default compiler capabilities for for Visual Studio. + * On other compilers CMake will detect which features are available and create + * a file like this. + * + * To Enable these features in Visual Studio, define the variables in this file. +*/ + +#ifndef __PolyVox_CompilerCapabilities_H__ +#define __PolyVox_CompilerCapabilities_H__ + +//#undef HAS_CXX11_CONSTEXPR + +#define HAS_CXX11_STATIC_ASSERT + +#define HAS_CXX11_CSTDINT_H + +#define HAS_CXX11_SHARED_PTR + +#endif diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/CompilerCapabilities.h.in b/library/PolyVoxCore/include/PolyVoxCore/Impl/CompilerCapabilities.h.in new file mode 100644 index 00000000..31f1ba80 --- /dev/null +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/CompilerCapabilities.h.in @@ -0,0 +1,17 @@ +/* + * This file is an input file for the CMake build system. It is processed and + * placed in the build directory by CMake. +*/ + +#ifndef __PolyVox_CompilerCapabilities_H__ +#define __PolyVox_CompilerCapabilities_H__ + +#cmakedefine HAS_CXX11_CONSTEXPR + +#cmakedefine HAS_CXX11_STATIC_ASSERT + +#cmakedefine HAS_CXX11_CSTDINT_H + +#cmakedefine HAS_CXX11_SHARED_PTR + +#endif diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/ErrorHandling.h b/library/PolyVoxCore/include/PolyVoxCore/Impl/ErrorHandling.h new file mode 100644 index 00000000..65c2789d --- /dev/null +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/ErrorHandling.h @@ -0,0 +1,124 @@ +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +#ifndef __PolyVox_ErrorHandling_H__ +#define __PolyVox_ErrorHandling_H__ + +#include //For std::exit +#include //For std::cerr +#include + +#define POLYVOX_ASSERTS_ENABLED +#define POLYVOX_THROW_ENABLED + +#if defined(_MSC_VER) + #define POLYVOX_HALT() __debugbreak() +#else + #define POLYVOX_HALT() std::exit(EXIT_FAILURE) +#endif + +#define POLYVOX_UNUSED(x) do { (void)sizeof(x); } while(0) + +/* + * Assertions + * ---------- + * The code below implements a custom assert function called POLYVOX_ASSERT which has a number of advantages compared + * to the standard C/C++ assert(). It is inspired by http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/ + * which provides code under the MIT license. + */ + +#ifdef POLYVOX_ASSERTS_ENABLED + + #define POLYVOX_ASSERT(condition, message) \ + do \ + { \ + if (!(condition)) \ + { \ + std::cerr << std::endl << std::endl; \ + std::cerr << " PolyVox Assertion Failed!" << std::endl; \ + std::cerr << " =========================" << std::endl; \ + std::cerr << " Condition: " << #condition << std::endl; \ + std::cerr << " Message: " << (message) << std::endl; \ + std::cerr << " Location: " << "Line " << __LINE__ << " of " << __FILE__ << std::endl << std::endl; \ + POLYVOX_HALT(); \ + } \ + } while(0) + +#else + + #define POLYVOX_ASSERT(condition, message) \ + do { POLYVOX_UNUSED(condition); POLYVOX_UNUSED(message); } while(0) + +#endif + +/* + * Static Assertions + * ----------------- + * These map to C+11 static_assert if available or our own implentation otherwise. + */ + +#if defined(HAS_CXX11_STATIC_ASSERT) + //In this case we can just use static_assert + #define POLYVOX_STATIC_ASSERT static_assert +#else + namespace PolyVox + { + // empty default template + template + struct StaticAssert {}; + + // template specialized on true + template <> + struct StaticAssert + { + // If the static assertion is failing then this function won't exist. It will then + // appear in the error message which gives a clue to the user about what is wrong. + static void ERROR_The_static_assertion_has_failed() {} + }; + } + + #define POLYVOX_STATIC_ASSERT(condition, message) StaticAssert<(condition)>::ERROR_The_static_assertion_has_failed(); +#endif + +/* + * Exceptions + * ---------- + * ... + */ +#ifdef POLYVOX_THROW_ENABLED + #define POLYVOX_THROW(type, message) throw type((message)) +#else + namespace PolyVox + { + typedef void (*ThrowHandler)(std::exception& e, const char* file, int line); + + ThrowHandler getThrowHandler(); + void setThrowHandler(ThrowHandler newHandler); + } + + #define POLYVOX_THROW(type, message) \ + type except = (type)((message)); \ + getThrowHandler()((except), __FILE__, __LINE__) +#endif + +#endif //__PolyVox_ErrorHandling_H__ diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/TypeDef.h b/library/PolyVoxCore/include/PolyVoxCore/Impl/TypeDef.h index 1bbfa07b..3724efe4 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/TypeDef.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/TypeDef.h @@ -24,6 +24,8 @@ freely, subject to the following restrictions: #ifndef __PolyVox_TypeDef_H__ #define __PolyVox_TypeDef_H__ +#include "PolyVoxCore/Impl/CompilerCapabilities.h" + //Definitions needed to make library functions accessable // See http://gcc.gnu.org/wiki/Visibility for more info. #if defined _WIN32 || defined __CYGWIN__ @@ -44,6 +46,13 @@ freely, subject to the following restrictions: #endif #endif +#if defined SWIG + //Do nothing in this case +#else + #undef POLYVOX_DEPRECATED + #define POLYVOX_DEPRECATED //Define it to nothing to avoid warnings +#endif + // Now we use the generic helper definitions above to define POLYVOX_API and POLYVOX_LOCAL. // POLYVOX_API is used for the public API symbols. It either imports or exports (or does nothing for static build) // POLYVOX_LOCAL is used for non-api symbols. @@ -65,9 +74,6 @@ freely, subject to the following restrictions: //To support old (pre-vc2010) Microsoft compilers we use boost to replace the //std::shared_ptr and potentially other C++0x features. To use this capability you //will need to make sure you have boost installed on your system. - #include - #define polyvox_shared_ptr boost::shared_ptr - #include #define polyvox_function boost::function @@ -75,13 +81,26 @@ freely, subject to the following restrictions: #define polyvox_bind boost::bind #define polyvox_placeholder_1 _1 #define polyvox_placeholder_2 _2 - - #include - #define static_assert(condition, message) BOOST_STATIC_ASSERT(condition) +#else + //We have a decent compiler - use real C++0x features + #include + #define polyvox_function std::function + #define polyvox_bind std::bind + #define polyvox_placeholder_1 std::placeholders::_1 + #define polyvox_placeholder_2 std::placeholders::_2 +#endif +#if defined(HAS_CXX11_CONSTEXPR) + #define polyvox_constexpr_const constexpr //constexpr which falls back to const + #define polyvox_constexpr constexpr //constexpr which falls back to nothing +#else + #define polyvox_constexpr_const const + #define polyvox_constexpr +#endif - //As long as we're requiring boost, we'll use it to compensate - //for the missing cstdint header too. +#if defined(HAS_CXX11_CSTDINT_H) + #include +#else #include using boost::int8_t; using boost::int16_t; @@ -89,17 +108,14 @@ freely, subject to the following restrictions: using boost::uint8_t; using boost::uint16_t; using boost::uint32_t; -#else - //We have a decent compiler - use real C++0x features - #include - #include +#endif + +#if defined(HAS_CXX11_SHARED_PTR) #include #define polyvox_shared_ptr std::shared_ptr - #define polyvox_function std::function - #define polyvox_bind std::bind - #define polyvox_placeholder_1 std::placeholders::_1 - #define polyvox_placeholder_2 std::placeholders::_2 - //#define static_assert static_assert //we can use this +#else + #include + #define polyvox_shared_ptr boost::shared_ptr #endif #endif diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolumeSampler.inl b/library/PolyVoxCore/include/PolyVoxCore/LargeVolumeSampler.inl index 530fafc0..b6f3fc4f 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolumeSampler.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/LargeVolumeSampler.inl @@ -21,10 +21,12 @@ freely, subject to the following restrictions: distribution. *******************************************************************************/ -#define BORDER_LOW(x) ((( x >> this->mVolume->m_uBlockSideLengthPower) << this->mVolume->m_uBlockSideLengthPower) != x) -#define BORDER_HIGH(x) ((( (x+1) >> this->mVolume->m_uBlockSideLengthPower) << this->mVolume->m_uBlockSideLengthPower) != (x+1)) -//#define BORDER_LOW(x) (( x % mVolume->m_uBlockSideLength) != 0) -//#define BORDER_HIGH(x) (( x % mVolume->m_uBlockSideLength) != mVolume->m_uBlockSideLength - 1) +#define CAN_GO_NEG_X(val) ((val > this->mVolume->getEnclosingRegion().getLowerCorner().getX()) && (val % this->mVolume->m_uBlockSideLength != 0)) +#define CAN_GO_POS_X(val) ((val < this->mVolume->getEnclosingRegion().getUpperCorner().getX()) && ((val + 1) % this->mVolume->m_uBlockSideLength != 0)) +#define CAN_GO_NEG_Y(val) ((val > this->mVolume->getEnclosingRegion().getLowerCorner().getY()) && (val % this->mVolume->m_uBlockSideLength != 0)) +#define CAN_GO_POS_Y(val) ((val < this->mVolume->getEnclosingRegion().getUpperCorner().getY()) && ((val + 1) % this->mVolume->m_uBlockSideLength != 0)) +#define CAN_GO_NEG_Z(val) ((val > this->mVolume->getEnclosingRegion().getLowerCorner().getZ()) && (val % this->mVolume->m_uBlockSideLength != 0)) +#define CAN_GO_POS_Z(val) ((val < this->mVolume->getEnclosingRegion().getUpperCorner().getZ()) && ((val + 1) % this->mVolume->m_uBlockSideLength != 0)) namespace PolyVox { @@ -280,7 +282,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1nx1ny1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) && BORDER_LOW(this->mYPosInVolume) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -290,7 +292,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1nx1ny0pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) && BORDER_LOW(this->mYPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) ) { return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength); } @@ -300,7 +302,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1nx1ny1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) && BORDER_LOW(this->mYPosInVolume) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -310,7 +312,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1nx0py1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -320,7 +322,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1nx0py0pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) ) { return *(mCurrentVoxel - 1); } @@ -330,7 +332,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1nx0py1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -340,7 +342,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1nx1py1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) && BORDER_HIGH(this->mYPosInVolume) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -350,7 +352,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1nx1py0pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) && BORDER_HIGH(this->mYPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) ) { return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength); } @@ -360,7 +362,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1nx1py1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) && BORDER_HIGH(this->mYPosInVolume) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -372,7 +374,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel0px1ny1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mYPosInVolume) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -382,7 +384,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel0px1ny0pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mYPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) ) { return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength); } @@ -392,7 +394,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel0px1ny1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mYPosInVolume) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -402,7 +404,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel0px0py1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -422,7 +424,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel0px0py1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -432,7 +434,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel0px1py1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mYPosInVolume) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -442,7 +444,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel0px1py0pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mYPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_Y(this->mYPosInVolume) ) { return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength); } @@ -452,7 +454,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel0px1py1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mYPosInVolume) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -464,7 +466,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1px1ny1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) && BORDER_LOW(this->mYPosInVolume) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -474,7 +476,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1px1ny0pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) && BORDER_LOW(this->mYPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) ) { return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength); } @@ -484,7 +486,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1px1ny1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) && BORDER_LOW(this->mYPosInVolume) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -494,7 +496,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1px0py1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -504,7 +506,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1px0py0pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) ) { return *(mCurrentVoxel + 1); } @@ -514,7 +516,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1px0py1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -524,7 +526,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1px1py1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) && BORDER_HIGH(this->mYPosInVolume) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -534,7 +536,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1px1py0pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) && BORDER_HIGH(this->mYPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) ) { return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength); } @@ -544,7 +546,7 @@ namespace PolyVox template VoxelType LargeVolume::Sampler::peekVoxel1px1py1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) && BORDER_HIGH(this->mYPosInVolume) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -552,5 +554,9 @@ namespace PolyVox } } -#undef BORDER_LOW -#undef BORDER_HIGH +#undef CAN_GO_NEG_X +#undef CAN_GO_POS_X +#undef CAN_GO_NEG_Y +#undef CAN_GO_POS_Y +#undef CAN_GO_NEG_Z +#undef CAN_GO_POS_Z diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h index 6fa98986..592564a8 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.h @@ -1,220 +1,217 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -#ifndef __PolyVox_SurfaceExtractor_H__ -#define __PolyVox_SurfaceExtractor_H__ - -#include "Impl/MarchingCubesTables.h" -#include "Impl/TypeDef.h" - -#include "PolyVoxCore/Array.h" -#include "PolyVoxCore/SurfaceMesh.h" -#include "PolyVoxCore/DefaultMarchingCubesController.h" - -namespace PolyVox -{ - template< typename VolumeType, typename Controller = DefaultMarchingCubesController > - class MarchingCubesSurfaceExtractor - { - public: - MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh >* result, Controller controller = Controller()); - - void execute(); - - private: - //Compute the cell bitmask for a particular slice in z. - template - uint32_t computeBitmaskForSlice(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask); - - //Compute the cell bitmask for a given cell. - template - void computeBitmaskForCell(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask); - - //Use the cell bitmasks to generate all the vertices needed for that slice - void generateVerticesForSlice(const Array2DUint8& pCurrentBitmask, - Array2DInt32& m_pCurrentVertexIndicesX, - Array2DInt32& m_pCurrentVertexIndicesY, - Array2DInt32& m_pCurrentVertexIndicesZ); - - //////////////////////////////////////////////////////////////////////////////// - // NOTE: These two functions are in the .h file rather than the .inl due to an apparent bug in VC2010. - //See http://stackoverflow.com/questions/1484885/strange-vc-compile-error-c2244 for details. - //////////////////////////////////////////////////////////////////////////////// - Vector3DFloat computeCentralDifferenceGradient(const typename VolumeType::Sampler& volIter) - { - //FIXME - Should actually use DensityType here, both in principle and because the maths may be - //faster (and to reduce casts). So it would be good to add a way to get DensityType from a voxel. - //But watch out for when the DensityType is unsigned and the difference could be negative. - float voxel1nx = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx0py0pz())); - float voxel1px = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px0py0pz())); - - float voxel1ny = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1ny0pz())); - float voxel1py = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1py0pz())); - - float voxel1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py1nz())); - float voxel1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py1pz())); - - return Vector3DFloat - ( - voxel1nx - voxel1px, - voxel1ny - voxel1py, - voxel1nz - voxel1pz - ); - } - - Vector3DFloat computeSobelGradient(const typename VolumeType::Sampler& volIter) - { - static const int weights[3][3][3] = { { {2,3,2}, {3,6,3}, {2,3,2} }, { - {3,6,3}, {6,0,6}, {3,6,3} }, { {2,3,2}, {3,6,3}, {2,3,2} } }; - - //FIXME - Should actually use DensityType here, both in principle and because the maths may be - //faster (and to reduce casts). So it would be good to add a way to get DensityType from a voxel. - //But watch out for when the DensityType is unsigned and the difference could be negative. - const float pVoxel1nx1ny1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1ny1nz())); - const float pVoxel1nx1ny0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1ny0pz())); - const float pVoxel1nx1ny1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1ny1pz())); - const float pVoxel1nx0py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx0py1nz())); - const float pVoxel1nx0py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx0py0pz())); - const float pVoxel1nx0py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx0py1pz())); - const float pVoxel1nx1py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1py1nz())); - const float pVoxel1nx1py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1py0pz())); - const float pVoxel1nx1py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1py1pz())); - - const float pVoxel0px1ny1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1ny1nz())); - const float pVoxel0px1ny0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1ny0pz())); - const float pVoxel0px1ny1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1ny1pz())); - const float pVoxel0px0py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py1nz())); - //const float pVoxel0px0py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py0pz())); - const float pVoxel0px0py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py1pz())); - const float pVoxel0px1py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1py1nz())); - const float pVoxel0px1py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1py0pz())); - const float pVoxel0px1py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1py1pz())); - - const float pVoxel1px1ny1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1ny1nz())); - const float pVoxel1px1ny0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1ny0pz())); - const float pVoxel1px1ny1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1ny1pz())); - const float pVoxel1px0py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px0py1nz())); - const float pVoxel1px0py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px0py0pz())); - const float pVoxel1px0py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px0py1pz())); - const float pVoxel1px1py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1py1nz())); - const float pVoxel1px1py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1py0pz())); - const float pVoxel1px1py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1py1pz())); - - const float xGrad(- weights[0][0][0] * pVoxel1nx1ny1nz - - weights[1][0][0] * pVoxel1nx1ny0pz - weights[2][0][0] * - pVoxel1nx1ny1pz - weights[0][1][0] * pVoxel1nx0py1nz - - weights[1][1][0] * pVoxel1nx0py0pz - weights[2][1][0] * - pVoxel1nx0py1pz - weights[0][2][0] * pVoxel1nx1py1nz - - weights[1][2][0] * pVoxel1nx1py0pz - weights[2][2][0] * - pVoxel1nx1py1pz + weights[0][0][2] * pVoxel1px1ny1nz + - weights[1][0][2] * pVoxel1px1ny0pz + weights[2][0][2] * - pVoxel1px1ny1pz + weights[0][1][2] * pVoxel1px0py1nz + - weights[1][1][2] * pVoxel1px0py0pz + weights[2][1][2] * - pVoxel1px0py1pz + weights[0][2][2] * pVoxel1px1py1nz + - weights[1][2][2] * pVoxel1px1py0pz + weights[2][2][2] * - pVoxel1px1py1pz); - - const float yGrad(- weights[0][0][0] * pVoxel1nx1ny1nz - - weights[1][0][0] * pVoxel1nx1ny0pz - weights[2][0][0] * - pVoxel1nx1ny1pz + weights[0][2][0] * pVoxel1nx1py1nz + - weights[1][2][0] * pVoxel1nx1py0pz + weights[2][2][0] * - pVoxel1nx1py1pz - weights[0][0][1] * pVoxel0px1ny1nz - - weights[1][0][1] * pVoxel0px1ny0pz - weights[2][0][1] * - pVoxel0px1ny1pz + weights[0][2][1] * pVoxel0px1py1nz + - weights[1][2][1] * pVoxel0px1py0pz + weights[2][2][1] * - pVoxel0px1py1pz - weights[0][0][2] * pVoxel1px1ny1nz - - weights[1][0][2] * pVoxel1px1ny0pz - weights[2][0][2] * - pVoxel1px1ny1pz + weights[0][2][2] * pVoxel1px1py1nz + - weights[1][2][2] * pVoxel1px1py0pz + weights[2][2][2] * - pVoxel1px1py1pz); - - const float zGrad(- weights[0][0][0] * pVoxel1nx1ny1nz + - weights[2][0][0] * pVoxel1nx1ny1pz - weights[0][1][0] * - pVoxel1nx0py1nz + weights[2][1][0] * pVoxel1nx0py1pz - - weights[0][2][0] * pVoxel1nx1py1nz + weights[2][2][0] * - pVoxel1nx1py1pz - weights[0][0][1] * pVoxel0px1ny1nz + - weights[2][0][1] * pVoxel0px1ny1pz - weights[0][1][1] * - pVoxel0px0py1nz + weights[2][1][1] * pVoxel0px0py1pz - - weights[0][2][1] * pVoxel0px1py1nz + weights[2][2][1] * - pVoxel0px1py1pz - weights[0][0][2] * pVoxel1px1ny1nz + - weights[2][0][2] * pVoxel1px1ny1pz - weights[0][1][2] * - pVoxel1px0py1nz + weights[2][1][2] * pVoxel1px0py1pz - - weights[0][2][2] * pVoxel1px1py1nz + weights[2][2][2] * - pVoxel1px1py1pz); - - //Note: The above actually give gradients going from low density to high density. - //For our normals we want the the other way around, so we switch the components as we return them. - return Vector3DFloat(-xGrad,-yGrad,-zGrad); - } - //////////////////////////////////////////////////////////////////////////////// - // End of compiler bug workaroumd. - //////////////////////////////////////////////////////////////////////////////// - - //Use the cell bitmasks to generate all the indices needed for that slice - void generateIndicesForSlice(const Array2DUint8& pPreviousBitmask, - const Array2DInt32& m_pPreviousVertexIndicesX, - const Array2DInt32& m_pPreviousVertexIndicesY, - const Array2DInt32& m_pPreviousVertexIndicesZ, - const Array2DInt32& m_pCurrentVertexIndicesX, - const Array2DInt32& m_pCurrentVertexIndicesY); - - //The volume data and a sampler to access it. - VolumeType* m_volData; - typename VolumeType::Sampler m_sampVolume; - - //Holds a position in volume space. - int32_t iXVolSpace; - int32_t iYVolSpace; - int32_t iZVolSpace; - - //Holds a position in region space. - uint32_t uXRegSpace; - uint32_t uYRegSpace; - uint32_t uZRegSpace; - - //Used to return the number of cells in a slice which contain triangles. - uint32_t m_uNoOfOccupiedCells; - - //The surface patch we are currently filling. - SurfaceMesh >* m_meshCurrent; - - //Information about the region we are currently processing - Region m_regSizeInVoxels; - Region m_regSizeInCells; - /*Region m_regSizeInVoxelsCropped; - Region m_regSizeInVoxelsUncropped; - Region m_regVolumeCropped;*/ - Region m_regSlicePrevious; - Region m_regSliceCurrent; - - //Our threshold value - typename Controller::DensityType m_tThreshold; - - //Used to convert arbitrary voxel types in densities and materials. - Controller m_controller; - }; -} - -#include "PolyVoxCore/MarchingCubesSurfaceExtractor.inl" - -#endif +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +#ifndef __PolyVox_SurfaceExtractor_H__ +#define __PolyVox_SurfaceExtractor_H__ + +#include "Impl/MarchingCubesTables.h" +#include "Impl/TypeDef.h" + +#include "PolyVoxCore/Array.h" +#include "PolyVoxCore/BaseVolume.h" //For wrap modes... should move these? +#include "PolyVoxCore/SurfaceMesh.h" +#include "PolyVoxCore/DefaultMarchingCubesController.h" + +namespace PolyVox +{ + template< typename VolumeType, typename Controller = DefaultMarchingCubesController > + class MarchingCubesSurfaceExtractor + { + public: + // This is a bit ugly - it seems that the C++03 syntax is different from the C++11 syntax? See this thread: http://stackoverflow.com/questions/6076015/typename-outside-of-template + // Long term we should probably come back to this and if the #ifdef is still needed then maybe it should check for C++11 mode instead of MSVC? +#if defined(_MSC_VER) + MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh >* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = VolumeType::VoxelType(0), Controller controller = Controller()); +#else + MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh >* result, WrapMode eWrapMode = WrapModes::Border, typename VolumeType::VoxelType tBorderValue = typename VolumeType::VoxelType(0), Controller controller = Controller()); +#endif + + void execute(); + + private: + //Compute the cell bitmask for a particular slice in z. + template + uint32_t computeBitmaskForSlice(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask); + + //Compute the cell bitmask for a given cell. + template + void computeBitmaskForCell(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask, uint32_t uXRegSpace, uint32_t uYRegSpace); + + //Use the cell bitmasks to generate all the vertices needed for that slice + void generateVerticesForSlice(const Array2DUint8& pCurrentBitmask, + Array2DInt32& m_pCurrentVertexIndicesX, + Array2DInt32& m_pCurrentVertexIndicesY, + Array2DInt32& m_pCurrentVertexIndicesZ); + + //////////////////////////////////////////////////////////////////////////////// + // NOTE: These two functions are in the .h file rather than the .inl due to an apparent bug in VC2010. + //See http://stackoverflow.com/questions/1484885/strange-vc-compile-error-c2244 for details. + //////////////////////////////////////////////////////////////////////////////// + Vector3DFloat computeCentralDifferenceGradient(const typename VolumeType::Sampler& volIter) + { + //FIXME - Should actually use DensityType here, both in principle and because the maths may be + //faster (and to reduce casts). So it would be good to add a way to get DensityType from a voxel. + //But watch out for when the DensityType is unsigned and the difference could be negative. + float voxel1nx = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx0py0pz())); + float voxel1px = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px0py0pz())); + + float voxel1ny = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1ny0pz())); + float voxel1py = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1py0pz())); + + float voxel1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py1nz())); + float voxel1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py1pz())); + + return Vector3DFloat + ( + voxel1nx - voxel1px, + voxel1ny - voxel1py, + voxel1nz - voxel1pz + ); + } + + Vector3DFloat computeSobelGradient(const typename VolumeType::Sampler& volIter) + { + static const int weights[3][3][3] = { { {2,3,2}, {3,6,3}, {2,3,2} }, { + {3,6,3}, {6,0,6}, {3,6,3} }, { {2,3,2}, {3,6,3}, {2,3,2} } }; + + //FIXME - Should actually use DensityType here, both in principle and because the maths may be + //faster (and to reduce casts). So it would be good to add a way to get DensityType from a voxel. + //But watch out for when the DensityType is unsigned and the difference could be negative. + const float pVoxel1nx1ny1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1ny1nz())); + const float pVoxel1nx1ny0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1ny0pz())); + const float pVoxel1nx1ny1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1ny1pz())); + const float pVoxel1nx0py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx0py1nz())); + const float pVoxel1nx0py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx0py0pz())); + const float pVoxel1nx0py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx0py1pz())); + const float pVoxel1nx1py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1py1nz())); + const float pVoxel1nx1py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1py0pz())); + const float pVoxel1nx1py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1nx1py1pz())); + + const float pVoxel0px1ny1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1ny1nz())); + const float pVoxel0px1ny0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1ny0pz())); + const float pVoxel0px1ny1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1ny1pz())); + const float pVoxel0px0py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py1nz())); + //const float pVoxel0px0py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py0pz())); + const float pVoxel0px0py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px0py1pz())); + const float pVoxel0px1py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1py1nz())); + const float pVoxel0px1py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1py0pz())); + const float pVoxel0px1py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel0px1py1pz())); + + const float pVoxel1px1ny1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1ny1nz())); + const float pVoxel1px1ny0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1ny0pz())); + const float pVoxel1px1ny1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1ny1pz())); + const float pVoxel1px0py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px0py1nz())); + const float pVoxel1px0py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px0py0pz())); + const float pVoxel1px0py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px0py1pz())); + const float pVoxel1px1py1nz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1py1nz())); + const float pVoxel1px1py0pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1py0pz())); + const float pVoxel1px1py1pz = static_cast(m_controller.convertToDensity(volIter.peekVoxel1px1py1pz())); + + const float xGrad(- weights[0][0][0] * pVoxel1nx1ny1nz - + weights[1][0][0] * pVoxel1nx1ny0pz - weights[2][0][0] * + pVoxel1nx1ny1pz - weights[0][1][0] * pVoxel1nx0py1nz - + weights[1][1][0] * pVoxel1nx0py0pz - weights[2][1][0] * + pVoxel1nx0py1pz - weights[0][2][0] * pVoxel1nx1py1nz - + weights[1][2][0] * pVoxel1nx1py0pz - weights[2][2][0] * + pVoxel1nx1py1pz + weights[0][0][2] * pVoxel1px1ny1nz + + weights[1][0][2] * pVoxel1px1ny0pz + weights[2][0][2] * + pVoxel1px1ny1pz + weights[0][1][2] * pVoxel1px0py1nz + + weights[1][1][2] * pVoxel1px0py0pz + weights[2][1][2] * + pVoxel1px0py1pz + weights[0][2][2] * pVoxel1px1py1nz + + weights[1][2][2] * pVoxel1px1py0pz + weights[2][2][2] * + pVoxel1px1py1pz); + + const float yGrad(- weights[0][0][0] * pVoxel1nx1ny1nz - + weights[1][0][0] * pVoxel1nx1ny0pz - weights[2][0][0] * + pVoxel1nx1ny1pz + weights[0][2][0] * pVoxel1nx1py1nz + + weights[1][2][0] * pVoxel1nx1py0pz + weights[2][2][0] * + pVoxel1nx1py1pz - weights[0][0][1] * pVoxel0px1ny1nz - + weights[1][0][1] * pVoxel0px1ny0pz - weights[2][0][1] * + pVoxel0px1ny1pz + weights[0][2][1] * pVoxel0px1py1nz + + weights[1][2][1] * pVoxel0px1py0pz + weights[2][2][1] * + pVoxel0px1py1pz - weights[0][0][2] * pVoxel1px1ny1nz - + weights[1][0][2] * pVoxel1px1ny0pz - weights[2][0][2] * + pVoxel1px1ny1pz + weights[0][2][2] * pVoxel1px1py1nz + + weights[1][2][2] * pVoxel1px1py0pz + weights[2][2][2] * + pVoxel1px1py1pz); + + const float zGrad(- weights[0][0][0] * pVoxel1nx1ny1nz + + weights[2][0][0] * pVoxel1nx1ny1pz - weights[0][1][0] * + pVoxel1nx0py1nz + weights[2][1][0] * pVoxel1nx0py1pz - + weights[0][2][0] * pVoxel1nx1py1nz + weights[2][2][0] * + pVoxel1nx1py1pz - weights[0][0][1] * pVoxel0px1ny1nz + + weights[2][0][1] * pVoxel0px1ny1pz - weights[0][1][1] * + pVoxel0px0py1nz + weights[2][1][1] * pVoxel0px0py1pz - + weights[0][2][1] * pVoxel0px1py1nz + weights[2][2][1] * + pVoxel0px1py1pz - weights[0][0][2] * pVoxel1px1ny1nz + + weights[2][0][2] * pVoxel1px1ny1pz - weights[0][1][2] * + pVoxel1px0py1nz + weights[2][1][2] * pVoxel1px0py1pz - + weights[0][2][2] * pVoxel1px1py1nz + weights[2][2][2] * + pVoxel1px1py1pz); + + //Note: The above actually give gradients going from low density to high density. + //For our normals we want the the other way around, so we switch the components as we return them. + return Vector3DFloat(-xGrad,-yGrad,-zGrad); + } + //////////////////////////////////////////////////////////////////////////////// + // End of compiler bug workaroumd. + //////////////////////////////////////////////////////////////////////////////// + + //Use the cell bitmasks to generate all the indices needed for that slice + void generateIndicesForSlice(const Array2DUint8& pPreviousBitmask, + const Array2DInt32& m_pPreviousVertexIndicesX, + const Array2DInt32& m_pPreviousVertexIndicesY, + const Array2DInt32& m_pPreviousVertexIndicesZ, + const Array2DInt32& m_pCurrentVertexIndicesX, + const Array2DInt32& m_pCurrentVertexIndicesY); + + //The volume data and a sampler to access it. + VolumeType* m_volData; + typename VolumeType::Sampler m_sampVolume; + + //Used to return the number of cells in a slice which contain triangles. + uint32_t m_uNoOfOccupiedCells; + + //The surface patch we are currently filling. + SurfaceMesh >* m_meshCurrent; + + //Information about the region we are currently processing + Region m_regSizeInVoxels; + Region m_regSizeInCells; + /*Region m_regSizeInVoxelsCropped; + Region m_regSizeInVoxelsUncropped; + Region m_regVolumeCropped;*/ + Region m_regSlicePrevious; + Region m_regSliceCurrent; + + //Used to convert arbitrary voxel types in densities and materials. + Controller m_controller; + + //Our threshold value + typename Controller::DensityType m_tThreshold; + }; +} + +#include "PolyVoxCore/MarchingCubesSurfaceExtractor.inl" + +#endif diff --git a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl index be2a6674..1f251862 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/MarchingCubesSurfaceExtractor.inl @@ -1,628 +1,628 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -namespace PolyVox -{ - template - MarchingCubesSurfaceExtractor::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh >* result, Controller controller) - :m_volData(volData) - ,m_sampVolume(volData) - ,m_meshCurrent(result) - ,m_regSizeInVoxels(region) - { - //m_regSizeInVoxels.cropTo(m_volData->getEnclosingRegion()); - m_regSizeInCells = m_regSizeInVoxels; - m_regSizeInCells.setUpperCorner(m_regSizeInCells.getUpperCorner() - Vector3DInt32(1,1,1)); - - m_controller = controller; - m_tThreshold = m_controller.getThreshold(); - - m_sampVolume.setWrapMode(m_controller.getWrapMode(), m_controller.getBorderValue()); - } - - template - void MarchingCubesSurfaceExtractor::execute() - { - m_meshCurrent->clear(); - - uint32_t uArrayWidth = m_regSizeInVoxels.getUpperCorner().getX() - m_regSizeInVoxels.getLowerCorner().getX() + 1; - uint32_t uArrayHeight = m_regSizeInVoxels.getUpperCorner().getY() - m_regSizeInVoxels.getLowerCorner().getY() + 1; - uint32_t arraySizes[2]= {uArrayWidth, uArrayHeight}; // Array dimensions - - //For edge indices - Array2DInt32 m_pPreviousVertexIndicesX(arraySizes); - Array2DInt32 m_pPreviousVertexIndicesY(arraySizes); - Array2DInt32 m_pPreviousVertexIndicesZ(arraySizes); - Array2DInt32 m_pCurrentVertexIndicesX(arraySizes); - Array2DInt32 m_pCurrentVertexIndicesY(arraySizes); - Array2DInt32 m_pCurrentVertexIndicesZ(arraySizes); - - Array2DUint8 pPreviousBitmask(arraySizes); - Array2DUint8 pCurrentBitmask(arraySizes); - - //Create a region corresponding to the first slice - m_regSlicePrevious = m_regSizeInVoxels; - Vector3DInt32 v3dUpperCorner = m_regSlicePrevious.getUpperCorner(); - v3dUpperCorner.setZ(m_regSlicePrevious.getLowerCorner().getZ()); //Set the upper z to the lower z to make it one slice thick. - m_regSlicePrevious.setUpperCorner(v3dUpperCorner); - m_regSliceCurrent = m_regSlicePrevious; - - uint32_t uNoOfNonEmptyCellsForSlice0 = 0; - uint32_t uNoOfNonEmptyCellsForSlice1 = 0; - - //Process the first slice (previous slice not available) - computeBitmaskForSlice(pPreviousBitmask, pCurrentBitmask); - uNoOfNonEmptyCellsForSlice1 = m_uNoOfOccupiedCells; - - if(uNoOfNonEmptyCellsForSlice1 != 0) - { - memset(m_pCurrentVertexIndicesX.getRawData(), 0xff, m_pCurrentVertexIndicesX.getNoOfElements() * 4); - memset(m_pCurrentVertexIndicesY.getRawData(), 0xff, m_pCurrentVertexIndicesY.getNoOfElements() * 4); - memset(m_pCurrentVertexIndicesZ.getRawData(), 0xff, m_pCurrentVertexIndicesZ.getNoOfElements() * 4); - generateVerticesForSlice(pCurrentBitmask, m_pCurrentVertexIndicesX, m_pCurrentVertexIndicesY, m_pCurrentVertexIndicesZ); - } - - std::swap(uNoOfNonEmptyCellsForSlice0, uNoOfNonEmptyCellsForSlice1); - pPreviousBitmask.swap(pCurrentBitmask); - m_pPreviousVertexIndicesX.swap(m_pCurrentVertexIndicesX); - m_pPreviousVertexIndicesY.swap(m_pCurrentVertexIndicesY); - m_pPreviousVertexIndicesZ.swap(m_pCurrentVertexIndicesZ); - - m_regSlicePrevious = m_regSliceCurrent; - m_regSliceCurrent.shift(Vector3DInt32(0,0,1)); - - //Process the other slices (previous slice is available) - for(int32_t uSlice = 1; uSlice <= m_regSizeInVoxels.getUpperCorner().getZ() - m_regSizeInVoxels.getLowerCorner().getZ(); uSlice++) - { - computeBitmaskForSlice(pPreviousBitmask, pCurrentBitmask); - uNoOfNonEmptyCellsForSlice1 = m_uNoOfOccupiedCells; - - if(uNoOfNonEmptyCellsForSlice1 != 0) - { - memset(m_pCurrentVertexIndicesX.getRawData(), 0xff, m_pCurrentVertexIndicesX.getNoOfElements() * 4); - memset(m_pCurrentVertexIndicesY.getRawData(), 0xff, m_pCurrentVertexIndicesY.getNoOfElements() * 4); - memset(m_pCurrentVertexIndicesZ.getRawData(), 0xff, m_pCurrentVertexIndicesZ.getNoOfElements() * 4); - generateVerticesForSlice(pCurrentBitmask, m_pCurrentVertexIndicesX, m_pCurrentVertexIndicesY, m_pCurrentVertexIndicesZ); - } - - if((uNoOfNonEmptyCellsForSlice0 != 0) || (uNoOfNonEmptyCellsForSlice1 != 0)) - { - generateIndicesForSlice(pPreviousBitmask, m_pPreviousVertexIndicesX, m_pPreviousVertexIndicesY, m_pPreviousVertexIndicesZ, m_pCurrentVertexIndicesX, m_pCurrentVertexIndicesY); - } - - std::swap(uNoOfNonEmptyCellsForSlice0, uNoOfNonEmptyCellsForSlice1); - pPreviousBitmask.swap(pCurrentBitmask); - m_pPreviousVertexIndicesX.swap(m_pCurrentVertexIndicesX); - m_pPreviousVertexIndicesY.swap(m_pCurrentVertexIndicesY); - m_pPreviousVertexIndicesZ.swap(m_pCurrentVertexIndicesZ); - - m_regSlicePrevious = m_regSliceCurrent; - m_regSliceCurrent.shift(Vector3DInt32(0,0,1)); - } - - m_meshCurrent->m_Region = m_regSizeInVoxels; - - m_meshCurrent->m_vecLodRecords.clear(); - LodRecord lodRecord; - lodRecord.beginIndex = 0; - lodRecord.endIndex = m_meshCurrent->getNoOfIndices(); - m_meshCurrent->m_vecLodRecords.push_back(lodRecord); - } - - template - template - uint32_t MarchingCubesSurfaceExtractor::computeBitmaskForSlice(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask) - { - m_uNoOfOccupiedCells = 0; - - const int32_t iMaxXVolSpace = m_regSliceCurrent.getUpperCorner().getX(); - const int32_t iMaxYVolSpace = m_regSliceCurrent.getUpperCorner().getY(); - - iZVolSpace = m_regSliceCurrent.getLowerCorner().getZ(); - uZRegSpace = iZVolSpace - m_regSizeInVoxels.getLowerCorner().getZ(); - - //Process the lower left corner - iYVolSpace = m_regSliceCurrent.getLowerCorner().getY(); - iXVolSpace = m_regSliceCurrent.getLowerCorner().getX(); - - uXRegSpace = iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX(); - uYRegSpace = iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY(); - - m_sampVolume.setPosition(iXVolSpace,iYVolSpace,iZVolSpace); - computeBitmaskForCell(pPreviousBitmask, pCurrentBitmask); - - //Process the edge where x is minimal. - iXVolSpace = m_regSliceCurrent.getLowerCorner().getX(); - m_sampVolume.setPosition(iXVolSpace, m_regSliceCurrent.getLowerCorner().getY(), iZVolSpace); - for(iYVolSpace = m_regSliceCurrent.getLowerCorner().getY() + 1; iYVolSpace <= iMaxYVolSpace; iYVolSpace++) - { - uXRegSpace = iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX(); - uYRegSpace = iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY(); - - m_sampVolume.movePositiveY(); - - computeBitmaskForCell(pPreviousBitmask, pCurrentBitmask); - } - - //Process the edge where y is minimal. - iYVolSpace = m_regSliceCurrent.getLowerCorner().getY(); - m_sampVolume.setPosition(m_regSliceCurrent.getLowerCorner().getX(), iYVolSpace, iZVolSpace); - for(iXVolSpace = m_regSliceCurrent.getLowerCorner().getX() + 1; iXVolSpace <= iMaxXVolSpace; iXVolSpace++) - { - uXRegSpace = iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX(); - uYRegSpace = iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY(); - - m_sampVolume.movePositiveX(); - - computeBitmaskForCell(pPreviousBitmask, pCurrentBitmask); - } - - //Process all remaining elemnents of the slice. In this case, previous x and y values are always available - for(iYVolSpace = m_regSliceCurrent.getLowerCorner().getY() + 1; iYVolSpace <= iMaxYVolSpace; iYVolSpace++) - { - m_sampVolume.setPosition(m_regSliceCurrent.getLowerCorner().getX(), iYVolSpace, iZVolSpace); - for(iXVolSpace = m_regSliceCurrent.getLowerCorner().getX() + 1; iXVolSpace <= iMaxXVolSpace; iXVolSpace++) - { - uXRegSpace = iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX(); - uYRegSpace = iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY(); - - m_sampVolume.movePositiveX(); - - computeBitmaskForCell(pPreviousBitmask, pCurrentBitmask); - } - } - - return m_uNoOfOccupiedCells; - } - - template - template - void MarchingCubesSurfaceExtractor::computeBitmaskForCell(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask) - { - uint8_t iCubeIndex = 0; - - typename VolumeType::VoxelType v000; - typename VolumeType::VoxelType v100; - typename VolumeType::VoxelType v010; - typename VolumeType::VoxelType v110; - typename VolumeType::VoxelType v001; - typename VolumeType::VoxelType v101; - typename VolumeType::VoxelType v011; - typename VolumeType::VoxelType v111; - - if(isPrevZAvail) - { - if(isPrevYAvail) - { - if(isPrevXAvail) - { - v111 = m_sampVolume.peekVoxel1px1py1pz(); - - //z - uint8_t iPreviousCubeIndexZ = pPreviousBitmask[uXRegSpace][uYRegSpace]; - iPreviousCubeIndexZ >>= 4; - - //y - uint8_t iPreviousCubeIndexY = pCurrentBitmask[uXRegSpace][uYRegSpace-1]; - iPreviousCubeIndexY &= 192; //192 = 128 + 64 - iPreviousCubeIndexY >>= 2; - - //x - uint8_t iPreviousCubeIndexX = pCurrentBitmask[uXRegSpace-1][uYRegSpace]; - iPreviousCubeIndexX &= 128; - iPreviousCubeIndexX >>= 1; - - iCubeIndex = iPreviousCubeIndexX | iPreviousCubeIndexY | iPreviousCubeIndexZ; - - if (m_controller.convertToDensity(v111) < m_tThreshold) iCubeIndex |= 128; - } - else //previous X not available - { - v011 = m_sampVolume.peekVoxel0px1py1pz(); - v111 = m_sampVolume.peekVoxel1px1py1pz(); - - //z - uint8_t iPreviousCubeIndexZ = pPreviousBitmask[uXRegSpace][uYRegSpace]; - iPreviousCubeIndexZ >>= 4; - - //y - uint8_t iPreviousCubeIndexY = pCurrentBitmask[uXRegSpace][uYRegSpace-1]; - iPreviousCubeIndexY &= 192; //192 = 128 + 64 - iPreviousCubeIndexY >>= 2; - - iCubeIndex = iPreviousCubeIndexY | iPreviousCubeIndexZ; - - if (m_controller.convertToDensity(v011) < m_tThreshold) iCubeIndex |= 64; - if (m_controller.convertToDensity(v111) < m_tThreshold) iCubeIndex |= 128; - } - } - else //previous Y not available - { - if(isPrevXAvail) - { - v101 = m_sampVolume.peekVoxel1px0py1pz(); - v111 = m_sampVolume.peekVoxel1px1py1pz(); - - //z - uint8_t iPreviousCubeIndexZ = pPreviousBitmask[uXRegSpace][uYRegSpace]; - iPreviousCubeIndexZ >>= 4; - - //x - uint8_t iPreviousCubeIndexX = pCurrentBitmask[uXRegSpace-1][uYRegSpace]; - iPreviousCubeIndexX &= 160; //160 = 128+32 - iPreviousCubeIndexX >>= 1; - - iCubeIndex = iPreviousCubeIndexX | iPreviousCubeIndexZ; - - if (m_controller.convertToDensity(v101) < m_tThreshold) iCubeIndex |= 32; - if (m_controller.convertToDensity(v111) < m_tThreshold) iCubeIndex |= 128; - } - else //previous X not available - { - v001 = m_sampVolume.peekVoxel0px0py1pz(); - v101 = m_sampVolume.peekVoxel1px0py1pz(); - v011 = m_sampVolume.peekVoxel0px1py1pz(); - v111 = m_sampVolume.peekVoxel1px1py1pz(); - - //z - uint8_t iPreviousCubeIndexZ = pPreviousBitmask[uXRegSpace][uYRegSpace]; - iCubeIndex = iPreviousCubeIndexZ >> 4; - - if (m_controller.convertToDensity(v001) < m_tThreshold) iCubeIndex |= 16; - if (m_controller.convertToDensity(v101) < m_tThreshold) iCubeIndex |= 32; - if (m_controller.convertToDensity(v011) < m_tThreshold) iCubeIndex |= 64; - if (m_controller.convertToDensity(v111) < m_tThreshold) iCubeIndex |= 128; - } - } - } - else //previous Z not available - { - if(isPrevYAvail) - { - if(isPrevXAvail) - { - v110 = m_sampVolume.peekVoxel1px1py0pz(); - v111 = m_sampVolume.peekVoxel1px1py1pz(); - - //y - uint8_t iPreviousCubeIndexY = pCurrentBitmask[uXRegSpace][uYRegSpace-1]; - iPreviousCubeIndexY &= 204; //204 = 128+64+8+4 - iPreviousCubeIndexY >>= 2; - - //x - uint8_t iPreviousCubeIndexX = pCurrentBitmask[uXRegSpace-1][uYRegSpace]; - iPreviousCubeIndexX &= 170; //170 = 128+32+8+2 - iPreviousCubeIndexX >>= 1; - - iCubeIndex = iPreviousCubeIndexX | iPreviousCubeIndexY; - - if (m_controller.convertToDensity(v110) < m_tThreshold) iCubeIndex |= 8; - if (m_controller.convertToDensity(v111) < m_tThreshold) iCubeIndex |= 128; - } - else //previous X not available - { - v010 = m_sampVolume.peekVoxel0px1py0pz(); - v110 = m_sampVolume.peekVoxel1px1py0pz(); - - v011 = m_sampVolume.peekVoxel0px1py1pz(); - v111 = m_sampVolume.peekVoxel1px1py1pz(); - - //y - uint8_t iPreviousCubeIndexY = pCurrentBitmask[uXRegSpace][uYRegSpace-1]; - iPreviousCubeIndexY &= 204; //204 = 128+64+8+4 - iPreviousCubeIndexY >>= 2; - - iCubeIndex = iPreviousCubeIndexY; - - if (m_controller.convertToDensity(v010) < m_tThreshold) iCubeIndex |= 4; - if (m_controller.convertToDensity(v110) < m_tThreshold) iCubeIndex |= 8; - if (m_controller.convertToDensity(v011) < m_tThreshold) iCubeIndex |= 64; - if (m_controller.convertToDensity(v111) < m_tThreshold) iCubeIndex |= 128; - } - } - else //previous Y not available - { - if(isPrevXAvail) - { - v100 = m_sampVolume.peekVoxel1px0py0pz(); - v110 = m_sampVolume.peekVoxel1px1py0pz(); - - v101 = m_sampVolume.peekVoxel1px0py1pz(); - v111 = m_sampVolume.peekVoxel1px1py1pz(); - - //x - uint8_t iPreviousCubeIndexX = pCurrentBitmask[uXRegSpace-1][uYRegSpace]; - iPreviousCubeIndexX &= 170; //170 = 128+32+8+2 - iPreviousCubeIndexX >>= 1; - - iCubeIndex = iPreviousCubeIndexX; - - if (m_controller.convertToDensity(v100) < m_tThreshold) iCubeIndex |= 2; - if (m_controller.convertToDensity(v110) < m_tThreshold) iCubeIndex |= 8; - if (m_controller.convertToDensity(v101) < m_tThreshold) iCubeIndex |= 32; - if (m_controller.convertToDensity(v111) < m_tThreshold) iCubeIndex |= 128; - } - else //previous X not available - { - v000 = m_sampVolume.getVoxel(); - v100 = m_sampVolume.peekVoxel1px0py0pz(); - v010 = m_sampVolume.peekVoxel0px1py0pz(); - v110 = m_sampVolume.peekVoxel1px1py0pz(); - - v001 = m_sampVolume.peekVoxel0px0py1pz(); - v101 = m_sampVolume.peekVoxel1px0py1pz(); - v011 = m_sampVolume.peekVoxel0px1py1pz(); - v111 = m_sampVolume.peekVoxel1px1py1pz(); - - if (m_controller.convertToDensity(v000) < m_tThreshold) iCubeIndex |= 1; - if (m_controller.convertToDensity(v100) < m_tThreshold) iCubeIndex |= 2; - if (m_controller.convertToDensity(v010) < m_tThreshold) iCubeIndex |= 4; - if (m_controller.convertToDensity(v110) < m_tThreshold) iCubeIndex |= 8; - if (m_controller.convertToDensity(v001) < m_tThreshold) iCubeIndex |= 16; - if (m_controller.convertToDensity(v101) < m_tThreshold) iCubeIndex |= 32; - if (m_controller.convertToDensity(v011) < m_tThreshold) iCubeIndex |= 64; - if (m_controller.convertToDensity(v111) < m_tThreshold) iCubeIndex |= 128; - } - } - } - - //Save the bitmask - pCurrentBitmask[uXRegSpace][iYVolSpace- m_regSizeInVoxels.getLowerCorner().getY()] = iCubeIndex; - - if(edgeTable[iCubeIndex] != 0) - { - ++m_uNoOfOccupiedCells; - } - } - - template - void MarchingCubesSurfaceExtractor::generateVerticesForSlice(const Array2DUint8& pCurrentBitmask, - Array2DInt32& m_pCurrentVertexIndicesX, - Array2DInt32& m_pCurrentVertexIndicesY, - Array2DInt32& m_pCurrentVertexIndicesZ) - { - int32_t iZVolSpace = m_regSliceCurrent.getLowerCorner().getZ(); - - //Iterate over each cell in the region - for(int32_t iYVolSpace = m_regSliceCurrent.getLowerCorner().getY(); iYVolSpace <= m_regSliceCurrent.getUpperCorner().getY(); iYVolSpace++) - { - const uint32_t uYRegSpace = iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY(); - - for(int32_t iXVolSpace = m_regSliceCurrent.getLowerCorner().getX(); iXVolSpace <= m_regSliceCurrent.getUpperCorner().getX(); iXVolSpace++) - { - //Current position - const uint32_t uXRegSpace = iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX(); - - //Determine the index into the edge table which tells us which vertices are inside of the surface - uint8_t iCubeIndex = pCurrentBitmask[uXRegSpace][uYRegSpace]; - - /* Cube is entirely in/out of the surface */ - if (edgeTable[iCubeIndex] == 0) - { - continue; - } - - //Check whether the generated vertex will lie on the edge of the region - - - m_sampVolume.setPosition(iXVolSpace,iYVolSpace,iZVolSpace); - const typename VolumeType::VoxelType v000 = m_sampVolume.getVoxel(); - const Vector3DFloat n000 = computeSobelGradient(m_sampVolume); - - /* Find the vertices where the surface intersects the cube */ - if (edgeTable[iCubeIndex] & 1) - { - m_sampVolume.movePositiveX(); - const typename VolumeType::VoxelType v100 = m_sampVolume.getVoxel(); - const Vector3DFloat n100 = computeSobelGradient(m_sampVolume); - - float fInterp = static_cast(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast(m_controller.convertToDensity(v100) - m_controller.convertToDensity(v000)); - - const Vector3DFloat v3dPosition(static_cast(iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()) + fInterp, static_cast(iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()), static_cast(iZVolSpace - m_regSizeInCells.getLowerCorner().getZ())); - - Vector3DFloat v3dNormal = (n100*fInterp) + (n000*(1-fInterp)); - v3dNormal.normalise(); - - //Choose one of the two materials to use for the vertex (we don't interpolate as interpolation of - //material IDs does not make sense). We take the largest, so that if we are working on a material-only - //volume we get the one which is non-zero. Both materials can be non-zero if our volume has a density component. - typename Controller::MaterialType uMaterial000 = m_controller.convertToMaterial(v000); - typename Controller::MaterialType uMaterial100 = m_controller.convertToMaterial(v100); - //typename Controller::MaterialType uMaterial = (std::max)(uMaterial000, uMaterial100); - typename Controller::MaterialType uMaterial = m_controller.blendMaterials(uMaterial000, uMaterial100, fInterp); - - PositionMaterialNormal surfaceVertex(v3dPosition, v3dNormal, uMaterial); - uint32_t uLastVertexIndex = m_meshCurrent->addVertex(surfaceVertex); - m_pCurrentVertexIndicesX[iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()][iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()] = uLastVertexIndex; - - m_sampVolume.moveNegativeX(); - } - if (edgeTable[iCubeIndex] & 8) - { - m_sampVolume.movePositiveY(); - const typename VolumeType::VoxelType v010 = m_sampVolume.getVoxel(); - const Vector3DFloat n010 = computeSobelGradient(m_sampVolume); - - float fInterp = static_cast(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast(m_controller.convertToDensity(v010) - m_controller.convertToDensity(v000)); - - const Vector3DFloat v3dPosition(static_cast(iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()), static_cast(iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()) + fInterp, static_cast(iZVolSpace - m_regSizeInVoxels.getLowerCorner().getZ())); - - Vector3DFloat v3dNormal = (n010*fInterp) + (n000*(1-fInterp)); - v3dNormal.normalise(); - - //Choose one of the two materials to use for the vertex (we don't interpolate as interpolation of - //material IDs does not make sense). We take the largest, so that if we are working on a material-only - //volume we get the one which is non-zero. Both materials can be non-zero if our volume has a density component. - typename Controller::MaterialType uMaterial000 = m_controller.convertToMaterial(v000); - typename Controller::MaterialType uMaterial010 = m_controller.convertToMaterial(v010); - //typename Controller::MaterialType uMaterial = (std::max)(uMaterial000, uMaterial010); - typename Controller::MaterialType uMaterial = m_controller.blendMaterials(uMaterial000, uMaterial010, fInterp); - - PositionMaterialNormal surfaceVertex(v3dPosition, v3dNormal, uMaterial); - uint32_t uLastVertexIndex = m_meshCurrent->addVertex(surfaceVertex); - m_pCurrentVertexIndicesY[iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()][iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()] = uLastVertexIndex; - - m_sampVolume.moveNegativeY(); - } - if (edgeTable[iCubeIndex] & 256) - { - m_sampVolume.movePositiveZ(); - const typename VolumeType::VoxelType v001 = m_sampVolume.getVoxel(); - const Vector3DFloat n001 = computeSobelGradient(m_sampVolume); - - float fInterp = static_cast(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast(m_controller.convertToDensity(v001) - m_controller.convertToDensity(v000)); - - const Vector3DFloat v3dPosition(static_cast(iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()), static_cast(iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()), static_cast(iZVolSpace - m_regSizeInVoxels.getLowerCorner().getZ()) + fInterp); - - Vector3DFloat v3dNormal = (n001*fInterp) + (n000*(1-fInterp)); - v3dNormal.normalise(); - - //Choose one of the two materials to use for the vertex (we don't interpolate as interpolation of - //material IDs does not make sense). We take the largest, so that if we are working on a material-only - //volume we get the one which is non-zero. Both materials can be non-zero if our volume has a density component. - typename Controller::MaterialType uMaterial000 = m_controller.convertToMaterial(v000); - typename Controller::MaterialType uMaterial001 = m_controller.convertToMaterial(v001); - //typename Controller::MaterialType uMaterial = (std::max)(uMaterial000, uMaterial001); - typename Controller::MaterialType uMaterial = m_controller.blendMaterials(uMaterial000, uMaterial001, fInterp); - - PositionMaterialNormal surfaceVertex(v3dPosition, v3dNormal, uMaterial); - uint32_t uLastVertexIndex = m_meshCurrent->addVertex(surfaceVertex); - m_pCurrentVertexIndicesZ[iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()][iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()] = uLastVertexIndex; - - m_sampVolume.moveNegativeZ(); - } - }//For each cell - } - } - - template - void MarchingCubesSurfaceExtractor::generateIndicesForSlice(const Array2DUint8& pPreviousBitmask, - const Array2DInt32& m_pPreviousVertexIndicesX, - const Array2DInt32& m_pPreviousVertexIndicesY, - const Array2DInt32& m_pPreviousVertexIndicesZ, - const Array2DInt32& m_pCurrentVertexIndicesX, - const Array2DInt32& m_pCurrentVertexIndicesY) - { - int32_t indlist[12]; - for(int i = 0; i < 12; i++) - { - indlist[i] = -1; - } - - for(int32_t iYVolSpace = m_regSlicePrevious.getLowerCorner().getY(); iYVolSpace <= m_regSizeInCells.getUpperCorner().getY(); iYVolSpace++) - { - for(int32_t iXVolSpace = m_regSlicePrevious.getLowerCorner().getX(); iXVolSpace <= m_regSizeInCells.getUpperCorner().getX(); iXVolSpace++) - { - int32_t iZVolSpace = m_regSlicePrevious.getLowerCorner().getZ(); - m_sampVolume.setPosition(iXVolSpace,iYVolSpace,iZVolSpace); - - //Current position - const uint32_t uXRegSpace = m_sampVolume.getPosition().getX() - m_regSizeInVoxels.getLowerCorner().getX(); - const uint32_t uYRegSpace = m_sampVolume.getPosition().getY() - m_regSizeInVoxels.getLowerCorner().getY(); - - //Determine the index into the edge table which tells us which vertices are inside of the surface - uint8_t iCubeIndex = pPreviousBitmask[uXRegSpace][uYRegSpace]; - - /* Cube is entirely in/out of the surface */ - if (edgeTable[iCubeIndex] == 0) - { - continue; - } - - /* Find the vertices where the surface intersects the cube */ - if (edgeTable[iCubeIndex] & 1) - { - indlist[0] = m_pPreviousVertexIndicesX[uXRegSpace][uYRegSpace]; - //assert(indlist[0] != -1); - } - if (edgeTable[iCubeIndex] & 2) - { - indlist[1] = m_pPreviousVertexIndicesY[uXRegSpace+1][uYRegSpace]; - //assert(indlist[1] != -1); - } - if (edgeTable[iCubeIndex] & 4) - { - indlist[2] = m_pPreviousVertexIndicesX[uXRegSpace][uYRegSpace+1]; - //assert(indlist[2] != -1); - } - if (edgeTable[iCubeIndex] & 8) - { - indlist[3] = m_pPreviousVertexIndicesY[uXRegSpace][uYRegSpace]; - //assert(indlist[3] != -1); - } - if (edgeTable[iCubeIndex] & 16) - { - indlist[4] = m_pCurrentVertexIndicesX[uXRegSpace][uYRegSpace]; - //assert(indlist[4] != -1); - } - if (edgeTable[iCubeIndex] & 32) - { - indlist[5] = m_pCurrentVertexIndicesY[uXRegSpace+1][uYRegSpace]; - //assert(indlist[5] != -1); - } - if (edgeTable[iCubeIndex] & 64) - { - indlist[6] = m_pCurrentVertexIndicesX[uXRegSpace][uYRegSpace+1]; - //assert(indlist[6] != -1); - } - if (edgeTable[iCubeIndex] & 128) - { - indlist[7] = m_pCurrentVertexIndicesY[uXRegSpace][uYRegSpace]; - //assert(indlist[7] != -1); - } - if (edgeTable[iCubeIndex] & 256) - { - indlist[8] = m_pPreviousVertexIndicesZ[uXRegSpace][uYRegSpace]; - //assert(indlist[8] != -1); - } - if (edgeTable[iCubeIndex] & 512) - { - indlist[9] = m_pPreviousVertexIndicesZ[uXRegSpace+1][uYRegSpace]; - //assert(indlist[9] != -1); - } - if (edgeTable[iCubeIndex] & 1024) - { - indlist[10] = m_pPreviousVertexIndicesZ[uXRegSpace+1][uYRegSpace+1]; - //assert(indlist[10] != -1); - } - if (edgeTable[iCubeIndex] & 2048) - { - indlist[11] = m_pPreviousVertexIndicesZ[uXRegSpace][uYRegSpace+1]; - //assert(indlist[11] != -1); - } - - for (int i=0;triTable[iCubeIndex][i]!=-1;i+=3) - { - int32_t ind0 = indlist[triTable[iCubeIndex][i ]]; - int32_t ind1 = indlist[triTable[iCubeIndex][i+1]]; - int32_t ind2 = indlist[triTable[iCubeIndex][i+2]]; - - if((ind0 != -1) && (ind1 != -1) && (ind2 != -1)) - { - m_meshCurrent->addTriangle(ind0, ind1, ind2); - } - }//For each triangle - }//For each cell - } - } -} +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +namespace PolyVox +{ + template + MarchingCubesSurfaceExtractor::MarchingCubesSurfaceExtractor(VolumeType* volData, Region region, SurfaceMesh >* result, WrapMode eWrapMode, typename VolumeType::VoxelType tBorderValue, Controller controller) + :m_volData(volData) + ,m_sampVolume(volData) + ,m_meshCurrent(result) + ,m_regSizeInVoxels(region) + ,m_controller(controller) + ,m_tThreshold(m_controller.getThreshold()) + { + //m_regSizeInVoxels.cropTo(m_volData->getEnclosingRegion()); + m_regSizeInCells = m_regSizeInVoxels; + m_regSizeInCells.setUpperCorner(m_regSizeInCells.getUpperCorner() - Vector3DInt32(1,1,1)); + + m_sampVolume.setWrapMode(eWrapMode, tBorderValue); + } + + template + void MarchingCubesSurfaceExtractor::execute() + { + m_meshCurrent->clear(); + + const uint32_t uArrayWidth = m_regSizeInVoxels.getUpperCorner().getX() - m_regSizeInVoxels.getLowerCorner().getX() + 1; + const uint32_t uArrayHeight = m_regSizeInVoxels.getUpperCorner().getY() - m_regSizeInVoxels.getLowerCorner().getY() + 1; + const uint32_t arraySizes[2]= {uArrayWidth, uArrayHeight}; // Array dimensions + + //For edge indices + Array2DInt32 m_pPreviousVertexIndicesX(arraySizes); + Array2DInt32 m_pPreviousVertexIndicesY(arraySizes); + Array2DInt32 m_pPreviousVertexIndicesZ(arraySizes); + Array2DInt32 m_pCurrentVertexIndicesX(arraySizes); + Array2DInt32 m_pCurrentVertexIndicesY(arraySizes); + Array2DInt32 m_pCurrentVertexIndicesZ(arraySizes); + + Array2DUint8 pPreviousBitmask(arraySizes); + Array2DUint8 pCurrentBitmask(arraySizes); + + //Create a region corresponding to the first slice + m_regSlicePrevious = m_regSizeInVoxels; + Vector3DInt32 v3dUpperCorner = m_regSlicePrevious.getUpperCorner(); + v3dUpperCorner.setZ(m_regSlicePrevious.getLowerCorner().getZ()); //Set the upper z to the lower z to make it one slice thick. + m_regSlicePrevious.setUpperCorner(v3dUpperCorner); + m_regSliceCurrent = m_regSlicePrevious; + + uint32_t uNoOfNonEmptyCellsForSlice0 = 0; + uint32_t uNoOfNonEmptyCellsForSlice1 = 0; + + //Process the first slice (previous slice not available) + computeBitmaskForSlice(pPreviousBitmask, pCurrentBitmask); + uNoOfNonEmptyCellsForSlice1 = m_uNoOfOccupiedCells; + + if(uNoOfNonEmptyCellsForSlice1 != 0) + { + memset(m_pCurrentVertexIndicesX.getRawData(), 0xff, m_pCurrentVertexIndicesX.getNoOfElements() * 4); + memset(m_pCurrentVertexIndicesY.getRawData(), 0xff, m_pCurrentVertexIndicesY.getNoOfElements() * 4); + memset(m_pCurrentVertexIndicesZ.getRawData(), 0xff, m_pCurrentVertexIndicesZ.getNoOfElements() * 4); + generateVerticesForSlice(pCurrentBitmask, m_pCurrentVertexIndicesX, m_pCurrentVertexIndicesY, m_pCurrentVertexIndicesZ); + } + + std::swap(uNoOfNonEmptyCellsForSlice0, uNoOfNonEmptyCellsForSlice1); + pPreviousBitmask.swap(pCurrentBitmask); + m_pPreviousVertexIndicesX.swap(m_pCurrentVertexIndicesX); + m_pPreviousVertexIndicesY.swap(m_pCurrentVertexIndicesY); + m_pPreviousVertexIndicesZ.swap(m_pCurrentVertexIndicesZ); + + m_regSlicePrevious = m_regSliceCurrent; + m_regSliceCurrent.shift(Vector3DInt32(0,0,1)); + + //Process the other slices (previous slice is available) + for(int32_t uSlice = 1; uSlice <= m_regSizeInVoxels.getUpperCorner().getZ() - m_regSizeInVoxels.getLowerCorner().getZ(); uSlice++) + { + computeBitmaskForSlice(pPreviousBitmask, pCurrentBitmask); + uNoOfNonEmptyCellsForSlice1 = m_uNoOfOccupiedCells; + + if(uNoOfNonEmptyCellsForSlice1 != 0) + { + memset(m_pCurrentVertexIndicesX.getRawData(), 0xff, m_pCurrentVertexIndicesX.getNoOfElements() * 4); + memset(m_pCurrentVertexIndicesY.getRawData(), 0xff, m_pCurrentVertexIndicesY.getNoOfElements() * 4); + memset(m_pCurrentVertexIndicesZ.getRawData(), 0xff, m_pCurrentVertexIndicesZ.getNoOfElements() * 4); + generateVerticesForSlice(pCurrentBitmask, m_pCurrentVertexIndicesX, m_pCurrentVertexIndicesY, m_pCurrentVertexIndicesZ); + } + + if((uNoOfNonEmptyCellsForSlice0 != 0) || (uNoOfNonEmptyCellsForSlice1 != 0)) + { + generateIndicesForSlice(pPreviousBitmask, m_pPreviousVertexIndicesX, m_pPreviousVertexIndicesY, m_pPreviousVertexIndicesZ, m_pCurrentVertexIndicesX, m_pCurrentVertexIndicesY); + } + + std::swap(uNoOfNonEmptyCellsForSlice0, uNoOfNonEmptyCellsForSlice1); + pPreviousBitmask.swap(pCurrentBitmask); + m_pPreviousVertexIndicesX.swap(m_pCurrentVertexIndicesX); + m_pPreviousVertexIndicesY.swap(m_pCurrentVertexIndicesY); + m_pPreviousVertexIndicesZ.swap(m_pCurrentVertexIndicesZ); + + m_regSlicePrevious = m_regSliceCurrent; + m_regSliceCurrent.shift(Vector3DInt32(0,0,1)); + } + + m_meshCurrent->m_Region = m_regSizeInVoxels; + + m_meshCurrent->m_vecLodRecords.clear(); + LodRecord lodRecord; + lodRecord.beginIndex = 0; + lodRecord.endIndex = m_meshCurrent->getNoOfIndices(); + m_meshCurrent->m_vecLodRecords.push_back(lodRecord); + } + + template + template + uint32_t MarchingCubesSurfaceExtractor::computeBitmaskForSlice(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask) + { + m_uNoOfOccupiedCells = 0; + + const int32_t iMaxXVolSpace = m_regSliceCurrent.getUpperCorner().getX(); + const int32_t iMaxYVolSpace = m_regSliceCurrent.getUpperCorner().getY(); + + const int32_t iZVolSpace = m_regSliceCurrent.getLowerCorner().getZ(); + + //Process the lower left corner + int32_t iYVolSpace = m_regSliceCurrent.getLowerCorner().getY(); + int32_t iXVolSpace = m_regSliceCurrent.getLowerCorner().getX(); + + uint32_t uXRegSpace = iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX(); + uint32_t uYRegSpace = iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY(); + + + m_sampVolume.setPosition(iXVolSpace,iYVolSpace,iZVolSpace); + computeBitmaskForCell(pPreviousBitmask, pCurrentBitmask, uXRegSpace, uYRegSpace); + + //Process the edge where x is minimal. + iXVolSpace = m_regSliceCurrent.getLowerCorner().getX(); + m_sampVolume.setPosition(iXVolSpace, m_regSliceCurrent.getLowerCorner().getY(), iZVolSpace); + for(iYVolSpace = m_regSliceCurrent.getLowerCorner().getY() + 1; iYVolSpace <= iMaxYVolSpace; iYVolSpace++) + { + uXRegSpace = iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX(); + uYRegSpace = iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY(); + + m_sampVolume.movePositiveY(); + + computeBitmaskForCell(pPreviousBitmask, pCurrentBitmask, uXRegSpace, uYRegSpace); + } + + //Process the edge where y is minimal. + iYVolSpace = m_regSliceCurrent.getLowerCorner().getY(); + m_sampVolume.setPosition(m_regSliceCurrent.getLowerCorner().getX(), iYVolSpace, iZVolSpace); + for(iXVolSpace = m_regSliceCurrent.getLowerCorner().getX() + 1; iXVolSpace <= iMaxXVolSpace; iXVolSpace++) + { + uXRegSpace = iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX(); + uYRegSpace = iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY(); + + m_sampVolume.movePositiveX(); + + computeBitmaskForCell(pPreviousBitmask, pCurrentBitmask, uXRegSpace, uYRegSpace); + } + + //Process all remaining elemnents of the slice. In this case, previous x and y values are always available + for(iYVolSpace = m_regSliceCurrent.getLowerCorner().getY() + 1; iYVolSpace <= iMaxYVolSpace; iYVolSpace++) + { + m_sampVolume.setPosition(m_regSliceCurrent.getLowerCorner().getX(), iYVolSpace, iZVolSpace); + for(iXVolSpace = m_regSliceCurrent.getLowerCorner().getX() + 1; iXVolSpace <= iMaxXVolSpace; iXVolSpace++) + { + uXRegSpace = iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX(); + uYRegSpace = iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY(); + + m_sampVolume.movePositiveX(); + + computeBitmaskForCell(pPreviousBitmask, pCurrentBitmask, uXRegSpace, uYRegSpace); + } + } + + return m_uNoOfOccupiedCells; + } + + template + template + void MarchingCubesSurfaceExtractor::computeBitmaskForCell(const Array2DUint8& pPreviousBitmask, Array2DUint8& pCurrentBitmask, uint32_t uXRegSpace, uint32_t uYRegSpace) + { + uint8_t iCubeIndex = 0; + + typename VolumeType::VoxelType v000; + typename VolumeType::VoxelType v100; + typename VolumeType::VoxelType v010; + typename VolumeType::VoxelType v110; + typename VolumeType::VoxelType v001; + typename VolumeType::VoxelType v101; + typename VolumeType::VoxelType v011; + typename VolumeType::VoxelType v111; + + if(isPrevZAvail) + { + if(isPrevYAvail) + { + if(isPrevXAvail) + { + v111 = m_sampVolume.peekVoxel1px1py1pz(); + + //z + uint8_t iPreviousCubeIndexZ = pPreviousBitmask[uXRegSpace][uYRegSpace]; + iPreviousCubeIndexZ >>= 4; + + //y + uint8_t iPreviousCubeIndexY = pCurrentBitmask[uXRegSpace][uYRegSpace-1]; + iPreviousCubeIndexY &= 192; //192 = 128 + 64 + iPreviousCubeIndexY >>= 2; + + //x + uint8_t iPreviousCubeIndexX = pCurrentBitmask[uXRegSpace-1][uYRegSpace]; + iPreviousCubeIndexX &= 128; + iPreviousCubeIndexX >>= 1; + + iCubeIndex = iPreviousCubeIndexX | iPreviousCubeIndexY | iPreviousCubeIndexZ; + + if (m_controller.convertToDensity(v111) < m_tThreshold) iCubeIndex |= 128; + } + else //previous X not available + { + v011 = m_sampVolume.peekVoxel0px1py1pz(); + v111 = m_sampVolume.peekVoxel1px1py1pz(); + + //z + uint8_t iPreviousCubeIndexZ = pPreviousBitmask[uXRegSpace][uYRegSpace]; + iPreviousCubeIndexZ >>= 4; + + //y + uint8_t iPreviousCubeIndexY = pCurrentBitmask[uXRegSpace][uYRegSpace-1]; + iPreviousCubeIndexY &= 192; //192 = 128 + 64 + iPreviousCubeIndexY >>= 2; + + iCubeIndex = iPreviousCubeIndexY | iPreviousCubeIndexZ; + + if (m_controller.convertToDensity(v011) < m_tThreshold) iCubeIndex |= 64; + if (m_controller.convertToDensity(v111) < m_tThreshold) iCubeIndex |= 128; + } + } + else //previous Y not available + { + if(isPrevXAvail) + { + v101 = m_sampVolume.peekVoxel1px0py1pz(); + v111 = m_sampVolume.peekVoxel1px1py1pz(); + + //z + uint8_t iPreviousCubeIndexZ = pPreviousBitmask[uXRegSpace][uYRegSpace]; + iPreviousCubeIndexZ >>= 4; + + //x + uint8_t iPreviousCubeIndexX = pCurrentBitmask[uXRegSpace-1][uYRegSpace]; + iPreviousCubeIndexX &= 160; //160 = 128+32 + iPreviousCubeIndexX >>= 1; + + iCubeIndex = iPreviousCubeIndexX | iPreviousCubeIndexZ; + + if (m_controller.convertToDensity(v101) < m_tThreshold) iCubeIndex |= 32; + if (m_controller.convertToDensity(v111) < m_tThreshold) iCubeIndex |= 128; + } + else //previous X not available + { + v001 = m_sampVolume.peekVoxel0px0py1pz(); + v101 = m_sampVolume.peekVoxel1px0py1pz(); + v011 = m_sampVolume.peekVoxel0px1py1pz(); + v111 = m_sampVolume.peekVoxel1px1py1pz(); + + //z + uint8_t iPreviousCubeIndexZ = pPreviousBitmask[uXRegSpace][uYRegSpace]; + iCubeIndex = iPreviousCubeIndexZ >> 4; + + if (m_controller.convertToDensity(v001) < m_tThreshold) iCubeIndex |= 16; + if (m_controller.convertToDensity(v101) < m_tThreshold) iCubeIndex |= 32; + if (m_controller.convertToDensity(v011) < m_tThreshold) iCubeIndex |= 64; + if (m_controller.convertToDensity(v111) < m_tThreshold) iCubeIndex |= 128; + } + } + } + else //previous Z not available + { + if(isPrevYAvail) + { + if(isPrevXAvail) + { + v110 = m_sampVolume.peekVoxel1px1py0pz(); + v111 = m_sampVolume.peekVoxel1px1py1pz(); + + //y + uint8_t iPreviousCubeIndexY = pCurrentBitmask[uXRegSpace][uYRegSpace-1]; + iPreviousCubeIndexY &= 204; //204 = 128+64+8+4 + iPreviousCubeIndexY >>= 2; + + //x + uint8_t iPreviousCubeIndexX = pCurrentBitmask[uXRegSpace-1][uYRegSpace]; + iPreviousCubeIndexX &= 170; //170 = 128+32+8+2 + iPreviousCubeIndexX >>= 1; + + iCubeIndex = iPreviousCubeIndexX | iPreviousCubeIndexY; + + if (m_controller.convertToDensity(v110) < m_tThreshold) iCubeIndex |= 8; + if (m_controller.convertToDensity(v111) < m_tThreshold) iCubeIndex |= 128; + } + else //previous X not available + { + v010 = m_sampVolume.peekVoxel0px1py0pz(); + v110 = m_sampVolume.peekVoxel1px1py0pz(); + + v011 = m_sampVolume.peekVoxel0px1py1pz(); + v111 = m_sampVolume.peekVoxel1px1py1pz(); + + //y + uint8_t iPreviousCubeIndexY = pCurrentBitmask[uXRegSpace][uYRegSpace-1]; + iPreviousCubeIndexY &= 204; //204 = 128+64+8+4 + iPreviousCubeIndexY >>= 2; + + iCubeIndex = iPreviousCubeIndexY; + + if (m_controller.convertToDensity(v010) < m_tThreshold) iCubeIndex |= 4; + if (m_controller.convertToDensity(v110) < m_tThreshold) iCubeIndex |= 8; + if (m_controller.convertToDensity(v011) < m_tThreshold) iCubeIndex |= 64; + if (m_controller.convertToDensity(v111) < m_tThreshold) iCubeIndex |= 128; + } + } + else //previous Y not available + { + if(isPrevXAvail) + { + v100 = m_sampVolume.peekVoxel1px0py0pz(); + v110 = m_sampVolume.peekVoxel1px1py0pz(); + + v101 = m_sampVolume.peekVoxel1px0py1pz(); + v111 = m_sampVolume.peekVoxel1px1py1pz(); + + //x + uint8_t iPreviousCubeIndexX = pCurrentBitmask[uXRegSpace-1][uYRegSpace]; + iPreviousCubeIndexX &= 170; //170 = 128+32+8+2 + iPreviousCubeIndexX >>= 1; + + iCubeIndex = iPreviousCubeIndexX; + + if (m_controller.convertToDensity(v100) < m_tThreshold) iCubeIndex |= 2; + if (m_controller.convertToDensity(v110) < m_tThreshold) iCubeIndex |= 8; + if (m_controller.convertToDensity(v101) < m_tThreshold) iCubeIndex |= 32; + if (m_controller.convertToDensity(v111) < m_tThreshold) iCubeIndex |= 128; + } + else //previous X not available + { + v000 = m_sampVolume.getVoxel(); + v100 = m_sampVolume.peekVoxel1px0py0pz(); + v010 = m_sampVolume.peekVoxel0px1py0pz(); + v110 = m_sampVolume.peekVoxel1px1py0pz(); + + v001 = m_sampVolume.peekVoxel0px0py1pz(); + v101 = m_sampVolume.peekVoxel1px0py1pz(); + v011 = m_sampVolume.peekVoxel0px1py1pz(); + v111 = m_sampVolume.peekVoxel1px1py1pz(); + + if (m_controller.convertToDensity(v000) < m_tThreshold) iCubeIndex |= 1; + if (m_controller.convertToDensity(v100) < m_tThreshold) iCubeIndex |= 2; + if (m_controller.convertToDensity(v010) < m_tThreshold) iCubeIndex |= 4; + if (m_controller.convertToDensity(v110) < m_tThreshold) iCubeIndex |= 8; + if (m_controller.convertToDensity(v001) < m_tThreshold) iCubeIndex |= 16; + if (m_controller.convertToDensity(v101) < m_tThreshold) iCubeIndex |= 32; + if (m_controller.convertToDensity(v011) < m_tThreshold) iCubeIndex |= 64; + if (m_controller.convertToDensity(v111) < m_tThreshold) iCubeIndex |= 128; + } + } + } + + //Save the bitmask + pCurrentBitmask[uXRegSpace][uYRegSpace] = iCubeIndex; + + if(edgeTable[iCubeIndex] != 0) + { + ++m_uNoOfOccupiedCells; + } + } + + template + void MarchingCubesSurfaceExtractor::generateVerticesForSlice(const Array2DUint8& pCurrentBitmask, + Array2DInt32& m_pCurrentVertexIndicesX, + Array2DInt32& m_pCurrentVertexIndicesY, + Array2DInt32& m_pCurrentVertexIndicesZ) + { + const int32_t iZVolSpace = m_regSliceCurrent.getLowerCorner().getZ(); + + //Iterate over each cell in the region + for(int32_t iYVolSpace = m_regSliceCurrent.getLowerCorner().getY(); iYVolSpace <= m_regSliceCurrent.getUpperCorner().getY(); iYVolSpace++) + { + const uint32_t uYRegSpace = iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY(); + + for(int32_t iXVolSpace = m_regSliceCurrent.getLowerCorner().getX(); iXVolSpace <= m_regSliceCurrent.getUpperCorner().getX(); iXVolSpace++) + { + //Current position + const uint32_t uXRegSpace = iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX(); + + //Determine the index into the edge table which tells us which vertices are inside of the surface + const uint8_t iCubeIndex = pCurrentBitmask[uXRegSpace][uYRegSpace]; + + /* Cube is entirely in/out of the surface */ + if (edgeTable[iCubeIndex] == 0) + { + continue; + } + + //Check whether the generated vertex will lie on the edge of the region + + + m_sampVolume.setPosition(iXVolSpace,iYVolSpace,iZVolSpace); + const typename VolumeType::VoxelType v000 = m_sampVolume.getVoxel(); + const Vector3DFloat n000 = computeCentralDifferenceGradient(m_sampVolume); + + /* Find the vertices where the surface intersects the cube */ + if (edgeTable[iCubeIndex] & 1) + { + m_sampVolume.movePositiveX(); + const typename VolumeType::VoxelType v100 = m_sampVolume.getVoxel(); + const Vector3DFloat n100 = computeCentralDifferenceGradient(m_sampVolume); + + const float fInterp = static_cast(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast(m_controller.convertToDensity(v100) - m_controller.convertToDensity(v000)); + + const Vector3DFloat v3dPosition(static_cast(iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()) + fInterp, static_cast(iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()), static_cast(iZVolSpace - m_regSizeInCells.getLowerCorner().getZ())); + + Vector3DFloat v3dNormal = (n100*fInterp) + (n000*(1-fInterp)); + v3dNormal.normalise(); + + //Choose one of the two materials to use for the vertex (we don't interpolate as interpolation of + //material IDs does not make sense). We take the largest, so that if we are working on a material-only + //volume we get the one which is non-zero. Both materials can be non-zero if our volume has a density component. + const typename Controller::MaterialType uMaterial000 = m_controller.convertToMaterial(v000); + const typename Controller::MaterialType uMaterial100 = m_controller.convertToMaterial(v100); + //const typename Controller::MaterialType uMaterial = (std::max)(uMaterial000, uMaterial100); + const typename Controller::MaterialType uMaterial = m_controller.blendMaterials(uMaterial000, uMaterial100, fInterp); + + const PositionMaterialNormal surfaceVertex(v3dPosition, v3dNormal, uMaterial); + const uint32_t uLastVertexIndex = m_meshCurrent->addVertex(surfaceVertex); + m_pCurrentVertexIndicesX[iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()][iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()] = uLastVertexIndex; + + m_sampVolume.moveNegativeX(); + } + if (edgeTable[iCubeIndex] & 8) + { + m_sampVolume.movePositiveY(); + const typename VolumeType::VoxelType v010 = m_sampVolume.getVoxel(); + const Vector3DFloat n010 = computeCentralDifferenceGradient(m_sampVolume); + + const float fInterp = static_cast(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast(m_controller.convertToDensity(v010) - m_controller.convertToDensity(v000)); + + const Vector3DFloat v3dPosition(static_cast(iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()), static_cast(iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()) + fInterp, static_cast(iZVolSpace - m_regSizeInVoxels.getLowerCorner().getZ())); + + Vector3DFloat v3dNormal = (n010*fInterp) + (n000*(1-fInterp)); + v3dNormal.normalise(); + + //Choose one of the two materials to use for the vertex (we don't interpolate as interpolation of + //material IDs does not make sense). We take the largest, so that if we are working on a material-only + //volume we get the one which is non-zero. Both materials can be non-zero if our volume has a density component. + const typename Controller::MaterialType uMaterial000 = m_controller.convertToMaterial(v000); + const typename Controller::MaterialType uMaterial010 = m_controller.convertToMaterial(v010); + //const typename Controller::MaterialType uMaterial = (std::max)(uMaterial000, uMaterial010); + const typename Controller::MaterialType uMaterial = m_controller.blendMaterials(uMaterial000, uMaterial010, fInterp); + + PositionMaterialNormal surfaceVertex(v3dPosition, v3dNormal, uMaterial); + uint32_t uLastVertexIndex = m_meshCurrent->addVertex(surfaceVertex); + m_pCurrentVertexIndicesY[iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()][iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()] = uLastVertexIndex; + + m_sampVolume.moveNegativeY(); + } + if (edgeTable[iCubeIndex] & 256) + { + m_sampVolume.movePositiveZ(); + const typename VolumeType::VoxelType v001 = m_sampVolume.getVoxel(); + const Vector3DFloat n001 = computeCentralDifferenceGradient(m_sampVolume); + + const float fInterp = static_cast(m_tThreshold - m_controller.convertToDensity(v000)) / static_cast(m_controller.convertToDensity(v001) - m_controller.convertToDensity(v000)); + + const Vector3DFloat v3dPosition(static_cast(iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()), static_cast(iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()), static_cast(iZVolSpace - m_regSizeInVoxels.getLowerCorner().getZ()) + fInterp); + + Vector3DFloat v3dNormal = (n001*fInterp) + (n000*(1-fInterp)); + v3dNormal.normalise(); + + //Choose one of the two materials to use for the vertex (we don't interpolate as interpolation of + //material IDs does not make sense). We take the largest, so that if we are working on a material-only + //volume we get the one which is non-zero. Both materials can be non-zero if our volume has a density component. + const typename Controller::MaterialType uMaterial000 = m_controller.convertToMaterial(v000); + const typename Controller::MaterialType uMaterial001 = m_controller.convertToMaterial(v001); + //const typename Controller::MaterialType uMaterial = (std::max)(uMaterial000, uMaterial001); + const typename Controller::MaterialType uMaterial = m_controller.blendMaterials(uMaterial000, uMaterial001, fInterp); + + const PositionMaterialNormal surfaceVertex(v3dPosition, v3dNormal, uMaterial); + const uint32_t uLastVertexIndex = m_meshCurrent->addVertex(surfaceVertex); + m_pCurrentVertexIndicesZ[iXVolSpace - m_regSizeInVoxels.getLowerCorner().getX()][iYVolSpace - m_regSizeInVoxels.getLowerCorner().getY()] = uLastVertexIndex; + + m_sampVolume.moveNegativeZ(); + } + }//For each cell + } + } + + template + void MarchingCubesSurfaceExtractor::generateIndicesForSlice(const Array2DUint8& pPreviousBitmask, + const Array2DInt32& m_pPreviousVertexIndicesX, + const Array2DInt32& m_pPreviousVertexIndicesY, + const Array2DInt32& m_pPreviousVertexIndicesZ, + const Array2DInt32& m_pCurrentVertexIndicesX, + const Array2DInt32& m_pCurrentVertexIndicesY) + { + int32_t indlist[12]; + for(int i = 0; i < 12; i++) + { + indlist[i] = -1; + } + + const int32_t iZVolSpace = m_regSlicePrevious.getLowerCorner().getZ(); + + for(int32_t iYVolSpace = m_regSlicePrevious.getLowerCorner().getY(); iYVolSpace <= m_regSizeInCells.getUpperCorner().getY(); iYVolSpace++) + { + for(int32_t iXVolSpace = m_regSlicePrevious.getLowerCorner().getX(); iXVolSpace <= m_regSizeInCells.getUpperCorner().getX(); iXVolSpace++) + { + m_sampVolume.setPosition(iXVolSpace,iYVolSpace,iZVolSpace); + + //Current position + const uint32_t uXRegSpace = m_sampVolume.getPosition().getX() - m_regSizeInVoxels.getLowerCorner().getX(); + const uint32_t uYRegSpace = m_sampVolume.getPosition().getY() - m_regSizeInVoxels.getLowerCorner().getY(); + + //Determine the index into the edge table which tells us which vertices are inside of the surface + const uint8_t iCubeIndex = pPreviousBitmask[uXRegSpace][uYRegSpace]; + + /* Cube is entirely in/out of the surface */ + if (edgeTable[iCubeIndex] == 0) + { + continue; + } + + /* Find the vertices where the surface intersects the cube */ + if (edgeTable[iCubeIndex] & 1) + { + indlist[0] = m_pPreviousVertexIndicesX[uXRegSpace][uYRegSpace]; + //assert(indlist[0] != -1); + } + if (edgeTable[iCubeIndex] & 2) + { + indlist[1] = m_pPreviousVertexIndicesY[uXRegSpace+1][uYRegSpace]; + //assert(indlist[1] != -1); + } + if (edgeTable[iCubeIndex] & 4) + { + indlist[2] = m_pPreviousVertexIndicesX[uXRegSpace][uYRegSpace+1]; + //assert(indlist[2] != -1); + } + if (edgeTable[iCubeIndex] & 8) + { + indlist[3] = m_pPreviousVertexIndicesY[uXRegSpace][uYRegSpace]; + //assert(indlist[3] != -1); + } + if (edgeTable[iCubeIndex] & 16) + { + indlist[4] = m_pCurrentVertexIndicesX[uXRegSpace][uYRegSpace]; + //assert(indlist[4] != -1); + } + if (edgeTable[iCubeIndex] & 32) + { + indlist[5] = m_pCurrentVertexIndicesY[uXRegSpace+1][uYRegSpace]; + //assert(indlist[5] != -1); + } + if (edgeTable[iCubeIndex] & 64) + { + indlist[6] = m_pCurrentVertexIndicesX[uXRegSpace][uYRegSpace+1]; + //assert(indlist[6] != -1); + } + if (edgeTable[iCubeIndex] & 128) + { + indlist[7] = m_pCurrentVertexIndicesY[uXRegSpace][uYRegSpace]; + //assert(indlist[7] != -1); + } + if (edgeTable[iCubeIndex] & 256) + { + indlist[8] = m_pPreviousVertexIndicesZ[uXRegSpace][uYRegSpace]; + //assert(indlist[8] != -1); + } + if (edgeTable[iCubeIndex] & 512) + { + indlist[9] = m_pPreviousVertexIndicesZ[uXRegSpace+1][uYRegSpace]; + //assert(indlist[9] != -1); + } + if (edgeTable[iCubeIndex] & 1024) + { + indlist[10] = m_pPreviousVertexIndicesZ[uXRegSpace+1][uYRegSpace+1]; + //assert(indlist[10] != -1); + } + if (edgeTable[iCubeIndex] & 2048) + { + indlist[11] = m_pPreviousVertexIndicesZ[uXRegSpace][uYRegSpace+1]; + //assert(indlist[11] != -1); + } + + for (int i=0;triTable[iCubeIndex][i]!=-1;i+=3) + { + const int32_t ind0 = indlist[triTable[iCubeIndex][i ]]; + const int32_t ind1 = indlist[triTable[iCubeIndex][i+1]]; + const int32_t ind2 = indlist[triTable[iCubeIndex][i+2]]; + + if((ind0 != -1) && (ind1 != -1) && (ind2 != -1)) + { + m_meshCurrent->addTriangle(ind0, ind1, ind2); + } + }//For each triangle + }//For each cell + } + } +} diff --git a/library/PolyVoxCore/include/PolyVoxCore/MaterialDensityPair.h b/library/PolyVoxCore/include/PolyVoxCore/MaterialDensityPair.h index d3df49b8..9ae3bca0 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/MaterialDensityPair.h +++ b/library/PolyVoxCore/include/PolyVoxCore/MaterialDensityPair.h @@ -119,13 +119,11 @@ namespace PolyVox { // Default to a threshold value halfway between the min and max possible values. m_tThreshold = (MaterialDensityPair::getMinDensity() + MaterialDensityPair::getMaxDensity()) / 2; - m_eWrapMode = WrapModes::Border; } DefaultMarchingCubesController(DensityType tThreshold) { m_tThreshold = tThreshold; - m_eWrapMode = WrapModes::Border; } DensityType convertToDensity(MaterialDensityPair voxel) @@ -138,35 +136,18 @@ namespace PolyVox return voxel.getMaterial(); } - MaterialDensityPair getBorderValue(void) - { - return m_tBorder; - } - DensityType getThreshold(void) { return m_tThreshold; } - WrapMode getWrapMode(void) - { - return m_eWrapMode; - } - void setThreshold(DensityType tThreshold) { m_tThreshold = tThreshold; } - void setWrapMode(WrapMode eWrapMode) - { - m_eWrapMode = eWrapMode; - } - private: DensityType m_tThreshold; - WrapMode m_eWrapMode; - MaterialDensityPair m_tBorder; }; typedef MaterialDensityPair MaterialDensityPair44; diff --git a/library/PolyVoxCore/include/PolyVoxCore/RawVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/RawVolume.inl index ae91695c..a1ca12ea 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/RawVolume.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/RawVolume.inl @@ -31,7 +31,7 @@ namespace PolyVox RawVolume::RawVolume(const Region& regValid) :BaseVolume(regValid) { - setBorderValue(VoxelType()); + this->setBorderValue(VoxelType()); //Create a volume of the right size. initialise(regValid); diff --git a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeSampler.inl b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeSampler.inl index 7920fc6b..8e8deddf 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeSampler.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeSampler.inl @@ -21,10 +21,12 @@ freely, subject to the following restrictions: distribution. *******************************************************************************/ -#define BORDER_LOW(x) ((( x >> this->mVolume->m_uBlockSideLengthPower) << this->mVolume->m_uBlockSideLengthPower) != x) -#define BORDER_HIGH(x) ((( (x+1) >> this->mVolume->m_uBlockSideLengthPower) << this->mVolume->m_uBlockSideLengthPower) != (x+1)) -//#define BORDER_LOW(x) (( x % this->mVolume->m_uBlockSideLength) != 0) -//#define BORDER_HIGH(x) (( x % this->mVolume->m_uBlockSideLength) != this->mVolume->m_uBlockSideLength - 1) +#define CAN_GO_NEG_X(val) ((val > this->mVolume->getEnclosingRegion().getLowerCorner().getX()) && (val % this->mVolume->m_uBlockSideLength != 0)) +#define CAN_GO_POS_X(val) ((val < this->mVolume->getEnclosingRegion().getUpperCorner().getX()) && ((val + 1) % this->mVolume->m_uBlockSideLength != 0)) +#define CAN_GO_NEG_Y(val) ((val > this->mVolume->getEnclosingRegion().getLowerCorner().getY()) && (val % this->mVolume->m_uBlockSideLength != 0)) +#define CAN_GO_POS_Y(val) ((val < this->mVolume->getEnclosingRegion().getUpperCorner().getY()) && ((val + 1) % this->mVolume->m_uBlockSideLength != 0)) +#define CAN_GO_NEG_Z(val) ((val > this->mVolume->getEnclosingRegion().getLowerCorner().getZ()) && (val % this->mVolume->m_uBlockSideLength != 0)) +#define CAN_GO_POS_Z(val) ((val < this->mVolume->getEnclosingRegion().getUpperCorner().getZ()) && ((val + 1) % this->mVolume->m_uBlockSideLength != 0)) namespace PolyVox { @@ -299,7 +301,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1nx1ny1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) && BORDER_LOW(this->mYPosInVolume) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -309,7 +311,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1nx1ny0pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) && BORDER_LOW(this->mYPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) ) { return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength); } @@ -319,7 +321,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1nx1ny1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) && BORDER_LOW(this->mYPosInVolume) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -329,7 +331,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1nx0py1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - 1 - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -339,7 +341,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1nx0py0pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) ) { return *(mCurrentVoxel - 1); } @@ -349,7 +351,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1nx0py1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -359,7 +361,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1nx1py1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) && BORDER_HIGH(this->mYPosInVolume) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -369,7 +371,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1nx1py0pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) && BORDER_HIGH(this->mYPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) ) { return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength); } @@ -379,7 +381,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1nx1py1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mXPosInVolume) && BORDER_HIGH(this->mYPosInVolume) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - 1 + this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -391,7 +393,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel0px1ny1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mYPosInVolume) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -401,7 +403,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel0px1ny0pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mYPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) ) { return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength); } @@ -411,7 +413,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel0px1ny1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mYPosInVolume) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -421,7 +423,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel0px0py1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -441,7 +443,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel0px0py1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -451,7 +453,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel0px1py1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mYPosInVolume) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -461,7 +463,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel0px1py0pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mYPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_Y(this->mYPosInVolume) ) { return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength); } @@ -471,7 +473,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel0px1py1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mYPosInVolume) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -483,7 +485,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1px1ny1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) && BORDER_LOW(this->mYPosInVolume) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -493,7 +495,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1px1ny0pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) && BORDER_LOW(this->mYPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) ) { return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength); } @@ -503,7 +505,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1px1ny1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) && BORDER_LOW(this->mYPosInVolume) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -513,7 +515,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1px0py1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + 1 - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -523,7 +525,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1px0py0pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) ) { return *(mCurrentVoxel + 1); } @@ -533,7 +535,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1px0py1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -543,7 +545,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1px1py1nz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) && BORDER_HIGH(this->mYPosInVolume) && BORDER_LOW(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_NEG_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength - this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -553,7 +555,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1px1py0pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) && BORDER_HIGH(this->mYPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) ) { return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength); } @@ -563,7 +565,7 @@ namespace PolyVox template VoxelType SimpleVolume::Sampler::peekVoxel1px1py1pz(void) const { - if((this->isCurrentPositionValid()) && BORDER_HIGH(this->mXPosInVolume) && BORDER_HIGH(this->mYPosInVolume) && BORDER_HIGH(this->mZPosInVolume) ) + if((this->isCurrentPositionValid()) && CAN_GO_POS_X(this->mXPosInVolume) && CAN_GO_POS_Y(this->mYPosInVolume) && CAN_GO_POS_Z(this->mZPosInVolume) ) { return *(mCurrentVoxel + 1 + this->mVolume->m_uBlockSideLength + this->mVolume->m_uBlockSideLength*this->mVolume->m_uBlockSideLength); } @@ -571,5 +573,9 @@ namespace PolyVox } } -#undef BORDER_LOW -#undef BORDER_HIGH +#undef CAN_GO_NEG_X +#undef CAN_GO_POS_X +#undef CAN_GO_NEG_Y +#undef CAN_GO_POS_Y +#undef CAN_GO_NEG_Z +#undef CAN_GO_POS_Z diff --git a/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl b/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl index af9f852b..7966cde8 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/SurfaceMesh.inl @@ -21,6 +21,8 @@ freely, subject to the following restrictions: distribution. *******************************************************************************/ +#include + namespace PolyVox { template diff --git a/library/PolyVoxCore/include/PolyVoxCore/Vector.h b/library/PolyVoxCore/include/PolyVoxCore/Vector.h index 12c3c201..db3da0f8 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Vector.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Vector.h @@ -24,11 +24,11 @@ freely, subject to the following restrictions: #ifndef __PolyVox_Vector_H__ #define __PolyVox_Vector_H__ +#include "Impl/ErrorHandling.h" #include "Impl/TypeDef.h" #include "PolyVoxForwardDeclarations.h" -#include #include #include #include diff --git a/library/PolyVoxCore/include/PolyVoxCore/Vector.inl b/library/PolyVoxCore/include/PolyVoxCore/Vector.inl index 9e65543a..8aea0d6d 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Vector.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Vector.inl @@ -52,10 +52,8 @@ namespace PolyVox */ template Vector::Vector(StorageType x, StorageType y) - { -#ifndef SWIGPYTHON // SWIG instantiates all constructors, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - //static_assert(Size == 2, "This constructor should only be used for vectors with two elements."); -#endif + { + POLYVOX_STATIC_ASSERT(Size == 2, "This constructor should only be used for vectors with two elements."); m_tElements[0] = x; m_tElements[1] = y; @@ -69,10 +67,8 @@ namespace PolyVox */ template Vector::Vector(StorageType x, StorageType y, StorageType z) - { -#ifndef SWIGPYTHON // SWIG instantiates all constructors, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - //static_assert(Size == 3, "This constructor should only be used for vectors with three elements."); -#endif + { + POLYVOX_STATIC_ASSERT(Size == 3, "This constructor should only be used for vectors with three elements."); m_tElements[0] = x; m_tElements[1] = y; @@ -89,10 +85,8 @@ namespace PolyVox */ template Vector::Vector(StorageType x, StorageType y, StorageType z, StorageType w) - { -#ifndef SWIGPYTHON // SWIG instantiates all constructors, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - //static_assert(Size == 4, "This constructor should only be used for vectors with four elements."); -#endif + { + POLYVOX_STATIC_ASSERT(Size == 4, "This constructor should only be used for vectors with four elements."); m_tElements[0] = x; m_tElements[1] = y; @@ -135,14 +129,14 @@ namespace PolyVox template Vector::~Vector(void) { - // We put the static_asserts in the destructor because there is one one of these, + // We put the static asserts in the destructor because there is one one of these, // where as there are multiple constructors. // Force a vector to have a length greater than one. There is no need for a // vector with one element, and supporting this would cause confusion over the // behaviour of the constructor taking a single value, as this fills all elements - // to that value rather than just the first one. - //static_assert(Size > 1, "Vector must have a length greater than one."); + // to that value rather than just the first one. + POLYVOX_STATIC_ASSERT(Size > 1, "Vector must have a length greater than one."); } /** @@ -420,7 +414,7 @@ namespace PolyVox template inline StorageType Vector::getElement(uint32_t index) const { - assert(index < Size); + POLYVOX_ASSERT(index < Size, "Attempted to access invalid vector element."); return m_tElements[index]; } @@ -447,10 +441,8 @@ namespace PolyVox */ template inline StorageType Vector::getZ(void) const - { -#ifndef SWIGPYTHON // SWIG instantiates all getters, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - //static_assert(Size >= 3, "You can only get the 'z' component from a vector with at least three elements."); -#endif + { + POLYVOX_STATIC_ASSERT(Size >= 3, "You can only get the 'z' component from a vector with at least three elements."); return m_tElements[2]; } @@ -460,10 +452,8 @@ namespace PolyVox */ template inline StorageType Vector::getW(void) const - { -#ifndef SWIGPYTHON // SWIG instantiates all getters, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - //static_assert(Size >= 4, "You can only get the 'w' component from a vector with at least four elements."); -#endif + { + POLYVOX_STATIC_ASSERT(Size >= 4, "You can only get the 'w' component from a vector with at least four elements."); return m_tElements[3]; } @@ -475,7 +465,7 @@ namespace PolyVox template inline void Vector::setElement(uint32_t index, StorageType tValue) { - assert(index < Size); + POLYVOX_ASSERT(index < Size, "Attempted to access invalid vector element."); m_tElements[index] = tValue; } @@ -500,10 +490,9 @@ namespace PolyVox */ template inline void Vector::setElements(StorageType x, StorageType y, StorageType z) - { -#ifndef SWIGPYTHON // SWIG instantiates all setters, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - //static_assert(Size >= 3, "You can only use this version of setElements() on a vector with at least three elements."); -#endif + { + POLYVOX_STATIC_ASSERT(Size >= 3, "You can only use this version of setElements() on a vector with at least three elements."); + m_tElements[0] = x; m_tElements[1] = y; m_tElements[2] = z; @@ -518,10 +507,9 @@ namespace PolyVox */ template inline void Vector::setElements(StorageType x, StorageType y, StorageType z, StorageType w) - { -#ifndef SWIGPYTHON // SWIG instantiates all setters, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - //static_assert(Size >= 4, "You can only use this version of setElements() on a vector with at least four elements."); -#endif + { + POLYVOX_STATIC_ASSERT(Size >= 4, "You can only use this version of setElements() on a vector with at least four elements."); + m_tElements[0] = x; m_tElements[1] = y; m_tElements[2] = z; @@ -551,11 +539,10 @@ namespace PolyVox */ template inline void Vector::setZ(StorageType tZ) - { -#ifndef SWIGPYTHON // SWIG instantiates all setters, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - //static_assert(Size >= 3, "You can only set the 'w' component from a vector with at least three elements."); -#endif - m_tElements[2] = tZ; + { + POLYVOX_STATIC_ASSERT(Size >= 3, "You can only set the 'w' component from a vector with at least three elements."); + + m_tElements[2] = tZ; } /** @@ -563,11 +550,10 @@ namespace PolyVox */ template inline void Vector::setW(StorageType tW) - { -#ifndef SWIGPYTHON // SWIG instantiates all setters, unless we can find a way around that. Should we use SWIGIMPORT here, and then %import this file rather then %include it? - //static_assert(Size >= 4, "You can only set the 'w' component from a vector with at least four elements."); -#endif - m_tElements[3] = tW; + { + POLYVOX_STATIC_ASSERT(Size >= 4, "You can only set the 'w' component from a vector with at least four elements."); + + m_tElements[3] = tW; } /** @@ -663,7 +649,7 @@ namespace PolyVox { // Standard float rules apply for divide-by-zero m_tElements[ct] /= fLength; - assert(m_tElements[ct] == m_tElements[ct]); //Will assert if NAN + POLYVOX_ASSERT(m_tElements[ct] == m_tElements[ct], "Obtained NAN during vector normalisation. Perhaps the input vector was too short?"); } } }//namespace PolyVox diff --git a/library/PolyVoxCore/source/Impl/Utility.cpp b/library/PolyVoxCore/source/Impl/Utility.cpp index a56d0210..c82c9630 100644 --- a/library/PolyVoxCore/source/Impl/Utility.cpp +++ b/library/PolyVoxCore/source/Impl/Utility.cpp @@ -1,65 +1,63 @@ -/******************************************************************************* -Copyright (c) 2005-2009 David Williams - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*******************************************************************************/ - -#include "PolyVoxCore/Impl/Utility.h" - -#include -#include - -namespace PolyVox -{ - //Note: this function only works for inputs which are a power of two and not zero - //If this is not the case then the output is undefined. - uint8_t logBase2(uint32_t uInput) - { - //Debug mode validation - assert(uInput != 0); - assert(isPowerOf2(uInput)); - - //Release mode validation - if(uInput == 0) - { - //throw std::invalid_argument("Cannot compute the log of zero."); - } - if(!isPowerOf2(uInput)) - { - //throw std::invalid_argument("Input must be a power of two in order to compute the log."); - } - - uint32_t uResult = 0; - while( (uInput >> uResult) != 0) - { - ++uResult; - } - return static_cast(uResult-1); - } - - - bool isPowerOf2(uint32_t uInput) - { - if(uInput == 0) - return false; - else - return ((uInput & (uInput-1)) == 0); - } -} +/******************************************************************************* +Copyright (c) 2005-2009 David Williams + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*******************************************************************************/ + +#include "PolyVoxCore/Impl/ErrorHandling.h" +#include "PolyVoxCore/Impl/Utility.h" + +namespace PolyVox +{ + //Note: this function only works for inputs which are a power of two and not zero + //If this is not the case then the output is undefined. + uint8_t logBase2(uint32_t uInput) + { + //Debug mode validation + POLYVOX_ASSERT(uInput != 0, "Cannot compute the log of zero."); + POLYVOX_ASSERT(isPowerOf2(uInput), "Input must be a power of two in order to compute the log."); + + //Release mode validation + if(uInput == 0) + { + POLYVOX_THROW(std::invalid_argument, "Cannot compute the log of zero."); + } + if(!isPowerOf2(uInput)) + { + POLYVOX_THROW(std::invalid_argument, "Input must be a power of two in order to compute the log."); + } + + uint32_t uResult = 0; + while( (uInput >> uResult) != 0) + { + ++uResult; + } + return static_cast(uResult-1); + } + + + bool isPowerOf2(uint32_t uInput) + { + if(uInput == 0) + return false; + else + return ((uInput & (uInput-1)) == 0); + } +} diff --git a/library/PolyVoxUtil/CMakeLists.txt b/library/PolyVoxUtil/CMakeLists.txt index 85065346..7fbdb43e 100644 --- a/library/PolyVoxUtil/CMakeLists.txt +++ b/library/PolyVoxUtil/CMakeLists.txt @@ -20,8 +20,6 @@ # 3. This notice may not be removed or altered from any source # distribution. -CMAKE_MINIMUM_REQUIRED(VERSION 2.6) - PROJECT(PolyVoxUtil) #Projects source files @@ -42,7 +40,7 @@ SOURCE_GROUP("Sources" FILES ${UTIL_SRC_FILES}) SOURCE_GROUP("Headers" FILES ${UTIL_INC_FILES}) #Tell CMake the paths -INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include ${PolyVoxCore_SOURCE_DIR}/include) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include ${PolyVoxCore_BINARY_DIR}/include ${PolyVoxCore_SOURCE_DIR}/include) #There has to be a better way! LINK_DIRECTORIES(${PolyVoxCore_BINARY_DIR}/debug ${PolyVoxCore_BINARY_DIR}/release ${PolyVoxCore_BINARY_DIR}) diff --git a/library/bindings/CMakeLists.txt b/library/bindings/CMakeLists.txt index e976c015..040d0afe 100644 --- a/library/bindings/CMakeLists.txt +++ b/library/bindings/CMakeLists.txt @@ -36,7 +36,7 @@ if(ENABLE_BINDINGS) include(${SWIG_USE_FILE}) include_directories(${PYTHON_INCLUDE_PATH}) - include_directories(${PolyVoxCore_SOURCE_DIR}/include ${PolyVoxCore_SOURCE_DIR}/include/PolyVoxCore) + include_directories(${PolyVoxCore_BINARY_DIR}/include ${PolyVoxCore_SOURCE_DIR}/include ${PolyVoxCore_SOURCE_DIR}/include/PolyVoxCore) link_directories(${PolyVoxCore_BINARY_DIR}) set(CMAKE_SWIG_FLAGS "") @@ -48,6 +48,11 @@ if(ENABLE_BINDINGS) set_target_properties(${SWIG_MODULE_PolyVoxCorePython_REAL_NAME} PROPERTIES OUTPUT_NAME _PolyVoxCore) #set_target_properties(${SWIG_MODULE_PolyVoxCore_REAL_NAME} PROPERTIES SUFFIX ".pyd") SET_PROPERTY(TARGET ${SWIG_MODULE_PolyVoxCorePython_REAL_NAME} PROPERTY FOLDER "Bindings") + + swig_add_module(PolyVoxCoreCSharp csharp PolyVoxCore.i) + set(SWIG_MODULE_PolyVoxCoreCSharp_EXTRA_FLAGS "-dllimport;PolyVoxCoreCSharp") #This _should_ be inside UseSWIG.cmake - http://www.cmake.org/Bug/view.php?id=13814 + swig_link_libraries(PolyVoxCoreCSharp PolyVoxCore) + SET_PROPERTY(TARGET ${SWIG_MODULE_PolyVoxCoreCSharp_REAL_NAME} PROPERTY FOLDER "Bindings") else() set(BUILD_BINDINGS OFF CACHE BOOL "Will the bindings be built" FORCE) endif() diff --git a/library/bindings/PolyVoxCore.i b/library/bindings/PolyVoxCore.i index c25aaf5e..e5ddfa72 100644 --- a/library/bindings/PolyVoxCore.i +++ b/library/bindings/PolyVoxCore.i @@ -1,6 +1,7 @@ %module PolyVoxCore #define POLYVOX_API +%include "PolyVoxCore/Impl/CompilerCapabilities.h" %include "Impl/TypeDef.h" #define __attribute__(x) //Silence DEPRECATED errors diff --git a/library/bindings/Raycast.i b/library/bindings/Raycast.i index ed1869ca..8ae2c297 100644 --- a/library/bindings/Raycast.i +++ b/library/bindings/Raycast.i @@ -2,6 +2,8 @@ %{ #include "Raycast.h" +#ifdef SWIGPYTHON + template class PyCallback { @@ -43,11 +45,17 @@ PolyVox::RaycastResult raycastWithEndpointsPython(VolumeType* volData, const Pol return PolyVox::raycastWithEndpoints(volData, v3dStart, v3dEnd, newCallback); } +#endif + %} %include "Raycast.h" +#ifdef SWIGPYTHON + template PolyVox::RaycastResult raycastWithEndpointsPython(VolumeType* volData, const PolyVox::Vector3DFloat& v3dStart, const PolyVox::Vector3DFloat& v3dEnd, PyObject *callback); %template(raycastWithEndpointsSimpleVolumeuint8) raycastWithEndpointsPython, PyCallback > >; + +#endif diff --git a/library/bindings/Vector.i b/library/bindings/Vector.i index 15830de6..37d0b3ab 100644 --- a/library/bindings/Vector.i +++ b/library/bindings/Vector.i @@ -34,13 +34,29 @@ PROPERTY(PolyVox::Vector, z, getZ, setZ) STR() }; -%template(Vector3DFloat) PolyVox::Vector<3,float,float>; -%template(Vector3DDouble) PolyVox::Vector<3,double,double>; -%template(Vector3DInt8) PolyVox::Vector<3,int8_t,int32_t>; -%template(Vector3DUint8) PolyVox::Vector<3,uint8_t,int32_t>; -%template(Vector3DInt16) PolyVox::Vector<3,int16_t,int32_t>; -%template(Vector3DUint16) PolyVox::Vector<3,uint16_t,int32_t>; -%template(Vector3DInt32) PolyVox::Vector<3,int32_t,int32_t>; -%template(Vector3DUint32) PolyVox::Vector<3,uint32_t,int32_t>; +%feature("pythonprepend") PolyVox::Vector::operator< %{ + import warnings + warnings.warn("deprecated", DeprecationWarning) +%} + +//%csattributes PolyVox::Vector::operator< "[System.Obsolete(\"deprecated\")]" + +%define VECTOR3(StorageType,OperationType,ReducedStorageType) +%ignore PolyVox::Vector<3,StorageType,OperationType>::Vector(ReducedStorageType,ReducedStorageType,ReducedStorageType,ReducedStorageType); +%ignore PolyVox::Vector<3,StorageType,OperationType>::Vector(ReducedStorageType,ReducedStorageType); +%ignore PolyVox::Vector<3,StorageType,OperationType>::getW() const; +%ignore PolyVox::Vector<3,StorageType,OperationType>::setW(ReducedStorageType); +%ignore PolyVox::Vector<3,StorageType,OperationType>::setElements(ReducedStorageType,ReducedStorageType,ReducedStorageType,ReducedStorageType); +%template(Vector3D ## StorageType) PolyVox::Vector<3,StorageType,OperationType>; +%enddef + +VECTOR3(float,float,float) +VECTOR3(double,double,double) +VECTOR3(int8_t,int32_t,signed char) +VECTOR3(uint8_t,int32_t,unsigned char) +VECTOR3(int16_t,int32_t,signed short) +VECTOR3(uint16_t,int32_t,unsigned short) +VECTOR3(int32_t,int32_t,signed int) +VECTOR3(uint32_t,int32_t,unsigned int) //%rename(assign) Vector3DFloat::operator=; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2aa52de1..d76c8167 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -39,7 +39,7 @@ MACRO(CREATE_TEST headerfile sourcefile executablename) SET_PROPERTY(TARGET ${executablename} PROPERTY FOLDER "Tests") ENDMACRO(CREATE_TEST) -INCLUDE_DIRECTORIES(${PolyVox_SOURCE_DIR}/PolyVoxCore/include ${CMAKE_CURRENT_BINARY_DIR}) +INCLUDE_DIRECTORIES(${PolyVoxCore_BINARY_DIR}/include ${PolyVox_SOURCE_DIR}/PolyVoxCore/include ${CMAKE_CURRENT_BINARY_DIR}) REMOVE_DEFINITIONS(-DQT_GUI_LIB) #Make sure the tests don't link to the QtGui # Test Template. Copy and paste this template for consistant naming. diff --git a/tests/TestRaycast.py b/tests/TestRaycast.py index 829654b1..76045ba0 100644 --- a/tests/TestRaycast.py +++ b/tests/TestRaycast.py @@ -13,16 +13,16 @@ class TestSurfaceExtractor(unittest.TestCase): def setUp(self): #Create a small volume - r = PolyVoxCore.Region(PolyVoxCore.Vector3DInt32(0,0,0), PolyVoxCore.Vector3DInt32(31,31,31)) + r = PolyVoxCore.Region(PolyVoxCore.Vector3Dint32_t(0,0,0), PolyVoxCore.Vector3Dint32_t(31,31,31)) self.vol = PolyVoxCore.SimpleVolumeuint8(r) #Set one single voxel to have a reasonably high density - self.vol.setVoxelAt(PolyVoxCore.Vector3DInt32(5, 5, 5), 200) + self.vol.setVoxelAt(PolyVoxCore.Vector3Dint32_t(5, 5, 5), 200) def test_hit_voxel(self): - self.assertEqual(PolyVoxCore.raycastWithEndpointsSimpleVolumeuint8(self.vol, PolyVoxCore.Vector3DFloat(0,0,0), PolyVoxCore.Vector3DFloat(31,31,31), test_functor), 1) + self.assertEqual(PolyVoxCore.raycastWithEndpointsSimpleVolumeuint8(self.vol, PolyVoxCore.Vector3Dfloat(0,0,0), PolyVoxCore.Vector3Dfloat(31,31,31), test_functor), 1) def test_miss_voxel(self): - self.assertEqual(PolyVoxCore.raycastWithEndpointsSimpleVolumeuint8(self.vol, PolyVoxCore.Vector3DFloat(0,0,0), PolyVoxCore.Vector3DFloat(0,31,31), test_functor), 0) + self.assertEqual(PolyVoxCore.raycastWithEndpointsSimpleVolumeuint8(self.vol, PolyVoxCore.Vector3Dfloat(0,0,0), PolyVoxCore.Vector3Dfloat(0,31,31), test_functor), 0) if __name__ == '__main__': unittest.main() diff --git a/tests/TestSurfaceExtractor.cpp b/tests/TestSurfaceExtractor.cpp index 4bc7fc15..80416eb4 100644 --- a/tests/TestSurfaceExtractor.cpp +++ b/tests/TestSurfaceExtractor.cpp @@ -59,16 +59,6 @@ public: { return 50.0f; } - - WrapMode getWrapMode(void) - { - return WrapModes::Border; - } - - float getBorderValue(void) - { - return 0.0f; - } }; // These 'writeDensityValueToVoxel' functions provide a unified interface for writting densities to primative and class voxel types. @@ -132,7 +122,7 @@ void testForType(SurfaceMesh& result) DefaultMarchingCubesController controller; controller.setThreshold(50); - MarchingCubesSurfaceExtractor< SimpleVolume > extractor(&volData, volData.getEnclosingRegion(), &result, controller); + MarchingCubesSurfaceExtractor< SimpleVolume > extractor(&volData, volData.getEnclosingRegion(), &result, WrapModes::Border, 0, controller); extractor.execute(); } @@ -156,7 +146,7 @@ void testCustomController(SurfaceMesh& result) } CustomMarchingCubesController controller; - MarchingCubesSurfaceExtractor< SimpleVolume, CustomMarchingCubesController > extractor(&volData, volData.getEnclosingRegion(), &result, controller); + MarchingCubesSurfaceExtractor< SimpleVolume, CustomMarchingCubesController > extractor(&volData, volData.getEnclosingRegion(), &result, WrapModes::Border, 0, controller); extractor.execute(); } diff --git a/tests/TestSurfaceExtractor.py b/tests/TestSurfaceExtractor.py index 9ba40815..34cedaf9 100644 --- a/tests/TestSurfaceExtractor.py +++ b/tests/TestSurfaceExtractor.py @@ -10,10 +10,10 @@ class TestSurfaceExtractor(unittest.TestCase): import PolyVoxCore #Create a small volume - r = PolyVoxCore.Region(PolyVoxCore.Vector3DInt32(0,0,0), PolyVoxCore.Vector3DInt32(31,31,31)) + r = PolyVoxCore.Region(PolyVoxCore.Vector3Dint32_t(0,0,0), PolyVoxCore.Vector3Dint32_t(31,31,31)) vol = PolyVoxCore.SimpleVolumeuint8(r) #Set one single voxel to have a reasonably high density - vol.setVoxelAt(PolyVoxCore.Vector3DInt32(5, 5, 5), 200) + vol.setVoxelAt(PolyVoxCore.Vector3Dint32_t(5, 5, 5), 200) self.mesh = PolyVoxCore.SurfaceMeshPositionMaterialNormal() extractor = PolyVoxCore.MarchingCubesSurfaceExtractorSimpleVolumeuint8(vol, r, self.mesh) extractor.execute() diff --git a/tests/testvolume.cpp b/tests/testvolume.cpp index 088670b2..47130f95 100644 --- a/tests/testvolume.cpp +++ b/tests/testvolume.cpp @@ -321,33 +321,33 @@ void TestVolume::testSimpleVolumeDirectAccess() void TestVolume::testSimpleVolumeSamplers() { - /*int32_t result = 0; + int32_t result = 0; QBENCHMARK { result = testSamplersWithWrapping(m_pSimpleVolume); } - QCOMPARE(result, static_cast(-601818385)); //FXME - Wrong value?!*/ + QCOMPARE(result, static_cast(-928601007)); } void TestVolume::testLargeVolumeDirectAccess() { - /*int32_t result = 0; + int32_t result = 0; QBENCHMARK { result = testDirectAccessWithWrapping(m_pLargeVolume); } - QCOMPARE(result, static_cast(-601818385)); //FXME - Wrong value?!*/ + QCOMPARE(result, static_cast(-928601007)); } void TestVolume::testLargeVolumeSamplers() { int32_t result = 0; - /*QBENCHMARK + QBENCHMARK { result = testSamplersWithWrapping(m_pLargeVolume); } - QCOMPARE(result, static_cast(-601818385)); //FXME - Wrong value?!*/ + QCOMPARE(result, static_cast(-928601007)); } QTEST_MAIN(TestVolume)