Camera
This commit is contained in:
@@ -5,6 +5,9 @@ export class Camera extends Component {
|
|||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
zoom: number;
|
zoom: number;
|
||||||
|
rotation = 0;
|
||||||
|
offset_x = 0;
|
||||||
|
offset_y = 0;
|
||||||
|
|
||||||
constructor(x = 0, y = 0, zoom = 1) {
|
constructor(x = 0, y = 0, zoom = 1) {
|
||||||
super();
|
super();
|
||||||
|
|||||||
+71
-12
@@ -1,3 +1,5 @@
|
|||||||
|
import { Camera } from "./components/camera.ts";
|
||||||
|
|
||||||
const MAX_SPRITES = 10000;
|
const MAX_SPRITES = 10000;
|
||||||
const VERTS_PER_SPRITE = 6;
|
const VERTS_PER_SPRITE = 6;
|
||||||
const FLOATS_PER_VERT = 4;
|
const FLOATS_PER_VERT = 4;
|
||||||
@@ -7,9 +9,19 @@ export let canvas: HTMLCanvasElement;
|
|||||||
let program: WebGLProgram;
|
let program: WebGLProgram;
|
||||||
let buffer: WebGLBuffer;
|
let buffer: WebGLBuffer;
|
||||||
|
|
||||||
|
const default_camera = new Camera();
|
||||||
|
let camera: Camera = default_camera;
|
||||||
|
|
||||||
|
let camera_pos_loc: WebGLUniformLocation | null;
|
||||||
|
let camera_zoom_loc: WebGLUniformLocation | null;
|
||||||
|
let camera_rot_loc: WebGLUniformLocation | null;
|
||||||
|
let camera_offset_loc: WebGLUniformLocation | null;
|
||||||
|
|
||||||
const vertex_data = new Float32Array(MAX_SPRITES * VERTS_PER_SPRITE * FLOATS_PER_VERT);
|
const vertex_data = new Float32Array(MAX_SPRITES * VERTS_PER_SPRITE * FLOATS_PER_VERT);
|
||||||
let vert_index = 0;
|
let vert_index = 0;
|
||||||
|
|
||||||
|
let pos_loc: GLint;
|
||||||
|
let uv_loc: GLint;
|
||||||
let resolution_loc: WebGLUniformLocation | null;
|
let resolution_loc: WebGLUniformLocation | null;
|
||||||
let _texture_loc: WebGLUniformLocation | null;
|
let _texture_loc: WebGLUniformLocation | null;
|
||||||
|
|
||||||
@@ -22,18 +34,46 @@ const glyphs: Record<string, { u0: number; v0: number; u1: number; v1: number }>
|
|||||||
const vertex_src = `
|
const vertex_src = `
|
||||||
attribute vec2 aPos;
|
attribute vec2 aPos;
|
||||||
attribute vec2 aUV;
|
attribute vec2 aUV;
|
||||||
|
|
||||||
varying vec2 vUV;
|
varying vec2 vUV;
|
||||||
|
|
||||||
uniform vec2 uResolution;
|
uniform vec2 uResolution;
|
||||||
|
|
||||||
|
uniform vec2 uCameraPos;
|
||||||
|
uniform float uCameraZoom;
|
||||||
|
uniform float uCameraRot;
|
||||||
|
uniform vec2 uCameraOffset;
|
||||||
|
|
||||||
void main(){
|
void main(){
|
||||||
vec2 zero = aPos / uResolution;
|
|
||||||
|
// world → camera space
|
||||||
|
vec2 pos = aPos - uCameraPos;
|
||||||
|
|
||||||
|
// rotation
|
||||||
|
float c = cos(uCameraRot);
|
||||||
|
float s = sin(uCameraRot);
|
||||||
|
pos = vec2(
|
||||||
|
pos.x * c - pos.y * s,
|
||||||
|
pos.x * s + pos.y * c
|
||||||
|
);
|
||||||
|
|
||||||
|
// zoom
|
||||||
|
pos *= uCameraZoom;
|
||||||
|
|
||||||
|
// screen offset (center camera)
|
||||||
|
pos += uCameraOffset;
|
||||||
|
|
||||||
|
// convert to clip space
|
||||||
|
vec2 zero = pos / uResolution;
|
||||||
vec2 clip = zero * 2.0 - 1.0;
|
vec2 clip = zero * 2.0 - 1.0;
|
||||||
gl_Position = vec4(clip * vec2(1,-1),0,1);
|
|
||||||
|
gl_Position = vec4(clip * vec2(1.0,-1.0),0.0,1.0);
|
||||||
|
|
||||||
vUV = aUV;
|
vUV = aUV;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const fragmentSrc = `
|
const fragment_src = `
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
varying vec2 vUV;
|
varying vec2 vUV;
|
||||||
uniform sampler2D uTex;
|
uniform sampler2D uTex;
|
||||||
@@ -60,7 +100,7 @@ export function init_window(canvas_element: HTMLCanvasElement) {
|
|||||||
|
|
||||||
gl = ctx;
|
gl = ctx;
|
||||||
|
|
||||||
program = create_program(vertex_src, fragmentSrc);
|
program = create_program(vertex_src, fragment_src);
|
||||||
|
|
||||||
buffer = gl.createBuffer()!;
|
buffer = gl.createBuffer()!;
|
||||||
|
|
||||||
@@ -68,17 +108,22 @@ export function init_window(canvas_element: HTMLCanvasElement) {
|
|||||||
|
|
||||||
const stride = FLOATS_PER_VERT * 4;
|
const stride = FLOATS_PER_VERT * 4;
|
||||||
|
|
||||||
const pos_loc = gl.getAttribLocation(program, "aPos");
|
pos_loc = gl.getAttribLocation(program, "aPos");
|
||||||
gl.enableVertexAttribArray(pos_loc);
|
gl.enableVertexAttribArray(pos_loc);
|
||||||
gl.vertexAttribPointer(pos_loc, 2, gl.FLOAT, false, stride, 0);
|
gl.vertexAttribPointer(pos_loc, 2, gl.FLOAT, false, stride, 0);
|
||||||
|
|
||||||
const uv_loc = gl.getAttribLocation(program, "aUV");
|
uv_loc = gl.getAttribLocation(program, "aUV");
|
||||||
gl.enableVertexAttribArray(uv_loc);
|
gl.enableVertexAttribArray(uv_loc);
|
||||||
gl.vertexAttribPointer(uv_loc, 2, gl.FLOAT, false, stride, 8);
|
gl.vertexAttribPointer(uv_loc, 2, gl.FLOAT, false, stride, 8);
|
||||||
|
|
||||||
resolution_loc = gl.getUniformLocation(program, "uResolution");
|
resolution_loc = gl.getUniformLocation(program, "uResolution");
|
||||||
_texture_loc = gl.getUniformLocation(program, "uTex");
|
_texture_loc = gl.getUniformLocation(program, "uTex");
|
||||||
|
|
||||||
|
camera_pos_loc = gl.getUniformLocation(program, "uCameraPos");
|
||||||
|
camera_zoom_loc = gl.getUniformLocation(program, "uCameraZoom");
|
||||||
|
camera_rot_loc = gl.getUniformLocation(program, "uCameraRot");
|
||||||
|
camera_offset_loc = gl.getUniformLocation(program, "uCameraOffset");
|
||||||
|
|
||||||
gl.useProgram(program);
|
gl.useProgram(program);
|
||||||
gl.uniform2f(resolution_loc, canvas.width, canvas.height);
|
gl.uniform2f(resolution_loc, canvas.width, canvas.height);
|
||||||
|
|
||||||
@@ -250,12 +295,17 @@ function flush_batch() {
|
|||||||
|
|
||||||
gl.useProgram(program);
|
gl.useProgram(program);
|
||||||
|
|
||||||
const posLoc = gl.getAttribLocation(program, "aPos");
|
//const posLoc = gl.getAttribLocation(program, "aPos");
|
||||||
const uvLoc = gl.getAttribLocation(program, "aUV");
|
//const uvLoc = gl.getAttribLocation(program, "aUV");
|
||||||
gl.enableVertexAttribArray(posLoc);
|
gl.enableVertexAttribArray(pos_loc);
|
||||||
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 16, 0);
|
gl.vertexAttribPointer(pos_loc, 2, gl.FLOAT, false, 16, 0);
|
||||||
gl.enableVertexAttribArray(uvLoc);
|
gl.enableVertexAttribArray(uv_loc);
|
||||||
gl.vertexAttribPointer(uvLoc, 2, gl.FLOAT, false, 16, 8);
|
gl.vertexAttribPointer(uv_loc, 2, gl.FLOAT, false, 16, 8);
|
||||||
|
|
||||||
|
gl.uniform2f(camera_pos_loc, Math.floor(camera.x), Math.floor(camera.y));
|
||||||
|
gl.uniform1f(camera_zoom_loc, camera.zoom);
|
||||||
|
gl.uniform1f(camera_rot_loc, camera.rotation);
|
||||||
|
gl.uniform2f(camera_offset_loc, camera.offset_x, camera.offset_y);
|
||||||
|
|
||||||
gl.bindTexture(gl.TEXTURE_2D, current_texture);
|
gl.bindTexture(gl.TEXTURE_2D, current_texture);
|
||||||
gl.drawArrays(gl.TRIANGLES, 0, vert_index / 4);
|
gl.drawArrays(gl.TRIANGLES, 0, vert_index / 4);
|
||||||
@@ -356,3 +406,12 @@ export function resize_canvas() {
|
|||||||
gl.uniform2f(resolution_loc, width, height);
|
gl.uniform2f(resolution_loc, width, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function begin_mode_2d(new_camera: Camera) {
|
||||||
|
camera = new_camera;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function end_mode_2d() {
|
||||||
|
flush_batch();
|
||||||
|
camera = default_camera;
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { PlayerControls } from "../components/player_controls.ts";
|
|||||||
import { PlayerInventory } from "../components/inventory.ts";
|
import { PlayerInventory } from "../components/inventory.ts";
|
||||||
import { Camera } from "../components/camera.ts";
|
import { Camera } from "../components/camera.ts";
|
||||||
import { Position } from "../../common/components/position.ts";
|
import { Position } from "../../common/components/position.ts";
|
||||||
|
import { canvas } from "../renderer.ts";
|
||||||
|
|
||||||
export class PlayerControlsSystem extends System {
|
export class PlayerControlsSystem extends System {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -66,6 +67,8 @@ export class PlayerControlsSystem extends System {
|
|||||||
|
|
||||||
camera.x = Math.round(position.x);
|
camera.x = Math.round(position.x);
|
||||||
camera.y = Math.round(position.y);
|
camera.y = Math.round(position.y);
|
||||||
|
camera.offset_x = Math.floor(canvas.width / 2);
|
||||||
|
camera.offset_y = Math.floor(canvas.height / 2);
|
||||||
|
|
||||||
const scroll = InputManager.get_wheel_delta();
|
const scroll = InputManager.get_wheel_delta();
|
||||||
if (scroll > 0) {
|
if (scroll > 0) {
|
||||||
@@ -73,18 +76,5 @@ export class PlayerControlsSystem extends System {
|
|||||||
} else if (scroll < 0) {
|
} else if (scroll < 0) {
|
||||||
player_inventory.hotbar_selected = Math.max(0, player_inventory.hotbar_selected - 1);
|
player_inventory.hotbar_selected = Math.max(0, player_inventory.hotbar_selected - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- APPLY BOUNDS ---
|
|
||||||
/*if (camera.bounds) {
|
|
||||||
camera.x = Math.max(
|
|
||||||
camera.bounds.min_x,
|
|
||||||
Math.min(camera.x, camera.bounds.max_x),
|
|
||||||
);
|
|
||||||
|
|
||||||
camera.y = Math.max(
|
|
||||||
camera.bounds.min_y,
|
|
||||||
Math.min(camera.y, camera.bounds.max_y),
|
|
||||||
);
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { Camera } from "$/client/components/camera.ts";
|
|||||||
import { render_animated_sprite, render_sprite } from "./rendering/sprites.ts";
|
import { render_animated_sprite, render_sprite } from "./rendering/sprites.ts";
|
||||||
import { render_dimension } from "./rendering/dimension.ts";
|
import { render_dimension } from "./rendering/dimension.ts";
|
||||||
import { render_player_inventory } from "./rendering/player.ts";
|
import { render_player_inventory } from "./rendering/player.ts";
|
||||||
|
import { begin_mode_2d, end_mode_2d } from "../renderer.ts";
|
||||||
|
|
||||||
export class RenderSystem extends System {
|
export class RenderSystem extends System {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -23,18 +24,20 @@ export class RenderSystem extends System {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
begin_mode_2d(camera);
|
||||||
|
|
||||||
for (const entity of world.get_entities()) {
|
for (const entity of world.get_entities()) {
|
||||||
const position = entity.get(Position);
|
const position = entity.get(Position);
|
||||||
const sprite = entity.get(Sprite);
|
const sprite = entity.get(Sprite);
|
||||||
|
|
||||||
if (sprite && position) {
|
if (sprite && position) {
|
||||||
render_sprite(sprite, position);
|
render_sprite(sprite, position, camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
const animated_sprite = entity.get(AnimatedSprite);
|
const animated_sprite = entity.get(AnimatedSprite);
|
||||||
|
|
||||||
if (animated_sprite && position) {
|
if (animated_sprite && position) {
|
||||||
render_animated_sprite(animated_sprite, position);
|
render_animated_sprite(animated_sprite, position, camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dimension = entity.get(Dimension);
|
const dimension = entity.get(Dimension);
|
||||||
@@ -42,7 +45,11 @@ export class RenderSystem extends System {
|
|||||||
if (dimension) {
|
if (dimension) {
|
||||||
render_dimension(dimension, camera);
|
render_dimension(dimension, camera);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end_mode_2d();
|
||||||
|
|
||||||
|
for (const entity of world.get_entities()) {
|
||||||
const player_inventory = entity.get(PlayerInventory);
|
const player_inventory = entity.get(PlayerInventory);
|
||||||
|
|
||||||
if (player_inventory && player_inventory.is_open) {
|
if (player_inventory && player_inventory.is_open) {
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { get_sprite_region } from "$/common/utils.ts";
|
|||||||
import { Dimension } from "$/client/components/dimension.ts";
|
import { Dimension } from "$/client/components/dimension.ts";
|
||||||
import { TEXTURE_SIZE, TILE_SIZE } from "$/common/constants.ts";
|
import { TEXTURE_SIZE, TILE_SIZE } from "$/common/constants.ts";
|
||||||
import { Camera, world_to_screen } from "$/client/components/camera.ts";
|
import { Camera, world_to_screen } from "$/client/components/camera.ts";
|
||||||
import { canvas, draw_texture_region } from "../../renderer.ts";
|
import { canvas, draw_texture_region } from "$/client/renderer.ts";
|
||||||
|
|
||||||
export function render_dimension(tilemap: Dimension, camera: Camera) {
|
export function render_dimension(dimension: Dimension, camera: Camera) {
|
||||||
for (const tile of tilemap.tiles) {
|
for (const tile of dimension.tiles) {
|
||||||
const region = get_sprite_region(tile.id);
|
const region = get_sprite_region(tile.id);
|
||||||
|
|
||||||
const x = tile.x * TILE_SIZE;
|
const x = tile.x * TILE_SIZE;
|
||||||
@@ -20,7 +20,7 @@ export function render_dimension(tilemap: Dimension, camera: Camera) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
draw_texture_region(
|
draw_texture_region(
|
||||||
tilemap.image,
|
dimension.image,
|
||||||
region.x * TEXTURE_SIZE,
|
region.x * TEXTURE_SIZE,
|
||||||
region.y * TEXTURE_SIZE,
|
region.y * TEXTURE_SIZE,
|
||||||
TEXTURE_SIZE,
|
TEXTURE_SIZE,
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { Position } from "$/common/components/position.ts";
|
import { Position } from "$/common/components/position.ts";
|
||||||
import { AnimatedSprite, Sprite } from "$/client/components/sprite.ts";
|
import { AnimatedSprite, Sprite } from "$/client/components/sprite.ts";
|
||||||
import { draw_texture_region } from "../../renderer.ts";
|
import { draw_texture_region } from "$/client/renderer.ts";
|
||||||
import { AssetManager } from "../../assets.ts";
|
import { Camera } from "$/client/components/camera.ts";
|
||||||
|
|
||||||
export function render_sprite(sprite: Sprite, position: Position) {
|
// TODO: camera scale
|
||||||
|
// TODO: only render if inside screen
|
||||||
|
|
||||||
|
export function render_sprite(sprite: Sprite, position: Position, _camera: Camera) {
|
||||||
draw_texture_region(
|
draw_texture_region(
|
||||||
sprite.image,
|
sprite.image,
|
||||||
sprite.source_x,
|
sprite.source_x,
|
||||||
@@ -22,6 +25,7 @@ export function render_sprite(sprite: Sprite, position: Position) {
|
|||||||
export function render_animated_sprite(
|
export function render_animated_sprite(
|
||||||
animated_sprite: AnimatedSprite,
|
animated_sprite: AnimatedSprite,
|
||||||
position: Position,
|
position: Position,
|
||||||
|
_camera: Camera,
|
||||||
) {
|
) {
|
||||||
const current_animation = animated_sprite.states[animated_sprite.current_state];
|
const current_animation = animated_sprite.states[animated_sprite.current_state];
|
||||||
if (!current_animation) {
|
if (!current_animation) {
|
||||||
@@ -30,7 +34,7 @@ export function render_animated_sprite(
|
|||||||
}
|
}
|
||||||
|
|
||||||
draw_texture_region(
|
draw_texture_region(
|
||||||
AssetManager.instance.get("bworld:player"),
|
animated_sprite.image,
|
||||||
current_animation.source_x[animated_sprite.animation_frame],
|
current_animation.source_x[animated_sprite.animation_frame],
|
||||||
current_animation.source_y[animated_sprite.animation_frame],
|
current_animation.source_y[animated_sprite.animation_frame],
|
||||||
current_animation.source_width,
|
current_animation.source_width,
|
||||||
|
|||||||
Reference in New Issue
Block a user