Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Text;

using SharpGLTF.SchemaReflection;

namespace SharpGLTF
{
class GaussianSplattingExtension : SchemaProcessor
{
private static string SchemaUri => KhronosExtensions.KhronosExtensionPath("KHR_gaussian_splatting", "mesh.primitive.KHR_gaussian_splatting.schema.json");

private const string ExtensionRootClassName = "KHR_gaussian_splatting glTF Mesh Primitive Extension";

public override IEnumerable<(string, SchemaType.Context)> ReadSchema()
{
var ctx = SchemaProcessing.LoadExtensionSchemaContext(SchemaUri);

yield return ("ext.GaussianSplatting.g", ctx);
}

public override void PrepareTypes(CodeGen.CSharpEmitter newEmitter, SchemaType.Context ctx)
{
newEmitter.SetRuntimeName(ExtensionRootClassName, "GaussianSplatting");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public static IEnumerable<SchemaProcessor> GetExtensionsProcessors()

// gpu mesh instancing
yield return new MeshGpuInstancingExtension();
yield return new GaussianSplattingExtension();

// textures
yield return new TextureTransformExtension();
Expand Down
16 changes: 16 additions & 0 deletions src/SharpGLTF.Core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ It also handles the way a mesh is brought from its local space to world space, i
- [x] [KHR_lights_punctual](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual)
- [x] [KHR_mesh_quantization](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_mesh_quantization)
- [x] [EXT_mesh_gpu_instancing](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Vendor/EXT_mesh_gpu_instancing)
- [x] [KHR_gaussian_splatting](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_gaussian_splatting)

#### KHR_gaussian_splatting quick usage

```c#
var primitive = mesh.CreatePrimitive().WithIndicesAutomatic(PrimitiveType.POINTS);
primitive.WithVertexAccessor("POSITION", positions);
primitive.WithVertexAccessor(GaussianSplatting.AttributeRotation, rotations);
primitive.WithVertexAccessor(GaussianSplatting.AttributeScale, scales);
primitive.WithVertexAccessor(GaussianSplatting.AttributeOpacity, opacities);
primitive.WithVertexAccessor(GaussianSplatting.AttributeSHDegree0Coef0, shDegree0);

var gaussian = primitive.UseGaussianSplatting();
gaussian.Kernel = GaussianSplatting.KernelEllipse;
gaussian.ColorSpace = GaussianSplatting.ColorSpaceSrgbRec709Display;
```

- [x] [KHR_materials_pbrSpecularGlossiness](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness)
- This extension has been declared _obsolete_ by Khronos, in favour of KHR_materials_specular + KHR_materials_IOR
Expand Down
113 changes: 113 additions & 0 deletions src/SharpGLTF.Core/Schema2/Generated/ext.GaussianSplatting.g.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// <auto-generated/>

//------------------------------------------------------------------------------------------------
// This file has been programatically generated; DON´T EDIT!
//------------------------------------------------------------------------------------------------

#pragma warning disable SA1001
#pragma warning disable SA1027
#pragma warning disable SA1028
#pragma warning disable SA1121
#pragma warning disable SA1205
#pragma warning disable SA1309
#pragma warning disable SA1402
#pragma warning disable SA1505
#pragma warning disable SA1507
#pragma warning disable SA1508
#pragma warning disable SA1652

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Numerics;
using System.Text.Json;

using JSONREADER = System.Text.Json.Utf8JsonReader;
using JSONWRITER = System.Text.Json.Utf8JsonWriter;
using FIELDINFO = SharpGLTF.Reflection.FieldInfo;


namespace SharpGLTF.Schema2
{
using Collections;

/// <summary>
/// Data defining a 3D Gaussian Splat primitive.
/// </summary>
#if NET6_0_OR_GREATER
[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
#endif
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("SharpGLTF.CodeGen", "1.0.0.0")]
partial class GaussianSplatting : ExtraProperties
{

#region reflection

public new const string SCHEMANAME = "KHR_gaussian_splatting";
protected override string GetSchemaName() => SCHEMANAME;

protected override IEnumerable<string> ReflectFieldsNames()
{
yield return "colorSpace";
yield return "kernel";
yield return "projection";
yield return "sortingMethod";
foreach(var f in base.ReflectFieldsNames()) yield return f;
}
protected override bool TryReflectField(string name, out FIELDINFO value)
{
switch(name)
{
case "colorSpace": value = FIELDINFO.From("colorSpace",this, instance => instance._colorSpace); return true;
case "kernel": value = FIELDINFO.From("kernel",this, instance => instance._kernel); return true;
case "projection": value = FIELDINFO.From("projection",this, instance => instance._projection ?? "perspective"); return true;
case "sortingMethod": value = FIELDINFO.From("sortingMethod",this, instance => instance._sortingMethod ?? "cameraDistance"); return true;
default: return base.TryReflectField(name, out value);
}
}

#endregion

#region data

private String _colorSpace;

private String _kernel;

private const String _projectionDefault = "perspective";
private String _projection;

private const String _sortingMethodDefault = "cameraDistance";
private String _sortingMethod;

#endregion

#region serialization

protected override void SerializeProperties(JSONWRITER writer)
{
base.SerializeProperties(writer);
SerializeProperty(writer, "colorSpace", _colorSpace);
SerializeProperty(writer, "kernel", _kernel);
SerializeProperty(writer, "projection", _projection);
SerializeProperty(writer, "sortingMethod", _sortingMethod);
}

protected override void DeserializeProperty(string jsonPropertyName, ref JSONREADER reader)
{
switch (jsonPropertyName)
{
case "colorSpace": DeserializePropertyValue<GaussianSplatting, String>(ref reader, this, out _colorSpace); break;
case "kernel": DeserializePropertyValue<GaussianSplatting, String>(ref reader, this, out _kernel); break;
case "projection": DeserializePropertyValue<GaussianSplatting, String>(ref reader, this, out _projection); break;
case "sortingMethod": DeserializePropertyValue<GaussianSplatting, String>(ref reader, this, out _sortingMethod); break;
default: base.DeserializeProperty(jsonPropertyName,ref reader); break;
}
}

#endregion

}

}
1 change: 1 addition & 0 deletions src/SharpGLTF.Core/Schema2/gltf.ExtensionsFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ static ExtensionsFactory()
RegisterExtension<Node, _NodePunctualLight>("KHR_lights_punctual", p=> new _NodePunctualLight(p));
RegisterExtension<Node, MeshGpuInstancing>("EXT_mesh_gpu_instancing", p=> new MeshGpuInstancing(p));
RegisterExtension<Node, _NodeVisibility>(_NodeVisibility.SCHEMANAME, p => new _NodeVisibility(p));
RegisterExtension<MeshPrimitive, GaussianSplatting>(GaussianSplatting.SCHEMANAME, p => new GaussianSplatting(p));

RegisterExtension<Material, MaterialUnlit>("KHR_materials_unlit", p => new MaterialUnlit(p));
RegisterExtension<Material, MaterialSheen>("KHR_materials_sheen", p => new MaterialSheen(p));
Expand Down
201 changes: 201 additions & 0 deletions src/SharpGLTF.Core/Schema2/gltf.MeshPrimitive.GaussianSplatting.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
using System;

namespace SharpGLTF.Schema2
{
public partial class GaussianSplatting
{
#region lifecycle

internal GaussianSplatting(MeshPrimitive primitive)
{
_Owner = primitive;
}

#endregion

#region constants

public const string KernelEllipse = "ellipse";

public const string ColorSpaceSrgbRec709Display = "srgb_rec709_display";
public const string ColorSpaceLinRec709Display = "lin_rec709_display";

public const string ProjectionPerspective = "perspective";
public const string SortingMethodCameraDistance = "cameraDistance";

public const string AttributeRotation = "KHR_gaussian_splatting:ROTATION";
public const string AttributeScale = "KHR_gaussian_splatting:SCALE";
public const string AttributeOpacity = "KHR_gaussian_splatting:OPACITY";
public const string AttributeSHDegree0Coef0 = "KHR_gaussian_splatting:SH_DEGREE_0_COEF_0";

#endregion

#region data (not serializable)

private readonly MeshPrimitive _Owner;

#endregion

#region properties

public MeshPrimitive LogicalParent => _Owner;

public string Kernel
{
get => _kernel;
set
{
Guard.NotNullOrEmpty(value, nameof(value));
_kernel = value;
}
}

public string ColorSpace
{
get => _colorSpace;
set
{
Guard.NotNullOrEmpty(value, nameof(value));
_colorSpace = value;
}
}

public string Projection
{
get => _projection ?? _projectionDefault;
set
{
value = value.AsEmptyNullable();
_projection = value == _projectionDefault ? null : value;
}
}

public string SortingMethod
{
get => _sortingMethod ?? _sortingMethodDefault;
set
{
value = value.AsEmptyNullable();
_sortingMethod = value == _sortingMethodDefault ? null : value;
}
}

#endregion

#region API

public static string GetSphericalHarmonicsAttribute(int degree, int coefficient)
{
Guard.MustBeBetweenOrEqualTo(degree, 0, 3, nameof(degree));

var coefficientCount = 2 * degree + 1;
Guard.MustBeBetweenOrEqualTo(coefficient, 0, coefficientCount - 1, nameof(coefficient));

return $"KHR_gaussian_splatting:SH_DEGREE_{degree}_COEF_{coefficient}";
}

#endregion
}

public sealed partial class MeshPrimitive
{
private static readonly string[] _GaussianRequiredAttributes =
{
GaussianSplatting.AttributeRotation,
GaussianSplatting.AttributeScale,
GaussianSplatting.AttributeOpacity,
GaussianSplatting.AttributeSHDegree0Coef0
};

public GaussianSplatting GetGaussianSplatting()
{
return this.GetExtension<GaussianSplatting>();
}

public GaussianSplatting UseGaussianSplatting()
{
var ext = GetGaussianSplatting();
if (ext == null)
{
ext = new GaussianSplatting(this);
this.SetExtension(ext);
}

return ext;
}

public void RemoveGaussianSplatting()
{
this.RemoveExtensions<GaussianSplatting>();
}

private void _ValidateGaussianSplatting(Validation.ValidationContext validate)
{
var gaussian = GetGaussianSplatting();
if (gaussian == null) return;

validate.EnumsAreEqual(nameof(DrawPrimitiveType), DrawPrimitiveType, PrimitiveType.POINTS);
validate.IsDefined("Attributes.POSITION", GetVertexAccessor("POSITION"));

foreach (var semantic in _GaussianRequiredAttributes)
{
validate.IsDefined($"Attributes.{semantic}", GetVertexAccessor(semantic));
}

var rotation = GetVertexAccessor(GaussianSplatting.AttributeRotation);
if (rotation != null)
{
validate.IsAnyOf
(
$"Attributes.{GaussianSplatting.AttributeRotation}.Format",
rotation.Format,
(DimensionType.VEC4, EncodingType.FLOAT),
(DimensionType.VEC4, EncodingType.BYTE, true),
(DimensionType.VEC4, EncodingType.SHORT, true)
);
}

var scale = GetVertexAccessor(GaussianSplatting.AttributeScale);
if (scale != null)
{
validate.IsAnyOf
(
$"Attributes.{GaussianSplatting.AttributeScale}.Format",
scale.Format,
(DimensionType.VEC3, EncodingType.FLOAT),
(DimensionType.VEC3, EncodingType.BYTE),
(DimensionType.VEC3, EncodingType.BYTE, true),
(DimensionType.VEC3, EncodingType.SHORT),
(DimensionType.VEC3, EncodingType.SHORT, true)
);
}

var opacity = GetVertexAccessor(GaussianSplatting.AttributeOpacity);
if (opacity != null)
{
validate.IsAnyOf
(
$"Attributes.{GaussianSplatting.AttributeOpacity}.Format",
opacity.Format,
(DimensionType.SCALAR, EncodingType.FLOAT),
(DimensionType.SCALAR, EncodingType.UNSIGNED_BYTE, true),
(DimensionType.SCALAR, EncodingType.UNSIGNED_SHORT, true)
);
}

var sh0 = GetVertexAccessor(GaussianSplatting.AttributeSHDegree0Coef0);
if (sh0 != null)
{
validate.IsAnyOf
(
$"Attributes.{GaussianSplatting.AttributeSHDegree0Coef0}.Format",
sh0.Format,
(DimensionType.VEC3, EncodingType.FLOAT)
);
}

validate.IsTrue(nameof(gaussian.Kernel), !string.IsNullOrWhiteSpace(gaussian.Kernel), "must be defined.");
validate.IsTrue(nameof(gaussian.ColorSpace), !string.IsNullOrWhiteSpace(gaussian.ColorSpace), "must be defined.");
}
}
}
2 changes: 2 additions & 0 deletions src/SharpGLTF.Core/Schema2/gltf.MeshPrimitive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ protected override void OnValidateContent(Validation.ValidationContext validate)
{
base.OnValidateContent(validate);

_ValidateGaussianSplatting(validate);

// all vertices must have the same vertex count

int vertexCount = -1;
Expand Down
Loading
Loading