Reorganize stuff, vertex attribute types
This commit is contained in:
parent
d0c1ecbd5d
commit
1f873f7d87
@ -31,6 +31,14 @@
|
|||||||
".": {
|
".": {
|
||||||
"types": "./dist/oktaeder.d.ts",
|
"types": "./dist/oktaeder.d.ts",
|
||||||
"import": "./dist/oktaeder.js"
|
"import": "./dist/oktaeder.js"
|
||||||
|
},
|
||||||
|
"./data": {
|
||||||
|
"types": "./dist/data/index.d.ts",
|
||||||
|
"import": "./dist/data/index.js"
|
||||||
|
},
|
||||||
|
"./resources": {
|
||||||
|
"types": "./dist/resources/index.d.ts",
|
||||||
|
"import": "./dist/resources/index.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
/*!
|
|
||||||
* 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 { Renderer } from "./Renderer";
|
|
||||||
|
|
||||||
export const INDEX_SIZE = 2;
|
|
||||||
|
|
||||||
export class IndexBuffer {
|
|
||||||
|
|
||||||
readonly type!: "IndexBuffer";
|
|
||||||
_renderer: Renderer;
|
|
||||||
|
|
||||||
_buffer: GPUBuffer;
|
|
||||||
|
|
||||||
constructor(renderer: Renderer, indexCount: number) {
|
|
||||||
Object.defineProperty(this, "type", { value: "IndexBuffer" });
|
|
||||||
|
|
||||||
this._renderer = renderer;
|
|
||||||
|
|
||||||
this._buffer = renderer._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._renderer._device.queue.writeBuffer(this._buffer, offset * INDEX_SIZE | 0, array);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
writeTypedArray(offset: number, indices: Uint16Array): IndexBuffer {
|
|
||||||
this._renderer._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";
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
/*!
|
|
||||||
* 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 { Material, MaterialProps } from "./Material";
|
|
||||||
import { Texture2D, Texture2DProps } from "./Texture2D";
|
|
||||||
import { VertexBuffer } from "./VertexBuffer";
|
|
||||||
|
|
||||||
export class Renderer {
|
|
||||||
|
|
||||||
_adapter: GPUAdapter;
|
|
||||||
_device: GPUDevice;
|
|
||||||
_context: GPUCanvasContext;
|
|
||||||
_format: GPUTextureFormat;
|
|
||||||
|
|
||||||
_textureWhite: Texture2D; // 1×1 rgba8unorm of [255, 255, 255, 255]
|
|
||||||
_textureBlack: Texture2D; // 1×1 rgba8unorm of [0, 0, 0, 255]
|
|
||||||
_textureNormal: Texture2D; // 1×1 rgba8unorm of [128, 128, 128, 255]
|
|
||||||
|
|
||||||
private constructor (
|
|
||||||
adapter: GPUAdapter,
|
|
||||||
device: GPUDevice,
|
|
||||||
context: GPUCanvasContext,
|
|
||||||
format: GPUTextureFormat,
|
|
||||||
) {
|
|
||||||
this._adapter = adapter;
|
|
||||||
this._device = device;
|
|
||||||
this._context = context;
|
|
||||||
this._format = format;
|
|
||||||
|
|
||||||
this._textureWhite = new Texture2D(this, {
|
|
||||||
width: 1,
|
|
||||||
height: 1,
|
|
||||||
});
|
|
||||||
this._textureWhite.writeTypedArray(new Uint8Array([255, 255, 255, 255]));
|
|
||||||
|
|
||||||
this._textureBlack = new Texture2D(this, {
|
|
||||||
width: 1,
|
|
||||||
height: 1,
|
|
||||||
});
|
|
||||||
this._textureBlack.writeTypedArray(new Uint8Array([0, 0, 0, 255]));
|
|
||||||
|
|
||||||
this._textureNormal = new Texture2D(this, {
|
|
||||||
width: 1,
|
|
||||||
height: 1,
|
|
||||||
});
|
|
||||||
this._textureNormal.writeTypedArray(new Uint8Array([128, 128, 128, 255]));
|
|
||||||
}
|
|
||||||
|
|
||||||
static async init(canvas: HTMLCanvasElement) {
|
|
||||||
if (!navigator.gpu) {
|
|
||||||
throw new Error("WebGPU is not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
const adapter = await navigator.gpu.requestAdapter({
|
|
||||||
powerPreference: "high-performance",
|
|
||||||
});
|
|
||||||
if (adapter === null) {
|
|
||||||
throw new Error("GPUAdapter is not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
const device = await adapter.requestDevice();
|
|
||||||
|
|
||||||
const context = canvas.getContext("webgpu");
|
|
||||||
if (context === null) {
|
|
||||||
throw new Error("GPUCanvasContext is not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
const format = navigator.gpu.getPreferredCanvasFormat();
|
|
||||||
context.configure({ device, format });
|
|
||||||
}
|
|
||||||
|
|
||||||
createTexture(props: Texture2DProps): Texture2D {
|
|
||||||
return new Texture2D(this, props);
|
|
||||||
}
|
|
||||||
|
|
||||||
createIndexBuffer(indexCount: number): IndexBuffer {
|
|
||||||
return new IndexBuffer(this, indexCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
createMaterial(props: MaterialProps): Material {
|
|
||||||
return new Material(this, props);
|
|
||||||
}
|
|
||||||
|
|
||||||
createVertexBuffer(vertexCount: number): VertexBuffer {
|
|
||||||
return new VertexBuffer(this, vertexCount);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
/*!
|
|
||||||
* 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 { Renderer } from "./Renderer";
|
|
||||||
|
|
||||||
export interface Texture2DProps {
|
|
||||||
name?: string;
|
|
||||||
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
|
|
||||||
sRGB?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Texture2D {
|
|
||||||
|
|
||||||
readonly type!: "Texture2D";
|
|
||||||
_renderer: Renderer;
|
|
||||||
|
|
||||||
_name: string;
|
|
||||||
|
|
||||||
_texture: GPUTexture;
|
|
||||||
_textureView: GPUTextureView;
|
|
||||||
|
|
||||||
constructor(renderer: Renderer, {
|
|
||||||
name = "",
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
sRGB = false,
|
|
||||||
}: Texture2DProps) {
|
|
||||||
Object.defineProperty(this, "type", { value: "Texture2D" });
|
|
||||||
|
|
||||||
this._renderer = renderer;
|
|
||||||
|
|
||||||
this._name = name;
|
|
||||||
|
|
||||||
this._renderer = renderer;
|
|
||||||
this._texture = renderer._device.createTexture({
|
|
||||||
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
|
|
||||||
size: { width, height },
|
|
||||||
format: sRGB ? "rgba8unorm-srgb" : "rgba8unorm",
|
|
||||||
});
|
|
||||||
this._textureView = this._texture.createView({
|
|
||||||
format: sRGB ? "rgba8unorm-srgb" : "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._renderer._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";
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
/*!
|
|
||||||
* 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 { Renderer } from "./Renderer";
|
|
||||||
import { Vector3Object } from "./Vector3";
|
|
||||||
|
|
||||||
export const VERTEX_SIZE = 12;
|
|
||||||
|
|
||||||
export class VertexBuffer {
|
|
||||||
|
|
||||||
readonly type!: "VertexBuffer";
|
|
||||||
_renderer: Renderer;
|
|
||||||
|
|
||||||
_buffer: GPUBuffer;
|
|
||||||
|
|
||||||
constructor(renderer: Renderer, vertexCount: number) {
|
|
||||||
Object.defineProperty(this, "type", { value: "VertexBuffer" });
|
|
||||||
|
|
||||||
this._renderer = renderer;
|
|
||||||
|
|
||||||
this._buffer = renderer._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._renderer._device.queue.writeBuffer(this._buffer, offset * VERTEX_SIZE | 0, array);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
writeTypedArray(offset: number, vertices: Float32Array): VertexBuffer {
|
|
||||||
this._renderer._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";
|
|
||||||
}
|
|
@ -4,7 +4,7 @@
|
|||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Node } from "./Node";
|
import { Node } from ".";
|
||||||
|
|
||||||
export type Camera = CameraOrthographic | CameraPerspective;
|
export type Camera = CameraOrthographic | CameraPerspective;
|
||||||
|
|
||||||
@ -43,8 +43,6 @@ export class CameraOrthographic {
|
|||||||
nearPlane,
|
nearPlane,
|
||||||
farPlane,
|
farPlane,
|
||||||
}: CameraOrthographicProps) {
|
}: CameraOrthographicProps) {
|
||||||
Object.defineProperty(this, "type", { value: "CameraOrthographic" });
|
|
||||||
|
|
||||||
this._name = name;
|
this._name = name;
|
||||||
|
|
||||||
this._verticalSize = verticalSize;
|
this._verticalSize = verticalSize;
|
||||||
@ -65,6 +63,8 @@ export class CameraOrthographic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(CameraOrthographic.prototype, "type", { value: "CameraOrthographic" });
|
||||||
|
|
||||||
export class CameraPerspective {
|
export class CameraPerspective {
|
||||||
|
|
||||||
readonly type!: "CameraPerspective";
|
readonly type!: "CameraPerspective";
|
||||||
@ -84,8 +84,6 @@ export class CameraPerspective {
|
|||||||
nearPlane,
|
nearPlane,
|
||||||
farPlane,
|
farPlane,
|
||||||
}: CameraPerspectiveProps) {
|
}: CameraPerspectiveProps) {
|
||||||
Object.defineProperty(this, "type", { value: "CameraPerspective" });
|
|
||||||
|
|
||||||
this._name = name;
|
this._name = name;
|
||||||
|
|
||||||
this._verticalFovRad = verticalFovRad;
|
this._verticalFovRad = verticalFovRad;
|
||||||
@ -106,6 +104,8 @@ export class CameraPerspective {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(CameraPerspective.prototype, "type", { value: "CameraPerspective" });
|
||||||
|
|
||||||
export function isCameraOrthographic(value: unknown): value is CameraOrthographic {
|
export function isCameraOrthographic(value: unknown): value is CameraOrthographic {
|
||||||
return Boolean(value) && (value as CameraOrthographic).type === "CameraOrthographic";
|
return Boolean(value) && (value as CameraOrthographic).type === "CameraOrthographic";
|
||||||
}
|
}
|
@ -4,7 +4,7 @@
|
|||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Vector3Object } from "./Vector3";
|
import { Vector3Object } from ".";
|
||||||
|
|
||||||
/* Named colors
|
/* Named colors
|
||||||
* black #000000 (0, 0, 0, 1)
|
* black #000000 (0, 0, 0, 1)
|
||||||
@ -63,8 +63,6 @@ export class Color {
|
|||||||
b: number;
|
b: number;
|
||||||
|
|
||||||
constructor(r: number, g: number, b: number) {
|
constructor(r: number, g: number, b: number) {
|
||||||
Object.defineProperty(this, "type", { value: "Color" });
|
|
||||||
|
|
||||||
this.r = r;
|
this.r = r;
|
||||||
this.g = g;
|
this.g = g;
|
||||||
this.b = b;
|
this.b = b;
|
||||||
@ -261,6 +259,8 @@ export class Color {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(Color.prototype, "type", { value: "Color" });
|
||||||
|
|
||||||
export function isColor(value: unknown): value is Color {
|
export function isColor(value: unknown): value is Color {
|
||||||
return Boolean(value) && (value as Color).type === "Color";
|
return Boolean(value) && (value as Color).type === "Color";
|
||||||
}
|
}
|
@ -4,8 +4,7 @@
|
|||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { QuaternionObject } from "./Quaternion";
|
import { QuaternionObject, Vector3Object } from ".";
|
||||||
import { Vector3Object } from "./Vector3";
|
|
||||||
|
|
||||||
export interface Matrix4x4Object {
|
export interface Matrix4x4Object {
|
||||||
readonly ix: number;
|
readonly ix: number;
|
||||||
@ -60,8 +59,6 @@ export class Matrix4x4 {
|
|||||||
kx: number, ky: number, kz: number, kw: number,
|
kx: number, ky: number, kz: number, kw: number,
|
||||||
tx: number, ty: number, tz: number, tw: number
|
tx: number, ty: number, tz: number, tw: number
|
||||||
) {
|
) {
|
||||||
Object.defineProperty(this, "type", { value: "Matrix4x4" });
|
|
||||||
|
|
||||||
this.ix = ix;
|
this.ix = ix;
|
||||||
this.iy = iy;
|
this.iy = iy;
|
||||||
this.iz = iz;
|
this.iz = iz;
|
||||||
@ -241,6 +238,8 @@ export class Matrix4x4 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(Matrix4x4.prototype, "type", { value: "Matrix4x4" });
|
||||||
|
|
||||||
export function isMatrix4x4(value: unknown): value is Matrix4x4 {
|
export function isMatrix4x4(value: unknown): value is Matrix4x4 {
|
||||||
return Boolean(value) && (value as Matrix4x4).type === "Matrix4x4";
|
return Boolean(value) && (value as Matrix4x4).type === "Matrix4x4";
|
||||||
}
|
}
|
@ -4,8 +4,7 @@
|
|||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IndexBuffer } from "./IndexBuffer";
|
import { IndexBuffer, VertexBuffer } from "../resources";
|
||||||
import { VertexBuffer } from "./VertexBuffer";
|
|
||||||
|
|
||||||
export type Submesh = {
|
export type Submesh = {
|
||||||
start: number,
|
start: number,
|
||||||
@ -36,8 +35,6 @@ export class Mesh {
|
|||||||
indexBuffer,
|
indexBuffer,
|
||||||
submeshes,
|
submeshes,
|
||||||
}: MeshProps) {
|
}: MeshProps) {
|
||||||
Object.defineProperty(this, "type", { value: "Mesh" });
|
|
||||||
|
|
||||||
this._name = name;
|
this._name = name;
|
||||||
|
|
||||||
this._vertexBuffer = vertexBuffer;
|
this._vertexBuffer = vertexBuffer;
|
||||||
@ -50,6 +47,8 @@ export class Mesh {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(Mesh.prototype, "type", { value: "Mesh" });
|
||||||
|
|
||||||
export function isMesh(value: unknown): value is Mesh {
|
export function isMesh(value: unknown): value is Mesh {
|
||||||
return Boolean(value) && (value as Mesh).type === "Mesh";
|
return Boolean(value) && (value as Mesh).type === "Mesh";
|
||||||
}
|
}
|
@ -4,11 +4,8 @@
|
|||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Camera } from "./Camera";
|
import { Camera, Mesh, Quaternion, QuaternionObject, Vector3, Vector3Object } from ".";
|
||||||
import { Material } from "./Material";
|
import { Material } from "../resources";
|
||||||
import { Mesh } from "./Mesh";
|
|
||||||
import { Quaternion, QuaternionObject } from "./Quaternion";
|
|
||||||
import { Vector3, Vector3Object } from "./Vector3";
|
|
||||||
|
|
||||||
export interface NodeProps {
|
export interface NodeProps {
|
||||||
readonly name?: string;
|
readonly name?: string;
|
||||||
@ -57,8 +54,6 @@ export class Node {
|
|||||||
materials = [],
|
materials = [],
|
||||||
children = [],
|
children = [],
|
||||||
}: NodeProps) {
|
}: NodeProps) {
|
||||||
Object.defineProperty(this, "type", { value: "Node" });
|
|
||||||
|
|
||||||
this._name = name;
|
this._name = name;
|
||||||
|
|
||||||
this._translation = translation !== undefined ? Vector3.fromObject(translation) : Vector3.zero();
|
this._translation = translation !== undefined ? Vector3.fromObject(translation) : Vector3.zero();
|
||||||
@ -85,6 +80,8 @@ export class Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(Node.prototype, "type", { value: "Node" });
|
||||||
|
|
||||||
export function isNode(value: unknown): value is Node {
|
export function isNode(value: unknown): value is Node {
|
||||||
return Boolean(value) && (value as Node).type === "Node";
|
return Boolean(value) && (value as Node).type === "Node";
|
||||||
}
|
}
|
@ -23,8 +23,6 @@ export class Quaternion {
|
|||||||
w: number;
|
w: number;
|
||||||
|
|
||||||
constructor(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.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.z = z;
|
this.z = z;
|
||||||
@ -68,6 +66,8 @@ export class Quaternion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(Quaternion.prototype, "type", { value: "Quaternion" });
|
||||||
|
|
||||||
export function isQuaternion(value: unknown): value is Quaternion {
|
export function isQuaternion(value: unknown): value is Quaternion {
|
||||||
return Boolean(value) && (value as Quaternion).type === "Quaternion";
|
return Boolean(value) && (value as Quaternion).type === "Quaternion";
|
||||||
}
|
}
|
@ -4,7 +4,7 @@
|
|||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Node } from "./Node";
|
import { Node } from ".";
|
||||||
|
|
||||||
export interface SceneProps {
|
export interface SceneProps {
|
||||||
readonly name?: string;
|
readonly name?: string;
|
||||||
@ -24,14 +24,14 @@ export class Scene {
|
|||||||
name = "",
|
name = "",
|
||||||
nodes = [],
|
nodes = [],
|
||||||
}: SceneProps) {
|
}: SceneProps) {
|
||||||
Object.defineProperty(this, "type", { value: "Scene" });
|
|
||||||
|
|
||||||
this._name = name;
|
this._name = name;
|
||||||
|
|
||||||
this._nodes = nodes;
|
this._nodes = nodes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(Scene.prototype, "type", { value: "Scene" });
|
||||||
|
|
||||||
export function isScene(value: unknown): value is Scene {
|
export function isScene(value: unknown): value is Scene {
|
||||||
return Boolean(value) && (value as Scene).type === "Scene";
|
return Boolean(value) && (value as Scene).type === "Scene";
|
||||||
}
|
}
|
87
src/data/Vector2.ts
Normal file
87
src/data/Vector2.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*!
|
||||||
|
* 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 Vector2Object {
|
||||||
|
readonly x: number;
|
||||||
|
readonly y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Vector2Tuple = readonly [x: number, y: number];
|
||||||
|
|
||||||
|
export class Vector2 {
|
||||||
|
|
||||||
|
readonly type!: "Vector2";
|
||||||
|
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
|
||||||
|
constructor(x: number, y: number) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromObject(object: Vector2Object): Vector2 {
|
||||||
|
return new Vector2(object.x, object.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromTuple(tuple: Vector2Tuple): Vector2 {
|
||||||
|
return new Vector2(...tuple);
|
||||||
|
}
|
||||||
|
|
||||||
|
static zero(): Vector2 {
|
||||||
|
return new Vector2(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static one(): Vector2 {
|
||||||
|
return new Vector2(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
set(x: number, y: number): Vector2 {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setObject(object: Vector2Object): Vector2 {
|
||||||
|
this.x = object.x;
|
||||||
|
this.y = object.y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTuple(tuple: Vector2Tuple): Vector2 {
|
||||||
|
this.x = tuple[0];
|
||||||
|
this.y = tuple[1];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setZero(): Vector2 {
|
||||||
|
this.x = 0;
|
||||||
|
this.y = 0;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOne(): Vector2 {
|
||||||
|
this.x = 1;
|
||||||
|
this.y = 1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setX(x: number): Vector2 {
|
||||||
|
this.x = x;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setY(y: number): Vector2 {
|
||||||
|
this.y = y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(Vector2.prototype, "type", { value: "Vector2" });
|
||||||
|
|
||||||
|
export function isVector2(value: unknown): value is Vector2 {
|
||||||
|
return Boolean(value) && (value as Vector2).type === "Vector2";
|
||||||
|
}
|
@ -21,8 +21,6 @@ export class Vector3 {
|
|||||||
z: number;
|
z: number;
|
||||||
|
|
||||||
constructor(x: number, y: number, z: number) {
|
constructor(x: number, y: number, z: number) {
|
||||||
Object.defineProperty(this, "type", { value: "Vector3" });
|
|
||||||
|
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.z = z;
|
this.z = z;
|
||||||
@ -95,6 +93,8 @@ export class Vector3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(Vector3.prototype, "type", { value: "Vector3" });
|
||||||
|
|
||||||
export function isVector3(value: unknown): value is Vector3 {
|
export function isVector3(value: unknown): value is Vector3 {
|
||||||
return Boolean(value) && (value as Vector3).type === "Vector3";
|
return Boolean(value) && (value as Vector3).type === "Vector3";
|
||||||
}
|
}
|
113
src/data/Vector4.ts
Normal file
113
src/data/Vector4.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*!
|
||||||
|
* 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 Vector4Object {
|
||||||
|
readonly x: number;
|
||||||
|
readonly y: number;
|
||||||
|
readonly z: number;
|
||||||
|
readonly w: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Vector4Tuple = readonly [x: number, y: number, z: number, w: number];
|
||||||
|
|
||||||
|
export class Vector4 {
|
||||||
|
|
||||||
|
readonly type!: "Vector4";
|
||||||
|
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
z: number;
|
||||||
|
w: number;
|
||||||
|
|
||||||
|
constructor(x: number, y: number, z: number, w: number) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
this.w = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromObject(object: Vector4Object): Vector4 {
|
||||||
|
return new Vector4(object.x, object.y, object.z, object.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromTuple(tuple: Vector4Tuple): Vector4 {
|
||||||
|
return new Vector4(...tuple);
|
||||||
|
}
|
||||||
|
|
||||||
|
static zero(): Vector4 {
|
||||||
|
return new Vector4(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static one(): Vector4 {
|
||||||
|
return new Vector4(1, 1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
set(x: number, y: number, z: number, w: number): Vector4 {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
this.w = w;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setObject(object: Vector4Object): Vector4 {
|
||||||
|
this.x = object.x;
|
||||||
|
this.y = object.y;
|
||||||
|
this.z = object.z;
|
||||||
|
this.w = object.w;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTuple(tuple: Vector4Tuple): Vector4 {
|
||||||
|
this.x = tuple[0];
|
||||||
|
this.y = tuple[1];
|
||||||
|
this.z = tuple[2];
|
||||||
|
this.w = tuple[3];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setZero(): Vector4 {
|
||||||
|
this.x = 0;
|
||||||
|
this.y = 0;
|
||||||
|
this.z = 0;
|
||||||
|
this.w = 0;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOne(): Vector4 {
|
||||||
|
this.x = 1;
|
||||||
|
this.y = 1;
|
||||||
|
this.z = 1;
|
||||||
|
this.w = 1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setX(x: number): Vector4 {
|
||||||
|
this.x = x;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setY(y: number): Vector4 {
|
||||||
|
this.y = y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setZ(z: number): Vector4 {
|
||||||
|
this.z = z;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setW(w: number): Vector4 {
|
||||||
|
this.w = w;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(Vector4.prototype, "type", { value: "Vector4" });
|
||||||
|
|
||||||
|
export function isVector4(value: unknown): value is Vector4 {
|
||||||
|
return Boolean(value) && (value as Vector4).type === "Vector4";
|
||||||
|
}
|
16
src/data/index.ts
Normal file
16
src/data/index.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/*!
|
||||||
|
* 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 "./Matrix4x4";
|
||||||
|
export * from "./Mesh";
|
||||||
|
export * from "./Node";
|
||||||
|
export * from "./Quaternion";
|
||||||
|
export * from "./Scene";
|
||||||
|
export * from "./Vector2";
|
||||||
|
export * from "./Vector3";
|
||||||
|
export * from "./Vector4";
|
118
src/oktaeder.ts
118
src/oktaeder.ts
@ -4,16 +4,108 @@
|
|||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from "./Camera";
|
import { IndexBuffer, IndexBufferProps, Material, MaterialProps, Texture2D, Texture2DProps, VertexBuffer, VertexBufferProps } from "./resources";
|
||||||
export * from "./Color";
|
|
||||||
export * from "./IndexBuffer";
|
export class Renderer {
|
||||||
export * from "./Material";
|
|
||||||
export * from "./Matrix4x4";
|
_adapter: GPUAdapter;
|
||||||
export * from "./Mesh";
|
_device: GPUDevice;
|
||||||
export * from "./Node";
|
_context: GPUCanvasContext;
|
||||||
export * from "./Quaternion";
|
_format: GPUTextureFormat;
|
||||||
export * from "./Renderer";
|
|
||||||
export * from "./Scene";
|
/** 1×1 rgba8unorm texture of [255, 255, 255, 255] */
|
||||||
export * from "./Texture2D";
|
_textureWhite: Texture2D;
|
||||||
export * from "./Vector3";
|
/** 1×1 rgba8unorm texture of [0, 0, 0, 255] */
|
||||||
export * from "./VertexBuffer";
|
_textureBlack: Texture2D;
|
||||||
|
/** 1×1 rgba8unorm texture of [128, 128, 128, 255] */
|
||||||
|
_textureNormal: Texture2D;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This constructor is intended primarily for internal use. Consider using
|
||||||
|
* `Renderer.createIndexBuffer` instead.
|
||||||
|
*/
|
||||||
|
private constructor (
|
||||||
|
adapter: GPUAdapter,
|
||||||
|
device: GPUDevice,
|
||||||
|
context: GPUCanvasContext,
|
||||||
|
format: GPUTextureFormat,
|
||||||
|
) {
|
||||||
|
this._adapter = adapter;
|
||||||
|
this._device = device;
|
||||||
|
this._context = context;
|
||||||
|
this._format = format;
|
||||||
|
|
||||||
|
this._textureWhite = new Texture2D(this, {
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
format: "linear",
|
||||||
|
});
|
||||||
|
this._textureWhite.writeFull(new Uint8Array([255, 255, 255, 255]));
|
||||||
|
|
||||||
|
this._textureBlack = new Texture2D(this, {
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
format: "linear",
|
||||||
|
});
|
||||||
|
this._textureBlack.writeFull(new Uint8Array([0, 0, 0, 255]));
|
||||||
|
|
||||||
|
this._textureNormal = new Texture2D(this, {
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
format: "linear",
|
||||||
|
});
|
||||||
|
this._textureNormal.writeFull(new Uint8Array([128, 128, 128, 255]));
|
||||||
|
}
|
||||||
|
|
||||||
|
static async init(canvas: HTMLCanvasElement) {
|
||||||
|
if (!navigator.gpu) {
|
||||||
|
throw new Error("WebGPU is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
const adapter = await navigator.gpu.requestAdapter({
|
||||||
|
powerPreference: "high-performance",
|
||||||
|
});
|
||||||
|
if (adapter === null) {
|
||||||
|
throw new Error("GPUAdapter is not available");
|
||||||
|
}
|
||||||
|
|
||||||
|
const device = await adapter.requestDevice();
|
||||||
|
|
||||||
|
const context = canvas.getContext("webgpu");
|
||||||
|
if (context === null) {
|
||||||
|
throw new Error("GPUCanvasContext is not available");
|
||||||
|
}
|
||||||
|
|
||||||
|
const format = navigator.gpu.getPreferredCanvasFormat();
|
||||||
|
context.configure({ device, format });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes resources internal to the renderer. Doesn't dispose any objects
|
||||||
|
* created with this renderer. The renderer should not be used after calling
|
||||||
|
* this method.
|
||||||
|
* @returns `this` for chaining
|
||||||
|
*/
|
||||||
|
dispose(): Renderer {
|
||||||
|
this._textureWhite.dispose();
|
||||||
|
this._textureBlack.dispose();
|
||||||
|
this._textureNormal.dispose();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
createIndexBuffer(props: IndexBufferProps): IndexBuffer {
|
||||||
|
return new IndexBuffer(this, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
createMaterial(props: MaterialProps): Material {
|
||||||
|
return new Material(this, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
createTexture(props: Texture2DProps): Texture2D {
|
||||||
|
return new Texture2D(this, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
createVertexBuffer(props: VertexBufferProps): VertexBuffer {
|
||||||
|
return new VertexBuffer(this, props);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
132
src/resources/IndexBuffer.ts
Normal file
132
src/resources/IndexBuffer.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/*!
|
||||||
|
* 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 { Renderer } from "../oktaeder";
|
||||||
|
|
||||||
|
export interface IndexBufferProps {
|
||||||
|
readonly name?: string;
|
||||||
|
|
||||||
|
readonly indexFormat: "uint16" | "uint32";
|
||||||
|
readonly indexCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IndexBufferResizeProps {
|
||||||
|
readonly indexFormat?: "uint16" | "uint32";
|
||||||
|
readonly indexCount?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IndexBuffer {
|
||||||
|
|
||||||
|
readonly type!: "IndexBuffer";
|
||||||
|
_renderer: Renderer;
|
||||||
|
|
||||||
|
_name: string;
|
||||||
|
|
||||||
|
_buffer: GPUBuffer;
|
||||||
|
_indexFormat: "uint16" | "uint32";
|
||||||
|
|
||||||
|
constructor(renderer: Renderer, {
|
||||||
|
name = "",
|
||||||
|
indexFormat,
|
||||||
|
indexCount,
|
||||||
|
}: IndexBufferProps) {
|
||||||
|
this._renderer = renderer;
|
||||||
|
|
||||||
|
this._name = name;
|
||||||
|
|
||||||
|
this._buffer = renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.INDEX,
|
||||||
|
size: indexCount * indexSize(indexFormat),
|
||||||
|
label: name,
|
||||||
|
});
|
||||||
|
this._indexFormat = indexFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys owned GPU resources. The index buffer should not be used after
|
||||||
|
* calling this method.
|
||||||
|
* @returns `this` for chaining
|
||||||
|
*/
|
||||||
|
dispose(): IndexBuffer {
|
||||||
|
this._buffer.destroy();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
get indexCount(): number {
|
||||||
|
return this._buffer.size / indexSize(this._indexFormat) | 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeArray(offset: number, indices: readonly number[]): IndexBuffer {
|
||||||
|
const array = this._indexFormat === "uint16" ? new Uint16Array(indices) : new Uint32Array(indices);
|
||||||
|
return this.writeTypedArray(offset, array);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeTypedArray(offset: number, indices: Uint16Array | Uint32Array): IndexBuffer {
|
||||||
|
if (
|
||||||
|
this._indexFormat === "uint16" && !(indices instanceof Uint16Array)
|
||||||
|
|| this._indexFormat === "uint32" && !(indices instanceof Uint32Array)
|
||||||
|
) {
|
||||||
|
throw new Error(`Cannot write typed array to a mismatched index type. Typed array is of type ${indices.constructor.name}. Index buffer [${this._name}] uses format ${this._indexFormat}`);
|
||||||
|
}
|
||||||
|
this._renderer._device.queue.writeBuffer(this._buffer, offset * indexSize(this._indexFormat) | 0, indices);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize the index buffer, discarding currently stored data.
|
||||||
|
* @param props Desired buffer properties. Any unspecified property will
|
||||||
|
* stay unchanged.
|
||||||
|
* @returns `this` for chaining
|
||||||
|
*/
|
||||||
|
resizeDiscard({
|
||||||
|
indexFormat = this._indexFormat,
|
||||||
|
indexCount = this.indexCount,
|
||||||
|
}: IndexBufferResizeProps): IndexBuffer {
|
||||||
|
this._buffer.destroy();
|
||||||
|
|
||||||
|
this._buffer = this._renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.INDEX,
|
||||||
|
size: indexCount * indexSize(indexFormat),
|
||||||
|
label: this._name,
|
||||||
|
});
|
||||||
|
this._indexFormat = indexFormat;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize the index buffer if it can't hold provided number of indices or
|
||||||
|
* its index format is smaller than provided, potentially discarding
|
||||||
|
* currently stored data.
|
||||||
|
* @param props Desired buffer properties. Any unspecified property will
|
||||||
|
* be ignored.
|
||||||
|
* @returns `this` for chaining
|
||||||
|
*/
|
||||||
|
ensureSizeDiscard({
|
||||||
|
indexFormat = this._indexFormat,
|
||||||
|
indexCount = this.indexCount,
|
||||||
|
}): IndexBuffer {
|
||||||
|
if (this.indexCount >= indexCount && indexSize(this._indexFormat) >= indexSize(indexFormat)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return this.resizeDiscard({
|
||||||
|
indexFormat,
|
||||||
|
indexCount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(IndexBuffer.prototype, "type", { value: "IndexBuffer" });
|
||||||
|
|
||||||
|
export function isIndexBuffer(value: unknown): value is IndexBuffer {
|
||||||
|
return Boolean(value) && (value as IndexBuffer).type === "IndexBuffer";
|
||||||
|
}
|
||||||
|
|
||||||
|
function indexSize(indexFormat: "uint16" | "uint32"): number {
|
||||||
|
switch (indexFormat) {
|
||||||
|
case "uint16": return 2;
|
||||||
|
case "uint32": return 4;
|
||||||
|
}
|
||||||
|
}
|
@ -4,9 +4,9 @@
|
|||||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Color, ColorObject } from "./Color";
|
import { Texture2D } from ".";
|
||||||
import { Renderer } from "./Renderer";
|
import { Color, ColorObject } from "../data";
|
||||||
import { Texture2D } from "./Texture2D";
|
import { Renderer } from "../oktaeder";
|
||||||
|
|
||||||
export const UNIFORM_BUFFER_SIZE = 64;
|
export const UNIFORM_BUFFER_SIZE = 64;
|
||||||
|
|
||||||
@ -81,8 +81,6 @@ export class Material {
|
|||||||
transparent = false,
|
transparent = false,
|
||||||
doubleSided = false,
|
doubleSided = false,
|
||||||
}: MaterialProps) {
|
}: MaterialProps) {
|
||||||
Object.defineProperty(this, "type", { value: "Material" });
|
|
||||||
|
|
||||||
this._renderer = renderer;
|
this._renderer = renderer;
|
||||||
|
|
||||||
this._name = name;
|
this._name = name;
|
||||||
@ -108,11 +106,18 @@ export class Material {
|
|||||||
this._doubleSided = doubleSided;
|
this._doubleSided = doubleSided;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys owned GPU resources. The material should not be used after
|
||||||
|
* calling this method.
|
||||||
|
* @returns `this` for chaining
|
||||||
|
*/
|
||||||
dispose(): Material {
|
dispose(): Material {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(Material.prototype, "type", { value: "Material" });
|
||||||
|
|
||||||
export function isMaterial(value: unknown): value is Material {
|
export function isMaterial(value: unknown): value is Material {
|
||||||
return Boolean(value) && (value as Material).type === "Material";
|
return Boolean(value) && (value as Material).type === "Material";
|
||||||
}
|
}
|
160
src/resources/Texture2D.ts
Normal file
160
src/resources/Texture2D.ts
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
/*!
|
||||||
|
* 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 { Vector2Object, Vector2Tuple } from "../data";
|
||||||
|
import { Renderer } from "../oktaeder";
|
||||||
|
|
||||||
|
export type Texture2DFormat =
|
||||||
|
| "linear"
|
||||||
|
| "srgb"
|
||||||
|
| "hdr"
|
||||||
|
| "depth"
|
||||||
|
;
|
||||||
|
|
||||||
|
export interface Texture2DProps {
|
||||||
|
readonly name?: string;
|
||||||
|
|
||||||
|
readonly width: number;
|
||||||
|
readonly height: number;
|
||||||
|
|
||||||
|
readonly format: Texture2DFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Texture2DAdvancedWriteProps {
|
||||||
|
readonly origin: Vector2Object | Vector2Tuple,
|
||||||
|
readonly data: BufferSource | SharedArrayBuffer,
|
||||||
|
readonly bytesPerRow: number,
|
||||||
|
readonly width: number,
|
||||||
|
readonly height: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Texture2D {
|
||||||
|
|
||||||
|
readonly type!: "Texture2D";
|
||||||
|
_renderer: Renderer;
|
||||||
|
|
||||||
|
_name: string;
|
||||||
|
|
||||||
|
_texture: GPUTexture;
|
||||||
|
_textureView: GPUTextureView;
|
||||||
|
_format: Texture2DFormat;
|
||||||
|
|
||||||
|
constructor(renderer: Renderer, {
|
||||||
|
name = "",
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
format,
|
||||||
|
}: Texture2DProps) {
|
||||||
|
this._renderer = renderer;
|
||||||
|
|
||||||
|
this._name = name;
|
||||||
|
|
||||||
|
const gpuFormat = gpuTextureFormat(format);
|
||||||
|
|
||||||
|
this._renderer = renderer;
|
||||||
|
this._texture = renderer._device.createTexture({
|
||||||
|
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
|
||||||
|
size: { width, height },
|
||||||
|
format: gpuFormat,
|
||||||
|
label: name
|
||||||
|
});
|
||||||
|
this._textureView = this._texture.createView({
|
||||||
|
format: gpuFormat,
|
||||||
|
dimension: "2d",
|
||||||
|
label: `${name}.textureView`,
|
||||||
|
});
|
||||||
|
this._format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose(): Texture2D {
|
||||||
|
this._texture.destroy();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
get width(): number {
|
||||||
|
return this._texture.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
get height(): number {
|
||||||
|
return this._texture.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
get bytesPerSample(): number {
|
||||||
|
return sampleSize(this._format);
|
||||||
|
}
|
||||||
|
|
||||||
|
get samplesPerPixel(): number {
|
||||||
|
return sampleCount(this._format);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFull(data: BufferSource | SharedArrayBuffer): Texture2D {
|
||||||
|
const bytesPerSample = this.bytesPerSample;
|
||||||
|
const samplesPerPixel = this.samplesPerPixel;
|
||||||
|
const bytesPerRow = this.width * samplesPerPixel * bytesPerSample;
|
||||||
|
const byteLength = this.height * bytesPerRow;
|
||||||
|
|
||||||
|
if (data.byteLength !== byteLength) {
|
||||||
|
throw new Error(`Cannot fully write to a texture with different byte length. Source data has byte length of ${data.byteLength}. Texture [${this._name}] is ${this.width}×${this.height} pixels in size, uses ${this._format} format at ${bytesPerSample} ${bytesPerSample === 1 ? "byte" : "bytes"} per sample and ${samplesPerPixel} ${samplesPerPixel === 1 ? "sample" : "samples"} per pixel, which makes its byte length equal to ${byteLength}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._renderer._device.queue.writeTexture(
|
||||||
|
{ texture: this._texture },
|
||||||
|
data,
|
||||||
|
{ bytesPerRow },
|
||||||
|
{ width: this.width, height: this.height },
|
||||||
|
);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
writePartial({
|
||||||
|
origin,
|
||||||
|
data,
|
||||||
|
bytesPerRow,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
}: Texture2DAdvancedWriteProps): Texture2D {
|
||||||
|
this._renderer._device.queue.writeTexture(
|
||||||
|
{ texture: this._texture, origin },
|
||||||
|
data,
|
||||||
|
{ bytesPerRow },
|
||||||
|
{ width, height },
|
||||||
|
);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(Texture2D.prototype, "type", { value: "Texture2D" });
|
||||||
|
|
||||||
|
export function isTexture2D(value: unknown): value is Texture2D {
|
||||||
|
return Boolean(value) && (value as Texture2D).type === "Texture2D";
|
||||||
|
}
|
||||||
|
|
||||||
|
function gpuTextureFormat(format: Texture2DFormat): GPUTextureFormat {
|
||||||
|
switch (format) {
|
||||||
|
case "linear": return "rgba8unorm";
|
||||||
|
case "srgb": return "rgba8unorm-srgb";
|
||||||
|
case "hdr": return "rgba16float";
|
||||||
|
case "depth": return "depth32float";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sampleCount(format: Texture2DFormat): number {
|
||||||
|
switch (format) {
|
||||||
|
case "linear": return 4;
|
||||||
|
case "srgb": return 4;
|
||||||
|
case "hdr": return 4;
|
||||||
|
case "depth": return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sampleSize(format: Texture2DFormat): number {
|
||||||
|
switch (format) {
|
||||||
|
case "linear": return 1;
|
||||||
|
case "srgb": return 1;
|
||||||
|
case "hdr": return 2;
|
||||||
|
case "depth": return 4;
|
||||||
|
}
|
||||||
|
}
|
386
src/resources/VertexBuffer.ts
Normal file
386
src/resources/VertexBuffer.ts
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
/*!
|
||||||
|
* 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 { Vector2Object, Vector3Object, Vector4Object } from "../data";
|
||||||
|
import { Renderer } from "../oktaeder";
|
||||||
|
|
||||||
|
export const POSITION_SIZE = 12;
|
||||||
|
export const TEX_COORD_SIZE = 8;
|
||||||
|
export const LIGHT_TEX_COORD_SIZE = 8;
|
||||||
|
export const NORMAL_SIZE = 12;
|
||||||
|
export const TANGENT_SIZE = 16;
|
||||||
|
|
||||||
|
export interface VertexBufferProps {
|
||||||
|
readonly name?: string;
|
||||||
|
|
||||||
|
readonly vertexCount: number;
|
||||||
|
|
||||||
|
readonly texCoord?: boolean;
|
||||||
|
readonly lightTexCoord?: boolean;
|
||||||
|
readonly normal?: boolean;
|
||||||
|
readonly tangent?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VertexBufferResizeProps {
|
||||||
|
readonly vertexCount?: number;
|
||||||
|
|
||||||
|
readonly texCoord?: boolean;
|
||||||
|
readonly lightTexCoord?: boolean;
|
||||||
|
readonly normal?: boolean;
|
||||||
|
readonly tangent?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VertexBufferWriteArrayProps {
|
||||||
|
readonly position?: readonly Vector3Object[];
|
||||||
|
readonly texCoord?: readonly Vector2Object[];
|
||||||
|
readonly lightTexCoord?: readonly Vector2Object[];
|
||||||
|
readonly normal?: readonly Vector3Object[];
|
||||||
|
readonly tangent?: readonly Vector4Object[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VertexBufferWriteTypedArrayProps {
|
||||||
|
readonly position?: Float32Array;
|
||||||
|
readonly texCoord?: Float32Array;
|
||||||
|
readonly lightTexCoord?: Float32Array;
|
||||||
|
readonly normal?: Float32Array;
|
||||||
|
readonly tangent?: Float32Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class VertexBuffer {
|
||||||
|
|
||||||
|
readonly type!: "VertexBuffer";
|
||||||
|
_renderer: Renderer;
|
||||||
|
|
||||||
|
_name: string;
|
||||||
|
|
||||||
|
_positionBuffer: GPUBuffer;
|
||||||
|
_texCoordBuffer: GPUBuffer | null;
|
||||||
|
_lightTexCoordBuffer: GPUBuffer | null;
|
||||||
|
_normalBuffer: GPUBuffer | null;
|
||||||
|
_tangentBuffer: GPUBuffer | null;
|
||||||
|
|
||||||
|
constructor(renderer: Renderer, {
|
||||||
|
name = "",
|
||||||
|
vertexCount,
|
||||||
|
texCoord = false,
|
||||||
|
lightTexCoord = false,
|
||||||
|
normal = false,
|
||||||
|
tangent = false,
|
||||||
|
}: VertexBufferProps) {
|
||||||
|
this._renderer = renderer;
|
||||||
|
|
||||||
|
this._name = name;
|
||||||
|
|
||||||
|
this._positionBuffer = renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||||
|
size: vertexCount * POSITION_SIZE,
|
||||||
|
label: `${this._name}.position`,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._texCoordBuffer = texCoord ? renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||||
|
size: vertexCount * TEX_COORD_SIZE,
|
||||||
|
label: `${this._name}.texCoord`,
|
||||||
|
}) : null;
|
||||||
|
|
||||||
|
this._lightTexCoordBuffer = lightTexCoord ? renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||||
|
size: vertexCount * LIGHT_TEX_COORD_SIZE,
|
||||||
|
label: `${this._name}.lightTexCoord`,
|
||||||
|
}) : null;
|
||||||
|
|
||||||
|
this._normalBuffer = normal ? renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||||
|
size: vertexCount * NORMAL_SIZE,
|
||||||
|
label: `${this._name}.normal`,
|
||||||
|
}) : null;
|
||||||
|
|
||||||
|
this._tangentBuffer = tangent ? renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||||
|
size: vertexCount * TANGENT_SIZE,
|
||||||
|
label: `${this._name}.tangent`,
|
||||||
|
}) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys owned GPU resources. The vertex buffer should not be used after
|
||||||
|
* calling this method.
|
||||||
|
* @returns `this` for chaining
|
||||||
|
*/
|
||||||
|
dispose(): VertexBuffer {
|
||||||
|
this._positionBuffer.destroy();
|
||||||
|
this._texCoordBuffer?.destroy();
|
||||||
|
this._lightTexCoordBuffer?.destroy();
|
||||||
|
this._normalBuffer?.destroy();
|
||||||
|
this._tangentBuffer?.destroy();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
get vertexCount(): number {
|
||||||
|
return this._positionBuffer.size / POSITION_SIZE | 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasTexCoord(): boolean {
|
||||||
|
return this._texCoordBuffer !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasLightTexCoord(): boolean {
|
||||||
|
return this._lightTexCoordBuffer !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasNormal(): boolean {
|
||||||
|
return this._normalBuffer !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasTangent(): boolean {
|
||||||
|
return this._tangentBuffer !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeArray(offset: number, {
|
||||||
|
position,
|
||||||
|
texCoord,
|
||||||
|
lightTexCoord,
|
||||||
|
normal,
|
||||||
|
tangent,
|
||||||
|
}: VertexBufferWriteArrayProps): VertexBuffer {
|
||||||
|
|
||||||
|
if (position !== undefined) {
|
||||||
|
const array = new Float32Array(position.length * 3);
|
||||||
|
for (let vi = 0, ptr = 0; vi < position.length; ++vi) {
|
||||||
|
const vertex = position[vi]!;
|
||||||
|
array[ptr++] = vertex.x;
|
||||||
|
array[ptr++] = vertex.y;
|
||||||
|
array[ptr++] = vertex.z;
|
||||||
|
}
|
||||||
|
this._renderer._device.queue.writeBuffer(this._positionBuffer, offset * POSITION_SIZE | 0, array);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texCoord !== undefined) {
|
||||||
|
if (this._texCoordBuffer === null) {
|
||||||
|
throw new Error(`Cannot write array to a missing vertex attribute. Tried writing texture coordinates and vertex buffer [${this._name}] does not have texture coordinates.`);
|
||||||
|
}
|
||||||
|
const array = new Float32Array(texCoord.length * 2);
|
||||||
|
for (let vi = 0, ptr = 0; vi < texCoord.length; ++vi) {
|
||||||
|
const vertex = texCoord[vi]!;
|
||||||
|
array[ptr++] = vertex.x;
|
||||||
|
array[ptr++] = vertex.y;
|
||||||
|
}
|
||||||
|
this._renderer._device.queue.writeBuffer(this._texCoordBuffer, offset * TEX_COORD_SIZE | 0, array);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lightTexCoord !== undefined) {
|
||||||
|
if (this._lightTexCoordBuffer === null) {
|
||||||
|
throw new Error(`Cannot write array to a missing vertex attribute. Tried writing light texture coordinates and vertex buffer [${this._name}] does not have light texture coordinates.`);
|
||||||
|
}
|
||||||
|
const array = new Float32Array(lightTexCoord.length * 2);
|
||||||
|
for (let vi = 0, ptr = 0; vi < lightTexCoord.length; ++vi) {
|
||||||
|
const vertex = lightTexCoord[vi]!;
|
||||||
|
array[ptr++] = vertex.x;
|
||||||
|
array[ptr++] = vertex.y;
|
||||||
|
}
|
||||||
|
this._renderer._device.queue.writeBuffer(this._lightTexCoordBuffer, offset * LIGHT_TEX_COORD_SIZE | 0, array);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normal !== undefined) {
|
||||||
|
if (this._normalBuffer === null) {
|
||||||
|
throw new Error(`Cannot write array to a missing vertex attribute. Tried writing normals and vertex buffer [${this._name}] does not have normals.`);
|
||||||
|
}
|
||||||
|
const array = new Float32Array(normal.length * 3);
|
||||||
|
for (let vi = 0, ptr = 0; vi < normal.length; ++vi) {
|
||||||
|
const vertex = normal[vi]!;
|
||||||
|
array[ptr++] = vertex.x;
|
||||||
|
array[ptr++] = vertex.y;
|
||||||
|
array[ptr++] = vertex.z;
|
||||||
|
}
|
||||||
|
this._renderer._device.queue.writeBuffer(this._normalBuffer, offset * NORMAL_SIZE | 0, array);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tangent !== undefined) {
|
||||||
|
if (this._tangentBuffer === null) {
|
||||||
|
throw new Error(`Cannot write array to a missing vertex attribute. Tried writing tangents and vertex buffer [${this._name}] does not have tangents.`);
|
||||||
|
}
|
||||||
|
const array = new Float32Array(tangent.length * 4);
|
||||||
|
for (let vi = 0, ptr = 0; vi < tangent.length; ++vi) {
|
||||||
|
const vertex = tangent[vi]!;
|
||||||
|
array[ptr++] = vertex.x;
|
||||||
|
array[ptr++] = vertex.y;
|
||||||
|
array[ptr++] = vertex.z;
|
||||||
|
array[ptr++] = vertex.w;
|
||||||
|
}
|
||||||
|
this._renderer._device.queue.writeBuffer(this._tangentBuffer, offset * TANGENT_SIZE | 0, array);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeTypedArray(offset: number, {
|
||||||
|
position,
|
||||||
|
texCoord,
|
||||||
|
lightTexCoord,
|
||||||
|
normal,
|
||||||
|
tangent,
|
||||||
|
}: VertexBufferWriteTypedArrayProps): VertexBuffer {
|
||||||
|
|
||||||
|
if (position !== undefined) {
|
||||||
|
this._renderer._device.queue.writeBuffer(this._positionBuffer, offset * POSITION_SIZE | 0, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texCoord !== undefined) {
|
||||||
|
if (this._texCoordBuffer === null) {
|
||||||
|
throw new Error(`Cannot write typed array to a missing vertex attribute. Tried writing texture coordinates and vertex buffer [${this._name}] does not have texture coordinates.`);
|
||||||
|
}
|
||||||
|
this._renderer._device.queue.writeBuffer(this._texCoordBuffer, offset * TEX_COORD_SIZE | 0, texCoord);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lightTexCoord !== undefined) {
|
||||||
|
if (this._lightTexCoordBuffer === null) {
|
||||||
|
throw new Error(`Cannot write typed array to a missing vertex attribute. Tried writing light texture coordinates and vertex buffer [${this._name}] does not have light texture coordinates.`);
|
||||||
|
}
|
||||||
|
this._renderer._device.queue.writeBuffer(this._lightTexCoordBuffer, offset * LIGHT_TEX_COORD_SIZE | 0, lightTexCoord);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normal !== undefined) {
|
||||||
|
if (this._normalBuffer === null) {
|
||||||
|
throw new Error(`Cannot write typed array to a missing vertex attribute. Tried writing normals and vertex buffer [${this._name}] does not have normals.`);
|
||||||
|
}
|
||||||
|
this._renderer._device.queue.writeBuffer(this._normalBuffer, offset * NORMAL_SIZE | 0, normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tangent !== undefined) {
|
||||||
|
if (this._tangentBuffer === null) {
|
||||||
|
throw new Error(`Cannot write typed array to a missing vertex attribute. Tried writing tangents and vertex buffer [${this._name}] does not have tangents.`);
|
||||||
|
}
|
||||||
|
this._renderer._device.queue.writeBuffer(this._tangentBuffer, offset * TANGENT_SIZE | 0, tangent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize the vertex buffer and/or add or remove vertex attributes,
|
||||||
|
* discarding currently stored data.
|
||||||
|
* @param props Desired buffer properties. Any unspecified property will
|
||||||
|
* stay unchanged.
|
||||||
|
* @returns `this` for chaining
|
||||||
|
*/
|
||||||
|
resizeDiscard({
|
||||||
|
vertexCount = this.vertexCount,
|
||||||
|
texCoord = this.hasTexCoord,
|
||||||
|
lightTexCoord = this.hasLightTexCoord,
|
||||||
|
normal = this.hasNormal,
|
||||||
|
tangent = this.hasTangent,
|
||||||
|
}: VertexBufferResizeProps): VertexBuffer {
|
||||||
|
|
||||||
|
this._positionBuffer.destroy();
|
||||||
|
this._texCoordBuffer?.destroy();
|
||||||
|
this._lightTexCoordBuffer?.destroy();
|
||||||
|
this._normalBuffer?.destroy();
|
||||||
|
this._tangentBuffer?.destroy();
|
||||||
|
|
||||||
|
this._positionBuffer = this._renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||||
|
size: vertexCount * POSITION_SIZE,
|
||||||
|
label: `${this._name}.position`,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._texCoordBuffer = texCoord ? this._renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||||
|
size: vertexCount * TEX_COORD_SIZE,
|
||||||
|
label: `${this._name}.texCoord`,
|
||||||
|
}) : null;
|
||||||
|
|
||||||
|
this._lightTexCoordBuffer = lightTexCoord ? this._renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||||
|
size: vertexCount * LIGHT_TEX_COORD_SIZE,
|
||||||
|
label: `${this._name}.lightTexCoord`,
|
||||||
|
}) : null;
|
||||||
|
|
||||||
|
this._normalBuffer = normal ? this._renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||||
|
size: vertexCount * NORMAL_SIZE,
|
||||||
|
label: `${this._name}.normal`,
|
||||||
|
}) : null;
|
||||||
|
|
||||||
|
this._tangentBuffer = tangent ? this._renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||||
|
size: vertexCount * TANGENT_SIZE,
|
||||||
|
label: `${this._name}.tangent`,
|
||||||
|
}) : null;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize the vertex buffer and/or add vertex attributes if it can't hold
|
||||||
|
* provided number of vertices or doesn't have provided attributes,
|
||||||
|
* potentially discarding currently stored data.
|
||||||
|
* @param props Desired buffer properties. Any unspecified property will be
|
||||||
|
* ignored.
|
||||||
|
* @returns `this` for chaining
|
||||||
|
*/
|
||||||
|
ensureSizeDiscard({
|
||||||
|
vertexCount = this.vertexCount,
|
||||||
|
texCoord = this.hasTexCoord,
|
||||||
|
lightTexCoord = this.hasLightTexCoord,
|
||||||
|
normal = this.hasNormal,
|
||||||
|
tangent = this.hasTangent,
|
||||||
|
}): VertexBuffer {
|
||||||
|
const currentVertexCount = this.vertexCount;
|
||||||
|
|
||||||
|
if (currentVertexCount < vertexCount) {
|
||||||
|
this._positionBuffer.destroy();
|
||||||
|
this._positionBuffer = this._renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||||
|
size: vertexCount * POSITION_SIZE,
|
||||||
|
label: `${this._name}.position`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentVertexCount < vertexCount || texCoord && !this.hasTexCoord) {
|
||||||
|
this._texCoordBuffer?.destroy();
|
||||||
|
this._texCoordBuffer = this._renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||||
|
size: vertexCount * TEX_COORD_SIZE,
|
||||||
|
label: `${this._name}.texCoord`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentVertexCount < vertexCount || lightTexCoord && !this.hasLightTexCoord) {
|
||||||
|
this._lightTexCoordBuffer?.destroy();
|
||||||
|
this._lightTexCoordBuffer = this._renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||||
|
size: vertexCount * LIGHT_TEX_COORD_SIZE,
|
||||||
|
label: `${this._name}.lightTexCoord`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentVertexCount < vertexCount || normal && !this.hasNormal) {
|
||||||
|
this._normalBuffer?.destroy();
|
||||||
|
this._normalBuffer = this._renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||||
|
size: vertexCount * NORMAL_SIZE,
|
||||||
|
label: `${this._name}.normal`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentVertexCount < vertexCount || tangent && !this.hasTangent) {
|
||||||
|
this._tangentBuffer?.destroy();
|
||||||
|
this._tangentBuffer = this._renderer._device.createBuffer({
|
||||||
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||||
|
size: vertexCount * TANGENT_SIZE,
|
||||||
|
label: `${this._name}.tangent`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(VertexBuffer.prototype, "type", { value: "VertexBuffer" });
|
||||||
|
|
||||||
|
export function isVertexBuffer(value: unknown): value is VertexBuffer {
|
||||||
|
return Boolean(value) && (value as VertexBuffer).type === "VertexBuffer";
|
||||||
|
}
|
10
src/resources/index.ts
Normal file
10
src/resources/index.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/*!
|
||||||
|
* 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 "./IndexBuffer";
|
||||||
|
export * from "./Material";
|
||||||
|
export * from "./Texture2D";
|
||||||
|
export * from "./VertexBuffer";
|
Loading…
Reference in New Issue
Block a user