using System;
using System.Threading.Tasks;
using MyInfoMate.Vr.Net;
using UnityEngine;
using UnityEngine.Video;
namespace MyInfoMate.Vr.Menu
{
///
/// Le fond immersif d'une visite — le reste de l'item E5, spécifié au §4 de
/// DOCS/v2/immersif-frontiere-plan.md.
///
/// Sans lui, le menu flotte dans le noir. C'est le défaut d'Unity : une scène
/// vide est un vide, et un visiteur qui met le casque se retrouve devant six panneaux
/// suspendus dans le néant. Avec le panorama du lieu derrière, il est dans le
/// musée avant d'avoir choisi quoi que ce soit.
///
/// Trois différences avec , et elles tiennent toutes à la même
/// chose — un fond n'est pas un contenu :
///
/// - on ne le choisit pas et on n'en sort pas : aucun panneau de retour ;
/// - il reste en place tant que la visite dure, y compris entre deux sections ;
/// - il s'efface devant une 360 ouverte, puis revient — SkyboxView restaure
/// le ciel précédent en se détruisant, et le ciel précédent, c'est celui-ci.
///
///
/// Son absence est un cas nominal : un lieu sans fond garde le noir, sans un mot.
///
public class ImmersiveBackdrop : MonoBehaviour
{
const int VideoWidth = 4096;
const int VideoHeight = 2048;
Material _sky;
Texture2D _image;
VideoPlayer _player;
RenderTexture _target;
///
/// Pose le fond de cette visite, s'il y en a un. Rend le composant créé, ou null —
/// pas de fond n'est pas une erreur.
///
public static async Task ApplyAsync(
ConfigurationExport export, ApiClient client)
{
var background = export?.ImmersiveBackground;
if (background == null || string.IsNullOrEmpty(background.ResourceUrl)) return null;
// Une scène 3D en fond est du ressort du viewer de scène, pas du ciel : la
// rendre ici donnerait un décor plaqué sur une sphère. Le §4 la prévoit, E7
// sait la charger — le jour où on les branche, c'est ici que ça se décide.
if (background.Kind == ConfigurationExport.ImmersiveBackgroundKind.Scene3D)
{
Debug.Log("[Backdrop] Fond de type Scène 3D : pas encore rendu en fond, " +
"le menu reste sur le ciel par défaut.");
return null;
}
var root = new GameObject("ImmersiveBackdrop");
var backdrop = root.AddComponent();
await backdrop.LoadAsync(background, client);
return backdrop;
}
async Task LoadAsync(ConfigurationExport.Backdrop background, ApiClient client)
{
_sky = PanoramicSky.CreateMaterial();
if (_sky == null) return;
if (background.Kind == ConfigurationExport.ImmersiveBackgroundKind.Video360)
PlayVideo(background, client);
else
await ShowImage(background, client);
}
async Task ShowImage(ConfigurationExport.Backdrop background, ApiClient client)
{
var texture = await PanoramicSky.LoadEquirectangularAsync(client, background.ResourceUrl);
if (texture == null || this == null) return;
_image = texture;
_sky.SetTexture("_MainTex", texture);
RenderSettings.skybox = _sky;
}
///
/// ⚠️ Une vidéo en fond tourne en permanence. Sur une borne qui fonctionne
/// huit heures, c'est un décodeur matériel occupé toute la journée et une batterie
/// qui descend plus vite — à mesurer avant de la vendre comme le cas normal. Le son
/// est coupé : un fond qui parle par-dessus le commentaire d'une section serait
/// intenable.
///
void PlayVideo(ConfigurationExport.Backdrop background, ApiClient client)
{
_target = new RenderTexture(VideoWidth, VideoHeight, 0);
_player = gameObject.AddComponent();
_player.source = VideoSource.Url;
_player.renderMode = VideoRenderMode.RenderTexture;
_player.targetTexture = _target;
_player.isLooping = true;
_player.audioOutputMode = VideoAudioOutputMode.None;
_player.errorReceived += (_, message) =>
Debug.LogError($"[Backdrop] Lecture vidéo impossible : {message}");
_sky.SetTexture("_MainTex", _target);
RenderSettings.skybox = _sky;
StartPlayback(background.ResourceUrl, client);
}
async void StartPlayback(string url, ApiClient client)
{
var path = await ContentCache.MediaPathAsync(client, url);
if (path == null || this == null || _player == null) return;
_player.url = new Uri(path).AbsoluteUri;
_player.Play();
}
void OnDestroy()
{
// Le ciel est un réglage global. On ne le remet à null que s'il est encore le
// nôtre : une 360 ouverte par-dessus a déjà pris la place, et lui arracher son
// ciel en repassant par ici la laisserait dans le noir.
if (RenderSettings.skybox == _sky) RenderSettings.skybox = null;
if (_player != null) _player.Stop();
if (_target != null) _target.Release();
if (_image != null) Destroy(_image);
}
}
}