The SFL file format has been introduced to provide a versatile file format to import and export Pointshop3D surfel objects and scenes. It's a binary format featuring an extensible set of surfel attributes, data compression, upward and downward compatibility, and transparent conversion of surfel attributes, coordinate systems, and color spaces.
This section gives a quick overview over features and technology of the SFL file format. For more information on the underlying technique, see The GFF Layer. The internal structure of an SFL file is describe in The SFL File Structure. An Introduction to the library's programming interface is given in SFL Application Interface. For a quick start refer to Example SFL Reader (Conversion to ASCII) or Example SFL Writer (Conversion from ASCII).
Surfel files tend to get larger than triangle meshes in terms of their surfel/vertex count. A potentially large number of attributes per surfel even increases the potential size of a surfel object definition. Thus, although an ASCII file format would have been easier to modify, we decided for a binary representation. The binary representation is decoupled from the local byte order, using network byte order by definition.
As the concept of a surfel includes an arbitrary set of surface properties associated with every surface sample, the format had to be extendible. The SFL format supports the addition of arbitrary surfel attributes without losing upward or downward compatibility.
With the development of SFL, we targeted a set of filter programs that apply simple algorithms to an object stored as SFL file. In the spirit of Unix filters, it should be possible to concatenate these filters using pipes. Therefore it was taken care that the SFL library can work also on non-seekable streams.
The SFL format is implemented as a instantiation of a more general file format, see The GFF Layer.
An SFL file is a container for scenes containing multiple surfel objects, each with its own transformation matrix. Each object can have different surfel attributes and be stored at multiple resolution levels.
The SFL format also contains meta-information regarding creation information (Author, Application, Time, ...), scene properties (camera matrix, comments, ...), coordinate system (left/right, x/y/z-up, units, ...), and color space (rgba, la, yuva).
SFL files are supposed to be read and written using the SFL library. This library contains no further library dependencies, in particular no dependencies from Pointshop3D. This should facilitate its usage in different software environments and on different operation systems.
By design, there is no imposition of the use of certain data structures. Any existing application should be able to use the SFL library withour having to change its internal representations.
There are certain surfel attributes that provide similar semantics using different representations (normal/radius vs. local tangent axes; single color vs. ambient/diffuse/specular colors; ...). As the supported set of attributes is likely to grow, older applications that do not know about later concepts wouldn't be able to read the new files, even if a suited interpretation of the data would exist. For those cases the library features a hidden conversion layer.
If, for example, an application only knows normal/radius splat definitions, but does not support the concept of tangent axes, it can declare its preferred representation (normal/radius in this example) to the library. If a file with tangent axes definition is read, the library converts this representation transparently to normal/radius. This would allow the application to read surfel files with elliptical splat definitions and to approximate it by circular splats.
Automatic conversions also cover color space transformation, as well as the transformation of coordinate systems (y-up/z-up; left-hand/right-hand; sheared coordinate systems) and units (inch, mm, ...) without requiring the application to deal with it on its own.
The SFL library internally uses a generic file format library called GFF. See The GFF Layer for details. This layer is only of interest for future extensions of the SFL file format. We will soon add functionality to the SFL API to allow for user-defined GFF extensions. The GFF DLL/library has to be loaded in order to use the SFL DLL/library.
This is a short description of the structure of an SFL file as far as it is needed for reading/writing SFL files using the SFL API:
In contrast to the GFF part of the library, the SFL library interface is C++. All actions are performed using objects of the classes sfl::InStream and sfl::OutStream, depending whether a file is read or written.
Both streams share the super class sfl::Stream, that allows some general settings affecting the API's semantics.
setMatrixApiRowMajor() specifies that all matrices exchanged with the library are supposed to be in row-major format. setMatrixApiColumnMajor() selects the column-major specification (which is used, e.g., by OpenGL).
The input and output stream objects implement a state machine, particularly storing the current hierarchy level inside the SFL file structure. Not all functions make sense in every situation. sfl::InStream::readSurfelPosition3(), e.g., only makes sense when a current surfel set/resolution has been selected and the stream is inside a beginSurfel()/endSurfel() pair. However, in many cases an instantaneous state change is possible and performed by the library. Keep an eye on the stderr output of the library to be informed about implicit state changes.
(Note: stderr output currently does not work in conjunction with Qt and Microsoft Visual Studio.)
For those that want to have a quick start, Example SFL Writer (Conversion from ASCII) contains a sample SFL writer application that converts a simple ASCII format to SFL. The remainder of this section gives an overview about the output API. Refer to the respective function documentation for more information.
In order to write an SFL file, call sfl::OutStream::open() to obtain an sfl::OutStream object. All subsequent operations are performed by member functions of sfl::OutStream. An sfl::OutStream is closed by sfl::OutStream::close().
The file consists of a header and the surfel set list. Output of the respective fields has to be surrounded by calls to sfl::OutStream::beginHeader()/sfl::OutStream::endHeader() and sfl::OutStream::beginSurfelSetList()/sfl::OutStream::endSurfelSetList().
After a call to sfl::OutStream::beginHeader() it is possible to set some generic file description strings using sfl::OutStream::setTitle(), sfl::OutStream::setAuthor(), sfl::OutStream::setApplication(), sfl::OutStream::setDescription, sfl::OutStream::setVersion(), and sfl::OutStream::setDate(). Syntax and semantic of the respective arguments are application dependent. Use them ad libitum.
sfl::OutStream::setUpVec3(), sfl::OutStream::setRightVec3(), and sfl::OutStream::setBackVec3() can be used to define the orientation of the world coordinate system. Defaults for right, up, and back are (1,0,0), (0,1,0), and (0,0,1).
sfl::OutStream::setDefaultUnits() defines the default units for all coordinates in the file. The respective argument defines the default unit length in meters. Default units can be overridden on a per-surfel-set basis.
sfl::OutStream::setOverAllBoundingBox() is a possibility to provide scene bounding box information for the reading application. The SFL library does not ensure consistency of this information.
An initial scene view camera transformation is specified using sfl::OutStream::setSceneCameraTransform4x4().
After all header information is written, finalize the header using sfl::OutStream::endHeader().
Subsequent surfel sets are written within a call to sfl::OutStream::beginSurfelSetList() and sfl::OutStream::endSurfelSetList().
Each surfel set is preluded by sfl::OutStream::beginSurfelSet(); this function also takes the surfel attributes and the number of resolutions that have to be written. See sfldefs.h for a list of supported surfel attributes. Note that the color model specification must be included in the attribute list (cf. asc2sfl.cpp).
After the call to sfl::OutStream::beginSurfelSet(), general properties of the surfel set can be defined. See for setSurfelSetXxx() in sfl::OutStream.
Finally, write all resolutions of the current surfel set within sfl::OutStream::beginResolution()/sfl::OutStream::endResolution() pairs before calling sfl::OutStream::endSurfelSet().
The surfels of a resolution are subsequently written within pairs of sfl::OutStream::beginSurfel()/sfl::OutStream::endSurfel(). For each surfel, dump its attributes using sfl::OutStream::dumpSurfelPosition3(), sfl::OutStream::dumpSurfelNormal3(), sfl::OutStream::dumpSurfelRadius(), sfl::OutStream::dumpSurfelTextureUV(), ... . See sfl::OutStream for the complete list of sfl::OutStream::dumpSurfelXxx() functions.
Again, there is an example reader, converting SFL files to a simple ASCII file format: Example SFL Reader (Conversion to ASCII). An overview of the input API follows.
Open an SFL file for reading using sfl::InStream::open(), which provides a sfl::InStream handle to the input stream. (Close this stream using sfl::InStream::close().)
After opening an SFL file, the header information is immediately available. Check for existance of a header entry using a query function such as, e.g., sfl::InStream::queryTitle(), and read the entry using a get function, e.g., sfl::InStream::getTitle().
The number of surfel sets in an SFL file can be determined using sfl::InStream::getNumSurfelSets(). The different surfel sets can be addresses using the sfl::InStream::seekSurfelSet() operation.
After a surfel set has been ``seeked'', the surfel attributes stored in that surfel set can be determined by sfl::InStream::getSurfelSetProperties(). Note that you are not forced to read the specific set of attributes reported by sfl::InStream::getSurfelSetProperties(). During reading you may ask for other attributes than stored. The missing information is either emulated from the data within the surfel set, or default values are returned instead. Before starting to read surfel attributes, you have to announce the set of attributes that you're possibly going to read using sfl::InStream::setPropertyHints().
For instance, you may ask for radii where only tangent axes are stored -- an the library will return radii that are estimated from the tangent axes. sfl::InStream::setPropertyHints() sets up all required conversions that will transparently be performed during reading.
Within the current surfel set, use sfl::InStream::seekResolution() to open the resolution you're going to read. In most cases, you'll want to read the highest resolution available. Assuming, the current sfl::InStream is stored in in, call in->seekResolution(in->getSurfelSetRes0Index()) to seek to the highest available resolution.
Once a resolution has been seeked, surfels can be read between subsequent calls of sfl::InStream::beginSurfel() and sfl::InStream::endSurfel(). Use the sfl::InStream::readSurfelXxx() functions, e.g., sfl::InStream::readSurfelPosition3(), to receive the attributes of the current surfel.
When receiving splat positions, normals, or tangent axes: Note that each surfel set has its own transformation matrix, obtainable by sfl::InStream::getSurfelSetTransform4x4(). All splat attributes refer to object coordinates. The transformation matrix is used to convert to world coordinates.
If you do not want to perform this transformation yourself, call sfl::InStream::setReadAsWorldCoordinates(true) to cause the API to deliver all coordinates (position, normal, tangent axes, ...) in world coordinates, rather than in the object system. Note that the automatic conversion correctly deals with projective and/or reflecting transformations, too.
Extending the set of possible surfel attributes requires additional fields and functions to be added to the SFL source code. Currently this requires a certain knowledge of the SFL source code. We are still searching for an automatic way to add new attributes.
If you are interested in adding new attributes, we offer to do it for you. Please send a short description of the required attribute, including
Note that Pointshop3D does not automatically support attributes added to the SFL library. It will be able to read the respective SFL files, but the new attributes will not be accesible from within Pointshop3D. If this is desired, the attributes manually have to be added to the Pointshop3D source code.
We almost expect that there is a couple of features missing in the API. Please send us any feedback regarding missing functionality or bugs.
Currently one major feature is planned: Useful filter tools ideally only affect certain attributes (e.g., position, normal, and radius) without changing the rest of the attributes. Unfortunately this behaviour is hard to implement using the current API, as the remaining attributes would have to be passed-through individually, requiring knowledge of the set of possible attributes.
As a remedy, we plan the introduction of a remainder object that encapsulates the untouched attributes. Analogously, the streams themselves will support to pass-through parts of the SFL file that were not accessed by the filter.
1.2.14 written by Dimitri van Heesch,
© 1997-2002