Compare commits

..

41 Commits

Author SHA1 Message Date
382ecfa713 its working look at this 2023-09-26 15:54:04 -07:00
28cad39b10 TEMP: teleport to spawn point 2023-09-26 14:36:56 -07:00
2af43480f1 wip 2023-09-26 14:36:08 -07:00
bb7ccd97bb wip 2023-09-26 14:32:33 -07:00
ff7b12e90e add regex dep for load_roblox 2023-09-26 14:31:00 -07:00
e70dc9ad0f keep textures out of git 2023-09-26 14:30:39 -07:00
c5deef8753 support rbxmx 2023-09-26 14:27:00 -07:00
7c2666fdf5 file loader multiplexer, pass dom to load_roblox 2023-09-26 14:26:53 -07:00
6da4c81826 rank device types and ignore user preference 2023-09-25 20:27:14 -07:00
c868a91a06 revert storage buffers: no igpus support 2023-09-25 20:27:14 -07:00
b513e4037d pick adapter better lole 2023-09-25 12:38:53 -07:00
a803ada0e4 zeroes2 inline maybe 2023-09-22 19:42:15 -07:00
eafcbae677 add model.rs 2023-09-22 19:42:15 -07:00
2e786b090f no need for hardcoded image size 2023-09-22 15:20:41 -07:00
70e8f7a0ad delete stupid lib file 2023-09-22 15:19:44 -07:00
48091fc15d print loaded object count for physics and graphics 2023-09-22 02:24:31 -07:00
23857d38d9 no reason to double reference 2023-09-21 20:56:24 -07:00
1c9bc347f6 clear prev map 2023-09-21 16:01:02 -07:00
c9afa2d059 only load Block shaped parts 2023-09-21 15:45:02 -07:00
1a66dfbaf7 v0.5.0 model color + drag & drop to load maps 2023-09-21 15:45:02 -07:00
847209aac4 runtime load physics 2023-09-21 15:45:02 -07:00
42ba757ec0 plumb color everywhere 2023-09-21 13:08:13 -07:00
1cee3b52ac switch entity_transforms to storage buffers to remove hardcoded part cap 2023-09-21 11:57:17 -07:00
e27ce3b507 dynamic image size 2023-09-21 11:56:03 -07:00
bc8f2bd566 finalize physics models 2023-09-21 00:03:14 -07:00
eed932212d comment code that will be deleted soon and cause merge conflicts for no reason 2023-09-20 23:45:55 -07:00
73edb9ff95 drag & drop to load roblox map 2023-09-20 23:45:55 -07:00
ae0c9e73ee make handy unit cube 2023-09-20 23:44:12 -07:00
953d424a57 load_roblox module 2023-09-20 23:44:12 -07:00
ca919b92fd add roblox deps 2023-09-20 23:44:12 -07:00
1de3501e89 no default transform 2023-09-20 22:29:46 -07:00
0135b17917 make proper model data and stop passing device into add_obj 2023-09-20 22:29:33 -07:00
1878528a4f v0.4.0 instanced rendering + model textures 2023-09-20 17:45:39 -07:00
25e80a7c17 scope skybox 2023-09-20 17:13:08 -07:00
21835d13f6 load squid texture 2023-09-20 17:13:08 -07:00
91f6a5261f move camera_uniforms and skybox_texture 2023-09-20 17:13:08 -07:00
fb4a5efa14 split main bind group, rename everything, organize GraphicsData 2023-09-20 17:13:08 -07:00
acb658f3e9 goal: repeating ground texture 2023-09-20 14:07:11 -07:00
7e427b3879 instanced rendering 2023-09-20 13:48:07 -07:00
d16485ae6d replace visual ground with custom ground model! 2023-09-20 13:37:46 -07:00
cdf695ee6e label bind group 2023-09-20 13:02:47 -07:00
14 changed files with 910 additions and 535 deletions

1
.gitignore vendored

@ -1 +1,2 @@
/target
/textures

26
Cargo.lock generated

@ -1442,6 +1442,20 @@ dependencies = [
"thiserror",
]
[[package]]
name = "rbx_xml"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bc65b70827519fdc4ab47416d1085b912f087fadab9ed415471b6daba635574"
dependencies = [
"base64",
"log",
"rbx_dom_weak",
"rbx_reflection",
"rbx_reflection_database",
"xml-rs",
]
[[package]]
name = "redox_syscall"
version = "0.3.5"
@ -1453,9 +1467,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.9.4"
version = "1.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29"
checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47"
dependencies = [
"aho-corasick",
"memchr",
@ -1465,9 +1479,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.3.7"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629"
checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795"
dependencies = [
"aho-corasick",
"memchr",
@ -1645,7 +1659,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strafe-client"
version = "0.3.0"
version = "0.5.0"
dependencies = [
"async-executor",
"bytemuck",
@ -1658,6 +1672,8 @@ dependencies = [
"rbx_binary",
"rbx_dom_weak",
"rbx_reflection_database",
"rbx_xml",
"regex",
"wgpu",
"winit",
]

@ -1,6 +1,6 @@
[package]
name = "strafe-client"
version = "0.3.0"
version = "0.5.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -17,6 +17,8 @@ pollster = "0.3.0"
rbx_binary = "0.7.1"
rbx_dom_weak = "2.5.0"
rbx_reflection_database = "0.2.7"
rbx_xml = "0.13.1"
regex = "1.9.5"
wgpu = "0.17.0"
winit = "0.28.6"

BIN
images/squid.dds Normal file

Binary file not shown.

Binary file not shown.

@ -16,6 +16,8 @@ pub enum PhysicsInstruction {
// bool,//true = Trigger; false = teleport
// bool,//true = Force
// )
//temp
SetPosition(glam::Vec3),
}
pub struct Body {
@ -135,8 +137,8 @@ pub struct PhysicsState {
pub hitbox_halfsize: glam::Vec3,
pub contacts: std::collections::HashSet::<RelativeCollision>,
//pub intersections: Vec<ModelId>,
pub models: Vec<ModelPhysics>,
//temp
pub models_cringe_clone: Vec<Model>,
pub temp_control_dir: glam::Vec3,
//camera must exist in state because wormholes modify the camera, also camera punch
//pub camera: Camera,
@ -264,13 +266,13 @@ impl Aabb {
type TreyMeshFace = AabbFace;
type TreyMesh = Aabb;
pub struct Model {
pub struct ModelPhysics {
//A model is a thing that has a hitbox. can be represented by a list of TreyMesh-es
//in this iteration, all it needs is extents.
transform: glam::Mat4,
}
impl Model {
impl ModelPhysics {
pub fn new(transform:glam::Mat4) -> Self {
Self{transform}
}
@ -314,10 +316,10 @@ pub struct RelativeCollision {
}
impl RelativeCollision {
pub fn mesh(&self,models:&Vec<Model>) -> TreyMesh {
pub fn mesh(&self,models:&Vec<ModelPhysics>) -> TreyMesh {
return models.get(self.model as usize).unwrap().face_mesh(self.face)
}
pub fn normal(&self,models:&Vec<Model>) -> glam::Vec3 {
pub fn normal(&self,models:&Vec<ModelPhysics>) -> glam::Vec3 {
return models.get(self.model as usize).unwrap().face_normal(self.face)
}
}
@ -368,7 +370,7 @@ impl PhysicsState {
fn contact_constrain_velocity(&self,velocity:&mut glam::Vec3){
for contact in self.contacts.iter() {
let n=contact.normal(&self.models_cringe_clone);
let n=contact.normal(&self.models);
let d=velocity.dot(n);
if d<0f32{
(*velocity)-=d/n.length_squared()*n;
@ -377,7 +379,7 @@ impl PhysicsState {
}
fn contact_constrain_acceleration(&self,acceleration:&mut glam::Vec3){
for contact in self.contacts.iter() {
let n=contact.normal(&self.models_cringe_clone);
let n=contact.normal(&self.models);
let d=acceleration.dot(n);
if d<0f32{
(*acceleration)-=d/n.length_squared()*n;
@ -457,7 +459,7 @@ impl PhysicsState {
let mut best_time=time_limit;
let mut exit_face:Option<TreyMeshFace>=None;
let mesh0=self.mesh();
let mesh1=self.models_cringe_clone.get(collision_data.model as usize).unwrap().mesh();
let mesh1=self.models.get(collision_data.model as usize).unwrap().mesh();
let (v,a)=(-self.body.velocity,self.body.acceleration);
//collect x
match collision_data.face {
@ -608,7 +610,7 @@ impl PhysicsState {
let mut best_time=time_limit;
let mut best_face:Option<TreyMeshFace>=None;
let mesh0=self.mesh();
let mesh1=self.models_cringe_clone.get(model_id as usize).unwrap().mesh();
let mesh1=self.models.get(model_id as usize).unwrap().mesh();
let (p,v,a)=(self.body.position,self.body.velocity,self.body.acceleration);
//collect x
for t in zeroes2(mesh0.max.x-mesh1.min.x,v.x,0.5*a.x) {
@ -733,7 +735,7 @@ impl crate::instruction::InstructionEmitter<PhysicsInstruction> for PhysicsState
collector.collect(self.predict_collision_end(self.time,time_limit,collision_data));
}
//check for collision start instructions (against every part in the game with no optimization!!)
for i in 0..self.models_cringe_clone.len() {
for i in 0..self.models.len() {
collector.collect(self.predict_collision_start(self.time,time_limit,i as u32));
}
if self.grounded {
@ -756,6 +758,7 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
//selectively update body
match &ins.instruction {
PhysicsInstruction::SetWalkTargetVelocity(_)
|PhysicsInstruction::SetPosition(_)
|PhysicsInstruction::SetControlDir(_) => self.time=ins.time,//TODO: queue instructions
PhysicsInstruction::RefreshWalkTarget
|PhysicsInstruction::ReachWalkTargetVelocity
@ -765,6 +768,15 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
|PhysicsInstruction::Jump => self.advance_time(ins.time),
}
match ins.instruction {
PhysicsInstruction::SetPosition(position)=>{
//temp
self.body.position=position;
//manual clear //for c in self.contacts{process_instruction(CollisionEnd(c))}
self.contacts.clear();
self.body.acceleration=self.gravity;
self.walk.state=WalkEnum::Reached;
self.grounded=false;
}
PhysicsInstruction::CollisionStart(c) => {
//check ground
match &c.face {
@ -854,4 +866,4 @@ impl crate::instruction::InstructionConsumer<PhysicsInstruction> for PhysicsStat
},
}
}
}
}

@ -53,7 +53,7 @@ pub trait Example: 'static + Sized {
device: &wgpu::Device,
queue: &wgpu::Queue,
);
fn update(&mut self, event: WindowEvent);
fn update(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, event: WindowEvent);
fn move_mouse(&mut self, delta: (f64,f64));
fn render(
&mut self,
@ -181,9 +181,41 @@ async fn setup<E: Example>(title: &str) -> Setup {
(size, surface)
};
let adapter = wgpu::util::initialize_adapter_from_env_or_default(&instance, Some(&surface))
.await
.expect("No suitable GPU adapters found on the system!");
let adapter;
let optional_features = E::optional_features();
let required_features = E::required_features();
//no helper function smh gotta write it myself
let adapters = instance.enumerate_adapters(backends);
let mut chosen_adapter = None;
let mut chosen_adapter_score=0;
for adapter in adapters {
if !adapter.is_surface_supported(&surface) {
continue;
}
let score=match adapter.get_info().device_type{
wgpu::DeviceType::IntegratedGpu=>3,
wgpu::DeviceType::DiscreteGpu=>4,
wgpu::DeviceType::VirtualGpu=>2,
wgpu::DeviceType::Other|wgpu::DeviceType::Cpu=>1,
};
let adapter_features = adapter.features();
if chosen_adapter_score<score&&adapter_features.contains(required_features) {
chosen_adapter_score=score;
chosen_adapter=Some(adapter);
}
}
if let Some(maybe_chosen_adapter) = chosen_adapter{
adapter=maybe_chosen_adapter;
}else{
panic!("No suitable GPU adapters found on the system!");
}
#[cfg(not(target_arch = "wasm32"))]
{
@ -191,15 +223,6 @@ async fn setup<E: Example>(title: &str) -> Setup {
println!("Using {} ({:?})", adapter_info.name, adapter_info.backend);
}
let optional_features = E::optional_features();
let required_features = E::required_features();
let adapter_features = adapter.features();
assert!(
adapter_features.contains(required_features),
"Adapter does not support required features for this example: {:?}",
required_features - adapter_features
);
let required_downlevel_capabilities = E::required_downlevel_capabilities();
let downlevel_capabilities = adapter.get_downlevel_capabilities();
assert!(
@ -223,7 +246,7 @@ async fn setup<E: Example>(title: &str) -> Setup {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: (optional_features & adapter_features) | required_features,
features: (optional_features & adapter.features()) | required_features,
limits: needed_limits,
},
trace_dir.ok().as_ref().map(std::path::Path::new),
@ -346,7 +369,7 @@ fn start<E: Example>(
println!("{:#?}", instance.generate_report());
}
_ => {
example.update(event);
example.update(&device,&queue,event);
}
},
event::Event::DeviceEvent {

@ -1,5 +0,0 @@
pub mod framework;
pub mod body;
pub mod zeroes;
pub mod instruction;
pub mod load_roblox;

@ -1,3 +1,9 @@
use std::todo;
use crate::model::{ModelData,ModelInstance};
use crate::primitives;
fn class_is_a(class: &str, superclass: &str) -> bool {
if class==superclass {
return true
@ -10,19 +16,125 @@ fn class_is_a(class: &str, superclass: &str) -> bool {
}
return false
}
pub fn get_objects(buf_thing: std::io::BufReader<&[u8]>, superclass: &str) -> Result<std::vec::Vec<rbx_dom_weak::Instance>, Box<dyn std::error::Error>> {
// Using buffered I/O is recommended with rbx_binary
let dom = rbx_binary::from_reader(buf_thing)?;
let mut objects = std::vec::Vec::<rbx_dom_weak::Instance>::new();
//move matching instances into objects
let (_,mut instances) = dom.into_raw();
for (_,instance) in instances.drain() {
if class_is_a(instance.class.as_str(), superclass) {
objects.push(instance);
fn recursive_collect_superclass(objects: &mut std::vec::Vec<rbx_dom_weak::types::Ref>,dom: &rbx_dom_weak::WeakDom, instance: &rbx_dom_weak::Instance, superclass: &str){
for &referent in instance.children() {
if let Some(c) = dom.get_by_ref(referent) {
if class_is_a(c.class.as_str(), superclass) {
objects.push(c.referent());//copy ref
}
recursive_collect_superclass(objects,dom,c,superclass);
}
}
return Ok(objects)
}
fn get_texture_refs(dom:&rbx_dom_weak::WeakDom) -> Vec<rbx_dom_weak::types::Ref>{
let mut objects = std::vec::Vec::new();
recursive_collect_superclass(&mut objects, dom, dom.root(),"Decal");
//get ids
//clear vec
//next class
objects
}
struct RobloxAssetId(u64);
struct RobloxAssetIdParseErr;
impl std::str::FromStr for RobloxAssetId {
type Err=RobloxAssetIdParseErr;
fn from_str(s: &str) -> Result<Self, Self::Err>{
let regman=regex::Regex::new(r"(\d+)$").unwrap();
if let Some(captures) = regman.captures(s) {
if captures.len()==2{//captures[0] is all captures concatenated, and then each individual capture
if let Ok(id) = captures[0].parse::<u64>() {
return Ok(Self(id));
}
}
}
Err(RobloxAssetIdParseErr)
}
}
pub fn generate_modeldatas_roblox(dom:rbx_dom_weak::WeakDom) -> Result<(Vec<ModelData>,Vec<String>,glam::Vec3), Box<dyn std::error::Error>>{
//ModelData includes texture dds
let mut spawn_point=glam::Vec3::ZERO;
//TODO: generate unit Block, Wedge, etc. after based on part shape lists
let mut modeldatas=crate::model::generate_modeldatas(primitives::the_unit_cube_lol(),ModelData::COLOR_FLOATS_WHITE);
let unit_cube_modeldata=modeldatas[0].clone();
let mut texture_id_from_asset_id=std::collections::HashMap::<u64,u32>::new();
let mut asset_id_from_texture_id=Vec::new();
let mut object_refs = std::vec::Vec::new();
let mut temp_objects = std::vec::Vec::new();
recursive_collect_superclass(&mut object_refs, &dom, dom.root(),"BasePart");
for object_ref in object_refs {
if let Some(object)=dom.get_by_ref(object_ref){
if let (
Some(rbx_dom_weak::types::Variant::CFrame(cf)),
Some(rbx_dom_weak::types::Variant::Vector3(size)),
Some(rbx_dom_weak::types::Variant::Float32(transparency)),
Some(rbx_dom_weak::types::Variant::Color3uint8(color3)),
Some(rbx_dom_weak::types::Variant::Enum(shape)),
) = (
object.properties.get("CFrame"),
object.properties.get("Size"),
object.properties.get("Transparency"),
object.properties.get("Color"),
object.properties.get("Shape"),//this will also skip unions
)
{
let model_instance=ModelInstance {
transform:glam::Mat4::from_translation(
glam::Vec3::new(cf.position.x,cf.position.y,cf.position.z)
)
* glam::Mat4::from_mat3(
glam::Mat3::from_cols(
glam::Vec3::new(cf.orientation.x.x,cf.orientation.y.x,cf.orientation.z.x),
glam::Vec3::new(cf.orientation.x.y,cf.orientation.y.y,cf.orientation.z.y),
glam::Vec3::new(cf.orientation.x.z,cf.orientation.y.z,cf.orientation.z.z),
),
)
* glam::Mat4::from_scale(
glam::Vec3::new(size.x,size.y,size.z)/2.0
),
color: glam::vec4(color3.r as f32/255f32, color3.g as f32/255f32, color3.b as f32/255f32, 1.0-*transparency),
};
if object.name=="MapStart"{
spawn_point=glam::Vec4Swizzles::xyz(model_instance.transform*glam::Vec3::Y.extend(1.0))+glam::vec3(0.0,2.5,0.0);
println!("Found MapStart{:?}",spawn_point);
}
if *transparency==1.0||shape.to_u32()!=1 {
continue;
}
temp_objects.clear();
recursive_collect_superclass(&mut temp_objects, &dom, object,"Decal");
let mut i_can_only_load_one_texture_per_model=None;
for &decal_ref in &temp_objects{
if let Some(decal)=dom.get_by_ref(decal_ref){
if let Some(rbx_dom_weak::types::Variant::Content(content)) = decal.properties.get("Texture") {
if let Ok(asset_id)=content.clone().into_string().parse::<RobloxAssetId>(){
if let Some(&texture_id)=texture_id_from_asset_id.get(&asset_id.0){
i_can_only_load_one_texture_per_model=Some(texture_id);
}else{
let texture_id=asset_id_from_texture_id.len();
texture_id_from_asset_id.insert(asset_id.0,texture_id as u32);
asset_id_from_texture_id.push(asset_id.0);
//make new model
let mut unit_cube_texture=unit_cube_modeldata.clone();
unit_cube_texture.texture=Some(texture_id as u32);
modeldatas.push(unit_cube_texture);
}
}
}
}
}
match i_can_only_load_one_texture_per_model{
//push to existing texture model
Some(texture_id)=>modeldatas[(texture_id+1) as usize].instances.push(model_instance),
//push instance to big unit cube in the sky
None=>modeldatas[0].instances.push(model_instance),
}
}
}
}
Ok((modeldatas,asset_id_from_texture_id.iter().map(|t|t.to_string()).collect(),spawn_point))
}

File diff suppressed because it is too large Load Diff

70
src/model.rs Normal file

@ -0,0 +1,70 @@
use bytemuck::{Pod, Zeroable};
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct Vertex {
pub pos: [f32; 3],
pub texture: [f32; 2],
pub normal: [f32; 3],
pub color: [f32; 4],
}
#[derive(Clone)]
pub struct ModelInstance {
pub transform: glam::Mat4,
pub color: glam::Vec4,
}
#[derive(Clone)]
pub struct ModelData {
pub instances: Vec<ModelInstance>,
pub vertices: Vec<Vertex>,
pub entities: Vec<Vec<u16>>,
pub texture: Option<u32>,
}
impl ModelData {
pub const COLOR_FLOATS_WHITE: [f32;4] = [1.0,1.0,1.0,1.0];
pub const COLOR_VEC4_WHITE: glam::Vec4 = glam::vec4(1.0,1.0,1.0,1.0);
}
pub fn generate_modeldatas(data:obj::ObjData,color:[f32;4]) -> Vec<ModelData>{
let mut modeldatas=Vec::new();
let mut vertices = Vec::new();
let mut vertex_index = std::collections::HashMap::<obj::IndexTuple,u16>::new();
for object in data.objects {
vertices.clear();
vertex_index.clear();
let mut entities = Vec::new();
for group in object.groups {
let mut indices = Vec::new();
for poly in group.polys {
for end_index in 2..poly.0.len() {
for &index in &[0, end_index - 1, end_index] {
let vert = poly.0[index];
if let Some(&i)=vertex_index.get(&vert){
indices.push(i);
}else{
let i=vertices.len() as u16;
vertices.push(Vertex {
pos: data.position[vert.0],
texture: data.texture[vert.1.unwrap()],
normal: data.normal[vert.2.unwrap()],
color,
});
vertex_index.insert(vert,i);
indices.push(i);
}
}
}
}
entities.push(indices);
}
modeldatas.push(ModelData {
instances: Vec::new(),
vertices:vertices.clone(),
entities,
texture: None,
});
}
modeldatas
}

76
src/primitives.rs Normal file

@ -0,0 +1,76 @@
pub fn the_unit_cube_lol() -> obj::ObjData{
obj::ObjData{
position: vec![
[-1.,-1., 1.],//left bottom back
[ 1.,-1., 1.],//right bottom back
[ 1., 1., 1.],//right top back
[-1., 1., 1.],//left top back
[-1., 1.,-1.],//left top front
[ 1., 1.,-1.],//right top front
[ 1.,-1.,-1.],//right bottom front
[-1.,-1.,-1.],//left bottom front
],
texture: vec![[0.0,0.0],[1.0,0.0],[1.0,1.0],[0.0,1.0]],
normal: vec![
[1.,0.,0.],//AabbFace::Right
[0.,1.,0.],//AabbFace::Top
[0.,0.,1.],//AabbFace::Back
[-1.,0.,0.],//AabbFace::Left
[0.,-1.,0.],//AabbFace::Bottom
[0.,0.,-1.],//AabbFace::Front
],
objects: vec![obj::Object{
name: "Unit Cube".to_owned(),
groups: vec![obj::Group{
name: "Cube Vertices".to_owned(),
index: 0,
material: None,
polys: vec![
// back (0, 0, 1)
obj::SimplePolygon(vec![
obj::IndexTuple(0,Some(0),Some(2)),
obj::IndexTuple(1,Some(1),Some(2)),
obj::IndexTuple(2,Some(2),Some(2)),
obj::IndexTuple(3,Some(3),Some(2)),
]),
// front (0, 0,-1)
obj::SimplePolygon(vec![
obj::IndexTuple(4,Some(0),Some(5)),
obj::IndexTuple(5,Some(1),Some(5)),
obj::IndexTuple(6,Some(2),Some(5)),
obj::IndexTuple(7,Some(3),Some(5)),
]),
// right (1, 0, 0)
obj::SimplePolygon(vec![
obj::IndexTuple(6,Some(0),Some(0)),
obj::IndexTuple(5,Some(1),Some(0)),
obj::IndexTuple(2,Some(2),Some(0)),
obj::IndexTuple(1,Some(3),Some(0)),
]),
// left (-1, 0, 0)
obj::SimplePolygon(vec![
obj::IndexTuple(0,Some(0),Some(3)),
obj::IndexTuple(3,Some(1),Some(3)),
obj::IndexTuple(4,Some(2),Some(3)),
obj::IndexTuple(7,Some(3),Some(3)),
]),
// top (0, 1, 0)
obj::SimplePolygon(vec![
obj::IndexTuple(5,Some(1),Some(1)),
obj::IndexTuple(4,Some(0),Some(1)),
obj::IndexTuple(3,Some(3),Some(1)),
obj::IndexTuple(2,Some(2),Some(1)),
]),
// bottom (0,-1, 0)
obj::SimplePolygon(vec![
obj::IndexTuple(1,Some(1),Some(4)),
obj::IndexTuple(0,Some(0),Some(4)),
obj::IndexTuple(7,Some(3),Some(4)),
obj::IndexTuple(6,Some(2),Some(4)),
]),
],
}]
}],
material_libs: Vec::new(),
}
}

@ -1,9 +1,4 @@
struct SkyOutput {
@builtin(position) position: vec4<f32>,
@location(0) sampledir: vec3<f32>,
};
struct Data {
struct Camera {
// from camera to screen
proj: mat4x4<f32>,
// from screen to camera
@ -13,9 +8,16 @@ struct Data {
// camera position
cam_pos: vec4<f32>,
};
//group 0 is the camera
@group(0)
@binding(0)
var<uniform> r_data: Data;
var<uniform> camera: Camera;
struct SkyOutput {
@builtin(position) position: vec4<f32>,
@location(0) sampledir: vec3<f32>,
};
@vertex
fn vs_sky(@builtin(vertex_index) vertex_index: u32) -> SkyOutput {
@ -30,8 +32,8 @@ fn vs_sky(@builtin(vertex_index) vertex_index: u32) -> SkyOutput {
);
// transposition = inversion for this orthonormal matrix
let inv_model_view = transpose(mat3x3<f32>(r_data.view[0].xyz, r_data.view[1].xyz, r_data.view[2].xyz));
let unprojected = r_data.proj_inv * pos;
let inv_model_view = transpose(mat3x3<f32>(camera.view[0].xyz, camera.view[1].xyz, camera.view[2].xyz));
let unprojected = camera.proj_inv * pos;
var result: SkyOutput;
result.sampledir = inv_model_view * unprojected.xyz;
@ -39,93 +41,71 @@ fn vs_sky(@builtin(vertex_index) vertex_index: u32) -> SkyOutput {
return result;
}
struct GroundOutput {
@builtin(position) position: vec4<f32>,
@location(4) pos: vec3<f32>,
};
@vertex
fn vs_ground(@builtin(vertex_index) vertex_index: u32) -> GroundOutput {
// hacky way to draw two triangles that make a square
let tmp1 = i32(vertex_index)/2-i32(vertex_index)/3;
let tmp2 = i32(vertex_index)&1;
let pos = vec3<f32>(
f32(tmp1) * 2.0 - 1.0,
0.0,
f32(tmp2) * 2.0 - 1.0
) * 160.0;
var result: GroundOutput;
result.pos = pos;
result.position = r_data.proj * r_data.view * vec4<f32>(pos, 1.0);
return result;
struct ModelInstance{
transform:mat4x4<f32>,
//texture_transform:mat3x3<f32>,
color:vec4<f32>,
}
//my fancy idea is to create a megatexture for each model that includes all the textures each intance will need
//the texture transform then maps the texture coordinates to the location of the specific texture
//group 1 is the model
const MAX_MODEL_INSTANCES=4096;
@group(1)
@binding(0)
var<uniform> model_instances: array<ModelInstance, MAX_MODEL_INSTANCES>;
@group(1)
@binding(1)
var model_texture: texture_2d<f32>;
@group(1)
@binding(2)
var model_sampler: sampler;
struct EntityOutput {
struct EntityOutputTexture {
@builtin(position) position: vec4<f32>,
@location(1) texture: vec2<f32>,
@location(2) normal: vec3<f32>,
@location(3) view: vec3<f32>,
@location(4) color: vec4<f32>,
};
@group(1)
@binding(0)
var<uniform> r_EntityTransform: mat4x4<f32>;
@vertex
fn vs_entity(
fn vs_entity_texture(
@builtin(instance_index) instance: u32,
@location(0) pos: vec3<f32>,
@location(1) texture: vec2<f32>,
@location(2) normal: vec3<f32>,
) -> EntityOutput {
var position: vec4<f32> = r_EntityTransform * vec4<f32>(pos, 1.0);
var result: EntityOutput;
result.normal = (r_EntityTransform * vec4<f32>(normal, 0.0)).xyz;
result.texture=texture;
result.view = position.xyz - r_data.cam_pos.xyz;
result.position = r_data.proj * r_data.view * position;
@location(3) color: vec4<f32>,
) -> EntityOutputTexture {
var position: vec4<f32> = model_instances[instance].transform * vec4<f32>(pos, 1.0);
var result: EntityOutputTexture;
result.normal = (model_instances[instance].transform * vec4<f32>(normal, 0.0)).xyz;
result.texture=texture;//(model_instances[instance].texture_transform * vec3<f32>(texture, 1.0)).xy;
result.color=model_instances[instance].color * color;
result.view = position.xyz - camera.cam_pos.xyz;
result.position = camera.proj * camera.view * position;
return result;
}
@group(0)
//group 2 is the skybox texture
@group(2)
@binding(0)
var cube_texture: texture_cube<f32>;
@group(2)
@binding(1)
var r_texture: texture_cube<f32>;
@group(0)
@binding(2)
var r_sampler: sampler;
var cube_sampler: sampler;
@fragment
fn fs_sky(vertex: SkyOutput) -> @location(0) vec4<f32> {
return textureSample(r_texture, r_sampler, vertex.sampledir);
return textureSample(cube_texture, model_sampler, vertex.sampledir);
}
@fragment
fn fs_entity(vertex: EntityOutput) -> @location(0) vec4<f32> {
fn fs_entity_texture(vertex: EntityOutputTexture) -> @location(0) vec4<f32> {
let incident = normalize(vertex.view);
let normal = normalize(vertex.normal);
let d = dot(normal, incident);
let reflected = incident - 2.0 * d * normal;
let dir = vec3<f32>(-1.0)+2.0*vec3<f32>(vertex.texture.x,0.0,vertex.texture.y);
let texture_color = textureSample(r_texture, r_sampler, dir).rgb;
let reflected_color = textureSample(r_texture, r_sampler, reflected).rgb;
return vec4<f32>(mix(vec3<f32>(0.1) + 0.5 * reflected_color,texture_color,1.0-pow(1.0-abs(d),2.0)), 1.0);
}
fn modulo_euclidean (a: f32, b: f32) -> f32 {
var m = a % b;
if (m < 0.0) {
if (b < 0.0) {
m -= b;
} else {
m += b;
}
}
return m;
}
@fragment
fn fs_ground(vertex: GroundOutput) -> @location(0) vec4<f32> {
let dir = vec3<f32>(-1.0)+vec3<f32>(modulo_euclidean(vertex.pos.x/16.,1.0),0.0,modulo_euclidean(vertex.pos.z/16.,1.0))*2.0;
return vec4<f32>(textureSample(r_texture, r_sampler, dir).rgb, 1.0);
let fragment_color = textureSample(model_texture, model_sampler, vertex.texture)*vertex.color;
let reflected_color = textureSample(cube_texture, cube_sampler, reflected).rgb;
return mix(vec4<f32>(vec3<f32>(0.1) + 0.5 * reflected_color,1.0),fragment_color,1.0-pow(1.0-abs(d),2.0));
}

@ -1,4 +1,5 @@
//find roots of polynomials
#[inline]
pub fn zeroes2(a0:f32,a1:f32,a2:f32) -> Vec<f32>{
if a2==0f32{
return zeroes1(a0, a1);