index.js
3.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// @drama/render-pixi —— Renderer 接口的 PixiJS v8 后端,公共面。
//
// 唯一公共入口(架构「禁止深导入」红线)。只导出 createPixiRenderer + CharacterSkin/SlotSkin
// typedef(render-pixi §3 / §5.6 公共面最小化)。
//
// Pixi 只在本包出现(render-pixi §5.2)。本包对外暴露的方法签名零 Pixi 入参:core 调
// applyActorPose/applyCamera 等时只传 SlotTransform / CameraRenderState / string / number。
// PIXI 命名空间在工厂处一次性解析(不外溢到接口):
// - 若 `opts.PIXI` 给了就用它(测试注入 stub、或调用方已持有的命名空间);
// - 否则动态 import 'pixi.js'。动态而非顶层静态 import,是为了让无 GPU 的 headless
// 单测(注入 stub Sprite)无需安装 / 加载真实 pixi.js 即可跑 §8 的纯映射用例。
import { createPixiRenderer as _createPixiRenderer } from './renderer.js'
/**
* @typedef {{
* rig: import('@drama/rig').RigDefinition,
* PIXI?: *,
* loadTexture?: (url:string)=>Promise<*>,
* resolveEffect?: (name:string)=>(import('./effects.js').EffectDef|null),
* faceFrameDt?: number,
* }} RendererOpts 工厂注入项:rig 必填;PIXI 在工厂处一次注入(不外溢到接口)。
*/
/**
* @typedef {import('@drama/core').Renderer & {
* addActor:(actorId:string, skin:CharacterSkin, placement:object)=>void,
* removeActor:(actorId:string)=>void,
* loadScene:(def?:object)=>Promise<void>,
* updateEffects:(dtMs:number)=>void,
* }} PixiRenderer @drama/core Renderer(零 Pixi 入参)+ 出场 / 换场扩展方法
*/
/**
* 创建满足 @drama/core Renderer 接口的 PixiJS 渲染后端。
* @param {import('pixi.js').Application} app 已 init 的 Pixi Application(由 player-vue / composition root 建)
* @param {RendererOpts} opts
* @returns {PixiRenderer}
*/
export function createPixiRenderer(app, opts) {
if (opts && opts.PIXI) return _createPixiRenderer(app, opts)
throw new Error(
'createPixiRenderer: opts.PIXI is required. Pass the imported `* as PIXI` namespace ' +
'(e.g. `import * as PIXI from "pixi.js"`). Renderer 方法签名仍零 Pixi——PIXI 仅在工厂处一次注入。',
)
}
/**
* 与 createPixiRenderer 等价,但自动 `import('pixi.js')` 注入 PIXI(浏览器 / 已装 pixi.js 环境)。
* 给 player-vue / studio 直接用;headless 单测走 createPixiRenderer + 注入 stub。
* @param {import('pixi.js').Application} app
* @param {Omit<RendererOpts,'PIXI'>} opts 同 createPixiRenderer(无需传 PIXI)
* @returns {Promise<PixiRenderer>}
*/
export async function createPixiRendererAuto(app, opts) {
const PIXI = await import('pixi.js')
return _createPixiRenderer(app, { ...opts, PIXI })
}
// ───────────────────────── 公共形状契约(render-pixi §3) ─────────────────────────
/**
* @typedef {Object} CharacterSkin 渲染需要的贴皮数据(来自 @drama/assets 的 character 出包)。
* @property {string} rigId
* @property {Record<string, SlotSkin>} parts slot 名 -> 该部件贴皮
*/
/**
* @typedef {Object} SlotSkin
* @property {string=} url 普通部件贴图
* @property {{x:number,y:number}=} anchor
* @property {number=} scale
* @property {number=} rotation
* @property {boolean=} mirror
* @property {number=} zOrder 覆盖 slot 默认层级
* @property {{x:number,y:number}=} bonePos 关节位置覆盖(对齐用)
* @property {Record<string,{static:string,talk?:string,mirror?:boolean,scale?:number}>=} emotions face 专用情绪帧
* @property {number=} talkFps 口型轮播帧率(face 用)
*/