Skip to content

Commit

Permalink
Deduplicated logic around generating materials between the low-level …
Browse files Browse the repository at this point in the history
…and high-level GLTF code, and optimized how textures are converted into GLTF images.
  • Loading branch information
MeltyPlayer committed Oct 24, 2023
1 parent 4e030db commit 8d28824
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 184 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Linq;

using fin.log;
using fin.model.util;
using fin.util.asserts;
using fin.util.image;

using SharpGLTF.Materials;
using SharpGLTF.Schema2;

using AlphaMode = SharpGLTF.Materials.AlphaMode;

namespace fin.model.io.exporters.gltf {
public interface IGltfModelExporter : IModelExporter {
bool UvIndices { get; set; }
Expand Down Expand Up @@ -61,84 +53,8 @@ public ModelRoot CreateModelRoot(IModel model, float scale) {
model.AnimationManager.Animations);

// Builds materials.
// TODO: Update this if GLTF is ever extended...
var finToTexCoordAndGltfMaterial =
new Dictionary<IMaterial, (IList<byte>, MaterialBuilder)>();
{
foreach (var finMaterial in model.MaterialManager.All) {
var gltfMaterial = new MaterialBuilder(finMaterial.Name)
.WithDoubleSide(finMaterial.CullingMode switch {
CullingMode.SHOW_FRONT_ONLY => false,
// Darn, guess we can't support this.
CullingMode.SHOW_BACK_ONLY => true,
CullingMode.SHOW_BOTH => true,
// Darn, guess we can't support this either.
CullingMode.SHOW_NEITHER => false,
_ => throw new ArgumentOutOfRangeException()
})
.WithSpecularGlossinessShader()
.WithSpecularGlossiness(new Vector3(0), 0);

switch (finMaterial) {
case IStandardMaterial standardMaterial: {
var diffuseTexture = standardMaterial.DiffuseTexture;
if (diffuseTexture != null) {
gltfMaterial.UseChannel(KnownChannel.Diffuse)
.UseTexture(diffuseTexture);
}

var normalTexture = standardMaterial.NormalTexture;
if (normalTexture != null) {
gltfMaterial.UseChannel(KnownChannel.Normal)
.UseTexture(normalTexture);
}

var emissiveTexture = standardMaterial.EmissiveTexture;
if (emissiveTexture != null) {
gltfMaterial.UseChannel(KnownChannel.Emissive)
.UseTexture(emissiveTexture);
}

/*var specularTexture = standardMaterial.SpecularTexture;
if (specularTexture != null) {
gltfMaterial.WithSpecularGlossiness(
GltfModelExporter.GetGltfImageFromFinTexture_(
specularTexture), new Vector3(.1f), .1f);
}*/

var ambientOcclusionTexture =
standardMaterial.AmbientOcclusionTexture;
if (ambientOcclusionTexture != null) {
gltfMaterial
.UseChannel(KnownChannel.Occlusion)
.UseTexture(ambientOcclusionTexture);
}

break;
}
default: {
var texture = PrimaryTextureFinder.GetFor(finMaterial);
if (texture != null) {
var alphaMode = texture.TransparencyType switch {
ImageTransparencyType.OPAQUE => AlphaMode.OPAQUE,
ImageTransparencyType.MASK => AlphaMode.MASK,
ImageTransparencyType.TRANSPARENT => AlphaMode.BLEND,
_ => throw new ArgumentOutOfRangeException()
};
gltfMaterial.WithAlpha(alphaMode);

gltfMaterial
.UseChannel(KnownChannel.Diffuse)
.UseTexture(texture);
}
break;
}
}

finToTexCoordAndGltfMaterial[finMaterial] =
(new byte[] { 0 }, gltfMaterial);
}
}
new GltfMaterialBuilder().GetMaterialBuilders(model.MaterialManager);

// Builds meshes.
var meshBuilder = new GltfMeshBuilder { UvIndices = this.UvIndices };
Expand Down Expand Up @@ -183,8 +99,8 @@ public void ExportModel(IModelExporterParams modelExporterParams) {

var writeSettings = new WriteSettings {
ImageWriting = this.Embedded
? ResourceWriteMode.EmbeddedAsBase64
: ResourceWriteMode.SatelliteFile,
? ResourceWriteMode.EmbeddedAsBase64
: ResourceWriteMode.SatelliteFile,
};

var outputPath = outputFile.FullPath;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;

using CommunityToolkit.HighPerformance.Helpers;

using fin.image;
using fin.model.util;
using fin.util.image;

using SharpGLTF.Materials;
using SharpGLTF.Memory;
using SharpGLTF.Schema2;

using AlphaMode = SharpGLTF.Materials.AlphaMode;

namespace fin.model.io.exporters.gltf {
public class GltfMaterialBuilder {
private readonly struct Fin2GltfImageConverter : IAction {
private readonly IImage[] finImages_;
private readonly IDictionary<IImage, MemoryImage> gltfImageByFinImage_;

public Fin2GltfImageConverter(IImage[] finImages,
IDictionary<IImage, MemoryImage>
gltfImageByFinImage) {
this.finImages_ = finImages;
this.gltfImageByFinImage_ = gltfImageByFinImage;
}

public void Invoke(int i) {
var finImage = this.finImages_[i];

using var imageStream = new MemoryStream();
finImage.ExportToStream(imageStream, LocalImageFormat.PNG);

this.gltfImageByFinImage_[finImage] =
new MemoryImage(imageStream.ToArray());
}
}

public IDictionary<IMaterial, Material> GetMaterials(
ModelRoot gltfModelRoot,
IMaterialManager finMaterialManager)
=> this.ConvertMaterials_(finMaterialManager)
.ToDictionary(tuple => tuple.Item1,
tuple => gltfModelRoot.CreateMaterial(tuple.Item2));

public IDictionary<IMaterial, MaterialBuilder> GetMaterialBuilders(
IMaterialManager finMaterialManager)
=> this.ConvertMaterials_(finMaterialManager)
.ToDictionary(tuple => tuple.Item1, tuple => tuple.Item2);

private IEnumerable<(IMaterial, MaterialBuilder)> ConvertMaterials_(
IMaterialManager finMaterialManager) {
var finImages = finMaterialManager.Textures
.Select(texture => texture.Image)
.Distinct()
.ToArray();
var gltfImageByFinImage = new ConcurrentDictionary<IImage, MemoryImage>();
ParallelHelper.For(0,
finImages.Length,
new Fin2GltfImageConverter(
finImages,
gltfImageByFinImage));

// TODO: Update this if GLTF is ever extended...
return finMaterialManager.All.Select(finMaterial => {
var gltfMaterialBuilder
= new MaterialBuilder(finMaterial.Name)
.WithDoubleSide(finMaterial.CullingMode switch {
CullingMode.SHOW_FRONT_ONLY => false,
// Darn, guess we can't support this.
CullingMode.SHOW_BACK_ONLY => true,
CullingMode.SHOW_BOTH => true,
// Darn, guess we can't support this either.
CullingMode.SHOW_NEITHER => false,
_ => throw new ArgumentOutOfRangeException()
})
.WithSpecularGlossinessShader()
.WithSpecularGlossiness(new Vector3(0), 0);

switch (finMaterial) {
case IStandardMaterial standardMaterial: {
var diffuseTexture = standardMaterial.DiffuseTexture;
if (diffuseTexture != null) {
gltfMaterialBuilder.UseChannel(KnownChannel.Diffuse)
.UseTexture(diffuseTexture,
gltfImageByFinImage[
diffuseTexture.Image]);
}

var normalTexture = standardMaterial.NormalTexture;
if (normalTexture != null) {
gltfMaterialBuilder.UseChannel(KnownChannel.Normal)
.UseTexture(normalTexture,
gltfImageByFinImage
[normalTexture.Image]);
}

var emissiveTexture = standardMaterial.EmissiveTexture;
if (emissiveTexture != null) {
gltfMaterialBuilder.UseChannel(KnownChannel.Emissive)
.UseTexture(emissiveTexture,
gltfImageByFinImage[
emissiveTexture.Image]);
}

/*var specularTexture = standardMaterial.SpecularTexture;
if (specularTexture != null) {
gltfMaterial.WithSpecularGlossiness(
GltfModelExporter.GetGltfImageFromFinTexture_(
specularTexture), new Vector3(.1f), .1f);
}*/

var ambientOcclusionTexture =
standardMaterial.AmbientOcclusionTexture;
if (ambientOcclusionTexture != null) {
gltfMaterialBuilder
.UseChannel(KnownChannel.Occlusion)
.UseTexture(ambientOcclusionTexture,
gltfImageByFinImage[
ambientOcclusionTexture.Image]);
}

break;
}
default: {
var texture = PrimaryTextureFinder.GetFor(finMaterial);
if (texture != null) {
var alphaMode = texture.TransparencyType switch {
ImageTransparencyType.OPAQUE => AlphaMode.OPAQUE,
ImageTransparencyType.MASK => AlphaMode.MASK,
ImageTransparencyType.TRANSPARENT => AlphaMode.BLEND,
_ => throw new ArgumentOutOfRangeException()
};
gltfMaterialBuilder.WithAlpha(alphaMode);

gltfMaterialBuilder
.UseChannel(KnownChannel.Diffuse)
.UseTexture(texture, gltfImageByFinImage[texture.Image]);
}

break;
}
}

return (finMaterial, gltfMaterial: gltfMaterialBuilder);
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ public IList<Mesh> BuildAndBindMesh(
ModelRoot gltfModel,
IModel model,
float scale,
Dictionary<IMaterial, (IList<byte>, MaterialBuilder)>
finToTexCoordAndGltfMaterial) {
IDictionary<IMaterial, MaterialBuilder> finToTexCoordAndGltfMaterial) {
var skin = model.Skin;
var vertexAccessor = ConsistentVertexAccessor.GetAccessorForModel(model);

Expand All @@ -52,7 +51,7 @@ public IList<Mesh> BuildAndBindMesh(
foreach (var primitive in finMesh.Primitives) {
MaterialBuilder materialBuilder;
if (primitive.Material != null) {
(_, materialBuilder) =
materialBuilder =
finToTexCoordAndGltfMaterial[primitive.Material];
} else {
materialBuilder = nullMaterialBuilder;
Expand Down Expand Up @@ -161,9 +160,9 @@ public IList<Mesh> BuildAndBindMesh(
gltfMeshBuilder.UsePrimitive(materialBuilder, 3);

foreach (var (v1, v2, v3) in primitive
.GetOrderedTriangleVertexIndices()
.Select(i => vertices[i])
.SeparateTriplets()) {
.GetOrderedTriangleVertexIndices()
.Select(i => vertices[i])
.SeparateTriplets()) {
triangles.AddTriangle(v1, v2, v3);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
namespace fin.model.io.exporters.gltf {
public static class GltfTextureUtil {
public static TextureBuilder UseTexture(this ChannelBuilder channelBuilder,
ITexture finTexture) {
ITexture finTexture,
MemoryImage memoryImage) {
var textureBuilder = channelBuilder.UseTexture();
textureBuilder
.WithPrimaryImage(
GltfTextureUtil.GetGltfImageFromFinTexture_(finTexture))
.WithPrimaryImage(memoryImage)
.WithCoordinateSet(0)
.WithSampler(
GltfTextureUtil.ConvertWrapMode_(finTexture.WrapModeU),
Expand All @@ -29,14 +29,6 @@ public static TextureBuilder UseTexture(this ChannelBuilder channelBuilder,
return textureBuilder;
}

private static MemoryImage GetGltfImageFromFinTexture_(
ITexture finTexture) {
using var imageStream = new MemoryStream();
finTexture.Image.ExportToStream(imageStream, finTexture.BestImageFormat);
var imageBytes = imageStream.ToArray();
return new MemoryImage(imageBytes);
}

private static TextureWrapMode ConvertWrapMode_(WrapMode wrapMode)
=> wrapMode switch {
WrapMode.CLAMP => TextureWrapMode.CLAMP_TO_EDGE,
Expand Down
Loading

0 comments on commit 8d28824

Please sign in to comment.