Skip to content

Commit

Permalink
Merge pull request #4 from B3zaleel/parse-mesh-data
Browse files Browse the repository at this point in the history
Implement parsing of mesh connectivity data
  • Loading branch information
B3zaleel authored Jul 16, 2024
2 parents 68a8495 + ac6c802 commit a053ef4
Show file tree
Hide file tree
Showing 37 changed files with 3,243 additions and 26 deletions.
6 changes: 6 additions & 0 deletions src/Draco/IO/Attributes/AttributeTransformData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Draco.IO.Attributes;

public class AttributeTransformData
{
public AttributeTransformType TransformType { get; set; } = AttributeTransformType.InvalidTransform;
}
9 changes: 9 additions & 0 deletions src/Draco/IO/Attributes/GeometryAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Draco.IO.Attributes;

public class GeometryAttribute
{
public GeometryAttributeType AttributeType { get; set; }
public bool Normalized { get; set; }
public long ByteOffset { get; set; }
public uint UniqueId { get; set; }
}
19 changes: 19 additions & 0 deletions src/Draco/IO/Attributes/MeshAttributeIndicesEncodingData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Draco.IO.Extensions;

namespace Draco.IO.Attributes;

public class MeshAttributeIndicesEncodingData
{
/// <summary>
/// Total number of encoded/decoded attribute entries.
/// </summary>
public int NumValues { get; set; } = 0;
public List<uint> EncodedAttributeValueIndexToCornerMap { get; set; } = [];
public List<int> VertexToEncodedAttributeValueIndexMap { get; set; } = [];

public MeshAttributeIndicesEncodingData(int numVertices)
{
EncodedAttributeValueIndexToCornerMap.Resize(numVertices, 0U);
VertexToEncodedAttributeValueIndexMap.Resize(numVertices, 0);
}
}
58 changes: 58 additions & 0 deletions src/Draco/IO/Attributes/PointAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using Draco.IO.Extensions;

namespace Draco.IO.Attributes;

public class PointAttribute : GeometryAttribute
{
private uint _numUniqueEntries = 0;
private readonly List<uint> _indicesMap = [];

public Stream? Buffer { get; private set; }
public bool IsMappingIdentity { get; private set; } = false;
public int IndicesMapSize { get => IsMappingIdentity ? 0 : _indicesMap.Count; }

/// <summary>
/// Contains the type and parameters of the transform that is applied on the attribute data.
/// </summary>
/// <value></value>
public AttributeTransformData? AttributeTransformData { get; set; }

public PointAttribute() { }

public PointAttribute(GeometryAttribute att)
{
AttributeType = att.AttributeType;
Normalized = att.Normalized;
ByteOffset = att.ByteOffset;
UniqueId = att.UniqueId;
}

public uint MappedIndex(uint pointIndex)
{
return IsMappingIdentity ? pointIndex : _indicesMap[(int)pointIndex];
}

public void Reset(int numAttributeValues)
{
// TODO: implement this
_numUniqueEntries = (uint)numAttributeValues;
}

public void SetIdentityMapping()
{
IsMappingIdentity = true;
_indicesMap.Clear();
}

public void SetExplicitMapping(int numPoints)
{
IsMappingIdentity = false;
_indicesMap.Resize(numPoints, Constants.kInvalidAttributeValueIndex);
}

public void SetPointMapEntry(uint pointIndex, uint entryIndex)
{
Assertions.ThrowIfNot(IsMappingIdentity);
_indicesMap[(int)pointIndex] = entryIndex;
}
}
41 changes: 41 additions & 0 deletions src/Draco/IO/BitCoders/RAnsBitDecoder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Draco.IO.Entropy;
using Draco.IO.Extensions;

namespace Draco.IO.BitCoders;

internal class RAnsBitDecoder
{
private byte _probZero = 0;
private AnsDecoder _ansDecoder = new();

public void StartDecoding(DecoderBuffer decoderBuffer)
{
_probZero = decoderBuffer.ReadByte();
_ansDecoder = new();
uint size_in_bytes = decoderBuffer.BitStream_Version < Constants.BitStreamVersion(2, 2) ? decoderBuffer.ReadUInt32() : (uint)decoderBuffer.DecodeVarIntUnsigned();
_ansDecoder.ReadInit(decoderBuffer.ReadBytes((int)size_in_bytes), (int)size_in_bytes);
}

public uint DecodeNextBit()
{
return _ansDecoder.RAbsRead(_probZero) ? 1U : 0U;
}

public uint DecodeLeastSignificantBits32(byte count)
{
Assertions.ThrowIfNot(count > 0, "Count must be greater than 0");
Assertions.ThrowIfNot(count <= 32, "Count must be less than or equal to 32");
uint value = 0;
while (count > 0)
{
value = (value << 1) + DecodeNextBit();
count--;
}
return value;
}

public void EndDecoding(DecoderBuffer decoderBuffer)
{
decoderBuffer.EndBitDecoding();
}
}
119 changes: 119 additions & 0 deletions src/Draco/IO/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,123 @@ public static class Metadata
{
public const ushort FlagMask = 32768;
}

public static class EncodingType
{
public const byte PointCloud = 0;
public const byte TriangularMesh = 1;
}

public static class EncodingMethod
{
public const byte SequentialEncoding = 0;
public const byte EdgeBreakerEncoding = 1;
}

public static class SequentialIndicesEncodingMethod
{
public const byte CompressedIndices = 0;
public const byte UncompressedIndices = 1;
}

public static class EdgeBreakerTraversalDecoderType
{
public const byte StandardEdgeBreaker = 0;
public const byte PredictiveEdgeBreaker = 1;
public const byte ValenceEdgeBreaker = 2;
}

// EdgeBreaker constants
// public const sbyte kInvalidCornerIndex = -1;
public const uint kInvalidCornerIndex = uint.MaxValue;
public const uint kInvalidVertexIndex = uint.MaxValue;
public const uint kInvalidFaceIndex = uint.MaxValue;
public const uint kInvalidAttributeValueIndex = uint.MaxValue;

public static class EdgeFaceName
{
public const sbyte LeftFaceEdge = 0;
public const byte RightFaceEdge = 1;
}

public static readonly byte[] EdgeBreakerSymbolToTopologyId =
[
EdgeBreakerTopologyBitPattern.C,
EdgeBreakerTopologyBitPattern.S,
EdgeBreakerTopologyBitPattern.L,
EdgeBreakerTopologyBitPattern.R,
EdgeBreakerTopologyBitPattern.E
];

public static class EdgeBreakerTopologyBitPattern
{
public const byte C = 0;
public const byte S = 1;
public const byte L = 3;
public const byte R = 5;
public const byte E = 7;
public const byte InitFace = 8;
public const byte Invalid = 9;
}

public static class EdgeBreakerValenceCodingMode
{
public const sbyte EdgeBreakerValenceMode_2_7 = 0;
}

// Valence EdgeBreaker constants
public const byte MinValence = 2;
public const byte MaxValence = 7;
public const byte NumUniqueValences = 6;
// ANS constants
public const ushort DracoAnsP8Precision = 256;
public const ushort DracoAnsP10Precision = 1024;
public const ushort DracoAnsLBase = 4096;
public const ushort AnsIOBase = 256;
public const ushort LRAnsBase = 4096;
public const ushort TaggedRAnsBase = 16384;
public const ushort TaggedRAnsPrecision = 4096;

public static class SymbolCoding
{
public const byte Tagged = 0;
public const byte Raw = 1;
}
}

public enum GeometryAttributeType : sbyte
{
Invalid = -1,
Position = 0,
Normal = 1,
Color = 2,
TexCoord = 3,
NamedAttributesCount = 4
}

/// <summary>
/// Represents different variants of <see cref="Mesh.Mesh"/> attributes.
/// </summary>
public enum MeshAttributeElementType : byte
{
/// <summary>
/// All corners attached to a vertex share the same attribute value. A typical example are the vertex positions and often vertex colors.
/// </summary>
VertexAttribute = 0,
/// <summary>
/// The most general attribute where every corner of the mesh can have a different attribute value. Often used for texture coordinates or normals.
/// </summary>
CornerAttribute = 1,
/// <summary>
/// All corners of a single face share the same value.
/// </summary>
FaceAttribute = 2
}

public enum AttributeTransformType : sbyte
{
InvalidTransform = -1,
NoTransform = 0,
QuantizationTransform = 1,
OctahedronTransform = 2,
}
60 changes: 49 additions & 11 deletions src/Draco/IO/DracoDecoder.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Draco.IO.Mesh;
using Draco.IO.Metadata;

namespace Draco.IO;
Expand All @@ -21,30 +22,32 @@ public void Decode(Stream stream)

public void Decode(BinaryReader binaryReader)
{
using var buffer = new DecoderBuffer(binaryReader);
Header = ParseHeader(buffer);
buffer.BitStream_Version = Header.Version;
using var decoderBuffer = new DecoderBuffer(binaryReader);
Header = ParseHeader(decoderBuffer);
decoderBuffer.BitStream_Version = Header.Version;
if (Header.Version >= Constants.BitStreamVersion(1, 3) && (Header.Flags & Constants.Metadata.FlagMask) == Constants.Metadata.FlagMask)
{
var metadataDecoder = new MetadataDecoder();
metadataDecoder.Decode(buffer);
metadataDecoder.Decode(decoderBuffer);
AttMetadata = metadataDecoder.AttMetadata;
FileMetadata = metadataDecoder.FileMetadata;
}
var connectivityDecoder = GetDecoder(decoderBuffer);
connectivityDecoder.DecodeConnectivity(decoderBuffer);
}

private static DracoHeader ParseHeader(DecoderBuffer buffer)
private static DracoHeader ParseHeader(DecoderBuffer decoderBuffer)
{
var dracoMagic = buffer.ReadASCIIBytes(Constants.DracoMagic.Length);
var dracoMagic = decoderBuffer.ReadASCIIBytes(Constants.DracoMagic.Length);
if (dracoMagic != Constants.DracoMagic)
{
throw new InvalidDataException("Invalid Draco file.");
}
var majorVersion = buffer.ReadByte();
var minorVersion = buffer.ReadByte();
var encoderType = buffer.ReadByte();
var encoderMethod = buffer.ReadByte();
var flags = buffer.ReadUInt16();
var majorVersion = decoderBuffer.ReadByte();
var minorVersion = decoderBuffer.ReadByte();
var encoderType = decoderBuffer.ReadByte();
var encoderMethod = decoderBuffer.ReadByte();
var flags = decoderBuffer.ReadUInt16();

return new DracoHeader(
majorVersion: majorVersion,
Expand All @@ -54,4 +57,39 @@ private static DracoHeader ParseHeader(DecoderBuffer buffer)
flags: flags
);
}

private IConnectivityDecoder GetDecoder(DecoderBuffer decoderBuffer)
{
if (Header!.EncoderType == Constants.EncodingType.PointCloud)
{
throw new NotImplementedException("Point cloud decoding is not implemented.");
}
else if (Header.EncoderType == Constants.EncodingType.TriangularMesh)
{
if (Header.EncoderMethod == Constants.EncodingMethod.SequentialEncoding)
{
return new MeshSequentialDecoder();
}
else if (Header.EncoderMethod == Constants.EncodingMethod.EdgeBreakerEncoding)
{
var traversalDecoderType = decoderBuffer.ReadByte();

return traversalDecoderType switch
{
Constants.EdgeBreakerTraversalDecoderType.StandardEdgeBreaker => new MeshEdgeBreakerTraversalDecoder(),
Constants.EdgeBreakerTraversalDecoderType.ValenceEdgeBreaker => new MeshEdgeBreakerTraversalValenceDecoder(),
Constants.EdgeBreakerTraversalDecoderType.PredictiveEdgeBreaker => new MeshEdgeBreakerTraversalPredictiveDecoder(),
_ => throw new InvalidDataException($"Unsupported edge breaker traversal decoder type {traversalDecoderType}"),
};
}
else
{
throw new InvalidDataException($"Unsupported encoder method {Header.EncoderMethod}.");
}
}
else
{
throw new InvalidDataException($"Unsupported encoder type {Header.EncoderType}.");
}
}
}
28 changes: 28 additions & 0 deletions src/Draco/IO/Entropy/Ans.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace Draco.IO.Entropy;

internal static class Ans
{
public static uint MemGetLE16(byte[] data)
{
uint value = (uint)data[1] << 8;
value |= data[0];
return value;
}

public static uint MemGetLE24(byte[] data)
{
var value = (uint)data[2] << 16;
value |= (uint)data[1] << 8;
value |= data[0];
return value;
}

public static uint MemGetLE32(byte[] data)
{
var value = (uint)data[3] << 24;
value |= (uint)data[2] << 16;
value |= (uint)data[1] << 8;
value |= data[0];
return value;
}
}
Loading

0 comments on commit a053ef4

Please sign in to comment.