wtf
This commit is contained in:
parent
1f18f3d16f
commit
aa97ec8837
11 changed files with 1822 additions and 138 deletions
|
@ -3,9 +3,12 @@
|
|||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Hello wasm-pack!</title>
|
||||
<link rel="stylesheet" type="text/css" href="static/res/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="c" width=1700 height=900></canvas>
|
||||
<div id="loader" class="loading">
|
||||
<canvas id="c" width=1700 height=900></canvas>
|
||||
</div>
|
||||
|
||||
<noscript>This page contains webassembly and javascript content, please enable javascript in your browser.</noscript>
|
||||
<script src="./bootstrap.js"></script>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Game } from "planetwars";
|
||||
import { Shader } from "./webgl/shader"
|
||||
|
||||
import { main } from './index.ts'
|
||||
import { set_instance } from './index.ts'
|
||||
|
||||
const URL = window.location.origin+window.location.pathname;
|
||||
const LOCATION = URL.substring(0, URL.lastIndexOf("/") + 1);
|
||||
|
@ -11,5 +11,5 @@ const game_location = LOCATION + "static/game.json";
|
|||
fetch(game_location)
|
||||
.then((r) => r.text())
|
||||
.then((response) => {
|
||||
main(Game.new(response));
|
||||
set_instance(Game.new(response));
|
||||
}).catch(console.error);
|
||||
|
|
|
@ -1,15 +1,122 @@
|
|||
import { Game } from "planetwars";
|
||||
import { memory } from "planetwars/plantwars_bg";
|
||||
import { Resizer, resizeCanvasToDisplaySize, FPSCounter } from "./webgl/util";
|
||||
import { Shader, Uniform4f, Uniform2fv, Uniform3fv, Uniform1i, Uniform1f, Uniform2f, ShaderFactory } from './webgl/shader';
|
||||
import { Renderer } from "./webgl/renderer";
|
||||
import { VertexBuffer, IndexBuffer } from "./webgl/buffer";
|
||||
import { VertexBufferLayout, VertexArray } from "./webgl/vertexBufferLayout";
|
||||
import { callbackify } from "util";
|
||||
|
||||
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);
|
||||
const CANVAS = <HTMLCanvasElement>document.getElementById("c");
|
||||
const RESOLUTION = [CANVAS.width, CANVAS.height];
|
||||
|
||||
const GL = CANVAS.getContext("webgl");
|
||||
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);
|
||||
|
||||
const positionBuffer = new VertexBuffer(GL, [
|
||||
-1, -1,
|
||||
-1, 1,
|
||||
1, -1,
|
||||
1, 1,
|
||||
]);
|
||||
|
||||
const layout = new VertexBufferLayout();
|
||||
layout.push(GL.FLOAT, 2, 4, "a_position");
|
||||
const vao = new VertexArray();
|
||||
vao.addBuffer(positionBuffer, layout);
|
||||
|
||||
const indexBuffer = new IndexBuffer(GL, [
|
||||
0, 1, 2,
|
||||
1, 2, 3,
|
||||
]);
|
||||
|
||||
var SHADERFACOTRY: ShaderFactory;
|
||||
ShaderFactory.create_factory(
|
||||
LOCATION + "static/shaders/frag/simple.glsl", LOCATION + "static/shaders/vert/simple.glsl"
|
||||
).then((e) => SHADERFACOTRY = e);
|
||||
|
||||
|
||||
function create_array(ptr: number, size: number): Float64Array {
|
||||
return new Float64Array(memory.buffer, ptr, size);
|
||||
}
|
||||
|
||||
export function main(game: Game) {
|
||||
class GameInstance {
|
||||
resizer: Resizer;
|
||||
game: Game;
|
||||
shader: Shader;
|
||||
renderer: Renderer;
|
||||
|
||||
constructor(game: Game) {
|
||||
this.game = game;
|
||||
this.shader = SHADERFACOTRY.create_shader(GL, {"MAX_CIRCLES": "50"});
|
||||
this.resizer = new Resizer(CANVAS, [...create_array(game.get_viewbox(), 4)], true);
|
||||
this.renderer = new Renderer();
|
||||
this.renderer.addToDraw(indexBuffer, vao, this.shader);
|
||||
}
|
||||
|
||||
render(time: number) {
|
||||
this.shader.uniform(GL, "u_circle_count", new Uniform1i(3));
|
||||
|
||||
this.shader.uniform(GL, "u_time", new Uniform1f(time * 0.001));
|
||||
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.shader.uniform(GL, "u_circles", new Uniform3fv([
|
||||
0, 0, 3.5,
|
||||
-2, -2, 2,
|
||||
5, 2, 4,
|
||||
]));
|
||||
this.shader.uniform(GL, "u_color", new Uniform4f([1, 1, 0, 1]));
|
||||
|
||||
this.renderer.render(GL);
|
||||
COUNTER.frame(time);
|
||||
}
|
||||
}
|
||||
|
||||
var game_instance: GameInstance;
|
||||
|
||||
export function set_instance(game: Game) {
|
||||
game_instance = new GameInstance(game);
|
||||
|
||||
console.log(game.turn_count());
|
||||
|
||||
console.log(create_array(game.get_viewbox(), 4));
|
||||
}
|
||||
|
||||
|
||||
function step(time: number) {
|
||||
if (game_instance) {
|
||||
game_instance.render(time);
|
||||
set_loading(false);
|
||||
} else {
|
||||
set_loading(true);
|
||||
}
|
||||
|
||||
requestAnimationFrame(step);
|
||||
}
|
||||
set_loading(true);
|
||||
|
||||
requestAnimationFrame(step);
|
||||
|
|
57
frontend/www/static/res/style.css
Normal file
57
frontend/www/static/res/style.css
Normal file
|
@ -0,0 +1,57 @@
|
|||
|
||||
.loading {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.loading::before{
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: #1c5ba2;
|
||||
border-radius: 100%;
|
||||
z-index: 5;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
-webkit-animation: slide-top 0.5s ease-out infinite alternate ;
|
||||
animation: slide-top 0.5s ease-out infinite alternate ;
|
||||
}
|
||||
|
||||
#c {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------
|
||||
* Generated by Animista on 2019-9-17 14:35:13
|
||||
* Licensed under FreeBSD License.
|
||||
* See http://animista.net/license for more info.
|
||||
* w: http://animista.net, t: @cssanimista
|
||||
* ---------------------------------------------- */
|
||||
|
||||
/**
|
||||
* ----------------------------------------
|
||||
* animation slide-top
|
||||
* ----------------------------------------
|
||||
*/
|
||||
@-webkit-keyframes slide-top {
|
||||
0% {
|
||||
-webkit-transform: translate(-50%, 50%);
|
||||
transform: translate(-50%, 50%);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: translate(-50%, -150%);
|
||||
transform: translate(-50%, -150%);
|
||||
}
|
||||
}
|
||||
@keyframes slide-top {
|
||||
0% {
|
||||
-webkit-transform: translate(-50%, 50%);
|
||||
transform: translate(-50%, 50%);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: translate(-50%, -150%);
|
||||
transform: translate(-50%, -150%);
|
||||
}
|
||||
}
|
27
frontend/www/static/shaders/frag/simple.glsl
Normal file
27
frontend/www/static/shaders/frag/simple.glsl
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
uniform int u_circle_count;
|
||||
uniform float u_time;
|
||||
uniform vec2 u_mouse;
|
||||
uniform vec4 u_viewbox; // [x, y, width, height]
|
||||
uniform vec2 u_resolution;
|
||||
uniform vec3 u_circles[$MAX_CIRCLES];
|
||||
uniform vec4 u_color;
|
||||
|
||||
varying vec2 v_pos;
|
||||
|
||||
void main() {
|
||||
vec2 uv = v_pos;
|
||||
|
||||
float alpha = 0.0;
|
||||
for (int i = 0; i < $MAX_CIRCLES; i++ ){
|
||||
if (i >= u_circle_count) { break; }
|
||||
float d = distance(uv.xy, u_circles[i].xy);
|
||||
alpha = max(1.0 - d/u_circles[i].z, alpha);
|
||||
}
|
||||
|
||||
gl_FragColor = u_color;
|
||||
gl_FragColor.w *= alpha;
|
||||
}
|
22
frontend/www/static/shaders/vert/simple.glsl
Normal file
22
frontend/www/static/shaders/vert/simple.glsl
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
attribute vec2 a_position;
|
||||
|
||||
uniform vec4 u_viewbox; // [x, y, width, height]
|
||||
uniform vec2 u_resolution;
|
||||
|
||||
varying vec2 v_pos;
|
||||
|
||||
void main() {
|
||||
|
||||
vec2 uv = ( a_position.xy + 1.0 ) * 0.5;
|
||||
|
||||
uv *= u_viewbox.zw;
|
||||
uv += u_viewbox.xy;
|
||||
|
||||
v_pos = uv.xy;
|
||||
|
||||
gl_Position = vec4(a_position.xy, 0.0, 1.0);
|
||||
}
|
115
frontend/www/webgl/index.ts
Normal file
115
frontend/www/webgl/index.ts
Normal file
|
@ -0,0 +1,115 @@
|
|||
|
||||
import { Shader, Uniform4f, Uniform2fv, Uniform3fv, Uniform1i, Uniform1f, Uniform2f, ShaderFactory } from './shader';
|
||||
import { resizeCanvasToDisplaySize, FPSCounter, onload2promise, Resizer } from "./util";
|
||||
import { VertexBuffer, IndexBuffer } from './buffer';
|
||||
import { VertexArray, VertexBufferLayout } from './vertexBufferLayout';
|
||||
import { Renderer } from './renderer';
|
||||
import { Texture } from './texture';
|
||||
|
||||
|
||||
async function main() {
|
||||
|
||||
const URL = window.location.origin+window.location.pathname;
|
||||
const LOCATION = URL.substring(0, URL.lastIndexOf("/") + 1);
|
||||
|
||||
|
||||
// Get A WebGL context
|
||||
var canvas = <HTMLCanvasElement>document.getElementById("c");
|
||||
const resolution = [canvas.width, canvas.height];
|
||||
|
||||
const resizer = new Resizer(canvas, [0, 0, 900, 900], true);
|
||||
|
||||
var gl = canvas.getContext("webgl");
|
||||
if (!gl) {
|
||||
return;
|
||||
}
|
||||
|
||||
const renderer = new Renderer();
|
||||
|
||||
const factory = await ShaderFactory.create_factory(LOCATION + "static/shaders/frag/simple.glsl", LOCATION + "static/shaders/vert/simple.glsl");
|
||||
const program = factory.create_shader(gl, {"MAX_CIRCLES": "50"});
|
||||
|
||||
var positions = [
|
||||
-1, -1, 0, 1,
|
||||
-1, 1, 0, 0,
|
||||
1, -1, 1, 1,
|
||||
1, 1, 1, 0,
|
||||
];
|
||||
|
||||
var positionBuffer = new VertexBuffer(gl, positions);
|
||||
var layout = new VertexBufferLayout();
|
||||
layout.push(gl.FLOAT, 2, 4, "a_position");
|
||||
layout.push(gl.FLOAT, 2, 4, "a_tex");
|
||||
|
||||
const vao = new VertexArray();
|
||||
vao.addBuffer(positionBuffer, layout);
|
||||
|
||||
resizeCanvasToDisplaySize(<HTMLCanvasElement>gl.canvas);
|
||||
|
||||
// Tell WebGL how to convert from clip space to pixels
|
||||
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
||||
|
||||
// Clear the canvas
|
||||
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);
|
||||
|
||||
program.bind(gl);
|
||||
vao.bind(gl, program);
|
||||
|
||||
var indices = [
|
||||
0, 1, 2,
|
||||
1, 2, 3,
|
||||
];
|
||||
|
||||
var indexBuffer = new IndexBuffer(gl, indices);
|
||||
indexBuffer.bind(gl);
|
||||
|
||||
renderer.addToDraw(indexBuffer, vao, program);
|
||||
|
||||
var blue = 1.0;
|
||||
var inc = 0.05;
|
||||
|
||||
const counter = new FPSCounter();
|
||||
|
||||
const step = function (time: number) {
|
||||
blue += inc;
|
||||
// if (blue > 1.0 || blue < 0.0) {
|
||||
// inc = -1 * inc;
|
||||
// blue += inc;
|
||||
// }
|
||||
|
||||
program.uniform(gl, "u_circle_count", new Uniform1i(3));
|
||||
|
||||
program.uniform(gl, "u_time", new Uniform1f(time * 0.001));
|
||||
program.uniform(gl, "u_mouse", new Uniform2f(resizer.get_mouse_pos()));
|
||||
program.uniform(gl, "u_viewbox", new Uniform4f(resizer.get_viewbox()));
|
||||
program.uniform(gl, "u_resolution", new Uniform2f(resolution));
|
||||
program.uniform(gl, "u_circles", new Uniform3fv([
|
||||
450, 450, 100,
|
||||
200, 200, 200,
|
||||
900, 0, 300,
|
||||
]));
|
||||
program.uniform(gl, "u_color", new Uniform4f([1, blue, 0, 1]));
|
||||
|
||||
renderer.render(gl);
|
||||
|
||||
counter.frame(time);
|
||||
requestAnimationFrame(step);
|
||||
}
|
||||
|
||||
requestAnimationFrame(step);
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
const loader = document.getElementById("loader");
|
||||
setInterval(() => {
|
||||
if (loader.classList.contains("loading")) {
|
||||
loader.classList.remove("loading")
|
||||
} else {
|
||||
loader.classList.add("loading");
|
||||
}
|
||||
}, 2000);
|
|
@ -1,7 +1,7 @@
|
|||
import { Dictionary } from './util';
|
||||
|
||||
function error(msg: string) {
|
||||
console.log(msg);
|
||||
console.error(msg);
|
||||
}
|
||||
|
||||
const defaultShaderType = [
|
||||
|
@ -9,6 +9,7 @@ const defaultShaderType = [
|
|||
"FRAGMENT_SHADER"
|
||||
];
|
||||
|
||||
/// Create Shader from Source string
|
||||
function loadShader(
|
||||
gl: WebGLRenderingContext,
|
||||
shaderSource: string,
|
||||
|
@ -41,6 +42,7 @@ function loadShader(
|
|||
return shader;
|
||||
}
|
||||
|
||||
/// Actually Create Program with Shader's
|
||||
function createProgram(
|
||||
gl: WebGLRenderingContext,
|
||||
shaders: WebGLShader[],
|
||||
|
@ -76,39 +78,45 @@ function createProgram(
|
|||
return program;
|
||||
}
|
||||
|
||||
function createShaderFromScript(
|
||||
gl: WebGLRenderingContext,
|
||||
scriptId: string,
|
||||
context: Dictionary<any>,
|
||||
opt_shaderType: number,
|
||||
opt_errorCallback: any,
|
||||
): WebGLShader {
|
||||
var shaderSource = "";
|
||||
var shaderType;
|
||||
var shaderScript = document.getElementById(scriptId) as HTMLScriptElement;
|
||||
if (!shaderScript) {
|
||||
console.log("*** Error: unknown script element" + scriptId);
|
||||
}
|
||||
shaderSource = shaderScript.text;
|
||||
export class ShaderFactory {
|
||||
frag_source: string;
|
||||
vert_source: string;
|
||||
|
||||
for (let key in context) {
|
||||
console.log("substitute " + key);
|
||||
shaderSource = shaderSource.replace(new RegExp("\\$" + key, 'g'), context[key]);
|
||||
static async create_factory(frag_url: string, vert_url: string): Promise<ShaderFactory> {
|
||||
const sources = await Promise.all([
|
||||
fetch(frag_url).then((r) => r.text()),
|
||||
fetch(vert_url).then((r) => r.text()),
|
||||
]);
|
||||
|
||||
return new ShaderFactory(sources[0], sources[1]);
|
||||
}
|
||||
|
||||
if (!opt_shaderType) {
|
||||
if (shaderScript.type === "x-shader/x-vertex") {
|
||||
shaderType = 35633;
|
||||
} else if (shaderScript.type === "x-shader/x-fragment") {
|
||||
shaderType = 35632;
|
||||
} else if (shaderType !== gl.VERTEX_SHADER && shaderType !== gl.FRAGMENT_SHADER) {
|
||||
console.log("*** Error: unknown shader type");
|
||||
constructor(frag_source: string, vert_source: string ) {
|
||||
this.frag_source = frag_source;
|
||||
this.vert_source = vert_source;
|
||||
}
|
||||
|
||||
create_shader(
|
||||
gl: WebGLRenderingContext,
|
||||
context?: Dictionary<string>,
|
||||
opt_attribs?: string[],
|
||||
opt_locations?: number[],
|
||||
opt_errorCallback?: any,
|
||||
): Shader {
|
||||
let vert = this.vert_source.slice();
|
||||
let frag = this.frag_source.slice();
|
||||
for (let key in context) {
|
||||
vert = vert.replace(new RegExp("\\$" + key, 'g'), context[key]);
|
||||
frag = frag.replace(new RegExp("\\$" + key, 'g'), context[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return loadShader(
|
||||
gl, shaderSource, opt_shaderType ? opt_shaderType : shaderType,
|
||||
opt_errorCallback);
|
||||
const shaders = [
|
||||
loadShader(gl, vert, gl.VERTEX_SHADER, opt_errorCallback),
|
||||
loadShader(gl, frag, gl.FRAGMENT_SHADER, opt_errorCallback),
|
||||
];
|
||||
|
||||
return new Shader(createProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback));
|
||||
}
|
||||
}
|
||||
|
||||
export class Shader {
|
||||
|
@ -116,22 +124,6 @@ export class Shader {
|
|||
uniformCache: Dictionary<WebGLUniformLocation>;
|
||||
attribCache: Dictionary<number>;
|
||||
|
||||
static createProgramFromScripts(
|
||||
gl: WebGLRenderingContext,
|
||||
shaderScriptIds: string[],
|
||||
context = {},
|
||||
opt_attribs?: string[],
|
||||
opt_locations?: number[],
|
||||
opt_errorCallback?: any,
|
||||
): Shader {
|
||||
var shaders = [];
|
||||
for (var ii = 0; ii < shaderScriptIds.length; ++ii) {
|
||||
shaders.push(createShaderFromScript(
|
||||
gl, shaderScriptIds[ii], context, (gl as any)[defaultShaderType[ii % 2]] as number, opt_errorCallback));
|
||||
}
|
||||
return new Shader(createProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback));
|
||||
}
|
||||
|
||||
static async createProgramFromUrls(
|
||||
gl: WebGLRenderingContext,
|
||||
vert_url: string,
|
||||
|
@ -198,6 +190,10 @@ export class Shader {
|
|||
|
||||
uniform.setUniform(gl, location);
|
||||
}
|
||||
|
||||
clear(gl: WebGLRenderingContext) {
|
||||
gl.deleteProgram(this.shader);
|
||||
}
|
||||
}
|
||||
|
||||
export interface Uniform {
|
||||
|
@ -226,6 +222,22 @@ export class Uniform3fv implements Uniform {
|
|||
}
|
||||
}
|
||||
|
||||
export class Uniform3f implements Uniform {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
|
||||
constructor(x: number, y: number, z: number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
|
||||
gl.uniform3f(location, this.x ,this.y, this.z);
|
||||
}
|
||||
}
|
||||
|
||||
export class Uniform1iv implements Uniform {
|
||||
data: number[];
|
||||
constructor(data: number[]) {
|
||||
|
@ -265,9 +277,9 @@ export class Uniform2f implements Uniform {
|
|||
x: number;
|
||||
y: number;
|
||||
|
||||
constructor(x: number, y: number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
constructor(xy: number[]) {
|
||||
this.x = xy[0];
|
||||
this.y = xy[1];
|
||||
}
|
||||
|
||||
setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
|
||||
|
@ -281,25 +293,14 @@ export class Uniform4f implements Uniform {
|
|||
v2: number;
|
||||
v3: number;
|
||||
|
||||
constructor(vec: number[]) {
|
||||
this.v0 = vec[0];
|
||||
this.v1 = vec[1];
|
||||
this.v2 = vec[2];
|
||||
this.v3 = vec[3];
|
||||
constructor(xyzw: number[]) {
|
||||
this.v0 = xyzw[0];
|
||||
this.v1 = xyzw[1];
|
||||
this.v2 = xyzw[2];
|
||||
this.v3 = xyzw[3];
|
||||
}
|
||||
|
||||
setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
|
||||
gl.uniform4f(location, this.v0, this.v1, this.v2, this.v3);
|
||||
}
|
||||
}
|
||||
|
||||
export class UniformMatrix3fv implements Uniform {
|
||||
data: number[];
|
||||
constructor(data: number[]) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
|
||||
gl.uniformMatrix3fv(location, false, this.data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ export class FPSCounter {
|
|||
|
||||
frame(now: number) {
|
||||
this.count += 1;
|
||||
if (now - this.last > 1) {
|
||||
if (now - this.last > 1000) {
|
||||
this.last = now;
|
||||
console.log(this.count + " fps");
|
||||
this.count = 0;
|
||||
|
@ -47,83 +47,122 @@ export class FPSCounter {
|
|||
}
|
||||
}
|
||||
|
||||
export class M3 {
|
||||
_data: any;
|
||||
export class Resizer {
|
||||
hoovering: boolean;
|
||||
dragging: boolean;
|
||||
|
||||
constructor(data: any) {
|
||||
this._data = data;
|
||||
}
|
||||
mouse_pos: number[];
|
||||
last_drag: number[];
|
||||
|
||||
static ident(): M3 {
|
||||
return new M3([
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1
|
||||
]);
|
||||
}
|
||||
viewbox: number[];
|
||||
orig_viewbox: number[];
|
||||
|
||||
multiply(other: M3): M3 {
|
||||
const a = this._data;
|
||||
const b = other._data;
|
||||
el_width: number;
|
||||
|
||||
var a00 = a[0 * 3 + 0];
|
||||
var a01 = a[0 * 3 + 1];
|
||||
var a02 = a[0 * 3 + 2];
|
||||
var a10 = a[1 * 3 + 0];
|
||||
var a11 = a[1 * 3 + 1];
|
||||
var a12 = a[1 * 3 + 2];
|
||||
var a20 = a[2 * 3 + 0];
|
||||
var a21 = a[2 * 3 + 1];
|
||||
var a22 = a[2 * 3 + 2];
|
||||
var b00 = b[0 * 3 + 0];
|
||||
var b01 = b[0 * 3 + 1];
|
||||
var b02 = b[0 * 3 + 2];
|
||||
var b10 = b[1 * 3 + 0];
|
||||
var b11 = b[1 * 3 + 1];
|
||||
var b12 = b[1 * 3 + 2];
|
||||
var b20 = b[2 * 3 + 0];
|
||||
var b21 = b[2 * 3 + 1];
|
||||
var b22 = b[2 * 3 + 2];
|
||||
scaleX = 1;
|
||||
scaleY = 1;
|
||||
|
||||
return new M3([
|
||||
b00 * a00 + b01 * a10 + b02 * a20,
|
||||
b00 * a01 + b01 * a11 + b02 * a21,
|
||||
b00 * a02 + b01 * a12 + b02 * a22,
|
||||
b10 * a00 + b11 * a10 + b12 * a20,
|
||||
b10 * a01 + b11 * a11 + b12 * a21,
|
||||
b10 * a02 + b11 * a12 + b12 * a22,
|
||||
b20 * a00 + b21 * a10 + b22 * a20,
|
||||
b20 * a01 + b21 * a11 + b22 * a21,
|
||||
b20 * a02 + b21 * a12 + b22 * a22,
|
||||
]);
|
||||
}
|
||||
constructor(el: HTMLCanvasElement, viewbox: number[], keep_aspect_ratio=false) {
|
||||
console.log("viewbox:" + viewbox);
|
||||
this.hoovering = false;
|
||||
this.dragging = false;
|
||||
|
||||
translation(x: number, y: number): M3 {
|
||||
const out = [...this._data];
|
||||
out[6] += x;
|
||||
out[7] += y;
|
||||
return new M3(out);
|
||||
}
|
||||
this.mouse_pos = [0, 0];
|
||||
this.last_drag = [0, 0];
|
||||
|
||||
rotate(rad: number): M3 {
|
||||
var c = Math.cos(rad);
|
||||
var s = Math.sin(rad);
|
||||
this.viewbox = [...viewbox];
|
||||
|
||||
const out = new M3([...this._data]);
|
||||
if (keep_aspect_ratio) {
|
||||
const or_width = this.viewbox[2];
|
||||
const or_height = this.viewbox[3];
|
||||
const scaleX = el.height / el.width;
|
||||
if (scaleX < 1) {
|
||||
this.scaleX= 1 / scaleX;
|
||||
|
||||
return out.multiply(new M3([
|
||||
c, -s, 0,
|
||||
s, c, 0,
|
||||
0, 0, 1
|
||||
]));
|
||||
}
|
||||
this.viewbox[2] *= this.scaleX;
|
||||
} else {
|
||||
this.scaleY = scaleX;
|
||||
this.viewbox[3] *= scaleX;
|
||||
}
|
||||
|
||||
scale(s_x: number, s_y = s_x, s_z = 1): M3 {
|
||||
const out = new M3([...this._data]);
|
||||
return out.multiply(new M3([
|
||||
s_x, 0, 0,
|
||||
0, s_y, 0,
|
||||
0, 0, s_z,
|
||||
]));
|
||||
}
|
||||
this.viewbox[0] -= (this.viewbox[2] - or_width) / 2;
|
||||
this.viewbox[1] -= (this.viewbox[3] - or_height) / 2;
|
||||
}
|
||||
|
||||
this.orig_viewbox = [...this.viewbox];
|
||||
|
||||
this.el_width = el.width;
|
||||
|
||||
el.addEventListener("mouseenter", this.mouseenter.bind(this), { capture: false, passive: true});
|
||||
el.addEventListener("mouseleave", this.mouseleave.bind(this), { capture: false, passive: true});
|
||||
el.addEventListener("mousemove", this.mousemove.bind(this), { capture: false, passive: true});
|
||||
el.addEventListener("mousedown", this.mousedown.bind(this), { capture: false, passive: true});
|
||||
el.addEventListener("mouseup", this.mouseup.bind(this), { capture: false, passive: true});
|
||||
|
||||
window.addEventListener('wheel', this.wheel.bind(this), { capture: false, passive: true});
|
||||
}
|
||||
|
||||
clip_viewbox() {
|
||||
this.viewbox[0] = Math.max(this.viewbox[0], this.orig_viewbox[0]);
|
||||
this.viewbox[1] = Math.max(this.viewbox[1], this.orig_viewbox[1]);
|
||||
|
||||
this.viewbox[0] = Math.min(this.viewbox[0] + this.viewbox[2], this.orig_viewbox[0] + this.orig_viewbox[2]) - this.viewbox[2];
|
||||
this.viewbox[1] = Math.min(this.viewbox[1] + this.viewbox[3], this.orig_viewbox[1] + this.orig_viewbox[3]) - this.viewbox[3];
|
||||
}
|
||||
|
||||
mouseenter() {
|
||||
this.hoovering = true;
|
||||
}
|
||||
|
||||
mouseleave() {
|
||||
this.hoovering = false;
|
||||
this.dragging = false;
|
||||
}
|
||||
|
||||
mousemove(e: MouseEvent) {
|
||||
this.mouse_pos = [e.offsetX, this.el_width - e.offsetY];
|
||||
|
||||
if (this.dragging) {
|
||||
const scale = this.viewbox[3] / this.orig_viewbox[3];
|
||||
this.viewbox[0] += (this.last_drag[0] - this.mouse_pos[0]) * scale;
|
||||
this.viewbox[1] += (this.last_drag[1] - this.mouse_pos[1]) * scale;
|
||||
|
||||
this.last_drag = [...this.mouse_pos];
|
||||
|
||||
this.clip_viewbox();
|
||||
}
|
||||
}
|
||||
|
||||
mousedown() {
|
||||
this.dragging = true;
|
||||
this.last_drag = [...this.mouse_pos];
|
||||
}
|
||||
|
||||
mouseup() {
|
||||
this.dragging = false;
|
||||
}
|
||||
|
||||
wheel(e: WheelEvent) {
|
||||
if (this.hoovering) {
|
||||
const dx = e.deltaY * this.scaleX;
|
||||
this.viewbox[2] += dx;
|
||||
this.viewbox[0] -= dx / 2;
|
||||
this.viewbox[2] = Math.min(this.viewbox[2], this.orig_viewbox[2]);
|
||||
|
||||
const dy = e.deltaY * this.scaleY;
|
||||
this.viewbox[3] += dy;
|
||||
this.viewbox[1] -= dy / 2;
|
||||
this.viewbox[3] = Math.min(this.viewbox[3], this.orig_viewbox[3]);
|
||||
|
||||
this.clip_viewbox();
|
||||
}
|
||||
}
|
||||
|
||||
get_viewbox(): number[] {
|
||||
return this.viewbox;
|
||||
}
|
||||
|
||||
get_mouse_pos(): number[] {
|
||||
return this.mouse_pos;
|
||||
}
|
||||
}
|
||||
|
|
20
frontend/www/webgl/webgl-utils.d.ts
vendored
Normal file
20
frontend/www/webgl/webgl-utils.d.ts
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]
|
||||
// Project: [~THE PROJECT NAME~]
|
||||
// Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>
|
||||
|
||||
/*~ This is the module template file. You should rename it to index.d.ts
|
||||
*~ and place it in a folder with the same name as the module.
|
||||
*~ For example, if you were writing a file for "super-greeter", this
|
||||
*~ file should be 'super-greeter/index.d.ts'
|
||||
*/
|
||||
|
||||
// /*~ If this module is a UMD module that exposes a global variable 'myLib' when
|
||||
// *~ loaded outside a module loader environment, declare that global here.
|
||||
// *~ Otherwise, delete this declaration.
|
||||
// */
|
||||
// export as namespace myLib;
|
||||
|
||||
/*~ If this module has methods, declare them as functions like so.
|
||||
*/
|
||||
export function createProgramFromScripts(gl: WebGLRenderingContext, shaderScriptIds: string[], opt_attribs?: string[], opt_locations?: number[], opt_errorCallback?: any): any;
|
||||
export function resizeCanvasToDisplaySize(canvas: HTMLCanvasElement, multiplier?: number): boolean;
|
1293
frontend/www/webgl/webgl-utils.js
Normal file
1293
frontend/www/webgl/webgl-utils.js
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue