Skip to content

Commit

Permalink
feat(meshinfo2): read mesh with GraphicsBuffer to support R/W off meshes
Browse files Browse the repository at this point in the history
  • Loading branch information
anatawa12 committed Sep 18, 2024
1 parent d940f30 commit 427c28b
Showing 1 changed file with 188 additions and 45 deletions.
233 changes: 188 additions & 45 deletions Internal/MeshInfo2/MeshInfo2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -331,48 +331,31 @@ public void ReadStaticMesh(Mesh mesh)
Vertices.Clear();
for (var i = 0; i < mesh.vertexCount; i++) Vertices.Add(new Vertex());

CopyVertexAttr(mesh.vertices, (x, v) => x.Position = v);
if (mesh.GetVertexAttributeDimension(VertexAttribute.Normal) != 0)
{
HasNormals = true;
CopyVertexAttr(mesh.normals, (x, v) => x.Normal = v);
}
if (mesh.GetVertexAttributeDimension(VertexAttribute.Tangent) != 0)
{
HasTangent = true;
CopyVertexAttr(mesh.tangents, (x, v) => x.Tangent = v);
}
if (mesh.GetVertexAttributeDimension(VertexAttribute.Color) != 0)
{
HasColor = true;
CopyVertexAttr(mesh.colors32, (x, v) => x.Color = v);
}
var vertexBuffers = GetVertexBuffers(mesh);

CopyVertexAttr(mesh, VertexAttribute.Position, vertexBuffers, DataParsers.Vector3Provider,
setHasAttribute: null,
assign: (x, v) => x.Position = v);

CopyVertexAttr(mesh, VertexAttribute.Normal, vertexBuffers, DataParsers.Vector3Provider,
setHasAttribute: _ => HasNormals = true,
assign: (x, v) => x.Normal = v);

var uv2 = new List<Vector2>(0);
var uv3 = new List<Vector3>(0);
var uv4 = new List<Vector4>(0);
for (var index = 0; index <= 7; index++)
CopyVertexAttr(mesh, VertexAttribute.Tangent, vertexBuffers, DataParsers.Vector4Provider,
setHasAttribute: _ => HasTangent = true,
assign: (x, v) => x.Tangent = v);

// TODO: this may lost precision or HDR color
CopyVertexAttr(mesh, VertexAttribute.Color, vertexBuffers, DataParsers.Color32Provider,
setHasAttribute: _ => HasColor = true,
assign: (x, v) => x.Color = v);

for (var uvChannel = 0; uvChannel <= 7; uvChannel++)
{
// ReSharper disable AccessToModifiedClosure
switch (mesh.GetVertexAttributeDimension(VertexAttribute.TexCoord0 + index))
{
case 2:
SetTexCoordStatus(index, TexCoordStatus.Vector2);
mesh.GetUVs(index, uv2);
CopyVertexAttr(uv2, (x, v) => x.SetTexCoord(index, v));
break;
case 3:
SetTexCoordStatus(index, TexCoordStatus.Vector3);
mesh.GetUVs(index, uv3);
CopyVertexAttr(uv3, (x, v) => x.SetTexCoord(index, v));
break;
case 4:
SetTexCoordStatus(index, TexCoordStatus.Vector4);
mesh.GetUVs(index, uv4);
CopyVertexAttr(uv4, (x, v) => x.SetTexCoord(index, v));
break;
}

CopyVertexAttr(mesh, VertexAttribute.TexCoord0 + uvChannel, vertexBuffers, DataParsers.Vector4Provider,
setHasAttribute: dims => SetTexCoordStatus(uvChannel, TexCoordStatus.Vector2 + (dims - 2)),
assign: (x, v) => x.SetTexCoord(uvChannel, v));
// ReSharper restore AccessToModifiedClosure
}

Expand All @@ -388,16 +371,176 @@ public void ReadStaticMesh(Mesh mesh)
Profiler.EndSample();
}

void CopyVertexAttr<T>(T[] attributes, Action<Vertex, T> assign)
private static (byte[] buffer, int stride)[] GetVertexBuffers(Mesh mesh)
{
for (var i = 0; i < attributes.Length; i++)
assign(Vertices[i], attributes[i]);
var vertexBufferCount = mesh.vertexBufferCount;
var vertexBuffers = new (byte[] buffer, int stride)[vertexBufferCount];
for (var i = 0; i < vertexBufferCount; i++)
{
var vertexBuffer = mesh.GetVertexBuffer(i);
var stride = vertexBuffer.stride;
var data = new byte[vertexBuffer.count * vertexBuffer.stride];
vertexBuffer.GetData(data);
vertexBuffers[i] = (data, stride);
}

return vertexBuffers;
}

delegate T DataParser<T>(byte[] data, int offset);
delegate DataParser<T> ReadDataProvider<T>(VertexAttributeFormat format, int dimension);

void CopyVertexAttr<T>(
Mesh mesh,
VertexAttribute attribute,
(byte[] buffer, int stride)[] vertexDataList,
ReadDataProvider<T> readProvider,
Action<int>? setHasAttribute,
Action<Vertex, T> assign)
{
var dimension = mesh.GetVertexAttributeDimension(attribute);

if (dimension == 0)
{
if (setHasAttribute == null)
throw new InvalidOperationException($"required attribute {attribute} does not exist");
return;
}

setHasAttribute?.Invoke(dimension);

var format = mesh.GetVertexAttributeFormat(attribute);
var stream = mesh.GetVertexAttributeStream(attribute);
var offset = mesh.GetVertexAttributeOffset(attribute);

var reader = readProvider(format, dimension);

var stride = vertexDataList[stream].stride;
var buffer = vertexDataList[stream].buffer;

for (var i = 0; i < Vertices.Count; i++)
{
var data = reader(buffer, stride * i + offset);
assign(Vertices[i], data);
}
}

void CopyVertexAttr<T>(List<T> attributes, Action<Vertex, T> assign)
static class DataParsers
{
for (var i = 0; i < attributes.Count; i++)
assign(Vertices[i], attributes[i]);
public static DataParser<Vector3> Vector3Provider(VertexAttributeFormat format, int dimension)
{
switch (dimension)
{
case 1:
var floatParser = FloatParser(format);
return (data, offset) => new Vector3(floatParser(data, offset), 0, 0);
case 2:
var vector2Parser = Vector2Parser(format);
return (data, offset) => vector2Parser(data, offset);
case 3:
return Vector3Parser(format);
case 4:
var vector4Parser = Vector4Parser(format);
return (data, offset) => vector4Parser(data, offset);
default:
throw new ArgumentOutOfRangeException(nameof(dimension), dimension, null);
}
}

public static DataParser<Vector4> Vector4Provider(VertexAttributeFormat format, int dimension)
{
switch (dimension)
{
case 1:
var floatParser = FloatParser(format);
return (data, offset) => new Vector4(floatParser(data, offset), 0, 0);
case 2:
var vector2Parser = Vector2Parser(format);
return (data, offset) => vector2Parser(data, offset);
case 3:
var vector3Parser = Vector3Parser(format);
return (data, offset) => vector3Parser(data, offset);
case 4:
return Vector4Parser(format);
default:
throw new ArgumentOutOfRangeException(nameof(dimension), dimension, null);
}
}

public static DataParser<Color32> Color32Provider(VertexAttributeFormat format, int dimension)
{
var vector4Parser = Vector4Provider(format, dimension);
return (data, offset) =>
{
var color = vector4Parser(data, offset);
color *= byte.MaxValue;
return new Color32((byte)color.x, (byte)color.y, (byte)color.z, (byte)color.w);
};
}

private static DataParser<Vector4> Vector4Parser(VertexAttributeFormat format)
{
var size = ValueSize(format);
var floatParser = FloatParser(format);
return (data, offset) => new Vector4(
floatParser(data, offset),
floatParser(data, offset + size),
floatParser(data, offset + size * 2),
floatParser(data, offset + size * 3));
}

private static DataParser<Vector3> Vector3Parser(VertexAttributeFormat format)
{
var size = ValueSize(format);
var floatParser = FloatParser(format);
return (data, offset) => new Vector3(
floatParser(data, offset),
floatParser(data, offset + size),
floatParser(data, offset + size * 2));
}

private static DataParser<Vector2> Vector2Parser(VertexAttributeFormat format)
{
var size = ValueSize(format);
var floatParser = FloatParser(format);
return (data, offset) => new Vector2(
floatParser(data, offset),
floatParser(data, offset + size));
}

private static int ValueSize(VertexAttributeFormat format) =>
format switch
{
VertexAttributeFormat.Float32 or VertexAttributeFormat.UInt32 or VertexAttributeFormat.SInt32 => 4,
VertexAttributeFormat.Float16 or VertexAttributeFormat.UNorm16 or VertexAttributeFormat.SNorm16
or VertexAttributeFormat.UInt16 or VertexAttributeFormat.SInt16 => 2,
VertexAttributeFormat.UNorm8 or VertexAttributeFormat.SNorm8 or VertexAttributeFormat.UInt8
or VertexAttributeFormat.SInt8 => 1,
_ => throw new ArgumentOutOfRangeException(nameof(format), format, null)
};

// those casts are checked with this gist
// https://gist.github.com/anatawa12/e27775280a273f7f433c8b427aeb3108
private static DataParser<float> FloatParser(VertexAttributeFormat format) =>
format switch
{
VertexAttributeFormat.Float32 => (data, offset) => BitConverter.ToSingle(data, offset),
VertexAttributeFormat.Float16 => (data, offset) =>
Mathf.HalfToFloat(BitConverter.ToUInt16(data, offset)),
VertexAttributeFormat.UNorm8 => (data, offset) => data[offset] / (float)byte.MaxValue,
// -128 become -1.007874015748 is correct behaior
VertexAttributeFormat.SNorm8 => (data, offset) => (sbyte)data[offset] / (float)sbyte.MaxValue,
VertexAttributeFormat.UNorm16 => (data, offset) => BitConverter.ToUInt16(data, offset) / (float)ushort.MaxValue,
VertexAttributeFormat.SNorm16 => (data, offset) => BitConverter.ToInt16(data, offset) / (float)short.MaxValue,
VertexAttributeFormat.UInt8 => (data, offset) => data[offset],
VertexAttributeFormat.SInt8 => (data, offset) => (sbyte)data[offset],
VertexAttributeFormat.UInt16 => (data, offset) => BitConverter.ToUInt16(data, offset),
VertexAttributeFormat.SInt16 => (data, offset) => BitConverter.ToInt16(data, offset),
// TODO: Those can be loose precision
VertexAttributeFormat.UInt32 => (data, offset) => BitConverter.ToUInt32(data, offset),
VertexAttributeFormat.SInt32 => (data, offset) => BitConverter.ToInt32(data, offset),
_ => throw new ArgumentOutOfRangeException(nameof(format), format, null)
};
}

private const int BitsPerTexCoordStatus = 2;
Expand Down

0 comments on commit 427c28b

Please sign in to comment.