Files
Master-Arbeit-Tom-Hempel/Unity-Master/Assets/Scripts/AvatarDataReader.cs
2025-09-21 22:42:26 +02:00

366 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using Newtonsoft.Json;
public class AvatarDataReader : MonoBehaviour
{
[Header("Avatar Configuration")]
[SerializeField] private Transform targetAvatarRoot;
[SerializeField] private string fileName = "avatar_sync_data.json";
[SerializeField] private bool readEveryFrame = true;
[SerializeField] private float updateRate = 60f; // Updates per second
[SerializeField] private bool smoothTransitions = true;
[SerializeField] private float transitionSpeed = 10f;
[Header("Synchronization Options")]
[SerializeField] private bool syncWorldPosition = true; // Apply avatar world position
[SerializeField] private bool syncWorldRotation = true; // Apply avatar world rotation
[SerializeField] private bool syncLocalScale = false; // Apply avatar local scale
[Header("Debug")]
[SerializeField] private bool showDebugInfo = false;
[SerializeField] private bool enableDebugMode = false;
[SerializeField] private string debugBoneName = "LeftArm"; // Bone to monitor
private string filePath;
private float lastUpdateTime;
private Dictionary<string, Transform> boneLookup;
private Dictionary<string, SkinnedMeshRenderer> skinnedMeshLookup;
private AvatarSyncData lastSyncData;
private bool isInitialized = false;
private int successfulReads = 0;
private int appliedBones = 0;
private Vector3 lastAppliedWorldPosition;
void Start()
{
// Set up file path
string syncFilesPath = Path.Combine(Application.dataPath, "Sync-Files");
filePath = Path.Combine(syncFilesPath, fileName);
// If targetAvatarRoot is not assigned, try to find it automatically
if (targetAvatarRoot == null)
{
targetAvatarRoot = transform;
}
// Cache target avatar components
CacheTargetAvatarComponents();
// Initialize debug tracking
if (enableDebugMode)
{
lastAppliedWorldPosition = targetAvatarRoot.position;
}
Debug.Log($"Avatar Data Reader initialized. Reading from: {filePath}");
Debug.Log($"World Position Sync: {syncWorldPosition}, World Rotation Sync: {syncWorldRotation}");
}
void CacheTargetAvatarComponents()
{
boneLookup = new Dictionary<string, Transform>();
skinnedMeshLookup = new Dictionary<string, SkinnedMeshRenderer>();
// Get all skinned mesh renderers
SkinnedMeshRenderer[] smrs = targetAvatarRoot.GetComponentsInChildren<SkinnedMeshRenderer>();
// Cache bones from SkinnedMeshRenderers
foreach (SkinnedMeshRenderer smr in smrs)
{
// Cache this skinned mesh renderer
if (!skinnedMeshLookup.ContainsKey(smr.gameObject.name))
{
skinnedMeshLookup.Add(smr.gameObject.name, smr);
}
// Cache all bones from this SkinnedMeshRenderer
if (smr.bones != null)
{
foreach (Transform bone in smr.bones)
{
if (bone != null && !boneLookup.ContainsKey(bone.name))
{
boneLookup.Add(bone.name, bone);
}
}
}
}
isInitialized = true;
if (showDebugInfo)
{
Debug.Log($"Cached {boneLookup.Count} bones and {skinnedMeshLookup.Count} skinned mesh renderers");
}
// Debug mode: Print some bone names and check for the debug bone
if (enableDebugMode)
{
Debug.Log("Reader - Found bones:");
int count = 0;
foreach (var kvp in boneLookup)
{
if (count < 10)
{
Debug.Log($" Reader Bone {count}: {kvp.Key}");
count++;
}
if (kvp.Key.Contains(debugBoneName))
{
Debug.Log($"Found debug bone in reader: {kvp.Key}");
}
}
}
}
void Update()
{
if (!isInitialized || !File.Exists(filePath))
return;
if (readEveryFrame)
{
ReadAndApplyAvatarData();
}
else
{
// Rate-limited updates
if (Time.time - lastUpdateTime >= 1f / updateRate)
{
ReadAndApplyAvatarData();
lastUpdateTime = Time.time;
}
}
}
void ReadAndApplyAvatarData()
{
try
{
string json = File.ReadAllText(filePath);
AvatarSyncData syncData = JsonConvert.DeserializeObject<AvatarSyncData>(json);
if (syncData == null)
{
if (showDebugInfo)
Debug.LogWarning("Failed to deserialize avatar sync data");
return;
}
successfulReads++;
appliedBones = 0;
// Apply root transform data
ApplyRootTransformData(syncData.rootTransform);
// Apply bone data
ApplyBoneData(syncData.bones);
// Apply blendshape data
ApplyBlendShapeData(syncData.blendShapes);
if (enableDebugMode && successfulReads % 60 == 0) // Log every 60 reads (about once per second)
{
Debug.Log($"Reader stats: {successfulReads} successful reads, {appliedBones} bones applied in last read");
}
lastSyncData = syncData;
}
catch (Exception e)
{
if (showDebugInfo)
Debug.LogError($"Error reading avatar data: {e.Message}");
}
}
void ApplyRootTransformData(RootTransformData rootData)
{
if (rootData == null)
return;
if (syncWorldPosition && rootData.worldPosition != null)
{
Vector3 targetWorldPosition = rootData.worldPosition.ToVector3();
// Debug: Log world position changes
if (enableDebugMode && Vector3.Distance(targetWorldPosition, lastAppliedWorldPosition) > 0.01f)
{
Debug.Log($"Applying world position: {targetWorldPosition} (was: {lastAppliedWorldPosition})");
lastAppliedWorldPosition = targetWorldPosition;
}
if (smoothTransitions)
{
targetAvatarRoot.position = Vector3.Lerp(
targetAvatarRoot.position,
targetWorldPosition,
transitionSpeed * Time.deltaTime
);
}
else
{
targetAvatarRoot.position = targetWorldPosition;
}
}
if (syncWorldRotation && rootData.worldRotation != null)
{
Quaternion targetWorldRotation = rootData.worldRotation.ToQuaternion();
if (smoothTransitions)
{
targetAvatarRoot.rotation = Quaternion.Lerp(
targetAvatarRoot.rotation,
targetWorldRotation,
transitionSpeed * Time.deltaTime
);
}
else
{
targetAvatarRoot.rotation = targetWorldRotation;
}
}
if (syncLocalScale && rootData.localScale != null)
{
Vector3 targetLocalScale = rootData.localScale.ToVector3();
if (smoothTransitions)
{
targetAvatarRoot.localScale = Vector3.Lerp(
targetAvatarRoot.localScale,
targetLocalScale,
transitionSpeed * Time.deltaTime
);
}
else
{
targetAvatarRoot.localScale = targetLocalScale;
}
}
}
void ApplyBoneData(List<BoneData> boneDataList)
{
if (boneDataList == null)
return;
foreach (BoneData boneData in boneDataList)
{
if (boneLookup.TryGetValue(boneData.boneName, out Transform targetBone))
{
// Convert serializable types back to Unity types
Vector3 targetPosition = boneData.position.ToVector3();
Quaternion targetRotation = boneData.rotation.ToQuaternion();
Vector3 targetScale = boneData.scale.ToVector3();
// Debug mode: Log changes to the monitored bone
if (enableDebugMode && boneData.boneName.Contains(debugBoneName))
{
Debug.Log($"Applying to bone {boneData.boneName}: Pos={targetPosition}, Rot={targetRotation}");
}
if (smoothTransitions)
{
// Smooth interpolation
targetBone.localPosition = Vector3.Lerp(
targetBone.localPosition,
targetPosition,
transitionSpeed * Time.deltaTime
);
targetBone.localRotation = Quaternion.Lerp(
targetBone.localRotation,
targetRotation,
transitionSpeed * Time.deltaTime
);
targetBone.localScale = Vector3.Lerp(
targetBone.localScale,
targetScale,
transitionSpeed * Time.deltaTime
);
}
else
{
// Direct application
targetBone.localPosition = targetPosition;
targetBone.localRotation = targetRotation;
targetBone.localScale = targetScale;
}
appliedBones++;
}
else if (showDebugInfo)
{
Debug.LogWarning($"Bone not found: {boneData.boneName}");
}
}
}
void ApplyBlendShapeData(List<BlendShapeData> blendShapeDataList)
{
if (blendShapeDataList == null)
return;
foreach (BlendShapeData blendShapeData in blendShapeDataList)
{
if (skinnedMeshLookup.TryGetValue(blendShapeData.meshName, out SkinnedMeshRenderer targetSmr))
{
if (targetSmr.sharedMesh != null && blendShapeData.weights != null)
{
int maxBlendShapes = Mathf.Min(targetSmr.sharedMesh.blendShapeCount, blendShapeData.weights.Length);
for (int i = 0; i < maxBlendShapes; i++)
{
if (smoothTransitions)
{
float currentWeight = targetSmr.GetBlendShapeWeight(i);
float targetWeight = blendShapeData.weights[i];
float newWeight = Mathf.Lerp(currentWeight, targetWeight, transitionSpeed * Time.deltaTime);
targetSmr.SetBlendShapeWeight(i, newWeight);
}
else
{
targetSmr.SetBlendShapeWeight(i, blendShapeData.weights[i]);
}
}
}
}
else if (showDebugInfo)
{
Debug.LogWarning($"SkinnedMeshRenderer not found: {blendShapeData.meshName}");
}
}
}
public void SetTargetAvatar(Transform newTargetRoot)
{
targetAvatarRoot = newTargetRoot;
if (isInitialized)
{
CacheTargetAvatarComponents();
}
}
public void SetFileName(string newFileName)
{
fileName = newFileName;
string syncFilesPath = Path.Combine(Application.dataPath, "Sync-Files");
filePath = Path.Combine(syncFilesPath, fileName);
}
public bool IsDataAvailable()
{
return File.Exists(filePath);
}
public float GetLastSyncTimestamp()
{
return lastSyncData?.timestamp ?? 0f;
}
}