better lighting
This commit is contained in:
parent
805a2713a3
commit
6ed4e17065
75 changed files with 6417 additions and 3667 deletions
|
|
@ -5,21 +5,51 @@ fn main() {
|
|||
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
let pxl8_src = PathBuf::from(&manifest_dir).join("../src");
|
||||
|
||||
println!("cargo:rerun-if-changed=../src/bsp/pxl8_bsp.h");
|
||||
println!("cargo:rerun-if-changed=../src/core/pxl8_log.c");
|
||||
println!("cargo:rerun-if-changed=../src/core/pxl8_log.h");
|
||||
println!("cargo:rerun-if-changed=../src/core/pxl8_types.h");
|
||||
println!("cargo:rerun-if-changed=../src/math/pxl8_math.c");
|
||||
println!("cargo:rerun-if-changed=../src/math/pxl8_math.h");
|
||||
println!("cargo:rerun-if-changed=../src/net/pxl8_protocol.c");
|
||||
println!("cargo:rerun-if-changed=../src/net/pxl8_protocol.h");
|
||||
println!("cargo:rerun-if-changed=../src/sim/pxl8_sim.c");
|
||||
println!("cargo:rerun-if-changed=../src/sim/pxl8_sim.h");
|
||||
println!("cargo:rerun-if-changed=../src/vxl/pxl8_vxl.c");
|
||||
println!("cargo:rerun-if-changed=../src/vxl/pxl8_vxl.h");
|
||||
|
||||
cc::Build::new()
|
||||
.file(pxl8_src.join("core/pxl8_log.c"))
|
||||
.file(pxl8_src.join("hal/pxl8_mem.c"))
|
||||
.file(pxl8_src.join("math/pxl8_math.c"))
|
||||
.file(pxl8_src.join("math/pxl8_noise.c"))
|
||||
.file(pxl8_src.join("sim/pxl8_sim.c"))
|
||||
.file(pxl8_src.join("vxl/pxl8_vxl.c"))
|
||||
.include(pxl8_src.join("bsp"))
|
||||
.include(pxl8_src.join("core"))
|
||||
.define("PXL8_SERVER", None)
|
||||
.compile("pxl8_log");
|
||||
.include(pxl8_src.join("hal"))
|
||||
.include(pxl8_src.join("math"))
|
||||
.include(pxl8_src.join("net"))
|
||||
.include(pxl8_src.join("sim"))
|
||||
.include(pxl8_src.join("vxl"))
|
||||
.compile("pxl8");
|
||||
|
||||
let bindings = bindgen::Builder::default()
|
||||
.header(pxl8_src.join("core/pxl8_log.h").to_str().unwrap())
|
||||
.header(pxl8_src.join("net/pxl8_protocol.h").to_str().unwrap())
|
||||
.header(pxl8_src.join("sim/pxl8_sim.h").to_str().unwrap())
|
||||
.header(pxl8_src.join("vxl/pxl8_vxl.h").to_str().unwrap())
|
||||
.header(pxl8_src.join("math/pxl8_noise.h").to_str().unwrap())
|
||||
.clang_arg(format!("-I{}", pxl8_src.join("bsp").display()))
|
||||
.clang_arg(format!("-I{}", pxl8_src.join("core").display()))
|
||||
.clang_arg(format!("-I{}", pxl8_src.join("math").display()))
|
||||
.clang_arg(format!("-I{}", pxl8_src.join("net").display()))
|
||||
.clang_arg(format!("-I{}", pxl8_src.join("sim").display()))
|
||||
.clang_arg(format!("-I{}", pxl8_src.join("vxl").display()))
|
||||
.blocklist_item("FP_NAN")
|
||||
.blocklist_item("FP_INFINITE")
|
||||
.blocklist_item("FP_ZERO")
|
||||
.blocklist_item("FP_SUBNORMAL")
|
||||
.blocklist_item("FP_NORMAL")
|
||||
.use_core()
|
||||
.rustified_enum(".*")
|
||||
.generate()
|
||||
|
|
@ -27,6 +57,6 @@ fn main() {
|
|||
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
bindings
|
||||
.write_to_file(out_path.join("protocol.rs"))
|
||||
.write_to_file(out_path.join("pxl8.rs"))
|
||||
.expect("Couldn't write bindings");
|
||||
}
|
||||
|
|
|
|||
315
pxl8d/src/bsp.rs
315
pxl8d/src/bsp.rs
|
|
@ -1,173 +1,216 @@
|
|||
extern crate alloc;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::math::Vec3;
|
||||
use crate::math::{Vec3, VEC3_ZERO};
|
||||
use crate::pxl8::*;
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct BspVertex {
|
||||
pub position: Vec3,
|
||||
pub type Vertex = pxl8_bsp_vertex;
|
||||
pub type Edge = pxl8_bsp_edge;
|
||||
pub type Face = pxl8_bsp_face;
|
||||
pub type Plane = pxl8_bsp_plane;
|
||||
pub type Node = pxl8_bsp_node;
|
||||
pub type Leaf = pxl8_bsp_leaf;
|
||||
pub type Portal = pxl8_bsp_portal;
|
||||
pub type CellPortals = pxl8_bsp_cell_portals;
|
||||
|
||||
impl Default for Edge {
|
||||
fn default() -> Self {
|
||||
Self { vertex: [0, 0] }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct BspEdge {
|
||||
pub vertex: [u16; 2],
|
||||
impl Default for Face {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
first_edge: 0,
|
||||
lightmap_offset: 0,
|
||||
num_edges: 0,
|
||||
plane_id: 0,
|
||||
side: 0,
|
||||
styles: [0; 4],
|
||||
material_id: 0,
|
||||
aabb_min: VEC3_ZERO,
|
||||
aabb_max: VEC3_ZERO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct BspFace {
|
||||
pub first_edge: u32,
|
||||
pub lightmap_offset: u32,
|
||||
pub num_edges: u16,
|
||||
pub plane_id: u16,
|
||||
pub side: u16,
|
||||
pub styles: [u8; 4],
|
||||
pub material_id: u16,
|
||||
pub aabb_min: Vec3,
|
||||
pub aabb_max: Vec3,
|
||||
impl Default for Leaf {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ambient_level: [0; 4],
|
||||
contents: 0,
|
||||
first_marksurface: 0,
|
||||
maxs: [0; 3],
|
||||
mins: [0; 3],
|
||||
num_marksurfaces: 0,
|
||||
visofs: -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct BspPlane {
|
||||
pub normal: Vec3,
|
||||
pub dist: f32,
|
||||
pub plane_type: i32,
|
||||
impl Default for Node {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
children: [0, 0],
|
||||
first_face: 0,
|
||||
maxs: [0; 3],
|
||||
mins: [0; 3],
|
||||
num_faces: 0,
|
||||
plane_id: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct BspNode {
|
||||
pub children: [i32; 2],
|
||||
pub first_face: u16,
|
||||
pub maxs: [i16; 3],
|
||||
pub mins: [i16; 3],
|
||||
pub num_faces: u16,
|
||||
pub plane_id: u32,
|
||||
impl Default for Plane {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
dist: 0.0,
|
||||
normal: VEC3_ZERO,
|
||||
type_: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct BspLeaf {
|
||||
pub ambient_level: [u8; 4],
|
||||
pub contents: i32,
|
||||
pub first_marksurface: u16,
|
||||
pub maxs: [i16; 3],
|
||||
pub mins: [i16; 3],
|
||||
pub num_marksurfaces: u16,
|
||||
pub visofs: i32,
|
||||
impl Default for Vertex {
|
||||
fn default() -> Self {
|
||||
Self { position: VEC3_ZERO }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct BspPortal {
|
||||
pub x0: f32,
|
||||
pub z0: f32,
|
||||
pub x1: f32,
|
||||
pub z1: f32,
|
||||
pub target_leaf: u32,
|
||||
impl Default for Portal {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
x0: 0.0,
|
||||
z0: 0.0,
|
||||
x1: 0.0,
|
||||
z1: 0.0,
|
||||
target_leaf: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct BspCellPortals {
|
||||
pub portals: [BspPortal; 4],
|
||||
pub num_portals: u8,
|
||||
impl Default for CellPortals {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
portals: [Portal::default(); 4],
|
||||
num_portals: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Bsp {
|
||||
pub vertices: Vec<BspVertex>,
|
||||
pub edges: Vec<BspEdge>,
|
||||
pub surfedges: Vec<i32>,
|
||||
pub planes: Vec<BspPlane>,
|
||||
pub faces: Vec<BspFace>,
|
||||
pub nodes: Vec<BspNode>,
|
||||
pub leafs: Vec<BspLeaf>,
|
||||
inner: pxl8_bsp,
|
||||
pub cell_portals: Box<[CellPortals]>,
|
||||
pub edges: Box<[Edge]>,
|
||||
pub faces: Box<[Face]>,
|
||||
pub leafs: Box<[Leaf]>,
|
||||
pub marksurfaces: Box<[u16]>,
|
||||
pub nodes: Box<[Node]>,
|
||||
pub planes: Box<[Plane]>,
|
||||
pub surfedges: Box<[i32]>,
|
||||
pub vertex_lights: Box<[u32]>,
|
||||
pub vertices: Box<[Vertex]>,
|
||||
pub visdata: Box<[u8]>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BspBuilder {
|
||||
pub cell_portals: Vec<CellPortals>,
|
||||
pub edges: Vec<Edge>,
|
||||
pub faces: Vec<Face>,
|
||||
pub leafs: Vec<Leaf>,
|
||||
pub marksurfaces: Vec<u16>,
|
||||
pub cell_portals: Vec<BspCellPortals>,
|
||||
pub visdata: Vec<u8>,
|
||||
pub nodes: Vec<Node>,
|
||||
pub planes: Vec<Plane>,
|
||||
pub surfedges: Vec<i32>,
|
||||
pub vertex_lights: Vec<u32>,
|
||||
pub vertices: Vec<Vertex>,
|
||||
pub visdata: Vec<u8>,
|
||||
}
|
||||
|
||||
impl BspBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BspBuilder> for Bsp {
|
||||
fn from(b: BspBuilder) -> Self {
|
||||
let cell_portals = b.cell_portals.into_boxed_slice();
|
||||
let edges = b.edges.into_boxed_slice();
|
||||
let faces = b.faces.into_boxed_slice();
|
||||
let leafs = b.leafs.into_boxed_slice();
|
||||
let marksurfaces = b.marksurfaces.into_boxed_slice();
|
||||
let nodes = b.nodes.into_boxed_slice();
|
||||
let planes = b.planes.into_boxed_slice();
|
||||
let surfedges = b.surfedges.into_boxed_slice();
|
||||
let vertex_lights = b.vertex_lights.into_boxed_slice();
|
||||
let vertices = b.vertices.into_boxed_slice();
|
||||
let visdata = b.visdata.into_boxed_slice();
|
||||
|
||||
let inner = pxl8_bsp {
|
||||
cell_portals: if cell_portals.is_empty() { core::ptr::null_mut() } else { cell_portals.as_ptr() as *mut _ },
|
||||
edges: if edges.is_empty() { core::ptr::null_mut() } else { edges.as_ptr() as *mut _ },
|
||||
faces: if faces.is_empty() { core::ptr::null_mut() } else { faces.as_ptr() as *mut _ },
|
||||
leafs: if leafs.is_empty() { core::ptr::null_mut() } else { leafs.as_ptr() as *mut _ },
|
||||
lightdata: core::ptr::null_mut(),
|
||||
lightmaps: core::ptr::null_mut(),
|
||||
marksurfaces: if marksurfaces.is_empty() { core::ptr::null_mut() } else { marksurfaces.as_ptr() as *mut _ },
|
||||
models: core::ptr::null_mut(),
|
||||
nodes: if nodes.is_empty() { core::ptr::null_mut() } else { nodes.as_ptr() as *mut _ },
|
||||
planes: if planes.is_empty() { core::ptr::null_mut() } else { planes.as_ptr() as *mut _ },
|
||||
surfedges: if surfedges.is_empty() { core::ptr::null_mut() } else { surfedges.as_ptr() as *mut _ },
|
||||
vertex_lights: if vertex_lights.is_empty() { core::ptr::null_mut() } else { vertex_lights.as_ptr() as *mut _ },
|
||||
vertices: if vertices.is_empty() { core::ptr::null_mut() } else { vertices.as_ptr() as *mut _ },
|
||||
visdata: if visdata.is_empty() { core::ptr::null_mut() } else { visdata.as_ptr() as *mut _ },
|
||||
lightdata_size: 0,
|
||||
num_cell_portals: cell_portals.len() as u32,
|
||||
num_edges: edges.len() as u32,
|
||||
num_faces: faces.len() as u32,
|
||||
num_leafs: leafs.len() as u32,
|
||||
num_lightmaps: 0,
|
||||
num_marksurfaces: marksurfaces.len() as u32,
|
||||
num_models: 0,
|
||||
num_nodes: nodes.len() as u32,
|
||||
num_planes: planes.len() as u32,
|
||||
num_surfedges: surfedges.len() as u32,
|
||||
num_vertex_lights: vertex_lights.len() as u32,
|
||||
num_vertices: vertices.len() as u32,
|
||||
visdata_size: visdata.len() as u32,
|
||||
};
|
||||
|
||||
Self {
|
||||
inner,
|
||||
cell_portals,
|
||||
edges,
|
||||
faces,
|
||||
leafs,
|
||||
marksurfaces,
|
||||
nodes,
|
||||
planes,
|
||||
surfedges,
|
||||
vertex_lights,
|
||||
vertices,
|
||||
visdata,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Bsp {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
vertices: Vec::new(),
|
||||
edges: Vec::new(),
|
||||
surfedges: Vec::new(),
|
||||
planes: Vec::new(),
|
||||
faces: Vec::new(),
|
||||
nodes: Vec::new(),
|
||||
leafs: Vec::new(),
|
||||
marksurfaces: Vec::new(),
|
||||
cell_portals: Vec::new(),
|
||||
visdata: Vec::new(),
|
||||
vertex_lights: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_leaf(&self, pos: Vec3) -> i32 {
|
||||
if self.nodes.is_empty() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let mut node_id: i32 = 0;
|
||||
|
||||
while node_id >= 0 {
|
||||
let node = &self.nodes[node_id as usize];
|
||||
let plane = &self.planes[node.plane_id as usize];
|
||||
|
||||
let dist = pos.dot(plane.normal) - plane.dist;
|
||||
node_id = node.children[if dist < 0.0 { 1 } else { 0 }];
|
||||
}
|
||||
|
||||
-(node_id + 1)
|
||||
}
|
||||
|
||||
pub fn point_solid(&self, pos: Vec3) -> bool {
|
||||
let leaf = self.find_leaf(pos);
|
||||
if leaf < 0 || leaf as usize >= self.leafs.len() {
|
||||
return true;
|
||||
}
|
||||
self.leafs[leaf as usize].contents == -1
|
||||
}
|
||||
|
||||
fn point_clear(&self, x: f32, y: f32, z: f32, radius: f32) -> bool {
|
||||
if self.point_solid(Vec3::new(x, y, z)) { return false; }
|
||||
if self.point_solid(Vec3::new(x + radius, y, z)) { return false; }
|
||||
if self.point_solid(Vec3::new(x - radius, y, z)) { return false; }
|
||||
if self.point_solid(Vec3::new(x, y, z + radius)) { return false; }
|
||||
if self.point_solid(Vec3::new(x, y, z - radius)) { return false; }
|
||||
true
|
||||
pub fn as_c_bsp(&self) -> &pxl8_bsp {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn trace(&self, from: Vec3, to: Vec3, radius: f32) -> Vec3 {
|
||||
if self.nodes.is_empty() {
|
||||
return to;
|
||||
}
|
||||
|
||||
if self.point_clear(to.x, to.y, to.z, radius) {
|
||||
return to;
|
||||
}
|
||||
|
||||
let x_ok = self.point_clear(to.x, from.y, from.z, radius);
|
||||
let z_ok = self.point_clear(from.x, from.y, to.z, radius);
|
||||
|
||||
if x_ok && z_ok {
|
||||
let dx = to.x - from.x;
|
||||
let dz = to.z - from.z;
|
||||
if dx * dx > dz * dz {
|
||||
Vec3::new(to.x, from.y, from.z)
|
||||
} else {
|
||||
Vec3::new(from.x, from.y, to.z)
|
||||
}
|
||||
} else if x_ok {
|
||||
Vec3::new(to.x, from.y, from.z)
|
||||
} else if z_ok {
|
||||
Vec3::new(from.x, from.y, to.z)
|
||||
} else {
|
||||
from
|
||||
}
|
||||
unsafe { pxl8_bsp_trace(&self.inner, from, to, radius) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Bsp {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
BspBuilder::new().into()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ pub mod stream;
|
|||
|
||||
use crate::bsp::Bsp;
|
||||
use crate::math::Vec3;
|
||||
use crate::pxl8::pxl8_vxl_trace;
|
||||
use crate::voxel::VoxelChunk;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
|
|
@ -35,7 +36,9 @@ impl Chunk {
|
|||
pub fn trace(&self, from: Vec3, to: Vec3, radius: f32) -> Vec3 {
|
||||
match self {
|
||||
Chunk::Bsp { bsp, .. } => bsp.trace(from, to, radius),
|
||||
Chunk::Vxl { data, .. } => data.trace(from, to, radius),
|
||||
Chunk::Vxl { cx, cy, cz, data, .. } => unsafe {
|
||||
pxl8_vxl_trace(data.chunk, *cx, *cy, *cz, from, to, radius)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,6 +77,18 @@ impl ClientChunkState {
|
|||
self.pending.clear();
|
||||
self.pending_messages.clear();
|
||||
}
|
||||
|
||||
pub fn clear_known_vxl(&mut self) {
|
||||
self.known.retain(|id, _| !matches!(id, ChunkId::Vxl(_, _, _)));
|
||||
}
|
||||
|
||||
pub fn clear_known_bsp(&mut self) {
|
||||
self.known.retain(|id, _| !matches!(id, ChunkId::Bsp(_)));
|
||||
}
|
||||
|
||||
pub fn forget(&mut self, id: &ChunkId) {
|
||||
self.known.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ClientChunkState {
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ fn panic(_info: &PanicInfo) -> ! {
|
|||
}
|
||||
|
||||
#[allow(dead_code, non_camel_case_types, non_snake_case, non_upper_case_globals)]
|
||||
pub mod protocol {
|
||||
include!(concat!(env!("OUT_DIR"), "/protocol.rs"));
|
||||
pub mod pxl8 {
|
||||
include!(concat!(env!("OUT_DIR"), "/pxl8.rs"));
|
||||
}
|
||||
|
||||
pub use bsp::*;
|
||||
|
|
@ -33,7 +33,7 @@ pub use chunk::*;
|
|||
pub use chunk::stream::*;
|
||||
pub use math::*;
|
||||
pub use procgen::{ProcgenParams, generate, generate_rooms};
|
||||
pub use protocol::*;
|
||||
pub use pxl8::*;
|
||||
pub use sim::*;
|
||||
pub use transport::*;
|
||||
pub use voxel::*;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::protocol::{pxl8_log, pxl8_log_init};
|
||||
use crate::pxl8::{pxl8_log, pxl8_log_init};
|
||||
use core::ffi::c_char;
|
||||
|
||||
static mut G_LOG: pxl8_log = pxl8_log {
|
||||
handler: None,
|
||||
level: crate::protocol::pxl8_log_level::PXL8_LOG_LEVEL_DEBUG,
|
||||
level: crate::pxl8::pxl8_log_level::PXL8_LOG_LEVEL_DEBUG,
|
||||
};
|
||||
|
||||
pub fn init() {
|
||||
|
|
@ -20,7 +20,7 @@ macro_rules! pxl8_debug {
|
|||
let _ = ::core::write!(&mut buf, $($arg)*);
|
||||
buf.push(0);
|
||||
unsafe {
|
||||
$crate::protocol::pxl8_log_write_debug(
|
||||
$crate::pxl8::pxl8_log_write_debug(
|
||||
concat!(file!(), "\0").as_ptr() as *const ::core::ffi::c_char,
|
||||
line!() as ::core::ffi::c_int,
|
||||
c"%s".as_ptr() as *const ::core::ffi::c_char,
|
||||
|
|
@ -38,7 +38,7 @@ macro_rules! pxl8_error {
|
|||
let _ = ::core::write!(&mut buf, $($arg)*);
|
||||
buf.push(0);
|
||||
unsafe {
|
||||
$crate::protocol::pxl8_log_write_error(
|
||||
$crate::pxl8::pxl8_log_write_error(
|
||||
concat!(file!(), "\0").as_ptr() as *const ::core::ffi::c_char,
|
||||
line!() as ::core::ffi::c_int,
|
||||
c"%s".as_ptr() as *const ::core::ffi::c_char,
|
||||
|
|
@ -56,7 +56,7 @@ macro_rules! pxl8_info {
|
|||
let _ = ::core::write!(&mut buf, $($arg)*);
|
||||
buf.push(0);
|
||||
unsafe {
|
||||
$crate::protocol::pxl8_log_write_info(
|
||||
$crate::pxl8::pxl8_log_write_info(
|
||||
c"%s".as_ptr() as *const ::core::ffi::c_char,
|
||||
buf.as_ptr(),
|
||||
);
|
||||
|
|
@ -72,7 +72,7 @@ macro_rules! pxl8_trace {
|
|||
let _ = ::core::write!(&mut buf, $($arg)*);
|
||||
buf.push(0);
|
||||
unsafe {
|
||||
$crate::protocol::pxl8_log_write_trace(
|
||||
$crate::pxl8::pxl8_log_write_trace(
|
||||
concat!(file!(), "\0").as_ptr() as *const ::core::ffi::c_char,
|
||||
line!() as ::core::ffi::c_int,
|
||||
c"%s".as_ptr() as *const ::core::ffi::c_char,
|
||||
|
|
@ -90,7 +90,7 @@ macro_rules! pxl8_warn {
|
|||
let _ = ::core::write!(&mut buf, $($arg)*);
|
||||
buf.push(0);
|
||||
unsafe {
|
||||
$crate::protocol::pxl8_log_write_warn(
|
||||
$crate::pxl8::pxl8_log_write_warn(
|
||||
concat!(file!(), "\0").as_ptr() as *const ::core::ffi::c_char,
|
||||
line!() as ::core::ffi::c_int,
|
||||
c"%s".as_ptr() as *const ::core::ffi::c_char,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ extern crate alloc;
|
|||
use pxl8d::*;
|
||||
use pxl8d::chunk::ChunkId;
|
||||
use pxl8d::chunk::stream::ClientChunkState;
|
||||
use pxl8d::math::Vec3;
|
||||
|
||||
const TICK_RATE: u64 = 30;
|
||||
const TICK_NS: u64 = 1_000_000_000 / TICK_RATE;
|
||||
|
|
@ -73,14 +74,15 @@ pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 {
|
|||
let mut player_id: Option<u64> = None;
|
||||
let mut last_client_tick: u64 = 0;
|
||||
let mut client_chunks = ClientChunkState::new();
|
||||
let mut client_stream_radius: i32 = 3;
|
||||
|
||||
let mut sequence: u32 = 0;
|
||||
let mut last_tick = get_time_ns();
|
||||
let mut entities_buf = [protocol::pxl8_entity_state {
|
||||
let mut entities_buf = [pxl8d::pxl8_entity_state {
|
||||
entity_id: 0,
|
||||
userdata: [0u8; 56],
|
||||
}; 64];
|
||||
let mut inputs_buf: [protocol::pxl8_input_msg; 16] = unsafe { core::mem::zeroed() };
|
||||
let mut inputs_buf: [pxl8d::pxl8_input_msg; 16] = unsafe { core::mem::zeroed() };
|
||||
|
||||
pxl8_debug!("[SERVER] Entering main loop");
|
||||
loop {
|
||||
|
|
@ -91,15 +93,15 @@ pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 {
|
|||
last_tick = now;
|
||||
let dt = (elapsed as f32) / 1_000_000_000.0;
|
||||
|
||||
let mut latest_input: Option<protocol::pxl8_input_msg> = None;
|
||||
let mut latest_input: Option<pxl8d::pxl8_input_msg> = None;
|
||||
while let Some(msg_type) = transport.recv() {
|
||||
match msg_type {
|
||||
x if x == protocol::pxl8_msg_type::PXL8_MSG_INPUT as u8 => {
|
||||
x if x == pxl8d::pxl8_msg_type::PXL8_MSG_INPUT as u8 => {
|
||||
latest_input = Some(transport.get_input());
|
||||
}
|
||||
x if x == protocol::pxl8_msg_type::PXL8_MSG_COMMAND as u8 => {
|
||||
x if x == pxl8d::pxl8_msg_type::PXL8_MSG_COMMAND as u8 => {
|
||||
let cmd = transport.get_command();
|
||||
if cmd.cmd_type == protocol::pxl8_cmd_type::PXL8_CMD_SPAWN_ENTITY as u16 {
|
||||
if cmd.cmd_type == pxl8d::pxl8_cmd_type::PXL8_CMD_SPAWN_ENTITY as u16 {
|
||||
let (x, y, z, _yaw, _pitch) = extract_spawn_position(&cmd.payload);
|
||||
player_id = Some(sim.spawn_player(x, y, z) as u64);
|
||||
|
||||
|
|
@ -110,6 +112,46 @@ pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 {
|
|||
pxl8_debug!("[SERVER] Sending CHUNK_ENTER for BSP id=1");
|
||||
transport.send_chunk_enter(1, transport::CHUNK_TYPE_BSP, sequence);
|
||||
sequence = sequence.wrapping_add(1);
|
||||
|
||||
pxl8_debug!("[SERVER] Preloading voxel chunks around spawn and door");
|
||||
let spawn_pos = Vec3 { x, y, z };
|
||||
let door_y = sim.voxels.find_surface_y(942.0, 416.0);
|
||||
let door_pos = Vec3 { x: 942.0, y: door_y, z: 416.0 };
|
||||
pxl8_debug!("[SERVER] Door placed at surface y={}", door_y);
|
||||
sim.voxels.load_chunks_around(spawn_pos, client_stream_radius);
|
||||
sim.voxels.load_chunks_around(door_pos, client_stream_radius);
|
||||
client_chunks.request_vxl_radius(spawn_pos, client_stream_radius, &sim.voxels);
|
||||
client_chunks.request_vxl_radius(door_pos, client_stream_radius, &sim.voxels);
|
||||
} else if cmd.cmd_type == pxl8d::pxl8_cmd_type::PXL8_CMD_EXIT_CHUNK as u16 {
|
||||
sim.world.clear_active();
|
||||
client_chunks.clear_pending();
|
||||
client_chunks.clear_known_vxl();
|
||||
client_chunks.clear_known_bsp();
|
||||
transport.send_chunk_exit(sequence);
|
||||
sequence = sequence.wrapping_add(1);
|
||||
let exit_x = f32::from_be_bytes([cmd.payload[0], cmd.payload[1], cmd.payload[2], cmd.payload[3]]);
|
||||
let exit_y = f32::from_be_bytes([cmd.payload[4], cmd.payload[5], cmd.payload[6], cmd.payload[7]]);
|
||||
let exit_z = f32::from_be_bytes([cmd.payload[8], cmd.payload[9], cmd.payload[10], cmd.payload[11]]);
|
||||
let exit_pos = Vec3 { x: exit_x, y: exit_y, z: exit_z };
|
||||
sim.teleport_player(exit_x, exit_y, exit_z);
|
||||
sim.voxels.load_chunks_around(exit_pos, client_stream_radius);
|
||||
client_chunks.request_vxl_radius(exit_pos, client_stream_radius, &sim.voxels);
|
||||
} else if cmd.cmd_type == pxl8d::pxl8_cmd_type::PXL8_CMD_ENTER_CHUNK as u16 {
|
||||
let chunk_id = u32::from_be_bytes([
|
||||
cmd.payload[0], cmd.payload[1], cmd.payload[2], cmd.payload[3]
|
||||
]);
|
||||
pxl8_debug!("[SERVER] Enter chunk command - entering BSP {}", chunk_id);
|
||||
if sim.world.contains(&ChunkId::Bsp(chunk_id)) {
|
||||
sim.world.set_active(ChunkId::Bsp(chunk_id));
|
||||
client_chunks.request(ChunkId::Bsp(chunk_id));
|
||||
transport.send_chunk_enter(chunk_id, transport::CHUNK_TYPE_BSP, sequence);
|
||||
sequence = sequence.wrapping_add(1);
|
||||
}
|
||||
} else if cmd.cmd_type == pxl8d::pxl8_cmd_type::PXL8_CMD_SET_CHUNK_SETTINGS as u16 {
|
||||
let render_dist = i32::from_be_bytes([
|
||||
cmd.payload[0], cmd.payload[1], cmd.payload[2], cmd.payload[3]
|
||||
]);
|
||||
client_stream_radius = render_dist.clamp(1, 8);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
@ -132,7 +174,7 @@ pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 {
|
|||
}
|
||||
});
|
||||
|
||||
let header = protocol::pxl8_snapshot_header {
|
||||
let header = pxl8d::pxl8_snapshot_header {
|
||||
entity_count: count as u16,
|
||||
event_count: 0,
|
||||
player_id: player_id.unwrap_or(0),
|
||||
|
|
@ -147,54 +189,36 @@ pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 {
|
|||
if let Some(player) = sim.get_player_position(pid) {
|
||||
let pos = Vec3 { x: player.0, y: player.1, z: player.2 };
|
||||
|
||||
if sim.world.active().is_some() {
|
||||
while let Some(chunk_id) = client_chunks.next_pending() {
|
||||
match chunk_id {
|
||||
ChunkId::Bsp(id) => {
|
||||
pxl8_debug!("[SERVER] Processing pending BSP chunk");
|
||||
if let Some(chunk) = sim.world.get(&chunk_id) {
|
||||
if let Some(bsp) = chunk.as_bsp() {
|
||||
let msgs = bsp_to_messages(bsp, id, chunk.version());
|
||||
pxl8_debug!("[SERVER] BSP serialized, queueing messages");
|
||||
client_chunks.queue_messages(msgs);
|
||||
client_chunks.mark_sent(chunk_id, chunk.version());
|
||||
}
|
||||
}
|
||||
}
|
||||
ChunkId::Vxl(cx, cy, cz) => {
|
||||
if let Some(chunk) = sim.voxels.get_chunk(cx, cy, cz) {
|
||||
let msgs = transport::ChunkMessage::from_voxel(chunk, 1);
|
||||
sim.voxels.load_chunks_around(pos, client_stream_radius);
|
||||
client_chunks.request_vxl_radius(pos, client_stream_radius, &sim.voxels);
|
||||
|
||||
while let Some(chunk_id) = client_chunks.next_pending() {
|
||||
match chunk_id {
|
||||
ChunkId::Bsp(id) => {
|
||||
if let Some(chunk) = sim.world.get(&chunk_id) {
|
||||
if let Some(bsp) = chunk.as_bsp() {
|
||||
let msgs = bsp_to_messages(bsp, id, chunk.version());
|
||||
client_chunks.queue_messages(msgs);
|
||||
client_chunks.mark_sent(chunk_id, 1);
|
||||
client_chunks.mark_sent(chunk_id, chunk.version());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _ in 0..4 {
|
||||
if let Some(msg) = client_chunks.next_message() {
|
||||
transport.send_chunk(&msg, sequence);
|
||||
sequence = sequence.wrapping_add(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
client_chunks.request_vxl_radius(pos, 2, &sim.voxels);
|
||||
|
||||
for _ in 0..2 {
|
||||
if let Some(chunk_id) = client_chunks.next_pending() {
|
||||
if let ChunkId::Vxl(cx, cy, cz) = chunk_id {
|
||||
if let Some(chunk) = sim.voxels.get_chunk(cx, cy, cz) {
|
||||
let msgs = transport::ChunkMessage::from_voxel(chunk, 1);
|
||||
for msg in &msgs {
|
||||
transport.send_chunk(msg, sequence);
|
||||
sequence = sequence.wrapping_add(1);
|
||||
}
|
||||
client_chunks.mark_sent(chunk_id, 1);
|
||||
}
|
||||
ChunkId::Vxl(cx, cy, cz) => {
|
||||
if let Some(chunk) = sim.voxels.get_chunk(cx, cy, cz) {
|
||||
let msgs = transport::ChunkMessage::from_voxel(chunk, 1);
|
||||
client_chunks.queue_messages(msgs);
|
||||
client_chunks.mark_sent(chunk_id, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _ in 0..8 {
|
||||
if let Some(msg) = client_chunks.next_message() {
|
||||
transport.send_chunk(&msg, sequence);
|
||||
sequence = sequence.wrapping_add(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -250,7 +274,7 @@ fn bsp_to_messages(bsp: &bsp::Bsp, id: u32, version: u32) -> alloc::vec::Vec<tra
|
|||
data.extend_from_slice(&p.normal.y.to_be_bytes());
|
||||
data.extend_from_slice(&p.normal.z.to_be_bytes());
|
||||
data.extend_from_slice(&p.dist.to_be_bytes());
|
||||
data.extend_from_slice(&p.plane_type.to_be_bytes());
|
||||
data.extend_from_slice(&p.type_.to_be_bytes());
|
||||
}
|
||||
|
||||
for f in &bsp.faces {
|
||||
|
|
|
|||
|
|
@ -1,42 +1,62 @@
|
|||
use core::ops::{Add, Mul, Sub};
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct Vec3 {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub z: f32,
|
||||
use crate::pxl8::pxl8_vec3;
|
||||
|
||||
pub type Vec3 = pxl8_vec3;
|
||||
|
||||
pub const VEC3_ZERO: Vec3 = Vec3 { x: 0.0, y: 0.0, z: 0.0 };
|
||||
pub const VEC3_Y: Vec3 = Vec3 { x: 0.0, y: 1.0, z: 0.0 };
|
||||
|
||||
pub trait Vec3Ext {
|
||||
fn new(x: f32, y: f32, z: f32) -> Self;
|
||||
fn dot(self, rhs: Self) -> f32;
|
||||
}
|
||||
|
||||
impl Vec3 {
|
||||
pub const ZERO: Self = Self { x: 0.0, y: 0.0, z: 0.0 };
|
||||
pub const Y: Self = Self { x: 0.0, y: 1.0, z: 0.0 };
|
||||
|
||||
pub const fn new(x: f32, y: f32, z: f32) -> Self {
|
||||
impl Vec3Ext for pxl8_vec3 {
|
||||
fn new(x: f32, y: f32, z: f32) -> Self {
|
||||
Self { x, y, z }
|
||||
}
|
||||
|
||||
pub fn dot(self, rhs: Self) -> f32 {
|
||||
fn dot(self, rhs: Self) -> f32 {
|
||||
self.x * rhs.x + self.y * rhs.y + self.z * rhs.z
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Vec3 {
|
||||
impl Default for pxl8_vec3 {
|
||||
fn default() -> Self {
|
||||
VEC3_ZERO
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for pxl8_vec3 {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: Self) -> Self {
|
||||
Self::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
|
||||
Self {
|
||||
x: self.x + rhs.x,
|
||||
y: self.y + rhs.y,
|
||||
z: self.z + rhs.z,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Vec3 {
|
||||
impl Sub for pxl8_vec3 {
|
||||
type Output = Self;
|
||||
fn sub(self, rhs: Self) -> Self {
|
||||
Self::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
|
||||
Self {
|
||||
x: self.x - rhs.x,
|
||||
y: self.y - rhs.y,
|
||||
z: self.z - rhs.z,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f32> for Vec3 {
|
||||
impl Mul<f32> for pxl8_vec3 {
|
||||
type Output = Self;
|
||||
fn mul(self, rhs: f32) -> Self {
|
||||
Self::new(self.x * rhs, self.y * rhs, self.z * rhs)
|
||||
Self {
|
||||
x: self.x * rhs,
|
||||
y: self.y * rhs,
|
||||
z: self.z * rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@ use alloc::vec;
|
|||
use alloc::vec::Vec;
|
||||
use libm::sqrtf;
|
||||
|
||||
use crate::bsp::{Bsp, BspVertex, BspEdge, BspFace, BspPlane, BspNode, BspLeaf, BspPortal, BspCellPortals};
|
||||
use crate::math::Vec3;
|
||||
use crate::bsp::{Bsp, BspBuilder, CellPortals, Edge, Face, Leaf, Node, Plane, Portal, Vertex};
|
||||
use crate::math::{Vec3, Vec3Ext};
|
||||
|
||||
pub const CELL_SIZE: f32 = 64.0;
|
||||
pub const WALL_HEIGHT: f32 = 128.0;
|
||||
pub const TRIM_HEIGHT: f32 = 12.0;
|
||||
pub const PVS_MAX_DEPTH: u32 = 64;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
|
@ -107,7 +108,7 @@ impl Bounds {
|
|||
}
|
||||
|
||||
struct BspBuildContext<'a> {
|
||||
bsp: &'a mut Bsp,
|
||||
bsp: &'a mut BspBuilder,
|
||||
grid: &'a RoomGrid,
|
||||
node_count: u32,
|
||||
plane_offset: u32,
|
||||
|
|
@ -133,7 +134,7 @@ fn carve_corridor_v(grid: &mut RoomGrid, y1: i32, y2: i32, x: i32) {
|
|||
}
|
||||
}
|
||||
|
||||
fn compute_face_aabb(face: &mut BspFace, verts: &[BspVertex], vert_idx: usize) {
|
||||
fn compute_face_aabb(face: &mut Face, verts: &[Vertex], vert_idx: usize) {
|
||||
face.aabb_min = Vec3::new(1e30, 1e30, 1e30);
|
||||
face.aabb_max = Vec3::new(-1e30, -1e30, -1e30);
|
||||
|
||||
|
|
@ -148,7 +149,7 @@ fn compute_face_aabb(face: &mut BspFace, verts: &[BspVertex], vert_idx: usize) {
|
|||
}
|
||||
}
|
||||
|
||||
fn build_bsp_node(ctx: &mut BspBuildContext, x0: i32, y0: i32, x1: i32, y1: i32, depth: i32) -> i32 {
|
||||
fn build_bsp_node_grid(ctx: &mut BspBuildContext, x0: i32, y0: i32, x1: i32, y1: i32, depth: i32) -> i32 {
|
||||
if x1 - x0 == 1 && y1 - y0 == 1 {
|
||||
let leaf_idx = y0 * ctx.grid.width + x0;
|
||||
return -(leaf_idx + 1);
|
||||
|
|
@ -164,16 +165,16 @@ fn build_bsp_node(ctx: &mut BspBuildContext, x0: i32, y0: i32, x1: i32, y1: i32,
|
|||
let mid_x = (x0 + x1) / 2;
|
||||
let split_pos = mid_x as f32 * CELL_SIZE;
|
||||
|
||||
ctx.bsp.planes[plane_idx as usize] = BspPlane {
|
||||
ctx.bsp.planes[plane_idx as usize] = Plane {
|
||||
normal: Vec3::new(1.0, 0.0, 0.0),
|
||||
dist: split_pos,
|
||||
plane_type: 0,
|
||||
type_: 0,
|
||||
};
|
||||
|
||||
let child0 = build_bsp_node(ctx, mid_x, y0, x1, y1, depth + 1);
|
||||
let child1 = build_bsp_node(ctx, x0, y0, mid_x, y1, depth + 1);
|
||||
let child0 = build_bsp_node_grid(ctx, mid_x, y0, x1, y1, depth + 1);
|
||||
let child1 = build_bsp_node_grid(ctx, x0, y0, mid_x, y1, depth + 1);
|
||||
|
||||
ctx.bsp.nodes[node_idx as usize] = BspNode {
|
||||
ctx.bsp.nodes[node_idx as usize] = Node {
|
||||
plane_id: plane_idx,
|
||||
children: [child0, child1],
|
||||
..Default::default()
|
||||
|
|
@ -182,16 +183,16 @@ fn build_bsp_node(ctx: &mut BspBuildContext, x0: i32, y0: i32, x1: i32, y1: i32,
|
|||
let mid_y = (y0 + y1) / 2;
|
||||
let split_pos = mid_y as f32 * CELL_SIZE;
|
||||
|
||||
ctx.bsp.planes[plane_idx as usize] = BspPlane {
|
||||
ctx.bsp.planes[plane_idx as usize] = Plane {
|
||||
normal: Vec3::new(0.0, 0.0, 1.0),
|
||||
dist: split_pos,
|
||||
plane_type: 0,
|
||||
type_: 0,
|
||||
};
|
||||
|
||||
let child0 = build_bsp_node(ctx, x0, mid_y, x1, y1, depth + 1);
|
||||
let child1 = build_bsp_node(ctx, x0, y0, x1, mid_y, depth + 1);
|
||||
let child0 = build_bsp_node_grid(ctx, x0, mid_y, x1, y1, depth + 1);
|
||||
let child1 = build_bsp_node_grid(ctx, x0, y0, x1, mid_y, depth + 1);
|
||||
|
||||
ctx.bsp.nodes[node_idx as usize] = BspNode {
|
||||
ctx.bsp.nodes[node_idx as usize] = Node {
|
||||
plane_id: plane_idx,
|
||||
children: [child0, child1],
|
||||
..Default::default()
|
||||
|
|
@ -201,9 +202,33 @@ fn build_bsp_node(ctx: &mut BspBuildContext, x0: i32, y0: i32, x1: i32, y1: i32,
|
|||
node_idx as i32
|
||||
}
|
||||
|
||||
fn build_cell_portals(grid: &RoomGrid) -> Vec<BspCellPortals> {
|
||||
fn build_bsp_node(ctx: &mut BspBuildContext, x0: i32, y0: i32, x1: i32, y1: i32, depth: i32, floor_leaf_idx: i32) -> i32 {
|
||||
let node_idx = ctx.node_count;
|
||||
ctx.node_count += 1;
|
||||
|
||||
let plane_idx = ctx.plane_offset;
|
||||
ctx.plane_offset += 1;
|
||||
|
||||
ctx.bsp.planes[plane_idx as usize] = Plane {
|
||||
normal: Vec3::new(0.0, 1.0, 0.0),
|
||||
dist: 0.0,
|
||||
type_: 1,
|
||||
};
|
||||
|
||||
let above_floor = build_bsp_node_grid(ctx, x0, y0, x1, y1, depth);
|
||||
|
||||
ctx.bsp.nodes[node_idx as usize] = Node {
|
||||
plane_id: plane_idx,
|
||||
children: [above_floor, -(floor_leaf_idx + 1)],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
node_idx as i32
|
||||
}
|
||||
|
||||
fn build_cell_portals(grid: &RoomGrid) -> Vec<CellPortals> {
|
||||
let total_cells = (grid.width * grid.height) as usize;
|
||||
let mut portals = vec![BspCellPortals::default(); total_cells];
|
||||
let mut portals = vec![CellPortals::default(); total_cells];
|
||||
|
||||
for y in 0..grid.height {
|
||||
for x in 0..grid.width {
|
||||
|
|
@ -218,7 +243,7 @@ fn build_cell_portals(grid: &RoomGrid) -> Vec<BspCellPortals> {
|
|||
if x > 0 && grid.get(x - 1, y) == 0 {
|
||||
let p = &mut portals[c];
|
||||
let idx = p.num_portals as usize;
|
||||
p.portals[idx] = BspPortal {
|
||||
p.portals[idx] = Portal {
|
||||
x0: cx,
|
||||
z0: cz,
|
||||
x1: cx,
|
||||
|
|
@ -230,7 +255,7 @@ fn build_cell_portals(grid: &RoomGrid) -> Vec<BspCellPortals> {
|
|||
if x < grid.width - 1 && grid.get(x + 1, y) == 0 {
|
||||
let p = &mut portals[c];
|
||||
let idx = p.num_portals as usize;
|
||||
p.portals[idx] = BspPortal {
|
||||
p.portals[idx] = Portal {
|
||||
x0: cx + CELL_SIZE,
|
||||
z0: cz + CELL_SIZE,
|
||||
x1: cx + CELL_SIZE,
|
||||
|
|
@ -242,7 +267,7 @@ fn build_cell_portals(grid: &RoomGrid) -> Vec<BspCellPortals> {
|
|||
if y > 0 && grid.get(x, y - 1) == 0 {
|
||||
let p = &mut portals[c];
|
||||
let idx = p.num_portals as usize;
|
||||
p.portals[idx] = BspPortal {
|
||||
p.portals[idx] = Portal {
|
||||
x0: cx + CELL_SIZE,
|
||||
z0: cz,
|
||||
x1: cx,
|
||||
|
|
@ -254,7 +279,7 @@ fn build_cell_portals(grid: &RoomGrid) -> Vec<BspCellPortals> {
|
|||
if y < grid.height - 1 && grid.get(x, y + 1) == 0 {
|
||||
let p = &mut portals[c];
|
||||
let idx = p.num_portals as usize;
|
||||
p.portals[idx] = BspPortal {
|
||||
p.portals[idx] = Portal {
|
||||
x0: cx,
|
||||
z0: cz + CELL_SIZE,
|
||||
x1: cx + CELL_SIZE,
|
||||
|
|
@ -277,8 +302,8 @@ struct FloodEntry {
|
|||
|
||||
fn portal_flood_bfs(
|
||||
start_leaf: u32,
|
||||
portals: &[BspCellPortals],
|
||||
leafs: &[BspLeaf],
|
||||
portals: &[CellPortals],
|
||||
leafs: &[Leaf],
|
||||
pvs: &mut [u8],
|
||||
num_leafs: u32,
|
||||
) {
|
||||
|
|
@ -325,8 +350,8 @@ fn portal_flood_bfs(
|
|||
|
||||
fn compute_leaf_pvs(
|
||||
start_leaf: u32,
|
||||
portals: &[BspCellPortals],
|
||||
leafs: &[BspLeaf],
|
||||
portals: &[CellPortals],
|
||||
leafs: &[Leaf],
|
||||
num_leafs: u32,
|
||||
) -> Vec<u8> {
|
||||
let pvs_bytes = ((num_leafs + 7) / 8) as usize;
|
||||
|
|
@ -357,7 +382,7 @@ fn rle_compress_pvs(pvs: &[u8]) -> Vec<u8> {
|
|||
out
|
||||
}
|
||||
|
||||
fn build_pvs_data(bsp: &mut Bsp, portals: &[BspCellPortals]) {
|
||||
fn build_pvs_data(bsp: &mut BspBuilder, portals: &[CellPortals]) {
|
||||
let num_leafs = bsp.leafs.len() as u32;
|
||||
let mut visdata = Vec::new();
|
||||
|
||||
|
|
@ -417,7 +442,7 @@ fn compute_vertex_light(
|
|||
total.min(1.0)
|
||||
}
|
||||
|
||||
fn compute_bsp_vertex_lighting(bsp: &mut Bsp, lights: &[LightSource], ambient: f32) {
|
||||
fn compute_bsp_vertex_lighting(bsp: &mut BspBuilder, lights: &[LightSource], ambient: f32) {
|
||||
if bsp.vertices.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
|
@ -462,35 +487,35 @@ fn compute_bsp_vertex_lighting(bsp: &mut Bsp, lights: &[LightSource], ambient: f
|
|||
}
|
||||
}
|
||||
|
||||
fn grid_to_bsp(bsp: &mut Bsp, grid: &RoomGrid) {
|
||||
let mut face_count = 0;
|
||||
fn grid_to_bsp(bsp: &mut BspBuilder, grid: &RoomGrid) {
|
||||
let mut wall_count = 0;
|
||||
let mut floor_ceiling_count = 0;
|
||||
|
||||
for y in 0..grid.height {
|
||||
for x in 0..grid.width {
|
||||
if grid.get(x, y) == 0 {
|
||||
if grid.get(x - 1, y) == 1 { face_count += 1; }
|
||||
if grid.get(x + 1, y) == 1 { face_count += 1; }
|
||||
if grid.get(x, y - 1) == 1 { face_count += 1; }
|
||||
if grid.get(x, y + 1) == 1 { face_count += 1; }
|
||||
if grid.get(x - 1, y) == 1 { wall_count += 1; }
|
||||
if grid.get(x + 1, y) == 1 { wall_count += 1; }
|
||||
if grid.get(x, y - 1) == 1 { wall_count += 1; }
|
||||
if grid.get(x, y + 1) == 1 { wall_count += 1; }
|
||||
floor_ceiling_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
face_count += floor_ceiling_count;
|
||||
let face_count = wall_count * 2 + floor_ceiling_count;
|
||||
let vertex_count = face_count * 4;
|
||||
|
||||
let total_cells = (grid.width * grid.height) as usize;
|
||||
let max_nodes = 2 * total_cells;
|
||||
let total_planes = face_count + max_nodes;
|
||||
let max_nodes = 2 * total_cells + 1;
|
||||
let total_planes = face_count + max_nodes + 1;
|
||||
|
||||
bsp.vertices = vec![BspVertex::default(); vertex_count];
|
||||
bsp.faces = vec![BspFace::default(); face_count];
|
||||
bsp.planes = vec![BspPlane::default(); total_planes];
|
||||
bsp.edges = vec![BspEdge::default(); vertex_count];
|
||||
bsp.vertices = vec![Vertex::default(); vertex_count];
|
||||
bsp.faces = vec![Face::default(); face_count];
|
||||
bsp.planes = vec![Plane::default(); total_planes];
|
||||
bsp.edges = vec![Edge::default(); vertex_count];
|
||||
bsp.surfedges = vec![0i32; vertex_count];
|
||||
bsp.nodes = vec![BspNode::default(); max_nodes];
|
||||
bsp.nodes = vec![Node::default(); max_nodes];
|
||||
|
||||
let mut face_cell = vec![0u32; face_count];
|
||||
|
||||
|
|
@ -506,9 +531,35 @@ fn grid_to_bsp(bsp: &mut Bsp, grid: &RoomGrid) {
|
|||
let cell_idx = (y * grid.width + x) as u32;
|
||||
|
||||
if grid.get(x - 1, y) == 1 {
|
||||
bsp.vertices[vert_idx + 0].position = Vec3::new(fx, 0.0, fy);
|
||||
bsp.vertices[vert_idx + 0].position = Vec3::new(fx, TRIM_HEIGHT, fy);
|
||||
bsp.vertices[vert_idx + 1].position = Vec3::new(fx, WALL_HEIGHT, fy);
|
||||
bsp.vertices[vert_idx + 2].position = Vec3::new(fx, WALL_HEIGHT, fy + CELL_SIZE);
|
||||
bsp.vertices[vert_idx + 3].position = Vec3::new(fx, TRIM_HEIGHT, fy + CELL_SIZE);
|
||||
|
||||
bsp.planes[face_idx].normal = Vec3::new(1.0, 0.0, 0.0);
|
||||
bsp.planes[face_idx].dist = fx;
|
||||
|
||||
bsp.faces[face_idx].plane_id = face_idx as u16;
|
||||
bsp.faces[face_idx].num_edges = 4;
|
||||
bsp.faces[face_idx].first_edge = edge_idx as u32;
|
||||
bsp.faces[face_idx].material_id = 1;
|
||||
|
||||
for i in 0..4 {
|
||||
bsp.edges[edge_idx + i].vertex[0] = (vert_idx + i) as u16;
|
||||
bsp.edges[edge_idx + i].vertex[1] = (vert_idx + ((i + 1) % 4)) as u16;
|
||||
bsp.surfedges[edge_idx + i] = (edge_idx + i) as i32;
|
||||
}
|
||||
|
||||
compute_face_aabb(&mut bsp.faces[face_idx], &bsp.vertices, vert_idx);
|
||||
face_cell[face_idx] = cell_idx;
|
||||
|
||||
vert_idx += 4;
|
||||
edge_idx += 4;
|
||||
face_idx += 1;
|
||||
|
||||
bsp.vertices[vert_idx + 0].position = Vec3::new(fx, 0.0, fy);
|
||||
bsp.vertices[vert_idx + 1].position = Vec3::new(fx, TRIM_HEIGHT, fy);
|
||||
bsp.vertices[vert_idx + 2].position = Vec3::new(fx, TRIM_HEIGHT, fy + CELL_SIZE);
|
||||
bsp.vertices[vert_idx + 3].position = Vec3::new(fx, 0.0, fy + CELL_SIZE);
|
||||
|
||||
bsp.planes[face_idx].normal = Vec3::new(1.0, 0.0, 0.0);
|
||||
|
|
@ -517,7 +568,7 @@ fn grid_to_bsp(bsp: &mut Bsp, grid: &RoomGrid) {
|
|||
bsp.faces[face_idx].plane_id = face_idx as u16;
|
||||
bsp.faces[face_idx].num_edges = 4;
|
||||
bsp.faces[face_idx].first_edge = edge_idx as u32;
|
||||
bsp.faces[face_idx].material_id = 0;
|
||||
bsp.faces[face_idx].material_id = 3;
|
||||
|
||||
for i in 0..4 {
|
||||
bsp.edges[edge_idx + i].vertex[0] = (vert_idx + i) as u16;
|
||||
|
|
@ -534,8 +585,8 @@ fn grid_to_bsp(bsp: &mut Bsp, grid: &RoomGrid) {
|
|||
}
|
||||
|
||||
if grid.get(x + 1, y) == 1 {
|
||||
bsp.vertices[vert_idx + 0].position = Vec3::new(fx + CELL_SIZE, 0.0, fy);
|
||||
bsp.vertices[vert_idx + 1].position = Vec3::new(fx + CELL_SIZE, 0.0, fy + CELL_SIZE);
|
||||
bsp.vertices[vert_idx + 0].position = Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, fy);
|
||||
bsp.vertices[vert_idx + 1].position = Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, fy + CELL_SIZE);
|
||||
bsp.vertices[vert_idx + 2].position = Vec3::new(fx + CELL_SIZE, WALL_HEIGHT, fy + CELL_SIZE);
|
||||
bsp.vertices[vert_idx + 3].position = Vec3::new(fx + CELL_SIZE, WALL_HEIGHT, fy);
|
||||
|
||||
|
|
@ -545,7 +596,33 @@ fn grid_to_bsp(bsp: &mut Bsp, grid: &RoomGrid) {
|
|||
bsp.faces[face_idx].plane_id = face_idx as u16;
|
||||
bsp.faces[face_idx].num_edges = 4;
|
||||
bsp.faces[face_idx].first_edge = edge_idx as u32;
|
||||
bsp.faces[face_idx].material_id = 0;
|
||||
bsp.faces[face_idx].material_id = 1;
|
||||
|
||||
for i in 0..4 {
|
||||
bsp.edges[edge_idx + i].vertex[0] = (vert_idx + i) as u16;
|
||||
bsp.edges[edge_idx + i].vertex[1] = (vert_idx + ((i + 1) % 4)) as u16;
|
||||
bsp.surfedges[edge_idx + i] = (edge_idx + i) as i32;
|
||||
}
|
||||
|
||||
compute_face_aabb(&mut bsp.faces[face_idx], &bsp.vertices, vert_idx);
|
||||
face_cell[face_idx] = cell_idx;
|
||||
|
||||
vert_idx += 4;
|
||||
edge_idx += 4;
|
||||
face_idx += 1;
|
||||
|
||||
bsp.vertices[vert_idx + 0].position = Vec3::new(fx + CELL_SIZE, 0.0, fy);
|
||||
bsp.vertices[vert_idx + 1].position = Vec3::new(fx + CELL_SIZE, 0.0, fy + CELL_SIZE);
|
||||
bsp.vertices[vert_idx + 2].position = Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, fy + CELL_SIZE);
|
||||
bsp.vertices[vert_idx + 3].position = Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, fy);
|
||||
|
||||
bsp.planes[face_idx].normal = Vec3::new(-1.0, 0.0, 0.0);
|
||||
bsp.planes[face_idx].dist = -(fx + CELL_SIZE);
|
||||
|
||||
bsp.faces[face_idx].plane_id = face_idx as u16;
|
||||
bsp.faces[face_idx].num_edges = 4;
|
||||
bsp.faces[face_idx].first_edge = edge_idx as u32;
|
||||
bsp.faces[face_idx].material_id = 3;
|
||||
|
||||
for i in 0..4 {
|
||||
bsp.edges[edge_idx + i].vertex[0] = (vert_idx + i) as u16;
|
||||
|
|
@ -562,8 +639,8 @@ fn grid_to_bsp(bsp: &mut Bsp, grid: &RoomGrid) {
|
|||
}
|
||||
|
||||
if grid.get(x, y - 1) == 1 {
|
||||
bsp.vertices[vert_idx + 0].position = Vec3::new(fx, 0.0, fy);
|
||||
bsp.vertices[vert_idx + 1].position = Vec3::new(fx + CELL_SIZE, 0.0, fy);
|
||||
bsp.vertices[vert_idx + 0].position = Vec3::new(fx, TRIM_HEIGHT, fy);
|
||||
bsp.vertices[vert_idx + 1].position = Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, fy);
|
||||
bsp.vertices[vert_idx + 2].position = Vec3::new(fx + CELL_SIZE, WALL_HEIGHT, fy);
|
||||
bsp.vertices[vert_idx + 3].position = Vec3::new(fx, WALL_HEIGHT, fy);
|
||||
|
||||
|
|
@ -573,7 +650,33 @@ fn grid_to_bsp(bsp: &mut Bsp, grid: &RoomGrid) {
|
|||
bsp.faces[face_idx].plane_id = face_idx as u16;
|
||||
bsp.faces[face_idx].num_edges = 4;
|
||||
bsp.faces[face_idx].first_edge = edge_idx as u32;
|
||||
bsp.faces[face_idx].material_id = 0;
|
||||
bsp.faces[face_idx].material_id = 1;
|
||||
|
||||
for i in 0..4 {
|
||||
bsp.edges[edge_idx + i].vertex[0] = (vert_idx + i) as u16;
|
||||
bsp.edges[edge_idx + i].vertex[1] = (vert_idx + ((i + 1) % 4)) as u16;
|
||||
bsp.surfedges[edge_idx + i] = (edge_idx + i) as i32;
|
||||
}
|
||||
|
||||
compute_face_aabb(&mut bsp.faces[face_idx], &bsp.vertices, vert_idx);
|
||||
face_cell[face_idx] = cell_idx;
|
||||
|
||||
vert_idx += 4;
|
||||
edge_idx += 4;
|
||||
face_idx += 1;
|
||||
|
||||
bsp.vertices[vert_idx + 0].position = Vec3::new(fx, 0.0, fy);
|
||||
bsp.vertices[vert_idx + 1].position = Vec3::new(fx + CELL_SIZE, 0.0, fy);
|
||||
bsp.vertices[vert_idx + 2].position = Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, fy);
|
||||
bsp.vertices[vert_idx + 3].position = Vec3::new(fx, TRIM_HEIGHT, fy);
|
||||
|
||||
bsp.planes[face_idx].normal = Vec3::new(0.0, 0.0, 1.0);
|
||||
bsp.planes[face_idx].dist = fy;
|
||||
|
||||
bsp.faces[face_idx].plane_id = face_idx as u16;
|
||||
bsp.faces[face_idx].num_edges = 4;
|
||||
bsp.faces[face_idx].first_edge = edge_idx as u32;
|
||||
bsp.faces[face_idx].material_id = 3;
|
||||
|
||||
for i in 0..4 {
|
||||
bsp.edges[edge_idx + i].vertex[0] = (vert_idx + i) as u16;
|
||||
|
|
@ -590,9 +693,35 @@ fn grid_to_bsp(bsp: &mut Bsp, grid: &RoomGrid) {
|
|||
}
|
||||
|
||||
if grid.get(x, y + 1) == 1 {
|
||||
bsp.vertices[vert_idx + 0].position = Vec3::new(fx, 0.0, fy + CELL_SIZE);
|
||||
bsp.vertices[vert_idx + 0].position = Vec3::new(fx, TRIM_HEIGHT, fy + CELL_SIZE);
|
||||
bsp.vertices[vert_idx + 1].position = Vec3::new(fx, WALL_HEIGHT, fy + CELL_SIZE);
|
||||
bsp.vertices[vert_idx + 2].position = Vec3::new(fx + CELL_SIZE, WALL_HEIGHT, fy + CELL_SIZE);
|
||||
bsp.vertices[vert_idx + 3].position = Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, fy + CELL_SIZE);
|
||||
|
||||
bsp.planes[face_idx].normal = Vec3::new(0.0, 0.0, -1.0);
|
||||
bsp.planes[face_idx].dist = -(fy + CELL_SIZE);
|
||||
|
||||
bsp.faces[face_idx].plane_id = face_idx as u16;
|
||||
bsp.faces[face_idx].num_edges = 4;
|
||||
bsp.faces[face_idx].first_edge = edge_idx as u32;
|
||||
bsp.faces[face_idx].material_id = 1;
|
||||
|
||||
for i in 0..4 {
|
||||
bsp.edges[edge_idx + i].vertex[0] = (vert_idx + i) as u16;
|
||||
bsp.edges[edge_idx + i].vertex[1] = (vert_idx + ((i + 1) % 4)) as u16;
|
||||
bsp.surfedges[edge_idx + i] = (edge_idx + i) as i32;
|
||||
}
|
||||
|
||||
compute_face_aabb(&mut bsp.faces[face_idx], &bsp.vertices, vert_idx);
|
||||
face_cell[face_idx] = cell_idx;
|
||||
|
||||
vert_idx += 4;
|
||||
edge_idx += 4;
|
||||
face_idx += 1;
|
||||
|
||||
bsp.vertices[vert_idx + 0].position = Vec3::new(fx, 0.0, fy + CELL_SIZE);
|
||||
bsp.vertices[vert_idx + 1].position = Vec3::new(fx, TRIM_HEIGHT, fy + CELL_SIZE);
|
||||
bsp.vertices[vert_idx + 2].position = Vec3::new(fx + CELL_SIZE, TRIM_HEIGHT, fy + CELL_SIZE);
|
||||
bsp.vertices[vert_idx + 3].position = Vec3::new(fx + CELL_SIZE, 0.0, fy + CELL_SIZE);
|
||||
|
||||
bsp.planes[face_idx].normal = Vec3::new(0.0, 0.0, -1.0);
|
||||
|
|
@ -601,7 +730,7 @@ fn grid_to_bsp(bsp: &mut Bsp, grid: &RoomGrid) {
|
|||
bsp.faces[face_idx].plane_id = face_idx as u16;
|
||||
bsp.faces[face_idx].num_edges = 4;
|
||||
bsp.faces[face_idx].first_edge = edge_idx as u32;
|
||||
bsp.faces[face_idx].material_id = 0;
|
||||
bsp.faces[face_idx].material_id = 3;
|
||||
|
||||
for i in 0..4 {
|
||||
bsp.edges[edge_idx + i].vertex[0] = (vert_idx + i) as u16;
|
||||
|
|
@ -661,7 +790,7 @@ fn grid_to_bsp(bsp: &mut Bsp, grid: &RoomGrid) {
|
|||
bsp.edges.truncate(edge_idx);
|
||||
bsp.surfedges.truncate(edge_idx);
|
||||
|
||||
bsp.leafs = vec![BspLeaf::default(); total_cells];
|
||||
bsp.leafs = vec![Leaf::default(); total_cells + 1];
|
||||
bsp.marksurfaces = vec![0u16; face_idx];
|
||||
|
||||
let mut faces_per_cell = vec![0u32; total_cells];
|
||||
|
|
@ -712,6 +841,17 @@ fn grid_to_bsp(bsp: &mut Bsp, grid: &RoomGrid) {
|
|||
}
|
||||
}
|
||||
|
||||
let floor_leaf_idx = total_cells as i32;
|
||||
let floor_leaf = &mut bsp.leafs[total_cells];
|
||||
floor_leaf.contents = -1;
|
||||
floor_leaf.mins = [0, i16::MIN, 0];
|
||||
floor_leaf.maxs = [
|
||||
(grid.width as f32 * CELL_SIZE) as i16,
|
||||
0,
|
||||
(grid.height as f32 * CELL_SIZE) as i16,
|
||||
];
|
||||
floor_leaf.visofs = -1;
|
||||
|
||||
let face_count_final = face_idx;
|
||||
let mut ctx = BspBuildContext {
|
||||
bsp,
|
||||
|
|
@ -720,7 +860,7 @@ fn grid_to_bsp(bsp: &mut Bsp, grid: &RoomGrid) {
|
|||
plane_offset: face_count_final as u32,
|
||||
};
|
||||
|
||||
build_bsp_node(&mut ctx, 0, 0, grid.width, grid.height, 0);
|
||||
build_bsp_node(&mut ctx, 0, 0, grid.width, grid.height, 0, floor_leaf_idx);
|
||||
|
||||
let node_count = ctx.node_count as usize;
|
||||
let plane_count = ctx.plane_offset as usize;
|
||||
|
|
@ -782,7 +922,7 @@ pub fn generate_rooms(params: &ProcgenParams) -> Bsp {
|
|||
}
|
||||
}
|
||||
|
||||
let mut bsp = Bsp::new();
|
||||
let mut bsp = BspBuilder::new();
|
||||
grid_to_bsp(&mut bsp, &grid);
|
||||
|
||||
let light_height = 80.0;
|
||||
|
|
@ -798,7 +938,7 @@ pub fn generate_rooms(params: &ProcgenParams) -> Bsp {
|
|||
|
||||
compute_bsp_vertex_lighting(&mut bsp, &lights, 0.1);
|
||||
|
||||
bsp
|
||||
bsp.into()
|
||||
}
|
||||
|
||||
pub fn generate(params: &ProcgenParams) -> Bsp {
|
||||
|
|
|
|||
172
pxl8d/src/sim.rs
172
pxl8d/src/sim.rs
|
|
@ -1,38 +1,29 @@
|
|||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use libm::{cosf, sinf, sqrtf};
|
||||
|
||||
use crate::math::Vec3;
|
||||
use crate::protocol::*;
|
||||
use crate::math::{Vec3, Vec3Ext, VEC3_ZERO};
|
||||
use crate::pxl8::*;
|
||||
use crate::voxel::VoxelWorld;
|
||||
use crate::world::World;
|
||||
|
||||
const ALIVE: u32 = 1 << 0;
|
||||
const PLAYER: u32 = 1 << 1;
|
||||
const GROUNDED: u32 = 1 << 2;
|
||||
pub type Entity = pxl8_sim_entity;
|
||||
|
||||
const ALIVE: u32 = PXL8_SIM_FLAG_ALIVE;
|
||||
const PLAYER: u32 = PXL8_SIM_FLAG_PLAYER;
|
||||
|
||||
const MAX_ENTITIES: usize = 1024;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Entity {
|
||||
pub flags: u32,
|
||||
pub kind: u16,
|
||||
pub pos: Vec3,
|
||||
pub vel: Vec3,
|
||||
pub yaw: f32,
|
||||
pub pitch: f32,
|
||||
}
|
||||
|
||||
impl Default for Entity {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
flags: 0,
|
||||
kind: 0,
|
||||
pos: Vec3::ZERO,
|
||||
vel: Vec3::ZERO,
|
||||
pos: VEC3_ZERO,
|
||||
vel: VEC3_ZERO,
|
||||
yaw: 0.0,
|
||||
pitch: 0.0,
|
||||
flags: 0,
|
||||
kind: 0,
|
||||
_pad: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -96,6 +87,13 @@ impl Simulation {
|
|||
id
|
||||
}
|
||||
|
||||
pub fn teleport_player(&mut self, x: f32, y: f32, z: f32) {
|
||||
if let Some(id) = self.player {
|
||||
let ent = &mut self.entities[id as usize];
|
||||
ent.pos = Vec3::new(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step(&mut self, inputs: &[pxl8_input_msg], dt: f32) {
|
||||
self.tick += 1;
|
||||
self.time += dt;
|
||||
|
|
@ -107,120 +105,68 @@ impl Simulation {
|
|||
self.integrate(dt);
|
||||
}
|
||||
|
||||
fn trace(&self, from: Vec3, to: Vec3, radius: f32) -> Vec3 {
|
||||
if self.world.active().is_some() {
|
||||
return self.world.trace(from, to, radius);
|
||||
fn make_sim_world(&self, pos: Vec3) -> pxl8_sim_world {
|
||||
if let Some(chunk) = self.world.active() {
|
||||
if let Some(bsp) = chunk.as_bsp() {
|
||||
return pxl8_sim_world {
|
||||
bsp: bsp.as_c_bsp(),
|
||||
vxl: core::ptr::null(),
|
||||
vxl_cx: 0,
|
||||
vxl_cy: 0,
|
||||
vxl_cz: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let cx = VoxelWorld::world_to_chunk(pos.x);
|
||||
let cy = VoxelWorld::world_to_chunk(pos.y);
|
||||
let cz = VoxelWorld::world_to_chunk(pos.z);
|
||||
|
||||
if let Some(chunk) = self.voxels.get_chunk(cx, cy, cz) {
|
||||
pxl8_sim_world {
|
||||
bsp: core::ptr::null(),
|
||||
vxl: chunk.chunk as *const _,
|
||||
vxl_cx: cx,
|
||||
vxl_cy: cy,
|
||||
vxl_cz: cz,
|
||||
}
|
||||
} else {
|
||||
pxl8_sim_world {
|
||||
bsp: core::ptr::null(),
|
||||
vxl: core::ptr::null(),
|
||||
vxl_cx: 0,
|
||||
vxl_cy: 0,
|
||||
vxl_cz: 0,
|
||||
}
|
||||
}
|
||||
self.voxels.trace(from, to, radius)
|
||||
}
|
||||
|
||||
fn integrate(&mut self, dt: f32) {
|
||||
const GRAVITY: f32 = 800.0;
|
||||
const FRICTION: f32 = 6.0;
|
||||
const RADIUS: f32 = 16.0;
|
||||
|
||||
for i in 0..self.entities.len() {
|
||||
let ent = &self.entities[i];
|
||||
if ent.flags & ALIVE == 0 || ent.flags & PLAYER != 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut vel = ent.vel;
|
||||
let pos = ent.pos;
|
||||
let flags = ent.flags;
|
||||
|
||||
vel.y = vel.y - GRAVITY * dt;
|
||||
|
||||
if flags & GROUNDED != 0 {
|
||||
let speed = sqrtf(vel.x * vel.x + vel.z * vel.z);
|
||||
if speed > 0.0 {
|
||||
let drop = speed * FRICTION * dt;
|
||||
let scale = (speed - drop).max(0.0) / speed;
|
||||
vel.x = vel.x * scale;
|
||||
vel.z = vel.z * scale;
|
||||
}
|
||||
}
|
||||
|
||||
let target = pos + vel * dt;
|
||||
let new_pos = self.trace(pos, target, RADIUS);
|
||||
|
||||
let world = self.make_sim_world(ent.pos);
|
||||
let ent = &mut self.entities[i];
|
||||
ent.vel = vel;
|
||||
ent.pos = new_pos;
|
||||
unsafe {
|
||||
pxl8_sim_integrate(ent, &world, dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn move_player(&mut self, input: &pxl8_input_msg, dt: f32) {
|
||||
let Some(id) = self.player else { return };
|
||||
let ent = &mut self.entities[id as usize];
|
||||
let ent = &self.entities[id as usize];
|
||||
if ent.flags & ALIVE == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
const MOVE_SPEED: f32 = 320.0;
|
||||
const GROUND_ACCEL: f32 = 10.0;
|
||||
const AIR_ACCEL: f32 = 1.0;
|
||||
const STOP_SPEED: f32 = 100.0;
|
||||
const FRICTION: f32 = 6.0;
|
||||
const RADIUS: f32 = 16.0;
|
||||
|
||||
let sin_yaw = sinf(input.yaw);
|
||||
let cos_yaw = cosf(input.yaw);
|
||||
|
||||
let input_len = sqrtf(input.move_x * input.move_x + input.move_y * input.move_y);
|
||||
let (move_dir, target_speed) = if input_len > 0.0 {
|
||||
let nx = input.move_x / input_len;
|
||||
let ny = input.move_y / input_len;
|
||||
let dir = Vec3::new(
|
||||
cos_yaw * nx - sin_yaw * ny,
|
||||
0.0,
|
||||
-sin_yaw * nx - cos_yaw * ny,
|
||||
);
|
||||
(dir, MOVE_SPEED)
|
||||
} else {
|
||||
(Vec3::ZERO, 0.0)
|
||||
};
|
||||
|
||||
let grounded = ent.flags & GROUNDED != 0;
|
||||
|
||||
if grounded {
|
||||
let speed = sqrtf(ent.vel.x * ent.vel.x + ent.vel.z * ent.vel.z);
|
||||
if speed > 0.0 {
|
||||
let control = speed.max(STOP_SPEED);
|
||||
let drop = control * FRICTION * dt;
|
||||
let scale = (speed - drop).max(0.0) / speed;
|
||||
ent.vel.x *= scale;
|
||||
ent.vel.z *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
if target_speed > 0.0 {
|
||||
let accel = if grounded { GROUND_ACCEL } else { AIR_ACCEL };
|
||||
let current = ent.vel.x * move_dir.x + ent.vel.z * move_dir.z;
|
||||
let add = target_speed - current;
|
||||
if add > 0.0 {
|
||||
let amount = (accel * target_speed * dt).min(add);
|
||||
ent.vel.x += move_dir.x * amount;
|
||||
ent.vel.z += move_dir.z * amount;
|
||||
}
|
||||
}
|
||||
|
||||
ent.yaw = input.yaw;
|
||||
ent.pitch = (ent.pitch - input.look_dy * 0.008).clamp(-1.5, 1.5);
|
||||
|
||||
let pos = ent.pos;
|
||||
let vel = ent.vel;
|
||||
let target = pos + vel * dt;
|
||||
let new_pos = self.trace(pos, target, RADIUS);
|
||||
let ground_check = self.trace(new_pos, new_pos - Vec3::Y * 2.0, RADIUS);
|
||||
|
||||
let world = self.make_sim_world(ent.pos);
|
||||
let ent = &mut self.entities[id as usize];
|
||||
ent.pos = new_pos;
|
||||
|
||||
if ground_check.y < new_pos.y - 1.0 {
|
||||
ent.flags &= !GROUNDED;
|
||||
} else {
|
||||
ent.flags |= GROUNDED;
|
||||
unsafe {
|
||||
pxl8_sim_move_player(ent, input, &world, dt);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ extern crate alloc;
|
|||
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use crate::protocol::*;
|
||||
use crate::protocol::pxl8_msg_type::*;
|
||||
use crate::pxl8::*;
|
||||
use crate::pxl8::pxl8_msg_type::*;
|
||||
use crate::voxel::VoxelChunk;
|
||||
|
||||
pub const DEFAULT_PORT: u16 = 7777;
|
||||
|
|
@ -374,6 +374,24 @@ impl Transport {
|
|||
sys::sendto(self.socket, &self.send_buf[..offset], &self.client_addr);
|
||||
}
|
||||
|
||||
pub fn send_chunk_exit(&mut self, sequence: u32) {
|
||||
if !self.has_client {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut offset = 0;
|
||||
|
||||
let msg_header = pxl8_msg_header {
|
||||
sequence,
|
||||
size: 0,
|
||||
type_: PXL8_MSG_CHUNK_EXIT as u8,
|
||||
version: PXL8_PROTOCOL_VERSION as u8,
|
||||
};
|
||||
offset += self.serialize_header(&msg_header, offset);
|
||||
|
||||
sys::sendto(self.socket, &self.send_buf[..offset], &self.client_addr);
|
||||
}
|
||||
|
||||
fn serialize_chunk_msg_header(&mut self, msg: &ChunkMessage, offset: usize) -> usize {
|
||||
let buf = &mut self.send_buf[offset..];
|
||||
buf[0] = msg.chunk_type;
|
||||
|
|
|
|||
|
|
@ -1,21 +1,22 @@
|
|||
extern crate alloc;
|
||||
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use libm::floorf;
|
||||
use core::ptr;
|
||||
|
||||
use crate::math::Vec3;
|
||||
use crate::pxl8::*;
|
||||
|
||||
pub const CHUNK_SIZE: usize = 32;
|
||||
pub const CHUNK_VOLUME: usize = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE;
|
||||
const CHUNK_SIZE: i32 = PXL8_VXL_CHUNK_SIZE as i32;
|
||||
const CHUNK_VOLUME: usize = PXL8_VXL_CHUNK_VOLUME as usize;
|
||||
const WORLD_CHUNK_SIZE: f32 = PXL8_VXL_WORLD_CHUNK_SIZE as f32;
|
||||
const AIR: u8 = PXL8_VXL_BLOCK_AIR as u8;
|
||||
const GRASS: u8 = 3;
|
||||
const DIRT: u8 = 2;
|
||||
const STONE: u8 = 1;
|
||||
|
||||
pub const AIR: u8 = 0;
|
||||
pub const STONE: u8 = 1;
|
||||
pub const DIRT: u8 = 2;
|
||||
pub const GRASS: u8 = 3;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VoxelChunk {
|
||||
pub blocks: [u8; CHUNK_VOLUME],
|
||||
pub chunk: *mut pxl8_vxl_chunk,
|
||||
pub cx: i32,
|
||||
pub cy: i32,
|
||||
pub cz: i32,
|
||||
|
|
@ -23,79 +24,57 @@ pub struct VoxelChunk {
|
|||
|
||||
impl VoxelChunk {
|
||||
pub fn new(cx: i32, cy: i32, cz: i32) -> Self {
|
||||
Self {
|
||||
blocks: [AIR; CHUNK_VOLUME],
|
||||
cx,
|
||||
cy,
|
||||
cz,
|
||||
}
|
||||
let chunk = unsafe { pxl8_vxl_chunk_create() };
|
||||
Self { chunk, cx, cy, cz }
|
||||
}
|
||||
|
||||
pub fn trace(&self, from: Vec3, to: Vec3, radius: f32) -> Vec3 {
|
||||
if !self.is_solid_radius(to.x, to.y, to.z, radius) {
|
||||
return to;
|
||||
}
|
||||
|
||||
let x_ok = !self.is_solid_radius(to.x, from.y, from.z, radius);
|
||||
let y_ok = !self.is_solid_radius(from.x, to.y, from.z, radius);
|
||||
let z_ok = !self.is_solid_radius(from.x, from.y, to.z, radius);
|
||||
|
||||
let mut result = from;
|
||||
if x_ok { result.x = to.x; }
|
||||
if y_ok { result.y = to.y; }
|
||||
if z_ok { result.z = to.z; }
|
||||
result
|
||||
}
|
||||
|
||||
fn world_to_local(x: f32, chunk_coord: i32) -> usize {
|
||||
let chunk_base = chunk_coord as f32 * CHUNK_SIZE as f32;
|
||||
let local = (x - chunk_base) as usize;
|
||||
local.min(CHUNK_SIZE - 1)
|
||||
}
|
||||
|
||||
fn is_solid_at(&self, x: f32, y: f32, z: f32) -> bool {
|
||||
let lx = Self::world_to_local(x, self.cx);
|
||||
let ly = Self::world_to_local(y, self.cy);
|
||||
let lz = Self::world_to_local(z, self.cz);
|
||||
self.get(lx, ly, lz) != AIR
|
||||
}
|
||||
|
||||
fn is_solid_radius(&self, x: f32, y: f32, z: f32, radius: f32) -> bool {
|
||||
self.is_solid_at(x - radius, y, z) ||
|
||||
self.is_solid_at(x + radius, y, z) ||
|
||||
self.is_solid_at(x, y - radius, z) ||
|
||||
self.is_solid_at(x, y + radius, z) ||
|
||||
self.is_solid_at(x, y, z - radius) ||
|
||||
self.is_solid_at(x, y, z + radius)
|
||||
}
|
||||
|
||||
pub fn index(x: usize, y: usize, z: usize) -> usize {
|
||||
x + y * CHUNK_SIZE + z * CHUNK_SIZE * CHUNK_SIZE
|
||||
}
|
||||
|
||||
pub fn get(&self, x: usize, y: usize, z: usize) -> u8 {
|
||||
if x >= CHUNK_SIZE || y >= CHUNK_SIZE || z >= CHUNK_SIZE {
|
||||
pub fn get(&self, x: i32, y: i32, z: i32) -> u8 {
|
||||
if self.chunk.is_null() {
|
||||
return AIR;
|
||||
}
|
||||
self.blocks[Self::index(x, y, z)]
|
||||
unsafe { pxl8_vxl_block_get(self.chunk, x, y, z) }
|
||||
}
|
||||
|
||||
pub fn set(&mut self, x: usize, y: usize, z: usize, block: u8) {
|
||||
if x < CHUNK_SIZE && y < CHUNK_SIZE && z < CHUNK_SIZE {
|
||||
self.blocks[Self::index(x, y, z)] = block;
|
||||
pub fn set(&mut self, x: i32, y: i32, z: i32, block: u8) {
|
||||
if self.chunk.is_null() {
|
||||
return;
|
||||
}
|
||||
unsafe { pxl8_vxl_block_set(self.chunk, x, y, z, block) }
|
||||
}
|
||||
|
||||
pub fn fill(&mut self, block: u8) {
|
||||
if self.chunk.is_null() {
|
||||
return;
|
||||
}
|
||||
unsafe { pxl8_vxl_block_fill(self.chunk, block) }
|
||||
}
|
||||
|
||||
pub fn is_uniform(&self) -> bool {
|
||||
if self.chunk.is_null() {
|
||||
return true;
|
||||
}
|
||||
unsafe { pxl8_vxl_chunk_is_uniform(self.chunk) }
|
||||
}
|
||||
|
||||
pub fn rle_encode(&self) -> Vec<u8> {
|
||||
if self.chunk.is_null() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut linear = vec![0u8; CHUNK_VOLUME];
|
||||
unsafe {
|
||||
pxl8_vxl_chunk_linearize(self.chunk, linear.as_mut_ptr());
|
||||
}
|
||||
|
||||
let mut result = Vec::new();
|
||||
let mut i = 0;
|
||||
|
||||
while i < CHUNK_VOLUME {
|
||||
let block = self.blocks[i];
|
||||
let block = linear[i];
|
||||
let mut run_len = 1usize;
|
||||
|
||||
while i + run_len < CHUNK_VOLUME
|
||||
&& self.blocks[i + run_len] == block
|
||||
&& linear[i + run_len] == block
|
||||
&& run_len < 256
|
||||
{
|
||||
run_len += 1;
|
||||
|
|
@ -110,6 +89,34 @@ impl VoxelChunk {
|
|||
}
|
||||
}
|
||||
|
||||
impl Drop for VoxelChunk {
|
||||
fn drop(&mut self) {
|
||||
if !self.chunk.is_null() {
|
||||
unsafe { pxl8_vxl_chunk_destroy(self.chunk) };
|
||||
self.chunk = ptr::null_mut();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for VoxelChunk {
|
||||
fn clone(&self) -> Self {
|
||||
let new_chunk = Self::new(self.cx, self.cy, self.cz);
|
||||
for z in 0..CHUNK_SIZE {
|
||||
for y in 0..CHUNK_SIZE {
|
||||
for x in 0..CHUNK_SIZE {
|
||||
let block = self.get(x, y, z);
|
||||
if block != AIR {
|
||||
unsafe {
|
||||
pxl8_vxl_block_set(new_chunk.chunk, x, y, z, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
new_chunk
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VoxelWorld {
|
||||
pub chunks: Vec<VoxelChunk>,
|
||||
pub seed: u64,
|
||||
|
|
@ -141,61 +148,7 @@ impl VoxelWorld {
|
|||
}
|
||||
|
||||
pub fn world_to_chunk(x: f32) -> i32 {
|
||||
floorf(x / CHUNK_SIZE as f32) as i32
|
||||
}
|
||||
|
||||
pub fn world_to_local(x: f32) -> usize {
|
||||
let chunk = floorf(x / CHUNK_SIZE as f32);
|
||||
let local = (x - chunk * CHUNK_SIZE as f32) as usize;
|
||||
local.min(CHUNK_SIZE - 1)
|
||||
}
|
||||
|
||||
pub fn is_solid(&self, x: f32, y: f32, z: f32) -> bool {
|
||||
let cx = Self::world_to_chunk(x);
|
||||
let cy = Self::world_to_chunk(y);
|
||||
let cz = Self::world_to_chunk(z);
|
||||
|
||||
let lx = Self::world_to_local(x);
|
||||
let ly = Self::world_to_local(y);
|
||||
let lz = Self::world_to_local(z);
|
||||
|
||||
match self.get_chunk(cx, cy, cz) {
|
||||
Some(chunk) => chunk.get(lx, ly, lz) != AIR,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_solid_radius(&self, x: f32, y: f32, z: f32, radius: f32) -> bool {
|
||||
self.is_solid(x - radius, y, z) ||
|
||||
self.is_solid(x + radius, y, z) ||
|
||||
self.is_solid(x, y - radius, z) ||
|
||||
self.is_solid(x, y + radius, z) ||
|
||||
self.is_solid(x, y, z - radius) ||
|
||||
self.is_solid(x, y, z + radius)
|
||||
}
|
||||
|
||||
pub fn trace(&self, from: Vec3, to: Vec3, radius: f32) -> Vec3 {
|
||||
if !self.is_solid_radius(to.x, to.y, to.z, radius) {
|
||||
return to;
|
||||
}
|
||||
|
||||
let x_ok = !self.is_solid_radius(to.x, from.y, from.z, radius);
|
||||
let y_ok = !self.is_solid_radius(from.x, to.y, from.z, radius);
|
||||
let z_ok = !self.is_solid_radius(from.x, from.y, to.z, radius);
|
||||
|
||||
let mut result = from;
|
||||
|
||||
if x_ok {
|
||||
result.x = to.x;
|
||||
}
|
||||
if y_ok {
|
||||
result.y = to.y;
|
||||
}
|
||||
if z_ok {
|
||||
result.z = to.z;
|
||||
}
|
||||
|
||||
result
|
||||
libm::floorf(x / WORLD_CHUNK_SIZE) as i32
|
||||
}
|
||||
|
||||
pub fn chunks_near(&self, pos: Vec3, radius: i32) -> Vec<(i32, i32, i32)> {
|
||||
|
|
@ -220,6 +173,37 @@ impl VoxelWorld {
|
|||
self.ensure_chunk(cx, cy, cz);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_surface_y(&mut self, world_x: f32, world_z: f32) -> f32 {
|
||||
let scale = PXL8_VXL_SCALE as f32;
|
||||
let block_x = libm::floorf(world_x / scale) as i32;
|
||||
let block_z = libm::floorf(world_z / scale) as i32;
|
||||
|
||||
let cx = libm::floorf(block_x as f32 / CHUNK_SIZE as f32) as i32;
|
||||
let cz = libm::floorf(block_z as f32 / CHUNK_SIZE as f32) as i32;
|
||||
let lx = ((block_x % CHUNK_SIZE) + CHUNK_SIZE) % CHUNK_SIZE;
|
||||
let lz = ((block_z % CHUNK_SIZE) + CHUNK_SIZE) % CHUNK_SIZE;
|
||||
|
||||
for cy in (-2..=2).rev() {
|
||||
self.ensure_chunk(cx, cy, cz);
|
||||
if let Some(chunk) = self.get_chunk(cx, cy, cz) {
|
||||
for ly in (0..CHUNK_SIZE).rev() {
|
||||
let block = chunk.get(lx, ly, lz);
|
||||
if block == GRASS {
|
||||
let world_y = (cy * CHUNK_SIZE + ly + 1) as f32 * scale;
|
||||
return world_y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn noise3d(x: i32, y: i32, z: i32, seed: u64) -> f32 {
|
||||
let h = hash(seed ^ (x as u64) ^ ((y as u64) << 21) ^ ((z as u64) << 42));
|
||||
(h & 0xFFFF) as f32 / 65535.0
|
||||
}
|
||||
|
||||
fn hash(mut x: u64) -> u64 {
|
||||
|
|
@ -231,11 +215,6 @@ fn hash(mut x: u64) -> u64 {
|
|||
x
|
||||
}
|
||||
|
||||
fn noise2d(x: i32, z: i32, seed: u64) -> f32 {
|
||||
let h = hash(seed ^ (x as u64) ^ ((z as u64) << 32));
|
||||
(h & 0xFFFF) as f32 / 65535.0
|
||||
}
|
||||
|
||||
fn smoothstep(t: f32) -> f32 {
|
||||
t * t * (3.0 - 2.0 * t)
|
||||
}
|
||||
|
|
@ -244,33 +223,84 @@ fn lerp(a: f32, b: f32, t: f32) -> f32 {
|
|||
a + (b - a) * t
|
||||
}
|
||||
|
||||
fn value_noise(x: f32, z: f32, seed: u64) -> f32 {
|
||||
let x0 = floorf(x) as i32;
|
||||
let z0 = floorf(z) as i32;
|
||||
fn value_noise_3d(x: f32, y: f32, z: f32, seed: u64) -> f32 {
|
||||
let x0 = libm::floorf(x) as i32;
|
||||
let y0 = libm::floorf(y) as i32;
|
||||
let z0 = libm::floorf(z) as i32;
|
||||
let x1 = x0 + 1;
|
||||
let y1 = y0 + 1;
|
||||
let z1 = z0 + 1;
|
||||
|
||||
let tx = smoothstep(x - x0 as f32);
|
||||
let ty = smoothstep(y - y0 as f32);
|
||||
let tz = smoothstep(z - z0 as f32);
|
||||
|
||||
let c00 = noise2d(x0, z0, seed);
|
||||
let c10 = noise2d(x1, z0, seed);
|
||||
let c01 = noise2d(x0, z1, seed);
|
||||
let c11 = noise2d(x1, z1, seed);
|
||||
let c000 = noise3d(x0, y0, z0, seed);
|
||||
let c100 = noise3d(x1, y0, z0, seed);
|
||||
let c010 = noise3d(x0, y1, z0, seed);
|
||||
let c110 = noise3d(x1, y1, z0, seed);
|
||||
let c001 = noise3d(x0, y0, z1, seed);
|
||||
let c101 = noise3d(x1, y0, z1, seed);
|
||||
let c011 = noise3d(x0, y1, z1, seed);
|
||||
let c111 = noise3d(x1, y1, z1, seed);
|
||||
|
||||
let a = lerp(c00, c10, tx);
|
||||
let b = lerp(c01, c11, tx);
|
||||
lerp(a, b, tz)
|
||||
let a00 = lerp(c000, c100, tx);
|
||||
let a10 = lerp(c010, c110, tx);
|
||||
let a01 = lerp(c001, c101, tx);
|
||||
let a11 = lerp(c011, c111, tx);
|
||||
|
||||
let b0 = lerp(a00, a10, ty);
|
||||
let b1 = lerp(a01, a11, ty);
|
||||
|
||||
lerp(b0, b1, tz)
|
||||
}
|
||||
|
||||
fn fbm(x: f32, z: f32, seed: u64, octaves: u32) -> f32 {
|
||||
fn fbm_3d(x: f32, y: f32, z: f32, seed: u64, octaves: u32) -> f32 {
|
||||
let mut value = 0.0;
|
||||
let mut amplitude = 1.0;
|
||||
let mut frequency = 1.0;
|
||||
let mut max_value = 0.0;
|
||||
|
||||
for i in 0..octaves {
|
||||
value += amplitude * value_noise(x * frequency, z * frequency, seed.wrapping_add(i as u64 * 1000));
|
||||
value += amplitude * value_noise_3d(
|
||||
x * frequency,
|
||||
y * frequency,
|
||||
z * frequency,
|
||||
seed.wrapping_add(i as u64 * 1000)
|
||||
);
|
||||
max_value += amplitude;
|
||||
amplitude *= 0.5;
|
||||
frequency *= 2.0;
|
||||
}
|
||||
|
||||
value / max_value
|
||||
}
|
||||
|
||||
fn fbm_2d(x: f32, z: f32, seed: u64, octaves: u32) -> f32 {
|
||||
let mut value = 0.0;
|
||||
let mut amplitude = 1.0;
|
||||
let mut frequency = 1.0;
|
||||
let mut max_value = 0.0;
|
||||
|
||||
for i in 0..octaves {
|
||||
let x0 = libm::floorf(x * frequency) as i32;
|
||||
let z0 = libm::floorf(z * frequency) as i32;
|
||||
let x1 = x0 + 1;
|
||||
let z1 = z0 + 1;
|
||||
|
||||
let tx = smoothstep(x * frequency - x0 as f32);
|
||||
let tz = smoothstep(z * frequency - z0 as f32);
|
||||
|
||||
let offset_seed = seed.wrapping_add(i as u64 * 1000);
|
||||
let c00 = (hash(offset_seed ^ (x0 as u64) ^ ((z0 as u64) << 32)) & 0xFFFF) as f32 / 65535.0;
|
||||
let c10 = (hash(offset_seed ^ (x1 as u64) ^ ((z0 as u64) << 32)) & 0xFFFF) as f32 / 65535.0;
|
||||
let c01 = (hash(offset_seed ^ (x0 as u64) ^ ((z1 as u64) << 32)) & 0xFFFF) as f32 / 65535.0;
|
||||
let c11 = (hash(offset_seed ^ (x1 as u64) ^ ((z1 as u64) << 32)) & 0xFFFF) as f32 / 65535.0;
|
||||
|
||||
let a = lerp(c00, c10, tx);
|
||||
let b = lerp(c01, c11, tx);
|
||||
value += amplitude * lerp(a, b, tz);
|
||||
|
||||
max_value += amplitude;
|
||||
amplitude *= 0.5;
|
||||
frequency *= 2.0;
|
||||
|
|
@ -280,29 +310,60 @@ fn fbm(x: f32, z: f32, seed: u64, octaves: u32) -> f32 {
|
|||
}
|
||||
|
||||
fn generate_chunk(chunk: &mut VoxelChunk, seed: u64) {
|
||||
let world_x = chunk.cx * CHUNK_SIZE as i32;
|
||||
let world_y = chunk.cy * CHUNK_SIZE as i32;
|
||||
let world_z = chunk.cz * CHUNK_SIZE as i32;
|
||||
let world_x = chunk.cx * CHUNK_SIZE;
|
||||
let world_y = chunk.cy * CHUNK_SIZE;
|
||||
let world_z = chunk.cz * CHUNK_SIZE;
|
||||
|
||||
for lz in 0..CHUNK_SIZE {
|
||||
for lx in 0..CHUNK_SIZE {
|
||||
let mut height_cache = [[0i32; 32]; 32];
|
||||
for lz in 0..32 {
|
||||
for lx in 0..32 {
|
||||
let wx = (world_x + lx as i32) as f32;
|
||||
let wz = (world_z + lz as i32) as f32;
|
||||
height_cache[lz][lx] = (fbm_2d(wx * 0.02, wz * 0.02, seed, 4) * 32.0) as i32;
|
||||
}
|
||||
}
|
||||
|
||||
let height = fbm(wx * 0.02, wz * 0.02, seed, 4) * 32.0 + 16.0;
|
||||
let height = height as i32;
|
||||
let mut density = [[[0.0f32; 33]; 33]; 33];
|
||||
for lz in 0..33 {
|
||||
for ly in 0..33 {
|
||||
for lx in 0..33 {
|
||||
let wx = (world_x + lx as i32) as f32;
|
||||
let wy = (world_y + ly as i32) as f32;
|
||||
let wz = (world_z + lz as i32) as f32;
|
||||
|
||||
for ly in 0..CHUNK_SIZE {
|
||||
let wy = world_y + ly as i32;
|
||||
let hx = lx.min(31);
|
||||
let hz = lz.min(31);
|
||||
let base_height = height_cache[hz][hx] as f32;
|
||||
let height_bias = (base_height - wy) * 0.1;
|
||||
|
||||
let block = if wy > height {
|
||||
AIR
|
||||
} else if wy == height {
|
||||
let cave_noise = fbm_3d(wx * 0.05, wy * 0.05, wz * 0.05, seed.wrapping_add(1000), 2);
|
||||
density[lz][ly][lx] = height_bias + (cave_noise - 0.55);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for lz in 0..CHUNK_SIZE {
|
||||
for ly in 0..CHUNK_SIZE {
|
||||
for lx in 0..CHUNK_SIZE {
|
||||
let d = density[lz as usize][ly as usize][lx as usize];
|
||||
if d <= 0.0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let d_above = density[lz as usize][(ly + 1) as usize][lx as usize];
|
||||
let is_surface = d_above <= 0.0;
|
||||
|
||||
let block = if is_surface {
|
||||
GRASS
|
||||
} else if wy > height - 4 {
|
||||
DIRT
|
||||
} else {
|
||||
STONE
|
||||
let wy = world_y + ly;
|
||||
let base_height = height_cache[lz as usize][lx as usize];
|
||||
let depth = base_height - wy;
|
||||
if depth < 4 {
|
||||
DIRT
|
||||
} else {
|
||||
STONE
|
||||
}
|
||||
};
|
||||
|
||||
chunk.set(lx, ly, lz, block);
|
||||
|
|
|
|||
|
|
@ -38,10 +38,6 @@ impl World {
|
|||
self.chunks.remove(id)
|
||||
}
|
||||
|
||||
pub fn set_active(&mut self, id: ChunkId) {
|
||||
self.active = Some(id);
|
||||
}
|
||||
|
||||
pub fn active(&self) -> Option<&Chunk> {
|
||||
self.active.as_ref().and_then(|id| self.chunks.get(id))
|
||||
}
|
||||
|
|
@ -50,6 +46,14 @@ impl World {
|
|||
self.active
|
||||
}
|
||||
|
||||
pub fn set_active(&mut self, id: ChunkId) {
|
||||
self.active = Some(id);
|
||||
}
|
||||
|
||||
pub fn clear_active(&mut self) {
|
||||
self.active = None;
|
||||
}
|
||||
|
||||
pub fn trace(&self, from: Vec3, to: Vec3, radius: f32) -> Vec3 {
|
||||
if let Some(chunk) = self.active() {
|
||||
return chunk.trace(from, to, radius);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue