Skip to content

Commit

Permalink
Merge branch 'main' into draco-unity-4
Browse files Browse the repository at this point in the history
  • Loading branch information
atteneder committed Aug 26, 2021
2 parents 0c275ec + 9f5161b commit 37a5efa
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 34 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- WebGL library is built with Emscripten 2.0.19 now
- Minimum required version is Unity 2021.2

## [3.1.0] - 2020-07-12
### Added
- `forceUnityLayout` parameter, to enforce a blend-shape and skinning compatible vertex buffer layout

## [3.0.3] - 2020-06-09
### Added
- Support for Lumin / Magic Leap
Expand Down
128 changes: 112 additions & 16 deletions Runtime/Scripts/DracoMeshLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,20 +96,31 @@ public void Dispose() {
/// <param name="requireTangents">If draco does not contain tangents and this is set to true, tangents and normals are calculated.</param>
/// <param name="weightsAttributeId">Draco attribute ID that contains bone weights (for skinning)</param>
/// <param name="jointsAttributeId">Draco attribute ID that contains bone joint indices (for skinning)</param>
/// <param name="forceUnityLayout">Enforces vertex buffer layout with highest compatibility. Enable this if you want to use blend shapes on the resulting mesh</param>
/// <returns>Unity Mesh or null in case of errors</returns>
public async Task<Mesh> ConvertDracoMeshToUnity(
NativeSlice<byte> encodedData,
bool requireNormals = false,
bool requireTangents = false,
int weightsAttributeId = -1,
int jointsAttributeId = -1
int jointsAttributeId = -1,
bool forceUnityLayout = false
)
{
var encodedDataPtr = GetUnsafeReadOnlyIntPtr(encodedData);
#if DRACO_MESH_DATA
var meshDataArray = Mesh.AllocateWritableMeshData(1);
var mesh = meshDataArray[0];
var result = await ConvertDracoMeshToUnity(mesh, encodedDataPtr, encodedData.Length, requireNormals, requireTangents, weightsAttributeId, jointsAttributeId);
var result = await ConvertDracoMeshToUnity(
mesh,
encodedDataPtr,
encodedData.Length,
requireNormals,
requireTangents,
weightsAttributeId,
jointsAttributeId,
forceUnityLayout
);
if (!result.success) {
meshDataArray.Dispose();
return null;
Expand All @@ -128,7 +139,15 @@ public async Task<Mesh> ConvertDracoMeshToUnity(
}
return unityMesh;
#else
return await ConvertDracoMeshToUnity(encodedDataPtr, encodedData.Length, requireNormals, requireTangents, weightsAttributeId, jointsAttributeId);
return await ConvertDracoMeshToUnity(
encodedDataPtr,
encodedData.Length,
requireNormals,
requireTangents,
weightsAttributeId,
jointsAttributeId,
forceUnityLayout
);
#endif
}

Expand All @@ -140,13 +159,15 @@ public async Task<Mesh> ConvertDracoMeshToUnity(
/// <param name="requireTangents">If draco does not contain tangents and this is set to true, tangents and normals are calculated.</param>
/// <param name="weightsAttributeId">Draco attribute ID that contains bone weights (for skinning)</param>
/// <param name="jointsAttributeId">Draco attribute ID that contains bone joint indices (for skinning)</param>
/// <param name="forceUnityLayout">Enforces vertex buffer layout with highest compatibility. Enable this if you want to use blend shapes on the resulting mesh</param>
/// <returns>Unity Mesh or null in case of errors</returns>
public async Task<Mesh> ConvertDracoMeshToUnity(
byte[] encodedData,
bool requireNormals = false,
bool requireTangents = false,
int weightsAttributeId = -1,
int jointsAttributeId = -1
int jointsAttributeId = -1,
bool forceUnityLayout = false
#if UNITY_EDITOR
,bool sync = false
#endif
Expand All @@ -156,7 +177,15 @@ public async Task<Mesh> ConvertDracoMeshToUnity(
#if DRACO_MESH_DATA
var meshDataArray = Mesh.AllocateWritableMeshData(1);
var mesh = meshDataArray[0];
var result = await ConvertDracoMeshToUnity(mesh, encodedDataPtr, encodedData.Length, requireNormals, requireTangents, weightsAttributeId, jointsAttributeId
var result = await ConvertDracoMeshToUnity(
mesh,
encodedDataPtr,
encodedData.Length,
requireNormals,
requireTangents,
weightsAttributeId,
jointsAttributeId,
forceUnityLayout
#if UNITY_EDITOR
,sync
#endif
Expand All @@ -176,7 +205,14 @@ public async Task<Mesh> ConvertDracoMeshToUnity(
}
return unityMesh;
#else
var result = await ConvertDracoMeshToUnity(encodedDataPtr, encodedData.Length, requireNormals, requireTangents, weightsAttributeId, jointsAttributeId
var result = await ConvertDracoMeshToUnity(
encodedDataPtr,
encodedData.Length,
requireNormals,
requireTangents,
weightsAttributeId,
jointsAttributeId,
forceUnityLayout
#if UNITY_EDITOR
,sync
#endif
Expand All @@ -187,34 +223,78 @@ public async Task<Mesh> ConvertDracoMeshToUnity(
}

#if DRACO_MESH_DATA
/// <summary>
/// Decodes a Draco mesh
/// </summary>
/// <param name="mesh">MeshData used to create the mesh</param>
/// <param name="encodedData">Compressed Draco data</param>
/// <param name="requireNormals">If draco does not contain normals and this is set to true, normals are calculated.</param>
/// <param name="requireTangents">If draco does not contain tangents and this is set to true, tangents and normals are calculated.</param>
/// <param name="weightsAttributeId">Draco attribute ID that contains bone weights (for skinning)</param>
/// <param name="jointsAttributeId">Draco attribute ID that contains bone joint indices (for skinning)</param>
/// <param name="forceUnityLayout">Enforces vertex buffer layout with highest compatibility. Enable this if you want to use blend shapes on the resulting mesh</param>
/// <returns>A DecodeResult</returns>
public async Task<DecodeResult> ConvertDracoMeshToUnity(
Mesh.MeshData mesh,
byte[] encodedData,
bool requireNormals = false,
bool requireTangents = false,
int weightsAttributeId = -1,
int jointsAttributeId = -1
int jointsAttributeId = -1,
bool forceUnityLayout = false
)
{
var encodedDataPtr = PinGCArrayAndGetDataAddress(encodedData, out var gcHandle);
var result = await ConvertDracoMeshToUnity(mesh, encodedDataPtr, encodedData.Length, requireNormals, requireTangents, weightsAttributeId, jointsAttributeId);
var result = await ConvertDracoMeshToUnity(
mesh,
encodedDataPtr,
encodedData.Length,
requireNormals,
requireTangents,
weightsAttributeId,
jointsAttributeId,
forceUnityLayout
);
UnsafeUtility.ReleaseGCObject(gcHandle);
return result;
}

public async Task<DecodeResult> ConvertDracoMeshToUnity(Mesh.MeshData mesh, NativeArray<byte> encodedData, bool requireNormals = false,
/// <summary>
/// Decodes a Draco mesh
/// </summary>
/// <param name="mesh">MeshData used to create the mesh</param>
/// <param name="encodedData">Compressed Draco data</param>
/// <param name="requireNormals">If draco does not contain normals and this is set to true, normals are calculated.</param>
/// <param name="requireTangents">If draco does not contain tangents and this is set to true, tangents and normals are calculated.</param>
/// <param name="weightsAttributeId">Draco attribute ID that contains bone weights (for skinning)</param>
/// <param name="jointsAttributeId">Draco attribute ID that contains bone joint indices (for skinning)</param>
/// <param name="forceUnityLayout">Enforces vertex buffer layout with highest compatibility. Enable this if you want to use blend shapes on the resulting mesh</param>
/// <returns>A DecodeResult</returns>
public async Task<DecodeResult> ConvertDracoMeshToUnity(
Mesh.MeshData mesh,
NativeArray<byte> encodedData,
bool requireNormals = false,
bool requireTangents = false,
int weightsAttributeId = -1,
int jointsAttributeId = -1
int jointsAttributeId = -1,
bool forceUnityLayout = false
#if UNITY_EDITOR
,bool sync = false
#endif
)
{
var encodedDataPtr = GetUnsafeReadOnlyIntPtr(encodedData);
return await ConvertDracoMeshToUnity(mesh, encodedDataPtr, encodedData.Length, requireNormals, requireTangents, weightsAttributeId, jointsAttributeId
return await ConvertDracoMeshToUnity(
mesh,
encodedDataPtr,
encodedData.Length,
requireNormals,
requireTangents,
weightsAttributeId,
jointsAttributeId,
forceUnityLayout
#if UNITY_EDITOR
,sync
,sync
#endif
);
}
Expand All @@ -228,7 +308,8 @@ async Task<DecodeResult> ConvertDracoMeshToUnity(
bool requireNormals,
bool requireTangents,
int weightsAttributeId = -1,
int jointsAttributeId = -1
int jointsAttributeId = -1,
bool forceUnityLayout = false
#if UNITY_EDITOR
,bool sync = false
#endif
Expand All @@ -240,7 +321,8 @@ async Task<Mesh> ConvertDracoMeshToUnity(
bool requireNormals,
bool requireTangents,
int weightsAttributeId = -1,
int jointsAttributeId = -1
int jointsAttributeId = -1,
bool forceUnityLayout = false
#if UNITY_EDITOR
,bool sync = false
#endif
Expand Down Expand Up @@ -275,9 +357,23 @@ async Task<Mesh> ConvertDracoMeshToUnity(
requireNormals = true;
}
#if DRACO_MESH_DATA
dracoNative.CreateMesh(out result.calculateNormals, requireNormals, requireTangents, weightsAttributeId, jointsAttributeId);
dracoNative.CreateMesh(
out result.calculateNormals,
requireNormals,
requireTangents,
weightsAttributeId,
jointsAttributeId,
forceUnityLayout
);
#else
dracoNative.CreateMesh(out var calculateNormals, requireNormals, requireTangents, weightsAttributeId, jointsAttributeId);
dracoNative.CreateMesh(
out var calculateNormals,
requireNormals,
requireTangents,
weightsAttributeId,
jointsAttributeId,
forceUnityLayout
);
#endif

#if UNITY_EDITOR
Expand Down
61 changes: 44 additions & 17 deletions Runtime/Scripts/DracoNative.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ void CalculateVertexParams(
bool requireTangents,
int weightsAttributeId,
int jointsAttributeId,
out bool calculateNormals
out bool calculateNormals,
bool forceUnityLayout = false
)
{
Profiler.BeginSample("CalculateVertexParams");
Expand Down Expand Up @@ -258,54 +259,71 @@ bool CreateAttributeMapById(VertexAttribute type, int id, DracoMesh* draco, out
if (requireTangents) {
attributes.Add(new CalculatedAttributeMap(VertexAttribute.Tangent, VertexAttributeFormat.Float32, 4, 4 ));
}
CreateAttributeMaps(AttributeType.COLOR, 1, dracoMesh);
var hasTexCoords = CreateAttributeMaps(AttributeType.TEX_COORD, 8, dracoMesh);
var hasTexCoordOrColor = CreateAttributeMaps(AttributeType.COLOR, 1, dracoMesh);
hasTexCoordOrColor |= CreateAttributeMaps(AttributeType.TEX_COORD, 8, dracoMesh);

var hasBlend = false;
var hasSkinning = false;
if (weightsAttributeId >= 0) {
if (CreateAttributeMapById(VertexAttribute.BlendWeight, weightsAttributeId, dracoMesh, out var map)) {
// BLENDHACK: Don't add bone weights, as they won't exist after Mesh.SetBoneWeights
// attributes.Add(map);
boneWeightMap = map;
hasBlend = true;
hasSkinning = true;
}
}
if (jointsAttributeId >= 0) {
if (CreateAttributeMapById(VertexAttribute.BlendIndices, jointsAttributeId, dracoMesh, out var map)) {
attributes.Add(map);
boneIndexMap = map;
hasBlend = true;
hasSkinning = true;
}
}

streamStrides = new int[maxStreamCount];
streamMemberCount = new int[maxStreamCount];
var streamIndex = 0;

// skinning requires SkinnedMeshRenderer layout
forceUnityLayout |= hasSkinning;

foreach (var attributeMap in attributes) {
// Stream assignment:
// Positions get a dedicated stream (0)
// The rest lands on stream 1

// If blend weights or blend indices are present, they land on stream 1
// while the rest is combined in stream 0
// TODO: BLENDHACK;
// A potentially following Mesh.SetBoneWeights changes stream assignment again!
// Maybe it would be better to rebuild Unity's logic?

// Mesh layout SkinnedMeshRenderer (used for skinning and blend shapes)
// requires:
// stream 0: position,normal,tangent
// stream 1: UVs,colors
// stream 2: blend weights/indices

switch (attributeMap.attribute) {
case VertexAttribute.Position:
// Attributes that define/change the position go to stream 0
streamIndex = 0;
break;
default:
// The rest to stream 1, but not if blend weights/joints are present
// In this case putting everything in stream 0 (except blend weights/joints) proved to work
streamIndex = hasBlend ? 0 : 1;
case VertexAttribute.Normal:
case VertexAttribute.Tangent:
streamIndex = forceUnityLayout ? 0 : 1;
break;
case VertexAttribute.TexCoord0:
case VertexAttribute.TexCoord1:
case VertexAttribute.TexCoord2:
case VertexAttribute.TexCoord3:
case VertexAttribute.TexCoord4:
case VertexAttribute.TexCoord5:
case VertexAttribute.TexCoord6:
case VertexAttribute.TexCoord7:
case VertexAttribute.Color:
streamIndex = 1;
break;
case VertexAttribute.BlendWeight:
case VertexAttribute.BlendIndices:
// Special case: blend weights/joints always have a special stream
streamIndex = 1;
streamIndex = hasTexCoordOrColor ? 2 : 1;
break;
}
#if !DRACO_MESH_DATA
Expand Down Expand Up @@ -465,20 +483,29 @@ public JobHandle DecodeVertexData(
return releaseDreacoMeshJobHandle;
}

public void CreateMesh(
internal void CreateMesh(
out bool calculateNormals,
bool requireNormals = false,
bool requireTangents = false,
int weightsAttributeId = -1,
int jointsAttributeId = -1
int jointsAttributeId = -1,
bool forceUnityLayout = false
)
{
Profiler.BeginSample("CreateMesh");

var dracoMesh = (DracoMesh*)dracoTempResources[meshPtrIndex];
allocator = dracoMesh->numVertices > persistentDataThreshold ? Allocator.Persistent : Allocator.TempJob;

CalculateVertexParams(dracoMesh, requireNormals, requireTangents, weightsAttributeId, jointsAttributeId, out calculateNormals);
CalculateVertexParams(
dracoMesh,
requireNormals,
requireTangents,
weightsAttributeId,
jointsAttributeId,
out calculateNormals,
forceUnityLayout
);

Profiler.BeginSample("SetParameters");
#if DRACO_MESH_DATA
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@
"url": "http://pixel.engineer"
},
"type": "library"
}
}

0 comments on commit 37a5efa

Please sign in to comment.