From 1332eaf0f0c65bf6b952fd92bb25e7b5e3ce65da Mon Sep 17 00:00:00 2001 From: L3P3 Date: Mon, 9 Sep 2024 14:05:08 +0200 Subject: [PATCH] in-block view --- package.json | 2 +- src/etc/constants.js | 3 +- src/game/c_settings.js | 2 +- src/game/m_game.js | 3 +- src/game/m_renderer.js | 599 ++++++++++++++++++++++------------------- 5 files changed, 329 insertions(+), 280 deletions(-) diff --git a/package.json b/package.json index 37a24e7..1458205 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "minicraft", - "version": "0.11.1", + "version": "0.11.2", "description": "voxel-based 3d game, written in javascript", "homepage": "https://l3p3.de/minicraft", "repository": { diff --git a/src/etc/constants.js b/src/etc/constants.js index 1856714..3cd5c21 100644 --- a/src/etc/constants.js +++ b/src/etc/constants.js @@ -78,13 +78,14 @@ export const BLOCK_COLORS = [ 0xe3eaed, // QUARTZ_BLOCK ]; -export const BLOCK_TYPE_FACE_LABELS = 'WEBTSN'.split(''); +export const BLOCK_TYPE_FACE_LABELS = 'WEBTSNI'; export const BLOCK_TYPE_FACE_W = 0; export const BLOCK_TYPE_FACE_E = 1; export const BLOCK_TYPE_FACE_B = 2; export const BLOCK_TYPE_FACE_T = 3; export const BLOCK_TYPE_FACE_S = 4; export const BLOCK_TYPE_FACE_N = 5; +export const BLOCK_TYPE_FACE_I = 6; export const SKY_COLOR = 0xffb184; diff --git a/src/game/c_settings.js b/src/game/c_settings.js index ed305f8..056a01a 100644 --- a/src/game/c_settings.js +++ b/src/game/c_settings.js @@ -140,7 +140,7 @@ export default function Settings({ }), ]), node_dom(`label[innerText=${locale_pixel_grouping}:]`, null, [ - node_dom('input[type=range][min=1][max=6][step=1]', { + node_dom('input[type=range][min=1][max=8][step=1]', { value: config.pixel_grouping, onchange: event => ( config_set({ diff --git a/src/game/m_game.js b/src/game/m_game.js index c70d942..d59f731 100644 --- a/src/game/m_game.js +++ b/src/game/m_game.js @@ -10,6 +10,7 @@ import { BLOCK_TYPE_DIRT, BLOCK_TYPE_FACE_B, BLOCK_TYPE_FACE_E, + BLOCK_TYPE_FACE_N, BLOCK_TYPE_FACE_S, BLOCK_TYPE_FACE_T, BLOCK_TYPE_FACE_W, @@ -455,7 +456,7 @@ export const game_key = (model, code, state) => { case BLOCK_TYPE_FACE_B: --y; break; case BLOCK_TYPE_FACE_T: ++y; break; case BLOCK_TYPE_FACE_S: --z; break; - default: ++z; + case BLOCK_TYPE_FACE_N: ++z; } if ( y >= 0 && diff --git a/src/game/m_renderer.js b/src/game/m_renderer.js index b02a318..3db66f7 100644 --- a/src/game/m_renderer.js +++ b/src/game/m_renderer.js @@ -1,12 +1,15 @@ import { BLOCK_COLORS, BLOCK_TYPE_AIR, + BLOCK_TYPE_FACE_I, BLOCK_TYPE_FACE_LABELS, CHUNK_HEIGHT, CHUNK_HEIGHT_L2, CHUNK_WIDTH_L2, COORDINATE_OFFSET, GAMEMODE_CREATIVE, + GAMEMODE_SPECTATOR, + ITEM_HANDLES, PLAYER_FOCUS_DISTANCE_CREATIVE, PLAYER_FOCUS_DISTANCE_NORMAL, SKY_COLOR, @@ -23,6 +26,7 @@ import { Math_floor, Math_min, Math_PI_180d, + Math_random, Math_round, Math_sin, Math_sqrt, @@ -142,6 +146,7 @@ export const renderer_render = (model, now) => { } = game; //let check_count = 0; + let block_inside = 0; if ( !world.flag_paused || @@ -158,6 +163,7 @@ export const renderer_render = (model, now) => { angle_v, block_focus_x, block_focus_z, + gamemode, position_x, position_y, position_z, @@ -205,307 +211,346 @@ export const renderer_render = (model, now) => { player.block_focus_z = player.block_focus_face = 0; let dim_next = 0; - player.block_focus_y = -1; - - tiles_data = /** @type {Uint32Array!} */ (tiles_data); - - //canvas_surface_data.fill(0xff0000ff); - for (let canvas_y = 0; canvas_y < resolution_y; ++canvas_y) { - const canvas_y_relative = (resolution_y_h - canvas_y) * resolution_y_1d__fov_y; - - const step_y_raw = canvas_y_relative * angle_v_cos - angle_v_sin; - const step_z_rot = canvas_y_relative * angle_v_sin + angle_v_cos; - const step_z_rot_sin = step_z_rot * angle_h_sin; - const step_z_rot_cos = step_z_rot * angle_h_cos; - - let canvas_x = 0; - let group_color = 0; - let group_at_end = false; - let group_last_filled = false; + player.block_focus_y = -1; + block_inside = world_block_get( + world, + position_x_shifted & world_width_m1, + position_y_shifted & (CHUNK_HEIGHT - 1), + position_z_shifted & world_width_m1 + ); - group: for ( - let canvas_x_group = 0; - canvas_x_group < resolution_x; - canvas_x_group += group_width - ) - for ( - let group_index = group_last_filled ? 1 : 0; - group_index < group_width; - ++group_index - ) { - // pixel_grouping disabled - if (group_width < 2) canvas_x = canvas_x_group; - // first canvas_x_group - else if (canvas_x_group === 0) { - canvas_x = ( - group_index === 0 - ? 0 - : group_index === 1 - ? group_width - : group_index - 1 - ); - group_at_end = group_index === 1; - } - // middle canvas_x_group - else if (canvas_x_group < group_last) { - canvas_x = ( - ( - group_at_end = group_index === 1 - ) - ? canvas_x_group + group_width - : canvas_x_group + group_index - 1 - ); - } - // last canvas_x_group - else { - if ( - ( - canvas_x = ( - group_index === 0 - ? canvas_x_group - 1 - : canvas_x_group + group_index + // inside block? + if ( + gamemode !== GAMEMODE_SPECTATOR && + block_inside > 0 + ) { + player.block_focus_x = position_x_shifted & world_width_m1; + player.block_focus_y = position_y_shifted & (CHUNK_HEIGHT - 1); + player.block_focus_z = position_z_shifted & world_width_m1; + player.block_focus_face = BLOCK_TYPE_FACE_I; + canvas_surface_data.fill(BLOCK_COLORS[block_inside] | 0xff000000); + } + else { + tiles_data = /** @type {Uint32Array!} */ (tiles_data); + + for (let canvas_y = 0; canvas_y < resolution_y; ++canvas_y) { + const canvas_y_relative = (resolution_y_h - canvas_y) * resolution_y_1d__fov_y; + + const step_y_raw = canvas_y_relative * angle_v_cos - angle_v_sin; + const step_z_rot = canvas_y_relative * angle_v_sin + angle_v_cos; + const step_z_rot_sin = step_z_rot * angle_h_sin; + const step_z_rot_cos = step_z_rot * angle_h_cos; + + let canvas_x = 0; + let group_color = 0; + let group_at_end = false; + let group_last_filled = false; + + group: for ( + let canvas_x_group = 0; + canvas_x_group < resolution_x; + canvas_x_group += group_width + ) + for ( + let group_index = group_last_filled ? 1 : 0; + group_index < group_width; + ++group_index + ) { + // pixel_grouping disabled + if (group_width < 2) canvas_x = canvas_x_group; + // first canvas_x_group + else if (canvas_x_group === 0) { + canvas_x = ( + group_index === 0 + ? 0 + : group_index === 1 + ? group_width + : group_index - 1 + ); + group_at_end = group_index === 1; + } + // middle canvas_x_group + else if (canvas_x_group < group_last) { + canvas_x = ( + ( + group_at_end = group_index === 1 ) - ) >= resolution_x - ) break group; - group_at_end = false; - } + ? canvas_x_group + group_width + : canvas_x_group + group_index - 1 + ); + } + // last canvas_x_group + else { + if ( + ( + canvas_x = ( + group_index === 0 + ? canvas_x_group - 1 + : canvas_x_group + group_index + ) + ) >= resolution_x + ) break group; + group_at_end = false; + } - const canvas_x_relative = (canvas_x - resolution_x_h) * resolution_x_1d__fov_x; + const canvas_x_relative = (canvas_x - resolution_x_h) * resolution_x_1d__fov_x; - const step_x_raw = step_z_rot_sin + angle_h_cos * canvas_x_relative; - const step_z_raw = step_z_rot_cos - angle_h_sin * canvas_x_relative; + const step_x_raw = step_z_rot_sin + angle_h_cos * canvas_x_relative; + const step_z_raw = step_z_rot_cos - angle_h_sin * canvas_x_relative; - const dim_offset = dim_next; + const dim_offset = dim_next; - let pixel_color = SKY_COLOR; - let pixel_factor = 1.0; + let pixel_color = SKY_COLOR; + let pixel_factor = 1.0; - let check_distance_min = view_distance; - // step for each x, y, z - for (let dim_i = 0; dim_i < 3; ++dim_i) { - const dim = (dim_offset + dim_i) % 3; + let check_distance_min = view_distance; + let check_distance_start = 0; - // https://jsben.ch/AqXcR - let step_dim = step_z_raw; - // subblock distance to first intersection - let offset = position_z_shifted_rest; - if (dim === 0) { - step_dim = step_x_raw; - offset = position_x_shifted_rest; - } - if (dim === 1) { - step_dim = step_y_raw; - offset = position_y_shifted_rest; - } - let step_normal = -1 / step_dim; - if (step_dim > 0) { - offset = 1 - offset; - step_normal *= -1; + // make nearest blocks partly transparent + if ( + gamemode === GAMEMODE_SPECTATOR && + block_inside > 0 + ) { + check_distance_start = Math_random() * 10; } - // https://jsben.ch/hKgi4 - const step_x = step_x_raw * step_normal; - const step_y = step_y_raw * step_normal; - const step_z = step_z_raw * step_normal; - const step_diagonal = Math_sqrt( - step_x * step_x + - step_y * step_y + - step_z * step_z - ); - - // initial position - let check_x = position_x_shifted + step_x * offset - ((dim === 0)&(step_dim < 0)|0); - let check_y = position_y_shifted + step_y * offset - ((dim === 1)&(step_dim < 0)|0); - let check_z = position_z_shifted + step_z * offset - ((dim === 2)&(step_dim < 0)|0); - let check_distance = step_diagonal * offset; - - // add steps until collision or out of range - // https://jsben.ch/kM67J - for ( - let check_x_int, check_y_int, check_z_int, block; - check_distance < check_distance_min; - check_x += step_x, - check_y += step_y, - check_z += step_z, - check_distance += step_diagonal - ) { - if (check_y < COORDINATE_OFFSET) { - if (step_y < 0) break; - continue; + // step for each x, y, z + for (let dim_i = 0; dim_i < 3; ++dim_i) { + const dim = (dim_offset + dim_i) % 3; + + // https://jsben.ch/AqXcR + let step_dim = step_z_raw; + // subblock distance to first intersection + let offset = position_z_shifted_rest; + if (dim === 0) { + step_dim = step_x_raw; + offset = position_x_shifted_rest; } - if (check_y >= COORDINATE_OFFSET + CHUNK_HEIGHT) { - if (step_y > 0) break; - continue; + if (dim === 1) { + step_dim = step_y_raw; + offset = position_y_shifted_rest; } - //++check_count; - if ( - ( - block = blocks[ - ( + let step_normal = -1 / step_dim; + if (step_dim > 0) { + offset = 1 - offset; + step_normal *= -1; + } + + // https://jsben.ch/hKgi4 + const step_x = step_x_raw * step_normal; + const step_y = step_y_raw * step_normal; + const step_z = step_z_raw * step_normal; + const step_diagonal = Math_sqrt( + step_x * step_x + + step_y * step_y + + step_z * step_z + ); + + // initial position + let check_x = position_x_shifted + step_x * offset - ((dim === 0)&(step_dim < 0)|0); + let check_y = position_y_shifted + step_y * offset - ((dim === 1)&(step_dim < 0)|0); + let check_z = position_z_shifted + step_z * offset - ((dim === 2)&(step_dim < 0)|0); + let check_distance = step_diagonal * offset; + + // add steps until collision or out of range + // https://jsben.ch/kM67J + for ( + let check_x_int, check_y_int, check_z_int, block; + check_distance < check_distance_min; + check_x += step_x, + check_y += step_y, + check_z += step_z, + check_distance += step_diagonal + ) { + if ( + gamemode === GAMEMODE_SPECTATOR && + check_distance < check_distance_start + ) continue; + if (check_y < COORDINATE_OFFSET) { + if (step_y < 0) break; + continue; + } + if (check_y >= COORDINATE_OFFSET + CHUNK_HEIGHT) { + if (step_y > 0) break; + continue; + } + //++check_count; + if ( + ( + block = blocks[ ( - check_x_int = check_x & world_width_m1 - ) << world_width_l2 | + ( + check_x_int = check_x & world_width_m1 + ) << world_width_l2 | + ( + check_z_int = check_z & world_width_m1 + ) + ) << CHUNK_HEIGHT_L2 | ( - check_z_int = check_z & world_width_m1 + check_y_int = check_y & (CHUNK_HEIGHT - 1) ) - ) << CHUNK_HEIGHT_L2 | - ( - check_y_int = check_y & (CHUNK_HEIGHT - 1) - ) - ] - ) !== BLOCK_TYPE_AIR - ) { - // collision - - if ( - canvas_y === pixel_focus_y && - canvas_x === pixel_focus_x && - check_distance <= focus_distance_min + ] + ) !== BLOCK_TYPE_AIR ) { - // set focus - player.block_focus_x = check_x_int; - player.block_focus_y = check_y_int; - player.block_focus_z = check_z_int; - player.block_focus_face = (step_dim < 0) | dim << 1; - focus_distance_min = check_distance; - } - - // calculate color - if (flag_textures) { - // shift so that block id 1 => tile 0 - --block; - - if (dim === 1) { - if (block === TILE_LOG_SIDE) - block = TILE_LOG_TOP; - else if (block === TILE_BOOKSHELF) - block = TILE_PLANKS; - else if ( - block === TILE_GRASS_TOP && - step_y > 0 - ) block = TILE_DIRT; + // collision + + if ( + gamemode !== GAMEMODE_SPECTATOR && + canvas_y === pixel_focus_y && + canvas_x === pixel_focus_x && + check_distance <= focus_distance_min + ) { + // set focus + player.block_focus_x = check_x_int; + player.block_focus_y = check_y_int; + player.block_focus_z = check_z_int; + player.block_focus_face = (step_dim < 0) | dim << 1; + focus_distance_min = check_distance; } - else if (block === TILE_GRASS_TOP) - block = TILE_GRASS_SIDE; - // pick pixel - const texture_pixel = tiles_data[ - block << (TILES_RESOLUTION_LOG2 * 2) | - ( - // y + // calculate color + if (flag_textures) { + // shift so that block id 1 => tile 0 + --block; + + if (dim === 1) { + if (block === TILE_LOG_SIDE) + block = TILE_LOG_TOP; + else if (block === TILE_BOOKSHELF) + block = TILE_PLANKS; + else if ( + block === TILE_GRASS_TOP && + step_y > 0 + ) block = TILE_DIRT; + } + else if (block === TILE_GRASS_TOP) + block = TILE_GRASS_SIDE; + + // pick pixel + const texture_pixel = tiles_data[ + block << (TILES_RESOLUTION_LOG2 * 2) | + ( + // y + ( + dim === 1 + ? check_z + : check_y + ) * TILES_RESOLUTION & (TILES_RESOLUTION - 1) + ) << TILES_RESOLUTION_LOG2 | + // x ( dim === 1 - ? check_z - : check_y + ? check_x + : ( + step_dim > 0 + ? check_x - check_z + : check_z - check_x + ) + COORDINATE_OFFSET ) * TILES_RESOLUTION & (TILES_RESOLUTION - 1) - ) << TILES_RESOLUTION_LOG2 | - // x + ]; + + // transparent pixel? + if (texture_pixel >>> 24 === 0) continue; + + // solid pixel + pixel_color = texture_pixel & 0xffffff; + } + else pixel_color = BLOCK_COLORS[block]; + + check_distance_min = check_distance; + pixel_factor = ( + // fake shadow to see edges ( - dim === 1 - ? check_x - : ( - step_dim > 0 - ? check_x - check_z - : check_z - check_x - ) + COORDINATE_OFFSET - ) * TILES_RESOLUTION & (TILES_RESOLUTION - 1) - ]; - - // transparent pixel? - if (texture_pixel >>> 24 === 0) continue; - - // solid pixel - pixel_color = texture_pixel & 0xffffff; + dim === 0 + ? .8 + : dim === 2 + ? .6 + : step_dim > 0 + ? .4 + : 1 + ) + + // highlight if focussed + ( + check_y_int !== block_focus_y || + check_x_int !== block_focus_x || + check_z_int !== block_focus_z + ? 0 + : .2 + ) + ); + dim_next = dim; + break; } - else pixel_color = BLOCK_COLORS[block]; + else if ( + gamemode === GAMEMODE_SPECTATOR && + check_distance < check_distance_start + ) check_distance_start = check_distance; - check_distance_min = check_distance; - pixel_factor = ( - // fake shadow to see edges - ( - dim === 0 - ? .8 - : dim === 2 - ? .6 - : step_dim > 0 - ? .4 - : 1 - ) + - // highlight if focussed - ( - check_y_int !== block_focus_y || - check_x_int !== block_focus_x || - check_z_int !== block_focus_z - ? 0 - : .2 + // no collision + } + } + + // apply lighting and alpha + pixel_color = ( + 0xff000000 | + Math_min((pixel_color >> 16) * pixel_factor, 0xff) << 16 | + Math_min(((pixel_color >> 8) & 0xff) * pixel_factor, 0xff) << 8 | + Math_min((pixel_color & 0xff) * pixel_factor, 0xff) + ); + + if ( + group_last_filled = ( + ( + group_at_end = group_at_end && ( + canvas_y !== pixel_focus_y || + canvas_x < pixel_focus_x || + canvas_x > pixel_focus_x + group_width ) + ) && + group_color === pixel_color + ) + ) { + if (group_width > 6) { + canvas_surface_data.fill( + group_color, + canvas_surface_data_index_row + canvas_x_group + 1, + canvas_surface_data_index_row + canvas_x_group + group_width + 1 ); - dim_next = dim; break; } - - // no collision + let i = canvas_surface_data_index_row + canvas_x_group + 1; + canvas_surface_data[i] = group_color; + canvas_surface_data[++i] = group_color; + if (group_width < 3) break; + canvas_surface_data[++i] = group_color; + if (group_width < 4) break; + canvas_surface_data[++i] = group_color; + if (group_width < 5) break; + canvas_surface_data[++i] = group_color; + if (group_width < 6) break; + canvas_surface_data[++i] = group_color; + break; } + if ( + group_at_end || + canvas_x === 0 + ) group_color = pixel_color; + canvas_surface_data[canvas_surface_data_index_row + canvas_x] = pixel_color; } - // apply lighting and alpha - pixel_color = ( - 0xff000000 | - Math_min((pixel_color >> 16) * pixel_factor, 0xff) << 16 | - Math_min(((pixel_color >> 8) & 0xff) * pixel_factor, 0xff) << 8 | - Math_min((pixel_color & 0xff) * pixel_factor, 0xff) - ); - - if ( - group_last_filled = ( - ( - group_at_end = group_at_end && ( - canvas_y !== pixel_focus_y || - canvas_x < pixel_focus_x || - canvas_x > pixel_focus_x + group_width - ) - ) && - group_color === pixel_color - ) - ) { - /*canvas_surface_data.fill( - //0xffff0000, - group_color, - canvas_surface_data_index_row + canvas_x_group + 1, - canvas_surface_data_index_row + canvas_x_group + group_width + 1 - );*/ - let i = canvas_surface_data_index_row + canvas_x_group + 1; - canvas_surface_data[i] = group_color; - canvas_surface_data[++i] = group_color; - if (group_width < 3) break; - canvas_surface_data[++i] = group_color; - if (group_width < 4) break; - canvas_surface_data[++i] = group_color; - if (group_width < 5) break; - canvas_surface_data[++i] = group_color; - if (group_width < 6) break; - canvas_surface_data[++i] = group_color; - break; - } - if ( - group_at_end || - canvas_x === 0 - ) group_color = pixel_color; - canvas_surface_data[canvas_surface_data_index_row + canvas_x] = pixel_color; + canvas_surface_data_index_row += resolution_x; } - canvas_surface_data_index_row += resolution_x; - } - - // cursor - if ( - flag_hud && - !cursor_cross - ) { - canvas_surface_data[ - resolution_x * pixel_focus_y + pixel_focus_x - ] ^= 0xffffff; + // cursor + if ( + flag_hud && + !cursor_cross + ) { + canvas_surface_data[ + resolution_x * pixel_focus_y + pixel_focus_x + ] ^= 0xffffff; + } } canvas_context.putImageData(canvas_surface, 0, 0); @@ -560,7 +605,7 @@ R: ${resolution_x}x${resolution_y} (x${config.resolution_scaling}), D: ${config. ) * CHUNK_HEIGHT >> 10 }k -E: 0/0 M: ${player.gamemode} +E: 0/0, M: ${player.gamemode}, I: ${block_inside} Position: ${ number_toFixed2(player.position_x) @@ -574,19 +619,21 @@ Angle: ${ } ${ number_toFixed2(player.angle_v * Math_PI_180d) } -Block: ${ +Focus: ${ player.block_focus_y < 0 - ? 0 + ? '' : player.block_focus_x + ' ' + player.block_focus_y + ' ' + player.block_focus_z + ' ' + BLOCK_TYPE_FACE_LABELS[player.block_focus_face] + ': ' + - world_block_get( - world, - player.block_focus_x, - player.block_focus_y, - player.block_focus_z - ) + ITEM_HANDLES[ + world_block_get( + world, + player.block_focus_x, + player.block_focus_y, + player.block_focus_z + ) + ] } Chunk abs: ${ Math_floor(player.position_x) >> CHUNK_WIDTH_L2