Added all the other GhostSystem Scripts
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user