initial upload
This commit is contained in:
331
Unity-Master/Assets/Scripts/AvatarDataWriter.cs
Normal file
331
Unity-Master/Assets/Scripts/AvatarDataWriter.cs
Normal file
@ -0,0 +1,331 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
[System.Serializable]
|
||||
public class SerializableVector3
|
||||
{
|
||||
public float x, y, z;
|
||||
|
||||
public SerializableVector3() { }
|
||||
|
||||
public SerializableVector3(Vector3 vector)
|
||||
{
|
||||
x = vector.x;
|
||||
y = vector.y;
|
||||
z = vector.z;
|
||||
}
|
||||
|
||||
public Vector3 ToVector3()
|
||||
{
|
||||
return new Vector3(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class SerializableQuaternion
|
||||
{
|
||||
public float x, y, z, w;
|
||||
|
||||
public SerializableQuaternion() { }
|
||||
|
||||
public SerializableQuaternion(Quaternion quaternion)
|
||||
{
|
||||
x = quaternion.x;
|
||||
y = quaternion.y;
|
||||
z = quaternion.z;
|
||||
w = quaternion.w;
|
||||
}
|
||||
|
||||
public Quaternion ToQuaternion()
|
||||
{
|
||||
return new Quaternion(x, y, z, w);
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class RootTransformData
|
||||
{
|
||||
public SerializableVector3 worldPosition;
|
||||
public SerializableQuaternion worldRotation;
|
||||
public SerializableVector3 localScale;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class BoneData
|
||||
{
|
||||
public string boneName; // e.g. "LeftArm", "RightHand"
|
||||
public SerializableVector3 position;
|
||||
public SerializableQuaternion rotation;
|
||||
public SerializableVector3 scale;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class BlendShapeData
|
||||
{
|
||||
public string meshName;
|
||||
public float[] weights;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class AvatarSyncData
|
||||
{
|
||||
public RootTransformData rootTransform;
|
||||
public List<BoneData> bones;
|
||||
public List<BlendShapeData> blendShapes;
|
||||
public float timestamp;
|
||||
}
|
||||
|
||||
public class AvatarDataWriter : MonoBehaviour
|
||||
{
|
||||
[Header("Avatar Configuration")]
|
||||
[SerializeField] private Transform avatarRoot;
|
||||
[SerializeField] private string fileName = "avatar_sync_data.json";
|
||||
[SerializeField] private bool writeEveryFrame = true;
|
||||
[SerializeField] private float updateRate = 60f; // Updates per second
|
||||
|
||||
[Header("Synchronization Options")]
|
||||
[SerializeField] private bool syncWorldPosition = true; // Sync avatar world position
|
||||
[SerializeField] private bool syncWorldRotation = true; // Sync avatar world rotation
|
||||
[SerializeField] private bool syncLocalScale = false; // Sync avatar local scale
|
||||
|
||||
[Header("Debug")]
|
||||
[SerializeField] private bool enableDebugMode = false;
|
||||
[SerializeField] private string debugBoneName = "LeftArm"; // Bone to monitor
|
||||
|
||||
private string filePath;
|
||||
private float lastUpdateTime;
|
||||
private List<Transform> allBones;
|
||||
private List<SkinnedMeshRenderer> skinnedMeshRenderers;
|
||||
private Vector3 lastDebugPosition;
|
||||
private Quaternion lastDebugRotation;
|
||||
private Vector3 lastRootWorldPosition;
|
||||
|
||||
void Start()
|
||||
{
|
||||
// Create Sync-Files directory if it doesn't exist
|
||||
string syncFilesPath = Path.Combine(Application.dataPath, "Sync-Files");
|
||||
if (!Directory.Exists(syncFilesPath))
|
||||
{
|
||||
Directory.CreateDirectory(syncFilesPath);
|
||||
}
|
||||
|
||||
filePath = Path.Combine(syncFilesPath, fileName);
|
||||
|
||||
// If avatarRoot is not assigned, try to find it automatically
|
||||
if (avatarRoot == null)
|
||||
{
|
||||
avatarRoot = transform;
|
||||
}
|
||||
|
||||
// Cache all bones and skinned mesh renderers
|
||||
CacheAvatarComponents();
|
||||
|
||||
// Initialize debug tracking
|
||||
if (enableDebugMode)
|
||||
{
|
||||
Transform debugBone = FindBoneByName(debugBoneName);
|
||||
if (debugBone != null)
|
||||
{
|
||||
lastDebugPosition = debugBone.localPosition;
|
||||
lastDebugRotation = debugBone.localRotation;
|
||||
Debug.Log($"Debug mode enabled. Monitoring bone: {debugBoneName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"Debug bone '{debugBoneName}' not found!");
|
||||
}
|
||||
|
||||
lastRootWorldPosition = avatarRoot.position;
|
||||
}
|
||||
|
||||
Debug.Log($"Avatar Data Writer initialized. Writing to: {filePath}");
|
||||
Debug.Log($"World Position Sync: {syncWorldPosition}, World Rotation Sync: {syncWorldRotation}");
|
||||
}
|
||||
|
||||
void CacheAvatarComponents()
|
||||
{
|
||||
allBones = new List<Transform>();
|
||||
skinnedMeshRenderers = new List<SkinnedMeshRenderer>();
|
||||
|
||||
// Get all skinned mesh renderers
|
||||
skinnedMeshRenderers.AddRange(avatarRoot.GetComponentsInChildren<SkinnedMeshRenderer>());
|
||||
|
||||
// Get all unique bones from all SkinnedMeshRenderers
|
||||
HashSet<Transform> uniqueBones = new HashSet<Transform>();
|
||||
foreach (SkinnedMeshRenderer smr in skinnedMeshRenderers)
|
||||
{
|
||||
if (smr.bones != null)
|
||||
{
|
||||
foreach (Transform bone in smr.bones)
|
||||
{
|
||||
if (bone != null)
|
||||
{
|
||||
uniqueBones.Add(bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allBones.AddRange(uniqueBones);
|
||||
|
||||
Debug.Log($"Cached {allBones.Count} bones and {skinnedMeshRenderers.Count} skinned mesh renderers");
|
||||
|
||||
// Debug: Print some bone names to help with troubleshooting
|
||||
if (enableDebugMode)
|
||||
{
|
||||
Debug.Log("Found bones:");
|
||||
for (int i = 0; i < Mathf.Min(10, allBones.Count); i++)
|
||||
{
|
||||
Debug.Log($" Bone {i}: {allBones[i].name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Transform FindBoneByName(string name)
|
||||
{
|
||||
foreach (Transform bone in allBones)
|
||||
{
|
||||
if (bone.name.Contains(name))
|
||||
{
|
||||
return bone;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// Debug mode: Check if the monitored bone has changed
|
||||
if (enableDebugMode)
|
||||
{
|
||||
Transform debugBone = FindBoneByName(debugBoneName);
|
||||
if (debugBone != null)
|
||||
{
|
||||
if (Vector3.Distance(debugBone.localPosition, lastDebugPosition) > 0.001f ||
|
||||
Quaternion.Angle(debugBone.localRotation, lastDebugRotation) > 0.1f)
|
||||
{
|
||||
Debug.Log($"Bone {debugBoneName} changed! Pos: {debugBone.localPosition}, Rot: {debugBone.localRotation}");
|
||||
lastDebugPosition = debugBone.localPosition;
|
||||
lastDebugRotation = debugBone.localRotation;
|
||||
}
|
||||
}
|
||||
|
||||
// Debug: Check if avatar root world position has changed
|
||||
if (Vector3.Distance(avatarRoot.position, lastRootWorldPosition) > 0.01f)
|
||||
{
|
||||
Debug.Log($"Avatar root world position changed! From: {lastRootWorldPosition} To: {avatarRoot.position}");
|
||||
lastRootWorldPosition = avatarRoot.position;
|
||||
}
|
||||
}
|
||||
|
||||
if (writeEveryFrame)
|
||||
{
|
||||
WriteAvatarData();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rate-limited updates
|
||||
if (Time.time - lastUpdateTime >= 1f / updateRate)
|
||||
{
|
||||
WriteAvatarData();
|
||||
lastUpdateTime = Time.time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteAvatarData()
|
||||
{
|
||||
try
|
||||
{
|
||||
AvatarSyncData syncData = new AvatarSyncData
|
||||
{
|
||||
rootTransform = new RootTransformData(),
|
||||
bones = new List<BoneData>(),
|
||||
blendShapes = new List<BlendShapeData>(),
|
||||
timestamp = Time.time
|
||||
};
|
||||
|
||||
// Capture root transform data
|
||||
if (syncWorldPosition)
|
||||
{
|
||||
syncData.rootTransform.worldPosition = new SerializableVector3(avatarRoot.position);
|
||||
}
|
||||
else
|
||||
{
|
||||
syncData.rootTransform.worldPosition = new SerializableVector3(Vector3.zero);
|
||||
}
|
||||
|
||||
if (syncWorldRotation)
|
||||
{
|
||||
syncData.rootTransform.worldRotation = new SerializableQuaternion(avatarRoot.rotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
syncData.rootTransform.worldRotation = new SerializableQuaternion(Quaternion.identity);
|
||||
}
|
||||
|
||||
if (syncLocalScale)
|
||||
{
|
||||
syncData.rootTransform.localScale = new SerializableVector3(avatarRoot.localScale);
|
||||
}
|
||||
else
|
||||
{
|
||||
syncData.rootTransform.localScale = new SerializableVector3(Vector3.one);
|
||||
}
|
||||
|
||||
// Capture bone data
|
||||
foreach (Transform bone in allBones)
|
||||
{
|
||||
BoneData boneData = new BoneData
|
||||
{
|
||||
boneName = bone.name,
|
||||
position = new SerializableVector3(bone.localPosition),
|
||||
rotation = new SerializableQuaternion(bone.localRotation),
|
||||
scale = new SerializableVector3(bone.localScale)
|
||||
};
|
||||
syncData.bones.Add(boneData);
|
||||
}
|
||||
|
||||
// Capture blendshape data
|
||||
foreach (SkinnedMeshRenderer smr in skinnedMeshRenderers)
|
||||
{
|
||||
if (smr.sharedMesh != null && smr.sharedMesh.blendShapeCount > 0)
|
||||
{
|
||||
float[] weights = new float[smr.sharedMesh.blendShapeCount];
|
||||
for (int i = 0; i < weights.Length; i++)
|
||||
{
|
||||
weights[i] = smr.GetBlendShapeWeight(i);
|
||||
}
|
||||
|
||||
BlendShapeData blendShapeData = new BlendShapeData
|
||||
{
|
||||
meshName = smr.gameObject.name, // Use GameObject name instead of path
|
||||
weights = weights
|
||||
};
|
||||
syncData.blendShapes.Add(blendShapeData);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to JSON and write to file
|
||||
string json = JsonConvert.SerializeObject(syncData, Formatting.Indented);
|
||||
File.WriteAllText(filePath, json);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"Error writing avatar data: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
// Write final data when component is disabled
|
||||
if (allBones != null && allBones.Count > 0)
|
||||
{
|
||||
WriteAvatarData();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user