Added all the other GhostSystem Scripts

This commit is contained in:
Thorbjoern
2025-05-26 01:23:52 +02:00
parent e5bca03433
commit 3ec28d4bc5
30 changed files with 1638 additions and 149 deletions
@@ -0,0 +1,88 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[Serializable]
public struct Vector3DTO
{
public float x;
public float y;
public float z;
public Vector3DTO(Vector3 v)
{
x = v.x;
y = v.y;
z = v.z;
}
public Vector3 ToVector3() => new Vector3(x, y, z);
}
[Serializable]
public struct QuaternionDTO
{
public float x;
public float y;
public float z;
public float w;
public QuaternionDTO(Quaternion q)
{
x = q.x;
y = q.y;
z = q.z;
w = q.w;
}
public Quaternion ToQuaternion() => new Quaternion(x, y, z, w);
}
[System.Serializable]
public class Vector3Serializable
{
public float x;
public float y;
public float z;
public Vector3Serializable() { }
public Vector3Serializable(Vector3 vec)
{
x = vec.x;
y = vec.y;
z = vec.z;
}
public Vector3 ToVector3()
{
return new Vector3(x, y, z);
}
}
[System.Serializable]
public class QuaternionSerializable
{
public float x;
public float y;
public float z;
public float w;
public QuaternionSerializable() { }
public QuaternionSerializable(Quaternion quat)
{
x = quat.x;
y = quat.y;
z = quat.z;
w = quat.w;
}
public Quaternion ToQuaternion()
{
return new Quaternion(x, y, z, w);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3ea3278ea48dfa24e94a08dcaff5041c
+41
View File
@@ -0,0 +1,41 @@
using Postgrest.Attributes;
using Postgrest.Models;
using UnityEngine;
using Newtonsoft.Json;
using System;
namespace GhostSystem
{
[Table("object_positions")]
public class FrameModel : BaseModel
{
[Column("user_id")]
public string UserId { get; set; }
[Column("scene_id")]
public string SceneId { get; set; }
[Column("object_id")]
public string ObjectId { get; set; }
[Column("timestamp")]
public string Timestamp { get; set; } // ISO 8601 string
[Column("position")]
public string PositionJson { get; set; } // stored as JSONB
[Column("rotation")]
public string RotationJson { get; set; } // stored as JSONB
[JsonIgnore]
public DateTime TimestampParsed => DateTime.Parse(Timestamp).ToUniversalTime();
[JsonIgnore]
public Vector3 Position => JsonConvert.DeserializeObject<Vector3Serializable>(PositionJson).ToVector3();
[JsonIgnore]
public Quaternion Rotation => JsonConvert.DeserializeObject<QuaternionSerializable>(RotationJson).ToQuaternion();
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 101e569ac87f30543a231b553aa2ec95
+26
View File
@@ -0,0 +1,26 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace GhostSystem
{
[System.Serializable]
public class GhostFrame
{
public string SceneId;
public string ObjectId;
public System.DateTime Timestamp;
public Vector3 Position;
public Quaternion Rotation;
public GhostFrame(string sceneId, string objectId, Vector3 pos, Quaternion rot)
{
SceneId = sceneId;
ObjectId = objectId;
Position = pos;
Rotation = rot;
Timestamp = System.DateTime.UtcNow;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8cbeee9cd1185344abb805e24491a4df
@@ -0,0 +1,129 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System;
namespace GhostSystem
{
public class GhostRecorderBatch : MonoBehaviour
{
public bool test = false;
public string SceneId;
public float SampleRate = 0.05f;
public List<Transform> TrackedObjects;
private float _timer;
private bool _isRecording;
private bool supabaseReady = false;
private bool sceneIdReady = false;
private async void OnEnable()
{
SupabaseManager.OnSuperbaseReady += HandleSupabaseReady;
RoomManager.OnSceneIdReady += HandleSceneIdReady;
}
private void OnDisable()
{
SupabaseManager.OnSuperbaseReady -= HandleSupabaseReady;
RoomManager.OnSceneIdReady -= HandleSceneIdReady;
}
private void HandleSupabaseReady()
{
supabaseReady = true;
}
private void HandleSceneIdReady(string id)
{
SceneId = id;
sceneIdReady = true;
}
private async void Update()
{
if (test) await TestInsert();
if (!_isRecording) return;
_timer += Time.deltaTime;
if (_timer >= SampleRate)
{
_timer = 0f;
foreach (var obj in TrackedObjects)
{
var frame = new GhostFrame(SceneId, obj.name, obj.position, obj.rotation);
await InsertFrame(frame);
}
}
}
private async Task InsertFrame(GhostFrame frame)
{
var position = new Vector3Serializable
{
x = frame.Position.x,
y = frame.Position.y,
z = frame.Position.z
};
var rotation = new QuaternionSerializable
{
x = frame.Rotation.x,
y = frame.Rotation.y,
z = frame.Rotation.z,
w = frame.Rotation.w
};
var data = new FrameModel
{
UserId = SupabaseManager.instance.userId,
SceneId = frame.SceneId,
ObjectId = frame.ObjectId,
Timestamp = frame.Timestamp.ToString("o"),
PositionJson = JsonConvert.SerializeObject(position),
RotationJson = JsonConvert.SerializeObject(rotation)
};
Debug.Log($"Insert frame with user_id: {SupabaseManager.instance.userId}");
await SupabaseManager.instance.supabase.From<FrameModel>().Insert(data);
}
public void StartRecording()
{
if (!supabaseReady || !sceneIdReady) return;
_isRecording = true;
Debug.Log($"Recording {SceneId}");
}
public void StopRecording()
{
_isRecording = false;
sceneIdReady = false;
Debug.Log($"Stop recording: {SceneId}");
}
private async Task TestInsert()
{
var pos = new Vector3Serializable { x = 1f, y = 2f, z = 3f };
var rot = new QuaternionSerializable { x = 0f, y = 0f, z = 0f, w = 1f };
var data = new FrameModel
{
UserId = SupabaseManager.instance.userId,
SceneId = "testScene",
ObjectId = "testObject",
Timestamp = DateTime.UtcNow.ToString("o"),
PositionJson = JsonConvert.SerializeObject(pos),
RotationJson = JsonConvert.SerializeObject(rot)
};
Debug.Log(JsonConvert.SerializeObject(data)); // Schau hier, ob es nur Strings sind.
await SupabaseManager.instance.supabase.From<FrameModel>().Insert(data);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: acfd4230ec07a824b980e2dd17ca8031
@@ -0,0 +1,231 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using System.Threading.Tasks;
using System;
using System.Reactive;
using UnityEngine.UI;
namespace GhostSystem
{
public class GhostReplayManager : MonoBehaviour
{
[Header("Replay Settings")]
public string SceneId;
public string UserFilter = null; // optional: filtere nach User-ID
public bool Loop = true;
public DateTime selectedStartTime;
public DateSelector dateSelector;
[Header("Target Objects")]
public List<TrackedObjectBinding> TrackedObjects;
[Header("Playback")]
public float PlaybackSpeed = 1f;
public bool AutoStart = true;
public Slider playbackSlider;
private bool isScrubbing = false;
private bool isPlaying = false;
private float playbackTime = 0f;
private float totalDuration = 0f;
private Dictionary<string, List<FrameModel>> objectTracks = new();
private bool supabaseReady = false;
private bool sceneIdReady = false;
private string loadedSceneId;
[Serializable]
public class TrackedObjectBinding
{
public string objectId;
public GameObject targetObject;
}
private async void OnEnable()
{
SupabaseManager.OnSuperbaseReady += HandleSupabaseReady;
RoomManager.OnSceneIdReady += HandleSceneIdReady;
}
private void OnDisable()
{
SupabaseManager.OnSuperbaseReady -= HandleSupabaseReady;
RoomManager.OnSceneIdReady -= HandleSceneIdReady;
}
private void HandleSupabaseReady()
{
supabaseReady = true;
TryInitializeReplay();
}
private void HandleSceneIdReady(string id)
{
sceneIdReady = true;
loadedSceneId = id;
TryInitializeReplay();
}
private async void TryInitializeReplay()
{
if (!supabaseReady || !sceneIdReady) return;
SceneId = loadedSceneId;
await LoadFrames();
StartReplay();
}
public async Task LoadFrames()
{
Debug.Log("SceneId used in query: " + SceneId);
var query = SupabaseManager.instance.supabase
.From<FrameModel>()
.Filter("scene_id", Postgrest.Constants.Operator.Equals, SceneId)
.Filter("timestamp", Postgrest.Constants.Operator.GreaterThanOrEqual, dateSelector.selectedDateTime().ToString("o"))
.Order("timestamp", Postgrest.Constants.Ordering.Ascending);
if (!string.IsNullOrEmpty(UserFilter))
query = query.Filter("user_id", Postgrest.Constants.Operator.Equals, UserFilter);
var result = await query.Get();
var frames = result.Models;
Debug.LogError("Frames: " + frames.Count);
objectTracks.Clear();
foreach (var frame in frames)
{
if (!objectTracks.ContainsKey(frame.ObjectId))
objectTracks[frame.ObjectId] = new List<FrameModel>();
objectTracks[frame.ObjectId].Add(frame);
//Debug.Log($"Loaded frame: {frame.ObjectId} at {frame.Timestamp}");
Debug.Log($"Loaded frame: {frame.ObjectId} at {frame.TimestampParsed} Pos: {frame.Position}");
Debug.Log($"Parsed timestamp: {frame.Timestamp} -> {frame.TimestampParsed.ToString("o")}" + "Pos JSON: " + frame.PositionJson);
}
Debug.LogWarning("Got the Data!");
Debug.LogWarning(objectTracks.Count);
// Gesamtzeit berechnen (in Sekunden)
if (frames.Count > 1)
{
totalDuration = (float)(frames.Last().TimestampParsed - frames.First().TimestampParsed).TotalSeconds;
}
}
public void StartReplay() // Für das Play UI-Element, um das Replay zu starten
{
isPlaying = true;
playbackTime = 0f;
Debug.LogError("Replay started: total duration = " + totalDuration);
}
public void PauseReplay() => isPlaying = false; // Für das Pause UI-Element, um das Replay zu pausieren
public void StopReplay() // Für das Stop UI-Element, um das Replay zu beenden
{
isPlaying = false;
playbackTime = 0f;
}
public void SetPlaybackTime(float time) // Für das Slider UI-Element zum Scrubben
{
playbackTime = Mathf.Clamp(time, 0f, totalDuration);
}
private void Update()
{
if (!isPlaying || objectTracks.Count == 0)
return;
playbackTime += Time.deltaTime * PlaybackSpeed;
if (playbackTime > totalDuration)
{
if (Loop)
playbackTime = 0f;
else
isPlaying = false;
}
DateTime currentTimestamp = objectTracks.Values
.SelectMany(list => list)
.First().TimestampParsed + TimeSpan.FromSeconds(playbackTime);
foreach (var binding in TrackedObjects)
{
if (!objectTracks.ContainsKey(binding.objectId))
continue;
var frames = objectTracks[binding.objectId];
if (frames.Count < 2)
continue;
FrameModel a = null, b = null;
for (int i = 0; i < frames.Count - 1; i++)
{
var tA = frames[i].TimestampParsed;
var tB = frames[i + 1].TimestampParsed;
if (tA <= currentTimestamp && tB >= currentTimestamp)
{
a = frames[i];
b = frames[i + 1];
break;
}
}
if (a != null && b != null)
{
double tA = a.TimestampParsed.Subtract(DateTime.UnixEpoch).TotalSeconds;
double tB = b.TimestampParsed.Subtract(DateTime.UnixEpoch).TotalSeconds;
double tCurrent = currentTimestamp.Subtract(DateTime.UnixEpoch).TotalSeconds;
float t = Mathf.Clamp01((float)((tCurrent - tA) / (tB - tA)));
Vector3 pos = Vector3.Lerp(a.Position, b.Position, t);
Quaternion rot = Quaternion.Slerp(a.Rotation, b.Rotation, t);
binding.targetObject.transform.SetPositionAndRotation(pos, rot);
}
}
if (!isScrubbing && playbackSlider != null && totalDuration > 0f)
{
playbackSlider.value = playbackTime / totalDuration;
}
}
/*public void OnSliderValueChanged(float value)
{
if (totalDuration <= 0f) return;
isScrubbing = true;
SetPlaybackTime(value * totalDuration);
}*/
public void SetSliderScrubValue(float sliderValue)
{
if (isScrubbing)
SetPlaybackTime(sliderValue * totalDuration);
}
public void OnSliderStartDrag()
{
isScrubbing = true;
}
public void OnSliderEndDrag()
{
isScrubbing = false;
SetPlaybackTime(playbackSlider.value * totalDuration);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 151f4fc8945972545b4591ce05f59a2a