Compare commits

..

1 commit

Author SHA1 Message Date
Halbear
cb12591f5d funny world gen (translated Terrain4J's noise to rust for funsies) 2026-09-05 21:51:55 +01:00
30 changed files with 4678 additions and 1963 deletions

1
.gitignore vendored
View file

@ -1,3 +1,2 @@
target
.DS_Store
Cargo.lock

10
.idea/.gitignore generated vendored Normal file
View file

@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

6
.idea/Pool.iml generated
View file

@ -3,7 +3,13 @@
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/stupid_display/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/mars/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/lang/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/stupid_display/target" />
<excludeFolder url="file://$MODULE_DIR$/target" />
<excludeFolder url="file://$MODULE_DIR$/src/mars/target" />
<excludeFolder url="file://$MODULE_DIR$/lang/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View file

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

8
.idea/dictionaries/project.xml generated Normal file
View file

@ -0,0 +1,8 @@
<component name="ProjectDictionaryState">
<dictionary name="project">
<words>
<w>forloop</w>
<w>vmcase</w>
</words>
</dictionary>
</component>

3033
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,18 +1,14 @@
[package]
#![recursion_limit = "256"]
name = "pool"
name = "game"
version = "0.1.0"
edition = "2024"
[features]
big_ids = []
check_ids = []
[profile.release]
strip = true
[[bin]]
name = "pool"
name = "game"
path = "src/main.rs"
[dependencies]
@ -21,17 +17,19 @@ anyhow = "1.0"
winit = { version = "0.30", features = ["android-native-activity"] }
env_logger = "0.11.10"
log = "0.4"
wgpu = "29.0.4"
wgpu = "29.0.3"
pollster = "0.4.0"
glam = { version = "0.33.3", features = [ "bytemuck" ] }
console_error_panic_hook = "0.1.7"
bytemuck = { version = "1.25.0", features = [ "derive" ]}
image = { version = "0.24", default-features = false, features = ["png", "jpeg"]}
mars = { path = "../Mars" }
rand = "0.8"
rand_chacha = "0.3"
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1.6"
wgpu = { version = "30.0.1", features = ["webgl"]}
wgpu = { version = "29.0.3", features = ["webgl"]}
wasm-bindgen = "0.2.121"
wasm-bindgen-futures = "0.4.71"
console_log = "1.0.0"

View file

@ -1,6 +1,6 @@
_Pool. Hop in, the water's warm._
Pool is a lightweight web-enabled multipurpose engine for UI apps and multiplayer games with physics.
Pool is a lightweight web-enabled multipurpose engine for UI apps and multiplayer games with physics designed to push the boundaries of what defines a contemporary user experience.
Taking inspiration from the Roblox game engine, Pool ships with its own scripting language, Mars, and shares a similar instance-service model.

View file

@ -1,8 +1,10 @@
use std::collections::HashMap;
// Credit of most code to https://sotrh.github.io/learn-wgpu/ since I'm not familiar with wgpu
use crate::render::{loader, Renderer};
use crate::world::{World, ObjectData};
use glam::{Affine3, Affine3A, EulerRot, Mat4, Quat, Vec2, Vec3, Vec4};
use crate::render::Renderer;
use crate::world::{SimpleObject, World};
use crate::permutation_array;
use crate::noise2d;
use glam::{Affine3A, EulerRot, Mat4, Quat, Vec2, Vec3, Vec4};
use std::sync::Arc;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
@ -16,12 +18,8 @@ use winit::{
keyboard::{KeyCode, PhysicalKey},
window::Window,
};
use crate::list::Id;
use crate::render::instance::{Material, ModelData};
use crate::render::instance::material::MaterialProperties;
use crate::render::texture::Texture;
use crate::state;
use crate::world::debug::OctreeDebug;
use crate::noise2d::Noise2D;
use crate::permutation_array::PermutationArray;
struct Controller {
buttons: HashMap<MouseButton, bool>,
@ -40,66 +38,76 @@ impl Controller {
}
pub struct AppState {
state: state::State,
world: Id<World>,
debug: OctreeDebug,
debug_model: ModelData,
world: World,
controller: Controller,
window: Arc<Window>,
clients: Vec<SimpleObject>,
chunks: Vec<(i32, i32)>,
Permutation: PermutationArray,
}
const BLOCKS: i32 = 2;
const BLOCKS: i32 = 100;
impl AppState {
// We don't need this to be async right now,
// but we will in the next tutorial
pub async fn new(window: Arc<Window>) -> Result<AppState, Box<dyn std::error::Error>> {
let mut state = state::State::new();
let (world,debug,debug_model) = {
let mut world = state.worlds.make(World::new());
state.renderer = Some(Renderer::new(&window).await?);
let renderer = state.renderer.as_mut().unwrap();
let debug_file = loader::load_from_gltf(renderer,include_bytes!("assets/debug.glb"));
let debug_model = debug_file.first_object().unwrap().model.unwrap();
{
let file = loader::load_from_gltf(renderer,include_bytes!("assets/sphere.glb"));
let mut block = file.first_object().unwrap();
let skybox = Texture::load_from_file_bytes(renderer,include_bytes!("assets/skybox2.png"), None);
renderer.set_skybox(skybox.unwrap());
let color = Texture::load_from_file_bytes(renderer,include_bytes!("assets/plank/color.png"), None);
let normal = Texture::load_from_file_bytes(renderer,include_bytes!("assets/plank/normal.png"), None);
let roughness = Texture::load_from_file_bytes(renderer,include_bytes!("assets/plank/roughness.png"), None);
let mat = Material::new(renderer, &color.unwrap(), &normal.unwrap(), &roughness.unwrap(), MaterialProperties::default());
block.model.as_mut().unwrap().material = mat;
for x in -BLOCKS..=BLOCKS {
for y in -BLOCKS..=BLOCKS {
let id = world.add_object(state.objects.make(block.clone()));
let block = state.objects.get(&id);
block.set_affine(Affine3::from_translation(
Vec3::new(-x as f32 * 3.0, -2.0, -y as f32 * 3.0)
));
block.model.as_mut().unwrap().instance.color = Vec4::new(
0.3,
(x + BLOCKS) as f32 / BLOCKS as f32,
(y + BLOCKS) as f32 / BLOCKS as f32,
1.0,
);
}
}
}
let debug = OctreeDebug::new();
(world.id,debug,debug_model)
};
let mut world = World::new();
world.add_renderer(Renderer::new(&window).await?);
let mut Permutation: PermutationArray = PermutationArray::new();
Permutation.generate_permutation_array(0);
let mut chunks = Vec::new();
let mut clients = Vec::new();
{
let file = world
.renderer
.as_mut()
.unwrap()
.load_from_gltf(include_bytes!("assets/cube.glb"));
let block = file.first_object().unwrap();
let skybox = world.renderer.as_mut().unwrap().load_texture_from_bytes(
include_bytes!("assets/skybox1.png"),
Some(image::ImageFormat::Png),
);
world.renderer.as_mut().unwrap().set_skybox(skybox);
//let color = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/color.png"));
//let normal = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/normal.png"));
//let roughness = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/roughness.png"));
//let mat = renderer.new_material(color,normal,roughness);
// for x in -BLOCKS..BLOCKS {
// for y in -BLOCKS..BLOCKS {
// let block = block.hard_clone();
// world.add_object(block.clone());
// {
// let mut model = block.0.borrow_mut();
// model.model.as_mut().unwrap().instance.transform = Mat4::from_translation(
// Vec3::new(-x as f32 * 1.0, Noise2D::fractal_noise(x as f64 * 0.1, y as f64*0.1, 5, 10.0, 0.05, &Permutation).round() as i64 as f32, -y as f32 * 1.0),
// )
// * Mat4::from_rotation_z(0f32 /*(x * y) as f32 / 1.23*/);
// model.model.as_mut().unwrap().instance.color = Vec4::new(
// 0.3,
// (x + BLOCKS) as f32 / BLOCKS as f32,
// (y + BLOCKS) as f32 / BLOCKS as f32,
// 1.0,
// );
// }
// clients.push(block)
// }
// }
}
Ok(Self {
state,
world,
debug,
debug_model,
clients,
controller: Controller::new(),
window,
chunks,
Permutation
})
}
pub fn bounds(&self) -> (u32, u32) {
let size = self.window.inner_size();
(size.width, size.height)
@ -107,7 +115,7 @@ impl AppState {
pub fn resize(&mut self, width: u32, height: u32) {
if width > 0 && height > 0 {
self.state.renderer.as_mut().unwrap().resize(width, height);
self.world.renderer.as_mut().unwrap().resize(width, height);
}
}
@ -120,7 +128,7 @@ impl AppState {
(KeyCode::Escape, true) => event_loop.exit(),
(KeyCode::Space, true) => {}
(KeyCode::KeyR, true) => {
self.state.renderer.as_mut().unwrap().eye.frame = Affine3A::IDENTITY
self.world.renderer.as_mut().unwrap().eye.frame = Affine3A::IDENTITY
}
_ => {}
}
@ -129,7 +137,7 @@ impl AppState {
fn handle_mouse_moved(&mut self, position: PhysicalPosition<f64>) {
if let Some(true) = self.controller.buttons.get(&MouseButton::Right) {
self.state.renderer.as_mut().unwrap().eye.rotate(
self.world.renderer.as_mut().unwrap().eye.rotate(
position.x as f32 - self.controller.mouse.x,
position.y as f32 - self.controller.mouse.y,
);
@ -158,6 +166,42 @@ impl App {
proxy,
}
}
pub fn GenerateChunk(ChunkX: i32, ChunkY: i32, size: i32, state: &mut AppState){
if(state.chunks.contains(&(ChunkX, ChunkY))){
return;
}
let file = state.world
.renderer
.as_mut()
.unwrap()
.load_from_gltf(include_bytes!("assets/cube.glb"));
let block = file.first_object().unwrap();
for x in 0..size {
for y in 0..size {
let worldPosX = ChunkX * size + x;
let worldPosY = ChunkY * size + y;
let block = block.hard_clone();
state.world.add_object(block.clone());
{
let mut model = block.0.borrow_mut();
model.model.as_mut().unwrap().instance.transform = Mat4::from_translation(
Vec3::new(worldPosX as f32 * 1.0, Noise2D::fractal_noise(worldPosX as f64 * 0.25, worldPosY as f64*0.25, 10, 20.0, 0.05, &state.Permutation).round() as i64 as f32, worldPosY as f32 * 1.0),
)
* Mat4::from_rotation_z(0f32 /*(x * y) as f32 / 1.23*/);
model.model.as_mut().unwrap().instance.color = Vec4::new(
0.3,
(x + size) as f32 / size as f32,
(y + size) as f32 / size as f32,
1.0,
);
}
state.clients.push(block);
}
}
state.chunks.push((ChunkX, ChunkY));
}
}
impl ApplicationHandler<AppState> for App {
@ -181,8 +225,6 @@ impl ApplicationHandler<AppState> for App {
let window = Arc::new(event_loop.create_window(window_attributes).unwrap());
window.set_title("Pool");
#[cfg(not(target_arch = "wasm32"))]
{
// If we are not on web we can use pollster to
@ -239,14 +281,15 @@ impl ApplicationHandler<AppState> for App {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::Resized(size) => state.resize(size.width, size.height),
WindowEvent::RedrawRequested => {
use std::time::Instant;
let now = Instant::now();
state.update();
let mut movement = Vec3::new(0.0, 0.0, 0.0);
let pressed = |keycode: KeyCode| {
matches!(state.controller.keys.get(&keycode), Some(true))
if let Some(true) = state.controller.keys.get(&keycode) {
true
} else {
false
}
};
if pressed(KeyCode::KeyA) {
@ -267,12 +310,30 @@ impl ApplicationHandler<AppState> for App {
if pressed(KeyCode::KeyQ) {
movement.y -= 1.0;
}
let world = state.state.worlds.get(&state.world);
state.debug.set(&mut state.state.objects, world, Some(state.debug_model.clone()));
state.debug.register(&mut state.state.objects,state.state.renderer.as_mut().unwrap());
world.step(&mut state.state.renderer, &mut state.state.objects, &mut state.state.lights);
state.state.renderer.as_mut().unwrap().eye.control(movement * 0.1);
match state.state.renderer.as_mut().unwrap().render(&state.window,&mut state.state.objects) {
let PosX = state.world.renderer.as_ref().unwrap().eye.frame.translation.x;
let Posy = state.world.renderer.as_ref().unwrap().eye.frame.translation.z;
let WorldPosX = (PosX / 16.0).floor() as i32;
let WorldPosY = (Posy / 16.0).floor() as i32;
let startX = WorldPosX - 5;
let endX = WorldPosX + 5;
let startY = WorldPosY - 5;
let endY = WorldPosY + 5;
for x in startX..endX {
for y in startY..endY {
App::GenerateChunk(x, y, 16, state);
}
}
state
.world
.renderer
.as_mut()
.unwrap()
.eye
.control(movement * 0.5);
match state.world.renderer.as_mut().unwrap().render(&state.window) {
Ok(_) => {}
Err(e) => {
// Log the error and exit gracefully
@ -280,9 +341,19 @@ impl ApplicationHandler<AppState> for App {
event_loop.exit();
}
}
let elapsed = now.elapsed();
//println!("Elapsed {:.2?}",elapsed);
for object in state.clients.iter() {
object
.0
.borrow_mut()
.model
.as_mut()
.unwrap()
.instance
.transform *= Mat4::from_rotation_translation(
Quat::from_euler(EulerRot::XYZ,0.00,0.00,0.00 /*0.001, -0.001, 0.001*/),
Vec3::new(0.0, 0.0, 0.0),
);
}
}
WindowEvent::MouseInput {
button,
@ -302,6 +373,9 @@ impl ApplicationHandler<AppState> for App {
_ => {}
}
}
}
pub fn run() -> anyhow::Result<()> {

View file

@ -15,6 +15,7 @@ struct Eye {
frame: mat4x4<f32>,
}
// Vertex shader
@group(0) @binding(0)
var<uniform> eye: Eye;
@group(0) @binding(1)
@ -87,36 +88,31 @@ var t_rough: texture_2d<f32>;
fn sky_aspect(look: vec3<f32>) -> vec4<f32> {
var pi = 3.14159;
let u_angle = atan2(look.x,look.z);
let u = (u_angle/pi) * 0.5 + 0.5; // from -pi/2 -> pi/2 into 0 -> 1
let u = (u_angle/pi) + 0.5; // from -pi/2 -> pi/2 into 0 -> 1
let v_angle = atan2(-look.y,sqrt(look.x * look.x + look.z * look.z));
let v = (v_angle/pi) - 0.5;//(v_angle/pi) + 0.5; // from -pi/2 -> pi/2 into 0 -> 1
let v = (v_angle/pi) + 0.5;//(v_angle/pi) + 0.5; // from -pi/2 -> pi/2 into 0 -> 1
let uv = vec2<f32>(u,v); // Get UV on skybox
return textureSample(sky_texture, sky_sampler, uv);
}
fn rotation(it: mat4x4<f32>) -> mat3x3<f32> {
fn rotation(mat: mat4x4<f32>) -> mat3x3<f32> {
return mat3x3<f32>(
it[0].xyz,
it[1].xyz,
it[2].xyz,
mat[0].xyz,
mat[1].xyz,
mat[2].xyz,
);
}
fn translation(it: mat4x4<f32>) -> vec4<f32> {
return it[3];
}
fn light_aspect(light: vec3<f32>, dir: vec3<f32>) -> vec4<f32> {
return vec4<f32>(0.0,0.0,0.0,0.0);
fn translation(mat: mat4x4<f32>) -> vec4<f32> {
//return vec4<f32>(mat[0][3],mat[1][3],mat[2][3],mat[3][3]);
return mat[3];
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
let object_normal: vec4<f32> = textureSample(t_normal, s_diffuse, in.tex_coords);
let object_rough: vec4<f32> = textureSample(t_rough, s_diffuse, in.tex_coords);
let diffuse_result = object_color.xyz * environment.ambient.xyz;
let diffuse_color = object_color.xyz;
let specular_color = vec3<f32>(0.0,0.0,0.0);
let reflection = sky_aspect(reflect(normalize(in.world_position-translation(eye.frame).xyz),normalize(in.world_normal)));
@ -133,13 +129,35 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
//let specular_strength = pow(max(dot(tangent_normal, half_dir), 0.0), 32.0);
//let specular_color = specular_strength * environment.light.xyz;
let reflect_factor = pow(object_rough.x,3);
let result = (environment.ambient.xyz + diffuse_color.xyz + specular_color.xyz) * object_color.xyz;
let diffuse_brightness = length(diffuse_color);
return vec4<f32>(reflection.xyz,object_color.a);
}
let diffuse_effect = (diffuse_result * (1 - reflect_factor)) + reflection.xyz * reflect_factor * diffuse_brightness;
struct SkyOutput {
@builtin(position) position: vec4<f32>,
@location(0) pos: vec4<f32> // unadulterated by WGSL
}
let result = (diffuse_effect.xyz + specular_color.xyz);
const TRI_VERTICES = array(
vec4(-1.0, -1.0, 1.0, 1.0),
vec4(-1.0, 1.0, 1.0, 1.0),
vec4( 1.0, -1.0, 1.0, 1.0),
vec4( 1.0, 1.0, 1.0, 1.0),
vec4(-1.0, 1.0, 1.0, 1.0),
vec4( 1.0, -1.0, 1.0, 1.0),
);
return vec4<f32>(result.xyz,object_color.a);
}
@vertex
fn vs_sky(@builtin(vertex_index) index: u32) -> SkyOutput {
var out: SkyOutput;
out.position = TRI_VERTICES[index];
out.pos = out.position;
return out;
}
@fragment
fn fs_sky(in: SkyOutput) -> @location(0) vec4<f32> {
let look = rotation(eye.frame) * in.pos.xyz;
return sky_aspect(look);
}

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 892 KiB

View file

@ -1,23 +1,7 @@
#![recursion_limit = "256"]
use std::error::Error;
use std::fmt::{Display, Formatter};
pub mod app;
pub mod render;
pub mod web;
pub mod world;
pub mod list;
pub mod state;
pub mod net;
#[derive(Debug)]
struct PoolError(String);
impl Display for PoolError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Error for PoolError {}
pub mod permutation_array;
pub mod noise2d;

View file

@ -1,185 +0,0 @@
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut};
use std::sync::Mutex;
type Index = u32;
pub struct Element<T> {
it: Option<T>,
count: Index,
}
pub struct FreeList<T> {
items: Vec<Option<T>>,
free: Vec<Index>,
}
#[derive(Eq,Hash,PartialEq)]
pub struct SingleId<T> {
index: Index,
phantom_data: PhantomData<T>
}
impl<T> From<SingleId<T>> for Id<T> {
fn from(value: SingleId<T>) -> Self {
Id {
index: value.index,
phantom_data: PhantomData,
}
}
}
impl<T> SingleId<T> {
pub(crate) fn shared(self) -> Id<T> {
Id {
index: self.index,
phantom_data: self.phantom_data
}
}
}
pub struct Id<T> {
index: Index,
phantom_data: PhantomData<T>,
}
pub struct RefId<'a, T> {
pub id: Id<T>,
it: &'a mut T
}
impl<'a, T> Deref for RefId<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.it
}
}
impl<'a, T> DerefMut for RefId<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.it
}
}
impl<T> From<RefId<'_, T>> for Id<T> {
fn from(value: RefId<T>) -> Self {
value.id
}
}
impl<T> Clone for Id<T> {
fn clone(&self) -> Self {
Id {
index: self.index,
phantom_data: PhantomData::default(),
}
}
}
impl<T> PartialEq for Id<T> {
fn eq(&self, other: &Self) -> bool {
self.index == other.index
}
}
impl<T> Eq for Id<T> {}
impl<T> Hash for Id<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.index.hash(state)
}
}
impl<T> Id<T> {
pub fn maybe(self) -> MaybeId<T> {
MaybeId(self)
}
}
pub struct MaybeId<T>(Id<T>);
impl<T> From<Id<T>> for MaybeId<T> {
fn from(value: Id<T>) -> Self {
MaybeId(value)
}
}
impl<T> Clone for MaybeId<T> {
fn clone(&self) -> Self {
if self.0.index != u32::MAX {
MaybeId(self.0.clone())
} else {
MaybeId::NULL
}
}
}
impl<T> MaybeId<T> {
pub const NULL: MaybeId<T> = MaybeId(Id { index: u32::MAX, phantom_data: PhantomData {}, });
pub fn unwrap(self) -> Id<T> {
if self.0.index == u32::MAX {
panic!()
} else {
self.0
}
}
pub fn exists(&self) -> Option<Id<T>> {
if self.0.index == u32::MAX {
None
} else {
Some(self.0.clone())
}
}
}
impl<T> Default for FreeList<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> FreeList<T> {
pub fn new() -> FreeList<T> {
FreeList {
items: Vec::new(),
free: Vec::new(),
}
}
pub fn get(&mut self, id: &Id<T>) -> &mut T {
self.items[id.index as usize].as_mut().unwrap()
}
pub fn get_ref(&mut self, id: Id<T>) -> RefId<'_, T> {
let it = self.get(&id);
RefId { id, it }
}
pub fn remove(&mut self, id: Id<T>) -> T {
self.free.push(id.index);
self.items[id.index as usize].take().unwrap()
}
pub fn make(&mut self, value: T) -> RefId<'_, T> { // todo: shouldn't panic if allocation fails
if let Some(free) = self.free.pop() {
self.items[free as usize] = Some(value);
RefId {
id: Id {
index: free,
phantom_data: PhantomData,
},
it: self.items[free as usize].as_mut().unwrap(),
}
} else {
self.items.push(Some(value));
let index = (self.items.len() - 1) as Index;
RefId {
id: Id {
index,
phantom_data: PhantomData,
},
it: self.items[index as usize].as_mut().unwrap(),
}
}
}
}

View file

@ -1,6 +1,6 @@
#![recursion_limit = "256"]
use pool::*;
use game::*;
fn main() {
app::run().unwrap();
}

View file

97
src/noise2d.rs Normal file
View file

@ -0,0 +1,97 @@
use crate::permutation_array;
use crate::permutation_array::PermutationArray;
#[derive(Debug, Clone, Copy)]
pub struct Vector2d {
pub x: f64,
pub y: f64,
}
impl Vector2d {
pub fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
pub fn dot(&self, other: Vector2d) -> f64 {
self.x * other.x + self.y * other.y
}
}
pub struct Noise2D;
impl Noise2D {
pub fn gen_gradient_vector(p: i32) -> Vector2d {
match p & 3 {
0 => Vector2d::new(1.0, 1.0),
1 => Vector2d::new(-1.0, 1.0),
2 => Vector2d::new(-1.0, -1.0),
_ => Vector2d::new(1.0, -1.0),
}
}
fn fade(t: f64) -> f64 {
t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
}
fn lerp(t: f64, x: f64, y: f64) -> f64 {
x + t * (y - x)
}
pub fn noise_2d(x: f64, y: f64, permutation: &[i32; 512]) -> f64 {
let x_floor = x.floor();
let y_floor = y.floor();
let x_grid = (x_floor as i32) & 255;
let y_grid = (y_floor as i32) & 255;
let xd = x - x_floor;
let yd = y - y_floor;
let tr = Vector2d::new(xd - 1.0, yd - 1.0);
let tl = Vector2d::new(xd, yd - 1.0);
let br = Vector2d::new(xd - 1.0, yd);
let bl = Vector2d::new(xd, yd);
let x_idx = x_grid as usize;
let y_idx = y_grid as usize;
let value_tr = permutation[permutation[x_idx + 1] as usize + y_idx + 1];
let value_tl = permutation[permutation[x_idx] as usize + y_idx + 1];
let value_br = permutation[permutation[x_idx + 1] as usize + y_idx];
let value_bl = permutation[permutation[x_idx] as usize + y_idx];
let d_tr = tr.dot(Self::gen_gradient_vector(value_tr));
let d_tl = tl.dot(Self::gen_gradient_vector(value_tl));
let d_br = br.dot(Self::gen_gradient_vector(value_br));
let d_bl = bl.dot(Self::gen_gradient_vector(value_bl));
let u = Self::fade(xd);
let v = Self::fade(yd);
Self::lerp(
u,
Self::lerp(v, d_bl, d_tl),
Self::lerp(v, d_br, d_tr),
)
}
pub fn fractal_noise(
x: f64,
y: f64,
octaves: i32,
mut amplitude: f64,
mut frequency: f64,
permutation: &PermutationArray,
) -> f64 {
let mut fractal_noise = 0.0;
for _ in 0..octaves {
let layer = amplitude * Self::noise_2d(x * frequency, y * frequency, permutation.get_permutation());
fractal_noise += layer;
amplitude *= 0.5;
frequency *= 2.0;
}
fractal_noise
}
}

39
src/permutation_array.rs Normal file
View file

@ -0,0 +1,39 @@
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha8Rng;
//i translated the Terrain4J Noise2D to Rust so i can mess around with the cubes
pub struct PermutationArray {
permutation: [i32; 512],
}
impl PermutationArray {
pub fn new() -> Self {
Self {
permutation: [0; 512],
}
}
pub fn get_permutation(&self) -> &[i32; 512] {
&self.permutation
}
pub fn generate_permutation_array(&mut self, seed: i64) {
let mut seed_bytes = [0u8; 32];
seed_bytes[0..8].copy_from_slice(&(seed as u64).to_le_bytes());
let mut rnd = ChaCha8Rng::from_seed(seed_bytes);
self.permutation = [0; 512];
for i in 0..256 {
self.permutation[i] = i as i32;
}
for p in (1..256).rev() {
let index = rnd.gen_range(0..=p);
self.permutation.swap(p, index);
}
for i in 0..256 {
self.permutation[i + 256] = self.permutation[i];
}
}
}

View file

@ -1,10 +1,9 @@
use crate::render::{Texture};
use crate::render::{MaterialProperties, SimpleTexture};
use bytemuck::{Pod, Zeroable};
use glam::camera::lh::proj::directx::perspective;
use glam::{Affine3A, EulerRot, Mat3A, Mat4, Vec3, Vec4};
use wgpu::util::DeviceExt;
use wgpu::{Device, Queue};
use crate::render::instance::material::MaterialProperties;
pub(crate) struct Eye {
pub(crate) frame: Affine3A,
@ -17,7 +16,6 @@ pub(crate) struct Eye {
pub(crate) camera_buffer: wgpu::Buffer,
pub(crate) layout: wgpu::BindGroupLayout,
pub(crate) group: wgpu::BindGroup,
pub sky_pipeline: wgpu::RenderPipeline,
}
#[repr(C)]
@ -51,7 +49,7 @@ impl Eye {
bytemuck::cast_slice(&[self.environment]),
);
}
pub(crate) fn new(device: &Device, config: &wgpu::SurfaceConfiguration, width: u32, height: u32, skybox: Texture) -> Eye {
pub(crate) fn new(device: &Device, width: u32, height: u32, skybox: SimpleTexture) -> Eye {
let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Camera Buffer"),
contents: bytemuck::cast_slice(&[
@ -65,7 +63,7 @@ impl Eye {
let dir = Vec3::new(1.0, 0.5, 1.0).normalize();
let environment = Environment {
ambient: Vec4::new(0.1, 0.1, 0.1, 0.0),
ambient: Vec4::new(0.15, 0.15, 0.15, 0.0),
light: Vec4::new(1.0, 1.0, 1.0, 0.0),
dir: Vec4::new(dir.x, dir.y, dir.z, 0.0),
};
@ -118,51 +116,6 @@ impl Eye {
label: Some("eye_bind_group_layout"),
});
let shader = device.create_shader_module(wgpu::include_wgsl!("sky.wgsl"));
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"),
bind_group_layouts: &[Some(&layout)],
immediate_size: 0,
});
//todo: use a pipeline cache!
let sky_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Sky Pipeline"),
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: Some("vs_sky"),
compilation_options: Default::default(),
buffers: &[],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: Some("fs_sky"),
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
// 4.
format: config.format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
front_face: wgpu::FrontFace::Cw,
..Default::default()
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: Some(false),
depth_compare: Some(wgpu::CompareFunction::LessEqual),
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState::default(),
multiview_mask: None,
cache: None,
});
let group = Eye::bind_group(&layout, device, &camera_buffer, &environment_buffer, skybox);
Eye {
@ -176,7 +129,6 @@ impl Eye {
layout,
environment,
environment_buffer,
sky_pipeline,
}
}
fn bind_group(
@ -184,7 +136,7 @@ impl Eye {
device: &wgpu::Device,
camera: &wgpu::Buffer,
environment: &wgpu::Buffer,
skybox: Texture,
skybox: SimpleTexture,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &layout,
@ -211,7 +163,7 @@ impl Eye {
label: Some("eye_bind_group"),
})
}
pub fn skybox(&mut self, device: &wgpu::Device, texture: Texture) {
pub fn skybox(&mut self, device: &wgpu::Device, texture: SimpleTexture) {
self.group = Eye::bind_group(
&self.layout,
device,
@ -220,9 +172,8 @@ impl Eye {
texture,
)
}
pub(crate) fn resize(&mut self, queue: &Queue, width: u32, height: u32) {
self.aspect_ratio = width as f32 / height as f32;
self.write(queue);
pub(crate) fn resize(&mut self, width: u32, height: u32) {
self.aspect_ratio = width as f32 / height as f32
}
pub(crate) fn control(&mut self, delta: Vec3) {
self.frame *= Affine3A::from_translation(delta);

View file

@ -1,130 +0,0 @@
use wgpu::Device;
use crate::list::{FreeList, Id};
use crate::render::Renderer;
use crate::render::texture::Texture;
pub struct Materials {
pub(crate) layout: wgpu::BindGroupLayout,
pub(crate) list: FreeList<Material>,
default: Material,
}
impl Materials {
pub(crate) fn new(device: &Device, default: Texture) -> Materials {
fn texture_bind_group_layout_entry(binding: u32) -> wgpu::BindGroupLayoutEntry {
wgpu::BindGroupLayoutEntry {
binding,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: true },
},
count: None,
}
}
let layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
texture_bind_group_layout_entry(1),
texture_bind_group_layout_entry(2),
texture_bind_group_layout_entry(3),
],
label: Some("texture_bind_group_layout"),
});
let default = Material::_new(
&device,
&layout,
&default,
&default,
&default,
MaterialProperties::default(),
);
Materials {
layout,
list: Default::default(),
default,
}
}
}
#[derive(Clone)]
pub struct Material {
pub(crate) group: wgpu::BindGroup,
}
pub struct MaterialProperties {
pub(crate) edge: wgpu::AddressMode,
pub(crate) filter: wgpu::FilterMode,
}
impl Default for MaterialProperties {
fn default() -> MaterialProperties {
MaterialProperties {
edge: wgpu::AddressMode::Repeat,
filter: wgpu::FilterMode::Linear,
}
}
}
impl MaterialProperties {
pub(crate) fn sampler(&self, device: &wgpu::Device) -> wgpu::Sampler {
device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: self.edge,
address_mode_v: self.edge,
address_mode_w: self.edge,
mag_filter: self.filter,
min_filter: self.filter,
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
..Default::default()
})
}
}
impl Material {
pub(crate) fn _new(
device: &wgpu::Device,
layout: &wgpu::BindGroupLayout,
base: &Texture,
normal: &Texture,
reflect: &Texture,
config: MaterialProperties,
) -> Material {
let sampler = config.sampler(&device);
let group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Sampler(&sampler),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(&base.view),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::TextureView(&normal.view),
},
wgpu::BindGroupEntry {
binding: 3,
resource: wgpu::BindingResource::TextureView(&reflect.view),
},
],
label: Some("diffuse_bind_group"),
});
Material { group }
}
pub fn new(renderer: &mut Renderer, base: &Texture, normal: &Texture, reflect: &Texture, config: MaterialProperties) -> Id<Material> {
renderer.instances.materials.list.make(Material::_new(&renderer.device, &renderer.instances.materials.layout, base, normal, reflect, config)).id
}
}

View file

@ -1,376 +0,0 @@
pub mod material;
use std::ops::Range;
use bytemuck::{Pod, Zeroable};
use glam::{Affine3, Mat4, Vec4};
use wgpu::{BindGroup, Device};
use wgpu::naga::FastHashMap;
use crate::list::{FreeList, Id, RefId};
use crate::render::{instance, Renderer};
use crate::render::eye::Eye;
pub(crate) use crate::render::instance::material::{Material, Materials};
use crate::render::mesh::{Mesh, TangentVertex};
use crate::render::texture::Texture;
use crate::world::ObjectData;
#[repr(C)]
#[derive(Pod, Zeroable, Copy, Clone)]
pub struct ModelInstance {
pub transform: Mat4,
pub color: Vec4,
pub lights: [u16; 16],
pub point_lights: u32,
pub spot_lights: u32,
pub metal: f32,
pub rough: f32,
}
#[repr(C)]
#[derive(Pod, Copy, Clone, Zeroable)]
pub struct LightInstance {
pub location: Vec4,
pub rotation: Vec4,
pub color: Vec4,
}
#[derive(Clone)]
pub struct LightData {
pub instance: LightInstance,
pub transform: Affine3,
}
#[derive(Clone)]
pub struct ModelData {
pub instance: ModelInstance,
pub material: Id<Material>,
pub mesh: Id<Mesh>,
}
pub struct Instances {
pub materials: Materials,
light_instances: FreeList<LightData>,
model_instances: FreeList<ModelData>,
pub(crate) pipeline: wgpu::RenderPipeline,
light_count: usize,
light_buffer: wgpu::Buffer,
model_count: usize,
pub(crate) model_buffer: wgpu::Buffer,
models_flag: bool,
pub(crate) program: Program,
models: FastHashMap<Id<Mesh>, FastHashMap<Id<Material>, FastHashMap<Id<ObjectData>,bool>>>,
}
pub enum Code {
Material(Material),
Mesh(Mesh),
Draw(Range<u32>),
}
pub struct Program(pub Vec<Code>);
impl Program {
fn push(&mut self, item: Code) {
self.0.push(item)
}
fn new() -> Program {
Program(Vec::new())
}
pub(crate) fn render(self, pass: &mut wgpu::RenderPass) {
let mut count = 0;
let mut indexed = false;
for code in self.0 {
match code {
Code::Material(material) => pass.set_bind_group(
Renderer::SIMPLE_RENDER_TEXTURE_GROUP_POSITION,
&material.group,
&[],
),
Code::Mesh(Mesh {vertices, indices, ..}) => {
pass.set_vertex_buffer(0, vertices.0.slice(..));
if let Some(indices) = indices {
pass.set_index_buffer(indices.0.slice(..), wgpu::IndexFormat::Uint32);
count = indices.1;
indexed = true;
} else {
count = vertices.1;
indexed = false;
}
}
Code::Draw(instances) => {
if indexed {
pass.draw_indexed(0..count, 0, instances)
} else {
pass.draw(0..count, instances);
}
}
}
}
}
}
impl Instances {
const MIN_SIZE: u64 = 64;
pub fn register_object(&mut self, mut object: RefId<ObjectData>) {
let model = object.model.as_mut().unwrap();
self.model_count += 1;
self.models
.entry(model.mesh.clone()).or_default()
.entry(model.material.clone()).or_default()
.insert(object.into(),self.models_flag);
}
/*pub fn register_light(&mut self, light: Id<LightData>) {
self.light_count += 1;
self.lights.insert(light);
}*/
pub fn reallocate_buffer(
device: &wgpu::Device,
buffer: &mut wgpu::Buffer,
count: usize,
item_size: usize,
) {
let size = buffer.size() / item_size as wgpu::BufferAddress;
if count > Instances::MIN_SIZE as usize {
let mut reallocate: Option<usize> = None;
if count < (size / 2) as usize {
reallocate = Some(count / 2);
} else if count > size as usize {
reallocate = Some(count * 2);
}
if let Some(new_size) = reallocate {
*buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Instance Buffer"),
size: (item_size * new_size) as wgpu::BufferAddress,
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX,
mapped_at_creation: false,
});
}
}
}
/*pub fn write_lights(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) {
//SimpleInstances::reallocate_buffer(device, queue, &mut self.light_ref_buffer, self.light_ref_last, size_of::<u32>());
/*
let mut light_ref_buffer = queue.write_buffer_with(
&self.light_ref_buffer,
0 as wgpu::BufferAddress,
wgpu::BufferSize::new(self.instance_buffer.size()).unwrap()
).unwrap();
*/
Instances::reallocate_buffer(
device,
&mut self.light_buffer,
self.light_count,
size_of::<WorldLight>(),
);
let mut buffer = queue
.write_buffer_with(
&self.light_buffer,
0 as wgpu::BufferAddress,
wgpu::BufferSize::new(self.light_buffer.size()).unwrap(),
)
.unwrap();
let stride = size_of::<WorldLight>();
for (new_index, (light, index)) in self.lights.iter_mut().enumerate() {
*index = new_index + 1;
let begin = *index * stride;
buffer
.slice(begin..begin + stride)
.copy_from_slice(bytemuck::cast_slice(&[light.0.borrow().instance]));
}
}*/
pub(crate) fn write_instances(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
mesh_list: &mut FreeList<Mesh>,
objects_list: &mut FreeList<ObjectData>,
) {
Instances::reallocate_buffer(
device,
&mut self.model_buffer,
self.model_count,
size_of::<ModelInstance>(),
);
let mut buffer = queue
.write_buffer_with(
&self.model_buffer,
0 as wgpu::BufferAddress,
wgpu::BufferSize::new(self.model_buffer.size()).unwrap(),
)
.unwrap();
let mut index: u32 = 0;
let stride = size_of::<ModelInstance>();
for (mesh, materials) in self.models.iter_mut() {
self.program.push(Code::Mesh(mesh_list.get(mesh).clone()));
for (material, objects) in materials.iter_mut() {
self.program.push(Code::Material(self.materials.list.get(material).clone()));
let before = index;
objects.retain(|object,flag| {
let object = objects_list.get(object);
if *flag == self.models_flag {
let begin = index as usize * stride;
buffer.slice(begin..begin + stride).copy_from_slice(bytemuck::cast_slice(&[object.model.as_mut().unwrap().instance]));
index += 1;
true
} else {
false
}
});
self.program.push(Code::Draw(before..index));
}
}
//println!("objects: {}",index);
self.models_flag = !self.models_flag;
}
pub(crate) fn desc() -> wgpu::VertexBufferLayout<'static> {
wgpu::VertexBufferLayout {
array_stride: size_of::<ModelInstance>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Instance,
attributes: &[
// todo: is this too big?
wgpu::VertexAttribute {
offset: 0,
shader_location: 4,
format: wgpu::VertexFormat::Float32x4,
},
wgpu::VertexAttribute {
offset: size_of::<[f32; 4]>() as wgpu::BufferAddress,
shader_location: 5,
format: wgpu::VertexFormat::Float32x4,
},
wgpu::VertexAttribute {
offset: size_of::<[f32; 8]>() as wgpu::BufferAddress,
shader_location: 6,
format: wgpu::VertexFormat::Float32x4,
},
wgpu::VertexAttribute {
offset: size_of::<[f32; 12]>() as wgpu::BufferAddress,
shader_location: 7,
format: wgpu::VertexFormat::Float32x4,
},
wgpu::VertexAttribute {
offset: size_of::<[f32; 16]>() as wgpu::BufferAddress,
shader_location: 8,
format: wgpu::VertexFormat::Float32x4,
},
wgpu::VertexAttribute {
offset: size_of::<[f32; 16]>() as wgpu::BufferAddress,
shader_location: 9,
format: wgpu::VertexFormat::Float32x4,
},
wgpu::VertexAttribute {
offset: size_of::<[f32; 16]>() as wgpu::BufferAddress,
shader_location: 10,
format: wgpu::VertexFormat::Float32x4,
},
wgpu::VertexAttribute {
offset: size_of::<[f32; 16]>() as wgpu::BufferAddress,
shader_location: 11,
format: wgpu::VertexFormat::Float32x4,
},
],
}
}
pub fn new(device: &wgpu::Device, eye: &Eye, config: &wgpu::SurfaceConfiguration, default: Texture) -> Instances {
let instance_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Instance Buffer"),
size: (size_of::<ModelInstance>() * Instances::MIN_SIZE as usize)
as wgpu::BufferAddress,
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let light_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Light Buffer"),
size: (size_of::<LightInstance>() * Instances::MIN_SIZE as usize)
as wgpu::BufferAddress,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
/*let light_ref_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Light Ref Buffer"),
size: (size_of::<u32>() * SimpleInstances::MIN_SIZE as usize) as wgpu::BufferAddress,
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});*/
let materials = Materials::new(device, default);
let shader = device.create_shader_module(wgpu::include_wgsl!("instance.wgsl"));
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"),
bind_group_layouts: &[Some(&eye.layout), Some(&materials.layout)],
immediate_size: 0,
});
let instance_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: Some("vs_main"),
buffers: &[TangentVertex::desc(), Instances::desc()],
compilation_options: wgpu::PipelineCompilationOptions::default(),
},
fragment: Some(wgpu::FragmentState {
// 3.
module: &shader,
entry_point: Some("fs_main"),
targets: &[Some(wgpu::ColorTargetState {
// 4.
format: config.format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: wgpu::PipelineCompilationOptions::default(),
}),
primitive: Default::default(), /*wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
}*/
depth_stencil: Some(wgpu::DepthStencilState {
stencil: wgpu::StencilState::default(),
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: Some(true),
depth_compare: Some(wgpu::CompareFunction::Less),
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
multiview_mask: None,
cache: None,
});
Instances {
materials,
light_instances: Default::default(),
model_instances: Default::default(),
pipeline: instance_pipeline,
light_count: 0,
//light_ref_last: 0,
model_count: 0,
light_buffer,
model_buffer: instance_buffer,
//light_ref_buffer,
//lights: Default::default(),
models_flag: false,
program: Program(Vec::new()),
models: Default::default(),
}
}
pub fn render() {
}
}

View file

@ -1,302 +0,0 @@
use std::collections::HashMap;
use glam::{Mat4, Vec3, Vec4};
use gltf::Semantic;
use wgpu::{Device, Queue};
use wgpu::util::DeviceExt;
use crate::list::{FreeList, Id, RefId};
use crate::render::instance::{Material, Materials, ModelData, ModelInstance};
use crate::render::instance::material::MaterialProperties;
use crate::render::mesh::{Mesh, TangentVertex};
use crate::render::texture::{Texture, TextureProperties};
use crate::render::{Renderer, Textures};
use crate::world::{ObjectData, AABB, ColliderData, Shape};
pub struct TreeNode {
object: Option<ObjectData>,
children: Vec<TreeNode>,
name: String,
}
impl TreeNode {
pub fn first_object(&self) -> Option<ObjectData> {
if let Some(object) = self.object.clone() {
Some(object)
} else {
for child in self.children.iter() {
if let Some(object) = child.first_object() {
return Some(object);
}
}
None
}
}
}
type GltfVertexBufferKey = (Option<usize>, Option<usize>, Option<usize>, Option<usize>);
pub fn new_texture_from_gltf(
device: &Device,
queue: &Queue,
info: &gltf::texture::Texture,
images: &Vec<gltf::image::Data>,
) -> Option<Texture> {
if let Some(image) = images.get(info.source().index()) {
let mut new_pixels = Vec::new();
let pixels: &Vec<u8>;
match image.format {
gltf::image::Format::R8G8B8 => {
for pixel in image.pixels.chunks(3) {
new_pixels.push(pixel[0]);
new_pixels.push(pixel[0]);
new_pixels.push(pixel[0]);
new_pixels.push(255);
}
pixels = &new_pixels;
}
gltf::image::Format::R8G8B8A8 => pixels = &image.pixels,
_ => return None,
}
Some(Texture::load(
device,
queue,
pixels.as_slice(),
TextureProperties {
width: image.width,
height: image.height,
},
))
} else {
None
}
}
pub fn new_mesh_from_gltf(
device: &Device,
primitive: gltf::Primitive,
meshes: &mut HashMap<GltfVertexBufferKey, Mesh>,
buffers: &[gltf::buffer::Data],
) -> Mesh {
let position_index = primitive
.get(&Semantic::Positions)
.and_then(|it| it.view()).map(|it| it.buffer().index());
let normals_index = primitive
.get(&Semantic::Normals)
.and_then(|it| it.view()).map(|it| it.buffer().index());
let tex_coords_index = primitive
.get(&Semantic::TexCoords(0))
.and_then(|it| it.view()).map(|it| it.buffer().index());
let indices_index = primitive
.indices()
.and_then(|it| it.view()).map(|it| it.buffer().index());
let tangent_index = primitive
.get(&Semantic::Tangents)
.and_then(|it| it.view()).map(|it| it.buffer().index());
let key = (
position_index,
normals_index,
tex_coords_index,
indices_index,
);
let insert = || {
let mut tangents = false;
let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()]));
let mut vertex_data: Vec<TangentVertex>;
let mut aabb = AABB(Vec3::ZERO,Vec3::ZERO);
if let Some(positions) = reader.read_positions() {
vertex_data = Vec::with_capacity(positions.len());
let mut normal = reader.read_normals().map(|it| it.into_iter());
let mut tex_coord = reader
.read_tex_coords(0)
.map(|it| it.into_f32().into_iter());
let mut tangent = reader.read_tangents().map(|it| it.into_iter());
tangents = tangent.is_some();
for position in positions {
aabb = aabb.extend_to(Vec3::from(position));
vertex_data.push(TangentVertex {
position,
normal: if let Some(ref mut normals) = normal {
if let Some(normal) = normals.next() {
normal
} else {
[0.0; 3]
}
} else {
[0.0; 3]
},
tex_coord: if let Some(ref mut tex_coords) = tex_coord {
tex_coords.next()
} else {
None
}
.unwrap_or([0.0; 2]),
tangent: [0.0; 3], // todo: tangent from model
bitangent: [0.0; 3],
})
}
} else {
vertex_data = Vec::new();
}
let vertex_buffer =
device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(vertex_data.as_slice()),
usage: wgpu::BufferUsages::VERTEX,
});
let vertices_result = (vertex_buffer, vertex_data.len() as u32);
let indices_result = if let Some(indices) = reader.read_indices() {
let index_data: Vec<u32> = indices.into_u32().collect();
Some((
device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"),
contents: bytemuck::cast_slice(index_data.as_slice()),
usage: wgpu::BufferUsages::INDEX,
}),
index_data.len() as u32,
))
} else {
None
};
Mesh {
indices: indices_result,
vertices: vertices_result,
aabb,
}
};
meshes.entry(key).or_insert_with(insert).clone()
}
pub fn load_node_from_gltf(
node: gltf::Node,
device: &Device,
queue: &Queue,
mesh_map: &mut HashMap<GltfVertexBufferKey, Mesh>,
texture_map: &mut HashMap<usize, Texture>,
materials: &mut Materials,
textures: &Textures,
meshes: &mut FreeList<Mesh>,
images: &Vec<gltf::image::Data>,
buffers: &Vec<gltf::buffer::Data>,
) -> TreeNode {
let mut tree_node = TreeNode {
object: None,
children: Vec::new(),
name: node.name().unwrap_or("Node").to_string(),
};
if let Some(mesh) = node.mesh() {
let len = mesh.primitives().len();
for primitive in mesh.primitives() {
let pbr = primitive.material().pbr_metallic_roughness();
let material = primitive.material();
let color = pbr.base_color_factor();
let metal = pbr.metallic_factor();
let rough = pbr.roughness_factor();
let light = primitive.material().emissive_factor();
let base = match pbr.base_color_texture() {
Some(info) => texture_map
.entry(info.texture().source().index())
.or_insert_with(|| new_texture_from_gltf(device,queue,&info.texture(), &images).unwrap_or_else(|| textures.default.clone()))
.clone(),
None => textures.default.clone(),
};
let reflect = match pbr.metallic_roughness_texture() {
Some(info) => texture_map
.entry(info.texture().source().index())
.or_insert_with(|| new_texture_from_gltf(device,queue,&info.texture(), &images).unwrap_or_else(|| textures.default.clone()))
.clone(),
None => textures.default.clone(),
};
let normal = match material.normal_texture() {
Some(info) => texture_map
.entry(info.texture().source().index())
.or_insert_with(|| new_texture_from_gltf(device,queue,&info.texture(), &images).unwrap_or_else(|| textures.default.clone()))
.clone(),
None => textures.default.clone(),
};
let material = materials.list.make(Material::_new(
&device,
&materials.layout,
&base,
&normal,
&reflect,
MaterialProperties {
edge: wgpu::AddressMode::Repeat,
filter: wgpu::FilterMode::Linear,
},
)).into();;
let mesh = new_mesh_from_gltf(device, primitive, mesh_map, buffers);
let aabb = mesh.aabb;
let object = ObjectData {
model: Some(ModelData {
instance: ModelInstance {
transform: Mat4::default(),
color: Vec4::from_array(color),
lights: [0; 16],
point_lights: 0,
spot_lights: 0,
metal,
rough,
},
material,
mesh: meshes.make(mesh).id,
}),
collider: ColliderData { shape: Shape::Sphere(aabb.1.distance(aabb.0)) },
affine: Default::default(),
asleep: false,
};
if len == 1 {
tree_node.object = Some(object);
} else {
tree_node.children.push(TreeNode {
object: Some(object),
children: Vec::new(),
name: "Primitive".to_string(),
})
}
}
}
for node in node.children() {
tree_node
.children
.push(load_node_from_gltf(node, device, queue, mesh_map, texture_map, materials, textures, meshes, images, buffers));
}
tree_node
}
pub fn _load_from_gltf(device: &Device, queue: &Queue, meshes: &mut FreeList<Mesh>, materials: &mut Materials, textures: &mut Textures, slice: impl AsRef<[u8]>) -> TreeNode {
let mut root = TreeNode {
object: None,
children: Vec::new(),
name: "Root".to_string(),
};
if let Ok((document, buffers, images)) = gltf::import_slice(slice) {
let mut mesh_map: HashMap<GltfVertexBufferKey, Mesh> = HashMap::new();
let mut texture_map: HashMap<usize, Texture> = HashMap::new();
for scene in document.scenes() {
let mut scene_node = TreeNode {
object: None,
children: Vec::new(),
name: scene.name().unwrap_or("Scene").to_string(),
};
for node in scene.nodes() {
scene_node.children.push(load_node_from_gltf(
node,
device,
queue,
&mut mesh_map,
&mut texture_map,
materials,
textures,
meshes,
&images,
&buffers,
));
}
root.children.push(scene_node)
}
}
root
}
pub fn load_from_gltf(renderer: &mut Renderer, slice: impl AsRef<[u8]>) -> TreeNode {
_load_from_gltf(&renderer.device, &renderer.queue, &mut renderer.meshes, &mut renderer.instances.materials, &mut renderer.textures, slice)
}

View file

@ -1,51 +0,0 @@
use crate::world::AABB;
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct TangentVertex {
pub(crate) position: [f32; 3],
pub(crate) normal: [f32; 3],
pub(crate) tex_coord: [f32; 2],
pub(crate) tangent: [f32; 3],
pub(crate) bitangent: [f32; 3],
}
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Vertex {
position: [f32; 3],
normal: [f32; 3],
tex_coord: [f32; 3],
}
impl TangentVertex {
pub(crate) fn desc() -> wgpu::VertexBufferLayout<'static> {
wgpu::VertexBufferLayout {
array_stride: size_of::<TangentVertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
wgpu::VertexAttribute {
offset: 0,
shader_location: 0,
format: wgpu::VertexFormat::Float32x3,
},
wgpu::VertexAttribute {
offset: size_of::<[f32; 3]>() as wgpu::BufferAddress,
shader_location: 1,
format: wgpu::VertexFormat::Float32x3,
},
wgpu::VertexAttribute {
offset: size_of::<[f32; 6]>() as wgpu::BufferAddress,
shader_location: 2,
format: wgpu::VertexFormat::Float32x2,
},
],
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Mesh {
pub(crate) indices: Option<(wgpu::Buffer, u32)>,
pub(crate) vertices: (wgpu::Buffer, u32),
pub(crate) aabb: AABB,
}

File diff suppressed because it is too large Load diff

View file

@ -1,72 +0,0 @@
struct Environment {
ambient: vec4<f32>,
light: vec4<f32>,
dir: vec4<f32>,
}
struct Eye {
// from camera to screen
proj: mat4x4<f32>,
// from screen to camera
inv: mat4x4<f32>,
// world to camera
view: mat4x4<f32>,
// camera transform
frame: mat4x4<f32>,
}
@group(0) @binding(0)
var<uniform> eye: Eye;
@group(0) @binding(1)
var<uniform> environment: Environment;
@group(0) @binding(2)
var sky_sampler: sampler;
@group(0) @binding(3)
var sky_texture: texture_2d<f32>;
// written on the 60th sleepless hour
fn sky_aspect(look: vec3<f32>) -> vec4<f32> {
var pi = 3.14159;
let u_angle = atan2(look.x,look.z);
let u = (u_angle/pi) * 0.5 + 0.5; // from -pi/2 -> pi/2 into 0 -> 1
let v_angle = atan2(-look.y,sqrt(look.x * look.x + look.z * look.z));
let v = (v_angle/pi) - 0.5;//(v_angle/pi) + 0.5; // from -pi/2 -> pi/2 into 0 -> 1
let uv = vec2<f32>(u,v); // Get UV on skybox
return textureSample(sky_texture, sky_sampler, uv);
}
struct SkyOutput {
@builtin(position) position: vec4<f32>,
@location(0) pos: vec4<f32> // unadulterated by WGSL
}
const VERTICES = array(
vec4(-1.0, -1.0, 1.0, 1.0),
vec4(-1.0, 1.0, 1.0, 1.0),
vec4( 1.0, -1.0, 1.0, 1.0),
vec4( 1.0, 1.0, 1.0, 1.0),
vec4(-1.0, 1.0, 1.0, 1.0),
vec4( 1.0, -1.0, 1.0, 1.0),
);
fn rotation(it: mat4x4<f32>) -> mat3x3<f32> {
return mat3x3<f32>(
it[0].xyz,
it[1].xyz,
it[2].xyz,
);
}
@vertex
fn vs_sky(@builtin(vertex_index) index: u32) -> SkyOutput {
var out: SkyOutput;
out.position = VERTICES[index];
out.pos = out.position;
return out;
}
@fragment
fn fs_sky(in: SkyOutput) -> @location(0) vec4<f32> {
let look = rotation(eye.frame) * in.pos.xyz;
return sky_aspect(look);
}

View file

@ -1,115 +0,0 @@
use image::EncodableLayout;
use wgpu::{Device, Queue};
use crate::render::Renderer;
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct Texture {
texture: wgpu::Texture,
pub(crate) view: wgpu::TextureView,
}
pub struct TextureProperties {
pub(crate) width: u32,
pub(crate) height: u32,
}
// todo: use texture compression!
impl Texture {
pub fn depth(config: &wgpu::SurfaceConfiguration, device: &Device) -> Texture {
let size = wgpu::Extent3d {
// 2.
width: config.width.max(1),
height: config.height.max(1),
depth_or_array_layers: 1,
};
let desc = wgpu::TextureDescriptor {
label: Some("depth texture"),
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Depth32Float,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT // 3.
| wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
};
let _depth_texture = device.create_texture(&desc);
let depth_view = _depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
Texture {
texture: _depth_texture,
view: depth_view,
}
}
pub fn load(
device: &Device,
queue: &Queue,
slice: impl AsRef<[u8]>,
properties: TextureProperties,
) -> Texture {
let size = wgpu::Extent3d {
width: properties.width,
height: properties.height,
depth_or_array_layers: 1,
};
let diffuse_texture = device.create_texture(&wgpu::TextureDescriptor {
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
label: Some("texture"),
view_formats: &[],
});
let diffuse_texture_view =
diffuse_texture.create_view(&wgpu::TextureViewDescriptor::default());
queue.write_texture(
wgpu::TexelCopyTextureInfo {
texture: &diffuse_texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
slice.as_ref(),
wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(4 * size.width),
rows_per_image: Some(size.height),
},
size,
);
Texture {
texture: diffuse_texture,
view: diffuse_texture_view,
}
}
fn _load_from_file_bytes(
device: &Device,
queue: &Queue,
slice: impl AsRef<[u8]>,
format: Option<image::ImageFormat>,
) -> Option<Texture> {
let image = if let Some(format) = format {
image::load_from_memory_with_format(slice.as_ref(), format)
} else {
image::load_from_memory(slice.as_ref())
};
if let Ok(data) = image {
let data = data.into_rgba8();
Some(Texture::load(
device,
queue,
data.as_bytes(),
TextureProperties {
width: data.width(),
height: data.height(),
},
))
} else {
println!("failed to load texture! error: {:?}", image.unwrap_err());
None
}
}
pub fn load_from_file_bytes(renderer: &mut Renderer, slice: impl AsRef<[u8]>, format: Option<image::ImageFormat>) -> Option<Texture> {
Texture::_load_from_file_bytes(&renderer.device,&renderer.queue,slice,format)
}
}

View file

@ -1,19 +0,0 @@
use crate::list::FreeList;
use crate::{render, world};
#[derive(Default)]
pub struct State {
pub objects: FreeList<world::ObjectData>,
pub lights: FreeList<render::instance::LightData>,
pub worlds: FreeList<world::World>,
pub renderer: Option<render::Renderer>,
pub screens: FreeList<render::Screen>
}
impl State {
pub fn new() -> State {
State {
..Default::default()
}
}
}

View file

View file

@ -1,100 +0,0 @@
use glam::{IVec3, Mat4, Quat, Vec3};
use wgpu::naga::FastHashMap;
use crate::list::{FreeList, Id};
use crate::render;
use crate::world::{ColliderData, ObjectData, OctreeBlock, Shape, World};
pub struct OctreeDebug {
map: FastHashMap<IVec3, Id<ObjectData>>
}
pub enum OctreeDebugOperation {
Add(IVec3,i32),
Remove(IVec3),
}
impl Default for OctreeDebug {
fn default() -> Self {
Self::new()
}
}
impl OctreeDebug {
pub fn new() -> OctreeDebug {
OctreeDebug {
map: FastHashMap::default()
}
}
fn set_block(
&mut self,
blocks: &mut FreeList<OctreeBlock>,
block: Id<OctreeBlock>,
objects: &mut FreeList<ObjectData>,
debug: &Option<render::instance::ModelData>,
pos: IVec3,
mut size: i32
) {
let entry = self.map.get(&pos);
if let Some(debug) = debug {
if entry.is_none() {
let mut model = debug.clone();
model.instance.transform = Mat4::from_scale_rotation_translation(Vec3::splat(size as f32),Quat::IDENTITY,pos.as_vec3());
self.map.insert(pos, objects.make(ObjectData {
model: Some(model),
collider: ColliderData {
shape: Shape::Sphere(size as f32),
},
affine: Default::default(),
asleep: false,
}).id);
println!("pos {} size {}",pos,size)
}
} else {
if entry.is_some() {
objects.remove(self.map.remove(&pos).unwrap());
}
}
size /= 2;
if let Some(block) = blocks.get(&block).blocks[0].exists() {
self.set_block(blocks,block,objects,debug, pos - (IVec3::new(-1, -1, -1) * size / 2), size);
}
if let Some(block) = blocks.get(&block).blocks[1].exists() {
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,-1,-1) * size / 2), size);
}
if let Some(block) = blocks.get(&block).blocks[2].exists() {
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,1,-1) * size / 2), size);
}
if let Some(block) = blocks.get(&block).blocks[3].exists() {
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,1,-1) * size / 2), size);
}
if let Some(block) = blocks.get(&block).blocks[4].exists() {
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,-1,1) * size / 2), size);
}
if let Some(block) = blocks.get(&block).blocks[5].exists() {
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,-1,1) * size / 2), size);
}
if let Some(block) = blocks.get(&block).blocks[6].exists() {
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,1,1) * size / 2), size);
}
if let Some(block) = blocks.get(&block).blocks[7].exists() {
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,1,1) * size / 2), size);
}
}
pub fn set(&mut self, objects: &mut FreeList<ObjectData>, world: &mut World, debug: Option<render::instance::ModelData>) {
for (index,block) in world.matter.map.iter_mut() {
self.set_block(
&mut world.matter.blocks,
block.clone(),
objects,
&debug,
index * OctreeBlock::CHUNK_SIZE,
OctreeBlock::CHUNK_SIZE,
)
}
}
pub fn register(&mut self, objects: &mut FreeList<ObjectData>, renderer: &mut render::Renderer) {
for (pos,block) in self.map.iter() {
renderer.instances.register_object(objects.get_ref(block.clone()));
}
}
}

View file

@ -1,379 +1,228 @@
pub mod debug;
use crate::render::instance::ModelData;
use glam::{Affine3, IVec3, Mat4, Quat, Vec3};
use crate::render::{Renderer, SimpleLightData, SimpleModelData};
use glam::{DVec3, IVec3, Mat4, UVec3, Vec3, Vec4Swizzles};
use std::cell::RefCell;
use std::hash::{BuildHasherDefault, Hash, Hasher};
use std::path::absolute;
use std::ops::{Div, Mul};
use std::rc::Rc;
use mars::vm::Object;
use wgpu::naga::{FastHashMap, FastHashSet};
use crate::{list, render};
use crate::list::{FreeList, Id, MaybeId, RefId, SingleId};
#[derive(Clone)]
pub enum Shape {
Block(Vec3),
Sphere(f32),
None,
}
#[derive(Clone)]
pub struct ColliderData {
pub struct SimpleColliderData {
pub transform: Mat4,
pub shape: Shape,
pub radius: f32,
}
impl ColliderData {
fn aabb(&self, affine: Affine3) -> AABB {
let (_scale, _rotation, translation) = affine.to_scale_rotation_translation();
match self.shape {
Shape::Block(size) => {
let radius_offset = Vec3::splat(size.length());
AABB(
translation - radius_offset,
translation + radius_offset,
)
#[derive(Clone)]
pub struct SimpleObjectData {
pub model: Option<SimpleModelData>,
pub collider: SimpleColliderData,
}
#[derive(Clone)]
pub struct SimpleLight(pub Rc<RefCell<SimpleLightData>>);
impl PartialEq for SimpleLight {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
}
impl Eq for SimpleLight {}
impl Hash for SimpleLight {
fn hash<H: Hasher>(&self, state: &mut H) {
(self.0.as_ptr() as *const RefCell<SimpleLight>).hash(state)
}
}
#[derive(Clone)]
pub struct SimpleObject(pub Rc<RefCell<SimpleObjectData>>);
impl SimpleObject {
pub(crate) fn hard_clone(&self) -> SimpleObject {
SimpleObject {
0: Rc::new(RefCell::new(SimpleObjectData {
model: self.0.borrow().model.clone(),
collider: self.0.borrow().collider.clone(),
})),
}
}
}
impl PartialEq for SimpleObject {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
}
impl Eq for SimpleObject {}
impl Hash for SimpleObject {
fn hash<H: Hasher>(&self, state: &mut H) {
(self.0.as_ptr() as *const RefCell<SimpleModelData>).hash(state);
}
}
#[derive(Eq, Hash, PartialEq, Clone)]
enum Interest {
Light(SimpleLight),
Object(SimpleObject),
}
struct Block {
debug: Option<SimpleObject>,
volume: u8,
velocity: Vec3,
material: u8,
interests: FastHashSet<Interest>,
blocks: [Option<Box<Block>>; 64],
}
const TREE_ATTACK: usize = 4;
const TREE_FLOOR: usize = 4;
impl Block {
fn new() -> Block {
Block {
debug: None,
volume: 0,
velocity: Vec3::ZERO,
material: 0,
interests: FastHashSet::with_hasher(BuildHasherDefault::default()),
blocks: [const { None }; const { TREE_ATTACK * TREE_ATTACK * TREE_ATTACK }],
}
}
fn place_pos(&mut self, interest: Interest, pos: Vec3, rad: f32, block_size: f32) {
let cs = block_size / const { TREE_ATTACK as f32 };
if rad < cs || block_size >= TREE_FLOOR as f32 {
let rel = ((pos + block_size / 2.0) / cs).round().as_uvec3();
let new_pos = pos - (rel.as_vec3() + cs / 2.0);
let index = (rel.x + rel.y * 4 + rel.z * 16) as usize;
match self.blocks[index] {
Some(ref mut block) => {
block.place_pos(interest, new_pos, rad, cs);
}
None => {
let mut block = Box::new(Block::new());
block.place_pos(interest, new_pos, rad, cs);
self.blocks[index] = Some(block);
}
}
Shape::Sphere(radius) => {
let radius_offset = Vec3::splat(radius);
AABB(
translation - radius_offset,
translation + radius_offset,
)
} else {
self.interests.insert(interest);
}
}
fn debug_step(&mut self, value: bool) {
match self.debug {
Some(ref mut debug) => {
if value {
} else {
}
}
None => {
if value {
} else {
}
}
}
for maybe_block in self.blocks.iter_mut() {
if let Some(block) = maybe_block {
block.debug_step(value)
}
}
}
}
#[derive(Clone)]
pub struct ObjectData {
pub model: Option<render::instance::ModelData>,
pub collider: ColliderData,
pub affine: Affine3,
pub asleep: bool,
}
impl ObjectData {
pub fn set_affine(&mut self, affine: Affine3) {
self.affine = affine;
self.asleep = false;
}
}
#[derive(Clone, Eq, PartialEq)]
pub enum InterestType {
Light(Id<render::instance::LightData>),
Object(Id<ObjectData>)
}
#[derive(Clone)]
pub struct Interest {
it: InterestType,
aabb: AABB,
}
#[derive(Clone)]
pub struct InterestNode {
interest: Interest,
next: MaybeId<InterestNode>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct AABB(pub(crate) Vec3, pub(crate) Vec3);
impl AABB {
pub(crate) fn new(translation: Vec3, size: Vec3) -> AABB {
AABB(translation - size, translation + size)
}
pub fn extend_to(&self, point: Vec3) -> AABB {
AABB(self.0.min(point),self.1.max(point))
}
pub fn bounded_by(&self, aabb: AABB) -> AABB {
AABB(self.0.max(aabb.0), self.1.min(aabb.1))
}
fn offset(&self) -> Vec3 {
self.1 - self.0
}
fn to_iaabb(&self) -> IAABB {
IAABB(self.0.as_ivec3(),self.1.as_ivec3())
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct IAABB(pub(crate) IVec3, pub(crate) IVec3);
impl IAABB {
pub(crate) fn new(translation: IVec3, size: IVec3) -> IAABB {
IAABB(translation - size, translation + size)
}
pub fn extend_to(&self, point: IVec3) -> IAABB {
IAABB(self.0.min(point),self.1.max(point))
}
pub fn bounded_by(&self, aabb: IAABB) -> IAABB {
IAABB(self.0.max(aabb.0), self.1.min(aabb.1))
}
fn offset(&self) -> IVec3 {
self.1 - self.0
}
fn to_aabb(self) -> AABB {
AABB(self.0.as_vec3(),self.1.as_vec3())
}
}
pub struct World {
pub(crate) matter: OctreeMap,
}
impl Default for World {
fn default() -> Self {
Self::new()
}
chunk_size: u32,
map: FastHashMap<IVec3, Block>,
pub renderer: Option<Renderer>,
debug_world_map: bool,
debug_world_map_object: Option<SimpleObject>,
}
impl World {
pub fn new() -> World {
World {
matter: OctreeMap::new(),
}
}
pub fn add_object(&mut self, object: RefId<ObjectData>) -> Id<ObjectData> {
self.matter.place_interest(Interest {
it: InterestType::Object(object.id.clone()),
aabb: object.collider.aabb(object.affine),
});
object.id
}
pub fn remove_object(&mut self, object: Id<ObjectData>) {
}
pub fn step(
&mut self,
maybe_renderer: &mut Option<render::Renderer>,
objects: &mut FreeList<ObjectData>,
_lights: &mut FreeList<render::instance::LightData>,
) {
fn reinsert(
block_id: &Id<OctreeBlock>,
blocks: &mut FreeList<OctreeBlock>,
interests: &mut FreeList<InterestNode>,
objects: &mut FreeList<ObjectData>
) {
let mut maybe_interest = &blocks.get(block_id).first;
while let Some(interest_id) = maybe_interest.exists() {
let interest = interests.get(&interest_id);
match interest.interest.it {
InterestType::Object(ref object_id) => {
}
_ => {}
}
maybe_interest = &interest.next;
}
for id in blocks.get(block_id).blocks.clone().iter() {
if let Some(id) = id.exists() {
reinsert(&id, blocks, interests, objects)
}
}
}
for (_pos,id) in self.matter.map.iter() {
reinsert(id, &mut self.matter.blocks, &mut self.matter.nodes, objects)
}
if let Some(renderer) = maybe_renderer {
fn consider(
block_id: &Id<OctreeBlock>,
renderer: &mut render::Renderer,
blocks: &mut FreeList<OctreeBlock>,
interests: &mut FreeList<InterestNode>,
objects: &mut FreeList<ObjectData>
) {
let mut maybe_interest = &blocks.get(block_id).first;
while let Some(interest_id) = maybe_interest.exists() {
let interest = interests.get(&interest_id);
match interest.interest.it {
InterestType::Object(ref object_id) => {
let mut object = objects.get_ref(object_id.clone());
let transform = Mat4::from_mat3_translation(object.affine.matrix3,object.affine.translation);
object.model.as_mut().unwrap().instance.transform = transform;
renderer.instances.register_object(object)
}
_ => {}
}
maybe_interest = &interest.next;
}
for id in blocks.get(block_id).blocks.clone().iter() {
if let Some(id) = id.exists() {
consider(&id, renderer, blocks, interests, objects)
}
}
}
for (_pos,id) in self.matter.map.iter() {
consider(id, renderer, &mut self.matter.blocks, &mut self.matter.nodes, objects)
}
}
}
fn sync(&mut self) {}
}
#[derive(Copy, Clone)]
struct Material {
volume: u8,
velocity: Vec3,
material: u8,
}
impl Material {
const fn new() -> Material {
Material {
volume: 0,
velocity: Vec3::ZERO,
material: 0
}
}
}
impl OctreeBlock {
const FLOOR: i32 = 4;
const CHUNK_SIZE: i32 = OctreeBlock::FLOOR * 64;
const MAX_IDEAL_INTEREST: usize = 4;
fn new() -> OctreeBlock {
OctreeBlock {
interests: 0,
material: Material::new(),
first: MaybeId::NULL,
blocks: [MaybeId::NULL;8],
}
}
}
struct OctreeBlock {
interests: u32, // 1
first: MaybeId<InterestNode>, // 1
material: Material, // 4
blocks: Blocks, // 8
}
impl OctreeBlock {
fn push_interest(&mut self, interests: &mut FreeList<InterestNode>, interest: Interest) {
self.first = interests.make(InterestNode {
interest: interest.clone(),
next: self.first.clone(),
}).id.maybe();
self.interests += 1;
}
fn remove_interest(&mut self, interests: &mut FreeList<InterestNode>, needle: Interest) { // todo: needed?
let mut maybe_last = MaybeId::NULL;
let mut maybe_this = self.first.clone();
while let Some(this) = maybe_this.exists() {
let interest = interests.get(&this);
let next = interest.next.clone();
if interest.interest.it == needle.it {
self.interests -= 1;
if let Some(last) = maybe_last.exists() {
interests.get(&last).next = interest.next.clone();
} else {
self.first = interest.next.clone();
break;
}
}
maybe_last = maybe_this;
maybe_this = next;
}
panic!()
}
fn place(
it: Id<OctreeBlock>,
blocks: &mut FreeList<OctreeBlock>,
interests: &mut FreeList<InterestNode>,
interest: Interest,
pos: IVec3,
size: i32,
) {
if pos == IVec3::ZERO || blocks.get(&it).interests < OctreeBlock::MAX_IDEAL_INTEREST as u32 {
blocks.get(&it).push_interest(interests, interest)
} else {
let mut index = 0;
let x = if pos.x > 0 {
index += 1;
-size
} else {
size
};
let y = if pos.y > 0 {
index += 2;
-size
} else {
size
};
let z = if pos.z > 0 {
index += 4;
-size
} else {
size
};
let offset = IVec3::new(x,y,z) / 2;
println!("pos {} {} {}",pos,pos + offset,size);
let id = if let Some(id) = blocks.get(&it).blocks[index as usize].exists() {
id
} else {
let id = blocks.make(OctreeBlock::new()).id;
blocks.get(&it).blocks[index as usize] = id.clone().into();
id
};
OctreeBlock::place(id,blocks,interests,interest,pos + offset,size / 2);
}
}
}
type Blocks = [MaybeId<OctreeBlock>; 8];
pub struct OctreeMap {
interests: FreeList<Interest>,
nodes: FreeList<InterestNode>,
blocks: FreeList<OctreeBlock>,
map: FastHashMap<IVec3, Id<OctreeBlock>>,
}
impl OctreeMap {
fn place_interest(&mut self, interest: Interest) {
let mut d = 4;
let offset = interest.aabb.1 - interest.aabb.0;
while offset.element_sum() > (d * 2) as f32 {
d *= 2
}
let iaabb = IAABB(
(interest.aabb.0 / d as f32).floor().as_ivec3() + IVec3::splat(d / 2),
(interest.aabb.1 / d as f32).floor().as_ivec3() + IVec3::splat(d / 2),
);
println!("attempting place I: {} {} {} A: {} {}",iaabb.0,iaabb.1,d,interest.aabb.0,interest.aabb.1);
for x in iaabb.0.x..iaabb.1.x {
for y in iaabb.0.y..iaabb.1.y {
for z in iaabb.0.z..iaabb.1.z {
let index = IVec3::new(x,y,z);
let absolute_pos = index * d;
let chunk_index = absolute_pos / OctreeBlock::CHUNK_SIZE;
let chunk_pos = chunk_index * OctreeBlock::CHUNK_SIZE;
let block = self.map.entry(chunk_index).or_insert_with(|| {
self.blocks.make(OctreeBlock::new()).id
});
let pos = absolute_pos - chunk_pos;
println!("placing {} {}",pos,OctreeBlock::CHUNK_SIZE);
OctreeBlock::place(
block.clone(),
&mut self.blocks,
&mut self.nodes,
interest.clone(),
pos,
OctreeBlock::CHUNK_SIZE,
);
}
}
}
}
fn new() -> OctreeMap {
OctreeMap {
interests: Default::default(),
nodes: Default::default(),
blocks: Default::default(),
chunk_size: 512,
map: FastHashMap::with_hasher(BuildHasherDefault::default()),
renderer: None,
debug_world_map: false,
debug_world_map_object: None,
}
}
}
pub fn add_renderer(&mut self, renderer: Renderer) {
self.renderer = Some(renderer);
self.debug_world_map_object = self
.renderer
.as_mut()
.unwrap()
.load_from_gltf(include_bytes!("../assets/cube.glb"))
.first_object()
}
pub fn add_object(&mut self, object: SimpleObject) {
self.place(Interest::Object(object.clone()));
if let Some(ref mut renderer) = self.renderer {
renderer.instances.add_object(object)
}
}
pub fn set_debug(&mut self, value: bool) {
self.debug_world_map = value;
}
pub fn light_step(&mut self) {}
pub fn object_step(&mut self) {}
pub fn debug_step(&mut self) {
self.light_step();
self.object_step();
for (index, item) in self.map.iter_mut() {
item.debug_step(self.debug_world_map);
}
}
fn place_pos(&mut self, interest: Interest, pos: Vec3, rad: f32) {
let cs = self.chunk_size as f32;
let c_rad = ((cs / 2.0) * (cs / 2.0)) * 3.0;
for x in (pos.x - rad).div(cs) as i32..=(pos.x + rad).div(cs).ceil() as i32 {
for y in (pos.y - rad).div(cs) as i32..=(pos.y + rad).div(cs).ceil() as i32 {
for z in (pos.z - rad).div(cs) as i32..=(pos.z + rad).div(cs).ceil() as i32 {
let block_pos = IVec3::new(x, y, z);
let block_pos_f32 = block_pos.as_vec3();
if block_pos_f32.distance_squared(pos) < rad + c_rad {
self.map.entry(block_pos).or_insert_with(|| {
let mut block = Block::new();
block.place_pos(
interest.clone(),
pos - block_pos_f32,
rad,
self.chunk_size as f32,
);
block
});
}
}
}
}
}
fn place(&mut self, interest: Interest) {
let (pos, rad) = match &interest {
Interest::Light(light) => {
let light = light.0.borrow();
(light.instance.location.xyz(), light.instance.color.length())
}
Interest::Object(object) => {
let collider = &object.0.borrow().collider;
(
collider.transform.to_scale_rotation_translation().2,
collider.radius,
)
}
};
self.place_pos(interest, pos, rad);
}
}