using System.Collections.Generic; using System.Linq; using Manager.DTOs; using ManagerService.DTOs; using static ManagerService.Data.SectionText; namespace ManagerService.Data.SubSection { /// Objet manipulé ou décor habité — voir . public enum Scene3DMode { // ⚠️ Persisté en int : toute valeur nouvelle s'ajoute à la fin. Asset = 0, Scene = 1 } /// /// Une scène 3D : un modèle glTF et ses points d'intérêt — lot XR-4, /// items E5 (le décor) et E7 (l'objet). /// /// Nommé « Scene3D » et pas « Model3D » alors qu'il ne porte aujourd'hui qu'un /// modèle et des points : le discriminateur TPH est une chaîne persistée en base. /// Le renommer tant que rien n'est en production coûte zéro ; le renommer après la /// première maquette livrée coûte une migration de données. Les objets posés, les /// personnages et la navigation prévus au §S4 de la conception viendront ici, en /// colonnes nullables — pas dans un second type à faire cohabiter. /// /// Pourquoi un type à part et pas une augmentée : une /// maquette n'est pas une carte. Les deux se ressemblent parce qu'elles portent des /// points d'intérêt, mais une carte a un fond cartographique, un zoom, un fournisseur /// et des coordonnées terrestres — dont une maquette n'a que faire. Tordre la Map /// aurait donné un type qui ment sur la moitié de ses champs. /// /// Ce qu'on réutilise en revanche : le . Il portait déjà /// deux rattachements (SectionMapId, SectionEventId) ; un troisième suit /// le même patron. Un point garde ainsi son titre, sa description, son image, ses /// contenus audio et son multilingue — et l'éditeur de points du back-office avec. /// Seule sa change de nature. /// public class SectionScene3D : Section { /// Ressource Model3D (glTF binaire) affichée par cette section. public string Model3DResourceId { get; set; } /// /// Ce que le visiteur fait de cette scène — et c'est une opposition franche /// (§4bis de DOCS/v2/immersif-frontiere-plan.md, arrêté le 11/09) : /// /// /// Asset — un objet posé devant lui, qu'il manipule : caméra /// orbitale, on tourne autour, les points tournent avec l'objet. L'épée du /// roi, une pièce de collection. /// Scene — un décor dans lequel il est : caméra fixe, on /// regarde autour, les points restent où ils sont. Une salle reconstituée. /// /// /// Même GLB, même modèle de point, même éditeur : ce qui change, c'est où l'on met /// le visiteur — et aucun fichier ne peut le deviner, d'où ce champ. /// public Scene3DMode Mode { get; set; } = Scene3DMode.Asset; /// /// URL du modèle, dupliquée comme l'est à côté /// de . Ce n'est pas de la redondance gratuite : le /// viewer et le casque ont besoin de l'URL sans faire de jointure, et l'export /// hors ligne la réécrit en chemin local. /// public string Model3DSource { get; set; } /// /// Points posés sur le modèle. Même collection que celle d'une Map, même éditeur, /// même contenu — seule la position diffère. /// public List Points { get; set; } public override string GetEmbeddableText(string language) => JoinText(new[] { BaseText(language) } .Concat((Points ?? new List()) .Select(p => p.GetEmbeddableText(language))) .ToArray()); /// /// Le modèle compte parmi les ressources de la visite : sans lui, un téléchargement /// hors ligne rapporterait les points d'une maquette absente. /// public override IEnumerable GetReferencedResourceIds(string language = null) => BaseResourceIds() .Concat(ResourceId(Model3DResourceId)) .Concat((Points ?? new List()) .SelectMany(p => p.GetReferencedResourceIds())); public Scene3DDTO ToDTO() { return new Scene3DDTO() { id = Id, label = Label, title = Title.ToList(), description = Description.ToList(), order = Order, type = Type, imageId = ImageId, imageSource = ImageSource, configurationId = ConfigurationId, isSubSection = IsSubSection, parentId = ParentId, isActive = IsActive, dateCreation = DateCreation, instanceId = InstanceId, latitude = Latitude, longitude = Longitude, meterZoneGPS = MeterZoneGPS, isBeacon = IsBeacon, beaconId = BeaconId, model3DResourceId = Model3DResourceId, model3DSource = Model3DSource, mode = Mode, points = (Points ?? new List()).Select(p => p.ToDTO()).ToList() }; } } }