aboutsummaryrefslogtreecommitdiff
path: root/web/pw-frontend/src/lib/visualizer/index.ts
diff options
context:
space:
mode:
authorIlion Beyst <ilion.beyst@gmail.com>2021-12-29 21:24:57 +0100
committerIlion Beyst <ilion.beyst@gmail.com>2021-12-29 21:25:29 +0100
commit0c6d978442b244ca3f29c1ffdd44b5007ae7ad93 (patch)
treebaae5fa459a49ecd362e548e0649e2f58c669a70 /web/pw-frontend/src/lib/visualizer/index.ts
parent3eeaab6cec70e7a06a99a1ac2662974f71064bee (diff)
downloadplanetwars.dev-0c6d978442b244ca3f29c1ffdd44b5007ae7ad93.tar.xz
planetwars.dev-0c6d978442b244ca3f29c1ffdd44b5007ae7ad93.zip
separate out visualizer library
Diffstat (limited to 'web/pw-frontend/src/lib/visualizer/index.ts')
-rw-r--r--web/pw-frontend/src/lib/visualizer/index.ts666
1 files changed, 0 insertions, 666 deletions
diff --git a/web/pw-frontend/src/lib/visualizer/index.ts b/web/pw-frontend/src/lib/visualizer/index.ts
deleted file mode 100644
index 363a1c5..0000000
--- a/web/pw-frontend/src/lib/visualizer/index.ts
+++ /dev/null
@@ -1,666 +0,0 @@
-import { Game } from "planetwars-rs";
-// import { memory } from "planetwars-rs/planetwars_rs_bg";
-// const memory = planetwars_bg.memory;
-import type { Dictionary } from './webgl/util';
-import type { BBox } from "./voronoi/voronoi-core";
-
-import {
- Resizer,
- resizeCanvasToDisplaySize,
- FPSCounter,
- url_to_mesh,
- Mesh,
-} from "./webgl/util";
-import {
- Shader,
- Uniform4f,
- Uniform3fv,
- Uniform1f,
- Uniform2f,
- ShaderFactory,
- Uniform3f,
- UniformMatrix3fv,
- UniformBool,
-} from "./webgl/shader";
-import { Renderer } from "./webgl/renderer";
-import { VertexBuffer, IndexBuffer } from "./webgl/buffer";
-import { VertexBufferLayout, VertexArray } from "./webgl/vertexBufferLayout";
-import { defaultLabelFactory, LabelFactory, Align, Label } from "./webgl/text";
-import { VoronoiBuilder } from "./voronoi/voronoi";
-
-// svg-mesh requires global to exist
-(window as any).global = window;
-
-
-
-function to_bbox(box: number[]): BBox {
- return {
- xl: box[0],
- xr: box[0] + box[2],
- yt: box[1],
- yb: box[1] + box[3],
- };
-}
-
-// function f32v(ptr: number, size: number): Float32Array {
-// return new Float32Array(memory.buffer, ptr, size);
-// }
-
-// function i32v(ptr: number, size: number): Int32Array {
-// return new Int32Array(memory.buffer, ptr, size);
-// }
-
-export function set_game_name(name: string) {
- ELEMENTS["name"].innerHTML = name;
-}
-
-export function set_loading(loading: boolean) {
- if (loading) {
- if (!ELEMENTS["main"].classList.contains("loading")) {
- ELEMENTS["main"].classList.add("loading");
- }
- } else {
- ELEMENTS["main"].classList.remove("loading");
- }
-}
-
-const ELEMENTS: any = {};
-var CANVAS: any;
-var RESOLUTION: any;
-var GL: any;
-var ms_per_frame: any;
-
-const LAYERS = {
- vor: -1, // Background
- planet: 1,
- planet_label: 2,
- ship: 3,
- ship_label: 4,
-};
-
-const COUNTER = new FPSCounter();
-
-
-
-export function init() {
- [
- "name",
- "turnCounter",
- "main",
- "turnSlider",
- "fileselect",
- "speed",
- "canvas",
- ].forEach((n) => (ELEMENTS[n] = document.getElementById(n)));
-
- CANVAS = ELEMENTS["canvas"];
- RESOLUTION = [CANVAS.width, CANVAS.height];
-
- ms_per_frame = parseInt(ELEMENTS["speed"].value);
-
- GL = CANVAS.getContext("webgl");
-
- GL.clearColor(0, 0, 0, 1);
- GL.clear(GL.COLOR_BUFFER_BIT);
-
- GL.enable(GL.BLEND);
- GL.blendFunc(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA);
-
- window.addEventListener(
- "resize",
- function () {
- resizeCanvasToDisplaySize(CANVAS);
-
- if (game_instance) {
- game_instance.on_resize();
- }
- },
- { capture: false, passive: true }
- );
-
- ELEMENTS["turnSlider"].oninput = function () {
- if (game_instance) {
- game_instance.updateTurn(parseInt(ELEMENTS["turnSlider"].value));
- }
- };
-
- ELEMENTS["speed"].onchange = function () {
- ms_per_frame = parseInt(ELEMENTS["speed"].value);
- };
-}
-
-export class GameInstance {
- resizer: Resizer;
- game: Game;
-
- shader: Shader;
- vor_shader: Shader;
- image_shader: Shader;
-
- text_factory: LabelFactory;
- planet_labels: Label[];
- ship_labels: Label[];
-
- ship_ibo: IndexBuffer;
- ship_vao: VertexArray;
- // TODO: find a better way
- max_num_ships: number;
-
- renderer: Renderer;
- planet_count: number;
-
- vor_builder: VoronoiBuilder;
-
- vor_counter = 3;
- use_vor = true;
- playing = true;
- time_stopped_delta = 0;
- last_time = 0;
- frame = -1;
-
- turn_count = 0;
-
- constructor(
- game: Game,
- meshes: Mesh[],
- ship_mesh: Mesh,
- shaders: Dictionary<ShaderFactory>
- ) {
- this.game = game;
- const planets = game.get_planets();
- this.planet_count = planets.length;
-
- this.shader = shaders["normal"].create_shader(GL, {
- MAX_CIRCLES: "" + planets.length,
- });
- this.image_shader = shaders["image"].create_shader(GL);
- this.vor_shader = shaders["vor"].create_shader(GL, {
- PLANETS: "" + planets.length,
- });
-
- this.text_factory = defaultLabelFactory(GL, this.image_shader);
- this.planet_labels = [];
- this.ship_labels = [];
-
- this.resizer = new Resizer(CANVAS, [...game.get_viewbox()], true);
- this.renderer = new Renderer();
- this.game.update_turn(0);
-
- // Setup key handling
- document.addEventListener("keydown", this.handleKey.bind(this));
-
- // List of [(x, y, r)] for all planets
- this._create_voronoi(planets);
- this._create_planets(planets, meshes);
-
- // create_shipes
- this.ship_ibo = new IndexBuffer(GL, ship_mesh.cells);
- const ship_positions = new VertexBuffer(GL, ship_mesh.positions);
- const ship_layout = new VertexBufferLayout();
- ship_layout.push(GL.FLOAT, 3, 4, "a_position");
- this.ship_vao = new VertexArray();
- this.ship_vao.addBuffer(ship_positions, ship_layout);
- this.max_num_ships = 0;
-
- // Set slider correctly
- this.turn_count = game.turn_count();
- ELEMENTS["turnSlider"].max = this.turn_count - 1 + "";
- }
-
- push_state(state: string) {
- this.game.push_state(state);
-
- if (this.frame == this.turn_count - 1) {
- this.playing = true;
- }
-
- // Set slider correctly
- this.turn_count = this.game.turn_count();
- this.updateTurnCounters();
- }
-
- _create_voronoi(planets: Float32Array) {
- const planet_points = [];
- for (let i = 0; i < planets.length; i += 3) {
- planet_points.push({ x: -planets[i], y: -planets[i + 1] });
- }
-
- const bbox = to_bbox(this.resizer.get_viewbox());
-
- this.vor_builder = new VoronoiBuilder(
- GL,
- this.vor_shader,
- planet_points,
- bbox
- );
- this.renderer.addRenderable(this.vor_builder.getRenderable(), LAYERS.vor);
- }
-
- _create_planets(planets: Float32Array, meshes: Mesh[]) {
- for (let i = 0; i < this.planet_count; i++) {
- {
- const transform = new UniformMatrix3fv([
- 1,
- 0,
- 0,
- 0,
- 1,
- 0,
- -planets[i * 3],
- -planets[i * 3 + 1],
- 1,
- ]);
-
- const indexBuffer = new IndexBuffer(
- GL,
- meshes[i % meshes.length].cells
- );
- const positionBuffer = new VertexBuffer(
- GL,
- meshes[i % meshes.length].positions
- );
-
- const layout = new VertexBufferLayout();
- layout.push(GL.FLOAT, 3, 4, "a_position");
- const vao = new VertexArray();
- vao.addBuffer(positionBuffer, layout);
-
- this.renderer.addToDraw(
- indexBuffer,
- vao,
- this.shader,
- {
- u_trans: transform,
- u_trans_next: transform,
- },
- [],
- LAYERS.planet
- );
- }
-
- {
- const transform = new UniformMatrix3fv([
- 1,
- 0,
- 0,
- 0,
- 1,
- 0,
- -planets[i * 3],
- -planets[i * 3 + 1] - 1.2,
- 1,
- ]);
-
- const label = this.text_factory.build(GL, transform);
- this.planet_labels.push(label);
- this.renderer.addRenderable(label.getRenderable(), LAYERS.planet_label);
- }
- }
- }
-
- on_resize() {
- this.resizer = new Resizer(CANVAS, [...this.game.get_viewbox()], true);
- const bbox = to_bbox(this.resizer.get_viewbox());
- this.vor_builder.resize(GL, bbox);
- }
-
- _update_state() {
- this._update_planets();
- this._update_ships();
- }
-
- _update_planets() {
- const colours = this.game.get_planet_colors();
- const planet_ships = this.game.get_planet_ships();
-
- this.vor_shader.uniform(GL, "u_planet_colours", new Uniform3fv(colours));
-
- for (let i = 0; i < this.planet_count; i++) {
- const u = new Uniform3f(
- colours[i * 6],
- colours[i * 6 + 1],
- colours[i * 6 + 2]
- );
- this.renderer.updateUniform(
- i,
- (us) => (us["u_color"] = u),
- LAYERS.planet
- );
- const u2 = new Uniform3f(
- colours[i * 6 + 3],
- colours[i * 6 + 4],
- colours[i * 6 + 5]
- );
- this.renderer.updateUniform(
- i,
- (us) => (us["u_color_next"] = u2),
- LAYERS.planet
- );
-
- this.planet_labels[i].setText(
- GL,
- "*" + planet_ships[i],
- Align.Middle,
- Align.Begin
- );
- }
- }
-
- _update_ships() {
- const ships = this.game.get_ship_locations();
- const labels = this.game.get_ship_label_locations();
- const ship_counts = this.game.get_ship_counts();
- const ship_colours = this.game.get_ship_colours();
-
- for (let i = this.max_num_ships; i < ship_counts.length; i++) {
- this.renderer.addToDraw(
- this.ship_ibo,
- this.ship_vao,
- this.shader,
- {},
- [],
- LAYERS.ship
- );
-
- const label = this.text_factory.build(GL);
- this.ship_labels.push(label);
- this.renderer.addRenderable(label.getRenderable(), LAYERS.ship_label);
- }
- if (ship_counts.length > this.max_num_ships)
- this.max_num_ships = ship_counts.length;
-
- // TODO: actually remove obsolete ships
- for (let i = 0; i < this.max_num_ships; i++) {
- if (i < ship_counts.length) {
- this.ship_labels[i].setText(
- GL,
- "" + ship_counts[i],
- Align.Middle,
- Align.Middle
- );
-
- this.renderer.enableRenderable(i, LAYERS.ship);
- this.renderer.enableRenderable(i, LAYERS.ship_label);
-
- const u = new Uniform3f(
- ship_colours[i * 3],
- ship_colours[i * 3 + 1],
- ship_colours[i * 3 + 2]
- );
-
- const t1 = new UniformMatrix3fv(ships.slice(i * 18, i * 18 + 9));
- const t2 = new UniformMatrix3fv(ships.slice(i * 18 + 9, i * 18 + 18));
-
- const tl1 = new UniformMatrix3fv(labels.slice(i * 18, i * 18 + 9));
- const tl2 = new UniformMatrix3fv(labels.slice(i * 18 + 9, i * 18 + 18));
-
- this.renderer.updateUniform(
- i,
- (us) => {
- us["u_color"] = u;
- us["u_color_next"] = u;
- us["u_trans"] = t1;
- us["u_trans_next"] = t2;
- },
- LAYERS.ship
- );
-
- this.renderer.updateUniform(
- i,
- (us) => {
- us["u_trans"] = tl1;
- us["u_trans_next"] = tl2;
- },
- LAYERS.ship_label
- );
- } else {
- this.renderer.disableRenderable(i, LAYERS.ship);
- this.renderer.disableRenderable(i, LAYERS.ship_label);
- }
- }
- }
-
- render(time: number) {
- COUNTER.frame(time);
-
- if (COUNTER.delta(time) < 30) {
- this.vor_counter = Math.min(3, this.vor_counter + 1);
- } else {
- this.vor_counter = Math.max(-3, this.vor_counter - 1);
- }
-
- if (this.vor_counter < -2) {
- this.use_vor = false;
- }
-
- // If not playing, still reder with different viewbox, so people can still pan etc.
- if (!this.playing) {
- this.last_time = time;
-
- this.shader.uniform(
- GL,
- "u_viewbox",
- new Uniform4f(this.resizer.get_viewbox())
- );
- this.vor_shader.uniform(
- GL,
- "u_viewbox",
- new Uniform4f(this.resizer.get_viewbox())
- );
- this.image_shader.uniform(
- GL,
- "u_viewbox",
- new Uniform4f(this.resizer.get_viewbox())
- );
-
- this.renderer.render(GL);
- return;
- }
-
- // Check if turn is still correct
- if (time > this.last_time + ms_per_frame) {
- this.last_time = time;
- this.updateTurn(this.frame + 1);
- if (this.frame == this.turn_count - 1) {
- this.playing = false;
- }
- }
-
- // Do GL things
- GL.bindFramebuffer(GL.FRAMEBUFFER, null);
- GL.viewport(0, 0, GL.canvas.width, GL.canvas.height);
- GL.clear(GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT);
-
- this.vor_shader.uniform(
- GL,
- "u_time",
- new Uniform1f((time - this.last_time) / ms_per_frame)
- );
- this.vor_shader.uniform(
- GL,
- "u_viewbox",
- new Uniform4f(this.resizer.get_viewbox())
- );
- this.vor_shader.uniform(GL, "u_resolution", new Uniform2f(RESOLUTION));
- this.vor_shader.uniform(GL, "u_vor", new UniformBool(this.use_vor));
-
- this.shader.uniform(
- GL,
- "u_time",
- new Uniform1f((time - this.last_time) / ms_per_frame)
- );
- this.shader.uniform(
- GL,
- "u_mouse",
- new Uniform2f(this.resizer.get_mouse_pos())
- );
- this.shader.uniform(
- GL,
- "u_viewbox",
- new Uniform4f(this.resizer.get_viewbox())
- );
- this.shader.uniform(GL, "u_resolution", new Uniform2f(RESOLUTION));
-
- this.image_shader.uniform(
- GL,
- "u_time",
- new Uniform1f((time - this.last_time) / ms_per_frame)
- );
- this.image_shader.uniform(
- GL,
- "u_mouse",
- new Uniform2f(this.resizer.get_mouse_pos())
- );
- this.image_shader.uniform(
- GL,
- "u_viewbox",
- new Uniform4f(this.resizer.get_viewbox())
- );
- this.image_shader.uniform(GL, "u_resolution", new Uniform2f(RESOLUTION));
-
- // Render
- this.renderer.render(GL);
-
- COUNTER.frame_end();
- }
-
- updateTurn(turn: number) {
- this.frame = Math.max(0, turn);
- const new_frame = this.game.update_turn(this.frame);
- if (new_frame < this.frame) {
- this.frame = new_frame;
- this.playing = false;
- } else {
- this._update_state();
- this.playing = true;
- }
-
- this.updateTurnCounters();
- }
-
- updateTurnCounters() {
- ELEMENTS["turnCounter"].innerHTML =
- this.frame + " / " + (this.turn_count - 1);
- ELEMENTS["turnSlider"].value = this.frame + "";
- ELEMENTS["turnSlider"].max = this.turn_count - 1 + "";
- }
-
- handleKey(event: KeyboardEvent) {
- // Space
- if (event.keyCode == 32) {
- if (this.playing) {
- this.playing = false;
- } else {
- this.playing = true;
- }
- }
-
- // Arrow left
- if (event.keyCode == 37) {
- // This feels more natural than -1 what it should be, I think
- this.updateTurn(this.frame - 2);
- }
-
- // Arrow right
- if (event.keyCode == 39) {
- this.updateTurn(this.frame + 1);
- }
-
- // d key
- if (event.keyCode == 68) {
- ELEMENTS["speed"].value = ms_per_frame + 10 + "";
- ELEMENTS["speed"].onchange(undefined);
- }
-
- // a key
- if (event.keyCode == 65) {
- ELEMENTS["speed"].value = Math.max(ms_per_frame - 10, 0) + "";
- ELEMENTS["speed"].onchange(undefined);
- }
- }
-}
-
-var game_instance: GameInstance;
-var meshes: Mesh[];
-var shaders: Dictionary<ShaderFactory>;
-
-export async function set_instance(source: string): Promise<GameInstance> {
- if (!meshes || !shaders) {
- const mesh_promises = ["ship.svg", "earth.svg", "mars.svg", "venus.svg"]
- .map((name) => "/static/res/assets/" + name)
- .map(url_to_mesh);
-
- const shader_promies = [
- (async () =>
- <[string, ShaderFactory]>[
- "normal",
- await ShaderFactory.create_factory(
- "/static/shaders/frag/simple.glsl",
- "/static/shaders/vert/simple.glsl"
- ),
- ])(),
- (async () =>
- <[string, ShaderFactory]>[
- "vor",
- await ShaderFactory.create_factory(
- "/static/shaders/frag/vor.glsl",
- "/static/shaders/vert/vor.glsl"
- ),
- ])(),
- (async () =>
- <[string, ShaderFactory]>[
- "image",
- await ShaderFactory.create_factory(
- "/static/shaders/frag/image.glsl",
- "/static/shaders/vert/simple.glsl"
- ),
- ])(),
- ];
- let shaders_array: [string, ShaderFactory][];
- [meshes, shaders_array] = await Promise.all([
- Promise.all(mesh_promises),
- Promise.all(shader_promies),
- ]);
-
- shaders = {};
- shaders_array.forEach(([name, fac]) => (shaders[name] = fac));
- }
-
- resizeCanvasToDisplaySize(CANVAS);
-
- game_instance = new GameInstance(
- Game.new(source),
- meshes.slice(1),
- meshes[0],
- shaders
- );
-
- set_loading(false);
- start();
- return game_instance;
-}
-
-var _animating = false;
-
-export function start() {
- if (_animating) {
- // already running
- return;
- }
- _animating = true;
- requestAnimationFrame(step);
-}
-
-export function stop() {
- _animating = false;
-}
-
-function step(time: number) {
- if (game_instance) {
- game_instance.render(time);
- }
-
- if (_animating) {
- requestAnimationFrame(step);
- }
-}