Référence technique
Créer un paquet Locus Package v2 complet
Sur cette page
- Commencer avec un paquet complet fonctionnel
- Structure de l’archive
- locusplace.json
- Référence des champs de l’enveloppe
- Métadonnées Package-v2 dans le ZIP
- Vue : destination.json
- Pièce : space.json
- Experience : experience.json
- Capacités facultatives et libellés traduits
- Provenance obligatoire
- Sécurité et limites de ressources
- Acquisition par fichiers et URL
- Vérification des paquets et exemples
Statut : contrat canonique de publication, stockage, composition et importation complète. À partir de Locus 1.2.1, cette archive peut être importée depuis Fichiers ou une URL HTTPS publique, pour les Vues comme pour les pièces. L’ancien format à plat décrit dans la
locus-asset-format.md reste une entrée de compatibilité.
Un .zip ou .locusplace est une archive ZIP ordinaire contenant un ou plusieurs objets Package-v2 Destination (Vues), Space (pièces) et Experience. Elle constitue l’enveloppe sécurisée de publication et d’intégrité utilisée par Locus et par les paquets portables complets. La racine du ZIP contient locusplace.json et
catalog/ directement ; ne les placez pas dans un dossier parent supplémentaire.
La vérification se fonde sur les octets de l’archive et les manifestes, jamais sur l’extension. Un paquet complet conserve packageID, les identifiants de contenu,
contentVersion et ses Experiences prises en charge ; réimporter la même empreinte de contenu est une opération idempotente sans effet. Les outils d’empaquetage et de vérification maintenus créent et contrôlent exactement ce format.
Cette enveloppe contient uniquement des données, pas des extensions exécutables. Dans une même version binaire de Locus, chaque capacité Package-v2 utilisée à l’exécution a le même sens, que l’archive soit intégrée, téléchargée depuis la Bibliothèque ou importée depuis Fichiers/URL. L’origine peut modifier les droits d’accès, le quota d’importation, l’autorité de mise à jour, le stockage, la suppression et les libellés, mais ne débloque jamais un autre schéma Pièce/Vue ni des fonctions de rendu différentes. Scripts, modules exécutables, bibliothèques dynamiques et fichiers exécutables sont exclus. Toutes les origines conservent les mêmes contrôles de confinement des chemins, d’intégrité, de ressources, de structure décodée, de fermeture des références et de version minimale.
Ce guide décrit l’enveloppe complète et les manifestes de base. La référence des champs documente les capacités facultatives des Vues et pièces, leurs ressources, plages, valeurs par défaut et versions minimales. Le schéma de l’enveloppe et schéma de provenance décrivent la structure JSON ; l’outil d’empaquetage et le validateur vérifient aussi les octets des fichiers et les règles entre champs. Un JSON Schema seul ne peut pas vérifier une archive ni garantir son confort sur Apple Vision Pro.
Commencer avec un paquet complet fonctionnel
Clonez ou téléchargez le dépôt public des outils. Exécutez ces commandes depuis la racine du dépôt sous macOS, avec Python 3.9 ou une version ultérieure et une installation Xcode fournissant
xcrun usdchecker et xcrun usdcat. La vérification de base utilise la bibliothèque standard Python,
/usr/bin/sips pour le décodage complet des images et les outils Xcode usdchecker et usdcat
pour les modèles. Les vérifications avancées des nuages, des réflexions/contacts de l’eau et des ciels sans soleil nécessitent aussi NumPy et Pillow : python3 -m pip install numpy pillow.
python3 examples/locusplace/build_examples.py /absolute/path/to/locusplace-examples
python3 tools/validate_locusplace.py --current-app-version 1.4.4 /absolute/path/to/locusplace-examples/view-only.locusplace
python3 tools/validate_locusplace.py --current-app-version 1.4.4 /absolute/path/to/locusplace-examples/room-only.locusplace
python3 tools/validate_locusplace.py --current-app-version 1.4.4 /absolute/path/to/locusplace-examples/combined.locusplace
Le dossier cible doit être absent ou vide. Ces petits exemples synthétiques servent à vérifier le format ; le dégradé et le cube ne constituent pas un paysage ou une pièce de qualité finale. Leur code source et les contenus générés sont sous licence Apache-2.0. Voir les instructions des exemples.
--current-app-version indique la version de Locus visée par la validation; cela ne met pas à jour l’application installée et ne lui ajoute aucune capacité. Les outils utilisent par prudence la valeur par défaut 1.0.0; indiquez explicitement la version visée. L’importation complète exige Locus 1.2.1 ou une version ultérieure ; chaque champ récent a sa propre version minimale. Les réglages du sol et des éléments intermédiaires des pièces exigent un build compatible à partir de 1.4.4. Ce guide ne prétend pas qu’un tel build est déjà disponible sur l’App Store.
Pour votre propre paquet, décompressez un exemple dans un dossier éditable ou créez la structure ci-dessous. Remplacez les ressources et leur provenance, choisissez vos propres identifiants stables et créez la sortie à l’extérieur de ce dossier :
python3 tools/pack_locusplace.py --current-app-version 1.4.4 MyEditablePlace MyPlace.zip
python3 tools/validate_locusplace.py --current-app-version 1.4.4 --json MyPlace.zip
L’outil d’empaquetage recalcule files et contentHash, vérifie l’archive, puis crée le fichier de sortie uniquement si la vérification réussit. Il refuse d’écraser une sortie existante et ne modifie pas les sources éditables. Importez le résultat via Lieux → Importer → Importer une Vue ou Importer une pièce → Fichiers, en choisissant une entrée correspondant à un type de contenu présent dans le paquet. Un paquet combiné installe ses contenus ensemble. Une URL HTTPS publique pointant directement vers le ZIP convient aussi. Vérifiez chaque siège, effet, lumière et son sur Apple Vision Pro avant de partager un lieu terminé.
Structure de l’archive
example.locusplace
├── locusplace.json
└── catalog/
├── destinations/<destination-id>/
│ ├── destination.json
│ ├── provenance.json
│ └── ...
├── spaces/<space-id>/
│ ├── space.json
│ ├── provenance.json
│ └── ...
└── experiences/<experience-id>/
└── experience.json
Tout élément de l’archive qui n’est pas un dossier, à l’exception du fichier locusplace.json doit être déclaré dans files. Chaque fichier déclaré doit exister. L’archive ne peut contenir que les trois collections de catalogue prévues, et chaque fichier doit appartenir à un identifiant de paquet déclaré dans contents. Les manifestes Package-v2 restent compatibles avec des champs futurs inconnus. Les champs inconnus de l’enveloppe, les versions de format non prises en charge, les collections et les modes de contenu sont toutefois rejetés par sécurité. La provenance suit son schéma publié.
Une archive contenant uniquement une Vue ou une pièce installe un contenu de catalogue réutilisable. Après résolution du catalogue, Locus crée les associations par défaut : chaque Vue avec Votre pièce, puis chaque Vue avec chaque pièce possédant une ouverture de Vue. Une Experience explicite remplace uniquement son association désignée et peut référencer le même paquet ou un identifiant stable déjà présent dans le catalogue intégré ou importé.
locusplace.json
{
"formatVersion": 1,
"packageID": "place.example-combined",
"contentVersion": "1.0.0",
"minimumAppVersion": "1.0.0",
"contents": {
"destinationIDs": ["destination.example-view"],
"spaceIDs": ["space.example-room"],
"experienceIDs": ["experience.example-combined"]
},
"files": [
{
"path": "catalog/destinations/destination.example-view/destination.json",
"byteCount": 214,
"sha256": "<64 lowercase hex characters>"
}
],
"contentHash": "<64 lowercase hex characters>"
}
packageID et tous les identifiants de contenu sont des identifiants ASCII stables de 128 octets au maximum. Ils commencent par une lettre ou un chiffre, puis n’utilisent que des lettres, des chiffres,
., _et -. Une version comporte exactement trois composantes numériques. Une mise à jour dont les octets changent doit conserver les mêmes identifiants de contenu déclarés et augmenter contentVersion. Importer de nouveau le même contentHash est une opération idempotente sans effet.
contentHash est le SHA-256 de tous les enregistrements de fichiers triés par chemin Unicode. Chaque enregistrement contribue ces octets UTF-8 :
path NUL decimal-byteCount NUL lowercase-sha256 NUL
Cette empreinte ne dépend ni des espaces du JSON ni de l’ordre des éléments du ZIP. Le validateur lit aussi chaque fichier déclaré en flux et vérifie sa taille réelle, son CRC ZIP et son SHA-256.
Référence des champs de l’enveloppe
| Champ | Obligatoire | Signification |
|---|---|---|
formatVersion |
oui | Entier 1; les versions inconnues de l’enveloppe sont rejetées par sécurité. |
packageID |
oui | Identité stable de ce flux d’installation et de mises à jour ; ce n’est pas un nom de fichier. |
contentVersion |
oui | Trois composantes numériques, par exemple 1.2.0; augmentez-la lorsque les octets changent sous les mêmes identifiants. |
minimumAppVersion |
oui | Version minimale de Locus autorisée à importer l’archive, avec trois composantes numériques également. |
contents.destinationIDs |
oui | Identifiants uniques de Vue appartenant à ce ZIP ; utilisez [] lorsqu’il n’y en a aucun. |
contents.spaceIDs |
oui | Identifiants uniques de pièce appartenant à ce ZIP ; utilisez [] lorsqu’il n’y en a aucun. |
contents.experienceIDs |
oui | Identifiants uniques d’Experience appartenant à ce ZIP ; utilisez [] lorsqu’il n’y en a aucun. |
files |
oui | Inventaire généré complet de chaque fichier de contenu, à l’exception de locusplace.json. |
files[].path |
oui | Chemin canonique relatif à l’archive, sous catalog/... . |
files[].byteCount |
oui | Nombre exact d’octets décompressés. |
files[].sha256 |
oui | SHA-256 des octets décompressés, sous forme de 64 caractères hexadécimaux minuscules. |
contentHash |
oui | Identité SHA-256 de l’inventaire canonique décrit ci-dessus. |
Métadonnées Package-v2 dans le ZIP
L’enveloppe complète utilise formatVersion: 1; chaque destination.json,
space.json et experience.json utilise formatVersion: 2. Les numéros de version de pièce v1–v10 figurant dans la référence du format de compatibilité à plat
appartiennent à l’entrée distincte à plat space.json en entrée. Ne modifiez pas un manifeste complet
space.json vers la version 10 pour utiliser les couches d’éclairage précalculé. Dans les paquets complets, les capacités facultatives sont activées par leurs champs typés et protégées par
minimumAppVersion.
Tous les chemins de ressources sont relatifs au dossier de leur manifeste, et non à la racine du ZIP. Chaque référence doit désigner un fichier ordinaire dans ce paquet. Les manifestes de base Package-v2 permettent des métadonnées compatibles avec des versions futures ; certains profils de capacité peuvent toutefois imposer une liste fermée de champs. Ne supposez pas qu’un champ inconnu sera rendu. L’enveloppe externe rejette les champs inconnus et la provenance doit respecter son schéma publié.
Les coordonnées sont en mètres, dans un repère direct, avec +Y vers le haut et −Z vers l’avant. L’ordre des quaternions est
[x, y, z, w]. Une transformation peut contenir translationMeters (trois nombres finis), orientationXYZW (quatre nombres finis formant un quaternion de longueur non nulle), et
scale (trois nombres finis strictement positifs).
Vue : destination.json
{
"formatVersion": 2,
"id": "destination.example-view",
"title": "Example View",
"panorama": {
"path": "panorama.jpg",
"projection": "equirectangular",
"width": 8192,
"height": 4096,
"initialYawDegrees": 0
},
"provenance": "provenance.json"
}
| Champ | Obligatoire | Signification et vérification |
|---|---|---|
formatVersion |
oui | Entier 2. |
id |
oui | Identifiant stable de Vue, identique à celui du dossier et de l’enveloppe. |
title |
oui | Nom de Vue non vide destiné aux visiteurs. |
panorama.path |
oui | JPEG ou PNG final relatif au paquet ; le type dépend des octets décodés, pas seulement de l’extension. |
panorama.projection |
oui | Exactement equirectangular. |
panorama.width, height |
oui | Dimensions décodées positives, exactement conformes aux valeurs déclarées, avec width == 2 * height. |
panorama.initialYawDegrees |
non | Rotation finie dans le repère canonique du panorama ; valeur par défaut : 0. |
sourceImage |
non | Image JPEG/PNG de contrôle facultative, référencée par un chemin relatif au paquet. Elle n’est pas rendue, mais un paquet complet peut la conserver comme donnée typée, décodée et soumise aux limites de ressources. Les sources éditables/DCC restent hors du ZIP d’exécution. |
thumbnail |
non | Image JPEG/PNG du catalogue, relative au paquet. |
caption |
non | Description lisible par une personne. |
generation.generator |
non | Nom exact du générateur ou de l’outil utilisé. |
generation.model |
non | Version facultative du modèle ou du processus de création. |
generation.generatedAt |
non | Horodatage facultatif ; RFC 3339 UTC est recommandé. |
depthLayers[] |
non dans Package v2 ; non pris en charge dans les ZIP publics importés | Ressources de profondeur ordonnées réservées. L’importation publique rejette ce champ tant que le moteur et le validateur d’octets ne prennent pas en charge son encodage. |
midground.path |
non | USDZ d’éléments intermédiaires rendu dans les deux modes d’expérience ; il suit les mêmes règles de contenu, de structure et de ressources pour les paquets intégrés, de la Bibliothèque ou importés. Voir la référence des éléments intermédiaires. |
midground.transform |
non | Transformation finie facultative pour midground.path. |
midground.terrainEntities |
non | Noms uniques et non vides des unités de terrain dans midground.path; le moteur les utilise pour ajuster le terrain sous l’emprise de la pièce. |
midground.water |
non | Déclaration d’eau rendue par l’application, avec masque/carte de normales validés et données de rive facultatives décrits dans la référence des champs. |
sky |
non | Ciel nocturne généré par l’application (étoiles, Voie lactée, aurores facultatives), affiché à travers un masque en niveaux de gris au format 2:1 validé sky.mask; exige minimumAppVersion >= 1.2.4. Voir référence du ciel. |
gpuTexture, gpuTextures |
non | Fichiers associés ASTC KTX1 validés, utilisables aussi bien dans les ZIP d’utilisateurs que dans les paquets de la Bibliothèque. Le fichier associé au panorama v1 exige 1.2.3; le tableau d’images sources v2 exige 1.3.0. Les empreintes des sources, les dimensions et les niveaux de mipmaps doivent correspondre. |
water, clouds, fireflies |
non | Déclarations d’eau à l’exécution, y compris la lueur, de mouvement de nuages peints et de lucioles ; exigent minimumAppVersion >= 1.3.0. Les ressources référencées et leurs limites suivent le contrat commun Package-v2. |
environment.sunPath |
non | Ciel créé sans soleil et disque solaire masqué facultatif ; exige 1.3.0 et son attestation de création. |
celestial |
non | Corps céleste animé et textures référencées ; exige 1.2.4, ou la version minimale supérieure imposée par ses fonctions facultatives (le mouvement et l’évolution de la couche nuageuse exigent 1.3.0). |
nearOrbit |
non | Profils d’orbite proche, ressources de texture et preuve de panorama de secours liée au contenu. v1 et v2 exigent 1.3.0; v2 inclut le manifeste des tuiles, leurs images et leurs fichiers GPU associés. |
splat.variants, splat.ply |
non dans Package v2 ; non pris en charge dans les ZIP publics importés | Références de ressources futures réservées. Le moteur splat a été supprimé ; l’importation publique rejette donc tout le champ. |
splat.metricScaleFactor |
non dans Package v2 ; non pris en charge dans les ZIP publics importés | Échelle finie et positive dans le contrat réservé. |
splat.groundPlaneOffset |
non dans Package v2 ; non pris en charge dans les ZIP publics importés | Décalage de sol fini défini par l’auteur dans le contrat réservé. |
splat.placement |
non dans Package v2 ; non pris en charge dans les ZIP publics importés | Transformation partagée facultative dans le contrat réservé. |
environment |
non | Luminosité de la Vue visible, petite image SDR facultative pour l’éclairage de la pièce et réglages de soleil direct ci-dessous. |
audio.path |
non dans Package v2 ; non pris en charge dans les ZIP publics importés | Fichier audio d’exécution réservé. L’importation publique rejette ce champ tant que la vérification complète des octets audio et leur lecture ne sont pas prises en charge. |
audio.loops |
avec audio Package-v2 ; non pris en charge dans les ZIP publics importés | Booléen contrôlant la répétition dans le contrat réservé. |
provenance |
oui pour les imports | JSON de provenance relatif au paquet. |
Les ZIP complets acceptent les champs réellement utilisés par cette application et les métadonnées explicitement conservées, telles que sourceImage, à condition de pouvoir vérifier le type des données et leur respect des limites de ressources. midground fait partie du contrat commun à l’exécution et exige minimumAppVersion >= 1.2.1.
depthLayers, splatet l’ancien bloc audio restent réservés ou retirés sans consommateur actif à l’exécution. Cette application les rejette donc quelle que soit leur origine, au lieu d’accepter des chemins sans contrôle de type. Lorsqu’une version future implémentera une de ces capacités, elle devra ajouter ses références de ressources, ses contrôles de type et de limites, son consommateur et sa version minimale au contrat commun, sans interrupteur distinct pour les imports publics.
12 288×6 144 (12K) est recommandé, sans être obligatoire. Des résolutions inférieures comme 8 192×4 096, 6 144×3 072 ou 4 096×2 048 sont valides si elles respectent strictement le rapport 2:1 et déclarent leurs dimensions décodées réelles. N’agrandissez pas une petite source pour annoncer 12K : cela n’ajoute aucun détail et gaspille de la mémoire.
Les coordonnées du panorama sont fixes : u = 0.5 correspond à l’angle 0 et à la direction initiale avant/monde −Z ; u augmente vers l’est, en direction de +X ; la couture se trouve à u = 0/1, à ±180° ; la première ligne de l’image représente le zénith. initialYawDegrees fait tourner l’image dans ce repère sans le redéfinir.
Éclairage facultatif d’une Vue :
{
"environment": {
"imageBasedLight": "thumbnail.jpg",
"skyGainEV": 0,
"exposureEV": 0,
"horizonPitchDegrees": 0,
"colorGrade": {"contrast": 1.15, "saturation": 1.2},
"directSun": {"enabled": false, "illuminanceLux": 5000}
}
}
| Champ | Signification |
|---|---|
imageBasedLight |
JPEG/PNG distinct au format 2:1, utilisé uniquement pour éclairer la géométrie et les réflexions de la pièce. Il ne doit pas réutiliser panorama.path; 1024×512 est recommandé. En l’absence d’image, un éclairage neutre de secours est utilisé. |
skyGainEV |
Gain EV fini appliqué uniquement au panorama visible. |
exposureEV |
Gain EV fini appliqué uniquement à l’IBL. |
horizonPitchDegrees |
Correction d’horizon finie définie par l’auteur. |
colorGrade.contrast |
Contraste facultatif du panorama SDR visible, de 0.5 à 2.0; 1.0 est neutre. |
colorGrade.saturation |
Saturation facultative du panorama SDR visible, de 0.0 à 2.0; 1.0 est neutre. |
directSun.enabled |
Indique si une lumière directionnelle distincte est définie. |
directSun.azimuthDegrees, elevationDegrees |
Angles de direction finis, tous deux obligatoires lorsque le soleil est activé. |
directSun.illuminanceLux |
Éclairement fini non négatif. |
L’étalonnage affecte uniquement le panorama SDR affiché. Il réutilise cette texture sans convertir la Vue en HDR ni recréer des détails absents des hautes lumières de la source. Un soleil désactivé peut conserver une valeur de 5000 lux afin que Ajouter le soleil démarre avec la valeur par défaut de l’application, sans modifier son état désactivé défini dans le paquet.
visibleSkyHDR et hdrManifest sont retirés et rejetés. Un EXR hors de l’USDZ ne peut pas servir de Vue visible ni d’image d’éclairage de la pièce. OpenEXR reste autorisé comme texture de pièce dans l’USDZ et y est vérifié.
Pièce : space.json
{
"formatVersion": 2,
"id": "space.example-room",
"title": "Example Room",
"scene": "scene.usdz",
"thumbnail": "thumbnail.jpg",
"caption": "A quiet house arranged around a sunlit courtyard.",
"previewCamera": {
"yawDegrees": -28,
"pitchDegrees": -10,
"zoom": 1.0
},
"seatedOrigin": {
"translationMeters": [0, 0, 0],
"orientationXYZW": [0, 0, 0, 1]
},
"safeHeadVolume": {
"centerMeters": [0, 1.2, 0],
"sizeMeters": [1.2, 0.6, 1.2]
},
"destinationOpenings": [
{
"id": "front",
"transform": {
"translationMeters": [0, 1.2, -2],
"orientationXYZW": [0, 0, 0, 1]
},
"widthMeters": 4,
"heightMeters": 2.5
}
],
"collision": {"mode": "embedded"},
"teleportCatalog": {"path": "teleport-points.json", "houseID": "example-room"},
"quality": {
"status": "unverified",
"triangleCount": 0,
"materialCount": 0,
"entityCount": 0,
"maxTextureDimension": 0
},
"provenance": "provenance.json"
}
| Champ | Obligatoire | Signification et vérification |
|---|---|---|
formatVersion |
oui | Entier 2. |
id |
oui | Identifiant stable de pièce, identique à celui du dossier et de l’enveloppe. |
title |
oui | Nom de pièce non vide destiné aux visiteurs. |
scene |
oui | USDZ autonome relatif au paquet. .blend et les autres fichiers sources de création ne sont pas des substituts et ne peuvent pas être inclus ailleurs dans le paquet. |
thumbnail |
non | JPEG ou PNG décodé affiché sur la carte de la pièce, relatif au paquet. S’il est déclaré, il doit exister, être entièrement décodable et respecter les limites d’images partagées de l’archive ; omettez-le pour utiliser l’icône de secours. |
caption |
non | Texte d’affichage non vide pour À propos de cette pièce, dans une limite de 2 000 octets UTF-8. |
previewCamera.yawDegrees, pitchDegrees |
non | Angles initiaux finis pour l’aperçu 3D délimité. Ils ne modifient pas le placement immersif. |
previewCamera.zoom |
avec caméra | Échelle initiale finie et positive pour l’aperçu 3D délimité. Réinitialiser restaure cette pose définie par l’auteur ; le visualiseur limite la valeur à la plage d’interaction prise en charge. |
seatedOrigin.translationMeters |
oui | Trois valeurs finies en mètres situant le repère de sol assis défini par l’auteur. |
seatedOrigin.orientationXYZW |
oui | Quatre composantes finies d’un quaternion normalisé ; le repère assis doit rester vertical, sans tangage ni roulis. |
safeHeadVolume.centerMeters |
oui | Trois valeurs finies en mètres dans les coordonnées de la pièce. |
safeHeadVolume.sizeMeters |
oui | Trois dimensions finies strictement positives. |
destinationOpenings |
oui | Tableau d’ouvertures rectangulaires. Un import de pièce autonome en exige exactement une ; zéro ou plusieurs ouvertures nécessitent une virtual-space Experience dans la même archive. |
destinationOpenings[].id |
par entrée | Identifiant de paquet unique utilisé pour la composition de l’Experience. |
destinationOpenings[].transform |
par entrée | Pose de position/quaternion obligatoire. |
destinationOpenings[].widthMeters, heightMeters |
par entrée | Dimensions finies strictement positives. |
collision.mode |
oui | embedded ou separate. |
collision.path |
conditionnel | Interdit pour embedded; pour separate, un USDZ autonome référencé par un chemin relatif au paquet est obligatoire. Il suit les mêmes règles de contenu, de textures et de géométrie que scene. Sa géométrie partage les limites de la pièce avec celle de la scène. Renommer un fichier source de création ne le rend pas valide. |
quality.status |
oui | unverified ou verified. |
quality.triangleCount, materialCount, entityCount, maxTextureDimension |
oui | Mesures non négatives fournies par l’auteur. Zéro signifie non mesuré ; les données USD réelles sont vérifiées indépendamment. |
teleportCatalog.path |
oui | JSON de téléportation relatif au paquet. La maison sélectionnée doit contenir au moins un point, sinon la pièce ne peut pas être utilisée en Espace virtuel. La limite JSON de 1 MiB s’applique selon le rôle du champ, même si le nom du fichier ne se termine pas par .json; les variantes de casse ou les alias Unicode du système de fichiers ne contournent pas cette limite. |
teleportCatalog.houseID |
oui | Identifiant sélectionnant un tableau non vide houses dans ce JSON. |
spatialAdaptation.wallEntities |
non | Noms d’entités USD uniques et non vides pour les murs ; ces métadonnées ne les masquent pas. |
spatialAdaptation.roofEntities |
non | Noms uniques et non vides, distincts de ceux des murs. |
spatialAdaptation.deskEntitiesByTeleportID |
non | Correspondance entre un identifiant de téléportation déclaré et un nom non vide d’entité de plateau de bureau créé par l’auteur. |
provenance |
oui | JSON de provenance relatif au paquet. |
L’USDZ utilise les mètres, un repère direct et +Y vers le haut. Il contient uniquement les données USD, textures et sons pris en charge à l’exécution. Ses membres doivent être stockés sans compression. Le validateur charge la scène réelle, résout ses couches et compte la géométrie ; des valeurs faibles ou nulles de quality ne peuvent pas dissimuler un modèle trop volumineux.
JSON de téléportation obligatoire (la maison sélectionnée doit contenir au moins un point) :
{
"houses": {
"example-room": [
{
"id": "work-desk",
"title": "Work desk",
"anchorXZ": [0.5, 0.5],
"sourceFloorOffset": 0,
"eyeHeight": 1.15,
"yawRadians": 0
}
]
}
}
anchorXZ contient deux valeurs finies normalisées comprises dans 0...1; sourceFloorOffset est fini et non négatif dans les unités du modèle créé ; eyeHeight est positif, fini et mesuré en mètres ; yawRadians est une rotation finie du modèle autour de +Y dans le repère du monde. Les identifiants de points sont uniques et les titres non vides.
Experience : experience.json
{
"formatVersion": 2,
"id": "experience.example-combined",
"title": "Example Room · Example View",
"destinationID": "destination.example-view",
"spaceID": "space.example-room",
"supportedModes": ["virtual-space"],
"composition": {
"destinationOpeningID": "front",
"destinationYawDegrees": 0,
"destinationPitchDegrees": 0,
"destinationExposureOffsetEV": 0
},
"compatibility": {
"minimumViewerVersion": "1.0.0",
"requiredCapabilities": ["world-tracking"]
}
}
| Champ | Obligatoire | Signification et vérification |
|---|---|---|
formatVersion |
oui | Entier 2. |
id |
oui | Identifiant stable d’Experience. |
title |
oui | Nom de carte non vide destiné aux visiteurs. |
destinationID |
oui | Identifiant stable de Vue dans ce ZIP ou déjà installé. |
spaceID |
conditionnel | Identifiant stable de pièce, obligatoire pour virtual-space, normalement omis pour room-portal. |
supportedModes |
oui | Tableau non vide sans doublons contenant uniquement virtual-space ou room-portal. |
composition.destinationOpeningID |
conditionnel | Obligatoire pour virtual-space; désigne une ouverture de la pièce référencée. |
composition.destinationYawDegrees |
non | Décalage fini d’orientation du ciel pour cette association. |
composition.destinationPitchDegrees |
non | Décalage fini d’inclinaison du ciel pour cette association. |
composition.destinationExposureOffsetEV |
non | Décalage relatif d’exposition fini pour cette association. |
compatibility.minimumViewerVersion |
non | Chaîne déclarative indiquant la version minimale du visualiseur. |
compatibility.requiredCapabilities |
non | Chaînes déclaratives de capacités. |
Les ZIP contenant une Vue seule ou une pièce seule installent un contenu réutilisable et rejoignent les associations par défaut. Une Experience explicite n’est nécessaire que pour remplacer l’ouverture, l’orientation, l’exposition, le titre, le mode ou la compatibilité d’une association. Les références peuvent être résolues dans la même archive ou parmi les identifiants stables intégrés ou déjà installés ; toute référence non résolue fait échouer l’import entier.
Capacités facultatives et libellés traduits
Ajoutez les champs facultatifs au manifeste de la Vue ou de la pièce qui les possède, et placez chaque ressource référencée à côté de ce manifeste. La référence des champs
couvre les médias et masques, le son et la musique des pièces, les éléments intermédiaires et le vent, l’eau, le ciel, les nuages, la trajectoire solaire, les corps célestes, l’orbite proche, les trous noirs, les lumières ponctuelles, les fichiers de textures GPU associés, les sièges et leurs groupes, les lumières, animations, surfaces vidéo, particules et couches d’éclairage précalculé. Elle définit aussi localizations,
provenanceDetailet les réglages suivants de la pièce : viewGroundFloorOffsetMeters,
viewGroundKeepOutet viewMidgroundVisibility .
Par exemple, ces champs facultatifs s’ajoutent à un manifeste de pièce par ailleurs complet et exigent dans l’enveloppe minimumAppVersion: "1.4.4" ou ultérieure :
{
"viewGroundFloorOffsetMeters": 0.2,
"viewGroundKeepOut": false,
"viewMidgroundVisibility": "terrainOnly"
}
Ils gardent le même sens pour les contenus importés, intégrés et de la Bibliothèque. Les identifiants de capacité de la référence appartiennent aux offres de la Bibliothèque ; n’ajoutez pas de champ inventé requiredCapabilities à locusplace.json. L’importation d’un paquet complet vérifie la version minimale de l’application et le contenu typé lui-même.
Provenance obligatoire
Chaque Destination et Space importé référence un document JSON de provenance. Il indique le créateur ou l’agence, soit une licence, soit une déclaration de droits réservés, les crédits demandés, les modifications, l’usage de l’IA et son fournisseur le cas échéant. Les URL de source ou de fichier original sont facultatives, mais doivent être en HTTPS, sans identifiants ni fragment. Une provenance absente, trop volumineuse ou mal formée entraîne le rejet de tout l’import. Voir la référence de provenance.
{
"formatVersion": 1,
"creatorOrAgency": "Your name or studio",
"sourcePageURL": "https://example.com/project",
"originalAssetURL": "https://example.com/original",
"license": {
"identifier": "YOUR-LICENSE-ID",
"name": "Your license name",
"url": "https://example.com/license"
},
"requestedCredit": "Credit line requested by the creator",
"modificationNotes": "Converted and packaged for Locus.",
"aiGenerated": false
}
| Champ | Obligatoire | Signification et contraintes |
|---|---|---|
formatVersion |
oui | Entier 1. |
creatorOrAgency |
oui | Nom non vide du créateur ou studio, 200 caractères au maximum. |
sourcePageURL |
non | Page du projet en HTTPS publique, sans identifiants ni fragment. |
originalAssetURL |
non | Page source HTTPS publique ; consigne l’origine sans inclure le fichier original. |
license.identifier |
conditionnel | Identifiant non vide, 100 caractères au maximum ; obligatoire avec license. Locus ne choisit pas la licence du contenu à votre place. |
license.name |
conditionnel | Nom non vide, 200 caractères au maximum ; obligatoire avec license. |
license.url |
conditionnel | URL HTTPS publique de licence ; obligatoire avec license. |
rights.statement |
conditionnel | Déclaration de droits non vide, 1 000 caractères au maximum ; à utiliser avec rights.url au lieu de license. |
rights.url |
conditionnel | URL HTTPS publique de la déclaration de droits ; un seul des champs suivants est obligatoire : license ou rights est obligatoire. |
requestedCredit |
oui | Ligne de crédit non vide, 1 000 caractères au maximum. |
modificationNotes |
oui | Notes de préparation non vides, 2 000 caractères au maximum. |
aiGenerated |
oui | Booléen. |
aiProvider |
conditionnel | Obligatoire et non vide lorsque aiGenerated est true; interdit lorsque la valeur est false ; 200 caractères au maximum. |
importSource |
non | Réservé à une Vue créée directement par Locus à partir d’une image : photos, filesou url. Les paquets d’auteurs externes doivent l’omettre. |
Le document est limité à 64 KiB. Ne recopiez pas la provenance d’un modèle après avoir remplacé ses ressources. La licence du dépôt et celle de vos contenus sont deux déclarations distinctes.
Le ZIP sert à la livraison à l’exécution, pas à sauvegarder les sources de création. Chaque fichier doit être accessible depuis un champ de ressource Package-v2 reconnu ; les fichiers non référencés sont rejetés même s’ils figurent dans locusplace.json.files. .blend, .blend1,
.blend2, .fbx, .obj, .mtl, .gltfet .glb sont rejetés même s’ils sont déclarés. Conservez ces sources hors du ZIP. Une pièce contient l’USDZ final ; une Vue contient le panorama 2:1 final et, éventuellement, une petite image JPEG/PNG 2:1 pour l’éclairage de la pièce.
Sécurité et limites de ressources
La vérification examine les octets, pas les extensions. Elle rejette les traversées de chemins, chemins absolus ou non canoniques, barres obliques inverses, caractères de contrôle, collisions de casse ou Unicode, liens symboliques, doublons, contenu chiffré, fichiers non déclarés, empreintes incorrectes, manifestes non pris en charge, images mal formées, textures EXR invalides dans un USDZ, structures USDZ mal formées, membres USDZ compressés et types de ressources USDZ non pris en charge. Les limites des pièces sont mesurées dans la scène USD réellement chargée par le système ; les déclarations de qualité de l’auteur ne peuvent pas réduire ces mesures.
La liste des formats d’image autorisés au niveau des octets est stricte : les panoramas importés et les champs d’image sourceImage,
thumbnailet imageBasedLight doivent être JPEG ou PNG ; les textures USDZ peuvent être JPEG, PNG ou OpenEXR. Les autres formats décodables par ImageIO sont rejetés plutôt qu’acceptés implicitement. Les formats sources 3D hors du modèle sont aussi rejetés. Un USDZ ne peut contenir que des membres USD/USDA/USDC, JPEG, PNG, OpenEXR, M4A, WAV et MP3.
| Limite de ressources | Limite v1 |
|---|---|
| Téléchargement ou archive locale | 1 GiB |
| Éléments du ZIP | 4,096 |
| Manifeste racine | 1 MiB |
| Tout fichier de contenu | 768 MiB |
| Total du contenu décompressé | 2 GiB |
| Taux de compression | 200:1 |
| Tout fichier JSON importé | 1 MiB |
| JSON de provenance | 64 KiB |
| Dimension d’image | 32 768 px |
| Une image | 150 millions de pixels |
| Toutes les images des Vues | 500 millions de pixels |
| Éléments d’un USDZ | 4,096 |
| Taille du fichier USDZ / taille décompressée | 768 MiB / 1,5 GiB |
| Dimension des textures USDZ / total des pixels | 16 384 px / 500 millions |
| Géométrie de pièce chargée | 5 millions de triangles, 1 024 matériaux, 100 000 entités |
Acquisition par fichiers et URL
Pour une Vue, la page d’importation accepte une image JPG, PNG ou HEIC depuis Photos, Fichiers ou une URL HTTPS publique directe. Locus normalise l’image en un paquet Package v2 contenant uniquement une Destination, puis utilise le même validateur et la même installation atomique. Fichiers et URL acceptent aussi les ZIP Package-v2 complets pour Vues et pièces, y compris les paquets combinés contenant le type choisi par le visiteur. Les ZIP à plat existants restent des entrées de compatibilité. Une archive mal formée contenant locusplace.json est rejetée comme paquet complet invalide, sans tentative de lecture au format à plat. Les imports par URL rejettent les identifiants, les fragments, localhost, .localet les réponses DNS contenant des adresses non publiques. Le nom d’hôte est résolu une fois par requête ; toutes les adresses doivent être publiques. La connexion TLS utilise précisément l’adresse IP résolue tout en authentifiant le nom d’hôte d’origine. Chaque redirection répète cette résolution et cette liaison à l’adresse avant toute nouvelle connexion. Cinq redirections au maximum sont suivies ; les tailles déclarées et effectivement téléchargées sont limitées.
La vérification et la résolution du catalogue se terminent dans un répertoire de préparation privé sur le même volume. Un verrou consultatif global à la bibliothèque sérialise l’inventaire, les contrôles de quota et la validation finale. Les premières installations utilisent un renommage atomique exclusif ; les mises à jour, un échange atomique de répertoires. Tout échec supprime la préparation et laisse le paquet installé intact.
Vérification des paquets et exemples
Les commandes ci-dessous gèrent l’enveloppe Package-v2 complète utilisée pour la publication et l’importation complète. Les anciens outils à plat restent documentés dans la
locus-asset-format.md.
Vérifiez l’un ou l’autre nom de fichier sans installation :
python3 tools/validate_locusplace.py --current-app-version 1.4.4 MyPlace.locusplace
python3 tools/validate_locusplace.py --current-app-version 1.4.4 MyPlace.zip
python3 tools/validate_locusplace.py --current-app-version 1.4.4 --json MyPlace.locusplace
Le code Python utilise la bibliothèque standard et ImageIO de macOS via /usr/bin/sipset les outils USD usdchecker et usdcat (fournis avec Xcode et ses outils en ligne de commande). Après la vérification structurelle JPEG ou PNG, ImageIO décode tous les pixels. La vérification EXR concerne uniquement les textures dans un USDZ. Les outils USD chargent la scène réelle de la pièce, résolvent ses couches et la mesurent. Tout échec produit un code de sortie non nul. Le programme vérifie l’archive, l’intégrité, Package v2, la provenance, les images/EXR et les USDZ. L’importateur visionOS reste la référence pour l’installation et la résolution des références entre catalogues.
Empaquetez un dossier éditable dans un ZIP ordinaire en recalculant tous les éléments d’intégrité ; le fichier n’est créé que si le validateur l’accepte :
python3 tools/pack_locusplace.py --current-app-version 1.4.4 MyEditablePlace MyPlace.zip
Générez de véritables archives d’exemple contenant une Vue seule, une pièce seule et un ensemble combiné dans un dossier temporaire ou ignoré, puis vérifiez-les :
python3 examples/locusplace/build_examples.py /tmp/locusplace-examples
for package in /tmp/locusplace-examples/*.locusplace; do
python3 tools/validate_locusplace.py --current-app-version 1.4.4 "$package"
done
Les exemples sont générés depuis le code source, sans télécharger de ressources privées ni appeler de service payant. Le générateur et les structures attendues sont documentés dans le README des exemples.