diff --git a/src/TileHandler.cs b/src/TileHandler.cs index ef74427..729efd9 100644 --- a/src/TileHandler.cs +++ b/src/TileHandler.cs @@ -11,13 +11,105 @@ using Wkx; using System.Text.Json.Nodes; using SharpGLTF.Schema2.Tiles3D; +using SharpGLTF.Geometry; +using SharpGLTF.Materials; namespace i3dm.export; public static class TileHandler { + public static byte[] GetGPUTile(List instances, bool UseScaleNonUniform) + { + var firstPosition = (Point)instances[0].Position; + var translation = ToYUp(firstPosition); + + var sceneBuilder = AddModels(instances, translation, UseScaleNonUniform); + + var settings = SceneBuilderSchema2Settings.WithGpuInstancing; + settings.GpuMeshInstancingMinCount = 0; + var finalModel = sceneBuilder.ToGltf2(settings); + + // todo: add metadata + foreach (var node in finalModel.LogicalNodes) + { + node.LocalTransform *= Matrix4x4.CreateTranslation(translation); + } + + var bytes = finalModel.WriteGLB().Array; + return bytes; + } + + private static SceneBuilder AddModels(IEnumerable instances, Vector3 translation, bool UseScaleNonUniform) + { + var sceneBuilder = new SceneBuilder(); + + var distinctModels = instances.Select(s => s.Model).Distinct(); + + foreach (var model in distinctModels) + { + AddModelInstancesToScene(sceneBuilder, instances, UseScaleNonUniform, translation, (string)model); + } + + return sceneBuilder; + } + + private static void AddModelInstancesToScene(SceneBuilder sceneBuilder, IEnumerable instances, bool UseScaleNonUniform, Vector3 translation, string model) + { + var pointId = 0; + var modelInstances = instances.Where(s => s.Model.Equals(model)).ToList(); + var modelRoot = ModelRoot.Load(model); + var meshBuilder = modelRoot.LogicalMeshes.First().ToMeshBuilder(); // todo: what if there are multiple meshes? + + foreach (var instance in modelInstances) + { + var sceneBuilderModel = GetSceneBuilder(meshBuilder, instance, UseScaleNonUniform, translation, pointId); + sceneBuilder.AddScene(sceneBuilderModel, Matrix4x4.Identity); + pointId++; + } + } + + private static SceneBuilder GetSceneBuilder(IMeshBuilder meshBuilder, Instance instance, bool UseScaleNonUniform, Vector3 translation, int pointId ) + { + var transformation = GetInstanceTransform(instance, UseScaleNonUniform, translation); + var json = "{\"_FEATURE_ID_0\":" + pointId + "}"; + var sceneBuilder = new SceneBuilder(); + sceneBuilder.AddRigidMesh(meshBuilder, transformation).WithExtras(JsonNode.Parse(json)); + return sceneBuilder; + } + + private static AffineTransform GetInstanceTransform(Instance instance, bool UseScaleNonUniform, Vector3 translation) + { + var point = (Point)instance.Position; + + var position = ToYUp(point); + + var enu = EnuCalculator.GetLocalEnu(0, new Vector3((float)point.X, (float)point.Y, (float)point.Z)); + var forward = Vector3.Cross(enu.East, enu.Up); + forward = Vector3.Normalize(forward); + var m4 = GetTransformationMatrix(enu, forward); + + var instanceQuaternion = Quaternion.CreateFromYawPitchRoll((float)instance.Yaw, (float)instance.Pitch, (float)instance.Roll); + var res = Quaternion.CreateFromRotationMatrix(m4); + + var position2 = position - translation; + + var scale = UseScaleNonUniform ? + new Vector3((float)instance.ScaleNonUniform[0], (float)instance.ScaleNonUniform[1], (float)instance.ScaleNonUniform[2]) : + new Vector3((float)instance.Scale, (float)instance.Scale, (float)instance.Scale); + + var transformation = new AffineTransform( + scale, + new Quaternion(-res.X, -res.Z, res.Y, res.W) * instanceQuaternion, + position2); + return transformation; + } + public static byte[] GetTile(List instances, bool UseExternalModel = false, bool UseScaleNonUniform = false, bool useGpuInstancing = false) { + if (useGpuInstancing) + { + // return GetGPUTile(instances, UseScaleNonUniform); + }; if (useGpuInstancing && instances.Select(s => s.Model).Distinct().Count() > 1) { var firstModel = instances.Select(s => s.Model).First(); @@ -91,12 +183,6 @@ private static void CalculateArrays(List instances, bool UseScaleNonUn } } - private static Vector3 GetPosition(Point p) - { - var vec = new Vector3((float)(p.X), (float)(p.Y), (float)(p.Z.GetValueOrDefault())); - return vec; - } - private static byte[] GetGpuGlb(object model, List positions, bool UseScaleNonUniform, List tags) { var modelRoot = ModelRoot.Load((string)model); @@ -106,49 +192,28 @@ private static byte[] GetGpuGlb(object model, List positions, bool Use var pointId = 0; - Vector3 translation = Vector3.One; - bool first = true; + var firstPosition = (Point)positions[0].Position; + var translation = ToYUp(firstPosition); foreach (var p in positions) { var point = (Point)p.Position; - - var p1 = new Point((double)point.X, (double)point.Z, (double)point.Y * -1); - - var scale = UseScaleNonUniform ? - new Vector3((float)p.ScaleNonUniform[0], (float)p.ScaleNonUniform[1], (float)p.ScaleNonUniform[2]) : - new Vector3((float)p.Scale, (float)p.Scale, (float)p.Scale); + var position = ToYUp(point); var enu = EnuCalculator.GetLocalEnu(0, new Vector3((float)point.X, (float)point.Y, (float)point.Z)); - var forward = Vector3.Cross(enu.East, enu.Up); forward = Vector3.Normalize(forward); - var m4 = new Matrix4x4(); - m4.M11 = enu.East.X; - m4.M21 = enu.East.Y; - m4.M31 = enu.East.Z; - - m4.M12 = enu.Up.X; - m4.M22 = enu.Up.Y; - m4.M32 = enu.Up.Z; - - m4.M13 = forward.X; - m4.M23 = forward.Y; - m4.M33 = forward.Z; + var m4 = GetTransformationMatrix(enu, forward); var instanceQuaternion = Quaternion.CreateFromYawPitchRoll((float)p.Yaw, (float)p.Pitch, (float)p.Roll); var res = Quaternion.CreateFromRotationMatrix(m4); - var position = new Vector3((float)p1.X, (float)p1.Y, (float)p1.Z); - - if (first) - { - translation = position; - first = false; - } - var position2 = position - translation; + var scale = UseScaleNonUniform ? + new Vector3((float)p.ScaleNonUniform[0], (float)p.ScaleNonUniform[1], (float)p.ScaleNonUniform[2]) : + new Vector3((float)p.Scale, (float)p.Scale, (float)p.Scale); + var transformation = new AffineTransform( scale, new Quaternion(-res.X, -res.Z, res.Y, res.W) * instanceQuaternion, @@ -165,32 +230,11 @@ private static byte[] GetGpuGlb(object model, List positions, bool Use if (tags.Count > 0 && tags[0] != null) { - var rootMetadata = gltf.UseStructuralMetadata(); - var schema = rootMetadata.UseEmbeddedSchema("schema"); - var schemaClass = schema.UseClassMetadata("propertyTable"); - - propertyTable = schemaClass.AddPropertyTable(positions.Count); - - var properties = TinyJson.GetProperties(tags[0]); - foreach (var property in properties) - { - var values = TinyJson.GetValues(tags, property); - - var nameProperty = schemaClass - .UseProperty(property) - .WithStringType(); - - // todo: use other types than string - var strings = values.Select(s => s.ToString()).ToArray(); - - propertyTable - .UseProperty(nameProperty) - .SetValues(strings); - } + propertyTable = GetPropertyTable(positions, tags, gltf); } - var featureId0 = propertyTable!= null? - new FeatureIDBuilder(positions.Count, 0, propertyTable): + var featureId0 = propertyTable != null ? + new FeatureIDBuilder(positions.Count, 0, propertyTable) : new FeatureIDBuilder(positions.Count, 0); gltf.LogicalNodes[0].AddInstanceFeatureIds(featureId0); @@ -202,6 +246,64 @@ private static byte[] GetGpuGlb(object model, List positions, bool Use return bytes; } + private static PropertyTable GetPropertyTable(List positions, List tags, ModelRoot gltf) + { + PropertyTable propertyTable; + var rootMetadata = gltf.UseStructuralMetadata(); + var schema = rootMetadata.UseEmbeddedSchema("schema"); + var schemaClass = schema.UseClassMetadata("propertyTable"); + + propertyTable = schemaClass.AddPropertyTable(positions.Count); + + var properties = TinyJson.GetProperties(tags[0]); + foreach (var property in properties) + { + var values = TinyJson.GetValues(tags, property); + + var nameProperty = schemaClass + .UseProperty(property) + .WithStringType(); + + // todo: use other types than string + var strings = values.Select(s => s.ToString()).ToArray(); + + propertyTable + .UseProperty(nameProperty) + .SetValues(strings); + } + + return propertyTable; + } + + private static Vector3 ToYUp(Point position) + { + return new Vector3((float)position.X, (float)position.Z, (float)position.Y * -1); + } + + private static Vector3 GetPosition(Point p) + { + var vec = new Vector3((float)(p.X), (float)(p.Y), (float)(p.Z.GetValueOrDefault())); + return vec; + } + + + private static Matrix4x4 GetTransformationMatrix((Vector3 East, Vector3 North, Vector3 Up) enu, Vector3 forward) + { + var m4 = new Matrix4x4(); + m4.M11 = enu.East.X; + m4.M21 = enu.East.Y; + m4.M31 = enu.East.Z; + + m4.M12 = enu.Up.X; + m4.M22 = enu.Up.Y; + m4.M32 = enu.Up.Z; + + m4.M13 = forward.X; + m4.M23 = forward.Y; + m4.M33 = forward.Z; + return m4; + } + private static I3dm.Tile.I3dm GetI3dm(object model, List positions, List scales, List scalesNonUniform, List normalUps, List normalRights, List tags, bool UseExternalModel = false, bool UseScaleNonUniform = false) { I3dm.Tile.I3dm i3dm = null;