mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-05 15:28:29 +08:00
All texture viewer features are now supported by the TextureWebglRenderer.
This commit is contained in:
parent
85ccdbb0a6
commit
9960d745c2
@ -25,14 +25,26 @@ export function floor_mod(dividend: number, divisor: number): number {
|
|||||||
return ((dividend % divisor) + divisor) % divisor;
|
return ((dividend % divisor) + divisor) % divisor;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Matrix4 {
|
export class Vec2 {
|
||||||
static of(...values: readonly number[]): Matrix4 {
|
constructor(public x: number, public y: number) {}
|
||||||
return new Matrix4(new Float32Array(values));
|
}
|
||||||
|
|
||||||
|
export function vec2_diff(v: Vec2, w: Vec2): Vec2 {
|
||||||
|
return new Vec2(v.x - w.x, v.y - w.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Vec3 {
|
||||||
|
constructor(public x: number, public y: number, public z: number) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Mat4 {
|
||||||
|
static of(...values: readonly number[]): Mat4 {
|
||||||
|
return new Mat4(new Float32Array(values));
|
||||||
}
|
}
|
||||||
|
|
||||||
static identity(): Matrix4 {
|
static identity(): Mat4 {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return Matrix4.of(
|
return Mat4.of(
|
||||||
1, 0, 0, 0,
|
1, 0, 0, 0,
|
||||||
0, 1, 0, 0,
|
0, 1, 0, 0,
|
||||||
0, 0, 1, 0,
|
0, 0, 1, 0,
|
||||||
@ -45,9 +57,19 @@ export class Matrix4 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function matrix4_product(a: Matrix4, b: Matrix4): Matrix4 {
|
export function mat4_product(a: Mat4, b: Mat4): Mat4 {
|
||||||
const array = new Float32Array(16);
|
const c = new Mat4(new Float32Array(16));
|
||||||
|
mat4_product_into_array(c.data, a, b);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mat4_multiply(a: Mat4, b: Mat4): void {
|
||||||
|
const array = new Float32Array(16);
|
||||||
|
mat4_product_into_array(array, a, b);
|
||||||
|
a.data.set(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mat4_product_into_array(array: Float32Array, a: Mat4, b: Mat4): void {
|
||||||
for (let i = 0; i < 4; i++) {
|
for (let i = 0; i < 4; i++) {
|
||||||
for (let j = 0; j < 4; j++) {
|
for (let j = 0; j < 4; j++) {
|
||||||
for (let k = 0; k < 4; k++) {
|
for (let k = 0; k < 4; k++) {
|
||||||
@ -55,6 +77,8 @@ export function matrix4_product(a: Matrix4, b: Matrix4): Matrix4 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return new Matrix4(array);
|
|
||||||
|
export class Quat {
|
||||||
|
constructor(public x: number, public y: number, public z: number, public w: number) {}
|
||||||
}
|
}
|
||||||
|
56
src/core/rendering/Camera.ts
Normal file
56
src/core/rendering/Camera.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { Mat4, Vec3 } from "../math";
|
||||||
|
import { Mat4Transform, Transform } from "./Transform";
|
||||||
|
|
||||||
|
export class Camera {
|
||||||
|
private readonly look_at: Vec3 = new Vec3(0, 0, 0);
|
||||||
|
private x_rot: number = 0;
|
||||||
|
private y_rot: number = 0;
|
||||||
|
private z_rot: number = 0;
|
||||||
|
private _zoom: number = 1;
|
||||||
|
private readonly _transform = new Mat4Transform(Mat4.identity());
|
||||||
|
|
||||||
|
get transform(): Transform {
|
||||||
|
return this._transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
pan(x: number, y: number, z: number): this {
|
||||||
|
this.look_at.x += x;
|
||||||
|
this.look_at.y += y;
|
||||||
|
this.look_at.z += z;
|
||||||
|
this.update_transform();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increase (or decrease) zoom by a factor.
|
||||||
|
*/
|
||||||
|
zoom(factor: number): this {
|
||||||
|
this._zoom *= factor;
|
||||||
|
this.look_at.x *= factor;
|
||||||
|
this.look_at.y *= factor;
|
||||||
|
this.look_at.z *= factor;
|
||||||
|
this.update_transform();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): this {
|
||||||
|
this.look_at.x = 0;
|
||||||
|
this.look_at.y = 0;
|
||||||
|
this.look_at.z = 0;
|
||||||
|
this.x_rot = 0;
|
||||||
|
this.y_rot = 0;
|
||||||
|
this.z_rot = 0;
|
||||||
|
this._zoom = 1;
|
||||||
|
this.update_transform();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private update_transform(): void {
|
||||||
|
this._transform.data[3] = -this.look_at.x;
|
||||||
|
this._transform.data[7] = -this.look_at.y;
|
||||||
|
this._transform.data[11] = -this.look_at.z;
|
||||||
|
this._transform.data[0] = this._zoom;
|
||||||
|
this._transform.data[5] = this._zoom;
|
||||||
|
this._transform.data[10] = this._zoom;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
import { Matrix4 } from "../math";
|
import { Mat4 } from "../math";
|
||||||
import { GL, VERTEX_POS_LOC, VERTEX_TEX_LOC } from "./VertexFormat";
|
import { GL, VERTEX_POS_LOC, VERTEX_TEX_LOC } from "./VertexFormat";
|
||||||
import { Texture } from "./Texture";
|
|
||||||
|
|
||||||
export class ShaderProgram {
|
export class ShaderProgram {
|
||||||
private readonly gl: GL;
|
private readonly gl: GL;
|
||||||
@ -51,13 +50,12 @@ export class ShaderProgram {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set_transform(matrix: Matrix4): void {
|
set_transform_uniform(matrix: Mat4): void {
|
||||||
this.gl.uniformMatrix4fv(this.transform_loc, true, matrix.data);
|
this.gl.uniformMatrix4fv(this.transform_loc, true, matrix.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_texture(texture: Texture): void {
|
set_texture_uniform(unit: GLenum): void {
|
||||||
const gl = this.gl;
|
this.gl.uniform1i(this.tex_sampler_loc, unit - this.gl.TEXTURE0);
|
||||||
gl.uniform1i(this.tex_sampler_loc, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bind(): void {
|
bind(): void {
|
||||||
|
@ -1,15 +1,23 @@
|
|||||||
import { Matrix4 } from "../math";
|
import { Mat4 } from "../math";
|
||||||
|
|
||||||
export interface Transform {
|
export interface Transform {
|
||||||
readonly matrix4: Matrix4;
|
readonly mat4: Mat4;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Mat4Transform implements Transform {
|
||||||
|
readonly data: Float32Array;
|
||||||
|
|
||||||
|
constructor(readonly mat4: Mat4) {
|
||||||
|
this.data = mat4.data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TranslateTransform implements Transform {
|
export class TranslateTransform implements Transform {
|
||||||
readonly matrix4: Matrix4;
|
readonly mat4: Mat4;
|
||||||
|
|
||||||
constructor(x: number, y: number, z: number) {
|
constructor(x: number, y: number, z: number) {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
this.matrix4 = Matrix4.of(
|
this.mat4 = Mat4.of(
|
||||||
1, 0, 0, x,
|
1, 0, 0, x,
|
||||||
0, 1, 0, y,
|
0, 1, 0, y,
|
||||||
0, 0, 1, z,
|
0, 0, 1, z,
|
||||||
@ -19,11 +27,11 @@ export class TranslateTransform implements Transform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class IdentityTransform implements Transform {
|
export class IdentityTransform implements Transform {
|
||||||
readonly matrix4: Matrix4;
|
readonly mat4: Mat4;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
this.matrix4 = Matrix4.of(
|
this.mat4 = Mat4.of(
|
||||||
1, 0, 0, 0,
|
1, 0, 0, 0,
|
||||||
0, 1, 0, 0,
|
0, 1, 0, 0,
|
||||||
0, 0, 1, 0,
|
0, 0, 1, 0,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Renderer } from "./Renderer";
|
import { Renderer } from "./Renderer";
|
||||||
import { Matrix4, matrix4_product } from "../math";
|
import { Mat4, mat4_product, Vec2, vec2_diff } from "../math";
|
||||||
import { ShaderProgram } from "./ShaderProgram";
|
import { ShaderProgram } from "./ShaderProgram";
|
||||||
import { GL } from "./VertexFormat";
|
import { GL } from "./VertexFormat";
|
||||||
import { Scene } from "./Scene";
|
import { Scene } from "./Scene";
|
||||||
@ -9,17 +9,17 @@ import {
|
|||||||
POS_TEX_VERTEX_SHADER_SOURCE,
|
POS_TEX_VERTEX_SHADER_SOURCE,
|
||||||
POS_VERTEX_SHADER_SOURCE,
|
POS_VERTEX_SHADER_SOURCE,
|
||||||
} from "./shader_sources";
|
} from "./shader_sources";
|
||||||
import { LogManager } from "../Logger";
|
import { Camera } from "./Camera";
|
||||||
|
|
||||||
const logger = LogManager.get("core/rendering/WebglRenderer");
|
|
||||||
|
|
||||||
export class WebglRenderer extends Renderer {
|
export class WebglRenderer extends Renderer {
|
||||||
private readonly gl: GL;
|
private readonly gl: GL;
|
||||||
private readonly shader_programs: ShaderProgram[];
|
private readonly shader_programs: ShaderProgram[];
|
||||||
private render_scheduled = false;
|
private animation_frame?: number;
|
||||||
private projection!: Matrix4;
|
private projection!: Mat4;
|
||||||
|
private pointer_pos?: Vec2;
|
||||||
|
|
||||||
protected readonly scene: Scene;
|
protected readonly scene: Scene;
|
||||||
|
protected readonly camera = new Camera();
|
||||||
|
|
||||||
readonly canvas_element: HTMLCanvasElement;
|
readonly canvas_element: HTMLCanvasElement;
|
||||||
|
|
||||||
@ -38,6 +38,7 @@ export class WebglRenderer extends Renderer {
|
|||||||
|
|
||||||
gl.enable(gl.DEPTH_TEST);
|
gl.enable(gl.DEPTH_TEST);
|
||||||
gl.enable(gl.CULL_FACE);
|
gl.enable(gl.CULL_FACE);
|
||||||
|
gl.clearColor(0.1, 0.1, 0.1, 1);
|
||||||
|
|
||||||
this.shader_programs = [
|
this.shader_programs = [
|
||||||
new ShaderProgram(gl, POS_VERTEX_SHADER_SOURCE, POS_FRAG_SHADER_SOURCE),
|
new ShaderProgram(gl, POS_VERTEX_SHADER_SOURCE, POS_FRAG_SHADER_SOURCE),
|
||||||
@ -48,7 +49,8 @@ export class WebglRenderer extends Renderer {
|
|||||||
|
|
||||||
this.set_size(800, 600);
|
this.set_size(800, 600);
|
||||||
|
|
||||||
requestAnimationFrame(this.render);
|
this.canvas_element.addEventListener("mousedown", this.mousedown);
|
||||||
|
this.canvas_element.addEventListener("wheel", this.wheel, { passive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose(): void {
|
dispose(): void {
|
||||||
@ -60,15 +62,21 @@ export class WebglRenderer extends Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
start_rendering(): void {
|
start_rendering(): void {
|
||||||
// TODO
|
this.schedule_render();
|
||||||
}
|
}
|
||||||
|
|
||||||
stop_rendering(): void {
|
stop_rendering(): void {
|
||||||
// TODO
|
if (this.animation_frame != undefined) {
|
||||||
|
cancelAnimationFrame(this.animation_frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.animation_frame = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
schedule_render = (): void => {
|
schedule_render = (): void => {
|
||||||
this.render_scheduled = true;
|
if (this.animation_frame == undefined) {
|
||||||
|
this.animation_frame = requestAnimationFrame(this.render);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
set_size(width: number, height: number): void {
|
set_size(width: number, height: number): void {
|
||||||
@ -77,32 +85,37 @@ export class WebglRenderer extends Renderer {
|
|||||||
this.gl.viewport(0, 0, width, height);
|
this.gl.viewport(0, 0, width, height);
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
this.projection = Matrix4.of(
|
this.projection = Mat4.of(
|
||||||
2/width, 0, 0, 0,
|
2/width, 0, 0, 0,
|
||||||
0, 2/height, 0, 0,
|
0, 2/height, 0, 0,
|
||||||
0, 0, 2/10, 0,
|
0, 0, 2/10, 0,
|
||||||
0, 0, 0, 1,
|
0, 0, 0, 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.schedule_render();
|
||||||
}
|
}
|
||||||
|
|
||||||
private render = (): void => {
|
private render = (): void => {
|
||||||
|
this.animation_frame = undefined;
|
||||||
const gl = this.gl;
|
const gl = this.gl;
|
||||||
|
|
||||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
this.scene.traverse((node, parent_transform) => {
|
const camera_project_mat = mat4_product(this.projection, this.camera.transform.mat4);
|
||||||
const transform = matrix4_product(parent_transform, node.transform.matrix4);
|
|
||||||
|
this.scene.traverse((node, parent_mat) => {
|
||||||
|
const mat = mat4_product(parent_mat, node.transform.mat4);
|
||||||
|
|
||||||
if (node.mesh) {
|
if (node.mesh) {
|
||||||
const program = this.shader_programs[node.mesh.format];
|
const program = this.shader_programs[node.mesh.format];
|
||||||
program.bind();
|
program.bind();
|
||||||
|
|
||||||
program.set_transform(transform);
|
program.set_transform_uniform(mat);
|
||||||
|
|
||||||
if (node.mesh.texture) {
|
if (node.mesh.texture) {
|
||||||
gl.activeTexture(gl.TEXTURE0);
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
node.mesh.texture.bind(gl);
|
node.mesh.texture.bind(gl);
|
||||||
program.set_texture(node.mesh.texture);
|
program.set_texture_uniform(gl.TEXTURE0);
|
||||||
}
|
}
|
||||||
|
|
||||||
node.mesh.render(gl);
|
node.mesh.render(gl);
|
||||||
@ -111,9 +124,43 @@ export class WebglRenderer extends Renderer {
|
|||||||
program.unbind();
|
program.unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
return transform;
|
return mat;
|
||||||
}, this.projection);
|
}, camera_project_mat);
|
||||||
|
};
|
||||||
|
|
||||||
requestAnimationFrame(this.render);
|
private mousedown = (evt: MouseEvent): void => {
|
||||||
|
if (evt.buttons === 1) {
|
||||||
|
this.pointer_pos = new Vec2(evt.clientX, evt.clientY);
|
||||||
|
|
||||||
|
window.addEventListener("mousemove", this.mousemove);
|
||||||
|
window.addEventListener("mouseup", this.mouseup);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private mousemove = (evt: MouseEvent): void => {
|
||||||
|
if (evt.buttons === 1) {
|
||||||
|
const new_pos = new Vec2(evt.clientX, evt.clientY);
|
||||||
|
const diff = vec2_diff(new_pos, this.pointer_pos!);
|
||||||
|
this.camera.pan(-diff.x, diff.y, 0);
|
||||||
|
this.pointer_pos = new_pos;
|
||||||
|
this.schedule_render();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private mouseup = (): void => {
|
||||||
|
this.pointer_pos = undefined;
|
||||||
|
|
||||||
|
window.removeEventListener("mousemove", this.mousemove);
|
||||||
|
window.removeEventListener("mouseup", this.mouseup);
|
||||||
|
};
|
||||||
|
|
||||||
|
private wheel = (evt: WheelEvent): void => {
|
||||||
|
if (evt.deltaY < 0) {
|
||||||
|
this.camera.zoom(1.1);
|
||||||
|
} else {
|
||||||
|
this.camera.zoom(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.schedule_render();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -60,8 +60,8 @@ export function initialize_viewer(
|
|||||||
let renderer: Renderer;
|
let renderer: Renderer;
|
||||||
|
|
||||||
if (gui_store.feature_active("renderer")) {
|
if (gui_store.feature_active("renderer")) {
|
||||||
const { WebglTextureRenderer } = await import("./rendering/WebglTextureRenderer");
|
const { TextureWebglRenderer } = await import("./rendering/TextureWebglRenderer");
|
||||||
renderer = new WebglTextureRenderer(controller);
|
renderer = new TextureWebglRenderer(controller);
|
||||||
} else {
|
} else {
|
||||||
const { TextureRenderer } = await import("./rendering/TextureRenderer");
|
const { TextureRenderer } = await import("./rendering/TextureRenderer");
|
||||||
renderer = new TextureRenderer(controller, create_three_renderer());
|
renderer = new TextureRenderer(controller, create_three_renderer());
|
||||||
|
@ -10,7 +10,7 @@ import { Texture, TextureFormat } from "../../core/rendering/Texture";
|
|||||||
|
|
||||||
const logger = LogManager.get("viewer/rendering/WebglTextureRenderer");
|
const logger = LogManager.get("viewer/rendering/WebglTextureRenderer");
|
||||||
|
|
||||||
export class WebglTextureRenderer extends WebglRenderer {
|
export class TextureWebglRenderer extends WebglRenderer {
|
||||||
private readonly disposer = new Disposer();
|
private readonly disposer = new Disposer();
|
||||||
|
|
||||||
constructor(ctrl: TextureController) {
|
constructor(ctrl: TextureController) {
|
||||||
@ -18,7 +18,9 @@ export class WebglTextureRenderer extends WebglRenderer {
|
|||||||
|
|
||||||
this.disposer.add_all(
|
this.disposer.add_all(
|
||||||
ctrl.textures.observe(({ value: textures }) => {
|
ctrl.textures.observe(({ value: textures }) => {
|
||||||
this.render_textures(textures);
|
this.scene.delete();
|
||||||
|
this.camera.reset();
|
||||||
|
this.create_quads(textures);
|
||||||
this.schedule_render();
|
this.schedule_render();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -29,9 +31,7 @@ export class WebglTextureRenderer extends WebglRenderer {
|
|||||||
this.disposer.dispose();
|
this.disposer.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private render_textures(textures: readonly XvrTexture[]): void {
|
private create_quads(textures: readonly XvrTexture[]): void {
|
||||||
this.scene.delete();
|
|
||||||
|
|
||||||
let total_width = 10 * (textures.length - 1); // 10px spacing between textures.
|
let total_width = 10 * (textures.length - 1); // 10px spacing between textures.
|
||||||
let total_height = 0;
|
let total_height = 0;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user