Skip to content

Commit

Permalink
Merge pull request #115 from Geodan/release_1_8
Browse files Browse the repository at this point in the history
to release 1.8 - adding 3D Tiles 1.1 metadata
  • Loading branch information
bertt authored Sep 25, 2023
2 parents 311fbc7 + 99cd99c commit 0e10a85
Show file tree
Hide file tree
Showing 20 changed files with 252 additions and 61 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,9 @@ If --username and/or --dbname are not specified the current username is used as
--default_metallic_roughness (Default: #008000) Default metallic roughness
--default_double_sided (Default: true) Default double sided
--double_sided (Default: true) Default double sided
--create_gltf (Default: true) Create glTF files
--max_features_per_tile (Default: 1000) maximum features per tile (Cesium)
Expand Down Expand Up @@ -330,6 +332,8 @@ Press F5 to start debugging.

## History

2023-09-22: release 1.8, adding 3D Tiles 1.1 Metadata support. Options added: create_gltf (default true), double_sided (default true)

2023-08-29: release 1.7.1, improve spatial index check

2023-08-29: release 1.7.0, add triangulator - runs only when geometry is not triangulated
Expand Down
20 changes: 11 additions & 9 deletions src/b3dm.tileset/QuadtreeTiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public QuadtreeTiler(NpgsqlConnection conn, string table, int epsg, string geome
this.skipCreateTiles = skipCreateTiles;
}

public List<Tile> GenerateTiles(BoundingBox bbox, Tile tile, List<Tile> tiles, int lod=0, bool addOutlines=false, double areaTolerance = 0.01, string defaultColor = "#FFFFFF", string defaultMetallicRoughness = "#008000", bool defaultDoubleSided=true)
public List<Tile> GenerateTiles(BoundingBox bbox, Tile tile, List<Tile> tiles, int lod = 0, bool addOutlines = false, double areaTolerance = 0.01, string defaultColor = "#FFFFFF", string defaultMetallicRoughness = "#008000", bool doubleSided = true, bool createGltf = false)
{
var where = (query != string.Empty ? $" and {query}" : String.Empty);

Expand Down Expand Up @@ -81,7 +81,7 @@ public List<Tile> GenerateTiles(BoundingBox bbox, Tile tile, List<Tile> tiles, i
var bboxQuad = new BoundingBox(xstart, ystart, xend, yend);
var new_tile = new Tile(z, tile.X * 2 + x, tile.Y * 2 + y);
new_tile.BoundingBox = bboxQuad.ToArray();
GenerateTiles(bboxQuad, new_tile, tiles, lod, addOutlines, areaTolerance, defaultColor, defaultMetallicRoughness, defaultDoubleSided);
GenerateTiles(bboxQuad, new_tile, tiles, lod, addOutlines, areaTolerance, defaultColor, defaultMetallicRoughness, doubleSided, createGltf);
}
}
}
Expand All @@ -91,29 +91,31 @@ public List<Tile> GenerateTiles(BoundingBox bbox, Tile tile, List<Tile> tiles, i
if (lodColumn != String.Empty) {
file += $"_{lod}";
}
file += ".b3dm";

var ext = createGltf ? ".glb" : ".b3dm";
file += ext;
Console.Write($"\rCreating tile: {file} ");
tile.ContentUri = file;

if (!skipCreateTiles) {

var geometries = GeometryRepository.GetGeometrySubset(conn, table, geometryColumn, translation, tile.BoundingBox, epsg, colorColumn, attributesColumn, where);
var bytes = B3dmWriter.ToB3dm(geometries, copyright, addOutlines, areaTolerance, defaultColor, defaultMetallicRoughness, defaultDoubleSided);
var bytes = TileWriter.ToTile(geometries, copyright, addOutlines, areaTolerance, defaultColor, defaultMetallicRoughness, doubleSided, createGltf);
tile.Lod = lod;

File.WriteAllBytes($"{outputFolder}{Path.AltDirectorySeparatorChar}{file}", bytes);

if(lodColumn!=String.Empty) {
if (lod < lods.Max()){
if (lodColumn != String.Empty) {
if (lod < lods.Max()) {
// take the next lod
var currentIndex = lods.FindIndex(p => p == lod);
var nextIndex = currentIndex + 1;
var nextLod = lods[nextIndex];
// make a copy of the tile
var t2=new Tile(tile.X, tile.Y, tile.Z);
var t2 = new Tile(tile.X, tile.Y, tile.Z);
t2.BoundingBox = tile.BoundingBox;
var lodNextTiles = GenerateTiles(bbox, t2, new List<Tile>(), nextLod, addOutlines, areaTolerance, defaultColor, defaultMetallicRoughness);
tile.Children=lodNextTiles;
var lodNextTiles = GenerateTiles(bbox, t2, new List<Tile>(), nextLod, addOutlines, areaTolerance, defaultColor, defaultMetallicRoughness, createGltf);
tile.Children = lodNextTiles;
};
}

Expand Down
5 changes: 3 additions & 2 deletions src/b3dm.tileset/TreeSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ public static string ToJson(List<Tile> tiles, double[] transform, double[] regio
return json; ;
}

public static TileSet ToImplicitTileset(double[] transform, double[] box, double maxGeometricError, int availableLevels, int subtreeLevels, Version version=null)
public static TileSet ToImplicitTileset(double[] transform, double[] box, double maxGeometricError, int availableLevels, int subtreeLevels, Version version = null, bool createGltf = false)
{
var ext = createGltf ? ".glb" : ".b3dm";
var geometricError = maxGeometricError;
var tileset = GetTilesetObject(version, maxGeometricError);
var t = new double[] { 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, transform[0], transform[1], transform[2], 1.0 };
var root = GetRoot(geometricError, t, box);
var content = new Content() { uri = "content/{level}_{x}_{y}.b3dm" };
var content = new Content() { uri = "content/{level}_{x}_{y}" + ext };
root.content = content;
var subtrees = new Subtrees() { uri = "subtrees/{level}_{x}_{y}.subtree" };
root.implicitTiling = new Implicittiling() { subdivisionScheme = "QUADTREE", availableLevels = availableLevels, subtreeLevels = subtreeLevels, subtrees = subtrees };
Expand Down
2 changes: 1 addition & 1 deletion src/b3dm.tileset/b3dm.tileset.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Npgsql" Version="7.0.4" />
<PackageReference Include="Npgsql" Version="7.0.6" />
<PackageReference Include="subtree" Version="1.4.4" />
</ItemGroup>

Expand Down
1 change: 1 addition & 0 deletions src/nuget.config
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="myget.org" value="https://www.myget.org/F/bertt/api/v3/index.json"/>
</packageSources>
</configuration>
2 changes: 1 addition & 1 deletion src/pg2b3dm.database.tests/pg2b3dm.database.tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.6.1">
<PackageReference Include="NUnit.Analyzers" Version="3.7.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
7 changes: 5 additions & 2 deletions src/pg2b3dm/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@ public class Options
[Option("default_metallic_roughness", Required = false, Default = "#008000", HelpText = "Default metallic roughness")]
public string DefaultMetallicRoughness { get; set; }

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

[Option("create_gltf", Required = false, Default = true, HelpText = "Create glTF")]
public bool? CreateGltf { get; set; }

// cesium specific options

Expand Down
13 changes: 8 additions & 5 deletions src/pg2b3dm/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ static void Main(string[] args)
var geometryColumn = o.GeometryColumn;
var defaultColor = o.DefaultColor;
var defaultMetallicRoughness = o.DefaultMetallicRoughness;
var defaultDoubleSided = (bool)o.DefaultDoubleSided;
var doubleSided = (bool)o.DoubleSided;
var createGltf = (bool)o.CreateGltf;

var query = o.Query;

var conn = new NpgsqlConnection(connectionString);
Expand Down Expand Up @@ -94,7 +96,8 @@ static void Main(string[] args)

Console.WriteLine($"Default color: {defaultColor}");
Console.WriteLine($"Default metallic roughness: {defaultMetallicRoughness}");
Console.WriteLine($"Default doublesided: {defaultDoubleSided}");
Console.WriteLine($"Doublesided: {doubleSided}");
Console.WriteLine($"Create glTF tiles: {createGltf}");

var att = !string.IsNullOrEmpty(o.AttributeColumns) ? o.AttributeColumns : "-";
Console.WriteLine($"Attribute columns: {att}");
Expand Down Expand Up @@ -170,7 +173,7 @@ static void Main(string[] args)
tile.BoundingBox = bbox_wgs84.ToArray();
Console.WriteLine($"Start generating tiles...");
var quadtreeTiler = new QuadtreeTiler(conn, table, sr, geometryColumn, o.MaxFeaturesPerTile, query, translation, o.ShadersColumn, o.AttributeColumns, lodcolumn, contentDirectory, lods, o.Copyright, skipCreateTiles);
var tiles = quadtreeTiler.GenerateTiles(bbox_wgs84, tile, new List<Tile>(), lodcolumn != string.Empty ? lods.First() : 0, addOutlines, areaTolerance, defaultColor, defaultMetallicRoughness, defaultDoubleSided);
var tiles = quadtreeTiler.GenerateTiles(bbox_wgs84, tile, new List<Tile>(), lodcolumn != string.Empty ? lods.First() : 0, addOutlines, areaTolerance, defaultColor, defaultMetallicRoughness, doubleSided,createGltf);
Console.WriteLine();
Console.WriteLine("Tiles created: " + tiles.Count(tile => tile.Available));

Expand All @@ -191,7 +194,7 @@ static void Main(string[] args)
var availableLevels = tiles.Max(t => t.Z) + 1;
Console.WriteLine("Available Levels: " + availableLevels);
Console.WriteLine("Subtree Levels: " + subtreeLevels);
var tilesetjson = TreeSerializer.ToImplicitTileset(translation, rootBoundingVolumeRegion, geometricErrors[0], availableLevels, subtreeLevels, version);
var tilesetjson = TreeSerializer.ToImplicitTileset(translation, rootBoundingVolumeRegion, geometricErrors[0], availableLevels, subtreeLevels, version, createGltf);
var file = $"{o.Output}{Path.AltDirectorySeparatorChar}tileset.json";
var json = JsonConvert.SerializeObject(tilesetjson, Formatting.Indented, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
Console.WriteLine("SubdivisionScheme: QUADTREE");
Expand Down Expand Up @@ -233,7 +236,7 @@ static void Main(string[] args)
var centerTileTranslation = Translation.GetTranslation(3857, new Point(center[0], center[1], 0)); ;

var geometries = GeometryRepository.GetGeometrySubset(conn, table, geometryColumn, centerTileTranslation, bounds, sr, o.ShadersColumn, o.AttributeColumns, query);
var bytes = B3dmWriter.ToB3dm(geometries, o.Copyright, false, areaTolerance, defaultColor, defaultMetallicRoughness);
var bytes = TileWriter.ToTile(geometries, o.Copyright, false, areaTolerance, defaultColor, defaultMetallicRoughness);
File.WriteAllBytes($@"{contentDirectory}{Path.AltDirectorySeparatorChar}{t.Z}-{t.X}-{t.Y}.b3dm", bytes);
Console.Write(".");
}
Expand Down
6 changes: 3 additions & 3 deletions src/pg2b3dm/pg2b3dm.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
<PackageOutputPath>./nupkg</PackageOutputPath>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<ToolCommandName>pg2b3dm</ToolCommandName>
<Version>1.7.1</Version>
<Version>1.8.0</Version>
<Description>Console tool for converting PostGIS geometries to B3dm tiles (3D Tiles)</Description>
<AssemblyVersion>1.7.1</AssemblyVersion>
<FileVersion>1.7.1</FileVersion>
<AssemblyVersion>1.8.0</AssemblyVersion>
<FileVersion>1.8.0</FileVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Company>Geodan</Company>
Expand Down
8 changes: 6 additions & 2 deletions src/wkb2gltf.core.tests/B3dmCreatorTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections.Generic;
using System.IO;
using B3dmCore;
using NUnit.Framework;
using Wkx;

Expand All @@ -20,10 +22,12 @@ public void CreateB3dmTest()
var attributes = new Dictionary<string, List<object>>();
attributes.Add("id", new List<object>() { "1" });
// act
var b3dm = B3dmCreator.GetB3dm(attributes, new List<List<Triangle>>() { triangles });
var bytes = TileCreator.GetTile(attributes, new List<List<Triangle>>() { triangles });

// assert
var stream = new MemoryStream(bytes);
var b3dm = B3dmReader.ReadB3dm(stream);
Assert.IsTrue(b3dm.B3dmHeader.Version == 1);
Assert.IsTrue(b3dm.BatchTableJson == "{\"id\":[\"1\"]}");
Assert.IsTrue(b3dm.BatchTableJson == "{\"id\":[\"1\"]} ");
}
}
2 changes: 1 addition & 1 deletion src/wkb2gltf.core.tests/B3dmWriterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public void FirstB3dmWriterTest()
geometryRecord.Attributes = attributes;

// act
var b3dmBytes = B3dmWriter.ToB3dm(new List<GeometryRecord> { geometryRecord }, addOutlines: true);
var b3dmBytes = TileWriter.ToTile(new List<GeometryRecord> { geometryRecord }, addOutlines: true);

// assert
Assert.IsTrue(b3dmBytes != null);
Expand Down
29 changes: 29 additions & 0 deletions src/wkb2gltf.core.tests/GlbCreatorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,35 @@ namespace Wkb2Gltf.Tests;

public class GlbCreatorTests
{

[Test]
public void CreateGltfWithAttributesTest()
{
// arrange
var p0 = new Point(0, 0, 0);
var p1 = new Point(1, 1, 0);
var p2 = new Point(1, 0, 0);

var triangle1 = new Triangle(p0, p1, p2, 0);
var triangles = new List<Triangle>() { triangle1 };

var attributes = new Dictionary<string, List<object>>();
attributes.Add("id", new List<object>() { "1" });
attributes.Add("id1", new List<object>() { 1 });
attributes.Add("id2", new List<object>() { (uint)1 });
attributes.Add("id3", new List<object>() { 1.1 });

// act
var bytes = TileCreator.GetTile(attributes, new List<List<Triangle>>() { triangles }, createGltf: true);
var fileName = Path.Combine(TestContext.CurrentContext.WorkDirectory, "gltf_withattributes.glb");
File.WriteAllBytes(fileName, bytes);


// assert
var model = ModelRoot.Load(fileName);
Assert.AreEqual(1, model.LogicalMeshes[0].Primitives.Count);
}

[Test]
public void CreateGlbWithDefaultColor() {

Expand Down
65 changes: 57 additions & 8 deletions src/wkb2gltf.core/GlbCreator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using SharpGLTF.Geometry;
Expand All @@ -12,31 +13,42 @@ namespace Wkb2Gltf;

public static class GlbCreator
{
public static byte[] GetGlb(List<List<Triangle>> triangles, string copyright = "", bool addOutlines = false, string defaultColor = "#FFFFFF", string defaultMetallicRoughness = "#008000", bool defaultDoubleSided = true)
public static byte[] GetGlb(List<List<Triangle>> triangles, string copyright = "", bool addOutlines = false, string defaultColor = "#FFFFFF", string defaultMetallicRoughness = "#008000", bool defaultDoubleSided = true, Dictionary<string, List<object>> attributes = null, bool createGltf = false, bool doubleSided=false)
{
var materialCache = new MaterialsCache();
var shader = new Shader();
shader.PbrMetallicRoughness = new PbrMetallicRoughness() { BaseColor = defaultColor, MetallicRoughness = defaultMetallicRoughness };
var defaultMaterial = MaterialCreator.CreateMaterial(shader, defaultDoubleSided);

var mesh = new MeshBuilder<VertexPositionNormal, VertexWithBatchId, VertexEmpty>("mesh");
var meshBatchId = new MeshBuilder<VertexPositionNormal, VertexWithBatchId, VertexEmpty>("mesh");
var meshFeatureIds = new MeshBuilder<VertexPositionNormal, VertexWithFeatureId, VertexEmpty>("mesh");

foreach (var tri in triangles) {
foreach (var triangle in tri) {
MaterialBuilder material;

if (triangle.Shader != null) {
material = materialCache.GetMaterialBuilderByShader(triangle.Shader);
material = materialCache.GetMaterialBuilderByShader(triangle.Shader, doubleSided);
}
else {
material = defaultMaterial;
}

DrawTriangle(triangle, material, mesh);
if (createGltf) {
DrawTriangleWithFeatureId(triangle, material, meshFeatureIds);
}
else {
DrawTriangleWithBatchId(triangle, material, meshBatchId);
}
}
}
var scene = new SceneBuilder();
scene.AddRigidMesh(mesh, Matrix4x4.Identity);
if(createGltf) {
scene.AddRigidMesh(meshFeatureIds, Matrix4x4.Identity);
}
else {
scene.AddRigidMesh(meshBatchId, Matrix4x4.Identity);
}
var model = scene.ToGltf2();
model.Asset.Copyright = copyright;

Expand All @@ -48,22 +60,59 @@ public static byte[] GetGlb(List<List<Triangle>> triangles, string copyright = "
model.LogicalNodes.First().LocalTransform = new SharpGLTF.Transforms.AffineTransform(localTransform);

if (addOutlines) {
foreach(var primitive in model.LogicalMeshes[0].Primitives) {
foreach (var primitive in model.LogicalMeshes[0].Primitives) {
primitive.AddOutlines();
}
}

if (createGltf && attributes!=null && attributes.Count>0) {
var ext = model.InitializeMetadataExtension("propertyTable", attributes.First().Value.Count);
foreach (var attribute in attributes) {
var type = attribute.Value.FirstOrDefault().GetType();
// todo: do not cast all these types to float
if(type == typeof(decimal) || type == typeof(double) || type == typeof(float)) {
var list = attribute.Value.ConvertAll(x => Convert.ToSingle(x));
model.AddMetadata(ext, attribute.Key, list);
}
else if (type == typeof(uint)) {
var list = attribute.Value.ConvertAll(x => (uint)x);
model.AddMetadata(ext, attribute.Key, list);
}
else if (type == typeof(int)) {
var list = attribute.Value.ConvertAll(x => (int)x);
model.AddMetadata(ext, attribute.Key, list);
}
else {
var list = attribute.Value.ConvertAll(x => x.ToString());
model.AddMetadata(ext, attribute.Key, list);
}

// todo add other types?
}
}


var bytes = model.WriteGLB().Array;

return bytes;
}

private static bool DrawTriangle(Triangle triangle, MaterialBuilder material, MeshBuilder<VertexPositionNormal, VertexWithBatchId, VertexEmpty> mesh)
private static bool DrawTriangleWithBatchId(Triangle triangle, MaterialBuilder material, MeshBuilder<VertexPositionNormal, VertexWithBatchId, VertexEmpty> mesh)
{
var normal = triangle.GetNormal();
var prim = mesh.UsePrimitive(material);
var vectors = triangle.ToVectors();
var indices = prim.AddTriangleWithBatchId(vectors, normal, triangle.GetBatchId());
return indices.Item1 > 0;
}

private static bool DrawTriangleWithFeatureId(Triangle triangle, MaterialBuilder material, MeshBuilder<VertexPositionNormal, VertexWithFeatureId, VertexEmpty> mesh)
{
var normal = triangle.GetNormal();
var prim = mesh.UsePrimitive(material);
var vectors = triangle.ToVectors();
var indices = prim.AddTriangleWithFeatureId(vectors, normal, triangle.GetBatchId());
return indices.Item1 > 0;
}

}
Loading

0 comments on commit 0e10a85

Please sign in to comment.