Reorganize stuff, vertex attribute types
This commit is contained in:
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;
|
||||
}
|
||||
}
|
||||
123
src/resources/Material.ts
Normal file
123
src/resources/Material.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
/*!
|
||||
* 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 { Texture2D } from ".";
|
||||
import { Color, ColorObject } from "../data";
|
||||
import { Renderer } from "../oktaeder";
|
||||
|
||||
export const UNIFORM_BUFFER_SIZE = 64;
|
||||
|
||||
export interface MaterialProps {
|
||||
name?: string;
|
||||
|
||||
baseColor?: ColorObject;
|
||||
partialCoverage?: number;
|
||||
occlusionTextureStrength?: number;
|
||||
metallic?: number;
|
||||
roughness?: number;
|
||||
normalScale?: number;
|
||||
emissive?: ColorObject;
|
||||
transmission?: ColorObject;
|
||||
collimation?: number;
|
||||
ior?: number;
|
||||
|
||||
baseColorPartialCoverageTexture?: Texture2D | null;
|
||||
occlusionMetallicRoughnessTexture?: Texture2D | null;
|
||||
normalTexture?: Texture2D | null;
|
||||
emissiveTexture?: Texture2D | null;
|
||||
transmissionCollimationTexture?: Texture2D | null;
|
||||
|
||||
transparent?: boolean;
|
||||
doubleSided?: boolean;
|
||||
}
|
||||
|
||||
export class Material {
|
||||
|
||||
readonly type!: "Material";
|
||||
_renderer: Renderer;
|
||||
|
||||
_name: string;
|
||||
|
||||
_baseColor: Color;
|
||||
_partialCoverage: number;
|
||||
_occlusionTextureStrength: number;
|
||||
_metallic: number;
|
||||
_roughness: number;
|
||||
_normalScale: number;
|
||||
_emissive: Color;
|
||||
_transmission: Color;
|
||||
_collimation: number;
|
||||
_ior: number;
|
||||
|
||||
_baseColorPartialCoverageTexture: Texture2D | null;
|
||||
_occlusionMetallicRoughnessTexture: Texture2D | null;
|
||||
_normalTexture: Texture2D | null;
|
||||
_emissiveTexture: Texture2D | null;
|
||||
_transmissionCollimationTexture: Texture2D | null;
|
||||
|
||||
_transparent: boolean;
|
||||
_doubleSided: boolean;
|
||||
|
||||
constructor(renderer: Renderer, {
|
||||
name = "",
|
||||
baseColor,
|
||||
partialCoverage = 1,
|
||||
occlusionTextureStrength = 1,
|
||||
metallic = 1,
|
||||
roughness = 1,
|
||||
normalScale = 1,
|
||||
emissive,
|
||||
transmission,
|
||||
collimation = 1,
|
||||
ior = 1.45,
|
||||
baseColorPartialCoverageTexture = null,
|
||||
occlusionMetallicRoughnessTexture = null,
|
||||
normalTexture = null,
|
||||
emissiveTexture = null,
|
||||
transmissionCollimationTexture = null,
|
||||
transparent = false,
|
||||
doubleSided = false,
|
||||
}: MaterialProps) {
|
||||
this._renderer = renderer;
|
||||
|
||||
this._name = name;
|
||||
|
||||
this._baseColor = baseColor !== undefined ? Color.fromObject(baseColor) : Color.white();
|
||||
this._partialCoverage = partialCoverage;
|
||||
this._occlusionTextureStrength = occlusionTextureStrength;
|
||||
this._metallic = metallic;
|
||||
this._roughness = roughness;
|
||||
this._normalScale = normalScale;
|
||||
this._emissive = emissive !== undefined ? Color.fromObject(emissive) : Color.black();
|
||||
this._transmission = transmission !== undefined ? Color.fromObject(transmission) : Color.black();
|
||||
this._collimation = collimation;
|
||||
this._ior = ior;
|
||||
|
||||
this._baseColorPartialCoverageTexture = baseColorPartialCoverageTexture;
|
||||
this._occlusionMetallicRoughnessTexture = occlusionMetallicRoughnessTexture;
|
||||
this._normalTexture = normalTexture;
|
||||
this._emissiveTexture = emissiveTexture;
|
||||
this._transmissionCollimationTexture = transmissionCollimationTexture;
|
||||
|
||||
this._transparent = transparent;
|
||||
this._doubleSided = doubleSided;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys owned GPU resources. The material should not be used after
|
||||
* calling this method.
|
||||
* @returns `this` for chaining
|
||||
*/
|
||||
dispose(): Material {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(Material.prototype, "type", { value: "Material" });
|
||||
|
||||
export function isMaterial(value: unknown): value is 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";
|
||||
Reference in New Issue
Block a user