Frontend/visualiser: enhancement show ship_count in visualiser (#23)
* make all fleets fly forward * make things beautiful
This commit is contained in:
parent
33abe9515f
commit
998cb3d535
13 changed files with 518 additions and 193 deletions
|
@ -14,6 +14,12 @@ mod types;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
macro_rules! console_log {
|
||||||
|
// Note that this is using the `log` function imported above during
|
||||||
|
// `bare_bones`
|
||||||
|
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
|
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
|
||||||
// allocator.
|
// allocator.
|
||||||
#[cfg(feature = "wee_alloc")]
|
#[cfg(feature = "wee_alloc")]
|
||||||
|
@ -25,11 +31,17 @@ pub struct Circle {
|
||||||
r: f32,
|
r: f32,
|
||||||
x: f32,
|
x: f32,
|
||||||
y: f32,
|
y: f32,
|
||||||
a1: f32,
|
a0: f32,
|
||||||
a2: f32,
|
ad: f32,
|
||||||
distance: usize,
|
distance: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use std::f32::consts::PI;
|
||||||
|
fn spr(from: f32) -> f32 {
|
||||||
|
let pi2 = PI*2.;
|
||||||
|
((from % pi2) + pi2) % pi2
|
||||||
|
}
|
||||||
|
|
||||||
impl Circle {
|
impl Circle {
|
||||||
pub fn new(p1: &types::Planet, p2: &types::Planet) -> Self {
|
pub fn new(p1: &types::Planet, p2: &types::Planet) -> Self {
|
||||||
let x1 = p1.x;
|
let x1 = p1.x;
|
||||||
|
@ -37,101 +49,55 @@ impl Circle {
|
||||||
let x2 = p2.x;
|
let x2 = p2.x;
|
||||||
let y2 = p2.y;
|
let y2 = p2.y;
|
||||||
|
|
||||||
|
// Distance between planets
|
||||||
let q = ((x2-x1).powi(2) + (y2-y1).powi(2)).sqrt();
|
let q = ((x2-x1).powi(2) + (y2-y1).powi(2)).sqrt();
|
||||||
|
// Center of between planets
|
||||||
let x3 = (x1+x2)/2.0;
|
let x3 = (x1+x2)/2.0;
|
||||||
let y3 = (y1+y2)/2.0;
|
let y3 = (y1+y2)/2.0;
|
||||||
|
|
||||||
let r = q * 1.1;
|
// Radius of circle
|
||||||
|
let r = q * 1.0;
|
||||||
|
|
||||||
let mut x = x3 + (r.powi(2)-(q/2.0).powi(2)).sqrt() * (y1-y2)/q;
|
// Center of circle
|
||||||
let mut y = y3 + (r.powi(2)-(q/2.0).powi(2)).sqrt() * (x2-x1)/q;
|
let x = x3 + (r.powi(2)-(q/2.0).powi(2)).sqrt() * (y1-y2)/q;
|
||||||
|
let y = y3 + (r.powi(2)-(q/2.0).powi(2)).sqrt() * (x2-x1)/q;
|
||||||
|
// console_log!("{},{} -> {},{} ({},{} r={})", x1, y1, x2, y2, x, y, r);
|
||||||
|
|
||||||
|
let a0 = spr((y - y1).atan2(x - x1));
|
||||||
|
let a2 = spr((y - y2).atan2(x - x2));
|
||||||
|
|
||||||
let mut a1 = (y - y1).atan2(x - x1);
|
let mut ad = spr(a0 - a2);
|
||||||
let mut a2 = (y - y2).atan2(x - x2);
|
if ad > PI {
|
||||||
|
ad = spr(a2 - a0);
|
||||||
if a2 < a1 {
|
|
||||||
|
|
||||||
x = x3 - (r.powi(2)-(q/2.0).powi(2)).sqrt() * (y1-y2)/q;
|
|
||||||
y = y3 - (r.powi(2)-(q/2.0).powi(2)).sqrt() * (x2-x1)/q;
|
|
||||||
|
|
||||||
a1 = (y - y1).atan2(x - x1);
|
|
||||||
a2 = (y - y2).atan2(x - x2);
|
|
||||||
}
|
}
|
||||||
|
// console_log!("a1 {} a2 {} ad {}", a0/PI * 180.0, a2/PI * 180.0, ad/PI*180.0);
|
||||||
|
|
||||||
let distance = q.ceil() as usize + 1;
|
let distance = q.ceil() as usize + 1;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
r, x, y, a1, a2, distance
|
r, x, y, a0, ad, distance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_for_remaining(&self, remaining: usize) -> (Mat3<f32>, Mat3<f32>) {
|
pub fn get_for_remaining(&self, remaining: usize) -> ((Mat3<f32>, f32), (Mat3<f32>, f32)) {
|
||||||
(
|
(
|
||||||
self.get_remaining(remaining),
|
self.get_remaining(remaining),
|
||||||
self.get_remaining((remaining + 1).min(self.distance - 1)),
|
self.get_remaining((remaining + 1).min(self.distance - 1)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_remaining(&self, remaining: usize) -> Mat3<f32> {
|
fn get_remaining(&self, remaining: usize) -> (Mat3<f32>, f32) {
|
||||||
let alpha = (self.a1 * remaining as f32 + (self.distance - remaining) as f32 * self.a2) / self.distance as f32;
|
let alpha = self.a0 + (1.0 - (remaining as f32 / self.distance as f32)) * self.ad;
|
||||||
|
|
||||||
let cos = alpha.cos();
|
let cos = alpha.cos();
|
||||||
let sin = alpha.sin();
|
let sin = alpha.sin();
|
||||||
Mat3::new(
|
(Mat3::new(
|
||||||
0.3, 0.0, 0.0,
|
0.3, 0.0, 0.0,
|
||||||
0.0, 0.4, 0.0,
|
0.0, 0.3, 0.0,
|
||||||
-self.x + cos * self.r, -self.y + sin * self.r, 0.3,
|
-self.x + cos * self.r, -self.y + sin * self.r, 0.3,
|
||||||
) * Mat3::rotate_z(alpha)
|
), alpha)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// struct Line {
|
|
||||||
// x1: f32,
|
|
||||||
// y1: f32,
|
|
||||||
// x2: f32,
|
|
||||||
// y2: f32,
|
|
||||||
// a: f32,
|
|
||||||
// d: usize,
|
|
||||||
// }
|
|
||||||
// impl Line {
|
|
||||||
// pub fn new(p1: &types::Planet, p2: &types::Planet) -> Self {
|
|
||||||
// let dx = p1.x - p2.x;
|
|
||||||
// let dy = p1.y - p2.y;
|
|
||||||
// let a = dy.atan2(dx);
|
|
||||||
// // let a = (dy / dx).atan();
|
|
||||||
// let d = (dx * dx + dy * dy).sqrt().ceil() as usize + 1;
|
|
||||||
|
|
||||||
// Self {
|
|
||||||
// x1: p1.x,
|
|
||||||
// x2: p2.x,
|
|
||||||
// y1: p1.y,
|
|
||||||
// y2: p2.y,
|
|
||||||
// d, a,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn get_for_remaining(&self, remaining: usize) -> (Mat3<f32>, Mat3<f32>) {
|
|
||||||
// (
|
|
||||||
// self.get_remaining(remaining),
|
|
||||||
// self.get_remaining((remaining + 1).min(self.d - 1)),
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn get_remaining(&self, remaining: usize) -> Mat3<f32> {
|
|
||||||
// let x = (self.x1 * remaining as f32 + (self.d - remaining) as f32 * self.x2) / self.d as f32;
|
|
||||||
// // let x = self.x1 + (remaining as f32 / self.d as f32) * (self.x2 - self.x1);
|
|
||||||
// let y = (self.y1 * remaining as f32 + (self.d - remaining) as f32 * self.y2) / self.d as f32;
|
|
||||||
|
|
||||||
// // let y = self.y1 + (remaining as f32 / self.d as f32) * (self.y2 - self.y1);
|
|
||||||
// Mat3::new(
|
|
||||||
// 0.3, 0.0, 0.0,
|
|
||||||
// 0.0, 0.3, 0.0,
|
|
||||||
// x, y, 0.3,
|
|
||||||
// ) * Mat3::rotate_z(self.a)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn create_voronoi(planets: &Vec<types::Planet>, bbox: f32) -> (Vec<Vec2<f32>>, Vec<usize>) {
|
fn create_voronoi(planets: &Vec<types::Planet>, bbox: f32) -> (Vec<Vec2<f32>>, Vec<usize>) {
|
||||||
let mut verts: Vec<Vec2<f32>> = planets.iter().map(|p| Vec2::new(p.x, p.y)).collect();
|
let mut verts: Vec<Vec2<f32>> = planets.iter().map(|p| Vec2::new(p.x, p.y)).collect();
|
||||||
let mut ids = Vec::new();
|
let mut ids = Vec::new();
|
||||||
|
@ -173,9 +139,13 @@ pub struct Game {
|
||||||
view_box: Vec<f32>,
|
view_box: Vec<f32>,
|
||||||
|
|
||||||
planets: Vec<Vec3<f32>>,
|
planets: Vec<Vec3<f32>>,
|
||||||
|
planet_ships: Vec<usize>,
|
||||||
|
|
||||||
ship_locations: Vec<[f32;9]>,
|
ship_locations: Vec<[f32;9]>,
|
||||||
|
ship_label_locations: Vec<[f32;9]>,
|
||||||
ship_colours: Vec<Vec3<f32>>,
|
ship_colours: Vec<Vec3<f32>>,
|
||||||
|
ship_counts: Vec<usize>,
|
||||||
|
|
||||||
current_planet_colours: Vec<Vec3<f32>>,
|
current_planet_colours: Vec<Vec3<f32>>,
|
||||||
|
|
||||||
voronoi_vertices: Vec<Vec2<f32>>,
|
voronoi_vertices: Vec<Vec2<f32>>,
|
||||||
|
@ -188,6 +158,8 @@ impl Game {
|
||||||
pub fn new(file: &str) -> Self {
|
pub fn new(file: &str) -> Self {
|
||||||
utils::set_panic_hook();
|
utils::set_panic_hook();
|
||||||
|
|
||||||
|
console_log!("Rust is busy being awesome!");
|
||||||
|
|
||||||
// First line is fucked but we just filter out things that cannot parse
|
// First line is fucked but we just filter out things that cannot parse
|
||||||
let states: Vec<types::State> = file.split("\n").filter_map(|line|
|
let states: Vec<types::State> = file.split("\n").filter_map(|line|
|
||||||
serde_json::from_str(line).ok()
|
serde_json::from_str(line).ok()
|
||||||
|
@ -210,13 +182,16 @@ impl Game {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
planets: utils::get_planets(&states[0].planets, 2.0),
|
planets: utils::get_planets(&states[0].planets, 2.0),
|
||||||
|
planet_ships: Vec::new(),
|
||||||
view_box,
|
view_box,
|
||||||
|
|
||||||
planet_map,
|
planet_map,
|
||||||
turn: 0,
|
turn: 0,
|
||||||
states,
|
states,
|
||||||
ship_locations: Vec::new(),
|
ship_locations: Vec::new(),
|
||||||
|
ship_label_locations: Vec::new(),
|
||||||
ship_colours: Vec::new(),
|
ship_colours: Vec::new(),
|
||||||
|
ship_counts: Vec::new(),
|
||||||
current_planet_colours: Vec::new(),
|
current_planet_colours: Vec::new(),
|
||||||
|
|
||||||
voronoi_vertices,
|
voronoi_vertices,
|
||||||
|
@ -233,6 +208,10 @@ impl Game {
|
||||||
self.planets.as_ptr()
|
self.planets.as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_planet_ships(&self) -> *const usize {
|
||||||
|
self.planet_ships.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_planet_colors(&self) -> *const Vec3<f32> {
|
pub fn get_planet_colors(&self) -> *const Vec3<f32> {
|
||||||
self.current_planet_colours.as_ptr()
|
self.current_planet_colours.as_ptr()
|
||||||
}
|
}
|
||||||
|
@ -248,13 +227,19 @@ impl Game {
|
||||||
pub fn update_turn(&mut self, turn: usize) -> usize {
|
pub fn update_turn(&mut self, turn: usize) -> usize {
|
||||||
self.turn = turn.min(self.states.len() -1);
|
self.turn = turn.min(self.states.len() -1);
|
||||||
|
|
||||||
|
self.update_planet_ships();
|
||||||
self.update_planet_colours();
|
self.update_planet_colours();
|
||||||
self.update_voronoi_colors();
|
self.update_voronoi_colors();
|
||||||
self.update_ship_locations();
|
self.update_ship_locations();
|
||||||
|
self.update_ship_counts();
|
||||||
|
|
||||||
self.turn
|
self.turn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_planet_ships(&mut self) {
|
||||||
|
self.planet_ships = self.states[self.turn].planets.iter().map(|p| p.ship_count as usize).collect();
|
||||||
|
}
|
||||||
|
|
||||||
fn update_voronoi_colors(&mut self) {
|
fn update_voronoi_colors(&mut self) {
|
||||||
for (i, p) in self.states[self.turn].planets.iter().enumerate() {
|
for (i, p) in self.states[self.turn].planets.iter().enumerate() {
|
||||||
self.voronoi_colors[i] = utils::COLORS[p.owner.unwrap_or(0) as usize % utils::COLORS.len()].into()
|
self.voronoi_colors[i] = utils::COLORS[p.owner.unwrap_or(0) as usize % utils::COLORS.len()].into()
|
||||||
|
@ -279,19 +264,32 @@ impl Game {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_ship_locations(&mut self) {
|
fn update_ship_locations(&mut self) {
|
||||||
let mut new_vec = Vec::new();
|
self.ship_locations = Vec::new();
|
||||||
|
self.ship_label_locations = Vec::new();
|
||||||
|
let t = Mat3::new(0.2, 0., 0.,
|
||||||
|
0., 0.2, 0.0,
|
||||||
|
0., -0.5, 0.2);
|
||||||
|
|
||||||
for ship in self.states[self.turn].expeditions.iter() {
|
for ship in self.states[self.turn].expeditions.iter() {
|
||||||
let (o1, o2) = self.planet_map.get(&(ship.origin.clone(), ship.destination.clone())).unwrap().get_for_remaining(ship.turns_remaining as usize);
|
let ((o1, a1), (o2, a2)) = self.planet_map.get(&(ship.origin.clone(), ship.destination.clone())).unwrap().get_for_remaining(ship.turns_remaining as usize);
|
||||||
new_vec.push(o1.to_array());
|
self.ship_locations.push((o1 * Mat3::rotate_z(a1)).to_array());
|
||||||
new_vec.push(o2.to_array());
|
self.ship_locations.push((o2 * Mat3::rotate_z(a2)).to_array());
|
||||||
|
|
||||||
|
self.ship_label_locations.push((o1 + t).to_array());
|
||||||
|
self.ship_label_locations.push((o2 + t).to_array());
|
||||||
}
|
}
|
||||||
self.ship_locations = new_vec;
|
|
||||||
|
|
||||||
self.ship_colours = self.states[self.turn].expeditions.iter().map(|s| {
|
self.ship_colours = self.states[self.turn].expeditions.iter().map(|s| {
|
||||||
utils::COLORS[s.owner as usize % utils::COLORS.len()].into()
|
utils::COLORS[s.owner as usize % utils::COLORS.len()].into()
|
||||||
}).collect();
|
}).collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_ship_counts(&mut self) {
|
||||||
|
self.ship_counts = self.states[self.turn].expeditions.iter().map(|s| {
|
||||||
|
s.ship_count as usize
|
||||||
|
}).collect();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_max_ships(&self) -> usize {
|
pub fn get_max_ships(&self) -> usize {
|
||||||
self.states.iter().map(|s| s.expeditions.len()).max().unwrap()
|
self.states.iter().map(|s| s.expeditions.len()).max().unwrap()
|
||||||
}
|
}
|
||||||
|
@ -304,10 +302,18 @@ impl Game {
|
||||||
self.ship_locations.as_ptr()
|
self.ship_locations.as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_ship_label_locations(&self) -> *const [f32;9] {
|
||||||
|
self.ship_label_locations.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_ship_colours(&self) -> *const Vec3<f32> {
|
pub fn get_ship_colours(&self) -> *const Vec3<f32> {
|
||||||
self.ship_colours.as_ptr()
|
self.ship_colours.as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_ship_counts(&self) -> *const usize {
|
||||||
|
self.ship_counts.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_voronoi_vert_count(&self) -> usize {
|
pub fn get_voronoi_vert_count(&self) -> usize {
|
||||||
self.voronoi_vertices.len()
|
self.voronoi_vertices.len()
|
||||||
}
|
}
|
||||||
|
@ -331,6 +337,8 @@ impl Game {
|
||||||
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern {
|
extern "C" {
|
||||||
fn alert(s: &str);
|
fn alert(s: &str);
|
||||||
|
#[wasm_bindgen(js_namespace = console)]
|
||||||
|
fn log(s: &str);
|
||||||
}
|
}
|
||||||
|
|
14
frontend/www/bootstrap.js
vendored
14
frontend/www/bootstrap.js
vendored
|
@ -2,14 +2,20 @@
|
||||||
// asynchronously. This `bootstrap.js` file does the single async import, so
|
// asynchronously. This `bootstrap.js` file does the single async import, so
|
||||||
// that no one else needs to worry about it again.
|
// that no one else needs to worry about it again.
|
||||||
// Import index.js that executes index.ts
|
// Import index.js that executes index.ts
|
||||||
var h = (_a, _b) => { }
|
var h = (_a, _b) => {}
|
||||||
|
|
||||||
export function handle(loc, e) {
|
export function handle(loc, e) {
|
||||||
h(loc, e);
|
h(loc, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
import("./index.js")
|
if (typeof mergeInto !== 'undefined') mergeInto(LibraryManager.library, {
|
||||||
.then(e => {
|
print: function() {
|
||||||
|
console.log("Hello world");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
import ("./index.js")
|
||||||
|
.then(e => {
|
||||||
h = e.handle;
|
h = e.handle;
|
||||||
})
|
})
|
||||||
.catch(e => console.error("Error importing `index.js`:", e));
|
.catch(e => console.error("Error importing `index.js`:", e));
|
|
@ -102,3 +102,5 @@ function parse_ini(inifile: string) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
on_load();
|
||||||
|
|
|
@ -5,7 +5,9 @@ import { Shader, Uniform4f, Uniform2fv, Uniform3fv, Uniform1i, Uniform1f, Unifor
|
||||||
import { Renderer } from "./webgl/renderer";
|
import { Renderer } from "./webgl/renderer";
|
||||||
import { VertexBuffer, IndexBuffer } from "./webgl/buffer";
|
import { VertexBuffer, IndexBuffer } from "./webgl/buffer";
|
||||||
import { VertexBufferLayout, VertexArray } from "./webgl/vertexBufferLayout";
|
import { VertexBufferLayout, VertexArray } from "./webgl/vertexBufferLayout";
|
||||||
|
import { Texture } from "./webgl/texture";
|
||||||
import { callbackify } from "util";
|
import { callbackify } from "util";
|
||||||
|
import { defaultLabelFactory, LabelFactory, Align, Label } from "./webgl/text";
|
||||||
|
|
||||||
function f32v(ptr: number, size: number): Float32Array {
|
function f32v(ptr: number, size: number): Float32Array {
|
||||||
return new Float32Array(memory.buffer, ptr, size);
|
return new Float32Array(memory.buffer, ptr, size);
|
||||||
|
@ -27,8 +29,8 @@ const COUNTER = new FPSCounter();
|
||||||
const LOADER = document.getElementById("main");
|
const LOADER = document.getElementById("main");
|
||||||
|
|
||||||
const SLIDER = <HTMLInputElement>document.getElementById("turnSlider");
|
const SLIDER = <HTMLInputElement>document.getElementById("turnSlider");
|
||||||
const FILESELECTOR = <HTMLInputElement> document.getElementById("fileselect");
|
const FILESELECTOR = <HTMLInputElement>document.getElementById("fileselect");
|
||||||
const SPEED = <HTMLInputElement> document.getElementById("speed");
|
const SPEED = <HTMLInputElement>document.getElementById("speed");
|
||||||
|
|
||||||
export function set_loading(loading: boolean) {
|
export function set_loading(loading: boolean) {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
|
@ -40,7 +42,7 @@ export function set_loading(loading: boolean) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const URL = window.location.origin+window.location.pathname;
|
const URL = window.location.origin + window.location.pathname;
|
||||||
export const LOCATION = URL.substring(0, URL.lastIndexOf("/") + 1);
|
export const LOCATION = URL.substring(0, URL.lastIndexOf("/") + 1);
|
||||||
const CANVAS = <HTMLCanvasElement>document.getElementById("c");
|
const CANVAS = <HTMLCanvasElement>document.getElementById("c");
|
||||||
const RESOLUTION = [CANVAS.width, CANVAS.height];
|
const RESOLUTION = [CANVAS.width, CANVAS.height];
|
||||||
|
@ -67,11 +69,23 @@ ShaderFactory.create_factory(
|
||||||
LOCATION + "static/shaders/frag/vor.glsl", LOCATION + "static/shaders/vert/vor.glsl"
|
LOCATION + "static/shaders/frag/vor.glsl", LOCATION + "static/shaders/vert/vor.glsl"
|
||||||
).then((e) => VOR_SHADER_FACTORY = e);
|
).then((e) => VOR_SHADER_FACTORY = e);
|
||||||
|
|
||||||
|
var IMAGE_SHADER_FACTORY: ShaderFactory;
|
||||||
|
ShaderFactory.create_factory(
|
||||||
|
LOCATION + "static/shaders/frag/image.glsl", LOCATION + "static/shaders/vert/simple.glsl"
|
||||||
|
).then((e) => IMAGE_SHADER_FACTORY = e);
|
||||||
|
|
||||||
class GameInstance {
|
class GameInstance {
|
||||||
resizer: Resizer;
|
resizer: Resizer;
|
||||||
game: Game;
|
game: Game;
|
||||||
|
|
||||||
shader: Shader;
|
shader: Shader;
|
||||||
vor_shader: Shader;
|
vor_shader: Shader;
|
||||||
|
image_shader: Shader;
|
||||||
|
|
||||||
|
text_factory: LabelFactory;
|
||||||
|
planet_labels: Label[];
|
||||||
|
ship_labels: Label[];
|
||||||
|
|
||||||
renderer: Renderer;
|
renderer: Renderer;
|
||||||
planet_count: number;
|
planet_count: number;
|
||||||
|
|
||||||
|
@ -86,15 +100,24 @@ class GameInstance {
|
||||||
|
|
||||||
turn_count = 0;
|
turn_count = 0;
|
||||||
|
|
||||||
constructor(game: Game, meshes: Mesh[], ship_mesh: Mesh) {
|
constructor(game: Game, meshes: Mesh[], ship_mesh: Mesh) {
|
||||||
this.game = game;
|
this.game = game;
|
||||||
this.planet_count = this.game.get_planet_count();
|
this.planet_count = this.game.get_planet_count();
|
||||||
this.shader = SHADERFACOTRY.create_shader(GL, {"MAX_CIRCLES": ''+this.planet_count});
|
|
||||||
this.vor_shader = VOR_SHADER_FACTORY.create_shader(GL, {"PLANETS": ''+this.planet_count});
|
this.shader = SHADERFACOTRY.create_shader(GL, { "MAX_CIRCLES": '' + this.planet_count });
|
||||||
|
this.image_shader = IMAGE_SHADER_FACTORY.create_shader(GL);
|
||||||
|
this.vor_shader = VOR_SHADER_FACTORY.create_shader(GL, { "PLANETS": '' + this.planet_count });
|
||||||
|
|
||||||
|
this.text_factory = defaultLabelFactory(GL, this.image_shader);
|
||||||
|
this.planet_labels = [];
|
||||||
|
this.ship_labels = [];
|
||||||
|
|
||||||
this.resizer = new Resizer(CANVAS, [...f32v(game.get_viewbox(), 4)], true);
|
this.resizer = new Resizer(CANVAS, [...f32v(game.get_viewbox(), 4)], true);
|
||||||
this.renderer = new Renderer();
|
this.renderer = new Renderer();
|
||||||
this.game.update_turn(0);
|
this.game.update_turn(0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const indexBuffer = new IndexBuffer(GL, [
|
const indexBuffer = new IndexBuffer(GL, [
|
||||||
0, 1, 2,
|
0, 1, 2,
|
||||||
1, 2, 3,
|
1, 2, 3,
|
||||||
|
@ -120,31 +143,44 @@ class GameInstance {
|
||||||
|
|
||||||
const planets = f32v(game.get_planets(), this.planet_count * 3);
|
const planets = f32v(game.get_planets(), this.planet_count * 3);
|
||||||
|
|
||||||
for(let i=0; i < this.planet_count; i++){
|
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 transform = new UniformMatrix3fv([
|
const indexBuffer = new IndexBuffer(GL, meshes[i % meshes.length].cells);
|
||||||
1, 0, 0,
|
const positionBuffer = new VertexBuffer(GL, meshes[i % meshes.length].positions);
|
||||||
0, 1, 0,
|
|
||||||
-planets[i*3], -planets[i*3+1], 1,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const indexBuffer = new IndexBuffer(GL, meshes[i % meshes.length].cells);
|
const layout = new VertexBufferLayout();
|
||||||
const positionBuffer = new VertexBuffer(GL, meshes[i % meshes.length].positions);
|
layout.push(GL.FLOAT, 3, 4, "a_position");
|
||||||
|
const vao = new VertexArray();
|
||||||
|
vao.addBuffer(positionBuffer, layout);
|
||||||
|
|
||||||
const layout = new VertexBufferLayout();
|
this.renderer.addToDraw(
|
||||||
layout.push(GL.FLOAT, 3, 4, "a_position");
|
indexBuffer,
|
||||||
const vao = new VertexArray();
|
vao,
|
||||||
vao.addBuffer(positionBuffer, layout);
|
this.shader,
|
||||||
|
{
|
||||||
|
"u_trans": transform,
|
||||||
|
"u_trans_next": transform,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
this.renderer.addToDraw(
|
{
|
||||||
indexBuffer,
|
const transform = new UniformMatrix3fv([
|
||||||
vao,
|
1., 0, 0,
|
||||||
this.shader,
|
0, 1., 0,
|
||||||
{
|
-planets[i * 3], -planets[i * 3 + 1] -1.2, 1.,
|
||||||
"u_trans": transform,
|
]);
|
||||||
"u_trans_next": transform,
|
|
||||||
}
|
const label = this.text_factory.build(GL, transform);
|
||||||
);
|
this.renderer.addRenderable(label);
|
||||||
|
this.planet_labels.push(label);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.turn_count = game.turn_count();
|
this.turn_count = game.turn_count();
|
||||||
|
@ -166,6 +202,10 @@ class GameInstance {
|
||||||
{}
|
{}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const label = this.text_factory.build(GL);
|
||||||
|
this.ship_labels.push(label);
|
||||||
|
this.renderer.addRenderable(label)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.vor_shader.uniform(GL, "u_planets", new Uniform3fv(planets));
|
this.vor_shader.uniform(GL, "u_planets", new Uniform3fv(planets));
|
||||||
|
@ -180,40 +220,59 @@ class GameInstance {
|
||||||
|
|
||||||
_update_state() {
|
_update_state() {
|
||||||
const colours = f32v(this.game.get_planet_colors(), this.planet_count * 6);
|
const colours = f32v(this.game.get_planet_colors(), this.planet_count * 6);
|
||||||
|
const planet_ships = i32v(this.game.get_planet_ships(), this.planet_count);
|
||||||
|
|
||||||
this.vor_shader.uniform(GL, "u_planet_colours", new Uniform3fv(colours));
|
this.vor_shader.uniform(GL, "u_planet_colours", new Uniform3fv(colours));
|
||||||
|
|
||||||
for(let i=0; i < this.planet_count; i++){
|
for (let i = 0; i < this.planet_count; i++) {
|
||||||
const u = new Uniform3f(colours[i*6], colours[i*6 + 1], colours[i*6 + 2]);
|
const u = new Uniform3f(colours[i * 6], colours[i * 6 + 1], colours[i * 6 + 2]);
|
||||||
this.renderer.updateUniform(i+1, (us) => us["u_color"] = u);
|
this.renderer.updateUniform(2 * i + 1, (us) => us["u_color"] = u);
|
||||||
const u2 = new Uniform3f(colours[i*6 + 3], colours[i*6 + 4], colours[i*6 + 5]);
|
const u2 = new Uniform3f(colours[i * 6 + 3], colours[i * 6 + 4], colours[i * 6 + 5]);
|
||||||
this.renderer.updateUniform(i+1, (us) => us["u_color_next"] = u2);
|
this.renderer.updateUniform(2 * i + 1, (us) => us["u_color_next"] = u2);
|
||||||
|
|
||||||
|
this.planet_labels[i].setText(GL, "*"+planet_ships[i], Align.Middle, Align.Begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ships = f32v(this.game.get_ship_locations(), this.game.get_ship_count() * 9 * 2);
|
const ship_count = this.game.get_ship_count();
|
||||||
const ship_colours = f32v(this.game.get_ship_colours(), this.game.get_ship_count() * 3);
|
const ships = f32v(this.game.get_ship_locations(), ship_count * 9 * 2);
|
||||||
|
const labels = f32v(this.game.get_ship_label_locations(), ship_count * 9 * 2);
|
||||||
|
const ship_counts = i32v(this.game.get_ship_counts(), ship_count);
|
||||||
|
const ship_colours = f32v(this.game.get_ship_colours(), ship_count * 3);
|
||||||
|
|
||||||
for (let i=0; i < this.game.get_max_ships(); i++) {
|
for (let i = 0; i < this.game.get_max_ships(); i++) {
|
||||||
const index = this.ship_indices[i];
|
const index = this.ship_indices[i];
|
||||||
if (i < this.game.get_ship_count()) {
|
if (i < ship_count) {
|
||||||
|
|
||||||
|
this.ship_labels[i].setText(GL, ""+ship_counts[i], Align.Middle, Align.Middle);
|
||||||
|
|
||||||
this.renderer.enableRendershit(index);
|
this.renderer.enableRendershit(index);
|
||||||
|
this.renderer.enableRendershit(index+1);
|
||||||
|
|
||||||
const u = new Uniform3f(ship_colours[i*3], ship_colours[i*3 + 1], ship_colours[i*3 + 2]);
|
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 t1 = new UniformMatrix3fv(new Float32Array(ships, i * 18, 9));
|
||||||
// const t2 = new UniformMatrix3fv(new Float32Array(ships, i * 18 + 9, 9));
|
// const t2 = new UniformMatrix3fv(new Float32Array(ships, i * 18 + 9, 9));
|
||||||
|
|
||||||
const t1 = new UniformMatrix3fv(ships.slice(i * 18, i * 18 + 9));
|
const t1 = new UniformMatrix3fv(ships.slice(i * 18, i * 18 + 9));
|
||||||
const t2 = new UniformMatrix3fv(ships.slice(i * 18 + 9, i * 18 + 18));
|
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(index, (us) => {
|
this.renderer.updateUniform(index, (us) => {
|
||||||
us["u_color"] = u;
|
us["u_color"] = u;
|
||||||
us["u_color_next"] = u;
|
us["u_color_next"] = u;
|
||||||
us["u_trans"] = t1;
|
us["u_trans"] = t1;
|
||||||
us["u_trans_next"] = t2;
|
us["u_trans_next"] = t2;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.renderer.updateUniform(index+1, (us) => {
|
||||||
|
us["u_trans"] = tl1;
|
||||||
|
us["u_trans_next"] = tl2;
|
||||||
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.renderer.disableRenderShift(index);
|
this.renderer.disableRenderShift(index);
|
||||||
|
this.renderer.disableRenderShift(index+1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,6 +295,7 @@ class GameInstance {
|
||||||
|
|
||||||
this.shader.uniform(GL, "u_viewbox", new Uniform4f(this.resizer.get_viewbox()));
|
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.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);
|
this.renderer.render(GL);
|
||||||
return;
|
return;
|
||||||
|
@ -258,7 +318,11 @@ class GameInstance {
|
||||||
this.shader.uniform(GL, "u_mouse", new Uniform2f(this.resizer.get_mouse_pos()));
|
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_viewbox", new Uniform4f(this.resizer.get_viewbox()));
|
||||||
this.shader.uniform(GL, "u_resolution", new Uniform2f(RESOLUTION));
|
this.shader.uniform(GL, "u_resolution", new Uniform2f(RESOLUTION));
|
||||||
this.shader.uniform(GL, "u_vor", new UniformBool(true));
|
|
||||||
|
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));
|
||||||
|
|
||||||
this.renderer.render(GL);
|
this.renderer.render(GL);
|
||||||
}
|
}
|
||||||
|
@ -332,33 +396,33 @@ export async function set_instance(source: string) {
|
||||||
set_loading(false);
|
set_loading(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('resize', function() {
|
window.addEventListener('resize', function () {
|
||||||
resizeCanvasToDisplaySize(CANVAS);
|
resizeCanvasToDisplaySize(CANVAS);
|
||||||
|
|
||||||
if (game_instance) {
|
if (game_instance) {
|
||||||
game_instance.on_resize();
|
game_instance.on_resize();
|
||||||
}
|
}
|
||||||
}, { capture: false, passive: true})
|
}, { capture: false, passive: true })
|
||||||
|
|
||||||
SLIDER.oninput = function() {
|
SLIDER.oninput = function () {
|
||||||
if (game_instance) {
|
if (game_instance) {
|
||||||
game_instance.updateTurn(parseInt(SLIDER.value));
|
game_instance.updateTurn(parseInt(SLIDER.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FILESELECTOR.onchange = function(){
|
FILESELECTOR.onchange = function () {
|
||||||
const file = FILESELECTOR.files[0];
|
const file = FILESELECTOR.files[0];
|
||||||
if(!file) { return; }
|
if (!file) { return; }
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
|
|
||||||
reader.onload = function() {
|
reader.onload = function () {
|
||||||
set_instance(<string> reader.result);
|
set_instance(<string>reader.result);
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
SPEED.onchange = function() {
|
SPEED.onchange = function () {
|
||||||
ms_per_frame = parseInt(SPEED.value);
|
ms_per_frame = parseInt(SPEED.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,46 +1,46 @@
|
||||||
{
|
{
|
||||||
"name": "create-wasm-app",
|
"name": "create-wasm-app",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "create an app to consume rust-generated wasm packages",
|
"description": "create an app to consume rust-generated wasm packages",
|
||||||
"main": "./bootstrap.js",
|
"main": "./bootstrap.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"develop": "webpack --mode development --watch",
|
"develop": "webpack --mode development --watch",
|
||||||
"build": "webpack --config webpack.config.js",
|
"build": "webpack --config webpack.config.js",
|
||||||
"start": "webpack-dev-server --content-base dist/"
|
"start": "webpack-dev-server --content-base dist/"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/rustwasm/create-wasm-app.git"
|
"url": "git+https://github.com/rustwasm/create-wasm-app.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"webassembly",
|
"webassembly",
|
||||||
"wasm",
|
"wasm",
|
||||||
"rust",
|
"rust",
|
||||||
"webpack",
|
"webpack",
|
||||||
"mozaic"
|
"mozaic"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"config-ini-parser": "^1.2.2",
|
"config-ini-parser": "^1.2.2",
|
||||||
"extract-svg-path": "^2.1.0",
|
"extract-svg-path": "^2.1.0",
|
||||||
"load-svg": "^1.0.0",
|
"load-svg": "^1.0.0",
|
||||||
"planetwars": "file:../pkg",
|
"planetwars": "file:../pkg",
|
||||||
"svg-mesh-3d": "^1.1.0",
|
"svg-mesh-3d": "^1.1.0",
|
||||||
"ts-heap": "^1.1.3"
|
"ts-heap": "^1.1.3"
|
||||||
},
|
},
|
||||||
"author": "Arthur Vercruysse <arthur.vercruysse@outlook.com>",
|
"author": "Arthur Vercruysse <arthur.vercruysse@outlook.com>",
|
||||||
"license": "(MIT OR Apache-2.0)",
|
"license": "(MIT OR Apache-2.0)",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/ajuvercr/Planetwars/issues"
|
"url": "https://github.com/ajuvercr/Planetwars/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/ajuvercr/Planetwars#Readme",
|
"homepage": "https://github.com/ajuvercr/Planetwars#Readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"webpack": "^4.29.3",
|
"webpack": "^4.29.3",
|
||||||
"ts-loader": "^6.0.2",
|
"ts-loader": "^6.0.2",
|
||||||
"static-eval": ">=2.0.0",
|
"static-eval": ">=2.0.0",
|
||||||
"typescript": "^3.5.2",
|
"typescript": "^3.5.2",
|
||||||
"webpack-cli": "^3.1.0",
|
"webpack-cli": "^3.1.0",
|
||||||
"webpack-dev-server": "^3.1.5",
|
"webpack-dev-server": "^3.1.5",
|
||||||
"copy-webpack-plugin": "^5.0.0"
|
"copy-webpack-plugin": "^5.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
BIN
frontend/www/static/res/assets/font.png
Normal file
BIN
frontend/www/static/res/assets/font.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 912 B |
BIN
frontend/www/static/res/assets/leaves.jpg
Normal file
BIN
frontend/www/static/res/assets/leaves.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
14
frontend/www/static/shaders/frag/image.glsl
Normal file
14
frontend/www/static/shaders/frag/image.glsl
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#ifdef GL_ES
|
||||||
|
precision mediump float;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Passed in from the vertex shader.
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
|
||||||
|
// The texture.
|
||||||
|
uniform sampler2D u_texture;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_FragColor = texture2D(u_texture, v_texCoord);
|
||||||
|
// gl_FragColor = vec4(0.7, 0.7, 0.0, 1.0);
|
||||||
|
}
|
33
frontend/www/static/shaders/vert/image.glsl
Normal file
33
frontend/www/static/shaders/vert/image.glsl
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#ifdef GL_ES
|
||||||
|
precision mediump float;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
attribute vec2 a_position;
|
||||||
|
attribute vec2 a_texCoord;
|
||||||
|
|
||||||
|
uniform float u_time;
|
||||||
|
|
||||||
|
uniform vec4 u_viewbox; // [x, y, width, height]
|
||||||
|
uniform vec2 u_resolution;
|
||||||
|
uniform mat3 u_trans;
|
||||||
|
|
||||||
|
varying vec2 v_pos;
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 pos = vec3(a_position, 1.0);
|
||||||
|
|
||||||
|
pos = u_trans * pos;
|
||||||
|
|
||||||
|
vec2 uv = pos.xy;
|
||||||
|
|
||||||
|
// Viewbox's center is top left, a_position's is in the center to the screen
|
||||||
|
// So translate and scale the viewbox**
|
||||||
|
uv -= u_viewbox.xy + (u_viewbox.zw * 0.5);
|
||||||
|
uv /= u_viewbox.zw * 0.5;
|
||||||
|
|
||||||
|
v_pos = (uv.xy + 1.0) * 0.5;
|
||||||
|
|
||||||
|
gl_Position = vec4(uv.xy, 0.0, 1.0);
|
||||||
|
v_texCoord = a_texCoord;
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ precision mediump float;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
attribute vec2 a_position;
|
attribute vec2 a_position;
|
||||||
|
attribute vec2 a_texCoord;
|
||||||
|
|
||||||
uniform float u_time;
|
uniform float u_time;
|
||||||
|
|
||||||
|
@ -12,6 +13,7 @@ uniform mat3 u_trans;
|
||||||
uniform mat3 u_trans_next;
|
uniform mat3 u_trans_next;
|
||||||
|
|
||||||
varying vec2 v_pos;
|
varying vec2 v_pos;
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 pos = vec3(a_position, 1.0);
|
vec3 pos = vec3(a_position, 1.0);
|
||||||
|
@ -30,4 +32,5 @@ void main() {
|
||||||
v_pos = (uv.xy + 1.0) * 0.5;
|
v_pos = (uv.xy + 1.0) * 0.5;
|
||||||
|
|
||||||
gl_Position = vec4(uv.xy, 0.0, 1.0);
|
gl_Position = vec4(uv.xy, 0.0, 1.0);
|
||||||
|
v_texCoord = a_texCoord;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ function createAndSetupTexture(gl: WebGLRenderingContext): WebGLTexture {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Foo implements Renderable {
|
export class Foo implements Renderable {
|
||||||
|
uniforms: Dictionary<Uniform>;
|
||||||
|
|
||||||
stages: Stage[];
|
stages: Stage[];
|
||||||
|
|
||||||
textures: WebGLTexture[];
|
textures: WebGLTexture[];
|
||||||
|
@ -25,30 +27,36 @@ export class Foo implements Renderable {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
|
|
||||||
|
|
||||||
constructor(gl: WebGLRenderingContext, width: number, height: number) {
|
constructor(gl: WebGLRenderingContext, width: number, height: number) {
|
||||||
|
this.uniforms = {};
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
|
|
||||||
for (let ii = 0; ii < 2; ++ii) {
|
for (let ii = 0; ii < 2; ++ii) {
|
||||||
const texture = createAndSetupTexture(gl);
|
const texture = createAndSetupTexture(gl);
|
||||||
this.textures.push(texture);
|
this.textures.push(texture);
|
||||||
|
|
||||||
// make the texture the same size as the image
|
// make the texture the same size as the image
|
||||||
gl.texImage2D(
|
gl.texImage2D(
|
||||||
gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0,
|
gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0,
|
||||||
gl.RGBA, gl.UNSIGNED_BYTE, null);
|
gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||||
|
|
||||||
// Create a framebuffer
|
// Create a framebuffer
|
||||||
const fbo = gl.createFramebuffer();
|
const fbo = gl.createFramebuffer();
|
||||||
this.framebuffers.push(fbo);
|
this.framebuffers.push(fbo);
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
|
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
|
||||||
|
|
||||||
// Attach a texture to it.
|
// Attach a texture to it.
|
||||||
gl.framebufferTexture2D(
|
gl.framebufferTexture2D(
|
||||||
gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getUniforms(): Dictionary<Uniform> {
|
||||||
|
return this.uniforms;
|
||||||
|
}
|
||||||
|
|
||||||
render(gl: WebGLRenderingContext) {
|
render(gl: WebGLRenderingContext) {
|
||||||
this.stages.forEach( (item, i) => {
|
this.stages.forEach( (item, i) => {
|
||||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffers[i%2]);
|
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffers[i%2]);
|
||||||
|
@ -62,6 +70,10 @@ class Stage implements Renderable {
|
||||||
program: Shader;
|
program: Shader;
|
||||||
uniforms: Dictionary<Uniform>;
|
uniforms: Dictionary<Uniform>;
|
||||||
|
|
||||||
|
getUniforms(): Dictionary<Uniform> {
|
||||||
|
return this.uniforms;
|
||||||
|
}
|
||||||
|
|
||||||
render(gl: WebGLRenderingContext) {
|
render(gl: WebGLRenderingContext) {
|
||||||
this.program.bind(gl);
|
this.program.bind(gl);
|
||||||
|
|
||||||
|
|
|
@ -6,18 +6,17 @@ import { Texture } from './texture';
|
||||||
import { Dictionary } from './util';
|
import { Dictionary } from './util';
|
||||||
|
|
||||||
export interface Renderable {
|
export interface Renderable {
|
||||||
|
getUniforms() : Dictionary<Uniform>;
|
||||||
render(gl: WebGLRenderingContext): void;
|
render(gl: WebGLRenderingContext): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
class RenderShit implements Renderable {
|
export class RenderShit implements Renderable {
|
||||||
ibo: IndexBuffer;
|
ibo: IndexBuffer;
|
||||||
va: VertexArray;
|
va: VertexArray;
|
||||||
shader: Shader;
|
shader: Shader;
|
||||||
textures: Texture[];
|
textures: Texture[];
|
||||||
uniforms: Dictionary<Uniform>;
|
uniforms: Dictionary<Uniform>;
|
||||||
|
|
||||||
enabled: boolean;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
ibo: IndexBuffer,
|
ibo: IndexBuffer,
|
||||||
va: VertexArray,
|
va: VertexArray,
|
||||||
|
@ -25,7 +24,6 @@ class RenderShit implements Renderable {
|
||||||
textures: Texture[],
|
textures: Texture[],
|
||||||
uniforms: Dictionary<Uniform>,
|
uniforms: Dictionary<Uniform>,
|
||||||
) {
|
) {
|
||||||
this.enabled = true;
|
|
||||||
this.ibo = ibo;
|
this.ibo = ibo;
|
||||||
this.va = va;
|
this.va = va;
|
||||||
this.shader = shader;
|
this.shader = shader;
|
||||||
|
@ -33,10 +31,11 @@ class RenderShit implements Renderable {
|
||||||
this.uniforms = uniforms;
|
this.uniforms = uniforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getUniforms(): Dictionary<Uniform> {
|
||||||
|
return this.uniforms;
|
||||||
|
}
|
||||||
|
|
||||||
render(gl: WebGLRenderingContext): void {
|
render(gl: WebGLRenderingContext): void {
|
||||||
if (!this.enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const indexBuffer = this.ibo;
|
const indexBuffer = this.ibo;
|
||||||
const vertexArray = this.va;
|
const vertexArray = this.va;
|
||||||
|
@ -73,35 +72,34 @@ class RenderShit implements Renderable {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Renderer {
|
export class Renderer {
|
||||||
renderables: RenderShit[];
|
renderables: [Renderable, boolean][];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.renderables = [];
|
this.renderables = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUniform(i: number, f: (uniforms: Dictionary<Uniform>) => void) {
|
updateUniform(i: number, f: (uniforms: Dictionary<Uniform>) => void) {
|
||||||
f(this.renderables[i].uniforms);
|
f(this.renderables[i][0].getUniforms());
|
||||||
}
|
}
|
||||||
|
|
||||||
disableRenderShift(i: number) {
|
disableRenderShift(i: number) {
|
||||||
this.renderables[i].enabled = false;
|
this.renderables[i][1] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
enableRendershit(i: number) {
|
enableRendershit(i: number) {
|
||||||
this.renderables[i].enabled = true;
|
this.renderables[i][1] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// addRenderable(item: Renderable) {
|
addRenderable(item: Renderable): number {
|
||||||
// this.renderables.push(item);
|
this.renderables.push([item, true]);
|
||||||
// }
|
return this.renderables.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
addToDraw(indexBuffer: IndexBuffer, vertexArray: VertexArray, shader: Shader, uniforms?: Dictionary<Uniform>, texture?: Texture[]): number {
|
addToDraw(indexBuffer: IndexBuffer, vertexArray: VertexArray, shader: Shader, uniforms?: Dictionary<Uniform>, texture?: Texture[]): number {
|
||||||
|
return this.addRenderable(
|
||||||
this.renderables.push(
|
|
||||||
new RenderShit(
|
new RenderShit(
|
||||||
indexBuffer,
|
indexBuffer,
|
||||||
vertexArray,
|
vertexArray,
|
||||||
|
@ -110,8 +108,6 @@ export class Renderer {
|
||||||
uniforms || {},
|
uniforms || {},
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return this.renderables.length - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render(gl: WebGLRenderingContext, frameBuffer?: WebGLFramebuffer, width?: number, height?: number) {
|
render(gl: WebGLRenderingContext, frameBuffer?: WebGLFramebuffer, width?: number, height?: number) {
|
||||||
|
@ -121,7 +117,8 @@ export class Renderer {
|
||||||
|
|
||||||
const maxTextures = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS);
|
const maxTextures = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS);
|
||||||
|
|
||||||
for (let r of this.renderables) {
|
for (let [r, e] of this.renderables) {
|
||||||
|
if (!e) continue;
|
||||||
r.render(gl);
|
r.render(gl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
186
frontend/www/webgl/text.ts
Normal file
186
frontend/www/webgl/text.ts
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
import { Texture } from "./texture";
|
||||||
|
import { Dictionary } from "./util";
|
||||||
|
import { Renderable, RenderShit } from "./renderer";
|
||||||
|
import { Uniform, Shader, UniformMatrix3fv } from "./shader";
|
||||||
|
import { IndexBuffer, VertexBuffer } from "./buffer";
|
||||||
|
import { VertexBufferLayout, VertexArray } from "./vertexBufferLayout";
|
||||||
|
|
||||||
|
|
||||||
|
export enum Align {
|
||||||
|
Begin,
|
||||||
|
End,
|
||||||
|
Middle,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GlypInfo {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FontInfo {
|
||||||
|
letterHeight: number;
|
||||||
|
spaceWidth: number;
|
||||||
|
spacing: number;
|
||||||
|
textureWidth: number;
|
||||||
|
textureHeight: number;
|
||||||
|
glyphInfos: Dictionary<GlypInfo>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LabelFactory {
|
||||||
|
texture: Texture;
|
||||||
|
font: FontInfo;
|
||||||
|
shader: Shader;
|
||||||
|
|
||||||
|
constructor(gl: WebGLRenderingContext, loc: string, font: FontInfo, shader: Shader) {
|
||||||
|
this.texture = Texture.fromImage(gl, loc, 'font');
|
||||||
|
this.font = font;
|
||||||
|
this.shader = shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
build(gl: WebGLRenderingContext, transform?: UniformMatrix3fv): Label {
|
||||||
|
return new Label(gl, this.shader, this.texture, this.font, transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Label implements Renderable {
|
||||||
|
inner: Renderable;
|
||||||
|
ib: IndexBuffer;
|
||||||
|
vb: VertexBuffer;
|
||||||
|
|
||||||
|
font: FontInfo;
|
||||||
|
|
||||||
|
constructor(gl: WebGLRenderingContext, shader: Shader, tex: Texture, font: FontInfo, transform?: UniformMatrix3fv) {
|
||||||
|
this.font = font;
|
||||||
|
|
||||||
|
const uniforms = transform ? { "u_trans": transform, "u_trans_next": transform, } : {};
|
||||||
|
this.ib = new IndexBuffer(gl, []);
|
||||||
|
this.vb = new VertexBuffer(gl, []);
|
||||||
|
|
||||||
|
const layout = new VertexBufferLayout();
|
||||||
|
layout.push(gl.FLOAT, 2, 4, "a_position");
|
||||||
|
layout.push(gl.FLOAT, 2, 4, "a_texCoord");
|
||||||
|
|
||||||
|
const vao = new VertexArray();
|
||||||
|
vao.addBuffer(this.vb, layout);
|
||||||
|
|
||||||
|
this.inner = new RenderShit(this.ib, vao, shader, [tex], uniforms);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUniforms(): Dictionary<Uniform> {
|
||||||
|
return this.inner.getUniforms();
|
||||||
|
}
|
||||||
|
|
||||||
|
render(gl: WebGLRenderingContext): void {
|
||||||
|
return this.inner.render(gl);
|
||||||
|
}
|
||||||
|
|
||||||
|
setText(gl: WebGLRenderingContext, text: string, h_align = Align.Begin, v_align = Align.Begin) {
|
||||||
|
const idxs = [];
|
||||||
|
const verts = [];
|
||||||
|
|
||||||
|
const letterHeight = this.font.letterHeight / this.font.textureHeight;
|
||||||
|
let xPos = 0;
|
||||||
|
|
||||||
|
switch (h_align) {
|
||||||
|
case Align.Begin:
|
||||||
|
break;
|
||||||
|
case Align.End:
|
||||||
|
xPos = -1 * [...text].map(n => this.font.glyphInfos[n] ? this.font.glyphInfos[n].width : this.font.spaceWidth).reduce((a, b) => a + b, 0) / this.font.letterHeight;
|
||||||
|
break;
|
||||||
|
case Align.Middle:
|
||||||
|
xPos = -1 * [...text].map(n => this.font.glyphInfos[n] ? this.font.glyphInfos[n].width : this.font.spaceWidth).reduce((a, b) => a + b, 0) / this.font.letterHeight / 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let yStart = 0;
|
||||||
|
switch (v_align) {
|
||||||
|
case Align.Begin:
|
||||||
|
break;
|
||||||
|
case Align.End:
|
||||||
|
yStart = 1;
|
||||||
|
break;
|
||||||
|
case Align.Middle:
|
||||||
|
yStart = 0.5;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let j = 0;
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
const info = this.font.glyphInfos[text[i]];
|
||||||
|
if (info) {
|
||||||
|
const dx = info.width / this.font.letterHeight;
|
||||||
|
const letterWidth = info.width / this.font.textureWidth;
|
||||||
|
const x0 = info.x / this.font.textureWidth;
|
||||||
|
const y0 = info.y / this.font.textureHeight;
|
||||||
|
verts.push(xPos, yStart, x0, y0);
|
||||||
|
verts.push(xPos + dx, yStart, x0 + letterWidth, y0);
|
||||||
|
verts.push(xPos, yStart-1, x0, y0 + letterHeight);
|
||||||
|
verts.push(xPos + dx, yStart-1, x0 + letterWidth, y0 + letterHeight);
|
||||||
|
xPos += dx;
|
||||||
|
|
||||||
|
idxs.push(j+0, j+1, j+2, j+1, j+2, j+3);
|
||||||
|
j += 4;
|
||||||
|
} else {
|
||||||
|
// Just move xPos
|
||||||
|
xPos += this.font.spaceWidth / this.font.letterHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ib.updateData(gl, idxs);
|
||||||
|
this.vb.updateData(gl, verts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function defaultLabelFactory(gl: WebGLRenderingContext, shader: Shader): LabelFactory {
|
||||||
|
const fontInfo = {
|
||||||
|
letterHeight: 8,
|
||||||
|
spaceWidth: 8,
|
||||||
|
spacing: -1,
|
||||||
|
textureWidth: 64,
|
||||||
|
textureHeight: 40,
|
||||||
|
glyphInfos: {
|
||||||
|
'a': { x: 0, y: 0, width: 8, },
|
||||||
|
'b': { x: 8, y: 0, width: 8, },
|
||||||
|
'c': { x: 16, y: 0, width: 8, },
|
||||||
|
'd': { x: 24, y: 0, width: 8, },
|
||||||
|
'e': { x: 32, y: 0, width: 8, },
|
||||||
|
'f': { x: 40, y: 0, width: 8, },
|
||||||
|
'g': { x: 48, y: 0, width: 8, },
|
||||||
|
'h': { x: 56, y: 0, width: 8, },
|
||||||
|
'i': { x: 0, y: 8, width: 8, },
|
||||||
|
'j': { x: 8, y: 8, width: 8, },
|
||||||
|
'k': { x: 16, y: 8, width: 8, },
|
||||||
|
'l': { x: 24, y: 8, width: 8, },
|
||||||
|
'm': { x: 32, y: 8, width: 8, },
|
||||||
|
'n': { x: 40, y: 8, width: 8, },
|
||||||
|
'o': { x: 48, y: 8, width: 8, },
|
||||||
|
'p': { x: 56, y: 8, width: 8, },
|
||||||
|
'q': { x: 0, y: 16, width: 8, },
|
||||||
|
'r': { x: 8, y: 16, width: 8, },
|
||||||
|
's': { x: 16, y: 16, width: 8, },
|
||||||
|
't': { x: 24, y: 16, width: 8, },
|
||||||
|
'u': { x: 32, y: 16, width: 8, },
|
||||||
|
'v': { x: 40, y: 16, width: 8, },
|
||||||
|
'w': { x: 48, y: 16, width: 8, },
|
||||||
|
'x': { x: 56, y: 16, width: 8, },
|
||||||
|
'y': { x: 0, y: 24, width: 8, },
|
||||||
|
'z': { x: 8, y: 24, width: 8, },
|
||||||
|
'0': { x: 16, y: 24, width: 8, },
|
||||||
|
'1': { x: 24, y: 24, width: 8, },
|
||||||
|
'2': { x: 32, y: 24, width: 8, },
|
||||||
|
'3': { x: 40, y: 24, width: 8, },
|
||||||
|
'4': { x: 48, y: 24, width: 8, },
|
||||||
|
'5': { x: 56, y: 24, width: 8, },
|
||||||
|
'6': { x: 0, y: 32, width: 8, },
|
||||||
|
'7': { x: 8, y: 32, width: 8, },
|
||||||
|
'8': { x: 16, y: 32, width: 8, },
|
||||||
|
'9': { x: 24, y: 32, width: 8, },
|
||||||
|
'-': { x: 32, y: 32, width: 8, },
|
||||||
|
'*': { x: 40, y: 32, width: 8, },
|
||||||
|
'!': { x: 48, y: 32, width: 8, },
|
||||||
|
'?': { x: 56, y: 32, width: 8, },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return new LabelFactory(gl, 'static/res/assets/font.png', fontInfo, shader);
|
||||||
|
}
|
Loading…
Reference in a new issue