Update readme, example project, fix runtime errors
This commit is contained in:
		| @@ -53,18 +53,20 @@ export class _BinaryWriter { | ||||
| 	} | ||||
|  | ||||
| 	ensureUnusedCapacity(desiredUnusedCapacity: number): _BinaryWriter { | ||||
| 		return this.ensureCapacity(this._buffer.byteLength + desiredUnusedCapacity); | ||||
| 		return this.ensureCapacity(this._length + desiredUnusedCapacity); | ||||
| 	} | ||||
|  | ||||
| 	writeU32(value: number): _BinaryWriter { | ||||
| 		this.ensureUnusedCapacity(4); | ||||
| 		this._dataView.setUint32(this._length, value, true); | ||||
| 		this._length += 4; | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	writeF32(value: number): _BinaryWriter { | ||||
| 		this.ensureUnusedCapacity(4); | ||||
| 		this._dataView.setFloat32(this._length, value, true); | ||||
| 		this._length += 4; | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| @@ -116,6 +118,19 @@ export class _BinaryWriter { | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	padToAlign(alignment: number): _BinaryWriter { | ||||
| 		const alignedLength = (this._length + alignment - 1) & ~(alignment - 1); | ||||
| 		const padding = alignedLength - this._length; | ||||
| 		if (padding === 0) { | ||||
| 			return this; | ||||
| 		} | ||||
|  | ||||
| 		this.ensureUnusedCapacity(padding); | ||||
| 		this._typedArray.fill(0, this._length, alignedLength); | ||||
| 		this._length = alignedLength; | ||||
| 		return this; | ||||
| 	} | ||||
|  | ||||
| 	alloc(byteLength: number): DataView { | ||||
| 		this.ensureUnusedCapacity(byteLength); | ||||
| 		const dataView = new DataView(this._buffer, this._length, byteLength); | ||||
|   | ||||
| @@ -26,7 +26,7 @@ export interface PerspectiveCameraProps { | ||||
|  | ||||
| export class OrthographicCamera { | ||||
|  | ||||
| 	readonly type!: "OrthographicCamera"; | ||||
| 	declare readonly type: "OrthographicCamera"; | ||||
|  | ||||
| 	_name: string; | ||||
|  | ||||
| @@ -101,7 +101,7 @@ export class OrthographicCamera { | ||||
|  | ||||
| export class PerspectiveCamera { | ||||
|  | ||||
| 	readonly type!: "PerspectiveCamera"; | ||||
| 	declare readonly type: "PerspectiveCamera"; | ||||
|  | ||||
| 	_name: string; | ||||
|  | ||||
|   | ||||
| @@ -56,7 +56,7 @@ export type ColorTuple = readonly [r: number, g: number, b: number]; | ||||
|  | ||||
| export class Color { | ||||
|  | ||||
| 	readonly type!: "Color"; | ||||
| 	declare readonly type: "Color"; | ||||
|  | ||||
| 	r: number; | ||||
| 	g: number; | ||||
|   | ||||
| @@ -22,7 +22,7 @@ export interface PointLightProps { | ||||
|  | ||||
| export class DirectionalLight { | ||||
|  | ||||
| 	readonly type!: "DirectionalLight"; | ||||
| 	get type(): "DirectionalLight" {}; | ||||
|  | ||||
| 	_name: string; | ||||
|  | ||||
| @@ -81,7 +81,7 @@ export class DirectionalLight { | ||||
|  | ||||
| export class PointLight { | ||||
|  | ||||
| 	readonly type!: "PointLight"; | ||||
| 	declare readonly type: "PointLight"; | ||||
|  | ||||
| 	_name: string; | ||||
|  | ||||
|   | ||||
| @@ -34,7 +34,7 @@ export interface MaterialProps { | ||||
|  | ||||
| export class Material { | ||||
|  | ||||
| 	readonly type!: "Material"; | ||||
| 	declare readonly type: "Material"; | ||||
|  | ||||
| 	_name: string; | ||||
|  | ||||
|   | ||||
| @@ -34,7 +34,7 @@ export type Matrix4x4Tuple = readonly [ | ||||
|  | ||||
| export class Matrix4x4 { | ||||
|  | ||||
| 	readonly type!: "Matrix4x4"; | ||||
| 	declare readonly type: "Matrix4x4"; | ||||
|  | ||||
| 	ix: number; | ||||
| 	iy: number; | ||||
|   | ||||
| @@ -23,7 +23,7 @@ export interface NodeProps { | ||||
|  | ||||
| export class Node { | ||||
|  | ||||
| 	readonly type!: "Node"; | ||||
| 	declare readonly type: "Node"; | ||||
|  | ||||
| 	_name: string; | ||||
|  | ||||
|   | ||||
| @@ -15,7 +15,7 @@ export type QuaternionTuple = readonly [x: number, y: number, z: number, w: numb | ||||
|  | ||||
| export class Quaternion { | ||||
|  | ||||
| 	readonly type!: "Quaternion"; | ||||
| 	declare readonly type: "Quaternion"; | ||||
|  | ||||
| 	x: number; | ||||
| 	y: number; | ||||
|   | ||||
| @@ -16,7 +16,7 @@ export interface SceneProps { | ||||
|  | ||||
| export class Scene { | ||||
|  | ||||
| 	readonly type!: "Scene"; | ||||
| 	declare readonly type: "Scene"; | ||||
|  | ||||
| 	_name: string; | ||||
|  | ||||
|   | ||||
| @@ -13,7 +13,7 @@ export type Vector2Tuple = readonly [x: number, y: number]; | ||||
|  | ||||
| export class Vector2 { | ||||
|  | ||||
| 	readonly type!: "Vector2"; | ||||
| 	declare readonly type: "Vector2"; | ||||
|  | ||||
| 	x: number; | ||||
| 	y: number; | ||||
|   | ||||
| @@ -14,7 +14,7 @@ export type Vector3Tuple = readonly [x: number, y: number, z: number]; | ||||
|  | ||||
| export class Vector3 { | ||||
|  | ||||
| 	readonly type!: "Vector3"; | ||||
| 	declare readonly type: "Vector3"; | ||||
|  | ||||
| 	x: number; | ||||
| 	y: number; | ||||
|   | ||||
| @@ -15,7 +15,7 @@ export type Vector4Tuple = readonly [x: number, y: number, z: number, w: number] | ||||
|  | ||||
| export class Vector4 { | ||||
|  | ||||
| 	readonly type!: "Vector4"; | ||||
| 	declare readonly type: "Vector4"; | ||||
|  | ||||
| 	x: number; | ||||
| 	y: number; | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import { _BinaryWriter as BinaryWriter } from "./_BinaryWriter"; | ||||
| import { _Mapping as Mapping } from "./_Mapping"; | ||||
| import { Camera, Material, Matrix4x4, Node, Scene, Vector3, isDirectionalLight, isPointLight, preOrder } from "./data"; | ||||
| import { IndexBuffer, IndexBufferProps, Texture2D, Texture2DProps, VertexBuffer, VertexBufferProps } from "./resources"; | ||||
| import { ShaderFlagKey, ShaderFlags, createPipeline, shaderFlagsKey } from "./shader"; | ||||
| import { GLOBAL_UNIFORMS_SIZE, MATERIAL_UNIFORMS_SIZE, OBJECT_UNIFORMS_SIZE, ShaderFlagKey, ShaderFlags, _createPipeline, _shaderFlagsKey } from "./shader"; | ||||
|  | ||||
| const _matrixOStoWSNormal = new Matrix4x4( | ||||
| 	NaN, NaN, NaN, NaN, | ||||
| @@ -117,6 +117,7 @@ export class Renderer { | ||||
| 			width: framebufferTexture.width, | ||||
| 			height: framebufferTexture.height, | ||||
| 			format: "depth", | ||||
| 			usage: GPUTextureUsage.RENDER_ATTACHMENT, | ||||
| 		}); | ||||
|  | ||||
| 		this._globalBindGroupLayout = device.createBindGroupLayout({ | ||||
| @@ -266,7 +267,7 @@ export class Renderer { | ||||
| 		this._globalBindGroup = device.createBindGroup({ | ||||
| 			layout: this._globalBindGroupLayout, | ||||
| 			entries: [ | ||||
| 				{ binding: 0, resource: { buffer: this._uniformBuffer } }, | ||||
| 				{ binding: 0, resource: { buffer: this._uniformBuffer, size: GLOBAL_UNIFORMS_SIZE } }, | ||||
| 				{ binding: 1, resource: { buffer: this._pointLightBuffer } }, | ||||
| 				{ binding: 2, resource: { buffer: this._directionalLightBuffer } }, | ||||
| 			], | ||||
| @@ -275,7 +276,7 @@ export class Renderer { | ||||
| 		this._objectBindGroup = device.createBindGroup({ | ||||
| 			layout: this._objectBindGroupLayout, | ||||
| 			entries: [ | ||||
| 				{ binding: 0, resource: { buffer: this._uniformBuffer } }, | ||||
| 				{ binding: 0, resource: { buffer: this._uniformBuffer, size: OBJECT_UNIFORMS_SIZE } }, | ||||
| 			], | ||||
| 			label: "Object", | ||||
| 		}); | ||||
| @@ -336,14 +337,14 @@ export class Renderer { | ||||
| 	} | ||||
|  | ||||
| 	_getOrCreatePipeline(flags: ShaderFlags): GPURenderPipeline { | ||||
| 		const key = shaderFlagsKey(flags); | ||||
| 		const key = _shaderFlagsKey(flags); | ||||
|  | ||||
| 		let pipeline = this._pipelineCache.get(key); | ||||
| 		if (pipeline !== undefined) { | ||||
| 			return pipeline; | ||||
| 		} | ||||
|  | ||||
| 		pipeline = createPipeline(this, flags); | ||||
| 		pipeline = _createPipeline(this, flags); | ||||
| 		this._pipelineCache.set(key, pipeline); | ||||
| 		return pipeline; | ||||
| 	} | ||||
| @@ -401,11 +402,12 @@ export class Renderer { | ||||
| 			this._uniformWriter.writeF32(material._normalScale); | ||||
| 			this._uniformWriter.writeColorF32(material._emissive); | ||||
| 			this._uniformWriter.writeF32(material._ior); | ||||
| 			this._uniformWriter.padToAlign(256); | ||||
|  | ||||
| 			const bindGroup = this._device.createBindGroup({ | ||||
| 				layout: this._materialBindGroupLayout, | ||||
| 				entries: [ | ||||
| 					{ binding: 0, resource: { buffer: this._uniformBuffer } }, | ||||
| 					{ binding: 0, resource: { buffer: this._uniformBuffer, size: MATERIAL_UNIFORMS_SIZE } }, | ||||
| 					{ binding: 1, resource: this._sampler }, | ||||
| 					{ binding: 2, resource: material._baseColorPartialCoverageTexture?._textureView ?? this._textureWhite._textureView }, | ||||
| 					{ binding: 3, resource: material._occlusionTexture?._textureView ?? this._textureWhite._textureView }, | ||||
| @@ -433,6 +435,7 @@ export class Renderer { | ||||
| 			object._updateWorldMatrix(); | ||||
| 			this._uniformWriter.writeMatrix4x4(object._worldMatrix); | ||||
| 			this._uniformWriter.writeMatrix4x4(_matrixOStoWSNormal.setObject(object._worldMatrix).inverseTransposeAffine()); | ||||
| 			this._uniformWriter.padToAlign(256); | ||||
| 			return offset; | ||||
| 		}); | ||||
|  | ||||
| @@ -491,9 +494,7 @@ export class Renderer { | ||||
| 		this._uniformWriter.writeColorF32(scene._ambientLight); | ||||
| 		this._uniformWriter.writeU32(pointLightCount); | ||||
| 		this._uniformWriter.writeU32(directionalLightCount); | ||||
| 		this._uniformWriter.writeU32(0); | ||||
| 		this._uniformWriter.writeU32(0); | ||||
| 		this._uniformWriter.writeU32(0); | ||||
| 		this._uniformWriter.padToAlign(256); | ||||
|  | ||||
| 		// upload uniforms | ||||
|  | ||||
| @@ -520,11 +521,18 @@ export class Renderer { | ||||
|  | ||||
| 			pass.setPipeline(renderPipeline); | ||||
|  | ||||
| 			/* WORKAROUND | ||||
| 			 * | ||||
| 			 * As of writing, Chrome doesn't support passing null as the second | ||||
| 			 * argument. We could (and should) bind the buffers unconditionally | ||||
| 			 * for increased safety. For now, we only do this when they are not | ||||
| 			 * null. | ||||
| 			 */ | ||||
| 			pass.setVertexBuffer(0, vertexBuffer._positionBuffer); | ||||
| 			pass.setVertexBuffer(1, vertexBuffer._texCoordBuffer); | ||||
| 			pass.setVertexBuffer(2, vertexBuffer._lightTexCoordBuffer); | ||||
| 			pass.setVertexBuffer(3, vertexBuffer._normalBuffer); | ||||
| 			pass.setVertexBuffer(4, vertexBuffer._tangentBuffer); | ||||
| 			if (vertexBuffer._texCoordBuffer !== null) pass.setVertexBuffer(1, vertexBuffer._texCoordBuffer); | ||||
| 			if (vertexBuffer._lightTexCoordBuffer !== null) pass.setVertexBuffer(2, vertexBuffer._lightTexCoordBuffer); | ||||
| 			if (vertexBuffer._normalBuffer !== null) pass.setVertexBuffer(3, vertexBuffer._normalBuffer); | ||||
| 			if (vertexBuffer._tangentBuffer !== null) pass.setVertexBuffer(4, vertexBuffer._tangentBuffer); | ||||
| 			pass.setIndexBuffer(indexBuffer._buffer, indexBuffer._indexFormat); | ||||
|  | ||||
| 			pass.setBindGroup(2, this._objectBindGroup, [objectOffset]); | ||||
|   | ||||
| @@ -20,7 +20,7 @@ export interface IndexBufferResizeProps { | ||||
|  | ||||
| export class IndexBuffer { | ||||
|  | ||||
| 	readonly type!: "IndexBuffer"; | ||||
| 	declare readonly type: "IndexBuffer"; | ||||
| 	_renderer: Renderer; | ||||
|  | ||||
| 	_name: string; | ||||
|   | ||||
| @@ -21,12 +21,15 @@ export interface Texture2DProps { | ||||
| 	readonly height: number; | ||||
|  | ||||
| 	readonly format: Texture2DFormat; | ||||
|  | ||||
| 	readonly usage?: GPUTextureUsageFlags; | ||||
| } | ||||
|  | ||||
| export interface Texture2DResizeProps { | ||||
| 	readonly width?: number; | ||||
| 	readonly height?: number; | ||||
| 	readonly format?: Texture2DFormat; | ||||
| 	readonly usage?: GPUTextureUsageFlags; | ||||
| } | ||||
|  | ||||
| export interface Texture2DAdvancedWriteProps { | ||||
| @@ -39,7 +42,7 @@ export interface Texture2DAdvancedWriteProps { | ||||
|  | ||||
| export class Texture2D { | ||||
|  | ||||
| 	readonly type!: "Texture2D"; | ||||
| 	declare readonly type: "Texture2D"; | ||||
| 	_renderer: Renderer; | ||||
|  | ||||
| 	_name: string; | ||||
| @@ -53,6 +56,7 @@ export class Texture2D { | ||||
| 		width, | ||||
| 		height, | ||||
| 		format, | ||||
| 		usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST, | ||||
| 	}: Texture2DProps) { | ||||
| 		this._renderer = renderer; | ||||
|  | ||||
| @@ -62,7 +66,7 @@ export class Texture2D { | ||||
|  | ||||
| 		this._renderer = renderer; | ||||
| 		this._texture = renderer._device.createTexture({ | ||||
| 			usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST, | ||||
| 			usage, | ||||
| 			size: { width, height }, | ||||
| 			format: gpuFormat, | ||||
| 			label: name | ||||
| @@ -147,13 +151,14 @@ export class Texture2D { | ||||
| 		width = this._texture.width, | ||||
| 		height = this._texture.height, | ||||
| 		format = this._format, | ||||
| 		usage = this._texture.usage, | ||||
| 	}: Texture2DResizeProps): Texture2D { | ||||
| 		this._texture.destroy(); | ||||
|  | ||||
| 		const gpuFormat = gpuTextureFormat(format); | ||||
|  | ||||
| 		this._texture = this._renderer._device.createTexture({ | ||||
| 			usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST, | ||||
| 			usage, | ||||
| 			size: { width, height }, | ||||
| 			format: gpuFormat, | ||||
| 			label: this._name | ||||
|   | ||||
| @@ -51,7 +51,7 @@ export interface VertexBufferWriteTypedArrayProps { | ||||
|  | ||||
| export class VertexBuffer { | ||||
|  | ||||
| 	readonly type!: "VertexBuffer"; | ||||
| 	declare readonly type: "VertexBuffer"; | ||||
| 	_renderer: Renderer; | ||||
|  | ||||
| 	_name: string; | ||||
|   | ||||
| @@ -6,6 +6,13 @@ | ||||
|  | ||||
| import { Renderer } from "./oktaeder"; | ||||
|  | ||||
| // 152 bytes padded to 256 | ||||
| export const GLOBAL_UNIFORMS_SIZE = 256; | ||||
| // 64 bytes padded to 256 | ||||
| export const MATERIAL_UNIFORMS_SIZE = 256; | ||||
| // 128 bytes padded to 256 | ||||
| export const OBJECT_UNIFORMS_SIZE = 256; | ||||
|  | ||||
| export type ShaderFlagKey = number; | ||||
|  | ||||
| export interface ShaderFlags { | ||||
| @@ -15,7 +22,7 @@ export interface ShaderFlags { | ||||
| 	readonly tangent: boolean; | ||||
| } | ||||
|  | ||||
| export function shaderFlagsKey({ | ||||
| export function _shaderFlagsKey({ | ||||
| 	texCoord, | ||||
| 	lightTexCoord, | ||||
| 	normal, | ||||
| @@ -29,13 +36,13 @@ export function shaderFlagsKey({ | ||||
| 	return key; | ||||
| } | ||||
|  | ||||
| export function createPipeline(renderer: Renderer, { | ||||
| export function _createPipeline(renderer: Renderer, { | ||||
| 	texCoord, | ||||
| 	lightTexCoord, | ||||
| 	normal, | ||||
| 	tangent, | ||||
| }: ShaderFlags): GPURenderPipeline { | ||||
| 	const shaderCode = createShaderCode({ texCoord, lightTexCoord, normal, tangent }); | ||||
| 	const shaderCode = _createShaderCode({ texCoord, lightTexCoord, normal, tangent }); | ||||
|  | ||||
| 	const shaderModule = renderer._device.createShaderModule({ | ||||
| 		code: shaderCode, | ||||
| @@ -130,7 +137,7 @@ export function createPipeline(renderer: Renderer, { | ||||
| 	return pipeline; | ||||
| } | ||||
|  | ||||
| export function createShaderCode({ | ||||
| export function _createShaderCode({ | ||||
| 	texCoord, | ||||
| 	lightTexCoord, | ||||
| 	normal, | ||||
| @@ -147,7 +154,7 @@ struct Vertex { | ||||
|  | ||||
| struct Varyings { | ||||
| 	@builtin(position) positionCS: vec4<f32>, | ||||
| 	@location(0) positionVS: vec4<f32>, | ||||
| 	@location(0) positionVS: vec3<f32>, | ||||
| 	${texCoord ? `@location(1) texCoord: vec2<f32>,` : ""} | ||||
| 	${lightTexCoord ? `@location(2) lightTexCoord: vec2<f32>,` : ""} | ||||
| 	${normal ? `@location(3) normalVS: vec3<f32>,` : ""} | ||||
| @@ -219,7 +226,7 @@ fn visibilityGGX(dotNL: f32, dotNV: f32, alpha: f32) -> f32 { | ||||
| 	let vGGX = dotNL * sqrt(dotNV * dotNV * (1.0 - alphaSquared) + alphaSquared); | ||||
| 	let lGGX = dotNV * sqrt(dotNL * dotNL * (1.0 - alphaSquared) + alphaSquared); | ||||
| 	let GGX = vGGX + lGGX; | ||||
| 	return GGX > 0.0 ? 0.5 / GGX : 0.0; | ||||
| 	return select(0.0, 0.5 / GGX, GGX > 0.0); | ||||
| } | ||||
|  | ||||
| fn distributionGGX(dotNH: f32, alpha: f32) -> f32 { | ||||
| @@ -270,7 +277,7 @@ fn screenSpaceMatrixTStoVS(positionVS: vec3<f32>, normalVS: vec3<f32>, texCoord: | ||||
| 	let bitangentVS = q1perp * uv0.y + q0perp * uv1.y; | ||||
|  | ||||
| 	let det = max(dot(tangentVS, tangentVS), dot(bitangentVS, bitangentVS)); | ||||
| 	let scale = (det == 0.0) ? 0.0 : inserseSqrt(det); | ||||
| 	let scale = select(0.0, inverseSqrt(det), det != 0.0); | ||||
|  | ||||
| 	return mat3x3(tangentVS * scale, bitangentVS * scale, normalVS); | ||||
| } | ||||
| @@ -297,10 +304,11 @@ fn vert(vertex: Vertex) -> Varyings { | ||||
| 	` : ""} | ||||
| 	${texCoord ? "output.texCoord = vertex.texCoord;" : ""} | ||||
| 	${lightTexCoord ? "output.lightTexCoord = vertex.lightTexCoord;" : ""} | ||||
| 	return output; | ||||
| } | ||||
|  | ||||
| @fragment | ||||
| fn frag(fragment: Varyings) -> @location(0) vec2<f32> { | ||||
| fn frag(fragment: Varyings) -> @location(0) vec4<f32> { | ||||
| 	var baseColor = _Material.baseColor; | ||||
| 	var partialCoverage = _Material.partialCoverage; | ||||
| 	var occlusion = 1.0; | ||||
| @@ -357,7 +365,7 @@ fn frag(fragment: Varyings) -> @location(0) vec2<f32> { | ||||
|  | ||||
| 	var outgoingRadiance = vec3(0.0); | ||||
|  | ||||
| 	for (var i: u32 = 0; i < _Global.pointLightCount; ++i) { | ||||
| 	for (var i: u32 = 0; i < _Global.pointLightCount; i++) { | ||||
| 		let light = _PointLights[i]; | ||||
|  | ||||
| 		let lightPositionVS = (_Global.matrixWStoVS * vec4(light.positionWS, 1.0)).xyz; | ||||
| @@ -373,7 +381,7 @@ fn frag(fragment: Varyings) -> @location(0) vec2<f32> { | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	for (var i: u32 = 0; i < _Global.directionalLightCount; ++i) { | ||||
| 	for (var i: u32 = 0; i < _Global.directionalLightCount; i++) { | ||||
| 		let light = _DirectionalLights[i]; | ||||
|  | ||||
| 		let lightDirectionVS = normalize((_Global.matrixWStoVS * vec4(light.directionWS, 0.0)).xyz); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user