Формат Scene Bundle
vrata v1 не читает сцены Unity .unity напрямую. Комната может указывать URL манифеста пакета сцены, и тогда runtime загружает пакет как веб-сцену.
Требования к сценам продуктового качества и пороги валидатора описаны в docs/scene-technical-requirements.md. Эта страница описывает контракт совместимости с runtime; документ с требованиями для новых сцен намеренно строже.
Поле манифеста комнаты
- Поле:
sceneBundle.url - Тип: строка
- Назначение: абсолютный или относительный URL файла
scene.json - Совместимость: поле необязательное; комнаты без
sceneBundleпо-прежнему используют встроенную резервную сцену
Структура пакета
scenes/the-hall-v1/
scene.json
scene.glb
preview.jpg
Проверка у себя
Перед публикацией автор сцены может проверить каталог пакета, файл scene.json или архив .zip:
pnpm --filter @vrata/asset-pipeline build
pnpm exec vrata scenes validate apps/runtime-web/public/assets/scenes/the-hall-v1
pnpm exec vrata scenes validate apps/runtime-web/public/assets/scenes/the-hall-v1 --json
Валидатор проверяет основу схемы scene.json, безопасность относительных путей, точки появления, основные ассеты сцены, на которые есть ссылки, необязательные файлы превью, необязательные карты переопределения материалов, безопасность записей в zip-архиве и базовые бюджеты размеров файлов. Вывод для человека рассчитан на локальную работу над сценой; с --json команда возвращает { ok, issues, stats } со стабильными полями code, path и message для CI и сценариев загрузки.
Пример проверки в CI:
pnpm --filter @vrata/asset-pipeline build
pnpm exec vrata scenes validate path/to/scene-bundle --json
Загрузка и публикация
Администраторы control plane публикуют пакеты сцен в формате .zip через /control-plane:
- Укажите id пакета и неизменяемую версию.
- Выберите файл
.zipсоscene.jsonи ассетами пакета, на которые он ссылается. - Отправьте форму пакета сцены. API проверяет архив общим валидатором до того, как опубликовать какие-либо метаданные.
- Корректно загруженный пакет появится в списке зарегистрированных пакетов сцен с размером, входной сценой и превью, если оно есть.
- Выберите комнату и нажмите
Bind selected scene bundle, чтобы привязать текущую или выбранную версию.
Эндпоинт загрузки — POST /api/scene-bundles/uploads, поля передаются в multipart/form-data:
bundle: обязательный файл.zip.bundleId: необязательный id в формате lower-kebab. Если он не указан, берётсяscene.json#/sceneId.version: необязательная неизменяемая версия. По умолчаниюv1.
При успехе ответ содержит сохранённый SceneBundleRecord, а также предупреждения и статистику проверки. Для некорректного пакета API возвращает 400 с error: "scene_bundle_validation_failed" и issues валидатора со стабильными полями code, path и message. Для загрузки нужно право control plane scene-bundle.write.
Режимы хранения:
- При самостоятельном запуске в production и staging загруженные объекты записываются в MinIO/S3-совместимое хранилище с параметрами
SCENE_BUNDLE_PROVIDER,MINIO_ENDPOINT,MINIO_ROOT_USER,MINIO_ROOT_PASSWORD,MINIO_BUCKETиMINIO_PUBLIC_BASE_URL. - Для развёртываний
s3-compatibleнужно дополнительно задатьSCENE_BUNDLE_S3_ENDPOINT,SCENE_BUNDLE_S3_REGION,SCENE_BUNDLE_S3_BUCKET,SCENE_BUNDLE_S3_PUBLIC_BASE_URL,SCENE_BUNDLE_S3_ACCESS_KEY_IDиSCENE_BUNDLE_S3_SECRET_ACCESS_KEY. - В dev- и test-окружениях без учётных данных объектного хранилища файлы хранятся в локальной файловой системе: в
SCENE_BUNDLE_LOCAL_UPLOAD_ROOTили вapps/runtime-web/public/assets/uploaded-scene-bundles. FEATURE_SCENE_BUNDLE_UPLOAD=falseотключает эндпоинт загрузки, а API метаданных, списка и привязки продолжают работать.
scene.json
Обязательные поля:
schemaVersion:1sceneId: стабильный идентификатор сценыlabel: понятное человеку название сценыsource: исходная система или название проекта UnityglbPath: путь к основному файлу ассета сцены относительноscene.json; runtime v1 сейчас принимает.gltf,.glbи.fbxspawnPoints: массив точек появления; runtime v1 использует первую запись. Точка появления может содержать необязательный конечныйyawв радианах.
Необязательные поля:
bounds:{ width, height, depth }renderMode:"default"или"clean";cleanскрывает резервную отладочную геометрию и включает более яркий вариант освещения сценыrenderProfile:"neutral-pbr"по явному выбору включает тонмаппинг AgX со сбалансированным рассеянным, полусферическим и направленным освещением для PBR-сцен. Пакеты без этого поля рендерятся по-старому.mediaSurfaces: физическая геометрия плоскостей runtime для медиаповерхностей конкретной сценыpreview: относительный путь к ассету превьюattributions: необязательные записи об авторстве исходных материалов, которые показываются в HUD runtimenotes: произвольные заметки об экспорте
Передача материалов в baked-pbr-v1
renderProfile: "baked-pbr-v1" сочетает запечённое диффузное освещение со стандартными PBR-материалами glTF и отражениями окружения. GLB передаёт lightmap в sRGB как emissiveTexture с texCoord: 1; у каждого затронутого меша должен быть TEXCOORD_1. Преобразование обозначают поля extras материала:
vrataLightMap: trueпереносит эту текстуру в слот lightMap в runtime.vrataLightMapIntensity— конечный множитель декодирования для запечённой кодировки.vrataOriginalEmissiveиvrataOriginalEmissiveIntensityвозвращают настоящее свечение поверхностей после того, как транспортная текстура убрана из слота emissive.vrataLightMapIncludesEnvironment: trueсообщает, что атлас уже содержит диффузную составляющую окружения и непрямого освещения. Тогда runtime сохраняет зеркальную составляющую окружения и multiscattering, но не добавляет для этого материала диффузный член IBL. Если поле не задано или равно false, существующие пакеты затеняются как раньше.
Последнее поле описывает кодировку атласа, а не визуальную подстройку под конкретную сцену. Если поле задано, оно должно быть логическим. Несовместимый физический шейдер Three.js отклоняется при загрузке, до изменения материалов. Если отдельный пользовательский хук материала убирает include физического шейдера, текущий шейдер сохраняется, а материал сообщает fallback, а не заявляет, что коррекция применена.
sceneDebug.materialSamples показывает lightMapIntensity и bakedEnvironmentMode (legacy, pending-compile, specular-only, fallback). Новые кандидаты, у которых атлас содержит полную облучённость, должны показывать specular-only после компиляции видимых материалов и всё равно проходить проверку реальных изображений из исходника и браузера. Сохраняйте динамический диапазон атласа и фиксируйте принятый способ кодирования и декодирования: ни большая интенсивность, ни эти метаданные не исправят обрезанные пиксели lightmap.
Логическое состояние медиаповерхностей сцены
При выдаче токена сессии комнаты API берёт идентификаторы медиаповерхностей из сохранённой привязки пакета сцены к комнате. Он подписывает claim sceneMediaSurfaces ограниченного размера с ID, названиями и разрешёнными типами объектов. Поля запроса на вход не могут передать этот claim. Сервис состояния комнаты проверяет токен и регистрирует недостающие логические поверхности до первого снимка, поэтому собственный ID вроде workspace-main поддерживает общие объекты на обычном пути комнаты.
Регистрация только добавляет поверхности: существующие поверхности и активные объекты сохраняются при переподключениях, прежние поверхности по умолчанию остаются. Политика существующих поверхностей не меняется, старые объекты не удаляются, трансформации из клиентского манифеста не подменяются. Для новых поверхностей действуют обычные проверки прав на команды и типов объектов. Поддерживаются и устаревшие определения с surfaceId, и определения F3 с id. Если манифест не удаётся прочитать или определения в нём некорректны, новые поверхности не выдаются, а прежние поверхности по умолчанию остаются доступны. Если из-за этого не хватает привязки, это видно в диагностике runtime о расхождении физических и логических поверхностей.
attributions
Пакет сцены может объявить видимые записи об авторстве исходных ассетов, использованных в сцене. Runtime показывает их в отдельном блоке HUD после загрузки пакета сцены.
Каждая запись описывает одну исходную работу:
title: название работыauthor: имя автора или лицензиараsource: HTTP(S)-адрес, по которому можно найти оригиналlicense: обозначение лицензии, напримерCC-BY-4.0authorUrl: необязательный HTTP(S)-адрес профиля автораlicenseUrl: необязательный HTTP(S)-адрес лицензииchanges: необязательная короткая заметка об изменениях, сделанных для сцены
mediaSurfaces
Пакет сцены может заменить встроенные резервные меши медиаповерхностей без пересборки образа runtime. Сцене принадлежат только физические видимые поверхности: идентификатор плоскости, её размещение, размер, пиксельная проекция, лицевая сторона и физическая досягаемость для ввода. Состоянию комнаты или шаблона принадлежат логическое назначение, названия, политика видимости, активные объекты и разрешённые типы объектов.
Каждая запись описывает одну поверхность, видимую в runtime:
surfaceId: непустой id медиаповерхности, уникальный в пределах манифеста.representation: строго"platform-runtime-plane".position: конечная мировая позиция{ x, y, z }в метрах.yaw: конечный поворот вокруг локальной оси Y в радианах.widthM,heightM: физический размер плоскости в метрах.pixelDimensions: размеры текстуры плоскости{ width, height }в виде положительных безопасных целых чисел. Каждая сторона не больше8192, общее число пикселей не больше33,554,432.frontFace: строго"local-positive-z"; лицевая сторона, которая отрисовывается и принимает ввод, — локальная +Z уTHREE.PlaneGeometry.input:{ enabled, maxDistanceM }, гдеenabled— логическое значение, аmaxDistanceM— положительное конечное расстояние ближнего контакта для ввода XR-карандашом и точкой на плоскости. Дальность луча контроллера или указателя оно не ограничивает.
Все числовые значения должны быть конечными. Физические и пиксельные размеры должны быть положительными. Для совместимости с текущим runtime новый массив mediaSurfaces в формате F3 должен быть непустым и содержать ровно одну поверхность с surfaceId: "debug-main". Повторяющиеся id недопустимы ни в одном формате.
Если mediaSurfaces не задан, сцена не загрузилась или пакеты сцен отключены, runtime оставляет встроенную резервную раскладку: основной экран на стене, стену-доску и экран ноутбука. Если mediaSurfaces задан, выбирать можно только физические меши, объявленные в сцене; только они видны в диагностике медиаповерхностей и принимают ввод на поверхности. Логическая поверхность из состояния комнаты без соответствующей геометрии в сцене не подменяется на debug-main.
Устаревшая форма v1
Runtime и проверка ассетов по-прежнему принимают визуальную форму mediaSurfaces, которая вошла в Scene Bundle v1 до проекции F3. Существующие пакеты могут использовать surfaceId, widthM, heightM, необязательные widthPx/heightPx, label, kind, visible, allowedObjectTypes и transform: { x, y, z, yaw?, pitch?, roll? }.
- Устаревшие
widthPxиheightPx, если они заданы, подчиняются тем же бюджетам, что и в F3: безопасные целые числа, не больше8192на сторону и не больше33,554,432пикселей всего. - Устаревшие
pitch,roll,visibleиlabelпо-прежнему действуют в runtime. - Устаревший
allowedObjectTypesпроверяется ради совместимости и проверок пары с шаблоном, но права на объекты в runtime определяются только логическим состоянием комнаты или шаблона. - Устаревшие поверхности используют прежнее расстояние ближнего контакта XR
0.06 mи не ограничивают дальность луча. - Массив только из устаревших записей корректен и без
debug-main. Смешивать в одном массиве записи F3 и устаревшие записи нельзя — такой массив явно отклоняется. - Новые и повторно публикуемые пакеты сцен должны использовать описанную выше форму F3
platform-runtime-plane.
Пример:
{
"schemaVersion": 1,
"sceneId": "sense-hall-v1",
"label": "Sense Hall",
"source": "sensetowervr",
"glbPath": "scene.glb",
"renderMode": "clean",
"renderProfile": "neutral-pbr",
"spawnPoints": [
{
"id": "main",
"position": { "x": 0, "y": 0, "z": 6 },
"yaw": 3.141592653589793
}
],
"mediaSurfaces": [
{
"surfaceId": "debug-main",
"representation": "platform-runtime-plane",
"position": { "x": -3.4, "y": 1.55, "z": 0.15 },
"yaw": 1.5707963267948966,
"widthM": 3.2,
"heightM": 1.8,
"pixelDimensions": { "width": 1920, "height": 1080 },
"frontFace": "local-positive-z",
"input": { "enabled": true, "maxDistanceM": 0.05 }
},
{
"surfaceId": "whiteboard-wall",
"representation": "platform-runtime-plane",
"position": { "x": 3.4, "y": 1.5, "z": 0.5 },
"yaw": -1.5707963267948966,
"widthM": 2.4,
"heightM": 1.25,
"pixelDimensions": { "width": 1920, "height": 1000 },
"frontFace": "local-positive-z",
"input": { "enabled": true, "maxDistanceM": 0.05 }
}
],
"bounds": { "width": 24, "height": 8, "depth": 24 },
"preview": "preview.jpg",
"attributions": [
{
"title": "Old Room",
"author": "Hansalex",
"authorUrl": "https://sketchfab.com/Hansalex",
"source": "https://sketchfab.com/3d-models/old-room-6173a3c88c384f768dfc80967b6527b4",
"license": "CC-BY-4.0",
"licenseUrl": "https://creativecommons.org/licenses/by/4.0/",
"changes": "Normalized to meters and optimized for Vrata runtime."
}
],
"notes": "v1 export without Unity behaviors"
}
Поведение runtime
- Если
sceneBundle.urlне задан, runtime использует встроенную резервную комнату. - Если
scene.jsonне прошёл проверку или содержит неизвестныйschemaVersion, runtime использует резервную комнату. - Если
scene.glbне загрузился, runtime использует резервную комнату. - После успешной загрузки пакета сцены резервные меши скрываются, а корневой объект игрока ставится в первую точку появления.
- После успешной загрузки пакета сцены применяется
mediaSurfaces, если он задан: runtime создаёт, обновляет или удаляет меши физических плоскостей так, чтобы они точно соответствовали манифесту. - После успешной загрузки пакета сцены в HUD показываются
attributions, если они заданы.
Источник: docs/scene-bundle-contract.md в vrata-labs/platform на ревизии 380b2e7ff1dd, лицензия Apache-2.0. GitHub