Files
Project-M/Packages/com.rukhanka.animation/Rukhanka.Hybrid/AvatarMask/AvatarMaskBakingSystem.cs
T
2026-05-31 14:27:52 -07:00

132 lines
4.3 KiB
C#

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rukhanka.Hybrid
{
internal struct AvatarMaskBakingDataBlob
{
#if RUKHANKA_DEBUG_INFO
public BlobString name;
public BlobArray<BlobString> includedBoneNames;
public float bakingTime;
#endif
public Hash128 hash;
public BlobArray<uint> includedBoneHashes;
public uint humanBodyPartsAvatarMask;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[TemporaryBakingType]
internal struct AvatarMaskBakingData: IBufferElementData
{
public Entity rigEntity;
public BlobAssetReference<AvatarMaskBakingDataBlob> dataBlob;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)]
[RequireMatchingQueriesForUpdate]
partial class AvatarMaskBakingSystem: SystemBase
{
BakingSystem bakingSystem;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
protected override void OnCreate()
{
bakingSystem = World.GetExistingSystemManaged<BakingSystem>();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
protected override void OnUpdate()
{
DynamicBuffer<NewBlobAssetDatabaseRecord<AvatarMaskBlob>> newBlobAssetRecords = default;
var ecb = new EntityCommandBuffer(Allocator.Temp);
foreach (var (avatarMaskDataArr, e) in SystemAPI.Query<DynamicBuffer<AvatarMaskBakingData>>()
.WithEntityAccess().WithOptions(EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabledEntities))
{
foreach (var am in avatarMaskDataArr)
{
if (!EntityManager.HasComponent<RigDefinitionComponent>(am.rigEntity))
continue;
var rigDef = EntityManager.GetComponentData<RigDefinitionComponent>(am.rigEntity);
var amb = MakeMaskForAvatar(rigDef.rigBlob, am.dataBlob);
if (!newBlobAssetRecords.IsCreated)
{
newBlobAssetRecords = ecb.AddBuffer<NewBlobAssetDatabaseRecord<AvatarMaskBlob>>(e);
}
var newAvatarMaskBlob = new NewBlobAssetDatabaseRecord<AvatarMaskBlob>()
{
hash = amb.Value.hash,
value = amb
};
newBlobAssetRecords.Add(newAvatarMaskBlob);
}
}
ecb.Playback(EntityManager);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BlobAssetReference<AvatarMaskBlob> MakeMaskForAvatar(BlobAssetReference<RigDefinitionBlob> rdb, BlobAssetReference<AvatarMaskBakingDataBlob> am)
{
var l = rdb.Value.bones.Length;
var avatarMaskContainerLength = (int)math.ceil(l / 32.0f);
var bb = new BlobBuilder(Allocator.Temp);
ref var ambBuilder = ref bb.ConstructRoot<AvatarMaskBlob>();
ambBuilder.hash = am.Value.hash;
ambBuilder.humanBodyPartsAvatarMask = am.Value.humanBodyPartsAvatarMask;
#if RUKHANKA_DEBUG_INFO
if (am.Value.name.Length > 0)
bb.AllocateString(ref ambBuilder.name, am.Value.name.ToString());
var includedBoneNames = bb.Allocate(ref ambBuilder.includedBoneNames, am.Value.includedBoneNames.Length);
for (var i = 0; i < includedBoneNames.Length; ++i)
{
bb.AllocateString(ref includedBoneNames[i], am.Value.includedBoneNames[i].ToString());
}
ambBuilder.bakingTime = am.Value.bakingTime;
#endif
var maskArr = bb.Allocate(ref ambBuilder.includedBoneMask, avatarMaskContainerLength);
for (var i = 0; i < l; ++i)
{
ref var rigBone = ref rdb.Value.bones[i];
var maskEntriesCount = am.Value.includedBoneHashes.Length;
var j = 0;
for (; j < maskEntriesCount; ++j)
{
var maskBoneHash = am.Value.includedBoneHashes[j];
if (maskBoneHash == rigBone.hash)
break;
}
if (j < maskEntriesCount)
{
var (uintIndex, mask) = AvatarMaskBlob.GetUintIndexAndMask(i);
var avatarMaskValue = maskArr[uintIndex];
avatarMaskValue |= mask;
maskArr[uintIndex] = avatarMaskValue;
}
}
var amb = bb.CreateBlobAssetReference<AvatarMaskBlob>(Allocator.Persistent);
bakingSystem.BlobAssetStore.TryAdd(am.Value.hash, ref amb);
return amb;
}
}
}