pub struct SpriteSheet { /* private fields */ }Expand description
A sprite sheet containing uniformly-sized sprites in a grid layout.
All sprites in a sprite sheet have the same dimensions and are arranged in rows and columns. This makes it efficient for animations where each frame is the same size.
§Example
ⓘ
// Create a sprite sheet from a texture
let sprite_sheet = SpriteSheet::new(
context,
texture,
SpriteSheetDescriptor {
sprite_width: 32,
sprite_height: 32,
columns: 8,
rows: 4,
..Default::default()
},
);
// Get UV coordinates for sprite at index 5
let uv = sprite_sheet.sprite_uv(5);
// Or by row/column
let uv = sprite_sheet.sprite_uv_at(1, 2);Implementations§
Source§impl SpriteSheet
impl SpriteSheet
Sourcepub fn new(
texture: Texture,
view: TextureView,
texture_width: u32,
texture_height: u32,
descriptor: SpriteSheetDescriptor,
) -> Self
pub fn new( texture: Texture, view: TextureView, texture_width: u32, texture_height: u32, descriptor: SpriteSheetDescriptor, ) -> Self
Create a new sprite sheet from an existing texture.
Sourcepub fn from_data(
context: &GraphicsContext,
data: &[u8],
texture_width: u32,
texture_height: u32,
descriptor: SpriteSheetDescriptor,
) -> Self
pub fn from_data( context: &GraphicsContext, data: &[u8], texture_width: u32, texture_height: u32, descriptor: SpriteSheetDescriptor, ) -> Self
Create a sprite sheet from raw pixel data.
§Arguments
context- Graphics contextdata- Raw RGBA pixel datatexture_width- Width of the texture in pixelstexture_height- Height of the texture in pixelsdescriptor- Sprite sheet configuration
Examples found in repository?
examples/sprite_sheet.rs (lines 180-192)
152fn main() {
153 logging::init();
154
155 run_app(|ctx| {
156 let graphics_ctx =
157 GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
158 let mut windows = HashMap::new();
159
160 let scale = Window::platform_dpi() as f32;
161 let window = ctx
162 .create_window(WindowDescriptor {
163 title: "Sprite Sheet Animation Example".to_string(),
164 size: Some(WinitPhysicalSize::new(400.0 * scale, 400.0 * scale)),
165 ..Default::default()
166 })
167 .expect("Failed to create window");
168
169 let renderable_window = RenderWindowBuilder::new()
170 .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
171 .with_depth_default()
172 .build(window, graphics_ctx.clone())
173 .expect("Failed to create render window");
174
175 let window_id = renderable_window.id();
176 windows.insert(window_id, renderable_window);
177
178 // Generate sprite sheet
179 let (sprite_data, tex_width, tex_height) = generate_sprite_sheet_data();
180 let sprite_sheet = SpriteSheet::from_data(
181 &graphics_ctx,
182 &sprite_data,
183 tex_width,
184 tex_height,
185 SpriteSheetDescriptor {
186 sprite_width: 64,
187 sprite_height: 64,
188 columns: 4,
189 rows: 1,
190 ..Default::default()
191 },
192 );
193
194 // Create animation (4 frames at 8 fps)
195 let animation = SpriteAnimation::new(4, 8.0);
196
197 // Create shader module
198 let shader = graphics_ctx
199 .device()
200 .create_shader_module(wgpu::ShaderModuleDescriptor {
201 label: Some("Sprite Shader"),
202 source: wgpu::ShaderSource::Wgsl(SHADER.into()),
203 });
204
205 // Create bind group layout
206 let bind_group_layout =
207 graphics_ctx
208 .device()
209 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
210 label: Some("Sprite Bind Group Layout"),
211 entries: &[
212 wgpu::BindGroupLayoutEntry {
213 binding: 0,
214 visibility: wgpu::ShaderStages::VERTEX,
215 ty: wgpu::BindingType::Buffer {
216 ty: wgpu::BufferBindingType::Uniform,
217 has_dynamic_offset: false,
218 min_binding_size: None,
219 },
220 count: None,
221 },
222 wgpu::BindGroupLayoutEntry {
223 binding: 1,
224 visibility: wgpu::ShaderStages::FRAGMENT,
225 ty: wgpu::BindingType::Texture {
226 sample_type: wgpu::TextureSampleType::Float { filterable: true },
227 view_dimension: wgpu::TextureViewDimension::D2,
228 multisampled: false,
229 },
230 count: None,
231 },
232 wgpu::BindGroupLayoutEntry {
233 binding: 2,
234 visibility: wgpu::ShaderStages::FRAGMENT,
235 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
236 count: None,
237 },
238 ],
239 });
240
241 // Create pipeline layout
242 let pipeline_layout =
243 graphics_ctx
244 .device()
245 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
246 label: Some("Sprite Pipeline Layout"),
247 bind_group_layouts: &[&bind_group_layout],
248 push_constant_ranges: &[],
249 });
250
251 // Create render pipeline
252 let pipeline =
253 graphics_ctx
254 .device()
255 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
256 label: Some("Sprite Pipeline"),
257 layout: Some(&pipeline_layout),
258 vertex: wgpu::VertexState {
259 module: &shader,
260 entry_point: Some("vs_main"),
261 buffers: &[wgpu::VertexBufferLayout {
262 array_stride: std::mem::size_of::<Vertex>() as u64,
263 step_mode: wgpu::VertexStepMode::Vertex,
264 attributes: &[
265 wgpu::VertexAttribute {
266 offset: 0,
267 shader_location: 0,
268 format: wgpu::VertexFormat::Float32x2,
269 },
270 wgpu::VertexAttribute {
271 offset: 8,
272 shader_location: 1,
273 format: wgpu::VertexFormat::Float32x2,
274 },
275 ],
276 }],
277 compilation_options: wgpu::PipelineCompilationOptions::default(),
278 },
279 fragment: Some(wgpu::FragmentState {
280 module: &shader,
281 entry_point: Some("fs_main"),
282 targets: &[Some(wgpu::ColorTargetState {
283 format: wgpu::TextureFormat::Bgra8UnormSrgb,
284 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
285 write_mask: wgpu::ColorWrites::ALL,
286 })],
287 compilation_options: wgpu::PipelineCompilationOptions::default(),
288 }),
289 primitive: wgpu::PrimitiveState {
290 topology: wgpu::PrimitiveTopology::TriangleList,
291 ..Default::default()
292 },
293 depth_stencil: None,
294 multisample: wgpu::MultisampleState::default(),
295 multiview: None,
296 cache: None,
297 });
298
299 // Create uniform buffer
300 let uniforms = Uniforms {
301 mvp: [
302 [1.0, 0.0, 0.0, 0.0],
303 [0.0, 1.0, 0.0, 0.0],
304 [0.0, 0.0, 1.0, 0.0],
305 [0.0, 0.0, 0.0, 1.0],
306 ],
307 };
308 let uniform_buffer =
309 graphics_ctx
310 .device()
311 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
312 label: Some("Uniform Buffer"),
313 contents: bytemuck::cast_slice(&[uniforms]),
314 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
315 });
316
317 // Create sampler
318 let sampler = graphics_ctx
319 .device()
320 .create_sampler(&wgpu::SamplerDescriptor {
321 label: Some("Sprite Sampler"),
322 mag_filter: wgpu::FilterMode::Linear,
323 min_filter: wgpu::FilterMode::Linear,
324 ..Default::default()
325 });
326
327 // Create bind group
328 let bind_group = graphics_ctx
329 .device()
330 .create_bind_group(&wgpu::BindGroupDescriptor {
331 label: Some("Sprite Bind Group"),
332 layout: &bind_group_layout,
333 entries: &[
334 wgpu::BindGroupEntry {
335 binding: 0,
336 resource: uniform_buffer.as_entire_binding(),
337 },
338 wgpu::BindGroupEntry {
339 binding: 1,
340 resource: wgpu::BindingResource::TextureView(sprite_sheet.view()),
341 },
342 wgpu::BindGroupEntry {
343 binding: 2,
344 resource: wgpu::BindingResource::Sampler(&sampler),
345 },
346 ],
347 });
348
349 // Initial vertex buffer (will be updated each frame with new UVs)
350 let vertices = create_quad_vertices(0.0, 0.0, 1.0, 1.0);
351 let vertex_buffer =
352 graphics_ctx
353 .device()
354 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
355 label: Some("Vertex Buffer"),
356 contents: bytemuck::cast_slice(&vertices),
357 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
358 });
359
360 Box::new(App {
361 _context: graphics_ctx,
362 windows,
363 pipeline,
364 bind_group,
365 vertex_buffer,
366 _uniform_buffer: uniform_buffer,
367 sprite_sheet,
368 animation,
369 last_update: Instant::now(),
370 })
371 });
372}Sourcepub fn sprite_uv(&self, index: u32) -> SpriteUV
pub fn sprite_uv(&self, index: u32) -> SpriteUV
Get UV coordinates for a sprite by linear index.
Sprites are indexed left-to-right, top-to-bottom, starting from 0.
Examples found in repository?
examples/sprite_sheet.rs (line 417)
404 fn update(
405 &mut self,
406 _ctx: &mut astrelis_winit::app::AppCtx,
407 _time: &astrelis_winit::FrameTime,
408 ) {
409 let now = Instant::now();
410 let dt = now.duration_since(self.last_update).as_secs_f32();
411 self.last_update = now;
412
413 // Update animation
414 if self.animation.update(dt) {
415 // Frame changed - update vertex buffer with new UVs
416 let frame = self.animation.current_frame();
417 let uv = self.sprite_sheet.sprite_uv(frame);
418 let vertices = create_quad_vertices(uv.u_min, uv.v_min, uv.u_max, uv.v_max);
419
420 // Get context from first window
421 if let Some(window) = self.windows.values().next() {
422 window.context().graphics_context().queue().write_buffer(
423 &self.vertex_buffer,
424 0,
425 bytemuck::cast_slice(&vertices),
426 );
427 }
428 }
429 }Sourcepub fn sprite_uv_at(&self, row: u32, col: u32) -> SpriteUV
pub fn sprite_uv_at(&self, row: u32, col: u32) -> SpriteUV
Get UV coordinates for a sprite by row and column.
Sourcepub fn sprite_count(&self) -> u32
pub fn sprite_count(&self) -> u32
Get the total number of sprites in the sheet.
Sourcepub fn sprite_size(&self) -> (u32, u32)
pub fn sprite_size(&self) -> (u32, u32)
Get the sprite dimensions in pixels.
Sourcepub fn view(&self) -> &TextureView
pub fn view(&self) -> &TextureView
Get the texture view for binding.
Examples found in repository?
examples/sprite_sheet.rs (line 340)
152fn main() {
153 logging::init();
154
155 run_app(|ctx| {
156 let graphics_ctx =
157 GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
158 let mut windows = HashMap::new();
159
160 let scale = Window::platform_dpi() as f32;
161 let window = ctx
162 .create_window(WindowDescriptor {
163 title: "Sprite Sheet Animation Example".to_string(),
164 size: Some(WinitPhysicalSize::new(400.0 * scale, 400.0 * scale)),
165 ..Default::default()
166 })
167 .expect("Failed to create window");
168
169 let renderable_window = RenderWindowBuilder::new()
170 .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
171 .with_depth_default()
172 .build(window, graphics_ctx.clone())
173 .expect("Failed to create render window");
174
175 let window_id = renderable_window.id();
176 windows.insert(window_id, renderable_window);
177
178 // Generate sprite sheet
179 let (sprite_data, tex_width, tex_height) = generate_sprite_sheet_data();
180 let sprite_sheet = SpriteSheet::from_data(
181 &graphics_ctx,
182 &sprite_data,
183 tex_width,
184 tex_height,
185 SpriteSheetDescriptor {
186 sprite_width: 64,
187 sprite_height: 64,
188 columns: 4,
189 rows: 1,
190 ..Default::default()
191 },
192 );
193
194 // Create animation (4 frames at 8 fps)
195 let animation = SpriteAnimation::new(4, 8.0);
196
197 // Create shader module
198 let shader = graphics_ctx
199 .device()
200 .create_shader_module(wgpu::ShaderModuleDescriptor {
201 label: Some("Sprite Shader"),
202 source: wgpu::ShaderSource::Wgsl(SHADER.into()),
203 });
204
205 // Create bind group layout
206 let bind_group_layout =
207 graphics_ctx
208 .device()
209 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
210 label: Some("Sprite Bind Group Layout"),
211 entries: &[
212 wgpu::BindGroupLayoutEntry {
213 binding: 0,
214 visibility: wgpu::ShaderStages::VERTEX,
215 ty: wgpu::BindingType::Buffer {
216 ty: wgpu::BufferBindingType::Uniform,
217 has_dynamic_offset: false,
218 min_binding_size: None,
219 },
220 count: None,
221 },
222 wgpu::BindGroupLayoutEntry {
223 binding: 1,
224 visibility: wgpu::ShaderStages::FRAGMENT,
225 ty: wgpu::BindingType::Texture {
226 sample_type: wgpu::TextureSampleType::Float { filterable: true },
227 view_dimension: wgpu::TextureViewDimension::D2,
228 multisampled: false,
229 },
230 count: None,
231 },
232 wgpu::BindGroupLayoutEntry {
233 binding: 2,
234 visibility: wgpu::ShaderStages::FRAGMENT,
235 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
236 count: None,
237 },
238 ],
239 });
240
241 // Create pipeline layout
242 let pipeline_layout =
243 graphics_ctx
244 .device()
245 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
246 label: Some("Sprite Pipeline Layout"),
247 bind_group_layouts: &[&bind_group_layout],
248 push_constant_ranges: &[],
249 });
250
251 // Create render pipeline
252 let pipeline =
253 graphics_ctx
254 .device()
255 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
256 label: Some("Sprite Pipeline"),
257 layout: Some(&pipeline_layout),
258 vertex: wgpu::VertexState {
259 module: &shader,
260 entry_point: Some("vs_main"),
261 buffers: &[wgpu::VertexBufferLayout {
262 array_stride: std::mem::size_of::<Vertex>() as u64,
263 step_mode: wgpu::VertexStepMode::Vertex,
264 attributes: &[
265 wgpu::VertexAttribute {
266 offset: 0,
267 shader_location: 0,
268 format: wgpu::VertexFormat::Float32x2,
269 },
270 wgpu::VertexAttribute {
271 offset: 8,
272 shader_location: 1,
273 format: wgpu::VertexFormat::Float32x2,
274 },
275 ],
276 }],
277 compilation_options: wgpu::PipelineCompilationOptions::default(),
278 },
279 fragment: Some(wgpu::FragmentState {
280 module: &shader,
281 entry_point: Some("fs_main"),
282 targets: &[Some(wgpu::ColorTargetState {
283 format: wgpu::TextureFormat::Bgra8UnormSrgb,
284 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
285 write_mask: wgpu::ColorWrites::ALL,
286 })],
287 compilation_options: wgpu::PipelineCompilationOptions::default(),
288 }),
289 primitive: wgpu::PrimitiveState {
290 topology: wgpu::PrimitiveTopology::TriangleList,
291 ..Default::default()
292 },
293 depth_stencil: None,
294 multisample: wgpu::MultisampleState::default(),
295 multiview: None,
296 cache: None,
297 });
298
299 // Create uniform buffer
300 let uniforms = Uniforms {
301 mvp: [
302 [1.0, 0.0, 0.0, 0.0],
303 [0.0, 1.0, 0.0, 0.0],
304 [0.0, 0.0, 1.0, 0.0],
305 [0.0, 0.0, 0.0, 1.0],
306 ],
307 };
308 let uniform_buffer =
309 graphics_ctx
310 .device()
311 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
312 label: Some("Uniform Buffer"),
313 contents: bytemuck::cast_slice(&[uniforms]),
314 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
315 });
316
317 // Create sampler
318 let sampler = graphics_ctx
319 .device()
320 .create_sampler(&wgpu::SamplerDescriptor {
321 label: Some("Sprite Sampler"),
322 mag_filter: wgpu::FilterMode::Linear,
323 min_filter: wgpu::FilterMode::Linear,
324 ..Default::default()
325 });
326
327 // Create bind group
328 let bind_group = graphics_ctx
329 .device()
330 .create_bind_group(&wgpu::BindGroupDescriptor {
331 label: Some("Sprite Bind Group"),
332 layout: &bind_group_layout,
333 entries: &[
334 wgpu::BindGroupEntry {
335 binding: 0,
336 resource: uniform_buffer.as_entire_binding(),
337 },
338 wgpu::BindGroupEntry {
339 binding: 1,
340 resource: wgpu::BindingResource::TextureView(sprite_sheet.view()),
341 },
342 wgpu::BindGroupEntry {
343 binding: 2,
344 resource: wgpu::BindingResource::Sampler(&sampler),
345 },
346 ],
347 });
348
349 // Initial vertex buffer (will be updated each frame with new UVs)
350 let vertices = create_quad_vertices(0.0, 0.0, 1.0, 1.0);
351 let vertex_buffer =
352 graphics_ctx
353 .device()
354 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
355 label: Some("Vertex Buffer"),
356 contents: bytemuck::cast_slice(&vertices),
357 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
358 });
359
360 Box::new(App {
361 _context: graphics_ctx,
362 windows,
363 pipeline,
364 bind_group,
365 vertex_buffer,
366 _uniform_buffer: uniform_buffer,
367 sprite_sheet,
368 animation,
369 last_update: Instant::now(),
370 })
371 });
372}Sourcepub fn texture_size(&self) -> (u32, u32)
pub fn texture_size(&self) -> (u32, u32)
Get texture dimensions.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for SpriteSheet
impl !RefUnwindSafe for SpriteSheet
impl Send for SpriteSheet
impl Sync for SpriteSheet
impl Unpin for SpriteSheet
impl UnsafeUnpin for SpriteSheet
impl !UnwindSafe for SpriteSheet
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Convert
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Convert
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more