]> git.nega.tv - josh/narcissus/commitdiff
shark: Add guns which shoot projectile sharks
authorJoshua Simmons <josh@nega.tv>
Sun, 5 May 2024 14:21:22 +0000 (16:21 +0200)
committerJoshua Simmons <josh@nega.tv>
Sun, 5 May 2024 14:38:06 +0000 (16:38 +0200)
engine/narcissus-maths/src/affine3.rs
title/shark/src/main.rs
title/shark/src/pipelines/basic.rs

index fd3210408a11b5c4d781bc326b420bd5094dfa32..a320abb976089f2652f00add215d2051c371abdb 100644 (file)
@@ -2,28 +2,35 @@ use crate::{Mat3, Point3, Vec3};
 
 /// Matrix and translation vector which together represent a 3d affine
 /// transformation.
-#[derive(Clone, Copy, PartialEq)]
+#[derive(Clone, Copy, PartialEq, Debug)]
 #[repr(C)]
 pub struct Affine3 {
     pub matrix: Mat3,
-    pub translate: Vec3,
+    pub translation: Vec3,
 }
 
 impl Affine3 {
     pub const ZERO: Affine3 = Affine3 {
         matrix: Mat3::ZERO,
-        translate: Vec3::ZERO,
+        translation: Vec3::ZERO,
     };
 
     pub const IDENTITY: Affine3 = Affine3 {
         matrix: Mat3::IDENTITY,
-        translate: Vec3::ZERO,
+        translation: Vec3::ZERO,
     };
 
+    pub fn new(matrix: Mat3, translation: Vec3) -> Self {
+        Self {
+            matrix,
+            translation,
+        }
+    }
+
     pub fn mul_affine3(&self, rhs: Affine3) -> Affine3 {
         Self {
             matrix: self.matrix * rhs.matrix,
-            translate: self.translate + rhs.translate,
+            translation: self.translation + rhs.translation,
         }
     }
 
@@ -32,7 +39,7 @@ impl Affine3 {
     }
 
     pub fn transform_point3(&self, point: Point3) -> Point3 {
-        self.matrix * point + self.translate
+        self.matrix * point + self.translation
     }
 }
 
index da6d461c0306eac06d7c0ea3db9aaa2abc76ac14..e0f133aa014e9c5d45664842b0d3c8de2b3440c6 100644 (file)
@@ -1,4 +1,4 @@
-use std::{fmt::Write, time::Instant};
+use std::fmt::Write;
 
 use crate::{
     fonts::{FontFamily, Fonts},
@@ -6,17 +6,18 @@ use crate::{
 };
 use helpers::{load_image, load_obj};
 use narcissus_app::{create_app, Event, Key, PressedState, WindowDesc};
-use narcissus_core::{default, rand::Pcg64, slice::array_windows};
+use narcissus_core::{default, rand::Pcg64, slice::array_windows, BitIter};
 use narcissus_font::{FontCollection, GlyphCache, HorizontalMetrics};
 use narcissus_gpu::{
-    create_device, Access, BufferImageCopy, BufferUsageFlags, ClearValue, DeviceExt, Extent2d,
-    Extent3d, ImageAspectFlags, ImageBarrier, ImageDesc, ImageDimension, ImageFormat, ImageLayout,
-    ImageTiling, ImageUsageFlags, LoadOp, MemoryLocation, Offset2d, Offset3d, RenderingAttachment,
-    RenderingDesc, Scissor, StoreOp, ThreadToken, Viewport,
+    create_device, Access, Bind, BufferImageCopy, BufferUsageFlags, ClearValue, DeviceExt,
+    Extent2d, Extent3d, ImageAspectFlags, ImageBarrier, ImageDesc, ImageDimension, ImageFormat,
+    ImageLayout, ImageTiling, ImageUsageFlags, IndexType, LoadOp, MemoryLocation, Offset2d,
+    Offset3d, RenderingAttachment, RenderingDesc, Scissor, StoreOp, ThreadToken, TypedBind,
+    Viewport,
 };
 use narcissus_maths::{
-    clamp, perlin_noise3, sin_cos_pi_f32, sin_pi_f32, vec3, Affine3, Deg, HalfTurn, Mat3, Mat4,
-    Point3, Vec3,
+    clamp, exp_f32, perlin_noise3, sin_pi_f32, vec3, Affine3, Deg, HalfTurn, Mat3, Mat4, Point3,
+    Vec3,
 };
 use pipelines::{BasicUniforms, PrimitiveInstance, PrimitiveVertex, TextUniforms};
 
@@ -26,12 +27,13 @@ mod pipelines;
 
 const SQRT_2: f32 = 0.70710677;
 
-const NUM_SHARKS: usize = 50;
 const GLYPH_CACHE_SIZE: usize = 1024;
 
+const ARCHTYPE_PROJECTILE_MAX: usize = 65536;
+
 struct GameVariables {
     game_speed: f32,
-    player_speed: f32,
+
     camera_distance: f32,
     camera_angle: Deg,
     camera_damping: f32,
@@ -39,11 +41,17 @@ struct GameVariables {
     camera_shake_decay: f32,
     camera_shake_max_offset: f32,
     camera_shake_frequency: f32,
+
+    player_speed: f32,
+
+    weapon_cooldown: f32,
+    weapon_projectile_speed: f32,
+    weapon_projectile_lifetime: f32,
 }
 
 static GAME_VARIABLES: GameVariables = GameVariables {
     game_speed: 1.0,
-    player_speed: 15.0,
+
     camera_distance: 55.0,
     camera_angle: Deg::new(60.0),
     camera_damping: 35.0,
@@ -51,6 +59,12 @@ static GAME_VARIABLES: GameVariables = GameVariables {
     camera_shake_decay: 2.0,
     camera_shake_max_offset: 2.0,
     camera_shake_frequency: 11.0,
+
+    player_speed: 15.0,
+
+    weapon_cooldown: 0.125,
+    weapon_projectile_speed: 20.0,
+    weapon_projectile_lifetime: 2.0,
 };
 
 #[derive(Clone, Copy, Debug)]
@@ -106,13 +120,17 @@ impl Actions {
 struct PlayerState {
     position: Point3,
     heading: Vec3,
+
+    weapon_cooldown: f32,
 }
 
 impl PlayerState {
     fn new() -> Self {
         Self {
             position: Point3::ZERO,
-            heading: Vec3::new(SQRT_2, 0.0, -SQRT_2),
+            heading: vec3(SQRT_2, 0.0, -SQRT_2),
+
+            weapon_cooldown: GAME_VARIABLES.weapon_cooldown,
         }
     }
 }
@@ -136,7 +154,7 @@ impl CameraState {
 
         // Rotate camera
         let one_on_sqrt2 = 1.0 / 2.0_f32.sqrt();
-        let eye_offset = Vec3::new(-base * one_on_sqrt2, height, -base * one_on_sqrt2);
+        let eye_offset = vec3(-base * one_on_sqrt2, height, -base * one_on_sqrt2);
 
         Self {
             eye_offset,
@@ -149,7 +167,7 @@ impl CameraState {
         }
     }
 
-    fn tick(&mut self, target: Point3, time: f32, dt: f32) {
+    fn tick(&mut self, target: Point3, time: f32, delta_time: f32) {
         if Point3::distance_sq(self.position, target)
             > (GAME_VARIABLES.camera_deadzone * GAME_VARIABLES.camera_deadzone)
         {
@@ -158,14 +176,14 @@ impl CameraState {
                 self.velocity.x,
                 target.x,
                 GAME_VARIABLES.camera_damping,
-                dt,
+                delta_time,
             );
             let (pos_z, vel_z) = simple_spring_damper_exact(
                 self.position.z,
                 self.velocity.z,
                 target.z,
                 GAME_VARIABLES.camera_damping,
-                dt,
+                delta_time,
             );
 
             self.position.z = pos_z;
@@ -174,7 +192,7 @@ impl CameraState {
             self.velocity.z = vel_z;
         }
 
-        self.shake -= GAME_VARIABLES.camera_shake_decay * dt;
+        self.shake -= GAME_VARIABLES.camera_shake_decay * delta_time;
         self.shake = clamp(self.shake, 0.0, 1.0);
 
         let t = time * GAME_VARIABLES.camera_shake_frequency;
@@ -191,26 +209,49 @@ impl CameraState {
     }
 }
 
+#[derive(Clone, Copy, Default)]
+struct ArchetypeProjectile8 {
+    position_x: [f32; 8],
+    position_z: [f32; 8],
+    velocity_x: [f32; 8],
+    velocity_z: [f32; 8],
+    lifetime: [f32; 8],
+}
+
 struct GameState {
+    rng: Pcg64,
+
     time: f32,
     actions: Actions,
     camera: CameraState,
     player: PlayerState,
+
+    archetype_projectile_bitmap_0: [u64; ARCHTYPE_PROJECTILE_MAX / 64 / 64],
+    archetype_projectile_bitmap_1: [u64; ARCHTYPE_PROJECTILE_MAX / 64],
+    archetype_projectile: Box<[ArchetypeProjectile8]>,
 }
 
 impl GameState {
     fn new() -> Self {
         Self {
+            rng: Pcg64::new(),
             time: 0.0,
             actions: Actions::new(),
             camera: CameraState::new(),
             player: PlayerState::new(),
+            archetype_projectile_bitmap_0: [0; ARCHTYPE_PROJECTILE_MAX / 64 / 64],
+            archetype_projectile_bitmap_1: [0; ARCHTYPE_PROJECTILE_MAX / 64],
+            archetype_projectile: vec![
+                ArchetypeProjectile8::default();
+                ARCHTYPE_PROJECTILE_MAX / 8
+            ]
+            .into_boxed_slice(),
         }
     }
 
-    fn tick(&mut self, dt: f32, action_queue: &[ActionEvent]) {
-        let dt = dt * GAME_VARIABLES.game_speed;
-        self.time += dt;
+    fn tick(&mut self, delta_time: f32, action_queue: &[ActionEvent]) {
+        let delta_time = delta_time * GAME_VARIABLES.game_speed;
+        self.time += delta_time;
 
         self.actions.tick(action_queue);
 
@@ -224,14 +265,14 @@ impl GameState {
             | (self.actions.is_active(Action::Right) as usize) << 3;
 
         // Pre-rotated values
-        const UP: Vec3 = Vec3::new(SQRT_2, 0.0, SQRT_2);
-        const DOWN: Vec3 = Vec3::new(-SQRT_2, 0.0, -SQRT_2);
-        const LEFT: Vec3 = Vec3::new(SQRT_2, 0.0, -SQRT_2);
-        const RIGHT: Vec3 = Vec3::new(-SQRT_2, 0.0, SQRT_2);
-        const UP_LEFT: Vec3 = Vec3::new(1.0, 0.0, 0.0);
-        const UP_RIGHT: Vec3 = Vec3::new(0.0, 0.0, 1.0);
-        const DOWN_LEFT: Vec3 = Vec3::new(0.0, 0.0, -1.0);
-        const DOWN_RIGHT: Vec3 = Vec3::new(-1.0, 0.0, 0.0);
+        const UP: Vec3 = vec3(SQRT_2, 0.0, SQRT_2);
+        const DOWN: Vec3 = vec3(-SQRT_2, 0.0, -SQRT_2);
+        const LEFT: Vec3 = vec3(SQRT_2, 0.0, -SQRT_2);
+        const RIGHT: Vec3 = vec3(-SQRT_2, 0.0, SQRT_2);
+        const UP_LEFT: Vec3 = vec3(1.0, 0.0, 0.0);
+        const UP_RIGHT: Vec3 = vec3(0.0, 0.0, 1.0);
+        const DOWN_LEFT: Vec3 = vec3(0.0, 0.0, -1.0);
+        const DOWN_RIGHT: Vec3 = vec3(-1.0, 0.0, 0.0);
 
         let movement = [
             // 0 0 0 0
@@ -272,19 +313,91 @@ impl GameState {
             self.player.heading = movement;
         }
 
-        self.player.position += movement * GAME_VARIABLES.player_speed * dt;
+        let player_velocity = movement * GAME_VARIABLES.player_speed;
+        self.player.position += player_velocity * delta_time;
+
+        self.camera
+            .tick(self.player.position, self.time, delta_time);
+
+        self.player.weapon_cooldown -= delta_time;
 
-        self.camera.tick(self.player.position, self.time, dt);
+        if self.player.weapon_cooldown <= 0.0 {
+            // fire!
+            let [x, y] = self.rng.next_uniform_unit_circle_f32();
+            let direction = vec3(x, 0.0, y);
+            let velocity = player_velocity + direction * GAME_VARIABLES.weapon_projectile_speed;
+            self.spawn_projectile(
+                self.player.position,
+                velocity,
+                GAME_VARIABLES.weapon_projectile_lifetime,
+            );
+
+            self.player.weapon_cooldown = GAME_VARIABLES.weapon_cooldown;
+        }
+
+        // Expire projectiles
+        for (base, base_word) in self.archetype_projectile_bitmap_0.iter_mut().enumerate() {
+            for i in BitIter::new(std::iter::once(*base_word)) {
+                let index = base * 64 + i;
+                let word = &mut self.archetype_projectile_bitmap_1[index];
+                for j in BitIter::new(std::iter::once(*word)) {
+                    let index = index * 64 + j;
+                    self.archetype_projectile[index / 8].lifetime[index % 8] -= delta_time;
+                    let dead = self.archetype_projectile[index / 8].lifetime[index % 8] <= 0.0;
+                    *word &= !((dead as u64) << j);
+                }
+                *base_word &= !(((*word == 0) as u64) << i);
+            }
+        }
+
+        // Move projectiles
+        for base in BitIter::new(self.archetype_projectile_bitmap_0.iter().copied()) {
+            for i in BitIter::new(std::iter::once(self.archetype_projectile_bitmap_1[base])) {
+                let i = base * 64 + i;
+                self.archetype_projectile[i / 8].position_x[i % 8] +=
+                    self.archetype_projectile[i / 8].velocity_x[i % 8] * delta_time;
+                self.archetype_projectile[i / 8].position_z[i % 8] +=
+                    self.archetype_projectile[i / 8].velocity_z[i % 8] * delta_time;
+            }
+        }
+    }
+
+    fn spawn_projectile(&mut self, position: Point3, velocity: Vec3, lifetime: f32) {
+        let i = BitIter::new(
+            self.archetype_projectile_bitmap_1
+                .iter()
+                .copied()
+                .map(|x| !x),
+        )
+        .next()
+        .unwrap();
+
+        self.archetype_projectile[i / 8].position_x[i % 8] = position.x;
+        self.archetype_projectile[i / 8].position_z[i % 8] = position.z;
+        self.archetype_projectile[i / 8].velocity_x[i % 8] = velocity.x;
+        self.archetype_projectile[i / 8].velocity_z[i % 8] = velocity.z;
+        self.archetype_projectile[i / 8].lifetime[i % 8] = lifetime;
+        self.archetype_projectile_bitmap_1[i / 64] |= 1 << i % 64;
+        self.archetype_projectile_bitmap_0[i / 64 / 64] |= 1 << ((i / 64) % 64);
     }
 }
 
 // https://theorangeduck.com/page/spring-roll-call
-fn simple_spring_damper_exact(x: f32, v: f32, x_goal: f32, damping: f32, dt: f32) -> (f32, f32) {
+fn simple_spring_damper_exact(
+    x: f32,
+    velocity: f32,
+    goal: f32,
+    damping: f32,
+    delta_time: f32,
+) -> (f32, f32) {
     let y = damping / 2.0;
-    let j0 = x - x_goal;
-    let j1 = v + j0 * y;
-    let eydt = (-y * dt).exp();
-    (eydt * (j0 + j1 * dt) + x_goal, eydt * (v - j1 * y * dt))
+    let j0 = x - goal;
+    let j1 = velocity + j0 * y;
+    let eydt = exp_f32(-y * delta_time);
+    (
+        eydt * (j0 + j1 * delta_time) + goal,
+        eydt * (velocity - j1 * y * delta_time),
+    )
 }
 
 pub fn main() {
@@ -299,9 +412,11 @@ pub fn main() {
         width: 800,
         height: 600,
     });
+
     let device = create_device(narcissus_gpu::DeviceBackend::Vulkan);
 
     let thread_token = ThreadToken::new();
+    let thread_token = &thread_token;
 
     let basic_pipeline = BasicPipeline::new(device.as_ref());
     let text_pipeline = TextPipeline::new(device.as_ref());
@@ -363,55 +478,58 @@ pub fn main() {
         );
 
         let mut cmd_encoder = device.request_cmd_encoder(&frame, &thread_token);
+        {
+            let cmd_encoder = &mut cmd_encoder;
 
-        device.cmd_barrier(
-            &mut cmd_encoder,
-            None,
-            &[
-                ImageBarrier::layout_optimal(
-                    &[Access::None],
-                    &[Access::ShaderSampledImageRead],
-                    glyph_atlas,
-                    ImageAspectFlags::COLOR,
-                ),
-                ImageBarrier::layout_optimal(
-                    &[Access::None],
+            device.cmd_barrier(
+                cmd_encoder,
+                None,
+                &[
+                    ImageBarrier::layout_optimal(
+                        &[Access::None],
+                        &[Access::ShaderSampledImageRead],
+                        glyph_atlas,
+                        ImageAspectFlags::COLOR,
+                    ),
+                    ImageBarrier::layout_optimal(
+                        &[Access::None],
+                        &[Access::TransferWrite],
+                        blåhaj_image,
+                        ImageAspectFlags::COLOR,
+                    ),
+                ],
+            );
+
+            device.cmd_copy_buffer_to_image(
+                cmd_encoder,
+                blåhaj_buffer.to_arg(),
+                blåhaj_image,
+                ImageLayout::Optimal,
+                &[BufferImageCopy {
+                    buffer_offset: 0,
+                    buffer_row_length: 0,
+                    buffer_image_height: 0,
+                    image_subresource: default(),
+                    image_offset: Offset3d { x: 0, y: 0, z: 0 },
+                    image_extent: Extent3d {
+                        width: blåhaj_image_data.width() as u32,
+                        height: blåhaj_image_data.width() as u32,
+                        depth: 1,
+                    },
+                }],
+            );
+
+            device.cmd_barrier(
+                cmd_encoder,
+                None,
+                &[ImageBarrier::layout_optimal(
                     &[Access::TransferWrite],
+                    &[Access::FragmentShaderSampledImageRead],
                     blåhaj_image,
                     ImageAspectFlags::COLOR,
-                ),
-            ],
-        );
-
-        device.cmd_copy_buffer_to_image(
-            &mut cmd_encoder,
-            blåhaj_buffer.to_arg(),
-            blåhaj_image,
-            ImageLayout::Optimal,
-            &[BufferImageCopy {
-                buffer_offset: 0,
-                buffer_row_length: 0,
-                buffer_image_height: 0,
-                image_subresource: default(),
-                image_offset: Offset3d { x: 0, y: 0, z: 0 },
-                image_extent: Extent3d {
-                    width: blåhaj_image_data.width() as u32,
-                    height: blåhaj_image_data.width() as u32,
-                    depth: 1,
-                },
-            }],
-        );
-
-        device.cmd_barrier(
-            &mut cmd_encoder,
-            None,
-            &[ImageBarrier::layout_optimal(
-                &[Access::TransferWrite],
-                &[Access::FragmentShaderSampledImageRead],
-                blåhaj_image,
-                ImageAspectFlags::COLOR,
-            )],
-        );
+                )],
+            );
+        }
 
         device.submit(&frame, cmd_encoder);
         device.end_frame(frame);
@@ -421,29 +539,6 @@ pub fn main() {
     let mut depth_height = 0;
     let mut depth_image = default();
 
-    let mut rng = Pcg64::new();
-    let shark_distance = 4.0;
-
-    let mut shark_transforms = Vec::new();
-
-    // Shark 0 is the ultimate shark of player control!
-    shark_transforms.push(Affine3 {
-        matrix: Mat3::IDENTITY,
-        translate: Vec3::ZERO,
-    });
-
-    for z in 0..NUM_SHARKS {
-        for x in 0..NUM_SHARKS {
-            let x = x as f32 * shark_distance - NUM_SHARKS as f32 / 2.0 * shark_distance;
-            let z = z as f32 * shark_distance - NUM_SHARKS as f32 / 2.0 * shark_distance;
-            shark_transforms.push(Affine3 {
-                matrix: Mat3::from_axis_rotation(Vec3::Y, HalfTurn::new(rng.next_f32() * 2.0))
-                    * Mat3::from_scale(Vec3::new(0.5, 0.5, 0.5)),
-                translate: vec3(x, 0.0, z),
-            })
-        }
-    }
-
     let mut font_size_str = String::new();
     let mut primitive_instances = Vec::new();
     let mut primitive_vertices = Vec::new();
@@ -453,391 +548,469 @@ pub fn main() {
     let mut action_queue = Vec::new();
     let mut game_state = GameState::new();
 
-    let start_time = Instant::now();
+    let mut basic_transforms = vec![];
 
     'main: loop {
         let frame = device.begin_frame();
+        {
+            let frame = &frame;
+
+            let (width, height, swapchain_image) = loop {
+                let (width, height) = main_window.extent();
+                if let Ok(result) = device.acquire_swapchain(
+                    &frame,
+                    main_window.upcast(),
+                    width,
+                    height,
+                    ImageFormat::BGRA8_SRGB,
+                ) {
+                    break result;
+                }
+            };
 
-        let (width, height, swapchain_image) = loop {
-            let (width, height) = main_window.extent();
-            if let Ok(result) = device.acquire_swapchain(
-                &frame,
-                main_window.upcast(),
-                width,
-                height,
-                ImageFormat::BGRA8_SRGB,
-            ) {
-                break result;
-            }
-        };
-
-        'poll_events: while let Some(event) = app.poll_event() {
-            use Event::*;
-            match event {
-                KeyPress {
-                    window_id: _,
-                    key,
-                    repeat,
-                    pressed,
-                    modifiers: _,
-                } => {
-                    if repeat {
-                        continue 'poll_events;
-                    }
-
-                    if key == Key::Escape {
-                        break 'main;
-                    }
-
-                    {
-                        let value = match pressed {
-                            PressedState::Released => 0.0,
-                            PressedState::Pressed => 1.0,
-                        };
-
-                        if key == Key::Left || key == Key::A {
-                            action_queue.push(ActionEvent {
-                                action: Action::Left,
-                                value,
-                            })
+            'poll_events: while let Some(event) = app.poll_event() {
+                use Event::*;
+                match event {
+                    KeyPress {
+                        window_id: _,
+                        key,
+                        repeat,
+                        pressed,
+                        modifiers: _,
+                    } => {
+                        if repeat {
+                            continue 'poll_events;
                         }
-                        if key == Key::Right || key == Key::D {
-                            action_queue.push(ActionEvent {
-                                action: Action::Right,
-                                value,
-                            })
-                        }
-                        if key == Key::Up || key == Key::W {
-                            action_queue.push(ActionEvent {
-                                action: Action::Up,
-                                value,
-                            })
-                        }
-                        if key == Key::Down || key == Key::S {
-                            action_queue.push(ActionEvent {
-                                action: Action::Down,
-                                value,
-                            })
+
+                        if key == Key::Escape {
+                            break 'main;
                         }
-                        if key == Key::Space {
-                            action_queue.push(ActionEvent {
-                                action: Action::Damage,
-                                value,
-                            })
+
+                        {
+                            let value = match pressed {
+                                PressedState::Released => 0.0,
+                                PressedState::Pressed => 1.0,
+                            };
+
+                            if key == Key::Left || key == Key::A {
+                                action_queue.push(ActionEvent {
+                                    action: Action::Left,
+                                    value,
+                                })
+                            }
+                            if key == Key::Right || key == Key::D {
+                                action_queue.push(ActionEvent {
+                                    action: Action::Right,
+                                    value,
+                                })
+                            }
+                            if key == Key::Up || key == Key::W {
+                                action_queue.push(ActionEvent {
+                                    action: Action::Up,
+                                    value,
+                                })
+                            }
+                            if key == Key::Down || key == Key::S {
+                                action_queue.push(ActionEvent {
+                                    action: Action::Down,
+                                    value,
+                                })
+                            }
+                            if key == Key::Space {
+                                action_queue.push(ActionEvent {
+                                    action: Action::Damage,
+                                    value,
+                                })
+                            }
                         }
                     }
+                    Quit => {
+                        break 'main;
+                    }
+                    Close { window_id } => {
+                        let window = app.window(window_id);
+                        device.destroy_swapchain(window.upcast());
+                    }
+                    _ => {}
                 }
-                Quit => {
-                    break 'main;
-                }
-                Close { window_id } => {
-                    let window = app.window(window_id);
-                    device.destroy_swapchain(window.upcast());
-                }
-                _ => {}
             }
-        }
-
-        game_state.tick(1.0 / 120.0, &action_queue);
-        action_queue.clear();
 
-        let mut cmd_encoder = device.request_cmd_encoder(&frame, &thread_token);
+            game_state.tick(1.0 / 120.0, &action_queue);
+            action_queue.clear();
 
-        if width != depth_width || height != depth_height {
-            device.destroy_image(&frame, depth_image);
-            depth_image = device.create_image(&ImageDesc {
-                memory_location: MemoryLocation::Device,
-                host_mapped: false,
-                usage: ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT,
-                dimension: ImageDimension::Type2d,
-                format: ImageFormat::DEPTH_F32,
-                tiling: ImageTiling::Optimal,
-                width,
-                height,
-                depth: 1,
-                layer_count: 1,
-                mip_levels: 1,
-            });
+            let half_turn_y = Mat3::from_axis_rotation(Vec3::Y, HalfTurn::new(0.5));
+            let scale = Mat3::from_scale(Vec3::splat(0.25));
 
-            device.cmd_barrier(
-                &mut cmd_encoder,
-                None,
-                &[ImageBarrier::layout_optimal(
-                    &[Access::None],
-                    &[Access::DepthStencilAttachmentWrite],
-                    depth_image,
-                    ImageAspectFlags::DEPTH,
-                )],
-            );
-
-            depth_width = width;
-            depth_height = height;
-        }
-
-        let frame_start = Instant::now() - start_time;
-        let frame_start = frame_start.as_secs_f32() * 0.125;
-
-        let orientation = {
-            let f = game_state.player.heading.normalized();
-            let r = Vec3::cross(f, Vec3::Y).normalized();
-            let u = Vec3::cross(r, f);
-            Mat3::from_rows([[r.x, u.x, -f.x], [r.y, u.y, -f.y], [r.z, u.z, -f.z]])
-        } * Mat3::from_axis_rotation(Vec3::Y, HalfTurn::new(0.5));
-
-        shark_transforms[0].matrix = orientation;
-        shark_transforms[0].translate = game_state.player.position.as_vec3();
-
-        for (i, transform) in shark_transforms.iter_mut().skip(1).enumerate() {
-            let direction = if i & 1 == 0 { 1.0 } else { -1.0 };
-            let (s, _) = sin_cos_pi_f32(frame_start + (i as f32) * 0.0125);
-            transform.translate.y = s;
-            transform.matrix *= Mat3::from_axis_rotation(Vec3::Y, HalfTurn::new(0.002 * direction))
-        }
-
-        let camera_from_model = game_state.camera.camera_from_model();
-        let clip_from_camera = Mat4::perspective_rev_inf_zo(
-            HalfTurn::new(1.0 / 3.0),
-            width as f32 / height as f32,
-            0.01,
-        );
-        let clip_from_model = clip_from_camera * camera_from_model;
-
-        // Do some Font Shit.'
-        let line0 = "Snarfe, Blåhaj! And the Quick Brown Fox jumped Over the Lazy doge.";
-        let line1 = "加盟国は、国際連合と協力して";
-
-        let mut x;
-        let mut y = 0.0;
-
-        let mut rng = Pcg64::new();
-
-        primitive_instances.clear();
-        primitive_vertices.clear();
-
-        for line in 0..2 {
-            let (font_family, font_size_px, text) = if line & 1 == 0 {
-                (FontFamily::RobotoRegular, 22.0, line0)
-            } else {
-                (FontFamily::NotoSansJapanese, 22.0, line1)
-            };
-
-            let font = fonts.font(font_family);
-            let scale = font.scale_for_size_px(font_size_px);
-
-            x = 0.0;
-            y += (font.ascent() - font.descent() + font.line_gap()) * scale;
-
-            font_size_str.clear();
-            write!(&mut font_size_str, "{font_size_px}: ").unwrap();
-
-            line_glyph_indices.clear();
-            line_glyph_indices.extend(font_size_str.chars().chain(text.chars()).map(|c| {
-                font.glyph_index(c)
-                    .unwrap_or_else(|| font.glyph_index('□').unwrap())
-            }));
-
-            line_kern_advances.clear();
-            line_kern_advances.push(0.0);
-            line_kern_advances.extend(
-                array_windows(line_glyph_indices.as_slice())
-                    .map(|&[prev_index, next_index]| font.kerning_advance(prev_index, next_index)),
-            );
-
-            'repeat_str: for _ in 0.. {
-                for (glyph_index, advance) in line_glyph_indices
-                    .iter()
-                    .copied()
-                    .zip(line_kern_advances.iter().copied())
-                {
-                    if x >= width as f32 {
-                        break 'repeat_str;
-                    }
-
-                    let touched_glyph_index =
-                        glyph_cache.touch_glyph(font_family, glyph_index, font_size_px);
-
-                    let HorizontalMetrics {
-                        advance_width,
-                        left_side_bearing: _,
-                    } = font.horizontal_metrics(glyph_index);
-
-                    x += advance * scale;
-
-                    let color =
-                        *rng.array_select(&[0xfffac228, 0xfff57d15, 0xffd44842, 0xff9f2a63]);
+            fn rotate_dir(dir: Vec3, up: Vec3) -> Mat3 {
+                let f = dir.normalized();
+                let r = Vec3::cross(f, up).normalized();
+                let u = Vec3::cross(r, f);
+                Mat3::from_rows([[r.x, u.x, -f.x], [r.y, u.y, -f.y], [r.z, u.z, -f.z]])
+            }
 
-                    let instance_index = primitive_instances.len() as u32;
-                    primitive_instances.push(PrimitiveInstance {
-                        x,
-                        y,
-                        touched_glyph_index,
-                        color,
-                    });
-                    let glyph_vertices = &[
-                        PrimitiveVertex::glyph(0, instance_index),
-                        PrimitiveVertex::glyph(1, instance_index),
-                        PrimitiveVertex::glyph(2, instance_index),
-                        PrimitiveVertex::glyph(2, instance_index),
-                        PrimitiveVertex::glyph(1, instance_index),
-                        PrimitiveVertex::glyph(3, instance_index),
-                    ];
-                    primitive_vertices.extend_from_slice(glyph_vertices);
-
-                    x += advance_width * scale;
+            let matrix = rotate_dir(game_state.player.heading, Vec3::Y) * half_turn_y;
+            let translation = game_state.player.position.as_vec3();
+            basic_transforms.push(Affine3::new(matrix, translation));
+
+            // Render projectiles
+            for base in BitIter::new(game_state.archetype_projectile_bitmap_0.iter().copied()) {
+                for i in BitIter::new(std::iter::once(
+                    game_state.archetype_projectile_bitmap_1[base],
+                )) {
+                    let i = base * 64 + i;
+                    let translation = vec3(
+                        game_state.archetype_projectile[i / 8].position_x[i % 8],
+                        0.0,
+                        game_state.archetype_projectile[i / 8].position_z[i % 8],
+                    );
+                    let velocity = vec3(
+                        game_state.archetype_projectile[i / 8].velocity_x[i % 8],
+                        0.0,
+                        game_state.archetype_projectile[i / 8].velocity_z[i % 8],
+                    );
+
+                    let matrix = rotate_dir(velocity, Vec3::Y) * half_turn_y * scale;
+                    basic_transforms.push(Affine3::new(matrix, translation))
                 }
             }
-        }
-
-        let atlas_width = glyph_cache.width() as u32;
-        let atlas_height = glyph_cache.height() as u32;
-
-        let (touched_glyphs, texture) = glyph_cache.update_atlas();
-
-        // If the atlas has been updated, we need to upload it to the GPU.
-        if let Some(texture) = texture {
-            let width = atlas_width;
-            let height = atlas_height;
-            let image = glyph_atlas;
-
-            let buffer = device.request_transient_buffer_with_data(
-                &frame,
-                &thread_token,
-                BufferUsageFlags::TRANSFER,
-                texture,
-            );
 
-            device.cmd_barrier(
-                &mut cmd_encoder,
-                None,
-                &[ImageBarrier::layout_optimal(
-                    &[Access::ShaderSampledImageRead],
-                    &[Access::TransferWrite],
-                    image,
-                    ImageAspectFlags::COLOR,
-                )],
+            let camera_from_model = game_state.camera.camera_from_model();
+            let clip_from_camera = Mat4::perspective_rev_inf_zo(
+                HalfTurn::new(1.0 / 3.0),
+                width as f32 / height as f32,
+                0.01,
             );
-
-            device.cmd_copy_buffer_to_image(
-                &mut cmd_encoder,
-                buffer.to_arg(),
-                image,
-                ImageLayout::Optimal,
-                &[BufferImageCopy {
-                    buffer_offset: 0,
-                    buffer_row_length: 0,
-                    buffer_image_height: 0,
-                    image_subresource: default(),
-                    image_offset: Offset3d { x: 0, y: 0, z: 0 },
-                    image_extent: Extent3d {
+            let clip_from_model = clip_from_camera * camera_from_model;
+
+            let mut cmd_encoder = device.request_cmd_encoder(&frame, &thread_token);
+            {
+                let cmd_encoder = &mut cmd_encoder;
+
+                if width != depth_width || height != depth_height {
+                    device.destroy_image(&frame, depth_image);
+                    depth_image = device.create_image(&ImageDesc {
+                        memory_location: MemoryLocation::Device,
+                        host_mapped: false,
+                        usage: ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT,
+                        dimension: ImageDimension::Type2d,
+                        format: ImageFormat::DEPTH_F32,
+                        tiling: ImageTiling::Optimal,
                         width,
                         height,
                         depth: 1,
-                    },
-                }],
-            );
-
-            device.cmd_barrier(
-                &mut cmd_encoder,
-                None,
-                &[ImageBarrier::layout_optimal(
-                    &[Access::TransferWrite],
-                    &[Access::FragmentShaderSampledImageRead],
-                    image,
-                    ImageAspectFlags::COLOR,
-                )],
-            );
-        }
-
-        device.cmd_begin_rendering(
-            &mut cmd_encoder,
-            &RenderingDesc {
-                x: 0,
-                y: 0,
-                width,
-                height,
-                color_attachments: &[RenderingAttachment {
-                    image: swapchain_image,
-                    load_op: LoadOp::Clear(ClearValue::ColorF32([1.0, 1.0, 1.0, 1.0])),
-                    store_op: StoreOp::Store,
-                }],
-                depth_attachment: Some(RenderingAttachment {
-                    image: depth_image,
-                    load_op: LoadOp::Clear(ClearValue::DepthStencil {
-                        depth: 0.0,
-                        stencil: 0,
-                    }),
-                    store_op: StoreOp::DontCare,
-                }),
-                stencil_attachment: None,
-            },
-        );
-
-        device.cmd_set_scissors(
-            &mut cmd_encoder,
-            &[Scissor {
-                offset: Offset2d { x: 0, y: 0 },
-                extent: Extent2d { width, height },
-            }],
-        );
-
-        device.cmd_set_viewports(
-            &mut cmd_encoder,
-            &[Viewport {
-                x: 0.0,
-                y: 0.0,
-                width: width as f32,
-                height: height as f32,
-                min_depth: 0.0,
-                max_depth: 1.0,
-            }],
-        );
-
-        // Render basic stuff.
-        basic_pipeline.bind(
-            device.as_ref(),
-            &frame,
-            &thread_token,
-            &mut cmd_encoder,
-            &BasicUniforms { clip_from_model },
-            &blåhaj_vertex_buffer,
-            &blåhaj_index_buffer,
-            shark_transforms.as_slice(),
-            blåhaj_image,
-        );
+                        layer_count: 1,
+                        mip_levels: 1,
+                    });
 
-        device.cmd_draw_indexed(
-            &mut cmd_encoder,
-            blåhaj_indices.len() as u32,
-            shark_transforms.len() as u32,
-            0,
-            0,
-            0,
-        );
+                    device.cmd_barrier(
+                        cmd_encoder,
+                        None,
+                        &[ImageBarrier::layout_optimal(
+                            &[Access::None],
+                            &[Access::DepthStencilAttachmentWrite],
+                            depth_image,
+                            ImageAspectFlags::DEPTH,
+                        )],
+                    );
+
+                    depth_width = width;
+                    depth_height = height;
+                }
 
-        // Render text stuff.
-        text_pipeline.bind(
-            device.as_ref(),
-            &frame,
-            &thread_token,
-            &mut cmd_encoder,
-            &TextUniforms {
-                screen_width: width,
-                screen_height: height,
-                atlas_width,
-                atlas_height,
-            },
-            primitive_vertices.as_slice(),
-            touched_glyphs,
-            primitive_instances.as_slice(),
-            glyph_atlas,
-        );
+                // Do some Font Shit.'
+                let line0 = "Snarfe, Blåhaj! And the Quick Brown Fox jumped Over the Lazy doge.";
+                let line1 = "加盟国は、国際連合と協力して";
+
+                let mut x;
+                let mut y = 0.0;
+
+                let mut rng = Pcg64::new();
+
+                primitive_instances.clear();
+                primitive_vertices.clear();
+
+                for line in 0..2 {
+                    let (font_family, font_size_px, text) = if line & 1 == 0 {
+                        (FontFamily::RobotoRegular, 22.0, line0)
+                    } else {
+                        (FontFamily::NotoSansJapanese, 22.0, line1)
+                    };
+
+                    let font = fonts.font(font_family);
+                    let scale = font.scale_for_size_px(font_size_px);
+
+                    x = 0.0;
+                    y += (font.ascent() - font.descent() + font.line_gap()) * scale;
+
+                    font_size_str.clear();
+                    write!(&mut font_size_str, "{font_size_px}: ").unwrap();
+
+                    line_glyph_indices.clear();
+                    line_glyph_indices.extend(font_size_str.chars().chain(text.chars()).map(|c| {
+                        font.glyph_index(c)
+                            .unwrap_or_else(|| font.glyph_index('□').unwrap())
+                    }));
+
+                    line_kern_advances.clear();
+                    line_kern_advances.push(0.0);
+                    line_kern_advances.extend(array_windows(line_glyph_indices.as_slice()).map(
+                        |&[prev_index, next_index]| font.kerning_advance(prev_index, next_index),
+                    ));
+
+                    'repeat_str: for _ in 0.. {
+                        for (glyph_index, advance) in line_glyph_indices
+                            .iter()
+                            .copied()
+                            .zip(line_kern_advances.iter().copied())
+                        {
+                            if x >= width as f32 {
+                                break 'repeat_str;
+                            }
+
+                            let touched_glyph_index =
+                                glyph_cache.touch_glyph(font_family, glyph_index, font_size_px);
+
+                            let HorizontalMetrics {
+                                advance_width,
+                                left_side_bearing: _,
+                            } = font.horizontal_metrics(glyph_index);
+
+                            x += advance * scale;
+
+                            let color = *rng
+                                .array_select(&[0xfffac228, 0xfff57d15, 0xffd44842, 0xff9f2a63]);
+
+                            let instance_index = primitive_instances.len() as u32;
+                            primitive_instances.push(PrimitiveInstance {
+                                x,
+                                y,
+                                touched_glyph_index,
+                                color,
+                            });
+                            let glyph_vertices = &[
+                                PrimitiveVertex::glyph(0, instance_index),
+                                PrimitiveVertex::glyph(1, instance_index),
+                                PrimitiveVertex::glyph(2, instance_index),
+                                PrimitiveVertex::glyph(2, instance_index),
+                                PrimitiveVertex::glyph(1, instance_index),
+                                PrimitiveVertex::glyph(3, instance_index),
+                            ];
+                            primitive_vertices.extend_from_slice(glyph_vertices);
+
+                            x += advance_width * scale;
+                        }
+                    }
+                }
 
-        device.cmd_draw(&mut cmd_encoder, primitive_vertices.len() as u32, 1, 0, 0);
+                let atlas_width = glyph_cache.width() as u32;
+                let atlas_height = glyph_cache.height() as u32;
+
+                let (touched_glyphs, texture) = glyph_cache.update_atlas();
+
+                // If the atlas has been updated, we need to upload it to the GPU.
+                if let Some(texture) = texture {
+                    let width = atlas_width;
+                    let height = atlas_height;
+                    let image = glyph_atlas;
+
+                    let buffer = device.request_transient_buffer_with_data(
+                        frame,
+                        thread_token,
+                        BufferUsageFlags::TRANSFER,
+                        texture,
+                    );
+
+                    device.cmd_barrier(
+                        cmd_encoder,
+                        None,
+                        &[ImageBarrier::layout_optimal(
+                            &[Access::ShaderSampledImageRead],
+                            &[Access::TransferWrite],
+                            image,
+                            ImageAspectFlags::COLOR,
+                        )],
+                    );
+
+                    device.cmd_copy_buffer_to_image(
+                        cmd_encoder,
+                        buffer.to_arg(),
+                        image,
+                        ImageLayout::Optimal,
+                        &[BufferImageCopy {
+                            buffer_offset: 0,
+                            buffer_row_length: 0,
+                            buffer_image_height: 0,
+                            image_subresource: default(),
+                            image_offset: Offset3d { x: 0, y: 0, z: 0 },
+                            image_extent: Extent3d {
+                                width,
+                                height,
+                                depth: 1,
+                            },
+                        }],
+                    );
+
+                    device.cmd_barrier(
+                        cmd_encoder,
+                        None,
+                        &[ImageBarrier::layout_optimal(
+                            &[Access::TransferWrite],
+                            &[Access::FragmentShaderSampledImageRead],
+                            image,
+                            ImageAspectFlags::COLOR,
+                        )],
+                    );
+                }
 
-        device.cmd_end_rendering(&mut cmd_encoder);
+                device.cmd_begin_rendering(
+                    cmd_encoder,
+                    &RenderingDesc {
+                        x: 0,
+                        y: 0,
+                        width,
+                        height,
+                        color_attachments: &[RenderingAttachment {
+                            image: swapchain_image,
+                            load_op: LoadOp::Clear(ClearValue::ColorF32([1.0, 1.0, 1.0, 1.0])),
+                            store_op: StoreOp::Store,
+                        }],
+                        depth_attachment: Some(RenderingAttachment {
+                            image: depth_image,
+                            load_op: LoadOp::Clear(ClearValue::DepthStencil {
+                                depth: 0.0,
+                                stencil: 0,
+                            }),
+                            store_op: StoreOp::DontCare,
+                        }),
+                        stencil_attachment: None,
+                    },
+                );
+
+                device.cmd_set_scissors(
+                    cmd_encoder,
+                    &[Scissor {
+                        offset: Offset2d { x: 0, y: 0 },
+                        extent: Extent2d { width, height },
+                    }],
+                );
+
+                device.cmd_set_viewports(
+                    cmd_encoder,
+                    &[Viewport {
+                        x: 0.0,
+                        y: 0.0,
+                        width: width as f32,
+                        height: height as f32,
+                        min_depth: 0.0,
+                        max_depth: 1.0,
+                    }],
+                );
+
+                // Render basic stuff.
+                {
+                    device.cmd_set_pipeline(cmd_encoder, basic_pipeline.pipeline);
+
+                    let basic_uniforms = BasicUniforms { clip_from_model };
+
+                    let uniform_buffer = device.request_transient_buffer_with_data(
+                        frame,
+                        thread_token,
+                        BufferUsageFlags::UNIFORM,
+                        &basic_uniforms,
+                    );
+
+                    let transform_buffer = device.request_transient_buffer_with_data(
+                        frame,
+                        thread_token,
+                        BufferUsageFlags::STORAGE,
+                        basic_transforms.as_slice(),
+                    );
+
+                    device.cmd_set_bind_group(
+                        frame,
+                        cmd_encoder,
+                        basic_pipeline.uniforms_bind_group_layout,
+                        0,
+                        &[Bind {
+                            binding: 0,
+                            array_element: 0,
+                            typed: TypedBind::UniformBuffer(&[uniform_buffer.to_arg()]),
+                        }],
+                    );
+
+                    device.cmd_set_bind_group(
+                        frame,
+                        cmd_encoder,
+                        basic_pipeline.storage_bind_group_layout,
+                        1,
+                        &[
+                            Bind {
+                                binding: 0,
+                                array_element: 0,
+                                typed: TypedBind::StorageBuffer(&[blåhaj_vertex_buffer.to_arg()]),
+                            },
+                            Bind {
+                                binding: 1,
+                                array_element: 0,
+                                typed: TypedBind::StorageBuffer(&[transform_buffer.to_arg()]),
+                            },
+                            Bind {
+                                binding: 2,
+                                array_element: 0,
+                                typed: TypedBind::Sampler(&[basic_pipeline.sampler]),
+                            },
+                            Bind {
+                                binding: 3,
+                                array_element: 0,
+                                typed: TypedBind::Image(&[(ImageLayout::Optimal, blåhaj_image)]),
+                            },
+                        ],
+                    );
+
+                    device.cmd_set_index_buffer(
+                        cmd_encoder,
+                        blåhaj_index_buffer.to_arg(),
+                        0,
+                        IndexType::U16,
+                    );
+
+                    device.cmd_draw_indexed(
+                        cmd_encoder,
+                        blåhaj_indices.len() as u32,
+                        basic_transforms.len() as u32,
+                        0,
+                        0,
+                        0,
+                    );
+
+                    // We're done with you now!
+                    basic_transforms.clear();
+                };
+
+                // Render text stuff.
+                text_pipeline.bind(
+                    device.as_ref(),
+                    frame,
+                    thread_token,
+                    cmd_encoder,
+                    &TextUniforms {
+                        screen_width: width,
+                        screen_height: height,
+                        atlas_width,
+                        atlas_height,
+                    },
+                    primitive_vertices.as_slice(),
+                    touched_glyphs,
+                    primitive_instances.as_slice(),
+                    glyph_atlas,
+                );
 
-        device.submit(&frame, cmd_encoder);
+                device.cmd_draw(cmd_encoder, primitive_vertices.len() as u32, 1, 0, 0);
 
+                device.cmd_end_rendering(cmd_encoder);
+            }
+            device.submit(&frame, cmd_encoder);
+        }
         device.end_frame(frame);
     }
 }
index 636fac913db2522643c33a45b2b73860765d8a61..2460777d9a307ea49d1c187ae72001ca478e6d43 100644 (file)
@@ -1,12 +1,11 @@
 use narcissus_core::default;
 use narcissus_gpu::{
-    Bind, BindGroupLayout, BindGroupLayoutDesc, BindGroupLayoutEntryDesc, BindingType, BlendMode,
-    BufferUsageFlags, CmdEncoder, CompareOp, CullingMode, Device, DeviceExt, Frame, FrontFace,
-    GraphicsPipelineDesc, GraphicsPipelineLayout, Image, ImageFormat, ImageLayout, IndexType,
-    PersistentBuffer, Pipeline, PolygonMode, Sampler, SamplerAddressMode, SamplerDesc,
-    SamplerFilter, ShaderDesc, ShaderStageFlags, ThreadToken, Topology, TypedBind,
+    BindGroupLayout, BindGroupLayoutDesc, BindGroupLayoutEntryDesc, BindingType, BlendMode,
+    CompareOp, CullingMode, Device, FrontFace, GraphicsPipelineDesc, GraphicsPipelineLayout,
+    ImageFormat, Pipeline, PolygonMode, Sampler, SamplerAddressMode, SamplerDesc, SamplerFilter,
+    ShaderDesc, ShaderStageFlags, Topology,
 };
-use narcissus_maths::{Affine3, Mat4};
+use narcissus_maths::Mat4;
 
 #[allow(unused)]
 #[repr(C)]
@@ -115,76 +114,4 @@ impl BasicPipeline {
             pipeline,
         }
     }
-
-    pub fn bind(
-        &self,
-        device: &(dyn Device + 'static),
-        frame: &Frame,
-        thread_token: &ThreadToken,
-        cmd_encoder: &mut CmdEncoder,
-        basic_uniforms: &BasicUniforms,
-        vertex_buffer: &PersistentBuffer,
-        index_buffer: &PersistentBuffer,
-        transforms: &[Affine3],
-        texture: Image,
-    ) {
-        let uniform_buffer = device.request_transient_buffer_with_data(
-            frame,
-            thread_token,
-            BufferUsageFlags::UNIFORM,
-            basic_uniforms,
-        );
-
-        let transform_buffer = device.request_transient_buffer_with_data(
-            frame,
-            thread_token,
-            BufferUsageFlags::STORAGE,
-            transforms,
-        );
-
-        device.cmd_set_pipeline(cmd_encoder, self.pipeline);
-
-        device.cmd_set_bind_group(
-            frame,
-            cmd_encoder,
-            self.uniforms_bind_group_layout,
-            0,
-            &[Bind {
-                binding: 0,
-                array_element: 0,
-                typed: TypedBind::UniformBuffer(&[uniform_buffer.to_arg()]),
-            }],
-        );
-
-        device.cmd_set_bind_group(
-            frame,
-            cmd_encoder,
-            self.storage_bind_group_layout,
-            1,
-            &[
-                Bind {
-                    binding: 0,
-                    array_element: 0,
-                    typed: TypedBind::StorageBuffer(&[vertex_buffer.to_arg()]),
-                },
-                Bind {
-                    binding: 1,
-                    array_element: 0,
-                    typed: TypedBind::StorageBuffer(&[transform_buffer.to_arg()]),
-                },
-                Bind {
-                    binding: 2,
-                    array_element: 0,
-                    typed: TypedBind::Sampler(&[self.sampler]),
-                },
-                Bind {
-                    binding: 3,
-                    array_element: 0,
-                    typed: TypedBind::Image(&[(ImageLayout::Optimal, texture)]),
-                },
-            ],
-        );
-
-        device.cmd_set_index_buffer(cmd_encoder, index_buffer.to_arg(), 0, IndexType::U16);
-    }
 }