Skip to content

Commit 16e0610

Browse files
committed
Fix the case when position and normal attributes use the same vertex buffer
1 parent 63cc32d commit 16e0610

File tree

1 file changed

+98
-26
lines changed

1 file changed

+98
-26
lines changed

Source/RunActivity/Viewer3D/GltfShape.cs

Lines changed: 98 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -338,8 +338,9 @@ public class GltfDistanceLevel : DistanceLevel
338338
// These are only temporary variables needed during the construction
339339
readonly Stack<int> TempStack = new Stack<int>();
340340
readonly List<VertexElement> VertexElements = new List<VertexElement>();
341-
readonly List<int> Accessors = new List<int>();
341+
readonly Dictionary<int, string> Accessors = new Dictionary<int, string>();
342342
string DebugName = "";
343+
internal const int BUFFER_INDEX_OFFSET = 10000; // Arbitrary number
343344

344345
public GltfDistanceLevel(GltfShape shape, int lodId, Gltf gltfFile, string gltfFileName, GltfDistanceLevel lod0DetailLevel)
345346
{
@@ -499,7 +500,7 @@ void GetBinaryData(GltfShape shape, Gltf gltfFile, string gltfFileName)
499500
// Sparse buffers may index into a null buffer, so create a real one for these.
500501
if (GetBufferViewSpan(accessor.BufferView, 0) is var buffer && buffer.IsEmpty)
501502
{
502-
BinaryBuffers.Add(1000 + a, new byte[accessor.Count * GetSizeInBytes(accessor)]);
503+
BinaryBuffers.Add(BUFFER_INDEX_OFFSET + a, new byte[accessor.Count * GetSizeInBytes(accessor)]);
503504
buffer = BinaryBuffers.Last().Value.AsSpan();
504505
}
505506
// It might have already been processed in another distance level.
@@ -550,48 +551,62 @@ void GetBinaryData(GltfShape shape, Gltf gltfFile, string gltfFileName)
550551
// Trigger the loading of the binary buffer.
551552
GetBufferViewSpan(bufferView.Key, 0);
552553

553-
var previousOffset = 0;
554554
var attributes = bufferView.GetEnumerator();
555555
var loop = attributes.MoveNext();
556+
var semantic = GetVertexElementSemantic(attributes.Current.Key, out var index);
556557
do
557558
{
558559
DebugName = "";
559560
VertexElements.Clear();
560561
Accessors.Clear();
561-
previousOffset = gltfFile.Accessors[attributes.Current.Value].ByteOffset;
562+
var previousOffset = gltfFile.Accessors[attributes.Current.Value].ByteOffset;
563+
var currentOffset = previousOffset;
564+
var previousAttribute = attributes.Current.Key;
565+
var bindingKey = attributes.Current.Value + (int)semantic * BUFFER_INDEX_OFFSET;
562566

563567
// For interleaved data, multiple vertexElements and multiple accessors will be in a single vertexBuffer.
564568
// For non-interleaved data, we create a distinct vertexBuffer for each accessor.
565569
// A bufferView may consist of a series of (non-interleaved) accessors of POSITION:NORMAL:POSITION:NORMAL:POSITION:NORMAL etc. (See: 2CylinderEngine)
566570
// Also e.g. TEXCOORDS_0 and TEXCOORDS_1 may refer to the same accessor.
571+
// Also POSITION and NORMAL may refer to the same accessor (see: PrimitiveModeNormalsTest), however due to the MonoGame limitations
572+
// we cannot use the same VertexBuffer with two different VertexElementUsage-s, duplicate ones are needed with the same data.
567573
do
568574
{
569-
if (!Accessors.Contains(attributes.Current.Value) && !VertexBufferBindings.ContainsKey(attributes.Current.Value))
575+
if (!Accessors.ContainsKey(attributes.Current.Value) && !VertexBufferBindings.ContainsKey(bindingKey))
570576
{
571577
VertexElements.Add(new VertexElement(gltfFile.Accessors[attributes.Current.Value].ByteOffset - previousOffset,
572-
GetVertexElementFormat(gltfFile.Accessors[attributes.Current.Value], shape.MsfsFlavoured),
573-
GetVertexElementSemantic(attributes.Current.Key, out var index), index));
574-
Accessors.Add(attributes.Current.Value);
578+
GetVertexElementFormat(gltfFile.Accessors[attributes.Current.Value], shape.MsfsFlavoured), semantic, index));
575579
if (Debugger.IsAttached) DebugName = (DebugName != "" ? DebugName + "," : "") + attributes.Current.Key;
576580
}
577-
loop = attributes.MoveNext();
581+
// Multiple accessors to same bufferview with same semantic. Will reuse the VertexBuffer:
582+
//while (loop && previousOffset == currentOffset && attributes.Current.Key == previousAttribute)
583+
{
584+
if (!Accessors.ContainsKey(attributes.Current.Value) && !VertexBufferBindings.ContainsKey(bindingKey))
585+
{
586+
Accessors.Add(attributes.Current.Value, attributes.Current.Key);
587+
}
588+
loop = attributes.MoveNext();
589+
semantic = GetVertexElementSemantic(attributes.Current.Key, out index);
590+
currentOffset = gltfFile.Accessors[attributes.Current.Value].ByteOffset;
591+
}
578592
}
579-
while (loop && gltfFile.Accessors[attributes.Current.Value].ByteOffset < previousOffset + byteStride);
593+
while (loop && previousOffset < currentOffset && currentOffset < previousOffset + byteStride);
580594

581-
if (Debugger.IsAttached) DebugName += ":" + Path.GetFileNameWithoutExtension(gltfFileName);
582-
583-
if (Accessors.All(a => VertexBufferBindings.ContainsKey(a)))
595+
if (VertexBufferBindings.ContainsKey(bindingKey))
584596
continue;
585597

586-
var vertexCount = gltfFile.Accessors[Accessors.First()].Count;
598+
if (Debugger.IsAttached) DebugName += ":" + Path.GetFileNameWithoutExtension(gltfFileName);
599+
600+
var vertexCount = Accessors.Max(a => gltfFile.Accessors[a.Key].Count);
587601
var vertexBuffer = new VertexBuffer(shape.Viewer.GraphicsDevice, new VertexDeclaration(byteStride, VertexElements.ToArray()), vertexCount, BufferUsage.None) { Name = DebugName };
588602

603+
byte[] binaryBuffer = null;
589604
if (gltfFile.BufferViews.ElementAtOrDefault(bufferView.Key) is var bv && bv != null)
590605
{
591-
var byteOffset = bv.ByteOffset + gltfFile.Accessors[Accessors.First()].ByteOffset;
606+
var byteOffset = bv.ByteOffset + Accessors.Min(a => gltfFile.Accessors[a.Key].ByteOffset);
592607
vertexBuffer.SetData(BinaryBuffers[bv.Buffer], byteOffset, vertexCount * byteStride);
593608
}
594-
else if (BinaryBuffers.TryGetValue(1000 + Accessors.First(), out var binaryBuffer))
609+
else if (Accessors.Any(a => BinaryBuffers.TryGetValue(BUFFER_INDEX_OFFSET + a.Key, out binaryBuffer)))
595610
{
596611
vertexBuffer.SetData(binaryBuffer);
597612
}
@@ -603,7 +618,25 @@ void GetBinaryData(GltfShape shape, Gltf gltfFile, string gltfFileName)
603618
}
604619

605620
var vertexBufferBinding = new VertexBufferBinding(vertexBuffer);
606-
VertexBufferBindings.Add(Accessors.First(), vertexBufferBinding);
621+
VertexBufferBindings.Add(bindingKey, vertexBufferBinding);
622+
623+
if (Accessors.Count > 1)
624+
{
625+
// Multiple accessors may refer to the same bufferview, in which case we may reuse the same VertexBuffer. (See: PrimitiveModeNormalsTest)
626+
// If the accessors ByteOffset is greater than the first, then it is interleaved, leave it alone. If eaqual, then reuse.
627+
// But we need to add it the the bindigs with all keys, otherwise it would not be found.
628+
var minByteOffset = Accessors.Min(a => gltfFile.Accessors[a.Key].ByteOffset);
629+
for (var i = 1; i < Accessors.Count; i++)
630+
{
631+
var accessor = Accessors.ElementAt(i);
632+
if (gltfFile.Accessors[accessor.Key].ByteOffset == minByteOffset)
633+
{
634+
semantic = GetVertexElementSemantic(accessor.Value, out _);
635+
bindingKey = accessor.Key + (int)semantic * BUFFER_INDEX_OFFSET;
636+
VertexBufferBindings.Add(bindingKey, vertexBufferBinding);
637+
}
638+
}
639+
}
607640
}
608641
while (loop);
609642
}
@@ -846,7 +879,7 @@ static float ToTwoByteFloat(byte[] bytes) // Hi, Lo
846879
return BitConverter.ToSingle(BitConverter.GetBytes((intVal & 0x8000) << 16 | (exp | mant) << 13), 0);
847880
}
848881

849-
static VertexElementUsage GetVertexElementSemantic(string semantic, out int index)
882+
internal static VertexElementUsage GetVertexElementSemantic(string semantic, out int index)
850883
{
851884
var split = semantic.Split('_');
852885
if (!int.TryParse(split.ElementAtOrDefault(1), out index))
@@ -1164,7 +1197,11 @@ public GltfSubObject(MeshPrimitive meshPrimitive, string name, int hierarchyInde
11641197
options |= SceneryMaterialOptions.PbrHasIndices;
11651198
}
11661199

1167-
var vertexAttributes = meshPrimitive.Attributes.SelectMany(a => distanceLevel.VertexBufferBindings.Where(kvp => kvp.Key == a.Value).Select(kvp => kvp.Value)).ToList();
1200+
var vertexAttributes = meshPrimitive.Attributes
1201+
.SelectMany(a => distanceLevel.VertexBufferBindings
1202+
.Where(kvp => kvp.Key == a.Value + (int)GltfDistanceLevel.GetVertexElementSemantic(a.Key, out _) * GltfDistanceLevel.BUFFER_INDEX_OFFSET)
1203+
.Select(kvp => kvp.Value))
1204+
.ToList();
11681205
var vertexCount = vertexAttributes.FirstOrDefault().VertexBuffer?.VertexCount ?? 0;
11691206

11701207
// Currently the below PBR vertex input combinations are possible. Any model must use one of those pipelines.
@@ -1198,8 +1235,8 @@ public GltfSubObject(MeshPrimitive meshPrimitive, string name, int hierarchyInde
11981235
MaxPosition = new Vector4(a.Max[0], a.Max[1], a.Max[2], 1);
11991236
}
12001237

1201-
// Cannot proceed without Normal at all, must add a dummy one.
1202-
if (!meshPrimitive.Attributes.ContainsKey("NORMAL"))
1238+
// Cannot proceed without Normal either, must add a dummy one.
1239+
if (!vertexAttributes.Any(a => a.VertexBuffer.VertexDeclaration.GetVertexElements().Any(e => e.VertexElementUsage == VertexElementUsage.Normal)))
12031240
{
12041241
vertexAttributes.Add(new VertexBufferBinding(new VertexBuffer(shape.Viewer.GraphicsDevice,
12051242
new VertexDeclaration(new VertexElement(0, VertexElementFormat.Color, VertexElementUsage.Normal, 0)), vertexCount, BufferUsage.None) { Name = "NORMAL_DUMMY" }));
@@ -1208,7 +1245,7 @@ public GltfSubObject(MeshPrimitive meshPrimitive, string name, int hierarchyInde
12081245
else
12091246
options |= SceneryMaterialOptions.PbrHasNormals;
12101247

1211-
// Cannot proceed without TexCoord_0 at all, must add a dummy one.
1248+
// Cannot proceed without TexCoord_0 neither, must add a dummy one.
12121249
if (!meshPrimitive.Attributes.ContainsKey("TEXCOORD_0"))
12131250
{
12141251
vertexAttributes.Add(new VertexBufferBinding(new VertexBuffer(shape.Viewer.GraphicsDevice,
@@ -1744,7 +1781,10 @@ public void Animate(int animationNumber, float time, Matrix[] animatedMatrices)
17441781
{ "AnimatedMorphCube".ToLower(), Matrix.CreateTranslation(0, 2, 0) },
17451782
{ "AnimatedMorphSphere".ToLower(), Matrix.CreateTranslation(0, 2, 0) },
17461783
{ "AnimatedTriangle".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1784+
{ "AnisotropyBarnLamp".ToLower(), Matrix.CreateScale(10f) * Matrix.CreateTranslation(0, 2, 0) },
17471785
{ "AntiqueCamera".ToLower(), Matrix.CreateScale(0.5f) },
1786+
{ "AnisotropyRotationTest".ToLower(), Matrix.CreateTranslation(0, 5, 0) },
1787+
{ "AnisotropyStrengthTest".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
17481788
{ "AttenuationTest".ToLower(), Matrix.CreateScale(0.3f) * Matrix.CreateTranslation(0, 4, 0) },
17491789
{ "Avocado".ToLower(), Matrix.CreateScale(30) },
17501790
{ "BarramundiFish".ToLower(), Matrix.CreateScale(10) },
@@ -1758,33 +1798,65 @@ public void Animate(int animationNumber, float time, Matrix[] animatedMatrices)
17581798
{ "BoxTexturedNonPowerOfTwo".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
17591799
{ "BoxVertexColors".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
17601800
{ "Buggy".ToLower(), Matrix.CreateScale(0.02f) * Matrix.CreateTranslation(0, 1, 0) },
1801+
{ "ChairDamaskPurplegold".ToLower(), Matrix.CreateScale(3f) },
17611802
{ "ClearCoatTest".ToLower(), Matrix.CreateScale(0.5f) * Matrix.CreateTranslation(0, 3, 0) },
1762-
{ "Corset".ToLower(), Matrix.CreateScale(30) * Matrix.CreateTranslation(0, 1, 0) },
1803+
{ "CompareAlphaCoverage".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1804+
{ "CompareAmbientOcclusion".ToLower(), Matrix.CreateScale(3) * Matrix.CreateTranslation(0, 1, 0) },
1805+
{ "CompareBaseColor".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1806+
{ "CompareClearcoat".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1807+
{ "CompareDispersion".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1808+
{ "CompareEmissiveStrength".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1809+
{ "CompareIor".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1810+
{ "CompareIridescence".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1811+
{ "CompareMetallic".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1812+
{ "CompareNormal".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1813+
{ "CompareRoughness".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1814+
{ "CompareSheen".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1815+
{ "CompareSpecular".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1816+
{ "CompareTransmission".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1817+
{ "CompareVolume".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1818+
{ "Corset".ToLower(), Matrix.CreateScale(30) },
17631819
{ "Cube".ToLower(), Matrix.CreateTranslation(0, 2, 0) },
17641820
{ "DamagedHelmet".ToLower(), Matrix.CreateTranslation(0, 2, 0) },
1765-
{ "DragonAttenuation".ToLower(), Matrix.CreateTranslation(0, 2, 0) },
1821+
{ "DiffuseTransmissionPlant".ToLower(), Matrix.CreateScale(4) * Matrix.CreateTranslation(0, 1, 0) },
1822+
{ "DiffuseTransmissionTeacup".ToLower(), Matrix.CreateScale(8) * Matrix.CreateTranslation(0, 1, 0) },
1823+
{ "DirectionalLight".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1824+
{ "DispersionTest".ToLower(), Matrix.CreateScale(16) * Matrix.CreateTranslation(0, 1, 0) },
1825+
{ "DragonAttenuation".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
1826+
{ "DragonDispersion".ToLower(), Matrix.CreateTranslation(0, 1, 0) },
17661827
{ "EmissiveStrengthTest".ToLower(), Matrix.CreateScale(0.5f) * Matrix.CreateTranslation(0, 3, 0) },
17671828
{ "EnvironmentTest".ToLower(), Matrix.CreateScale(0.5f) },
17681829
{ "FlightHelmet".ToLower(), Matrix.CreateScale(5) },
17691830
{ "Fox".ToLower(), Matrix.CreateScale(0.02f) },
17701831
{ "GearboxAssy".ToLower(), Matrix.CreateScale(0.5f) * Matrix.CreateTranslation(80, -5, 0) },
17711832
{ "GlamVelvetSofa".ToLower(), Matrix.CreateScale(2) },
1833+
{ "GlassBrokenWindow".ToLower(), Matrix.CreateScale(5) },
1834+
{ "GlassHurricaneCandleHolder".ToLower(), Matrix.CreateScale(5) },
1835+
{ "GlassVaseFlowers".ToLower(), Matrix.CreateScale(10) },
17721836
{ "InterpolationTest".ToLower(), Matrix.CreateScale(0.5f) * Matrix.CreateTranslation(0, 2, 0) },
1773-
{ "IridescenceDielectricSpheres".ToLower(), Matrix.CreateScale(0.2f) * Matrix.CreateTranslation(0, 4, 0) },
1837+
{ "IORTestGrid".ToLower(), Matrix.CreateScale(5) * Matrix.CreateTranslation(0, 2, 0) },
1838+
{ "IridescenceAbalone".ToLower(), Matrix.CreateScale(5) * Matrix.CreateTranslation(0, 1, 0) },
1839+
{ "IridescenceDielectricSpheres".ToLower(), Matrix.CreateScale(0.2f) * Matrix.CreateTranslation(0, 3, 0) },
17741840
{ "IridescenceLamp".ToLower(), Matrix.CreateScale(5) },
1775-
{ "IridescenceMetallicSpheres".ToLower(), Matrix.CreateScale(0.2f) * Matrix.CreateTranslation(0, 4, 0) },
1841+
{ "IridescenceMetallicSpheres".ToLower(), Matrix.CreateScale(0.2f) * Matrix.CreateTranslation(0, 3, 0) },
17761842
{ "IridescenceSuzanne".ToLower(), Matrix.CreateTranslation(0, 2, 0) },
17771843
{ "IridescentDishWithOlives".ToLower(), Matrix.CreateScale(10) },
17781844
{ "Lantern".ToLower(), Matrix.CreateScale(0.2f) },
1845+
{ "MandarinOrange".ToLower(), Matrix.CreateScale(30) },
17791846
{ "MaterialsVariantsShoe".ToLower(), Matrix.CreateScale(5) },
1847+
{ "MeshPrimitiveModes".ToLower(), Matrix.CreateTranslation(0, 5, 0) },
17801848
{ "MetalRoughSpheres".ToLower(), Matrix.CreateTranslation(0, 5, 0) },
17811849
{ "MetalRoughSpheresNoTextures".ToLower(), Matrix.CreateScale(800) * Matrix.CreateTranslation(0, 1, 0) },
17821850
{ "MorphPrimitivesTest".ToLower(), Matrix.CreateScale(2) * Matrix.CreateTranslation(0, 1, 0) },
17831851
{ "MosquitoInAmber".ToLower(), Matrix.CreateScale(25) * Matrix.CreateTranslation(0, 1, 0) },
17841852
{ "MultiUVTest".ToLower(), Matrix.CreateTranslation(0, 2, 0) },
1853+
{ "NegativeScaleTest".ToLower(), Matrix.CreateTranslation(0, 3, 0) },
17851854
{ "NormalTangentMirrorTest".ToLower(), Matrix.CreateScale(2) * Matrix.CreateTranslation(0, 2, 0) },
17861855
{ "NormalTangentTest".ToLower(), Matrix.CreateScale(2) * Matrix.CreateTranslation(0, 2, 0) },
17871856
{ "OrientationTest".ToLower(), Matrix.CreateScale(0.2f) * Matrix.CreateTranslation(0, 2, 0) },
1857+
{ "PlaysetLightTest".ToLower(), Matrix.CreateScale(30) },
1858+
{ "PotOfCoals".ToLower(), Matrix.CreateScale(30) },
1859+
{ "PrimitiveModeNormalsTest".ToLower(), Matrix.CreateScale(0.5f) * Matrix.CreateTranslation(0, 5, 0) },
17881860
{ "ReciprocatingSaw".ToLower(), Matrix.CreateScale(0.01f) * Matrix.CreateTranslation(0, 3, 0) },
17891861
{ "RecursiveSkeletons".ToLower(), Matrix.CreateScale(0.05f) },
17901862
{ "RiggedSimple".ToLower(), Matrix.CreateTranslation(0, 5, 0) },

0 commit comments

Comments
 (0)