Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix build and ignore generated files #9

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ path.h
*build*

*.vox
.vscode
*.egg-info
dist
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

55 changes: 30 additions & 25 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
cmake_minimum_required(VERSION 2.6)
project(VoxSurf)
cmake_minimum_required(VERSION 3.10)
project(pyvoxsurf)

INCLUDE_DIRECTORIES(
${PROJECT_SOURCE_DIR}/LibSL-small
${PROJECT_SOURCE_DIR}/LibSL-small/src/
${PROJECT_SOURCE_DIR}/LibSL-small/src/LibSL
)
add_subdirectory(lib/LibSL-small)
set(XTL_INCLUDE_DIRS lib/xtl/include lib/xtensor/include
lib/xtensor-python/include)

CONFIGURE_FILE(
"${CMAKE_CURRENT_SOURCE_DIR}/path.h.in"
"${CMAKE_CURRENT_SOURCE_DIR}/path.h"
)
find_package(HDF5 COMPONENTS CXX)
if(${HDF5_FOUND})
add_executable(VoxSurfExample src/pyvoxsurf/example.cpp)
target_compile_features(VoxSurfExample PRIVATE cxx_std_17)
target_include_directories(VoxSurfExample PRIVATE ${HDF5_INCLUDE_DIRS}
${XTL_INCLUDE_DIRS})
target_link_libraries(VoxSurfExample PRIVATE voxelizer ${HDF5_LIBRARIES})
endif(${HDF5_FOUND})

ADD_EXECUTABLE(VoxSurf
main.cpp
LibSL-small/src/LibSL/Math/Math.cpp
LibSL-small/src/LibSL/Math/Vertex.cpp
LibSL-small/src/LibSL/Mesh/Mesh.cpp
LibSL-small/src/LibSL/Mesh/MeshFormat_stl.cpp
LibSL-small/src/LibSL/Mesh/VertexFormat_dynamic.cpp
LibSL-small/src/LibSL/System/System.cpp
LibSL-small/src/LibSL/CppHelpers/CppHelpers.cpp
)

if(WIN32)
target_link_libraries(VoxSurf shlwapi)
endif(WIN32)
add_library(voxelizer STATIC src/pyvoxsurf/voxelizer.cpp)
target_compile_features(voxelizer PRIVATE cxx_std_17)
target_link_libraries(voxelizer PUBLIC LibSL)
target_include_directories(voxelizer PRIVATE ${XTL_INCLUDE_DIRS})

set_target_properties(LibSL PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
set_target_properties(voxelizer PROPERTIES POSITION_INDEPENDENT_CODE TRUE)

find_package(Python COMPONENTS Interpreter NumPy)
add_subdirectory(lib/pybind11)
pybind11_add_module(pyvoxsurf src/pyvoxsurf/pyvoxsurf.cpp)
target_compile_features(pyvoxsurf PUBLIC cxx_std_14)
target_include_directories(
pyvoxsurf PRIVATE ${Python_INCLUDE_DIRS} ${Python_NumPy_INCLUDE_DIRS}
${XTL_INCLUDE_DIRS})
target_link_libraries(pyvoxsurf PUBLIC voxelizer)
install(TARGETS pyvoxsurf DESTINATION ${Python_SITELIB})
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2018 sylefeb
Copyright (c) 2020 sylefeb, mjgalindo, jttoombs

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
1 change: 0 additions & 1 deletion LibSL-small
Submodule LibSL-small deleted from 57f6f3
106 changes: 81 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,94 @@
# VoxSurf
A simple, easily hackable C++ surface voxelizer (STL=>voxels)
# PyVoxSurf
A Python C++ extension based on VoxSurf for voxelization of 3D meshes.

Takes as input a file 'model.stl' from the source directory.
Outputs a voxel file named 'out.slab.vox' that can be read by MagicaVoxel https://ephtracy.github.io/
This is the 'slab' format as exported by MagicaVoxel, which is the format used by the Slab6 editor http://www.advsys.net/ken/download.htm.
**NOTE**: PyPi distribution is only compatible with Microsoft Windows

This is meant as an introductory, easily modifiable implementation, and includes absoltutely zero optimization (e.g. SSE and multi-core would come to mind) nor any option such as performing a conservative voxelization. For other implementations, see links below.
### Principle
1. Rasterize triangles using three 2D axis aligned grids, using integer arithmetic (fixed floating point) for robust triangle interior checks
2. [Optional] Fill interior of voxelized surface with either of two schemes: **Inside** - fastest method evaluates whether a voxel is inside from only one direction or **Robust** - evaluates whether a voxel is inside from all three directions and a voting determines final status

The basic principle is to rasterize triangles using three 2D axis aligned grids, using integer arithmetic (fixed floating point) for robust triangle interior checks.
## Usage

The code now supports filling the interior with voxels, with an optional voting scheme in case the input mesh has cracks (not strictly watertight). The scheme is quite simple and efficient. A bit is flipped every time a surface is contained in a voxel. After all surfaces are rasterized into voxels, if the bit is set the voxel is considered on the boundary, otherwise it is considered empty. Thus, only voxels crossed by odd number of surfaces are considered as belonging to the boundary (this is a form of winding number). The intervals in between boundary voxels are then filled. This is done for all three directions to allow for a voting scheme in case the input mesh has cracks.
pyvoxsurf.**voxelize_stl**

Very simple, CPU only, no dependencies and surprisingly efficient despite the straightforward implementation. Higher resolutions could easily be reached by not storing the voxels as a dense 3D array (e.g. use blocking or an octree).
| Argument | Type | Default | Description |
| ------------- | ------------- | ------------- | ------------- |
| `filename` | string | | Filename of .stl file
| `resolution` | integer | | Number of voxel slices in the z-axis
| `bounds` | [2x3] array | | [Optional] Min and max bounds in (x, y, z) coordinates of desired voxel volume
| `voxel_fill` | string | "None" | [Optional] "None", "Inside", or "Robust" type of filling

Here is a relatively large model voxelized at 1024^3 in ~1.5 seconds on a Core i5-3570, 3.4GHz. (Model: [Ford engine block by Ford](https://www.thingiverse.com/thing:40257), rendered in MagicaVoxel viewer).

![voxels](vox1024.jpg)
```python
import pyvoxsurf
from mayavi import mlab

## Compiling
volume1 = pyvoxsurf.voxelize_stl("model.stl",200,[],"Robust")
print(volume1.shape)

Clone the main repo, then enter the directory and type:<br>
```
git submodule init
git submodule update
cmake .
make
# Visualize voxelized model
from tvtk.util.ctf import PiecewiseFunction
mlab.figure(size=(800,800))
vol = mlab.pipeline.volume(mlab.pipeline.scalar_field(volume1))
mlab.title('Voxelized model',height=0.9,size=0.5)
mlab.orientation_axes()
otf = PiecewiseFunction()
otf.add_point(0,0)
otf.add_point(0.001, 1)
otf.add_point(1,1)
vol._otf = otf
vol._volume_property.set_scalar_opacity(otf)
mlab.show()
```
![volume1](https://raw.githubusercontent.com/jttoombs/PyVoxSurf/master/docs/volume1.png)

pyvoxsurf.**voxelize**

| Argument | Type | Default | Description |
| ------------- | ------------- | ------------- | ------------- |
| `vertices` | [nx3] array | | Vertex positions in (x, y, z) coordinates
| `triangle_indices` | [nx3] array | | Indices of connected vertices forming triangles of mesh
| `bounds` | [2x3] array | | Min and max bounds in (x, y, z) coordinates of desired voxel volume
| `resolution` | integer | | Number of voxel slices in the z-axis
| `voxel_fill` | string | "None" | [Optional] "None", "Inside", or "Robust" type of filling

```python
import pyvoxsurf
import trimesh
import numpy as np
from mayavi import mlab

mesh = trimesh.load("model.stl") # Load stl file

Tested with Viusal Studio 2017 and gcc 6.2.1
# Find the max and min coordinates of the mesh to form a bounding box
mesh_min_corner = [np.min(mesh.vertices[:,0]), np.min(mesh.vertices[:,1]), np.min(mesh.vertices[:,2])]
mesh_max_corner = [np.max(mesh.vertices[:,0]), np.max(mesh.vertices[:,1]), np.max(mesh.vertices[:,2])]
bounds = np.stack((mesh_min_corner,mesh_max_corner))

volume2 = pyvoxsurf.voxelize(mesh.vertices,mesh.faces,bounds,100,"Inside")
print(volume2.shape)

# Visualize voxelized model
from tvtk.util.ctf import PiecewiseFunction
mlab.figure(size=(800,800))
vol = mlab.pipeline.volume(mlab.pipeline.scalar_field(volume2))
mlab.title('Voxelized model',height=0.9,size=0.5)
mlab.orientation_axes()
otf = PiecewiseFunction()
otf.add_point(0,0)
otf.add_point(0.001, 1)
otf.add_point(1,1)
vol._otf = otf
vol._volume_property.set_scalar_opacity(otf)
mlab.show()

```
![volume2](https://raw.githubusercontent.com/jttoombs/PyVoxSurf/master/docs/volume2.png)

## Links
* CUDA voxelization (GPU, fast) https://github.com/Forceflow/cuda_voxelizer
* Michael Schwarz and Hans-Peter Seidel paper on the topic http://research.michael-schwarz.com/publ/files/vox-siga10.pdf
* Header only voxelization in C https://github.com/karimnaaji/voxelizer

## Credits
The included STL model is [3D knot by chylld](https://www.thingiverse.com/thing:5506/#files)

- [VoxSurf](https://github.com/sylefeb/VoxSurf) by sylefeb
- [VoxSurf Pybind11 bindings](https://github.com/mjgalindo/VoxSurf) by mjgalindo
- PyVoxSurf packaging and documentation by jttoombs
- STL model of [3D knot by
chylld](https://www.thingiverse.com/thing:5506/#files)
Binary file added docs/volume1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/volume2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
0 model.stl → examples/model.stl
100755 → 100644
File renamed without changes.
49 changes: 49 additions & 0 deletions examples/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import pyvoxsurf
from mayavi import mlab

volume1 = pyvoxsurf.voxelize_stl("model.stl",200,[],"Robust")
print(volume1.shape)

# Visualize voxelized model
from tvtk.util.ctf import PiecewiseFunction
mlab.figure(size=(800,800))
vol = mlab.pipeline.volume(mlab.pipeline.scalar_field(volume1))
mlab.title('Voxelized model',height=0.9,size=0.5)
mlab.orientation_axes()
otf = PiecewiseFunction()
otf.add_point(0,0)
otf.add_point(0.001, 1)
otf.add_point(1,1)
vol._otf = otf
vol._volume_property.set_scalar_opacity(otf)
mlab.show()


import pyvoxsurf
import trimesh
import numpy as np
from mayavi import mlab

mesh = trimesh.load("model.stl") # Load stl file

# Find the max and min coordinates of the mesh to form a bounding box
mesh_min_corner = [np.min(mesh.vertices[:,0]), np.min(mesh.vertices[:,1]), np.min(mesh.vertices[:,2])]
mesh_max_corner = [np.max(mesh.vertices[:,0]), np.max(mesh.vertices[:,1]), np.max(mesh.vertices[:,2])]
bounds = np.stack((mesh_min_corner,mesh_max_corner))

volume2 = pyvoxsurf.voxelize(mesh.vertices,mesh.faces,bounds,100,"Inside")
print(volume2.shape)

# Visualize voxelized model
from tvtk.util.ctf import PiecewiseFunction
mlab.figure(size=(800,800))
vol = mlab.pipeline.volume(mlab.pipeline.scalar_field(volume2))
mlab.title('Voxelized model',height=0.9,size=0.5)
mlab.orientation_axes()
otf = PiecewiseFunction()
otf.add_point(0,0)
otf.add_point(0.001, 1)
otf.add_point(1,1)
vol._otf = otf
vol._volume_property.set_scalar_opacity(otf)
mlab.show()
93 changes: 93 additions & 0 deletions lib/LibSL-small/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

PROJECT(LibSL-small)

SET(LibSL_DIR ${CMAKE_CURRENT_SOURCE_DIR})

INCLUDE_DIRECTORIES(
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/src/LibSL
${CMAKE_CURRENT_SOURCE_DIR}/src/LibSL/loki
)

SET(LIBSL_SMALL_CORE
src/LibSL/LibSL.h
src/LibSL/CppHelpers/BasicParser.h
src/LibSL/CppHelpers/CppHelpers.cpp
src/LibSL/CppHelpers/CppHelpers.h
src/LibSL/Errors/Errors.h
src/LibSL/Geometry/AAB.h
src/LibSL/Image/Filter.h
src/LibSL/Image/Image.cpp
src/LibSL/Image/Image.h
src/LibSL/Image/ImageFormat_TGA.cpp
src/LibSL/Image/ImageFormat_TGA.h
src/LibSL/Image/tga.cpp
src/LibSL/Image/tga.h
src/LibSL/Math/Math.cpp
src/LibSL/Math/Math.h
src/LibSL/Math/Matrix4x4.h
src/LibSL/Math/Tuple.h
src/LibSL/Math/Vertex.cpp
src/LibSL/Math/Vertex.h
src/LibSL/Memory/Array.h
src/LibSL/Memory/Array2D.h
src/LibSL/Memory/Array3D.h
src/LibSL/Memory/ArrayTools.h
src/LibSL/Memory/Pointer.h
src/LibSL/Mesh/Mesh.cpp
src/LibSL/Mesh/Mesh.h
src/LibSL/Mesh/MeshFormat_stl.cpp
src/LibSL/Mesh/MeshFormat_stl.h
src/LibSL/Mesh/VertexFormat.h
src/LibSL/Mesh/VertexFormat_base.h
src/LibSL/Mesh/VertexFormat_descriptor.h
src/LibSL/Mesh/VertexFormat_dynamic.cpp
src/LibSL/Mesh/VertexFormat_dynamic.h
src/LibSL/Mesh/VertexFormat_reference.h
src/LibSL/StlHelpers/StlHelpers.cpp
src/LibSL/StlHelpers/StlHelpers.h
src/LibSL/SvgHelpers/SvgHelpers.cpp
src/LibSL/System/System.cpp
src/LibSL/System/System.h
src/LibSL/System/Types.h
)

IF(NOT WIN32)
SET(LIBSL_SMALL_CORE
${LIBSL_SMALL_CORE}
src/LibSL/Image/ImageFormat_JPG.cpp
src/LibSL/Image/ImageFormat_JPG.h
src/LibSL/Image/ImageFormat_PNG.cpp
src/LibSL/Image/ImageFormat_PNG.h
)
ENDIF(NOT WIN32)

ADD_LIBRARY(LibSL STATIC ${LIBSL_SMALL_CORE})

IF(WIN32)
TARGET_LINK_LIBRARIES(LibSL shlwapi psapi)
ELSE(WIN32)
FIND_PACKAGE( ZLIB REQUIRED )
TARGET_LINK_LIBRARIES(LibSL png jpeg pthread ${ZLIB_LIBRARIES})
ENDIF(WIN32)

SET_TARGET_PROPERTIES(LibSL PROPERTIES DEBUG_POSTFIX "-d")
SET_PROPERTY(TARGET LibSL APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES
${CMAKE_CURRENT_SOURCE_DIR}/src/
${CMAKE_CURRENT_SOURCE_DIR}/src/LibSL
${CMAKE_CURRENT_SOURCE_DIR}/src/LibSL/loki/
)

ADD_DEFINITIONS(-D_UNICODE -DUNICODE)

# Enable max performance
ADD_DEFINITIONS("-DNDEBUG")
ADD_DEFINITIONS("-DLIBSL_RELEASE")

INSTALL(TARGETS LibSL
RUNTIME DESTINATION ${CMAKE_SOURCE_DIR}/bin
LIBRARY DESTINATION ${CMAKE_SOURCE_DIR}/lib
ARCHIVE DESTINATION ${CMAKE_SOURCE_DIR}/lib
)
21 changes: 21 additions & 0 deletions lib/LibSL-small/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2018 sylefeb

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
3 changes: 3 additions & 0 deletions lib/LibSL-small/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# LibSL-small
A tiny subset of [LibSL](https://github.com/sylefeb/LibSL) for use in stand-alone, no-dependency projects.
Typically included as a submodule from a parent git repo.
Loading