blobt
4 years ago
17 changed files with 2763 additions and 9 deletions
-
22src/App.tsx
-
7src/examples/Test.ts
-
2src/render/core/Application.ts
-
2src/render/core/Timer.ts
-
415src/renderi/commons/Util.ts
-
57src/renderi/commons/types.ts
-
406src/renderi/core/Application.ts
-
88src/renderi/core/Event.ts
-
42src/renderi/core/Timer.ts
-
50src/renderi/lib/CameraApplication.ts
-
700src/renderi/math/Mat4.ts
-
470src/renderi/math/Quat.ts
-
42src/renderi/math/Vec2.ts
-
303src/renderi/math/Vec3.ts
-
136src/renderi/math/Vec4.ts
-
6src/renderi/math/index.ts
-
24src/renderi/webgl/GLApplication.ts
@ -0,0 +1,7 @@ |
|||||
|
import { Application } from "../renderi/core/Application"; |
||||
|
|
||||
|
export class Test extends Application { |
||||
|
public constructor(canvas: HTMLCanvasElement) { |
||||
|
super(canvas); |
||||
|
} |
||||
|
} |
@ -0,0 +1,415 @@ |
|||||
|
import { EPSILON, EPlaneLoc } from "./types"; |
||||
|
import { vec2, vec3, vec4, mat4 } from "../math"; |
||||
|
import { quat } from "../math/Quat"; |
||||
|
|
||||
|
export class Util { |
||||
|
/** |
||||
|
* 判断参数x是否是2的n次方,即x是不是1、2、4、8、16、32、64、..... |
||||
|
* @param x |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static isPowerOfTwo(x: number) { |
||||
|
return (x & (x - 1)) === 0; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 取下一个2的n次方数 |
||||
|
* 如果x为3,则返回4 |
||||
|
* 如果x为4,则返回4 |
||||
|
* 如果x为5,则返回8 |
||||
|
* @param x |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static getNextPowerOfTwo(x: number): number { |
||||
|
if (x <= 0) { |
||||
|
throw new Error("参数必须要大于0!") |
||||
|
} |
||||
|
--x; |
||||
|
for (var i = 1; i < 32; i <<= 1) { |
||||
|
let t:number = x >> i; |
||||
|
x = x | t; |
||||
|
} |
||||
|
return x + 1; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 角度/弧度互转函数 |
||||
|
* @param degree |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static toRadian(degree: number): number { |
||||
|
return degree * Math.PI / 180; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 浮点容差相等函数 |
||||
|
* @param left |
||||
|
* @param right |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static numberEuaqls(left: number, right: number): boolean { |
||||
|
|
||||
|
if (Math.abs(left - right) > EPSILON) { |
||||
|
return false; |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* |
||||
|
* @param x |
||||
|
* @param min |
||||
|
* @param max |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static clamp(x: number, min: number, max: number): number { |
||||
|
return (x < min) ? min : (x > max) ? max : x; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 将局部坐标系标示的一个点变换到屏幕坐标系上 |
||||
|
* @param localPt |
||||
|
* @param mvp |
||||
|
* @param viewport |
||||
|
* @param viewportPt |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static obj2GLViewportSpace(localPt: vec3, mvp: mat4, viewport: Int32Array | Float32Array, viewportPt: vec3): boolean { |
||||
|
|
||||
|
let v: vec4 = new vec4([localPt.x, localPt.y, localPt.z, 1.0]); |
||||
|
//这里相当于 v = mvp * v, 将顶点从local坐标系变换到投影坐标系
|
||||
|
mvp.multiplyVec4(v, v); |
||||
|
|
||||
|
//如果变换后的w为0,则返回false
|
||||
|
if (v.w === 0.0) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 将裁剪坐标系的点的x / y / z分量除以w,得到normalized坐标值[ -1 , 1 ]之间
|
||||
|
v.x /= v.w; |
||||
|
v.y /= v.w; |
||||
|
v.z /= v.w; |
||||
|
|
||||
|
// [-1 , 1]标示的点变换到视口坐标系
|
||||
|
v.x = v.x * 0.5 + 0.5; |
||||
|
v.y = v.y * 0.5 + 0.5; |
||||
|
v.z = v.z * 0.5 + 0.5; |
||||
|
|
||||
|
// 视口坐标系再变换到屏幕坐标系
|
||||
|
viewportPt.x = v.x * viewport[2] + viewport[0]; |
||||
|
viewportPt.y = v.y * viewport[3] + viewport[1]; |
||||
|
viewportPt.z = v.z; |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 给出3个点,计算法向 |
||||
|
* @param a |
||||
|
* @param b |
||||
|
* @param c |
||||
|
* @param result |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static computeNormal(a: vec3, b: vec3, c: vec3, result: vec3 | null): vec3 { |
||||
|
if (!result) { |
||||
|
result = new vec3(); |
||||
|
} |
||||
|
|
||||
|
let l0: vec3 = new vec3(); |
||||
|
let l1: vec3 = new vec3(); |
||||
|
|
||||
|
vec3.difference(b, a, l0); |
||||
|
vec3.difference(c, a, l1); |
||||
|
vec3.cross(l0, l1, result); |
||||
|
result.normalize(); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 通过3个点,创建一个平面 |
||||
|
* @param a |
||||
|
* @param b |
||||
|
* @param c |
||||
|
* @param result |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static planeFromPoints(a: vec3, b: vec3, c: vec3, result: vec4 | null = null): vec4 { |
||||
|
if (!result) { |
||||
|
result = new vec4(); |
||||
|
} |
||||
|
|
||||
|
let normal: vec3 = new vec3(); |
||||
|
Util.computeNormal(a, b, c, normal); |
||||
|
let d: number = -vec3.dot(normal, a); |
||||
|
|
||||
|
result.x = normal.x; |
||||
|
result.y = normal.y; |
||||
|
result.z = normal.z; |
||||
|
result.w = d; |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 通过平面的一个点和法线创建一个平面 |
||||
|
* @param p |
||||
|
* @param normal |
||||
|
* @param result |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static planeFromPointNormal(p: vec3, normal: vec3, result: vec4 | null = null): vec4 { |
||||
|
if (!result) { |
||||
|
result = new vec4(); |
||||
|
} |
||||
|
let d: number = -vec3.dot(normal, p); |
||||
|
|
||||
|
result.x = normal.x; |
||||
|
result.y = normal.y; |
||||
|
result.z = normal.z; |
||||
|
result.w = d; |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 通过多边形创建一个屏幕 |
||||
|
* @param polygon |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static planeFromPolygon(polygon: vec3[]): vec4 { |
||||
|
if (polygon.length < 3) { |
||||
|
throw new Error("The number of vertices of polygon must be greater than or equal to 3 "); |
||||
|
} |
||||
|
return Util.planeFromPoints(polygon[0], polygon[1], polygon[2]); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 点到平面距离 |
||||
|
* @param plane |
||||
|
* @param point |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static planeDistanceFromPoint(plane: vec4, point: vec3): number { |
||||
|
return (point.x * plane.x + point.y * plane.y + point.z * plane.z + plane.w); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断点和平面的关系 |
||||
|
* @param plane |
||||
|
* @param point |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static planeTestPoint(plane: vec4, point: vec3): EPlaneLoc { |
||||
|
let num: number = Util.planeDistanceFromPoint(plane, point); |
||||
|
if (num > EPSILON) { |
||||
|
return EPlaneLoc.FRONT; |
||||
|
} else if (num < - EPSILON) { |
||||
|
return EPlaneLoc.BACK; |
||||
|
} else { |
||||
|
return EPlaneLoc.COPLANAR; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 平面单位化 |
||||
|
* @param plane |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static planeNormalize(plane: vec4): number { |
||||
|
let length: number; |
||||
|
length = Math.sqrt(plane.x * plane.x + plane.y * plane.y + plane.z * plane.z); |
||||
|
if (length === 0) { |
||||
|
throw new Error("The plane area is 0"); |
||||
|
} |
||||
|
plane.x /= length; |
||||
|
plane.y /= length; |
||||
|
plane.z /= length; |
||||
|
plane.w /= length; |
||||
|
return length; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取包围盒 |
||||
|
* @param v |
||||
|
* @param mins |
||||
|
* @param maxs |
||||
|
*/ |
||||
|
public static boundBoxAddPoint(v: vec3, mins: vec3, maxs: vec3): void { |
||||
|
|
||||
|
if (v.x < mins.x) { |
||||
|
mins.x = v.x; |
||||
|
} |
||||
|
if (v.x > maxs.x) { |
||||
|
maxs.x = v.x; |
||||
|
} |
||||
|
|
||||
|
if (v.y < mins.y) { |
||||
|
mins.y = v.y; |
||||
|
} |
||||
|
if (v.y > maxs.y) { |
||||
|
maxs.y = v.y; |
||||
|
} |
||||
|
|
||||
|
if (v.z < mins.z) { |
||||
|
mins.z = v.z; |
||||
|
} |
||||
|
if (v.z > maxs.z) { |
||||
|
maxs.z = v.z; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 清除包围盒 |
||||
|
* @param mins |
||||
|
* @param maxs |
||||
|
* @param value |
||||
|
*/ |
||||
|
public static boundBoxClear(mins: vec3, maxs: vec3, value: number = Infinity): void { |
||||
|
mins.x = mins.y = mins.z = value; // 初始化时,让mins表示浮点数的最大范围
|
||||
|
maxs.x = maxs.y = maxs.z = -value; // 初始化是,让maxs表示浮点数的最小范围
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取包围盒的中心点坐标 |
||||
|
* @param mins |
||||
|
* @param maxs |
||||
|
* @param out |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static boundBoxGetCenter(mins: vec3, maxs: vec3, out: vec3 | null = null): vec3 { |
||||
|
if (out === null) { |
||||
|
out = new vec3(); |
||||
|
} |
||||
|
//(maxs + mins) * 0.5
|
||||
|
vec3.sum(mins, maxs, out); |
||||
|
out.scale(0.5); |
||||
|
return out; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取包围盒的8个顶点 |
||||
|
* @param mins |
||||
|
* @param maxs |
||||
|
* @param pts8 |
||||
|
* /3--------/7 | |
||||
|
* / | / | |
||||
|
* / | / | |
||||
|
* 1---------5 | |
||||
|
* | /2- - -|- -6 |
||||
|
* | / | / |
||||
|
* |/ | / |
||||
|
* 0---------4/ |
||||
|
*/ |
||||
|
public static boundBoxGet8Points(mins: vec3, maxs: vec3, pts8: vec3[]): void { |
||||
|
|
||||
|
//获取中心点
|
||||
|
let center: vec3 = Util.boundBoxGetCenter(mins, maxs); |
||||
|
|
||||
|
//获取最大点到中心点之间的距离向量
|
||||
|
let maxs2center: vec3 = vec3.difference(center, maxs); |
||||
|
|
||||
|
pts8.push(new vec3([center.x + maxs2center.x, center.y + maxs2center.y, center.z + maxs2center.z])); // 0
|
||||
|
pts8.push(new vec3([center.x + maxs2center.x, center.y - maxs2center.y, center.z + maxs2center.z])); // 1
|
||||
|
pts8.push(new vec3([center.x + maxs2center.x, center.y + maxs2center.y, center.z - maxs2center.z])); // 2
|
||||
|
pts8.push(new vec3([center.x + maxs2center.x, center.y - maxs2center.y, center.z - maxs2center.z])); // 3
|
||||
|
pts8.push(new vec3([center.x - maxs2center.x, center.y + maxs2center.y, center.z + maxs2center.z])); // 4
|
||||
|
pts8.push(new vec3([center.x - maxs2center.x, center.y - maxs2center.y, center.z + maxs2center.z])); // 5
|
||||
|
pts8.push(new vec3([center.x - maxs2center.x, center.y + maxs2center.y, center.z - maxs2center.z])); // 6
|
||||
|
pts8.push(new vec3([center.x - maxs2center.x, center.y - maxs2center.y, center.z - maxs2center.z])); // 7
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 包围盒坐标变换 |
||||
|
* @param mat |
||||
|
* @param mins |
||||
|
* @param maxs |
||||
|
*/ |
||||
|
public static boundBoxTransform(mat: mat4, mins: vec3, maxs: vec3): void { |
||||
|
// 获得局部坐标系表示的AABB的8个顶点坐标
|
||||
|
let pts: vec3[] = []; |
||||
|
Util.boundBoxGet8Points(mins, maxs, pts); |
||||
|
|
||||
|
//变换后的顶点
|
||||
|
let out: vec3 = new vec3(); |
||||
|
|
||||
|
for (let i: number = 0; i < pts.length; i++) { |
||||
|
// 将局部坐标表示的顶点变换到mat坐标空间中去,变换后的结果放在out变量中
|
||||
|
mat.multiplyVec3(pts[i], out); |
||||
|
Util.boundBoxAddPoint(out, mins, maxs); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断某个点是否在包围盒内 |
||||
|
* @param point |
||||
|
* @param mins |
||||
|
* @param maxs |
||||
|
*/ |
||||
|
public static boundBoxContainPoint(point: vec3, mins: vec3, maxs: vec3): boolean { |
||||
|
return (point.x >= mins.x && point.x <= maxs.x && point.y >= mins.y && point.y <= maxs.y && point.z >= mins.z && point.z <= maxs.z); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* |
||||
|
* @param min1 判断包围盒1 是的在包围盒2内 |
||||
|
* @param max1 |
||||
|
* @param min2 |
||||
|
* @param max2 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static boundBoxBoundBoxOverlap(min1: vec3, max1: vec3, min2: vec3, max2: vec3): boolean { |
||||
|
if (min1.x > max2.x) return false; |
||||
|
if (max1.x < min2.x) return false; |
||||
|
if (min1.y > max2.y) return false; |
||||
|
if (max1.y < min2.y) return false; |
||||
|
if (min1.z > max2.z) return false; |
||||
|
if (max1.z < min2.z) return false; |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 交换y轴 和 z轴 |
||||
|
* @param v |
||||
|
* @param scale |
||||
|
*/ |
||||
|
public static convertVec3IDCoord2GLCoord(v: vec3, scale: number = 10.0): void { |
||||
|
let f: number = v.y; |
||||
|
v.y = v.z; |
||||
|
v.z = -f; |
||||
|
if (!Util.numberEuaqls(scale, 0) && !Util.numberEuaqls(scale, 1.0)) { |
||||
|
v.x /= scale; |
||||
|
v.y /= scale; |
||||
|
v.z /= scale; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* |
||||
|
* @param v |
||||
|
*/ |
||||
|
public static convertVec2IDCoord2GLCoord(v: vec2): void { |
||||
|
v.y = 1.0 - v.y; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* |
||||
|
* @param pos |
||||
|
* @param q |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static matrixFrom(pos: vec3, q: quat, dest: mat4 | null = null): mat4 { |
||||
|
if (dest === null) { |
||||
|
dest = new mat4(); |
||||
|
} |
||||
|
|
||||
|
q.toMat4(dest); |
||||
|
dest.values[12] = pos.x; |
||||
|
dest.values[13] = pos.y; |
||||
|
dest.values[14] = pos.z; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
} |
@ -0,0 +1,57 @@ |
|||||
|
export let EPSILON: number = 0.0001; |
||||
|
|
||||
|
export enum EShaderType { |
||||
|
VS_SHADER, |
||||
|
FS_SHADER |
||||
|
} |
||||
|
|
||||
|
export enum EGLSLESDataType { |
||||
|
FLOAT_VEC2 = 0x8B50, |
||||
|
FLOAT_VEC3, |
||||
|
FLOAT_VEC4, |
||||
|
INT_VEC2, |
||||
|
INT_VEC3, |
||||
|
INT_VEC4, |
||||
|
BOOL, |
||||
|
BOOL_VEC2, |
||||
|
BOOL_VEC3, |
||||
|
BOOL_VEC4, |
||||
|
FLOAT_MAT2, |
||||
|
FLOAT_MAT3, |
||||
|
FLOAT_MAT4, |
||||
|
SAMPLER_2D, |
||||
|
SAMPLER_CUBE, |
||||
|
|
||||
|
FLOAT = 0x1406, |
||||
|
INT = 0x1404 |
||||
|
} |
||||
|
|
||||
|
export enum EGLTexWrapType { |
||||
|
GL_REPEAT, //设置为gl对应的常量
|
||||
|
GL_MIRRORED_REPEAT, |
||||
|
GL_CLAMP_TO_EDGE |
||||
|
} |
||||
|
|
||||
|
export enum EPlaneLoc { |
||||
|
FRONT, // 在平面的正面
|
||||
|
BACK, // 在平面的背面
|
||||
|
COPLANAR // 与平面共面
|
||||
|
} |
||||
|
|
||||
|
export enum ECameraType { |
||||
|
FPSCAMERA, |
||||
|
FLYCAMERA |
||||
|
} |
||||
|
|
||||
|
export enum EMatrixMode { |
||||
|
MODELVIEW, |
||||
|
PROJECTION, |
||||
|
TEXTURE |
||||
|
} |
||||
|
|
||||
|
export enum EAxisType { |
||||
|
NONE = -1, |
||||
|
XAXIS, |
||||
|
YAXIS, |
||||
|
ZAXIS |
||||
|
} |
@ -0,0 +1,406 @@ |
|||||
|
import { Timer, TimerCallback } from "./Timer"; |
||||
|
import { vec2 } from "../math"; |
||||
|
import { EInputEventType, CanvasMouseEvent, CanvasKeyBoardEvent } from "./Event"; |
||||
|
|
||||
|
export class Application implements EventListenerObject { |
||||
|
/** |
||||
|
* @var 定时器数组 |
||||
|
*/ |
||||
|
public timers: Timer[] = []; |
||||
|
|
||||
|
/** |
||||
|
* @var 定时器id |
||||
|
*/ |
||||
|
private _timerId: number = -1; |
||||
|
|
||||
|
/** |
||||
|
* @var 帧率 |
||||
|
*/ |
||||
|
private _fps: number = 0; |
||||
|
|
||||
|
/** |
||||
|
* @var 是否反转y轴 |
||||
|
*/ |
||||
|
public isFlipYCoord: boolean = false; |
||||
|
|
||||
|
/** |
||||
|
* @var 绘图canvas |
||||
|
*/ |
||||
|
public canvas: HTMLCanvasElement; |
||||
|
|
||||
|
/** |
||||
|
* @var 是否每次移动都会触发mouse事件 |
||||
|
*/ |
||||
|
public isSupportMouseMove: boolean; |
||||
|
|
||||
|
/** |
||||
|
* @var 鼠标是否被按下 |
||||
|
*/ |
||||
|
protected _isMouseDown: boolean; |
||||
|
|
||||
|
/** |
||||
|
* @var 为了支持鼠标按下drag事件 |
||||
|
*/ |
||||
|
protected _isRightMouseDown: boolean = false; |
||||
|
|
||||
|
/** |
||||
|
* @var 是否进入循环状态 |
||||
|
*/ |
||||
|
protected _start: boolean = false; |
||||
|
|
||||
|
/** |
||||
|
* @var 由Window对象的requestAnimationFrame返回的大于0的id号, |
||||
|
* 我们可以使用cancelAnimationFrame ( this ._requestId )来取消动画循环. |
||||
|
*/ |
||||
|
protected _requestId: number = -1; |
||||
|
|
||||
|
/** |
||||
|
* @var 计算当前更新与上一次更新之间的时间差 |
||||
|
*/ |
||||
|
protected _lastTime !: number; |
||||
|
protected _startTime !: number; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* @function 渲染每帧画面的回调函数 |
||||
|
*/ |
||||
|
public frameCallback: ((app: Application) => void) | null; |
||||
|
|
||||
|
public constructor(canvas: HTMLCanvasElement) { |
||||
|
|
||||
|
this.canvas = canvas; |
||||
|
|
||||
|
// canvas元素能够监听鼠标事件
|
||||
|
this.canvas.addEventListener("mousedown", this, false); |
||||
|
this.canvas.addEventListener("mouseup", this, false); |
||||
|
this.canvas.addEventListener("mousemove", this, false); |
||||
|
|
||||
|
// 很重要一点,键盘事件不能在canvas中触发,但是能在全局的window对象中触发
|
||||
|
// 因此我们能在window对象中监听键盘事件
|
||||
|
window.addEventListener("keydown", this, false); |
||||
|
window.addEventListener("keyup", this, false); |
||||
|
window.addEventListener("keypress", this, false); |
||||
|
|
||||
|
//初始化时候mousedown 和 mousemvoe不开启
|
||||
|
this._isMouseDown = false; |
||||
|
this.isSupportMouseMove = false; |
||||
|
|
||||
|
this.frameCallback = null; |
||||
|
|
||||
|
//禁止右键菜单
|
||||
|
document.oncontextmenu = function () { return false }; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 启动渲染 |
||||
|
*/ |
||||
|
public start(): void { |
||||
|
if (this._start === false) { |
||||
|
this._start = true; |
||||
|
|
||||
|
this._startTime = -1; |
||||
|
this._lastTime = -1; |
||||
|
|
||||
|
this._requestId = requestAnimationFrame((msec: number): void => { |
||||
|
this.step(msec); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断当前Application是否一直在调用requestAnimationFrame |
||||
|
* @returns boolean |
||||
|
*/ |
||||
|
public isRunning(): boolean { |
||||
|
return this._start; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 停止渲染 |
||||
|
*/ |
||||
|
public stop(): void { |
||||
|
if (this._start) { |
||||
|
cancelAnimationFrame(this._requestId); |
||||
|
this._startTime = -1; |
||||
|
this._lastTime = -1; |
||||
|
this._start = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 循环函数 |
||||
|
* @param timeStamp 当前时间戳 |
||||
|
*/ |
||||
|
protected step(timeStamp: number): void { |
||||
|
|
||||
|
if (this._startTime === -1) { |
||||
|
this._startTime = timeStamp; |
||||
|
} |
||||
|
|
||||
|
if (this._lastTime === -1) { |
||||
|
this._lastTime = timeStamp; |
||||
|
} |
||||
|
|
||||
|
//计算当前时间和第一次调用step时候的时间差
|
||||
|
let elapsedMsec = timeStamp - this._startTime; |
||||
|
|
||||
|
//计算当前时间和上一次调用step时候的时间差
|
||||
|
let intervalMSec = timeStamp - this._lastTime; |
||||
|
let intervalSec = intervalMSec / 1000.0; |
||||
|
this._lastTime = timeStamp; |
||||
|
|
||||
|
//计算帧率
|
||||
|
if (intervalMSec !== 0) { |
||||
|
this._fps = 1000 / intervalMSec; |
||||
|
} |
||||
|
|
||||
|
//处理timer
|
||||
|
this._handleTimers(intervalSec); |
||||
|
|
||||
|
//更新
|
||||
|
this.update(elapsedMsec, intervalSec); |
||||
|
|
||||
|
//渲染
|
||||
|
this.render(); |
||||
|
|
||||
|
//触发帧回调函数
|
||||
|
if (this.frameCallback !== null) { |
||||
|
this.frameCallback(this); |
||||
|
} |
||||
|
|
||||
|
//递归调用
|
||||
|
requestAnimationFrame((elapsedMsec: number): void => { |
||||
|
this.step(elapsedMsec); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 虚函数 |
||||
|
* @param elapsedMsec 毫秒 |
||||
|
* @param intervalSec 秒 |
||||
|
*/ |
||||
|
public update(elapsedMsec: number, intervalSec: number): void { } |
||||
|
|
||||
|
/** |
||||
|
* 虚函数 |
||||
|
*/ |
||||
|
public render(): void { } |
||||
|
|
||||
|
/** |
||||
|
* 处理定时器事件 |
||||
|
* @param intervalSec |
||||
|
*/ |
||||
|
private _handleTimers(intervalSec: number): void { |
||||
|
//遍历整个timer
|
||||
|
for (let i = 0; i < this.timers.length; i++) { |
||||
|
let timer: Timer = this.timers[i]; |
||||
|
// 如果当前timer enabled为false,那么继续循环
|
||||
|
if (timer.enabled === false) { |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
//减少秒数,倒计时
|
||||
|
timer.countdown -= intervalSec; |
||||
|
|
||||
|
if (timer.countdown < 0) { |
||||
|
// 调用回调函数
|
||||
|
timer.callback(timer.id, timer.callbackData); |
||||
|
if (timer.onlyOnce === false) { |
||||
|
timer.countdown = timer.timeout; |
||||
|
} else { |
||||
|
this.removeTimer(timer.id); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 添加一个定时器 |
||||
|
* @param callback |
||||
|
* @param timeout |
||||
|
* @param onlyOnce |
||||
|
* @param data |
||||
|
* @returns |
||||
|
*/ |
||||
|
public addTimer(callback: TimerCallback, timeout: number = 1.0, onlyOnce: boolean = false, data: any = undefined): number { |
||||
|
let timer: Timer; |
||||
|
|
||||
|
for (let i = 0; i < this.timers.length; i++) { |
||||
|
timer = this.timers[i]; |
||||
|
if (timer.enabled === false) { |
||||
|
timer.callback = callback; |
||||
|
timer.callbackData = data; |
||||
|
timer.timeout = timeout; |
||||
|
timer.enabled = true; |
||||
|
timer.onlyOnce = onlyOnce; |
||||
|
return timer.id; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
timer = new Timer(callback); |
||||
|
timer.callback = callback; |
||||
|
timer.callbackData = data; |
||||
|
timer.timeout = timeout; |
||||
|
timer.enabled = true; |
||||
|
timer.onlyOnce = onlyOnce; |
||||
|
|
||||
|
this.timers.push(timer); |
||||
|
timer.id = ++this._timerId; |
||||
|
return timer.id; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 清除定时器 |
||||
|
* @param id |
||||
|
* @returns |
||||
|
*/ |
||||
|
public removeTimer(id: number): boolean { |
||||
|
let ret: boolean = false; |
||||
|
for (let i = 0; i < this.timers.length; i++) { |
||||
|
if (this.timers[i].id === id) { |
||||
|
let timer: Timer = this.timers[i]; |
||||
|
timer.enabled = false; |
||||
|
ret = true; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 事件处理函数 |
||||
|
* @param evt |
||||
|
*/ |
||||
|
public handleEvent(evt: Event): void { |
||||
|
switch (evt.type) { |
||||
|
case "mousedown": |
||||
|
this._isMouseDown = true; |
||||
|
this.onMouseDown(this._toCanvasMouseEvent(evt, EInputEventType.MOUSEDOWN)); |
||||
|
break; |
||||
|
case "mouseup": |
||||
|
this._isMouseDown = false; |
||||
|
this.onMouseDown(this._toCanvasMouseEvent(evt, EInputEventType.MOUSEUP)); |
||||
|
break; |
||||
|
case "mousemove": |
||||
|
if (this.isSupportMouseMove) { |
||||
|
this.onMouseMove(this._toCanvasMouseEvent(evt, EInputEventType.MOUSEMOVE)); |
||||
|
} |
||||
|
if (this._isMouseDown) { |
||||
|
this.onMouseDrag(this._toCanvasMouseEvent(evt, EInputEventType.MOUSEDRAG)); |
||||
|
} |
||||
|
break; |
||||
|
case "keypress": |
||||
|
this.onKeyPress(this._toCanvasKeyBoardEvent(evt, EInputEventType.KEYPRESS)); |
||||
|
break; |
||||
|
case "keydown": |
||||
|
this.onKeyDown(this._toCanvasKeyBoardEvent(evt, EInputEventType.MOUSEDOWN)); |
||||
|
break; |
||||
|
case "keyup": |
||||
|
this.onKeyUp(this._toCanvasKeyBoardEvent(evt, EInputEventType.KEYUP)); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 将鼠标的指针位置变换为当前canvas的位置 |
||||
|
* @param evt |
||||
|
*/ |
||||
|
public viewportToCanvasCoordinate(evt: MouseEvent): vec2 { |
||||
|
let rect: ClientRect = this.getMouseCanvas().getBoundingClientRect(); |
||||
|
|
||||
|
//target是触发事件的元素,这里是canvas
|
||||
|
if (evt.target) { |
||||
|
let x: number = evt.clientX - rect.left; |
||||
|
let y: number = evt.clientY - rect.top; |
||||
|
|
||||
|
if (this.isFlipYCoord) { |
||||
|
y = this.getMouseCanvas().height - y; |
||||
|
} |
||||
|
let pos: vec2 = new vec2([x, y]); |
||||
|
return pos; |
||||
|
} |
||||
|
|
||||
|
throw new Error("evt.target is null"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 把DOM 事件转成自己的事件 |
||||
|
* @param evt |
||||
|
* @param type |
||||
|
*/ |
||||
|
private _toCanvasMouseEvent(evt: Event, type: EInputEventType): CanvasMouseEvent { |
||||
|
let event: MouseEvent = evt as MouseEvent;//向下转型,将Event转换为MouseEvent
|
||||
|
|
||||
|
if (type === EInputEventType.MOUSEDOWN && event.button === 2) { |
||||
|
this._isRightMouseDown = true; |
||||
|
} else if (type === EInputEventType.MOUSEUP && event.button === 2) { |
||||
|
this._isRightMouseDown = false; |
||||
|
} |
||||
|
|
||||
|
let buttonNum: number = event.button; |
||||
|
|
||||
|
if (this._isRightMouseDown && type === EInputEventType.MOUSEDRAG) { |
||||
|
buttonNum = 2; |
||||
|
} |
||||
|
|
||||
|
//将鼠标位置变换到canvas坐标系位置
|
||||
|
let mousePosition: vec2 = this.viewportToCanvasCoordinate(event); |
||||
|
|
||||
|
let ret: CanvasMouseEvent = new CanvasMouseEvent(type, mousePosition, buttonNum, event.altKey, event.ctrlKey, event.shiftKey); |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 把DOM 事件转成自己的事件 |
||||
|
* @param evt |
||||
|
* @param type |
||||
|
*/ |
||||
|
private _toCanvasKeyBoardEvent(evt: Event, type: EInputEventType): CanvasKeyBoardEvent { |
||||
|
let event: KeyboardEvent = evt as KeyboardEvent; |
||||
|
let ret: CanvasKeyBoardEvent = new CanvasKeyBoardEvent(type, event.key, event.keyCode, event.repeat, event.altKey, event.ctrlKey, event.shiftKey); |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
protected onMouseDown(evt: CanvasMouseEvent): void { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
protected onMouseUp(evt: CanvasMouseEvent): void { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
protected onMouseMove(evt: CanvasMouseEvent): void { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
protected onMouseDrag(evt: CanvasMouseEvent): void { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
protected onKeyDown(evt: CanvasKeyBoardEvent): void { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
protected onKeyUp(evt: CanvasKeyBoardEvent): void { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
protected onKeyPress(evt: CanvasKeyBoardEvent): void { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取 |
||||
|
* @returns |
||||
|
*/ |
||||
|
protected getMouseCanvas(): HTMLCanvasElement { |
||||
|
return this.canvas; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取帧率 |
||||
|
*/ |
||||
|
public get fps(): number { |
||||
|
return this._fps; |
||||
|
} |
||||
|
} |
@ -0,0 +1,88 @@ |
|||||
|
import { vec2 } from "../math"; |
||||
|
|
||||
|
/** |
||||
|
* appliaction所有事件 |
||||
|
*/ |
||||
|
export enum EInputEventType { |
||||
|
MOUSEEVENT, //总类。表示鼠标事件
|
||||
|
MOUSEDOWN, |
||||
|
MOUSEUP, |
||||
|
MOUSEMOVE, |
||||
|
MOUSEDRAG, |
||||
|
KEYBOARDEVENT, //总类,表示键盘事件
|
||||
|
KEYUP, |
||||
|
KEYDOWN, |
||||
|
KEYPRESS |
||||
|
} |
||||
|
|
||||
|
export class CanvasInputEvent { |
||||
|
/** |
||||
|
* @var alt键是否被按下 |
||||
|
*/ |
||||
|
public altKey: boolean; |
||||
|
|
||||
|
/** |
||||
|
* @var ctrl键是否被按下 |
||||
|
*/ |
||||
|
public ctrlKey: boolean; |
||||
|
|
||||
|
/** |
||||
|
* @var shift键是否被按下 |
||||
|
*/ |
||||
|
public shiftKey: boolean; |
||||
|
|
||||
|
/** |
||||
|
* @var 当前事件类型 |
||||
|
*/ |
||||
|
public type: EInputEventType; |
||||
|
|
||||
|
public constructor(type: EInputEventType, altKey: boolean = false, ctrlKey: boolean = false, shiftKey: boolean = false) { |
||||
|
this.type = type; |
||||
|
this.altKey = altKey; |
||||
|
this.ctrlKey = ctrlKey; |
||||
|
this.shiftKey = shiftKey; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
export class CanvasMouseEvent extends CanvasInputEvent { |
||||
|
/** |
||||
|
* @var 0 : 鼠标左键 ,1 : 鼠标中键,2 : 鼠标右键 |
||||
|
*/ |
||||
|
public button: number; |
||||
|
|
||||
|
/** |
||||
|
* @var canvas 坐标系的位置 |
||||
|
*/ |
||||
|
public canvasPosition: vec2; |
||||
|
|
||||
|
public constructor(type: EInputEventType, canvasPos: vec2, button: number, altKey: boolean = false, ctrlKey: boolean = false, shiftKey: boolean = false) { |
||||
|
super(type, altKey, ctrlKey, shiftKey); |
||||
|
this.canvasPosition = canvasPos; |
||||
|
this.button = button; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export class CanvasKeyBoardEvent extends CanvasInputEvent { |
||||
|
/** |
||||
|
* @var 当前按下按键的字符 |
||||
|
*/ |
||||
|
public key: string; |
||||
|
|
||||
|
/** |
||||
|
* @var 当前按下按键的ascii码 |
||||
|
*/ |
||||
|
public keyCode: number; |
||||
|
|
||||
|
/** |
||||
|
* @var 是否不停触发 |
||||
|
*/ |
||||
|
public repeat: boolean; |
||||
|
|
||||
|
public constructor(type: EInputEventType, key: string, keyCode: number, repeat: boolean, altKey: boolean = false, ctrlKey: boolean = false, shiftKey: boolean = false) { |
||||
|
super(type, altKey, ctrlKey, shiftKey); |
||||
|
this.key = key; |
||||
|
this.keyCode = keyCode; |
||||
|
this.repeat = repeat; |
||||
|
} |
||||
|
} |
@ -0,0 +1,42 @@ |
|||||
|
export type TimerCallback = (id: number, data: any) => void; |
||||
|
|
||||
|
export class Timer { |
||||
|
/** |
||||
|
* @var 计时器id |
||||
|
*/ |
||||
|
public id: number = -1; |
||||
|
|
||||
|
/** |
||||
|
* @var 计时器是否开启 |
||||
|
*/ |
||||
|
public enabled: boolean = false; |
||||
|
|
||||
|
/** |
||||
|
* @function 回调函数,到时间后自动调用 |
||||
|
*/ |
||||
|
public callback: TimerCallback; |
||||
|
|
||||
|
/** |
||||
|
* @var 回调函数参数 |
||||
|
*/ |
||||
|
public callbackData: any = undefined; |
||||
|
|
||||
|
/** |
||||
|
* @var update次数 |
||||
|
*/ |
||||
|
public countdown: number = 0; |
||||
|
|
||||
|
/** |
||||
|
* @var 超时次数??? |
||||
|
*/ |
||||
|
public timeout: number = 0; |
||||
|
|
||||
|
/** |
||||
|
* @var 是否只执行一次 |
||||
|
*/ |
||||
|
public onlyOnce: boolean = false; |
||||
|
|
||||
|
constructor(callback: TimerCallback) { |
||||
|
this.callback = callback; |
||||
|
} |
||||
|
} |
@ -0,0 +1,50 @@ |
|||||
|
import { GLApplication } from "../webgl/GLApplication"; |
||||
|
|
||||
|
export class CameraApplication extends GLApplication { |
||||
|
/** |
||||
|
* @var 相机控制 |
||||
|
*/ |
||||
|
public camera: Camera; |
||||
|
|
||||
|
public constructor(canvas: HTMLCanvasElement, contextAttributes: WebGLContextAttributes = { premultipliedAlpha: false }, need2d: boolean = false) { |
||||
|
super(canvas, contextAttributes, need2d); |
||||
|
this.camera = new Camera(this.gl, canvas.width, canvas.height, 45, 1, 200); |
||||
|
this.camera.z = 4; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 调用camera的update,这样才能实时计算camera的投影和视图矩阵,这样才能保证计算机的正常运行 |
||||
|
* 如果要做CameraApplication的子类复写本函数,记得在函数最后调用: super.update(elapsedMsec, intervalSec) |
||||
|
* @param elapsedMsec |
||||
|
* @param intervalSec |
||||
|
*/ |
||||
|
public update(elapsedMsec: number, intervalSec: number) { |
||||
|
this.camera.update(intervalSec); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 复写onKeyPress |
||||
|
* @param evt |
||||
|
*/ |
||||
|
public onKeyPress(evt: CanvasKeyBoardEvent): void { |
||||
|
if (evt.key === "w") { |
||||
|
this.camera.moveForward(-1); // 摄像机向前运行
|
||||
|
} else if (evt.key === "s") { |
||||
|
this.camera.moveForward(1); // 摄像机向后运行
|
||||
|
} else if (evt.key === "a") { |
||||
|
this.camera.moveRightward(1); // 摄像机向右运行
|
||||
|
} else if (evt.key === "d") { |
||||
|
this.camera.moveRightward(-1); // 摄像机向左运行
|
||||
|
} else if (evt.key === "z") { |
||||
|
this.camera.moveUpward(1); // 摄像机向上运行
|
||||
|
} else if (evt.key === "x") { |
||||
|
this.camera.moveUpward(-1); // 摄像机向下运行
|
||||
|
} else if (evt.key === "y") { |
||||
|
this.camera.yaw(1); // 摄像机绕本身的Y轴旋转
|
||||
|
} else if (evt.key === "r") { |
||||
|
this.camera.roll(1); // 摄像机绕本身的Z轴旋转
|
||||
|
} else if (evt.key == "p") { |
||||
|
this.camera.pitch(1); // 摄像机绕本身的X轴旋转
|
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,700 @@ |
|||||
|
import { EPSILON } from "../commons/types"; |
||||
|
import { vec3, vec4 } from "."; |
||||
|
|
||||
|
export class mat4 { |
||||
|
|
||||
|
public values = new Float32Array(16); |
||||
|
|
||||
|
public static identity = new mat4().setIdentity(); |
||||
|
public static m0 = new mat4(); |
||||
|
public static m1 = new mat4(); |
||||
|
|
||||
|
public constructor(values: number[] | null = null) { |
||||
|
if (values) { |
||||
|
this.set(values); |
||||
|
} |
||||
|
else { |
||||
|
this.setIdentity(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public set(values: number[]): mat4 { |
||||
|
for (let i = 0; i < 16; i++) { |
||||
|
this.values[i] = values[i]; |
||||
|
} |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public at(index: number): number { |
||||
|
return this.values[index]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 复制当前矩阵到目标矩阵 |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public copy(dest: mat4 | null = null): mat4 { |
||||
|
if (!dest) dest = new mat4(); |
||||
|
|
||||
|
for (let i = 0; i < 16; i++) { |
||||
|
dest.values[i] = this.values[i]; |
||||
|
} |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断矩阵是否相等 |
||||
|
* @param matrix |
||||
|
* @param threshold |
||||
|
* @returns |
||||
|
*/ |
||||
|
public equals(matrix: mat4, threshold = EPSILON): boolean { |
||||
|
for (let i = 0; i < 16; i++) { |
||||
|
if (Math.abs(this.values[i] - matrix.at(i)) > threshold) |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取一行 |
||||
|
* @param index |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public getRow(index: number, dest: vec4 | null = null): vec4 { |
||||
|
if (dest === null) { |
||||
|
dest = new vec4(); |
||||
|
} |
||||
|
dest.x = this.values[index * 4 + 0]; |
||||
|
dest.y = this.values[index * 4 + 1]; |
||||
|
dest.z = this.values[index * 4 + 2]; |
||||
|
dest.w = this.values[index * 4 + 3]; |
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取一列 |
||||
|
* @param index |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public getColumn(index: number, dest: vec4 | null = null): vec4 { |
||||
|
if (dest === null) { |
||||
|
dest = new vec4(); |
||||
|
} |
||||
|
dest.x = this.values[index]; |
||||
|
dest.y = this.values[index + 4]; |
||||
|
dest.z = this.values[index + 8]; |
||||
|
dest.w = this.values[index + 12]; |
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public getPosition(dest: vec3 | null = null): vec3 { |
||||
|
if (dest === null) { |
||||
|
dest = new vec3(); |
||||
|
} |
||||
|
dest.x = this.values[12]; |
||||
|
dest.y = this.values[13]; |
||||
|
dest.z = this.values[14]; |
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取x轴 |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public getXAxis(dest: vec3 | null = null): vec3 { |
||||
|
if (dest === null) { |
||||
|
dest = new vec3(); |
||||
|
} |
||||
|
dest.x = this.values[0]; |
||||
|
dest.y = this.values[1]; |
||||
|
dest.z = this.values[2]; |
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取y轴 |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public getYAxis(dest: vec3 | null = null): vec3 { |
||||
|
if (dest === null) { |
||||
|
dest = new vec3(); |
||||
|
} |
||||
|
dest.x = this.values[4]; |
||||
|
dest.y = this.values[5]; |
||||
|
dest.z = this.values[6]; |
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取z轴 |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public getZAxis(dest: vec3 | null = null): vec3 { |
||||
|
if (dest === null) { |
||||
|
dest = new vec3(); |
||||
|
} |
||||
|
dest.x = this.values[8]; |
||||
|
dest.y = this.values[9]; |
||||
|
dest.z = this.values[10]; |
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* |
||||
|
* @param idx |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public getAxis(idx: number, dest: vec3 | null = null): vec3 { |
||||
|
if (idx === 0) { |
||||
|
return this.getXAxis(dest); |
||||
|
} else if (idx === 1) { |
||||
|
return this.getYAxis(dest); |
||||
|
} else { |
||||
|
return this.getZAxis(dest); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置成单位矩阵 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public setIdentity(): mat4 { |
||||
|
this.values[0] = 1; |
||||
|
this.values[1] = 0; |
||||
|
this.values[2] = 0; |
||||
|
this.values[3] = 0; |
||||
|
this.values[4] = 0; |
||||
|
this.values[5] = 1; |
||||
|
this.values[6] = 0; |
||||
|
this.values[7] = 0; |
||||
|
this.values[8] = 0; |
||||
|
this.values[9] = 0; |
||||
|
this.values[10] = 1; |
||||
|
this.values[11] = 0; |
||||
|
this.values[12] = 0; |
||||
|
this.values[13] = 0; |
||||
|
this.values[14] = 0; |
||||
|
this.values[15] = 1; |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 转置 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public transpose(): mat4 { |
||||
|
let temp01 = this.values[1], temp02 = this.values[2], |
||||
|
temp03 = this.values[3], temp12 = this.values[6], |
||||
|
temp13 = this.values[7], temp23 = this.values[11]; |
||||
|
|
||||
|
this.values[1] = this.values[4]; |
||||
|
this.values[2] = this.values[8]; |
||||
|
this.values[3] = this.values[12]; |
||||
|
this.values[4] = temp01; |
||||
|
|
||||
|
this.values[6] = this.values[9]; |
||||
|
this.values[7] = this.values[13]; |
||||
|
this.values[8] = temp02; |
||||
|
this.values[9] = temp12; |
||||
|
this.values[11] = this.values[14]; |
||||
|
this.values[12] = temp03; |
||||
|
this.values[13] = temp13; |
||||
|
this.values[14] = temp23; |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取矩阵行列式 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public determinant(): number { |
||||
|
let a00 = this.values[0], a01 = this.values[1], a02 = this.values[2], a03 = this.values[3], |
||||
|
a10 = this.values[4], a11 = this.values[5], a12 = this.values[6], a13 = this.values[7], |
||||
|
a20 = this.values[8], a21 = this.values[9], a22 = this.values[10], a23 = this.values[11], |
||||
|
a30 = this.values[12], a31 = this.values[13], a32 = this.values[14], a33 = this.values[15]; |
||||
|
|
||||
|
let det00 = a00 * a11 - a01 * a10, |
||||
|
det01 = a00 * a12 - a02 * a10, |
||||
|
det02 = a00 * a13 - a03 * a10, |
||||
|
det03 = a01 * a12 - a02 * a11, |
||||
|
det04 = a01 * a13 - a03 * a11, |
||||
|
det05 = a02 * a13 - a03 * a12, |
||||
|
det06 = a20 * a31 - a21 * a30, |
||||
|
det07 = a20 * a32 - a22 * a30, |
||||
|
det08 = a20 * a33 - a23 * a30, |
||||
|
det09 = a21 * a32 - a22 * a31, |
||||
|
det10 = a21 * a33 - a23 * a31, |
||||
|
det11 = a22 * a33 - a23 * a32; |
||||
|
|
||||
|
return (det00 * det11 - det01 * det10 + det02 * det09 + det03 * det08 - det04 * det07 + det05 * det06); |
||||
|
} |
||||
|
|
||||
|
public det(): number { |
||||
|
return this.determinant(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 逆矩阵 |
||||
|
* @param out |
||||
|
* @returns |
||||
|
*/ |
||||
|
public inverse(out: mat4): boolean { |
||||
|
this.copy(out); |
||||
|
let a00 = out.values[0], a01 = out.values[1], a02 = out.values[2], a03 = out.values[3], |
||||
|
a10 = out.values[4], a11 = out.values[5], a12 = out.values[6], a13 = out.values[7], |
||||
|
a20 = out.values[8], a21 = out.values[9], a22 = out.values[10], a23 = out.values[11], |
||||
|
a30 = out.values[12], a31 = out.values[13], a32 = out.values[14], a33 = out.values[15]; |
||||
|
|
||||
|
let det00 = a00 * a11 - a01 * a10, |
||||
|
det01 = a00 * a12 - a02 * a10, |
||||
|
det02 = a00 * a13 - a03 * a10, |
||||
|
det03 = a01 * a12 - a02 * a11, |
||||
|
det04 = a01 * a13 - a03 * a11, |
||||
|
det05 = a02 * a13 - a03 * a12, |
||||
|
det06 = a20 * a31 - a21 * a30, |
||||
|
det07 = a20 * a32 - a22 * a30, |
||||
|
det08 = a20 * a33 - a23 * a30, |
||||
|
det09 = a21 * a32 - a22 * a31, |
||||
|
det10 = a21 * a33 - a23 * a31, |
||||
|
det11 = a22 * a33 - a23 * a32; |
||||
|
|
||||
|
let det = (det00 * det11 - det01 * det10 + det02 * det09 + det03 * det08 - det04 * det07 + det05 * det06); |
||||
|
|
||||
|
if (!det) |
||||
|
return false; |
||||
|
|
||||
|
det = 1.0 / det; |
||||
|
|
||||
|
out.values[0] = (a11 * det11 - a12 * det10 + a13 * det09) * det; |
||||
|
out.values[1] = (-a01 * det11 + a02 * det10 - a03 * det09) * det; |
||||
|
out.values[2] = (a31 * det05 - a32 * det04 + a33 * det03) * det; |
||||
|
out.values[3] = (-a21 * det05 + a22 * det04 - a23 * det03) * det; |
||||
|
out.values[4] = (-a10 * det11 + a12 * det08 - a13 * det07) * det; |
||||
|
out.values[5] = (a00 * det11 - a02 * det08 + a03 * det07) * det; |
||||
|
out.values[6] = (-a30 * det05 + a32 * det02 - a33 * det01) * det; |
||||
|
out.values[7] = (a20 * det05 - a22 * det02 + a23 * det01) * det; |
||||
|
out.values[8] = (a10 * det10 - a11 * det08 + a13 * det06) * det; |
||||
|
out.values[9] = (-a00 * det10 + a01 * det08 - a03 * det06) * det; |
||||
|
out.values[10] = (a30 * det04 - a31 * det02 + a33 * det00) * det; |
||||
|
out.values[11] = (-a20 * det04 + a21 * det02 - a23 * det00) * det; |
||||
|
out.values[12] = (-a10 * det09 + a11 * det07 - a12 * det06) * det; |
||||
|
out.values[13] = (a00 * det09 - a01 * det07 + a02 * det06) * det; |
||||
|
out.values[14] = (-a30 * det03 + a31 * det01 - a32 * det00) * det; |
||||
|
out.values[15] = (a20 * det03 - a21 * det01 + a22 * det00) * det; |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 矩阵相乘 |
||||
|
* @param matrix |
||||
|
* @returns |
||||
|
*/ |
||||
|
public multiply(matrix: mat4): mat4 { |
||||
|
let a00 = this.values[0], a01 = this.values[1], a02 = this.values[2], a03 = this.values[3]; |
||||
|
let a10 = this.values[4], a11 = this.values[5], a12 = this.values[6], a13 = this.values[7]; |
||||
|
let a20 = this.values[8], a21 = this.values[9], a22 = this.values[10], a23 = this.values[11]; |
||||
|
let a30 = this.values[12], a31 = this.values[13], a32 = this.values[14], a33 = this.values[15]; |
||||
|
|
||||
|
let b0 = matrix.at(0), |
||||
|
b1 = matrix.at(1), |
||||
|
b2 = matrix.at(2), |
||||
|
b3 = matrix.at(3); |
||||
|
|
||||
|
this.values[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; |
||||
|
this.values[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; |
||||
|
this.values[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; |
||||
|
this.values[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; |
||||
|
|
||||
|
b0 = matrix.at(4); |
||||
|
b1 = matrix.at(5); |
||||
|
b2 = matrix.at(6); |
||||
|
b3 = matrix.at(7); |
||||
|
|
||||
|
this.values[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; |
||||
|
this.values[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; |
||||
|
this.values[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; |
||||
|
this.values[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; |
||||
|
|
||||
|
b0 = matrix.at(8); |
||||
|
b1 = matrix.at(9); |
||||
|
b2 = matrix.at(10); |
||||
|
b3 = matrix.at(11); |
||||
|
|
||||
|
this.values[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; |
||||
|
this.values[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; |
||||
|
this.values[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; |
||||
|
this.values[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; |
||||
|
|
||||
|
b0 = matrix.at(12); |
||||
|
b1 = matrix.at(13); |
||||
|
b2 = matrix.at(14); |
||||
|
b3 = matrix.at(15); |
||||
|
|
||||
|
this.values[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; |
||||
|
this.values[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; |
||||
|
this.values[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; |
||||
|
this.values[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 矩阵乘以3维度向量 |
||||
|
* @param vector |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public multiplyVec3(vector: vec3, dest: vec3 | null = null): vec3 { |
||||
|
if (!dest) dest = new vec3(); |
||||
|
let x = vector.x, |
||||
|
y = vector.y, |
||||
|
z = vector.z; |
||||
|
|
||||
|
dest.x = this.values[0] * x + this.values[4] * y + this.values[8] * z + this.values[12]; |
||||
|
dest.y = this.values[1] * x + this.values[5] * y + this.values[9] * z + this.values[13]; |
||||
|
dest.z = this.values[2] * x + this.values[6] * y + this.values[10] * z + this.values[14]; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 矩阵乘4维向量 |
||||
|
* @param vector |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public multiplyVec4(vector: vec4, dest: vec4 | null = null): vec4 { |
||||
|
if (!dest) dest = new vec4(); |
||||
|
|
||||
|
let x = vector.x, |
||||
|
y = vector.y, |
||||
|
z = vector.z, |
||||
|
w = vector.w; |
||||
|
|
||||
|
dest.x = this.values[0] * x + this.values[4] * y + this.values[8] * z + this.values[12] * w; |
||||
|
dest.y = this.values[1] * x + this.values[5] * y + this.values[9] * z + this.values[13] * w; |
||||
|
dest.z = this.values[2] * x + this.values[6] * y + this.values[10] * z + this.values[14] * w; |
||||
|
dest.w = this.values[3] * x + this.values[7] * y + this.values[11] * z + this.values[15] * w; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 矩阵平移 |
||||
|
* @param vector |
||||
|
* @returns |
||||
|
*/ |
||||
|
public translate(vector: vec3): mat4 { |
||||
|
let x = vector.x, |
||||
|
y = vector.y, |
||||
|
z = vector.z; |
||||
|
|
||||
|
this.values[12] += this.values[0] * x + this.values[4] * y + this.values[8] * z; |
||||
|
this.values[13] += this.values[1] * x + this.values[5] * y + this.values[9] * z; |
||||
|
this.values[14] += this.values[2] * x + this.values[6] * y + this.values[10] * z; |
||||
|
this.values[15] += this.values[3] * x + this.values[7] * y + this.values[11] * z; |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 矩阵缩放 |
||||
|
* @param vector |
||||
|
* @returns |
||||
|
*/ |
||||
|
public scale(vector: vec3): mat4 { |
||||
|
let x = vector.x, |
||||
|
y = vector.y, |
||||
|
z = vector.z; |
||||
|
|
||||
|
this.values[0] *= x; |
||||
|
this.values[1] *= x; |
||||
|
this.values[2] *= x; |
||||
|
this.values[3] *= x; |
||||
|
|
||||
|
this.values[4] *= y; |
||||
|
this.values[5] *= y; |
||||
|
this.values[6] *= y; |
||||
|
this.values[7] *= y; |
||||
|
|
||||
|
this.values[8] *= z; |
||||
|
this.values[9] *= z; |
||||
|
this.values[10] *= z; |
||||
|
this.values[11] *= z; |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 矩阵旋转 |
||||
|
* @param angle |
||||
|
* @param axis |
||||
|
* @returns |
||||
|
*/ |
||||
|
public rotate(angle: number, axis: vec3): mat4 | null { |
||||
|
let x = axis.x, |
||||
|
y = axis.y, |
||||
|
z = axis.z; |
||||
|
|
||||
|
let length = Math.sqrt(x * x + y * y + z * z); |
||||
|
|
||||
|
if (!length) |
||||
|
return null; |
||||
|
|
||||
|
if (length !== 1) { |
||||
|
length = 1 / length; |
||||
|
x *= length; |
||||
|
y *= length; |
||||
|
z *= length; |
||||
|
} |
||||
|
|
||||
|
let s = Math.sin(angle); |
||||
|
let c = Math.cos(angle); |
||||
|
|
||||
|
let t = 1.0 - c; |
||||
|
|
||||
|
let a00 = this.values[0], a01 = this.values[1], a02 = this.values[2], a03 = this.values[3], |
||||
|
a10 = this.values[4], a11 = this.values[5], a12 = this.values[6], a13 = this.values[7], |
||||
|
a20 = this.values[8], a21 = this.values[9], a22 = this.values[10], a23 = this.values[11]; |
||||
|
|
||||
|
let b00 = x * x * t + c, b01 = y * x * t + z * s, b02 = z * x * t - y * s, |
||||
|
b10 = x * y * t - z * s, b11 = y * y * t + c, b12 = z * y * t + x * s, |
||||
|
b20 = x * z * t + y * s, b21 = y * z * t - x * s, b22 = z * z * t + c; |
||||
|
|
||||
|
this.values[0] = a00 * b00 + a10 * b01 + a20 * b02; |
||||
|
this.values[1] = a01 * b00 + a11 * b01 + a21 * b02; |
||||
|
this.values[2] = a02 * b00 + a12 * b01 + a22 * b02; |
||||
|
this.values[3] = a03 * b00 + a13 * b01 + a23 * b02; |
||||
|
|
||||
|
this.values[4] = a00 * b10 + a10 * b11 + a20 * b12; |
||||
|
this.values[5] = a01 * b10 + a11 * b11 + a21 * b12; |
||||
|
this.values[6] = a02 * b10 + a12 * b11 + a22 * b12; |
||||
|
this.values[7] = a03 * b10 + a13 * b11 + a23 * b12; |
||||
|
|
||||
|
this.values[8] = a00 * b20 + a10 * b21 + a20 * b22; |
||||
|
this.values[9] = a01 * b20 + a11 * b21 + a21 * b22; |
||||
|
this.values[10] = a02 * b20 + a12 * b21 + a22 * b22; |
||||
|
this.values[11] = a03 * b20 + a13 * b21 + a23 * b22; |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 生成一个视锥体矩阵矩阵 |
||||
|
* @param left |
||||
|
* @param right |
||||
|
* @param bottom |
||||
|
* @param top |
||||
|
* @param near |
||||
|
* @param far |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static frustum(left: number, right: number, bottom: number, top: number, near: number, far: number): mat4 { |
||||
|
let rl = (right - left), |
||||
|
tb = (top - bottom), |
||||
|
fn = (far - near); |
||||
|
|
||||
|
return new mat4([ |
||||
|
(near * 2) / rl, |
||||
|
0, |
||||
|
0, |
||||
|
0, |
||||
|
|
||||
|
0, |
||||
|
(near * 2) / tb, |
||||
|
0, |
||||
|
0, |
||||
|
|
||||
|
(right + left) / rl, |
||||
|
(top + bottom) / tb, |
||||
|
-(far + near) / fn, |
||||
|
-1, |
||||
|
|
||||
|
0, |
||||
|
0, |
||||
|
-(far * near * 2) / fn, |
||||
|
0 |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 生成一个透视投影矩阵 |
||||
|
* @param fov |
||||
|
* @param aspect |
||||
|
* @param near |
||||
|
* @param far |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static perspective(fov: number, aspect: number, near: number, far: number): mat4 { |
||||
|
let top = near * Math.tan(fov * 0.5), |
||||
|
right = top * aspect; |
||||
|
|
||||
|
return mat4.frustum(-right, right, -top, top, near, far); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 生成一个正交投影矩阵 |
||||
|
* @param left |
||||
|
* @param right |
||||
|
* @param bottom |
||||
|
* @param top |
||||
|
* @param near |
||||
|
* @param far |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static orthographic(left: number, right: number, bottom: number, top: number, near: number, far: number): mat4 { |
||||
|
let rl = (right - left), |
||||
|
tb = (top - bottom), |
||||
|
fn = (far - near); |
||||
|
|
||||
|
return new mat4([ |
||||
|
2 / rl, |
||||
|
0, |
||||
|
0, |
||||
|
0, |
||||
|
|
||||
|
0, |
||||
|
2 / tb, |
||||
|
0, |
||||
|
0, |
||||
|
|
||||
|
0, |
||||
|
0, |
||||
|
-2 / fn, |
||||
|
0, |
||||
|
|
||||
|
-(left + right) / rl, |
||||
|
-(top + bottom) / tb, |
||||
|
-(far + near) / fn, |
||||
|
1 |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 生成一个视图矩阵 |
||||
|
* @param position |
||||
|
* @param target |
||||
|
* @param up |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static lookAt(position: vec3, target: vec3, up: vec3 = vec3.up): mat4 { |
||||
|
if (position.equals(target)) { |
||||
|
return this.identity; |
||||
|
} |
||||
|
|
||||
|
let z = vec3.difference(position, target).normalize(); |
||||
|
let x = vec3.cross(up, z).normalize(); |
||||
|
let y = vec3.cross(z, x).normalize(); |
||||
|
|
||||
|
return new mat4([ |
||||
|
x.x, |
||||
|
y.x, |
||||
|
z.x, |
||||
|
0, |
||||
|
|
||||
|
x.y, |
||||
|
y.y, |
||||
|
z.y, |
||||
|
0, |
||||
|
|
||||
|
x.z, |
||||
|
y.z, |
||||
|
z.z, |
||||
|
0, |
||||
|
|
||||
|
-vec3.dot(x, position), |
||||
|
-vec3.dot(y, position), |
||||
|
-vec3.dot(z, position), |
||||
|
1 |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 合成mvp矩阵的 |
||||
|
* @param m1 |
||||
|
* @param m2 |
||||
|
* @param result |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static product(m1: mat4, m2: mat4, result: mat4 | null = null): mat4 { |
||||
|
let a00 = m1.at(0), a01 = m1.at(1), a02 = m1.at(2), a03 = m1.at(3), |
||||
|
a10 = m1.at(4), a11 = m1.at(5), a12 = m1.at(6), a13 = m1.at(7), |
||||
|
a20 = m1.at(8), a21 = m1.at(9), a22 = m1.at(10), a23 = m1.at(11), |
||||
|
a30 = m1.at(12), a31 = m1.at(13), a32 = m1.at(14), a33 = m1.at(15); |
||||
|
|
||||
|
let b00 = m2.at(0), b01 = m2.at(1), b02 = m2.at(2), b03 = m2.at(3), |
||||
|
b10 = m2.at(4), b11 = m2.at(5), b12 = m2.at(6), b13 = m2.at(7), |
||||
|
b20 = m2.at(8), b21 = m2.at(9), b22 = m2.at(10), b23 = m2.at(11), |
||||
|
b30 = m2.at(12), b31 = m2.at(13), b32 = m2.at(14), b33 = m2.at(15); |
||||
|
|
||||
|
if (result) { |
||||
|
result.set([ |
||||
|
b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30, |
||||
|
b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31, |
||||
|
b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32, |
||||
|
b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33, |
||||
|
|
||||
|
b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30, |
||||
|
b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31, |
||||
|
b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32, |
||||
|
b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33, |
||||
|
|
||||
|
b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30, |
||||
|
b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31, |
||||
|
b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32, |
||||
|
b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33, |
||||
|
|
||||
|
b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30, |
||||
|
b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31, |
||||
|
b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32, |
||||
|
b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33 |
||||
|
]); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
else { |
||||
|
return new mat4([ |
||||
|
b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30, |
||||
|
b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31, |
||||
|
b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32, |
||||
|
b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33, |
||||
|
|
||||
|
b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30, |
||||
|
b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31, |
||||
|
b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32, |
||||
|
b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33, |
||||
|
|
||||
|
b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30, |
||||
|
b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31, |
||||
|
b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32, |
||||
|
b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33, |
||||
|
|
||||
|
b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30, |
||||
|
b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31, |
||||
|
b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32, |
||||
|
b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33 |
||||
|
]); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,470 @@ |
|||||
|
import { EPSILON } from "../commons/types"; |
||||
|
import { vec3, vec4, mat4 } from "."; |
||||
|
|
||||
|
export class quat { |
||||
|
|
||||
|
public values = new Float32Array(4); |
||||
|
|
||||
|
static identity = new quat().setIdentity(); |
||||
|
static q0 = new quat(); |
||||
|
static q1 = new quat(); |
||||
|
static q2 = new quat(); |
||||
|
|
||||
|
public get x(): number { |
||||
|
return this.values[0]; |
||||
|
} |
||||
|
|
||||
|
public get y(): number { |
||||
|
return this.values[1]; |
||||
|
} |
||||
|
|
||||
|
public get z(): number { |
||||
|
return this.values[2]; |
||||
|
} |
||||
|
|
||||
|
public get w(): number { |
||||
|
return this.values[3]; |
||||
|
} |
||||
|
|
||||
|
public set x(value: number) { |
||||
|
this.values[0] = value; |
||||
|
} |
||||
|
|
||||
|
public set y(value: number) { |
||||
|
this.values[1] = value; |
||||
|
} |
||||
|
|
||||
|
public set z(value: number) { |
||||
|
this.values[2] = value; |
||||
|
} |
||||
|
|
||||
|
public set w(value: number) { |
||||
|
this.values[3] = value; |
||||
|
} |
||||
|
|
||||
|
public constructor() { |
||||
|
this.setIdentity(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置一个标准四元数 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public setIdentity(): quat { |
||||
|
this.x = 0; |
||||
|
this.y = 0; |
||||
|
this.z = 0; |
||||
|
this.w = 1; |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public at(index: number): number { |
||||
|
return this.values[index]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 重置 |
||||
|
*/ |
||||
|
public reset(): void { |
||||
|
for (let i = 0; i < 4; i++) { |
||||
|
this.values[i] = 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public copy(dest: quat | null = null): quat { |
||||
|
if (!dest) dest = new quat(); |
||||
|
|
||||
|
for (let i = 0; i < 4; i++) { |
||||
|
dest.values[i] = this.values[i]; |
||||
|
} |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
public roll(): number { |
||||
|
let x = this.x, |
||||
|
y = this.y, |
||||
|
z = this.z, |
||||
|
w = this.w; |
||||
|
|
||||
|
return Math.atan2(2.0 * (x * y + w * z), w * w + x * x - y * y - z * z); |
||||
|
} |
||||
|
|
||||
|
public pitch(): number { |
||||
|
let x = this.x, |
||||
|
y = this.y, |
||||
|
z = this.z, |
||||
|
w = this.w; |
||||
|
|
||||
|
return Math.atan2(2.0 * (y * z + w * x), w * w - x * x - y * y + z * z); |
||||
|
} |
||||
|
|
||||
|
public yaw(): number { |
||||
|
return Math.asin(2.0 * (this.x * this.z - this.w * this.y)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断是否相等 |
||||
|
* @param vector |
||||
|
* @param threshold |
||||
|
* @returns |
||||
|
*/ |
||||
|
public equals(vector: quat, threshold = EPSILON): boolean { |
||||
|
for (let i = 0; i < 4; i++) { |
||||
|
if (Math.abs(this.values[i] - vector.at(i)) > threshold) |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 计算W |
||||
|
* @returns |
||||
|
*/ |
||||
|
public calculateW(): quat { |
||||
|
let x = this.x, |
||||
|
y = this.y, |
||||
|
z = this.z; |
||||
|
|
||||
|
this.w = -(Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z))); |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 四元数点乘 |
||||
|
* @param q1 |
||||
|
* @param q2 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static dot(q1: quat, q2: quat): number { |
||||
|
return q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 四元数求逆 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public inverse(): quat { |
||||
|
let dot = quat.dot(this, this); |
||||
|
|
||||
|
if (!dot) { |
||||
|
this.setIdentity(); |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
let invDot = dot ? 1.0 / dot : 0; |
||||
|
|
||||
|
this.x *= -invDot; |
||||
|
this.y *= -invDot; |
||||
|
this.z *= -invDot; |
||||
|
this.w *= invDot; |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public conjugate(): quat { |
||||
|
this.values[0] *= -1; |
||||
|
this.values[1] *= -1; |
||||
|
this.values[2] *= -1; |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 四元数求模 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public length(): number { |
||||
|
let x = this.x, |
||||
|
y = this.y, |
||||
|
z = this.z, |
||||
|
w = this.w; |
||||
|
|
||||
|
return Math.sqrt(x * x + y * y + z * z + w * w); |
||||
|
} |
||||
|
|
||||
|
public normalize(dest: quat | null = null): quat { |
||||
|
if (!dest) dest = this; |
||||
|
|
||||
|
let x = this.x, |
||||
|
y = this.y, |
||||
|
z = this.z, |
||||
|
w = this.w; |
||||
|
|
||||
|
let length = Math.sqrt(x * x + y * y + z * z + w * w); |
||||
|
|
||||
|
if (!length) { |
||||
|
dest.x = 0; |
||||
|
dest.y = 0; |
||||
|
dest.z = 0; |
||||
|
dest.w = 0; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
length = 1 / length; |
||||
|
|
||||
|
dest.x = x * length; |
||||
|
dest.y = y * length; |
||||
|
dest.z = z * length; |
||||
|
dest.w = w * length; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
public add(other: quat): quat { |
||||
|
for (let i = 0; i < 4; i++) { |
||||
|
this.values[i] += other.at(i); |
||||
|
} |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
// 这个是左到右结合 this.cross.other
|
||||
|
public multiply(other: quat): quat { |
||||
|
let q1x = this.values[0], |
||||
|
q1y = this.values[1], |
||||
|
q1z = this.values[2], |
||||
|
q1w = this.values[3]; |
||||
|
|
||||
|
let q2x = other.x, |
||||
|
q2y = other.y, |
||||
|
q2z = other.z, |
||||
|
q2w = other.w; |
||||
|
|
||||
|
this.x = q1x * q2w + q1w * q2x + q1y * q2z - q1z * q2y; |
||||
|
this.y = q1y * q2w + q1w * q2y + q1z * q2x - q1x * q2z; |
||||
|
this.z = q1z * q2w + q1w * q2z + q1x * q2y - q1y * q2x; |
||||
|
this.w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z; |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 四元数乘以向量 |
||||
|
* @param vector |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public multiplyVec3(vector: vec3, dest: vec3 | null = null): vec3 { |
||||
|
if (!dest) dest = new vec3(); |
||||
|
|
||||
|
let x = vector.x, |
||||
|
y = vector.y, |
||||
|
z = vector.z; |
||||
|
|
||||
|
let qx = this.x, |
||||
|
qy = this.y, |
||||
|
qz = this.z, |
||||
|
qw = this.w; |
||||
|
|
||||
|
//
|
||||
|
let ix = qw * x + qy * z - qz * y, |
||||
|
iy = qw * y + qz * x - qx * z, |
||||
|
iz = qw * z + qx * y - qy * x, |
||||
|
iw = -qx * x - qy * y - qz * z; |
||||
|
|
||||
|
dest.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; |
||||
|
dest.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; |
||||
|
dest.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
public toMat4(dest: mat4 | null = null): mat4 { |
||||
|
if (!dest) dest = new mat4(); |
||||
|
|
||||
|
let x = this.x, |
||||
|
y = this.y, |
||||
|
z = this.z, |
||||
|
w = this.w, |
||||
|
|
||||
|
x2 = x + x, |
||||
|
y2 = y + y, |
||||
|
z2 = z + z, |
||||
|
|
||||
|
xx = x * x2, |
||||
|
xy = x * y2, |
||||
|
xz = x * z2, |
||||
|
yy = y * y2, |
||||
|
yz = y * z2, |
||||
|
zz = z * z2, |
||||
|
wx = w * x2, |
||||
|
wy = w * y2, |
||||
|
wz = w * z2; |
||||
|
|
||||
|
dest.set([ |
||||
|
1 - (yy + zz), |
||||
|
xy + wz, |
||||
|
xz - wy, |
||||
|
0, |
||||
|
|
||||
|
(xy - wz), |
||||
|
(1 - (xx + zz)), |
||||
|
(yz + wx), |
||||
|
0, |
||||
|
|
||||
|
xz + wy, |
||||
|
yz - wx, |
||||
|
1 - (xx + yy), |
||||
|
0, |
||||
|
|
||||
|
0, |
||||
|
0, |
||||
|
0, |
||||
|
1 |
||||
|
]); |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
public static sum(q1: quat, q2: quat, dest: quat | null = null): quat { |
||||
|
if (!dest) dest = new quat(); |
||||
|
|
||||
|
dest.x = q1.x + q2.x; |
||||
|
dest.y = q1.y + q2.y; |
||||
|
dest.z = q1.z + q2.z; |
||||
|
dest.w = q1.w + q2.w; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
public static product(q1: quat, q2: quat, dest: quat | null = null): quat { |
||||
|
if (!dest) dest = new quat(); |
||||
|
|
||||
|
let q1x = q1.x, |
||||
|
q1y = q1.y, |
||||
|
q1z = q1.z, |
||||
|
q1w = q1.w, |
||||
|
|
||||
|
q2x = q2.x, |
||||
|
q2y = q2.y, |
||||
|
q2z = q2.z, |
||||
|
q2w = q2.w; |
||||
|
|
||||
|
dest.x = q1x * q2w + q1w * q2x + q1y * q2z - q1z * q2y; |
||||
|
dest.y = q1y * q2w + q1w * q2y + q1z * q2x - q1x * q2z; |
||||
|
dest.z = q1z * q2w + q1w * q2z + q1x * q2y - q1y * q2x; |
||||
|
dest.w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
public static cross(q1: quat, q2: quat, dest: quat | null = null): quat { |
||||
|
if (!dest) dest = new quat(); |
||||
|
|
||||
|
let q1x = q1.x, |
||||
|
q1y = q1.y, |
||||
|
q1z = q1.z, |
||||
|
q1w = q1.w, |
||||
|
|
||||
|
q2x = q2.x, |
||||
|
q2y = q2.y, |
||||
|
q2z = q2.z, |
||||
|
q2w = q2.w; |
||||
|
|
||||
|
dest.x = q1w * q2z + q1z * q2w + q1x * q2y - q1y * q2x; |
||||
|
dest.y = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z; |
||||
|
dest.z = q1w * q2x + q1x * q2w + q1y * q2z - q1z * q2y; |
||||
|
dest.w = q1w * q2y + q1y * q2w + q1z * q2x - q1x * q2z; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
public static shortMix(q1: quat, q2: quat, time: number, dest: quat | null = null): quat { |
||||
|
if (!dest) dest = new quat(); |
||||
|
|
||||
|
if (time <= 0.0) { |
||||
|
q1.copy(q1); |
||||
|
|
||||
|
return dest; |
||||
|
} else if (time >= 1.0) { |
||||
|
q2.copy(dest); |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
let cos = quat.dot(q1, q2), |
||||
|
q2a = q2.copy(); |
||||
|
|
||||
|
if (cos < 0.0) { |
||||
|
q2a.inverse(); |
||||
|
cos = -cos; |
||||
|
} |
||||
|
|
||||
|
let k0: number, |
||||
|
k1: number; |
||||
|
|
||||
|
if (cos > 0.9999) { |
||||
|
k0 = 1 - time; |
||||
|
k1 = 0 + time; |
||||
|
} |
||||
|
else { |
||||
|
let sin: number = Math.sqrt(1 - cos * cos); |
||||
|
let angle: number = Math.atan2(sin, cos); |
||||
|
|
||||
|
let oneOverSin: number = 1 / sin; |
||||
|
|
||||
|
k0 = Math.sin((1 - time) * angle) * oneOverSin; |
||||
|
k1 = Math.sin((0 + time) * angle) * oneOverSin; |
||||
|
} |
||||
|
|
||||
|
dest.x = k0 * q1.x + k1 * q2a.x; |
||||
|
dest.y = k0 * q1.y + k1 * q2a.y; |
||||
|
dest.z = k0 * q1.z + k1 * q2a.z; |
||||
|
dest.w = k0 * q1.w + k1 * q2a.w; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
public static mix(q1: quat, q2: quat, time: number, dest: quat | null = null): quat { |
||||
|
if (!dest) dest = new quat(); |
||||
|
|
||||
|
let cosHalfTheta = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w; |
||||
|
|
||||
|
if (Math.abs(cosHalfTheta) >= 1.0) { |
||||
|
q1.copy(dest); |
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
let halfTheta = Math.acos(cosHalfTheta), |
||||
|
sinHalfTheta = Math.sqrt(1.0 - cosHalfTheta * cosHalfTheta); |
||||
|
|
||||
|
if (Math.abs(sinHalfTheta) < 0.001) { |
||||
|
dest.x = q1.x * 0.5 + q2.x * 0.5; |
||||
|
dest.y = q1.y * 0.5 + q2.y * 0.5; |
||||
|
dest.z = q1.z * 0.5 + q2.z * 0.5; |
||||
|
dest.w = q1.w * 0.5 + q2.w * 0.5; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
let ratioA = Math.sin((1 - time) * halfTheta) / sinHalfTheta, |
||||
|
ratioB = Math.sin(time * halfTheta) / sinHalfTheta; |
||||
|
|
||||
|
dest.x = q1.x * ratioA + q2.x * ratioB; |
||||
|
dest.y = q1.y * ratioA + q2.y * ratioB; |
||||
|
dest.z = q1.z * ratioA + q2.z * ratioB; |
||||
|
dest.w = q1.w * ratioA + q2.w * ratioB; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
static fromAxis(axis: vec3, angle: number, dest: quat | null = null): quat { |
||||
|
if (!dest) dest = new quat(); |
||||
|
|
||||
|
angle *= 0.5; |
||||
|
let sin = Math.sin(angle); |
||||
|
|
||||
|
dest.x = axis.x * sin; |
||||
|
dest.y = axis.y * sin; |
||||
|
dest.z = axis.z * sin; |
||||
|
dest.w = Math.cos(angle); |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
} |
@ -0,0 +1,42 @@ |
|||||
|
/** |
||||
|
* 二维向量 |
||||
|
*/ |
||||
|
export class vec2 { |
||||
|
|
||||
|
/** |
||||
|
* @var 数据存储内存 |
||||
|
*/ |
||||
|
public values = new Float32Array(2); |
||||
|
|
||||
|
public get x(): number { |
||||
|
return this.values[0]; |
||||
|
} |
||||
|
|
||||
|
public get y(): number { |
||||
|
return this.values[1]; |
||||
|
} |
||||
|
|
||||
|
public set x(value: number) { |
||||
|
this.values[0] = value; |
||||
|
} |
||||
|
|
||||
|
public set y(value: number) { |
||||
|
this.values[1] = value; |
||||
|
} |
||||
|
|
||||
|
public constructor(values: number[] | null = null) { |
||||
|
if (values) { |
||||
|
this.x = values[0]; |
||||
|
this.y = values[1]; |
||||
|
} else { |
||||
|
this.x = this.y = 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
copy(dest: vec2 | null = null): vec2 { |
||||
|
if (!dest) dest = new vec2(); |
||||
|
dest.x = this.x; |
||||
|
dest.y = this.y; |
||||
|
return dest; |
||||
|
} |
||||
|
} |
@ -0,0 +1,303 @@ |
|||||
|
import { EPSILON } from "../commons/types"; |
||||
|
|
||||
|
export class vec3 { |
||||
|
/** |
||||
|
* @var |
||||
|
*/ |
||||
|
public values = new Float32Array(3); |
||||
|
|
||||
|
public get x(): number { |
||||
|
return this.values[0]; |
||||
|
} |
||||
|
|
||||
|
public get y(): number { |
||||
|
return this.values[1]; |
||||
|
} |
||||
|
|
||||
|
public get z(): number { |
||||
|
return this.values[2]; |
||||
|
} |
||||
|
|
||||
|
public set x(value: number) { |
||||
|
this.values[0] = value; |
||||
|
} |
||||
|
|
||||
|
public set y(value: number) { |
||||
|
this.values[1] = value; |
||||
|
} |
||||
|
|
||||
|
public set z(value: number) { |
||||
|
this.values[2] = value; |
||||
|
} |
||||
|
|
||||
|
public constructor(values: number[] | null = null) { |
||||
|
if (values !== null) { |
||||
|
this.x = values[0]; |
||||
|
this.y = values[1]; |
||||
|
this.z = values[2]; |
||||
|
} else { |
||||
|
this.x = this.y = this.z = 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public at(index: number): number { |
||||
|
return this.values[index]; |
||||
|
} |
||||
|
|
||||
|
public reset(x: number = 0, y: number = 0, z: number = 0): void { |
||||
|
this.x = x; |
||||
|
this.y = y; |
||||
|
this.z = z; |
||||
|
} |
||||
|
|
||||
|
public copy(dest: vec3 | null = null): vec3 { |
||||
|
if (!dest) dest = new vec3(); |
||||
|
|
||||
|
dest.x = this.x; |
||||
|
dest.y = this.y; |
||||
|
dest.z = this.z; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 取负向量 |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public negate(dest: vec3 | null = null): vec3 { |
||||
|
if (!dest) dest = this; |
||||
|
|
||||
|
dest.x = -this.x; |
||||
|
dest.y = -this.y; |
||||
|
dest.z = -this.z; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断是否相等 |
||||
|
* @param vector |
||||
|
* @param threshold |
||||
|
* @returns |
||||
|
*/ |
||||
|
public equals(vector: vec3, threshold = EPSILON): boolean { |
||||
|
if (Math.abs(this.x - vector.x) > threshold) |
||||
|
return false; |
||||
|
|
||||
|
if (Math.abs(this.y - vector.y) > threshold) |
||||
|
return false; |
||||
|
|
||||
|
if (Math.abs(this.z - vector.z) > threshold) |
||||
|
return false; |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取向量的模 |
||||
|
*/ |
||||
|
public get length(): number { |
||||
|
return Math.sqrt(this.length2); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 向量模的平方 |
||||
|
*/ |
||||
|
public get length2(): number { |
||||
|
let x = this.x, |
||||
|
y = this.y, |
||||
|
z = this.z; |
||||
|
|
||||
|
return (x * x + y * y + z * z); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 向量相加 |
||||
|
* @param vector |
||||
|
* @returns |
||||
|
*/ |
||||
|
add(vector: vec3): vec3 { |
||||
|
this.x += vector.x; |
||||
|
this.y += vector.y; |
||||
|
this.z += vector.z; |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 向量相减 |
||||
|
* @param vector |
||||
|
* @returns |
||||
|
*/ |
||||
|
subtract(vector: vec3): vec3 { |
||||
|
this.x -= vector.x; |
||||
|
this.y -= vector.y; |
||||
|
this.z -= vector.z; |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 向量缩放 |
||||
|
* @param value |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public scale(value: number, dest: vec3 | null = null): vec3 { |
||||
|
if (!dest) { |
||||
|
dest = this; |
||||
|
} else { |
||||
|
this.copy(dest); |
||||
|
} |
||||
|
|
||||
|
dest.x *= value; |
||||
|
dest.y *= value; |
||||
|
dest.z *= value; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 向量单位化 |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public normalize(dest: vec3 | null = null): vec3 { |
||||
|
if (!dest) { |
||||
|
dest = this; |
||||
|
} |
||||
|
|
||||
|
let length = this.length; |
||||
|
|
||||
|
if (length === 1) { |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
if (length === 0) { |
||||
|
dest.x = 0 |
||||
|
dest.y = 0; |
||||
|
dest.z = 0; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
dest.x /= length; |
||||
|
dest.y /= length; |
||||
|
dest.z /= length; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 向量单位化 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public normalize2(): number { |
||||
|
let length = this.length; |
||||
|
this.x /= length; |
||||
|
this.y /= length; |
||||
|
this.z /= length; |
||||
|
return length; |
||||
|
} |
||||
|
|
||||
|
public print(): void { |
||||
|
console.log("[ " + |
||||
|
this.x + " " + |
||||
|
this.y + " " + |
||||
|
this.z |
||||
|
+" ]"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 向量缩放 |
||||
|
* @param vector |
||||
|
* @param value |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static multiplyScalar(vector: vec3, value: number, dest: vec3 | null = null): vec3 { |
||||
|
if (!dest) dest = new vec3(); |
||||
|
dest.x *= value; |
||||
|
dest.y *= value; |
||||
|
dest.z *= value; |
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 向量叉乘 |
||||
|
* @param vector |
||||
|
* @param vector2 |
||||
|
* @param dest |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static cross(vector: vec3, vector2: vec3, dest: vec3 | null = null): vec3 { |
||||
|
if (!dest) dest = new vec3(); |
||||
|
|
||||
|
let x = vector.x, |
||||
|
y = vector.y, |
||||
|
z = vector.z; |
||||
|
|
||||
|
let x2 = vector2.x, |
||||
|
y2 = vector2.y, |
||||
|
z2 = vector2.z; |
||||
|
|
||||
|
dest.x = y * z2 - z * y2; |
||||
|
dest.y = z * x2 - x * z2; |
||||
|
dest.z = x * y2 - y * x2; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 向量点乘 |
||||
|
* @param vector |
||||
|
* @param vector2 |
||||
|
* @returns |
||||
|
*/ |
||||
|
public static dot(vector: vec3, vector2: vec3): number { |
||||
|
let x = vector.x, |
||||
|
y = vector.y, |
||||
|
z = vector.z; |
||||
|
|
||||
|
let x2 = vector2.x, |
||||
|
y2 = vector2.y, |
||||
|
z2 = vector2.z; |
||||
|
|
||||
|
return (x * x2 + y * y2 + z * z2); |
||||
|
} |
||||
|
|
||||
|
public static sum(vector: vec3, vector2: vec3, dest: vec3 | null = null): vec3 { |
||||
|
if (!dest) dest = new vec3(); |
||||
|
|
||||
|
dest.x = vector.x + vector2.x; |
||||
|
dest.y = vector.y + vector2.y; |
||||
|
dest.z = vector.z + vector2.z; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
public static difference(vector: vec3, vector2: vec3, dest: vec3 | null = null): vec3 { |
||||
|
if (!dest) dest = new vec3(); |
||||
|
|
||||
|
dest.x = vector.x - vector2.x; |
||||
|
dest.y = vector.y - vector2.y; |
||||
|
dest.z = vector.z - vector2.z; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
static readonly up = new vec3([0, 1, 0]); |
||||
|
static readonly down = new vec3([0, -1, 0]); |
||||
|
static readonly right = new vec3([1, 0, 0]); |
||||
|
static readonly left = new vec3([-1, 0, 0]); |
||||
|
static readonly forward = new vec3([0, 0, 1]); |
||||
|
static readonly backward = new vec3([0, 0, -1]); |
||||
|
|
||||
|
static readonly zero = new vec3([0, 0, 0]); |
||||
|
|
||||
|
static v0 = new vec3([0, 0, 0]); |
||||
|
static v1 = new vec3([0, 0, 0]); |
||||
|
static v2 = new vec3([0, 0, 0]); |
||||
|
} |
@ -0,0 +1,136 @@ |
|||||
|
import { EPSILON } from "../commons/types"; |
||||
|
import { vec3 } from "./Vec3"; |
||||
|
|
||||
|
export class vec4 { |
||||
|
|
||||
|
public values = new Float32Array(4); |
||||
|
|
||||
|
public get x(): number { |
||||
|
return this.values[0]; |
||||
|
} |
||||
|
|
||||
|
public get y(): number { |
||||
|
return this.values[1]; |
||||
|
} |
||||
|
|
||||
|
public get z(): number { |
||||
|
return this.values[2]; |
||||
|
} |
||||
|
|
||||
|
public get w(): number { |
||||
|
return this.values[3]; |
||||
|
} |
||||
|
|
||||
|
public set x(value: number) { |
||||
|
this.values[0] = value; |
||||
|
} |
||||
|
|
||||
|
public set y(value: number) { |
||||
|
this.values[1] = value; |
||||
|
} |
||||
|
|
||||
|
public set z(value: number) { |
||||
|
this.values[2] = value; |
||||
|
} |
||||
|
|
||||
|
public set w(value: number) { |
||||
|
this.values[3] = value; |
||||
|
} |
||||
|
|
||||
|
public get vec3(): vec3 { |
||||
|
return new vec3([this.x, this.y, this.z]); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* RGBA |
||||
|
*/ |
||||
|
public get r(): number { |
||||
|
return this.values[0]; |
||||
|
} |
||||
|
|
||||
|
public get g(): number { |
||||
|
return this.values[1]; |
||||
|
} |
||||
|
|
||||
|
public get b(): number { |
||||
|
return this.values[2]; |
||||
|
} |
||||
|
|
||||
|
public get a(): number { |
||||
|
return this.values[3]; |
||||
|
} |
||||
|
|
||||
|
public set r(value: number) { |
||||
|
this.values[0] = value; |
||||
|
} |
||||
|
|
||||
|
public set g(value: number) { |
||||
|
this.values[1] = value; |
||||
|
} |
||||
|
|
||||
|
public set b(value: number) { |
||||
|
this.values[2] = value; |
||||
|
} |
||||
|
|
||||
|
public set a(value: number) { |
||||
|
this.values[3] = value; |
||||
|
} |
||||
|
|
||||
|
public constructor(values: number[] | null = null) { |
||||
|
if (values) { |
||||
|
this.x = values[0]; |
||||
|
this.y = values[1]; |
||||
|
this.z = values[2]; |
||||
|
this.w = values[3]; |
||||
|
} else { |
||||
|
this.x = this.y = this.z = this.w = 0.0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public at(index: number): number { |
||||
|
return this.values[index]; |
||||
|
} |
||||
|
|
||||
|
public reset(): void { |
||||
|
this.x = 0; |
||||
|
this.y = 0; |
||||
|
this.z = 0; |
||||
|
this.w = 0; |
||||
|
} |
||||
|
|
||||
|
public copy(dest: vec4 | null = null): vec4 { |
||||
|
if (!dest) dest = new vec4(); |
||||
|
|
||||
|
dest.x = this.x; |
||||
|
dest.y = this.y; |
||||
|
dest.z = this.z; |
||||
|
dest.w = this.w; |
||||
|
|
||||
|
return dest; |
||||
|
} |
||||
|
|
||||
|
public equals(vector: vec4, threshold = EPSILON): boolean { |
||||
|
if (Math.abs(this.x - vector.x) > threshold) |
||||
|
return false; |
||||
|
|
||||
|
if (Math.abs(this.y - vector.y) > threshold) |
||||
|
return false; |
||||
|
|
||||
|
if (Math.abs(this.z - vector.z) > threshold) |
||||
|
return false; |
||||
|
|
||||
|
if (Math.abs(this.w - vector.w) > threshold) |
||||
|
return false; |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
static red: vec4 = new vec4([1.0, 0.0, 0.0, 1.0]); |
||||
|
static green: vec4 = new vec4([0.0, 1.0, 0.0, 1.0]); |
||||
|
static blue: vec4 = new vec4([0.0, 0.0, 1.0, 1.0]); |
||||
|
static black: vec4 = new vec4([0, 0, 0, 0]); |
||||
|
|
||||
|
static v0: vec4 = new vec4(); |
||||
|
static v1: vec4 = new vec4(); |
||||
|
static v2: vec4 = new vec4(); |
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
export * from './Vec2'; |
||||
|
export * from './Vec3'; |
||||
|
export * from './Vec4'; |
||||
|
export * from './Mat4'; |
||||
|
export * from './Quat'; |
||||
|
export * from '../commons/Util'; |
@ -0,0 +1,24 @@ |
|||||
|
import { Application } from "../core/Application"; |
||||
|
|
||||
|
export class GLApplication extends Application { |
||||
|
/** |
||||
|
* @var WebGL |
||||
|
*/ |
||||
|
public gl: WebGLRenderingContext; |
||||
|
|
||||
|
/** |
||||
|
* @var 模拟OpenGL1.1中的矩阵堆栈 |
||||
|
*/ |
||||
|
public matStack: GLWordMatrixStack; |
||||
|
|
||||
|
/** |
||||
|
* @var 模拟OpenGL1.1的立即绘制模式 |
||||
|
*/ |
||||
|
public builder: GLMeshBuilder; |
||||
|
|
||||
|
/** |
||||
|
* @var canvas 用来绘制文字 |
||||
|
*/ |
||||
|
protected canvas2D: HTMLCanvasElement | null = null; |
||||
|
protected ctx2D: CanvasRenderingContext2D | null = null; |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue