A problem which could be seen to be limiting widespread adoption of signed distance fields is the lack of a standard interchange format between applications which would produce and consume them. For example, it would be equivalent to STL/OBJ in the following workflow, but for signed distance fields rather than polygonal meshes:
Thingiverse -----------> Blender -----------> Cura ---------> 3D Printer
(STL/OBJ) (BLEND) (STL/OBJ) (3MF) (GCODE)
This document drafts such a format.
The key difference between common 3D model interchange formats such as OBJ and STL, and an interchange format for signed distance fields, is that while polygonal models can be expressed through fairly simple tables of data, signed distance fields are mathematical functions equating locations in 3D space to distances to the nearest surface (with negative values given when below said surface).
Tabular data is considerably easier to consume than logic. To maximise the appeal of implementing this interchange format, it is important to focus on keeping its instruction set and format minimal and simplistic for machine-machine communication.
A little-endian binary file.
Offset | Description | Example | Description |
---|---|---|---|
00 | Signature | 53 44 46 |
"SDF" |
01 | Version | 00 |
|
04 | No. Dimensions | 02 |
2-dimensional |
03 |
3-dimensional | ||
05 | Unit Scale | 00 00 80 3F |
Meters (1.0) |
0A D7 23 3C |
Centimetres (0.01) |
All instructions are an instruction ID followed by the arguments required by that instruction. The result of the last instruction in the file is taken as the distance to the nearest surface.
ID | A | B | C | Result | Equivalent C | Undefined Behaviour |
---|---|---|---|---|---|---|
00 | Float | N/A | N/A | Float | -A | |
01 | Float | Float | N/A | Float | A + B | |
02 | Float | Float | N/A | Float | A - B | |
03 | Float | Float | N/A | Float | A * B | |
04 | Float | Float | N/A | Float | A / B | Division by zero |
05 | Float | N/A | N/A | Float | sinf(A) | |
06 | Float | N/A | N/A | Float | cosf(A) | |
07 | Float | N/A | N/A | Float | tanf(A) | |
08 | Float | N/A | N/A | Float | asinf(A) | |
09 | Float | N/A | N/A | Float | acosf(A) | |
0A | Float | N/A | N/A | Float | atanf(A) | |
0B | Float | N/A | N/A | Float | sinhf(A) | |
0C | Float | N/A | N/A | Float | coshf(A) | |
0D | Float | N/A | N/A | Float | tanhf(A) | |
0E | Float | N/A | N/A | Float | asinhf(A) | |
0F | Float | N/A | N/A | Float | acoshf(A) | |
10 | Float | N/A | N/A | Float | atanhf(A) | |
11 | Float | Float | N/A | Float | powf(A, B) | |
12 | Float | N/A | N/A | Float | expf(A) | |
13 | Float | N/A | N/A | Float | logf(A) | |
14 | Float | N/A | N/A | Float | exp2f(A) | |
15 | Float | N/A | N/A | Float | log2f(A) | |
16 | Float | N/A | N/A | Float | sqrtf(A) | |
17 | Float | Float | N/A | Bool | A > B | A == B |
18 | Bool | Float | Float | Float | A ? B : C | |
19 | Bool | Bool | Bool | Bool | A ? B : C | |
1A | Bool | N/A | N/A | Bool | !A | |
1B | Bool | Bool | N/A | Bool | A == B | |
1C | Bool | Bool | N/A | Bool | A != B | |
1D | Bool | Bool | N/A | Bool | A && B | |
1E | Bool | Bool | N/A | Bool | A | |
1F | Float | N/A | N/A | N/A | floorf(A) | |
20 | Float | N/A | N/A | N/A | ceilf(A) |
Given four dimensions and two previous instructions:
Argument | Description |
---|---|
00 00 00 00 | X |
01 00 00 00 | Y |
02 00 00 00 | Z |
03 00 00 00 | W |
04 00 00 00 | Previous instruction A |
05 00 00 00 | Previous instruction B |
FF FF FF FF 33 33 DB 41 | 24.4 (float constant) |
The following file produces a unit sphere at (0, 0, 0).
53 44 46 // "SDF"
00 // Version 0
03 // 3-dimensional
00 00 80 3F // Meters
03 00 00 00 00 00 00 00 00 // X * X
03 01 00 00 00 01 00 00 00 // Y * Y
03 02 00 00 00 02 00 00 00 // Z * Z
01 03 00 00 00 04 00 00 00 // X * X + Y * Y
01 06 00 00 00 05 00 00 00 // X * X + Y * Y + Z * Z
16 07 00 00 00 // sqrtf(X * X + Y * Y + Z * Z)
02 08 00 00 00 FF FF FF FF 00 00 80 3F // sqrtf(X * X + Y * Y + Z * Z) - 1.0f