planet-wars/frontend/www/index.ts

254 lines
8.2 KiB
TypeScript
Raw Normal View History

2019-09-17 12:03:16 +00:00
import { Game } from "planetwars";
2019-09-17 16:27:44 +00:00
import { memory } from "planetwars/plantwars_bg";
2019-09-19 15:52:48 +00:00
import { Resizer, resizeCanvasToDisplaySize, FPSCounter, url_to_mesh, Mesh } from "./webgl/util";
import { Shader, Uniform4f, Uniform2fv, Uniform3fv, Uniform1i, Uniform1f, Uniform2f, ShaderFactory, Uniform3f, UniformMatrix3fv } from './webgl/shader';
2019-09-17 18:19:04 +00:00
import { Renderer } from "./webgl/renderer";
import { VertexBuffer, IndexBuffer } from "./webgl/buffer";
import { VertexBufferLayout, VertexArray } from "./webgl/vertexBufferLayout";
import { callbackify } from "util";
2019-09-17 16:27:44 +00:00
2019-09-18 10:29:56 +00:00
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);
}
2019-09-17 18:19:04 +00:00
const COUNTER = new FPSCounter();
const LOADER = document.getElementById("loader");
function set_loading(loading: boolean) {
if (loading) {
if (!LOADER.classList.contains("loading")) {
LOADER.classList.add("loading");
}
} else {
LOADER.classList.remove("loading");
}
}
const URL = window.location.origin+window.location.pathname;
const LOCATION = URL.substring(0, URL.lastIndexOf("/") + 1);
2019-09-17 16:27:44 +00:00
const CANVAS = <HTMLCanvasElement>document.getElementById("c");
2019-09-17 18:19:04 +00:00
const RESOLUTION = [CANVAS.width, CANVAS.height];
const GL = CANVAS.getContext("webgl");
2019-09-19 15:52:48 +00:00
2019-09-17 18:19:04 +00:00
resizeCanvasToDisplaySize(<HTMLCanvasElement>GL.canvas);
GL.viewport(0, 0, GL.canvas.width, GL.canvas.height);
GL.clearColor(0, 0, 0, 0);
GL.clear(GL.COLOR_BUFFER_BIT);
GL.enable(GL.BLEND);
GL.blendFunc(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA);
var SHADERFACOTRY: ShaderFactory;
ShaderFactory.create_factory(
LOCATION + "static/shaders/frag/simple.glsl", LOCATION + "static/shaders/vert/simple.glsl"
).then((e) => SHADERFACOTRY = e);
2019-09-17 16:27:44 +00:00
2019-09-20 15:48:00 +00:00
2019-09-17 18:19:04 +00:00
class GameInstance {
resizer: Resizer;
game: Game;
shader: Shader;
renderer: Renderer;
2019-09-18 10:29:56 +00:00
planet_count: number;
2019-09-21 08:17:35 +00:00
playing = true; // 0 is paused, 1 is playing but not rerendered, 2 is playing and rerendered
time_stopped_delta = 0;
2019-09-18 10:29:56 +00:00
last_time = 0;
frame = -1;
2019-09-17 18:19:04 +00:00
2019-09-20 15:48:00 +00:00
ship_indices: number[];
constructor(game: Game, meshes: Mesh[], ship_mesh: Mesh) {
2019-09-17 18:19:04 +00:00
this.game = game;
2019-09-18 10:29:56 +00:00
this.planet_count = this.game.get_planet_count();
this.shader = SHADERFACOTRY.create_shader(GL, {"MAX_CIRCLES": ''+this.planet_count});
this.resizer = new Resizer(CANVAS, [...f32v(game.get_viewbox(), 4)], true);
2019-09-17 18:19:04 +00:00
this.renderer = new Renderer();
2019-09-19 16:07:46 +00:00
this.game.update_turn(0);
2019-09-19 15:52:48 +00:00
2019-09-21 08:17:35 +00:00
// Setup key handling
document.addEventListener('keydown', this.handleKey.bind(this));
2019-09-19 15:52:48 +00:00
const planets = f32v(game.get_planets(), this.planet_count * 3);
for(let i=0; i < this.planet_count; i++){
const transform = new UniformMatrix3fv([
1, 0, 0,
0, 1, 0,
2019-09-20 17:31:32 +00:00
-planets[i*3], -planets[i*3+1], 1,
2019-09-19 15:52:48 +00:00
]);
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,
2019-09-20 15:48:00 +00:00
"u_trans_next": transform,
2019-09-19 15:52:48 +00:00
}
2019-09-20 15:48:00 +00:00
);
2019-09-19 15:52:48 +00:00
}
2019-09-18 10:29:56 +00:00
2019-09-20 15:48:00 +00:00
this.ship_indices = [];
const 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");
const ship_vao = new VertexArray();
ship_vao.addBuffer(ship_positions, ship_layout);
for (let i = 0; i < this.game.get_max_ships(); i++) {
this.ship_indices.push(
this.renderer.addToDraw(
ship_ibo,
ship_vao,
this.shader,
{}
)
);
}
2019-09-17 18:19:04 +00:00
}
2019-09-21 08:32:57 +00:00
_update_state() {
const colours = f32v(this.game.get_planet_colors(), this.planet_count * 6);
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);
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);
}
const ships = f32v(this.game.get_ship_locations(), this.game.get_ship_count() * 9 * 2);
const ship_colours = f32v(this.game.get_ship_colours(), this.game.get_ship_count() * 3);
for (let i=0; i < this.game.get_max_ships(); i++) {
const index = this.ship_indices[i];
if (i < this.game.get_ship_count()) {
this.renderer.enableRendershit(index);
const u = new Uniform3f(ship_colours[i*3], ship_colours[i*3 + 1], ship_colours[i*3 + 2]);
// const t1 = new UniformMatrix3fv(new Float32Array(ships, i * 18, 9));
// const t2 = new UniformMatrix3fv(new Float32Array(ships, i * 18 + 9, 9));
const t1 = new UniformMatrix3fv(ships.slice(i * 18, i * 18 + 9));
const t2 = new UniformMatrix3fv(ships.slice(i * 18 + 9, i * 18 + 18));
this.renderer.updateUniform(index, (us) => {
us["u_color"] = u;
us["u_color_next"] = u;
us["u_trans"] = t1;
us["u_trans_next"] = t2;
});
} else {
this.renderer.disableRenderShift(index);
}
}
}
2019-09-17 18:19:04 +00:00
render(time: number) {
2019-09-21 08:17:35 +00:00
COUNTER.frame(time);
if (!this.playing) {
this.last_time = time;
return;
}
2019-09-20 15:48:00 +00:00
if (time > this.last_time + 500) {
2019-09-18 10:29:56 +00:00
this.last_time = time;
2019-09-21 08:32:57 +00:00
this.updateTurn(this.frame + 1);
2019-09-19 15:52:48 +00:00
}
2019-09-20 15:48:00 +00:00
2019-09-19 15:52:48 +00:00
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);
2019-09-17 18:19:04 +00:00
2019-09-20 15:48:00 +00:00
this.shader.uniform(GL, "u_time", new Uniform1f((time - this.last_time) / 500));
2019-09-17 18:19:04 +00:00
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));
2019-09-21 08:17:35 +00:00
this.shader.uniform(GL, "u_animated", new Uniform1i(+this.playing));
2019-09-17 18:19:04 +00:00
this.renderer.render(GL);
2019-09-21 08:17:35 +00:00
}
2019-09-19 15:52:48 +00:00
2019-09-21 08:32:57 +00:00
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();
}
}
2019-09-21 08:17:35 +00:00
handleKey(event: KeyboardEvent) {
console.log(event.keyCode);
console.log(event.key);
// Space
if (event.keyCode == 32) {
if (this.playing) {
this.playing = false;
} else {
this.playing = true;
}
2019-09-21 08:32:57 +00:00
}
// Arrow left
if (event.keyCode == 37) {
// This feels more natural than -1 what it should be, I think
this.updateTurn(this.frame - 2);
}
2019-09-21 08:17:35 +00:00
2019-09-21 08:32:57 +00:00
// Arrow right
if (event.keyCode == 39) {
this.updateTurn(this.frame + 1);
2019-09-21 08:17:35 +00:00
}
2019-09-17 18:19:04 +00:00
}
}
var game_instance: GameInstance;
2019-09-19 15:52:48 +00:00
export async function set_instance(game: Game) {
const meshes = await Promise.all(
2019-09-20 15:48:00 +00:00
["ship.svg", "earth.svg", "mars.svg", "venus.svg"].map(
2019-09-19 15:52:48 +00:00
(name) => "static/res/assets/" + name
).map(url_to_mesh)
);
2019-09-20 15:48:00 +00:00
game_instance = new GameInstance(game, meshes.slice(1), meshes[0]);
2019-09-17 12:03:16 +00:00
}
2019-09-17 18:19:04 +00:00
function step(time: number) {
if (game_instance) {
set_loading(false);
2019-09-20 15:48:00 +00:00
game_instance.render(time);
2019-09-17 18:19:04 +00:00
} else {
set_loading(true);
}
requestAnimationFrame(step);
}
set_loading(true);
requestAnimationFrame(step);