mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-04 22:58: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;
|
||||
}
|
||||
|
||||
export class Matrix4 {
|
||||
static of(...values: readonly number[]): Matrix4 {
|
||||
return new Matrix4(new Float32Array(values));
|
||||
export class Vec2 {
|
||||
constructor(public x: number, public y: number) {}
|
||||
}
|
||||
|
||||
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
|
||||
return Matrix4.of(
|
||||
return Mat4.of(
|
||||
1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
@ -45,9 +57,19 @@ export class Matrix4 {
|
||||
}
|
||||
}
|
||||
|
||||
export function matrix4_product(a: Matrix4, b: Matrix4): Matrix4 {
|
||||
const array = new Float32Array(16);
|
||||
export function mat4_product(a: Mat4, b: Mat4): Mat4 {
|
||||
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 j = 0; j < 4; j++) {
|
||||
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 { Texture } from "./Texture";
|
||||
|
||||
export class ShaderProgram {
|
||||
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);
|
||||
}
|
||||
|
||||
set_texture(texture: Texture): void {
|
||||
const gl = this.gl;
|
||||
gl.uniform1i(this.tex_sampler_loc, 0);
|
||||
set_texture_uniform(unit: GLenum): void {
|
||||
this.gl.uniform1i(this.tex_sampler_loc, unit - this.gl.TEXTURE0);
|
||||
}
|
||||
|
||||
bind(): void {
|
||||
|
@ -1,15 +1,23 @@
|
||||
import { Matrix4 } from "../math";
|
||||
import { Mat4 } from "../math";
|
||||
|
||||
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 {
|
||||
readonly matrix4: Matrix4;
|
||||
readonly mat4: Mat4;
|
||||
|
||||
constructor(x: number, y: number, z: number) {
|
||||
// prettier-ignore
|
||||
this.matrix4 = Matrix4.of(
|
||||
this.mat4 = Mat4.of(
|
||||
1, 0, 0, x,
|
||||
0, 1, 0, y,
|
||||
0, 0, 1, z,
|
||||
@ -19,11 +27,11 @@ export class TranslateTransform implements Transform {
|
||||
}
|
||||
|
||||
export class IdentityTransform implements Transform {
|
||||
readonly matrix4: Matrix4;
|
||||
readonly mat4: Mat4;
|
||||
|
||||
constructor() {
|
||||
// prettier-ignore
|
||||
this.matrix4 = Matrix4.of(
|
||||
this.mat4 = Mat4.of(
|
||||
1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Renderer } from "./Renderer";
|
||||
import { Matrix4, matrix4_product } from "../math";
|
||||
import { Mat4, mat4_product, Vec2, vec2_diff } from "../math";
|
||||
import { ShaderProgram } from "./ShaderProgram";
|
||||
import { GL } from "./VertexFormat";
|
||||
import { Scene } from "./Scene";
|
||||
@ -9,17 +9,17 @@ import {
|
||||
POS_TEX_VERTEX_SHADER_SOURCE,
|
||||
POS_VERTEX_SHADER_SOURCE,
|
||||
} from "./shader_sources";
|
||||
import { LogManager } from "../Logger";
|
||||
|
||||
const logger = LogManager.get("core/rendering/WebglRenderer");
|
||||
import { Camera } from "./Camera";
|
||||
|
||||
export class WebglRenderer extends Renderer {
|
||||
private readonly gl: GL;
|
||||
private readonly shader_programs: ShaderProgram[];
|
||||
private render_scheduled = false;
|
||||
private projection!: Matrix4;
|
||||
private animation_frame?: number;
|
||||
private projection!: Mat4;
|
||||
private pointer_pos?: Vec2;
|
||||
|
||||
protected readonly scene: Scene;
|
||||
protected readonly camera = new Camera();
|
||||
|
||||
readonly canvas_element: HTMLCanvasElement;
|
||||
|
||||
@ -38,6 +38,7 @@ export class WebglRenderer extends Renderer {
|
||||
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
gl.enable(gl.CULL_FACE);
|
||||
gl.clearColor(0.1, 0.1, 0.1, 1);
|
||||
|
||||
this.shader_programs = [
|
||||
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);
|
||||
|
||||
requestAnimationFrame(this.render);
|
||||
this.canvas_element.addEventListener("mousedown", this.mousedown);
|
||||
this.canvas_element.addEventListener("wheel", this.wheel, { passive: true });
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@ -60,15 +62,21 @@ export class WebglRenderer extends Renderer {
|
||||
}
|
||||
|
||||
start_rendering(): void {
|
||||
// TODO
|
||||
this.schedule_render();
|
||||
}
|
||||
|
||||
stop_rendering(): void {
|
||||
// TODO
|
||||
if (this.animation_frame != undefined) {
|
||||
cancelAnimationFrame(this.animation_frame);
|
||||
}
|
||||
|
||||
this.animation_frame = undefined;
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -77,32 +85,37 @@ export class WebglRenderer extends Renderer {
|
||||
this.gl.viewport(0, 0, width, height);
|
||||
|
||||
// prettier-ignore
|
||||
this.projection = Matrix4.of(
|
||||
this.projection = Mat4.of(
|
||||
2/width, 0, 0, 0,
|
||||
0, 2/height, 0, 0,
|
||||
0, 0, 2/10, 0,
|
||||
0, 0, 0, 1,
|
||||
);
|
||||
|
||||
this.schedule_render();
|
||||
}
|
||||
|
||||
private render = (): void => {
|
||||
this.animation_frame = undefined;
|
||||
const gl = this.gl;
|
||||
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
|
||||
this.scene.traverse((node, parent_transform) => {
|
||||
const transform = matrix4_product(parent_transform, node.transform.matrix4);
|
||||
const camera_project_mat = mat4_product(this.projection, this.camera.transform.mat4);
|
||||
|
||||
this.scene.traverse((node, parent_mat) => {
|
||||
const mat = mat4_product(parent_mat, node.transform.mat4);
|
||||
|
||||
if (node.mesh) {
|
||||
const program = this.shader_programs[node.mesh.format];
|
||||
program.bind();
|
||||
|
||||
program.set_transform(transform);
|
||||
program.set_transform_uniform(mat);
|
||||
|
||||
if (node.mesh.texture) {
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
node.mesh.texture.bind(gl);
|
||||
program.set_texture(node.mesh.texture);
|
||||
program.set_texture_uniform(gl.TEXTURE0);
|
||||
}
|
||||
|
||||
node.mesh.render(gl);
|
||||
@ -111,9 +124,43 @@ export class WebglRenderer extends Renderer {
|
||||
program.unbind();
|
||||
}
|
||||
|
||||
return transform;
|
||||
}, this.projection);
|
||||
return mat;
|
||||
}, 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;
|
||||
|
||||
if (gui_store.feature_active("renderer")) {
|
||||
const { WebglTextureRenderer } = await import("./rendering/WebglTextureRenderer");
|
||||
renderer = new WebglTextureRenderer(controller);
|
||||
const { TextureWebglRenderer } = await import("./rendering/TextureWebglRenderer");
|
||||
renderer = new TextureWebglRenderer(controller);
|
||||
} else {
|
||||
const { TextureRenderer } = await import("./rendering/TextureRenderer");
|
||||
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");
|
||||
|
||||
export class WebglTextureRenderer extends WebglRenderer {
|
||||
export class TextureWebglRenderer extends WebglRenderer {
|
||||
private readonly disposer = new Disposer();
|
||||
|
||||
constructor(ctrl: TextureController) {
|
||||
@ -18,7 +18,9 @@ export class WebglTextureRenderer extends WebglRenderer {
|
||||
|
||||
this.disposer.add_all(
|
||||
ctrl.textures.observe(({ value: textures }) => {
|
||||
this.render_textures(textures);
|
||||
this.scene.delete();
|
||||
this.camera.reset();
|
||||
this.create_quads(textures);
|
||||
this.schedule_render();
|
||||
}),
|
||||
);
|
||||
@ -29,9 +31,7 @@ export class WebglTextureRenderer extends WebglRenderer {
|
||||
this.disposer.dispose();
|
||||
}
|
||||
|
||||
private render_textures(textures: readonly XvrTexture[]): void {
|
||||
this.scene.delete();
|
||||
|
||||
private create_quads(textures: readonly XvrTexture[]): void {
|
||||
let total_width = 10 * (textures.length - 1); // 10px spacing between textures.
|
||||
let total_height = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user