Thea
|
A toolkit for visual computing with a focus on geometry processing, especially for interactive 3D modeling applications.
Source code is available at https://github.com/sidch/Thea.
Online documentation is available at https://sidch.github.io/Thea.
Author: Siddhartha Chaudhuri. Released under the BSD license (see LICENSE.txt
).
If you find a bug, please let me know promptly. Thank you!
!!! IMPORTANT !!! If you last cloned this repository before Sep 19, 2021, please delete your local copy and re-clone a fresh copy. Some history was rewritten during a cleanup and commit refs have changed. If you just do a git pull
without deleting and re-cloning, bad things can happen when the old and new histories get merged (Git will hopefully warn you about this).
Thea is a library of C++ classes for computer graphics, primarily for 3D geometry processing. It is the core library I use for nearly all my research projects, and it is also the core library for Adobe Fuse, which I originally authored. As such, it is developed for personal use and its features reflect this: please do not write to me to asking for specific features to be included. However, over time, it has become quite general-purpose. Among its features are:
Thea is constantly under development and a few parts are incomplete. Use at your own risk! I do not provide any support (unless you have bugs to report), and I make no correctness or robustness guarantees for any part of the code. Parts of the library are reasonably battle-tested (e.g. in Fuse), and parts are one-off inclusions rarely used in anger or tested thoroughly.
Thea is heavily influenced by Morgan McGuire's G3D library. It started out as an extension of G3D and still has significant chunks of code adapted from it (e.g. for binary/text I/O, plugin management, the rendersystem interface and OpenGL plugin, color handling, timers, quaternions, CRC32, atomic integers, and random numbers).
The Thea library is not related to the independently and contemporaneously developed Thea Render photorealistic rendering engine.
Thea has a particular focus on being a foundation layer for interactive 3D modeling applications. It is written in standards-compliant C++11 for speed, and is designed with the following objectives in mind:
BvhN
) can efficiently support any bounded objects in any-dimensional space under any metricVector3 = Eigen::Matrix<Real, 3, 1, MatrixLayout::COLUMN_MAJOR | Eigen::DontAlign>
)enable_if
+ type_traits
is often better than polymorphismThere are a few more specific design choices that apply to specific submodules. Since Thea focuses on interactive 3D modeling, the main mesh class (GeneralMesh
) is optimized for fast local updates on the CPU, with low-overhead synchronization with the GPU. Specifically:
Contrast this with libraries optimized for static geometry and/or global processing passes, e.g. trimesh2 or libigl, which represent meshes as dense arrays of elements, referenced by integer indices. Of course, Thea also has another mesh class (DisplayMesh
) which has similar behaviour and is a good choice for more static applications, as well as a halfedge data structure (DCELMesh
). In conjunction, Thea's bounding volume hierarchy class (BvhN
), used to detect UI interactions with a mesh, tries to minimize the latency of recomputing the tree after the underlying geometry is changed.
Here is a simple "Hello World" example:
Here is a more complicated example, to perform 3D shape remeshing. We will do this in four steps:
Let's see the code for these steps one at a time. For simplicity, we'll omit some generic boilerplate code and #include
directives, and assume
commands have been issued in advance.
First, we will load the input mesh:
A MeshGroup
is a hierarchical collection of meshes, encoding a scene graph. This matches the structure of most general-purpose mesh formats. A GeneralMesh
object represents a node of this graph, packing vertex, edge and face information for a single component.
Next, we'll create a "functor" class that evaluates a signed distance field around the shape. It returns 0 for points on the surface of the shape, positive values for points outside the shape boundary, and negative values for points inside the shape boundary.
Note that we are using a convenient specialization of the general bounding volume hierarchy class (BvhN
) for polygon meshes, for which we enable acceleration of nearest neighbor queries using a proxy BVH of sample points. The computation and use of this internal acceleration structure is abstracted away behind a single function call. Also, Thea supports the L2 distance metric by default, but the BVH can take any other metric as a template argument, and limit the search to a maximum distance. intx
is Thea's default integer type used for indexing, defined as std::ptrdiff_t
.
Now, we'll do the actual remeshing:
Stopwatch
is one of many utility classes. ImplicitSurfaceMesher
also optionally wraps CGAL's Boissonnat-Oudot polygonizer. THEA_CONSOLE
is an object that behaves exactly like std::cout
but automatically adds a newline at the end. THEA_WARNING
and THEA_ERROR
also prefix the line with the current time, source filename, line number, and a warning/error flag.
Finally, we'll save the output mesh:
Figure: (Left) Input mesh. (Right) After remeshing. Artifacts are because of the specific (Bloomenthal) polygonizer used. Thea's bundled RenderShape utility was used to render these images.
Here is a complete, compilable version of the above example: remesh.cpp
For some real-world samples, see the applications in the Thea/Code/Source/Tools
folder.
Thea is written in standards-compliant C++11, and should compile with any recent compiler on Mac, Linux and Windows. It uses CMake as a cross-platform buildsystem. However, I do not normally work on Windows, and do not currently provide build instructions for this platform. I have successfully done Windows builds in the past and there is no reason why it should not work with a bit of effort getting the dependencies installed. I will try to add full Windows instructions in the future, time permitting.
Thea relies on Boost, Eigen (3.3 or later), lib3ds, FreeImage and ARPACK. (A couple of classes also depend on CGAL, but these are optional – if CGAL is not found on the system, these classes will be omitted from the build.) A convenient script installs all of these on Unix-like systems (Mac and Linux), as follows. Both local (no root) and system-wide (needs root) installs are supported.
Assume $basedir
is some directory where you're going to check out the source code, and $prefix
is some directory where you'll install stuff (e.g. $basedir/Installations
or /usr/local
).
For a local install (no root perms needed to write to $prefix
):
For a system-wide install:
--use-root
will try to use apt-get
on Ubuntu/Debian, omit it if you want to build everything from scratch regardless. --with-wxwidgets
is needed to build Browse3D, a bundled GUI application for viewing 3D files: it can be omitted if so desired. Add --with-osmesa
to install OSMesa for headless CPU-only rendering (good for remote servers). Replace 4 with the actual number of hardware threads on your system, typically 2, 4, or 8.
The above step will install the necessary libraries by compiling them from source (if not apt-get
able) and placing the result in $prefix
. To install an individual library, call install.sh --with-<package> ...
with the same --use-root
, --prefix
etc arguments as above. Carefully check for errors (warnings are generally ok). If there are errors, you probably need to explicitly install some third-party libraries/tools – see the error messages – and rerun the command. E.g. I have found some barebones server installs without the zlib or Expat development packages, required by Mesa: in this particular case, the source packages are included and you can use ./install.sh --with-expat --with-zlib ...
. Make sure there are no errors in the output before proceeding further.
Assuming there were no errors while installing the dependencies, execute the following commands:
By default, CMake looks for the dependencies in the CMAKE_INSTALL_PREFIX
directory. If for some reason the dependencies are located somewhere else, e.g. in $deps
, you can point CMake to it by adding -DTHEA_DEPS_ROOT="$deps"
. The bundled tools are installed to $prefix/bin/Thea
: add this to your executable search path (e.g. the system PATH
variable) as needed. A quick way to check if everything has installed correctly is to run
and see if a window pops up displaying a 3D teapot, or
to render the teapot to an image file.
To build with OSMesa instead of the system OpenGL driver, add -DWITH_OSMESA=true
to the CMake line above. The RenderShape tool will then use OSMesa. To run some test scripts (several probably out of date), run make test
after building. To omit building the tests altogether, pass -DWITH_TESTS=false
to CMake. To change the build type (by default Release
), set -DCMAKE_BUILD_TYPE=Debug|Release|RelWithDebInfo
.
C++ versions: Thea itself follows the C++11 standard. However, other packages it depends on may enforce more recent standards: e.g. CGAL has recently moved to C++14 by default. Hence, the build script detects the latest standard (upto C++17) supported by the compiler and uses that to build. To force the compiler to operate in strict C++11 mode, pass -DFORCE_CXX11=true
to CMake.
To generate HTML documentation for the API, run Doxygen in the Thea/Code/Documentation
folder. Then, open html/index.html
in a browser.
This is probably the best place to start looking at the toolkit.
Note that many convenience types, such as Vector3
and Matrix4
, are typedefs (for Eigen::Matrix<Real, 3, 1, ...>
and Eigen::Matrix<Real, 4, 4, ...>
respectively) and don't show up in the Class Index. Some will show up in Namespaces –> Namespace Members –> Typedefs. For others, you will have to look at the source code. Documenting all of these properly is work-in-progress.)
GCC/Clang-specific: You MUST compile with strict aliasing turned OFF. This is achieved with -fno-strict-aliasing
. I also recommend -Wall -g2 -O2
(all "W"arnings, debu"g"gable binaries, "O"ptimize for speed). -O2
messes up the debugging a bit so turn it off temporarily if you can't track down your bug.
The usual command line to link your program with the library is:
If you're using CMake for your own code, a convenient FindThea.cmake
module in Thea/Code/Build/Common/CMake/Modules
(or directly from https://github.com/sidch/CMake) allows you to do FIND_PACKAGE(Thea)
, including locating all the necessary dependencies.