bgfx/3rdparty/openctm/doc/DevelopersManual.tex

679 lines
24 KiB
TeX
Raw Normal View History

2012-10-07 23:41:18 -04:00
%-------------------------------------------------------------------------------
% Document: OpenCTM Developers Manual
% Author: Marcus Geelnard
% Compile: pdflatex DevelopersManual.tex
%-------------------------------------------------------------------------------
% Note: You need a LaTeX environment to build this document as PDF. The
% recommended way is to install TeX Live (http://www.tug.org/texlive/) and
% a decent LaTeX editor (e.g. texmaker, LEd, etc).
%
% Ubuntu: sudo apt-get install texlive-full
% Mac OS X: http://www.tug.org/mactex/ (MacTeX.mpkg.zip)
%
% To build the PDF document, run pdflatex twice on this .tex file (in order to
% correctly build the TOC).
%-------------------------------------------------------------------------------
% Use the OpenCTM TeX style
\input{openctm-tex.sty}
% Document properties
\author{Marcus Geelnard}
\title{OpenCTM Developers Manual}
% PDF specific document properties
\hypersetup{pdftitle={OpenCTM Developers Manual}}
\hypersetup{pdfauthor={Marcus Geelnard}}
\hypersetup{pdfkeywords={OpenCTM,manual}}
% Document contents
\begin{document}
%--[ Title page ]---------------------------------------------------------------
\begin{titlepage}
\begin{center}
~
\vspace{5cm}
\includegraphics[width=10.0cm]{logo.pdf}
\vspace{0.4cm}
{\large Software Library version 1.0.3}
\vspace{1.0cm}
{\Large Developers Manual}
\vspace{1.5cm}
Copyright \copyright \ 2009-2010 Marcus Geelnard
\end{center}
\end{titlepage}
%--[ Table of contents ]--------------------------------------------------------
\tableofcontents
%-------------------------------------------------------------------------------
\chapter{Introduction}
The OpenCTM file format is an open format for storing 3D triangle meshes.
One of the main advantages over other similar file formats is its ability
to losslessly compress the triangle geometry to a fraction of the corresponding
raw data size.
This document describes how to use the OpenCTM API to load and save OpenCTM
format files. It is mostly written for C/C++ users, but should be useful for
other programming languages too, since the concepts and function calls are
virtually identical regardless of programming language.
For a complete reference to the OpenCTM API, please use the Doxygen generated
OpenCTM API Reference, which describes all API functions, types, constants etc.
%-------------------------------------------------------------------------------
\chapter{Concepts}
\section{The OpenCTM API}
The OpenCTM API makes it easy to read and write OpenCTM format files. The API is
implemented in the form of a software library that an application can be linked
to in order to access the OpenCTM API.
The software library itself is written in standard, portable C language, but
can be used from many other programming languages (writing language bindings
for new languages should be fairly straight forward, since the API was written
with cross-language portability in mind).
\section{The triangle mesh}
The triangle mesh, in OpenCTM terms, is managed in a format that is well suited
for a modern 3D rendering pipeline, such as OpenGL.
At a glance, the OpenCTM mesh has the following properties:
\begin{itemize}
\item A vertex is a set of attributes that uniquely identify the vertex.
This includes: vertex coordinate, normal, UV coordinate(s) and
custom vertex attribute(s) (such as color, weight, etc).
\item A triangle is described by three vertex indices.
\item In the OpenCTM API, these mesh data are treated as arrays (an integer
array for the triangle indices, and floating point arrays for the
vertex data).
\item All vertex data arrays in a mesh must have the same number of elements
(for instance, there is exactly one normal associated with each
vertex coordinate).
\item All mesh data are optional, except for the triangle indices and the
vertex coordinates. For instance, it is possible to leave out the
normal information.
\end{itemize}
For an example of the mesh data structure see table \ref{tab:MeshVert} (vertex
data) and table \ref{tab:MeshTri} (triangle data).
\begin{table}[p]
\centering
\begin{tabular}{|l|l|l|l|l|l|l|l|}\hline
\textbf{Index} & 0 & 1 & 2 & 3 & 4 & \textellipsis & N\\ \hline
\textbf{Vertex} & $v_0$ & $v_1$ & $v_2$ & $v_3$ & $v_4$ & \textellipsis & $v_N$\\ \hline
\textbf{Normal} & $n_0$ & $n_1$ & $n_2$ & $n_3$ & $n_4$ & \textellipsis & $n_N$\\ \hline
\textbf{UVCoord1} & $t1_0$ & $t1_1$ & $t1_2$ & $t1_3$ & $t1_4$ & \textellipsis & $t1_N$\\ \hline
\textbf{UVCoord2} & $t2_0$ & $t2_1$ & $t2_2$ & $t2_3$ & $t2_4$ & \textellipsis & $t2_N$\\ \hline
\textbf{Attrib1} & $a1_0$ & $a1_1$ & $a1_2$ & $a1_3$ & $a1_4$ & \textellipsis & $a1_N$\\ \hline
\textbf{Attrib2} & $a2_0$ & $a2_1$ & $a2_2$ & $a2_3$ & $a2_4$ & \textellipsis & $a2_N$\\ \hline
\end{tabular}
\caption{Mesh vertex data structure in OpenCTM, for a mesh with normals,
two UV coordinates per vertex, and two custom attributes per vertex.}
\label{tab:MeshVert}
\end{table}
\begin{table}[p]
\centering
\begin{tabular}{|l|l|l|l|l|l|l|l|}\hline
\textbf{Triangle} & $tri_0$ & $tri_1$ & $tri_2$ & $tri_3$ & $tri_4$ & \textellipsis & $tri_M$\\ \hline
\end{tabular}
\caption{Mesh triangle data structure in OpenCTM, where $tri_k$ is a tuple of
three vertex indices. For instance,
$tri_0=(0, 1, 2)$,
$tri_1=(0, 2, 3)$,
$tri_2=(3, 5, 4)$, \textellipsis}
\label{tab:MeshTri}
\end{table}
\subsection{Triangle indices}
\label{sec:MeshIndices}
Each triangle is described by three integers: one vertex index for each corner
of the triangle). The triangle index array looks like this:
\begin{tabular}{|l|l|l|l|l|l|l|l|l|l|}\hline
$tri^0_0$ & $tri^1_0$ & $tri^2_0$ & $tri^0_1$ & $tri^1_1$ & $tri^2_1$ & \textellipsis & $tri^0_M$ & $tri^1_M$ & $tri^2_M$\\ \hline
\end{tabular}
\textellipsis where $tri^j_k$ is the vertex index for the $j$:th corner of the
$k$:th triangle.
\subsection{Vertex coordinates}
Each vertex coordinate is described by three floating point values: $x$, $y$
and $z$. The vertex coordinate array looks like this:
\begin{tabular}{|l|l|l|l|l|l|l|l|l|l|l|}\hline
$x_0$ & $y_0$ & $z_0$ & $x_1$ & $y_1$ & $z_1$ & \textellipsis & $x_N$ & $y_N$ & $z_N$\\ \hline
\end{tabular}
\textellipsis where $x_k$, $y_k$ and $z_k$ are the $x$, $y$ and $z$ coordinates
of the $k$:th vertex.
\subsection{Normals}
Each normal is described by three floating point values: $x$, $y$
and $z$. The normal array looks like this:
\begin{tabular}{|l|l|l|l|l|l|l|l|l|l|l|}\hline
$x_0$ & $y_0$ & $z_0$ & $x_1$ & $y_1$ & $z_1$ & \textellipsis & $x_N$ & $y_N$ & $z_N$\\ \hline
\end{tabular}
\textellipsis where $x_k$, $y_k$ and $z_k$ are the $x$, $y$ and $z$ components
of the $k$:th normal.
\subsection{UV coordinates}
A mesh may have several UV maps, where each UV map is described by:
\begin{itemize}
\item A UV coordinate array.
\item A unique UV map name.
\item A file name reference (optional).
\end{itemize}
Each UV coordinate is described by two floating point values: $u$ and $v$.
A UV coordinate array looks like this:
\begin{tabular}{|l|l|l|l|l|l|l|l|l|l|}\hline
$u_0$ & $v_0$ & $u_1$ & $v_1$ & $u_2$ & $v_2$ & \textellipsis & $u_N$ & $v_N$\\ \hline
\end{tabular}
\textellipsis where $u_k$ and $v_k$ are the $u$ and $v$ components
of the $k$:th UV coordinate.
\subsection{Custom vertex attributes}
A mesh may have several custom vertex attribute maps, where each attribute map
is described by:
\begin{itemize}
\item A vertex attribute array.
\item A unique attribute map name.
\end{itemize}
Each vertex attribute is described by four floating point values: $a$, $b$, $c$
and $d$. An attribute array looks like this:
\begin{tabular}{|l|l|l|l|l|l|l|l|l|l|l|l|l|}\hline
$a_0$ & $b_0$ & $c_0$ & $d_0$ & $a_1$ & $b_1$ & $c_1$ & $d_1$ & \textellipsis & $a_N$ & $b_N$ & $c_N$ & $d_N$\\ \hline
\end{tabular}
\textellipsis where $a_k$, $b_k$, $c_k$ and $d_k$ are the four attribute values
of the $k$:th attribute.
\section{The OpenCTM context}
The OpenCTM API uses a \emph{context} for almost all operations (function calls).
The context is created and destroyed with the functions ctmNewContext() and
ctmFreeContext(), respectively.
A program may instantiate any number of contexts, and all OpenCTM function
calls are completely thread safe (multiple threads can use the OpenCTM API
at the same time), as long as each context instance is handled by a single
thread.
Each context is fully self contained and independent of other contexts.
There are two types of OpenCTM context: \emph{import contexts} and
\emph{export contexts}. Import contexts are used for importing OpenCTM files,
and export contexts are used for exporting OpenCTM files.
The context type is selected when creating the context.
%-------------------------------------------------------------------------------
\chapter{Compression Methods}
The OpenCTM file format supports a few different compression methods, each
with its own advantages and disadvantages. The API makes it possible to
select which method to use when creating OpenCTM files (the default method
is MG1).
\section{RAW}
The RAW compression method is not really a compression method, since it only
stores the data in a raw, uncompressed form. The result is a file with the same
size and data format as the in-memory mesh data structure.
The RAW method is mostly useful for testing purposes, but can be preferred in
certain situations, for instance when file writing speeds and a small memory
footprint is more important than minimizing file sizes.
Another situation where the RAW method can be useful is when you need an
easily parsable binary file format. Usually the OpenCTM API can be used in
almost any application, but in some environments, such as certain script
languages or data inspecion tools, it can be handy to have access to the
raw data.
\section{MG1}
The MG1 compression method effectively reduces the size of the mesh data
by re-coding the connectivity information of the mesh into an easily
compressible format. The data is then compressed using LZMA.
The floating point data, such as vertex coordinates and normals, is fully
preserved in the MG1 method, by simply applying lossless LZMA compression
to it.
Under typical condititions, the connectivity information is compressed to
about two bytes per triangle (17\% of the original size), and vertex data
is compressed to about 75\% of the original size.
While creating MG1 files can be a relatively slow process (compared to the
RAW method, for instance) the reading speed is usually very high, thanks to
the fast LZMA decoder and the uncomplicated data format.
\section{MG2}
The MG2 compression method offers the highest level of compression among the
different OpenCTM methods. It uses the same method for compressing connectivity
information as the MG1 method, but does a better job at compressing vertex
data.
Vertex data is converted to a fixed point representation, which allows for
efficient, lossless, prediction based data compression algorithms.
In short, the MG2 method divides the mesh into small sub-spaces, sorts the data
geometrically, and applies delta-prediction to the data, which effectively
lowers the data entropy. The re-coded vertex data is then compressed with
LZMA.
When using the OpenCTM API for creating MG2 files you can trade mesh resolution
for compression ratio, and the API provides several functions for controlling
the resolution of different vertex attributes independently. Therefor it is
usually important to know the resolution requirements for your specific
application when using the MG2 method.
In some applications, such as games, movies and art, it is important that the
3D model is not visually degraded by compression. In such applications
you will typically tune your resolution settings using trial and error,
until you find a setting that does not alter the model visually.
In other applications, such as CAD/CAM, 3D scanning, calibration, etc,
reasonable resolution settings can usually be derived from the limitations
of the process in which the model is used. For instance, there is usually no
need for nanometer precision in the design of an airplane wing, and there
is little use of micrometer resolution in a manufacturing process that can
not reproduce features smaller than 0.15 mm.
As a side effect of the fact that MG2 produces smaller files than the MG1
method does, loading files is usually faster with the MG2 method than with
the MG1 method. Saving files with the MG2 method is about as fast as with
the MG1 method.
%-------------------------------------------------------------------------------
\chapter{Basic Usage}
\section{Prerequisites}
To use the OpenCTM API, you need to include the OpenCTM include file, like this:
\begin{lstlisting}
#include <openctm.h>
\end{lstlisting}
You also need to link with the OpenCTM import library. For instance, in MS
Visual Studio you can add "openctm.lib" to your Additional Dependencies field
in the Linker section. For gcc/g++ or similar compilers, you will typically
add -lopenctm to the list of compiler options, for instance:
\begin{lstlisting}
> g++ -o foo foo.cpp -lopenctm
\end{lstlisting}
\section{Loading OpenCTM files}
Below is a minimal example of how to load an OpenCTM file with the OpenCTM API,
in just a few lines of code:
\begin{lstlisting}
CTMcontext context;
CTMuint vertCount, triCount, * indices;
CTMfloat * vertices;
// Create a new importer context
context = ctmNewContext(CTM_IMPORT);
// Load the OpenCTM file
ctmLoad(context, "mymesh.ctm");
if(ctmGetError(context) == CTM_NONE)
{
// Access the mesh data
vertCount = ctmGetInteger(context, CTM_VERTEX_COUNT);
vertices = ctmGetFloatArray(context, CTM_VERTICES);
triCount = ctmGetInteger(context, CTM_TRIANGLE_COUNT);
indices = ctmGetIntegerArray(context, CTM_INDICES);
// Deal with the mesh (e.g. transcode it to our
// internal representation)
// ...
}
// Free the context
ctmFreeContext(context);
\end{lstlisting}
\section{Creating OpenCTM files}
Below is a minimal example of how to save an OpenCTM file with the OpenCTM API,
in just a few lines of code:
\begin{lstlisting}
void MySaveFile(CTMuint aVertCount, CTMuint aTriCount,
CTMfloat * aVertices, CTMuint * aIndices,
const char * aFileName)
{
CTMcontext context;
// Create a new exporter context
context = ctmNewContext(CTM_EXPORT);
// Define our mesh representation to OpenCTM
ctmDefineMesh(context, aVertices, aVertCount, aIndices, aTriCount, NULL);
// Save the OpenCTM file
ctmSave(context, aFileName);
// Free the context
ctmFreeContext(context);
}
\end{lstlisting}
%-------------------------------------------------------------------------------
\chapter{Controlling Compression}
When creating OpenCTM files, one of the most important things to control with
the API is the compression method.
\section{Selecting the compression method}
You can select which compression method to use with the ctmCompressionMethod()
function. The different options are:
\begin{tabular}{|l|l|}\hline
\textbf{Name} & \textbf{Description}\\ \hline
CTM\_METHOD\_RAW & Use the RAW compression method.\\ \hline
CTM\_METHOD\_MG1 & Use the MG1 compression method (default).\\ \hline
CTM\_METHOD\_MG2 & Use the MG2 compression method.\\ \hline
\end{tabular}
For instance, to select the MG2 compression method for a given OpenCTM context,
use:
\begin{lstlisting}
ctmCompressionMethod(context, CTM_METHOD_MG2);
\end{lstlisting}
\section{Selecting the compression level}
You can select which LZMA compression level to use with the ctmCompressionLevel()
function. The compression level can be in the range 0-9, where 0 is the fastest
compression, and 9 is the best compression. The compression level also affects the
amount of memory that is used during compression (anywhere from a few megabytes to
several hundred megabytes).
\begin{lstlisting}
ctmCompressionMethod(context, 4);
\end{lstlisting}
The default compression level is 1.
\section{Selecting fixed point precision}
When the MG2 compression method is used, further compression control is provided
through the API that deals with the fixed point precision for different vertex
attributes. The different attribute precisions that can be controlled are:
\begin{tabular}{|l|l|}\hline
\textbf{Attribute} & \textbf{API function}\\ \hline
Vertex coordinate & ctmVertexPrecision() / ctmVertexPrecisionRel()\\ \hline
Normal & ctmNormalPrecision()\\ \hline
UV coordinates & ctmUVCoordPrecision()\\ \hline
Custom attributes & ctmAttribPrecision()\\ \hline
\end{tabular}
Reasonable default values for the fixed point precisions are selected by the API
unless the corresponding API functions are called. However, the API does not know
the requirements for the mesh, which is why it is always a good idea to specify
the fixed point precision that is most relevant for your specific mesh.
\subsection{Vertex coordinate precision}
The vertex coordinate precision can be controlled in two ways:
\begin{itemize}
\item Absolute precision - ctmVertexPrecision().
\item Relative precision - ctmVertexPrecisionRel().
\end{itemize}
You typically specify the absolute precision when you know the properties of the
mesh and what is going to be used for (for instance, if it is a product of a
measurment procecss, or if it will be used in a manufacturing process). For
example, if the vertex coordinate unit is meters, and the precision is specified
as $0.001$, the fixed point precision will be 1 mm:
\begin{lstlisting}
ctmVertexPrecision(context, 0.001);
\end{lstlisting}
When you do not know much about the mesh, it can be useful to specify the
relative precision. The ctmVertexPrecisionRel() function will analyze the mesh
to find a useful base measure, which is multiplied by a scaling factor that
is given as an argument to the function.
The relative precision function uses the average triangle edge length as the
base measure. So for example, if you specify $0.01$ as the relative precision,
the precision will be 1\% of the average triangle edge length, which is usually
a good figure for meshes that will be used in visualization applications:
\begin{lstlisting}
ctmVertexPrecisionRel(context, 0.01);
\end{lstlisting}
It should be noted that unlike the ctmVertexPrecision() function, the
ctmVertexPrecisionRel() function requires that the mesh has been specified
before calling the function.
The default vertex coordinate precision is $2^{-10} \approx 0.00098$.
\subsection{Normal precision}
In the MG2 compression method, each vertex normal is represented in spherical
coordinates (the coordinate system is aligned to the average normal of all
triangles that connect to the vertex).
The precision controls both the angular resolution and the radial resolution
(magnitude). For instance, $0.01$ means that the circle is divided into 100
steps, and the normal magnitude is rounded to 2 decimals:
\begin{lstlisting}
ctmNormalPrecision(context, 0.01);
\end{lstlisting}
The default normal precision is $2^{-8} \approx 0.0039$.
\subsection{UV coordinate precision}
UV coordinate precision is specified on a per UV map basis, and
gives the absolute precision in UV coordinate space.
The effects of different precisions depend on many different things. For
instance if the UV map is used for mapping a 2D texture onto the triangle
mesh, the resolution of the texture can influence the required UV
coordinate precision (e.g. a 4096x4096 texture may require better
precision than a 256x256 texture). The resolution of the mesh may also
affect the required UV coordinate precision.
To specify a resolution of $0.001$ for the UV map $uvMap$, use:
\begin{lstlisting}
ctmUVCoordPrecision(context, uvMap, 0.001);
\end{lstlisting}
The default UV coordinate precision is $2^{-12} \approx 0.00024$.
\subsection{Custom attribute precision}
As with UV coordinates, the precision for custom vertex attributes are
specified on a per attribute basis.
The precision of a custom attribute depends entirely on the type of
attribute. For instance, standard color attributes typically do not require
more then eigh bits per component, which means that $1/256$ is a good
precision setting (if the value range is $[0,1]$):
\begin{lstlisting}
ctmAttribPrecision(context, attribMap, 1.0/256.0);
\end{lstlisting}
For integer values, the precision $1.0$ is a good choice.
The default vertex attribute precision is $2^{-8} \approx 0.0039$.
%-------------------------------------------------------------------------------
\chapter{Error Handling}
An error can occur when calling any of the OpenCTM API functions. To check
for errors, call the ctmGetError() function, which returns a positive error
code if something went wrong, or zero (CTM\_NONE) if no error has occured.
See \ref{tab:ErrorCodes} for a list of possible error codes.
\begin{table}[p]
\centering
\begin{tabular}{|l|p{7cm}|}\hline
\textbf{Code} & \textbf{Description}\\ \hline
CTM\_NONE (zero) & No error has occured (everything is OK).\\ \hline
CTM\_INVALID\_CONTEXT & The OpenCTM context was invalid (e.g. NULL).\\ \hline
CTM\_INVALID\_ARGUMENT & A function argument was invalid.\\ \hline
CTM\_INVALID\_OPERATION & The operation is not allowed.\\ \hline
CTM\_INVALID\_MESH & The mesh was invalid (e.g. no vertices).\\ \hline
CTM\_OUT\_OF\_MEMORY & Not enough memory to proceed.\\ \hline
CTM\_FILE\_ERROR & File I/O error.\\ \hline
CTM\_BAD\_FORMAT & File format error (e.g. unrecognized format or corrupted file).\\ \hline
CTM\_LZMA\_ERROR & An error occured within the LZMA library.\\ \hline
CTM\_INTERNAL\_ERROR & An internal error occured (indicates a bug).\\ \hline
CTM\_UNSUPPORTED\_FORMAT\_VERSION & Unsupported file format version.\\ \hline
\end{tabular}
\caption{OpenCTM error codes.}
\label{tab:ErrorCodes}
\end{table}
The last error code that indicates a failure is stored per OpenCTM context
until the ctmGetError() function is called. Calling the function will reset
the error state.
It is also possible to convert an error code to an error string, using the
ctmErrorString() function, which takes an error code as its argument, and
returns a constant C string (pointer to a null terminated UTF-8 format
character string).
%-------------------------------------------------------------------------------
\chapter{C++ Extensions}
To take better advantage of some of the C++ language features, such as
exception handling, a few C++ wrapper classes are availbale through the standard
API when compiling a C++ program. As usual, just include "openctm.h", and you
will have access to two C++ classes: CTMimporer and CTMexporter.
The main differences between the C++ classes and the standard API are:
\begin{itemize}
\item The C++ classes call ctmNewContext() and ctmFreeContext() in their
constructors and destructors respectively, which makes it easier to
use the C++ dynamic scope mechanisms (such as exception handling).
\item Whenever an OpenCTM error occurs, an exception is thrown. Hence, there
is no method corresponding to the ctmGetError() function.
\end{itemize}
\section{The CTMimporter class}
Here is an example of how to use the CTMimporter class in C++:
\begin{lstlisting}
try
{
// Create a new OpenCTM importer object
CTMimporter ctm;
// Load the OpenCTM file
ctm.Load("mymesh.ctm");
// Access the mesh data
CTMuint vertCount = ctm.GetInteger(CTM_VERTEX_COUNT);
CTMfloat * vertices = ctm.GetFloatArray(CTM_VERTICES);
CTMuint triCount = ctm.GetInteger(CTM_TRIANGLE_COUNT);
CTMuint * indices = ctm.GetIntegerArray(CTM_INDICES);
// Deal with the mesh (e.g. transcode it to our
// internal representation)
// ...
}
catch(exception &e)
{
cout << "Error: " << e.what() << endl;
}
\end{lstlisting}
\section{The CTMexporter class}
Here is an example of how to use the CTMexporter class in C++:
\begin{lstlisting}
void MySaveFile(CTMuint aVertCount, CTMuint aTriCount,
CTMfloat * aVertices, CTMuint * aIndices,
const char * aFileName)
{
try
{
// Create a new OpenCTM exporter object
CTMexporter ctm;
// Define our mesh representation to OpenCTM
ctm.DefineMesh(aVertices, aVertCount, aIndices, aTriCount, NULL);
// Save the OpenCTM file
ctm.Save(aFileName);
}
catch(exception &e)
{
cout << "Error: " << e.what() << endl;
}
}
\end{lstlisting}
\end{document}