Smuggler/Importer.cs

394 lines
12 KiB
C#

using System.Collections.Generic;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Smuggler
{
public static class Importer
{
public static ModelData ImportGLB(GraphicsDevice graphicsDevice, Stream stream)
{
var sharpModel = SharpGLTF.Schema2.ModelRoot.ReadGLB(stream);
var vertexType = InferVertexType(sharpModel);
if (vertexType == null) { return null; }
var meshes = new List<MeshData>();
foreach (var mesh in sharpModel.LogicalMeshes)
{
var meshParts = new List<MeshPartData>();
foreach (var primitive in mesh.Primitives)
{
meshParts.Add(
ImportMeshPart(graphicsDevice, vertexType, primitive)
);
}
meshes.Add(new MeshData(meshParts.ToArray()));
}
var model = new ModelData(meshes.ToArray());
return model;
}
private static System.Type InferVertexType(SharpGLTF.Schema2.ModelRoot model)
{
bool hasColor = false;
bool hasNormal = false;
bool hasTexture = false;
if (model.LogicalMeshes.Count == 0) { return null; }
if (model.LogicalMeshes[0].Primitives.Count == 0) { return null; }
var samplePrimitive = model.LogicalMeshes[0].Primitives[0];
if (samplePrimitive.GetVertexAccessor("POSITION") == null)
{
System.Console.WriteLine("There is no Position attribute on this model's vertices. That seems weird!");
return null;
}
if (samplePrimitive.GetVertexAccessor("COLOR") != null)
{
hasColor = true;
}
if (samplePrimitive.GetVertexAccessor("NORMAL") != null)
{
hasNormal = true;
}
if (samplePrimitive.GetVertexAccessor("TEXCOORD_0") != null)
{
hasTexture = true;
}
if (hasColor && hasNormal && hasTexture)
{
// FIXME: Implement VertexPositionColorNormalTexture
return null;
}
if (hasColor && hasTexture)
{
return typeof(VertexPositionColorTexture);
}
if (hasNormal && hasTexture)
{
return typeof(VertexPositionNormalTexture);
}
if (hasColor)
{
return typeof(VertexPositionColor);
}
if (hasTexture)
{
return typeof(VertexPositionTexture);
}
System.Console.WriteLine("Could not infer vertex type");
return null;
}
private static MeshPartData ImportMeshPart(GraphicsDevice graphicsDevice, System.Type vertexType, SharpGLTF.Schema2.MeshPrimitive primitive)
{
var indices = Indices(primitive);
var positions = Positions(primitive);
var triangles = Triangles(primitive);
var vertexBuffer = new VertexBuffer(
graphicsDevice,
vertexType,
positions.Length,
BufferUsage.None
);
var indexBuffer = new IndexBuffer(
graphicsDevice,
IndexElementSize.ThirtyTwoBits,
indices.Length,
BufferUsage.None
);
indexBuffer.SetData(indices);
if (vertexType == typeof(VertexPositionColor))
{
ImportVertexPositionColor(primitive, vertexBuffer, indices, positions);
}
else if (vertexType == typeof(VertexPositionColorTexture))
{
ImportVertexPositionColorTexture(primitive, vertexBuffer, indices, positions);
}
else if (vertexType == typeof(VertexPositionNormalTexture))
{
ImportVertexPositionNormalTexture(primitive, vertexBuffer, indices, positions);
}
else if (vertexType == typeof(VertexPositionTexture))
{
ImportVertexPositionTexture(primitive, vertexBuffer, indices, positions);
}
var meshPartData = new MeshPartData(
vertexBuffer,
indexBuffer,
positions,
triangles
);
if (primitive.Material != null)
{
var normalChannel = primitive.Material.FindChannel("Normal");
if (normalChannel.HasValue)
{
if (normalChannel.Value.Texture != null)
{
meshPartData.NormalTexture = Texture2D.FromStream(
graphicsDevice,
normalChannel.Value.Texture.PrimaryImage.Content.Open()
);
}
}
var albedoChannel = primitive.Material.FindChannel("BaseColor");
if (albedoChannel.HasValue)
{
if (albedoChannel.Value.Texture != null)
{
meshPartData.AlbedoTexture = Texture2D.FromStream(
graphicsDevice,
albedoChannel.Value.Texture.PrimaryImage.Content.Open()
);
}
var parameter = albedoChannel.Value.Parameter;
meshPartData.Albedo = new Vector3(
parameter.X,
parameter.Y,
parameter.Z
);
}
var metallicRoughnessChannel = primitive.Material.FindChannel("MetallicRoughness");
if (metallicRoughnessChannel.HasValue)
{
if (metallicRoughnessChannel.Value.Texture != null)
{
meshPartData.MetallicRoughnessTexture = Texture2D.FromStream(
graphicsDevice,
metallicRoughnessChannel.Value.Texture.PrimaryImage.Content.Open()
);
}
var parameter = metallicRoughnessChannel.Value.Parameter;
meshPartData.Metallic = parameter.X;
meshPartData.Roughness = parameter.Y;
}
}
return meshPartData;
}
/* Attribute Getters */
private static Vector3[] Positions(SharpGLTF.Schema2.MeshPrimitive primitive)
{
var positionAccessor = primitive.GetVertexAccessor("POSITION").AsVector3Array();
var positions = new Vector3[positionAccessor.Count];
for (int i = 0; i < positions.Length; i++)
{
var position = positionAccessor[i];
positions[i] = new Vector3(-position.X, -position.Z, position.Y);
}
return positions;
}
private static Color[] Colors(SharpGLTF.Schema2.MeshPrimitive primitive)
{
var colorAccessor = primitive.GetVertexAccessor("COLOR").AsColorArray();
var colors = new Color[colorAccessor.Count];
for (int i = 0; i < colors.Length; i++)
{
var color = colorAccessor[i];
colors[i] = new Color(color.X, color.Y, color.Z, color.W);
}
return colors;
}
private static Vector3[] Normals(SharpGLTF.Schema2.MeshPrimitive primitive)
{
var normalAccessor = primitive.GetVertexAccessor("NORMAL").AsVector3Array();
var normals = new Vector3[normalAccessor.Count];
for (int i = 0; i < normals.Length; i++)
{
var normal = normalAccessor[i];
normals[i] = new Vector3(-normal.X, -normal.Z, normal.Y);
}
return normals;
}
private static Vector2[] TexCoords(SharpGLTF.Schema2.MeshPrimitive primitive)
{
var texcoordAccessor = primitive.GetVertexAccessor("TEXCOORD_0").AsVector2Array();
var texcoords = new Vector2[texcoordAccessor.Count];
for (int i = 0; i < texcoords.Length; i++)
{
var texcoord = texcoordAccessor[i];
texcoords[i] = new Vector2(texcoord.X, texcoord.Y);
}
return texcoords;
}
private static Triangle[] Triangles(SharpGLTF.Schema2.MeshPrimitive primitive)
{
var triangles = new List<Triangle>();
foreach (var (a, b, c) in primitive.GetTriangleIndices())
{
triangles.Add(new Triangle(a, b, c));
}
return triangles.ToArray();
}
private static uint[] Indices(SharpGLTF.Schema2.MeshPrimitive primitive)
{
var indexAccessor = primitive.GetIndexAccessor().AsIndicesArray();
var indices = new uint[indexAccessor.Count];
for (int i = 0; i < indices.Length; i++)
{
indices[i] = indexAccessor[i];
}
return indices;
}
/* Vertex Builders */
private static void ImportVertexPositionColor(
SharpGLTF.Schema2.MeshPrimitive primitive,
VertexBuffer vertexBuffer,
uint[] indices,
Vector3[] positions
)
{
var colors = Colors(primitive);
var vertices = new VertexPositionColor[positions.Length];
foreach (var index in indices)
{
var position = positions[index];
var color = colors[index];
vertices[index] = new VertexPositionColor(
position,
color
);
}
vertexBuffer.SetData(vertices);
}
private static void ImportVertexPositionColorTexture(
SharpGLTF.Schema2.MeshPrimitive primitive,
VertexBuffer vertexBuffer,
uint[] indices,
Vector3[] positions
)
{
var colors = Colors(primitive);
var texcoords = TexCoords(primitive);
var vertices = new VertexPositionColorTexture[positions.Length];
foreach (var index in indices)
{
var position = positions[index];
var color = colors[index];
var texcoord = texcoords[index];
vertices[index] = new VertexPositionColorTexture(
position,
color,
texcoord
);
}
vertexBuffer.SetData(vertices);
}
private static void ImportVertexPositionNormalTexture(
SharpGLTF.Schema2.MeshPrimitive primitive,
VertexBuffer vertexBuffer,
uint[] indices,
Vector3[] positions
)
{
var normals = Normals(primitive);
var texcoords = TexCoords(primitive);
var vertices = new VertexPositionNormalTexture[positions.Length];
foreach (var index in indices)
{
var position = positions[index];
var normal = normals[index];
var texcoord = texcoords[index];
vertices[index] = new VertexPositionNormalTexture(
position,
normal,
texcoord
);
}
vertexBuffer.SetData(vertices);
}
private static void ImportVertexPositionTexture(
SharpGLTF.Schema2.MeshPrimitive primitive,
VertexBuffer vertexBuffer,
uint[] indices,
Vector3[] positions
)
{
var texcoords = TexCoords(primitive);
var vertices = new VertexPositionTexture[positions.Length];
foreach (var index in indices)
{
var position = positions[index];
var texcoord = texcoords[index];
vertices[index] = new VertexPositionTexture(
position,
texcoord
);
}
vertexBuffer.SetData(vertices);
}
}
}