A hackable template for creating small and fast browser games.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Clear Game.Cameras in FrameSetup; separate xr variants of sys_camera, sys_render_forward

+561 -233
+4 -2
Animate/game.ts
··· 29 29 LightDetails = new Float32Array(4 * 8); 30 30 31 31 override FrameUpdate(delta: number) { 32 + // Camera. 33 + sys_resize(this, delta); 34 + sys_camera(this, delta); 35 + 32 36 // Player input. 33 37 sys_control(this, delta); 34 38 ··· 39 43 // Rendering. 40 44 sys_audio_listener(this, delta); 41 45 sys_audio_source(this, delta); 42 - sys_resize(this, delta); 43 - sys_camera(this, delta); 44 46 sys_light(this, delta); 45 47 sys_render_forward(this, delta); 46 48 }
+6 -3
DeferredShading/game.ts
··· 38 38 MeshCube = mesh_cube(this.Gl); 39 39 MeshQuad = mesh_quad(this.Gl); 40 40 41 - Targets: { 41 + override Targets: { 42 42 Gbuffer: DeferredTarget; 43 43 Shaded: ForwardTarget; 44 44 Sun: DepthTarget; ··· 63 63 } 64 64 65 65 override FrameUpdate(delta: number) { 66 + sys_resize(this, delta); 67 + sys_camera(this, delta); 68 + 66 69 sys_control_always(this, delta); 70 + 67 71 sys_move(this, delta); 68 72 sys_transform(this, delta); 69 - sys_resize(this, delta); 70 - sys_camera(this, delta); 73 + 71 74 sys_light(this, delta); 72 75 sys_render_depth(this, delta); 73 76 sys_render_deferred(this, delta);
+1 -20
DeferredShading/systems/sys_resize.ts
··· 1 - import {resize_deferred_target} from "../../common/framebuffer.js"; 2 - import {Game} from "../game.js"; 3 - 4 - export function sys_resize(game: Game, delta: number) { 5 - if (game.ViewportWidth != window.innerWidth || game.ViewportHeight != window.innerHeight) { 6 - game.ViewportResized = true; 7 - } 8 - 9 - if (game.ViewportResized) { 10 - game.ViewportWidth = game.Canvas3D.width = game.Canvas2D.width = window.innerWidth; 11 - game.ViewportHeight = game.Canvas3D.height = game.Canvas2D.height = window.innerHeight; 12 - 13 - resize_deferred_target( 14 - game.Gl, 15 - game.Targets.Gbuffer, 16 - game.ViewportWidth, 17 - game.ViewportHeight 18 - ); 19 - } 20 - } 1 + ../../core/systems/sys_resize.ts
+5 -4
FirstPerson/game.ts
··· 1 - import {GameXR} from "../common/game.js"; 1 + import {Game3D} from "../common/game.js"; 2 2 import {mat_forward_colored_gouraud} from "../materials/mat_forward_colored_gouraud.js"; 3 3 import {mesh_cube} from "../meshes/cube.js"; 4 4 import {sys_camera} from "./systems/sys_camera.js"; ··· 15 15 import {sys_ui} from "./systems/sys_ui.js"; 16 16 import {World} from "./world.js"; 17 17 18 - export class Game extends GameXR { 18 + export class Game extends Game3D { 19 19 World = new World(); 20 20 21 21 MaterialColoredGouraud = mat_forward_colored_gouraud(this.Gl); ··· 26 26 LightDetails = new Float32Array(4 * 8); 27 27 28 28 override FrameUpdate(delta: number) { 29 + sys_resize(this, delta); 30 + sys_camera(this, delta); 31 + 29 32 sys_control_keyboard(this, delta); 30 33 sys_control_mouse_move(this, delta); 31 34 sys_control_touch_move(this, delta); ··· 34 37 sys_move(this, delta); 35 38 sys_transform(this, delta); 36 39 37 - sys_resize(this, delta); 38 - sys_camera(this, delta); 39 40 sys_light(this, delta); 40 41 sys_render_forward(this, delta); 41 42 sys_draw(this, delta);
+7 -4
ForwardShading/game.ts
··· 1 - import {GameXR} from "../common/game.js"; 1 + import {Game3D} from "../common/game.js"; 2 2 import {mat_forward_colored_flat} from "../materials/mat_forward_colored_flat.js"; 3 3 import {mat_forward_colored_gouraud} from "../materials/mat_forward_colored_gouraud.js"; 4 4 import {mat_forward_colored_phong} from "../materials/mat_forward_colored_phong.js"; ··· 23 23 import {sys_transform} from "./systems/sys_transform.js"; 24 24 import {World} from "./world.js"; 25 25 26 - export class Game extends GameXR { 26 + export class Game extends Game3D { 27 27 World = new World(); 28 28 29 29 MaterialColoredPoints = mat_forward_colored_points(this.Gl); ··· 48 48 LightDetails = new Float32Array(4 * 8); 49 49 50 50 override FrameUpdate(delta: number) { 51 + sys_resize(this, delta); 52 + sys_camera(this, delta); 53 + 51 54 sys_control_always(this, delta); 55 + 52 56 sys_move(this, delta); 53 57 sys_transform(this, delta); 54 - sys_resize(this, delta); 55 - sys_camera(this, delta); 58 + 56 59 sys_light(this, delta); 57 60 sys_render_forward(this, delta); 58 61 }
+1 -1
ForwardShadows/game.ts
··· 25 25 LightPositions = new Float32Array(4 * 8); 26 26 LightDetails = new Float32Array(4 * 8); 27 27 28 - Targets = { 28 + override Targets = { 29 29 Sun: create_depth_target(this.Gl, 2048, 2048), 30 30 }; 31 31
+3 -1
Instancing/game.ts
··· 19 19 LightDetails = new Float32Array(4 * 8); 20 20 21 21 override FrameUpdate(delta: number) { 22 - sys_transform(this, delta); 23 22 sys_resize(this, delta); 24 23 sys_camera(this, delta); 24 + 25 + sys_transform(this, delta); 26 + 25 27 sys_light(this, delta); 26 28 sys_render_forward(this, delta); 27 29 }
+3 -2
Minimal3D/game.ts
··· 1 - import {GameXR} from "../common/game.js"; 1 + import {Game3D} from "../common/game.js"; 2 2 import {mat_forward_colored_gouraud} from "../materials/mat_forward_colored_gouraud.js"; 3 3 import {mesh_cube} from "../meshes/cube.js"; 4 4 import {sys_camera} from "./systems/sys_camera.js"; ··· 8 8 import {sys_transform} from "./systems/sys_transform.js"; 9 9 import {World} from "./world.js"; 10 10 11 - export class Game extends GameXR { 11 + export class Game extends Game3D { 12 12 World = new World(); 13 13 14 14 MaterialColoredGouraud = mat_forward_colored_gouraud(this.Gl); ··· 22 22 sys_transform(this, delta); 23 23 sys_resize(this, delta); 24 24 sys_camera(this, delta); 25 + 25 26 sys_light(this, delta); 26 27 sys_render_forward(this, delta); 27 28 }
+5 -3
Monkey/game.ts
··· 1 - import {GameXR} from "../common/game.js"; 1 + import {Game3D} from "../common/game.js"; 2 2 import {Mesh} from "../common/mesh.js"; 3 3 import {mat_forward_colored_gouraud} from "../materials/mat_forward_colored_gouraud.js"; 4 4 import {mat_forward_colored_phong} from "../materials/mat_forward_colored_phong.js"; ··· 9 9 import {sys_transform} from "./systems/sys_transform.js"; 10 10 import {World} from "./world.js"; 11 11 12 - export class Game extends GameXR { 12 + export class Game extends Game3D { 13 13 World = new World(); 14 14 15 15 MaterialColoredGouraud = mat_forward_colored_gouraud(this.Gl); ··· 22 22 LightDetails = new Float32Array(4 * 8); 23 23 24 24 override FrameUpdate(delta: number) { 25 - sys_transform(this, delta); 26 25 sys_resize(this, delta); 27 26 sys_camera(this, delta); 27 + 28 + sys_transform(this, delta); 29 + 28 30 sys_light(this, delta); 29 31 sys_render_forward(this, delta); 30 32 }
+6 -4
NewProject3D/game.ts
··· 1 - import {GameXR} from "../common/game.js"; 1 + import {Game3D} from "../common/game.js"; 2 2 import {mat_forward_colored_phong} from "../materials/mat_forward_colored_phong.js"; 3 3 import {mesh_cube} from "../meshes/cube.js"; 4 4 import {sys_animate} from "./systems/sys_animate.js"; ··· 31 31 import {sys_ui} from "./systems/sys_ui.js"; 32 32 import {World} from "./world.js"; 33 33 34 - export class Game extends GameXR { 34 + export class Game extends Game3D { 35 35 World = new World(); 36 36 37 37 MaterialColoredShaded = mat_forward_colored_phong(this.Gl); ··· 60 60 // Event loop. 61 61 sys_poll(this, delta); 62 62 63 + // Camera. 64 + sys_resize(this, delta); 65 + sys_camera(this, delta); 66 + 63 67 // Player input. 64 68 sys_control_keyboard(this, delta); 65 69 sys_control_mouse_move(this, delta); ··· 83 87 // Rendering. 84 88 sys_audio_listener(this, delta); 85 89 sys_audio_source(this, delta); 86 - sys_resize(this, delta); 87 - sys_camera(this, delta); 88 90 sys_light(this, delta); 89 91 sys_render_forward(this, delta); 90 92 sys_draw(this, delta);
+4 -2
Particles/game.ts
··· 17 17 Textures: Record<string, WebGLTexture> = {}; 18 18 19 19 override FrameUpdate(delta: number) { 20 + sys_resize(this, delta); 21 + sys_camera(this, delta); 22 + 20 23 sys_particles(this, delta); 21 24 sys_shake(this, delta); 22 25 sys_transform(this, delta); 23 - sys_resize(this, delta); 24 - sys_camera(this, delta); 26 + 25 27 sys_render_forward(this, delta); 26 28 } 27 29 }
+8 -6
PathFinding/game.ts
··· 1 - import {GameXR} from "../common/game.js"; 1 + import {Game3D} from "../common/game.js"; 2 2 import {Mesh} from "../common/mesh.js"; 3 3 import {Entity} from "../common/world.js"; 4 4 import {mat_forward_colored_gouraud} from "../materials/mat_forward_colored_gouraud.js"; ··· 22 22 import {sys_transform} from "./systems/sys_transform.js"; 23 23 import {World} from "./world.js"; 24 24 25 - export class Game extends GameXR { 25 + export class Game extends Game3D { 26 26 World = new World(); 27 27 28 28 MaterialColoredLine = mat_forward_colored_line(this.Gl); ··· 39 39 Selected?: Entity; 40 40 41 41 override FrameUpdate(delta: number) { 42 - // Camera controls and picking. 42 + // Camera and picking. 43 + sys_resize(this, delta); 44 + sys_camera(this, delta); 45 + sys_pick(this, delta); 46 + 47 + // User input. 43 48 sys_control_keyboard(this, delta); 44 49 sys_control_mouse_drag(this, delta); 45 50 sys_control_touch_drag(this, delta); 46 51 sys_control_dolly(this, delta); 47 - sys_pick(this, delta); 48 52 49 53 // Player order. 50 54 sys_control_player(this, delta); ··· 58 62 sys_collide(this, delta); 59 63 60 64 // Rendering. 61 - sys_resize(this, delta); 62 - sys_camera(this, delta); 63 65 sys_light(this, delta); 64 66 sys_render_forward(this, delta); 65 67 sys_draw(this, delta);
+6 -3
Render2D/game.ts
··· 7 7 import {sys_move2d} from "./systems/sys_move2d.js"; 8 8 import {sys_physics2d_integrate} from "./systems/sys_physics2d_integrate.js"; 9 9 import {sys_render2d} from "./systems/sys_render2d.js"; 10 - import {sys_resize} from "./systems/sys_resize.js"; 10 + import {sys_resize2d} from "./systems/sys_resize2d.js"; 11 11 import {sys_transform} from "./systems/sys_transform.js"; 12 12 import {sys_transform2d} from "./systems/sys_transform2d.js"; 13 13 import {World} from "./world.js"; ··· 97 97 } 98 98 99 99 override FrameUpdate(delta: number) { 100 + sys_resize2d(this, delta); 101 + sys_camera2d(this, delta); 102 + 100 103 sys_control_player(this, delta); 101 104 sys_control_always2d(this, delta); 105 + 102 106 sys_move2d(this, delta); 103 107 sys_physics2d_integrate(this, delta); 104 108 sys_transform2d(this, delta); 105 109 sys_transform(this, delta); 106 - sys_resize(this, delta); 107 - sys_camera2d(this, delta); 110 + 108 111 sys_render2d(this, delta); 109 112 } 110 113 }
+2 -13
Render2D/systems/sys_camera2d.ts
··· 3 3 */ 4 4 5 5 import {copy, get_translation, multiply} from "../../common/mat4.js"; 6 - import {ProjectionKind, resize_ortho_keeping_unit_size} from "../../common/projection.js"; 7 6 import {CameraKind} from "../components/com_camera.js"; 8 7 import {Game} from "../game.js"; 9 8 import {Has} from "../world.js"; ··· 11 10 const QUERY = Has.Transform | Has.Camera; 12 11 13 12 export function sys_camera2d(game: Game, delta: number) { 14 - game.Cameras = []; 15 13 for (let i = 0; i < game.World.Signature.length; i++) { 16 14 if ((game.World.Signature[i] & QUERY) === QUERY) { 17 15 let camera = game.World.Camera[i]; 18 - if (camera.Kind === CameraKind.Xr) { 19 - throw new Error("XR not implemented"); 16 + if (camera.Kind !== CameraKind.Forward) { 17 + throw new Error("Only Forward cameras are supported."); 20 18 } 21 19 22 20 let transform = game.World.Transform[i]; 23 21 let projection = camera.Projection; 24 - 25 - if (game.ViewportResized) { 26 - let aspect = game.ViewportWidth / game.ViewportHeight; 27 - switch (projection.Kind) { 28 - case ProjectionKind.Ortho: 29 - resize_ortho_keeping_unit_size(projection, aspect, game.ViewportHeight, 16); 30 - break; 31 - } 32 - } 33 22 34 23 copy(camera.View, transform.Self); 35 24 multiply(camera.Pv, projection.Projection, camera.View);
-1
Render2D/systems/sys_resize.ts
··· 1 - ../../core/systems/sys_resize.ts
+40
Render2D/systems/sys_resize2d.ts
··· 1 + /** 2 + * @module systems/sys_resize2d 3 + */ 4 + 5 + import {ProjectionKind, resize_ortho_keeping_unit_size} from "../../common/projection.js"; 6 + import {CameraKind} from "../components/com_camera.js"; 7 + import {Game} from "../game.js"; 8 + import {Has} from "../world.js"; 9 + 10 + const QUERY = Has.Camera; 11 + 12 + export function sys_resize2d(game: Game, delta: number) { 13 + if (game.ViewportWidth != window.innerWidth || game.ViewportHeight != window.innerHeight) { 14 + game.ViewportResized = true; 15 + } 16 + 17 + if (game.ViewportResized) { 18 + game.ViewportWidth = game.Canvas3D.width = game.Canvas2D.width = window.innerWidth; 19 + game.ViewportHeight = game.Canvas3D.height = game.Canvas2D.height = window.innerHeight; 20 + 21 + for (let i = 0; i < game.World.Signature.length; i++) { 22 + if ((game.World.Signature[i] & QUERY) === QUERY) { 23 + let camera = game.World.Camera[i]; 24 + if ( 25 + camera.Kind == CameraKind.Forward && 26 + camera.Projection.Kind == ProjectionKind.Ortho 27 + ) { 28 + let aspect = game.ViewportWidth / game.ViewportHeight; 29 + resize_ortho_keeping_unit_size( 30 + camera.Projection, 31 + aspect, 32 + game.ViewportHeight, 33 + 16 34 + ); 35 + break; 36 + } 37 + } 38 + } 39 + } 40 + }
+8 -5
RenderTexture/game.ts
··· 1 1 import {create_forward_target} from "../common/framebuffer.js"; 2 - import {GameXR} from "../common/game.js"; 2 + import {Game3D} from "../common/game.js"; 3 3 import {mat_forward_textured_unlit} from "../materials/mat_forward_textured_unlit.js"; 4 4 import {mesh_cube} from "../meshes/cube.js"; 5 5 import {mesh_plane} from "../meshes/plane.js"; ··· 11 11 import {sys_transform} from "./systems/sys_transform.js"; 12 12 import {World} from "./world.js"; 13 13 14 - export class Game extends GameXR { 14 + export class Game extends Game3D { 15 15 World = new World(); 16 16 17 17 MaterialTexturedUnlit = mat_forward_textured_unlit(this.Gl); ··· 19 19 MeshPlane = mesh_plane(this.Gl); 20 20 21 21 Textures: Record<string, WebGLTexture> = {}; 22 - Targets = { 22 + override Targets = { 23 23 Minimap: create_forward_target(this.Gl, 256, 256), 24 24 }; 25 25 ··· 28 28 LightDetails = new Float32Array(4 * 8); 29 29 30 30 override FrameUpdate(delta: number) { 31 + sys_resize(this, delta); 32 + sys_camera(this, delta); 33 + 31 34 sys_control_always(this, delta); 35 + 32 36 sys_move(this, delta); 33 37 sys_transform(this, delta); 34 - sys_resize(this, delta); 35 - sys_camera(this, delta); 38 + 36 39 sys_render_forward(this, delta); 37 40 } 38 41 }
+6 -4
RigidBody/game.ts
··· 1 - import {GameXR} from "../common/game.js"; 1 + import {Game3D} from "../common/game.js"; 2 2 import {mat_forward_colored_gouraud} from "../materials/mat_forward_colored_gouraud.js"; 3 3 import {mesh_cube} from "../meshes/cube.js"; 4 4 import {mesh_hand} from "../meshes/hand.js"; ··· 18 18 import {sys_transform} from "./systems/sys_transform.js"; 19 19 import {World} from "./world.js"; 20 20 21 - export class Game extends GameXR { 21 + export class Game extends Game3D { 22 22 World = new World(); 23 23 24 24 MaterialColoredGouraud = mat_forward_colored_gouraud(this.Gl); ··· 43 43 // Destroy entities past their age. 44 44 sys_lifespan(this, delta); 45 45 46 + // Camera. 47 + sys_resize(this, delta); 48 + sys_camera(this, delta); 49 + 46 50 // Input and AI. 47 51 sys_control_always(this, delta); 48 52 ··· 53 57 sys_transform(this, delta); 54 58 55 59 // Rendering. 56 - sys_resize(this, delta); 57 - sys_camera(this, delta); 58 60 sys_light(this, delta); 59 61 sys_render_forward(this, delta); 60 62 }
+6 -5
Trigger/game.ts
··· 1 - import {GameXR} from "../common/game.js"; 1 + import {Game3D} from "../common/game.js"; 2 2 import {mat_forward_colored_gouraud} from "../materials/mat_forward_colored_gouraud.js"; 3 3 import {mat_forward_colored_wireframe} from "../materials/mat_forward_colored_unlit.js"; 4 4 import {mesh_cube} from "../meshes/cube.js"; ··· 14 14 import {sys_trigger} from "./systems/sys_trigger.js"; 15 15 import {World} from "./world.js"; 16 16 17 - export class Game extends GameXR { 17 + export class Game extends Game3D { 18 18 World = new World(); 19 19 20 20 MaterialColoredWireframe = mat_forward_colored_wireframe(this.Gl); ··· 26 26 LightDetails = new Float32Array(4 * 8); 27 27 28 28 override FrameUpdate(delta: number) { 29 + sys_resize(this, delta); 30 + sys_camera(this, delta); 31 + 29 32 sys_control_always(this, delta); 33 + 30 34 sys_move(this, delta); 31 35 sys_transform(this, delta); 32 - 33 36 sys_collide(this, delta); 34 37 sys_trigger(this, delta); 35 38 36 - sys_resize(this, delta); 37 - sys_camera(this, delta); 38 39 sys_light(this, delta); 39 40 sys_render_forward(this, delta); 40 41 sys_debug(this, delta);
+20 -7
WebXR/game.ts
··· 3 3 import {mesh_cube} from "../meshes/cube.js"; 4 4 import {mesh_hand} from "../meshes/hand.js"; 5 5 import {sys_camera} from "./systems/sys_camera.js"; 6 + import {sys_camera_xr} from "./systems/sys_camera_xr.js"; 6 7 import {sys_control_xr} from "./systems/sys_control_xr.js"; 7 8 import {sys_light} from "./systems/sys_light.js"; 8 9 import {sys_render_forward} from "./systems/sys_render_forward.js"; 10 + import {sys_render_xr} from "./systems/sys_render_xr.js"; 9 11 import {sys_resize} from "./systems/sys_resize.js"; 10 12 import {sys_transform} from "./systems/sys_transform.js"; 11 13 import {sys_ui} from "./systems/sys_ui.js"; ··· 23 25 LightDetails = new Float32Array(4 * 8); 24 26 25 27 override FrameUpdate(delta: number) { 26 - sys_control_xr(this, delta); 27 - sys_transform(this, delta); 28 - sys_resize(this, delta); 29 - sys_camera(this, delta); 30 - sys_light(this, delta); 31 - sys_render_forward(this, delta); 32 - sys_ui(this, delta); 28 + if (this.XrFrame) { 29 + sys_camera_xr(this, delta); 30 + 31 + sys_control_xr(this, delta); 32 + sys_transform(this, delta); 33 + 34 + sys_light(this, delta); 35 + sys_render_xr(this, delta); 36 + } else { 37 + sys_resize(this, delta); 38 + sys_camera(this, delta); 39 + 40 + sys_transform(this, delta); 41 + 42 + sys_light(this, delta); 43 + sys_render_forward(this, delta); 44 + sys_ui(this, delta); 45 + } 33 46 } 34 47 }
+1 -91
WebXR/systems/sys_camera.ts
··· 1 - /** 2 - * @module systems/sys_camera 3 - */ 4 - 5 - import {copy, create, get_translation, invert, multiply} from "../../common/mat4.js"; 6 - import {ProjectionKind, resize_ortho, resize_perspective} from "../../common/projection.js"; 7 - import {Entity} from "../../common/world.js"; 8 - import {Camera, CameraKind, CameraXr, XrEye} from "../components/com_camera.js"; 9 - import {Game} from "../game.js"; 10 - import {Has} from "../world.js"; 11 - 12 - const QUERY = Has.Transform | Has.Camera; 13 - 14 - export function sys_camera(game: Game, delta: number) { 15 - game.Cameras = []; 16 - 17 - for (let i = 0; i < game.World.Signature.length; i++) { 18 - if ((game.World.Signature[i] & QUERY) === QUERY) { 19 - let camera = game.World.Camera[i]; 20 - switch (camera.Kind) { 21 - case CameraKind.Xr: 22 - if (game.XrFrame) { 23 - update_xr(game, i, camera); 24 - game.Cameras.push(i); 25 - } 26 - break; 27 - case CameraKind.Forward: 28 - if (!game.XrFrame) { 29 - update_camera(game, i, camera); 30 - game.Cameras.push(i); 31 - } 32 - break; 33 - default: 34 - update_camera(game, i, camera); 35 - game.Cameras.push(i); 36 - break; 37 - } 38 - } 39 - } 40 - } 41 - 42 - function update_camera(game: Game, entity: Entity, camera: Exclude<Camera, CameraXr>) { 43 - let transform = game.World.Transform[entity]; 44 - let projection = camera.Projection; 45 - 46 - if (game.ViewportResized) { 47 - let aspect = 48 - camera.Kind === CameraKind.Forward 49 - ? game.ViewportWidth / game.ViewportHeight 50 - : camera.Target.Width / camera.Target.Height; 51 - switch (projection.Kind) { 52 - case ProjectionKind.Perspective: { 53 - resize_perspective(projection, aspect); 54 - break; 55 - } 56 - case ProjectionKind.Ortho: 57 - resize_ortho(projection, aspect); 58 - break; 59 - } 60 - } 61 - 62 - copy(camera.View, transform.Self); 63 - multiply(camera.Pv, projection.Projection, camera.View); 64 - get_translation(camera.Position, transform.World); 65 - } 66 - 67 - function update_xr(game: Game, entity: Entity, camera: CameraXr) { 68 - let transform = game.World.Transform[entity]; 69 - let pose = game.XrFrame!.getViewerPose(game.XrSpace); 70 - 71 - camera.Eyes = []; 72 - for (let viewpoint of pose.views) { 73 - let eye: XrEye = { 74 - Viewpoint: viewpoint, 75 - View: create(), 76 - Pv: create(), 77 - Position: [0, 0, 0], 78 - }; 79 - 80 - // Compute the eye's world matrix. 81 - multiply(eye.View, transform.World, viewpoint.transform.matrix); 82 - get_translation(eye.Position, eye.View); 83 - 84 - // Compute the view matrix. 85 - invert(eye.View, eye.View); 86 - // Compute the PV matrix. 87 - multiply(eye.Pv, viewpoint.projectionMatrix, eye.View); 88 - 89 - camera.Eyes.push(eye); 90 - } 91 - } 1 + ../../core/systems/sys_camera.ts
+1
WebXR/systems/sys_camera_xr.ts
··· 1 + ../../core/systems/sys_camera_xr.ts
+1
WebXR/systems/sys_render_xr.ts
··· 1 + ../../core/systems/sys_render_xr.ts
+2
common/framebuffer.ts
··· 10 10 GL_TEXTURE_2D, 11 11 } from "./webgl.js"; 12 12 13 + export type RenderTarget = ForwardTarget | DeferredTarget | DepthTarget; 14 + 13 15 export interface ForwardTarget { 14 16 Framebuffer: WebGLFramebuffer; 15 17 Width: number;
+22 -15
common/game.ts
··· 1 + import {RenderTarget} from "./framebuffer.js"; 1 2 import {GL_CULL_FACE, GL_DEPTH_TEST, GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA} from "./webgl.js"; 2 3 import {Entity, WorldImpl} from "./world.js"; 3 4 ··· 151 152 152 153 let tick = (now: number) => { 153 154 let delta = (now - last) / 1000; 154 - this.FrameSetup(delta); 155 + last = now; 156 + 157 + this.Running = requestAnimationFrame(tick); 155 158 159 + this.FrameSetup(delta); 156 160 accumulator += delta; 157 161 while (accumulator >= step) { 158 162 accumulator -= step; ··· 161 165 } 162 166 this.FrameUpdate(delta); 163 167 this.FrameReset(delta); 164 - 165 - last = now; 166 - this.Running = requestAnimationFrame(tick); 167 168 }; 168 169 169 170 this.Stop(); ··· 263 264 Canvas3D = document.querySelector("#scene")! as HTMLCanvasElement; 264 265 Gl = this.Canvas3D.getContext("webgl2")!; 265 266 267 + Audio = new AudioContext(); 266 268 Cameras: Array<Entity> = []; 267 - Audio = new AudioContext(); 269 + Targets: Record<string, RenderTarget> = {}; 268 270 269 271 constructor() { 270 272 super(); ··· 273 275 this.Gl.enable(GL_CULL_FACE); 274 276 275 277 this.Gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 278 + } 279 + 280 + override FrameSetup(delta: number) { 281 + super.FrameSetup(delta); 282 + this.Cameras = []; 276 283 } 277 284 } 278 285 ··· 303 310 304 311 let tick = (now: number, frame?: XRFrame) => { 305 312 let delta = (now - last) / 1000; 306 - this.FrameSetup(delta); 313 + last = now; 314 + 315 + if (frame) { 316 + this.XrFrame = frame; 317 + this.Running = this.XrFrame.session.requestAnimationFrame(tick); 318 + } else { 319 + this.XrFrame = undefined; 320 + this.Running = requestAnimationFrame(tick); 321 + } 307 322 323 + this.FrameSetup(delta); 308 324 accumulator += delta; 309 325 while (accumulator >= step) { 310 326 accumulator -= step; ··· 313 329 } 314 330 this.FrameUpdate(delta); 315 331 this.FrameReset(delta); 316 - 317 - last = now; 318 - if (frame) { 319 - this.XrFrame = frame; 320 - this.Running = this.XrFrame.session.requestAnimationFrame(tick); 321 - } else { 322 - this.XrFrame = undefined; 323 - this.Running = requestAnimationFrame(tick); 324 - } 325 332 }; 326 333 327 334 if (this.XrSession) {
+5 -25
core/systems/sys_camera.ts
··· 3 3 */ 4 4 5 5 import {copy, get_translation, multiply} from "../../common/mat4.js"; 6 - import {ProjectionKind, resize_ortho, resize_perspective} from "../../common/projection.js"; 7 6 import {Entity} from "../../common/world.js"; 8 7 import {Camera, CameraKind, CameraXr} from "../components/com_camera.js"; 9 8 import {Game} from "../game.js"; ··· 12 11 const QUERY = Has.Transform | Has.Camera; 13 12 14 13 export function sys_camera(game: Game, delta: number) { 15 - game.Cameras = []; 16 - 17 14 for (let i = 0; i < game.World.Signature.length; i++) { 18 15 if ((game.World.Signature[i] & QUERY) === QUERY) { 19 - game.Cameras.push(i); 20 16 let camera = game.World.Camera[i]; 21 17 switch (camera.Kind) { 22 - case CameraKind.Xr: 23 - throw new Error( 24 - "XR camera is not part of core/. See WebXR/systems/sys_camera." 25 - ); 26 - default: 18 + case CameraKind.Forward: 19 + case CameraKind.Deferred: 20 + case CameraKind.Framebuffer: 21 + case CameraKind.Depth: 27 22 update_camera(game, i, camera); 23 + game.Cameras.push(i); 28 24 break; 29 25 } 30 26 } ··· 34 30 function update_camera(game: Game, entity: Entity, camera: Exclude<Camera, CameraXr>) { 35 31 let transform = game.World.Transform[entity]; 36 32 let projection = camera.Projection; 37 - 38 - if (game.ViewportResized) { 39 - let aspect = 40 - camera.Kind === CameraKind.Forward 41 - ? game.ViewportWidth / game.ViewportHeight 42 - : camera.Target.Width / camera.Target.Height; 43 - switch (projection.Kind) { 44 - case ProjectionKind.Perspective: { 45 - resize_perspective(projection, aspect); 46 - break; 47 - } 48 - case ProjectionKind.Ortho: 49 - resize_ortho(projection, aspect); 50 - break; 51 - } 52 - } 53 33 54 34 copy(camera.View, transform.Self); 55 35 multiply(camera.Pv, projection.Projection, camera.View);
+51
core/systems/sys_camera_xr.ts
··· 1 + /** 2 + * @module systems/sys_camera_xr 3 + */ 4 + 5 + import {create, get_translation, invert, multiply} from "../../common/mat4.js"; 6 + import {Entity} from "../../common/world.js"; 7 + import {CameraKind, CameraXr, XrEye} from "../components/com_camera.js"; 8 + import {Game} from "../game.js"; 9 + import {Has} from "../world.js"; 10 + 11 + const QUERY = Has.Transform | Has.Camera; 12 + 13 + export function sys_camera_xr(game: Game, delta: number) { 14 + for (let i = 0; i < game.World.Signature.length; i++) { 15 + if ((game.World.Signature[i] & QUERY) === QUERY) { 16 + let camera = game.World.Camera[i]; 17 + switch (camera.Kind) { 18 + case CameraKind.Xr: 19 + update_xr(game, i, camera); 20 + game.Cameras.push(i); 21 + break; 22 + } 23 + } 24 + } 25 + } 26 + 27 + function update_xr(game: Game, entity: Entity, camera: CameraXr) { 28 + let transform = game.World.Transform[entity]; 29 + let pose = game.XrFrame!.getViewerPose(game.XrSpace); 30 + 31 + camera.Eyes = []; 32 + for (let viewpoint of pose.views) { 33 + let eye: XrEye = { 34 + Viewpoint: viewpoint, 35 + View: create(), 36 + Pv: create(), 37 + Position: [0, 0, 0], 38 + }; 39 + 40 + // Compute the eye's world matrix. 41 + multiply(eye.View, transform.World, viewpoint.transform.matrix); 42 + get_translation(eye.Position, eye.View); 43 + 44 + // Compute the view matrix. 45 + invert(eye.View, eye.View); 46 + // Compute the PV matrix. 47 + multiply(eye.Pv, viewpoint.projectionMatrix, eye.View); 48 + 49 + camera.Eyes.push(eye); 50 + } 51 + }
-12
core/systems/sys_render_forward.ts
··· 44 44 game.Gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 45 45 render_all(game, camera, camera.Target.RenderTexture); 46 46 break; 47 - case CameraKind.Xr: 48 - let layer = game.XrFrame!.session.renderState.baseLayer!; 49 - game.Gl.bindFramebuffer(GL_FRAMEBUFFER, layer.framebuffer); 50 - game.Gl.clearColor(...camera.ClearColor); 51 - game.Gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 52 - 53 - for (let eye of camera.Eyes) { 54 - let viewport = layer.getViewport(eye.Viewpoint); 55 - game.Gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height); 56 - render_all(game, eye); 57 - } 58 - break; 59 47 } 60 48 } 61 49 }
+278
core/systems/sys_render_xr.ts
··· 1 + /** 2 + * @module systems/sys_render_forward 3 + */ 4 + 5 + import {distance_squared_from_point} from "../../common/mat4.js"; 6 + import {Material} from "../../common/material.js"; 7 + import { 8 + GL_ARRAY_BUFFER, 9 + GL_BLEND, 10 + GL_COLOR_BUFFER_BIT, 11 + GL_DEPTH_BUFFER_BIT, 12 + GL_FLOAT, 13 + GL_FRAMEBUFFER, 14 + GL_TEXTURE0, 15 + GL_TEXTURE1, 16 + GL_TEXTURE2, 17 + GL_TEXTURE3, 18 + GL_TEXTURE_2D, 19 + GL_UNSIGNED_SHORT, 20 + } from "../../common/webgl.js"; 21 + import {Entity} from "../../common/world.js"; 22 + import {CameraEye, CameraKind} from "../components/com_camera.js"; 23 + import {Render, RenderKind, RenderPhase} from "../components/com_render.js"; 24 + import {Game} from "../game.js"; 25 + import {Has} from "../world.js"; 26 + 27 + const QUERY = Has.Transform | Has.Render; 28 + 29 + export function sys_render_xr(game: Game, delta: number) { 30 + for (let camera_entity of game.Cameras) { 31 + let camera = game.World.Camera[camera_entity]; 32 + switch (camera.Kind) { 33 + case CameraKind.Xr: 34 + let layer = game.XrFrame!.session.renderState.baseLayer!; 35 + game.Gl.bindFramebuffer(GL_FRAMEBUFFER, layer.framebuffer); 36 + game.Gl.clearColor(...camera.ClearColor); 37 + game.Gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 38 + 39 + for (let eye of camera.Eyes) { 40 + let viewport = layer.getViewport(eye.Viewpoint); 41 + game.Gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height); 42 + render_all(game, eye); 43 + } 44 + break; 45 + } 46 + } 47 + } 48 + 49 + function render_all(game: Game, eye: CameraEye, current_target?: WebGLTexture) { 50 + // Keep track of the current state to minimize switching. 51 + let current_material: Material<unknown> | null = null; 52 + let current_front_face: GLenum | null = null; 53 + 54 + // Transparent objects to be sorted by distance to camera and rendered later. 55 + let transparent_entities: Array<Entity> = []; 56 + 57 + // First render opaque objects. 58 + for (let ent = 0; ent < game.World.Signature.length; ent++) { 59 + if ((game.World.Signature[ent] & QUERY) === QUERY) { 60 + let render = game.World.Render[ent]; 61 + 62 + if (render.Phase === RenderPhase.Transparent) { 63 + // Store transparent objects in a separate array to render them later. 64 + transparent_entities.push(ent); 65 + continue; 66 + } 67 + 68 + if (render.Material !== current_material) { 69 + current_material = render.Material; 70 + use_material(game, render, eye); 71 + } 72 + 73 + if (render.FrontFace !== current_front_face) { 74 + current_front_face = render.FrontFace; 75 + game.Gl.frontFace(render.FrontFace); 76 + } 77 + 78 + draw_entity(game, ent, current_target); 79 + } 80 + } 81 + 82 + // Sort transparent objects by distance to camera, from back to front, to 83 + // enforce overdraw and blend them in the correct order. 84 + transparent_entities.sort((a, b) => { 85 + let transform_a = game.World.Transform[a]; 86 + let transform_b = game.World.Transform[b]; 87 + return ( 88 + distance_squared_from_point(transform_b.World, eye.Position) - 89 + distance_squared_from_point(transform_a.World, eye.Position) 90 + ); 91 + }); 92 + 93 + game.Gl.enable(GL_BLEND); 94 + 95 + for (let i = 0; i < transparent_entities.length; i++) { 96 + let ent = transparent_entities[i]; 97 + let render = game.World.Render[ent]; 98 + 99 + if (render.Material !== current_material) { 100 + current_material = render.Material; 101 + use_material(game, render, eye); 102 + } 103 + 104 + if (render.FrontFace !== current_front_face) { 105 + current_front_face = render.FrontFace; 106 + game.Gl.frontFace(render.FrontFace); 107 + } 108 + 109 + draw_entity(game, ent, current_target); 110 + } 111 + 112 + game.Gl.disable(GL_BLEND); 113 + } 114 + 115 + function use_material(game: Game, render: Render, eye: CameraEye) { 116 + switch (render.Kind) { 117 + case RenderKind.ColoredUnlit: 118 + game.Gl.useProgram(render.Material.Program); 119 + game.Gl.uniformMatrix4fv(render.Material.Locations.Pv, false, eye.Pv); 120 + break; 121 + case RenderKind.ColoredShaded: 122 + game.Gl.useProgram(render.Material.Program); 123 + game.Gl.uniformMatrix4fv(render.Material.Locations.Pv, false, eye.Pv); 124 + game.Gl.uniform3fv(render.Material.Locations.Eye, eye.Position); 125 + game.Gl.uniform4fv(render.Material.Locations.LightPositions, game.LightPositions); 126 + game.Gl.uniform4fv(render.Material.Locations.LightDetails, game.LightDetails); 127 + break; 128 + case RenderKind.TexturedUnlit: 129 + game.Gl.useProgram(render.Material.Program); 130 + game.Gl.uniformMatrix4fv(render.Material.Locations.Pv, false, eye.Pv); 131 + break; 132 + case RenderKind.TexturedShaded: 133 + game.Gl.useProgram(render.Material.Program); 134 + game.Gl.uniformMatrix4fv(render.Material.Locations.Pv, false, eye.Pv); 135 + game.Gl.uniform3fv(render.Material.Locations.Eye, eye.Position); 136 + game.Gl.uniform4fv(render.Material.Locations.LightPositions, game.LightPositions); 137 + game.Gl.uniform4fv(render.Material.Locations.LightDetails, game.LightDetails); 138 + break; 139 + case RenderKind.Vertices: 140 + game.Gl.useProgram(render.Material.Program); 141 + game.Gl.uniformMatrix4fv(render.Material.Locations.Pv, false, eye.Pv); 142 + break; 143 + case RenderKind.MappedShaded: 144 + game.Gl.useProgram(render.Material.Program); 145 + game.Gl.uniformMatrix4fv(render.Material.Locations.Pv, false, eye.Pv); 146 + game.Gl.uniform3fv(render.Material.Locations.Eye, eye.Position); 147 + game.Gl.uniform4fv(render.Material.Locations.LightPositions, game.LightPositions); 148 + game.Gl.uniform4fv(render.Material.Locations.LightDetails, game.LightDetails); 149 + break; 150 + } 151 + } 152 + 153 + function draw_entity(game: Game, entity: Entity, current_target?: WebGLTexture) { 154 + let transform = game.World.Transform[entity]; 155 + let render = game.World.Render[entity]; 156 + 157 + switch (render.Kind) { 158 + case RenderKind.ColoredUnlit: 159 + game.Gl.uniformMatrix4fv(render.Material.Locations.World, false, transform.World); 160 + game.Gl.uniform4fv(render.Material.Locations.Color, render.Color); 161 + game.Gl.bindVertexArray(render.Vao); 162 + game.Gl.drawElements( 163 + render.Material.Mode, 164 + render.Mesh.IndexCount, 165 + GL_UNSIGNED_SHORT, 166 + 0 167 + ); 168 + game.Gl.bindVertexArray(null); 169 + 170 + break; 171 + case RenderKind.ColoredShaded: 172 + game.Gl.uniformMatrix4fv(render.Material.Locations.World, false, transform.World); 173 + game.Gl.uniformMatrix4fv(render.Material.Locations.Self, false, transform.Self); 174 + game.Gl.uniform4fv(render.Material.Locations.DiffuseColor, render.DiffuseColor); 175 + game.Gl.uniform4fv(render.Material.Locations.SpecularColor, render.SpecularColor); 176 + game.Gl.uniform1f(render.Material.Locations.Shininess, render.Shininess); 177 + game.Gl.bindVertexArray(render.Vao); 178 + game.Gl.drawElements( 179 + render.Material.Mode, 180 + render.Mesh.IndexCount, 181 + GL_UNSIGNED_SHORT, 182 + 0 183 + ); 184 + game.Gl.bindVertexArray(null); 185 + break; 186 + case RenderKind.TexturedUnlit: 187 + if (render.Texture === current_target) { 188 + // Prevent feedback loop between the active render target 189 + // and the texture being rendered. 190 + break; 191 + } 192 + 193 + game.Gl.uniformMatrix4fv(render.Material.Locations.World, false, transform.World); 194 + game.Gl.uniform4fv(render.Material.Locations.Color, render.Color); 195 + 196 + game.Gl.activeTexture(GL_TEXTURE0); 197 + game.Gl.bindTexture(GL_TEXTURE_2D, render.Texture); 198 + game.Gl.uniform1i(render.Material.Locations.TextureMap, 0); 199 + 200 + game.Gl.bindVertexArray(render.Vao); 201 + game.Gl.drawElements( 202 + render.Material.Mode, 203 + render.Mesh.IndexCount, 204 + GL_UNSIGNED_SHORT, 205 + 0 206 + ); 207 + game.Gl.bindVertexArray(null); 208 + break; 209 + case RenderKind.TexturedShaded: 210 + if (render.Texture === current_target) { 211 + // Prevent feedback loop between the active render target 212 + // and the texture being rendered. 213 + break; 214 + } 215 + 216 + game.Gl.uniformMatrix4fv(render.Material.Locations.World, false, transform.World); 217 + game.Gl.uniformMatrix4fv(render.Material.Locations.Self, false, transform.Self); 218 + game.Gl.uniform4fv(render.Material.Locations.DiffuseColor, render.DiffuseColor); 219 + game.Gl.uniform4fv(render.Material.Locations.SpecularColor, render.SpecularColor); 220 + game.Gl.uniform1f(render.Material.Locations.Shininess, render.Shininess); 221 + 222 + game.Gl.activeTexture(GL_TEXTURE0); 223 + game.Gl.bindTexture(GL_TEXTURE_2D, render.Texture); 224 + game.Gl.uniform1i(render.Material.Locations.DiffuseMap, 0); 225 + 226 + game.Gl.bindVertexArray(render.Vao); 227 + game.Gl.drawElements( 228 + render.Material.Mode, 229 + render.Mesh.IndexCount, 230 + GL_UNSIGNED_SHORT, 231 + 0 232 + ); 233 + game.Gl.bindVertexArray(null); 234 + break; 235 + case RenderKind.Vertices: 236 + game.Gl.uniformMatrix4fv(render.Material.Locations.World, false, transform.World); 237 + game.Gl.uniform4fv(render.Material.Locations.Color, render.Color); 238 + game.Gl.bindBuffer(GL_ARRAY_BUFFER, render.VertexBuffer); 239 + game.Gl.enableVertexAttribArray(render.Material.Locations.VertexPosition); 240 + game.Gl.vertexAttribPointer( 241 + render.Material.Locations.VertexPosition, 242 + 3, 243 + GL_FLOAT, 244 + false, 245 + 0, 246 + 0 247 + ); 248 + game.Gl.drawArrays(render.Material.Mode, 0, render.IndexCount); 249 + break; 250 + case RenderKind.MappedShaded: 251 + game.Gl.uniformMatrix4fv(render.Material.Locations.World, false, transform.World); 252 + game.Gl.uniformMatrix4fv(render.Material.Locations.Self, false, transform.Self); 253 + 254 + game.Gl.uniform4fv(render.Material.Locations.DiffuseColor, render.DiffuseColor); 255 + 256 + game.Gl.activeTexture(GL_TEXTURE1); 257 + game.Gl.bindTexture(GL_TEXTURE_2D, render.DiffuseMap); 258 + game.Gl.uniform1i(render.Material.Locations.DiffuseMap, 1); 259 + 260 + game.Gl.activeTexture(GL_TEXTURE2); 261 + game.Gl.bindTexture(GL_TEXTURE_2D, render.NormalMap); 262 + game.Gl.uniform1i(render.Material.Locations.NormalMap, 2); 263 + 264 + game.Gl.activeTexture(GL_TEXTURE3); 265 + game.Gl.bindTexture(GL_TEXTURE_2D, render.RoughnessMap); 266 + game.Gl.uniform1i(render.Material.Locations.RoughnessMap, 3); 267 + 268 + game.Gl.bindVertexArray(render.Vao); 269 + game.Gl.drawElements( 270 + render.Material.Mode, 271 + render.Mesh.IndexCount, 272 + GL_UNSIGNED_SHORT, 273 + 0 274 + ); 275 + game.Gl.bindVertexArray(null); 276 + break; 277 + } 278 + }
+59
core/systems/sys_resize.ts
··· 2 2 * @module systems/sys_resize 3 3 */ 4 4 5 + import {resize_deferred_target} from "../../common/framebuffer.js"; 6 + import { 7 + Projection, 8 + ProjectionKind, 9 + resize_ortho, 10 + resize_perspective, 11 + } from "../../common/projection.js"; 12 + import {CameraKind} from "../components/com_camera.js"; 5 13 import {Game} from "../game.js"; 14 + import {Has} from "../world.js"; 15 + 16 + const QUERY = Has.Camera; 6 17 7 18 export function sys_resize(game: Game, delta: number) { 8 19 if (game.ViewportWidth != window.innerWidth || game.ViewportHeight != window.innerHeight) { ··· 12 23 if (game.ViewportResized) { 13 24 game.ViewportWidth = game.Canvas3D.width = game.Canvas2D.width = window.innerWidth; 14 25 game.ViewportHeight = game.Canvas3D.height = game.Canvas2D.height = window.innerHeight; 26 + 27 + for (let i = 0; i < game.World.Signature.length; i++) { 28 + if ((game.World.Signature[i] & QUERY) === QUERY) { 29 + let camera = game.World.Camera[i]; 30 + switch (camera.Kind) { 31 + case CameraKind.Forward: 32 + update_projection( 33 + camera.Projection, 34 + game.ViewportWidth / game.ViewportHeight 35 + ); 36 + break; 37 + case CameraKind.Framebuffer: 38 + case CameraKind.Depth: 39 + update_projection( 40 + camera.Projection, 41 + camera.Target.Width / camera.Target.Height 42 + ); 43 + break; 44 + case CameraKind.Deferred: 45 + resize_deferred_target( 46 + game.Gl, 47 + camera.Target, 48 + game.ViewportWidth, 49 + game.ViewportHeight 50 + ); 51 + update_projection( 52 + camera.Projection, 53 + camera.Target.Width / camera.Target.Height 54 + ); 55 + break; 56 + case CameraKind.Xr: 57 + // The eye projections are managed by the WebXR API. 58 + break; 59 + } 60 + } 61 + } 62 + } 63 + } 64 + 65 + function update_projection(projection: Projection, aspect: number) { 66 + switch (projection.Kind) { 67 + case ProjectionKind.Perspective: { 68 + resize_perspective(projection, aspect); 69 + break; 70 + } 71 + case ProjectionKind.Ortho: 72 + resize_ortho(projection, aspect); 73 + break; 15 74 } 16 75 }