Skip to content
On this page

3D 模型与动作

SpectrumGraphics 把 Blockbench 工程视为作者输入,而不是运行时格式。.bbmodel 先被严格校验、归一化并编译成带 SHA-256 内容哈希的 .sgmodel;客户端渲染线程只读取稳定的 .sgmodel

能力阶段

阶段已实现内容
M1Blockbench bbmodel V4/V5 编译、Cube、刚性 Mesh、多贴图、骨骼、Locator、动画轨道
M2世界模型实例、Anchor、距离/视锥裁剪、光照和材质路径、替换锚定实体
M3独立动作播放器、循环/单次、速度、暂停/恢复/seek/stop、cross-fade、完成事件
M4版本化模型场景协议、Pack models/、热重载、资源哈希与管理指令
M5typed 参数、状态、条件/完成迁移、优先级、cross-fade 与有序事件

从 Blockbench 编译

powershell
.\gradlew.bat :core:model3d:compileSgModel `
  -PsgModelInput=example.bbmodel `
  -PsgModelOutput=build/models/example.sgmodel `
  -PsgModelId=example:npc

支持:

  • bbmodel V4/V5;
  • Cube、刚性 Mesh、多贴图材质;
  • 父子骨骼、Locator 和静止姿势;
  • Linear、Step、Catmull-Rom、Bezier 关键帧;
  • V4/V5 动画轴差异归一化;
  • 源文件、骨骼、顶点、三角形、动画和关键帧预算。

当前一个顶点只归属一个骨骼,即刚性蒙皮。权重蒙皮、IK、MoLang/Bedrock 控制器和相机轨道尚未伪装为已支持能力。

发布 .sgmodel

text
plugins/SpectrumGraphics/resource/models/guide.sgmodel

然后:

text
/sg assets build model-2026-01
/sg assets publish model-2026-01

模型场景 YAML 和模型二进制是不同内容:YAML 放在 Pack models/ 或独立 model/.sgmodel 放在独立客户端 resource/ 发布中。

模型场景

Pack models/guide.yml

yaml
schema: spectrumgraphics/model/v1
scene: example:guide

assets:
  example:guide:
    source: models/guide.sgmodel

instances:
  - id: guide
    asset: example:guide
    animation: idle
    replaceEntity: true
    anchor:
      entityName:
        pattern: '^向导(?: · .+)?$'
        match: regex
        point: feet
        missing: last-known
    offset: [0, 0, 0]
    rotation: [0, 0, 0]
    scale: 1
    visibility:
      maxDistance: 64
      fadeFrom: 48

清单需要:

yaml
features: [models, assets]

独立场景放在 plugins/SpectrumGraphics/model/*.yml

Anchor

模型与 WorldCanvas 共用 Anchor 语义:

yaml
anchor: { location: [10, 64, 10], face: center }
anchor: { position: [10.5, 64.0, 10.5] }
anchor: { entity: 550e8400-e29b-41d4-a716-446655440000, point: center }
anchor: { localPlayer: true, point: eyes }
anchor: { entityName: 任务使者 }

显示名正则:

yaml
anchor:
  entityName:
    pattern: '^任务使者(?: · .+)?$'
    match: regex
    point: feet
    missing: last-known

非法正则在加载阶段拒绝。有多个匹配时选择最近实体并用 UUID 稳定打破平局。replaceEntity: true 只在锚定模型有效时隐藏选中的原版实体。

动作播放器

每个实例有独立 ModelAnimationPlayer

  • playpauseresumestopseek
  • loop 或 once;
  • 播放速度;
  • cross-fade;
  • 动作完成只发出一次;
  • 旋转转为四元数并用最短路径 Slerp,避免欧拉角翻转。

管理指令暴露常用播放与停止,完整控制可通过 API 或 M5 Controller。

M5 动作控制器

yaml
controllers:
  locomotion:
    initial: idle
    parameters:
      moving: false
      speed: 0
      stance: normal
    states:
      idle: { animation: idle, loop: true }
      walk: { animation: walk, loop: true, speed: 1.0 }
      wave: { animation: wave, loop: false }
    transitions:
      - from: idle
        to: walk
        when: { moving: true }
        fade: 150
        priority: 10
      - from: walk
        to: idle
        conditions:
          - { parameter: speed, op: less-or-equal, value: 0 }
        fade: 150
      - from: wave
        to: idle
        on: completion
        fade: 100

instances:
  - id: guide
    asset: example:guide
    controller: locomotion
    anchor: { position: [0, 64, 0] }

参数只允许 boolean、number、string,并在加载和运行时检查类型。比较操作:equalsnot-equalsgreatergreater-or-equallessless-or-equal;boolean/string 只适合相等与不等。

一次求值最多执行一个迁移:优先选择 priority 较高者,相同优先级按目标状态 ID 稳定排序。on: completion 适合非循环动作完成后回到待机。

指令测试

text
/sg model load
/sg model spawn example:guide
/sg model play example:guide guide walk
/sg model stop example:guide guide

控制台需要在 spawn/play/stop 最后指定在线玩家。play 指令使用 150 ms cross-fade 并播放一次。

修改 YAML 后再次 /sg model load:命令先原子校验,失败保留旧场景;成功后已经 spawn 且绑定仓库的场景会自动更新。

API 驱动参数

kotlin
SpectrumGraphics.api.setModelParameters(
    player,
    "example:guide",
    "guide",
    mapOf(
        "moving" to ModelParameterValue.BooleanValue(true),
        "speed" to ModelParameterValue.NumberValue(1.0),
    ),
)

val subscription = SpectrumGraphics.api.onModelEvent { target, event ->
    // 动作完成或控制器迁移事件
}

写操作必须在服务端主线程调用。关闭插件时应关闭 subscription。