Формат Scene Bundle

vrata v1 не читает сцены Unity .unity напрямую. Комната может указывать URL манифеста пакета сцены, и тогда runtime загружает пакет как веб-сцену.

Требования к сценам продуктового качества и пороги валидатора описаны в docs/scene-technical-requirements.md. Эта страница описывает контракт совместимости с runtime; документ с требованиями для новых сцен намеренно строже.

Поле манифеста комнаты

Структура пакета

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:

Эндпоинт загрузки — POST /api/scene-bundles/uploads, поля передаются в multipart/form-data:

При успехе ответ содержит сохранённый SceneBundleRecord, а также предупреждения и статистику проверки. Для некорректного пакета API возвращает 400 с error: "scene_bundle_validation_failed" и issues валидатора со стабильными полями code, path и message. Для загрузки нужно право control plane scene-bundle.write.

Режимы хранения:

scene.json

Обязательные поля:

Необязательные поля:

Передача материалов в baked-pbr-v1

renderProfile: "baked-pbr-v1" сочетает запечённое диффузное освещение со стандартными PBR-материалами glTF и отражениями окружения. GLB передаёт lightmap в sRGB как emissiveTexture с texCoord: 1; у каждого затронутого меша должен быть TEXCOORD_1. Преобразование обозначают поля extras материала:

Последнее поле описывает кодировку атласа, а не визуальную подстройку под конкретную сцену. Если поле задано, оно должно быть логическим. Несовместимый физический шейдер 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 после загрузки пакета сцены.

Каждая запись описывает одну исходную работу:

mediaSurfaces

Пакет сцены может заменить встроенные резервные меши медиаповерхностей без пересборки образа runtime. Сцене принадлежат только физические видимые поверхности: идентификатор плоскости, её размещение, размер, пиксельная проекция, лицевая сторона и физическая досягаемость для ввода. Состоянию комнаты или шаблона принадлежат логическое назначение, названия, политика видимости, активные объекты и разрешённые типы объектов.

Каждая запись описывает одну поверхность, видимую в runtime:

Все числовые значения должны быть конечными. Физические и пиксельные размеры должны быть положительными. Для совместимости с текущим 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? }.

Пример:

{
  "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

Источник: docs/scene-bundle-contract.md в vrata-labs/platform на ревизии 380b2e7ff1dd, лицензия Apache-2.0. GitHub