Define basic data structure
This commit is contained in:
parent
31732e11a8
commit
e2cef2d172
@ -2,7 +2,11 @@
|
||||
"name": "oktaeder",
|
||||
"version": "0.1.0",
|
||||
"description": "3D rendering library for WebGPU",
|
||||
"keywords": ["3d", "gltf", "wegbpu"],
|
||||
"keywords": [
|
||||
"3d",
|
||||
"gltf",
|
||||
"wegbpu"
|
||||
],
|
||||
"homepage": "https://github.com/iszn11/oktaeder",
|
||||
"bugs": {
|
||||
"url": "https://github.com/iszn11/oktaeder/issues"
|
||||
@ -20,6 +24,7 @@
|
||||
"tslib": "^2.6.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@webgpu/types": "^0.1.34",
|
||||
"typescript": "5.1.6"
|
||||
},
|
||||
"exports": {
|
||||
|
7
pnpm-lock.yaml
generated
7
pnpm-lock.yaml
generated
@ -10,12 +10,19 @@ dependencies:
|
||||
version: 2.6.1
|
||||
|
||||
devDependencies:
|
||||
'@webgpu/types':
|
||||
specifier: ^0.1.34
|
||||
version: 0.1.34
|
||||
typescript:
|
||||
specifier: 5.1.6
|
||||
version: 5.1.6
|
||||
|
||||
packages:
|
||||
|
||||
/@webgpu/types@0.1.34:
|
||||
resolution: {integrity: sha512-9mXtH+CC8q+Ku7Z+1XazNIte81FvfdXwR2lLRO7Ykzjd/hh1J1krJa0gtnkF1kvP11psUmKEPKo7iMTeEcUpNA==}
|
||||
dev: true
|
||||
|
||||
/tslib@2.6.1:
|
||||
resolution: {integrity: sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==}
|
||||
dev: false
|
||||
|
115
src/Camera.ts
Normal file
115
src/Camera.ts
Normal file
@ -0,0 +1,115 @@
|
||||
/*!
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Node } from "./Node";
|
||||
|
||||
export type Camera = CameraOrthographic | CameraPerspective;
|
||||
|
||||
export interface CameraOrthographicProps {
|
||||
readonly name?: string;
|
||||
|
||||
readonly verticalSize: number;
|
||||
readonly nearPlane: number;
|
||||
readonly farPlane: number;
|
||||
}
|
||||
|
||||
export interface CameraPerspectiveProps {
|
||||
readonly name?: string;
|
||||
|
||||
readonly verticalFovRad: number;
|
||||
readonly nearPlane: number;
|
||||
readonly farPlane: number;
|
||||
}
|
||||
|
||||
export class CameraOrthographic {
|
||||
|
||||
readonly type!: "CameraOrthographic";
|
||||
|
||||
_name: string;
|
||||
|
||||
_verticalSize: number;
|
||||
_nearPlane: number;
|
||||
_farPlane: number;
|
||||
|
||||
/** backreference */
|
||||
_node: Node | undefined;
|
||||
|
||||
constructor({
|
||||
name = "",
|
||||
verticalSize,
|
||||
nearPlane,
|
||||
farPlane,
|
||||
}: CameraOrthographicProps) {
|
||||
Object.defineProperty(this, "type", { value: "CameraOrthographic" });
|
||||
|
||||
this._name = name;
|
||||
|
||||
this._verticalSize = verticalSize;
|
||||
this._nearPlane = nearPlane;
|
||||
this._farPlane = farPlane;
|
||||
|
||||
this._node = undefined;
|
||||
}
|
||||
|
||||
detach(): Camera {
|
||||
if (this._node === undefined) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this._node._camera = undefined;
|
||||
this._node = undefined;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export class CameraPerspective {
|
||||
|
||||
readonly type!: "CameraPerspective";
|
||||
|
||||
_name: string;
|
||||
|
||||
_verticalFovRad: number;
|
||||
_nearPlane: number;
|
||||
_farPlane: number;
|
||||
|
||||
/** backreference */
|
||||
_node: Node | undefined;
|
||||
|
||||
constructor({
|
||||
name = "",
|
||||
verticalFovRad,
|
||||
nearPlane,
|
||||
farPlane,
|
||||
}: CameraPerspectiveProps) {
|
||||
Object.defineProperty(this, "type", { value: "CameraPerspective" });
|
||||
|
||||
this._name = name;
|
||||
|
||||
this._verticalFovRad = verticalFovRad;
|
||||
this._nearPlane = nearPlane;
|
||||
this._farPlane = farPlane;
|
||||
|
||||
this._node = undefined;
|
||||
}
|
||||
|
||||
detach(): Camera {
|
||||
if (this._node === undefined) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this._node._camera = undefined;
|
||||
this._node = undefined;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export function isCameraOrthographic(value: unknown): value is CameraOrthographic {
|
||||
return Boolean(value) && (value as CameraOrthographic).type === "CameraOrthographic";
|
||||
}
|
||||
|
||||
export function isCameraPerspective(value: unknown): value is CameraPerspective {
|
||||
return Boolean(value) && (value as CameraPerspective).type === "CameraPerspective";
|
||||
}
|
266
src/Color.ts
Normal file
266
src/Color.ts
Normal file
@ -0,0 +1,266 @@
|
||||
/*!
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Vector3Object } from "./Vector3";
|
||||
|
||||
/* Named colors
|
||||
* black #000000 (0, 0, 0, 1)
|
||||
* silver #C0C0C0 (192 / 255, 192 / 255, 192 / 255, 1)
|
||||
* gray #808080 (128 / 255, 128 / 255, 128 / 255, 1)
|
||||
* white #FFFFFF (1, 1, 1, 1)
|
||||
* maroon #800000 (128 / 255, 0, 0, 1)
|
||||
* red #FF0000 (1, 0, 0, 1)
|
||||
* purple #800080 (128 / 255, 0, 128 / 255, 1)
|
||||
* fuchsia #FF00FF (1, 0, 1, 1)
|
||||
* green #008000 (0, 128 / 255, 0, 1)
|
||||
* lime #00FF00 (0, 255, 0, 1)
|
||||
* olive #808000 (128 / 255, 128 / 255, 0, 1)
|
||||
* yellow #FFFF00 (1, 1, 0, 1)
|
||||
* navy #000080 (0, 0, 128 / 255, 1)
|
||||
* blue #0000FF (0, 0, 1, 1)
|
||||
* teal #008080 (0, 128 / 255, 128 / 255, 1)
|
||||
* aqua #00FFFF (0, 1, 1, 1)
|
||||
* orange #FFA500 (1, 165 / 255, 0, 1)
|
||||
*/
|
||||
|
||||
export type ColorName =
|
||||
| "black"
|
||||
| "silver"
|
||||
| "gray"
|
||||
| "white"
|
||||
| "maroon"
|
||||
| "red"
|
||||
| "purple"
|
||||
| "fuchsia"
|
||||
| "green"
|
||||
| "lime"
|
||||
| "olive"
|
||||
| "yellow"
|
||||
| "navy"
|
||||
| "blue"
|
||||
| "teal"
|
||||
| "aqua"
|
||||
| "orange"
|
||||
;
|
||||
|
||||
export interface ColorObject {
|
||||
readonly r: number;
|
||||
readonly g: number;
|
||||
readonly b: number;
|
||||
}
|
||||
|
||||
export type ColorTuple = readonly [r: number, g: number, b: number];
|
||||
|
||||
export class Color {
|
||||
|
||||
readonly type!: "Color";
|
||||
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
|
||||
constructor(r: number, g: number, b: number) {
|
||||
Object.defineProperty(this, "type", { value: "Color" });
|
||||
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
static fromObject(object: ColorObject): Color {
|
||||
return new Color(object.r, object.g, object.b);
|
||||
}
|
||||
|
||||
static fromTuple(tuple: ColorTuple): Color {
|
||||
return new Color(...tuple);
|
||||
}
|
||||
|
||||
static fromName(name: ColorName): Color {
|
||||
switch (name) {
|
||||
case "black": return new Color(0, 0, 0);
|
||||
case "silver": return new Color(192 / 255, 192 / 255, 192 / 255);
|
||||
case "gray": return new Color(128 / 255, 128 / 255, 128 / 255);
|
||||
case "white": return new Color(1, 1, 1);
|
||||
case "maroon": return new Color(128 / 255, 0, 0);
|
||||
case "red": return new Color(1, 0, 0);
|
||||
case "purple": return new Color(128 / 255, 0, 128 / 255);
|
||||
case "fuchsia": return new Color(1, 0, 1);
|
||||
case "green": return new Color(0, 128 / 255, 0);
|
||||
case "lime": return new Color(0, 255, 0);
|
||||
case "olive": return new Color(128 / 255, 128 / 255, 0);
|
||||
case "yellow": return new Color(1, 1, 0);
|
||||
case "navy": return new Color(0, 0, 128 / 255);
|
||||
case "blue": return new Color(0, 0, 1);
|
||||
case "teal": return new Color(0, 128 / 255, 128 / 255);
|
||||
case "aqua": return new Color(0, 1, 1);
|
||||
case "orange": return new Color(1, 165 / 255, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static fromVector3(vector: Vector3Object): Color {
|
||||
return new Color(vector.x, vector.y, vector.z);
|
||||
}
|
||||
|
||||
static white(): Color {
|
||||
return new Color(1, 1, 1);
|
||||
}
|
||||
|
||||
static black(): Color {
|
||||
return new Color(0, 0, 0);
|
||||
}
|
||||
|
||||
set(r: number, g: number, b: number): Color {
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
return this;
|
||||
}
|
||||
|
||||
setObject(object: ColorObject): Color {
|
||||
this.r = object.r;
|
||||
this.g = object.g;
|
||||
this.b = object.b;
|
||||
return this;
|
||||
}
|
||||
|
||||
setTuple(tuple: ColorTuple): Color {
|
||||
this.r = tuple[0];
|
||||
this.g = tuple[1];
|
||||
this.b = tuple[2];
|
||||
return this;
|
||||
}
|
||||
|
||||
setName(name: ColorName): Color {
|
||||
switch (name) {
|
||||
case "black":
|
||||
this.r = 0;
|
||||
this.g = 0;
|
||||
this.b = 0;
|
||||
break;
|
||||
case "silver":
|
||||
this.r = 192 / 255;
|
||||
this.g = 192 / 255;
|
||||
this.b = 192 / 255;
|
||||
break;
|
||||
case "gray":
|
||||
this.r = 128 / 255;
|
||||
this.g = 128 / 255;
|
||||
this.b = 128 / 255;
|
||||
break;
|
||||
case "white":
|
||||
this.r = 1;
|
||||
this.g = 1;
|
||||
this.b = 1;
|
||||
break;
|
||||
case "maroon":
|
||||
this.r = 128 / 255;
|
||||
this.g = 0;
|
||||
this.b = 0;
|
||||
break;
|
||||
case "red":
|
||||
this.r = 1;
|
||||
this.g = 0;
|
||||
this.b = 0;
|
||||
break;
|
||||
case "purple":
|
||||
this.r = 128 / 255;
|
||||
this.g = 0;
|
||||
this.b = 128 / 255;
|
||||
break;
|
||||
case "fuchsia":
|
||||
this.r = 1;
|
||||
this.g = 0;
|
||||
this.b = 1;
|
||||
break;
|
||||
case "green":
|
||||
this.r = 0;
|
||||
this.g = 128 / 255;
|
||||
this.b = 0;
|
||||
break;
|
||||
case "lime":
|
||||
this.r = 0;
|
||||
this.g = 255;
|
||||
this.b = 0;
|
||||
break;
|
||||
case "olive":
|
||||
this.r = 128 / 255;
|
||||
this.g = 128 / 255;
|
||||
this.b = 0;
|
||||
break;
|
||||
case "yellow":
|
||||
this.r = 1;
|
||||
this.g = 1;
|
||||
this.b = 0;
|
||||
break;
|
||||
case "navy":
|
||||
this.r = 0;
|
||||
this.g = 0;
|
||||
this.b = 128 / 255;
|
||||
break;
|
||||
case "blue":
|
||||
this.r = 0;
|
||||
this.g = 0;
|
||||
this.b = 1;
|
||||
break;
|
||||
case "teal":
|
||||
this.r = 0;
|
||||
this.g = 128 / 255;
|
||||
this.b = 128 / 255;
|
||||
break;
|
||||
case "aqua":
|
||||
this.r = 0;
|
||||
this.g = 1;
|
||||
this.b = 1;
|
||||
break;
|
||||
case "orange":
|
||||
this.r = 1;
|
||||
this.g = 165 / 255;
|
||||
this.b = 0;
|
||||
break;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
setVector3(vector: Vector3Object): Color {
|
||||
this.r = vector.x;
|
||||
this.g = vector.y;
|
||||
this.b = vector.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
setWhite(): Color {
|
||||
this.r = 1;
|
||||
this.g = 1;
|
||||
this.b = 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
setBlack(): Color {
|
||||
this.r = 0;
|
||||
this.g = 0;
|
||||
this.b = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
setR(r: number): Color {
|
||||
this.r = r;
|
||||
return this;
|
||||
}
|
||||
|
||||
setG(g: number): Color {
|
||||
this.g = g;
|
||||
return this;
|
||||
}
|
||||
|
||||
setB(b: number): Color {
|
||||
this.b = b;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export function isColor(value: unknown): value is Color {
|
||||
return Boolean(value) && (value as Color).type === "Color";
|
||||
}
|
49
src/IndexBuffer.ts
Normal file
49
src/IndexBuffer.ts
Normal file
@ -0,0 +1,49 @@
|
||||
/*!
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
export const INDEX_SIZE = 2;
|
||||
|
||||
export class IndexBuffer {
|
||||
|
||||
readonly type!: "IndexBuffer";
|
||||
|
||||
_device: GPUDevice;
|
||||
_buffer: GPUBuffer;
|
||||
|
||||
constructor(device: GPUDevice, indexCount: number) {
|
||||
Object.defineProperty(this, "type", { value: "IndexBuffer" });
|
||||
|
||||
this._device = device;
|
||||
this._buffer = device.createBuffer({
|
||||
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.INDEX,
|
||||
size: indexCount * INDEX_SIZE,
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): IndexBuffer {
|
||||
this._buffer.destroy();
|
||||
return this;
|
||||
}
|
||||
|
||||
get vertexCount(): number {
|
||||
return this._buffer.size / INDEX_SIZE | 0;
|
||||
}
|
||||
|
||||
writeArray(offset: number, indices: readonly number[]): IndexBuffer {
|
||||
const array = new Uint16Array(indices);
|
||||
this._device.queue.writeBuffer(this._buffer, offset * INDEX_SIZE | 0, array);
|
||||
return this;
|
||||
}
|
||||
|
||||
writeTypedArray(offset: number, indices: Uint16Array): IndexBuffer {
|
||||
this._device.queue.writeBuffer(this._buffer, offset * INDEX_SIZE | 0, indices);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export function isIndexBuffer(value: unknown): value is IndexBuffer {
|
||||
return Boolean(value) && (value as IndexBuffer).type === "IndexBuffer";
|
||||
}
|
76
src/Material.ts
Normal file
76
src/Material.ts
Normal file
@ -0,0 +1,76 @@
|
||||
/*!
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Color, ColorObject } from "./Color";
|
||||
|
||||
export interface MaterialProps {
|
||||
name?: string;
|
||||
|
||||
baseColor?: ColorObject;
|
||||
metallic?: number;
|
||||
roughness?: number;
|
||||
emissive?: ColorObject;
|
||||
partialCoverage?: number;
|
||||
transmission?: ColorObject;
|
||||
collimation?: number;
|
||||
ior?: number;
|
||||
|
||||
doubleSided?: boolean;
|
||||
}
|
||||
|
||||
export class Material {
|
||||
|
||||
readonly type!: "Material";
|
||||
|
||||
_name: string;
|
||||
|
||||
_baseColor: Color;
|
||||
_metallic: number;
|
||||
_roughness: number;
|
||||
_emissive: Color;
|
||||
_partialCoverage: number;
|
||||
_transmission: Color;
|
||||
_collimation: number;
|
||||
_ior: number;
|
||||
|
||||
_doubleSided: boolean;
|
||||
|
||||
constructor({
|
||||
name = "",
|
||||
baseColor,
|
||||
metallic = 1,
|
||||
roughness = 1,
|
||||
emissive,
|
||||
partialCoverage = 1,
|
||||
transmission,
|
||||
collimation = 1,
|
||||
ior = 1.45,
|
||||
doubleSided = false,
|
||||
}: MaterialProps) {
|
||||
Object.defineProperty(this, "type", { value: "Material" });
|
||||
|
||||
this._name = name;
|
||||
|
||||
this._baseColor = baseColor !== undefined ? Color.fromObject(baseColor) : Color.white();
|
||||
this._metallic = metallic;
|
||||
this._roughness = roughness;
|
||||
this._emissive = emissive !== undefined ? Color.fromObject(emissive) : Color.black();
|
||||
this._partialCoverage = partialCoverage;
|
||||
this._transmission = transmission !== undefined ? Color.fromObject(transmission) : Color.black();
|
||||
this._collimation = collimation;
|
||||
this._ior = ior;
|
||||
|
||||
this._doubleSided = doubleSided;
|
||||
}
|
||||
|
||||
get isTransparent(): boolean {
|
||||
return this._partialCoverage < 1 || this._transmission.r > 0 || this._transmission.g > 0 || this._transmission.b > 0;
|
||||
}
|
||||
}
|
||||
|
||||
export function isMaterial(value: unknown): value is Material {
|
||||
return Boolean(value) && (value as Material).type === "Material";
|
||||
}
|
246
src/Matrix4x4.ts
Normal file
246
src/Matrix4x4.ts
Normal file
@ -0,0 +1,246 @@
|
||||
/*!
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { QuaternionObject } from "./Quaternion";
|
||||
import { Vector3Object } from "./Vector3";
|
||||
|
||||
export interface Matrix4x4Object {
|
||||
readonly ix: number;
|
||||
readonly iy: number;
|
||||
readonly iz: number;
|
||||
readonly iw: number;
|
||||
readonly jx: number;
|
||||
readonly jy: number;
|
||||
readonly jz: number;
|
||||
readonly jw: number;
|
||||
readonly kx: number;
|
||||
readonly ky: number;
|
||||
readonly kz: number;
|
||||
readonly kw: number;
|
||||
readonly tx: number;
|
||||
readonly ty: number;
|
||||
readonly tz: number;
|
||||
readonly tw: number;
|
||||
}
|
||||
|
||||
export type Matrix4x4Tuple = readonly [
|
||||
ix: number, iy: number, iz: number, iw: number,
|
||||
jx: number, jy: number, jz: number, jw: number,
|
||||
kx: number, ky: number, kz: number, kw: number,
|
||||
tx: number, ty: number, tz: number, tw: number,
|
||||
];
|
||||
|
||||
export class Matrix4x4 {
|
||||
|
||||
readonly type!: "Matrix4x4";
|
||||
|
||||
ix: number;
|
||||
iy: number;
|
||||
iz: number;
|
||||
iw: number;
|
||||
jx: number;
|
||||
jy: number;
|
||||
jz: number;
|
||||
jw: number;
|
||||
kx: number;
|
||||
ky: number;
|
||||
kz: number;
|
||||
kw: number;
|
||||
tx: number;
|
||||
ty: number;
|
||||
tz: number;
|
||||
tw: number;
|
||||
|
||||
constructor(
|
||||
ix: number, iy: number, iz: number, iw: number,
|
||||
jx: number, jy: number, jz: number, jw: number,
|
||||
kx: number, ky: number, kz: number, kw: number,
|
||||
tx: number, ty: number, tz: number, tw: number
|
||||
) {
|
||||
Object.defineProperty(this, "type", { value: "Matrix4x4" });
|
||||
|
||||
this.ix = ix;
|
||||
this.iy = iy;
|
||||
this.iz = iz;
|
||||
this.iw = iw;
|
||||
this.jx = jx;
|
||||
this.jy = jy;
|
||||
this.jz = jz;
|
||||
this.jw = jw;
|
||||
this.kx = kx;
|
||||
this.ky = ky;
|
||||
this.kz = kz;
|
||||
this.kw = kw;
|
||||
this.tx = tx;
|
||||
this.ty = ty;
|
||||
this.tz = tz;
|
||||
this.tw = tw;
|
||||
}
|
||||
|
||||
static fromObject(object: Matrix4x4Object): Matrix4x4 {
|
||||
return new Matrix4x4(
|
||||
object.ix, object.iy, object.iz, object.iw,
|
||||
object.jx, object.jy, object.jz, object.jw,
|
||||
object.kx, object.ky, object.kz, object.kw,
|
||||
object.tx, object.ty, object.tz, object.tw,
|
||||
);
|
||||
}
|
||||
|
||||
static fromTuple(tuple: Matrix4x4Tuple): Matrix4x4 {
|
||||
return new Matrix4x4(...tuple);
|
||||
}
|
||||
|
||||
static fromTranslation(translation: Vector3Object): Matrix4x4 {
|
||||
return new Matrix4x4(
|
||||
1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
translation.x, translation.y, translation.z, 1,
|
||||
);
|
||||
}
|
||||
|
||||
static fromQuaternion(quaternion: QuaternionObject): Matrix4x4 {
|
||||
const xx = quaternion.x * quaternion.x;
|
||||
const xy = quaternion.x * quaternion.y;
|
||||
const xz = quaternion.x * quaternion.z;
|
||||
const xw = quaternion.x * quaternion.w;
|
||||
const yy = quaternion.y * quaternion.y;
|
||||
const yz = quaternion.y * quaternion.z;
|
||||
const yw = quaternion.y * quaternion.w;
|
||||
const zz = quaternion.z * quaternion.z;
|
||||
const zw = quaternion.z * quaternion.w;
|
||||
|
||||
return new Matrix4x4(
|
||||
1 - 2 * (yy + zz), 2 * (xy + zw), 2 * (xz - yw), 0,
|
||||
2 * (xy - zw), 1 - 2 * (xx + zz), 2 * (yz + xw), 0,
|
||||
2 * (xz + yw), 2 * (yz - xw), 1 - 2 * (xx + yy), 0,
|
||||
0, 0, 0, 1,
|
||||
);
|
||||
}
|
||||
|
||||
static fromScale(scale: Vector3Object): Matrix4x4 {
|
||||
return new Matrix4x4(
|
||||
scale.x, 0, 0, 0,
|
||||
0, scale.y, 0, 0,
|
||||
0, 0, scale.z, 0,
|
||||
0, 0, 0, 1
|
||||
);
|
||||
}
|
||||
|
||||
setObject(object: Matrix4x4Object): Matrix4x4 {
|
||||
this.ix = object.ix;
|
||||
this.iy = object.iy;
|
||||
this.iz = object.iz;
|
||||
this.iw = object.iw;
|
||||
this.jx = object.jx;
|
||||
this.jy = object.jy;
|
||||
this.jz = object.jz;
|
||||
this.jw = object.jw;
|
||||
this.kx = object.kx;
|
||||
this.ky = object.ky;
|
||||
this.kz = object.kz;
|
||||
this.kw = object.kw;
|
||||
this.tx = object.tx;
|
||||
this.ty = object.ty;
|
||||
this.tz = object.tz;
|
||||
this.tw = object.tw;
|
||||
return this;
|
||||
}
|
||||
|
||||
setTuple(tuple: Matrix4x4Tuple): Matrix4x4 {
|
||||
this.ix = tuple[0];
|
||||
this.iy = tuple[1];
|
||||
this.iz = tuple[2];
|
||||
this.iw = tuple[3];
|
||||
this.jx = tuple[4];
|
||||
this.jy = tuple[5];
|
||||
this.jz = tuple[6];
|
||||
this.jw = tuple[7];
|
||||
this.kx = tuple[8];
|
||||
this.ky = tuple[9];
|
||||
this.kz = tuple[10];
|
||||
this.kw = tuple[11];
|
||||
this.tx = tuple[12];
|
||||
this.ty = tuple[13];
|
||||
this.tz = tuple[14];
|
||||
this.tw = tuple[15];
|
||||
return this;
|
||||
}
|
||||
|
||||
setTranslation(translation: Vector3Object): Matrix4x4 {
|
||||
this.ix = 1;
|
||||
this.iy = 0;
|
||||
this.iz = 0;
|
||||
this.iw = 0;
|
||||
this.jx = 0;
|
||||
this.jy = 1;
|
||||
this.jz = 0;
|
||||
this.jw = 0;
|
||||
this.kx = 0;
|
||||
this.ky = 0;
|
||||
this.kz = 1;
|
||||
this.kw = 0;
|
||||
this.tx = translation.x;
|
||||
this.ty = translation.y;
|
||||
this.tz = translation.z;
|
||||
this.tw = 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
setQuaternion(quaternion: QuaternionObject): Matrix4x4 {
|
||||
const xx = quaternion.x * quaternion.x;
|
||||
const xy = quaternion.x * quaternion.y;
|
||||
const xz = quaternion.x * quaternion.z;
|
||||
const xw = quaternion.x * quaternion.w;
|
||||
const yy = quaternion.y * quaternion.y;
|
||||
const yz = quaternion.y * quaternion.z;
|
||||
const yw = quaternion.y * quaternion.w;
|
||||
const zz = quaternion.z * quaternion.z;
|
||||
const zw = quaternion.z * quaternion.w;
|
||||
|
||||
this.ix = 1 - 2 * (yy + zz);
|
||||
this.iy = 2 * (xy + zw);
|
||||
this.iz = 2 * (xz - yw);
|
||||
this.iw = 0;
|
||||
this.jx = 2 * (xy - zw);
|
||||
this.jy = 1 - 2 * (xx + zz);
|
||||
this.jz = 2 * (yz + xw);
|
||||
this.jw = 0;
|
||||
this.kx = 2 * (xz + yw);
|
||||
this.ky = 2 * (yz - xw);
|
||||
this.kz = 1 - 2 * (xx + yy);
|
||||
this.kw = 0;
|
||||
this.tx = 0;
|
||||
this.ty = 0;
|
||||
this.tz = 0;
|
||||
this.tw = 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
setScale(scale: Vector3Object): Matrix4x4 {
|
||||
this.ix = scale.x;
|
||||
this.iy = 0;
|
||||
this.iz = 0;
|
||||
this.iw = 0;
|
||||
this.jx = 0;
|
||||
this.jy = scale.y;
|
||||
this.jz = 0;
|
||||
this.jw = 0;
|
||||
this.kx = 0;
|
||||
this.ky = 0;
|
||||
this.kz = scale.z;
|
||||
this.kw = 0;
|
||||
this.tx = 0;
|
||||
this.ty = 0;
|
||||
this.tz = 0;
|
||||
this.tw = 1;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export function isMatrix4x4(value: unknown): value is Matrix4x4 {
|
||||
return Boolean(value) && (value as Matrix4x4).type === "Matrix4x4";
|
||||
}
|
42
src/Mesh.ts
Normal file
42
src/Mesh.ts
Normal file
@ -0,0 +1,42 @@
|
||||
/*!
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { IndexBuffer } from "./IndexBuffer";
|
||||
import { VertexBuffer } from "./VertexBuffer";
|
||||
|
||||
export interface MeshProps {
|
||||
readonly name?: string;
|
||||
|
||||
readonly vertexBuffer: VertexBuffer;
|
||||
readonly indexBuffer: IndexBuffer;
|
||||
}
|
||||
|
||||
export class Mesh {
|
||||
|
||||
readonly type!: "Mesh"
|
||||
|
||||
_name: string;
|
||||
|
||||
_vertexBuffer: VertexBuffer;
|
||||
_indexBuffer: IndexBuffer;
|
||||
|
||||
constructor({
|
||||
name = "",
|
||||
vertexBuffer,
|
||||
indexBuffer,
|
||||
}: MeshProps) {
|
||||
Object.defineProperty(this, "type", { value: "Mesh" });
|
||||
|
||||
this._name = name;
|
||||
|
||||
this._vertexBuffer = vertexBuffer;
|
||||
this._indexBuffer = indexBuffer
|
||||
}
|
||||
}
|
||||
|
||||
export function isMesh(value: unknown): value is Mesh {
|
||||
return Boolean(value) && (value as Mesh).type === "Mesh";
|
||||
}
|
84
src/Node.ts
Normal file
84
src/Node.ts
Normal file
@ -0,0 +1,84 @@
|
||||
/*!
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Camera } from "./Camera";
|
||||
import { Mesh } from "./Mesh";
|
||||
import { Quaternion, QuaternionObject } from "./Quaternion";
|
||||
import { Vector3, Vector3Object } from "./Vector3";
|
||||
|
||||
export interface NodeProps {
|
||||
readonly name?: string;
|
||||
|
||||
readonly translation?: Vector3Object;
|
||||
readonly rotation?: QuaternionObject;
|
||||
readonly scale?: Vector3Object;
|
||||
|
||||
readonly camera?: Camera;
|
||||
readonly mesh?: Mesh;
|
||||
|
||||
readonly children?: Node[];
|
||||
}
|
||||
|
||||
export class Node {
|
||||
|
||||
readonly type!: "Node";
|
||||
|
||||
_name: string;
|
||||
|
||||
_translation: Vector3;
|
||||
_rotation: Quaternion;
|
||||
_scale: Vector3;
|
||||
|
||||
/** unique */
|
||||
_camera: Camera | undefined;
|
||||
/** shared */
|
||||
_mesh: Mesh | undefined;
|
||||
|
||||
/** unique */
|
||||
_children: Node[];
|
||||
|
||||
/** backreference */
|
||||
_parent: Node | undefined;
|
||||
|
||||
constructor({
|
||||
name = "",
|
||||
translation,
|
||||
rotation,
|
||||
scale,
|
||||
camera,
|
||||
mesh,
|
||||
children = [],
|
||||
}: NodeProps) {
|
||||
Object.defineProperty(this, "type", { value: "Node" });
|
||||
|
||||
this._name = name;
|
||||
|
||||
this._translation = translation !== undefined ? Vector3.fromObject(translation) : Vector3.zero();
|
||||
this._rotation = rotation !== undefined ? Quaternion.fromObject(rotation) : Quaternion.identity();
|
||||
this._scale = scale !== undefined ? Vector3.fromObject(scale) : Vector3.one();
|
||||
|
||||
this._camera = camera;
|
||||
this._mesh = mesh;
|
||||
|
||||
this._children = children;
|
||||
|
||||
this._parent = undefined;
|
||||
|
||||
if (this._camera !== undefined) {
|
||||
this._camera._node = this;
|
||||
}
|
||||
|
||||
if (this._children !== undefined) {
|
||||
for (const child of this._children) {
|
||||
child._parent = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isNode(value: unknown): value is Node {
|
||||
return Boolean(value) && (value as Node).type === "Node";
|
||||
}
|
73
src/Quaternion.ts
Normal file
73
src/Quaternion.ts
Normal file
@ -0,0 +1,73 @@
|
||||
/*!
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
export interface QuaternionObject {
|
||||
readonly x: number;
|
||||
readonly y: number;
|
||||
readonly z: number;
|
||||
readonly w: number;
|
||||
}
|
||||
|
||||
export type QuaternionTuple = readonly [x: number, y: number, z: number, w: number];
|
||||
|
||||
export class Quaternion {
|
||||
|
||||
readonly type!: "Quaternion";
|
||||
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
w: number;
|
||||
|
||||
constructor(x: number, y: number, z: number, w: number) {
|
||||
Object.defineProperty(this, "type", { value: "Quaternion" });
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
static fromObject(object: QuaternionObject): Quaternion {
|
||||
return new Quaternion(object.x, object.y, object.z, object.w);
|
||||
}
|
||||
|
||||
static fromTuple(tuple: QuaternionTuple): Quaternion {
|
||||
return new Quaternion(...tuple);
|
||||
}
|
||||
|
||||
static identity(): Quaternion {
|
||||
return new Quaternion(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
setObject(object: QuaternionObject): Quaternion {
|
||||
this.x = object.x;
|
||||
this.y = object.y;
|
||||
this.z = object.z;
|
||||
this.w = object.w;
|
||||
return this;
|
||||
}
|
||||
|
||||
setTuple(tuple: QuaternionTuple): Quaternion {
|
||||
this.x = tuple[0];
|
||||
this.y = tuple[1];
|
||||
this.z = tuple[2];
|
||||
this.w = tuple[3];
|
||||
return this;
|
||||
}
|
||||
|
||||
setIdentity(): Quaternion {
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.z = 0;
|
||||
this.w = 1;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export function isQuaternion(value: unknown): value is Quaternion {
|
||||
return Boolean(value) && (value as Quaternion).type === "Quaternion";
|
||||
}
|
37
src/Scene.ts
Normal file
37
src/Scene.ts
Normal file
@ -0,0 +1,37 @@
|
||||
/*!
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Node } from "./Node";
|
||||
|
||||
export interface SceneProps {
|
||||
readonly name?: string;
|
||||
|
||||
readonly nodes?: Node[];
|
||||
}
|
||||
|
||||
export class Scene {
|
||||
|
||||
readonly type!: "Scene";
|
||||
|
||||
_name: string | undefined;
|
||||
|
||||
_nodes: Node[];
|
||||
|
||||
constructor({
|
||||
name = "",
|
||||
nodes = [],
|
||||
}: SceneProps) {
|
||||
Object.defineProperty(this, "type", { value: "Scene" });
|
||||
|
||||
this._name = name;
|
||||
|
||||
this._nodes = nodes;
|
||||
}
|
||||
}
|
||||
|
||||
export function isScene(value: unknown): value is Scene {
|
||||
return Boolean(value) && (value as Scene).type === "Scene";
|
||||
}
|
72
src/Texture2D.ts
Normal file
72
src/Texture2D.ts
Normal file
@ -0,0 +1,72 @@
|
||||
/*!
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
export interface Texture2DProps {
|
||||
name?: string;
|
||||
device: GPUDevice;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export class Texture2D {
|
||||
|
||||
readonly type!: "Texture2D";
|
||||
|
||||
_name: string;
|
||||
|
||||
_device: GPUDevice;
|
||||
_texture: GPUTexture;
|
||||
_textureView: GPUTextureView;
|
||||
|
||||
constructor({
|
||||
name = "",
|
||||
device,
|
||||
width,
|
||||
height,
|
||||
}: Texture2DProps) {
|
||||
Object.defineProperty(this, "type", { value: "Texture2D" });
|
||||
|
||||
this._name = name;
|
||||
|
||||
this._device = device;
|
||||
this._texture = device.createTexture({
|
||||
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
|
||||
size: { width, height },
|
||||
format: "rgba8unorm",
|
||||
});
|
||||
this._textureView = this._texture.createView({
|
||||
format: "rgba8unorm",
|
||||
dimension: "2d",
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): Texture2D {
|
||||
this._texture.destroy();
|
||||
return this;
|
||||
}
|
||||
|
||||
get width(): number {
|
||||
return this._texture.width;
|
||||
}
|
||||
|
||||
get height(): number {
|
||||
return this._texture.height;
|
||||
}
|
||||
|
||||
writeTypedArray(data: Uint8Array): Texture2D {
|
||||
this._device.queue.writeTexture(
|
||||
{ texture: this._texture },
|
||||
data,
|
||||
{ bytesPerRow: 4 * this._texture.width },
|
||||
{ width: this._texture.width, height: this._texture.height },
|
||||
);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export function isTexture2D(value: unknown): value is Texture2D {
|
||||
return Boolean(value) && (value as Texture2D).type === "Texture2D";
|
||||
}
|
100
src/Vector3.ts
Normal file
100
src/Vector3.ts
Normal file
@ -0,0 +1,100 @@
|
||||
/*!
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
export interface Vector3Object {
|
||||
readonly x: number;
|
||||
readonly y: number;
|
||||
readonly z: number;
|
||||
}
|
||||
|
||||
export type Vector3Tuple = readonly [x: number, y: number, z: number];
|
||||
|
||||
export class Vector3 {
|
||||
|
||||
readonly type!: "Vector3";
|
||||
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
|
||||
constructor(x: number, y: number, z: number) {
|
||||
Object.defineProperty(this, "type", { value: "Vector3" });
|
||||
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
static fromObject(object: Vector3Object): Vector3 {
|
||||
return new Vector3(object.x, object.y, object.z);
|
||||
}
|
||||
|
||||
static fromTuple(tuple: Vector3Tuple): Vector3 {
|
||||
return new Vector3(...tuple);
|
||||
}
|
||||
|
||||
static zero(): Vector3 {
|
||||
return new Vector3(0, 0, 0);
|
||||
}
|
||||
|
||||
static one(): Vector3 {
|
||||
return new Vector3(1, 1, 1);
|
||||
}
|
||||
|
||||
set(x: number, y: number, z: number): Vector3 {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
setObject(object: Vector3Object): Vector3 {
|
||||
this.x = object.x;
|
||||
this.y = object.y;
|
||||
this.z = object.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
setTuple(tuple: Vector3Tuple): Vector3 {
|
||||
this.x = tuple[0];
|
||||
this.y = tuple[1];
|
||||
this.z = tuple[2];
|
||||
return this;
|
||||
}
|
||||
|
||||
setZero(): Vector3 {
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.z = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
setOne(): Vector3 {
|
||||
this.x = 1;
|
||||
this.y = 1;
|
||||
this.z = 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
setX(x: number): Vector3 {
|
||||
this.x = x;
|
||||
return this;
|
||||
}
|
||||
|
||||
setY(y: number): Vector3 {
|
||||
this.y = y;
|
||||
return this;
|
||||
}
|
||||
|
||||
setZ(z: number): Vector3 {
|
||||
this.z = z;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export function isVector3(value: unknown): value is Vector3 {
|
||||
return Boolean(value) && (value as Vector3).type === "Vector3";
|
||||
}
|
57
src/VertexBuffer.ts
Normal file
57
src/VertexBuffer.ts
Normal file
@ -0,0 +1,57 @@
|
||||
/*!
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Vector3Object } from "./Vector3";
|
||||
|
||||
export const VERTEX_SIZE = 12;
|
||||
|
||||
export class VertexBuffer {
|
||||
|
||||
readonly type!: "VertexBuffer";
|
||||
|
||||
_device: GPUDevice;
|
||||
_buffer: GPUBuffer;
|
||||
|
||||
constructor(device: GPUDevice, vertexCount: number) {
|
||||
Object.defineProperty(this, "type", { value: "VertexBuffer" });
|
||||
|
||||
this._device = device;
|
||||
this._buffer = device.createBuffer({
|
||||
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||
size: vertexCount * VERTEX_SIZE,
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): VertexBuffer {
|
||||
this._buffer.destroy();
|
||||
return this;
|
||||
}
|
||||
|
||||
get vertexCount(): number {
|
||||
return this._buffer.size / VERTEX_SIZE | 0;
|
||||
}
|
||||
|
||||
writeArray(offset: number, vertices: readonly Vector3Object[]): VertexBuffer {
|
||||
const array = new Float32Array(vertices.length * 3);
|
||||
for (let vi = 0, ptr = 0; vi < vertices.length; ++vi) {
|
||||
const vertex = vertices[vi]!;
|
||||
array[ptr++] = vertex.x;
|
||||
array[ptr++] = vertex.y;
|
||||
array[ptr++] = vertex.z;
|
||||
}
|
||||
this._device.queue.writeBuffer(this._buffer, offset * VERTEX_SIZE | 0, array);
|
||||
return this;
|
||||
}
|
||||
|
||||
writeTypedArray(offset: number, vertices: Float32Array): VertexBuffer {
|
||||
this._device.queue.writeBuffer(this._buffer, offset * VERTEX_SIZE | 0, vertices);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export function isVertexBuffer(value: unknown): value is VertexBuffer {
|
||||
return Boolean(value) && (value as VertexBuffer).type === "VertexBuffer";
|
||||
}
|
@ -1 +1,18 @@
|
||||
export {};
|
||||
/*!
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
export * from "./Camera";
|
||||
export * from "./Color";
|
||||
export * from "./IndexBuffer";
|
||||
export * from "./Material";
|
||||
export * from "./Matrix4x4";
|
||||
export * from "./Mesh";
|
||||
export * from "./Node";
|
||||
export * from "./Quaternion";
|
||||
export * from "./Scene";
|
||||
export * from "./Texture2D";
|
||||
export * from "./Vector3";
|
||||
export * from "./VertexBuffer";
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022", "DOM"],
|
||||
"typeRoots": ["./node_modules/@webgpu/types"],
|
||||
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
|
Loading…
Reference in New Issue
Block a user