1use crate::context::WgpuClump;
5use crate::engine_handle::Engine;
6use crate::resource::{self, InProgressResource, LoadingOp, ResourceId, ResourceType};
7use crate::vectors::Vec2;
8use crate::{layouts, ERROR_TEXTURE_DATA};
9use image::{GenericImageView, ImageError};
10use std::fmt::Display;
11use std::io::Error;
12use std::path::Path;
13
14pub struct Texture {
17 pub(crate) _view: wgpu::TextureView,
18 pub(crate) bind_group: wgpu::BindGroup,
19 pub(crate) size: Vec2<f32>,
20}
21
22impl Texture {
23 pub fn new<P>(engine: &mut Engine, path: P, loading_op: LoadingOp) -> ResourceId<Texture>
25 where
26 P: AsRef<Path>,
27 {
28 let typed_id = resource::generate_id::<Texture>();
29 let id = typed_id.get_id();
30 let path = path.as_ref();
31 let ip_resource = InProgressResource::new(
32 path,
33 id,
34 ResourceType::Image(
35 SamplerType::LinearInterpolation,
36 SamplerType::NearestNeighbor,
37 ),
38 loading_op,
39 );
40
41 engine.loader.load(ip_resource, engine.get_proxy());
42
43 typed_id
44 }
45
46 pub fn new_with_sampler<P>(
48 engine: &mut Engine,
49 path: P,
50 sampler: SamplerType,
51 loading_op: LoadingOp,
52 ) -> ResourceId<Texture>
53 where
54 P: AsRef<Path>,
55 {
56 let typed_id = resource::generate_id::<Texture>();
57 let id = typed_id.get_id();
58 let path = path.as_ref();
59 let ip_resource =
60 InProgressResource::new(path, id, ResourceType::Image(sampler, sampler), loading_op);
61
62 engine.loader.blocking_load(ip_resource, engine.get_proxy());
63
64 typed_id
65 }
66
67 pub fn new_with_mag_min_sampler<P>(
71 engine: &mut Engine,
72 path: P,
73 mag_sampler: SamplerType,
74 min_sampler: SamplerType,
75 loading_op: LoadingOp,
76 ) -> ResourceId<Texture>
77 where
78 P: AsRef<Path>,
79 {
80 let typed_id = resource::generate_id::<Texture>();
81 let id = typed_id.get_id();
82 let path = path.as_ref();
83 let ip_resource = InProgressResource::new(
84 path,
85 id,
86 ResourceType::Image(mag_sampler, min_sampler),
87 loading_op,
88 );
89
90 engine.loader.blocking_load(ip_resource, engine.get_proxy());
91
92 typed_id
93 }
94
95 pub(crate) fn from_resource_data(
96 engine: &Engine,
97 label: Option<&str>,
98 data: Vec<u8>,
99 mag_sampler: SamplerType,
100 min_sampler: SamplerType,
101 ) -> Result<Self, TextureError> {
102 let img = image::load_from_memory(&data)?;
103 Ok(Self::from_image(
104 engine,
105 img,
106 label,
107 mag_sampler,
108 min_sampler,
109 ))
110 }
111
112 pub(crate) fn new_direct(
113 view: wgpu::TextureView,
114 bind_group: wgpu::BindGroup,
115 size: Vec2<f32>,
116 ) -> Self {
117 Self {
118 _view: view,
119 bind_group,
120 size,
121 }
122 }
123
124 pub(crate) fn default(engine: &Engine) -> Self {
125 let image = image::load_from_memory(ERROR_TEXTURE_DATA).unwrap();
126 Self::from_image(
127 engine,
128 image,
129 Some("Error Texture"),
130 SamplerType::LinearInterpolation,
131 SamplerType::NearestNeighbor,
132 )
133 }
134
135 fn from_image(
136 engine: &Engine,
137 img: image::DynamicImage,
138 label: Option<&str>,
139 mag_filter: SamplerType,
140 min_filter: SamplerType,
141 ) -> Self {
142 let wgpu = &engine.context.as_ref().expect("need graphic context").wgpu;
143 let diffuse_rgba = img.to_rgba8();
144 let (width, height) = img.dimensions();
145
146 let texture_size = wgpu::Extent3d {
147 width,
148 height,
149 depth_or_array_layers: 1,
150 };
151
152 let texture = wgpu.device.create_texture(&wgpu::TextureDescriptor {
153 size: texture_size,
154 mip_level_count: 1,
155 sample_count: 1,
156 dimension: wgpu::TextureDimension::D2,
157 format: wgpu::TextureFormat::Rgba8UnormSrgb,
158 view_formats: &[],
159 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
162 label,
163 });
164
165 wgpu.queue.write_texture(
166 wgpu::ImageCopyTextureBase {
167 texture: &texture,
168 mip_level: 0,
169 origin: wgpu::Origin3d::ZERO,
170 aspect: wgpu::TextureAspect::All,
171 },
172 &diffuse_rgba,
173 wgpu::ImageDataLayout {
174 offset: 0,
175 bytes_per_row: Some(4 * width),
176 rows_per_image: Some(height),
177 },
178 texture_size,
179 );
180
181 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
182 let bind_group_layout = layouts::create_texture_layout(&wgpu.device);
183
184 let texture_sampler = wgpu.device.create_sampler(&wgpu::SamplerDescriptor {
185 address_mode_u: wgpu::AddressMode::Repeat,
187 address_mode_v: wgpu::AddressMode::Repeat,
188 address_mode_w: wgpu::AddressMode::ClampToEdge,
189 mag_filter: mag_filter.into(),
192 min_filter: min_filter.into(),
193 mipmap_filter: wgpu::FilterMode::Nearest,
194 ..Default::default()
195 });
196
197 let bind_group = wgpu.device.create_bind_group(&wgpu::BindGroupDescriptor {
198 layout: &bind_group_layout,
199 entries: &[
200 wgpu::BindGroupEntry {
201 binding: 0,
202 resource: wgpu::BindingResource::TextureView(&view),
203 },
204 wgpu::BindGroupEntry {
205 binding: 1,
206 resource: wgpu::BindingResource::Sampler(&texture_sampler),
207 },
208 ],
209 label: Some("diffuse_bind_group"),
210 });
211
212 let size = Vec2 {
213 x: width as f32,
214 y: height as f32,
215 };
216
217 Self {
218 _view: view,
219 bind_group,
220 size,
221 }
222 }
223}
224
225#[derive(Debug)]
228pub(crate) enum TextureError {
229 IoError(Error),
230 ImageError(ImageError),
231}
232
233impl From<Error> for TextureError {
234 fn from(value: Error) -> TextureError {
235 Self::IoError(value)
236 }
237}
238
239impl From<ImageError> for TextureError {
240 fn from(value: ImageError) -> Self {
241 Self::ImageError(value)
242 }
243}
244
245pub struct UniformTexture {
249 inner_texture: Option<InnerTexture>,
250 size: Vec2<u32>,
251 mag_sampler: SamplerType,
252 min_sampler: SamplerType,
253 needs_update: bool,
256}
257
258impl UniformTexture {
259 pub fn new(engine: &Engine, size: Vec2<u32>) -> Self {
263 let inner_texture = engine.context.as_ref().map(|c| {
264 InnerTexture::from_wgpu(
265 size,
266 SamplerType::LinearInterpolation,
267 SamplerType::NearestNeighbor,
268 c.get_texture_format(),
269 &c.wgpu,
270 )
271 });
272
273 Self {
274 inner_texture,
275 size,
276 mag_sampler: SamplerType::LinearInterpolation,
277 min_sampler: SamplerType::NearestNeighbor,
278 needs_update: true,
279 }
280 }
281
282 pub fn new_with_sampler(
286 engine: &Engine,
287 size: Vec2<u32>,
288 mag_sampler: SamplerType,
289 min_sampler: SamplerType,
290 ) -> Self {
291 let inner_texture = engine.context.as_ref().map(|c| {
292 InnerTexture::from_wgpu(
293 size,
294 mag_sampler,
295 min_sampler,
296 c.get_texture_format(),
297 &c.wgpu,
298 )
299 });
300
301 Self {
302 inner_texture,
303 size,
304 mag_sampler,
305 min_sampler,
306 needs_update: true,
307 }
308 }
309
310 pub(crate) fn resize(
311 &mut self,
312 new_size: Vec2<u32>,
313 wgpu: &WgpuClump,
314 format: wgpu::TextureFormat,
315 ) {
316 if self.inner_texture.is_none() {
317 self.inner_texture = Some(InnerTexture::from_wgpu(
318 new_size,
319 self.mag_sampler,
320 self.min_sampler,
321 format,
322 wgpu,
323 ));
324 self.size = new_size;
325 return;
326 }
327
328 self.inner_texture
329 .as_mut()
330 .unwrap()
331 .resize(new_size, wgpu, format);
332 self.needs_update = true;
333 }
334
335 pub fn get_size(&self) -> Vec2<u32> {
337 self.size
338 }
339
340 pub(crate) fn get_sampler(&self) -> &wgpu::Sampler {
341 &self.inner_texture.as_ref().unwrap().sampler
342 }
343
344 pub(crate) fn get_sampler_info(&self) -> (SamplerType, SamplerType) {
345 (self.mag_sampler, self.min_sampler)
346 }
347
348 pub(crate) fn make_render_view<'a>(
349 &'a mut self,
350 wgpu: &WgpuClump,
351 format: wgpu::TextureFormat,
352 ) -> &'a wgpu::TextureView {
353 if self.inner_texture.is_none() {
354 self.inner_texture = Some(InnerTexture::from_wgpu(
355 self.size,
356 self.mag_sampler,
357 self.min_sampler,
358 format,
359 wgpu,
360 ));
361 }
362
363 self.inner_texture.as_mut().unwrap().make_render_view()
364 }
365
366 pub(crate) fn make_view(
367 &mut self,
368 wgpu: &WgpuClump,
369 format: wgpu::TextureFormat,
370 ) -> wgpu::TextureView {
371 if self.inner_texture.is_none() {
372 self.inner_texture = Some(InnerTexture::from_wgpu(
373 self.size,
374 self.mag_sampler,
375 self.min_sampler,
376 format,
377 wgpu,
378 ));
379 }
380
381 self.inner_texture.as_ref().unwrap().make_view()
382 }
383
384 pub(crate) fn updated(&mut self) {
385 self.needs_update = false;
386 }
387
388 pub(crate) fn needs_update(&self) -> bool {
389 self.needs_update
390 }
391}
392
393struct InnerTexture {
394 inner_texture: wgpu::Texture,
395 view: wgpu::TextureView,
396 sampler: wgpu::Sampler,
397}
398
399impl InnerTexture {
400 fn from_wgpu(
401 size: Vec2<u32>,
402 mag_sampler: SamplerType,
403 min_sampler: SamplerType,
404 format: wgpu::TextureFormat,
405 wgpu: &WgpuClump,
406 ) -> Self {
407 let sampler = wgpu.device.create_sampler(&wgpu::SamplerDescriptor {
408 label: Some("Uniform Texture Sampler"),
409 address_mode_u: wgpu::AddressMode::Repeat,
410 address_mode_v: wgpu::AddressMode::Repeat,
411 address_mode_w: wgpu::AddressMode::ClampToEdge,
412 mag_filter: mag_sampler.into(),
413 min_filter: min_sampler.into(),
414 mipmap_filter: wgpu::FilterMode::Nearest,
415 ..Default::default()
416 });
417
418 let inner_texture = wgpu.device.create_texture(&wgpu::TextureDescriptor {
419 label: Some("Uniform Texture"),
420 size: wgpu::Extent3d {
421 width: size.x,
422 height: size.y,
423 depth_or_array_layers: 1,
424 },
425 dimension: wgpu::TextureDimension::D2,
426 mip_level_count: 1,
427 sample_count: 1,
428 format,
429 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
430 view_formats: &[],
431 });
432
433 let view = inner_texture.create_view(&wgpu::TextureViewDescriptor::default());
434
435 Self {
436 inner_texture,
437 view,
438 sampler,
439 }
440 }
441
442 fn resize(&mut self, new_size: Vec2<u32>, wgpu: &WgpuClump, format: wgpu::TextureFormat) {
443 let inner_texture = wgpu.device.create_texture(&wgpu::TextureDescriptor {
444 label: Some("Uniform Texture"),
445 size: wgpu::Extent3d {
446 width: new_size.x,
447 height: new_size.y,
448 depth_or_array_layers: 1,
449 },
450 dimension: wgpu::TextureDimension::D2,
451 mip_level_count: 1,
452 sample_count: 1,
453 format,
454 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
455 view_formats: &[],
456 });
457
458 let new_view = inner_texture.create_view(&wgpu::TextureViewDescriptor {
459 label: Some("Uniform Texture View"),
460 ..Default::default()
461 });
462
463 self.inner_texture = inner_texture;
464 self.view = new_view;
465 }
466
467 pub(crate) fn make_view(&self) -> wgpu::TextureView {
468 self.inner_texture
469 .create_view(&wgpu::TextureViewDescriptor {
470 label: Some("Uniform Texture View"),
471 ..Default::default()
472 })
473 }
474
475 pub(crate) fn make_render_view(&mut self) -> &wgpu::TextureView {
476 self.view = self
477 .inner_texture
478 .create_view(&wgpu::TextureViewDescriptor {
479 label: Some("Uniform Texture View"),
480 ..Default::default()
481 });
482
483 &self.view
484 }
485}
486
487impl std::error::Error for TextureError {}
488
489impl Display for TextureError {
490 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
491 match self {
492 Self::IoError(e) => write!(f, "{}", e),
493 Self::ImageError(e) => write!(f, "{}", e),
494 }
495 }
496}
497
498#[derive(Clone, Copy, Debug, PartialEq, Eq)]
500pub enum SamplerType {
501 NearestNeighbor,
506 LinearInterpolation,
510}
511
512impl From<SamplerType> for wgpu::FilterMode {
513 fn from(value: SamplerType) -> Self {
514 match value {
515 SamplerType::LinearInterpolation => wgpu::FilterMode::Linear,
516 SamplerType::NearestNeighbor => wgpu::FilterMode::Nearest,
517 }
518 }
519}