├── .gitignore ├── CMakeLists.txt ├── Header.png ├── LICENSE ├── README.md ├── cmake ├── AddCompilerFlag.cmake ├── CXX11.cmake ├── CheckCCompilerFlag.cmake ├── CheckCXXCompilerFlag.cmake ├── CheckMicCCompilerFlag.cmake ├── CheckMicCXXCompilerFlag.cmake ├── FindSDL.cmake └── OptimizeForArchitecture.cmake ├── models └── XYZRGB-Dragon.oct ├── run_builder.bat ├── run_viewer.bat ├── setup_builds.bat ├── setup_builds.sh └── src ├── ChunkedAllocator.hpp ├── Debug.cpp ├── Debug.hpp ├── Events.cpp ├── Events.hpp ├── IntTypes.hpp ├── Main.cpp ├── PlyLoader.cpp ├── PlyLoader.hpp ├── SDLMain.h ├── SDLMain.m ├── ThreadBarrier.cpp ├── ThreadBarrier.hpp ├── Timer.hpp ├── Util.cpp ├── Util.hpp ├── VoxelData.cpp ├── VoxelData.hpp ├── VoxelOctree.cpp ├── VoxelOctree.hpp ├── math ├── Mat4.cpp ├── Mat4.hpp ├── MatrixStack.cpp ├── MatrixStack.hpp └── Vec3.hpp ├── third-party ├── lz4.c ├── lz4.h ├── ply.h ├── plyfile.c ├── tribox3.c └── tribox3.h └── thread ├── TaskGroup.hpp ├── ThreadPool.cpp ├── ThreadPool.hpp ├── ThreadUtils.cpp └── ThreadUtils.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | /* 2 | /*/ 3 | !/.gitignore 4 | !/CMakeLists.txt 5 | !/setup_builds.sh 6 | !/setup_builds.bat 7 | !/Header.png 8 | !/README.md 9 | !/LICENSE 10 | !/src/ 11 | !/models/ 12 | !/cmake/ 13 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(sparse-voxel-octrees) 3 | 4 | set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) 5 | 6 | include(CXX11) 7 | check_for_cxx11_compiler(CXX11_COMPILER) 8 | 9 | if(CXX11_COMPILER) 10 | enable_cxx11() 11 | else() 12 | message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} seems to have no C++11 support. Please try again with a more recent compiler version.") 13 | endif() 14 | 15 | include(OptimizeForArchitecture) 16 | OptimizeForArchitecture() 17 | 18 | if (MSVC) 19 | add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS) 20 | endif() 21 | 22 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Vc_ARCHITECTURE_FLAGS}") 23 | 24 | if (CMAKE_COMPILER_IS_GNUCXX) 25 | set(CXX_WARNINGS "-Wall -Wextra -Wpointer-arith -Wcast-align -fstrict-aliasing -Wno-unused-local-typedefs") 26 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_WARNINGS} -fvisibility-inlines-hidden") 27 | endif() 28 | 29 | find_package(SDL REQUIRED) 30 | 31 | include_directories(src ${SDL_INCLUDE_DIR}) 32 | file(GLOB_RECURSE Sources "src/*.hpp" "src/*.cpp" "src/*.c") 33 | 34 | if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 35 | set(Sources ${Sources} "src/SDLMain.m") 36 | endif() 37 | 38 | if (WIN32) 39 | add_executable(sparse-voxel-octrees WIN32 ${Sources}) 40 | else() 41 | add_executable(sparse-voxel-octrees ${Sources}) 42 | endif() 43 | target_link_libraries(sparse-voxel-octrees ${SDLMAIN_LIBRARY} ${SDL_LIBRARY}) 44 | 45 | set_target_properties(sparse-voxel-octrees PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/bin) 46 | set_target_properties(sparse-voxel-octrees PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/bin) -------------------------------------------------------------------------------- /Header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tunabrain/sparse-voxel-octrees/c4c2a9d3015056c269c73430f223e69e3b933b83/Header.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Benedikt Bitterli 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 19 | 3. This notice may not be removed or altered from any source 20 | distribution. 21 | 22 | The CMake models in the cmake/ subfolder were written by Alexander Neundorf, 23 | Matthias Kretz and Nathan Osman and come with their own licenses. See the 24 | respective .cmake files for more information. 25 | 26 | The files src/third-party/tribox.c and src/third-party/tribox.h are courtesy 27 | of Tomas Akenine-Moller 28 | 29 | The files src/third-party/lc4.c and src/third-party/lc4.h are part of the LZ4 30 | compression library available here: https://github.com/Cyan4973/lz4 31 | 32 | Its license text is reproduced here 33 | 34 | LZ4 - Fast LZ compression algorithm 35 | Copyright (C) 2011-2015, Yann Collet. 36 | 37 | BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) 38 | 39 | Redistribution and use in source and binary forms, with or without 40 | modification, are permitted provided that the following conditions are 41 | met: 42 | 43 | * Redistributions of source code must retain the above copyright 44 | notice, this list of conditions and the following disclaimer. 45 | * Redistributions in binary form must reproduce the above 46 | copyright notice, this list of conditions and the following disclaimer 47 | in the documentation and/or other materials provided with the 48 | distribution. 49 | 50 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 51 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 52 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 53 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 54 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 55 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 56 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 57 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 58 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 59 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 60 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 | 62 | You can contact the author at : 63 | - LZ4 source repository : https://github.com/Cyan4973/lz4 64 | - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c 65 | 66 | 67 | The files src/third-party/ply.h and src/third-party/plyfile.c are part of the PLY 68 | library. Its license text is reproduced here 69 | 70 | Copyright (c) 1994 The Board of Trustees of The Leland Stanford 71 | Junior University. All rights reserved. 72 | 73 | Permission to use, copy, modify and distribute this software and its 74 | documentation for any purpose is hereby granted without fee, provided 75 | that the above copyright notice and this permission notice appear in 76 | all copies of this software and that you do not sell the software. 77 | 78 | THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND, 79 | EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 80 | WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![XYZRGB Dragon](https://raw.github.com/tunabrain/sparse-voxel-octrees/master/Header.png) 2 | 3 | Sparse Voxel Octrees 4 | ========= 5 | 6 | This project provides a multithreaded, CPU Sparse Voxel Octree implementation in C++, capable of raytracing large datasets in real-time, converting raw voxel files to octrees and converting mesh data (in form of PLY files) to voxel octrees. 7 | 8 | The conversion routines are capable of handling datasets much larger than the working memory, allowing the creation and rendering of very large octrees (resolution 8192x8192x8192 and up). 9 | 10 | This implementation closely follows the paper [Efficient Sparse Voxel Octrees](https://research.nvidia.com/publication/efficient-sparse-voxel-octrees) by Samuli Laine and Tero Karras. 11 | 12 | The XYZRGB dragon belongs the the Stanford 3D Scanning Repository and is available from [their homepage](http://graphics.stanford.edu/data/3Dscanrep/) 13 | 14 | Compilation 15 | =========== 16 | 17 | A recent compiler cupporting C++11, CMake 2.8 and SDL 1.2 are required to build. 18 | 19 | To build on Linux, you can use the `setup_builds.sh` shell script to setup build and release configurations using CMake. After running `setup_builds.sh`, run `make` inside the newly created `build/release/` folder. Alternatively, you can use the standard CMake CLI to configure the project. 20 | 21 | To build on Windows, you will need Visual Studio 2013 or later. Before running CMake, make sure that 22 | 23 | * CMake is on the `PATH` environment variable. An easy check to verify that this is the case is to open CMD and type `cmake`, which should output the CMake CLI help. 24 | * You have Windows 64bit binaries of SDL 1.2. These are available [here](https://www.libsdl.org/download-1.2.php). Make sure to grab the `SDL-devel-1.2.XX-VC.zip`. Note that if you are using a newer version of Visual Studio, you may need to compile SDL yourself in order to be compatible. Please see the SDL website for details 25 | * The environment variable `SDLDIR` exists and is set to the path to the folder containing SDL1.2 (you will have to set it up manually - it needs to be a system environment variable, not a user variable, for CMake to find it). CMake will use this variable to find the SDL relevant files and configure MSVC to use them 26 | 27 | After these prerequisites are setup, you can run `setup_builds.bat` to create the Visual Studio files. It will create a folder `vstudio` containing the `sparse-voxel-octrees.sln` solution. 28 | 29 | Alternatively, you can also run CMake manually or setup the MSVC project yourself, without CMake. The sources don't require special build flags, so the latter is easily doable if you can't get CMake to work. 30 | 31 | To build on macOS, you will need to install SDL first (i.e. `brew install sdl`). Then build it like a regular CMake project: 32 | 33 | mkdir build 34 | cd build 35 | cmake ../ 36 | make 37 | ./sparse-voxel-octrees -viewer ../models/XYZRGB-Dragon.oct 38 | 39 | On macOS, you may need to click+drag within the application window first to make the render visible. 40 | 41 | Note: If building fails on macOS, you can try commenting out the follow lines in CMakeLists.txt 42 | 43 | #if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 44 | # set(Sources ${Sources} "src/SDLMain.m") 45 | #endif() 46 | 47 | Usage 48 | ===== 49 | 50 | On startup, the program will load the sample octree and render it. Left mouse rotates the model, right mouse zooms. Escape quits the program. In order to make CLI arguments easier on Windows, you can use run_viewer.bat to start the viewer. 51 | 52 | Note that due to repository size considerations, the sample octree has poor resolution (256x256x256). You can generate larger octrees using the code, however. See Main.cpp:initScene for details. You can also use run_builder.bat to build the XYZ RGB dragon model. To do this, simply download the XYZ RGB dragon model from http://graphics.stanford.edu/data/3Dscanrep/ and place it in the models folder. 53 | 54 | Code 55 | ==== 56 | 57 | Main.cpp controls application setup, thread spawning and basic rendering (should move this into a different file instead at some point). 58 | 59 | VoxelOctree.cpp provides routines for octree raymarching as well as generating, saving and loading octrees. It uses VoxelData.cpp, which robustly handles fast access to non-square, non-power-of-two voxel data not completely loaded in memory. 60 | 61 | The VoxelData class can also pull voxel data directly from PlyLoader.cpp, generating data from triangle meshes on demand, instead of from file, which vastly improves conversion performance due to elimination of file I/O. 62 | -------------------------------------------------------------------------------- /cmake/AddCompilerFlag.cmake: -------------------------------------------------------------------------------- 1 | # - Add a given compiler flag to flags variables. 2 | # AddCompilerFlag( []) 3 | # or 4 | # AddCompilerFlag( [C_FLAGS ] [CXX_FLAGS ] [C_RESULT ] 5 | # [CXX_RESULT ]) 6 | 7 | #============================================================================= 8 | # Copyright 2010-2013 Matthias Kretz 9 | # 10 | # Redistribution and use in source and binary forms, with or without 11 | # modification, are permitted provided that the following conditions are 12 | # met: 13 | # 14 | # * Redistributions of source code must retain the above copyright notice, 15 | # this list of conditions and the following disclaimer. 16 | # 17 | # * Redistributions in binary form must reproduce the above copyright notice, 18 | # this list of conditions and the following disclaimer in the documentation 19 | # and/or other materials provided with the distribution. 20 | # 21 | # * The names of Kitware, Inc., the Insight Consortium, or the names of 22 | # any consortium members, or of any contributors, may not be used to 23 | # endorse or promote products derived from this software without 24 | # specific prior written permission. 25 | # 26 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 27 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 30 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 32 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 33 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 34 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | #============================================================================= 37 | 38 | get_filename_component(_currentDir "${CMAKE_CURRENT_LIST_FILE}" PATH) 39 | include("${_currentDir}/CheckCCompilerFlag.cmake") 40 | include("${_currentDir}/CheckCXXCompilerFlag.cmake") 41 | include("${_currentDir}/CheckMicCCompilerFlag.cmake") 42 | include("${_currentDir}/CheckMicCXXCompilerFlag.cmake") 43 | 44 | macro(AddCompilerFlag _flag) 45 | string(REGEX REPLACE "[-.+/:= ]" "_" _flag_esc "${_flag}") 46 | 47 | set(_c_flags "CMAKE_C_FLAGS") 48 | set(_cxx_flags "CMAKE_CXX_FLAGS") 49 | set(_mic_c_flags "CMAKE_MIC_C_FLAGS") 50 | set(_mic_cxx_flags "CMAKE_MIC_CXX_FLAGS") 51 | set(_c_result tmp) 52 | set(_cxx_result tmp) 53 | set(_mic_c_result) 54 | set(_mic_cxx_result) 55 | if(${ARGC} EQUAL 2) 56 | message(WARNING "Deprecated use of the AddCompilerFlag macro.") 57 | unset(_c_result) 58 | set(_cxx_result ${ARGV1}) 59 | elseif(${ARGC} GREATER 2) 60 | set(state 0) 61 | unset(_c_flags) 62 | unset(_cxx_flags) 63 | unset(_mic_c_flags) 64 | unset(_mic_cxx_flags) 65 | unset(_c_result) 66 | unset(_cxx_result) 67 | unset(_mic_c_result) 68 | unset(_mic_cxx_result) 69 | foreach(_arg ${ARGN}) 70 | if(_arg STREQUAL "C_FLAGS") 71 | set(state 1) 72 | if(NOT DEFINED _c_result) 73 | set(_c_result tmp0) 74 | endif() 75 | elseif(_arg STREQUAL "CXX_FLAGS") 76 | set(state 2) 77 | if(NOT DEFINED _cxx_result) 78 | set(_cxx_result tmp1) 79 | endif() 80 | elseif(_arg STREQUAL "C_RESULT") 81 | set(state 3) 82 | elseif(_arg STREQUAL "CXX_RESULT") 83 | set(state 4) 84 | elseif(_arg STREQUAL "MIC_C_RESULT") 85 | set(state 5) 86 | elseif(_arg STREQUAL "MIC_CXX_RESULT") 87 | set(state 6) 88 | elseif(_arg STREQUAL "MIC_C_FLAGS") 89 | if(NOT DEFINED _mic_c_result) 90 | set(_mic_c_result tmp2) 91 | endif() 92 | set(state 7) 93 | elseif(_arg STREQUAL "MIC_CXX_FLAGS") 94 | if(NOT DEFINED _mic_cxx_result) 95 | set(_mic_cxx_result tmp3) 96 | endif() 97 | set(state 8) 98 | elseif(state EQUAL 1) 99 | set(_c_flags "${_arg}") 100 | elseif(state EQUAL 2) 101 | set(_cxx_flags "${_arg}") 102 | elseif(state EQUAL 3) 103 | set(_c_result "${_arg}") 104 | elseif(state EQUAL 4) 105 | set(_cxx_result "${_arg}") 106 | elseif(state EQUAL 5) 107 | set(_mic_c_result "${_arg}") 108 | elseif(state EQUAL 6) 109 | set(_mic_cxx_result "${_arg}") 110 | elseif(state EQUAL 7) 111 | set(_mic_c_flags "${_arg}") 112 | elseif(state EQUAL 8) 113 | set(_mic_cxx_flags "${_arg}") 114 | else() 115 | message(FATAL_ERROR "Syntax error for AddCompilerFlag") 116 | endif() 117 | endforeach() 118 | endif() 119 | 120 | if("${_flag}" STREQUAL "-mfma") 121 | # Compiling with FMA3 support may fail only at the assembler level. 122 | # In that case we need to have such an instruction in the test code 123 | set(_code "#include 124 | __m128 foo(__m128 x) { return _mm_fmadd_ps(x, x, x); } 125 | int main() { return 0; }") 126 | elseif("${_flag}" STREQUAL "-stdlib=libc++") 127 | # Compiling with libc++ not only requires a compiler that understands it, but also 128 | # the libc++ headers itself 129 | set(_code "#include 130 | int main() { return 0; }") 131 | else() 132 | set(_code "int main() { return 0; }") 133 | endif() 134 | 135 | if(DEFINED _c_result) 136 | check_c_compiler_flag("${_flag}" check_c_compiler_flag_${_flag_esc} "${_code}") 137 | set(${_c_result} ${check_c_compiler_flag_${_flag_esc}}) 138 | endif() 139 | if(DEFINED _cxx_result) 140 | check_cxx_compiler_flag("${_flag}" check_cxx_compiler_flag_${_flag_esc} "${_code}") 141 | set(${_cxx_result} ${check_cxx_compiler_flag_${_flag_esc}}) 142 | endif() 143 | 144 | if(check_c_compiler_flag_${_flag_esc} AND DEFINED _c_flags) 145 | if(${_c_flags}) 146 | set(${_c_flags} "${${_c_flags}} ${_flag}") 147 | else() 148 | set(${_c_flags} "${_flag}") 149 | endif() 150 | endif() 151 | if(check_cxx_compiler_flag_${_flag_esc} AND DEFINED _cxx_flags) 152 | if(${_cxx_flags}) 153 | set(${_cxx_flags} "${${_cxx_flags}} ${_flag}") 154 | else() 155 | set(${_cxx_flags} "${_flag}") 156 | endif() 157 | endif() 158 | 159 | if(MIC_NATIVE_FOUND) 160 | if(DEFINED _mic_c_result) 161 | check_mic_c_compiler_flag("${_flag}" check_mic_c_compiler_flag_${_flag_esc} "${_code}") 162 | set(${_mic_c_result} ${check_mic_c_compiler_flag_${_flag_esc}}) 163 | endif() 164 | if(DEFINED _mic_cxx_result) 165 | check_mic_cxx_compiler_flag("${_flag}" check_mic_cxx_compiler_flag_${_flag_esc} "${_code}") 166 | set(${_mic_cxx_result} ${check_mic_cxx_compiler_flag_${_flag_esc}}) 167 | endif() 168 | 169 | if(check_mic_c_compiler_flag_${_flag_esc} AND DEFINED _mic_c_flags) 170 | set(${_mic_c_flags} "${${_mic_c_flags}} ${_flag}") 171 | endif() 172 | if(check_mic_cxx_compiler_flag_${_flag_esc} AND DEFINED _mic_cxx_flags) 173 | set(${_mic_cxx_flags} "${${_mic_cxx_flags}} ${_flag}") 174 | endif() 175 | endif() 176 | endmacro(AddCompilerFlag) 177 | -------------------------------------------------------------------------------- /cmake/CXX11.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013 Nathan Osman 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | # THE SOFTWARE. 20 | 21 | # Determines whether or not the compiler supports C++11 22 | macro(check_for_cxx11_compiler _VAR) 23 | message(STATUS "Checking for C++11 compiler") 24 | set(${_VAR}) 25 | if((MSVC AND (MSVC10 OR MSVC11 OR MSVC12 OR MSVC14 OR MSVC15)) OR 26 | (CMAKE_COMPILER_IS_GNUCXX AND NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 4.6) OR 27 | (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 3.1)) 28 | set(${_VAR} 1) 29 | message(STATUS "Checking for C++11 compiler - available") 30 | else() 31 | message(STATUS "Checking for C++11 compiler - unavailable") 32 | endif() 33 | endmacro() 34 | 35 | # Sets the appropriate flag to enable C++11 support 36 | macro(enable_cxx11) 37 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 38 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") 39 | endif() 40 | endmacro() 41 | 42 | -------------------------------------------------------------------------------- /cmake/CheckCCompilerFlag.cmake: -------------------------------------------------------------------------------- 1 | # - Check whether the C compiler supports a given flag. 2 | # CHECK_C_COMPILER_FLAG( ) 3 | # - the compiler flag 4 | # - variable to store the result 5 | # This internally calls the check_c_source_compiles macro. 6 | # See help for CheckCSourceCompiles for a listing of variables 7 | # that can modify the build. 8 | 9 | #============================================================================= 10 | # Copyright 2006-2009 Kitware, Inc. 11 | # Copyright 2006 Alexander Neundorf 12 | # Copyright 2011-2013 Matthias Kretz 13 | # 14 | # Redistribution and use in source and binary forms, with or without 15 | # modification, are permitted provided that the following conditions are 16 | # met: 17 | # 18 | # * Redistributions of source code must retain the above copyright notice, 19 | # this list of conditions and the following disclaimer. 20 | # 21 | # * Redistributions in binary form must reproduce the above copyright notice, 22 | # this list of conditions and the following disclaimer in the documentation 23 | # and/or other materials provided with the distribution. 24 | # 25 | # * The names of Kitware, Inc., the Insight Consortium, or the names of 26 | # any consortium members, or of any contributors, may not be used to 27 | # endorse or promote products derived from this software without 28 | # specific prior written permission. 29 | # 30 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 31 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 34 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 36 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 37 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 38 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 | #============================================================================= 41 | 42 | INCLUDE(CheckCSourceCompiles) 43 | 44 | MACRO (CHECK_C_COMPILER_FLAG _FLAG _RESULT) 45 | SET(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") 46 | SET(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}") 47 | if(${ARGC} GREATER 2) 48 | SET(TEST_SOURCE "${ARGV2}") 49 | else() 50 | SET(TEST_SOURCE "int main() { return 0;}") 51 | endif() 52 | CHECK_C_SOURCE_COMPILES("${TEST_SOURCE}" ${_RESULT} 53 | # Some compilers do not fail with a bad flag 54 | FAIL_REGEX "error: bad value (.*) for .* switch" # GNU 55 | FAIL_REGEX "argument unused during compilation" # clang 56 | FAIL_REGEX "is valid for .* but not for C" # GNU 57 | FAIL_REGEX "unrecognized .*option" # GNU 58 | FAIL_REGEX "ignored for target" # GNU 59 | FAIL_REGEX "ignoring unknown option" # MSVC 60 | FAIL_REGEX "[Uu]nknown option" # HP 61 | FAIL_REGEX "[Ww]arning: [Oo]ption" # SunPro 62 | FAIL_REGEX "command option .* is not recognized" # XL 63 | FAIL_REGEX "WARNING: unknown flag:" # Open64 64 | FAIL_REGEX "command line error" # ICC 65 | FAIL_REGEX "command line warning" # ICC 66 | FAIL_REGEX "#10236:" # ICC: File not found 67 | FAIL_REGEX " #10159: " # ICC 68 | FAIL_REGEX " #10353: " # ICC: option '-mfma' ignored, suggest using '-march=core-avx2' 69 | ) 70 | SET (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") 71 | ENDMACRO (CHECK_C_COMPILER_FLAG) 72 | 73 | -------------------------------------------------------------------------------- /cmake/CheckCXXCompilerFlag.cmake: -------------------------------------------------------------------------------- 1 | # - Check whether the CXX compiler supports a given flag. 2 | # CHECK_CXX_COMPILER_FLAG( ) 3 | # - the compiler flag 4 | # - variable to store the result 5 | # This internally calls the check_cxx_source_compiles macro. See help 6 | # for CheckCXXSourceCompiles for a listing of variables that can 7 | # modify the build. 8 | 9 | #============================================================================= 10 | # Copyright 2006-2009 Kitware, Inc. 11 | # Copyright 2006 Alexander Neundorf 12 | # Copyright 2011-2013 Matthias Kretz 13 | # 14 | # Redistribution and use in source and binary forms, with or without 15 | # modification, are permitted provided that the following conditions are 16 | # met: 17 | # 18 | # * Redistributions of source code must retain the above copyright notice, 19 | # this list of conditions and the following disclaimer. 20 | # 21 | # * Redistributions in binary form must reproduce the above copyright notice, 22 | # this list of conditions and the following disclaimer in the documentation 23 | # and/or other materials provided with the distribution. 24 | # 25 | # * The names of Kitware, Inc., the Insight Consortium, or the names of 26 | # any consortium members, or of any contributors, may not be used to 27 | # endorse or promote products derived from this software without 28 | # specific prior written permission. 29 | # 30 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 31 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 34 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 36 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 37 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 38 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 | #============================================================================= 41 | 42 | INCLUDE(CheckCXXSourceCompiles) 43 | 44 | MACRO (CHECK_CXX_COMPILER_FLAG _FLAG _RESULT) 45 | SET(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") 46 | SET(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}") 47 | if(${ARGC} GREATER 2) 48 | SET(TEST_SOURCE "${ARGV2}") 49 | else() 50 | SET(TEST_SOURCE "int main() { return 0;}") 51 | endif() 52 | CHECK_CXX_SOURCE_COMPILES("${TEST_SOURCE}" ${_RESULT} 53 | # Some compilers do not fail with a bad flag 54 | FAIL_REGEX "error: bad value (.*) for .* switch" # GNU 55 | FAIL_REGEX "argument unused during compilation" # clang 56 | FAIL_REGEX "is valid for .* but not for C\\\\+\\\\+" # GNU 57 | FAIL_REGEX "unrecognized .*option" # GNU 58 | FAIL_REGEX "ignored for target" # GNU 59 | FAIL_REGEX "ignoring unknown option" # MSVC 60 | FAIL_REGEX "[Uu]nknown option" # HP 61 | FAIL_REGEX "[Ww]arning: [Oo]ption" # SunPro 62 | FAIL_REGEX "command option .* is not recognized" # XL 63 | FAIL_REGEX "WARNING: unknown flag:" # Open64 64 | FAIL_REGEX "command line error" # ICC 65 | FAIL_REGEX "command line warning" # ICC 66 | FAIL_REGEX "#10236:" # ICC: File not found 67 | FAIL_REGEX " #10159: " # ICC 68 | FAIL_REGEX " #10353: " # ICC: option '-mfma' ignored, suggest using '-march=core-avx2' 69 | ) 70 | SET (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") 71 | ENDMACRO (CHECK_CXX_COMPILER_FLAG) 72 | 73 | -------------------------------------------------------------------------------- /cmake/CheckMicCCompilerFlag.cmake: -------------------------------------------------------------------------------- 1 | # - Check whether the MIC C compiler supports a given flag. 2 | # CHECK_MIC_C_COMPILER_FLAG( ) 3 | # - the compiler flag 4 | # - variable to store the result 5 | # This internally calls the check_c_source_compiles macro. See help 6 | # for CheckCSourceCompiles for a listing of variables that can 7 | # modify the build. 8 | 9 | #============================================================================= 10 | # Copyright 2006-2009 Kitware, Inc. 11 | # Copyright 2006 Alexander Neundorf 12 | # Copyright 2011-2013 Matthias Kretz 13 | # 14 | # Redistribution and use in source and binary forms, with or without 15 | # modification, are permitted provided that the following conditions are 16 | # met: 17 | # 18 | # * Redistributions of source code must retain the above copyright notice, 19 | # this list of conditions and the following disclaimer. 20 | # 21 | # * Redistributions in binary form must reproduce the above copyright notice, 22 | # this list of conditions and the following disclaimer in the documentation 23 | # and/or other materials provided with the distribution. 24 | # 25 | # * The names of Kitware, Inc., the Insight Consortium, or the names of 26 | # any consortium members, or of any contributors, may not be used to 27 | # endorse or promote products derived from this software without 28 | # specific prior written permission. 29 | # 30 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 31 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 34 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 36 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 37 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 38 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 | #============================================================================= 41 | 42 | macro(check_mic_c_compiler_flag _FLAG _RESULT) 43 | if("${_RESULT}" MATCHES "^${_RESULT}$") 44 | set(_tmpdir "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp") 45 | if(${ARGC} GREATER 2) 46 | file(WRITE "${_tmpdir}/src.c" "${ARGV2}") 47 | else() 48 | file(WRITE "${_tmpdir}/src.c" "int main() { return 0; }") 49 | endif() 50 | 51 | execute_process( 52 | COMMAND "${MIC_CC}" -mmic -c -o "${_tmpdir}/src.o" 53 | "${_FLAG}" "${_tmpdir}/src.c" 54 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 55 | RESULT_VARIABLE ${_RESULT} 56 | OUTPUT_VARIABLE OUTPUT 57 | ERROR_VARIABLE OUTPUT 58 | ) 59 | 60 | if(${_RESULT}) 61 | foreach(_fail_regex 62 | "error: bad value (.*) for .* switch" # GNU 63 | "argument unused during compilation" # clang 64 | "is valid for .* but not for C" # GNU 65 | "unrecognized .*option" # GNU 66 | "ignored for target" # GNU 67 | "ignoring unknown option" # MSVC 68 | "[Uu]nknown option" # HP 69 | "[Ww]arning: [Oo]ption" # SunPro 70 | "command option .* is not recognized" # XL 71 | "WARNING: unknown flag:" # Open64 72 | "command line error" # ICC 73 | "command line warning" # ICC 74 | "#10236:" # ICC: File not found 75 | ) 76 | if("${OUTPUT}" MATCHES "${_fail_regex}") 77 | set(${_RESULT} FALSE) 78 | endif() 79 | endforeach() 80 | endif() 81 | 82 | if(${_RESULT}) 83 | set(${_RESULT} 1 CACHE INTERNAL "Test ${_FLAG}") 84 | message(STATUS "Performing Test Check MIC C Compiler flag ${_FLAG} - Success") 85 | file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log 86 | "Performing MIC C Compiler Flag Test ${_FLAG} succeded with the following output:\n" 87 | "${OUTPUT}\n" 88 | "COMMAND: ${MIC_CC} -mmic -c -o ${_tmpdir}/src.o ${_FLAG} ${_tmpdir}/src.cpp\n" 89 | ) 90 | else() 91 | message(STATUS "Performing Test Check MIC C Compiler flag ${_FLAG} - Failed") 92 | set(${_RESULT} "" CACHE INTERNAL "Test ${_FLAG}") 93 | file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log 94 | "Performing MIC C Compiler Flag Test ${_FLAG} failed with the following output:\n" 95 | "${OUTPUT}\n" 96 | "COMMAND: ${MIC_CC} -mmic -c -o ${_tmpdir}/src.o ${_FLAG} ${_tmpdir}/src.cpp\n" 97 | ) 98 | endif() 99 | endif() 100 | endmacro() 101 | 102 | -------------------------------------------------------------------------------- /cmake/CheckMicCXXCompilerFlag.cmake: -------------------------------------------------------------------------------- 1 | # - Check whether the MIC CXX compiler supports a given flag. 2 | # CHECK_MIC_CXX_COMPILER_FLAG( ) 3 | # - the compiler flag 4 | # - variable to store the result 5 | # This internally calls the check_cxx_source_compiles macro. See help 6 | # for CheckCXXSourceCompiles for a listing of variables that can 7 | # modify the build. 8 | 9 | #============================================================================= 10 | # Copyright 2006-2009 Kitware, Inc. 11 | # Copyright 2006 Alexander Neundorf 12 | # Copyright 2011-2013 Matthias Kretz 13 | # 14 | # Redistribution and use in source and binary forms, with or without 15 | # modification, are permitted provided that the following conditions are 16 | # met: 17 | # 18 | # * Redistributions of source code must retain the above copyright notice, 19 | # this list of conditions and the following disclaimer. 20 | # 21 | # * Redistributions in binary form must reproduce the above copyright notice, 22 | # this list of conditions and the following disclaimer in the documentation 23 | # and/or other materials provided with the distribution. 24 | # 25 | # * The names of Kitware, Inc., the Insight Consortium, or the names of 26 | # any consortium members, or of any contributors, may not be used to 27 | # endorse or promote products derived from this software without 28 | # specific prior written permission. 29 | # 30 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' 31 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 34 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 36 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 37 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 38 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 | #============================================================================= 41 | 42 | macro(check_mic_cxx_compiler_flag _FLAG _RESULT) 43 | if("${_RESULT}" MATCHES "^${_RESULT}$") 44 | set(_tmpdir "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp") 45 | if(${ARGC} GREATER 2) 46 | file(WRITE "${_tmpdir}/src.cpp" "${ARGV2}") 47 | else() 48 | file(WRITE "${_tmpdir}/src.cpp" "int main() { return 0; }") 49 | endif() 50 | 51 | execute_process( 52 | COMMAND "${MIC_CXX}" -mmic -c -o "${_tmpdir}/src.o" 53 | "${_FLAG}" "${_tmpdir}/src.cpp" 54 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 55 | RESULT_VARIABLE ${_RESULT} 56 | OUTPUT_VARIABLE OUTPUT 57 | ERROR_VARIABLE OUTPUT 58 | ) 59 | 60 | if(${_RESULT} EQUAL 0) 61 | foreach(_fail_regex 62 | "error: bad value (.*) for .* switch" # GNU 63 | "argument unused during compilation" # clang 64 | "is valid for .* but not for C\\\\+\\\\+" # GNU 65 | "unrecognized .*option" # GNU 66 | "ignored for target" # GNU 67 | "ignoring unknown option" # MSVC 68 | "[Uu]nknown option" # HP 69 | "[Ww]arning: [Oo]ption" # SunPro 70 | "command option .* is not recognized" # XL 71 | "WARNING: unknown flag:" # Open64 72 | "command line error" # ICC 73 | "command line warning" # ICC 74 | "#10236:" # ICC: File not found 75 | ) 76 | if("${OUTPUT}" MATCHES "${_fail_regex}") 77 | set(${_RESULT} FALSE) 78 | endif() 79 | endforeach() 80 | endif() 81 | 82 | if(${_RESULT} EQUAL 0) 83 | set(${_RESULT} 1 CACHE INTERNAL "Test ${_FLAG}") 84 | message(STATUS "Performing Test Check MIC C++ Compiler flag ${_FLAG} - Success") 85 | file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log 86 | "Performing MIC C++ Compiler Flag Test ${_FLAG} succeded with the following output:\n" 87 | "${OUTPUT}\n" 88 | "COMMAND: ${MIC_CXX} -mmic -c -o ${_tmpdir}/src.o ${_FLAG} ${_tmpdir}/src.cpp\n" 89 | ) 90 | else() 91 | message(STATUS "Performing Test Check MIC C++ Compiler flag ${_FLAG} - Failed") 92 | set(${_RESULT} "" CACHE INTERNAL "Test ${_FLAG}") 93 | file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log 94 | "Performing MIC C++ Compiler Flag Test ${_FLAG} failed with the following output:\n" 95 | "${OUTPUT}\n" 96 | "COMMAND: ${MIC_CXX} -mmic -c -o ${_tmpdir}/src.o ${_FLAG} ${_tmpdir}/src.cpp\n" 97 | ) 98 | endif() 99 | endif() 100 | endmacro() 101 | 102 | -------------------------------------------------------------------------------- /cmake/FindSDL.cmake: -------------------------------------------------------------------------------- 1 | # - Locate SDL library 2 | # This module defines 3 | # SDL_LIBRARY, the name of the library to link against 4 | # SDL_FOUND, if false, do not try to link to SDL 5 | # SDL_INCLUDE_DIR, where to find SDL.h 6 | # SDL_VERSION_STRING, human-readable string containing the version of SDL 7 | # 8 | # This module responds to the the flag: 9 | # SDL_BUILDING_LIBRARY 10 | # If this is defined, then no SDL_main will be linked in because 11 | # only applications need main(). 12 | # Otherwise, it is assumed you are building an application and this 13 | # module will attempt to locate and set the the proper link flags 14 | # as part of the returned SDL_LIBRARY variable. 15 | # 16 | # Don't forget to include SDLmain.h and SDLmain.m your project for the 17 | # OS X framework based version. (Other versions link to -lSDLmain which 18 | # this module will try to find on your behalf.) Also for OS X, this 19 | # module will automatically add the -framework Cocoa on your behalf. 20 | # 21 | # 22 | # Additional Note: If you see an empty SDL_LIBRARY_TEMP in your configuration 23 | # and no SDL_LIBRARY, it means CMake did not find your SDL library 24 | # (SDL.dll, libsdl.so, SDL.framework, etc). 25 | # Set SDL_LIBRARY_TEMP to point to your SDL library, and configure again. 26 | # Similarly, if you see an empty SDLMAIN_LIBRARY, you should set this value 27 | # as appropriate. These values are used to generate the final SDL_LIBRARY 28 | # variable, but when these values are unset, SDL_LIBRARY does not get created. 29 | # 30 | # 31 | # $SDLDIR is an environment variable that would 32 | # correspond to the ./configure --prefix=$SDLDIR 33 | # used in building SDL. 34 | # l.e.galup 9-20-02 35 | # 36 | # Modified by Eric Wing. 37 | # Added code to assist with automated building by using environmental variables 38 | # and providing a more controlled/consistent search behavior. 39 | # Added new modifications to recognize OS X frameworks and 40 | # additional Unix paths (FreeBSD, etc). 41 | # Also corrected the header search path to follow "proper" SDL guidelines. 42 | # Added a search for SDLmain which is needed by some platforms. 43 | # Added a search for threads which is needed by some platforms. 44 | # Added needed compile switches for MinGW. 45 | # 46 | # On OSX, this will prefer the Framework version (if found) over others. 47 | # People will have to manually change the cache values of 48 | # SDL_LIBRARY to override this selection or set the CMake environment 49 | # CMAKE_INCLUDE_PATH to modify the search paths. 50 | # 51 | # Note that the header path has changed from SDL/SDL.h to just SDL.h 52 | # This needed to change because "proper" SDL convention 53 | # is #include "SDL.h", not . This is done for portability 54 | # reasons because not all systems place things in SDL/ (see FreeBSD). 55 | 56 | #============================================================================= 57 | # Copyright 2003-2009 Kitware, Inc. 58 | # Copyright 2012 Benjamin Eikel 59 | # 60 | # Distributed under the OSI-approved BSD License (the "License"); 61 | # see accompanying file Copyright.txt for details. 62 | # 63 | # This software is distributed WITHOUT ANY WARRANTY; without even the 64 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 65 | # See the License for more information. 66 | #============================================================================= 67 | # (To distribute this file outside of CMake, substitute the full 68 | # License text for the above reference.) 69 | 70 | find_path(SDL_INCLUDE_DIR SDL.h 71 | HINTS 72 | ENV SDLDIR 73 | PATH_SUFFIXES SDL include/SDL include/SDL12 include/SDL11 include 74 | ) 75 | 76 | # SDL-1.1 is the name used by FreeBSD ports... 77 | # don't confuse it for the version number. 78 | find_library(SDL_LIBRARY_TEMP 79 | NAMES SDL SDL-1.1 80 | HINTS 81 | ENV SDLDIR ENV LIBRARY_PATH 82 | PATH_SUFFIXES lib lib/x64 83 | ) 84 | 85 | if(NOT SDL_BUILDING_LIBRARY) 86 | if(NOT ${SDL_INCLUDE_DIR} MATCHES ".framework") 87 | # Non-OS X framework versions expect you to also dynamically link to 88 | # SDLmain. This is mainly for Windows and OS X. Other (Unix) platforms 89 | # seem to provide SDLmain for compatibility even though they don't 90 | # necessarily need it. 91 | find_library(SDLMAIN_LIBRARY 92 | NAMES SDLmain SDLmain-1.1 93 | HINTS 94 | ENV SDLDIR ENV LIBRARY_PATH 95 | PATH_SUFFIXES lib lib/x64 96 | PATHS 97 | /sw 98 | /opt/local 99 | /opt/csw 100 | /opt 101 | ) 102 | endif() 103 | endif() 104 | 105 | # SDL may require threads on your system. 106 | # The Apple build may not need an explicit flag because one of the 107 | # frameworks may already provide it. 108 | # But for non-OSX systems, I will use the CMake Threads package. 109 | if(NOT APPLE) 110 | find_package(Threads) 111 | endif() 112 | 113 | # MinGW needs an additional library, mwindows 114 | # It's total link flags should look like -lmingw32 -lSDLmain -lSDL -lmwindows 115 | # (Actually on second look, I think it only needs one of the m* libraries.) 116 | if(MINGW) 117 | set(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") 118 | endif() 119 | 120 | if(SDL_LIBRARY_TEMP) 121 | # For SDLmain 122 | if(SDLMAIN_LIBRARY AND NOT SDL_BUILDING_LIBRARY) 123 | list(FIND SDL_LIBRARY_TEMP "${SDLMAIN_LIBRARY}" _SDL_MAIN_INDEX) 124 | if(_SDL_MAIN_INDEX EQUAL -1) 125 | set(SDL_LIBRARY_TEMP "${SDLMAIN_LIBRARY}" ${SDL_LIBRARY_TEMP}) 126 | endif() 127 | unset(_SDL_MAIN_INDEX) 128 | endif() 129 | 130 | # For OS X, SDL uses Cocoa as a backend so it must link to Cocoa. 131 | # CMake doesn't display the -framework Cocoa string in the UI even 132 | # though it actually is there if I modify a pre-used variable. 133 | # I think it has something to do with the CACHE STRING. 134 | # So I use a temporary variable until the end so I can set the 135 | # "real" variable in one-shot. 136 | if(APPLE) 137 | set(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} "-framework Cocoa") 138 | endif() 139 | 140 | # For threads, as mentioned Apple doesn't need this. 141 | # In fact, there seems to be a problem if I used the Threads package 142 | # and try using this line, so I'm just skipping it entirely for OS X. 143 | if(NOT APPLE) 144 | set(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) 145 | endif() 146 | 147 | # For MinGW library 148 | if(MINGW) 149 | set(SDL_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL_LIBRARY_TEMP}) 150 | endif() 151 | 152 | # Set the final string here so the GUI reflects the final state. 153 | set(SDL_LIBRARY ${SDL_LIBRARY_TEMP} CACHE STRING "Where the SDL Library can be found") 154 | # Set the temp variable to INTERNAL so it is not seen in the CMake GUI 155 | set(SDL_LIBRARY_TEMP "${SDL_LIBRARY_TEMP}" CACHE INTERNAL "") 156 | endif() 157 | 158 | if(SDL_INCLUDE_DIR AND EXISTS "${SDL_INCLUDE_DIR}/SDL_version.h") 159 | file(STRINGS "${SDL_INCLUDE_DIR}/SDL_version.h" SDL_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+[0-9]+$") 160 | file(STRINGS "${SDL_INCLUDE_DIR}/SDL_version.h" SDL_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_MINOR_VERSION[ \t]+[0-9]+$") 161 | file(STRINGS "${SDL_INCLUDE_DIR}/SDL_version.h" SDL_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_PATCHLEVEL[ \t]+[0-9]+$") 162 | string(REGEX REPLACE "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL_VERSION_MAJOR "${SDL_VERSION_MAJOR_LINE}") 163 | string(REGEX REPLACE "^#define[ \t]+SDL_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL_VERSION_MINOR "${SDL_VERSION_MINOR_LINE}") 164 | string(REGEX REPLACE "^#define[ \t]+SDL_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL_VERSION_PATCH "${SDL_VERSION_PATCH_LINE}") 165 | set(SDL_VERSION_STRING ${SDL_VERSION_MAJOR}.${SDL_VERSION_MINOR}.${SDL_VERSION_PATCH}) 166 | unset(SDL_VERSION_MAJOR_LINE) 167 | unset(SDL_VERSION_MINOR_LINE) 168 | unset(SDL_VERSION_PATCH_LINE) 169 | unset(SDL_VERSION_MAJOR) 170 | unset(SDL_VERSION_MINOR) 171 | unset(SDL_VERSION_PATCH) 172 | endif() 173 | 174 | include(FindPackageHandleStandardArgs) 175 | 176 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL 177 | REQUIRED_VARS SDL_LIBRARY SDL_INCLUDE_DIR 178 | VERSION_VAR SDL_VERSION_STRING) 179 | -------------------------------------------------------------------------------- /models/XYZRGB-Dragon.oct: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tunabrain/sparse-voxel-octrees/c4c2a9d3015056c269c73430f223e69e3b933b83/models/XYZRGB-Dragon.oct -------------------------------------------------------------------------------- /run_builder.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd bin 3 | sparse-voxel-octrees -builder --resolution 1024 --mode 0 ../models/xyzrgb_dragon.ply ../models/XYZRGB-Dragon.oct -------------------------------------------------------------------------------- /run_viewer.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd bin 3 | sparse-voxel-octrees -viewer ../models/XYZRGB-Dragon.oct -------------------------------------------------------------------------------- /setup_builds.bat: -------------------------------------------------------------------------------- 1 | rmdir /S /Q vstudio 2 | mkdir vstudio 3 | cd vstudio 4 | cmake ../ -G "Visual Studio 12 2013 Win64" -------------------------------------------------------------------------------- /setup_builds.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | GENERATOR="" 3 | if [ "$(uname -o)" = "Msys" ]; 4 | then 5 | GENERATOR=(-G "MSYS Makefiles") 6 | fi 7 | if [[ -n "$*" ]]; 8 | then 9 | GENERATOR=(-G "$*") 10 | fi 11 | 12 | ROOTDIR="$(dirname "$(readlink -fn "$0")")" 13 | 14 | rm -rf build 15 | mkdir build 16 | cd build 17 | 18 | mkdir release 19 | cd release 20 | cmake -DCMAKE_BUILD_TYPE=Release "${GENERATOR[@]}" ${ROOTDIR} 21 | cd .. 22 | 23 | mkdir debug 24 | cd debug 25 | cmake -DCMAKE_BUILD_TYPE=Debug "${GENERATOR[@]}" ${ROOTDIR} 26 | -------------------------------------------------------------------------------- /src/ChunkedAllocator.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef CHUNKEDALLOCATOR_HPP_ 25 | #define CHUNKEDALLOCATOR_HPP_ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | template 33 | class ChunkedAllocator { 34 | static const size_t ChunkSize = 4096; 35 | 36 | struct InsertionPoint { 37 | size_t idx; 38 | Type data; 39 | }; 40 | 41 | size_t _size; 42 | std::vector> _data; 43 | std::vector _insertions; 44 | 45 | public: 46 | ChunkedAllocator() : _size(0) {} 47 | 48 | size_t size() { 49 | return _size; 50 | } 51 | 52 | size_t insertionCount() { 53 | return _insertions.size(); 54 | } 55 | 56 | Type &operator[](size_t i) { 57 | return _data[i/ChunkSize][i % ChunkSize]; 58 | } 59 | 60 | const Type &operator[](size_t i) const { 61 | return _data[i/ChunkSize][i % ChunkSize]; 62 | } 63 | 64 | void pushBack(Type t) { 65 | if ((_size % ChunkSize) == 0) 66 | _data.emplace_back(new Type[ChunkSize]); 67 | 68 | _data[_size/ChunkSize][_size % ChunkSize] = std::move(t); 69 | _size++; 70 | } 71 | 72 | void insert(size_t index, Type data) { 73 | _insertions.emplace_back(InsertionPoint{index, std::move(data)}); 74 | } 75 | 76 | std::unique_ptr finalize() { 77 | std::sort(_insertions.begin(), _insertions.end(), [](const InsertionPoint &a, const InsertionPoint &b) { 78 | return a.idx < b.idx; 79 | }); 80 | 81 | size_t length = _size + _insertions.size(); 82 | std::unique_ptr result(new Type[length]); 83 | 84 | size_t insertionIdx = 0; 85 | size_t outputOffset = 0; 86 | size_t inputOffset = 0; 87 | while (inputOffset < _size) { 88 | size_t dataIdx = inputOffset/ChunkSize; 89 | size_t dataOffset = inputOffset % ChunkSize; 90 | 91 | // Clear out data blocks once we've copied them completely 92 | if (dataOffset == 0 && dataIdx > 0) 93 | _data[dataIdx - 1].reset(); 94 | 95 | size_t copySize = std::min(ChunkSize - dataOffset, _size - inputOffset); 96 | if (insertionIdx < _insertions.size()) 97 | copySize = std::min(copySize, _insertions[insertionIdx].idx - inputOffset); 98 | 99 | if (copySize > 0) { 100 | std::memcpy(result.get() + outputOffset, _data[dataIdx].get() + dataOffset, copySize*sizeof(Type)); 101 | inputOffset += copySize; 102 | outputOffset += copySize; 103 | } 104 | 105 | if (insertionIdx < _insertions.size() && _insertions[insertionIdx].idx == inputOffset) 106 | result[outputOffset++] = std::move(_insertions[insertionIdx++].data); 107 | } 108 | 109 | _insertions.clear(); 110 | _data.clear(); 111 | _size = 0; 112 | 113 | return std::move(result); 114 | } 115 | }; 116 | 117 | #endif /* CHUNKEDALLOCATOR_HPP_ */ 118 | -------------------------------------------------------------------------------- /src/Debug.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "Debug.hpp" 29 | 30 | const char *levelStr[] = {"WARN", "INFO", "DEBUG"}; 31 | 32 | void debugLog(const char *module, DebugLevel level, const char *fmt, ...) 33 | { 34 | if (level > DEBUG_LEVEL) 35 | return; 36 | va_list argp; 37 | va_start(argp, fmt); 38 | printf("%s | %-10s | ", levelStr[level], module); 39 | vprintf(fmt, argp); 40 | va_end(argp); 41 | fflush(stdout); 42 | } 43 | 44 | void debugAssert(const char *file, int line, bool exp, const char *format, ...) { 45 | if (!exp) { 46 | printf("ASSERTION FAILURE: %s:%d: ", file, line); 47 | va_list args; 48 | va_start(args, format); 49 | vprintf(format, args); 50 | va_end(args); 51 | fflush(stdout); 52 | exit(EXIT_FAILURE); 53 | } 54 | } 55 | 56 | void debugFail(const char *file, int line, const char *format, ...) { 57 | printf("PROGRAM FAILURE: %s:%d: ", file, line); 58 | va_list args; 59 | va_start(args, format); 60 | vprintf(format, args); 61 | va_end(args); 62 | fflush(stdout); 63 | exit(EXIT_FAILURE); 64 | } 65 | -------------------------------------------------------------------------------- /src/Debug.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef DEBUG_HPP_ 25 | #define DEBUG_HPP_ 26 | 27 | #define DEBUG_LEVEL DEBUG 28 | 29 | enum DebugLevel { 30 | WARN, 31 | INFO, 32 | DEBUG 33 | }; 34 | 35 | #ifndef NDEBUG 36 | # define DBG(MODULE, LEVEL, ...) debugLog(MODULE, LEVEL, __VA_ARGS__) 37 | # define ASSERT(EXP, ...) debugAssert(__FILE__, __LINE__, (bool)(EXP), __VA_ARGS__) 38 | # define FAIL(...) debugFail(__FILE__, __LINE__, __VA_ARGS__) 39 | #else 40 | # define DBG(MODULE, LEVEL, FMT, ...) do {} while (0); 41 | # define ASSERT(A, B, ...) do { (bool)(A); } while (0); 42 | # define FAIL(A, ...) do {} while (0); 43 | #endif 44 | 45 | void debugLog(const char *module, DebugLevel level, const char *format, ...); 46 | void debugAssert(const char *file, int line, bool exp, const char *format, ...); 47 | void debugFail(const char *file, int line, const char *format, ...); 48 | 49 | #endif /* DEBUG_HPP_ */ 50 | -------------------------------------------------------------------------------- /src/Events.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include "Events.hpp" 25 | 26 | #include 27 | 28 | static int mouseX = 0; 29 | static int mouseY = 0; 30 | static int mouseZ = 0; 31 | static int mouseXSpeed = 0; 32 | static int mouseYSpeed = 0; 33 | static int mouseZSpeed = 0; 34 | static int mouseDown[] = {0, 0}; 35 | static char keyHit[SDLK_LAST]; 36 | static char keyDown[SDLK_LAST]; 37 | 38 | static void processEvent(SDL_Event event) { 39 | switch (event.type) { 40 | case SDL_MOUSEMOTION: 41 | mouseX = event.motion.x; 42 | mouseY = event.motion.y; 43 | mouseXSpeed = event.motion.xrel; 44 | mouseYSpeed = event.motion.yrel; 45 | 46 | break; 47 | case SDL_MOUSEBUTTONDOWN: 48 | if (event.button.button == SDL_BUTTON_WHEELUP) { 49 | mouseZSpeed = 1; 50 | mouseZ += 1; 51 | } else if(event.button.button == SDL_BUTTON_WHEELDOWN) { 52 | mouseZSpeed = -1; 53 | mouseZ -= 1; 54 | } else if (event.button.button == SDL_BUTTON_LEFT) 55 | mouseDown[0] = 1; 56 | else if (event.button.button == SDL_BUTTON_RIGHT) 57 | mouseDown[1] = 1; 58 | 59 | break; 60 | case SDL_MOUSEBUTTONUP: 61 | if (event.button.button == SDL_BUTTON_LEFT) 62 | mouseDown[0] = 0; 63 | else if (event.button.button == SDL_BUTTON_RIGHT) 64 | mouseDown[1] = 0; 65 | 66 | break; 67 | case SDL_KEYDOWN: 68 | keyHit[event.key.keysym.sym] = 1; 69 | keyDown[event.key.keysym.sym] = 1; 70 | 71 | break; 72 | case SDL_KEYUP: 73 | keyDown[event.key.keysym.sym] = 0; 74 | 75 | break; 76 | case SDL_QUIT: 77 | exit(0); 78 | } 79 | } 80 | 81 | int waitEvent() { 82 | SDL_Event event; 83 | SDL_WaitEvent(&event); 84 | processEvent(event); 85 | 86 | return event.type; 87 | } 88 | 89 | void checkEvents() { 90 | SDL_Event event; 91 | while (SDL_PollEvent(&event)) 92 | processEvent(event); 93 | } 94 | 95 | int getMouseX() { 96 | return mouseX; 97 | } 98 | 99 | int getMouseY() { 100 | return mouseY; 101 | } 102 | 103 | int getMouseZ() { 104 | return mouseZ; 105 | } 106 | 107 | int getMouseXSpeed() { 108 | int Temp = mouseXSpeed; 109 | 110 | mouseXSpeed = 0; 111 | 112 | return Temp; 113 | } 114 | 115 | int getMouseYSpeed() { 116 | int Temp = mouseYSpeed; 117 | 118 | mouseYSpeed = 0; 119 | 120 | return Temp; 121 | } 122 | 123 | int getMouseZSpeed() { 124 | int Temp = mouseZSpeed; 125 | 126 | mouseZSpeed = 0; 127 | 128 | return Temp; 129 | } 130 | 131 | int getMouseDown(int Button) { 132 | return mouseDown[Button]; 133 | } 134 | 135 | int getKeyHit(int Key) { 136 | int Temp = keyHit[Key]; 137 | 138 | keyHit[Key] = 0; 139 | 140 | return Temp; 141 | } 142 | 143 | int getKeyDown(int Key) { 144 | return keyDown[Key]; 145 | } 146 | -------------------------------------------------------------------------------- /src/Events.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef EVENTS_HPP_ 25 | #define EVENTS_HPP_ 26 | 27 | void checkEvents(); 28 | int waitEvent(); 29 | int getMouseX(); 30 | int getMouseY(); 31 | int getMouseZ(); 32 | int getMouseXSpeed(); 33 | int getMouseYSpeed(); 34 | int getMouseZSpeed(); 35 | int getMouseDown(int button); 36 | int getKeyHit(int button); 37 | int getKeyDown(int button); 38 | 39 | #endif /* EVENTS_HPP_ */ 40 | -------------------------------------------------------------------------------- /src/IntTypes.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef INTTYPES_HPP_ 25 | #define INTTYPES_HPP_ 26 | 27 | #include 28 | 29 | typedef std::uint8_t uint8; 30 | typedef std::uint16_t uint16; 31 | typedef std::uint32_t uint32; 32 | typedef std::uint64_t uint64; 33 | 34 | typedef std::int8_t int8; 35 | typedef std::int16_t int16; 36 | typedef std::int32_t int32; 37 | typedef std::int64_t int64; 38 | 39 | #endif /* INTTYPES_HPP_ */ 40 | -------------------------------------------------------------------------------- /src/Main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | 25 | #include "ThreadBarrier.hpp" 26 | #include "VoxelOctree.hpp" 27 | #include "PlyLoader.hpp" 28 | #include "VoxelData.hpp" 29 | #include "Events.hpp" 30 | #include "Timer.hpp" 31 | #include "Util.hpp" 32 | 33 | #include "thread/ThreadUtils.hpp" 34 | #include "thread/ThreadPool.hpp" 35 | 36 | #include "math/MatrixStack.hpp" 37 | #include "math/Vec3.hpp" 38 | #include "math/Mat4.hpp" 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | 52 | #ifndef M_PI 53 | #define M_PI 3.14159265358979323846 54 | #endif 55 | 56 | /* Number of threads to use - adapt this to your platform for optimal results */ 57 | static const int NumThreads = 16; 58 | /* Screen resolution */ 59 | static const int GWidth = 1280; 60 | static const int GHeight = 720; 61 | 62 | static const float AspectRatio = GHeight/(float)GWidth; 63 | static const int TileSize = 8; 64 | 65 | static SDL_Surface *backBuffer; 66 | static ThreadBarrier *barrier; 67 | 68 | static std::atomic doTerminate; 69 | static std::atomic renderHalfSize; 70 | 71 | struct BatchData { 72 | int id; 73 | int x0, y0; 74 | int x1, y1; 75 | 76 | int tilesX, tilesY; 77 | float *depthBuffer; 78 | VoxelOctree *tree; 79 | }; 80 | 81 | Vec3 shade(int intNormal, const Vec3 &ray, const Vec3 &light) { 82 | Vec3 n; 83 | float c; 84 | decompressMaterial(intNormal, n, c); 85 | 86 | float d = std::max(light.dot(ray.reflect(n)), 0.0f); 87 | float specular = d*d; 88 | 89 | return c*0.9f*std::abs(light.dot(n)) + specular*0.2f; 90 | } 91 | 92 | void renderTile(int x0, int y0, int x1, int y1, int stride, float scale, float zx, float zy, float zz, 93 | const Mat4 &tform, const Vec3 &light, VoxelOctree *tree, const Vec3 &pos, float minT) { 94 | uint32 *buffer = (uint32 *)backBuffer->pixels; 95 | int pitch = backBuffer->pitch; 96 | 97 | float dy = AspectRatio - y0*scale; 98 | for (int y = y0; y < y1; ++y, dy -= scale) { 99 | float dx = -1.0f + x0*scale; 100 | for (int x = x0; x < x1; ++x, dx += scale) { 101 | int cornerX = x - ((x - x0) % stride); 102 | int cornerY = y - ((y - y0) % stride); 103 | if (cornerX != x || cornerY != y) { 104 | buffer[x + y*pitch/4] = buffer[cornerX + cornerY*pitch/4]; 105 | continue; 106 | } 107 | 108 | Vec3 dir = Vec3( 109 | dx*tform.a11 + dy*tform.a12 + zx, 110 | dx*tform.a21 + dy*tform.a22 + zy, 111 | dx*tform.a31 + dy*tform.a32 + zz 112 | ); 113 | dir *= invSqrt(dir.x*dir.x + dir.y*dir.y + dir.z*dir.z); 114 | 115 | uint32 intNormal; 116 | float t; 117 | Vec3 col; 118 | if (tree->raymarch(pos + dir*minT, dir, 0.0f, intNormal, t)) 119 | col = shade(intNormal, dir, light); 120 | 121 | #ifdef __APPLE__ 122 | uint32 color = 123 | uint32(std::min(col.x, 1.0f)*255.0) << 8 | 124 | (uint32(std::min(col.y, 1.0f)*255.0) << 16) | 125 | (uint32(std::min(col.z, 1.0f)*255.0) << 24) | 126 | 0x000000FFu; 127 | #else 128 | uint32 color = 129 | uint32(std::min(col.x, 1.0f)*255.0) | 130 | (uint32(std::min(col.y, 1.0f)*255.0) << 8) | 131 | (uint32(std::min(col.z, 1.0f)*255.0) << 16) | 132 | 0xFF000000u; 133 | #endif 134 | buffer[x + y*pitch/4] = color; 135 | } 136 | } 137 | } 138 | 139 | void renderBatch(BatchData *data) { 140 | const float TreeMiss = 1e10; 141 | 142 | int x0 = data->x0, y0 = data->y0; 143 | int x1 = data->x1, y1 = data->y1; 144 | int tilesX = data->tilesX; 145 | int tilesY = data->tilesY; 146 | float *depthBuffer = data->depthBuffer; 147 | VoxelOctree *tree = data->tree; 148 | 149 | Mat4 tform; 150 | MatrixStack::get(INV_MODELVIEW_STACK, tform); 151 | 152 | Vec3 pos = tform*Vec3() + tree->center() + Vec3(1.0); 153 | 154 | tform.a14 = tform.a24 = tform.a34 = 0.0f; 155 | 156 | float scale = 2.0f/GWidth; 157 | float tileScale = TileSize*scale; 158 | float planeDist = 1.0f/std::tan(float(M_PI)/6.0f); 159 | float zx = planeDist*tform.a13, zy = planeDist*tform.a23, zz = planeDist*tform.a33; 160 | float coarseScale = 2.0f*TileSize/(planeDist*GHeight); 161 | int stride = renderHalfSize ? 3 : 1; 162 | 163 | Vec3 light = (tform*Vec3(-1.0, 1.0, -1.0)).normalize(); 164 | 165 | std::memset((uint8 *)backBuffer->pixels + y0*backBuffer->pitch, 0, (y1 - y0)*backBuffer->pitch); 166 | 167 | float dy = AspectRatio - y0*scale; 168 | for (int y = 0, idx = 0; y < tilesY; y++, dy -= tileScale) { 169 | float dx = -1.0f + x0*scale; 170 | for (int x = 0; x < tilesX; x++, dx += tileScale, idx++) { 171 | Vec3 dir = Vec3( 172 | dx*tform.a11 + dy*tform.a12 + zx, 173 | dx*tform.a21 + dy*tform.a22 + zy, 174 | dx*tform.a31 + dy*tform.a32 + zz 175 | ); 176 | dir *= invSqrt(dir.x*dir.x + dir.y*dir.y + dir.z*dir.z); 177 | 178 | uint32 intNormal; 179 | float t; 180 | Vec3 col; 181 | if (tree->raymarch(pos, dir, coarseScale, intNormal, t)) 182 | depthBuffer[idx] = t; 183 | else 184 | depthBuffer[idx] = TreeMiss; 185 | 186 | if (x > 0 && y > 0) { 187 | float minT = std::min(std::min(depthBuffer[idx], depthBuffer[idx - 1]), 188 | std::min(depthBuffer[idx - tilesX], 189 | depthBuffer[idx - tilesX - 1])); 190 | 191 | if (minT != TreeMiss) { 192 | int tx0 = (x - 1)*TileSize + x0; 193 | int ty0 = (y - 1)*TileSize + y0; 194 | int tx1 = std::min(tx0 + TileSize, x1); 195 | int ty1 = std::min(ty0 + TileSize, y1); 196 | renderTile(tx0, ty0, tx1, ty1, stride, scale, zx, zy, zz, tform, light, tree, pos, 197 | std::max(minT - 0.03f, 0.0f)); 198 | } 199 | } 200 | } 201 | } 202 | } 203 | 204 | int renderLoop(void *threadData) { 205 | BatchData *data = (BatchData *)threadData; 206 | 207 | float radius = 1.0f; 208 | float pitch = 0.0f; 209 | float yaw = 0.0f; 210 | 211 | if (data->id == 0) { 212 | MatrixStack::set(VIEW_STACK, Mat4::translate(Vec3(0.0f, 0.0f, -radius))); 213 | MatrixStack::set(MODEL_STACK, Mat4()); 214 | } 215 | 216 | while (!doTerminate) { 217 | barrier->waitPre(); 218 | renderBatch(data); 219 | barrier->waitPost(); 220 | 221 | if (data->id == 0) { 222 | if (SDL_MUSTLOCK(backBuffer)) 223 | SDL_UnlockSurface(backBuffer); 224 | 225 | SDL_UpdateRect(backBuffer, 0, 0, 0, 0); 226 | 227 | int event; 228 | while ((event = waitEvent()) && (event == SDL_MOUSEMOTION && !getMouseDown(0) && !getMouseDown(1))); 229 | 230 | if (getKeyDown(SDLK_ESCAPE)) { 231 | doTerminate = true; 232 | barrier->releaseAll(); 233 | } 234 | 235 | float mx = float(getMouseXSpeed()); 236 | float my = float(getMouseYSpeed()); 237 | if (getMouseDown(0) && (mx != 0 || my != 0)) { 238 | pitch = std::fmod(pitch - my, 360.0f); 239 | yaw = std::fmod(yaw + (std::fabs(pitch) > 90.0f ? mx : -mx), 360.0f); 240 | 241 | if (pitch > 180.0f) pitch -= 360.0f; 242 | else if (pitch < -180.0f) pitch += 360.0f; 243 | 244 | MatrixStack::set(MODEL_STACK, Mat4::rotXYZ(Vec3(pitch, 0.0f, 0.0f))* 245 | Mat4::rotXYZ(Vec3(0.0f, yaw, 0.0f))); 246 | renderHalfSize = true; 247 | } else if (getMouseDown(1) && my != 0) { 248 | radius *= std::min(std::max(1.0f - my*0.01f, 0.5f), 1.5f); 249 | radius = std::min(radius, 25.0f); 250 | MatrixStack::set(VIEW_STACK, Mat4::translate(Vec3(0.0f, 0.0f, -radius))); 251 | renderHalfSize = true; 252 | } else { 253 | renderHalfSize = false; 254 | } 255 | 256 | if (SDL_MUSTLOCK(backBuffer)) 257 | SDL_LockSurface(backBuffer); 258 | } 259 | } 260 | 261 | return 0; 262 | } 263 | 264 | /* Maximum allowed memory allocation sizes for lookup table and cache blocks. 265 | * Larger => faster conversion usually, but adapt this to your own RAM size. 266 | * The conversion will still succeed with memory sizes much, much smaller than 267 | * the size of the voxel data, only slower. 268 | */ 269 | static const size_t dataMemory = int64_t(1024)*1024*1024; 270 | 271 | void printHelp() { 272 | std::cout << "Usage: sparse-voxel-octrees [options] filename ..." << std::endl; 273 | std::cout << "Options:" << std::endl; 274 | std::cout << "-builder set program to SVO building mode." << std::endl; 275 | std::cout << " --resolution set voxel resolution. r is an integer which equals to a power of 2." << std::endl; 276 | std::cout << " --mode set where to generate voxel data, m equals 0 or 1, where 0 indicates GENERATE_IN_MEMORY while 1 indicates GENERATE_ON_DISK." << std::endl; 277 | std::cout << "-viewer set program to SVO rendering mode." << std::endl << std::endl; 278 | std::cout << "Examples:" << std::endl; 279 | std::cout << " sparse-voxel-octrees -builder --resolution 256 --mode 0 ../models/xyzrgb_dragon.ply ../models/xyzrgb_dragon.oct" << std::endl; 280 | std::cout << " sparse-voxel-octrees -builder ../models/xyzrgb_dragon.ply ../models/xyzrgb_dragon.oct" << std::endl; 281 | std::cout << " sparse-voxel-octrees -viewer ../models/XYZRGB-Dragon.oct" << std::endl << std::endl << std::endl; 282 | } 283 | 284 | int main(int argc, char *argv[]) { 285 | 286 | unsigned int resolution = 256; //default resolution 287 | unsigned int mode = 0; //default to generate in memory 288 | std::string inputFile = ""; 289 | std::string outputFile = ""; 290 | 291 | /* parse arguments */ 292 | if ((argc == 8) && (std::string(argv[1]) == "-builder")) { 293 | resolution = atoi(argv[3]); 294 | mode = atoi(argv[5]); 295 | inputFile = argv[6]; 296 | outputFile = argv[7]; 297 | } 298 | else if ((argc == 4) && (std::string(argv[1]) == "-builder")) { 299 | inputFile = argv[2]; 300 | outputFile = argv[3]; 301 | } 302 | else if ((argc == 3) && (std::string(argv[1]) == "-viewer")) 303 | inputFile = argv[2]; 304 | else { 305 | std::cout << "Invalid arguments! Please refer to the help info!" << std::endl; 306 | printHelp(); 307 | return 0; 308 | } 309 | 310 | Timer timer; 311 | 312 | if (std::string(argv[1]) == "-builder") { 313 | ThreadUtils::startThreads(ThreadUtils::idealThreadCount()); 314 | 315 | if (mode) { //generate on disk 316 | std::unique_ptr loader(new PlyLoader(inputFile.c_str())); 317 | loader->convertToVolume("models/temp.voxel", resolution, dataMemory); 318 | std::unique_ptr data(new VoxelData("models/temp.voxel", dataMemory)); 319 | std::unique_ptr tree(new VoxelOctree(data.get())); 320 | tree->save(outputFile.c_str()); 321 | } 322 | else { //generate in memory 323 | std::unique_ptr loader(new PlyLoader(inputFile.c_str())); 324 | std::unique_ptr data(new VoxelData(loader.get(), resolution, dataMemory)); 325 | std::unique_ptr tree(new VoxelOctree(data.get())); 326 | tree->save(outputFile.c_str()); 327 | } 328 | timer.bench("Octree initialization took"); 329 | return 0; 330 | } 331 | 332 | if (std::string(argv[1]) == "-viewer") { 333 | std::unique_ptr tree(new VoxelOctree(inputFile.c_str())); 334 | 335 | timer.bench("Octree initialization took"); 336 | 337 | SDL_Init(SDL_INIT_VIDEO); 338 | 339 | SDL_WM_SetCaption("Sparse Voxel Octrees", "Sparse Voxel Octrees"); 340 | backBuffer = SDL_SetVideoMode(GWidth, GHeight, 32, SDL_SWSURFACE); 341 | 342 | SDL_Thread *threads[NumThreads - 1]; 343 | BatchData threadData[NumThreads]; 344 | 345 | barrier = new ThreadBarrier(NumThreads); 346 | doTerminate = false; 347 | 348 | if (SDL_MUSTLOCK(backBuffer)) 349 | SDL_LockSurface(backBuffer); 350 | 351 | int stride = (GHeight - 1) / NumThreads + 1; 352 | for (int i = 0; i < NumThreads; i++) { 353 | threadData[i].id = i; 354 | threadData[i].tree = tree.get(); 355 | threadData[i].x0 = 0; 356 | threadData[i].x1 = GWidth; 357 | threadData[i].y0 = i*stride; 358 | threadData[i].y1 = std::min((i + 1)*stride, GHeight); 359 | threadData[i].tilesX = (threadData[i].x1 - threadData[i].x0 - 1) / TileSize + 2; 360 | threadData[i].tilesY = (threadData[i].y1 - threadData[i].y0 - 1) / TileSize + 2; 361 | threadData[i].depthBuffer = new float[threadData[i].tilesX*threadData[i].tilesY]; 362 | } 363 | 364 | for (int i = 1; i < NumThreads; i++) 365 | threads[i - 1] = SDL_CreateThread(&renderLoop, (void *)&threadData[i]); 366 | 367 | renderLoop((void *)&threadData[0]); 368 | 369 | for (int i = 1; i < NumThreads; i++) 370 | SDL_WaitThread(threads[i - 1], 0); 371 | 372 | if (SDL_MUSTLOCK(backBuffer)) 373 | SDL_UnlockSurface(backBuffer); 374 | 375 | SDL_Quit(); 376 | } 377 | 378 | return 0; 379 | } 380 | -------------------------------------------------------------------------------- /src/PlyLoader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include "PlyLoader.hpp" 25 | #include "Debug.hpp" 26 | #include "Timer.hpp" 27 | #include "Util.hpp" 28 | 29 | #include "thread/ThreadUtils.hpp" 30 | #include "thread/ThreadPool.hpp" 31 | 32 | #include "third-party/tribox3.h" 33 | #include "third-party/ply.h" 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | Triangle::Triangle(const Vertex &_v1, const Vertex &_v2, const Vertex &_v3) : 41 | v1(_v1), v2(_v2), v3(_v3) { 42 | 43 | lower = Vec3( 44 | std::min(v1.pos.x, std::min(v2.pos.x, v3.pos.x)), 45 | std::min(v1.pos.y, std::min(v2.pos.y, v3.pos.y)), 46 | std::min(v1.pos.z, std::min(v2.pos.z, v3.pos.z)) 47 | ); 48 | upper = Vec3( 49 | std::max(v1.pos.x, std::max(v2.pos.x, v3.pos.x)), 50 | std::max(v1.pos.y, std::max(v2.pos.y, v3.pos.y)), 51 | std::max(v1.pos.z, std::max(v2.pos.z, v3.pos.z)) 52 | ); 53 | } 54 | 55 | bool Triangle::barycentric(const Vec3 &p, float &lambda1, float &lambda2) const { 56 | Vec3 f1 = v1.pos - p; 57 | Vec3 f2 = v2.pos - p; 58 | Vec3 f3 = v3.pos - p; 59 | float area = (v1.pos - v2.pos).cross(v1.pos - v3.pos).length(); 60 | lambda1 = f2.cross(f3).length()/area; 61 | lambda2 = f3.cross(f1).length()/area; 62 | 63 | return lambda1 >= 0.0f && lambda2 >= 0.0f && lambda1 + lambda2 <= 1.0f; 64 | } 65 | 66 | PlyLoader::PlyLoader(const char *path) : _isBigEndian(false), _lower(1e30f), _upper(-1e30f) { 67 | PlyFile *file; 68 | 69 | openPly(path, file); 70 | readVertices(file); 71 | rescaleVertices(); 72 | readTriangles(file); 73 | ply_close(file); 74 | 75 | std::cout << "Triangle count: " << _tris.size() << ", taking up " 76 | << prettyPrintMemory(_tris.size()*sizeof(Triangle)) << " of memory" << std::endl; 77 | 78 | std::vector().swap(_verts); /* Get rid of vertex data */ 79 | } 80 | 81 | void PlyLoader::openPly(const char *path, PlyFile *&file) { 82 | int elemCount, fileType; 83 | char **elemNames; 84 | float version; 85 | file = ply_open_for_reading(path, &elemCount, &elemNames, &fileType, &version); 86 | 87 | ASSERT(file != 0, "Failed to open PLY at %s\n", path); 88 | 89 | _isBigEndian = (file->file_type == PLY_BINARY_BE); 90 | 91 | bool hasVerts = false, hasTris = false; 92 | for (int i = 0; i < elemCount; i++) { 93 | if (!strcmp(elemNames[i], "vertex")) 94 | hasVerts = true; 95 | else if (!strcmp(elemNames[i], "face")) 96 | hasTris = true; 97 | else 98 | DBG("PLY loader", WARN, "Ignoring unknown element %s\n", elemNames[i]); 99 | } 100 | 101 | ASSERT(hasVerts && hasTris, "PLY file has to have triangles and vertices\n"); 102 | } 103 | 104 | template 105 | T toHostOrder(T src, bool isBigEndian) 106 | { 107 | if (!isBigEndian) 108 | return src; 109 | 110 | union { 111 | char bytes[sizeof(T)]; 112 | T type; 113 | } srcBytes, dstBytes; 114 | 115 | srcBytes.type = src; 116 | for (size_t i = 0; i < sizeof(T); ++i) 117 | dstBytes.bytes[i] = srcBytes.bytes[sizeof(T) - i - 1]; 118 | 119 | return dstBytes.type; 120 | } 121 | 122 | void PlyLoader::readVertices(PlyFile *file) { 123 | int vertCount, vertPropCount; 124 | PlyProperty **vertProps = ply_get_element_description(file, "vertex", &vertCount, &vertPropCount); 125 | 126 | _verts.reserve(vertCount); 127 | 128 | const char *vpNames[] = {"x", "y", "z", "nx", "ny", "nz", "red", "green", "blue"}; 129 | bool vpAvail[9] = {false}; 130 | for (int i = 0; i < vertPropCount; i++) { 131 | for (int t = 0; t < 9; t++) { 132 | if (!strcmp(vertProps[i]->name, vpNames[t])) { 133 | vertProps[i]->internal_type = PLY_FLOAT; 134 | vertProps[i]->offset = t*sizeof(float); 135 | ply_get_property(file, "vertex", vertProps[i]); 136 | vpAvail[t] = true; 137 | break; 138 | } 139 | } 140 | } 141 | 142 | _hasNormals = vpAvail[3] && vpAvail[4] && vpAvail[5]; 143 | 144 | float vertData[9], elemData[9]; 145 | float vertDefault[] = { 146 | 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 255.0f, 255.0f, 255.0f 147 | }; 148 | for (int i = 0; i < vertCount; i++) { 149 | ply_get_element(file, (void *)elemData); 150 | 151 | for (int t = 0; t < 9; t++) 152 | vertData[t] = (vpAvail[t] ? toHostOrder(elemData[t], _isBigEndian) : vertDefault[t]); 153 | 154 | _verts.push_back(Vertex( 155 | Vec3(vertData[0], vertData[1], vertData[2]), 156 | Vec3(vertData[3], vertData[4], vertData[5]), 157 | Vec3(vertData[6], vertData[7], vertData[8]) 158 | )); 159 | 160 | for (int t = 0; t < 3; t++) { 161 | _lower.a[t] = std::min(_lower.a[t], vertData[t]); 162 | _upper.a[t] = std::max(_upper.a[t], vertData[t]); 163 | } 164 | } 165 | } 166 | 167 | void PlyLoader::rescaleVertices() { 168 | Vec3 diff = _upper - _lower; 169 | int largestDim = 2; 170 | if (diff.x > diff.y && diff.x > diff.z) 171 | largestDim = 0; 172 | else if (diff.y > diff.z) 173 | largestDim = 1; 174 | float factor = 1.0f/diff.a[largestDim]; 175 | 176 | for (unsigned i = 0; i < _verts.size(); i++) 177 | _verts[i].pos = (_verts[i].pos - _lower)*factor; 178 | 179 | _upper *= factor; 180 | _lower *= factor; 181 | } 182 | 183 | void PlyLoader::readTriangles(PlyFile *file) { 184 | int triCount, triPropCount; 185 | PlyProperty **triProps = ply_get_element_description(file, "face", &triCount, &triPropCount); 186 | 187 | _tris.reserve(triCount); 188 | 189 | for (int i = 0; i < triPropCount; i++) { 190 | if (!strcmp(triProps[i]->name, "vertex_indices")) { 191 | triProps[i]->count_internal = PLY_INT; 192 | triProps[i]->internal_type = PLY_INT; 193 | triProps[i]->offset = 0; 194 | triProps[i]->count_offset = sizeof(int*); 195 | ply_get_property(file, "face", triProps[i]); 196 | break; 197 | } else if (i == triPropCount - 1) 198 | FAIL("No face information found\n"); 199 | } 200 | 201 | struct { int *elems; int count; } face; 202 | for (int i = 0; i < triCount; i++) { 203 | ply_get_element(file, (void *)&face); 204 | 205 | if (!face.count) 206 | continue; 207 | 208 | int v0 = toHostOrder(face.elems[0], _isBigEndian); 209 | int v1 = toHostOrder(face.elems[1], _isBigEndian); 210 | for (int t = 2; t < face.count; t++) { 211 | int v2 = toHostOrder(face.elems[t], _isBigEndian); 212 | _tris.push_back(Triangle(_verts[v0], _verts[v1], _verts[v2])); 213 | if (!_hasNormals) { 214 | Vec3 n = (_verts[v1].pos - _verts[v0].pos).cross(_verts[v2].pos - _verts[v0].pos).normalize(); 215 | _tris.back().v1.normal = n; 216 | _tris.back().v2.normal = n; 217 | _tris.back().v3.normal = n; 218 | } 219 | v1 = v2; 220 | } 221 | 222 | free(face.elems); 223 | } 224 | } 225 | 226 | void PlyLoader::pointToGrid(const Vec3 &p, int &x, int &y, int &z) 227 | { 228 | x = (int)(p.x*(_sideLength - 2) + 1.0f); 229 | y = (int)(p.y*(_sideLength - 2) + 1.0f); 230 | z = (int)(p.z*(_sideLength - 2) + 1.0f); 231 | } 232 | 233 | template 234 | void PlyLoader::iterateOverlappingBlocks(const Triangle &t, LoopBody body) 235 | { 236 | int lx, ly, lz; 237 | int ux, uy, uz; 238 | pointToGrid(t.lower, lx, ly, lz); 239 | pointToGrid(t.upper, ux, uy, uz); 240 | int lgridX = (lx + 0)/_subBlockW, lgridY = (ly + 0)/_subBlockH, lgridZ = (lz + 0)/_subBlockD; 241 | int ugridX = (ux + 1)/_subBlockW, ugridY = (uy + 1)/_subBlockH, ugridZ = (uz + 1)/_subBlockD; 242 | 243 | int maxSide = std::max(ugridX - lgridX, std::max(ugridY - lgridY, ugridZ - lgridZ)); 244 | 245 | if (maxSide > 0) { 246 | float hx = _subBlockW/float(_sideLength - 2); 247 | float hy = _subBlockH/float(_sideLength - 2); 248 | float hz = _subBlockD/float(_sideLength - 2); 249 | float triVs[][3] = { 250 | {t.v1.pos.x, t.v1.pos.y, t.v1.pos.z}, 251 | {t.v2.pos.x, t.v2.pos.y, t.v2.pos.z}, 252 | {t.v3.pos.x, t.v3.pos.y, t.v3.pos.z} 253 | }; 254 | float halfSize[] = {0.5f*hx, 0.5f*hy, 0.5f*hz}; 255 | float center[3]; 256 | 257 | center[2] = (lgridZ + 0.5f)*hz; 258 | for (int z = lgridZ; z <= ugridZ; ++z, center[2] += hz) { 259 | center[1] = (lgridY + 0.5f)*hy; 260 | for (int y = lgridY; y <= ugridY; ++y, center[1] += hy) { 261 | center[0] = (lgridX + 0.5f)*hx; 262 | for (int x = lgridX; x <= ugridX; ++x, center[0] += hx) 263 | if (triBoxOverlap(center, halfSize, triVs)) 264 | body(x + _gridW*(y + _gridH*z)); 265 | } 266 | } 267 | } else { 268 | body(lgridX + _gridW*(lgridY + _gridH*lgridZ)); 269 | } 270 | } 271 | 272 | void PlyLoader::buildBlockLists() 273 | { 274 | _blockOffsets.resize(_gridW*_gridH*_gridD + 1, 0); 275 | 276 | for (size_t i = 0; i < _tris.size(); ++i) 277 | iterateOverlappingBlocks(_tris[i], [&](size_t idx) { _blockOffsets[1 + idx]++; }); 278 | 279 | for (size_t i = 1; i < _blockOffsets.size(); ++i) 280 | _blockOffsets[i] += _blockOffsets[i - 1]; 281 | 282 | _blockLists.reset(new uint32[_blockOffsets.back()]); 283 | 284 | for (uint32 i = 0; i < _tris.size(); ++i) 285 | iterateOverlappingBlocks(_tris[i], [&](size_t idx) { _blockLists[_blockOffsets[idx]++] = i; }); 286 | 287 | for (int i = int(_blockOffsets.size() - 1); i >= 1; --i) 288 | _blockOffsets[i] = _blockOffsets[i - 1]; 289 | _blockOffsets[0] = 0; 290 | 291 | std::cout << "PlyLoader block lists take up an additional " 292 | << prettyPrintMemory((_blockOffsets.size() + _blockOffsets.back())*sizeof(uint32)) 293 | << " of memory" << std::endl; 294 | } 295 | 296 | void PlyLoader::writeTriangleCell(uint32 *data, int x, int y, int z, 297 | float cx, float cy, float cz, const Triangle &t) { 298 | size_t idx = (x - _bufferX) + size_t(_bufferW)*(y - _bufferY + size_t(_bufferH)*(z - _bufferZ)); 299 | 300 | float lambda1, lambda2, lambda3; 301 | if (!t.barycentric(Vec3(cx, cy, cz), lambda1, lambda2)) { 302 | lambda1 = std::min(std::max(lambda1, 0.0f), 1.0f); 303 | lambda2 = std::min(std::max(lambda2, 0.0f), 1.0f); 304 | float tau = lambda1 + lambda2; 305 | if (tau > 1.0f) { 306 | lambda1 /= tau; 307 | lambda2 /= tau; 308 | } 309 | } 310 | lambda3 = 1.0f - lambda1 - lambda2; 311 | 312 | Vec3 normal = (t.v1.normal*lambda1 + t.v2.normal*lambda2 + t.v3.normal*lambda3).normalize(); 313 | Vec3 color = t.v1.color*lambda1 + t.v2.color*lambda2 + t.v3.color*lambda3; 314 | /* Only store luminance - we only care about AO anyway */ 315 | float shade = color.dot(Vec3(0.2126f, 0.7152f, 0.0722f))*(1.0f/256.0f); 316 | 317 | if (data[idx] == 0) { 318 | _counts[idx] = 1; 319 | data[idx] = compressMaterial(normal, shade); 320 | } else { 321 | float currentRatio = _counts[idx]/(_counts[idx] + 1.0f); 322 | float newRatio = 1.0f - currentRatio; 323 | 324 | Vec3 currentNormal; 325 | float currentShade; 326 | decompressMaterial(data[idx], currentNormal, currentShade); 327 | 328 | Vec3 newNormal = currentNormal*currentRatio + normal*newRatio; 329 | float newShade = currentShade*currentRatio + shade*newRatio; 330 | if (newNormal.dot(newNormal) < 1e-3f) 331 | newNormal = currentNormal; 332 | 333 | data[idx] = compressMaterial(newNormal, newShade); 334 | _counts[idx] = std::min(int(_counts[idx]) + 1, 255); 335 | } 336 | } 337 | 338 | void PlyLoader::triangleToVolume(uint32 *data, const Triangle &t, int offX, int offY, int offZ) { 339 | int lx, ly, lz; 340 | int ux, uy, uz; 341 | pointToGrid(t.lower, lx, ly, lz); 342 | pointToGrid(t.upper, ux, uy, uz); 343 | 344 | lx = std::max(lx, _bufferX + offX); 345 | ly = std::max(ly, _bufferY + offY); 346 | lz = std::max(lz, _bufferZ + offZ); 347 | ux = std::min(ux, _bufferX + std::min(offX + _subBlockW, _bufferW) - 1); 348 | uy = std::min(uy, _bufferY + std::min(offY + _subBlockH, _bufferH) - 1); 349 | uz = std::min(uz, _bufferZ + std::min(offZ + _subBlockD, _bufferD) - 1); 350 | 351 | if (lx > ux || ly > uy || lz > uz) 352 | return; 353 | 354 | float hx = 1.0f/(_sideLength - 2); 355 | float triVs[][3] = { 356 | {t.v1.pos.x, t.v1.pos.y, t.v1.pos.z}, 357 | {t.v2.pos.x, t.v2.pos.y, t.v2.pos.z}, 358 | {t.v3.pos.x, t.v3.pos.y, t.v3.pos.z} 359 | }; 360 | float halfSize[] = {0.5f*hx, 0.5f*hx, 0.5f*hx}; 361 | float center[3]; 362 | 363 | center[2] = (lz - 0.5f)*hx; 364 | for (int z = lz; z <= uz; z++, center[2] += hx) { 365 | center[1] = (ly - 0.5f)*hx; 366 | for (int y = ly; y <= uy; y++, center[1] += hx) { 367 | center[0] = (lx - 0.5f)*hx; 368 | for (int x = lx; x <= ux; x++, center[0] += hx) { 369 | if (triBoxOverlap(center, halfSize, triVs)) 370 | writeTriangleCell(data, x, y, z, center[0], center[1], center[2], t); 371 | } 372 | } 373 | } 374 | } 375 | 376 | size_t PlyLoader::blockMemRequirement(int w, int h, int d) { 377 | size_t elementCost = sizeof(uint8); 378 | return elementCost*size_t(w)*size_t(h)*size_t(d); 379 | } 380 | 381 | void findBestBlockPartition(int &w, int &h, int &d, int numThreads) 382 | { 383 | auto maximum = [&]() -> int& { 384 | if (w > h && w > d) return w; else if (h > d) return h; else return d; 385 | }; 386 | auto median = [&]() -> int& { 387 | int max = std::max(w, std::max(h, d)); 388 | int min = std::min(w, std::min(h, d)); 389 | if (w != min && w != max) return w; 390 | else if (h != min && h != max) return h; 391 | else return d; 392 | }; 393 | auto minimum = [&]() -> int& { 394 | if (w < h && w < d) return w; else if (h < d) return h; else return d; 395 | }; 396 | 397 | int usedThreads = 1; 398 | while (usedThreads < numThreads) { 399 | if ((maximum() % 2) == 0) maximum() /= 2; 400 | else if (( median() % 2) == 0) median() /= 2; 401 | else if ((minimum() % 2) == 0) minimum() /= 2; 402 | else break; 403 | usedThreads *= 2; 404 | } 405 | } 406 | 407 | void PlyLoader::setupBlockProcessing(int sideLength, int blockW, int blockH, int blockD, 408 | int volumeW, int volumeH, int volumeD) { 409 | _conversionTimer.start(); 410 | 411 | size_t elementCount = size_t(blockW)*size_t(blockH)*size_t(blockD); 412 | _counts.reset(new uint8[elementCount]); 413 | 414 | _sideLength = sideLength - 2; 415 | _blockW = _subBlockW = blockW; 416 | _blockH = _subBlockH = blockH; 417 | _blockD = _subBlockD = blockD; 418 | findBestBlockPartition(_subBlockW, _subBlockH, _subBlockD, ThreadUtils::pool->threadCount()); 419 | _partitionW = _blockW/_subBlockW; 420 | _partitionH = _blockH/_subBlockH; 421 | _partitionD = _blockD/_subBlockD; 422 | _numPartitions = _partitionW*_partitionH*_partitionD; 423 | std::cout << "Partitioning cache block into " << _partitionW << "x" << _partitionH 424 | << "x" << _partitionD << " over " << _numPartitions << " threads (per thread block is " 425 | << _subBlockW << "x" << _subBlockH << "x" << _subBlockD << ")" << std::endl; 426 | _volumeW = volumeW; 427 | _volumeH = volumeH; 428 | _volumeD = volumeD; 429 | _gridW = _partitionW*(_volumeW + _blockW - 1)/_blockW; 430 | _gridH = _partitionH*(_volumeH + _blockH - 1)/_blockH; 431 | _gridD = _partitionD*(_volumeD + _blockD - 1)/_blockD; 432 | 433 | _processedBlocks = 0; 434 | _numNonZeroBlocks = 0; 435 | 436 | buildBlockLists(); 437 | } 438 | 439 | void PlyLoader::processBlock(uint32 *data, int x, int y, int z, int w, int h, int d) { 440 | _bufferX = x; 441 | _bufferY = y; 442 | _bufferZ = z; 443 | _bufferW = w; 444 | _bufferH = h; 445 | _bufferD = d; 446 | 447 | ThreadUtils::pool->enqueue([&](uint32 i, uint32, uint32){ 448 | int px = i % _partitionW; 449 | int py = (i/_partitionW) % _partitionH; 450 | int pz = i/(_partitionW*_partitionH); 451 | 452 | int blockIdx = (x/_subBlockW + px) + _gridW*((y/_subBlockH + py) + _gridH*(z/_subBlockD + pz)); 453 | int start = _blockOffsets[blockIdx]; 454 | int end = _blockOffsets[blockIdx + 1]; 455 | 456 | for (int i = start; i < end; ++i) 457 | triangleToVolume(data, _tris[_blockLists[i]], px*_subBlockW, py*_subBlockH, pz*_subBlockD); 458 | }, _numPartitions)->wait(); 459 | 460 | _processedBlocks++; 461 | 462 | _conversionTimer.stop(); 463 | double elapsed = _conversionTimer.elapsed(); 464 | 465 | std::cout << "Processed block " << _processedBlocks << "/" << _numNonZeroBlocks 466 | << " (" << (_processedBlocks*100)/_numNonZeroBlocks << "%) after " 467 | << int(elapsed) << " seconds. "; 468 | if (_processedBlocks < _numNonZeroBlocks) 469 | std::cout << "Approximate time to finish: " << int((_numNonZeroBlocks - _processedBlocks)*elapsed/_processedBlocks) 470 | << " seconds."; 471 | else 472 | std::cout << "All blocks processed! Post processing..."; 473 | std::cout << std::endl; 474 | } 475 | 476 | bool PlyLoader::isBlockEmpty(int x, int y, int z) 477 | { 478 | for (int pz = 0; pz < _partitionD; ++pz) { 479 | for (int py = 0; py < _partitionH; ++py) { 480 | for (int px = 0; px < _partitionW; ++px) { 481 | int blockIdx = (x/_subBlockW + px) + _gridW*((y/_subBlockH + py) + _gridH*(z/_subBlockD + pz)); 482 | int start = _blockOffsets[blockIdx]; 483 | int end = _blockOffsets[blockIdx + 1]; 484 | 485 | if (start != end) { 486 | _numNonZeroBlocks++; 487 | return false; 488 | } 489 | } 490 | } 491 | } 492 | return true; 493 | } 494 | 495 | void PlyLoader::teardownBlockProcessing() { 496 | _counts.reset(); 497 | } 498 | 499 | void PlyLoader::suggestedDimensions(int sideLength, int &w, int &h, int &d) { 500 | Vec3 sizes = (_upper - _lower)*float(sideLength - 2); 501 | w = int(sizes.x) + 2; 502 | h = int(sizes.y) + 2; 503 | d = int(sizes.z) + 2; 504 | } 505 | 506 | void PlyLoader::convertToVolume(const char *path, int maxSize, size_t memoryBudget) { 507 | FILE *fp = fopen(path, "wb"); 508 | if (!fp) 509 | return; 510 | 511 | int w, h, d; 512 | suggestedDimensions(maxSize, w, h, d); 513 | 514 | size_t sliceCost = blockMemRequirement(w, h, 1); 515 | int sliceZ = std::min((int)(memoryBudget/sliceCost), d); 516 | ASSERT(sliceZ != 0, "Not enough memory available for single slice"); 517 | 518 | uint32 *data = new uint32[size_t(w)*size_t(h)*size_t(sliceZ)]; 519 | setupBlockProcessing(maxSize, w, h, sliceZ, w, h, d); 520 | 521 | fwrite(&w, 4, 1, fp); 522 | fwrite(&h, 4, 1, fp); 523 | fwrite(&d, 4, 1, fp); 524 | 525 | for (int z = 0; z < d; z += sliceZ) { 526 | processBlock(data, 0, 0, z, w, h, sliceZ); 527 | 528 | fwrite(data, sizeof(uint32), size_t(w)*size_t(h)*size_t(std::min(sliceZ, d - z)), fp); 529 | } 530 | fclose(fp); 531 | 532 | teardownBlockProcessing(); 533 | delete[] data; 534 | } 535 | -------------------------------------------------------------------------------- /src/PlyLoader.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef PLYLOADER_HPP_ 25 | #define PLYLOADER_HPP_ 26 | 27 | #include "math/Vec3.hpp" 28 | 29 | #include "IntTypes.hpp" 30 | #include "Timer.hpp" 31 | 32 | #include 33 | #include 34 | 35 | struct PlyFile; 36 | 37 | struct Vertex { 38 | Vec3 pos, normal, color; 39 | 40 | Vertex() {}; 41 | Vertex(const Vec3 &p, const Vec3 &n, const Vec3 &c) : 42 | pos(p), normal(n), color(c) {} 43 | }; 44 | 45 | struct Triangle { 46 | Vertex v1, v2, v3; 47 | Vec3 lower, upper; 48 | 49 | bool barycentric(const Vec3 &p, float &lambda1, float &lambda2) const; 50 | 51 | Triangle() {}; 52 | Triangle(const Vertex &_v1, const Vertex &_v2, const Vertex &_v3); 53 | }; 54 | 55 | class PlyLoader { 56 | bool _hasNormals; 57 | bool _isBigEndian; 58 | 59 | std::vector _verts; 60 | std::vector _tris; 61 | std::vector _blockOffsets; 62 | std::unique_ptr _blockLists; 63 | 64 | Timer _conversionTimer; 65 | int _processedBlocks; 66 | int _numNonZeroBlocks; 67 | Vec3 _lower, _upper; 68 | 69 | std::unique_ptr _counts; 70 | int _sideLength; 71 | int _volumeW, _volumeH, _volumeD; 72 | int _blockW, _blockH, _blockD; 73 | int _subBlockW, _subBlockH, _subBlockD; 74 | int _partitionW, _partitionH, _partitionD; 75 | int _numPartitions; 76 | int _gridW, _gridH, _gridD; 77 | int _bufferX, _bufferY, _bufferZ; 78 | int _bufferW, _bufferH, _bufferD; 79 | 80 | void writeTriangleCell(uint32 *data, int x, int y, int z, 81 | float cx, float cy, float cz, const Triangle &t); 82 | void triangleToVolume(uint32 *data, const Triangle &t, int offX, int offY, int offZ); 83 | 84 | void openPly(const char *path, PlyFile *&file); 85 | void readVertices(PlyFile *file); 86 | void rescaleVertices(); 87 | void readTriangles(PlyFile *file); 88 | 89 | void pointToGrid(const Vec3 &p, int &x, int &y, int &z); 90 | 91 | template 92 | void iterateOverlappingBlocks(const Triangle &t, LoopBody body); 93 | 94 | void buildBlockLists(); 95 | 96 | public: 97 | PlyLoader(const char *path); 98 | 99 | void suggestedDimensions(int sideLength, int &w, int &h, int &d); 100 | 101 | size_t blockMemRequirement(int w, int h, int d); 102 | void setupBlockProcessing(int sideLength, int blockW, int blockH, int blockD, 103 | int volumeW, int volumeH, int volumeD); 104 | void processBlock(uint32 *data, int x, int y, int z, int w, int h, int d); 105 | bool isBlockEmpty(int x, int y, int z); 106 | void teardownBlockProcessing(); 107 | 108 | void convertToVolume(const char *path, int maxSize, size_t memoryBudget); 109 | 110 | const std::vector &tris() const { 111 | return _tris; 112 | } 113 | }; 114 | 115 | #endif /* OBJLOADER_HPP_ */ 116 | -------------------------------------------------------------------------------- /src/SDLMain.h: -------------------------------------------------------------------------------- 1 | /* SDLMain.m - main entry point for our Cocoa-ized SDL app 2 | Initial Version: Darrell Walisser 3 | Non-NIB-Code & other changes: Max Horn 4 | 5 | Feel free to customize this file to suit your needs 6 | */ 7 | 8 | #ifndef _SDLMain_h_ 9 | #define _SDLMain_h_ 10 | 11 | #import 12 | 13 | @interface SDLMain : NSObject 14 | @end 15 | 16 | #endif /* _SDLMain_h_ */ 17 | -------------------------------------------------------------------------------- /src/SDLMain.m: -------------------------------------------------------------------------------- 1 | /* SDLMain.m - main entry point for our Cocoa-ized SDL app 2 | Initial Version: Darrell Walisser 3 | Non-NIB-Code & other changes: Max Horn 4 | 5 | Feel free to customize this file to suit your needs 6 | */ 7 | 8 | #include 9 | #include "SDLMain.h" 10 | #include /* for MAXPATHLEN */ 11 | #include 12 | 13 | /* For some reaon, Apple removed setAppleMenu from the headers in 10.4, 14 | but the method still is there and works. To avoid warnings, we declare 15 | it ourselves here. */ 16 | @interface NSApplication(SDL_Missing_Methods) 17 | - (void)setAppleMenu:(NSMenu *)menu; 18 | @end 19 | 20 | /* Use this flag to determine whether we use SDLMain.nib or not */ 21 | #define SDL_USE_NIB_FILE 0 22 | 23 | /* Use this flag to determine whether we use CPS (docking) or not */ 24 | #define SDL_USE_CPS 1 25 | #ifdef SDL_USE_CPS 26 | /* Portions of CPS.h */ 27 | typedef struct CPSProcessSerNum 28 | { 29 | UInt32 lo; 30 | UInt32 hi; 31 | } CPSProcessSerNum; 32 | 33 | extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); 34 | extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); 35 | extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); 36 | 37 | #endif /* SDL_USE_CPS */ 38 | 39 | static int gArgc; 40 | static char **gArgv; 41 | static BOOL gFinderLaunch; 42 | static BOOL gCalledAppMainline = FALSE; 43 | 44 | static NSString *getApplicationName(void) 45 | { 46 | const NSDictionary *dict; 47 | NSString *appName = 0; 48 | 49 | /* Determine the application name */ 50 | dict = (const NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); 51 | if (dict) 52 | appName = [dict objectForKey: @"CFBundleName"]; 53 | 54 | if (![appName length]) 55 | appName = [[NSProcessInfo processInfo] processName]; 56 | 57 | return appName; 58 | } 59 | 60 | #if SDL_USE_NIB_FILE 61 | /* A helper category for NSString */ 62 | @interface NSString (ReplaceSubString) 63 | - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString; 64 | @end 65 | #endif 66 | 67 | @interface NSApplication (SDLApplication) 68 | @end 69 | 70 | @implementation NSApplication (SDLApplication) 71 | /* Invoked from the Quit menu item */ 72 | - (void)terminate:(id)sender 73 | { 74 | /* Post a SDL_QUIT event */ 75 | SDL_Event event; 76 | event.type = SDL_QUIT; 77 | SDL_PushEvent(&event); 78 | } 79 | @end 80 | 81 | /* The main class of the application, the application's delegate */ 82 | @implementation SDLMain 83 | 84 | /* Set the working directory to the .app's parent directory */ 85 | - (void) setupWorkingDirectory:(BOOL)shouldChdir 86 | { 87 | if (shouldChdir) 88 | { 89 | char parentdir[MAXPATHLEN]; 90 | CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle()); 91 | CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url); 92 | if (CFURLGetFileSystemRepresentation(url2, 1, (UInt8 *)parentdir, MAXPATHLEN)) { 93 | chdir(parentdir); /* chdir to the binary app's parent */ 94 | } 95 | CFRelease(url); 96 | CFRelease(url2); 97 | } 98 | } 99 | 100 | #if SDL_USE_NIB_FILE 101 | 102 | /* Fix menu to contain the real app name instead of "SDL App" */ 103 | - (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName 104 | { 105 | NSRange aRange; 106 | NSEnumerator *enumerator; 107 | NSMenuItem *menuItem; 108 | 109 | aRange = [[aMenu title] rangeOfString:@"SDL App"]; 110 | if (aRange.length != 0) 111 | [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]]; 112 | 113 | enumerator = [[aMenu itemArray] objectEnumerator]; 114 | while ((menuItem = [enumerator nextObject])) 115 | { 116 | aRange = [[menuItem title] rangeOfString:@"SDL App"]; 117 | if (aRange.length != 0) 118 | [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]]; 119 | if ([menuItem hasSubmenu]) 120 | [self fixMenu:[menuItem submenu] withAppName:appName]; 121 | } 122 | } 123 | 124 | #else 125 | 126 | static void setApplicationMenu(void) 127 | { 128 | /* warning: this code is very odd */ 129 | NSMenu *appleMenu; 130 | NSMenuItem *menuItem; 131 | NSString *title; 132 | NSString *appName; 133 | 134 | appName = getApplicationName(); 135 | appleMenu = [[NSMenu alloc] initWithTitle:@""]; 136 | 137 | /* Add menu items */ 138 | title = [@"About " stringByAppendingString:appName]; 139 | [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; 140 | 141 | [appleMenu addItem:[NSMenuItem separatorItem]]; 142 | 143 | title = [@"Hide " stringByAppendingString:appName]; 144 | [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; 145 | 146 | menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; 147 | [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; 148 | 149 | [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; 150 | 151 | [appleMenu addItem:[NSMenuItem separatorItem]]; 152 | 153 | title = [@"Quit " stringByAppendingString:appName]; 154 | [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; 155 | 156 | 157 | /* Put menu into the menubar */ 158 | menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; 159 | [menuItem setSubmenu:appleMenu]; 160 | [[NSApp mainMenu] addItem:menuItem]; 161 | 162 | /* Tell the application object that this is now the application menu */ 163 | [NSApp setAppleMenu:appleMenu]; 164 | 165 | /* Finally give up our references to the objects */ 166 | [appleMenu release]; 167 | [menuItem release]; 168 | } 169 | 170 | /* Create a window menu */ 171 | static void setupWindowMenu(void) 172 | { 173 | NSMenu *windowMenu; 174 | NSMenuItem *windowMenuItem; 175 | NSMenuItem *menuItem; 176 | 177 | windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; 178 | 179 | /* "Minimize" item */ 180 | menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; 181 | [windowMenu addItem:menuItem]; 182 | [menuItem release]; 183 | 184 | /* Put menu into the menubar */ 185 | windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; 186 | [windowMenuItem setSubmenu:windowMenu]; 187 | [[NSApp mainMenu] addItem:windowMenuItem]; 188 | 189 | /* Tell the application object that this is now the window menu */ 190 | [NSApp setWindowsMenu:windowMenu]; 191 | 192 | /* Finally give up our references to the objects */ 193 | [windowMenu release]; 194 | [windowMenuItem release]; 195 | } 196 | 197 | /* Replacement for NSApplicationMain */ 198 | static void CustomApplicationMain (int argc, char **argv) 199 | { 200 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 201 | SDLMain *sdlMain; 202 | 203 | /* Ensure the application object is initialised */ 204 | [NSApplication sharedApplication]; 205 | 206 | #ifdef SDL_USE_CPS 207 | { 208 | CPSProcessSerNum PSN; 209 | /* Tell the dock about us */ 210 | if (!CPSGetCurrentProcess(&PSN)) 211 | if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) 212 | if (!CPSSetFrontProcess(&PSN)) 213 | [NSApplication sharedApplication]; 214 | } 215 | #endif /* SDL_USE_CPS */ 216 | 217 | /* Set up the menubar */ 218 | [NSApp setMainMenu:[[NSMenu alloc] init]]; 219 | setApplicationMenu(); 220 | setupWindowMenu(); 221 | 222 | /* Create SDLMain and make it the app delegate */ 223 | sdlMain = [[SDLMain alloc] init]; 224 | [NSApp setDelegate:sdlMain]; 225 | 226 | /* Start the main event loop */ 227 | [NSApp run]; 228 | 229 | [sdlMain release]; 230 | [pool release]; 231 | } 232 | 233 | #endif 234 | 235 | 236 | /* 237 | * Catch document open requests...this lets us notice files when the app 238 | * was launched by double-clicking a document, or when a document was 239 | * dragged/dropped on the app's icon. You need to have a 240 | * CFBundleDocumentsType section in your Info.plist to get this message, 241 | * apparently. 242 | * 243 | * Files are added to gArgv, so to the app, they'll look like command line 244 | * arguments. Previously, apps launched from the finder had nothing but 245 | * an argv[0]. 246 | * 247 | * This message may be received multiple times to open several docs on launch. 248 | * 249 | * This message is ignored once the app's mainline has been called. 250 | */ 251 | - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename 252 | { 253 | const char *temparg; 254 | size_t arglen; 255 | char *arg; 256 | char **newargv; 257 | 258 | if (!gFinderLaunch) /* MacOS is passing command line args. */ 259 | return FALSE; 260 | 261 | if (gCalledAppMainline) /* app has started, ignore this document. */ 262 | return FALSE; 263 | 264 | temparg = [filename UTF8String]; 265 | arglen = SDL_strlen(temparg) + 1; 266 | arg = (char *) SDL_malloc(arglen); 267 | if (arg == NULL) 268 | return FALSE; 269 | 270 | newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2)); 271 | if (newargv == NULL) 272 | { 273 | SDL_free(arg); 274 | return FALSE; 275 | } 276 | gArgv = newargv; 277 | 278 | SDL_strlcpy(arg, temparg, arglen); 279 | gArgv[gArgc++] = arg; 280 | gArgv[gArgc] = NULL; 281 | return TRUE; 282 | } 283 | 284 | 285 | /* Called when the internal event loop has just started running */ 286 | - (void) applicationDidFinishLaunching: (NSNotification *) note 287 | { 288 | int status; 289 | 290 | /* Set the working directory to the .app's parent directory */ 291 | [self setupWorkingDirectory:gFinderLaunch]; 292 | 293 | #if SDL_USE_NIB_FILE 294 | /* Set the main menu to contain the real app name instead of "SDL App" */ 295 | [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()]; 296 | #endif 297 | 298 | /* Hand off to main application code */ 299 | gCalledAppMainline = TRUE; 300 | status = SDL_main (gArgc, gArgv); 301 | 302 | /* We're done, thank you for playing */ 303 | exit(status); 304 | } 305 | @end 306 | 307 | 308 | @implementation NSString (ReplaceSubString) 309 | 310 | - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString 311 | { 312 | unsigned int bufferSize; 313 | unsigned int selfLen = [self length]; 314 | unsigned int aStringLen = [aString length]; 315 | unichar *buffer; 316 | NSRange localRange; 317 | NSString *result; 318 | 319 | bufferSize = selfLen + aStringLen - aRange.length; 320 | buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar)); 321 | 322 | /* Get first part into buffer */ 323 | localRange.location = 0; 324 | localRange.length = aRange.location; 325 | [self getCharacters:buffer range:localRange]; 326 | 327 | /* Get middle part into buffer */ 328 | localRange.location = 0; 329 | localRange.length = aStringLen; 330 | [aString getCharacters:(buffer+aRange.location) range:localRange]; 331 | 332 | /* Get last part into buffer */ 333 | localRange.location = aRange.location + aRange.length; 334 | localRange.length = selfLen - localRange.location; 335 | [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange]; 336 | 337 | /* Build output string */ 338 | result = [NSString stringWithCharacters:buffer length:bufferSize]; 339 | 340 | NSDeallocateMemoryPages(buffer, bufferSize); 341 | 342 | return result; 343 | } 344 | 345 | @end 346 | 347 | 348 | 349 | #ifdef main 350 | # undef main 351 | #endif 352 | 353 | 354 | /* Main entry point to executable - should *not* be SDL_main! */ 355 | int main (int argc, char **argv) 356 | { 357 | /* Copy the arguments into a global variable */ 358 | /* This is passed if we are launched by double-clicking */ 359 | if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) { 360 | gArgv = (char **) SDL_malloc(sizeof (char *) * 2); 361 | gArgv[0] = argv[0]; 362 | gArgv[1] = NULL; 363 | gArgc = 1; 364 | gFinderLaunch = YES; 365 | } else { 366 | int i; 367 | gArgc = argc; 368 | gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1)); 369 | for (i = 0; i <= argc; i++) 370 | gArgv[i] = argv[i]; 371 | gFinderLaunch = NO; 372 | } 373 | 374 | #if SDL_USE_NIB_FILE 375 | NSApplicationMain (argc, argv); 376 | #else 377 | CustomApplicationMain (argc, argv); 378 | #endif 379 | return 0; 380 | } 381 | 382 | -------------------------------------------------------------------------------- /src/ThreadBarrier.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include "ThreadBarrier.hpp" 25 | 26 | #include 27 | 28 | ThreadBarrier::ThreadBarrier(int numThreads) : _numThreads(numThreads) { 29 | _barrierMutex = SDL_CreateMutex(); 30 | _turnstile1 = SDL_CreateSemaphore(0); 31 | _turnstile2 = SDL_CreateSemaphore(0); 32 | _waitCount = 0; 33 | } 34 | 35 | ThreadBarrier::~ThreadBarrier() { 36 | SDL_DestroyMutex(_barrierMutex); 37 | SDL_DestroySemaphore(_turnstile1); 38 | SDL_DestroySemaphore(_turnstile2); 39 | } 40 | 41 | void ThreadBarrier::waitPre() { 42 | SDL_mutexP(_barrierMutex); 43 | if (++_waitCount == _numThreads) 44 | for (int i = 0; i < _numThreads; i++) 45 | SDL_SemPost(_turnstile1); 46 | SDL_mutexV(_barrierMutex); 47 | 48 | SDL_SemWait(_turnstile1); 49 | } 50 | 51 | void ThreadBarrier::waitPost() { 52 | SDL_mutexP(_barrierMutex); 53 | if (--_waitCount == 0) 54 | for (int i = 0; i < _numThreads; i++) 55 | SDL_SemPost(_turnstile2); 56 | SDL_mutexV(_barrierMutex); 57 | 58 | SDL_SemWait(_turnstile2); 59 | } 60 | 61 | void ThreadBarrier::releaseAll() { 62 | for (int i = 0; i < _numThreads; i++) { 63 | SDL_SemPost(_turnstile1); 64 | SDL_SemPost(_turnstile2); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/ThreadBarrier.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef THREADBARRIER_HPP_ 25 | #define THREADBARRIER_HPP_ 26 | 27 | #include /* Cannot use forward declaration for thread primitives, unfortunately */ 28 | 29 | #include 30 | 31 | class ThreadBarrier { 32 | int _numThreads; 33 | std::atomic _waitCount; 34 | 35 | SDL_mutex *_barrierMutex; 36 | SDL_sem *_turnstile1, *_turnstile2; 37 | public: 38 | ThreadBarrier(int numThreads); 39 | ~ThreadBarrier(); 40 | 41 | void waitPre(); 42 | void waitPost(); 43 | void releaseAll(); 44 | }; 45 | 46 | #endif /* THREADBARRIER_HPP_ */ 47 | -------------------------------------------------------------------------------- /src/Timer.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef TIMER_HPP_ 25 | #define TIMER_HPP_ 26 | 27 | #include 28 | #include 29 | #include 30 | #if _WIN32 31 | #include 32 | #endif 33 | 34 | // std::chrono::high_resolution_clock has disappointing accuracy on windows 35 | // On windows, we use the WinAPI high performance counter instead 36 | class Timer 37 | { 38 | #if _WIN32 39 | LARGE_INTEGER _pfFrequency; 40 | LARGE_INTEGER _start, _stop; 41 | #else 42 | std::chrono::time_point _start, _stop; 43 | #endif 44 | public: 45 | Timer() 46 | { 47 | #if _WIN32 48 | QueryPerformanceFrequency(&_pfFrequency); 49 | #endif 50 | start(); 51 | } 52 | 53 | void start() 54 | { 55 | #if _WIN32 56 | QueryPerformanceCounter(&_start); 57 | #else 58 | _start = std::chrono::high_resolution_clock::now(); 59 | #endif 60 | } 61 | 62 | void stop() 63 | { 64 | #if _WIN32 65 | QueryPerformanceCounter(&_stop); 66 | #else 67 | _stop = std::chrono::high_resolution_clock::now(); 68 | #endif 69 | } 70 | 71 | void bench(const std::string &s) 72 | { 73 | stop(); 74 | std::cout << s << ": " << elapsed() << " s" << std::endl; 75 | } 76 | 77 | double elapsed() const 78 | { 79 | #if _WIN32 80 | return double(_stop.QuadPart - _start.QuadPart)/double(_pfFrequency.QuadPart); 81 | #else 82 | return std::chrono::duration(_stop - _start).count(); 83 | #endif 84 | } 85 | }; 86 | 87 | #endif /* TIMER_HPP_ */ 88 | -------------------------------------------------------------------------------- /src/Util.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include "Util.hpp" 25 | 26 | #include 27 | 28 | std::string prettyPrintMemory(uint64 size) 29 | { 30 | const char *unit; 31 | uint64 base; 32 | if (size < 1024) { 33 | base = 1; 34 | unit = " bytes"; 35 | } else if (size < 1024*1024) { 36 | base = 1024; 37 | unit = " KB"; 38 | } else if (size < uint64(1024)*1024*1024) { 39 | base = 1024*1024; 40 | unit = " MB"; 41 | } else { 42 | base = uint64(1024)*1024*1024; 43 | unit = " GB"; 44 | } 45 | 46 | std::ostringstream out; 47 | 48 | if (size/base < 10) 49 | out << ((size*100)/base)*0.01; 50 | else if (size/base < 100) 51 | out << ((size*10)/base)*0.1; 52 | else 53 | out << size/base; 54 | out << unit; 55 | 56 | return out.str(); 57 | } 58 | -------------------------------------------------------------------------------- /src/Util.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef UTIL_HPP_ 25 | #define UTIL_HPP_ 26 | 27 | #include "math/Vec3.hpp" 28 | 29 | #include "IntTypes.hpp" 30 | 31 | #include 32 | 33 | std::string prettyPrintMemory(uint64 size); 34 | 35 | static inline float uintBitsToFloat(uint32 i) { 36 | union { uint32 i; float f; } unionHack; 37 | unionHack.i = i; 38 | return unionHack.f; 39 | } 40 | 41 | static inline uint32 floatBitsToUint(float f) { 42 | union { uint32 i; float f; } unionHack; 43 | unionHack.f = f; 44 | return unionHack.i; 45 | } 46 | 47 | static inline float invSqrt(float x) { //Inverse square root as used in Quake III 48 | float x2 = x*0.5f; 49 | float y = x; 50 | 51 | uint32 i = floatBitsToUint(y); 52 | i = 0x5f3759df - (i >> 1); 53 | 54 | y = uintBitsToFloat(i); 55 | y = y*(1.5f - x2*y*y); 56 | 57 | return y; 58 | } 59 | 60 | static inline void fastNormalization(Vec3 &v) { 61 | v *= invSqrt(v.dot(v)); 62 | } 63 | 64 | static inline uint32 compressMaterial(const Vec3 &n, float shade) { 65 | const int32 uScale = (1 << 11) - 1; 66 | const int32 vScale = (1 << 11) - 1; 67 | 68 | uint32 face = 0; 69 | float dominantDir = fabsf(n.x); 70 | if (fabsf(n.y) > dominantDir) dominantDir = fabsf(n.y), face = 1; 71 | if (fabsf(n.z) > dominantDir) dominantDir = fabsf(n.z), face = 2; 72 | 73 | uint32 sign = n.a[face] < 0.0f; 74 | 75 | const int mod3[] = {0, 1, 2, 0, 1}; 76 | float n1 = n.a[mod3[face + 1]]/dominantDir; 77 | float n2 = n.a[mod3[face + 2]]/dominantDir; 78 | 79 | uint32 u = std::min((int32)((n1*0.5f + 0.5f)*uScale), 0x7FF); 80 | uint32 v = std::min((int32)((n2*0.5f + 0.5f)*vScale), 0x7FF); 81 | uint32 c = std::min((int32)(shade*127.0f), 0x7F); 82 | 83 | return (sign << 31) | (face << 29) | (u << 18) | v << 7 | c; 84 | } 85 | 86 | static inline void decompressMaterial(uint32 normal, Vec3 &dst, float &shade) { 87 | uint32 sign = (normal & 0x80000000) >> 31; 88 | uint32 face = (normal & 0x60000000) >> 29; 89 | uint32 u = (normal & 0x1FFC0000) >> 18; 90 | uint32 v = (normal & 0x0003FF80) >> 7; 91 | uint32 c = (normal & 0x0000007F); 92 | 93 | const int mod3[] = {0, 1, 2, 0, 1}; 94 | dst.a[ face ] = (sign ? -1.0f : 1.0f); 95 | dst.a[mod3[face + 1]] = u*4.8852e-4f*2.0f - 1.0f; 96 | dst.a[mod3[face + 2]] = v*4.8852e-4f*2.0f - 1.0f; 97 | 98 | fastNormalization(dst); 99 | shade = c*1.0f/127.0f; 100 | } 101 | 102 | static inline int roundToPow2(int x) { 103 | int y; 104 | for (y = 1; y < x; y *= 2); 105 | return y; 106 | } 107 | 108 | //See http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog 109 | static inline int findHighestBit(uint32 v) { 110 | static const uint32 b[] = { 111 | 0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000 112 | }; 113 | uint32 r = (v & b[0]) != 0; 114 | for (int i = 4; i > 0; i--) 115 | r |= ((v & b[i]) != 0) << i; 116 | return r; 117 | } 118 | 119 | #endif /* UTIL_H_ */ 120 | -------------------------------------------------------------------------------- /src/VoxelData.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include "VoxelData.hpp" 25 | #include "PlyLoader.hpp" 26 | #include "Debug.hpp" 27 | 28 | #include "thread/ThreadUtils.hpp" 29 | #include "thread/ThreadPool.hpp" 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | VoxelData::VoxelData(const char *path, size_t mem) : _loader(0) { 38 | _dataStream = fopen(path, "rb"); 39 | 40 | if (_dataStream != NULL) { 41 | fread(&_dataW, 4, 1, _dataStream); 42 | fread(&_dataH, 4, 1, _dataStream); 43 | fread(&_dataD, 4, 1, _dataStream); 44 | 45 | init(mem); 46 | buildTopLut(); 47 | } 48 | } 49 | 50 | VoxelData::VoxelData(PlyLoader *loader, int sideLength, size_t mem) : _dataStream(0), _loader(loader) { 51 | loader->suggestedDimensions(sideLength, _dataW, _dataH, _dataD); 52 | init(mem); 53 | int size = int(_maxCacheableSize); 54 | loader->setupBlockProcessing(sideLength, size, size, size, _dataW, _dataH, _dataD); 55 | buildTopLut(); 56 | } 57 | 58 | VoxelData::~VoxelData() { 59 | if (_dataStream) 60 | fclose(_dataStream); 61 | else if (_loader) 62 | _loader->teardownBlockProcessing(); 63 | } 64 | 65 | static uint64 countCellsInHiarchicalGrid(int numLevels) 66 | { 67 | uint64 result = 0; 68 | uint64 size = 1; 69 | for (int i = 0; i < numLevels; ++i) { 70 | result += size; 71 | size *= 8; 72 | } 73 | return result; 74 | } 75 | 76 | static void buildHierarchicalGridLinks(uint8 *base, int numLevels, std::vector &dst) 77 | { 78 | size_t offset = 0; 79 | size_t size = 1; 80 | for (int i = 0; i < numLevels; ++i) { 81 | dst.push_back(base + offset); 82 | offset += size; 83 | size *= 8; 84 | } 85 | } 86 | 87 | template 88 | void VoxelData::upsampleLutLevel(int l) { 89 | int size = 2 << l; 90 | 91 | auto innerBody = [&](int z) { 92 | for (int y = 0; y < size; y += 2) { 93 | for (int x = 0; x < size; x += 2) { 94 | int value = 95 | getLut(l + 1, x, y + 0, z + 0) | getLut(l + 1, x + 1, y + 0, z + 0) | 96 | getLut(l + 1, x, y + 1, z + 0) | getLut(l + 1, x + 1, y + 1, z + 0) | 97 | getLut(l + 1, x, y + 0, z + 1) | getLut(l + 1, x + 1, y + 0, z + 1) | 98 | getLut(l + 1, x, y + 1, z + 1) | getLut(l + 1, x + 1, y + 1, z + 1); 99 | 100 | getLut(l, x >> 1, y >> 1, z >> 1) = (value != 0); 101 | } 102 | } 103 | }; 104 | 105 | if (size < 128) { 106 | for (int z = 0; z < size; z += 2) 107 | innerBody(z); 108 | } else { 109 | int threadCount = ThreadUtils::pool->threadCount(); 110 | ThreadUtils::pool->enqueue([&](uint32 id, uint32, uint32) { 111 | int start = (((size/2)*id)/threadCount)*2; 112 | int end = (((size/2)*(id + 1))/threadCount)*2; 113 | 114 | for (int z = start; z < end; z += 2) 115 | innerBody(z); 116 | }, threadCount)->wait(); 117 | } 118 | } 119 | 120 | void VoxelData::buildTopLutBlock(int cx, int cy, int cz) { 121 | int size = int(_maxCacheableSize); 122 | int bufferW = std::min(size, _dataW - cx); 123 | int bufferH = std::min(size, _dataH - cy); 124 | int bufferD = std::min(size, _dataD - cz); 125 | if (bufferW <= 0 || bufferH <= 0 || bufferD <= 0) 126 | return; 127 | 128 | bool empty; 129 | if (_loader) { 130 | empty = _loader->isBlockEmpty(cx, cy, cz); 131 | } else { 132 | cacheData(cx, cy, cz, bufferW, bufferH, bufferD); 133 | 134 | empty = true; 135 | for (size_t i = 0; i < size_t(bufferW)*size_t(bufferH)*size_t(bufferD); ++i) { 136 | if (_bufferedData[i]) { 137 | empty = false; 138 | break; 139 | } 140 | } 141 | } 142 | 143 | if (!empty) 144 | getTopLut(_topLutLevels - 1, cx >> _lowLutLevels, cy >> _lowLutLevels, cz >> _lowLutLevels) = 1; 145 | } 146 | 147 | void VoxelData::buildTopLut() { 148 | if (_topLutLevels == 0) 149 | return; 150 | 151 | for (int z = 0; z < _virtualDataD; z += int(_maxCacheableSize)) 152 | for (int y = 0; y < _virtualDataH; y += int(_maxCacheableSize)) 153 | for (int x = 0; x < _virtualDataW; x += int(_maxCacheableSize)) 154 | buildTopLutBlock(x, y, z); 155 | 156 | for (int i = _topLutLevels - 2; i >= 0; i--) 157 | upsampleLutLevel(i); 158 | } 159 | 160 | void VoxelData::buildLowLut() { 161 | int threadCount = ThreadUtils::pool->threadCount(); 162 | ThreadUtils::pool->enqueue([&](uint32 id, uint32, uint32) { 163 | int start = (((_bufferD/2)*id)/threadCount)*2; 164 | int end = (((_bufferD/2)*(id + 1))/threadCount)*2; 165 | 166 | for (int z = start; z < end; ++z) 167 | for (int y = 0; y < _bufferH; ++y) 168 | for (int x = 0; x < _bufferW; ++x) 169 | if (_bufferedData[x + size_t(_bufferW)*(y + size_t(_bufferH)*z)]) 170 | getLowLut(_lowLutLevels - 1, x/2, y/2, z/2) = 1; 171 | }, threadCount)->wait(); 172 | 173 | for (int i = _lowLutLevels - 2; i >= 0; i--) 174 | upsampleLutLevel(i); 175 | } 176 | 177 | void VoxelData::cacheData(int x, int y, int z, int w, int h, int d) { 178 | if (_loader) { 179 | _loader->processBlock(_bufferedData.get(), x, y, z, w, h, d); 180 | return; 181 | } 182 | 183 | uint64_t zStride = _dataH*(uint64_t)_dataW; 184 | uint64_t yStride = (uint64_t)_dataW; 185 | uint64_t offsetZ = z*zStride; 186 | uint64_t offsetY = y*yStride; 187 | uint64_t offsetX = x; 188 | uint64_t baseOffset = (offsetX + offsetY + offsetZ)*sizeof(uint32) + 3*sizeof(uint32); 189 | uint64_t offset = 0; 190 | 191 | for (uint64_t voxelZ = 0; voxelZ < (unsigned)d; voxelZ++) { 192 | for (uint64_t voxelY = 0; voxelY < (unsigned)h; voxelY++) { 193 | #ifdef _MSC_VER 194 | _fseeki64(_dataStream, baseOffset + offset, SEEK_SET); 195 | #elif __APPLE__ 196 | fseeko(_dataStream, baseOffset + offset, SEEK_SET); 197 | #else 198 | fseeko64(_dataStream, baseOffset + offset, SEEK_SET); 199 | #endif 200 | 201 | fread(_bufferedData.get() + (voxelY + voxelZ*h)*w, sizeof(uint32), w, _dataStream); 202 | 203 | offset += yStride*sizeof(uint32); 204 | } 205 | 206 | baseOffset += zStride*sizeof(uint32); 207 | offset = 0; 208 | } 209 | } 210 | 211 | void VoxelData::init(size_t mem) { 212 | _virtualDataW = roundToPow2(_dataW); 213 | _virtualDataH = roundToPow2(_dataH); 214 | _virtualDataD = roundToPow2(_dataD); 215 | _highestVirtualBit = findHighestBit(std::max(_virtualDataW, std::max(_virtualDataH, _virtualDataD))); 216 | 217 | _cellCost = sizeof(uint32); 218 | if (_loader) 219 | _cellCost += _loader->blockMemRequirement(1, 1, 1); 220 | 221 | auto partitionCost = [&](int level) { 222 | int lowBit = _highestVirtualBit - level; 223 | uint64 topLutCost = countCellsInHiarchicalGrid(level + 1); 224 | uint64 lowLutCost = countCellsInHiarchicalGrid(lowBit); 225 | uint64 gridCost = (uint64(1) << uint64(lowBit*3))*_cellCost; 226 | return topLutCost + lowLutCost + gridCost; 227 | }; 228 | 229 | int largestLowerLevel = -1; 230 | uint64 smallestSize = std::numeric_limits::max(); 231 | for (int i = _highestVirtualBit; i >= 0; --i) { 232 | if (partitionCost(i) < mem) 233 | largestLowerLevel = _highestVirtualBit - i; 234 | smallestSize = std::min(smallestSize, partitionCost(i)); 235 | } 236 | if (largestLowerLevel == -1) { 237 | std::cout << "Not enough memory to convert voxel volume. Require at least " 238 | << prettyPrintMemory(smallestSize) << std::endl; 239 | std::exit(-1); 240 | } 241 | 242 | _lowLutLevels = largestLowerLevel; 243 | _topLutLevels = _highestVirtualBit - largestLowerLevel + 1; 244 | _maxCacheableSize = size_t(1) << largestLowerLevel; 245 | 246 | _topLut.reset(new uint8[countCellsInHiarchicalGrid(_topLutLevels)]()); 247 | _lowLut.reset(new uint8[countCellsInHiarchicalGrid(_lowLutLevels)]()); 248 | _bufferedData.reset(new uint32[size_t(1) << size_t(largestLowerLevel*3)]()); 249 | 250 | std::cout << "Using a cache block of size " << _maxCacheableSize << "^3, taking up " 251 | << prettyPrintMemory(partitionCost(_topLutLevels - 1)) << " in memory."; 252 | if (largestLowerLevel < _highestVirtualBit) { 253 | std::cout << " For the next size of " << _maxCacheableSize*2 << "^3, you would need " 254 | << prettyPrintMemory(partitionCost(_topLutLevels - 2)) << " of memory"; 255 | } 256 | std::cout << std::endl; 257 | 258 | buildHierarchicalGridLinks(_topLut.get(), _topLutLevels, _topTable); 259 | buildHierarchicalGridLinks(_lowLut.get(), _lowLutLevels, _lowTable); 260 | 261 | _bufferX = _bufferY = _bufferZ = _bufferW = _bufferH = _bufferD = 0; 262 | } 263 | 264 | void VoxelData::prepareDataAccess(int x, int y, int z, int size) { 265 | int width = std::min(size, _dataW - x); 266 | int height = std::min(size, _dataH - y); 267 | int depth = std::min(size, _dataD - z); 268 | 269 | if (width <= 0 || height <= 0 || depth <= 0) 270 | return; 271 | 272 | if (x >= _bufferX && 273 | y >= _bufferY && 274 | z >= _bufferZ && 275 | x + width <= _bufferX + _bufferW && 276 | y + height <= _bufferY + _bufferH && 277 | z + depth <= _bufferZ + _bufferD) 278 | return; 279 | 280 | if (size_t(size) <= _maxCacheableSize) { 281 | _bufferX = x; 282 | _bufferY = y; 283 | _bufferZ = z; 284 | _bufferW = width; 285 | _bufferH = height; 286 | _bufferD = depth; 287 | 288 | cacheData(x, y, z, width, height, depth); 289 | buildLowLut(); 290 | } 291 | } 292 | 293 | int VoxelData::sideLength() const { 294 | return std::max(_virtualDataW, std::max(_virtualDataH, _virtualDataD)); 295 | } 296 | 297 | Vec3 VoxelData::getCenter() const { 298 | return Vec3( 299 | _dataW*0.5f/sideLength(), 300 | _dataH*0.5f/sideLength(), 301 | _dataD*0.5f/sideLength() 302 | ); 303 | } 304 | -------------------------------------------------------------------------------- /src/VoxelData.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef VOXELDATA_HPP_ 25 | #define VOXELDATA_HPP_ 26 | 27 | #include "math/Vec3.hpp" 28 | 29 | #include "IntTypes.hpp" 30 | #include "Util.hpp" 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | class PlyLoader; 37 | 38 | class VoxelData { 39 | FILE *_dataStream; 40 | PlyLoader *_loader; 41 | 42 | int _dataW; 43 | int _dataH; 44 | int _dataD; 45 | 46 | int _virtualDataW; 47 | int _virtualDataH; 48 | int _virtualDataD; 49 | 50 | int _highestVirtualBit; 51 | int _lowLutLevels; 52 | 53 | size_t _maxCacheableSize; 54 | 55 | size_t _cellCost; 56 | 57 | std::unique_ptr _topLut, _lowLut; 58 | std::vector _topTable; 59 | std::vector _lowTable; 60 | int _minLutStep; 61 | int _topLutLevels; 62 | 63 | std::unique_ptr _bufferedData; 64 | 65 | int _bufferX, _bufferY, _bufferZ; 66 | int _bufferW, _bufferH, _bufferD; 67 | 68 | template 69 | void upsampleLutLevel(int l); 70 | 71 | void buildTopLutBlock(int x, int y, int z); 72 | 73 | void buildTopLut(); 74 | void buildLowLut(); 75 | 76 | void cacheData(int x, int y, int z, int w, int h, int d); 77 | 78 | void init(size_t mem); 79 | 80 | inline size_t lutIdx(int l, int x, int y, int z) const { 81 | return x + ((size_t)y << l) + ((size_t)z << 2*l); 82 | } 83 | 84 | inline uint8 &getTopLut(int l, int x, int y, int z) { 85 | return _topTable[l][lutIdx(l, x, y, z)]; 86 | } 87 | 88 | inline uint8 &getLowLut(int l, int x, int y, int z) { 89 | return _lowTable[l][lutIdx(l, x, y, z)]; 90 | } 91 | 92 | template 93 | inline uint8 &getLut(int l, int x, int y, int z) 94 | { 95 | if (isTop) 96 | return getTopLut(l, x, y, z); 97 | else 98 | return getLowLut(l, x, y, z); 99 | } 100 | 101 | public: 102 | VoxelData(const char *path, size_t mem); 103 | VoxelData(PlyLoader *loader, int sideLength, size_t mem); 104 | ~VoxelData(); 105 | 106 | inline uint32 getVoxel(int x, int y, int z) const { 107 | if (x >= _dataW || y >= _dataH || z >= _dataD) 108 | return 0; 109 | size_t idx = size_t(x - _bufferX) + _bufferW*(size_t(y - _bufferY) + _bufferH*size_t(z - _bufferZ)); 110 | return _bufferedData[idx]; 111 | } 112 | 113 | inline uint32 getVoxelDestructive(int x, int y, int z) { 114 | if (x >= _dataW || y >= _dataH || z >= _dataD) 115 | return 0; 116 | size_t idx = size_t(x - _bufferX) + _bufferW*(size_t(y - _bufferY) + _bufferH*size_t(z - _bufferZ)); 117 | uint32 value = _bufferedData[idx]; 118 | if (value) 119 | _bufferedData[idx] = 0; 120 | return value; 121 | } 122 | 123 | inline bool cubeContainsVoxelsDestructive(int x, int y, int z, int size) { 124 | if (x >= _dataW || y >= _dataH || z >= _dataD) 125 | return false; 126 | 127 | int bit = findHighestBit(size); 128 | if (size == 1) { 129 | return getVoxel(x, y, z) != 0; 130 | } else if (bit < _lowLutLevels) { 131 | uint8 value = getLowLut(_lowLutLevels - bit, (x - _bufferX) >> bit, (y - _bufferY) >> bit, (z - _bufferZ) >> bit); 132 | if (value != 0) 133 | getLowLut(_lowLutLevels - bit, (x - _bufferX) >> bit, (y - _bufferY) >> bit, (z - _bufferZ) >> bit) = 0; 134 | return value != 0; 135 | } else { 136 | return getTopLut(_highestVirtualBit - bit, x >> bit, y >> bit, z >> bit) != 0; 137 | } 138 | } 139 | 140 | void prepareDataAccess(int x, int y, int z, int size); 141 | 142 | int sideLength() const; 143 | Vec3 getCenter() const; 144 | }; 145 | 146 | 147 | #endif /* VOXELDATA_H_ */ 148 | -------------------------------------------------------------------------------- /src/VoxelOctree.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include "VoxelOctree.hpp" 25 | #include "VoxelData.hpp" 26 | #include "Debug.hpp" 27 | #include "Util.hpp" 28 | 29 | #include "third-party/lz4.h" 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | static const uint32 BitCount[] = { 37 | 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 38 | 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 39 | 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 40 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 41 | 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 42 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 43 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 44 | 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 45 | 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 46 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 47 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 48 | 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 49 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 50 | 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 51 | 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 52 | 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 53 | }; 54 | 55 | static const size_t CompressionBlockSize = 64*1024*1024; 56 | 57 | VoxelOctree::VoxelOctree(const char *path) : _voxels(0) { 58 | FILE *fp = fopen(path, "rb"); 59 | 60 | if (fp) { 61 | 62 | fread(_center.a, sizeof(float), 3, fp); 63 | fread(&_octreeSize, sizeof(uint64), 1, fp); 64 | 65 | _octree.reset(new uint32[_octreeSize]); 66 | 67 | std::unique_ptr buffer(new char[LZ4_compressBound(CompressionBlockSize)]); 68 | char *dst = reinterpret_cast(_octree.get()); 69 | 70 | LZ4_streamDecode_t *stream = LZ4_createStreamDecode(); 71 | LZ4_setStreamDecode(stream, dst, 0); 72 | 73 | uint64 compressedSize = 0; 74 | for (uint64 offset = 0; offset < _octreeSize*sizeof(uint32); offset += CompressionBlockSize) { 75 | uint64 compSize; 76 | fread(&compSize, sizeof(uint64), 1, fp); 77 | fread(buffer.get(), sizeof(char), size_t(compSize), fp); 78 | 79 | int outSize = std::min(_octreeSize*sizeof(uint32) - offset, CompressionBlockSize); 80 | LZ4_decompress_fast_continue(stream, buffer.get(), dst + offset, outSize); 81 | compressedSize += compSize + 8; 82 | } 83 | LZ4_freeStreamDecode(stream); 84 | 85 | fclose(fp); 86 | 87 | std::cout << "Octree size: " << prettyPrintMemory(_octreeSize*sizeof(uint32)) 88 | << " Compressed size: " << prettyPrintMemory(compressedSize) << std::endl; 89 | } 90 | } 91 | 92 | void VoxelOctree::save(const char *path) { 93 | FILE *fp = fopen(path, "wb"); 94 | 95 | if (fp) { 96 | fwrite(_center.a, sizeof(float), 3, fp); 97 | fwrite(&_octreeSize, sizeof(uint64), 1, fp); 98 | 99 | LZ4_stream_t *stream = LZ4_createStream(); 100 | LZ4_resetStream(stream); 101 | 102 | std::unique_ptr buffer(new char[LZ4_compressBound(CompressionBlockSize)]); 103 | const char *src = reinterpret_cast(_octree.get()); 104 | 105 | uint64 compressedSize = 0; 106 | for (uint64 offset = 0; offset < _octreeSize*sizeof(uint32); offset += CompressionBlockSize) { 107 | int outSize = int(std::min(_octreeSize*sizeof(uint32) - offset, uint64(CompressionBlockSize))); 108 | uint64 compSize = LZ4_compress_continue(stream, src + offset, buffer.get(), outSize); 109 | 110 | fwrite(&compSize, sizeof(uint64), 1, fp); 111 | fwrite(buffer.get(), sizeof(char), size_t(compSize), fp); 112 | 113 | compressedSize += compSize + 8; 114 | } 115 | 116 | LZ4_freeStream(stream); 117 | 118 | fclose(fp); 119 | 120 | std::cout << "Octree size: " << prettyPrintMemory(_octreeSize*sizeof(uint32)) 121 | << " Compressed size: " << prettyPrintMemory(compressedSize) << std::endl; 122 | } 123 | } 124 | 125 | VoxelOctree::VoxelOctree(VoxelData *voxels) 126 | : _voxels(voxels) 127 | { 128 | std::unique_ptr> octreeAllocator(new ChunkedAllocator()); 129 | octreeAllocator->pushBack(0); 130 | 131 | buildOctree(*octreeAllocator, 0, 0, 0, _voxels->sideLength(), 0); 132 | (*octreeAllocator)[0] |= 1 << 18; 133 | 134 | _octreeSize = octreeAllocator->size() + octreeAllocator->insertionCount(); 135 | _octree = octreeAllocator->finalize(); 136 | _center = _voxels->getCenter(); 137 | } 138 | 139 | uint64 VoxelOctree::buildOctree(ChunkedAllocator &allocator, int x, int y, int z, int size, uint64 descriptorIndex) { 140 | _voxels->prepareDataAccess(x, y, z, size); 141 | 142 | int halfSize = size >> 1; 143 | 144 | int posX[] = {x + halfSize, x, x + halfSize, x, x + halfSize, x, x + halfSize, x}; 145 | int posY[] = {y + halfSize, y + halfSize, y, y, y + halfSize, y + halfSize, y, y}; 146 | int posZ[] = {z + halfSize, z + halfSize, z + halfSize, z + halfSize, z, z, z, z}; 147 | 148 | uint64 childOffset = uint64(allocator.size()) - descriptorIndex; 149 | 150 | int childCount = 0; 151 | int childIndices[8]; 152 | uint32 childMask = 0; 153 | for (int i = 0; i < 8; i++) { 154 | if (_voxels->cubeContainsVoxelsDestructive(posX[i], posY[i], posZ[i], halfSize)) { 155 | childMask |= 128 >> i; 156 | childIndices[childCount++] = i; 157 | } 158 | } 159 | 160 | bool hasLargeChildren = false; 161 | uint32 leafMask; 162 | if (halfSize == 1) { 163 | leafMask = 0; 164 | 165 | for (int i = 0; i < childCount; i++) { 166 | int idx = childIndices[childCount - i - 1]; 167 | allocator.pushBack(_voxels->getVoxelDestructive(posX[idx], posY[idx], posZ[idx])); 168 | } 169 | } else { 170 | leafMask = childMask; 171 | for (int i = 0; i < childCount; i++) 172 | allocator.pushBack(0); 173 | 174 | uint64 grandChildOffsets[8]; 175 | uint64 delta = 0; 176 | uint64 insertionCount = allocator.insertionCount(); 177 | for (int i = 0; i < childCount; i++) { 178 | int idx = childIndices[childCount - i - 1]; 179 | grandChildOffsets[i] = delta + buildOctree(allocator, posX[idx], posY[idx], posZ[idx], 180 | halfSize, descriptorIndex + childOffset + i); 181 | delta += allocator.insertionCount() - insertionCount; 182 | insertionCount = allocator.insertionCount(); 183 | if (grandChildOffsets[i] > 0x3FFF) 184 | hasLargeChildren = true; 185 | } 186 | 187 | for (int i = 0; i < childCount; i++) { 188 | uint64 childIndex = descriptorIndex + childOffset + i; 189 | uint64 offset = grandChildOffsets[i]; 190 | if (hasLargeChildren) { 191 | offset += childCount - i; 192 | allocator.insert(childIndex + 1, uint32(offset)); 193 | allocator[childIndex] |= 0x20000; 194 | offset >>= 32; 195 | } 196 | allocator[childIndex] |= uint32(offset << 18); 197 | } 198 | } 199 | 200 | allocator[descriptorIndex] = (childMask << 8) | leafMask; 201 | if (hasLargeChildren) 202 | allocator[descriptorIndex] |= 0x10000; 203 | 204 | return childOffset; 205 | } 206 | 207 | bool VoxelOctree::raymarch(const Vec3 &o, const Vec3 &d, float rayScale, uint32 &normal, float &t) { 208 | struct StackEntry { 209 | uint64 offset; 210 | float maxT; 211 | }; 212 | StackEntry rayStack[MaxScale + 1]; 213 | 214 | float ox = o.x, oy = o.y, oz = o.z; 215 | float dx = d.x, dy = d.y, dz = d.z; 216 | 217 | if (std::fabs(dx) < 1e-4f) dx = 1e-4f; 218 | if (std::fabs(dy) < 1e-4f) dy = 1e-4f; 219 | if (std::fabs(dz) < 1e-4f) dz = 1e-4f; 220 | 221 | float dTx = 1.0f/-std::fabs(dx); 222 | float dTy = 1.0f/-std::fabs(dy); 223 | float dTz = 1.0f/-std::fabs(dz); 224 | 225 | float bTx = dTx*ox; 226 | float bTy = dTy*oy; 227 | float bTz = dTz*oz; 228 | 229 | uint8 octantMask = 7; 230 | if (dx > 0.0f) octantMask ^= 1, bTx = 3.0f*dTx - bTx; 231 | if (dy > 0.0f) octantMask ^= 2, bTy = 3.0f*dTy - bTy; 232 | if (dz > 0.0f) octantMask ^= 4, bTz = 3.0f*dTz - bTz; 233 | 234 | float minT = std::max(2.0f*dTx - bTx, std::max(2.0f*dTy - bTy, 2.0f*dTz - bTz)); 235 | float maxT = std::min( dTx - bTx, std::min( dTy - bTy, dTz - bTz)); 236 | minT = std::max(minT, 0.0f); 237 | 238 | uint32 current = 0; 239 | uint64 parent = 0; 240 | int idx = 0; 241 | float posX = 1.0f; 242 | float posY = 1.0f; 243 | float posZ = 1.0f; 244 | int scale = MaxScale - 1; 245 | 246 | float scaleExp2 = 0.5f; 247 | 248 | if (1.5f*dTx - bTx > minT) idx ^= 1, posX = 1.5f; 249 | if (1.5f*dTy - bTy > minT) idx ^= 2, posY = 1.5f; 250 | if (1.5f*dTz - bTz > minT) idx ^= 4, posZ = 1.5f; 251 | 252 | while (scale < MaxScale) { 253 | if (current == 0) 254 | current = _octree[parent]; 255 | 256 | float cornerTX = posX*dTx - bTx; 257 | float cornerTY = posY*dTy - bTy; 258 | float cornerTZ = posZ*dTz - bTz; 259 | float maxTC = std::min(cornerTX, std::min(cornerTY, cornerTZ)); 260 | 261 | int childShift = idx ^ octantMask; 262 | uint32 childMasks = current << childShift; 263 | 264 | if ((childMasks & 0x8000) && minT <= maxT) { 265 | if (maxTC*rayScale >= scaleExp2) { 266 | t = maxTC; 267 | return true; 268 | } 269 | 270 | float maxTV = std::min(maxT, maxTC); 271 | float half = scaleExp2*0.5f; 272 | float centerTX = half*dTx + cornerTX; 273 | float centerTY = half*dTy + cornerTY; 274 | float centerTZ = half*dTz + cornerTZ; 275 | 276 | if (minT <= maxTV) { 277 | uint64 childOffset = current >> 18; 278 | if (current & 0x20000) 279 | childOffset = (childOffset << 32) | uint64(_octree[parent + 1]); 280 | 281 | if (!(childMasks & 0x80)) { 282 | normal = _octree[childOffset + parent + BitCount[((childMasks >> (8 + childShift)) << childShift) & 127]]; 283 | 284 | break; 285 | } 286 | 287 | rayStack[scale].offset = parent; 288 | rayStack[scale].maxT = maxT; 289 | 290 | uint32 siblingCount = BitCount[childMasks & 127]; 291 | parent += childOffset + siblingCount; 292 | if (current & 0x10000) 293 | parent += siblingCount; 294 | 295 | idx = 0; 296 | scale--; 297 | scaleExp2 = half; 298 | 299 | if (centerTX > minT) idx ^= 1, posX += scaleExp2; 300 | if (centerTY > minT) idx ^= 2, posY += scaleExp2; 301 | if (centerTZ > minT) idx ^= 4, posZ += scaleExp2; 302 | 303 | maxT = maxTV; 304 | current = 0; 305 | 306 | continue; 307 | } 308 | } 309 | 310 | int stepMask = 0; 311 | if (cornerTX <= maxTC) stepMask ^= 1, posX -= scaleExp2; 312 | if (cornerTY <= maxTC) stepMask ^= 2, posY -= scaleExp2; 313 | if (cornerTZ <= maxTC) stepMask ^= 4, posZ -= scaleExp2; 314 | 315 | minT = maxTC; 316 | idx ^= stepMask; 317 | 318 | if ((idx & stepMask) != 0) { 319 | int differingBits = 0; 320 | if (stepMask & 1) differingBits |= floatBitsToUint(posX) ^ floatBitsToUint(posX + scaleExp2); 321 | if (stepMask & 2) differingBits |= floatBitsToUint(posY) ^ floatBitsToUint(posY + scaleExp2); 322 | if (stepMask & 4) differingBits |= floatBitsToUint(posZ) ^ floatBitsToUint(posZ + scaleExp2); 323 | scale = (floatBitsToUint((float)differingBits) >> 23) - 127; 324 | scaleExp2 = uintBitsToFloat((scale - MaxScale + 127) << 23); 325 | 326 | parent = rayStack[scale].offset; 327 | maxT = rayStack[scale].maxT; 328 | 329 | int shX = floatBitsToUint(posX) >> scale; 330 | int shY = floatBitsToUint(posY) >> scale; 331 | int shZ = floatBitsToUint(posZ) >> scale; 332 | posX = uintBitsToFloat(shX << scale); 333 | posY = uintBitsToFloat(shY << scale); 334 | posZ = uintBitsToFloat(shZ << scale); 335 | idx = (shX & 1) | ((shY & 1) << 1) | ((shZ & 1) << 2); 336 | 337 | current = 0; 338 | } 339 | } 340 | 341 | if (scale >= MaxScale) 342 | return false; 343 | 344 | t = minT; 345 | return true; 346 | } 347 | -------------------------------------------------------------------------------- /src/VoxelOctree.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef VOXELOCTREE_HPP_ 25 | #define VOXELOCTREE_HPP_ 26 | 27 | #include "math/Vec3.hpp" 28 | 29 | #include "ChunkedAllocator.hpp" 30 | #include "IntTypes.hpp" 31 | 32 | #include 33 | #include 34 | 35 | class VoxelData; 36 | 37 | class VoxelOctree { 38 | static const int32 MaxScale = 23; 39 | 40 | uint64 _octreeSize; 41 | std::unique_ptr _octree; 42 | 43 | VoxelData *_voxels; 44 | Vec3 _center; 45 | 46 | uint64 buildOctree(ChunkedAllocator &allocator, int x, int y, int z, int size, uint64 descriptorIndex); 47 | 48 | public: 49 | VoxelOctree(const char *path); 50 | VoxelOctree(VoxelData *voxels); 51 | 52 | void save(const char *path); 53 | bool raymarch(const Vec3 &o, const Vec3 &d, float rayScale, uint32 &normal, float &t); 54 | 55 | Vec3 center() const { 56 | return _center; 57 | } 58 | }; 59 | 60 | #endif /* VOXELOCTREE_HPP_ */ 61 | -------------------------------------------------------------------------------- /src/math/Mat4.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include "Mat4.hpp" 25 | 26 | #include 27 | 28 | #ifndef M_PI 29 | #define M_PI 3.14159265358979323846 30 | #endif 31 | 32 | Mat4::Mat4() { 33 | a12 = a13 = a14 = 0.0f; 34 | a21 = a23 = a24 = 0.0f; 35 | a31 = a32 = a34 = 0.0f; 36 | a41 = a42 = a43 = 0.0f; 37 | a11 = a22 = a33 = a44 = 1.0f; 38 | } 39 | 40 | Mat4::Mat4( 41 | float _a11, float _a12, float _a13, float _a14, 42 | float _a21, float _a22, float _a23, float _a24, 43 | float _a31, float _a32, float _a33, float _a34, 44 | float _a41, float _a42, float _a43, float _a44) : 45 | a11(_a11), a12(_a12), a13(_a13), a14(_a14), 46 | a21(_a21), a22(_a22), a23(_a23), a24(_a24), 47 | a31(_a31), a32(_a32), a33(_a33), a34(_a34), 48 | a41(_a41), a42(_a42), a43(_a43), a44(_a44) {} 49 | 50 | Mat4 Mat4::transpose() const { 51 | return Mat4( 52 | a11, a21, a31, a41, 53 | a12, a22, a32, a42, 54 | a13, a23, a33, a43, 55 | a14, a24, a34, a44 56 | ); 57 | } 58 | 59 | Mat4 Mat4::pseudoInvert() const { 60 | Mat4 trans = translate(Vec3(-a14, -a24, -a34)); 61 | Mat4 rot = transpose(); 62 | rot.a41 = rot.a42 = rot.a43 = 0.0f; 63 | 64 | return rot*trans; 65 | } 66 | 67 | Mat4 Mat4::operator*(const Mat4 &b) const { 68 | Mat4 result; 69 | for (int i = 0; i < 4; i++) 70 | for (int t = 0; t < 4; t++) 71 | result.a[i*4 + t] = 72 | a[i*4 + 0]*b.a[0*4 + t] + 73 | a[i*4 + 1]*b.a[1*4 + t] + 74 | a[i*4 + 2]*b.a[2*4 + t] + 75 | a[i*4 + 3]*b.a[3*4 + t]; 76 | 77 | return result; 78 | } 79 | 80 | Vec3 Mat4::operator*(const Vec3 &b) const { 81 | return Vec3( 82 | a11*b.x + a12*b.y + a13*b.z + a14, 83 | a21*b.x + a22*b.y + a23*b.z + a24, 84 | a31*b.x + a32*b.y + a33*b.z + a34 85 | ); 86 | } 87 | 88 | Vec3 Mat4::transformVector(const Vec3 &b) const { 89 | return Vec3( 90 | a11*b.x + a12*b.y + a13*b.z, 91 | a21*b.x + a22*b.y + a23*b.z, 92 | a31*b.x + a32*b.y + a33*b.z 93 | ); 94 | } 95 | 96 | Mat4 Mat4::translate(const Vec3 &v) { 97 | return Mat4( 98 | 1.0f, 0.0f, 0.0f, v.x, 99 | 0.0f, 1.0f, 0.0f, v.y, 100 | 0.0f, 0.0f, 1.0f, v.z, 101 | 0.0f, 0.0f, 0.0f, 1.0f 102 | ); 103 | } 104 | 105 | Mat4 Mat4::scale(const Vec3 &s) { 106 | return Mat4( 107 | s.x, 0.0f, 0.0f, 0.0f, 108 | 0.0f, s.y, 0.0f, 0.0f, 109 | 0.0f, 0.0f, s.z, 0.0f, 110 | 0.0f, 0.0f, 0.0f, 1.0f 111 | ); 112 | } 113 | 114 | Mat4 Mat4::rotXYZ(const Vec3 &rot) { 115 | Vec3 r = rot*float(M_PI)/180.0f; 116 | float c[] = {std::cos(r.x), std::cos(r.y), std::cos(r.z)}; 117 | float s[] = {std::sin(r.x), std::sin(r.y), std::sin(r.z)}; 118 | 119 | return Mat4( 120 | c[1]*c[2], -c[0]*s[2] + s[0]*s[1]*c[2], s[0]*s[2] + c[0]*s[1]*c[2], 0.0f, 121 | c[1]*s[2], c[0]*c[2] + s[0]*s[1]*s[2], -s[0]*c[2] + c[0]*s[1]*s[2], 0.0f, 122 | -s[1], s[0]*c[1], c[0]*c[1], 0.0f, 123 | 0.0f, 0.0f, 0.0f, 1.0f 124 | ); 125 | } 126 | 127 | Mat4 Mat4::rotYZX(const Vec3 &rot) { 128 | Vec3 r = rot*float(M_PI)/180.0f; 129 | float c[] = {std::cos(r.x), std::cos(r.y), std::cos(r.z)}; 130 | float s[] = {std::sin(r.x), std::sin(r.y), std::sin(r.z)}; 131 | 132 | return Mat4( 133 | c[1]*c[2], c[0]*c[1]*s[2] - s[0]*s[1], c[0]*s[1] + c[1]*s[0]*s[2], 0.0f, 134 | -s[2], c[0]*c[2], c[2]*s[0], 0.0f, 135 | -c[2]*s[1], -c[1]*s[0] - c[0]*s[1]*s[2], c[0]*c[1] - s[0]*s[1]*s[2], 0.0f, 136 | 0.0f, 0.0f, 0.0f, 1.0f 137 | ); 138 | } 139 | 140 | Mat4 Mat4::rotAxis(const Vec3 &axis, float angle) { 141 | angle *= float(M_PI)/180.0f; 142 | float s = std::sin(angle); 143 | float c = std::cos(angle); 144 | float c1 = 1.0f - c; 145 | float x = axis.x; 146 | float y = axis.y; 147 | float z = axis.z; 148 | 149 | return Mat4( 150 | c + x*x*c1, x*y*c1 - z*s, x*z*c1 + y*s, 0.0f, 151 | y*x*c1 + z*s, c + y*y*c1, y*z*c1 - x*s, 0.0f, 152 | z*x*c1 - y*s, z*y*c1 + x*s, c + z*z*c1, 0.0f, 153 | 0.0f, 0.0f, 0.0f, 1.0f 154 | ); 155 | } 156 | 157 | Mat4 Mat4::ortho(float l, float r, float b, float t, float n, float f) { 158 | return Mat4( 159 | 2.0f/(r-l), 0.0f, 0.0f, -(r+l)/(r-l), 160 | 0.0f, 2.0f/(t-b), 0.0f, -(t+b)/(t-b), 161 | 0.0f, 0.0f, -2.0f/(f-n), -(f+n)/(f-n), 162 | 0.0f, 0.0f, 0.0f, 1.0f 163 | ); 164 | } 165 | 166 | Mat4 Mat4::perspective(float aov, float ratio, float near, float far) { 167 | float t = 1.0f/std::tan(aov*(float(M_PI)/360.0f)); 168 | float a = (far + near)/(far - near); 169 | float b = 2.0f*far*near/(far - near); 170 | float c = t/ratio; 171 | 172 | return Mat4( 173 | c, 0.0f, 0.0f, 0.0f, 174 | 0.0f, t, 0.0f, 0.0f, 175 | 0.0f, 0.0f, -a, -b, 176 | 0.0f, 0.0f, -1.0f, 0.0f 177 | ); 178 | } 179 | 180 | Mat4 Mat4::lookAt(const Vec3 &pos, const Vec3 &fwd, const Vec3 &up) { 181 | Vec3 f = fwd.normalize(); 182 | Vec3 r = f.cross(up).normalize(); 183 | Vec3 u = r.cross(f).normalize(); 184 | 185 | return Mat4( 186 | r.x, u.x, f.x, pos.x, 187 | r.y, u.y, f.y, pos.y, 188 | r.z, u.z, f.z, pos.z, 189 | 0.0f, 0.0f, 0.0f, 1.0f 190 | ); 191 | 192 | } 193 | -------------------------------------------------------------------------------- /src/math/Mat4.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef MATH_MAT4_HPP_ 25 | #define MATH_MAT4_HPP_ 26 | 27 | #include "Vec3.hpp" 28 | 29 | struct Mat4 { 30 | union { 31 | struct { 32 | float a11, a12, a13, a14; 33 | float a21, a22, a23, a24; 34 | float a31, a32, a33, a34; 35 | float a41, a42, a43, a44; 36 | }; 37 | float a[16]; 38 | }; 39 | 40 | Mat4(); 41 | Mat4(float _a11, float _a12, float _a13, float _a14, 42 | float _a21, float _a22, float _a23, float _a24, 43 | float _a31, float _a32, float _a33, float _a34, 44 | float _a41, float _a42, float _a43, float _a44); 45 | 46 | Mat4 transpose() const; 47 | Mat4 pseudoInvert() const; 48 | 49 | Mat4 operator*(const Mat4 &b) const; 50 | Vec3 operator*(const Vec3 &b) const; 51 | 52 | Vec3 transformVector(const Vec3 &v) const; 53 | 54 | static Mat4 translate(const Vec3 &v); 55 | static Mat4 scale(const Vec3 &s); 56 | static Mat4 rotXYZ(const Vec3 &rot); 57 | static Mat4 rotYZX(const Vec3 &rot); 58 | static Mat4 rotAxis(const Vec3 &axis, float angle); 59 | 60 | static Mat4 ortho(float l, float r, float b, float t, float near, float far); 61 | static Mat4 perspective(float aov, float ratio, float near, float far); 62 | static Mat4 lookAt(const Vec3 &pos, const Vec3 &fwd, const Vec3 &up); 63 | }; 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/math/MatrixStack.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include "MatrixStack.hpp" 25 | #include "Debug.hpp" 26 | 27 | #include 28 | 29 | std::stack MatrixStack::_stacks[3] = { 30 | std::stack(std::deque(1, Mat4())), 31 | std::stack(std::deque(1, Mat4())), 32 | std::stack(std::deque(1, Mat4())) 33 | }; 34 | 35 | void MatrixStack::set(StackName n, const Mat4 &m) { 36 | ASSERT(n <= VIEW_STACK, "Cannot manipulate virtual stacks\n"); 37 | _stacks[n].top() = m; 38 | } 39 | 40 | void MatrixStack::mulR(StackName n, const Mat4 &m) { 41 | ASSERT(n <= VIEW_STACK, "Cannot manipulate virtual stacks\n"); 42 | _stacks[n].top() = _stacks[n].top()*m; 43 | } 44 | 45 | void MatrixStack::mulL(StackName n, const Mat4 &m) { 46 | ASSERT(n <= VIEW_STACK, "Cannot manipulate virtual stacks\n"); 47 | _stacks[n].top() = m*_stacks[n].top(); 48 | } 49 | 50 | void MatrixStack::get(StackName n, Mat4 &m) { 51 | switch(n) { 52 | case PROJECTION_STACK: 53 | case MODEL_STACK: 54 | case VIEW_STACK: 55 | m = _stacks[n].top(); 56 | break; 57 | case MODELVIEW_STACK: 58 | m = _stacks[VIEW_STACK].top().pseudoInvert()*_stacks[MODEL_STACK].top(); 59 | break; 60 | case MODELVIEWPROJECTION_STACK: 61 | m = _stacks[PROJECTION_STACK].top()* 62 | _stacks[VIEW_STACK].top().pseudoInvert()* 63 | _stacks[MODEL_STACK].top(); 64 | break; 65 | case INV_MODEL_STACK: 66 | m = _stacks[MODEL_STACK].top().pseudoInvert(); 67 | break; 68 | case INV_VIEW_STACK: 69 | m = _stacks[VIEW_STACK].top().pseudoInvert(); 70 | break; 71 | case INV_MODELVIEW_STACK: 72 | m = _stacks[MODEL_STACK].top().pseudoInvert()*_stacks[VIEW_STACK].top(); 73 | break; 74 | default: 75 | FAIL("Invalid matrix stack\n"); 76 | } 77 | } 78 | 79 | void MatrixStack::copyPush(StackName n) { 80 | ASSERT(n <= VIEW_STACK, "Cannot manipulate virtual stacks\n"); 81 | _stacks[n].push(_stacks[n].top()); 82 | } 83 | 84 | void MatrixStack::push(StackName n) { 85 | ASSERT(n <= VIEW_STACK, "Cannot manipulate virtual stacks\n"); 86 | _stacks[n].push(Mat4()); 87 | } 88 | 89 | void MatrixStack::pop(StackName n) { 90 | ASSERT(n <= VIEW_STACK, "Cannot manipulate virtual stacks\n"); 91 | _stacks[n].pop(); 92 | } 93 | -------------------------------------------------------------------------------- /src/math/MatrixStack.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef RENDER_MATRIXSTACK_HPP_ 25 | #define RENDER_MATRIXSTACK_HPP_ 26 | 27 | #include "Mat4.hpp" 28 | 29 | #include 30 | 31 | enum StackName { 32 | PROJECTION_STACK, 33 | MODEL_STACK, 34 | VIEW_STACK, 35 | /* Virtual stacks */ 36 | MODELVIEW_STACK, 37 | MODELVIEWPROJECTION_STACK, 38 | INV_MODEL_STACK, 39 | INV_VIEW_STACK, 40 | INV_MODELVIEW_STACK, 41 | 42 | MATRIX_STACK_COUNT 43 | }; 44 | 45 | enum StackFlag { 46 | PROJECTION_FLAG = (1 << 0), 47 | MODEL_FLAG = (1 << 1), 48 | VIEW_FLAG = (1 << 2), 49 | /* Virtual stacks */ 50 | MODELVIEW_FLAG = (1 << 3), 51 | MODELVIEWPROJECTION_FLAG = (1 << 4), 52 | INV_MODEL_FLAG = (1 << 5), 53 | INV_VIEW_FLAG = (1 << 6), 54 | INV_MODELVIEW_FLAG = (1 << 7) 55 | }; 56 | 57 | class MatrixStack { 58 | static std::stack _stacks[]; 59 | 60 | MatrixStack(); 61 | 62 | public: 63 | static void set(StackName n, const Mat4 &m); 64 | static void mulR(StackName n, const Mat4 &m); 65 | static void mulL(StackName n, const Mat4 &m); 66 | static void get(StackName n, Mat4 &m); 67 | 68 | static void copyPush(StackName n); 69 | static void push(StackName n); 70 | static void pop(StackName n); 71 | }; 72 | 73 | 74 | #endif /* RENDER_MATRIXSTACK_HPP_ */ 75 | -------------------------------------------------------------------------------- /src/math/Vec3.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef MATH_VEC3_HPP_ 25 | #define MATH_VEC3_HPP_ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | struct Vec3 { 32 | union { 33 | struct { float x, y, z; }; 34 | float a[3]; 35 | }; 36 | 37 | Vec3() : x(0.0f), y(0.0f), z(0.0f) {} 38 | Vec3(float a) : x(a), y(a), z(a) {} 39 | Vec3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} 40 | 41 | Vec3 cross(const Vec3 &b) const { 42 | return Vec3(y*b.z - z*b.y, z*b.x - x*b.z, x*b.y - y*b.x); 43 | } 44 | 45 | Vec3 invert() const { 46 | return Vec3(1.0f/x, 1.0f/y, 1.0f/z); 47 | } 48 | 49 | float dot(const Vec3 &b) const { 50 | return x*b.x + y*b.y + z*b.z; 51 | } 52 | 53 | float length() const { 54 | return std::sqrt(x*x + y*y + z*z); 55 | } 56 | 57 | Vec3 normalize() const { 58 | float invSqrt = 1.0f/std::sqrt(x*x + y*y + z*z); 59 | 60 | return Vec3(x*invSqrt, y*invSqrt, z*invSqrt); 61 | } 62 | 63 | Vec3 reflect(const Vec3 &n) const { 64 | float proj = (n.dot(*this))*2.0f; 65 | /* Overloaded operators not defined yet */ 66 | return Vec3( 67 | this->x - n.x*proj, 68 | this->y - n.y*proj, 69 | this->z - n.z*proj 70 | ); 71 | } 72 | 73 | Vec3 &operator+=(const Vec3 &b) { 74 | x += b.x; 75 | y += b.y; 76 | z += b.z; 77 | return *this; 78 | } 79 | 80 | Vec3 &operator-=(const Vec3 &b) { 81 | x -= b.x; 82 | y -= b.y; 83 | z -= b.z; 84 | return *this; 85 | } 86 | 87 | Vec3 &operator*=(const Vec3 &b) { 88 | x *= b.x; 89 | y *= b.y; 90 | z *= b.z; 91 | return *this; 92 | } 93 | 94 | Vec3 &operator/=(const Vec3 &b) { 95 | x /= b.x; 96 | y /= b.y; 97 | z /= b.z; 98 | return *this; 99 | } 100 | 101 | Vec3 &operator*=(float b) { 102 | x *= b; 103 | y *= b; 104 | z *= b; 105 | return *this; 106 | } 107 | 108 | Vec3 &operator/=(float b) { 109 | x /= b; 110 | y /= b; 111 | z /= b; 112 | return *this; 113 | } 114 | 115 | Vec3 operator-() const { 116 | return Vec3(-x, -y, -z); 117 | } 118 | 119 | Vec3 operator+(const Vec3 &b) const { 120 | return Vec3(x + b.x, y + b.y, z + b.z); 121 | } 122 | 123 | Vec3 operator-(const Vec3 &b) const { 124 | return Vec3(x - b.x, y - b.y, z - b.z); 125 | } 126 | 127 | Vec3 operator*(const Vec3 &b) const { 128 | return Vec3(x*b.x, y*b.y, z*b.z); 129 | } 130 | 131 | Vec3 operator/(const Vec3 &b) const { 132 | return Vec3(x/b.x, y/b.y, z/b.z); 133 | } 134 | 135 | Vec3 operator*(float b) const { 136 | return Vec3(x*b, y*b, z*b); 137 | } 138 | 139 | Vec3 operator/(float b) const { 140 | return Vec3(x/b, y/b, z/b); 141 | } 142 | 143 | bool operator>(const Vec3 &b) const { 144 | return x > b.x && y > b.y && z > b.z; 145 | } 146 | 147 | bool operator<(const Vec3 &b) const { 148 | return x < b.x && y < b.y && z < b.z; 149 | } 150 | 151 | bool operator>=(const Vec3 &b) const { 152 | return x >= b.x && y >= b.y && z >= b.z; 153 | } 154 | 155 | bool operator<=(const Vec3 &b) const { 156 | return x <= b.x && y <= b.y && z <= b.z; 157 | } 158 | 159 | bool operator==(const Vec3 &b) const { 160 | return x == b.x && y == b.y && z == b.z; 161 | } 162 | 163 | bool operator!=(const Vec3 &b) const { 164 | return x != b.x || y != b.y || z != b.z; 165 | } 166 | 167 | friend std::ostream &operator<< (std::ostream &stream, const Vec3 &v) { 168 | return stream << "(" << v.x << ", " << v.y << ", " << v.z << ")"; 169 | } 170 | }; 171 | 172 | static inline Vec3 operator*(float a, const Vec3 &b) { 173 | return Vec3(a*b.x, a*b.y, a*b.z); 174 | } 175 | 176 | static inline Vec3 operator/(float a, const Vec3 &b) { 177 | return Vec3(a/b.x, a/b.y, a/b.z); 178 | } 179 | 180 | namespace std { 181 | 182 | static inline Vec3 fabs(const Vec3 &v) { 183 | return Vec3(fabs(v.x), fabs(v.y), fabs(v.z)); 184 | } 185 | 186 | static inline bool isnan(const Vec3 &v) { 187 | return isnan(v.x) || isnan(v.y) || isnan(v.z); 188 | } 189 | 190 | static inline Vec3 exp(const Vec3 &v) { 191 | return Vec3( 192 | std::exp(v.x), 193 | std::exp(v.y), 194 | std::exp(v.z) 195 | ); 196 | } 197 | 198 | static inline Vec3 pow(const Vec3 &v, float p) { 199 | return Vec3( 200 | std::pow(v.x, p), 201 | std::pow(v.y, p), 202 | std::pow(v.z, p) 203 | ); 204 | } 205 | 206 | } 207 | 208 | #endif 209 | -------------------------------------------------------------------------------- /src/third-party/lz4.h: -------------------------------------------------------------------------------- 1 | /* 2 | LZ4 - Fast LZ compression algorithm 3 | Header File 4 | Copyright (C) 2011-2015, Yann Collet. 5 | 6 | BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are 10 | met: 11 | 12 | * Redistributions of source code must retain the above copyright 13 | notice, this list of conditions and the following disclaimer. 14 | * Redistributions in binary form must reproduce the above 15 | copyright notice, this list of conditions and the following disclaimer 16 | in the documentation and/or other materials provided with the 17 | distribution. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | You can contact the author at : 32 | - LZ4 source repository : https://github.com/Cyan4973/lz4 33 | - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c 34 | */ 35 | #pragma once 36 | 37 | #if defined (__cplusplus) 38 | extern "C" { 39 | #endif 40 | 41 | /* 42 | * lz4.h provides block compression functions, and gives full buffer control to programmer. 43 | * If you need to generate inter-operable compressed data (respecting LZ4 frame specification), 44 | * and can let the library handle its own memory, please use lz4frame.h instead. 45 | */ 46 | 47 | /************************************** 48 | * Version 49 | **************************************/ 50 | #define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ 51 | #define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */ 52 | #define LZ4_VERSION_RELEASE 1 /* for tweaks, bug-fixes, or development */ 53 | #define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) 54 | int LZ4_versionNumber (void); 55 | 56 | /************************************** 57 | * Tuning parameter 58 | **************************************/ 59 | /* 60 | * LZ4_MEMORY_USAGE : 61 | * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) 62 | * Increasing memory usage improves compression ratio 63 | * Reduced memory usage can improve speed, due to cache effect 64 | * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache 65 | */ 66 | #define LZ4_MEMORY_USAGE 14 67 | 68 | 69 | /************************************** 70 | * Simple Functions 71 | **************************************/ 72 | 73 | int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize); 74 | int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize); 75 | 76 | /* 77 | LZ4_compress_default() : 78 | Compresses 'sourceSize' bytes from buffer 'source' 79 | into already allocated 'dest' buffer of size 'maxDestSize'. 80 | Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize). 81 | It also runs faster, so it's a recommended setting. 82 | If the function cannot compress 'source' into a more limited 'dest' budget, 83 | compression stops *immediately*, and the function result is zero. 84 | As a consequence, 'dest' content is not valid. 85 | This function never writes outside 'dest' buffer, nor read outside 'source' buffer. 86 | sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE 87 | maxDestSize : full or partial size of buffer 'dest' (which must be already allocated) 88 | return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize) 89 | or 0 if compression fails 90 | 91 | LZ4_decompress_safe() : 92 | compressedSize : is the precise full size of the compressed block. 93 | maxDecompressedSize : is the size of destination buffer, which must be already allocated. 94 | return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize) 95 | If destination buffer is not large enough, decoding will stop and output an error code (<0). 96 | If the source stream is detected malformed, the function will stop decoding and return a negative result. 97 | This function is protected against buffer overflow exploits, including malicious data packets. 98 | It never writes outside output buffer, nor reads outside input buffer. 99 | */ 100 | 101 | 102 | /************************************** 103 | * Advanced Functions 104 | **************************************/ 105 | #define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ 106 | #define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) 107 | 108 | /* 109 | LZ4_compressBound() : 110 | Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) 111 | This function is primarily useful for memory allocation purposes (destination buffer size). 112 | Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). 113 | Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize) 114 | inputSize : max supported value is LZ4_MAX_INPUT_SIZE 115 | return : maximum output size in a "worst case" scenario 116 | or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE) 117 | */ 118 | int LZ4_compressBound(int inputSize); 119 | 120 | /* 121 | LZ4_compress_fast() : 122 | Same as LZ4_compress_default(), but allows to select an "acceleration" factor. 123 | The larger the acceleration value, the faster the algorithm, but also the lesser the compression. 124 | It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. 125 | An acceleration value of "1" is the same as regular LZ4_compress_default() 126 | Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1. 127 | */ 128 | int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration); 129 | 130 | 131 | /* 132 | LZ4_compress_fast_extState() : 133 | Same compression function, just using an externally allocated memory space to store compression state. 134 | Use LZ4_sizeofState() to know how much memory must be allocated, 135 | and allocate it on 8-bytes boundaries (using malloc() typically). 136 | Then, provide it as 'void* state' to compression function. 137 | */ 138 | int LZ4_sizeofState(void); 139 | int LZ4_compress_fast_extState (void* state, const char* source, char* dest, int inputSize, int maxDestSize, int acceleration); 140 | 141 | 142 | /* 143 | LZ4_compress_destSize() : 144 | Reverse the logic, by compressing as much data as possible from 'source' buffer 145 | into already allocated buffer 'dest' of size 'targetDestSize'. 146 | This function either compresses the entire 'source' content into 'dest' if it's large enough, 147 | or fill 'dest' buffer completely with as much data as possible from 'source'. 148 | *sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'. 149 | New value is necessarily <= old value. 150 | return : Nb bytes written into 'dest' (necessarily <= targetDestSize) 151 | or 0 if compression fails 152 | */ 153 | int LZ4_compress_destSize (const char* source, char* dest, int* sourceSizePtr, int targetDestSize); 154 | 155 | 156 | /* 157 | LZ4_decompress_fast() : 158 | originalSize : is the original and therefore uncompressed size 159 | return : the number of bytes read from the source buffer (in other words, the compressed size) 160 | If the source stream is detected malformed, the function will stop decoding and return a negative result. 161 | Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes. 162 | note : This function fully respect memory boundaries for properly formed compressed data. 163 | It is a bit faster than LZ4_decompress_safe(). 164 | However, it does not provide any protection against intentionally modified data stream (malicious input). 165 | Use this function in trusted environment only (data to decode comes from a trusted source). 166 | */ 167 | int LZ4_decompress_fast (const char* source, char* dest, int originalSize); 168 | 169 | /* 170 | LZ4_decompress_safe_partial() : 171 | This function decompress a compressed block of size 'compressedSize' at position 'source' 172 | into destination buffer 'dest' of size 'maxDecompressedSize'. 173 | The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached, 174 | reducing decompression time. 175 | return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize) 176 | Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller. 177 | Always control how many bytes were decoded. 178 | If the source stream is detected malformed, the function will stop decoding and return a negative result. 179 | This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets 180 | */ 181 | int LZ4_decompress_safe_partial (const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize); 182 | 183 | 184 | /*********************************************** 185 | * Streaming Compression Functions 186 | ***********************************************/ 187 | #define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4) 188 | #define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(long long)) 189 | /* 190 | * LZ4_stream_t 191 | * information structure to track an LZ4 stream. 192 | * important : init this structure content before first use ! 193 | * note : only allocated directly the structure if you are statically linking LZ4 194 | * If you are using liblz4 as a DLL, please use below construction methods instead. 195 | */ 196 | typedef struct { long long table[LZ4_STREAMSIZE_U64]; } LZ4_stream_t; 197 | 198 | /* 199 | * LZ4_resetStream 200 | * Use this function to init an allocated LZ4_stream_t structure 201 | */ 202 | void LZ4_resetStream (LZ4_stream_t* streamPtr); 203 | 204 | /* 205 | * LZ4_createStream will allocate and initialize an LZ4_stream_t structure 206 | * LZ4_freeStream releases its memory. 207 | * In the context of a DLL (liblz4), please use these methods rather than the static struct. 208 | * They are more future proof, in case of a change of LZ4_stream_t size. 209 | */ 210 | LZ4_stream_t* LZ4_createStream(void); 211 | int LZ4_freeStream (LZ4_stream_t* streamPtr); 212 | 213 | /* 214 | * LZ4_loadDict 215 | * Use this function to load a static dictionary into LZ4_stream. 216 | * Any previous data will be forgotten, only 'dictionary' will remain in memory. 217 | * Loading a size of 0 is allowed. 218 | * Return : dictionary size, in bytes (necessarily <= 64 KB) 219 | */ 220 | int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); 221 | 222 | /* 223 | * LZ4_compress_fast_continue 224 | * Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression ratio. 225 | * Important : Previous data blocks are assumed to still be present and unmodified ! 226 | * 'dst' buffer must be already allocated. 227 | * If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. 228 | * If not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero. 229 | */ 230 | int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int maxDstSize, int acceleration); 231 | 232 | /* 233 | * LZ4_saveDict 234 | * If previously compressed data block is not guaranteed to remain available at its memory location 235 | * save it into a safer place (char* safeBuffer) 236 | * Note : you don't need to call LZ4_loadDict() afterwards, 237 | * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue() 238 | * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error 239 | */ 240 | int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dictSize); 241 | 242 | 243 | /************************************************ 244 | * Streaming Decompression Functions 245 | ************************************************/ 246 | 247 | #define LZ4_STREAMDECODESIZE_U64 4 248 | #define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long)) 249 | typedef struct { unsigned long long table[LZ4_STREAMDECODESIZE_U64]; } LZ4_streamDecode_t; 250 | /* 251 | * LZ4_streamDecode_t 252 | * information structure to track an LZ4 stream. 253 | * init this structure content using LZ4_setStreamDecode or memset() before first use ! 254 | * 255 | * In the context of a DLL (liblz4) please prefer usage of construction methods below. 256 | * They are more future proof, in case of a change of LZ4_streamDecode_t size in the future. 257 | * LZ4_createStreamDecode will allocate and initialize an LZ4_streamDecode_t structure 258 | * LZ4_freeStreamDecode releases its memory. 259 | */ 260 | LZ4_streamDecode_t* LZ4_createStreamDecode(void); 261 | int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream); 262 | 263 | /* 264 | * LZ4_setStreamDecode 265 | * Use this function to instruct where to find the dictionary. 266 | * Setting a size of 0 is allowed (same effect as reset). 267 | * Return : 1 if OK, 0 if error 268 | */ 269 | int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize); 270 | 271 | /* 272 | *_continue() : 273 | These decoding functions allow decompression of multiple blocks in "streaming" mode. 274 | Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB) 275 | In the case of a ring buffers, decoding buffer must be either : 276 | - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions) 277 | In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB). 278 | - Larger than encoding buffer, by a minimum of maxBlockSize more bytes. 279 | maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block. 280 | In which case, encoding and decoding buffers do not need to be synchronized, 281 | and encoding ring buffer can have any size, including small ones ( < 64 KB). 282 | - _At least_ 64 KB + 8 bytes + maxBlockSize. 283 | In which case, encoding and decoding buffers do not need to be synchronized, 284 | and encoding ring buffer can have any size, including larger than decoding buffer. 285 | Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer, 286 | and indicate where it is saved using LZ4_setStreamDecode() 287 | */ 288 | int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxDecompressedSize); 289 | int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize); 290 | 291 | 292 | /* 293 | Advanced decoding functions : 294 | *_usingDict() : 295 | These decoding functions work the same as 296 | a combination of LZ4_setStreamDecode() followed by LZ4_decompress_x_continue() 297 | They are stand-alone. They don't need nor update an LZ4_streamDecode_t structure. 298 | */ 299 | int LZ4_decompress_safe_usingDict (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize); 300 | int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalSize, const char* dictStart, int dictSize); 301 | 302 | 303 | 304 | /************************************** 305 | * Obsolete Functions 306 | **************************************/ 307 | /* Deprecate Warnings */ 308 | /* Should these warnings messages be a problem, 309 | it is generally possible to disable them, 310 | with -Wno-deprecated-declarations for gcc 311 | or _CRT_SECURE_NO_WARNINGS in Visual for example. 312 | You can also define LZ4_DEPRECATE_WARNING_DEFBLOCK. */ 313 | #ifndef LZ4_DEPRECATE_WARNING_DEFBLOCK 314 | # define LZ4_DEPRECATE_WARNING_DEFBLOCK 315 | # define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) 316 | # if (LZ4_GCC_VERSION >= 405) || defined(__clang__) 317 | # define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) 318 | # elif (LZ4_GCC_VERSION >= 301) 319 | # define LZ4_DEPRECATED(message) __attribute__((deprecated)) 320 | # elif defined(_MSC_VER) 321 | # define LZ4_DEPRECATED(message) __declspec(deprecated(message)) 322 | # else 323 | # pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler") 324 | # define LZ4_DEPRECATED(message) 325 | # endif 326 | #endif /* LZ4_DEPRECATE_WARNING_DEFBLOCK */ 327 | 328 | /* Obsolete compression functions */ 329 | /* These functions are planned to start generate warnings by r131 approximately */ 330 | int LZ4_compress (const char* source, char* dest, int sourceSize); 331 | int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize); 332 | int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); 333 | int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); 334 | int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); 335 | int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); 336 | 337 | /* Obsolete decompression functions */ 338 | /* These function names are completely deprecated and must no longer be used. 339 | They are only provided here for compatibility with older programs. 340 | - LZ4_uncompress is the same as LZ4_decompress_fast 341 | - LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe 342 | These function prototypes are now disabled; uncomment them only if you really need them. 343 | It is highly recommended to stop using these prototypes and migrate to maintained ones */ 344 | /* int LZ4_uncompress (const char* source, char* dest, int outputSize); */ 345 | /* int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); */ 346 | 347 | /* Obsolete streaming functions; use new streaming interface whenever possible */ 348 | LZ4_DEPRECATED("use LZ4_createStream() instead") void* LZ4_create (char* inputBuffer); 349 | LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void); 350 | LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void* state, char* inputBuffer); 351 | LZ4_DEPRECATED("use LZ4_saveDict() instead") char* LZ4_slideInputBuffer (void* state); 352 | 353 | /* Obsolete streaming decoding functions */ 354 | LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); 355 | LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); 356 | 357 | 358 | #if defined (__cplusplus) 359 | } 360 | #endif 361 | -------------------------------------------------------------------------------- /src/third-party/ply.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Header for PLY polygon files. 4 | 5 | - Greg Turk, March 1994 6 | 7 | A PLY file contains a single polygonal _object_. 8 | 9 | An object is composed of lists of _elements_. Typical elements are 10 | vertices, faces, edges and materials. 11 | 12 | Each type of element for a given object has one or more _properties_ 13 | associated with the element type. For instance, a vertex element may 14 | have as properties three floating-point values x,y,z and three unsigned 15 | chars for red, green and blue. 16 | 17 | --------------------------------------------------------------- 18 | 19 | Copyright (c) 1994 The Board of Trustees of The Leland Stanford 20 | Junior University. All rights reserved. 21 | 22 | Permission to use, copy, modify and distribute this software and its 23 | documentation for any purpose is hereby granted without fee, provided 24 | that the above copyright notice and this permission notice appear in 25 | all copies of this software and that you do not sell the software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND, 28 | EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 29 | WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 30 | 31 | */ 32 | 33 | #ifndef __PLY_H__ 34 | #define __PLY_H__ 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | #include 41 | #include 42 | 43 | #define PLY_ASCII 1 /* ascii PLY file */ 44 | #define PLY_BINARY_BE 2 /* binary PLY file, big endian */ 45 | #define PLY_BINARY_LE 3 /* binary PLY file, little endian */ 46 | 47 | #define PLY_OKAY 0 /* ply routine worked okay */ 48 | #define PLY_ERROR -1 /* error in ply routine */ 49 | 50 | /* scalar data types supported by PLY format */ 51 | 52 | #define PLY_START_TYPE 0 53 | #define PLY_CHAR 1 54 | #define PLY_SHORT 2 55 | #define PLY_INT 3 56 | #define PLY_UCHAR 4 57 | #define PLY_USHORT 5 58 | #define PLY_UINT 6 59 | #define PLY_FLOAT 7 60 | #define PLY_DOUBLE 8 61 | #define PLY_END_TYPE 9 62 | 63 | #define PLY_SCALAR 0 64 | #define PLY_LIST 1 65 | 66 | 67 | typedef struct PlyProperty { /* description of a property */ 68 | 69 | char *name; /* property name */ 70 | int external_type; /* file's data type */ 71 | int internal_type; /* program's data type */ 72 | int offset; /* offset bytes of prop in a struct */ 73 | 74 | int is_list; /* 1 = list, 0 = scalar */ 75 | int count_external; /* file's count type */ 76 | int count_internal; /* program's count type */ 77 | int count_offset; /* offset byte for list count */ 78 | 79 | } PlyProperty; 80 | 81 | typedef struct PlyElement { /* description of an element */ 82 | char *name; /* element name */ 83 | int num; /* number of elements in this object */ 84 | int size; /* size of element (bytes) or -1 if variable */ 85 | int nprops; /* number of properties for this element */ 86 | PlyProperty **props; /* list of properties in the file */ 87 | char *store_prop; /* flags: property wanted by user? */ 88 | int other_offset; /* offset to un-asked-for props, or -1 if none*/ 89 | int other_size; /* size of other_props structure */ 90 | } PlyElement; 91 | 92 | typedef struct PlyOtherProp { /* describes other properties in an element */ 93 | char *name; /* element name */ 94 | int size; /* size of other_props */ 95 | int nprops; /* number of properties in other_props */ 96 | PlyProperty **props; /* list of properties in other_props */ 97 | } PlyOtherProp; 98 | 99 | typedef struct OtherData { /* for storing other_props for an other element */ 100 | void *other_props; 101 | } OtherData; 102 | 103 | typedef struct OtherElem { /* data for one "other" element */ 104 | char *elem_name; /* names of other elements */ 105 | int elem_count; /* count of instances of each element */ 106 | OtherData **other_data; /* actual property data for the elements */ 107 | PlyOtherProp *other_props; /* description of the property data */ 108 | } OtherElem; 109 | 110 | typedef struct PlyOtherElems { /* "other" elements, not interpreted by user */ 111 | int num_elems; /* number of other elements */ 112 | OtherElem *other_list; /* list of data for other elements */ 113 | } PlyOtherElems; 114 | 115 | typedef struct PlyFile { /* description of PLY file */ 116 | FILE *fp; /* file pointer */ 117 | int file_type; /* ascii or binary */ 118 | float version; /* version number of file */ 119 | int nelems; /* number of elements of object */ 120 | PlyElement **elems; /* list of elements */ 121 | int num_comments; /* number of comments */ 122 | char **comments; /* list of comments */ 123 | int num_obj_info; /* number of items of object information */ 124 | char **obj_info; /* list of object info items */ 125 | PlyElement *which_elem; /* which element we're currently writing */ 126 | PlyOtherElems *other_elems; /* "other" elements from a PLY file */ 127 | } PlyFile; 128 | 129 | /* memory allocation */ 130 | extern char *my_alloc(); 131 | #define myalloc(mem_size) my_alloc((mem_size), __LINE__, __FILE__) 132 | 133 | 134 | /*** delcaration of routines ***/ 135 | 136 | extern PlyFile *ply_write(FILE *, int, char **, int); 137 | extern PlyFile *ply_open_for_writing(char *, int, char **, int, float *); 138 | extern void ply_describe_element(PlyFile *, char *, int, int, PlyProperty *); 139 | extern void ply_describe_property(PlyFile *, char *, PlyProperty *); 140 | extern void ply_element_count(PlyFile *, char *, int); 141 | extern void ply_header_complete(PlyFile *); 142 | extern void ply_put_element_setup(PlyFile *, char *); 143 | extern void ply_put_element(PlyFile *, void *); 144 | extern void ply_put_comment(PlyFile *, char *); 145 | extern void ply_put_obj_info(PlyFile *, char *); 146 | extern PlyFile *ply_read(FILE *, int *, char ***); 147 | extern PlyFile *ply_open_for_reading(const char *, int *, char ***, int *, float *); 148 | extern PlyProperty **ply_get_element_description(PlyFile *, const char *, int*, int*); 149 | extern void ply_get_element_setup( PlyFile *, char *, int, PlyProperty *); 150 | extern void ply_get_property(PlyFile *, const char *, PlyProperty *); 151 | extern PlyOtherProp *ply_get_other_properties(PlyFile *, char *, int); 152 | extern void ply_get_element(PlyFile *, void *); 153 | extern char **ply_get_comments(PlyFile *, int *); 154 | extern char **ply_get_obj_info(PlyFile *, int *); 155 | extern void ply_close(PlyFile *); 156 | extern void ply_get_info(PlyFile *, float *, int *); 157 | extern PlyOtherElems *ply_get_other_element (PlyFile *, char *, int); 158 | extern void ply_describe_other_elements ( PlyFile *, PlyOtherElems *); 159 | extern void ply_put_other_elements (PlyFile *); 160 | extern void ply_free_other_elements (PlyOtherElems *); 161 | 162 | extern int equal_strings(const char *, const char *); 163 | 164 | 165 | #ifdef __cplusplus 166 | } 167 | #endif 168 | #endif /* !__PLY_H__ */ 169 | 170 | -------------------------------------------------------------------------------- /src/third-party/tribox3.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tunabrain/sparse-voxel-octrees/c4c2a9d3015056c269c73430f223e69e3b933b83/src/third-party/tribox3.c -------------------------------------------------------------------------------- /src/third-party/tribox3.h: -------------------------------------------------------------------------------- 1 | #ifndef TRIBOX3_H_ 2 | #define TRIBOX3_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | int triBoxOverlap(float boxcenter[3],float boxhalfsize[3],float triverts[3][3]); 9 | 10 | #ifdef __cplusplus 11 | } 12 | #endif 13 | 14 | #endif /* TRIBOX3_H_ */ 15 | -------------------------------------------------------------------------------- /src/thread/TaskGroup.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef TASKGROUP_HPP_ 25 | #define TASKGROUP_HPP_ 26 | 27 | #include "IntTypes.hpp" 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | class TaskGroup 37 | { 38 | typedef std::function TaskFunc; 39 | typedef std::function Finisher; 40 | 41 | TaskFunc _func; 42 | Finisher _finisher; 43 | 44 | std::exception_ptr _exceptionPtr; 45 | std::atomic _startedSubTasks; 46 | std::atomic _finishedSubTasks; 47 | uint32 _numSubTasks; 48 | 49 | std::mutex _waitMutex; 50 | std::condition_variable _waitCond; 51 | std::atomic _done, _abort; 52 | 53 | void finish() 54 | { 55 | if (_finisher && !_abort) 56 | _finisher(); 57 | 58 | _done = true; 59 | _waitCond.notify_all(); 60 | } 61 | 62 | public: 63 | TaskGroup(TaskFunc func, Finisher finisher, uint32 numSubTasks) 64 | : _func(std::move(func)), 65 | _finisher(std::move(finisher)), 66 | _startedSubTasks(0), 67 | _finishedSubTasks(0), 68 | _numSubTasks(numSubTasks), 69 | _done(false), 70 | _abort(false) 71 | { 72 | } 73 | 74 | void run(uint32 threadId, uint32 taskId) 75 | { 76 | try { 77 | _func(taskId, _numSubTasks, threadId); 78 | } catch (...) { 79 | _exceptionPtr = std::current_exception(); 80 | } 81 | 82 | uint32 num = ++_finishedSubTasks; 83 | if (num == _numSubTasks || (_abort && num == _startedSubTasks)) 84 | finish(); 85 | } 86 | 87 | void wait() 88 | { 89 | std::unique_lock lock(_waitMutex); 90 | _waitCond.wait(lock, [this]{return _done == true;}); 91 | 92 | if (_exceptionPtr) 93 | std::rethrow_exception(_exceptionPtr); 94 | } 95 | 96 | void abort() 97 | { 98 | _abort = true; 99 | _done = _done || _startedSubTasks == 0; 100 | } 101 | 102 | bool isAborting() const 103 | { 104 | return _abort; 105 | } 106 | 107 | bool isDone() const 108 | { 109 | return _done; 110 | } 111 | 112 | uint32 startSubTask() 113 | { 114 | return _startedSubTasks++; 115 | } 116 | 117 | uint32 numSubTasks() const 118 | { 119 | return _numSubTasks; 120 | } 121 | }; 122 | 123 | #endif /* TASKGROUP_HPP_ */ 124 | -------------------------------------------------------------------------------- /src/thread/ThreadPool.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include "ThreadPool.hpp" 25 | 26 | #include 27 | 28 | ThreadPool::ThreadPool(uint32 threadCount) 29 | : _threadCount(threadCount), 30 | _terminateFlag(false) 31 | { 32 | startThreads(); 33 | } 34 | 35 | ThreadPool::~ThreadPool() 36 | { 37 | stop(); 38 | } 39 | 40 | std::shared_ptr ThreadPool::acquireTask(uint32 &subTaskId) 41 | { 42 | if (_terminateFlag) 43 | return nullptr; 44 | std::shared_ptr task = _tasks.front(); 45 | if (task->isAborting()) { 46 | _tasks.pop_front(); 47 | return nullptr; 48 | } 49 | subTaskId = task->startSubTask(); 50 | if (subTaskId == task->numSubTasks() - 1) 51 | _tasks.pop_front(); 52 | return std::move(task); 53 | } 54 | 55 | void ThreadPool::runWorker(uint32 threadId) 56 | { 57 | while (!_terminateFlag) { 58 | uint32 subTaskId; 59 | std::shared_ptr task; 60 | { 61 | std::unique_lock lock(_taskMutex); 62 | _taskCond.wait(lock, [this]{return _terminateFlag || !_tasks.empty();}); 63 | task = acquireTask(subTaskId); 64 | } 65 | if (task) 66 | task->run(threadId, subTaskId); 67 | } 68 | } 69 | 70 | void ThreadPool::startThreads() 71 | { 72 | _terminateFlag = false; 73 | for (uint32 i = 0; i < _threadCount; ++i) { 74 | _workers.emplace_back(new std::thread(&ThreadPool::runWorker, this, uint32(_workers.size()))); 75 | _idToNumericId.insert(std::make_pair(_workers.back()->get_id(), i)); 76 | } 77 | } 78 | 79 | void ThreadPool::yield(TaskGroup &wait) 80 | { 81 | std::chrono::milliseconds waitSpan(10); 82 | uint32 id = _threadCount; // Threads not in the pool get a previously unassigned id 83 | 84 | auto iter = _idToNumericId.find(std::this_thread::get_id()); 85 | if (iter != _idToNumericId.end()) 86 | id = iter->second; 87 | 88 | while (!wait.isDone() && !_terminateFlag) { 89 | uint32 subTaskId; 90 | std::shared_ptr task; 91 | { 92 | std::unique_lock lock(_taskMutex); 93 | if (!_taskCond.wait_for(lock, waitSpan, [this]{return _terminateFlag || !_tasks.empty();})) 94 | continue; 95 | task = acquireTask(subTaskId); 96 | } 97 | if (task) 98 | task->run(id, subTaskId); 99 | } 100 | } 101 | 102 | void ThreadPool::reset() 103 | { 104 | stop(); 105 | _tasks.clear(); 106 | startThreads(); 107 | } 108 | 109 | void ThreadPool::stop() 110 | { 111 | _terminateFlag = true; 112 | { 113 | std::unique_lock lock(_taskMutex); 114 | _taskCond.notify_all(); 115 | } 116 | while (!_workers.empty()) { 117 | _workers.back()->detach(); 118 | _workers.pop_back(); 119 | } 120 | } 121 | 122 | std::shared_ptr ThreadPool::enqueue(TaskFunc func, int numSubtasks, Finisher finisher) 123 | { 124 | std::shared_ptr task(std::make_shared(std::move(func), 125 | std::move(finisher), numSubtasks)); 126 | 127 | { 128 | std::unique_lock lock(_taskMutex); 129 | _tasks.emplace_back(task); 130 | if (numSubtasks == 1) 131 | _taskCond.notify_one(); 132 | else 133 | _taskCond.notify_all(); 134 | } 135 | 136 | return std::move(task); 137 | } 138 | -------------------------------------------------------------------------------- /src/thread/ThreadPool.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef THREADPOOL_HPP_ 25 | #define THREADPOOL_HPP_ 26 | 27 | #include "TaskGroup.hpp" 28 | 29 | #include "IntTypes.hpp" 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | class ThreadPool 42 | { 43 | typedef std::function TaskFunc; 44 | typedef std::function Finisher; 45 | 46 | uint32 _threadCount; 47 | std::vector> _workers; 48 | std::atomic _terminateFlag; 49 | 50 | std::deque> _tasks; 51 | std::mutex _taskMutex; 52 | std::condition_variable _taskCond; 53 | 54 | std::unordered_map _idToNumericId; 55 | 56 | std::shared_ptr acquireTask(uint32 &subTaskId); 57 | void runWorker(uint32 threadId); 58 | void startThreads(); 59 | 60 | public: 61 | ThreadPool(uint32 threadCount); 62 | ~ThreadPool(); 63 | 64 | void yield(TaskGroup &wait); 65 | 66 | void reset(); 67 | void stop(); 68 | 69 | std::shared_ptr enqueue(TaskFunc func, int numSubtasks = 1, 70 | Finisher finisher = Finisher()); 71 | 72 | uint32 threadCount() const 73 | { 74 | return _threadCount; 75 | } 76 | }; 77 | 78 | #endif /* THREADPOOL_HPP_ */ 79 | -------------------------------------------------------------------------------- /src/thread/ThreadUtils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #include "ThreadPool.hpp" 25 | 26 | #include 27 | #if _WIN32 28 | #include 29 | #else 30 | #include 31 | #endif 32 | #include 33 | 34 | namespace ThreadUtils { 35 | 36 | ThreadPool *pool = nullptr; 37 | 38 | uint32 idealThreadCount() 39 | { 40 | // std::thread::hardware_concurrency support is not great, so let's try 41 | // native APIs first 42 | #if _WIN32 43 | SYSTEM_INFO info; 44 | GetSystemInfo(&info); 45 | if (info.dwNumberOfProcessors > 0) 46 | return info.dwNumberOfProcessors; 47 | #else 48 | int nprocs = sysconf(_SC_NPROCESSORS_ONLN); 49 | if (nprocs > 0) 50 | return nprocs; 51 | #endif 52 | unsigned n = std::thread::hardware_concurrency(); 53 | if (n > 0) 54 | return n; 55 | 56 | // All attempts failed. Let's take a guess 57 | return 4; 58 | } 59 | 60 | void startThreads(int numThreads) 61 | { 62 | pool = new ThreadPool(numThreads); 63 | } 64 | 65 | void parallelFor(uint32 start, uint32 end, uint32 partitions, std::function func) 66 | { 67 | auto taskRun = [&func, start, end](uint32 idx, uint32 num, uint32 /*threadId*/) 68 | { 69 | uint32 span = (end - start + num - 1)/num; 70 | uint32 iStart = start + span*idx; 71 | uint32 iEnd = std::min(iStart + span, end); 72 | for (uint32 i = iStart; i < iEnd; ++i) 73 | func(i); 74 | }; 75 | if (partitions == 1) 76 | taskRun(0, 1, 0); 77 | else 78 | pool->yield(*pool->enqueue(taskRun, partitions)); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/thread/ThreadUtils.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Benedikt Bitterli 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source 21 | distribution. 22 | */ 23 | 24 | #ifndef THREADUTILS_HPP_ 25 | #define THREADUTILS_HPP_ 26 | 27 | #include "IntTypes.hpp" 28 | 29 | #include 30 | 31 | class ThreadPool; 32 | 33 | namespace ThreadUtils { 34 | 35 | extern ThreadPool *pool; 36 | 37 | uint32 idealThreadCount(); 38 | void startThreads(int numThreads); 39 | 40 | void parallelFor(uint32 start, uint32 end, uint32 partitions, std::function func); 41 | 42 | } 43 | 44 | #endif /* THREADUTILS_HPP_ */ 45 | --------------------------------------------------------------------------------