diff --git a/client/components/camera.ts b/client/components/camera.ts index 77d4078..ff43ae2 100644 --- a/client/components/camera.ts +++ b/client/components/camera.ts @@ -13,29 +13,3 @@ export class Camera extends Component { near = 0.1; far = 1000; } - -export function screen_to_world( - screen_x: number, - screen_y: number, - camera: Camera, - screen_width: number, - screen_height: number, -) { - // return { - // x: (screen_x - screen_width / 2) / camera.zoom + camera.x, - // y: (screen_y - screen_height / 2) / camera.zoom + camera.y, - // }; -} - -export function world_to_screen( - world_x: number, - world_y: number, - camera: Camera, - screen_width: number, - screen_height: number, -) { - // return { - // x: (world_x - camera.x) / camera.zoom + screen_width / 2, - // y: (world_y - camera.y) / camera.zoom + screen_height / 2, - // }; -} diff --git a/client/components/dimension.ts b/client/components/dimension.ts index 6ff2158..9eb49a3 100644 --- a/client/components/dimension.ts +++ b/client/components/dimension.ts @@ -24,6 +24,8 @@ export interface Chunk { vertexBuffer?: WebGLBuffer; vertexCount?: number; dirty: boolean; + vertexTransparentBuffer?: WebGLBuffer; + vertexTransparentCount?: number; } export class Dimension extends Component { diff --git a/client/generation.ts b/client/generation.ts index ae8a8f2..970c886 100644 --- a/client/generation.ts +++ b/client/generation.ts @@ -35,7 +35,7 @@ export function generate( // simple terrain layers if (y === terrain_height) { - id = "bworld:grass"; + id = "bworld:leaves"; } else if (y > terrain_height - 4) { id = "bworld:dirt"; } diff --git a/client/systems/clickable_system.ts b/client/systems/clickable_system.ts index 50e27a8..9c5cc9a 100644 --- a/client/systems/clickable_system.ts +++ b/client/systems/clickable_system.ts @@ -5,49 +5,10 @@ import { ClientWorld } from "../client_world.ts"; import { ClickableSprite } from "../components/clickable.ts"; import { point_inside_rec } from "$/common/utils.ts"; import { InputManager } from "../input_manager.ts"; -import { Camera, screen_to_world } from "../components/camera.ts"; +import { Camera } from "../components/camera.ts"; import { canvas } from "../renderer/mod.ts"; export class ClickableSystem extends System { update(world: ClientWorld, _delta: number): void { - const [player] = world.get_tag("player")!; - const camera = player.get(Camera)!; - - const mouse = InputManager.get_mouse_position(); - - const world_mouse = screen_to_world( - mouse.x, - mouse.y, - camera, - canvas.width, - canvas.height, - ); - - for (const entity of world.get_entities()) { - const position = entity.get(Position); - - if (!position) { - continue; - } - - const clickable_sprite = entity.get(ClickableSprite); - const sprite = entity.get(Sprite); - - if (clickable_sprite && sprite) { - clickable_sprite.clicked = false; - const hovered = point_inside_rec( - world_mouse.x, - world_mouse.y, - position.x, - position.y, - sprite.width, - sprite.height, - ); - if (hovered && InputManager.is_mouse_pressed(clickable_sprite.button)) { - InputManager.consume_mouse(clickable_sprite.button); - clickable_sprite.clicked = true; - } - } - } } } diff --git a/client/systems/render_system.ts b/client/systems/render_system.ts index 6a08125..6d8d38f 100644 --- a/client/systems/render_system.ts +++ b/client/systems/render_system.ts @@ -30,7 +30,7 @@ export class RenderSystem extends System { const dimension = entity.get(Dimension); if (dimension) { - render_dimension(dimension /*, camera*/); + render_dimension(dimension, camera); } } diff --git a/client/systems/rendering/dimension.ts b/client/systems/rendering/dimension.ts index af702d3..165038c 100644 --- a/client/systems/rendering/dimension.ts +++ b/client/systems/rendering/dimension.ts @@ -1,24 +1,32 @@ -import { get_sprite_region } from "$/common/utils.ts"; +import { distance_point_point, get_sprite_region } from "$/common/utils.ts"; import { Chunk, CHUNK_SIDE_SIZE, Dimension } from "$/client/components/dimension.ts"; import { TEXTURE_SIZE } from "$/common/constants.ts"; import { EverythingRegistry, TileRegistry } from "$/common/everything_registry.ts"; import { flush_buffer, gl, push_cube_to_mesh, set_current_texture } from "$/client/renderer/mod.ts"; +import { Camera } from "$/client/components/camera.ts"; -export function render_dimension(dimension: Dimension /*, camera: Camera*/) { +export function render_dimension(dimension: Dimension, camera: Camera) { set_current_texture(dimension.image.tex); for (const chunk of dimension.chunks) { if (chunk.dirty) { - if (chunk.vertexBuffer) { + if (chunk.vertexBuffer && chunk.vertexTransparentBuffer) { gl.deleteBuffer(chunk.vertexBuffer); + gl.deleteBuffer(chunk.vertexTransparentBuffer); } chunk.dirty = false; - make_chunk_mesh(chunk, dimension); + make_chunk_mesh(chunk, dimension, camera); } - render_chunk(chunk); + render_chunk_opaque(chunk); + } + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + gl.depthMask(false); + for (const chunk of dimension.chunks) { + render_chunk_transparent(chunk); } } -function render_chunk(chunk: Chunk) { +function render_chunk_opaque(chunk: Chunk) { if (!chunk.vertexBuffer || !chunk.vertexCount) { return; } @@ -26,27 +34,41 @@ function render_chunk(chunk: Chunk) { flush_buffer(chunk.vertexBuffer, chunk.vertexCount); } -function make_chunk_mesh(chunk: Chunk, dimension: Dimension) { +function render_chunk_transparent(chunk: Chunk) { + if (!chunk.vertexTransparentBuffer || !chunk.vertexTransparentCount) { + return; + } + + flush_buffer(chunk.vertexTransparentBuffer, chunk.vertexTransparentCount); +} + +function make_chunk_mesh(chunk: Chunk, dimension: Dimension, camera: Camera) { const vertices: number[] = []; + const transparent_vertices: number[] = []; let count = 0; + let transparent_count = 0; + const transparent_blocks: [ + number, + number, + number, + number, + number, + boolean, + boolean, + boolean, + boolean, + boolean, + boolean, + ][] = []; const blocks_registry = EverythingRegistry.get_registry("blocks"); for (let i = 0; i < chunk.blocks.length; i += 1) { const block_nid = chunk.blocks[i]; if (block_nid === 0) { continue; } - const [x, y, z] = dimension.index_to_xyz(i); - - const wx = chunk.x * CHUNK_SIDE_SIZE + x; - const wz = chunk.z * CHUNK_SIDE_SIZE + z; - const front = dimension.get_block(wx, y, wz + 1) === 0; - const back = dimension.get_block(wx, y, wz - 1) === 0; - const left = dimension.get_block(wx - 1, y, wz) === 0; - const right = dimension.get_block(wx + 1, y, wz) === 0; - const top = dimension.get_block(wx, y + 1, wz) === 0; - const bottom = dimension.get_block(wx, y - 1, wz) === 0; const tile_info = blocks_registry[block_nid]; + let texture_id = "bworld:missing"; if (tile_info?.texture_id) { if (typeof tile_info.texture_id === "string") { @@ -56,32 +78,106 @@ function make_chunk_mesh(chunk: Chunk, dimension: Dimension) { } } + const [x, y, z] = dimension.index_to_xyz(i); + + const wx = chunk.x * CHUNK_SIDE_SIZE + x; + const wz = chunk.z * CHUNK_SIDE_SIZE + z; + + const show_face = (x: number, y: number, z: number) => { + const block = dimension.get_block(x, y, z); + if (block === 0) { + return true; + } + const block_info = blocks_registry[block]; + if (block_info?.transparent) { + return true; + } + return false; + }; + const front = show_face(wx, y, wz + 1); + const back = show_face(wx, y, wz - 1); + const left = show_face(wx - 1, y, wz); + const right = show_face(wx + 1, y, wz); + const top = show_face(wx, y + 1, wz); + const bottom = show_face(wx, y - 1, wz); + const region = get_sprite_region(texture_id); - count = push_cube_to_mesh( - vertices, - count, + if (tile_info.transparent) { + transparent_blocks.push([ + wx, + y, + wz, + region.x * TEXTURE_SIZE, + region.y * TEXTURE_SIZE, + front, + back, + left, + right, + top, + bottom, + ]); + } else { + count = push_cube_to_mesh( + vertices, + count, + dimension.image, + wx, + y, + wz, + 1, + 1, + 1, + region.x * TEXTURE_SIZE, + region.y * TEXTURE_SIZE, + TEXTURE_SIZE, + TEXTURE_SIZE, + 1, + 1, + 1, + 1, + front, + back, + left, + right, + top, + bottom, + ); + } + } + + transparent_blocks.sort((a, b) => { + const da = distance_point_point(a[0], a[1], a[2], camera.x, camera.y, camera.z); + const db = distance_point_point(b[0], b[1], b[2], camera.x, camera.y, camera.z); + + return da - db; + }); + + for (const block of transparent_blocks) { + transparent_count = push_cube_to_mesh( + transparent_vertices, + transparent_count, dimension.image, - wx, - y, - wz, + block[0], + block[1], + block[2], 1, 1, 1, - region.x * TEXTURE_SIZE, - region.y * TEXTURE_SIZE, + block[3], + block[4], TEXTURE_SIZE, TEXTURE_SIZE, 1, 1, 1, 1, - front, - back, - left, - right, - top, - bottom, + block[5], + block[6], + block[7], + block[8], + block[9], + block[10], ); } @@ -94,4 +190,14 @@ function make_chunk_mesh(chunk: Chunk, dimension: Dimension) { new Float32Array(vertices), gl.STATIC_DRAW, ); + + chunk.vertexTransparentBuffer = gl.createBuffer(); + chunk.vertexTransparentCount = transparent_count / 9; + + gl.bindBuffer(gl.ARRAY_BUFFER, chunk.vertexTransparentBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array(transparent_vertices), + gl.STATIC_DRAW, + ); } diff --git a/client/tiles/log.ts b/client/tiles/log.ts new file mode 100644 index 0000000..368440d --- /dev/null +++ b/client/tiles/log.ts @@ -0,0 +1,12 @@ +import { EverythingRegistry, TileRegistry } from "$/common/everything_registry.ts"; + +EverythingRegistry.register("blocks", "bworld:log", { + texture_id: "bworld:log_side", + has_collision: true, +}); + +EverythingRegistry.register("blocks", "bworld:leaves", { + texture_id: "bworld:leaves", + has_collision: true, + transparent: true, +}); diff --git a/client/tiles/mod.ts b/client/tiles/mod.ts index feb1635..8d71187 100644 --- a/client/tiles/mod.ts +++ b/client/tiles/mod.ts @@ -6,3 +6,4 @@ import "./water.ts"; import "./chest.ts"; import "./furnace.ts"; import "./stone.ts"; +import "./log.ts"; diff --git a/common/everything_registry.ts b/common/everything_registry.ts index 38f47ed..67498c8 100644 --- a/common/everything_registry.ts +++ b/common/everything_registry.ts @@ -53,6 +53,7 @@ export class EverythingRegistry { export interface TileRegistry { texture_id: string | ((tile: Block) => string); has_collision: boolean; + transparent?: boolean; on_create?(world: ClientWorld, tile: Block): void; on_click?(world: ClientWorld, tile: Block): void; diff --git a/common/utils.ts b/common/utils.ts index 6a14f83..1115078 100644 --- a/common/utils.ts +++ b/common/utils.ts @@ -36,3 +36,10 @@ export function distance_point_rectangle(px: number, py: number, sqx: number, sq return Math.sqrt(dx * dx + dy * dy); } + +export function distance_point_point(ax: number, ay: number, az: number, bx: number, by: number, bz: number) { + const dx = ax - bx; + const dy = ay - by; + const dz = az - bz; + return dx * dx + dy * dy + dz * dz; +}