Skip to content

Commit b166e0d

Browse files
committed
Initial commit: Draco GLB decoder CLI
Add a Java CLI tool to decode Draco-compressed GLB/GLTF files to plain GLB 2.0 using LWJGL Assimp. Includes main application, decoder utility, Maven configuration, usage documentation, and .gitignore.
1 parent 1716e86 commit b166e0d

5 files changed

Lines changed: 405 additions & 0 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/out
2+
/target
3+
*.glb
4+
*.json

README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# java-draco-decoder
2+
3+
Tiny CLI to **decode Draco-compressed GLB/GLTF** into plain **GLB 2.0** via LWJGL Assimp. Perfect pre-step before CPU voxelization.
4+
5+
## Requirements
6+
7+
* JDK 17+, Maven 3.8+
8+
* LWJGL 3.3.2+ with Assimp (Draco bundled)
9+
10+
## Build
11+
12+
```bash
13+
mvn -q -DskipTests clean package
14+
# → target/draco-decoder-cli-1.0.0-all.jar
15+
```
16+
17+
## Usage
18+
19+
```bash
20+
java -jar target/draco-decoder-cli-1.0.0-all.jar \
21+
-f in.glb [-f in2.glb ...] \
22+
[-filelist list.txt] [-o out] [-j jobs] [-v] [-h]
23+
```
24+
25+
**Options**
26+
27+
* `-f <path>` add input (repeatable)
28+
* `-filelist <txt>` one path per line
29+
* `-o <dir>` output dir (default `./out`)
30+
* `-j <n>` parallel jobs (default: #CPU)
31+
* `-v` verbose
32+
* `-h` help
33+
34+
**Examples**
35+
36+
```bash
37+
# single
38+
java -jar ... -f tile.glb -o out
39+
# batch
40+
java -jar ... -f a.glb -f b.glb -o out -j 8
41+
# list
42+
java -jar ... -filelist assets.txt -o out
43+
```
44+
45+
**Output**
46+
Each `in.glb``out/in-decoded.glb` (no Draco).
47+
48+
## Notes
49+
50+
* Uses Assimp with safe defaults (`Triangulate`, `JoinIdenticalVertices`, etc.) and exports `glb2`.
51+
* If you voxelize after: you can diff geometry vs CUDA output by sorting `.xyzi` arrays with `jq` and `diff`.
52+
53+
## Troubleshooting
54+
55+
* Missing natives / `UnsatisfiedLinkError`: ensure LWJGL Assimp natives for your OS are available.
56+
* “Draco library not present” under `-v` is informational; if import works, you’re good.
57+
58+
## Embed (one-liner)
59+
60+
```java
61+
AssimpDracoDecode.decodeToUncompressedGlb(new File("in.glb"), new File("out/in-decoded.glb"), true);
62+
```

pom.xml

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>com.example</groupId>
7+
<artifactId>draco-decoder-cli</artifactId>
8+
<version>1.0.0</version>
9+
<name>draco-decoder-cli</name>
10+
11+
<properties>
12+
<maven.compiler.source>17</maven.compiler.source>
13+
<maven.compiler.target>17</maven.compiler.target>
14+
<lwjgl.version>3.3.6</lwjgl.version>
15+
</properties>
16+
17+
<dependencies>
18+
<!-- LWJGL core + Assimp bindings -->
19+
<dependency>
20+
<groupId>org.lwjgl</groupId>
21+
<artifactId>lwjgl</artifactId>
22+
<version>${lwjgl.version}</version>
23+
</dependency>
24+
<dependency>
25+
<groupId>org.lwjgl</groupId>
26+
<artifactId>lwjgl-assimp</artifactId>
27+
<version>${lwjgl.version}</version>
28+
</dependency>
29+
30+
<!-- Natives for all major platforms (ok for a “fat” CLI jar) -->
31+
<!-- Windows -->
32+
<dependency>
33+
<groupId>org.lwjgl</groupId>
34+
<artifactId>lwjgl</artifactId>
35+
<version>${lwjgl.version}</version>
36+
<classifier>natives-windows</classifier>
37+
</dependency>
38+
<dependency>
39+
<groupId>org.lwjgl</groupId>
40+
<artifactId>lwjgl-assimp</artifactId>
41+
<version>${lwjgl.version}</version>
42+
<classifier>natives-windows</classifier>
43+
</dependency>
44+
<dependency>
45+
<groupId>org.lwjgl</groupId>
46+
<artifactId>lwjgl</artifactId>
47+
<version>${lwjgl.version}</version>
48+
<classifier>natives-windows-arm64</classifier>
49+
</dependency>
50+
<dependency>
51+
<groupId>org.lwjgl</groupId>
52+
<artifactId>lwjgl-assimp</artifactId>
53+
<version>${lwjgl.version}</version>
54+
<classifier>natives-windows-arm64</classifier>
55+
</dependency>
56+
<!-- Linux -->
57+
<dependency>
58+
<groupId>org.lwjgl</groupId>
59+
<artifactId>lwjgl</artifactId>
60+
<version>${lwjgl.version}</version>
61+
<classifier>natives-linux</classifier>
62+
</dependency>
63+
<dependency>
64+
<groupId>org.lwjgl</groupId>
65+
<artifactId>lwjgl-assimp</artifactId>
66+
<version>${lwjgl.version}</version>
67+
<classifier>natives-linux</classifier>
68+
</dependency>
69+
<dependency>
70+
<groupId>org.lwjgl</groupId>
71+
<artifactId>lwjgl</artifactId>
72+
<version>${lwjgl.version}</version>
73+
<classifier>natives-linux-arm64</classifier>
74+
</dependency>
75+
<dependency>
76+
<groupId>org.lwjgl</groupId>
77+
<artifactId>lwjgl-assimp</artifactId>
78+
<version>${lwjgl.version}</version>
79+
<classifier>natives-linux-arm64</classifier>
80+
</dependency>
81+
<!-- macOS -->
82+
<dependency>
83+
<groupId>org.lwjgl</groupId>
84+
<artifactId>lwjgl</artifactId>
85+
<version>${lwjgl.version}</version>
86+
<classifier>natives-macos</classifier>
87+
</dependency>
88+
<dependency>
89+
<groupId>org.lwjgl</groupId>
90+
<artifactId>lwjgl-assimp</artifactId>
91+
<version>${lwjgl.version}</version>
92+
<classifier>natives-macos</classifier>
93+
</dependency>
94+
<dependency>
95+
<groupId>org.lwjgl</groupId>
96+
<artifactId>lwjgl</artifactId>
97+
<version>${lwjgl.version}</version>
98+
<classifier>natives-macos-arm64</classifier>
99+
</dependency>
100+
<dependency>
101+
<groupId>org.lwjgl</groupId>
102+
<artifactId>lwjgl-assimp</artifactId>
103+
<version>${lwjgl.version}</version>
104+
<classifier>natives-macos-arm64</classifier>
105+
</dependency>
106+
</dependencies>
107+
108+
<build>
109+
<plugins>
110+
<!-- single runnable jar -->
111+
<plugin>
112+
<groupId>org.apache.maven.plugins</groupId>
113+
<artifactId>maven-shade-plugin</artifactId>
114+
<version>3.5.0</version>
115+
<executions>
116+
<execution>
117+
<phase>package</phase>
118+
<goals><goal>shade</goal></goals>
119+
<configuration>
120+
<createDependencyReducedPom>false</createDependencyReducedPom>
121+
<finalName>draco-decoder-cli-1.0.0-all</finalName>
122+
<transformers>
123+
<!-- keep LWJGL service loaders for native extraction -->
124+
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
125+
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
126+
<mainClass>com.example.draco.Main</mainClass>
127+
</transformer>
128+
</transformers>
129+
</configuration>
130+
</execution>
131+
</executions>
132+
</plugin>
133+
</plugins>
134+
</build>
135+
</project>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.example.draco;
2+
3+
import org.lwjgl.assimp.*;
4+
import org.lwjgl.system.Configuration;
5+
import org.lwjgl.system.SharedLibrary;
6+
7+
import java.io.File;
8+
import java.nio.file.Files;
9+
import java.nio.file.Path;
10+
11+
/**
12+
* Imports a (possibly Draco-compressed) GLTF/GLB via Assimp and exports a plain GLB (no Draco).
13+
*/
14+
public final class AssimpDracoDecode {
15+
16+
private AssimpDracoDecode() {}
17+
18+
public static void decodeToUncompressedGlb(File input, File output, boolean verbose) throws Exception {
19+
if (!input.isFile()) throw new IllegalArgumentException("Input does not exist: " + input);
20+
21+
// Optional: enable LWJGL/Assimp diagnostics when -v
22+
if (verbose) {
23+
Configuration.DISABLE_FUNCTION_CHECKS.set(false);
24+
Configuration.DEBUG.set(true);
25+
}
26+
27+
// Ensure natives are loaded (Assimp + (if bundled) Draco)
28+
Assimp.getLibrary(); // load assimp native
29+
try {
30+
SharedLibrary draco = Assimp.getDraco(); // touch Draco library (if available)
31+
if (verbose) {
32+
System.out.println("[Native] Draco: " + draco.getName() + " path=" + draco.getPath());
33+
}
34+
} catch (Throwable t) {
35+
if (verbose) System.out.println("[Native] Draco library not present (continuing): " + t);
36+
}
37+
38+
// Typical post-process flags. Not strictly needed for "decode", but safe defaults.
39+
int ppFlags =
40+
Assimp.aiProcess_ValidateDataStructure |
41+
Assimp.aiProcess_Triangulate |
42+
Assimp.aiProcess_JoinIdenticalVertices |
43+
Assimp.aiProcess_ImproveCacheLocality |
44+
Assimp.aiProcess_SortByPType;
45+
46+
if (verbose) System.out.println("[Import] " + input.getAbsolutePath());
47+
48+
AIScene scene = Assimp.aiImportFile(input.getAbsolutePath(), ppFlags);
49+
if (scene == null) {
50+
throw new IllegalStateException("Assimp import failed: " + Assimp.aiGetErrorString());
51+
}
52+
53+
try {
54+
Path outPath = output.toPath();
55+
Files.createDirectories(outPath.getParent());
56+
57+
// Exporter format id for binary glTF 2.0 is "glb2"
58+
int rc = Assimp.aiExportScene(scene, "glb2", outPath.toString(), 0);
59+
if (rc != Assimp.aiReturn_SUCCESS) {
60+
throw new IllegalStateException("Assimp export failed (rc=" + rc + ")");
61+
}
62+
if (verbose) System.out.println("[Export] Wrote " + outPath.toAbsolutePath());
63+
} finally {
64+
Assimp.aiReleaseImport(scene);
65+
}
66+
}
67+
}

0 commit comments

Comments
 (0)