Files
AR-Menu/Library/PackageCache/com.unity.cloud.gltfast@db5a82ec0b47/Runtime/Scripts/VertexBufferBones.cs
2025-11-30 08:35:03 +02:00

266 lines
9.3 KiB
C#

// SPDX-FileCopyrightText: 2023 Unity Technologies and the glTFast authors
// SPDX-License-Identifier: Apache-2.0
using System;
using GLTFast.Jobs;
using GLTFast.Logging;
using GLTFast.Schema;
using GLTFast.Vertex;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.Rendering;
namespace GLTFast
{
sealed class VertexBufferBones : IDisposable
{
readonly ICodeLogger m_Logger;
NativeArray<VBones> m_Data;
public VertexBufferBones(int vertexCount, ICodeLogger logger)
{
m_Logger = logger;
Profiler.BeginSample("AllocateNativeArray");
m_Data = new NativeArray<VBones>(vertexCount, VertexBufferGeneratorBase.defaultAllocator);
Profiler.EndSample();
}
public unsafe JobHandle? ScheduleVertexBonesJob(
int weightsAccessorIndex,
int jointsAccessorIndex,
int offset,
IGltfBuffers buffers
)
{
Profiler.BeginSample("ScheduleVertexBonesJob");
buffers.GetAccessorAndData(weightsAccessorIndex, out var weightsAcc, out var weightsData, out var weightsByteStride);
if (weightsAcc.IsSparse)
{
m_Logger?.Error(LogCode.SparseAccessor, "bone weights");
}
var vDataPtr = (byte*)m_Data.GetUnsafeReadOnlyPtr();
JobHandle weightsHandle;
JobHandle jointsHandle;
{
var h = GetWeightsJob(
weightsData,
weightsAcc.count,
weightsAcc.componentType,
weightsByteStride,
(float4*)(vDataPtr + offset * sizeof(VBones)),
32
);
if (h.HasValue)
{
weightsHandle = h.Value;
}
else
{
Profiler.EndSample();
return null;
}
}
{
buffers.GetAccessorAndData(jointsAccessorIndex, out var jointsAcc, out var jointsData, out var jointsByteStride);
if (jointsAcc.IsSparse)
{
m_Logger?.Error(LogCode.SparseAccessor, "bone joints");
}
var h = GetJointsJob(
jointsData,
jointsAcc.count,
jointsAcc.componentType,
jointsByteStride,
(uint4*)(vDataPtr + offset * sizeof(VBones) + sizeof(float4)),
32,
m_Logger
);
if (h.HasValue)
{
jointsHandle = h.Value;
}
else
{
Profiler.EndSample();
return null;
}
}
var jobHandle = JobHandle.CombineDependencies(weightsHandle, jointsHandle);
var skinWeights = (int)QualitySettings.skinWeights;
#if UNITY_EDITOR
// If this is design-time import, fix and import all weights.
if(!UnityEditor.EditorApplication.isPlaying || skinWeights < 4) {
if (!UnityEditor.EditorApplication.isPlaying) {
skinWeights = 4;
}
#else
if (skinWeights < 4)
{
#endif
var job = new SortAndNormalizeBoneWeightsJob
{
bones = m_Data,
skinWeights = math.max(1, skinWeights)
};
jobHandle = job.Schedule(m_Data.Length, GltfImport.DefaultBatchCount, jobHandle);
}
#if GLTFAST_SAFE
else {
// Re-normalizing alone is sufficient
var job = new RenormalizeBoneWeightsJob {
bones = m_Data,
};
jobHandle = job.Schedule(m_Data.Length, GltfImport.DefaultBatchCount, jobHandle);
}
#endif
Profiler.EndSample();
return jobHandle;
}
public void AddDescriptors(VertexAttributeDescriptor[] dst, int offset, int stream)
{
dst[offset] = new VertexAttributeDescriptor(VertexAttribute.BlendWeight, VertexAttributeFormat.Float32, 4, stream);
dst[offset + 1] = new VertexAttributeDescriptor(VertexAttribute.BlendIndices, VertexAttributeFormat.UInt32, 4, stream);
}
public void ApplyOnMesh(
UnityEngine.Mesh msh,
int stream,
MeshUpdateFlags flags = MeshGeneratorBase.defaultMeshUpdateFlags
)
{
Profiler.BeginSample("ApplyBones");
msh.SetVertexBufferData(m_Data, 0, 0, m_Data.Length, stream, flags);
Profiler.EndSample();
}
public void Dispose()
{
if (m_Data.IsCreated)
{
m_Data.Dispose();
}
}
unsafe JobHandle? GetWeightsJob(
void* input,
int count,
GltfComponentType inputType,
int inputByteStride,
float4* output,
int outputByteStride
)
{
Profiler.BeginSample("GetWeightsJob");
JobHandle? jobHandle;
switch (inputType)
{
case GltfComponentType.Float:
var jobTangentI = new ConvertBoneWeightsFloatToFloatInterleavedJob();
jobTangentI.inputByteStride = inputByteStride > 0 ? inputByteStride : 16;
jobTangentI.input = (byte*)input;
jobTangentI.outputByteStride = outputByteStride;
jobTangentI.result = output;
#if UNITY_COLLECTIONS
jobHandle = jobTangentI.ScheduleBatch(count,GltfImport.DefaultBatchCount);
#else
jobHandle = jobTangentI.Schedule(count, GltfImport.DefaultBatchCount);
#endif
break;
case GltfComponentType.UnsignedShort:
{
var job = new ConvertBoneWeightsUInt16ToFloatInterleavedJob
{
inputByteStride = inputByteStride > 0 ? inputByteStride : 8,
input = (byte*)input,
outputByteStride = outputByteStride,
result = output
};
#if UNITY_COLLECTIONS
jobHandle = job.ScheduleBatch(count,GltfImport.DefaultBatchCount);
#else
jobHandle = job.Schedule(count, GltfImport.DefaultBatchCount);
#endif
break;
}
case GltfComponentType.UnsignedByte:
{
var job = new ConvertBoneWeightsUInt8ToFloatInterleavedJob
{
inputByteStride = inputByteStride > 0 ? inputByteStride : 4,
input = (byte*)input,
outputByteStride = outputByteStride,
result = output
};
#if UNITY_COLLECTIONS
jobHandle = job.ScheduleBatch(count,GltfImport.DefaultBatchCount);
#else
jobHandle = job.Schedule(count, GltfImport.DefaultBatchCount);
#endif
break;
}
default:
m_Logger?.Error(LogCode.TypeUnsupported, "Weights", inputType.ToString());
jobHandle = null;
break;
}
Profiler.EndSample();
return jobHandle;
}
static unsafe JobHandle? GetJointsJob(
void* input,
int count,
GltfComponentType inputType,
int inputByteStride,
uint4* output,
int outputByteStride,
ICodeLogger logger
)
{
Profiler.BeginSample("GetJointsJob");
JobHandle? jobHandle;
switch (inputType)
{
case GltfComponentType.UnsignedByte:
var jointsUInt8Job = new ConvertBoneJointsUInt8ToUInt32Job();
jointsUInt8Job.inputByteStride = inputByteStride > 0 ? inputByteStride : 4;
jointsUInt8Job.input = (byte*)input;
jointsUInt8Job.outputByteStride = outputByteStride;
jointsUInt8Job.result = output;
jobHandle = jointsUInt8Job.Schedule(count, GltfImport.DefaultBatchCount);
break;
case GltfComponentType.UnsignedShort:
var jointsUInt16Job = new ConvertBoneJointsUInt16ToUInt32Job();
jointsUInt16Job.inputByteStride = inputByteStride > 0 ? inputByteStride : 8;
jointsUInt16Job.input = (byte*)input;
jointsUInt16Job.outputByteStride = outputByteStride;
jointsUInt16Job.result = output;
jobHandle = jointsUInt16Job.Schedule(count, GltfImport.DefaultBatchCount);
break;
default:
logger?.Error(LogCode.TypeUnsupported, "Joints", inputType.ToString());
jobHandle = null;
break;
}
Profiler.EndSample();
return jobHandle;
}
}
}