Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 11 additions & 34 deletions apps/kbve/axum-kbve/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
},
"build": {
"executor": "@monodon/rust:build",
"outputs": [
"{options.target-dir}"
],
"outputs": ["{options.target-dir}"],
"options": {
"target-dir": "dist/target/axum-kbve"
},
Expand All @@ -30,9 +28,7 @@
},
"test": {
"executor": "@monodon/rust:test",
"outputs": [
"{options.target-dir}"
],
"outputs": ["{options.target-dir}"],
"options": {
"target-dir": "dist/target/axum-kbve"
},
Expand All @@ -44,18 +40,14 @@
},
"lint": {
"executor": "@monodon/rust:lint",
"outputs": [
"{options.target-dir}"
],
"outputs": ["{options.target-dir}"],
"options": {
"target-dir": "dist/target/axum-kbve"
}
},
"run": {
"executor": "@monodon/rust:run",
"outputs": [
"{options.target-dir}"
],
"outputs": ["{options.target-dir}"],
"options": {
"target-dir": "dist/target/axum-kbve"
},
Expand All @@ -79,9 +71,7 @@
},
"container": {
"executor": "nx:run-commands",
"dependsOn": [
"container-prep"
],
"dependsOn": ["container-prep"],
"defaultConfiguration": "local",
"options": {
"parallel": false
Expand Down Expand Up @@ -121,21 +111,14 @@
"local": {
"load": true,
"push": false,
"tags": [
"kbve/kbve:latest"
]
"tags": ["kbve/kbve:latest"]
},
"production": {
"load": true,
"push": false,
"metadata": {
"images": [
"ghcr.io/kbve/kbve",
"kbve/kbve"
],
"tags": [
"latest"
]
"images": ["ghcr.io/kbve/kbve", "kbve/kbve"],
"tags": ["latest"]
},
"build-args": [
"BUILD_BASE=ghcr.io/kbve/kbve-build-base:latest"
Expand All @@ -151,9 +134,7 @@
"ci": {
"load": true,
"push": false,
"tags": [
"kbve/kbve:latest"
],
"tags": ["kbve/kbve:latest"],
"build-args": [
"BUILD_BASE=ghcr.io/kbve/kbve-build-base:latest"
],
Expand All @@ -177,16 +158,12 @@
"local": {
"load": true,
"push": false,
"tags": [
"ghcr.io/kbve/kbve-build-base:latest"
]
"tags": ["ghcr.io/kbve/kbve-build-base:latest"]
},
"production": {
"load": true,
"push": false,
"tags": [
"ghcr.io/kbve/kbve-build-base:latest"
],
"tags": ["ghcr.io/kbve/kbve-build-base:latest"],
"cache-from": [
"type=registry,ref=ghcr.io/kbve/kbve-build-base:buildcache"
],
Expand Down
26 changes: 15 additions & 11 deletions apps/kbve/isometric/src-tauri/assets/shaders/pixelate.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,10 @@ fn sample_block(uv: vec2<f32>, resolution: vec2<f32>) -> vec4<f32> {
fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
let resolution = vec2<f32>(textureDimensions(screen_texture));

// Work in logical pixel space (like Three.js / CSS pixels).
// This gives the SAME block count on any display regardless of DPI.
// MacBook 2x: logical = 2048/2 = 1024, blocks = 1024/4 = 256
// LG 4K 1x: logical = 1024/1 = 1024, blocks = 1024/4 = 256
let logical_res = resolution / max(settings.scale_factor, 1.0);
let block_count = floor(logical_res / max(settings.pixel_size, 1.0));

// Current block in the grid (resolution-independent)
// Current block in the grid
let block = floor(in.uv * block_count);
let block_uv = (block + 0.5) / block_count;

Expand Down Expand Up @@ -63,13 +59,21 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {

let edge_factor = min(max(normal_edge, depth_edge), 1.0);

// Edge outline — fixed proportion of each block (same on all displays)
let block_pos = fract(in.uv * block_count);
let edge_width = 1.0 / settings.pixel_size; // same fraction regardless of DPI
let at_edge = select(0.0, 1.0,
block_pos.x < edge_width || block_pos.y < edge_width);
// Edge application depends on mode:
var final_edge: f32;
if settings.pixel_size < 1.5 {
// Low-res render-to-texture mode: each pixel IS one block.
// Apply edge darkening directly to edge pixels (no block outline).
final_edge = edge_factor;
} else {
// Normal post-process mode: darken only at block boundaries.
let block_pos = fract(in.uv * block_count);
let edge_width = 1.0 / settings.pixel_size;
let at_edge = select(0.0, 1.0,
block_pos.x < edge_width || block_pos.y < edge_width);
final_edge = edge_factor * at_edge;
}

let final_edge = edge_factor * at_edge;
let result = mix(color.rgb, color.rgb * 0.35, final_edge);
return vec4(result, color.a);
}
32 changes: 20 additions & 12 deletions apps/kbve/isometric/src-tauri/src/game/camera.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use bevy::prelude::*;

use super::pixelate::PixelateSettings;
use super::player::{Player, PlayerMovement};

const CAMERA_OFFSET: Vec3 = Vec3::new(15.0, 20.0, 15.0);
const VIEWPORT_HEIGHT: f32 = 20.0;

#[derive(Component)]
pub struct IsometricCamera;
Expand All @@ -10,28 +13,33 @@ pub struct IsometricCameraPlugin;
impl Plugin for IsometricCameraPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, setup_camera);
app.add_systems(Update, camera_follow_player.after(PlayerMovement));
}
}

fn setup_camera(mut commands: Commands) {
// Isometric camera looking down at the tile grid
let camera_pos = Vec3::new(15.0, 20.0, 15.0);

commands.spawn((
Camera3d::default(),
Projection::from(OrthographicProjection {
scaling_mode: bevy::camera::ScalingMode::FixedVertical {
viewport_height: 20.0,
viewport_height: VIEWPORT_HEIGHT,
},
..OrthographicProjection::default_3d()
}),
Transform::from_translation(camera_pos).looking_at(Vec3::ZERO, Vec3::Y),
Transform::from_translation(CAMERA_OFFSET).looking_at(Vec3::ZERO, Vec3::Y),
IsometricCamera,
PixelateSettings {
pixel_size: 4.0,
edge_strength: 0.15,
depth_edge_strength: 0.1,
scale_factor: 1.0, // auto-updated from window each frame
},
));
}

fn camera_follow_player(
player_query: Query<&Transform, (With<Player>, Without<IsometricCamera>)>,
mut camera_query: Query<&mut Transform, (With<IsometricCamera>, Without<Player>)>,
) {
let Ok(player_tf) = player_query.single() else {
return;
};
let Ok(mut camera_tf) = camera_query.single_mut() else {
return;
};
camera_tf.translation = player_tf.translation + CAMERA_OFFSET;
}
1 change: 1 addition & 0 deletions apps/kbve/isometric/src-tauri/src/game/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ pub mod pixelate;
pub mod player;
pub mod scene_objects;
pub mod state;
pub mod terrain;
pub mod tilemap;
32 changes: 20 additions & 12 deletions apps/kbve/isometric/src-tauri/src/game/object_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use super::scene_objects::{
AnimatedCrystal, Collider, HoverOutline, Occludable, OriginalEmissive, RotatingBox,
on_pointer_out, on_pointer_over,
};
use super::terrain::TerrainMap;

// ---------------------------------------------------------------------------
// Object Kind
Expand Down Expand Up @@ -217,12 +218,17 @@ fn handle_spawn_messages(
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut registry: ResMut<ObjectRegistry>,
mut terrain: ResMut<TerrainMap>,
mut messages: MessageReader<SpawnObjectMessage>,
) {
for msg in messages.read() {
// Y is terrain-relative: adjust to absolute position
let terrain_h = terrain.height_at_world(msg.position.x, msg.position.z);
let absolute_pos = Vec3::new(msg.position.x, terrain_h + msg.position.y, msg.position.z);

let placement = ObjectPlacement {
kind: msg.kind,
position: [msg.position.x, msg.position.y, msg.position.z],
position: [absolute_pos.x, absolute_pos.y, absolute_pos.z],
rotation_y: msg.rotation_y,
};
let id = registry.insert(placement);
Expand All @@ -231,7 +237,7 @@ fn handle_spawn_messages(
&mut meshes,
&mut materials,
msg.kind,
msg.position,
absolute_pos,
msg.rotation_y,
id,
);
Expand Down Expand Up @@ -292,9 +298,10 @@ fn spawn_object_entity(
kind,
ObjectInstance { registry_id },
RotatingBox,
Collider {
half_x: half,
half_z: half,
// Cylinder collider — snug fit with slight buffer for rotation
Collider::Cylinder {
radius: half * 1.2,
half_y: half,
},
Occludable,
OriginalEmissive(LinearRgba::BLACK),
Expand Down Expand Up @@ -322,9 +329,9 @@ fn spawn_object_entity(
kind,
ObjectInstance { registry_id },
RotatingBox,
Collider {
half_x: half,
half_z: half,
Collider::Cylinder {
radius: half * 1.2,
half_y: half,
},
Occludable,
OriginalEmissive(LinearRgba::BLACK),
Expand Down Expand Up @@ -374,8 +381,9 @@ fn spawn_object_entity(
Transform::from_translation(position),
kind,
ObjectInstance { registry_id },
Collider {
Collider::Aabb {
half_x: 0.4,
half_y: 2.0,
half_z: 0.4,
},
Occludable,
Expand All @@ -401,9 +409,9 @@ fn spawn_object_entity(
Transform::from_translation(position),
kind,
ObjectInstance { registry_id },
Collider {
half_x: radius,
half_z: radius,
Collider::Cylinder {
radius,
half_y: radius,
},
Occludable,
OriginalEmissive(LinearRgba::BLACK),
Expand Down
3 changes: 2 additions & 1 deletion apps/kbve/isometric/src-tauri/src/game/pixelate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub struct PixelatePlugin;
impl Plugin for PixelatePlugin {
fn build(&self, app: &mut App) {
app.add_plugins(FullscreenMaterialPlugin::<PixelateSettings>::default());
app.add_systems(Update, sync_scale_factor);
// scale_factor sync disabled — when rendering to a low-res texture,
// there's no DPI scaling and scale_factor must stay 1.0.
}
}
Loading