├── .gitattributes ├── .gitignore ├── CMakeLists.txt ├── README.md ├── msvc ├── octree2svo.vcxproj ├── octree2svo.vcxproj.filters ├── svo_convert.vcxproj ├── svo_convert.vcxproj.filters └── svo_tools.sln └── src ├── octree2svo ├── DataPoint.h ├── Node.h ├── build_linux.sh ├── file_tools.h ├── main.cpp └── octree_io.h └── svo_convert ├── DataPoint.h ├── Node.h ├── OctreeBuilder.cpp ├── OctreeBuilder.h ├── build_svo_convert.sh ├── geo_primitives.h ├── intersection.h ├── main.cpp ├── mesh_operations.h ├── morton.h ├── octree2svo.h ├── svo_convert.h ├── svo_convert_util.h ├── voxelization.cpp └── voxelization.h /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 19 | !packages/*/build/ 20 | 21 | # MSTest test Results 22 | [Tt]est[Rr]esult*/ 23 | [Bb]uild[Ll]og.* 24 | 25 | *_i.c 26 | *_p.c 27 | *.ilk 28 | *.meta 29 | *.obj 30 | *.pch 31 | *.pdb 32 | *.pgc 33 | *.pgd 34 | *.rsp 35 | *.sbr 36 | *.tlb 37 | *.tli 38 | *.tlh 39 | *.tmp 40 | *.tmp_proj 41 | *.log 42 | *.vspscc 43 | *.vssscc 44 | .builds 45 | *.pidb 46 | *.log 47 | *.scc 48 | 49 | # Visual C++ cache files 50 | ipch/ 51 | *.aps 52 | *.ncb 53 | *.opensdf 54 | *.sdf 55 | *.cachefile 56 | 57 | # Visual Studio profiler 58 | *.psess 59 | *.vsp 60 | *.vspx 61 | 62 | # Guidance Automation Toolkit 63 | *.gpState 64 | 65 | # ReSharper is a .NET coding add-in 66 | _ReSharper*/ 67 | *.[Rr]e[Ss]harper 68 | 69 | # TeamCity is a build add-in 70 | _TeamCity* 71 | 72 | # DotCover is a Code Coverage Tool 73 | *.dotCover 74 | 75 | # NCrunch 76 | *.ncrunch* 77 | .*crunch*.local.xml 78 | 79 | # Installshield output folder 80 | [Ee]xpress/ 81 | 82 | # DocProject is a documentation generator add-in 83 | DocProject/buildhelp/ 84 | DocProject/Help/*.HxT 85 | DocProject/Help/*.HxC 86 | DocProject/Help/*.hhc 87 | DocProject/Help/*.hhk 88 | DocProject/Help/*.hhp 89 | DocProject/Help/Html2 90 | DocProject/Help/html 91 | 92 | # Click-Once directory 93 | publish/ 94 | 95 | # Publish Web Output 96 | *.Publish.xml 97 | 98 | # NuGet Packages Directory 99 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 100 | #packages/ 101 | 102 | # Windows Azure Build Output 103 | csx 104 | *.build.csdef 105 | 106 | # Windows Store app package directory 107 | AppPackages/ 108 | 109 | # Others 110 | sql/ 111 | *.Cache 112 | ClientBin/ 113 | [Ss]tyle[Cc]op.* 114 | ~$* 115 | *~ 116 | *.dbmdl 117 | *.[Pp]ublish.xml 118 | *.pfx 119 | *.publishsettings 120 | 121 | # RIA/Silverlight projects 122 | Generated_Code/ 123 | 124 | # Backup & report files from converting an old project file to a newer 125 | # Visual Studio version. Backup files are not needed, because we have git ;-) 126 | _UpgradeReport_Files/ 127 | Backup*/ 128 | UpgradeLog*.XML 129 | UpgradeLog*.htm 130 | 131 | # SQL Server files 132 | App_Data/*.mdf 133 | App_Data/*.ldf 134 | 135 | 136 | #LightSwitch generated files 137 | GeneratedArtifacts/ 138 | _Pvt_Extensions/ 139 | ModelManifest.xml 140 | 141 | # ========================= 142 | # Windows detritus 143 | # ========================= 144 | 145 | # Windows image file caches 146 | Thumbs.db 147 | ehthumbs.db 148 | 149 | # Folder config file 150 | Desktop.ini 151 | 152 | # Recycle Bin used on file shares 153 | $RECYCLE.BIN/ 154 | 155 | # Mac desktop service store files 156 | .DS_Store 157 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8) 2 | 3 | PROJECT ( svo_tools ) 4 | 5 | add_custom_target(svo_tools SOURCES 6 | ./src/svo_tools/svo_convert/DataPoint.h 7 | ./src/svo_tools/svo_convert/geo_primitives.h 8 | ./src/svo_tools/svo_convert/intersection.h 9 | ./src/svo_tools/svo_convert/Node.h 10 | ./src/svo_tools/svo_convert/morton.h 11 | ./src/svo_tools/svo_convert/mesh_operations.h 12 | ./src/svo_tools/svo_convert/octree2svo.h 13 | ./src/svo_tools/svo_convert/svo_convert_util.h) 14 | 15 | IF (NOT APPLE) 16 | FIND_PACKAGE ( OpenMP REQUIRED ) 17 | ENDIF (NOT APPLE) 18 | 19 | IF (MSVC) 20 | ADD_DEFINITIONS ( -openmp ) 21 | ELSEIF (NOT APPLE) 22 | # Linux uses GCC 23 | ADD_DEFINITIONS ( -fopenmp ) 24 | ENDIF (MSVC) 25 | 26 | INCLUDE_DIRECTORIES ( 27 | $ENV{TRIMESH2_ROOT}/include 28 | ) 29 | 30 | IF (APPLE) 31 | LINK_DIRECTORIES ( 32 | $ENV{TRIMESH2_ROOT}/lib.Darwin64 33 | ) 34 | ELSEIF (UNIX) 35 | LINK_DIRECTORIES ( 36 | $ENV{TRIMESH2_ROOT}/lib.Linux64 37 | ) 38 | ENDIF (APPLE) 39 | 40 | ADD_EXECUTABLE ( svo_convert 41 | ./src/svo_tools/svo_convert/main.cpp 42 | ./src/svo_tools/svo_convert/OctreeBuilder.cpp 43 | ./src/svo_tools/svo_convert/voxelization.cpp 44 | ) 45 | 46 | IF (NOT APPLE) 47 | TARGET_LINK_LIBRARIES ( svo_convert 48 | trimesh 49 | gomp 50 | ) 51 | ELSE() 52 | TARGET_LINK_LIBRARIES ( svo_convert 53 | trimesh 54 | ) 55 | ENDIF (NOT APPLE) 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # svo_tools 2 | This repository contains tools for working with the SVO file format, the format used in the alpha-stage virtual world developed by Hifi (**[Hifi repo](https://github.com/worklist/hifi "Hifi repo")**). 3 | 4 | Currently, this repository contains: 5 | * **svo_convert**, a command-line tool to convert any model file (.ply, .3ds, ...) to an SVO structure, through voxelization and SVO building. 6 | * **octree2svo**, a command-line tool to convert octrees generated using my **[Out-of-core Sparse Voxel Octree Builder](https://github.com/Forceflow/ooc_svo_builder "ooc_svo_builder github repo")** to .svo files. 7 | 8 | All methods are based on **[Out-of-core Sparse Voxel Octree Builder](https://github.com/Forceflow/ooc_svo_builder "ooc_svo_builder github repo")**. 9 | Still very much in active development. 10 | 11 | ## Binaries 12 | I put up some precompiled binaries for Windows, Linux and OSX on the **[releases page](https://github.com/Forceflow/svo_tools/releases)** 13 | 14 | # svo_convert 15 | 16 | ## svo_convert Building 17 | **The only dependencies** for building is **[TriMesh2](http://gfx.cs.princeton.edu/proj/trimesh2/)** (used for model file I/O). The Trimesh2 distribution comes with pre-built binaries for all platforms, so just go ahead and **[download](http://gfx.cs.princeton.edu/proj/trimesh2/)** it, and unzip it to a location you remember. 18 | OpenMP support is optional, and is disabled for OSX. 19 | 20 | ### Windows 21 | Build using *src/svo_tools/svo_tools.sln* VS2012 on Win 64-bit. 32-bit building is not encouraged, but should work. You can grab a free version of Visual Studio Express **[here](http://www.microsoft.com/visualstudio/eng/downloads)**. The TriMesh2 libraries are built against MinGW, so when you're using the VS compiler, you have to rebuild the TriMesh2 library. This can be easily done using the MSVC project **[here](http://gfx.cs.princeton.edu/proj/trimesh2/src/trimesh2-2.11-MSVC.zip)**. 22 | 23 | ### Linux 24 | Build using gcc and cmake. Make sure you specify the environment variable TRIMESH2_ROOT. 25 | 26 | A typical compile on Linux would go like this: 27 |
 28 | git clone https://github.com/Forceflow/svo_tools
 29 | export TRIMESH2_ROOT=/home/jeroen/development/trimesh2/
 30 | cd svo_tools
 31 | cmake .
 32 | make
 33 | 
34 | 35 | ### OSX 36 | We'll be using some tools which are available in the Apple-provided *Command Line Tools for Xcode*, which can be downloaded from within Xcode (like described **[here](http://stackoverflow.com/questions/9353444/how-to-use-install-gcc-on-mac-os-x-10-8-xcode-4-4 here)**) or as a **[stand-alone download](https://developer.apple.com/downloads/)**. 37 | 38 | #### Compiling TriMesh2 with clang 39 | In order to be able to use TriMesh2 with the default Apple clang compiler, we have to recompile the GCC-built binaries that came with it. 40 | To do this, we have to disable OpenMP support, because clang doesn't have it. 41 | 42 | The following steps are required: 43 | * Download the latest TriMesh2 zip from the **[TriMesh2 page](http://gfx.cs.princeton.edu/proj/trimesh2/)** and unzip it. 44 | * Navigate to your TriMesh2 folder 45 | * Open the file *MakeDefs.Darwin64* and remove the *-fopenmp* switch from *ARCHOPTS* 46 | * In the TriMesh2 folder, run *make* 47 | 48 | This should recompile TriMesh2 with clang. If you cannot complete the steps in this paragraph, I've prepared a clang-compiled TriMesh2 version for download **[here](http://www.forceflow.be/temp/trimesh2-2.12-clang_version.zip)**. 49 | 50 | #### Compiling 51 | The only thing left to do is to set the TRIMESH2_ROOT environment variable to point to your local TriMesh2 folder. 52 | 53 | A typical compile on OSX would go like this: 54 |
 55 | git clone https://github.com/Forceflow/svo_tools
 56 | export TRIMESH2_ROOT=/Users/jeroen/development/trimesh2/
 57 | cd svo_tools
 58 | cmake .
 59 | make
 60 | 
61 | 62 | svo_convert Usage 63 | ----------------- 64 |
 65 | svo_convert -f /path/to/file.ply -s (gridsize) -c (color mode)
 66 | 
67 | 68 | Full option list: 69 | * **-f** */path/to/file.ply* : Path to model file. Currently the file formats supported are those by TriMesh2: .ply, .3ds, .off, .obj. 70 | * **-s** *value* : Gridsize - only powers of 2 up to 1024 are supported: (1,2,4,8,16,32,64,128,256,512,1024). 71 | * **-c** *colormode* : Colormode -Options : 72 | * *model* : Tries to grab colors from vertices. If the mesh has no colored vertices, it falls back to fixed color. 73 | * *fixed* : All voxels get a fixed color (white). 74 | * *normal* : Voxels get colored according to the normals of the mesh. 75 | 76 | svo_convert Examples 77 | -------------------- 78 | 79 |
 80 | svo_convert -f /home/jeroen/models/bunny.ply -s 512
 81 | 
82 | Will generate a file named bunny.svo in the same folder, with an SVO of gridsize 512x512x512. It will use the default color mode, trying to fetch colors from the vertices, and failing that, will color the model white. 83 | 84 |
 85 | svo_convert -f /home/jeroen/models/horse.ply -s 256 -c normal
 86 | 
87 | Will generate a file named horse.svo in the same folder, with an SVO of gridsize 256x256x256. It will use the normal-based color mode, in which each voxel gets colored according to the mesh face normal. 88 | 89 | #octree2svo 90 | 91 | octree2svo Building 92 | ------------------- 93 | 94 | * **Windows** : Build using VS2012 on Win 64-bit. 32-bit building is not encouraged. 95 | * **Linux** : Build using build_linux.sh. 96 | 97 | **Dependencies** for building are **[Trimesh2](http://gfx.cs.princeton.edu/proj/trimesh2/)** and OpenMP support. You can specify the location of the Trimesh2 library in both the VS solution file and the linux build script. 98 | 99 | octree2svo Usage 100 | ---------------- 101 | 102 | * **octree2svo** -f /path/to/file.octree 103 | 104 | Full option list: 105 | * **-f** */path/to/file.octree* : Path to *.octree* file. The *.octreenodes* and *.octreedata* files should be in the same folder. 106 | * **-c** *color_mode* : (color_mode can be: *fixed*, *from_octree*) Specify the mode in which voxels should be colored. Default is a fixed white color, but you can also force octree2svo to use the colors embedded in the *.octree* file format by using switch *from_octree*. 107 | * **-h** : Print help and exit 108 | 109 | octree2svo Examples 110 | ------------------- 111 | 112 | So the full pipeline (including tools from [ooc_svo_builder](https://github.com/Forceflow/ooc_svo_builder)) to get from a model file in .ply, obj or .3ds format to an .svo file might look like this if you want a bunny with fixed colors: 113 | 114 | * **tri_convert_binary** -f *bunny.ply* 115 | * **svo_builder_binary** -f *bunny.tri* -s *1024* 116 | * **octree2svo** -f *bunny1024_1.octree* 117 | 118 | And if you want a bunny with voxel colors from the sampled normals: 119 | 120 | * **tri_convert** -f *bunny.ply* 121 | * **svo_builder** -f *bunny.tri* -s *1024* -c *normal* 122 | * **octree2svo** -f *bunny1024_1.octree* -c *from_octree* 123 | 124 | Each of these tools has more configuration options, please refer to the documentation. 125 | -------------------------------------------------------------------------------- /msvc/octree2svo.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug Binary 6 | Win32 7 | 8 | 9 | Debug Binary 10 | x64 11 | 12 | 13 | Debug 14 | Win32 15 | 16 | 17 | Debug 18 | x64 19 | 20 | 21 | Release Binary 22 | Win32 23 | 24 | 25 | Release Binary 26 | x64 27 | 28 | 29 | Release 30 | Win32 31 | 32 | 33 | Release 34 | x64 35 | 36 | 37 | 38 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7} 39 | octree2svo 40 | 41 | 42 | 43 | Application 44 | true 45 | v120 46 | MultiByte 47 | 48 | 49 | Application 50 | true 51 | v120 52 | MultiByte 53 | 54 | 55 | Application 56 | true 57 | v120 58 | MultiByte 59 | 60 | 61 | Application 62 | true 63 | v120 64 | MultiByte 65 | 66 | 67 | Application 68 | false 69 | v120 70 | true 71 | MultiByte 72 | 73 | 74 | Application 75 | false 76 | v120 77 | true 78 | MultiByte 79 | 80 | 81 | Application 82 | false 83 | v120 84 | true 85 | MultiByte 86 | 87 | 88 | Application 89 | false 90 | v120 91 | true 92 | MultiByte 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | C:\libs\trimesh2\include;$(IncludePath) 124 | 125 | 126 | C:\libs\trimesh2\include;$(IncludePath) 127 | 128 | 129 | C:\libs\trimesh2_new\include;$(IncludePath) 130 | 131 | 132 | C:\libs\trimesh2\include;$(IncludePath) 133 | 134 | 135 | 136 | Level3 137 | Disabled 138 | true 139 | 140 | 141 | true 142 | 143 | 144 | 145 | 146 | Level3 147 | Disabled 148 | true 149 | 150 | 151 | true 152 | 153 | 154 | 155 | 156 | Level3 157 | Disabled 158 | true 159 | 160 | 161 | true 162 | 163 | 164 | 165 | 166 | Level3 167 | Disabled 168 | true 169 | 170 | 171 | true 172 | 173 | 174 | 175 | 176 | Level3 177 | MaxSpeed 178 | true 179 | true 180 | true 181 | 182 | 183 | true 184 | true 185 | true 186 | 187 | 188 | 189 | 190 | Level3 191 | MaxSpeed 192 | true 193 | true 194 | true 195 | 196 | 197 | true 198 | true 199 | true 200 | 201 | 202 | 203 | 204 | Level3 205 | MaxSpeed 206 | true 207 | true 208 | true 209 | 210 | 211 | true 212 | true 213 | true 214 | 215 | 216 | 217 | 218 | Level3 219 | MaxSpeed 220 | true 221 | true 222 | true 223 | 224 | 225 | true 226 | true 227 | true 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /msvc/octree2svo.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | 23 | 24 | Header Files 25 | 26 | 27 | Header Files 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | -------------------------------------------------------------------------------- /msvc/svo_convert.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4} 23 | svo_convert 24 | 25 | 26 | 27 | Application 28 | true 29 | v120 30 | MultiByte 31 | 32 | 33 | Application 34 | true 35 | v120 36 | MultiByte 37 | 38 | 39 | Application 40 | false 41 | v120 42 | true 43 | MultiByte 44 | 45 | 46 | Application 47 | false 48 | v120 49 | true 50 | MultiByte 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | C:\libs\trimesh2\include;$(IncludePath) 70 | C:\libs\trimesh2\lib.Win64;$(LibraryPath) 71 | 72 | 73 | C:\libs\trimesh2\include;$(IncludePath) 74 | C:\libs\trimesh2_new\lib.Win64;$(VCInstallDir)lib\amd64;$(VCInstallDir)atlmfc\lib\amd64;$(WindowsSDK_LibraryPath_x64); 75 | 76 | 77 | C:\libs\trimesh2\include;$(IncludePath) 78 | 79 | 80 | C:\libs\trimesh2\include;$(IncludePath) 81 | 82 | 83 | 84 | Level3 85 | Disabled 86 | true 87 | 88 | 89 | true 90 | 91 | 92 | 93 | 94 | Level3 95 | Disabled 96 | true 97 | 98 | 99 | true 100 | trimeshd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 101 | 102 | 103 | 104 | 105 | Level3 106 | MaxSpeed 107 | true 108 | true 109 | true 110 | 111 | 112 | true 113 | true 114 | true 115 | 116 | 117 | 118 | 119 | Level3 120 | MaxSpeed 121 | true 122 | true 123 | true 124 | 125 | 126 | true 127 | true 128 | true 129 | trimesh.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /msvc/svo_convert.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {ff2b33e8-0338-4212-b618-26bff1420861} 18 | 19 | 20 | {853acc85-c366-4d48-8ecf-79d9640d64d4} 21 | 22 | 23 | {983c7bf5-43f8-4d4a-ba6f-9b5946b97019} 24 | 25 | 26 | {75865de6-1c12-419b-a6bb-dcf5297223e8} 27 | 28 | 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | 41 | 42 | Header Files\voxelization 43 | 44 | 45 | Header Files\data_structures 46 | 47 | 48 | Header Files\data_structures 49 | 50 | 51 | Header Files\util 52 | 53 | 54 | Header Files\util 55 | 56 | 57 | Header Files\util 58 | 59 | 60 | Header Files\data_structures 61 | 62 | 63 | Header Files\octree_builder 64 | 65 | 66 | Header Files\util 67 | 68 | 69 | Header Files 70 | 71 | 72 | Header Files 73 | 74 | 75 | -------------------------------------------------------------------------------- /msvc/svo_tools.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "svo_convert", "svo_convert.vcxproj", "{5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "octree2svo", "octree2svo.vcxproj", "{C5BB7196-5B24-4E24-8A08-138B3AF188C7}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug Binary|Win32 = Debug Binary|Win32 13 | Debug Binary|x64 = Debug Binary|x64 14 | Debug|Win32 = Debug|Win32 15 | Debug|x64 = Debug|x64 16 | Release Binary|Win32 = Release Binary|Win32 17 | Release Binary|x64 = Release Binary|x64 18 | Release|Win32 = Release|Win32 19 | Release|x64 = Release|x64 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}.Debug Binary|Win32.ActiveCfg = Debug|Win32 23 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}.Debug Binary|Win32.Build.0 = Debug|Win32 24 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}.Debug Binary|x64.ActiveCfg = Debug|x64 25 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}.Debug Binary|x64.Build.0 = Debug|x64 26 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}.Debug|Win32.ActiveCfg = Debug|Win32 27 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}.Debug|Win32.Build.0 = Debug|Win32 28 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}.Debug|x64.ActiveCfg = Debug|x64 29 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}.Debug|x64.Build.0 = Debug|x64 30 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}.Release Binary|Win32.ActiveCfg = Release|Win32 31 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}.Release Binary|Win32.Build.0 = Release|Win32 32 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}.Release Binary|x64.ActiveCfg = Release|x64 33 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}.Release Binary|x64.Build.0 = Release|x64 34 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}.Release|Win32.ActiveCfg = Release|Win32 35 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}.Release|Win32.Build.0 = Release|Win32 36 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}.Release|x64.ActiveCfg = Release|x64 37 | {5E5DE3B0-2CAC-47CE-8E27-D75D4537C4A4}.Release|x64.Build.0 = Release|x64 38 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7}.Debug Binary|Win32.ActiveCfg = Debug Binary|Win32 39 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7}.Debug Binary|Win32.Build.0 = Debug Binary|Win32 40 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7}.Debug Binary|x64.ActiveCfg = Debug Binary|x64 41 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7}.Debug Binary|x64.Build.0 = Debug Binary|x64 42 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7}.Debug|Win32.ActiveCfg = Debug|Win32 43 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7}.Debug|Win32.Build.0 = Debug|Win32 44 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7}.Debug|x64.ActiveCfg = Debug|x64 45 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7}.Debug|x64.Build.0 = Debug|x64 46 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7}.Release Binary|Win32.ActiveCfg = Release Binary|Win32 47 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7}.Release Binary|Win32.Build.0 = Release Binary|Win32 48 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7}.Release Binary|x64.ActiveCfg = Release Binary|x64 49 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7}.Release Binary|x64.Build.0 = Release Binary|x64 50 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7}.Release|Win32.ActiveCfg = Release|Win32 51 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7}.Release|Win32.Build.0 = Release|Win32 52 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7}.Release|x64.ActiveCfg = Release|x64 53 | {C5BB7196-5B24-4E24-8A08-138B3AF188C7}.Release|x64.Build.0 = Release|x64 54 | EndGlobalSection 55 | GlobalSection(SolutionProperties) = preSolution 56 | HideSolutionNode = FALSE 57 | EndGlobalSection 58 | EndGlobal 59 | -------------------------------------------------------------------------------- /src/octree2svo/DataPoint.h: -------------------------------------------------------------------------------- 1 | #ifndef DATAPOINT_H_ 2 | #define DATAPOINT_H_ 3 | 4 | #include 5 | 6 | using namespace trimesh; 7 | 8 | // Class representing a voxel payload 9 | class DataPoint { 10 | public: 11 | float opacity; 12 | vec3 color; 13 | vec3 normal; 14 | 15 | DataPoint(); 16 | DataPoint(float opacity, vec3 color); 17 | DataPoint(float opacity, vec3 color, vec3 normal); 18 | bool isEmpty() const; 19 | }; 20 | 21 | 22 | inline DataPoint::DataPoint() : opacity(0.0f), color(vec3(0,0,0)), normal(vec3(0.0f,0.0f,0.0f)){ 23 | } 24 | 25 | inline DataPoint::DataPoint(float opacity, vec3 color) : opacity(opacity), color(color){ 26 | } 27 | 28 | inline DataPoint::DataPoint(float opacity, vec3 color, vec3 normal) : opacity(opacity), color(color), normal(normal){ 29 | } 30 | 31 | inline bool DataPoint::isEmpty() const{ 32 | return (opacity == 0.0f); 33 | } 34 | 35 | #endif // DATAPOINT_H_ -------------------------------------------------------------------------------- /src/octree2svo/Node.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_H_ 2 | #define NODE_H_ 3 | 4 | #pragma once 5 | 6 | #include "DataPoint.h" 7 | 8 | #define NOCHILD -1 9 | #define NODATA 0 10 | 11 | // An SVO node. Only contains child pointers, extend this if you want parent pointers as well 12 | class Node 13 | { 14 | public: 15 | size_t data; 16 | size_t children_base; 17 | char children_offset[8]; 18 | 19 | DataPoint data_cache; // only if you want to refine octree (clustering) 20 | 21 | Node(); 22 | bool hasChild(unsigned int i) const; 23 | size_t getChildPos(unsigned int i) const; 24 | bool isLeaf() const; 25 | bool hasData() const; 26 | bool isNull() const; 27 | }; 28 | 29 | // Default constructor 30 | inline Node::Node() : data(0), children_base(0), data_cache(DataPoint()){ 31 | for(unsigned int i = 0; i<8; i++){ 32 | children_offset[i] = NOCHILD; 33 | } 34 | } 35 | 36 | // Check if this Node has a child at position i 37 | inline bool Node::hasChild(unsigned int i) const{ 38 | return !(children_offset[i] == NOCHILD); 39 | } 40 | 41 | // Get the full index of the child at position i 42 | inline size_t Node::getChildPos(unsigned int i) const{ 43 | if(children_offset[i] == NOCHILD){ 44 | return 0; 45 | } else { 46 | return children_base + children_offset[i]; 47 | } 48 | } 49 | 50 | // If this node doesn't have data and is a leaf node, it's a null node 51 | inline bool Node::isNull() const{ 52 | return isLeaf() && !hasData(); 53 | } 54 | 55 | // If this node doesn;t have any children, it's a leaf node 56 | inline bool Node::isLeaf() const{ 57 | for(unsigned int i = 0; i<8; i++){ 58 | if(children_offset[i] != NOCHILD){ 59 | return false; 60 | } 61 | } 62 | return true; 63 | } 64 | 65 | // If the data pointer is NODATA, there is no data 66 | inline bool Node::hasData() const{ 67 | return !(data == NODATA); 68 | } 69 | 70 | #endif /* NODE_H_ */ 71 | -------------------------------------------------------------------------------- /src/octree2svo/build_linux.sh: -------------------------------------------------------------------------------- 1 | 2 | #!/bin/bash 3 | 4 | ## SPECIFY TRIMESH LOCATION HERE (and do a make there first) 5 | TRIMESH_DIR=/home/jeroen/development/trimesh2 6 | 7 | ## COMPILE AND LINK DEFINITIONS 8 | COMPILE="g++ -c -O3 -g -std=c++11 -I ${TRIMESH_DIR}/include/" 9 | LINK="g++ -o octree2svo" 10 | 11 | ############################################################################################# 12 | ## BUILDING STARTS HERE 13 | 14 | ## CLEAN 15 | echo "Removing old versions ..." 16 | rm *.o 17 | rm octree2svo 18 | 19 | ## BUILD BINARY VOXELIZATION VERSION 20 | echo "Compiling..." 21 | ${COMPILE} main.cpp 22 | echo "Linking binary voxelization build..." 23 | ${LINK} *.o 24 | 25 | echo "Cleaning up ..." 26 | rm *.o 27 | 28 | echo "Done" 29 | -------------------------------------------------------------------------------- /src/octree2svo/file_tools.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_TOOLS_H_ 2 | #define FILE_TOOLS_H_ 3 | 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | // Various file operations, implemented in standard C 10 | 11 | // Copy files from src to dst 12 | inline void copy_file(const std::string& src, const std::string& dst){ 13 | char buf[8192]; 14 | size_t size; 15 | FILE* source = fopen(src.c_str(), "rb"); 16 | FILE* dest = fopen(dst.c_str(), "wb"); 17 | while (size = fread(buf, 1, BUFSIZ, source)) { 18 | fwrite(buf, 1, size, dest); 19 | } 20 | fclose(source); 21 | fclose(dest); 22 | } 23 | 24 | // Check if a file exists 25 | inline bool file_exists(const std::string& name) { 26 | if (FILE *file = fopen(name.c_str(), "r")) { 27 | fclose(file); 28 | return true; 29 | } else { 30 | return false; 31 | } 32 | } 33 | 34 | #endif -------------------------------------------------------------------------------- /src/octree2svo/main.cpp: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_DEPRECATE // so yeah, no fopen_s (it's not portable) 2 | #include 3 | #include 4 | #include 5 | #include "octree_io.h" 6 | #include "Node.h" 7 | #include "DataPoint.h" 8 | 9 | #if _WIN32 || _WIN64 10 | #if _WIN64 11 | #define ENVIRONMENT64 12 | #else 13 | #define ENVIRONMENT32 14 | #endif 15 | #endif 16 | 17 | using namespace std; 18 | using namespace trimesh; 19 | 20 | enum ColorMode {COLOR_FIXED, COLOR_FROMOCTREE}; 21 | 22 | // Program version 23 | string version = "1.0"; 24 | 25 | // Program parameters 26 | string filename = ""; 27 | ColorMode color_mode = COLOR_FIXED; // by default, just use a fixed color 28 | ivec3 fixed_color = ivec3(255,255,255); 29 | 30 | // Program data 31 | Node* nodes; 32 | DataPoint* data; 33 | FILE* svo_out; 34 | 35 | void printInfo() { 36 | cout << "--------------------------------------------------------------------" << endl; 37 | cout << "octree2svo v" << version << endl; 38 | #ifdef _WIN32 || _WIN64 39 | cout << "Windows "; 40 | #endif 41 | #ifdef __linux__ 42 | cout << "Linux "; 43 | #endif 44 | #ifdef ENVIRONMENT64 45 | cout << "64-bit version" << endl; 46 | #endif 47 | #ifdef ENVIRONMENT32 48 | cout << "32-bit version" << endl; 49 | #endif 50 | cout << "Jeroen Baert - jeroen.baert@cs.kuleuven.be - www.forceflow.be" << endl; 51 | cout << "--------------------------------------------------------------------" << endl << endl; 52 | } 53 | 54 | void printHelp() { 55 | std::cout << "Example: octree2svo -f /home/jeroen/bunny.octree" << endl; 56 | std::cout << "" << endl; 57 | std::cout << "All available program options:" << endl; 58 | std::cout << "" << endl; 59 | std::cout << "-f Path to a .octree input file." << endl; 60 | std::cout << "-c Color mode. Options: fixed (default), from_octree" << endl; 61 | std::cout << "-h Print help and exit." << endl; 62 | } 63 | 64 | void printInvalid() { 65 | std::cout << "Not enough or invalid arguments, please try again." << endl; 66 | std::cout << "At the bare minimum, I need a path to an .octree file" << endl<< "" << endl; 67 | printHelp(); 68 | } 69 | 70 | void parseParameters(int argc, char* argv[], string& filename){ 71 | cout << "Reading program parameters ..." << endl; 72 | // Input argument validation 73 | if(argc < 3){printInvalid(); exit(0);} 74 | for (int i = 1; i < argc; i++) { 75 | // parse filename 76 | if (string(argv[i]) == "-f") { 77 | filename = argv[i + 1]; 78 | size_t check_tri = filename.find(".octree"); 79 | if(check_tri == string::npos){ 80 | cout << "Data filename does not end in .trip - I only support that file format" << endl; printInvalid();exit(0); 81 | } 82 | i++; 83 | } else if (string(argv[i]) == "-c") { 84 | string color_input = string(argv[i+1]); 85 | if(color_input == "fixed") { 86 | color_mode = COLOR_FIXED; 87 | } else if(color_input == "from_octree") { 88 | color_mode = COLOR_FROMOCTREE; 89 | } else { 90 | cout << "Unrecognized color switch: " << color_input << ", so reverting to fixed color." << endl; 91 | } 92 | i++; 93 | } else if (string(argv[i]) == "-h") { 94 | printHelp(); exit(0); 95 | } else { 96 | printInvalid(); exit(0); 97 | } 98 | } 99 | cout << " Filename: " << filename << endl; 100 | } 101 | 102 | void parseNode(const Node &node){ 103 | // create bitsets we'll write out 104 | bitset<8> child_tree_exists; 105 | bitset<8> child_colored; 106 | 107 | child_tree_exists.reset(); 108 | child_colored.reset(); 109 | 110 | for(int i = 0; i < 8; i++) { 111 | if(node.hasChild(7-i)){ // if this node has a child at position i 112 | if(! nodes[node.getChildPos(7-i)].isLeaf()){ // if it is not a leaf, there's a tree 113 | child_tree_exists.set(i, true); 114 | } else { 115 | child_colored.set(i, true); // it's a leaf child: color it 116 | } 117 | } 118 | } 119 | 120 | // write these to file: convert bitset to unsigned char to write to file 121 | unsigned char child_tree_exists_byte = (unsigned char) child_tree_exists.to_ulong(); 122 | unsigned char child_colored_byte = (unsigned char) child_colored.to_ulong(); 123 | 124 | // write child_colored byte 125 | fwrite(& child_colored_byte, sizeof(unsigned char), 1, svo_out); 126 | 127 | // write color for each child (in RGB) 128 | for(int i = 7; i >= 0; i--){ // we write them left to right in the byte order 129 | if(child_colored[i]){ 130 | unsigned char r = (unsigned char) fixed_color[0]; 131 | unsigned char g = (unsigned char) fixed_color[1]; 132 | unsigned char b = (unsigned char) fixed_color[2]; 133 | if(color_mode == COLOR_FROMOCTREE){ 134 | DataPoint dpoint = data[nodes[node.getChildPos(7-i)].data]; // fetch datapoint 135 | r = (unsigned char) (dpoint.color[0] * 255.0f); 136 | g = (unsigned char) (dpoint.color[1] * 255.0f); 137 | b = (unsigned char) (dpoint.color[2] * 255.0f); 138 | } 139 | fwrite(& r, sizeof(unsigned char), 1, svo_out); 140 | fwrite(& g, sizeof(unsigned char), 1, svo_out); 141 | fwrite(& b, sizeof(unsigned char), 1, svo_out); 142 | } 143 | } 144 | 145 | // write tree exists bytes 146 | fwrite(& child_tree_exists_byte, sizeof(unsigned char), 1, svo_out); 147 | 148 | // start writing child nodes (recursively) 149 | for(int i = 7; i >= 0; i--) { // we write them left to right in the byte order 150 | if(child_tree_exists[i]){ 151 | parseNode(nodes[node.getChildPos(7-i)]); 152 | } 153 | } 154 | } 155 | 156 | int main(int argc, char *argv[]){ 157 | printInfo(); 158 | 159 | // Parse program parameters 160 | parseParameters(argc,argv,filename); 161 | 162 | // Get octree info 163 | OctreeInfo tree_info; 164 | parseOctreeHeader(filename,tree_info); 165 | tree_info.print(); 166 | /*if(!tree_info.filesExist()){ 167 | cout << "Not all required octree files (.octree, .octreenodes, .octreedata) exist. Regenerate them with svo_builder." << endl; 168 | exit(0); 169 | }*/ 170 | 171 | // allocate memory for nodes and read them 172 | cout << "Allocating " << (tree_info.n_nodes * sizeof(Node))/1024/1024 << " Mb for Nodes" << endl; 173 | nodes = new Node[tree_info.n_nodes]; 174 | string nodes_file = tree_info.base_filename + string(".octreenodes"); 175 | FILE* f_nodes = fopen(nodes_file.c_str(), "rb"); 176 | for(size_t i = 0; i < tree_info.n_nodes; i++){ 177 | Node node = Node(); 178 | readNode(f_nodes,node); 179 | nodes[i] = node; 180 | } 181 | fclose(f_nodes); 182 | 183 | // allocate memory for data points and read them 184 | cout << "Allocating " << (tree_info.n_data * sizeof(DataPoint))/1024/1024 << " Mb for Data" << endl; 185 | data = new DataPoint[tree_info.n_data]; 186 | string data_file = tree_info.base_filename + string(".octreedata"); 187 | FILE* f_data = fopen(data_file.c_str(), "rb"); 188 | for(size_t i = 0; i < tree_info.n_data; i++){ 189 | DataPoint datapoint = DataPoint(); 190 | readDataPoint(f_data,datapoint); 191 | data[i] = datapoint; 192 | } 193 | fclose(f_data); 194 | 195 | // open output file 196 | string svo_file = tree_info.base_filename + string(".svo"); 197 | svo_out = fopen(svo_file.c_str(), "wb"); 198 | 199 | // Write octal code for root node (all zero) 200 | unsigned char zero = 0; 201 | fwrite(& zero, sizeof(unsigned char), 1, svo_out); 202 | 203 | // open root node and start parsing recursively 204 | parseNode(nodes[tree_info.n_nodes-1]); // parse root node 205 | fclose(svo_out); 206 | } 207 | -------------------------------------------------------------------------------- /src/octree2svo/octree_io.h: -------------------------------------------------------------------------------- 1 | #ifndef OCTREE_IO_H_ 2 | #define OCTREE_IO_H_ 3 | 4 | #include 5 | #include 6 | #include "Node.h" 7 | #include "DataPoint.h" 8 | 9 | using namespace std; 10 | 11 | #define DATAPOINT_SIZE 7 12 | 13 | // File containing all the octree IO methods 14 | 15 | // Internal format to represent an octree 16 | struct OctreeInfo { 17 | int version; 18 | string base_filename; 19 | size_t gridlength; 20 | size_t n_nodes; 21 | size_t n_data; 22 | 23 | OctreeInfo() : version(1), base_filename(string("")), gridlength(1024), n_nodes(0), n_data(0) {} 24 | OctreeInfo(int version, string base_filename, size_t gridlength, size_t n_nodes, size_t n_data) : version(version), base_filename(base_filename), gridlength(gridlength), n_nodes(n_nodes), n_data(n_data) {} 25 | 26 | void print() const{ 27 | cout << " version: " << version << endl; 28 | cout << " base_filename: " << base_filename << endl; 29 | cout << " grid length: " << gridlength << endl; 30 | cout << " n_nodes: " << n_nodes << endl; 31 | cout << " n_data: " << n_data << endl; 32 | } 33 | }; 34 | 35 | size_t writeDataPoint(FILE* data_out, const DataPoint &d, size_t &b_data_pos); 36 | void readDataPoint(FILE* f, DataPoint &d); 37 | size_t writeNode(FILE* node_out, const Node &n, size_t &b_node_pos); 38 | inline void readNode(FILE* f, Node &n); 39 | 40 | void writeOctreeHeader(const std::string &filename, const OctreeInfo &i); 41 | int parseOctreeHeader(const std::string &filename, OctreeInfo &i); 42 | 43 | // Write a data point to file 44 | inline size_t writeDataPoint(FILE* data_out, const DataPoint &d, size_t &b_data_pos){ 45 | fwrite(& d.opacity, sizeof(float), DATAPOINT_SIZE, data_out); 46 | //fwrite(& d.color[0], sizeof(float), 3, data_out); 47 | //fwrite(& d.normal[0], sizeof(float), 3, data_out); 48 | b_data_pos++; 49 | return b_data_pos-1; 50 | } 51 | 52 | // Read a data point from a file 53 | inline void readDataPoint(FILE* f, DataPoint &d){ 54 | fread(& d.opacity, sizeof(float), DATAPOINT_SIZE, f); 55 | //fread(& d.color[0], sizeof(float), 3, f); 56 | //fread(& d.normal[0], sizeof(float), 3, f); 57 | } 58 | 59 | // Write an octree node to file 60 | inline size_t writeNode(FILE* node_out, const Node &n, size_t &b_node_pos){ 61 | fwrite(& n.data, sizeof(size_t), 3, node_out); 62 | //fwrite(& n.children_base, sizeof(size_t), 1, node_out); 63 | //fwrite(& n.children_offset[0], sizeof(char), 8, node_out); 64 | b_node_pos++; 65 | return b_node_pos-1; 66 | } 67 | 68 | // Read a Node from a file 69 | inline void readNode(FILE* f, Node &n){ 70 | fread(& n.data, sizeof(size_t), 3, f); 71 | //fread(& n.children_base, sizeof(size_t), 1, f); 72 | //fread(& n.children_offset[0], sizeof(char), 8, f); 73 | } 74 | 75 | // Write an octree header to a file 76 | inline void writeOctreeHeader(const std::string &filename, const OctreeInfo &i){ 77 | ofstream outfile; 78 | outfile.open(filename.c_str(), ios::out); 79 | outfile << "#octreeheader 1" << endl; 80 | outfile << "gridlength " << i.gridlength << endl; 81 | outfile << "n_nodes " << i.n_nodes << endl; 82 | outfile << "n_data " << i.n_data << endl; 83 | outfile << "END" << endl; 84 | outfile.close(); 85 | } 86 | 87 | // Parse a given octree header, store info in OctreeInfo struct 88 | inline int parseOctreeHeader(const std::string &filename, OctreeInfo &i){ 89 | cout << " reading octree header from " << filename << " ... " << endl; 90 | ifstream headerfile; 91 | headerfile.open(filename.c_str(), ios::in); 92 | 93 | i.base_filename = filename.substr(0,filename.find_last_of(".")); 94 | 95 | string line; bool done = false; 96 | headerfile >> line >> i.version; 97 | if (line.compare("#octreeheader") != 0) {cout << " Error: first line reads [" << line << "] instead of [#octreeheader]" << endl; return 0;} 98 | 99 | while(headerfile.good() && !done) { 100 | headerfile >> line; 101 | if (line.compare("END") == 0) done = true; // when we encounter data keyword, we're at the end of the ASCII header 102 | else if (line.compare("gridlength") == 0) {headerfile >> i.gridlength;} 103 | else if (line.compare("n_nodes") == 0) {headerfile >> i.n_nodes;} 104 | else if (line.compare("n_data") == 0) {headerfile >> i.n_data;} 105 | else { cout << " unrecognized keyword [" << line << "], skipping" << endl; 106 | char c; do { c = headerfile.get(); } while(headerfile.good() && (c != '\n')); 107 | } 108 | } 109 | 110 | headerfile.close(); 111 | return 1; 112 | } 113 | 114 | #endif -------------------------------------------------------------------------------- /src/svo_convert/DataPoint.h: -------------------------------------------------------------------------------- 1 | #ifndef DATAPOINT_H_ 2 | #define DATAPOINT_H_ 3 | 4 | #include 5 | 6 | using namespace trimesh; 7 | 8 | // Class representing a voxel payload 9 | class DataPoint { 10 | public: 11 | float opacity; 12 | vec3 color; 13 | vec3 normal; 14 | 15 | DataPoint(); 16 | DataPoint(float opacity, vec3 color); 17 | DataPoint(float opacity, vec3 color, vec3 normal); 18 | bool isEmpty() const; 19 | }; 20 | 21 | 22 | inline DataPoint::DataPoint() : opacity(0.0f), color(vec3(0,0,0)), normal(vec3(0.0f,0.0f,0.0f)){ 23 | } 24 | 25 | inline DataPoint::DataPoint(float opacity, vec3 color) : opacity(opacity), color(color){ 26 | } 27 | 28 | inline DataPoint::DataPoint(float opacity, vec3 color, vec3 normal) : opacity(opacity), color(color), normal(normal){ 29 | } 30 | 31 | inline bool DataPoint::isEmpty() const{ 32 | return (opacity == 0.0f); 33 | } 34 | 35 | #endif // DATAPOINT_H_ -------------------------------------------------------------------------------- /src/svo_convert/Node.h: -------------------------------------------------------------------------------- 1 | #ifndef NODE_H_ 2 | #define NODE_H_ 3 | 4 | #pragma once 5 | 6 | #include "DataPoint.h" 7 | 8 | #define NOCHILD -1 9 | #define NODATA 0 10 | 11 | // This is how an array of a leaf node will look 12 | const char LEAF[] {NOCHILD, NOCHILD, NOCHILD, NOCHILD, NOCHILD, NOCHILD, NOCHILD, NOCHILD}; 13 | 14 | // An SVO node. Only contains child pointers, extend this if you want parent pointers as well 15 | class Node 16 | { 17 | public: 18 | size_t data; 19 | size_t children_base; 20 | char children_offset[8]; 21 | 22 | DataPoint data_cache; // only if you want to refine octree (clustering) 23 | 24 | Node(); 25 | bool hasChild(unsigned int i) const; 26 | size_t getChildPos(unsigned int i) const; 27 | bool isLeaf() const; 28 | bool hasData() const; 29 | bool isNull() const; 30 | }; 31 | 32 | // Default constructor 33 | inline Node::Node() : data(0), children_base(0), data_cache(DataPoint()){ 34 | memset(children_offset, (char)NOCHILD, 8); 35 | } 36 | 37 | // Check if this Node has a child at position i 38 | inline bool Node::hasChild(unsigned int i) const{ 39 | return !(children_offset[i] == NOCHILD); 40 | } 41 | 42 | // Get the full index of the child at position i 43 | inline size_t Node::getChildPos(unsigned int i) const{ 44 | if (children_offset[i] == NOCHILD){ 45 | return 0; 46 | } 47 | else { 48 | return children_base + children_offset[i]; 49 | } 50 | } 51 | 52 | // If this node doesn't have data and is a leaf node, it's a null node 53 | inline bool Node::isNull() const{ 54 | return isLeaf() && !hasData(); 55 | } 56 | 57 | // If this node doesn;t have any children, it's a leaf node 58 | inline bool Node::isLeaf() const{ 59 | if (memcmp(children_offset, LEAF, 8 * sizeof(char)) == 0){ 60 | return true; 61 | } 62 | return false; 63 | } 64 | 65 | // If the data pointer is NODATA, there is no data 66 | inline bool Node::hasData() const{ 67 | return !(data == NODATA); 68 | } 69 | 70 | #endif /* NODE_H_ */ 71 | -------------------------------------------------------------------------------- /src/svo_convert/OctreeBuilder.cpp: -------------------------------------------------------------------------------- 1 | #include "OctreeBuilder.h" 2 | 3 | OctreeBuilder::OctreeBuilder(size_t gridlength, bool generate_levels) : 4 | gridlength(gridlength), b_node_pos(0), b_data_pos(0), b_current_morton(0), generate_levels(generate_levels) { 5 | // Setup building variables 6 | b_maxdepth = log2((unsigned int) gridlength); 7 | b_buffers.resize(b_maxdepth+1); 8 | for(int i = 0; i < b_maxdepth+1; i++){b_buffers[i].reserve(8);} 9 | b_max_morton = mortonEncode_LUT(int(gridlength-1),int(gridlength-1),int(gridlength-1)); 10 | // Push back first data point (which is always NULL) 11 | octree_data.push_back(DataPoint()); 12 | } 13 | 14 | // Finalize the tree: add rest of empty nodes, make sure root node is on top 15 | void OctreeBuilder::finalizeTree(){ 16 | // fill octree 17 | if(b_current_morton < b_max_morton){ 18 | fastAddEmpty((b_max_morton - b_current_morton)+1); 19 | } 20 | // write root node 21 | octree_nodes.push_back(b_buffers[0][0]); 22 | } 23 | 24 | // Group 8 nodes, write non-empty nodes to disk and create parent node 25 | Node OctreeBuilder::groupNodes(const vector &buffer){ 26 | Node parent = Node(); 27 | bool first_stored_child = true; 28 | for(int k = 0; k<8; k++){ 29 | if(!buffer[k].isNull()){ 30 | if(first_stored_child){ 31 | octree_nodes.push_back(buffer[k]); 32 | parent.children_base = octree_nodes.size()-1; 33 | parent.children_offset[k] = 0; 34 | first_stored_child = false; 35 | } else { 36 | octree_nodes.push_back(buffer[k]); 37 | parent.children_offset[k] = (char) ((octree_nodes.size() - 1) - parent.children_base); 38 | } 39 | } else { 40 | parent.children_offset[k] = NOCHILD; 41 | } 42 | } 43 | 44 | // SIMPLE LEVEL CONSTRUCTION 45 | if(generate_levels){ 46 | DataPoint d = DataPoint(); 47 | float notnull = 0.0f; 48 | for(int i = 0; i < 8; i++){ // this node has no data: need to refine 49 | if(!buffer[i].isNull()) 50 | notnull++; 51 | d.opacity += buffer[i].data_cache.opacity; 52 | d.color += buffer[i].data_cache.color; 53 | d.normal += buffer[i].data_cache.normal; 54 | } 55 | d.color = d.color / notnull; 56 | vec3 tonormalize = (vec3) (d.normal / notnull); 57 | d.normal = normalize(tonormalize); 58 | d.opacity = d.opacity / notnull; 59 | // set it in the parent node 60 | octree_data.push_back(d); 61 | parent.data = octree_data.size()-1; 62 | parent.data_cache = d; 63 | } 64 | return parent; 65 | } 66 | 67 | // Add an empty datapoint at a certain buffer level, and refine upwards from there 68 | void OctreeBuilder::addEmptyDataPoint(const int buffer){ 69 | b_buffers[buffer].push_back(Node()); 70 | // REFINE BUFFERS: check from touched buffer, upwards 71 | for(int d = buffer; d >= 0; d--){ 72 | if(b_buffers[d].size() == 8){ // if we have 8 nodes 73 | assert(d-1 >= 0); 74 | if(isBufferEmpty(b_buffers[d])){ 75 | b_buffers[d-1].push_back(Node()); // push back NULL node to represent 8 empty nodes 76 | } else { 77 | b_buffers[d-1].push_back(groupNodes(b_buffers[d])); // push back parent node 78 | } 79 | b_buffers.at(d).clear(); // clear the 8 nodes on this level 80 | } else { 81 | break; // break the for loop: no upper levels will need changing 82 | } 83 | } 84 | b_current_morton = (uint64_t) (b_current_morton + pow(8.0, b_maxdepth - buffer)); // because we're adding at a certain level 85 | } 86 | 87 | // Add a datapoint to the octree: this is the main method used to push datapoints 88 | void OctreeBuilder::addDataPoint(const uint64_t morton_number, const DataPoint& data_point){ 89 | // PADDING FOR MISSED MORTON NUMBERS 90 | if(morton_number != b_current_morton){ 91 | fastAddEmpty(morton_number - b_current_morton); 92 | } 93 | 94 | // ADD NODE TO BUFFER 95 | Node node = Node(); // create empty node 96 | if(!data_point.isEmpty()) { 97 | octree_data.push_back(data_point); 98 | node.data = octree_data.size()-1; 99 | node.data_cache = data_point; // store data as cache 100 | } 101 | b_buffers.at(b_maxdepth).push_back(node); 102 | 103 | // REFINE BUFFERS: check all levels (bottom up) and group 8 nodes on a higher level 104 | for(int d = b_maxdepth; d >= 0; d--){ 105 | if(b_buffers[d].size() == 8){ // if we have 8 nodes 106 | if(isBufferEmpty(b_buffers[d])){ 107 | b_buffers[d-1].push_back(Node()); // push back NULL node to represent 8 empty nodes 108 | } else { 109 | b_buffers[d-1].push_back(groupNodes(b_buffers[d])); // push back parent node 110 | } 111 | b_buffers.at(d).clear(); // clear the 8 nodes on this level 112 | } else { 113 | break; // break the for loop: no upper levels will need changing 114 | } 115 | } 116 | 117 | // INCREASE CURRENT MORTON NUMBER 118 | b_current_morton++; 119 | } -------------------------------------------------------------------------------- /src/svo_convert/OctreeBuilder.h: -------------------------------------------------------------------------------- 1 | #ifndef OCTREE_BUILDER_H_ 2 | #define OCTREE_BUILDER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "morton.h" 9 | #include "Node.h" 10 | #include "svo_convert_util.h" 11 | #include "voxelization.h" 12 | 13 | using namespace std; 14 | 15 | // Octreebuilder class. You pass this class DataPoints, it builds an octree from them. 16 | class OctreeBuilder { 17 | public: 18 | vector< vector< Node > > b_buffers; 19 | size_t gridlength; 20 | int b_maxdepth; // maximum octree depth 21 | uint64_t b_current_morton; // current morton position 22 | uint64_t b_max_morton; // maximum morton position 23 | size_t b_data_pos; // current output data position (array index) 24 | size_t b_node_pos; // current output node position (array index) 25 | 26 | vector octree_nodes; 27 | vector octree_data; 28 | 29 | // configuration 30 | bool generate_levels; // switch to enable basic generation of higher octree levels 31 | 32 | OctreeBuilder(size_t gridlength, bool generate_levels); 33 | void buildOctree(char* voxels, vector& data); 34 | 35 | private: 36 | void addDataPoint(const uint64_t morton_number, const DataPoint& point); 37 | void finalizeTree(); 38 | bool isBufferEmpty(const vector &buffer); 39 | Node groupNodes(const vector &buffer); 40 | void addEmptyDataPoint(const int buffer); 41 | int highestNonEmptyBuffer(); 42 | int computeBestFillBuffer(const size_t budget); 43 | void fastAddEmpty(const size_t budget); 44 | }; 45 | 46 | // Read voxels and data from arrays and build the SVO 47 | inline void OctreeBuilder::buildOctree(char* voxels, vector& data){ 48 | sort(data.begin(), data.end()); // sort voxel data 49 | for (std::vector::iterator it = data.begin(); it != data.end(); ++it){ 50 | DataPoint d = DataPoint(); 51 | d.opacity = 1.0; 52 | d.normal = it->normal; 53 | d.color = it->color; 54 | addDataPoint(it->morton, d); 55 | } 56 | finalizeTree(); 57 | } 58 | 59 | // Check if a buffer contains non-empty nodes 60 | inline bool OctreeBuilder::isBufferEmpty(const vector &buffer){ 61 | for(int k = 0; k<8; k++){ 62 | if(!buffer[k].isNull()){ 63 | return false; 64 | } 65 | } 66 | return true; 67 | } 68 | 69 | // Find the highest non empty buffer, return its index 70 | inline int OctreeBuilder::highestNonEmptyBuffer(){ 71 | int highest_found = b_maxdepth; // highest means "lower in buffer id" here. 72 | for(int k = b_maxdepth; k>=0; k--){ 73 | if(b_buffers[k].size() == 0){ // this buffer level is empty 74 | highest_found--; 75 | } else { // this buffer level is nonempty: break 76 | return highest_found; 77 | } 78 | } 79 | return highest_found; 80 | } 81 | 82 | // Compute the best fill buffer given the budget 83 | inline int OctreeBuilder::computeBestFillBuffer(const size_t budget){ 84 | // which power of 8 fits in budget? 85 | int budget_buffer_suggestion = b_maxdepth-findPowerOf8(budget); 86 | // if our current guess is already b_maxdepth, return that, no need to test further 87 | if(budget_buffer_suggestion == b_maxdepth){return b_maxdepth;} 88 | // best fill buffer is maximum of suggestion and highest non_empty buffer 89 | return max(budget_buffer_suggestion, highestNonEmptyBuffer()); 90 | } 91 | 92 | // A method to quickly add empty nodes 93 | inline void OctreeBuilder::fastAddEmpty(const size_t budget){ 94 | size_t r_budget = budget; 95 | while (r_budget > 0){ 96 | int buffer = computeBestFillBuffer(r_budget); 97 | addEmptyDataPoint(buffer); 98 | size_t budget_hit = (size_t) pow(8.0,b_maxdepth-buffer); 99 | r_budget = r_budget - budget_hit; 100 | } 101 | } 102 | 103 | #endif // OCTREE_BUILDER_H_ -------------------------------------------------------------------------------- /src/svo_convert/build_svo_convert.sh: -------------------------------------------------------------------------------- 1 | ## SPECIFY TRIMESH LOCATION HERE (and do a make there first) 2 | TRIMESH_DIR=/home/jeroen/development/trimesh2 3 | 4 | ## COMPILE AND LINK DEFINITIONS 5 | COMPILE="g++ -c -O3 -I ${TRIMESH_DIR}/include/" 6 | LINK="g++ -o svo_convert" 7 | LINK_OPTS="-L${TRIMESH_DIR}/lib.Linux64 -ltrimesh -fopenmp -static" 8 | 9 | ############################################################################################# 10 | 11 | ## CLEAN 12 | echo "Cleaning old versions ..." 13 | rm *.o 14 | rm svo_convert 15 | rm a.out 16 | 17 | ## BUILD BINARY VOXELIZATION VERSION 18 | echo "Building..." 19 | ${COMPILE} main.cpp 20 | ${LINK} main.o ${LINK_OPTS} 21 | 22 | echo "Done" 23 | -------------------------------------------------------------------------------- /src/svo_convert/geo_primitives.h: -------------------------------------------------------------------------------- 1 | #ifndef GEO_PRIMITIVES_H_ 2 | #define GEO_PRIMITIVES_H_ 3 | 4 | template 5 | struct AABox { 6 | T min; 7 | T max; 8 | AABox(): min(T()), max(T()){} 9 | AABox(T min, T max): min(min), max(max){} 10 | }; 11 | 12 | #endif -------------------------------------------------------------------------------- /src/svo_convert/intersection.h: -------------------------------------------------------------------------------- 1 | #ifndef INTERSECTION_H_ 2 | #define INTERSECTION_H_ 3 | 4 | #include 5 | #include "geo_primitives.h" 6 | 7 | using namespace trimesh; 8 | using namespace std; 9 | 10 | // Intersection methods 11 | inline AABox computeBoundingBox(const vec3 &v0, const vec3 &v1, const vec3 &v2){ 12 | AABox answer = AABox(vec3(0.0f,0.0f,0.0f), vec3(0.0f,0.0f,0.0f)); 13 | answer.min[0] = min(v0[0],min(v1[0],v2[0])); 14 | answer.min[1] = min(v0[1],min(v1[1],v2[1])); 15 | answer.min[2] = min(v0[2],min(v1[2],v2[2])); 16 | answer.max[0] = max(v0[0],max(v1[0],v2[0])); 17 | answer.max[1] = max(v0[1],max(v1[1],v2[1])); 18 | answer.max[2] = max(v0[2],max(v1[2],v2[2])); 19 | return answer; 20 | } 21 | 22 | template 23 | inline bool intersectBoxBox(const AABox &a, const AABox &b){ 24 | if(a.max[0] < b.min[0]) {return false;} 25 | if(a.min[0] > b.max[0]) {return false;} 26 | if(a.max[1] < b.min[1]) {return false;} 27 | if(a.min[1] > b.max[1]) {return false;} 28 | if(a.max[2] < b.min[2]) {return false;} 29 | if(a.min[2] > b.max[2]) {return false;} 30 | return true; // intersection or inside 31 | } 32 | 33 | #endif /*INTERSECTION_H_*/ -------------------------------------------------------------------------------- /src/svo_convert/main.cpp: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_DEPRECATE // so yeah, no fopen_s (it's not portable) 2 | 3 | #include "svo_convert.h" 4 | 5 | using namespace std; 6 | using namespace trimesh; 7 | 8 | #if _WIN32 || _WIN64 9 | #if _WIN64 10 | #define ENVIRONMENT64 11 | #else 12 | #define ENVIRONMENT32 13 | #endif 14 | #endif 15 | 16 | // (GLOBAL) Program version 17 | string version = "1.2"; 18 | 19 | // (GLOBAL) Program parameters 20 | string filename = ""; 21 | size_t gridsize = 512; 22 | bool verbose = true; 23 | ColorMode color_mode = COLOR_FROM_MODEL; 24 | vec3 FIXED_COLOR = vec3(1.0f, 1.0f, 1.0f); 25 | 26 | void printInfo(){ 27 | cout << "-------------------------------------------------------------" << endl; 28 | cout << "Model to .SVO converter " << version << endl; 29 | #ifdef _WIN32 || _WIN64 30 | cout << "Windows version" << endl; 31 | #endif 32 | #ifdef __LINUX__ 33 | cout << "Linux version" << endl; 34 | #endif 35 | #ifdef __APPLE__ 36 | cout << "OSX version" << endl; 37 | #endif 38 | #ifdef ENVIRONMENT64 39 | cout << "64-bit version" << endl; 40 | #endif 41 | #ifdef ENVIRONMENT32 42 | cout << "32-bit version" << endl; 43 | #endif 44 | cout << "Jeroen Baert - jeroen.baert@cs.kuleuven.be - www.forceflow.be" << endl; 45 | cout << "-------------------------------------------------------------" << endl << endl; 46 | } 47 | 48 | void printHelp(){ 49 | cout << "Available program parameters:" << endl; 50 | cout << " -f (filename) : Path to model file (.obj, .ply, .3ds, .ray)" << endl; 51 | cout << " -s (gridsize) : Grid size, should be power of 2 (1,2,4,8,16,32,64,128,256,512 or 1024)" << endl; 52 | cout << " -c