using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
namespace MyInfoMate.Vr.Scene
{
///
/// Un personnage posé dans la scène : GLB chargé au runtime, animation idle en
/// boucle, orientation vers le visiteur.
///
/// Pas de lip sync, jamais — écarté au niveau produit. Un personnage stylisé qui
/// respire et regarde dans la bonne direction porte l'essentiel de la présence ;
/// une bouche mal synchronisée la détruit.
///
public class PersonaInstance : MonoBehaviour
{
[SerializeField] Transform lookAtTarget;
[SerializeField] bool gazeAtVisitor = true;
[SerializeField] float gazeDegreesPerSecond = 90f;
Quaternion _restRotation;
public async Task LoadAsync(string url, Transform visitorHead)
{
lookAtTarget = visitorHead;
_restRotation = transform.localRotation;
var model = await GltfLoader.LoadAsync(url, transform, "Model");
if (model == null) return false;
PlayIdle(model);
return true;
}
///
/// ⚠️ glTFast expose les animations de deux façons selon
/// GltfImportSettings.AnimationMethod : Legacy (composant
/// , le défaut) ou Mecanim (un Animator et
/// son controller). Ce code suit le défaut ; si le personnage reste figé alors
/// que le GLB contient bien des clips, c'est le premier endroit à regarder.
///
/// On joue le clip nommé
/// « idle » s'il existe, sinon le premier — un pack d'animations tiers ne
/// respecte aucune convention de nommage, et refuser de jouer parce que le
/// nom ne colle pas donnerait un personnage figé sans message d'erreur.
///
void PlayIdle(GameObject model)
{
var animation = model.GetComponentInChildren();
if (animation == null) return;
var clip = animation.Cast()
.FirstOrDefault(s => s.name.ToLowerInvariant().Contains("idle"))
?? animation.Cast().FirstOrDefault();
if (clip == null)
{
Debug.LogWarning($"[Persona] {name} : aucune animation dans le GLB");
return;
}
clip.wrapMode = WrapMode.Loop;
animation.Play(clip.name);
}
void Update()
{
if (!gazeAtVisitor || lookAtTarget == null) return;
// Rotation sur Y seulement : un personnage qui bascule la tête vers un
// visiteur assis ou grand donne un effet de pantin.
var direction = lookAtTarget.position - transform.position;
direction.y = 0f;
if (direction.sqrMagnitude < 0.01f) return;
transform.rotation = Quaternion.RotateTowards(
transform.rotation,
Quaternion.LookRotation(direction),
gazeDegreesPerSecond * Time.deltaTime);
}
public void ReturnToRest() => transform.localRotation = _restRotation;
}
}