mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-11-29 15:22:58 +08:00
461 lines
11 KiB
TypeScript
461 lines
11 KiB
TypeScript
import { mat3, mat4, ReadonlyMat3, ReadonlyVec3, vec2, vec3 } from 'gl-matrix';
|
||
|
||
export interface ITransformUpdatable {
|
||
updateTransform?(): void;
|
||
}
|
||
|
||
export class Transform {
|
||
mat: mat3 = mat3.create();
|
||
|
||
x: number = 0;
|
||
y: number = 0;
|
||
scaleX: number = 1;
|
||
scaleY: number = 1;
|
||
rad: number = 0;
|
||
|
||
/** 有没有对这个Transform进行过修改,用于优化常规表现 */
|
||
private modified: boolean = false;
|
||
|
||
/** 绑定的可更新元素 */
|
||
bindedObject?: ITransformUpdatable;
|
||
|
||
/**
|
||
* 对这个变换实例添加绑定对象,当矩阵变换时,自动调用其 update 函数
|
||
* @param obj 要绑定的对象
|
||
*/
|
||
bind(obj?: ITransformUpdatable) {
|
||
this.bindedObject = obj;
|
||
}
|
||
|
||
/**
|
||
* 重设所有参数
|
||
*/
|
||
reset() {
|
||
mat3.identity(this.mat);
|
||
this.x = 0;
|
||
this.y = 0;
|
||
this.scaleX = 1;
|
||
this.scaleY = 1;
|
||
this.rad = 0;
|
||
this.modified = false;
|
||
this.bindedObject?.updateTransform?.();
|
||
}
|
||
|
||
/**
|
||
* 修改缩放,叠加关系
|
||
*/
|
||
scale(x: number, y: number = x): this {
|
||
mat3.scale(this.mat, this.mat, [x, y]);
|
||
this.scaleX *= x;
|
||
this.scaleY *= y;
|
||
this.modified = true;
|
||
this.bindedObject?.updateTransform?.();
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 移动,叠加关系
|
||
*/
|
||
translate(x: number, y: number): this {
|
||
mat3.translate(this.mat, this.mat, [x, y]);
|
||
this.x += x;
|
||
this.y += y;
|
||
this.modified = true;
|
||
this.bindedObject?.updateTransform?.();
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 旋转,叠加关系
|
||
*/
|
||
rotate(rad: number): this {
|
||
mat3.rotate(this.mat, this.mat, rad);
|
||
this.rad += rad;
|
||
if (this.rad >= Math.PI * 2) {
|
||
const n = Math.floor(this.rad / Math.PI / 2);
|
||
this.rad -= n * Math.PI * 2;
|
||
}
|
||
this.modified = true;
|
||
this.bindedObject?.updateTransform?.();
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 设置缩放,非叠加关系
|
||
*/
|
||
setScale(x: number, y: number = x): this {
|
||
mat3.scale(this.mat, this.mat, [x / this.scaleX, y / this.scaleY]);
|
||
this.scaleX = x;
|
||
this.scaleY = y;
|
||
this.modified = true;
|
||
this.bindedObject?.updateTransform?.();
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 设置位置,非叠加关系
|
||
*/
|
||
setTranslate(x: number, y: number): this {
|
||
mat3.translate(this.mat, this.mat, [x - this.x, y - this.y]);
|
||
this.x = x;
|
||
this.y = y;
|
||
this.modified = true;
|
||
this.bindedObject?.updateTransform?.();
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 设置旋转,非叠加关系
|
||
*/
|
||
setRotate(rad: number): this {
|
||
mat3.rotate(this.mat, this.mat, rad - this.rad);
|
||
this.rad = rad;
|
||
this.modified = true;
|
||
this.bindedObject?.updateTransform?.();
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 设置变换矩阵,叠加模式
|
||
* @param a 水平缩放
|
||
* @param b 垂直倾斜
|
||
* @param c 水平倾斜
|
||
* @param d 垂直缩放
|
||
* @param e 水平移动
|
||
* @param f 垂直移动
|
||
*/
|
||
transform(
|
||
a: number,
|
||
b: number,
|
||
c: number,
|
||
d: number,
|
||
e: number,
|
||
f: number
|
||
): this {
|
||
mat3.multiply(
|
||
this.mat,
|
||
this.mat,
|
||
mat3.fromValues(a, b, 0, c, d, 0, e, f, 1)
|
||
);
|
||
this.calAttributes();
|
||
this.bindedObject?.updateTransform?.();
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 设置变换矩阵,非叠加模式
|
||
* @param a 水平缩放
|
||
* @param b 垂直倾斜
|
||
* @param c 水平倾斜
|
||
* @param d 垂直缩放
|
||
* @param e 水平移动
|
||
* @param f 垂直移动
|
||
*/
|
||
setTransform(
|
||
a: number,
|
||
b: number,
|
||
c: number,
|
||
d: number,
|
||
e: number,
|
||
f: number
|
||
): this {
|
||
mat3.set(this.mat, a, b, 0, c, d, 0, e, f, 1);
|
||
this.calAttributes();
|
||
this.bindedObject?.updateTransform?.();
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 重新计算 translation scaling rotation
|
||
*/
|
||
calAttributes() {
|
||
const [x, y] = getTranslation(this.mat);
|
||
const [scaleX, scaleY] = getScaling(this.mat);
|
||
const rad = getRotation(this.mat);
|
||
this.x = x;
|
||
this.y = y;
|
||
this.scaleX = scaleX;
|
||
this.scaleY = scaleY;
|
||
this.rad = rad;
|
||
if (x === 0 && y === 0 && scaleX === 1 && scaleY === 1 && rad === 0) {
|
||
this.modified = false;
|
||
} else {
|
||
this.modified = true;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 与一个矩阵相乘,返回其计算结果(不改变原矩阵)
|
||
* @param transform 变换矩阵
|
||
*/
|
||
multiply(transform: Transform): Transform {
|
||
if (this.modified) {
|
||
const result = new Transform();
|
||
mat3.multiply(result.mat, this.mat, transform.mat);
|
||
result.modified = true;
|
||
return result;
|
||
} else {
|
||
return transform.clone();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 复制这个变换矩阵
|
||
*/
|
||
clone() {
|
||
const transform = new Transform();
|
||
transform.mat = mat3.clone(this.mat);
|
||
return transform;
|
||
}
|
||
|
||
/**
|
||
* 根据变换矩阵的信息,将一个点转换为计算后的位置
|
||
* @param x 横坐标
|
||
* @param y 纵坐标
|
||
*/
|
||
transformed(x: number, y: number): vec3 {
|
||
if (!this.modified) return [x, y, 1];
|
||
return multiplyVec3(this.mat, [x, y, 1]);
|
||
}
|
||
|
||
/**
|
||
* 根据变换矩阵的信息,将一个计算后的位置逆转换为原位置
|
||
* @param x 横坐标
|
||
* @param y 纵坐标
|
||
*/
|
||
untransformed(x: number, y: number): vec3 {
|
||
if (!this.modified) return [x, y, 1];
|
||
const invert = mat3.create();
|
||
mat3.invert(invert, this.mat);
|
||
return multiplyVec3(invert, [x, y, 1]);
|
||
}
|
||
|
||
/**
|
||
* 根据变换矩阵的信息,将一个点转换为计算后的位置
|
||
* @param transform 变换矩阵
|
||
* @param x 横坐标
|
||
* @param y 纵坐标
|
||
*/
|
||
static transformed(transform: Transform, x: number, y: number) {
|
||
return multiplyVec3(transform.mat, [x, y, 1]);
|
||
}
|
||
|
||
/**
|
||
* 根据变换矩阵的信息,将一个计算后的位置逆转换为原位置
|
||
* @param transform 变换矩阵
|
||
* @param x 横坐标
|
||
* @param y 纵坐标
|
||
*/
|
||
static untransformed(transform: Transform, x: number, y: number) {
|
||
const invert = mat3.create();
|
||
mat3.invert(invert, transform.mat);
|
||
return multiplyVec3(invert, [x, y, 1]);
|
||
}
|
||
|
||
/** 单位矩阵 */
|
||
static get identity() {
|
||
return new Transform();
|
||
}
|
||
}
|
||
|
||
function multiplyVec3(mat: ReadonlyMat3, vec: ReadonlyVec3): vec3 {
|
||
const v0 = vec[0];
|
||
const v1 = vec[1];
|
||
const v2 = vec[2];
|
||
return [
|
||
mat[0] * v0 + mat[3] * v1 + mat[6] * v2,
|
||
mat[1] * v0 + mat[4] * v1 + mat[7] * v2,
|
||
mat[2] * v0 + mat[5] * v1 + mat[8] * v2
|
||
];
|
||
}
|
||
|
||
function getTranslation(mat: ReadonlyMat3): vec2 {
|
||
return [mat[6], mat[7]];
|
||
}
|
||
|
||
function getScaling(mat: ReadonlyMat3): vec2 {
|
||
return [Math.hypot(mat[0], mat[3]), Math.hypot(mat[1], mat[4])];
|
||
}
|
||
|
||
function getRotation(mat: ReadonlyMat3): number {
|
||
return Math.atan2(mat[3], mat[0]);
|
||
}
|
||
|
||
export class Transform3D {
|
||
mat: mat4 = mat4.create();
|
||
|
||
/** 绑定的可更新元素 */
|
||
bindedObject?: ITransformUpdatable;
|
||
|
||
/**
|
||
* 绑定可更新对象
|
||
* @param obj 要绑定的对象
|
||
*/
|
||
bind(obj?: ITransformUpdatable) {
|
||
this.bindedObject = obj;
|
||
}
|
||
|
||
/**
|
||
* 重置为单位矩阵
|
||
*/
|
||
reset(): this {
|
||
mat4.identity(this.mat);
|
||
this.bindedObject?.updateTransform?.();
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 应用缩放变换
|
||
* @param x X轴缩放
|
||
* @param y Y轴缩放
|
||
* @param z Z轴缩放
|
||
*/
|
||
scale(x: number, y: number, z: number): this {
|
||
mat4.scale(this.mat, this.mat, [x, y, z]);
|
||
this.bindedObject?.updateTransform?.();
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 应用平移变换
|
||
* @param x X轴平移
|
||
* @param y Y轴平移
|
||
* @param z Z轴平移
|
||
*/
|
||
translate(x: number, y: number, z: number): this {
|
||
mat4.translate(this.mat, this.mat, [x, y, z]);
|
||
this.bindedObject?.updateTransform?.();
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 应用旋转变换
|
||
* @param rad 旋转角度(弧度)
|
||
* @param axis 旋转轴
|
||
*/
|
||
rotate(rad: number, axis: vec3): this {
|
||
mat4.rotate(this.mat, this.mat, rad, axis);
|
||
this.bindedObject?.updateTransform?.();
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 应用绕X轴旋转
|
||
* @param rad 旋转角度(弧度)
|
||
*/
|
||
rotateX(rad: number): this {
|
||
return this.rotate(rad, [1, 0, 0]);
|
||
}
|
||
|
||
/**
|
||
* 应用绕Y轴旋转
|
||
* @param rad 旋转角度(弧度)
|
||
*/
|
||
rotateY(rad: number): this {
|
||
return this.rotate(rad, [0, 1, 0]);
|
||
}
|
||
|
||
/**
|
||
* 应用绕Z轴旋转
|
||
* @param rad 旋转角度(弧度)
|
||
*/
|
||
rotateZ(rad: number): this {
|
||
return this.rotate(rad, [0, 0, 1]);
|
||
}
|
||
|
||
/**
|
||
* 设置视图矩阵
|
||
* @param eye 摄像机位置
|
||
* @param center 目标位置
|
||
* @param up 上方向量
|
||
*/
|
||
lookAt(eye: vec3, center: vec3, up: vec3): this {
|
||
mat4.lookAt(this.mat, eye, center, up);
|
||
this.bindedObject?.updateTransform?.();
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 设置透视投影矩阵
|
||
* @param fovy 垂直视野角度(弧度)
|
||
* @param aspect 宽高比
|
||
* @param near 近平面
|
||
* @param far 远平面
|
||
*/
|
||
perspective(fovy: number, aspect: number, near: number, far: number): this {
|
||
mat4.perspective(this.mat, fovy, aspect, near, far);
|
||
this.bindedObject?.updateTransform?.();
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 设置正交投影矩阵
|
||
* @param left 左平面
|
||
* @param right 右平面
|
||
* @param bottom 底平面
|
||
* @param top 顶平面
|
||
* @param near 近平面
|
||
* @param far 远平面
|
||
*/
|
||
ortho(
|
||
left: number,
|
||
right: number,
|
||
bottom: number,
|
||
top: number,
|
||
near: number,
|
||
far: number
|
||
): this {
|
||
mat4.ortho(this.mat, left, right, bottom, top, near, far);
|
||
this.bindedObject?.updateTransform?.();
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 与另一个变换矩阵相乘
|
||
* @param transform 另一个变换矩阵
|
||
*/
|
||
multiply(transform: Transform3D): Transform3D {
|
||
const result = new Transform3D();
|
||
mat4.multiply(result.mat, this.mat, transform.mat);
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 克隆当前变换矩阵
|
||
*/
|
||
clone(): Transform3D {
|
||
const clone = new Transform3D();
|
||
mat4.copy(clone.mat, this.mat);
|
||
return clone;
|
||
}
|
||
|
||
/**
|
||
* 将点应用变换
|
||
* @param point 要变换的点
|
||
*/
|
||
transformed(point: vec3): vec3 {
|
||
const result = vec3.create();
|
||
vec3.transformMat4(result, point, this.mat);
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 将点应用逆变换
|
||
* @param point 要逆变换的点
|
||
*/
|
||
untransformed(point: vec3): vec3 {
|
||
const inverse = mat4.create();
|
||
mat4.invert(inverse, this.mat);
|
||
const result = vec3.create();
|
||
vec3.transformMat4(result, point, inverse);
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 静态方法:单位矩阵
|
||
*/
|
||
static get identity(): Transform3D {
|
||
return new Transform3D();
|
||
}
|
||
}
|