geom.js
4.04 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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// geom.js — 纯渲染几何(接收数字、返回写入值,零 Pixi、零求值)。
//
// 这里只放「把 core 已算好的 SlotTransform / facing / worldY 翻译成 Sprite 写入值」时
// 需要的纯几何换算:facing 翻转符号、头部扭头反射轴镜像、纵深透视缩放与 zIndex。
//
// 红线:本文件**绝不**出现矩阵相乘 / 关键帧采样 / 镜头补间——那些是 @drama/core 的活
// (render-pixi §5.1)。core 已把世界矩阵 decompose 成 SlotTransform,这里只摆放。
// 镜像反射轴这类「依赖具体贴图锚点的纯渲染几何」按 render-pixi §3 留在本包。
const DEG = Math.PI / 180
/**
* 贴图基准朝向约定(移植自原型 character-actor.js `_applyRootScale`):
* 本套角色贴图基准朝向 = 屏幕左;故 facing='left' 不翻转,facing='right' 才水平翻转。
* 返回根容器水平缩放符号(±1),调用方乘到 `root.scale.x` 上。
* @param {'left'|'right'} facing
* @returns {number} -1(翻转)或 1(不翻转)
*/
export function bodyFlipSign(facing) {
return facing === 'right' ? -1 : 1
}
/**
* 头部独立扭头镜像符号(移植自原型 `update` 的 headMirror):
* headFacing 与 facing 不同 → 头 + 脸水平翻转(看向另一边),返回 -1;否则 1。
* @param {'left'|'right'} facing
* @param {('left'|'right'|null)=} headFacing
* @returns {number} -1 或 1
*/
export function headMirrorSign(facing, headFacing) {
return headFacing && headFacing !== facing ? -1 : 1
}
/**
* 头/脸部件在扭头镜像下的位置反射(移植自原型 `px = 2*headBoneX - d.x`):
* 翻转的反射轴 = 头骨节点 x;脸的偏移也要绕它镜像才不错位。
* 仅当 headMirror===-1 时反射;否则原样返回。
* @param {number} x 部件原始 x(来自 SlotTransform)
* @param {number} headBoneX 头骨世界 x(反射轴)
* @param {number} headMirror -1 或 1
* @returns {number}
*/
export function reflectHeadX(x, headBoneX, headMirror) {
return headMirror === -1 ? 2 * headBoneX - x : x
}
/**
* 把一个 SlotTransform 解出最终要写进 Sprite 的属性值(纯数据→纯数据)。
* mirror=true 触发水平翻转(scale.x *= -1);可选叠加头部扭头镜像(slot 为 head/face 时)。
* @param {import('@drama/core').SlotTransform} t
* @param {object} [mods]
* @param {number=} mods.headMirror -1/1,仅对 head/face slot 传入
* @param {number=} mods.headBoneX 反射轴 x,仅 headMirror=-1 时用
* @param {boolean=} mods.isHeadSlot 该 slot 是否参与扭头镜像(head/face)
* @returns {{x:number,y:number,rotation:number,scaleX:number,scaleY:number}}
*/
export function resolveSlotWrite(t, mods = {}) {
const headMirror = mods.isHeadSlot ? (mods.headMirror || 1) : 1
let scaleX = t.scaleX
if (t.mirror) scaleX = -scaleX
scaleX *= headMirror
let x = t.x
if (mods.isHeadSlot && headMirror === -1) {
x = reflectHeadX(t.x, mods.headBoneX || 0, headMirror)
}
return { x, y: t.y, rotation: t.rotation, scaleX, scaleY: t.scaleY }
}
/**
* 纵深透视缩放(移植自原型 `effScale` 的 depthScale 部分):
* 线性插值:y=nearY(近/下)→ nearScale,y=farY(远/上)→ farScale,区间外夹紧。
* 返回缩放倍数(相对 baseScale 的乘数)。无 depth 配置时调用方应跳过(恒 1)。
* @param {number} worldY
* @param {{nearY:number,farY:number,nearScale:number,farScale:number}} depth
* @returns {number}
*/
export function perspectiveScale(worldY, depth) {
const { nearY, farY, nearScale, farScale } = depth
const t = (worldY - farY) / (nearY - farY || 1)
const clamped = Math.max(0, Math.min(1, t))
return farScale + (nearScale - farScale) * clamped
}
/**
* 纵深遮挡排序键(移植自原型 `zIndex = Math.round(worldY)`):
* 越靠下(worldY 越大=越近)越靠前。遮挡片用 baseY 同尺度,人物 worldY 小于 baseY 被遮。
* @param {number} worldY
* @returns {number}
*/
export function depthZIndex(worldY) {
return Math.round(worldY)
}
export { DEG }