├── .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 : Coloring of voxels (Options: model (default), fixed, normal)" << endl;
53 | cout << "Example: " << endl;
54 | cout << " svo_convert -f bunny.ply -s 256 -c normal" << endl;
55 | }
56 |
57 | void printInvalid(){
58 | std::cout << "Not enough or invalid arguments, please try again.\n" << endl;
59 | printHelp();
60 | }
61 |
62 | void parseProgramParameters(int argc, char* argv[]){
63 | string color_mode_s = "Color from model (fallback to fixed color if model has no color)";
64 | // Input argument validation
65 | if(argc<3){ // not enough arguments
66 | printInvalid(); exit(0);
67 | }
68 | for (int i = 1; i < argc; i++) {
69 | if (string(argv[i]) == "-f") {
70 | filename = argv[i + 1];
71 | i++;
72 | } else if (string(argv[i]) == "-s") {
73 | gridsize = atoi(argv[i + 1]);
74 | if (!isPowerOf2(gridsize)) {
75 | cout << "Requested gridsize is not a power of 2" << endl; printInvalid(); exit(0);
76 | }
77 | i++;
78 | } else if (string(argv[i]) == "-c") {
79 | string color_input = string(argv[i + 1]);
80 | if (color_input == "model") {
81 | color_mode = COLOR_FROM_MODEL; // default
82 | } else if (color_input == "normal") {
83 | color_mode = COLOR_NORMAL;
84 | color_mode_s = "From face normals";
85 | } else if (color_input == "fixed") {
86 | color_mode = COLOR_FIXED;
87 | color_mode_s = "Fixed";
88 | } else {
89 | cout << "Unrecognized color switch: " << color_input << ", so defaulting to colors from model." << endl;
90 | }
91 | i++;
92 | } else {
93 | printInvalid(); exit(0);
94 | }
95 | }
96 | if (verbose){
97 | cout << " filename: " << filename << endl;
98 | cout << " gridsize: " << gridsize << endl;
99 | cout << " color mode: " << color_mode_s << endl;
100 | }
101 | }
102 |
103 | int main(int argc, char *argv[]){
104 | // Parse parameters
105 |
106 | printInfo();
107 | cout << "Reading program parameters ..." << endl;
108 | parseProgramParameters(argc,argv);
109 |
110 | convert2svo(filename,gridsize, color_mode);
111 | }
112 |
--------------------------------------------------------------------------------
/src/svo_convert/mesh_operations.h:
--------------------------------------------------------------------------------
1 | #ifndef MESH_OPERATIONS_H_
2 | #define MESH_OPERATIONS_H_
3 |
4 | #include
5 | #include "geo_primitives.h"
6 |
7 | using namespace std;
8 | using namespace trimesh;
9 |
10 | // Create bounding cube around a mesh (pad if the bbox is not a cube)
11 | inline AABox createMeshBBCube(const TriMesh* mesh){
12 | vec3 mesh_min = mesh->bbox.min;
13 | vec3 mesh_max = mesh->bbox.max;
14 | vec3 lengths = mesh_max-mesh_min;
15 | for(int i=0; i<3; i++){
16 | float delta = lengths.max() - lengths[i];
17 | if(delta != 0){
18 | mesh_min[i] = mesh_min[i] - (delta / 2.0f);
19 | mesh_max[i] = mesh_max[i] + (delta / 2.0f);
20 | }
21 | }
22 | return AABox(mesh_min,mesh_max);
23 | }
24 |
25 | inline void moveToOrigin(TriMesh* mesh, AABox bounding_box){
26 | // TODO: might be interesting for OpenMP optimalization
27 | for(size_t i = 0; i < mesh->vertices.size(); i++){
28 | mesh->vertices[i] = mesh->vertices[i] - bounding_box.min;
29 | }
30 | }
31 |
32 | #endif
33 |
--------------------------------------------------------------------------------
/src/svo_convert/morton.h:
--------------------------------------------------------------------------------
1 | // Various methods of computing morton codes
2 | // Check http://www.forceflow.be/2013/10/07/morton-encodingdecoding-through-bit-interleaving-implementations/ for more info
3 | // by Jeroen Baert - jeroen.baert@cs.kuleuven.be - www.forceflow.be
4 |
5 | #ifndef MORTON_H_
6 | #define MORTON_H_
7 |
8 | #include
9 | #include
10 |
11 | using namespace std;
12 |
13 | uint64_t mortonEncode_LUT(unsigned int x, unsigned int y, unsigned int z);
14 | uint64_t mortonEncode_magicbits(unsigned int x, unsigned int y, unsigned int z);
15 | uint64_t mortonEncode_for(unsigned int x, unsigned int y, unsigned int z);
16 | void mortonDecode(uint64_t morton, unsigned int& x, unsigned int& y, unsigned int& z);
17 |
18 | // VERSION WITH FOR LOOP
19 | // ---------------------
20 | inline uint64_t mortonEncode_for(unsigned int x, unsigned int y, unsigned int z){
21 | uint64_t answer = 0;
22 | for (uint64_t i = 0; i < (sizeof(int)* CHAR_BIT); ++i) {
23 | answer |= ((x & ((uint64_t)1 << i)) << 2 * i) | ((y & ((uint64_t)1 << i)) << (2 * i + 1)) | ((z & ((uint64_t)1 << i)) << (2 * i + 2));
24 | }
25 | return answer;
26 | }
27 |
28 | // VERSION WITH MAGIC BITS
29 | // -----------------------
30 | inline uint64_t splitBy3(int a){
31 | uint64_t x = a & 0x1fffff;
32 | x = (x | x << 32) & 0x1f00000000ffff;
33 | x = (x | x << 16) & 0x1f0000ff0000ff;
34 | x = (x | x << 8) & 0x100f00f00f00f00f;
35 | x = (x | x << 4) & 0x10c30c30c30c30c3;
36 | x = (x | x << 2) & 0x1249249249249249;
37 | return x;
38 | }
39 |
40 | inline uint64_t mortonEncode_magicbits(unsigned int x, unsigned int y, unsigned int z){
41 | uint64_t answer = 0;
42 | answer |= splitBy3(x) | splitBy3(y) << 1 | splitBy3(z) << 2;
43 | return answer;
44 | }
45 |
46 | // VERSION WITH LOOKUP TABLE
47 | // -------------------------
48 | static const uint32_t morton256_x[256] =
49 | {
50 | 0x00000000,
51 | 0x00000001, 0x00000008, 0x00000009, 0x00000040, 0x00000041, 0x00000048, 0x00000049, 0x00000200,
52 | 0x00000201, 0x00000208, 0x00000209, 0x00000240, 0x00000241, 0x00000248, 0x00000249, 0x00001000,
53 | 0x00001001, 0x00001008, 0x00001009, 0x00001040, 0x00001041, 0x00001048, 0x00001049, 0x00001200,
54 | 0x00001201, 0x00001208, 0x00001209, 0x00001240, 0x00001241, 0x00001248, 0x00001249, 0x00008000,
55 | 0x00008001, 0x00008008, 0x00008009, 0x00008040, 0x00008041, 0x00008048, 0x00008049, 0x00008200,
56 | 0x00008201, 0x00008208, 0x00008209, 0x00008240, 0x00008241, 0x00008248, 0x00008249, 0x00009000,
57 | 0x00009001, 0x00009008, 0x00009009, 0x00009040, 0x00009041, 0x00009048, 0x00009049, 0x00009200,
58 | 0x00009201, 0x00009208, 0x00009209, 0x00009240, 0x00009241, 0x00009248, 0x00009249, 0x00040000,
59 | 0x00040001, 0x00040008, 0x00040009, 0x00040040, 0x00040041, 0x00040048, 0x00040049, 0x00040200,
60 | 0x00040201, 0x00040208, 0x00040209, 0x00040240, 0x00040241, 0x00040248, 0x00040249, 0x00041000,
61 | 0x00041001, 0x00041008, 0x00041009, 0x00041040, 0x00041041, 0x00041048, 0x00041049, 0x00041200,
62 | 0x00041201, 0x00041208, 0x00041209, 0x00041240, 0x00041241, 0x00041248, 0x00041249, 0x00048000,
63 | 0x00048001, 0x00048008, 0x00048009, 0x00048040, 0x00048041, 0x00048048, 0x00048049, 0x00048200,
64 | 0x00048201, 0x00048208, 0x00048209, 0x00048240, 0x00048241, 0x00048248, 0x00048249, 0x00049000,
65 | 0x00049001, 0x00049008, 0x00049009, 0x00049040, 0x00049041, 0x00049048, 0x00049049, 0x00049200,
66 | 0x00049201, 0x00049208, 0x00049209, 0x00049240, 0x00049241, 0x00049248, 0x00049249, 0x00200000,
67 | 0x00200001, 0x00200008, 0x00200009, 0x00200040, 0x00200041, 0x00200048, 0x00200049, 0x00200200,
68 | 0x00200201, 0x00200208, 0x00200209, 0x00200240, 0x00200241, 0x00200248, 0x00200249, 0x00201000,
69 | 0x00201001, 0x00201008, 0x00201009, 0x00201040, 0x00201041, 0x00201048, 0x00201049, 0x00201200,
70 | 0x00201201, 0x00201208, 0x00201209, 0x00201240, 0x00201241, 0x00201248, 0x00201249, 0x00208000,
71 | 0x00208001, 0x00208008, 0x00208009, 0x00208040, 0x00208041, 0x00208048, 0x00208049, 0x00208200,
72 | 0x00208201, 0x00208208, 0x00208209, 0x00208240, 0x00208241, 0x00208248, 0x00208249, 0x00209000,
73 | 0x00209001, 0x00209008, 0x00209009, 0x00209040, 0x00209041, 0x00209048, 0x00209049, 0x00209200,
74 | 0x00209201, 0x00209208, 0x00209209, 0x00209240, 0x00209241, 0x00209248, 0x00209249, 0x00240000,
75 | 0x00240001, 0x00240008, 0x00240009, 0x00240040, 0x00240041, 0x00240048, 0x00240049, 0x00240200,
76 | 0x00240201, 0x00240208, 0x00240209, 0x00240240, 0x00240241, 0x00240248, 0x00240249, 0x00241000,
77 | 0x00241001, 0x00241008, 0x00241009, 0x00241040, 0x00241041, 0x00241048, 0x00241049, 0x00241200,
78 | 0x00241201, 0x00241208, 0x00241209, 0x00241240, 0x00241241, 0x00241248, 0x00241249, 0x00248000,
79 | 0x00248001, 0x00248008, 0x00248009, 0x00248040, 0x00248041, 0x00248048, 0x00248049, 0x00248200,
80 | 0x00248201, 0x00248208, 0x00248209, 0x00248240, 0x00248241, 0x00248248, 0x00248249, 0x00249000,
81 | 0x00249001, 0x00249008, 0x00249009, 0x00249040, 0x00249041, 0x00249048, 0x00249049, 0x00249200,
82 | 0x00249201, 0x00249208, 0x00249209, 0x00249240, 0x00249241, 0x00249248, 0x00249249
83 | };
84 |
85 | static const uint32_t morton256_y[256] = {
86 | 0x00000000,
87 | 0x00000002, 0x00000010, 0x00000012, 0x00000080, 0x00000082, 0x00000090, 0x00000092, 0x00000400,
88 | 0x00000402, 0x00000410, 0x00000412, 0x00000480, 0x00000482, 0x00000490, 0x00000492, 0x00002000,
89 | 0x00002002, 0x00002010, 0x00002012, 0x00002080, 0x00002082, 0x00002090, 0x00002092, 0x00002400,
90 | 0x00002402, 0x00002410, 0x00002412, 0x00002480, 0x00002482, 0x00002490, 0x00002492, 0x00010000,
91 | 0x00010002, 0x00010010, 0x00010012, 0x00010080, 0x00010082, 0x00010090, 0x00010092, 0x00010400,
92 | 0x00010402, 0x00010410, 0x00010412, 0x00010480, 0x00010482, 0x00010490, 0x00010492, 0x00012000,
93 | 0x00012002, 0x00012010, 0x00012012, 0x00012080, 0x00012082, 0x00012090, 0x00012092, 0x00012400,
94 | 0x00012402, 0x00012410, 0x00012412, 0x00012480, 0x00012482, 0x00012490, 0x00012492, 0x00080000,
95 | 0x00080002, 0x00080010, 0x00080012, 0x00080080, 0x00080082, 0x00080090, 0x00080092, 0x00080400,
96 | 0x00080402, 0x00080410, 0x00080412, 0x00080480, 0x00080482, 0x00080490, 0x00080492, 0x00082000,
97 | 0x00082002, 0x00082010, 0x00082012, 0x00082080, 0x00082082, 0x00082090, 0x00082092, 0x00082400,
98 | 0x00082402, 0x00082410, 0x00082412, 0x00082480, 0x00082482, 0x00082490, 0x00082492, 0x00090000,
99 | 0x00090002, 0x00090010, 0x00090012, 0x00090080, 0x00090082, 0x00090090, 0x00090092, 0x00090400,
100 | 0x00090402, 0x00090410, 0x00090412, 0x00090480, 0x00090482, 0x00090490, 0x00090492, 0x00092000,
101 | 0x00092002, 0x00092010, 0x00092012, 0x00092080, 0x00092082, 0x00092090, 0x00092092, 0x00092400,
102 | 0x00092402, 0x00092410, 0x00092412, 0x00092480, 0x00092482, 0x00092490, 0x00092492, 0x00400000,
103 | 0x00400002, 0x00400010, 0x00400012, 0x00400080, 0x00400082, 0x00400090, 0x00400092, 0x00400400,
104 | 0x00400402, 0x00400410, 0x00400412, 0x00400480, 0x00400482, 0x00400490, 0x00400492, 0x00402000,
105 | 0x00402002, 0x00402010, 0x00402012, 0x00402080, 0x00402082, 0x00402090, 0x00402092, 0x00402400,
106 | 0x00402402, 0x00402410, 0x00402412, 0x00402480, 0x00402482, 0x00402490, 0x00402492, 0x00410000,
107 | 0x00410002, 0x00410010, 0x00410012, 0x00410080, 0x00410082, 0x00410090, 0x00410092, 0x00410400,
108 | 0x00410402, 0x00410410, 0x00410412, 0x00410480, 0x00410482, 0x00410490, 0x00410492, 0x00412000,
109 | 0x00412002, 0x00412010, 0x00412012, 0x00412080, 0x00412082, 0x00412090, 0x00412092, 0x00412400,
110 | 0x00412402, 0x00412410, 0x00412412, 0x00412480, 0x00412482, 0x00412490, 0x00412492, 0x00480000,
111 | 0x00480002, 0x00480010, 0x00480012, 0x00480080, 0x00480082, 0x00480090, 0x00480092, 0x00480400,
112 | 0x00480402, 0x00480410, 0x00480412, 0x00480480, 0x00480482, 0x00480490, 0x00480492, 0x00482000,
113 | 0x00482002, 0x00482010, 0x00482012, 0x00482080, 0x00482082, 0x00482090, 0x00482092, 0x00482400,
114 | 0x00482402, 0x00482410, 0x00482412, 0x00482480, 0x00482482, 0x00482490, 0x00482492, 0x00490000,
115 | 0x00490002, 0x00490010, 0x00490012, 0x00490080, 0x00490082, 0x00490090, 0x00490092, 0x00490400,
116 | 0x00490402, 0x00490410, 0x00490412, 0x00490480, 0x00490482, 0x00490490, 0x00490492, 0x00492000,
117 | 0x00492002, 0x00492010, 0x00492012, 0x00492080, 0x00492082, 0x00492090, 0x00492092, 0x00492400,
118 | 0x00492402, 0x00492410, 0x00492412, 0x00492480, 0x00492482, 0x00492490, 0x00492492
119 | };
120 |
121 | static const uint32_t morton256_z[256] = {
122 | 0x00000000,
123 | 0x00000004, 0x00000020, 0x00000024, 0x00000100, 0x00000104, 0x00000120, 0x00000124, 0x00000800,
124 | 0x00000804, 0x00000820, 0x00000824, 0x00000900, 0x00000904, 0x00000920, 0x00000924, 0x00004000,
125 | 0x00004004, 0x00004020, 0x00004024, 0x00004100, 0x00004104, 0x00004120, 0x00004124, 0x00004800,
126 | 0x00004804, 0x00004820, 0x00004824, 0x00004900, 0x00004904, 0x00004920, 0x00004924, 0x00020000,
127 | 0x00020004, 0x00020020, 0x00020024, 0x00020100, 0x00020104, 0x00020120, 0x00020124, 0x00020800,
128 | 0x00020804, 0x00020820, 0x00020824, 0x00020900, 0x00020904, 0x00020920, 0x00020924, 0x00024000,
129 | 0x00024004, 0x00024020, 0x00024024, 0x00024100, 0x00024104, 0x00024120, 0x00024124, 0x00024800,
130 | 0x00024804, 0x00024820, 0x00024824, 0x00024900, 0x00024904, 0x00024920, 0x00024924, 0x00100000,
131 | 0x00100004, 0x00100020, 0x00100024, 0x00100100, 0x00100104, 0x00100120, 0x00100124, 0x00100800,
132 | 0x00100804, 0x00100820, 0x00100824, 0x00100900, 0x00100904, 0x00100920, 0x00100924, 0x00104000,
133 | 0x00104004, 0x00104020, 0x00104024, 0x00104100, 0x00104104, 0x00104120, 0x00104124, 0x00104800,
134 | 0x00104804, 0x00104820, 0x00104824, 0x00104900, 0x00104904, 0x00104920, 0x00104924, 0x00120000,
135 | 0x00120004, 0x00120020, 0x00120024, 0x00120100, 0x00120104, 0x00120120, 0x00120124, 0x00120800,
136 | 0x00120804, 0x00120820, 0x00120824, 0x00120900, 0x00120904, 0x00120920, 0x00120924, 0x00124000,
137 | 0x00124004, 0x00124020, 0x00124024, 0x00124100, 0x00124104, 0x00124120, 0x00124124, 0x00124800,
138 | 0x00124804, 0x00124820, 0x00124824, 0x00124900, 0x00124904, 0x00124920, 0x00124924, 0x00800000,
139 | 0x00800004, 0x00800020, 0x00800024, 0x00800100, 0x00800104, 0x00800120, 0x00800124, 0x00800800,
140 | 0x00800804, 0x00800820, 0x00800824, 0x00800900, 0x00800904, 0x00800920, 0x00800924, 0x00804000,
141 | 0x00804004, 0x00804020, 0x00804024, 0x00804100, 0x00804104, 0x00804120, 0x00804124, 0x00804800,
142 | 0x00804804, 0x00804820, 0x00804824, 0x00804900, 0x00804904, 0x00804920, 0x00804924, 0x00820000,
143 | 0x00820004, 0x00820020, 0x00820024, 0x00820100, 0x00820104, 0x00820120, 0x00820124, 0x00820800,
144 | 0x00820804, 0x00820820, 0x00820824, 0x00820900, 0x00820904, 0x00820920, 0x00820924, 0x00824000,
145 | 0x00824004, 0x00824020, 0x00824024, 0x00824100, 0x00824104, 0x00824120, 0x00824124, 0x00824800,
146 | 0x00824804, 0x00824820, 0x00824824, 0x00824900, 0x00824904, 0x00824920, 0x00824924, 0x00900000,
147 | 0x00900004, 0x00900020, 0x00900024, 0x00900100, 0x00900104, 0x00900120, 0x00900124, 0x00900800,
148 | 0x00900804, 0x00900820, 0x00900824, 0x00900900, 0x00900904, 0x00900920, 0x00900924, 0x00904000,
149 | 0x00904004, 0x00904020, 0x00904024, 0x00904100, 0x00904104, 0x00904120, 0x00904124, 0x00904800,
150 | 0x00904804, 0x00904820, 0x00904824, 0x00904900, 0x00904904, 0x00904920, 0x00904924, 0x00920000,
151 | 0x00920004, 0x00920020, 0x00920024, 0x00920100, 0x00920104, 0x00920120, 0x00920124, 0x00920800,
152 | 0x00920804, 0x00920820, 0x00920824, 0x00920900, 0x00920904, 0x00920920, 0x00920924, 0x00924000,
153 | 0x00924004, 0x00924020, 0x00924024, 0x00924100, 0x00924104, 0x00924120, 0x00924124, 0x00924800,
154 | 0x00924804, 0x00924820, 0x00924824, 0x00924900, 0x00924904, 0x00924920, 0x00924924
155 | };
156 |
157 | inline uint64_t mortonEncode_LUT(unsigned int x, unsigned int y, unsigned int z){
158 | uint64_t answer = 0;
159 | answer = morton256_z[(z >> 16) & 0xFF] |
160 | morton256_y[(y >> 16) & 0xFF] |
161 | morton256_x[(x >> 16) & 0xFF];
162 | answer = answer << 48 |
163 | morton256_z[(z >> 8) & 0xFF] |
164 | morton256_y[(y >> 8) & 0xFF] |
165 | morton256_x[(x >> 8) & 0xFF];
166 | answer = answer << 24 |
167 | morton256_z[(z)& 0xFF] |
168 | morton256_y[(y)& 0xFF] |
169 | morton256_x[(x)& 0xFF];
170 | return answer;
171 | }
172 |
173 | // decode a given 64-bit morton code to an integer (x,y,z) coordinate
174 | inline void mortonDecode(uint64_t morton, unsigned int& x, unsigned int& y, unsigned int& z){
175 | x = 0;
176 | y = 0;
177 | z = 0;
178 | for (uint64_t i = 0; i < (sizeof(uint64_t)* CHAR_BIT) / 3; ++i) {
179 | x |= ((morton & (uint64_t(1ull) << uint64_t((3ull * i) + 0ull))) >> uint64_t(((3ull * i) + 0ull) - i));
180 | y |= ((morton & (uint64_t(1ull) << uint64_t((3ull * i) + 1ull))) >> uint64_t(((3ull * i) + 1ull) - i));
181 | z |= ((morton & (uint64_t(1ull) << uint64_t((3ull * i) + 2ull))) >> uint64_t(((3ull * i) + 2ull) - i));
182 | }
183 | }
184 |
185 | #endif // MORTON_H_
--------------------------------------------------------------------------------
/src/svo_convert/octree2svo.h:
--------------------------------------------------------------------------------
1 | #ifndef OCTREE2SVO_H_
2 | #define OCTREE2SVO_H_
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "Node.h"
9 | #include "DataPoint.h"
10 |
11 | using namespace std;
12 |
13 | // config and convert2svo stuff
14 | FILE* svo_out;
15 |
16 | inline void parseNode(const Node &node, const vector &nodes, const vector &data){
17 | // create bitsets we'll write out
18 | bitset<8> child_tree_exists;
19 | bitset<8> child_colored;
20 |
21 | child_tree_exists.reset();
22 | child_colored.reset();
23 |
24 | for(int i = 0; i < 8; i++) {
25 | if(node.hasChild(7-i)){ // if this node has a child at position i
26 | if(! nodes[node.getChildPos(7-i)].isLeaf()){ // if it is not a leaf, there's a tree
27 | child_tree_exists.set(i, true);
28 | } else {
29 | child_colored.set(i, true); // it's a leaf child: color it
30 | }
31 | }
32 | }
33 |
34 | // write these to file: convert bitset to unsigned char to write to file
35 | unsigned char child_tree_exists_byte = (unsigned char) child_tree_exists.to_ulong();
36 | unsigned char child_colored_byte = (unsigned char) child_colored.to_ulong();
37 |
38 | // write child_colored byte
39 | fwrite(& child_colored_byte, sizeof(unsigned char), 1, svo_out);
40 |
41 | // write color for each child (in RGB)
42 | for(int i = 7; i >= 0; i--){ // we write them left to right in the byte order
43 | if(child_colored[i]){
44 | DataPoint dpoint = data[nodes[node.getChildPos(7-i)].data]; // fetch datapoint
45 | unsigned char r = (unsigned char) (dpoint.color[0] * 255.0f);
46 | unsigned char g = (unsigned char) (dpoint.color[1] * 255.0f);
47 | unsigned char b = (unsigned char) (dpoint.color[2] * 255.0f);
48 | fwrite(& r, sizeof(unsigned char), 1, svo_out);
49 | fwrite(& g, sizeof(unsigned char), 1, svo_out);
50 | fwrite(& b, sizeof(unsigned char), 1, svo_out);
51 | }
52 | }
53 |
54 | // write tree exists bytes
55 | fwrite(& child_tree_exists_byte, sizeof(unsigned char), 1, svo_out);
56 |
57 | // start writing child nodes (recursively)
58 | for(int i = 7; i >= 0; i--) { // we write them left to right in the byte order
59 | if(child_tree_exists[i]){
60 | parseNode(nodes[node.getChildPos(7-i)], nodes, data);
61 | }
62 | }
63 | }
64 |
65 | inline void octree2SVO(string filename, const vector &nodes, const vector &data){
66 | // open output file
67 | string svo_file = filename + string(".svo");
68 | svo_out = fopen(svo_file.c_str(), "wb");
69 |
70 | // Write octal code for root node (all zero)
71 | unsigned char zero = 0;
72 | fwrite(& zero, sizeof(unsigned char), 1, svo_out);
73 |
74 | // open root node and start parsing recursively
75 | parseNode(nodes[(nodes.size()-1)], nodes,data); // parse root node
76 | fclose(svo_out);
77 | cout << " written to " << svo_file << endl;
78 | }
79 |
80 | #endif
--------------------------------------------------------------------------------
/src/svo_convert/svo_convert.h:
--------------------------------------------------------------------------------
1 | #ifndef SVO_CONVERT_H_
2 | #define SVO_CONVERT_H_
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "svo_convert_util.h"
9 | #include "mesh_operations.h"
10 | #include "voxelization.h"
11 | #include "OctreeBuilder.h"
12 | #include "octree2svo.h"
13 |
14 | void convert2svo(string filename, size_t gridsize, ColorMode color_mode){
15 | // Read mesh, calculate bbox and move to origin
16 | TriMesh* mesh = TriMesh::read(filename.c_str());
17 | mesh->need_faces(); // unpack triangle strips so we have faces
18 | mesh->need_bbox(); // compute the bounding box
19 | mesh->need_normals();
20 | if (mesh->colors.empty() && color_mode == COLOR_FROM_MODEL){
21 | cout << "WARNING: Mesh has no vertex colors, reverting to fixed color mode." << endl;
22 | }
23 |
24 | AABox mesh_bbcube = createMeshBBCube(mesh);
25 | cout << "Moving mesh to origin ..." << endl;
26 | moveToOrigin(mesh, mesh_bbcube);
27 | float unitlength = (mesh_bbcube.max[0] - mesh_bbcube.min[0]) / ((float) gridsize);
28 |
29 | // Prepare voxel storage
30 | cout << "Preparing voxel storage ..." << endl;
31 | size_t max_index = gridsize*gridsize*gridsize;
32 | char* voxels = new char[max_index]; // Array holds 0 if there is no voxel, and an index if there is voxel data
33 | vector data; // Dynamic-sized array holding voxel data
34 | size_t nfilled = 0;
35 | cout << " allocated " << (max_index*sizeof(char))/1024 << " kb for voxel table." << endl;
36 |
37 | // Voxelize
38 | cout << "Voxelizing ..." << endl;
39 | voxelize(mesh, gridsize, unitlength, color_mode, voxels, data, nfilled);
40 | cout << " found " << nfilled << " voxels." << endl;
41 |
42 | // SVO builder
43 | cout << "Building SVO ..." << endl;
44 | OctreeBuilder builder = OctreeBuilder(gridsize,false);
45 | builder.buildOctree(voxels, data);
46 | cout << " octree built with " << builder.octree_nodes.size() << " nodes and " << builder.octree_data.size() << " data points." << endl;
47 |
48 | // Free memory
49 | delete voxels; data.clear();
50 |
51 | // Converting to SVO file
52 | cout << "Convert to .SVO file..." << endl;
53 | string base_filename = filename.substr(0,filename.find_last_of("."));
54 | octree2SVO(base_filename,builder.octree_nodes, builder.octree_data);
55 | }
56 | #endif
57 |
--------------------------------------------------------------------------------
/src/svo_convert/svo_convert_util.h:
--------------------------------------------------------------------------------
1 | // Header file with misc helper functions and definitions
2 |
3 | #ifndef SVO_CONVERT_UTIL_H_
4 | #define SVO_CONVERT_UTIL_H_
5 |
6 | using namespace std;
7 | using namespace trimesh;
8 |
9 | // unsigned integer vector
10 | typedef Vec<3, unsigned int> uivec3;
11 |
12 | // Take the average of 3 TriMesh Vec's
13 | template
14 | inline Vec average3Vec(const Vec v0, const Vec v1, const Vec v2){
15 | Vec answer;
16 | for (size_t i = 0; i < D; i++){
17 | answer[i] = (v0[i] + v1[i] + v2[i]) / 3.0f;
18 | }
19 | return answer;
20 | }
21 |
22 | // Clamp a value between low and high
23 | template T clampval(const T& value, const T& low, const T& high) {
24 | return value < low ? low : (value > high ? high : value);
25 | }
26 |
27 | // Find the highest power of 8 which fits in value n
28 | inline int findPowerOf8(size_t n){
29 | if(n == 0){return 0;}
30 | int highest_index = 0;
31 | while(n >>= 1){
32 | highest_index++;
33 | }
34 | return (highest_index/3);
35 | }
36 |
37 | // Take the log2 of an unsigned int value
38 | inline unsigned int log2(unsigned int val) {
39 | unsigned int ret = -1;
40 | while (val != 0) {
41 | val >>= 1;
42 | ret++;
43 | }
44 | return ret;
45 | }
46 |
47 | // Test whether or not the given unsigned int is a power of 2
48 | inline int isPowerOf2(unsigned int x){
49 | return ((x != 0) && !(x & (x - 1)));
50 | }
51 |
52 | #endif // SVO_CONVERT_UTIL_H_
--------------------------------------------------------------------------------
/src/svo_convert/voxelization.cpp:
--------------------------------------------------------------------------------
1 | #include "voxelization.h"
2 |
3 | using namespace std;
4 | using namespace trimesh;
5 |
6 | // Implementation of http://research.michael-schwarz.com/publ/2010/vox/ (Schwarz & Seidel)
7 | // Adapted for mortoncode based subgrids
8 | // by Jeroen Baert - jeroen.baert@cs.kuleuven.be
9 |
10 | #define X 0
11 | #define Y 1
12 | #define Z 2
13 |
14 | // Get the shading normal for a given triangle (average of 3 vertex shading normals)
15 | vec3 getTriangleShadingNormal(const TriMesh* mesh, size_t face_id){
16 | return average3Vec<3, float>(mesh->normals[mesh->faces[face_id][X]], mesh->normals[mesh->faces[face_id][Y]], mesh->normals[mesh->faces[face_id][Z]]);
17 | }
18 |
19 | // Get the given color for this triangle
20 | vec3 getTriangleColor(const TriMesh* mesh, size_t face_id, ColorMode c){
21 | if (c == COLOR_FROM_MODEL){
22 | if (!mesh->colors.empty()){ // if this mesh has colors, we're going to grab them for this triangle
23 | vec3 t_v0_color = mesh->colors[mesh->faces[face_id][0]];
24 | vec3 t_v1_color = mesh->colors[mesh->faces[face_id][1]];
25 | vec3 t_v2_color = mesh->colors[mesh->faces[face_id][2]];
26 | return average3Vec(t_v0_color, t_v1_color, t_v1_color); // average vertex colors to get triangle color (TODO: less lazy to interpolate)
27 | }
28 | return FIXED_COLOR; // fallback if model contains no color: fixed color
29 | } else if (c == COLOR_FIXED){
30 | return FIXED_COLOR;
31 | } else if (c == COLOR_NORMAL){
32 | vec3 normal = getTriangleShadingNormal(mesh, face_id);
33 | normal = normalize(normal); // get normal in range between -1.0 and 1.0
34 | return (vec3((normal[0] + 1.0f) / 2.0f, (normal[1] + 1.0f) / 2.0f, (normal[2] + 1.0f) / 2.0f));
35 | }
36 | return FIXED_COLOR;
37 | }
38 |
39 | void voxelize(const TriMesh* mesh, size_t gridsize, float unitlength, ColorMode color_mode, char* voxels, vector& data, size_t& nfilled){
40 | size_t morton_start = 0;
41 | size_t morton_end = gridsize*gridsize*gridsize;
42 |
43 | // Clear data
44 | memset(voxels, EMPTY_VOXEL, (morton_end - morton_start)*sizeof(char));
45 | data.clear();
46 |
47 | // compute partition min and max in grid coords
48 | AABox p_bbox_grid;
49 | mortonDecode(morton_start, p_bbox_grid.min[2], p_bbox_grid.min[1], p_bbox_grid.min[0]);
50 | mortonDecode(morton_end-1, p_bbox_grid.max[2], p_bbox_grid.max[1], p_bbox_grid.max[0]);
51 |
52 | // COMMON PROPERTIES FOR ALL TRIANGLES
53 | float unit_div = 1.0f / unitlength;
54 | vec3 delta_p = vec3(unitlength,unitlength,unitlength);
55 |
56 | for(size_t i = 0; i < mesh->faces.size(); i++){
57 | vec3 t_v0 = mesh->vertices[mesh->faces[i][0]];
58 | vec3 t_v1 = mesh->vertices[mesh->faces[i][1]];
59 | vec3 t_v2 = mesh->vertices[mesh->faces[i][2]];
60 |
61 | // compute triangle bbox in world and grid
62 | AABox t_bbox_world = computeBoundingBox(t_v0,t_v1,t_v2);
63 | AABox t_bbox_grid;
64 | t_bbox_grid.min[0] = (int) (t_bbox_world.min[0] * unit_div); // using integer rounding to construct bbox
65 | t_bbox_grid.min[1] = (int) (t_bbox_world.min[1] * unit_div);
66 | t_bbox_grid.min[2] = (int) (t_bbox_world.min[2] * unit_div);
67 | t_bbox_grid.max[0] = (int) (t_bbox_world.max[0] * unit_div);
68 | t_bbox_grid.max[1] = (int) (t_bbox_world.max[1] * unit_div);
69 | t_bbox_grid.max[2] = (int) (t_bbox_world.max[2] * unit_div);
70 |
71 | // clamp grid bounding box to partition bounding box
72 | t_bbox_grid.min[0] = clampval(t_bbox_grid.min[0], p_bbox_grid.min[0], p_bbox_grid.max[0]);
73 | t_bbox_grid.min[1] = clampval(t_bbox_grid.min[1], p_bbox_grid.min[1], p_bbox_grid.max[1]);
74 | t_bbox_grid.min[2] = clampval(t_bbox_grid.min[2], p_bbox_grid.min[2], p_bbox_grid.max[2]);
75 | t_bbox_grid.max[0] = clampval(t_bbox_grid.max[0], p_bbox_grid.min[0], p_bbox_grid.max[0]);
76 | t_bbox_grid.max[1] = clampval(t_bbox_grid.max[1], p_bbox_grid.min[1], p_bbox_grid.max[1]);
77 | t_bbox_grid.max[2] = clampval(t_bbox_grid.max[2], p_bbox_grid.min[2], p_bbox_grid.max[2]);
78 |
79 | // COMMON PROPERTIES FOR THE TRIANGLE
80 | vec3 e0 = t_v1 - t_v0;
81 | vec3 e1 = t_v2 - t_v1;
82 | vec3 e2 = t_v0 - t_v2;
83 | vec3 to_normalize = (e0) CROSS (e1);
84 | vec3 n = normalize(to_normalize); // triangle normal
85 | // PLANE TEST PROPERTIES
86 | vec3 c = vec3(0.0f,0.0f,0.0f); // critical point
87 | if(n[X] > 0) { c[X] = unitlength;}
88 | if(n[Y] > 0) { c[Y] = unitlength;}
89 | if(n[Z] > 0) { c[Z] = unitlength;}
90 | float d1 = n DOT (c - t_v0);
91 | float d2 = n DOT ((delta_p - c) - t_v0);
92 | // PROJECTION TEST PROPERTIES
93 | // XY plane
94 | vec2 n_xy_e0 = vec2(-1.0f*e0[Y], e0[X]);
95 | vec2 n_xy_e1 = vec2(-1.0f*e1[Y], e1[X]);
96 | vec2 n_xy_e2 = vec2(-1.0f*e2[Y], e2[X]);
97 | if(n[Z] < 0.0f) {
98 | n_xy_e0 = -1.0f * n_xy_e0;
99 | n_xy_e1 = -1.0f * n_xy_e1;
100 | n_xy_e2 = -1.0f * n_xy_e2;
101 | }
102 | float d_xy_e0 = (-1.0f * (n_xy_e0 DOT vec2(t_v0[X],t_v0[Y]))) + max(0.0f, unitlength*n_xy_e0[0]) + max(0.0f, unitlength*n_xy_e0[1]);
103 | float d_xy_e1 = (-1.0f * (n_xy_e1 DOT vec2(t_v1[X],t_v1[Y]))) + max(0.0f, unitlength*n_xy_e1[0]) + max(0.0f, unitlength*n_xy_e1[1]);
104 | float d_xy_e2 = (-1.0f * (n_xy_e2 DOT vec2(t_v2[X],t_v2[Y]))) + max(0.0f, unitlength*n_xy_e2[0]) + max(0.0f, unitlength*n_xy_e2[1]);
105 |
106 | // YZ plane
107 | vec2 n_yz_e0 = vec2(-1.0f*e0[Z], e0[Y]);
108 | vec2 n_yz_e1 = vec2(-1.0f*e1[Z], e1[Y]);
109 | vec2 n_yz_e2 = vec2(-1.0f*e2[Z], e2[Y]);
110 | if(n[X] < 0.0f) {
111 | n_yz_e0 = -1.0f * n_yz_e0;
112 | n_yz_e1 = -1.0f * n_yz_e1;
113 | n_yz_e2 = -1.0f * n_yz_e2;
114 | }
115 | float d_yz_e0 = (-1.0f * (n_yz_e0 DOT vec2(t_v0[Y],t_v0[Z]))) + max(0.0f, unitlength*n_yz_e0[0]) + max(0.0f, unitlength*n_yz_e0[1]);
116 | float d_yz_e1 = (-1.0f * (n_yz_e1 DOT vec2(t_v1[Y],t_v1[Z]))) + max(0.0f, unitlength*n_yz_e1[0]) + max(0.0f, unitlength*n_yz_e1[1]);
117 | float d_yz_e2 = (-1.0f * (n_yz_e2 DOT vec2(t_v2[Y],t_v2[Z]))) + max(0.0f, unitlength*n_yz_e2[0]) + max(0.0f, unitlength*n_yz_e2[1]);
118 |
119 | // ZX plane
120 | vec2 n_zx_e0 = vec2(-1.0f*e0[X], e0[Z]);
121 | vec2 n_zx_e1 = vec2(-1.0f*e1[X], e1[Z]);
122 | vec2 n_zx_e2 = vec2(-1.0f*e2[X], e2[Z]);
123 | if(n[Y] < 0.0f) {
124 | n_zx_e0 = -1.0f * n_zx_e0;
125 | n_zx_e1 = -1.0f * n_zx_e1;
126 | n_zx_e2 = -1.0f * n_zx_e2;
127 | }
128 | float d_xz_e0 = (-1.0f * (n_zx_e0 DOT vec2(t_v0[Z],t_v0[X]))) + max(0.0f, unitlength*n_zx_e0[0]) + max(0.0f, unitlength*n_zx_e0[1]);
129 | float d_xz_e1 = (-1.0f * (n_zx_e1 DOT vec2(t_v1[Z],t_v1[X]))) + max(0.0f, unitlength*n_zx_e1[0]) + max(0.0f, unitlength*n_zx_e1[1]);
130 | float d_xz_e2 = (-1.0f * (n_zx_e2 DOT vec2(t_v2[Z],t_v2[X]))) + max(0.0f, unitlength*n_zx_e2[0]) + max(0.0f, unitlength*n_zx_e2[1]);
131 |
132 | // test possible grid boxes for overlap
133 | for(int x = t_bbox_grid.min[X]; x <= t_bbox_grid.max[X]; x++){
134 | for(int y = t_bbox_grid.min[Y]; y <= t_bbox_grid.max[Y]; y++){
135 | for(int z = t_bbox_grid.min[Z]; z <= t_bbox_grid.max[Z]; z++){
136 |
137 | uint64_t index = mortonEncode_LUT(z,y,x);
138 |
139 | if(! voxels[index-morton_start] == EMPTY_VOXEL){continue;} // already marked, continue
140 |
141 | // TRIANGLE PLANE THROUGH BOX TEST
142 | vec3 p = vec3(x*unitlength,y*unitlength,z*unitlength);
143 | float nDOTp = n DOT p;
144 | if( (nDOTp + d1) * (nDOTp + d2) > 0.0f ){continue;}
145 |
146 | // PROJECTION TESTS
147 | // XY
148 | vec2 p_xy = vec2(p[X],p[Y]);
149 | if (((n_xy_e0 DOT p_xy) + d_xy_e0) < 0.0f){continue;}
150 | if (((n_xy_e1 DOT p_xy) + d_xy_e1) < 0.0f){continue;}
151 | if (((n_xy_e2 DOT p_xy) + d_xy_e2) < 0.0f){continue;}
152 |
153 | // YZ
154 | vec2 p_yz = vec2(p[Y],p[Z]);
155 | if (((n_yz_e0 DOT p_yz) + d_yz_e0) < 0.0f){continue;}
156 | if (((n_yz_e1 DOT p_yz) + d_yz_e1) < 0.0f){continue;}
157 | if (((n_yz_e2 DOT p_yz) + d_yz_e2) < 0.0f){continue;}
158 |
159 | // XZ
160 | vec2 p_zx = vec2(p[Z],p[X]);
161 | if (((n_zx_e0 DOT p_zx) + d_xz_e0) < 0.0f){continue;}
162 | if (((n_zx_e1 DOT p_zx) + d_xz_e1) < 0.0f){continue;}
163 | if (((n_zx_e2 DOT p_zx) + d_xz_e2) < 0.0f){continue;}
164 |
165 | voxels[index - morton_start] = FULL_VOXEL;
166 | data.push_back(VoxelData(index, getTriangleShadingNormal(mesh, i), getTriangleColor(mesh, i, color_mode))); // we ignore data limits for colored voxelization
167 | nfilled++;
168 | continue;
169 | }
170 | }
171 | }
172 | }
173 | }
--------------------------------------------------------------------------------
/src/svo_convert/voxelization.h:
--------------------------------------------------------------------------------
1 | #ifndef VOXELIZATION_H_
2 | #define VOXELIZATION_H_
3 |
4 | #include
5 | #include "svo_convert_util.h"
6 | #include "geo_primitives.h"
7 | #include "morton.h"
8 | #include "intersection.h"
9 |
10 | using namespace std;
11 | using namespace trimesh;
12 |
13 | #define EMPTY_VOXEL 0 // char 0 means empty voxel
14 | #define FULL_VOXEL 1 // char 1 means filled voxel
15 |
16 | // Voxel color options
17 | enum ColorMode {COLOR_FROM_MODEL, COLOR_FIXED, COLOR_NORMAL};
18 | extern vec3 FIXED_COLOR;
19 |
20 | // Struct to hold collected voxel data
21 | // This struct defines VoxelData for our voxelizer - this is defined different depending on compile project
22 | // This is the main memory hogger: the less data you store here, the better.
23 | struct VoxelData{
24 | uint64_t morton;
25 | vec3 normal;
26 | vec3 color;
27 | VoxelData() : morton(0), normal(vec3(0.0f, 0.0f, 0.0f)), color(vec3(0.0f, 0.0f, 0.0f)){}
28 | VoxelData(uint64_t morton, vec3 normal, vec3 color) : morton(morton), normal(normal), color(color){}
29 |
30 | bool operator >(const VoxelData &a) const{
31 | return morton > a.morton;
32 | }
33 |
34 | bool operator <(const VoxelData &a) const{
35 | return morton < a.morton;
36 | }
37 | };
38 |
39 | void voxelize(const TriMesh* mesh, size_t gridsize, float unitlength, ColorMode c, char* voxels, vector& data, size_t& nfilled);
40 |
41 | #endif
--------------------------------------------------------------------------------