using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking; /// /// HOY Gallery V2 — GalleryManager /// /// Reads manifest.json (with exact Spatial world coordinates) and places /// each ArtworkFrame at the precise position/rotation/scale extracted from /// Carmen Hoyos' Spatial scene-data.json. /// /// Room geometry: Spatial "Isle Gallery" preset (environment 8) /// Reconstructed from artwork positions found in the scene data: /// /// Central hall: X ≈ -9 to +9, Z ≈ -16 to +5 (width ~18 m, depth ~21 m) /// West wing: X ≈ -22 to -13, Z ≈ -16 to +5 (width ~9 m) /// East wing: X ≈ +13 to +22, Z ≈ -16 to +5 (width ~9 m) /// Ceiling height: ~5.5 m (estimated from artwork Y ≈ 2.2 m centre) /// /// Spawn point: (-0.36, 0, -2.34) facing ~north (from scene JSON) /// public class GalleryManager : MonoBehaviour { [Header("Prefabs")] public GameObject artworkFramePrefab; [Header("Materials")] public Material wallMaterial; public Material floorMaterial; public Material ceilingMaterial; [Header("Lighting")] public Light mainDirectionalLight; // ── Room geometry constants (Isle Gallery reconstruction) ───────────────── // Central hall const float HALL_W = 18f; // X: -9 to +9 const float HALL_D = 22f; // Z: -17 to +5 const float HALL_X = 0f; const float HALL_Z = -6f; // centre of Z range // Side wings (same depth as hall, narrower) const float WING_W = 9f; // X width of each wing const float WING_D = 22f; // Z depth matches hall const float WING_E_X = 18f; // East wing centre X (+13 to +22 → centre ~17.5) const float WING_W_X = -18f; // West wing centre X (-22 to -13 → centre ~-17.5) const float WING_Z = -6f; // same Z centre as hall // Connecting corridors between hall and wings (fill the gap) const float CORR_W = 4f; const float CORR_D = HALL_D; const float CEILING_H = 5.5f; const float FLOOR_Y = 0f; void Start() { BuildRoom(); SetupLighting(); StartCoroutine(LoadManifest()); } // ── Room construction ───────────────────────────────────────────────────── void BuildRoom() { var root = new GameObject("Room"); root.transform.SetParent(transform); // ── Central hall ────────────────────────────────────────────────────── MakePlane(root, "Floor_Hall", new Vector3(HALL_X, FLOOR_Y, HALL_Z), new Vector3(0,0,0), HALL_W, HALL_D, floorMaterial); MakePlane(root, "Ceil_Hall", new Vector3(HALL_X, CEILING_H,HALL_Z), new Vector3(180,0,0), HALL_W, HALL_D, ceilingMaterial); MakePlane(root, "Wall_N_Hall", new Vector3(HALL_X, CEILING_H/2f, -17f),new Vector3(90,180,0), HALL_W, CEILING_H, wallMaterial); MakePlane(root, "Wall_S_Hall", new Vector3(HALL_X, CEILING_H/2f, 5f), new Vector3(90,0,0), HALL_W, CEILING_H, wallMaterial); // Inner walls facing the hall (between hall and wings, partial) MakePlane(root, "Wall_IE_Hall", new Vector3( 9f, CEILING_H/2f, HALL_Z), new Vector3(90,270,0), HALL_D, CEILING_H, wallMaterial); MakePlane(root, "Wall_IW_Hall", new Vector3(-9f, CEILING_H/2f, HALL_Z), new Vector3(90,90,0), HALL_D, CEILING_H, wallMaterial); // ── East wing ───────────────────────────────────────────────────────── MakePlane(root, "Floor_E", new Vector3(WING_E_X, FLOOR_Y, WING_Z), new Vector3(0,0,0), WING_W, WING_D, floorMaterial); MakePlane(root, "Ceil_E", new Vector3(WING_E_X, CEILING_H,WING_Z), new Vector3(180,0,0), WING_W, WING_D, ceilingMaterial); MakePlane(root, "Wall_N_E", new Vector3(WING_E_X, CEILING_H/2f,-17f), new Vector3(90,180,0), WING_W, CEILING_H, wallMaterial); MakePlane(root, "Wall_S_E", new Vector3(WING_E_X, CEILING_H/2f, 5f), new Vector3(90,0,0), WING_W, CEILING_H, wallMaterial); MakePlane(root, "Wall_Ext_E",new Vector3(22.5f, CEILING_H/2f,WING_Z), new Vector3(90,270,0), WING_D, CEILING_H, wallMaterial); // ── West wing ───────────────────────────────────────────────────────── MakePlane(root, "Floor_W", new Vector3(WING_W_X, FLOOR_Y, WING_Z), new Vector3(0,0,0), WING_W, WING_D, floorMaterial); MakePlane(root, "Ceil_W", new Vector3(WING_W_X, CEILING_H,WING_Z), new Vector3(180,0,0), WING_W, WING_D, ceilingMaterial); MakePlane(root, "Wall_N_W", new Vector3(WING_W_X, CEILING_H/2f,-17f), new Vector3(90,180,0),WING_W, CEILING_H, wallMaterial); MakePlane(root, "Wall_S_W", new Vector3(WING_W_X, CEILING_H/2f, 5f), new Vector3(90,0,0), WING_W, CEILING_H, wallMaterial); MakePlane(root, "Wall_Ext_W",new Vector3(-22.5f,CEILING_H/2f,WING_Z), new Vector3(90,90,0), WING_D, CEILING_H, wallMaterial); // ── Partial interior walls (X ≈ ±14) facing wings ──────────────────── // These are the inner faces of the wing walls visible from the hall MakePlane(root, "Wall_IE_Wing", new Vector3(14f, CEILING_H/2f, WING_Z), new Vector3(90,90,0), WING_D, CEILING_H, wallMaterial); MakePlane(root, "Wall_IW_Wing", new Vector3(-14f,CEILING_H/2f, WING_Z), new Vector3(90,270,0), WING_D, CEILING_H, wallMaterial); // ── Floor boxing / skirting colliders (invisible, stop player clipping) AddWallCollider(root, "Col_N", new Vector3(0, 1f, -17.5f), new Vector3(50f, 2f, 0.3f)); AddWallCollider(root, "Col_S", new Vector3(0, 1f, 5.5f), new Vector3(50f, 2f, 0.3f)); AddWallCollider(root, "Col_EE", new Vector3(23f, 1f, WING_Z), new Vector3(0.3f, 2f, WING_D)); AddWallCollider(root, "Col_WW", new Vector3(-23f, 1f, WING_Z), new Vector3(0.3f, 2f, WING_D)); } void MakePlane(GameObject parent, string name, Vector3 pos, Vector3 euler, float w, float d, Material mat) { var go = GameObject.CreatePrimitive(PrimitiveType.Plane); go.name = name; go.transform.SetParent(parent.transform); go.transform.position = pos; go.transform.eulerAngles = euler; go.transform.localScale = new Vector3(w * 0.1f, 1f, d * 0.1f); Destroy(go.GetComponent()); if (mat != null) go.GetComponent().sharedMaterial = mat; } void AddWallCollider(GameObject parent, string name, Vector3 pos, Vector3 size) { var go = new GameObject(name); go.transform.SetParent(parent.transform); go.transform.position = pos; var col = go.AddComponent(); col.size = size; } // ── Lighting ────────────────────────────────────────────────────────────── void SetupLighting() { RenderSettings.ambientMode = UnityEngine.Rendering.AmbientMode.Flat; RenderSettings.ambientLight = new Color(0.9f, 0.9f, 0.9f); if (mainDirectionalLight != null) { mainDirectionalLight.intensity = 0.3f; mainDirectionalLight.color = Color.white; mainDirectionalLight.shadows = LightShadows.None; mainDirectionalLight.transform.eulerAngles = new Vector3(60, -20, 0); } // Track lights grid covering all three zones SpawnTrackLights(); } void SpawnTrackLights() { // Central hall float[] hx = { -6f, -2f, 2f, 6f }; float[] hz = { -14f, -10f, -6f, -2f, 2f }; foreach (var x in hx) foreach (var z in hz) AddPointLight(x, CEILING_H - 0.2f, z, 1.0f, 7f); // East wing float[] ex = { 15f, 18f, 21f }; float[] ez = { -14f, -8f, -2f, 3f }; foreach (var x in ex) foreach (var z in ez) AddPointLight(x, CEILING_H - 0.2f, z, 0.9f, 6f); // West wing float[] wx = { -15f, -18f, -21f }; foreach (var x in wx) foreach (var z in ez) AddPointLight(x, CEILING_H - 0.2f, z, 0.9f, 6f); } void AddPointLight(float x, float y, float z, float intensity, float range) { var go = new GameObject("TL"); go.transform.SetParent(transform); go.transform.position = new Vector3(x, y, z); var l = go.AddComponent(); l.type = LightType.Point; l.color = new Color(1f, 0.97f, 0.92f); l.intensity = intensity; l.range = range; l.shadows = LightShadows.None; } // ── Manifest loading ────────────────────────────────────────────────────── IEnumerator LoadManifest() { string path = System.IO.Path.Combine(Application.streamingAssetsPath, "manifest.json"); #if UNITY_WEBGL && !UNITY_EDITOR using var req = UnityWebRequest.Get(path); yield return req.SendWebRequest(); if (req.result != UnityWebRequest.Result.Success) { Debug.LogError($"[HOY] Manifest load failed: {req.error}"); yield break; } SpawnArtworks(JsonUtility.FromJson(req.downloadHandler.text)); #else if (!System.IO.File.Exists(path)) { Debug.LogError($"[HOY] manifest.json not found: {path}"); yield break; } SpawnArtworks(JsonUtility.FromJson(System.IO.File.ReadAllText(path))); yield return null; #endif } void SpawnArtworks(GalleryManifest manifest) { if (manifest?.artworks == null) return; foreach (var a in manifest.artworks) { if (a.file.Contains("NEEDS_MAPPING")) { Debug.LogWarning($"[HOY] Skipping unmapped artwork: spatialId={a.spatialId}"); continue; } var pos = new Vector3(a.position.x, a.position.y, a.position.z); var rot = Quaternion.Euler(0, a.rotationY, 0); var go = Instantiate(artworkFramePrefab, pos, rot, transform); go.name = $"Frame_{a.spatialId}_{a.title}"; go.transform.localScale = Vector3.one * a.scale; var frame = go.GetComponent(); frame?.Initialise(a); } Debug.Log($"[HOY] Spawned artworks from Spatial coordinates."); } } // ── Data classes ────────────────────────────────────────────────────────────── [Serializable] public class GalleryManifest { public ArtworkData[] artworks; } [Serializable] public class ArtworkData { public string spatialId; public string title; public string artist; public string type; // "video" | "image" public string file; // "Videos/xxx.mp4" | "Images/xxx.jpeg" public SpatialPosition position; public float rotationY; public float scale; public string comment; // "NEEDS_FILE" or "" } [Serializable] public class SpatialPosition { public float x, y, z; }