Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/pg2b3dm.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,7 @@
<Project Path="pg2b3dm/pg2b3dm.csproj" />
<Project Path="wkb2gltf.core.tests/wkb2gltf.tests.csproj" />
<Project Path="wkb2gltf.core/wkb2gltf.csproj" />
<Folder Name="/samples/">
<Project Path="samples/GltfExport/GltfExport.csproj" />
</Folder>
</Solution>
23 changes: 23 additions & 0 deletions src/samples/GltfExport/GltfExport.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<AssemblyName>gltfexport</AssemblyName>
<RootNamespace>GltfExport</RootNamespace>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Npgsql" Version="10.0.2" />
<PackageReference Include="Wkx" Version="0.5.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\wkb2gltf.core\wkb2gltf.csproj" />
</ItemGroup>

</Project>
40 changes: 40 additions & 0 deletions src/samples/GltfExport/Options.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using CommandLine;
using SharpGLTF.Materials;

namespace GltfExport;

public class Options
{
[Option("connection", Required = true, HelpText = "Database connection string.")]
public string Connection { get; set; } = string.Empty;

[Option('o', "output", Required = false, Default = "output", HelpText = "Output directory.")]
public string Output { get; set; } = "output";

[Option('t', "table", Required = true, HelpText = "Database table, include database schema if needed.")]
public string Table { get; set; } = string.Empty;

[Option('c', "column", Required = false, Default = "geom", HelpText = "Geometry column.")]
public string GeometryColumn { get; set; } = "geom";

[Option("shaderscolumn", Required = false, Default = "", HelpText = "Shaders column.")]
public string ShadersColumn { get; set; } = string.Empty;

[Option("idcolumn", Required = false, Default = "id", HelpText = "Id column.")]
public string IdColumn { get; set; } = "id";

[Option("default_color", Required = false, Default = "#FFFFFF", HelpText = "Default color, in RGB(A) order.")]
public string DefaultColor { get; set; } = "#FFFFFF";

[Option("default_metallic_roughness", Required = false, Default = "#008000", HelpText = "Default metallic roughness.")]
public string DefaultMetallicRoughness { get; set; } = "#008000";

[Option("double_sided", Required = false, Default = true, HelpText = "Default double sided.")]
public bool DoubleSided { get; set; } = true;

[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.")]
public AlphaMode DefaultAlphaMode { get; set; } = AlphaMode.OPAQUE;

[Option("alpha_cutoff", Required = false, Default = 0.5f, HelpText = "Default glTF material AlphaCutoff (used with MASK alpha mode).")]
public float AlphaCutoff { get; set; } = 0.5f;
}
124 changes: 124 additions & 0 deletions src/samples/GltfExport/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using CommandLine;
using Newtonsoft.Json;
using Npgsql;
using SharpGLTF.Materials;
using Wkb2Gltf;
using Wkx;
using WkbTriangle = Wkb2Gltf.Triangle;

namespace GltfExport;

class Program
{
static void Main(string[] args)
{
var version = Assembly.GetEntryAssembly()?.GetName().Version;
Console.WriteLine($"Tool: GltfExport {version}");

Parser.Default.ParseArguments<Options>(args).WithParsed(o =>
{
Console.WriteLine($"Table: {o.Table}");
Console.WriteLine($"Geometry column: {o.GeometryColumn}");
Console.WriteLine($"Id column: {o.IdColumn}");
Console.WriteLine($"Shaders column: {(string.IsNullOrEmpty(o.ShadersColumn) ? "-" : o.ShadersColumn)}");
Console.WriteLine($"Output directory: {o.Output}");
Console.WriteLine($"Default color: {o.DefaultColor}");
Console.WriteLine($"Default metallic roughness: {o.DefaultMetallicRoughness}");
Console.WriteLine($"Double sided: {o.DoubleSided}");
Console.WriteLine($"Default alpha mode: {o.DefaultAlphaMode}");
Console.WriteLine($"Alpha cutoff: {o.AlphaCutoff}");

Directory.CreateDirectory(o.Output);

var sql = BuildQuery(o.Table, o.GeometryColumn, o.IdColumn, o.ShadersColumn);

using var conn = new NpgsqlConnection(o.Connection);
conn.Open();

var cmd = new NpgsqlCommand(sql, conn);
var reader = cmd.ExecuteReader();

var written = 0;
var skipped = 0;

while (reader.Read())
{
var id = reader.GetFieldValue<object>(0).ToString()!;
var safeId = SanitizeFileName(id);

byte[]? glbBytes = null;
try
{
var stream = reader.GetStream(1);
var geometry = Geometry.Deserialize<WkbSerializer>(stream);

ShaderColors? shaderColors = null;
if (!string.IsNullOrEmpty(o.ShadersColumn))
{
var json = reader.IsDBNull(2) ? null : reader.GetString(2);
if (json != null)
{
shaderColors = JsonConvert.DeserializeObject<ShaderColors>(json);
}
}

var record = new GeometryRecord(0) { Geometry = geometry, Shader = shaderColors };
var triangles = record.GetTriangles(translation: null);

glbBytes = GlbCreator.GetGlb(
triangles: new List<List<WkbTriangle>> { triangles },
createGltf: true,
defaultColor: o.DefaultColor,
defaultMetallicRoughness: o.DefaultMetallicRoughness,
defaultDoubleSided: o.DoubleSided,
defaultAlphaMode: o.DefaultAlphaMode,
alphaCutoff: o.AlphaCutoff
);
}
catch (Exception ex)
{
Console.WriteLine($"Warning: skipping id '{id}': {ex.Message}");
skipped++;
continue;
}

if (glbBytes == null)
{
Console.WriteLine($"Warning: skipping id '{id}': no geometry produced.");
skipped++;
continue;
}

var outputFile = Path.Combine(o.Output, $"{safeId}.glb");
File.WriteAllBytes(outputFile, glbBytes);
written++;
}

reader.Close();
conn.Close();

Console.WriteLine($"Done. Written: {written}, Skipped: {skipped}.");
});
}

private static string BuildQuery(string table, string geomColumn, string idColumn, string shadersColumn)
{
var select = $"SELECT {idColumn}::text, ST_AsBinary({geomColumn})";
if (!string.IsNullOrEmpty(shadersColumn))
{
select += $", {shadersColumn}";
}
return $"{select} FROM {table}";
}

private static string SanitizeFileName(string name)
{
var invalid = Path.GetInvalidFileNameChars();
return string.Concat(name.Select(c => invalid.Contains(c) ? '_' : c));
}
}
58 changes: 58 additions & 0 deletions src/samples/GltfExport/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# GltfExport

A .NET 8.0 console tool that exports PostGIS geometries as individual glTF 2.0 (`.glb`) files — one file per row, named `{id}.glb`.

Geometry coordinates are used **as-is** (no translation or reprojection is applied).

## Requirements

- .NET 8.0 SDK
- A PostgreSQL database with PostGIS extension
- A table containing a geometry column and an id column

## Usage

```bash
gltfexport --connection "Host=localhost;Database=mydb;Username=myuser;Password=mypassword" \
-t myschema.mytable \
-o ./output
```

## Parameters

| Parameter | Short | Required | Default | Description |
|---|---|---|---|---|
| `--connection` | | Yes | | PostgreSQL connection string |
| `--table` | `-t` | Yes | | Database table (include schema if needed, e.g. `public.buildings`) |
| `--output` | `-o` | No | `output` | Output directory for `.glb` files |
| `--column` | `-c` | No | `geom` | Geometry column |
| `--idcolumn` | | No | `id` | Id column — used as the output filename |
| `--shaderscolumn` | | No | *(empty)* | Shaders column (JSON with PBR material colors) |
| `--default_color` | | No | `#FFFFFF` | Default color in RGB(A) order |
| `--default_metallic_roughness` | | No | `#008000` | Default metallic roughness |
| `--double_sided` | | No | `true` | Double-sided rendering |
| `--default_alpha_mode` | | No | `OPAQUE` | glTF AlphaMode: `OPAQUE`, `BLEND`, or `MASK` |
| `--alpha_cutoff` | | No | `0.5` | Alpha cutoff value (used with `MASK` alpha mode) |
| `--help` | | | | Display help |
| `--version` | | | | Display version information |

## Output

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 `_`).

## Example

Given a table `public.buildings` with columns `gid` (integer), `geom` (geometry), and `shader` (json):

```bash
gltfexport \
--connection "Host=localhost;Database=citydb;Username=postgres" \
-t public.buildings \
-c geom \
--idcolumn gid \
--shaderscolumn shader \
--default_color "#CCCCCC" \
-o ./glb_output
```

This produces files like `./glb_output/1.glb`, `./glb_output/2.glb`, etc.
Loading