技术参考
完整 Locus Package v2 制作指南
本页内容
状态:正式的发布、存储、组合及完整功能导入契约。Locus 1.2.1 及以上版本可通过文件或公开 HTTPS URL 导入此归档,用于 View 和 Room。旧的扁平格式见
locus-asset-format.md 仍可作为兼容输入。
一个 .zip 或 .locusplace 文件是一个普通 ZIP 归档,包含一个或多个 Package v2 Destination(产品名称为 View)、Space(Room)及 Experience。Locus 自身和支持完整功能的可移植包均使用这一安全发布与完整性封装。ZIP 根目录包含 locusplace.json 和
catalog/ 直接放置,不要再套一层父目录。
校验基于归档字节与 manifest,而非文件扩展名。完整包会保留其 packageID、内容 ID、
contentVersion 更新流及受支持的 Experience;导入相同内容哈希时为幂等操作,不产生变化。维护中的打包器和校验器会生成并检查这一确切格式。
此外层封装只承载数据,不承载可执行扩展。对于同一个 Locus 二进制文件,运行时使用的每项 Package v2 能力,无论归档是内置、从 Library 下载,还是从文件或 URL 导入,内容语义都相同。来源可能影响使用权、自定义导入额度、更新权限、存储、移除及 UI 标签,但不会启用另一套 Room/View schema 或渲染功能。脚本、插件、动态库及可执行成员不属于此格式。所有来源均遵守相同的路径隔离、完整性、资源预算、结构解码、引用闭合及最低版本检查。
本指南介绍完整外层封装及基础 manifest。 字段参考 介绍可选的 View 与 Room 能力、相关资产、取值范围及最低 app 版本。 外层封装 schema 和 来源信息 schema 描述 JSON 的结构;打包器与校验器还会检查文件字节和跨字段规则。单凭 JSON Schema 无法校验归档,也无法证明它在 Apple Vision Pro 上的体验舒适。
从可用的完整包开始
克隆或下载 公开工具仓库。在 macOS 上从该仓库根目录运行以下命令,需要 Python 3.9 或更高版本,以及安装有 Xcode 并提供以下工具:
xcrun usdchecker 和 xcrun usdcat。基础校验使用 Python 标准库、
/usr/bin/sips 完成完整图像解码,并使用 Xcode 的 usdchecker 和 usdcat
处理模型。高级云层、水体反射与接触、无太阳天空检查还需要 NumPy 和 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
目标目录必须不存在或为空。这些小型合成示例用于验证格式;渐变图和立方体并非生产级场景或成品 Room。其源码与生成内容采用 Apache-2.0 许可。参见 示例说明.
--current-app-version 指定本次校验的目标 Locus 版本,校验将以此 为准;它不会升级已安装的 App,也不会赋予 App 新能力。工具保守地默认为 1.0.0;请明确指定目标版本。完整包导入要求 Locus 1.2.1 或更高版本,新字段还受各自最低版本限制。Room 的地面和中景控制要求支持这些字段的 1.4.4 或更新构建;本指南不表示该构建已经在 App Store 上架。
制作自己的包时,可将示例解压到可编辑文件夹,或按下方结构创建目录。替换资产与来源信息,使用自己的稳定 ID,并将输出归档保存到 该文件夹之外的 路径:
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
打包器会重新计算 files 和 contentHash,校验归档后才生成输出文件。它拒绝覆盖已有输出,并保持可编辑源文件不变。通过以下入口导入结果: Places → 导入 → 导入 View 或 导入 Room → 文件,选择与包内内容类型相符的导入入口。组合包会一起安装其中的内容,也可通过直接指向 ZIP 的公开 HTTPS URL 导入。分享成品前,请在 Apple Vision Pro 上检查每个座位、效果、灯光和声音。
归档结构
example.locusplace
├── locusplace.json
└── catalog/
├── destinations/<destination-id>/
│ ├── destination.json
│ ├── provenance.json
│ └── ...
├── spaces/<space-id>/
│ ├── space.json
│ ├── provenance.json
│ └── ...
└── experiences/<experience-id>/
└── experience.json
除根目录的 locusplace.json 以外,所有非目录项都必须列入 files中。每个已声明文件都必须存在。归档只能包含规定的三个目录集合,每个文件都必须位于 contents中声明的包 ID 对应目录内。Package-v2 清单可向前兼容未知字段;但未知的封装清单字段、不支持的格式版本、集合及内容模式会被安全拒绝。来源信息遵循其已公开的格式规范。
仅含 View 或仅含 Room 的归档会安装可复用的目录内容。目录解析后,Locus 会生成默认配对:每个 View 与“你的房间”配对,也与每个声明 View 开口的 Room 配对。显式 Experience 仅替换其指定配对,可引用同一归档内的内容,或内置/已导入目录中已有的稳定 ID。
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 以及所有内容 ID 都是最长 128 字节的稳定 ASCII 标识符。必须以字母或数字开头,之后只能使用字母、数字、
., _,以及 -。版本号恰好由三个数字分量组成。若更新改变了字节内容,必须保留原先声明的内容 ID,并提高 contentVersion。再次导入相同的 contentHash 为幂等操作,不产生变化。
contentHash 对按 Unicode 路径排序的所有文件记录计算 SHA-256。每条记录贡献以下 UTF-8 字节:
path NUL decimal-byteCount NUL lowercase-sha256 NUL
此哈希不受 JSON 空白及 ZIP 成员顺序影响。校验器还会流式核实每个声明文件的实际字节数、ZIP CRC 和 SHA-256。
封装清单字段参考
| 字段 | 必填 | 含义 |
|---|---|---|
formatVersion |
是 | 整数 1;未知的封装格式版本会被安全拒绝。 |
packageID |
是 | 此安装及更新流的稳定标识,不是文件名。 |
contentVersion |
是 | 三个数字部分,例如 1.2.0;相同 ID 下的字节发生变化时应提高版本。 |
minimumAppVersion |
是 | 允许导入该归档的最低 Locus 版本,同样由三个数字部分组成。 |
contents.destinationIDs |
是 | 此 ZIP 所拥有且不重复的 View ID;没有时使用 [] 表示没有。 |
contents.spaceIDs |
是 | 此 ZIP 所拥有且不重复的 Room ID;没有时使用 [] 表示没有。 |
contents.experienceIDs |
是 | 此 ZIP 所拥有且不重复的 Experience ID;没有时使用 [] 表示没有。 |
files |
是 | 自动生成的完整资源文件清单,不包含 locusplace.json. |
files[].path |
是 | 规范的归档相对 catalog/... 路径。 |
files[].byteCount |
是 | 解压后的精确字节数。 |
files[].sha256 |
是 | 解压后字节的 SHA-256,由 64 个小写十六进制字符组成。 |
contentHash |
是 | 上述规范化载荷清单的 SHA-256 标识。 |
ZIP 内的 Package v2 元数据
完整外层封装使用 formatVersion: 1;每个 destination.json,
space.json 和 experience.json 使用 formatVersion: 2。以下参考中的 Room v1–v10 编号 扁平兼容格式参考
属于独立的扁平格式 space.json 输入。不要修改完整包的
space.json 设为版本 10 来使用烘焙光照层。在完整包中,可选能力由其有类型定义的字段启用,最低版本要求则由以下字段限定:
minimumAppVersion.
包内资源路径均相对于清单所在目录,而非 ZIP 根目录。每个引用都必须解析为该包内的普通文件。基础 Package-v2 清单允许向前兼容的元数据,但单个能力配置可能采用封闭字段集。不要假设未知字段会被渲染。外层封装清单拒绝未知字段,来源信息须遵循已公开的格式规范。
坐标采用右手系,单位为米,+Y 向上、−Z 向前。四元数顺序为
[x, y, z, w]。变换可包含 translationMeters (三个有限数值)、 orientationXYZW (四个有限数值组成的非零长度四元数),以及
scale (三个有限正数)。
View: 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"
}
| 字段 | 必填 | 含义与校验 |
|---|---|---|
formatVersion |
是 | 整数 2. |
id |
是 | 稳定的 View ID,必须与目录及外层封装中的 ID 相同。 |
title |
是 | 非空的用户可见 View 名称。 |
panorama.path |
是 | 最终 JPEG 或 PNG,路径相对于包目录;类型由解码字节决定,而非仅看扩展名。 |
panorama.projection |
是 | 恰好为 equirectangular. |
panorama.width, height |
是 | 准确声明的正解码尺寸,且 width == 2 * height. |
panorama.initialYawDegrees |
否 | 标准全景坐标系中的有限旋转角;默认值为 0. |
sourceImage |
否 | 可选的包内相对路径 JPEG/PNG 审核图像。它不会参与渲染,但完整包可将其作为经过类型识别、解码和预算检查的数据保留。可编辑源文件与 DCC 源文件仍须放在运行时 ZIP 之外。 |
thumbnail |
否 | 包内相对路径的 JPEG/PNG 目录图像。 |
caption |
否 | 供人阅读的描述。 |
generation.generator |
否 | 真实的生成器或工具名称。 |
generation.model |
否 | 可选的模型或工作流版本。 |
generation.generatedAt |
否 | 可选时间戳;推荐 RFC 3339 UTC。 |
depthLayers[] |
Package v2 中不支持;公开 ZIP 导入不支持 | 预留的有序深度资产。在产品运行时与字节级校验器支持其编码之前,公开导入会拒绝该字段。 |
midground.path |
否 | 两种 Experience Mode 都会渲染的运行时中景 USDZ。内置、Library 和导入的包均遵守同一套成员允许清单、结构与资源预算。参见 中景参考. |
midground.transform |
否 | 用于以下内容的可选有限变换: midground.path. |
midground.terrainEntities |
否 | 以下字段中不重复且非空的地面单元名称: midground.path;运行时据此调整 Room 占地范围下的地形。 |
midground.water |
否 | App 渲染的水面声明,包含经校验的遮罩/法线贴图,以及 字段参考. |
sky |
否 | App 渲染的程序化夜空(星星、银河及可选极光),通过经校验的 2:1 灰度遮罩 sky.mask;要求 minimumAppVersion >= 1.2.4。参阅 天空参考. |
gpuTexture, gpuTextures |
否 | 经过校验的 ASTC KTX1 辅助纹理,用户 ZIP 与 Library 包均可使用。v1 全景辅助纹理要求 1.2.3;v2 源图数组要求 1.3.0。源文件哈希、尺寸和 mip 层级必须匹配。 |
water, clouds, fireflies |
否 | 运行时水体(含发光)、绘制云层运动与萤火虫声明;要求 minimumAppVersion >= 1.3.0。其引用资源及资源预算遵循共同的 Package-v2 合同。 |
environment.sunPath |
否 | 作者声明的无太阳天空及可选遮罩太阳圆盘;要求 1.3.0 及其作者声明。 |
celestial |
否 | 动画天体及其引用纹理;要求 1.2.4,或其可选功能要求的更高版本(云层运动/演化要求 1.3.0). |
nearOrbit |
否 | 近轨道配置、纹理资产及与内容绑定的回退证明。v1 和 v2 要求 1.3.0;v2 包含分块清单、分块图像及其 GPU 旁路纹理。 |
splat.variants, splat.ply |
Package v2 中不支持;公开 ZIP 导入不支持 | 预留的未来资产引用。splat 后端已移除,因此公开导入会拒绝整个字段。 |
splat.metricScaleFactor |
Package v2 中不支持;公开 ZIP 导入不支持 | 预留契约中的有限正缩放值。 |
splat.groundPlaneOffset |
Package v2 中不支持;公开 ZIP 导入不支持 | 预留契约中的有限地面偏移量。 |
splat.placement |
Package v2 中不支持;公开 ZIP 导入不支持 | 预留契约中的可选共享变换。 |
environment |
否 | 下方为可见 View 亮度、可选的小型 SDR Room 光照图及直射太阳光默认值。 |
audio.path |
Package v2 中不支持;公开 ZIP 导入不支持 | 预留的运行时音频文件。在产品支持完整音频字节校验与播放之前,公开导入会拒绝该字段。 |
audio.loops |
与 Package v2 音频一起使用;公开 ZIP 导入不支持 | 保留合同中控制是否循环的布尔值。 |
provenance |
导入支持 | 来源信息 JSON,路径相对于包目录。 |
完整 ZIP 接受该 App 版本实际使用的字段,以及明确保留的元数据,例如 sourceImage;前提是能验证资源类型及其是否符合资源预算。 midground 属于公共运行时契约,要求 minimumAppVersion >= 1.2.1.
depthLayers, splat,以及旧版 audio 块仍为预留或已停用,当前没有运行时使用方。因此当前二进制文件会对所有来源拒绝这些内容,而不会接受未经类型校验的路径。后续二进制文件实现某项能力时,必须将该字段的资产引用、类型与预算检查、运行时使用方及最低 app 版本一并加入公共契约,不得另设公开导入开关。
推荐 12,288×6,144(12K),但这不是强制要求。 较低的分辨率,例如 8,192×4,096、6,144×3,072 或 4,096×2,048,只要严格为 2:1 并声明真实解码尺寸,也都是有效的。不要仅为标称 12K 而放大小尺寸源图:这不会增加细节,只会浪费内存。
全景坐标固定如下: u = 0.5 对应 yaw 0,即初始正前方/世界 −Z; u 向东、即 +X 方向递增;接缝位于 u = 0/1,对应 yaw ±180°;图像最上方一行是天顶。 initialYawDegrees 在此坐标系内旋转图像,不会重新定义坐标系。
可选 View 光照:
{
"environment": {
"imageBasedLight": "thumbnail.jpg",
"skyGainEV": 0,
"exposureEV": 0,
"horizonPitchDegrees": 0,
"colorGrade": {"contrast": 1.15, "saturation": 1.2},
"directSun": {"enabled": false, "illuminanceLux": 5000}
}
}
| 字段 | 含义 |
|---|---|
imageBasedLight |
独立的 2:1 JPEG/PNG,仅用于照亮 Room 几何体及反射。不得复用 panorama.path;推荐 1024×512,省略时使用中性的渲染照明回退值。 |
skyGainEV |
仅用于可见全景的有限 EV 增益。 |
exposureEV |
仅用于 IBL 的有限 EV 增益。 |
horizonPitchDegrees |
作者设置的有限地平线校正量。 |
colorGrade.contrast |
可选的 SDR 可见全景对比度,范围为 0.5 至 2.0; 1.0 为中性值。 |
colorGrade.saturation |
可选的 SDR 可见全景饱和度,范围为 0.0 至 2.0; 1.0 为中性值。 |
directSun.enabled |
是否由作者配置独立的方向光。 |
directSun.azimuthDegrees, elevationDegrees |
有限方向角;启用时两项均为必填。 |
directSun.illuminanceLux |
有限、非负的照度。 |
调色只影响显示的 SDR 全景图。它复用原图纹理,不会把 View 转换成 HDR,也不会补回源图缺失的高光细节。关闭的太阳仍可保存 5000 lux,使得 添加阳光 从 app 默认值开始,不改变包中作者设定的关闭状态。
visibleSkyHDR 和 hdrManifest 已停用,会被拒绝。外层包中的 EXR 不可作为可见 View 或 Room 光照图。OpenEXR 仍可作为 Room 资产的 USDZ 纹理格式,并在模型内部校验。
Room: 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"
}
| 字段 | 必填 | 含义与校验 |
|---|---|---|
formatVersion |
是 | 整数 2. |
id |
是 | 稳定的 Room ID,必须与目录及外层封装中的 ID 相同。 |
title |
是 | 非空的用户可见 Room 名称。 |
scene |
是 | 自包含的 USDZ,路径相对于包目录。 .blend 以及其他制作源文件均不可替代最终资产,也不得放在包内其他位置。 |
thumbnail |
否 | Room 卡片显示的 JPEG 或 PNG,路径相对于包目录。若声明,文件必须存在且能完整解码,并计入归档图像预算;省略则使用默认图标。 |
caption |
否 | 非空的用户可见文案,用于 关于此 Room,最多 2,000 个 UTF-8 字节。 |
previewCamera.yawDegrees, pitchDegrees |
否 | 有边界的 3D 预览所用的有限初始角度,不影响沉浸式摆放。 |
previewCamera.zoom |
以及相机 | 有边界的 3D 预览所用的有限正初始缩放值。重置会返回此初始姿态;查看器会将其限制在支持的交互范围内。 |
seatedOrigin.translationMeters |
是 | 用于定位作者设定的落座地面参考点的三个有限米制分量。 |
seatedOrigin.orientationXYZW |
是 | 四个有限且归一化的四元数分量;落座坐标系必须保持直立,不可有俯仰或侧倾。 |
safeHeadVolume.centerMeters |
是 | Room 坐标中的三个有限米制分量。 |
safeHeadVolume.sizeMeters |
是 | 三个有限且严格为正的尺寸。 |
destinationOpenings |
是 | 矩形开口数组。独立的纯 Room 导入必须恰好有一个开口;零个或多个开口则要求作者在同一归档中提供 virtual-space Experience。 |
destinationOpenings[].id |
每项 | Experience 组合使用的唯一包 ID。 |
destinationOpenings[].transform |
每项 | 必填的位置与四元数姿态。 |
destinationOpenings[].widthMeters, heightMeters |
每项 | 有限且严格为正的尺寸。 |
collision.mode |
是 | embedded 或 separate. |
collision.path |
有条件 | 以下情况下禁止使用: embedded;对于 separate时则必须提供包内相对路径指向的独立 USDZ,并按照与 scene相同的成员、纹理和几何规则进行校验。其几何数据与场景几何共同计入 Room 的资源预算。仅改名的制作源文件不合法。 |
quality.status |
是 | unverified 或 verified. |
quality.triangleCount, materialCount, entityCount, maxTextureDimension |
是 | 作者填写的非负测量值。零表示未测量;实际 USD 会单独检查。 |
teleportCatalog.path |
是 | 传送点 JSON,路径相对于包目录。选中的房屋必须至少有一个点,否则 Room 无法进入 Virtual Space。其 1 MiB JSON 预算按字段用途强制执行,即使文件名不以下列扩展名结尾: .json;文件系统的大小写或 Unicode 别名不能绕过此限制。 |
teleportCatalog.houseID |
是 | 用于选择该 JSON 中一个非空房屋条目的 ID,条目位于 houses 数组中。 |
spatialAdaptation.wallEntities |
否 | 不重复且非空的墙体 USD 实体名;此元数据不会隐藏墙体。 |
spatialAdaptation.roofEntities |
否 | 不重复、非空且与墙体名称不重叠的名称。 |
spatialAdaptation.deskEntitiesByTeleportID |
否 | 从已声明的传送点 ID 到非空的作者指定桌子实体名的映射。 |
provenance |
是 | 来源信息 JSON,路径相对于包目录。 |
USDZ 使用米制、右手坐标系、+Y 朝上,且仅包含受支持的运行时 USD、纹理与音频成员。USDZ 成员必须以存储方式写入,不得压缩。校验器会加载并展平真实 stage,统计几何体;即使 quality 值很小或为零,也无法掩盖超预算模型。
必填传送点 JSON(选中的房屋必须至少有一个点):
{
"houses": {
"example-room": [
{
"id": "work-desk",
"title": "Work desk",
"anchorXZ": [0.5, 0.5],
"sourceFloorOffset": 0,
"eyeHeight": 1.15,
"yawRadians": 0
}
]
}
}
anchorXZ 是两个有限的归一化数值,范围为 0...1; sourceFloorOffset 以作者模型单位计量,必须有限且非负; eyeHeight 以米计量,必须有限且为正; yawRadians 是在世界坐标系中绕 +Y 的有限模型旋转角。点 ID 必须唯一,标题不得为空。
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"]
}
}
| 字段 | 必填 | 含义与校验 |
|---|---|---|
formatVersion |
是 | 整数 2. |
id |
是 | 稳定的 Experience ID。 |
title |
是 | 非空的用户可见单元名称。 |
destinationID |
是 | 此 ZIP 中或已安装的稳定 View ID。 |
spaceID |
有条件 | 稳定的 Room ID;以下字段要求它: virtual-space;通常在以下模式省略: room-portal. |
supportedModes |
是 | 非空且元素不重复的数组,只能包含 virtual-space 或 room-portal. |
composition.destinationOpeningID |
有条件 | 以下情况下必填: virtual-space;指定所引用 Room 中的一个开口。 |
composition.destinationYawDegrees |
否 | 此组合的有限天空对齐偏移量。 |
composition.destinationPitchDegrees |
否 | 此组合的有限天空俯仰偏移量。 |
composition.destinationExposureOffsetEV |
否 | 此组合的有限相对曝光偏移量。 |
compatibility.minimumViewerVersion |
否 | 声明式最低 viewer 版本字符串。 |
compatibility.requiredCapabilities |
否 | 声明式能力标识字符串。 |
纯 View 和纯 Room ZIP 会安装可复用内容,并加入默认组合矩阵。只有需要覆盖某个组合的开口、朝向、曝光、标题、模式或兼容性时,才需要显式 Experience。引用可指向同一归档内的内容,或内置及已安装的稳定 ID;任何无法解析的引用都会使整个导入失败。
可选能力与翻译标签
将可选字段写入其所属 View 或 Room 的清单,并把每个引用资源放在相应清单旁。 字段参考
涵盖媒体与蒙版、声音及 Room 音乐、中景与风、水体、天空、云层、太阳路径、天体、近轨道、黑洞、点光源、GPU 辅助纹理、座位与分组、灯光、动画、视频表面、粒子及烘焙光照层。它也定义了 localizations,
provenanceDetail,以及 Room 的 viewGroundFloorOffsetMeters,
viewGroundKeepOut,以及 viewMidgroundVisibility 控制项。
例如,以下可选字段应放入其他内容已完整的 Room manifest 中,并要求外层封装的 minimumAppVersion: "1.4.4" 或更高版本:
{
"viewGroundFloorOffsetMeters": 0.2,
"viewGroundKeepOut": false,
"viewMidgroundVisibility": "terrainOnly"
}
这些字段在导入、内置和 Library 内容中含义相同。字段参考中的 Library 能力名称属于 Library offer;不要将自行编造的 requiredCapabilities 字段添加到 locusplace.json。完整包导入流程会检查最低 App 版本和实际的类型化内容。
必填来源信息
每个导入的 Destination 和 Space 都指向来源 JSON 文档,记录作者或机构、许可或保留所有权利声明(二选一)、要求的署名、修改说明、是否使用 AI,以及适用时的 AI 提供方。来源页面/原始资源 URL 可选,但必须为 HTTPS,且不含凭据或片段。来源信息缺失、超出大小限制或格式错误都会使整个导入失败。参阅 来源信息参考.
{
"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
}
| 字段 | 必填 | 含义与约束 |
|---|---|---|
formatVersion |
是 | 整数 1. |
creatorOrAgency |
是 | 非空的作者或工作室名称,最多 200 个字符。 |
sourcePageURL |
否 | 公开 HTTPS 项目页面;不得包含凭据或片段。 |
originalAssetURL |
否 | 公开 HTTPS 来源页面;记录来源,而不嵌入原文件。 |
license.identifier |
有条件 | 非空标识符,最多 100 个字符;使用以下对象时必填: license。Locus 不会替作者选择资源许可。 |
license.name |
有条件 | 非空名称,最多 200 个字符;使用以下对象时必填: license. |
license.url |
有条件 | 公开 HTTPS 许可 URL;使用以下对象时必填: license. |
rights.statement |
有条件 | 非空权利声明,最多 1,000 个字符;应搭配 rights.url 而不是 license. |
rights.url |
有条件 | 公开 HTTPS 权利声明 URL;以下两项必须恰好提供一项: license 或 rights 为必填。 |
requestedCredit |
是 | 非空署名文本,最多 1,000 个字符。 |
modificationNotes |
是 | 非空制作说明,最多 2,000 个字符。 |
aiGenerated |
是 | 布尔值。 |
aiProvider |
有条件 | 以下情况下为必填且不得为空: aiGenerated 为 true;为 false 时禁止提供,最多 200 个字符。 |
importSource |
否 | 仅保留给 Locus 直接从图像创建的 View: photos, files或 url。外部作者制作的包应省略它。 |
该文档限 64 KiB。替换资产后,不要继续使用入门示例的来源信息。仓库许可与用户资产许可是两项独立声明。
ZIP 用于运行时交付,不是制作源文件备份。每个载荷文件都必须能从已识别的 Package v2 资产字段引用到;无引用文件一律拒绝,即使它们已列在 locusplace.json.files. .blend, .blend1,
.blend2, .fbx, .obj, .mtl, .gltf,以及 .glb 文件即使已声明也会被拒绝。这些源文件应保留在 ZIP 之外。Room 交付最终 USDZ;打包 View 交付最终 2:1 全景,以及可选的一张小型 2:1 JPEG/PNG Room 光照图。
安全与资源预算
校验检查实际字节,不靠扩展名。它会拒绝路径穿越、绝对路径或非规范路径、反斜杠、控制字符、大小写或 Unicode 冲突、符号链接、重复条目、加密内容、未声明文件、哈希不匹配、不受支持的 manifest、损坏图像、USDZ 内无效 EXR 纹理、损坏的 USDZ 结构、压缩的 USDZ 成员及不受支持的 USDZ 资产类型。Room 预算基于系统加载器实际解析的 USD stage 检查;作者自报的质量信息只是制作元数据,无法降低这些统计值。
字节级图像允许清单是严格限定的:导入的全景、 sourceImage,
thumbnail,以及 imageBasedLight 栅格图像字段仅允许 JPEG 或 PNG;USDZ 纹理可用 JPEG、PNG 或 OpenEXR。不会隐式接受其他 ImageIO 可解码格式。外层包中的 3D 制作格式也会被拒绝。USDZ 仅允许包含 USD/USDA/USDC、JPEG、PNG、OpenEXR、M4A、WAV 和 MP3 成员。
| 资源预算 | v1 限制 |
|---|---|
| 下载文件或本地归档 | 1 GiB |
| ZIP 条目数 | 4,096 |
| 根 manifest | 1 MiB |
| 任一资源文件 | 768 MiB |
| 解压后载荷总量 | 2 GiB |
| 压缩比 | 200:1 |
| 任一导入的 JSON 文件 | 1 MiB |
| 来源信息 JSON | 64 KiB |
| 图像尺寸 | 32,768 px |
| 一张图像 | 1.5 亿像素 |
| 所有 View 图像 | 5 亿像素 |
| USDZ 条目数 | 4,096 |
| USDZ 文件大小/解压后字节数 | 768 MiB / 1.5 GiB |
| USDZ 纹理尺寸/总像素数 | 16,384 px/5 亿 |
| 已加载的 Room 几何体 | 500 万个三角形、1,024 个材质、100,000 个实体 |
文件和 URL 导入
导入 View 时,可以从照片、文件或公开的直接 HTTPS URL 选择一张 JPG、PNG 或 HEIC。Locus 会将图像规范化为仅含 Destination 的 Package v2,并通过同一个校验器和原子安装流程处理这个临时包。文件和 URL 也支持完整的 Package v2 ZIP,可用于 View、Room,以及包含访客所选内容类型的组合包。现有扁平 View/Room ZIP 仍可作为兼容输入。如果格式错误的归档包含 locusplace.json ,会被视为无效完整包而拒绝,绝不会回退到扁平解析器。URL 导入拒绝凭据、片段、localhost、 .local及非公网 DNS 解析结果。每次请求只解析一次主机名,所有返回地址都必须是公网地址。TLS 连接会连接到解析得到的确切数字 IP,同时验证原始主机名。每次重定向都会在建立新连接前重新解析并固定地址,最多跟随五次重定向;声明大小和实际流式下载大小都受到限制。
校验与目录解析在同一卷的私有暂存目录内完成。整个库的咨询锁会串行处理清单、配额检查及提交。首次安装使用排他的原子重命名,更新使用原子目录交换。任何失败都会移除暂存内容,并保持已安装包不变。
包校验与示例
以下命令维护发布及完整功能导入所用的完整 Package v2 外层封装。旧扁平格式工具仍记录在
locus-asset-format.md.
无需安装即可校验任意一种文件名:
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
Python 代码使用标准库、通过以下方式调用的 macOS ImageIO: /usr/bin/sips,以及 USD 工具 usdchecker 和 usdcat (随 Xcode/命令行工具安装)。完成 JPEG 或 PNG 结构校验后,ImageIO 会解码全部像素;EXR 校验仅用于 USDZ 内部的纹理。USD 工具会加载实际的房间 USD 场景、展开其图层并测量几何数据。任何校验失败都会返回非零退出码。检查范围涵盖归档、完整性、Package-v2、来源信息、图像/EXR 和 USDZ。安装及跨目录引用解析仍以 visionOS 导入器为准。
将可编辑目录打包为普通 ZIP,重新计算所有完整性记录,并仅在校验器接受结果后输出:
python3 tools/pack_locusplace.py --current-app-version 1.4.4 MyEditablePlace MyPlace.zip
在临时目录或已忽略的目录中生成真实的纯 View、纯 Room 和组合示例归档,然后校验:
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
示例从源代码生成,无须下载私有资产或调用付费服务。构建器与预期目录结构记录在 示例 README.