Skip to content

Commit 723f43d

Browse files
committed
add gltf export sample
1 parent 3823eed commit 723f43d

5 files changed

Lines changed: 248 additions & 0 deletions

File tree

src/pg2b3dm.slnx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,7 @@
2222
<Project Path="pg2b3dm/pg2b3dm.csproj" />
2323
<Project Path="wkb2gltf.core.tests/wkb2gltf.tests.csproj" />
2424
<Project Path="wkb2gltf.core/wkb2gltf.csproj" />
25+
<Folder Name="/samples/">
26+
<Project Path="samples/GltfExport/GltfExport.csproj" />
27+
</Folder>
2528
</Solution>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<AssemblyName>gltfexport</AssemblyName>
7+
<RootNamespace>GltfExport</RootNamespace>
8+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
9+
<Nullable>enable</Nullable>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="CommandLineParser" Version="2.9.1" />
14+
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
15+
<PackageReference Include="Npgsql" Version="10.0.2" />
16+
<PackageReference Include="Wkx" Version="0.5.1" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<ProjectReference Include="..\..\wkb2gltf.core\wkb2gltf.csproj" />
21+
</ItemGroup>
22+
23+
</Project>

src/samples/GltfExport/Options.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using CommandLine;
2+
using SharpGLTF.Materials;
3+
4+
namespace GltfExport;
5+
6+
public class Options
7+
{
8+
[Option("connection", Required = true, HelpText = "Database connection string.")]
9+
public string Connection { get; set; } = string.Empty;
10+
11+
[Option('o', "output", Required = false, Default = "output", HelpText = "Output directory.")]
12+
public string Output { get; set; } = "output";
13+
14+
[Option('t', "table", Required = true, HelpText = "Database table, include database schema if needed.")]
15+
public string Table { get; set; } = string.Empty;
16+
17+
[Option('c', "column", Required = false, Default = "geom", HelpText = "Geometry column.")]
18+
public string GeometryColumn { get; set; } = "geom";
19+
20+
[Option("shaderscolumn", Required = false, Default = "", HelpText = "Shaders column.")]
21+
public string ShadersColumn { get; set; } = string.Empty;
22+
23+
[Option("idcolumn", Required = false, Default = "id", HelpText = "Id column.")]
24+
public string IdColumn { get; set; } = "id";
25+
26+
[Option("default_color", Required = false, Default = "#FFFFFF", HelpText = "Default color, in RGB(A) order.")]
27+
public string DefaultColor { get; set; } = "#FFFFFF";
28+
29+
[Option("default_metallic_roughness", Required = false, Default = "#008000", HelpText = "Default metallic roughness.")]
30+
public string DefaultMetallicRoughness { get; set; } = "#008000";
31+
32+
[Option("double_sided", Required = false, Default = true, HelpText = "Default double sided.")]
33+
public bool DoubleSided { get; set; } = true;
34+
35+
[Option("default_alpha_mode", Required = false, Default = AlphaMode.OPAQUE, HelpText = "Default glTF material AlphaMode. Other values: BLEND and MASK. Defines how the alpha value is interpreted.")]
36+
public AlphaMode DefaultAlphaMode { get; set; } = AlphaMode.OPAQUE;
37+
38+
[Option("alpha_cutoff", Required = false, Default = 0.5f, HelpText = "Default glTF material AlphaCutoff (used with MASK alpha mode).")]
39+
public float AlphaCutoff { get; set; } = 0.5f;
40+
}

src/samples/GltfExport/Program.cs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Reflection;
6+
using CommandLine;
7+
using Newtonsoft.Json;
8+
using Npgsql;
9+
using SharpGLTF.Materials;
10+
using Wkb2Gltf;
11+
using Wkx;
12+
using WkbTriangle = Wkb2Gltf.Triangle;
13+
14+
namespace GltfExport;
15+
16+
class Program
17+
{
18+
static void Main(string[] args)
19+
{
20+
var version = Assembly.GetEntryAssembly()?.GetName().Version;
21+
Console.WriteLine($"Tool: GltfExport {version}");
22+
23+
Parser.Default.ParseArguments<Options>(args).WithParsed(o =>
24+
{
25+
Console.WriteLine($"Table: {o.Table}");
26+
Console.WriteLine($"Geometry column: {o.GeometryColumn}");
27+
Console.WriteLine($"Id column: {o.IdColumn}");
28+
Console.WriteLine($"Shaders column: {(string.IsNullOrEmpty(o.ShadersColumn) ? "-" : o.ShadersColumn)}");
29+
Console.WriteLine($"Output directory: {o.Output}");
30+
Console.WriteLine($"Default color: {o.DefaultColor}");
31+
Console.WriteLine($"Default metallic roughness: {o.DefaultMetallicRoughness}");
32+
Console.WriteLine($"Double sided: {o.DoubleSided}");
33+
Console.WriteLine($"Default alpha mode: {o.DefaultAlphaMode}");
34+
Console.WriteLine($"Alpha cutoff: {o.AlphaCutoff}");
35+
36+
Directory.CreateDirectory(o.Output);
37+
38+
var sql = BuildQuery(o.Table, o.GeometryColumn, o.IdColumn, o.ShadersColumn);
39+
40+
using var conn = new NpgsqlConnection(o.Connection);
41+
conn.Open();
42+
43+
var cmd = new NpgsqlCommand(sql, conn);
44+
var reader = cmd.ExecuteReader();
45+
46+
var written = 0;
47+
var skipped = 0;
48+
49+
while (reader.Read())
50+
{
51+
var id = reader.GetFieldValue<object>(0).ToString()!;
52+
var safeId = SanitizeFileName(id);
53+
54+
byte[]? glbBytes = null;
55+
try
56+
{
57+
var stream = reader.GetStream(1);
58+
var geometry = Geometry.Deserialize<WkbSerializer>(stream);
59+
60+
ShaderColors? shaderColors = null;
61+
if (!string.IsNullOrEmpty(o.ShadersColumn))
62+
{
63+
var json = reader.IsDBNull(2) ? null : reader.GetString(2);
64+
if (json != null)
65+
{
66+
shaderColors = JsonConvert.DeserializeObject<ShaderColors>(json);
67+
}
68+
}
69+
70+
var record = new GeometryRecord(0) { Geometry = geometry, Shader = shaderColors };
71+
var triangles = record.GetTriangles(translation: null);
72+
73+
glbBytes = GlbCreator.GetGlb(
74+
triangles: new List<List<WkbTriangle>> { triangles },
75+
createGltf: true,
76+
defaultColor: o.DefaultColor,
77+
defaultMetallicRoughness: o.DefaultMetallicRoughness,
78+
defaultDoubleSided: o.DoubleSided,
79+
defaultAlphaMode: o.DefaultAlphaMode,
80+
alphaCutoff: o.AlphaCutoff
81+
);
82+
}
83+
catch (Exception ex)
84+
{
85+
Console.WriteLine($"Warning: skipping id '{id}': {ex.Message}");
86+
skipped++;
87+
continue;
88+
}
89+
90+
if (glbBytes == null)
91+
{
92+
Console.WriteLine($"Warning: skipping id '{id}': no geometry produced.");
93+
skipped++;
94+
continue;
95+
}
96+
97+
var outputFile = Path.Combine(o.Output, $"{safeId}.glb");
98+
File.WriteAllBytes(outputFile, glbBytes);
99+
written++;
100+
}
101+
102+
reader.Close();
103+
conn.Close();
104+
105+
Console.WriteLine($"Done. Written: {written}, Skipped: {skipped}.");
106+
});
107+
}
108+
109+
private static string BuildQuery(string table, string geomColumn, string idColumn, string shadersColumn)
110+
{
111+
var select = $"SELECT {idColumn}::text, ST_AsBinary({geomColumn})";
112+
if (!string.IsNullOrEmpty(shadersColumn))
113+
{
114+
select += $", {shadersColumn}";
115+
}
116+
return $"{select} FROM {table}";
117+
}
118+
119+
private static string SanitizeFileName(string name)
120+
{
121+
var invalid = Path.GetInvalidFileNameChars();
122+
return string.Concat(name.Select(c => invalid.Contains(c) ? '_' : c));
123+
}
124+
}

src/samples/GltfExport/README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# GltfExport
2+
3+
A .NET 8.0 console tool that exports PostGIS geometries as individual glTF 2.0 (`.glb`) files — one file per row, named `{id}.glb`.
4+
5+
Geometry coordinates are used **as-is** (no translation or reprojection is applied).
6+
7+
## Requirements
8+
9+
- .NET 8.0 SDK
10+
- A PostgreSQL database with PostGIS extension
11+
- A table containing a geometry column and an id column
12+
13+
## Usage
14+
15+
```bash
16+
gltfexport --connection "Host=localhost;Database=mydb;Username=myuser;Password=mypassword" \
17+
-t myschema.mytable \
18+
-o ./output
19+
```
20+
21+
## Parameters
22+
23+
| Parameter | Short | Required | Default | Description |
24+
|---|---|---|---|---|
25+
| `--connection` | | Yes | | PostgreSQL connection string |
26+
| `--table` | `-t` | Yes | | Database table (include schema if needed, e.g. `public.buildings`) |
27+
| `--output` | `-o` | No | `output` | Output directory for `.glb` files |
28+
| `--column` | `-c` | No | `geom` | Geometry column |
29+
| `--idcolumn` | | No | `id` | Id column — used as the output filename |
30+
| `--shaderscolumn` | | No | *(empty)* | Shaders column (JSON with PBR material colors) |
31+
| `--default_color` | | No | `#FFFFFF` | Default color in RGB(A) order |
32+
| `--default_metallic_roughness` | | No | `#008000` | Default metallic roughness |
33+
| `--double_sided` | | No | `true` | Double-sided rendering |
34+
| `--default_alpha_mode` | | No | `OPAQUE` | glTF AlphaMode: `OPAQUE`, `BLEND`, or `MASK` |
35+
| `--alpha_cutoff` | | No | `0.5` | Alpha cutoff value (used with `MASK` alpha mode) |
36+
| `--help` | | | | Display help |
37+
| `--version` | | | | Display version information |
38+
39+
## Output
40+
41+
Each row in the table produces one `.glb` file in the output directory, named after the value in the id column (invalid filename characters are replaced with `_`).
42+
43+
## Example
44+
45+
Given a table `public.buildings` with columns `gid` (integer), `geom` (geometry), and `shader` (json):
46+
47+
```bash
48+
gltfexport \
49+
--connection "Host=localhost;Database=citydb;Username=postgres" \
50+
-t public.buildings \
51+
-c geom \
52+
--idcolumn gid \
53+
--shaderscolumn shader \
54+
--default_color "#CCCCCC" \
55+
-o ./glb_output
56+
```
57+
58+
This produces files like `./glb_output/1.glb`, `./glb_output/2.glb`, etc.

0 commit comments

Comments
 (0)