Expand material data structure, renderer class
This commit is contained in:
parent
2c3020ff95
commit
174201afc1
@ -35,7 +35,7 @@ export class CameraOrthographic {
|
||||
_farPlane: number;
|
||||
|
||||
/** backreference */
|
||||
_node: Node | undefined;
|
||||
_node: Node | null;
|
||||
|
||||
constructor({
|
||||
name = "",
|
||||
@ -51,16 +51,16 @@ export class CameraOrthographic {
|
||||
this._nearPlane = nearPlane;
|
||||
this._farPlane = farPlane;
|
||||
|
||||
this._node = undefined;
|
||||
this._node = null;
|
||||
}
|
||||
|
||||
detach(): Camera {
|
||||
if (this._node === undefined) {
|
||||
if (this._node === null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this._node._camera = undefined;
|
||||
this._node = undefined;
|
||||
this._node._camera = null;
|
||||
this._node = null;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@ -76,7 +76,7 @@ export class CameraPerspective {
|
||||
_farPlane: number;
|
||||
|
||||
/** backreference */
|
||||
_node: Node | undefined;
|
||||
_node: Node | null;
|
||||
|
||||
constructor({
|
||||
name = "",
|
||||
@ -92,16 +92,16 @@ export class CameraPerspective {
|
||||
this._nearPlane = nearPlane;
|
||||
this._farPlane = farPlane;
|
||||
|
||||
this._node = undefined;
|
||||
this._node = null;
|
||||
}
|
||||
|
||||
detach(): Camera {
|
||||
if (this._node === undefined) {
|
||||
if (this._node === null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this._node._camera = undefined;
|
||||
this._node = undefined;
|
||||
this._node._camera = null;
|
||||
this._node = null;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -4,20 +4,23 @@
|
||||
* 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;
|
||||
|
||||
_device: GPUDevice;
|
||||
_buffer: GPUBuffer;
|
||||
|
||||
constructor(device: GPUDevice, indexCount: number) {
|
||||
constructor(renderer: Renderer, indexCount: number) {
|
||||
Object.defineProperty(this, "type", { value: "IndexBuffer" });
|
||||
|
||||
this._device = device;
|
||||
this._buffer = device.createBuffer({
|
||||
this._renderer = renderer;
|
||||
|
||||
this._buffer = renderer._device.createBuffer({
|
||||
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.INDEX,
|
||||
size: indexCount * INDEX_SIZE,
|
||||
});
|
||||
@ -34,12 +37,12 @@ export class IndexBuffer {
|
||||
|
||||
writeArray(offset: number, indices: readonly number[]): IndexBuffer {
|
||||
const array = new Uint16Array(indices);
|
||||
this._device.queue.writeBuffer(this._buffer, offset * INDEX_SIZE | 0, array);
|
||||
this._renderer._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);
|
||||
this._renderer._device.queue.writeBuffer(this._buffer, offset * INDEX_SIZE | 0, indices);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -5,69 +5,111 @@
|
||||
*/
|
||||
|
||||
import { Color, ColorObject } from "./Color";
|
||||
import { Renderer } from "./Renderer";
|
||||
import { Texture2D } from "./Texture2D";
|
||||
|
||||
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;
|
||||
partialCoverage?: number;
|
||||
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;
|
||||
_partialCoverage: number;
|
||||
_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({
|
||||
constructor(renderer: Renderer, {
|
||||
name = "",
|
||||
baseColor,
|
||||
partialCoverage = 1,
|
||||
occlusionTextureStrength = 1,
|
||||
metallic = 1,
|
||||
roughness = 1,
|
||||
normalScale = 1,
|
||||
emissive,
|
||||
partialCoverage = 1,
|
||||
transmission,
|
||||
collimation = 1,
|
||||
ior = 1.45,
|
||||
baseColorPartialCoverageTexture = null,
|
||||
occlusionMetallicRoughnessTexture = null,
|
||||
normalTexture = null,
|
||||
emissiveTexture = null,
|
||||
transmissionCollimationTexture = null,
|
||||
transparent = false,
|
||||
doubleSided = false,
|
||||
}: MaterialProps) {
|
||||
Object.defineProperty(this, "type", { value: "Material" });
|
||||
|
||||
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._partialCoverage = partialCoverage;
|
||||
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;
|
||||
}
|
||||
|
||||
get isTransparent(): boolean {
|
||||
return this._partialCoverage < 1 || this._transmission.r > 0 || this._transmission.g > 0 || this._transmission.b > 0;
|
||||
dispose(): Material {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
13
src/Mesh.ts
13
src/Mesh.ts
@ -7,11 +7,17 @@
|
||||
import { IndexBuffer } from "./IndexBuffer";
|
||||
import { VertexBuffer } from "./VertexBuffer";
|
||||
|
||||
export type Submesh = {
|
||||
start: number,
|
||||
length: number,
|
||||
};
|
||||
|
||||
export interface MeshProps {
|
||||
readonly name?: string;
|
||||
|
||||
readonly vertexBuffer: VertexBuffer;
|
||||
readonly indexBuffer: IndexBuffer;
|
||||
readonly submeshes: Submesh[];
|
||||
}
|
||||
|
||||
export class Mesh {
|
||||
@ -22,11 +28,13 @@ export class Mesh {
|
||||
|
||||
_vertexBuffer: VertexBuffer;
|
||||
_indexBuffer: IndexBuffer;
|
||||
_submeshes: Submesh[];
|
||||
|
||||
constructor({
|
||||
name = "",
|
||||
vertexBuffer,
|
||||
indexBuffer,
|
||||
submeshes,
|
||||
}: MeshProps) {
|
||||
Object.defineProperty(this, "type", { value: "Mesh" });
|
||||
|
||||
@ -34,6 +42,11 @@ export class Mesh {
|
||||
|
||||
this._vertexBuffer = vertexBuffer;
|
||||
this._indexBuffer = indexBuffer
|
||||
this._submeshes = submeshes;
|
||||
}
|
||||
|
||||
get submeshCount(): number {
|
||||
return this._submeshes.length;
|
||||
}
|
||||
}
|
||||
|
||||
|
26
src/Node.ts
26
src/Node.ts
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import { Camera } from "./Camera";
|
||||
import { Material } from "./Material";
|
||||
import { Mesh } from "./Mesh";
|
||||
import { Quaternion, QuaternionObject } from "./Quaternion";
|
||||
import { Vector3, Vector3Object } from "./Vector3";
|
||||
@ -16,8 +17,9 @@ export interface NodeProps {
|
||||
readonly rotation?: QuaternionObject;
|
||||
readonly scale?: Vector3Object;
|
||||
|
||||
readonly camera?: Camera;
|
||||
readonly mesh?: Mesh;
|
||||
readonly camera?: Camera | null;
|
||||
readonly mesh?: Mesh | null;
|
||||
readonly materials?: Material[];
|
||||
|
||||
readonly children?: Node[];
|
||||
}
|
||||
@ -33,23 +35,26 @@ export class Node {
|
||||
_scale: Vector3;
|
||||
|
||||
/** unique */
|
||||
_camera: Camera | undefined;
|
||||
_camera: Camera | null;
|
||||
/** shared */
|
||||
_mesh: Mesh | undefined;
|
||||
_mesh: Mesh | null;
|
||||
/** shared */
|
||||
_materials: Material[];
|
||||
|
||||
/** unique */
|
||||
_children: Node[];
|
||||
|
||||
/** backreference */
|
||||
_parent: Node | undefined;
|
||||
_parent: Node | null;
|
||||
|
||||
constructor({
|
||||
name = "",
|
||||
translation,
|
||||
rotation,
|
||||
scale,
|
||||
camera,
|
||||
mesh,
|
||||
camera = null,
|
||||
mesh = null,
|
||||
materials = [],
|
||||
children = [],
|
||||
}: NodeProps) {
|
||||
Object.defineProperty(this, "type", { value: "Node" });
|
||||
@ -62,16 +67,17 @@ export class Node {
|
||||
|
||||
this._camera = camera;
|
||||
this._mesh = mesh;
|
||||
this._materials = materials;
|
||||
|
||||
this._children = children;
|
||||
|
||||
this._parent = undefined;
|
||||
this._parent = null;
|
||||
|
||||
if (this._camera !== undefined) {
|
||||
if (this._camera !== null) {
|
||||
this._camera._node = this;
|
||||
}
|
||||
|
||||
if (this._children !== undefined) {
|
||||
if (this._children !== null) {
|
||||
for (const child of this._children) {
|
||||
child._parent = this;
|
||||
}
|
||||
|
91
src/Renderer.ts
Normal file
91
src/Renderer.ts
Normal file
@ -0,0 +1,91 @@
|
||||
/*!
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ export class Scene {
|
||||
|
||||
readonly type!: "Scene";
|
||||
|
||||
_name: string | undefined;
|
||||
_name: string;
|
||||
|
||||
_nodes: Node[];
|
||||
|
||||
|
@ -4,41 +4,47 @@
|
||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Renderer } from "./Renderer";
|
||||
|
||||
export interface Texture2DProps {
|
||||
name?: string;
|
||||
device: GPUDevice;
|
||||
|
||||
width: number;
|
||||
height: number;
|
||||
|
||||
sRGB?: boolean;
|
||||
}
|
||||
|
||||
export class Texture2D {
|
||||
|
||||
readonly type!: "Texture2D";
|
||||
_renderer: Renderer;
|
||||
|
||||
_name: string;
|
||||
|
||||
_device: GPUDevice;
|
||||
_texture: GPUTexture;
|
||||
_textureView: GPUTextureView;
|
||||
|
||||
constructor({
|
||||
constructor(renderer: Renderer, {
|
||||
name = "",
|
||||
device,
|
||||
width,
|
||||
height,
|
||||
sRGB = false,
|
||||
}: Texture2DProps) {
|
||||
Object.defineProperty(this, "type", { value: "Texture2D" });
|
||||
|
||||
this._renderer = renderer;
|
||||
|
||||
this._name = name;
|
||||
|
||||
this._device = device;
|
||||
this._texture = device.createTexture({
|
||||
this._renderer = renderer;
|
||||
this._texture = renderer._device.createTexture({
|
||||
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
|
||||
size: { width, height },
|
||||
format: "rgba8unorm",
|
||||
format: sRGB ? "rgba8unorm-srgb" : "rgba8unorm",
|
||||
});
|
||||
this._textureView = this._texture.createView({
|
||||
format: "rgba8unorm",
|
||||
format: sRGB ? "rgba8unorm-srgb" : "rgba8unorm",
|
||||
dimension: "2d",
|
||||
});
|
||||
}
|
||||
@ -57,7 +63,7 @@ export class Texture2D {
|
||||
}
|
||||
|
||||
writeTypedArray(data: Uint8Array): Texture2D {
|
||||
this._device.queue.writeTexture(
|
||||
this._renderer._device.queue.writeTexture(
|
||||
{ texture: this._texture },
|
||||
data,
|
||||
{ bytesPerRow: 4 * this._texture.width },
|
||||
|
@ -4,6 +4,7 @@
|
||||
* obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Renderer } from "./Renderer";
|
||||
import { Vector3Object } from "./Vector3";
|
||||
|
||||
export const VERTEX_SIZE = 12;
|
||||
@ -11,15 +12,16 @@ export const VERTEX_SIZE = 12;
|
||||
export class VertexBuffer {
|
||||
|
||||
readonly type!: "VertexBuffer";
|
||||
_renderer: Renderer;
|
||||
|
||||
_device: GPUDevice;
|
||||
_buffer: GPUBuffer;
|
||||
|
||||
constructor(device: GPUDevice, vertexCount: number) {
|
||||
constructor(renderer: Renderer, vertexCount: number) {
|
||||
Object.defineProperty(this, "type", { value: "VertexBuffer" });
|
||||
|
||||
this._device = device;
|
||||
this._buffer = device.createBuffer({
|
||||
this._renderer = renderer;
|
||||
|
||||
this._buffer = renderer._device.createBuffer({
|
||||
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
|
||||
size: vertexCount * VERTEX_SIZE,
|
||||
});
|
||||
@ -42,12 +44,12 @@ export class VertexBuffer {
|
||||
array[ptr++] = vertex.y;
|
||||
array[ptr++] = vertex.z;
|
||||
}
|
||||
this._device.queue.writeBuffer(this._buffer, offset * VERTEX_SIZE | 0, array);
|
||||
this._renderer._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);
|
||||
this._renderer._device.queue.writeBuffer(this._buffer, offset * VERTEX_SIZE | 0, vertices);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ export * from "./Matrix4x4";
|
||||
export * from "./Mesh";
|
||||
export * from "./Node";
|
||||
export * from "./Quaternion";
|
||||
export * from "./Renderer";
|
||||
export * from "./Scene";
|
||||
export * from "./Texture2D";
|
||||
export * from "./Vector3";
|
||||
|
Loading…
Reference in New Issue
Block a user