diff --git a/src/render/math/Mat4.ts b/src/render/math/Mat4.ts index 08e1cf29..344c970c 100755 --- a/src/render/math/Mat4.ts +++ b/src/render/math/Mat4.ts @@ -5,6 +5,10 @@ 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); @@ -393,4 +397,304 @@ export class mat4 { 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 + ]); + } + + /** + * ?????? + * @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 + ]); + } + } } \ No newline at end of file diff --git a/src/render/math/Quat.ts b/src/render/math/Quat.ts new file mode 100755 index 00000000..5d92280b --- /dev/null +++ b/src/render/math/Quat.ts @@ -0,0 +1,470 @@ +import { EPSILON } from "../common"; +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; + } +} \ No newline at end of file diff --git a/src/render/math/TSM.ts b/src/render/math/TSM.ts index 59959ec6..137ddbc5 100755 --- a/src/render/math/TSM.ts +++ b/src/render/math/TSM.ts @@ -920,6 +920,14 @@ export class mat4 ] ); } + /** + * 生成一个透视投影矩阵 + * @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 ), diff --git a/src/render/math/index.ts b/src/render/math/index.ts index 5e0c2a5c..70ee1c92 100755 --- a/src/render/math/index.ts +++ b/src/render/math/index.ts @@ -1,3 +1,5 @@ export * from './Vec2'; export * from './Vec3'; -export * from './Vec4'; \ No newline at end of file +export * from './Vec4'; +export * from './Mat4'; +export * from './Quat'; \ No newline at end of file diff --git a/src/render/webgl/WebGLAttribState.ts b/src/render/webgl/WebGLAttribState.ts new file mode 100755 index 00000000..b69f1283 --- /dev/null +++ b/src/render/webgl/WebGLAttribState.ts @@ -0,0 +1,47 @@ +export type GLAttribBits = number; +export type GLAttribOffsetMap = { [key: string]: number }; + +export class GLAttribState { + + public static readonly POSITION_BIT: number = (1 << 0); + public static readonly POSITION_COMPONENT: number = 3; + public static readonly POSITION_NAME: string = "aPosition"; + public static readonly POSITION_LOCATION: number = 0; + + public static readonly TEXCOORD_BIT: number = (1 << 1); + public static readonly TEXCOORD_COMPONENT: number = 2; + public static readonly TEXCOORD_NAME: string = "aTexCoord"; + public static readonly TEXCOORD_LOCATION: number = 1; + + public static readonly TEXCOORD1_BIT: number = (1 << 2); + public static readonly TEXCOORD1_COMPONENT: number = 2; + public static readonly TEXCOORD1_NAME: string = "aTexCoord1"; + public static readonly TEXCOORD1_LOCATION: number = 2; + + public static readonly NORMAL_BIT: number = (1 << 3); + public static readonly NORMAL_COMPONENT: number = 3; + public static readonly NORMAL_NAME: string = "aNormal"; + public static readonly NORMAL_LOCATION: number = 3; + + public static readonly TANGENT_BIT: number = (1 << 4); + public static readonly TANGENT_COMPONENT: number = 4; + public static readonly TANGENT_NAME: string = "aTangent"; + public static readonly TANGENT_LOCATION: number = 4; + + public static readonly COLOR_BIT: number = (1 << 5); + public static readonly COLOR_COMPONENT: number = 4; + public static readonly COLOR_NAME: string = "aColor"; + public static readonly COLOR_LOCATION: number = 5; + + public static readonly ATTRIBSTRIDE: string = "STRIDE"; + public static readonly ATTRIBBYTELENGTH: string = "BYTELENGTH"; + + public static readonly FLOAT32_SIZE = Float32Array.BYTES_PER_ELEMENT; + public static readonly UINT16_SIZE = Uint16Array.BYTES_PER_ELEMENT; + + public static makeVertexAttribs(useTexcoord0: boolean, useTexcoord1: boolean, useNormal: boolean, useTangent: boolean, useColor: boolean): GLAttribBits { + let bits: GLAttribBits = GLAttribState.POSITION_BIT; // 总是要使用位置属性 + + return bits; + } +} \ No newline at end of file