Skip to content

Commit

Permalink
add support for multiple models with gpu instancing
Browse files Browse the repository at this point in the history
  • Loading branch information
bertt committed Jun 19, 2024
1 parent d49d997 commit 07775d9
Showing 1 changed file with 161 additions and 59 deletions.
220 changes: 161 additions & 59 deletions src/TileHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Instance> 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<Instance> 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<Instance> 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<MaterialBuilder> 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<Instance> 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();
Expand Down Expand Up @@ -91,12 +183,6 @@ private static void CalculateArrays(List<Instance> 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<Instance> positions, bool UseScaleNonUniform, List<JArray> tags)
{
var modelRoot = ModelRoot.Load((string)model);
Expand All @@ -106,49 +192,28 @@ private static byte[] GetGpuGlb(object model, List<Instance> 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,
Expand All @@ -165,32 +230,11 @@ private static byte[] GetGpuGlb(object model, List<Instance> 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);
Expand All @@ -202,6 +246,64 @@ private static byte[] GetGpuGlb(object model, List<Instance> positions, bool Use
return bytes;
}

private static PropertyTable GetPropertyTable(List<Instance> positions, List<JArray> 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<Vector3> positions, List<float> scales, List<Vector3> scalesNonUniform, List<Vector3> normalUps, List<Vector3> normalRights, List<JArray> tags, bool UseExternalModel = false, bool UseScaleNonUniform = false)
{
I3dm.Tile.I3dm i3dm = null;
Expand Down

0 comments on commit 07775d9

Please sign in to comment.