Netcode Bootstrap

This commit is contained in:
Luis Gonzalez
2026-05-31 14:27:52 -07:00
parent 99d8d2d2a9
commit 7fa77ce821
1813 changed files with 2921554 additions and 84 deletions
@@ -0,0 +1,84 @@
#if UNITY_EDITOR
using System.Collections.Generic;
using Rukhanka.Toolbox;
using Unity.Collections;
using Unity.Entities;
using UnityEngine;
using FixedStringName = Unity.Collections.FixedString512Bytes;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rukhanka.Hybrid
{
public class AvatarMaskBaker
{
internal BlobAssetReference<AvatarMaskBakingDataBlob> CreateAvatarMaskBlob(IBaker baker, AvatarMask am, RigDefinitionAuthoring rd)
{
if (am == null)
return default;
var blobHash = BakingUtils.ComputeAvatarMaskHash(am, rd);
var blobExists = baker.TryGetBlobAssetReference<AvatarMaskBakingDataBlob>(blobHash, out var avatarMaskBlob);
if (blobExists)
return avatarMaskBlob;
var bb = new BlobBuilder(Allocator.Temp);
ref var amb = ref bb.ConstructRoot<AvatarMaskBakingDataBlob>();
amb.hash = blobHash;
#if RUKHANKA_DEBUG_INFO
if (am.name.Length > 0)
bb.AllocateString(ref amb.name, am.name);
var startTimeMarker = Time.realtimeSinceStartup;
#endif
// Generic avatar mask
var avatarMaskIncludedBones = new List<string>();
for (int i = 0; i < am.transformCount; ++i)
{
var bonePath = am.GetTransformPath(i);
var boneActive = am.GetTransformActive(i);
if (bonePath.Length == 0 || !boneActive) continue;
var boneNames = bonePath.Split('/');
var leafBoneName = boneNames[^1];
avatarMaskIncludedBones.Add(leafBoneName);
}
var includedBoneHashes = bb.Allocate(ref amb.includedBoneHashes, avatarMaskIncludedBones.Count);
#if RUKHANKA_DEBUG_INFO
var includedBonePaths = bb.Allocate(ref amb.includedBoneNames, avatarMaskIncludedBones.Count);
#endif
for (var i = 0; i < includedBoneHashes.Length; ++i)
{
var leafBoneName = avatarMaskIncludedBones[i];
#if RUKHANKA_DEBUG_INFO
bb.AllocateString(ref includedBonePaths[i], leafBoneName);
#endif
includedBoneHashes[i] = new FixedStringName(leafBoneName).CalculateHash32();
}
// Humanoid avatar mask
var humanBodyPartsCount = (int)AvatarMaskBodyPart.LastBodyPart;
amb.humanBodyPartsAvatarMask = 0;
for (int i = 0; i < humanBodyPartsCount; ++i)
{
var ambp = (AvatarMaskBodyPart)i;
if (am.GetHumanoidBodyPartActive(ambp))
amb.humanBodyPartsAvatarMask |= 1u << i;
}
#if RUKHANKA_DEBUG_INFO
var dt = Time.realtimeSinceStartupAsDouble - startTimeMarker;
amb.bakingTime = (float)dt;
#endif
var rv = bb.CreateBlobAssetReference<AvatarMaskBakingDataBlob>(Allocator.Persistent);
baker.AddBlobAssetWithCustomHash(ref rv, blobHash);
return rv;
}
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: d8815f346c0ffcd4a91d7954749ca057
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 298480
packageName: Rukhanka Animation System 2
packageVersion: 2.9.0
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/AvatarMask/AvatarMaskBaker.cs
uploadId: 897522
@@ -0,0 +1,131 @@
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;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 5ee83ceaf7c5a9e4a9c8192105c07930
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 298480
packageName: Rukhanka Animation System 2
packageVersion: 2.9.0
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/AvatarMask/AvatarMaskBakingSystem.cs
uploadId: 897522