From c9891410d9edbf112902f39d114a7fcb9d9405ee Mon Sep 17 00:00:00 2001 From: Daan Vanden Bosch Date: Sun, 26 Apr 2020 22:19:26 +0200 Subject: [PATCH] Improved WebGPU renderer: - The renderer now uses buffer memory mapping instead of the deprecated setSubData - It can now render models without texture - It can now use S3TC textures --- assets/shaders/pos_norm.frag.spv | Bin 0 -> 1620 bytes assets/shaders/pos_norm.vert.spv | Bin 0 -> 1652 bytes ...gment_shader.frag.spv => pos_tex.frag.spv} | Bin 916 -> 920 bytes assets/shaders/pos_tex.vert.spv | Bin 0 -> 1480 bytes assets/shaders/vertex_shader.vert.spv | Bin 1476 -> 0 bytes .../resources/shaders/pos_norm.frag | 23 + .../resources/shaders/pos_norm.vert | 16 + .../{fragment_shader.frag => pos_tex.frag} | 0 .../{vertex_shader.vert => pos_tex.vert} | 2 +- assets_generation/update_shaders.ts | 7 +- src/core/Severity.ts | 2 +- src/core/model/index.ts | 6 +- src/core/rendering/Gfx.ts | 4 +- src/core/rendering/GfxRenderer.ts | 5 +- src/core/rendering/Mesh.ts | 18 +- src/core/rendering/MeshBuilder.ts | 35 +- src/core/rendering/VertexFormat.ts | 66 +-- .../rendering/conversion/ninja_geometry.ts | 4 +- src/core/rendering/meshes.ts | 4 +- src/core/rendering/webgl/WebglGfx.ts | 23 +- src/core/rendering/webgl/WebglRenderer.ts | 8 +- src/core/rendering/webgpu/WebgpuGfx.ts | 108 +++-- src/core/rendering/webgpu/WebgpuRenderer.ts | 443 +++++++++++------- .../model/QuestEventActionModel.ts | 4 +- src/viewer/index.ts | 12 +- src/viewer/rendering/TextureRenderer.ts | 4 +- test/src/core/rendering/StubGfxRenderer.ts | 2 +- 27 files changed, 490 insertions(+), 306 deletions(-) create mode 100644 assets/shaders/pos_norm.frag.spv create mode 100644 assets/shaders/pos_norm.vert.spv rename assets/shaders/{fragment_shader.frag.spv => pos_tex.frag.spv} (70%) create mode 100644 assets/shaders/pos_tex.vert.spv delete mode 100644 assets/shaders/vertex_shader.vert.spv create mode 100644 assets_generation/resources/shaders/pos_norm.frag create mode 100644 assets_generation/resources/shaders/pos_norm.vert rename assets_generation/resources/shaders/{fragment_shader.frag => pos_tex.frag} (100%) rename assets_generation/resources/shaders/{vertex_shader.vert => pos_tex.vert} (87%) diff --git a/assets/shaders/pos_norm.frag.spv b/assets/shaders/pos_norm.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..3eebdf4bb8503a64e23019651548d9b0c2fcc139 GIT binary patch literal 1620 zcmZ{kTTc@~6vs!}q88+05%0QK6qUA=M2*p4-{1u!i65X@+qG#}cFDFS@l9j=27VtC zpW*X(Nlg6zW_QSFbegmCKesuTIn&Zq{j4!(%sKs9CSQwYN)lsc%(Rd(JG-xUTcdHm z_2_XMi#bzbvo`bcD+^ad2jMV5J}bH@s*4t6i%vmACF>(7v}(`(szAir*a%)7zD)a* zINHn7UNjm-{h${QqhuT$P2&A9*=)C~|Fjy1*&rG>qvXh0w*F(O!?JhH!*r-aNw!*# zhe;G{R>{BoM-A}jl~*qv*_tcTQrFuo9CVX3I|yS*O6H&f&IMJtR}a+@8Jg?UiWar!%Yt_c=(=&?-sbG zsbTt7^kweA%la~3fu*u0(#@OEos^BP*Xpqi#8VS9hx~cL(`RJs;yG(h^z5Ye#qskw z-)ZPx@gq-ckVDR{Cal$Q{1wZ-?<^_r>%zppp`BRp#E0j-AS2%u?c_jx*x%8f*PPqK z*9)18vnu}N_;{-!PyFc@GDqa$bo3S8rze_AV(?a3#Qv_Fr?rzKJ!MA8%kj*d&ofi- z{3B2c{b6m1r)SJAeg3Q{*fCeg=;aGV=X_+&pT4s2$DX;y=2PL%Y}0S#9|{>Wjtm`V zk|Q#ArkgK!CbN&t$HJfar!UCyrzT|Bb6?CnclAxWKs)QFbjWXu9*XE2*u^Yn;4>?fs<8W=hm2|AK$X zU*(I5>pPuOYodqjp0n0o`|QgZW~S>EW2VfU{#s_VR?M^*V-`$VCu7=2$4AX9KW#pE zxQ}AV%&=L_vbkW-CH?mOFa|8iuFG!88nTM?+{cBH&sr@7>^SaypEHPMVwq zSr(jnCs7#0xpzK{KKb#%{{H+wRk@$`f_yKC&z-XQAEho!zw8Z?P?u8Vd_4-|z&n^X zretPie`+tO=SAUO)a?Z6#~{sv&w^q{JZF4K7Up3Ri>+8ay;xp3j>E43I_G1C^1X_} z!Rs)~{rDsp`vxYu~Ma=1>eXVJ(cdF-JZ)cBi9XIC^s&XSi8e zTORI1cPNJbSiA#zhwGO8o72Z?E?M0uj=Ym`VB%iL++A=kj@;Z&?zVC>n;jX;;o1mV z%$%wdm|0EYMuEw(s$1!^reFHxyslqzd$J9&bK1f9*YwMOk0)a_3aro4&T~tcnW2ZP z>W!J3y8|EQNq12@`S3p}Fy9ayd6u<%vMCvPz|4`gDZHYcd#Q^$FKZ_+@%UG@!-1>9 z%#av(@~>%UrtstkGg~jR$bCa*%(v(0>pGE}8o@UU?Bc<^ z4I02FVl27Xb~f@{jC`;81gxp^1( JU+VI)>^D}bhA;pC literal 0 HcmV?d00001 diff --git a/assets/shaders/fragment_shader.frag.spv b/assets/shaders/pos_tex.frag.spv similarity index 70% rename from assets/shaders/fragment_shader.frag.spv rename to assets/shaders/pos_tex.frag.spv index f3a1a1b128d821cd9ee39afb23ff9cf0b1b50999..d63fd7e51524c14f6a5bcf48a579d93ea8b7e07d 100644 GIT binary patch delta 164 zcmbQjK7*Z&nMs+Qfng$BLA?qC69WUoAw~uUAY#zhSMV?J%}*)KNew8B?@JwIoXMM zh6V;e6$~KplEk9))Dqp)yfTI2g3O{aLp@`lI6Ja9RF9q^Ob^hYqDtL@{7j&Mxrv#1 iF!9pjRNdl|{G!D4RNbV~w6xSB1_pNq=FQ2Brc3}qLMb-@ diff --git a/assets/shaders/pos_tex.vert.spv b/assets/shaders/pos_tex.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..745379058ec73877e8674bc02b2252d4f3445817 GIT binary patch literal 1480 zcmZ{kUuzRV6vd}aH;JjX*4jU6jT=+7Dos;R5ky54(uazXqOV~|wqbFz8+JEAeex^# zmHbq`2%g_&Cq~c-cki5Y&)k`NXGm$eSutkGRP}pcW@E!Fi!o-^lr=M^b9#2#O0z-h z(c?CXHB-Xnnssv_pF8@FydVOs$*#-p$_``|>B+|^nf*NH4-s%jNAA1v+juYu{caNX z{WSFlZa)nCD08Qi@RJuEx7)RUsxmJb`q`l$O|7!^AElBdUk=A{prjnK)(nHlcaLkv zIA%rm=j@JZz9>8ldtE>I=qH)~Sy1eZXHG0mgDi+6v6Wm;Ejh0}&VsK#Ivb;hioFhl z@tYvcyr}QbV|0?(Bn;;hxU}tj^q=qfR)y)lnZDwOEch+={HD2>H;R zh@l<^eMfI`GiU$i^f=9B89C79?@>9Z^|p-BDR5&3b4=a!1Ozr65>gql9+Z@C&SGr5`>__}rfw_A) z_F0#AWlJ*l0n-o0uJDFDc{x{ItLPCNxi{sh502bm`UXdQtC*vw#$9>N%-+PW$+Is! z>%sIDjL<$(M!fFsi6&$s7 zwUU}$8SBv#r#Ih~msmr#BXeb(k6txp+%@NWq3XcsY(4frfV0w*`!f20mcD_1=~Pc; FzX1VLf3pAp literal 0 HcmV?d00001 diff --git a/assets/shaders/vertex_shader.vert.spv b/assets/shaders/vertex_shader.vert.spv deleted file mode 100644 index f9aea7ef332bce8159c8b0244b9cc425ee713831..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1476 zcmY+E+iuf95QaBR?F0%frSt%l;7|?)l&BF0At68rq*SOEMHSpeCUqhU$Bt|#0LvmkoC{Y5-)mFPz8}qjkki(I^eAZFUOTx#|;50}- z2WcLB?>f?+8 zrQbW#M5FPlI|83&K_|--RTOl-%toUiHRf3LFn`usp%z&2CW?Ejc+3_abF47-V5^e8 zJj8={EQDFIxO4cGePGf5v4*fK5@Nu!Z&5Lr^BoDRU)t@$v^FzuE!n)oWt%sNO+Qt2)?aAJhCN6cgbcz{)5qnFTd4Uldo4JAE-!Av?>2Xh*n#qm- zx-@ygIgia;!Kel9zQmZHZ{TwuoTVP(-Iaz@<-4anesJPpb1zlC2ig<6@?#&C*mJ!T zc)S@F{r*v8SGp;SbIg+URxEnkkTLXMtnW*G&mMebgChy;WjT3ygOHj~o^6 zc8Ss3sZP?fC*eGN{LJQ;;^J#db|s#K`k2*$guAA`*Xj-%Ue!(Severity); +export const Severities: readonly Severity[] = enum_values(Severity); export function severity_from_string(str: string): Severity { const severity = (Severity as any)[str.slice(0, 1).toUpperCase() + str.slice(1).toLowerCase()]; diff --git a/src/core/model/index.ts b/src/core/model/index.ts index 804a058c..1bf4f22f 100644 --- a/src/core/model/index.ts +++ b/src/core/model/index.ts @@ -7,7 +7,7 @@ export enum Server { Ephinea = "Ephinea", } -export const Servers: Server[] = enum_values(Server); +export const Servers: readonly Server[] = enum_values(Server); export enum SectionId { Viridia, @@ -22,7 +22,7 @@ export enum SectionId { Whitill, } -export const SectionIds: SectionId[] = enum_values(SectionId); +export const SectionIds: readonly SectionId[] = enum_values(SectionId); export enum Difficulty { Normal, @@ -31,4 +31,4 @@ export enum Difficulty { Ultimate, } -export const Difficulties: Difficulty[] = enum_values(Difficulty); +export const Difficulties: readonly Difficulty[] = enum_values(Difficulty); diff --git a/src/core/rendering/Gfx.ts b/src/core/rendering/Gfx.ts index 0944d8fa..6d869f9a 100644 --- a/src/core/rendering/Gfx.ts +++ b/src/core/rendering/Gfx.ts @@ -1,9 +1,9 @@ import { Texture, TextureFormat } from "./Texture"; -import { VertexFormat } from "./VertexFormat"; +import { VertexFormatType } from "./VertexFormat"; export interface Gfx { create_gfx_mesh( - format: VertexFormat, + format: VertexFormatType, vertex_data: ArrayBuffer, index_data: ArrayBuffer, texture?: Texture, diff --git a/src/core/rendering/GfxRenderer.ts b/src/core/rendering/GfxRenderer.ts index 3cb7a80c..4deab5f0 100644 --- a/src/core/rendering/GfxRenderer.ts +++ b/src/core/rendering/GfxRenderer.ts @@ -17,9 +17,10 @@ export abstract class GfxRenderer implements Renderer { abstract readonly gfx: Gfx; readonly scene = new Scene(); readonly camera: Camera; - readonly canvas_element: HTMLCanvasElement = document.createElement("canvas"); + readonly canvas_element: HTMLCanvasElement; - protected constructor(projection: Projection) { + protected constructor(canvas_element: HTMLCanvasElement, projection: Projection) { + this.canvas_element = canvas_element; this.canvas_element.width = this.width; this.canvas_element.height = this.height; this.canvas_element.addEventListener("mousedown", this.mousedown); diff --git a/src/core/rendering/Mesh.ts b/src/core/rendering/Mesh.ts index 22b56ec0..27abd707 100644 --- a/src/core/rendering/Mesh.ts +++ b/src/core/rendering/Mesh.ts @@ -1,4 +1,4 @@ -import { VertexFormat } from "./VertexFormat"; +import { VertexFormatType } from "./VertexFormat"; import { Texture } from "./Texture"; import { Gfx } from "./Gfx"; import { @@ -10,16 +10,16 @@ import { export class Mesh { /* eslint-disable no-dupe-class-members */ - static builder(format: VertexFormat.PosNorm): PosNormMeshBuilder; - static builder(format: VertexFormat.PosTex): PosTexMeshBuilder; - static builder(format: VertexFormat.PosNormTex): PosNormTexMeshBuilder; - static builder(format: VertexFormat): MeshBuilder { + static builder(format: VertexFormatType.PosNorm): PosNormMeshBuilder; + static builder(format: VertexFormatType.PosTex): PosTexMeshBuilder; + static builder(format: VertexFormatType.PosNormTex): PosNormTexMeshBuilder; + static builder(format: VertexFormatType): MeshBuilder { switch (format) { - case VertexFormat.PosNorm: + case VertexFormatType.PosNorm: return new PosNormMeshBuilder(); - case VertexFormat.PosTex: + case VertexFormatType.PosTex: return new PosTexMeshBuilder(); - case VertexFormat.PosNormTex: + case VertexFormatType.PosNormTex: return new PosNormTexMeshBuilder(); } } @@ -28,7 +28,7 @@ export class Mesh { gfx_mesh: unknown; constructor( - readonly format: VertexFormat, + readonly format: VertexFormatType, readonly vertex_data: ArrayBuffer, readonly index_data: ArrayBuffer, readonly index_count: number, diff --git a/src/core/rendering/MeshBuilder.ts b/src/core/rendering/MeshBuilder.ts index 0b167b1f..4a1b0e9c 100644 --- a/src/core/rendering/MeshBuilder.ts +++ b/src/core/rendering/MeshBuilder.ts @@ -1,14 +1,11 @@ import { Texture } from "./Texture"; -import { - vertex_format_normal_offset, - vertex_format_size, - vertex_format_tex_offset, - VertexFormat, -} from "./VertexFormat"; +import { VERTEX_FORMATS, VertexFormat, VertexFormatType } from "./VertexFormat"; import { Mesh } from "./Mesh"; import { Vec2, Vec3 } from "../math/linear_algebra"; export abstract class MeshBuilder { + private readonly format: VertexFormat; + protected readonly vertex_data: { pos: Vec3; normal?: Vec3; @@ -21,7 +18,9 @@ export abstract class MeshBuilder { return this.vertex_data.length; } - protected constructor(private readonly format: VertexFormat) {} + protected constructor(format_type: VertexFormatType) { + this.format = VERTEX_FORMATS[format_type]; + } triangle(v1: number, v2: number, v3: number): this { this.index_data.push(v1, v2, v3); @@ -29,9 +28,9 @@ export abstract class MeshBuilder { } build(): Mesh { - const v_size = vertex_format_size(this.format); - const v_normal_offset = vertex_format_normal_offset(this.format); - const v_tex_offset = vertex_format_tex_offset(this.format); + const v_size = this.format.size; + const v_normal_offset = this.format.normal_offset; + const v_tex_offset = this.format.tex_offset; const v_data = new ArrayBuffer(this.vertex_data.length * v_size); const v_view = new DataView(v_data); let i = 0; @@ -41,13 +40,13 @@ export abstract class MeshBuilder { v_view.setFloat32(i + 4, pos.y, true); v_view.setFloat32(i + 8, pos.z, true); - if (v_normal_offset !== -1) { + if (v_normal_offset != undefined) { v_view.setFloat32(i + v_normal_offset, normal!.x, true); v_view.setFloat32(i + v_normal_offset + 4, normal!.y, true); v_view.setFloat32(i + v_normal_offset + 8, normal!.z, true); } - if (v_tex_offset !== -1) { + if (v_tex_offset != undefined) { v_view.setUint16(i + v_tex_offset, tex!.x * 0xffff, true); v_view.setUint16(i + v_tex_offset + 2, tex!.y * 0xffff, true); } @@ -56,16 +55,16 @@ export abstract class MeshBuilder { } // Make index data divisible by 4 for WebGPU. - const i_data = new Uint16Array(2 * Math.ceil(this.index_data.length / 2)); - i_data.set(this.index_data); + const i_data = new ArrayBuffer(4 * Math.ceil(this.index_data.length / 2)); + new Uint16Array(i_data).set(this.index_data); - return new Mesh(this.format, v_data, i_data, this.index_data.length, this._texture); + return new Mesh(this.format.type, v_data, i_data, this.index_data.length, this._texture); } } export class PosNormMeshBuilder extends MeshBuilder { constructor() { - super(VertexFormat.PosNorm); + super(VertexFormatType.PosNorm); } vertex(pos: Vec3, normal: Vec3): this { @@ -76,7 +75,7 @@ export class PosNormMeshBuilder extends MeshBuilder { export class PosTexMeshBuilder extends MeshBuilder { constructor() { - super(VertexFormat.PosTex); + super(VertexFormatType.PosTex); } vertex(pos: Vec3, tex: Vec2): this { @@ -92,7 +91,7 @@ export class PosTexMeshBuilder extends MeshBuilder { export class PosNormTexMeshBuilder extends MeshBuilder { constructor() { - super(VertexFormat.PosNormTex); + super(VertexFormatType.PosNormTex); } vertex(pos: Vec3, normal: Vec3, tex: Vec2): this { diff --git a/src/core/rendering/VertexFormat.ts b/src/core/rendering/VertexFormat.ts index 6f034cdb..3f157dd8 100644 --- a/src/core/rendering/VertexFormat.ts +++ b/src/core/rendering/VertexFormat.ts @@ -1,41 +1,41 @@ -export enum VertexFormat { +export enum VertexFormatType { PosNorm, PosTex, PosNormTex, } +export type VertexFormat = { + readonly type: VertexFormatType; + readonly size: number; + readonly normal_offset?: number; + readonly tex_offset?: number; + readonly uniform_buffer_size: number; +}; + +export const VERTEX_FORMATS: readonly VertexFormat[] = [ + { + type: VertexFormatType.PosNorm, + size: 24, + normal_offset: 12, + tex_offset: undefined, + uniform_buffer_size: 4 * (16 + 9), + }, + { + type: VertexFormatType.PosTex, + size: 16, + normal_offset: undefined, + tex_offset: 12, + uniform_buffer_size: 4 * 16, + }, + // TODO: add VertexFormat for PosNormTex. + // { + // type: VertexFormatType.PosNormTex, + // size: 28, + // normal_offset: 12, + // tex_offset: 24, + // }, +]; + export const VERTEX_POS_LOC = 0; export const VERTEX_NORMAL_LOC = 1; export const VERTEX_TEX_LOC = 2; - -export function vertex_format_size(format: VertexFormat): number { - switch (format) { - case VertexFormat.PosNorm: - return 24; - case VertexFormat.PosTex: - return 16; - case VertexFormat.PosNormTex: - return 28; - } -} - -export function vertex_format_normal_offset(format: VertexFormat): number { - switch (format) { - case VertexFormat.PosTex: - return -1; - case VertexFormat.PosNorm: - case VertexFormat.PosNormTex: - return 12; - } -} - -export function vertex_format_tex_offset(format: VertexFormat): number { - switch (format) { - case VertexFormat.PosNorm: - return -1; - case VertexFormat.PosTex: - return 12; - case VertexFormat.PosNormTex: - return 24; - } -} diff --git a/src/core/rendering/conversion/ninja_geometry.ts b/src/core/rendering/conversion/ninja_geometry.ts index 4dd72048..107bfc6b 100644 --- a/src/core/rendering/conversion/ninja_geometry.ts +++ b/src/core/rendering/conversion/ninja_geometry.ts @@ -3,7 +3,7 @@ import { NjcmModel } from "../../data_formats/parsing/ninja/njcm"; import { XjModel } from "../../data_formats/parsing/ninja/xj"; import { vec3_to_math } from "./index"; import { Mesh } from "../Mesh"; -import { VertexFormat } from "../VertexFormat"; +import { VertexFormatType } from "../VertexFormat"; import { EulerOrder, Quat } from "../../math/quaternions"; import { mat3_vec3_multiply_into, @@ -54,7 +54,7 @@ class VerticesHolder { class MeshCreator { private readonly vertices = new VerticesHolder(); - private readonly builder = Mesh.builder(VertexFormat.PosNorm); + private readonly builder = Mesh.builder(VertexFormatType.PosNorm); to_mesh(object: NjObject): Mesh { this.object_to_mesh(object, Mat4.identity()); diff --git a/src/core/rendering/meshes.ts b/src/core/rendering/meshes.ts index c8d20ac5..9c9a73fa 100644 --- a/src/core/rendering/meshes.ts +++ b/src/core/rendering/meshes.ts @@ -1,10 +1,10 @@ import { Mesh } from "./Mesh"; -import { VertexFormat } from "./VertexFormat"; +import { VertexFormatType } from "./VertexFormat"; import { Vec3 } from "../math/linear_algebra"; export function cube_mesh(): Mesh { return ( - Mesh.builder(VertexFormat.PosNorm) + Mesh.builder(VertexFormatType.PosNorm) // Front .vertex(new Vec3(1, 1, -1), new Vec3(0, 0, -1)) .vertex(new Vec3(-1, 1, -1), new Vec3(0, 0, -1)) diff --git a/src/core/rendering/webgl/WebglGfx.ts b/src/core/rendering/webgl/WebglGfx.ts index 3f042ef3..1a2d92db 100644 --- a/src/core/rendering/webgl/WebglGfx.ts +++ b/src/core/rendering/webgl/WebglGfx.ts @@ -1,13 +1,11 @@ import { Gfx } from "../Gfx"; import { Texture, TextureFormat } from "../Texture"; import { - vertex_format_normal_offset, - vertex_format_size, - vertex_format_tex_offset, + VERTEX_FORMATS, VERTEX_NORMAL_LOC, VERTEX_POS_LOC, VERTEX_TEX_LOC, - VertexFormat, + VertexFormatType, } from "../VertexFormat"; export type WebglMesh = { @@ -20,7 +18,7 @@ export class WebglGfx implements Gfx { constructor(private readonly gl: WebGL2RenderingContext) {} create_gfx_mesh( - format: VertexFormat, + format_type: VertexFormatType, vertex_data: ArrayBuffer, index_data: ArrayBuffer, texture?: Texture, @@ -46,35 +44,32 @@ export class WebglGfx implements Gfx { gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); gl.bufferData(gl.ARRAY_BUFFER, vertex_data, gl.STATIC_DRAW); - const vertex_size = vertex_format_size(format); + const format = VERTEX_FORMATS[format_type]; + const vertex_size = format.size; gl.vertexAttribPointer(VERTEX_POS_LOC, 3, gl.FLOAT, true, vertex_size, 0); gl.enableVertexAttribArray(VERTEX_POS_LOC); - const normal_offset = vertex_format_normal_offset(format); - - if (normal_offset !== -1) { + if (format.normal_offset != undefined) { gl.vertexAttribPointer( VERTEX_NORMAL_LOC, 3, gl.FLOAT, true, vertex_size, - normal_offset, + format.normal_offset, ); gl.enableVertexAttribArray(VERTEX_NORMAL_LOC); } - const tex_offset = vertex_format_tex_offset(format); - - if (tex_offset !== -1) { + if (format.tex_offset != undefined) { gl.vertexAttribPointer( VERTEX_TEX_LOC, 2, gl.UNSIGNED_SHORT, true, vertex_size, - tex_offset, + format.tex_offset, ); gl.enableVertexAttribArray(VERTEX_TEX_LOC); } diff --git a/src/core/rendering/webgl/WebglRenderer.ts b/src/core/rendering/webgl/WebglRenderer.ts index 09b2368c..e08836c2 100644 --- a/src/core/rendering/webgl/WebglRenderer.ts +++ b/src/core/rendering/webgl/WebglRenderer.ts @@ -7,7 +7,7 @@ import pos_tex_frag_shader_source from "./pos_tex.frag"; import { GfxRenderer } from "../GfxRenderer"; import { WebglGfx, WebglMesh } from "./WebglGfx"; import { Projection } from "../Camera"; -import { VertexFormat } from "../VertexFormat"; +import { VertexFormat, VertexFormatType } from "../VertexFormat"; import { SceneNode } from "../Scene"; export class WebglRenderer extends GfxRenderer { @@ -17,7 +17,7 @@ export class WebglRenderer extends GfxRenderer { readonly gfx: WebglGfx; constructor(projection: Projection) { - super(projection); + super(document.createElement("canvas"), projection); const gl = this.canvas_element.getContext("webgl2"); @@ -32,12 +32,12 @@ export class WebglRenderer extends GfxRenderer { gl.clearColor(0.1, 0.1, 0.1, 1); this.shader_programs = []; - this.shader_programs[VertexFormat.PosNorm] = new ShaderProgram( + this.shader_programs[VertexFormatType.PosNorm] = new ShaderProgram( gl, pos_norm_vert_shader_source, pos_norm_frag_shader_source, ); - this.shader_programs[VertexFormat.PosTex] = new ShaderProgram( + this.shader_programs[VertexFormatType.PosTex] = new ShaderProgram( gl, pos_tex_vert_shader_source, pos_tex_frag_shader_source, diff --git a/src/core/rendering/webgpu/WebgpuGfx.ts b/src/core/rendering/webgpu/WebgpuGfx.ts index 3cb12170..2a172cc3 100644 --- a/src/core/rendering/webgpu/WebgpuGfx.ts +++ b/src/core/rendering/webgpu/WebgpuGfx.ts @@ -1,6 +1,7 @@ import { Gfx } from "../Gfx"; import { Texture, TextureFormat } from "../Texture"; -import { VertexFormat } from "../VertexFormat"; +import { VERTEX_FORMATS, VertexFormatType } from "../VertexFormat"; +import { assert } from "../../util"; export type WebgpuMesh = { readonly uniform_buffer: GPUBuffer; @@ -12,29 +13,38 @@ export type WebgpuMesh = { export class WebgpuGfx implements Gfx { constructor( private readonly device: GPUDevice, - private readonly bind_group_layout: GPUBindGroupLayout, + private readonly bind_group_layouts: readonly GPUBindGroupLayout[], ) {} create_gfx_mesh( - format: VertexFormat, + format_type: VertexFormatType, vertex_data: ArrayBuffer, index_data: ArrayBuffer, texture?: Texture, ): WebgpuMesh { + const format = VERTEX_FORMATS[format_type]; + const uniform_buffer = this.device.createBuffer({ - size: 4 * 16, + size: format.uniform_buffer_size, usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.UNIFORM, // eslint-disable-line no-undef }); - const bind_group = this.device.createBindGroup({ - layout: this.bind_group_layout, - entries: [ - { - binding: 0, - resource: { - buffer: uniform_buffer, - }, + const bind_group_entries: GPUBindGroupEntry[] = [ + { + binding: 0, + resource: { + buffer: uniform_buffer, }, + }, + ]; + + if (format.tex_offset != undefined) { + assert( + texture, + () => `Vertex format ${VertexFormatType[format_type]} requires a texture.`, + ); + + bind_group_entries.push( { binding: 1, resource: this.device.createSampler({ @@ -44,24 +54,29 @@ export class WebgpuGfx implements Gfx { }, { binding: 2, - resource: (texture!.gfx_texture as GPUTexture).createView(), + resource: (texture.gfx_texture as GPUTexture).createView(), }, - ], + ); + } + + const bind_group = this.device.createBindGroup({ + layout: this.bind_group_layouts[format_type], + entries: bind_group_entries, }); - const vertex_buffer = this.device.createBuffer({ + const [vertex_buffer, vertex_array_buffer] = this.device.createBufferMapped({ size: vertex_data.byteLength, - usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX, // eslint-disable-line no-undef + usage: GPUBufferUsage.VERTEX, // eslint-disable-line no-undef }); + new Uint8Array(vertex_array_buffer).set(new Uint8Array(vertex_data)); + vertex_buffer.unmap(); - vertex_buffer.setSubData(0, new Uint8Array(vertex_data)); - - const index_buffer = this.device.createBuffer({ + const [index_buffer, index_array_buffer] = this.device.createBufferMapped({ size: index_data.byteLength, - usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.INDEX, // eslint-disable-line no-undef + usage: GPUBufferUsage.INDEX, // eslint-disable-line no-undef }); - - index_buffer.setSubData(0, new Uint16Array(index_data)); + new Uint8Array(index_array_buffer).set(new Uint8Array(index_data)); + index_buffer.unmap(); return { uniform_buffer, @@ -85,20 +100,19 @@ export class WebgpuGfx implements Gfx { height: number, data: ArrayBuffer, ): GPUTexture { - if (format === TextureFormat.RGBA_S3TC_DXT1 || format === TextureFormat.RGBA_S3TC_DXT3) { - // Chrome's WebGPU implementation doesn't support compressed textures yet. Use a dummy - // texture instead. - const ab = new ArrayBuffer(16); - const ba = new Uint32Array(ab); + let texture_format: string; + let bytes_per_pixel: number; - ba[0] = 0xffff0000; - ba[1] = 0xff00ff00; - ba[2] = 0xff0000ff; - ba[3] = 0xff00ffff; + switch (format) { + case TextureFormat.RGBA_S3TC_DXT1: + texture_format = "bc1-rgba-unorm"; + bytes_per_pixel = 2; + break; - width = 2; - height = 2; - data = ab; + case TextureFormat.RGBA_S3TC_DXT3: + texture_format = "bc2-rgba-unorm"; + bytes_per_pixel = 4; + break; } const texture = this.device.createTexture({ @@ -107,18 +121,14 @@ export class WebgpuGfx implements Gfx { height, depth: 1, }, - format: "rgba8unorm", + format: (texture_format as any) as GPUTextureFormat, usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.SAMPLED, // eslint-disable-line no-undef }); + // Bytes per row must be a multiple of 256. const bytes_per_row = Math.ceil((4 * width) / 256) * 256; const data_size = bytes_per_row * height; - const buffer = this.device.createBuffer({ - size: data_size, - usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC, // eslint-disable-line no-undef - }); - let buffer_data: Uint8Array; if (data_size === data.byteLength) { @@ -130,19 +140,23 @@ export class WebgpuGfx implements Gfx { for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { - const idx = 4 * x + bytes_per_row * y; + const idx = bytes_per_pixel * x + bytes_per_row * y; - buffer_data[idx] = orig_data[orig_idx]; - buffer_data[idx + 1] = orig_data[orig_idx + 1]; - buffer_data[idx + 2] = orig_data[orig_idx + 2]; - buffer_data[idx + 3] = orig_data[orig_idx + 3]; + for (let i = 0; i < bytes_per_pixel; i++) { + buffer_data[idx + i] = orig_data[orig_idx + i]; + } - orig_idx += 4; + orig_idx += bytes_per_pixel; } } } - buffer.setSubData(0, buffer_data); + const [buffer, array_buffer] = this.device.createBufferMapped({ + size: data_size, + usage: GPUBufferUsage.COPY_SRC, // eslint-disable-line no-undef + }); + new Uint8Array(array_buffer).set(buffer_data); + buffer.unmap(); const command_encoder = this.device.createCommandEncoder(); command_encoder.copyBufferToTexture( diff --git a/src/core/rendering/webgpu/WebgpuRenderer.ts b/src/core/rendering/webgpu/WebgpuRenderer.ts index 77058dcc..c67c69c4 100644 --- a/src/core/rendering/webgpu/WebgpuRenderer.ts +++ b/src/core/rendering/webgpu/WebgpuRenderer.ts @@ -1,199 +1,326 @@ -import { LogManager } from "../../Logger"; -import { vertex_format_size, VertexFormat } from "../VertexFormat"; +import { + VERTEX_FORMATS, + VERTEX_NORMAL_LOC, + VERTEX_POS_LOC, + VERTEX_TEX_LOC, + VertexFormat, + VertexFormatType, +} from "../VertexFormat"; import { GfxRenderer } from "../GfxRenderer"; -import { mat4_multiply } from "../../math/linear_algebra"; +import { Mat4, mat4_multiply } from "../../math/linear_algebra"; import { WebgpuGfx, WebgpuMesh } from "./WebgpuGfx"; import { ShaderLoader } from "./ShaderLoader"; import { HttpClient } from "../../HttpClient"; import { Projection } from "../Camera"; +import { Mesh } from "../Mesh"; -const logger = LogManager.get("core/rendering/webgpu/WebgpuRenderer"); +type PipelineDetails = { + readonly pipeline: GPURenderPipeline; + readonly bind_group_layout: GPUBindGroupLayout; +}; + +export async function create_webgpu_renderer( + projection: Projection, + http_client: HttpClient, +): Promise { + if (window.navigator.gpu == undefined) { + throw new Error("WebGPU not supported on this device."); + } + + const canvas_element = document.createElement("canvas"); + const context = canvas_element.getContext("gpupresent") as GPUCanvasContext | null; + + if (context == null) { + throw new Error("Failed to initialize gpupresent context."); + } + + const adapter = await window.navigator.gpu.requestAdapter(); + const device = await adapter.requestDevice({ + extensions: ["textureCompressionBC"] as any as GPUExtensionName[], + }); + const shader_loader = new ShaderLoader(http_client); + + const texture_format = "bgra8unorm"; + + const swap_chain = context.configureSwapChain({ + device, + format: texture_format, + }); + + const pipelines: PipelineDetails[] = await Promise.all( + VERTEX_FORMATS.map(format => + create_pipeline(format, device, texture_format, shader_loader), + ), + ); + + return new WebgpuRenderer(canvas_element, projection, device, swap_chain, pipelines); +} + +async function create_pipeline( + format: VertexFormat, + device: GPUDevice, + texture_format: GPUTextureFormat, + shader_loader: ShaderLoader, +): Promise { + const bind_group_layout_entries: GPUBindGroupLayoutEntry[] = [ + { + binding: 0, + visibility: GPUShaderStage.VERTEX, // eslint-disable-line no-undef + type: "uniform-buffer", + }, + ]; + + if (format.tex_offset != undefined) { + bind_group_layout_entries.push( + { + binding: 1, + visibility: GPUShaderStage.FRAGMENT, // eslint-disable-line no-undef + type: "sampler", + }, + { + binding: 2, + visibility: GPUShaderStage.FRAGMENT, // eslint-disable-line no-undef + type: "sampled-texture", + }, + ); + } + + const bind_group_layout = device.createBindGroupLayout({ + entries: bind_group_layout_entries, + }); + + let shader_name: string; + + switch (format.type) { + case VertexFormatType.PosNorm: + shader_name = "pos_norm"; + break; + case VertexFormatType.PosTex: + shader_name = "pos_tex"; + break; + case VertexFormatType.PosNormTex: + shader_name = "pos_norm_tex"; + break; + } + + const vertex_shader_source = await shader_loader.load(`${shader_name}.vert`); + const fragment_shader_source = await shader_loader.load(`${shader_name}.frag`); + + const vertex_attributes: GPUVertexAttributeDescriptor[] = [ + { + format: "float3", + offset: 0, + shaderLocation: VERTEX_POS_LOC, + }, + ]; + + if (format.normal_offset != undefined) { + vertex_attributes.push({ + format: "float3", + offset: format.normal_offset, + shaderLocation: VERTEX_NORMAL_LOC, + }); + } + + if (format.tex_offset != undefined) { + vertex_attributes.push({ + format: "ushort2norm", + offset: format.tex_offset, + shaderLocation: VERTEX_TEX_LOC, + }); + } + + const pipeline = device.createRenderPipeline({ + layout: device.createPipelineLayout({ bindGroupLayouts: [bind_group_layout] }), + vertexStage: { + module: device.createShaderModule({ + code: vertex_shader_source, + }), + entryPoint: "main", + }, + fragmentStage: { + module: device.createShaderModule({ + code: fragment_shader_source, + }), + entryPoint: "main", + }, + primitiveTopology: "triangle-list", + colorStates: [{ format: texture_format }], + depthStencilState: { + format: "depth24plus", + depthWriteEnabled: true, + depthCompare: "less", + }, + vertexState: { + indexFormat: "uint16", + vertexBuffers: [ + { + arrayStride: format.size, + stepMode: "vertex", + attributes: vertex_attributes, + }, + ], + }, + }); + + return { pipeline, bind_group_layout }; +} /** * Uses the experimental WebGPU API for rendering. */ export class WebgpuRenderer extends GfxRenderer { private disposed: boolean = false; - /** - * Is defined when the renderer is fully initialized. - */ - private gpu?: { - gfx: WebgpuGfx; - device: GPUDevice; - swap_chain: GPUSwapChain; - pipeline: GPURenderPipeline; - }; - private shader_loader: ShaderLoader; + private depth_texture!: GPUTexture; - get gfx(): WebgpuGfx { - return this.gpu!.gfx; - } + readonly gfx: WebgpuGfx; - constructor(projection: Projection, http_client: HttpClient) { - super(projection); + constructor( + canvas_element: HTMLCanvasElement, + projection: Projection, + private readonly device: GPUDevice, + private readonly swap_chain: GPUSwapChain, + private readonly pipelines: readonly { + pipeline: GPURenderPipeline; + bind_group_layout: GPUBindGroupLayout; + }[], + ) { + super(canvas_element, projection); - this.shader_loader = new ShaderLoader(http_client); + this.gfx = new WebgpuGfx( + device, + pipelines.map(p => p.bind_group_layout), + ); - this.initialize(); - } - - private async initialize(): Promise { - try { - if (window.navigator.gpu == undefined) { - logger.error("WebGPU not supported on this device."); - return; - } - - const context = this.canvas_element.getContext("gpupresent") as GPUCanvasContext | null; - - if (context == null) { - logger.error("Failed to initialize gpupresent context."); - return; - } - - const adapter = await window.navigator.gpu.requestAdapter(); - const device = await adapter.requestDevice(); - const vertex_shader_source = await this.shader_loader.load("vertex_shader.vert"); - const fragment_shader_source = await this.shader_loader.load("fragment_shader.frag"); - - if (!this.disposed) { - const swap_chain_format = "bgra8unorm"; - - const swap_chain = context.configureSwapChain({ - device: device, - format: swap_chain_format, - }); - - const bind_group_layout = device.createBindGroupLayout({ - entries: [ - { - binding: 0, - visibility: GPUShaderStage.VERTEX, // eslint-disable-line no-undef - type: "uniform-buffer", - }, - { - binding: 1, - visibility: GPUShaderStage.FRAGMENT, // eslint-disable-line no-undef - type: "sampler", - }, - { - binding: 2, - visibility: GPUShaderStage.FRAGMENT, // eslint-disable-line no-undef - type: "sampled-texture", - }, - ], - }); - - const pipeline = device.createRenderPipeline({ - layout: device.createPipelineLayout({ bindGroupLayouts: [bind_group_layout] }), - vertexStage: { - module: device.createShaderModule({ - code: vertex_shader_source, - }), - entryPoint: "main", - }, - fragmentStage: { - module: device.createShaderModule({ - code: fragment_shader_source, - }), - entryPoint: "main", - }, - primitiveTopology: "triangle-list", - colorStates: [{ format: swap_chain_format }], - vertexState: { - indexFormat: "uint16", - vertexBuffers: [ - { - arrayStride: vertex_format_size(VertexFormat.PosTex), - stepMode: "vertex", - attributes: [ - { - format: "float3", - offset: 0, - shaderLocation: 0, - }, - { - format: "ushort2norm", - offset: 12, - shaderLocation: 1, - }, - ], - }, - ], - }, - }); - - this.gpu = { - gfx: new WebgpuGfx(device, bind_group_layout), - device, - swap_chain, - pipeline, - }; - - this.set_size(this.width, this.height); - } - } catch (e) { - logger.error("Failed to initialize WebGPU renderer.", e); - } + this.set_size(this.width, this.height); } dispose(): void { this.disposed = true; + + this.depth_texture.destroy(); + super.dispose(); } set_size(width: number, height: number): void { - // There seems to be a bug in chrome's WebGPU implementation that requires you to set a - // canvas element's width and height after it's added to the DOM. - if (this.gpu) { - this.canvas_element.width = width; - this.canvas_element.height = height; - } + this.canvas_element.width = width; + this.canvas_element.height = height; + + this.depth_texture?.destroy(); + this.depth_texture = this.device.createTexture({ + size: { + width, + height, + depth: 1, + }, + format: "depth24plus", + usage: GPUTextureUsage.OUTPUT_ATTACHMENT | GPUTextureUsage.COPY_SRC, // eslint-disable-line no-undef + }); super.set_size(width, height); } protected render(): void { - if (this.gpu) { - const { device, swap_chain, pipeline } = this.gpu; + const command_encoder = this.device.createCommandEncoder(); - const command_encoder = device.createCommandEncoder(); - const texture_view = swap_chain.getCurrentTexture().createView(); + // Traverse the scene graph and sort the meshes into vertex format-specific buckets. + const draw_data: { mesh: Mesh; mvp_mat: Mat4 }[][] = VERTEX_FORMATS.map(() => []); - const pass_encoder = command_encoder.beginRenderPass({ - colorAttachments: [ - { - attachment: texture_view, - loadValue: { r: 0.1, g: 0.1, b: 0.1, a: 1 }, - }, - ], + const camera_project_mat = mat4_multiply( + this.camera.projection_matrix, + this.camera.view_matrix, + ); + + let uniform_buffer_size = 0; + + this.scene.traverse((node, parent_mat) => { + const mat = mat4_multiply(parent_mat, node.transform); + + if (node.mesh) { + uniform_buffer_size += VERTEX_FORMATS[node.mesh.format].uniform_buffer_size; + + draw_data[node.mesh.format].push({ + mesh: node.mesh, + mvp_mat: mat, + }); + } + + return mat; + }, camera_project_mat); + + let uniform_buffer: GPUBuffer | undefined; + + // Upload uniform data. + if (uniform_buffer_size > 0) { + let uniform_array_buffer: ArrayBuffer; + + [uniform_buffer, uniform_array_buffer] = this.device.createBufferMapped({ + size: uniform_buffer_size, + usage: GPUBufferUsage.COPY_SRC, // eslint-disable-line no-undef }); - pass_encoder.setPipeline(pipeline); + const uniform_array = new Float32Array(uniform_array_buffer); + let uniform_buffer_pos = 0; - const camera_project_mat = mat4_multiply( - this.camera.projection_matrix, - this.camera.view_matrix, - ); + for (const vertex_format of VERTEX_FORMATS) { + for (const { mesh, mvp_mat } of draw_data[vertex_format.type]) { + const copy_pos = 4 * uniform_buffer_pos; + uniform_array.set(mvp_mat.data, uniform_buffer_pos); + uniform_buffer_pos += mvp_mat.data.length; - this.scene.traverse((node, parent_mat) => { - const mat = mat4_multiply(parent_mat, node.transform); + if (vertex_format.normal_offset != undefined) { + const normal_mat = mvp_mat.normal_mat3(); + uniform_array.set(normal_mat.data, uniform_buffer_pos); + uniform_buffer_pos += normal_mat.data.length; + } - if (node.mesh) { - const gfx_mesh = node.mesh.gfx_mesh as WebgpuMesh; - gfx_mesh.uniform_buffer.setSubData(0, mat.data); - pass_encoder.setBindGroup(0, gfx_mesh.bind_group); - pass_encoder.setVertexBuffer(0, gfx_mesh.vertex_buffer); - pass_encoder.setIndexBuffer(gfx_mesh.index_buffer); - pass_encoder.drawIndexed(node.mesh.index_count, 1, 0, 0, 0); + command_encoder.copyBufferToBuffer( + uniform_buffer, + copy_pos, + (mesh.gfx_mesh as WebgpuMesh).uniform_buffer, + 0, + vertex_format.uniform_buffer_size, + ); } + } - return mat; - }, camera_project_mat); - - pass_encoder.endPass(); - - device.defaultQueue.submit([command_encoder.finish()]); + uniform_buffer.unmap(); } + + const texture_view = this.swap_chain.getCurrentTexture().createView(); + const pass_encoder = command_encoder.beginRenderPass({ + colorAttachments: [ + { + attachment: texture_view, + loadValue: { r: 0.1, g: 0.1, b: 0.1, a: 1 }, + }, + ], + depthStencilAttachment: { + attachment: this.depth_texture.createView(), + depthLoadValue: 1, + depthStoreOp: "store", + stencilLoadValue: "load", + stencilStoreOp: "store", + }, + }); + + // Render all meshes per vertex format. + for (const vertex_format of VERTEX_FORMATS) { + pass_encoder.setPipeline(this.pipelines[vertex_format.type].pipeline); + + for (const { mesh } of draw_data[vertex_format.type]) { + const gfx_mesh = mesh.gfx_mesh as WebgpuMesh; + pass_encoder.setBindGroup(0, gfx_mesh.bind_group); + pass_encoder.setVertexBuffer(0, gfx_mesh.vertex_buffer); + pass_encoder.setIndexBuffer(gfx_mesh.index_buffer); + pass_encoder.drawIndexed(mesh.index_count, 1, 0, 0, 0); + } + } + + pass_encoder.endPass(); + this.device.defaultQueue.submit([command_encoder.finish()]); + + uniform_buffer?.destroy(); } } diff --git a/src/quest_editor/model/QuestEventActionModel.ts b/src/quest_editor/model/QuestEventActionModel.ts index 0f8d73c5..83c16795 100644 --- a/src/quest_editor/model/QuestEventActionModel.ts +++ b/src/quest_editor/model/QuestEventActionModel.ts @@ -10,7 +10,9 @@ export enum QuestEventActionType { Lock, } -export const QuestEventActionTypes = enum_values(QuestEventActionType); +export const QuestEventActionTypes: readonly QuestEventActionType[] = enum_values( + QuestEventActionType, +); export type QuestEventActionModel = | QuestEventActionSpawnNpcsModel diff --git a/src/viewer/index.ts b/src/viewer/index.ts index cc0bc451..5da676e2 100644 --- a/src/viewer/index.ts +++ b/src/viewer/index.ts @@ -46,12 +46,14 @@ export function initialize_viewer( let renderer: Renderer; if (gui_store.feature_active("webgpu")) { - const { WebgpuRenderer } = await import("../core/rendering/webgpu/WebgpuRenderer"); + const { create_webgpu_renderer } = await import( + "../core/rendering/webgpu/WebgpuRenderer" + ); const { ModelGfxRenderer } = await import("./rendering/ModelGfxRenderer"); renderer = new ModelGfxRenderer( store, - new WebgpuRenderer(Projection.Perspective, http_client), + await create_webgpu_renderer(Projection.Perspective, http_client), ); } else if (gui_store.feature_active("webgl")) { const { WebglRenderer } = await import("../core/rendering/webgl/WebglRenderer"); @@ -82,10 +84,12 @@ export function initialize_viewer( let renderer: Renderer; if (gui_store.feature_active("webgpu")) { - const { WebgpuRenderer } = await import("../core/rendering/webgpu/WebgpuRenderer"); + const { create_webgpu_renderer } = await import( + "../core/rendering/webgpu/WebgpuRenderer" + ); renderer = new TextureRenderer( controller, - new WebgpuRenderer(Projection.Orthographic, http_client), + await create_webgpu_renderer(Projection.Orthographic, http_client), ); } else { const { WebglRenderer } = await import("../core/rendering/webgl/WebglRenderer"); diff --git a/src/viewer/rendering/TextureRenderer.ts b/src/viewer/rendering/TextureRenderer.ts index 2b4897f5..d883850f 100644 --- a/src/viewer/rendering/TextureRenderer.ts +++ b/src/viewer/rendering/TextureRenderer.ts @@ -2,7 +2,7 @@ import { Disposer } from "../../core/observable/Disposer"; import { LogManager } from "../../core/Logger"; import { TextureController } from "../controllers/texture/TextureController"; import { XvrTexture } from "../../core/data_formats/parsing/ninja/texture"; -import { VertexFormat } from "../../core/rendering/VertexFormat"; +import { VertexFormatType } from "../../core/rendering/VertexFormat"; import { Mesh } from "../../core/rendering/Mesh"; import { GfxRenderer } from "../../core/rendering/GfxRenderer"; import { Renderer } from "../../core/rendering/Renderer"; @@ -81,7 +81,7 @@ export class TextureRenderer implements Renderer { } private create_quad(tex: XvrTexture): Mesh { - return Mesh.builder(VertexFormat.PosTex) + return Mesh.builder(VertexFormatType.PosTex) .vertex(new Vec3(0, 0, 0), new Vec2(0, 1)) .vertex(new Vec3(tex.width, 0, 0), new Vec2(1, 1)) diff --git a/test/src/core/rendering/StubGfxRenderer.ts b/test/src/core/rendering/StubGfxRenderer.ts index a440b6fa..e2bf7710 100644 --- a/test/src/core/rendering/StubGfxRenderer.ts +++ b/test/src/core/rendering/StubGfxRenderer.ts @@ -8,7 +8,7 @@ export class StubGfxRenderer extends GfxRenderer { } constructor() { - super(Projection.Orthographic); + super(document.createElement("canvas"), Projection.Orthographic); } protected render(): void {} // eslint-disable-line