1use ddsfile::{Caps2, D3DFormat};
44use fast_image_resize as fr;
45use fast_image_resize::ResizeOptions;
46use fxhash::FxHasher;
47use fyrox_core::visitor::error::VisitError;
48use fyrox_core::visitor::pod::PodVecView;
49use fyrox_core::{
50 algebra::{Vector2, Vector3},
51 futures::io::Error,
52 io::FileError,
53 num_traits::Bounded,
54 reflect::prelude::*,
55 sparse::AtomicIndex,
56 uuid,
57 uuid::Uuid,
58 uuid_provider,
59 visitor::{Visit, VisitResult, Visitor},
60 TypeUuidProvider,
61};
62use fyrox_resource::{
63 embedded_data_source, io::ResourceIo, manager::BuiltInResource, options::ImportOptions,
64 untyped::ResourceKind, Resource, ResourceData, TEXTURE_RESOURCE_UUID,
65};
66use image::{ColorType, DynamicImage, ImageError, ImageFormat, Pixel};
67use lazy_static::lazy_static;
68use serde::{Deserialize, Serialize};
69use std::{
70 fmt::{Debug, Display, Formatter},
71 hash::{Hash, Hasher},
72 io::Cursor,
73 ops::{Deref, DerefMut, Shr},
74 path::Path,
75 sync::Arc,
76};
77use strum_macros::{AsRefStr, EnumString, VariantNames};
78
79pub mod loader;
80
81#[derive(Copy, Clone, Debug, Reflect)]
83pub enum TextureKind {
84 Line {
86 length: u32,
88 },
89 Rectangle {
91 width: u32,
93 height: u32,
95 },
96 Cube {
98 size: u32,
100 },
101 Volume {
103 width: u32,
105 height: u32,
107 depth: u32,
109 },
110}
111
112impl TextureKind {
113 #[inline]
115 pub fn line_length(&self) -> Option<u32> {
116 if let Self::Line { length } = self {
117 Some(*length)
118 } else {
119 None
120 }
121 }
122
123 #[inline]
125 pub fn rectangle_size(&self) -> Option<Vector2<u32>> {
126 if let Self::Rectangle { width, height } = self {
127 Some(Vector2::new(*width, *height))
128 } else {
129 None
130 }
131 }
132
133 #[inline]
135 pub fn cube_size(&self) -> Option<u32> {
136 if let Self::Cube { size } = self {
137 Some(*size)
138 } else {
139 None
140 }
141 }
142
143 #[inline]
145 pub fn volume_size(&self) -> Option<Vector3<u32>> {
146 if let Self::Volume {
147 width,
148 height,
149 depth,
150 } = self
151 {
152 Some(Vector3::new(*width, *height, *depth))
153 } else {
154 None
155 }
156 }
157}
158
159impl Default for TextureKind {
160 fn default() -> Self {
161 Self::Rectangle {
162 width: 0,
163 height: 0,
164 }
165 }
166}
167
168impl Visit for TextureKind {
169 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
170 let mut region = visitor.enter_region(name)?;
171
172 let mut id = match self {
173 TextureKind::Line { .. } => 0,
174 TextureKind::Rectangle { .. } => 1,
175 TextureKind::Cube { .. } => 2,
176 TextureKind::Volume { .. } => 3,
177 };
178 id.visit("Id", &mut region)?;
179 if region.is_reading() {
180 *self = match id {
181 0 => TextureKind::Line { length: 0 },
182 1 => TextureKind::Rectangle {
183 width: 0,
184 height: 0,
185 },
186 2 => TextureKind::Cube { size: 0 },
187 3 => TextureKind::Volume {
188 width: 0,
189 height: 0,
190 depth: 0,
191 },
192 _ => {
193 return VisitResult::Err(VisitError::User(format!(
194 "Invalid texture kind {id}!"
195 )))
196 }
197 };
198 }
199 match self {
200 TextureKind::Line { length } => {
201 length.visit("Length", &mut region)?;
202 }
203 TextureKind::Rectangle { width, height } => {
204 width.visit("Width", &mut region)?;
205 height.visit("Height", &mut region)?;
206 }
207 TextureKind::Cube { size } => {
208 size.visit("Width", &mut region)?;
210 }
211 TextureKind::Volume {
212 width,
213 height,
214 depth,
215 } => {
216 width.visit("Width", &mut region)?;
217 height.visit("Height", &mut region)?;
218 depth.visit("Depth", &mut region)?;
219 }
220 }
221
222 Ok(())
223 }
224}
225
226#[derive(Default, Clone, Reflect)]
228pub struct TextureBytes(Vec<u8>);
229
230impl Visit for TextureBytes {
231 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
232 self.0.visit(name, visitor)
233 }
234}
235
236impl Debug for TextureBytes {
237 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
238 write!(f, "Texture has {} bytes", self.0.len())
239 }
240}
241
242impl From<Vec<u8>> for TextureBytes {
243 fn from(bytes: Vec<u8>) -> Self {
244 Self(bytes)
245 }
246}
247
248impl Deref for TextureBytes {
249 type Target = Vec<u8>;
250
251 fn deref(&self) -> &Self::Target {
252 &self.0
253 }
254}
255
256impl DerefMut for TextureBytes {
257 fn deref_mut(&mut self) -> &mut Self::Target {
258 &mut self.0
259 }
260}
261
262#[derive(Debug, Clone, Reflect)]
264pub struct Texture {
265 kind: TextureKind,
266 bytes: TextureBytes,
267 pixel_kind: TexturePixelKind,
268 minification_filter: TextureMinificationFilter,
269 magnification_filter: TextureMagnificationFilter,
270 s_wrap_mode: TextureWrapMode,
271 t_wrap_mode: TextureWrapMode,
272 r_wrap_mode: TextureWrapMode,
273 base_level: usize,
274 max_level: usize,
275 min_lod: f32,
276 max_lod: f32,
277 lod_bias: f32,
278 mip_count: u32,
279 anisotropy: f32,
280 modifications_counter: u64,
281 sampler_properties_modifications: u64,
282 is_render_target: bool,
283 #[doc(hidden)]
284 #[reflect(hidden)]
285 pub cache_index: Arc<AtomicIndex>,
286}
287
288impl TypeUuidProvider for Texture {
289 fn type_uuid() -> Uuid {
290 TEXTURE_RESOURCE_UUID
291 }
292}
293
294impl ResourceData for Texture {
295 fn type_uuid(&self) -> Uuid {
296 <Self as TypeUuidProvider>::type_uuid()
297 }
298
299 fn save(&mut self, path: &Path) -> Result<(), Box<dyn std::error::Error>> {
300 let color_type = match self.pixel_kind {
301 TexturePixelKind::R8 => ColorType::L8,
302 TexturePixelKind::Luminance8 => ColorType::L8,
303 TexturePixelKind::RGB8 | TexturePixelKind::SRGB8 => ColorType::Rgb8,
304 TexturePixelKind::RGBA8 | TexturePixelKind::SRGBA8 => ColorType::Rgba8,
305 TexturePixelKind::RG8 => ColorType::La8,
306 TexturePixelKind::LuminanceAlpha8 => ColorType::La8,
307 TexturePixelKind::R16 => ColorType::L16,
308 TexturePixelKind::Luminance16 => ColorType::L16,
309 TexturePixelKind::RG16 => ColorType::La16,
310 TexturePixelKind::LuminanceAlpha16 => ColorType::La16,
311 TexturePixelKind::RGB16 => ColorType::Rgb16,
312 TexturePixelKind::RGBA16 => ColorType::Rgba16,
313 TexturePixelKind::RGB32F => ColorType::Rgb32F,
314 TexturePixelKind::RGBA32F => ColorType::Rgba32F,
315 TexturePixelKind::DXT1RGB
316 | TexturePixelKind::DXT1RGBA
317 | TexturePixelKind::DXT3RGBA
318 | TexturePixelKind::DXT5RGBA
319 | TexturePixelKind::R8RGTC
320 | TexturePixelKind::RG8RGTC
321 | TexturePixelKind::BGR8
322 | TexturePixelKind::BGRA8
323 | TexturePixelKind::RGB16F
324 | TexturePixelKind::R32F
325 | TexturePixelKind::R16F => return Err(Box::new(TextureError::UnsupportedFormat)),
326 };
327 if let TextureKind::Rectangle { width, height } = self.kind {
328 Ok(image::save_buffer(
329 path,
330 self.bytes.as_ref(),
331 width,
332 height,
333 color_type,
334 )?)
335 } else {
336 Err(Box::new(TextureError::UnsupportedFormat))
337 }
338 }
339
340 fn can_be_saved(&self) -> bool {
341 true
342 }
343
344 fn try_clone_box(&self) -> Option<Box<dyn ResourceData>> {
345 Some(Box::new(self.clone()))
346 }
347}
348
349impl Visit for Texture {
350 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
351 let mut region = visitor.enter_region(name)?;
352
353 let mut kind = self.pixel_kind.id();
354 kind.visit("KindId", &mut region)?;
355 if region.is_reading() {
356 self.pixel_kind = TexturePixelKind::new(kind)?;
357 }
358
359 self.minification_filter
360 .visit("MinificationFilter", &mut region)?;
361 self.magnification_filter
362 .visit("MagnificationFilter", &mut region)?;
363 self.anisotropy.visit("Anisotropy", &mut region)?;
364 self.s_wrap_mode.visit("SWrapMode", &mut region)?;
365 self.t_wrap_mode.visit("TWrapMode", &mut region)?;
366 let _ = self.t_wrap_mode.visit("RWrapMode", &mut region);
367 self.mip_count.visit("MipCount", &mut region)?;
368 self.kind.visit("Kind", &mut region)?;
369 let mut bytes_view = PodVecView::from_pod_vec(&mut self.bytes);
370 let _ = bytes_view.visit("Data", &mut region);
371
372 let _ = self.base_level.visit("BaseLevel", &mut region);
373 let _ = self.max_level.visit("MaxLevel", &mut region);
374 let _ = self.min_lod.visit("MinLod", &mut region);
375 let _ = self.max_lod.visit("MaxLod", &mut region);
376 let _ = self.lod_bias.visit("LodBias", &mut region);
377
378 Ok(())
379 }
380}
381
382impl Default for Texture {
383 fn default() -> Self {
387 Self {
388 kind: TextureKind::Rectangle {
389 width: 0,
390 height: 0,
391 },
392 bytes: Default::default(),
393 pixel_kind: TexturePixelKind::RGBA8,
394 minification_filter: TextureMinificationFilter::LinearMipMapLinear,
395 magnification_filter: TextureMagnificationFilter::Linear,
396 s_wrap_mode: TextureWrapMode::Repeat,
397 t_wrap_mode: TextureWrapMode::Repeat,
398 r_wrap_mode: TextureWrapMode::Repeat,
399 base_level: 0,
400 max_level: default_max_level(),
401 min_lod: default_min_lod(),
402 max_lod: default_max_lod(),
403 lod_bias: 0.0,
404 mip_count: 1,
405 anisotropy: 16.0,
406 modifications_counter: 0,
407 sampler_properties_modifications: 1,
408 is_render_target: false,
409 cache_index: Default::default(),
410 }
411 }
412}
413
414#[derive(
416 Default, Copy, Clone, Deserialize, Serialize, Debug, Reflect, AsRefStr, EnumString, VariantNames,
417)]
418pub enum MipFilter {
419 Nearest,
422 #[default]
425 Bilinear,
426 Hamming,
428 CatmullRom,
431 Lanczos,
434}
435
436uuid_provider!(MipFilter = "8fa17c0e-6889-4540-b396-97db4dc952aa");
437
438impl MipFilter {
439 fn into_filter_type(self) -> fr::FilterType {
440 match self {
441 MipFilter::Nearest => fr::FilterType::Box,
442 MipFilter::Bilinear => fr::FilterType::Bilinear,
443 MipFilter::CatmullRom => fr::FilterType::CatmullRom,
444 MipFilter::Hamming => fr::FilterType::Hamming,
445 MipFilter::Lanczos => fr::FilterType::Lanczos3,
446 }
447 }
448}
449
450#[derive(Clone, Deserialize, Serialize, Debug, Reflect)]
468pub struct TextureImportOptions {
469 #[serde(default)]
470 pub(crate) minification_filter: TextureMinificationFilter,
471 #[serde(default)]
472 pub(crate) magnification_filter: TextureMagnificationFilter,
473 #[serde(default)]
474 pub(crate) s_wrap_mode: TextureWrapMode,
475 #[serde(default)]
476 pub(crate) t_wrap_mode: TextureWrapMode,
477 #[serde(default)]
478 pub(crate) r_wrap_mode: TextureWrapMode,
479 #[serde(default)]
480 pub(crate) anisotropy: f32,
481 #[serde(default)]
482 pub(crate) compression: CompressionOptions,
483 #[serde(default)]
484 pub(crate) mip_filter: MipFilter,
485 #[serde(default)]
486 pub(crate) flip_green_channel: bool,
487 #[serde(default)]
488 pub(crate) base_level: usize,
489 #[serde(default = "default_max_level")]
490 pub(crate) max_level: usize,
491 #[serde(default = "default_min_lod")]
492 pub(crate) min_lod: f32,
493 #[serde(default = "default_max_lod")]
494 pub(crate) max_lod: f32,
495 #[serde(default)]
496 pub(crate) lod_bias: f32,
497}
498
499fn default_max_level() -> usize {
500 1000
501}
502
503fn default_min_lod() -> f32 {
504 -1000.0
505}
506
507fn default_max_lod() -> f32 {
508 1000.0
509}
510
511impl Default for TextureImportOptions {
512 fn default() -> Self {
513 Self {
514 minification_filter: TextureMinificationFilter::LinearMipMapLinear,
515 magnification_filter: TextureMagnificationFilter::Linear,
516 s_wrap_mode: TextureWrapMode::Repeat,
517 t_wrap_mode: TextureWrapMode::Repeat,
518 r_wrap_mode: TextureWrapMode::Repeat,
519 anisotropy: 16.0,
520 compression: CompressionOptions::default(),
521 mip_filter: Default::default(),
522 flip_green_channel: false,
523 base_level: 0,
524 max_level: default_max_level(),
525 min_lod: default_min_lod(),
526 max_lod: default_max_lod(),
527 lod_bias: 0.0,
528 }
529 }
530}
531
532impl ImportOptions for TextureImportOptions {}
533
534impl TextureImportOptions {
535 pub fn with_minification_filter(
538 mut self,
539 minification_filter: TextureMinificationFilter,
540 ) -> Self {
541 self.minification_filter = minification_filter;
542 self
543 }
544
545 pub fn set_minification_filter(&mut self, minification_filter: TextureMinificationFilter) {
548 self.minification_filter = minification_filter;
549 }
550
551 pub fn with_magnification_filter(
554 mut self,
555 magnification_filter: TextureMagnificationFilter,
556 ) -> Self {
557 self.magnification_filter = magnification_filter;
558 self
559 }
560
561 pub fn set_magnification_filter(&mut self, magnification_filter: TextureMagnificationFilter) {
564 self.magnification_filter = magnification_filter;
565 }
566
567 pub fn with_s_wrap_mode(mut self, s_wrap_mode: TextureWrapMode) -> Self {
570 self.s_wrap_mode = s_wrap_mode;
571 self
572 }
573
574 pub fn set_s_wrap_mode(&mut self, s_wrap_mode: TextureWrapMode) {
577 self.s_wrap_mode = s_wrap_mode;
578 }
579
580 pub fn with_t_wrap_mode(mut self, t_wrap_mode: TextureWrapMode) -> Self {
583 self.t_wrap_mode = t_wrap_mode;
584 self
585 }
586
587 pub fn set_t_wrap_mode(&mut self, t_wrap_mode: TextureWrapMode) {
590 self.t_wrap_mode = t_wrap_mode;
591 }
592
593 pub fn with_anisotropy(mut self, anisotropy: f32) -> Self {
596 self.anisotropy = anisotropy.min(1.0);
597 self
598 }
599
600 pub fn set_anisotropy(&mut self, anisotropy: f32) {
603 self.anisotropy = anisotropy.min(1.0);
604 }
605
606 pub fn with_compression(mut self, compression: CompressionOptions) -> Self {
608 self.compression = compression;
609 self
610 }
611
612 pub fn set_compression(&mut self, compression: CompressionOptions) {
614 self.compression = compression;
615 }
616
617 pub fn with_base_level(mut self, base_level: usize) -> Self {
619 self.base_level = base_level;
620 self
621 }
622
623 pub fn set_base_level(&mut self, base_level: usize) {
625 self.base_level = base_level;
626 }
627
628 pub fn with_max_level(mut self, max_level: usize) -> Self {
630 self.max_level = max_level;
631 self
632 }
633
634 pub fn set_max_level(&mut self, max_level: usize) {
636 self.max_level = max_level;
637 }
638
639 pub fn with_min_lod(mut self, min_lod: f32) -> Self {
641 self.min_lod = min_lod;
642 self
643 }
644
645 pub fn set_min_lod(&mut self, min_lod: f32) {
647 self.min_lod = min_lod;
648 }
649
650 pub fn with_max_lod(mut self, max_lod: f32) -> Self {
652 self.max_lod = max_lod;
653 self
654 }
655
656 pub fn set_max_lod(&mut self, max_lod: f32) {
658 self.max_lod = max_lod;
659 }
660
661 pub fn with_lod_bias(mut self, lod_bias: f32) -> Self {
663 self.lod_bias = lod_bias;
664 self
665 }
666
667 pub fn set_lod_bias(&mut self, lod_bias: f32) {
669 self.lod_bias = lod_bias;
670 }
671}
672
673lazy_static! {
674 pub static ref PLACEHOLDER: BuiltInResource<Texture> = BuiltInResource::new("__PlaceholderTexture", embedded_data_source!("default.png"),
676 |data| {
677 TextureResource::load_from_memory(
678 uuid!("58b0e112-a21a-481f-b305-a2dc5a8bea1f"),
679 ResourceKind::External,
680 data,
681 Default::default()
682 )
683 .unwrap()
684 });
685 pub static ref PURE_COLOR: BuiltInResource<Texture> = BuiltInResource::new("__PureColorTexture", embedded_data_source!("pure_color.png"),
687 |data| {
688 TextureResource::load_from_memory(
689 uuid!("9709eef2-305c-44da-91e5-6f293d74408a"),
690 ResourceKind::External,
691 data,
692 Default::default()
693 )
694 .unwrap()
695 });
696}
697
698pub type TextureResource = Resource<Texture>;
700
701pub trait TextureResourceExtension: Sized {
703 fn new_render_target(width: u32, height: u32) -> Self;
707
708 fn new_cube_render_target(resolution: u32) -> Self;
712
713 fn new_render_target_with_format(width: u32, height: u32, pixel_kind: TexturePixelKind)
717 -> Self;
718
719 fn load_from_memory(
737 resource_uuid: Uuid,
738 kind: ResourceKind,
739 data: &[u8],
740 import_options: TextureImportOptions,
741 ) -> Result<Self, TextureError>;
742
743 fn from_bytes(
746 resource_uuid: Uuid,
747 kind: TextureKind,
748 pixel_kind: TexturePixelKind,
749 bytes: Vec<u8>,
750 resource_kind: ResourceKind,
751 ) -> Option<Self>;
752
753 fn deep_clone(&self) -> Self;
756}
757
758impl TextureResourceExtension for TextureResource {
759 fn new_render_target(width: u32, height: u32) -> Self {
760 Self::new_render_target_with_format(width, height, TexturePixelKind::RGBA8)
761 }
762
763 fn new_cube_render_target(size: u32) -> Self {
764 Resource::new_ok(
765 Default::default(),
766 Default::default(),
767 Texture {
768 kind: TextureKind::Cube { size },
770 bytes: Default::default(),
771 pixel_kind: TexturePixelKind::RGBA8,
772 minification_filter: TextureMinificationFilter::Linear,
773 magnification_filter: TextureMagnificationFilter::Linear,
774 s_wrap_mode: TextureWrapMode::Repeat,
775 t_wrap_mode: TextureWrapMode::Repeat,
776 r_wrap_mode: TextureWrapMode::Repeat,
777 base_level: 0,
778 max_level: 1000,
779 min_lod: -1000.0,
780 max_lod: 1000.0,
781 lod_bias: 0.0,
782 mip_count: 1,
783 anisotropy: 1.0,
784 modifications_counter: 0,
785 sampler_properties_modifications: 1,
786 is_render_target: true,
787 cache_index: Default::default(),
788 },
789 )
790 }
791
792 fn new_render_target_with_format(
793 width: u32,
794 height: u32,
795 pixel_kind: TexturePixelKind,
796 ) -> Self {
797 Resource::new_ok(
798 Default::default(),
799 Default::default(),
800 Texture {
801 kind: TextureKind::Rectangle { width, height },
803 bytes: Default::default(),
804 pixel_kind,
805 minification_filter: TextureMinificationFilter::Linear,
806 magnification_filter: TextureMagnificationFilter::Linear,
807 s_wrap_mode: TextureWrapMode::Repeat,
808 t_wrap_mode: TextureWrapMode::Repeat,
809 r_wrap_mode: TextureWrapMode::Repeat,
810 base_level: 0,
811 max_level: 1000,
812 min_lod: -1000.0,
813 max_lod: 1000.0,
814 lod_bias: 0.0,
815 mip_count: 1,
816 anisotropy: 1.0,
817 modifications_counter: 0,
818 sampler_properties_modifications: 1,
819 is_render_target: true,
820 cache_index: Default::default(),
821 },
822 )
823 }
824
825 fn load_from_memory(
826 resource_uuid: Uuid,
827 kind: ResourceKind,
828 data: &[u8],
829 import_options: TextureImportOptions,
830 ) -> Result<Self, TextureError> {
831 Ok(Resource::new_ok(
832 resource_uuid,
833 kind,
834 Texture::load_from_memory(data, import_options)?,
835 ))
836 }
837
838 fn from_bytes(
839 resource_uuid: Uuid,
840 kind: TextureKind,
841 pixel_kind: TexturePixelKind,
842 bytes: Vec<u8>,
843 resource_kind: ResourceKind,
844 ) -> Option<Self> {
845 Some(Resource::new_ok(
846 resource_uuid,
847 resource_kind,
848 Texture::from_bytes(kind, pixel_kind, bytes)?,
849 ))
850 }
851
852 fn deep_clone(&self) -> Self {
853 let kind = self.header().kind;
854 let data = self.data_ref().clone();
855 Resource::new_ok(Uuid::new_v4(), kind, data)
856 }
857}
858
859#[derive(
862 Copy,
863 Clone,
864 Debug,
865 Hash,
866 PartialOrd,
867 PartialEq,
868 Deserialize,
869 Serialize,
870 Reflect,
871 VariantNames,
872 EnumString,
873 AsRefStr,
874 Visit,
875 Eq,
876)]
877#[repr(u32)]
878pub enum TextureMagnificationFilter {
879 Nearest = 0,
882
883 Linear = 1,
886}
887
888uuid_provider!(TextureMagnificationFilter = "824f5b6c-8957-42db-9ebc-ef2a5dece5ab");
889
890impl Default for TextureMagnificationFilter {
891 fn default() -> Self {
892 Self::Linear
893 }
894}
895
896#[derive(
899 Copy,
900 Clone,
901 Debug,
902 Hash,
903 PartialOrd,
904 PartialEq,
905 Deserialize,
906 Serialize,
907 Reflect,
908 VariantNames,
909 EnumString,
910 AsRefStr,
911 Visit,
912 Eq,
913)]
914#[repr(u32)]
915pub enum TextureMinificationFilter {
916 Nearest = 0,
919
920 NearestMipMapNearest = 1,
924
925 NearestMipMapLinear = 2,
930
931 Linear = 3,
934
935 LinearMipMapNearest = 4,
939
940 LinearMipMapLinear = 5,
945}
946
947uuid_provider!(TextureMinificationFilter = "0ec9e072-6d0a-47b2-a9c2-498cac4de22b");
948
949impl TextureMinificationFilter {
950 pub fn is_using_mip_mapping(self) -> bool {
952 match self {
953 TextureMinificationFilter::Nearest | TextureMinificationFilter::Linear => false,
954 TextureMinificationFilter::NearestMipMapNearest
955 | TextureMinificationFilter::LinearMipMapLinear
956 | TextureMinificationFilter::NearestMipMapLinear
957 | TextureMinificationFilter::LinearMipMapNearest => true,
958 }
959 }
960}
961
962impl Default for TextureMinificationFilter {
963 fn default() -> Self {
964 Self::LinearMipMapLinear
965 }
966}
967
968#[derive(
970 Copy,
971 Clone,
972 Debug,
973 Hash,
974 PartialOrd,
975 PartialEq,
976 Deserialize,
977 Serialize,
978 Reflect,
979 VariantNames,
980 EnumString,
981 AsRefStr,
982 Visit,
983 Eq,
984)]
985#[repr(u32)]
986pub enum TextureWrapMode {
987 Repeat = 0,
990
991 ClampToEdge = 1,
994
995 ClampToBorder = 2,
999
1000 MirroredRepeat = 3,
1004
1005 MirrorClampToEdge = 4,
1008}
1009
1010uuid_provider!(TextureWrapMode = "e360d139-4374-4323-a66d-d192809d9d87");
1011
1012impl Default for TextureWrapMode {
1013 fn default() -> Self {
1014 Self::Repeat
1015 }
1016}
1017
1018#[derive(Copy, Clone, PartialEq, Eq, Debug, Reflect)]
1020#[repr(u32)]
1021pub enum TexturePixelKind {
1022 R8 = 0,
1024
1025 RGB8 = 1,
1027
1028 RGBA8 = 2,
1030
1031 RG8 = 3,
1033
1034 R16 = 4,
1036
1037 RG16 = 5,
1039
1040 BGR8 = 6,
1042
1043 BGRA8 = 7,
1045
1046 RGB16 = 8,
1048
1049 RGBA16 = 9,
1051
1052 DXT1RGB = 10,
1054
1055 DXT1RGBA = 11,
1057
1058 DXT3RGBA = 12,
1060
1061 DXT5RGBA = 13,
1063
1064 R8RGTC = 14,
1066
1067 RG8RGTC = 15,
1069
1070 RGB32F = 16,
1072
1073 RGBA32F = 17,
1075
1076 Luminance8 = 18,
1083
1084 LuminanceAlpha8 = 19,
1091
1092 Luminance16 = 20,
1099
1100 LuminanceAlpha16 = 21,
1107
1108 RGB16F = 22,
1110
1111 R32F = 23,
1113
1114 R16F = 24,
1116
1117 SRGBA8 = 25,
1118 SRGB8 = 26,
1119}
1120
1121impl TexturePixelKind {
1122 fn new(id: u32) -> Result<Self, String> {
1123 match id {
1124 0 => Ok(Self::R8),
1125 1 => Ok(Self::RGB8),
1126 2 => Ok(Self::RGBA8),
1127 3 => Ok(Self::RG8),
1128 4 => Ok(Self::R16),
1129 5 => Ok(Self::RG16),
1130 6 => Ok(Self::BGR8),
1131 7 => Ok(Self::BGRA8),
1132 8 => Ok(Self::RGB16),
1133 9 => Ok(Self::RGBA16),
1134 10 => Ok(Self::DXT1RGB),
1135 11 => Ok(Self::DXT1RGBA),
1136 12 => Ok(Self::DXT3RGBA),
1137 13 => Ok(Self::DXT5RGBA),
1138 14 => Ok(Self::R8RGTC),
1139 15 => Ok(Self::RG8RGTC),
1140 16 => Ok(Self::RGB32F),
1141 17 => Ok(Self::RGBA32F),
1142 18 => Ok(Self::Luminance8),
1143 19 => Ok(Self::LuminanceAlpha8),
1144 20 => Ok(Self::Luminance16),
1145 21 => Ok(Self::LuminanceAlpha16),
1146 22 => Ok(Self::RGB16F),
1147 23 => Ok(Self::R32F),
1148 24 => Ok(Self::R16F),
1149 25 => Ok(Self::SRGBA8),
1150 26 => Ok(Self::SRGB8),
1151 _ => Err(format!("Invalid texture kind {id}!")),
1152 }
1153 }
1154
1155 fn id(self) -> u32 {
1156 self as u32
1157 }
1158
1159 pub fn size_in_bytes(&self) -> Option<usize> {
1162 match self {
1163 Self::R8 | Self::Luminance8 => Some(1),
1164 Self::RGB8 | Self::SRGB8 | Self::BGR8 => Some(3),
1165 Self::RGBA8
1166 | Self::SRGBA8
1167 | Self::RG16
1168 | Self::BGRA8
1169 | Self::LuminanceAlpha16
1170 | Self::R32F => Some(4),
1171 Self::RG8 | Self::R16 | Self::LuminanceAlpha8 | Self::Luminance16 | Self::R16F => {
1172 Some(2)
1173 }
1174 Self::RGB16 | Self::RGB16F => Some(6),
1175 Self::RGBA16 => Some(8),
1176 Self::RGB32F => Some(12),
1177 Self::RGBA32F => Some(16),
1178 Self::DXT1RGB
1181 | Self::DXT1RGBA
1182 | Self::DXT3RGBA
1183 | Self::DXT5RGBA
1184 | Self::R8RGTC
1185 | Self::RG8RGTC => None,
1186 }
1187 }
1188}
1189
1190#[derive(Debug)]
1192pub enum TextureError {
1193 UnsupportedFormat,
1195 Io(std::io::Error),
1197 Image(image::ImageError),
1199 FileLoadError(FileError),
1201}
1202
1203impl Display for TextureError {
1204 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1205 match self {
1206 TextureError::UnsupportedFormat => {
1207 write!(f, "Unsupported format!")
1208 }
1209 TextureError::Io(v) => {
1210 write!(f, "An i/o error has occurred: {v}")
1211 }
1212 TextureError::Image(v) => {
1213 write!(f, "Image loading error {v}")
1214 }
1215 TextureError::FileLoadError(v) => {
1216 write!(f, "A file load error has occurred {v:?}")
1217 }
1218 }
1219 }
1220}
1221
1222impl std::error::Error for TextureError {}
1223
1224impl From<FileError> for TextureError {
1225 fn from(v: FileError) -> Self {
1226 Self::FileLoadError(v)
1227 }
1228}
1229
1230impl From<image::ImageError> for TextureError {
1231 fn from(v: ImageError) -> Self {
1232 Self::Image(v)
1233 }
1234}
1235
1236impl From<std::io::Error> for TextureError {
1237 fn from(v: Error) -> Self {
1238 Self::Io(v)
1239 }
1240}
1241
1242fn ceil_div_4(x: u32) -> u32 {
1243 x.div_ceil(4)
1244}
1245
1246#[derive(
1254 Copy,
1255 Clone,
1256 Deserialize,
1257 Serialize,
1258 PartialEq,
1259 Eq,
1260 Debug,
1261 Reflect,
1262 VariantNames,
1263 EnumString,
1264 AsRefStr,
1265)]
1266#[repr(u32)]
1267pub enum CompressionOptions {
1268 NoCompression = 0,
1270
1271 Speed = 1,
1277
1278 Quality = 2,
1284}
1285
1286uuid_provider!(CompressionOptions = "fbdcc081-d0b8-4b62-9925-2de6c013fbf5");
1287
1288impl Default for CompressionOptions {
1289 fn default() -> Self {
1290 Self::NoCompression
1291 }
1292}
1293
1294fn transmute_slice<T>(bytes: &[u8]) -> &'_ [T] {
1295 unsafe {
1298 std::slice::from_raw_parts(
1299 bytes.as_ptr() as *const T,
1300 bytes.len() / std::mem::size_of::<T>(),
1301 )
1302 }
1303}
1304
1305fn transmute_slice_mut<T>(bytes: &mut [u8]) -> &'_ mut [T] {
1306 unsafe {
1309 std::slice::from_raw_parts_mut(
1310 bytes.as_ptr() as *mut T,
1311 bytes.len() / std::mem::size_of::<T>(),
1312 )
1313 }
1314}
1315
1316fn compress_bc1<T: tbc::color::ColorRgba8>(bytes: &[u8], width: usize, height: usize) -> Vec<u8> {
1317 tbc::encode_image_bc1_conv_u8::<T>(transmute_slice::<T>(bytes), width, height)
1318}
1319
1320fn compress_bc3<T: tbc::color::ColorRgba8>(bytes: &[u8], width: usize, height: usize) -> Vec<u8> {
1321 tbc::encode_image_bc3_conv_u8::<T>(transmute_slice::<T>(bytes), width, height)
1322}
1323
1324fn compress_r8_bc4<T: tbc::color::ColorRed8>(bytes: &[u8], width: usize, height: usize) -> Vec<u8> {
1325 tbc::encode_image_bc4_r8_conv_u8::<T>(transmute_slice::<T>(bytes), width, height)
1326}
1327
1328fn compress_rg8_bc4<T: tbc::color::ColorRedGreen8>(
1329 bytes: &[u8],
1330 width: usize,
1331 height: usize,
1332) -> Vec<u8> {
1333 tbc::encode_image_bc4_rg8_conv_u8::<T>(transmute_slice::<T>(bytes), width, height)
1334}
1335
1336fn data_hash(data: &[u8]) -> u64 {
1337 let mut hasher = FxHasher::default();
1338 data.hash(&mut hasher);
1339 hasher.finish()
1340}
1341
1342fn try_compress(
1343 pixel_kind: TexturePixelKind,
1344 bytes: &[u8],
1345 w: usize,
1346 h: usize,
1347 compression: CompressionOptions,
1348) -> Option<(Vec<u8>, TexturePixelKind)> {
1349 match (pixel_kind, compression) {
1350 (TexturePixelKind::RGB8, CompressionOptions::Speed) => Some((
1351 compress_bc1::<tbc::color::Rgb8>(bytes, w, h),
1352 TexturePixelKind::DXT1RGB,
1353 )),
1354 (TexturePixelKind::RGB8, CompressionOptions::Quality) => Some((
1355 compress_bc3::<tbc::color::Rgb8>(bytes, w, h),
1356 TexturePixelKind::DXT5RGBA,
1357 )),
1358 (TexturePixelKind::RGBA8, CompressionOptions::Speed) => Some((
1359 compress_bc1::<tbc::color::Rgba8>(bytes, w, h),
1360 TexturePixelKind::DXT1RGBA,
1361 )),
1362 (TexturePixelKind::RGBA8, CompressionOptions::Quality) => Some((
1363 compress_bc3::<tbc::color::Rgba8>(bytes, w, h),
1364 TexturePixelKind::DXT5RGBA,
1365 )),
1366 (TexturePixelKind::R8, CompressionOptions::Speed)
1367 | (TexturePixelKind::R8, CompressionOptions::Quality)
1368 | (TexturePixelKind::Luminance8, CompressionOptions::Speed)
1369 | (TexturePixelKind::Luminance8, CompressionOptions::Quality) => Some((
1370 compress_r8_bc4::<tbc::color::Red8>(bytes, w, h),
1371 TexturePixelKind::R8RGTC,
1372 )),
1373 (TexturePixelKind::RG8, CompressionOptions::Speed)
1374 | (TexturePixelKind::RG8, CompressionOptions::Quality)
1375 | (TexturePixelKind::LuminanceAlpha8, CompressionOptions::Speed)
1376 | (TexturePixelKind::LuminanceAlpha8, CompressionOptions::Quality) => Some((
1377 compress_rg8_bc4::<tbc::color::RedGreen8>(bytes, w, h),
1378 TexturePixelKind::RG8RGTC,
1379 )),
1380 _ => None,
1381 }
1382}
1383
1384fn bytes_in_mip_level(kind: TextureKind, pixel_kind: TexturePixelKind, mip: usize) -> u32 {
1385 let pixel_count = match kind {
1386 TextureKind::Line { length } => length.shr(mip),
1387 TextureKind::Rectangle { width, height } => width.shr(mip) * height.shr(mip),
1388 TextureKind::Cube { size } => 6 * size.shr(mip).pow(2),
1389 TextureKind::Volume {
1390 width,
1391 height,
1392 depth,
1393 } => width.shr(mip) * height.shr(mip) * depth.shr(mip),
1394 };
1395 match pixel_kind {
1396 TexturePixelKind::R8 | TexturePixelKind::Luminance8 => pixel_count,
1398 TexturePixelKind::R16
1399 | TexturePixelKind::LuminanceAlpha8
1400 | TexturePixelKind::Luminance16
1401 | TexturePixelKind::RG8
1402 | TexturePixelKind::R16F => 2 * pixel_count,
1403 TexturePixelKind::RGB8 | TexturePixelKind::SRGB8 | TexturePixelKind::BGR8 => {
1404 3 * pixel_count
1405 }
1406 TexturePixelKind::RGBA8
1407 | TexturePixelKind::SRGBA8
1408 | TexturePixelKind::BGRA8
1409 | TexturePixelKind::RG16
1410 | TexturePixelKind::LuminanceAlpha16
1411 | TexturePixelKind::R32F => 4 * pixel_count,
1412 TexturePixelKind::RGB16 | TexturePixelKind::RGB16F => 6 * pixel_count,
1413 TexturePixelKind::RGBA16 => 8 * pixel_count,
1414 TexturePixelKind::RGB32F => 12 * pixel_count,
1415 TexturePixelKind::RGBA32F => 16 * pixel_count,
1416
1417 TexturePixelKind::DXT1RGB
1419 | TexturePixelKind::DXT1RGBA
1420 | TexturePixelKind::DXT3RGBA
1421 | TexturePixelKind::DXT5RGBA
1422 | TexturePixelKind::R8RGTC
1423 | TexturePixelKind::RG8RGTC => {
1424 let block_size = match pixel_kind {
1425 TexturePixelKind::DXT1RGB
1426 | TexturePixelKind::DXT1RGBA
1427 | TexturePixelKind::R8RGTC => 8,
1428 TexturePixelKind::DXT3RGBA
1429 | TexturePixelKind::DXT5RGBA
1430 | TexturePixelKind::RG8RGTC => 16,
1431 _ => unreachable!(),
1432 };
1433 match kind {
1434 TextureKind::Line { length } => ceil_div_4(length) * block_size,
1435 TextureKind::Rectangle { width, height } => {
1436 ceil_div_4(width) * ceil_div_4(height) * block_size
1437 }
1438 TextureKind::Cube { size } => 6 * ceil_div_4(size).pow(2) * block_size,
1439 TextureKind::Volume {
1440 width,
1441 height,
1442 depth,
1443 } => ceil_div_4(width) * ceil_div_4(height) * ceil_div_4(depth) * block_size,
1444 }
1445 }
1446 }
1447}
1448
1449fn mip_byte_offset(kind: TextureKind, pixel_kind: TexturePixelKind, mut mip: usize) -> usize {
1450 let mut offset = 0;
1452 loop {
1453 offset += bytes_in_mip_level(kind, pixel_kind, mip) as usize;
1454 mip = mip.saturating_sub(1);
1455 if mip == 0 {
1456 break;
1457 }
1458 }
1459 offset
1460}
1461
1462fn convert_pixel_type_enum(pixel_kind: TexturePixelKind) -> fr::PixelType {
1463 match pixel_kind {
1464 TexturePixelKind::R8 | TexturePixelKind::Luminance8 => fr::PixelType::U8,
1465 TexturePixelKind::RGB8 | TexturePixelKind::BGR8 => fr::PixelType::U8x3,
1466 TexturePixelKind::RGBA8 | TexturePixelKind::BGRA8 => fr::PixelType::U8x4,
1467 TexturePixelKind::RG8 | TexturePixelKind::LuminanceAlpha8 => fr::PixelType::U8x2,
1468 TexturePixelKind::R16 | TexturePixelKind::Luminance16 => fr::PixelType::U16,
1469 TexturePixelKind::RG16 | TexturePixelKind::LuminanceAlpha16 => fr::PixelType::U16x2,
1470 TexturePixelKind::RGB16 => fr::PixelType::U16x3,
1471 TexturePixelKind::RGBA16 => fr::PixelType::U16x4,
1472 TexturePixelKind::R32F => fr::PixelType::F32,
1473 _ => unreachable!(),
1474 }
1475}
1476
1477fn flip_green_channel<'a, P>(pixels: impl Iterator<Item = &'a mut P>)
1478where
1479 P: Pixel + 'a,
1480{
1481 for pixel in pixels {
1482 let green = &mut pixel.channels_mut()[1];
1483 let inverted = P::Subpixel::max_value() - *green;
1484 *green = inverted;
1485 }
1486}
1487
1488impl Texture {
1489 pub fn load_from_memory(
1511 data: &[u8],
1512 import_options: TextureImportOptions,
1513 ) -> Result<Self, TextureError> {
1514 if let Ok(dds) = ddsfile::Dds::read(&mut Cursor::new(data)) {
1519 let d3dformat = dds
1520 .get_d3d_format()
1521 .ok_or(TextureError::UnsupportedFormat)?;
1522 let mip_count = dds.get_num_mipmap_levels();
1523 let mut bytes = dds.data;
1524
1525 let pixel_kind = match d3dformat {
1527 D3DFormat::DXT1 => TexturePixelKind::DXT1RGBA,
1528 D3DFormat::DXT3 => TexturePixelKind::DXT3RGBA,
1529 D3DFormat::DXT5 => TexturePixelKind::DXT5RGBA,
1530 D3DFormat::L8 | D3DFormat::A8 => TexturePixelKind::R8,
1531 D3DFormat::L16 => TexturePixelKind::R16,
1532 D3DFormat::R8G8B8 => TexturePixelKind::RGB8,
1533 D3DFormat::A8L8 => TexturePixelKind::RG8,
1534 D3DFormat::A8R8G8B8 => {
1535 TexturePixelKind::RGBA8
1548 }
1549 D3DFormat::G16R16 => {
1550 assert_eq!(bytes.len() % 4, 0);
1552 for chunk in bytes.chunks_exact_mut(4) {
1553 let gh = chunk[0];
1555 let gl = chunk[1];
1556 let rh = chunk[2];
1558 let rl = chunk[3];
1559 chunk[0] = rh;
1561 chunk[1] = rl;
1562 chunk[2] = gh;
1563 chunk[3] = gl;
1564 }
1565 TexturePixelKind::RG16
1566 }
1567 _ => return Err(TextureError::UnsupportedFormat),
1568 };
1569
1570 Ok(Self {
1571 pixel_kind,
1572 modifications_counter: 0,
1573 minification_filter: import_options.minification_filter,
1574 magnification_filter: import_options.magnification_filter,
1575 s_wrap_mode: import_options.s_wrap_mode,
1576 t_wrap_mode: import_options.t_wrap_mode,
1577 r_wrap_mode: import_options.r_wrap_mode,
1578 base_level: import_options.base_level,
1579 max_level: import_options.max_level,
1580 min_lod: import_options.min_lod,
1581 max_lod: import_options.max_lod,
1582 anisotropy: import_options.anisotropy,
1583 mip_count,
1584 bytes: bytes.into(),
1585 kind: if dds.header.caps2 & Caps2::CUBEMAP == Caps2::CUBEMAP {
1586 TextureKind::Cube {
1587 size: dds.header.width,
1588 }
1589 } else if dds.header.caps2 & Caps2::VOLUME == Caps2::VOLUME {
1590 TextureKind::Volume {
1591 width: dds.header.width,
1592 height: dds.header.height,
1593 depth: dds.header.depth.unwrap(),
1594 }
1595 } else {
1596 TextureKind::Rectangle {
1597 width: dds.header.width,
1598 height: dds.header.height,
1599 }
1600 },
1601 is_render_target: false,
1602 cache_index: Default::default(),
1603 lod_bias: import_options.lod_bias,
1604 sampler_properties_modifications: 1,
1605 })
1606 } else {
1607 let mut dyn_img = image::load_from_memory(data)
1609 .or_else(|_| image::load_from_memory_with_format(data, ImageFormat::Tga))?;
1613
1614 let width = dyn_img.width();
1615 let height = dyn_img.height();
1616
1617 if import_options.flip_green_channel {
1618 match dyn_img {
1619 DynamicImage::ImageRgb8(ref mut img) => flip_green_channel(img.pixels_mut()),
1620 DynamicImage::ImageRgba8(ref mut img) => flip_green_channel(img.pixels_mut()),
1621 DynamicImage::ImageRgb16(ref mut img) => flip_green_channel(img.pixels_mut()),
1622 DynamicImage::ImageRgba16(ref mut img) => flip_green_channel(img.pixels_mut()),
1623 DynamicImage::ImageRgb32F(ref mut img) => flip_green_channel(img.pixels_mut()),
1624 DynamicImage::ImageRgba32F(ref mut img) => flip_green_channel(img.pixels_mut()),
1625 _ => (),
1626 }
1627 }
1628
1629 let src_pixel_kind = match dyn_img {
1630 DynamicImage::ImageLuma8(_) => TexturePixelKind::Luminance8,
1631 DynamicImage::ImageLumaA8(_) => TexturePixelKind::LuminanceAlpha8,
1632 DynamicImage::ImageRgb8(_) => TexturePixelKind::RGB8,
1633 DynamicImage::ImageRgba8(_) => TexturePixelKind::RGBA8,
1634 DynamicImage::ImageLuma16(_) => TexturePixelKind::Luminance16,
1635 DynamicImage::ImageLumaA16(_) => TexturePixelKind::LuminanceAlpha16,
1636 DynamicImage::ImageRgb16(_) => TexturePixelKind::RGB16,
1637 DynamicImage::ImageRgba16(_) => TexturePixelKind::RGBA16,
1638 DynamicImage::ImageRgb32F(_) => TexturePixelKind::RGB32F,
1639 DynamicImage::ImageRgba32F(_) => TexturePixelKind::RGBA32F,
1640 _ => return Err(TextureError::UnsupportedFormat),
1641 };
1642 let mut final_pixel_kind = src_pixel_kind;
1643
1644 let mut mip_count = 0;
1645 let mut bytes = Vec::with_capacity(
1646 width as usize * height as usize * src_pixel_kind.size_in_bytes().unwrap_or(4),
1647 );
1648
1649 if import_options.minification_filter.is_using_mip_mapping() {
1650 let src_pixel_type = convert_pixel_type_enum(src_pixel_kind);
1651 let mut level_width = width;
1652 let mut level_height = height;
1653 let mut current_level = fr::images::Image::from_vec_u8(
1654 level_width,
1655 level_height,
1656 dyn_img.as_bytes().to_vec(),
1657 src_pixel_type,
1658 )
1659 .map_err(|_| TextureError::UnsupportedFormat)?;
1660
1661 while level_width != 0 && level_height != 0 {
1662 if mip_count != 0 {
1663 let mut dst_img =
1664 fr::images::Image::new(level_width, level_height, src_pixel_type);
1665
1666 let mut resizer = fr::Resizer::new();
1667
1668 resizer
1669 .resize(
1670 ¤t_level,
1671 &mut dst_img,
1672 Some(&ResizeOptions {
1673 algorithm: fr::ResizeAlg::Convolution(
1674 import_options.mip_filter.into_filter_type(),
1675 ),
1676 cropping: Default::default(),
1677 mul_div_alpha: true,
1678 }),
1679 )
1680 .expect("Pixel types must match!");
1681
1682 current_level = dst_img;
1683 }
1684
1685 mip_count += 1;
1686
1687 if import_options.compression == CompressionOptions::NoCompression {
1688 bytes.extend_from_slice(current_level.buffer())
1689 } else if let Some((compressed_data, new_pixel_kind)) = try_compress(
1690 src_pixel_kind,
1691 current_level.buffer(),
1692 level_width as usize,
1693 level_height as usize,
1694 import_options.compression,
1695 ) {
1696 final_pixel_kind = new_pixel_kind;
1697 bytes.extend_from_slice(&compressed_data);
1698 } else {
1699 bytes.extend_from_slice(current_level.buffer())
1700 }
1701
1702 level_width = level_width.checked_shr(1).unwrap_or_default();
1703 level_height = level_height.checked_shr(1).unwrap_or_default();
1704 }
1705 } else {
1706 mip_count = 1;
1707
1708 if import_options.compression == CompressionOptions::NoCompression {
1709 bytes.extend_from_slice(dyn_img.as_bytes());
1710 } else if let Some((compressed_data, new_pixel_kind)) = try_compress(
1711 src_pixel_kind,
1712 dyn_img.as_bytes(),
1713 width as usize,
1714 height as usize,
1715 import_options.compression,
1716 ) {
1717 final_pixel_kind = new_pixel_kind;
1718 bytes.extend_from_slice(&compressed_data);
1719 } else {
1720 bytes.extend_from_slice(dyn_img.as_bytes())
1721 }
1722 }
1723
1724 Ok(Self {
1725 pixel_kind: final_pixel_kind,
1726 kind: TextureKind::Rectangle { width, height },
1727 modifications_counter: 0,
1728 bytes: bytes.into(),
1729 mip_count,
1730 minification_filter: import_options.minification_filter,
1731 magnification_filter: import_options.magnification_filter,
1732 s_wrap_mode: import_options.s_wrap_mode,
1733 t_wrap_mode: import_options.t_wrap_mode,
1734 r_wrap_mode: import_options.r_wrap_mode,
1735 base_level: import_options.base_level,
1736 max_level: import_options.max_level,
1737 min_lod: import_options.min_lod,
1738 max_lod: import_options.max_lod,
1739 anisotropy: import_options.anisotropy,
1740 is_render_target: false,
1741 cache_index: Default::default(),
1742 lod_bias: import_options.lod_bias,
1743 sampler_properties_modifications: 1,
1744 })
1745 }
1746 }
1747
1748 pub(crate) async fn load_from_file<P: AsRef<Path>>(
1755 path: P,
1756 io: &dyn ResourceIo,
1757 import_options: TextureImportOptions,
1758 ) -> Result<Self, TextureError> {
1759 let data = io.load_file(path.as_ref()).await?;
1760 Self::load_from_memory(&data, import_options)
1761 }
1762
1763 pub fn from_bytes(
1769 kind: TextureKind,
1770 pixel_kind: TexturePixelKind,
1771 bytes: Vec<u8>,
1772 ) -> Option<Self> {
1773 if bytes_in_mip_level(kind, pixel_kind, 0) != bytes.len() as u32 {
1774 None
1775 } else {
1776 Some(Self {
1777 kind,
1778 modifications_counter: 0,
1779 bytes: bytes.into(),
1780 pixel_kind,
1781 ..Default::default()
1782 })
1783 }
1784 }
1785
1786 #[inline]
1788 pub fn set_minification_filter(&mut self, filter: TextureMinificationFilter) {
1789 self.minification_filter = filter;
1790 self.sampler_properties_modifications += 1;
1791 }
1792
1793 #[inline]
1795 pub fn minification_filter(&self) -> TextureMinificationFilter {
1796 self.minification_filter
1797 }
1798
1799 #[inline]
1801 pub fn set_magnification_filter(&mut self, filter: TextureMagnificationFilter) {
1802 self.magnification_filter = filter;
1803 self.sampler_properties_modifications += 1;
1804 }
1805
1806 #[inline]
1808 pub fn magnification_filter(&self) -> TextureMagnificationFilter {
1809 self.magnification_filter
1810 }
1811
1812 #[inline]
1814 pub fn set_s_wrap_mode(&mut self, s_wrap_mode: TextureWrapMode) {
1815 self.s_wrap_mode = s_wrap_mode;
1816 self.sampler_properties_modifications += 1;
1817 }
1818
1819 #[inline]
1821 pub fn s_wrap_mode(&self) -> TextureWrapMode {
1822 self.s_wrap_mode
1823 }
1824
1825 #[inline]
1827 pub fn set_t_wrap_mode(&mut self, t_wrap_mode: TextureWrapMode) {
1828 self.t_wrap_mode = t_wrap_mode;
1829 self.sampler_properties_modifications += 1;
1830 }
1831
1832 #[inline]
1834 pub fn t_wrap_mode(&self) -> TextureWrapMode {
1835 self.t_wrap_mode
1836 }
1837
1838 #[inline]
1840 pub fn set_r_wrap_mode(&mut self, r_wrap_mode: TextureWrapMode) {
1841 self.r_wrap_mode = r_wrap_mode;
1842 self.sampler_properties_modifications += 1;
1843 }
1844
1845 pub fn sampler_modifications_count(&self) -> u64 {
1846 self.sampler_properties_modifications
1847 }
1848
1849 #[inline]
1851 pub fn r_wrap_mode(&self) -> TextureWrapMode {
1852 self.r_wrap_mode
1853 }
1854
1855 #[inline]
1857 pub fn mip_count(&self) -> u32 {
1858 self.mip_count
1859 }
1860
1861 #[inline]
1863 pub fn kind(&self) -> TextureKind {
1864 self.kind
1865 }
1866
1867 #[inline]
1869 pub fn modifications_count(&self) -> u64 {
1870 self.modifications_counter
1871 }
1872
1873 #[inline]
1875 pub fn calculate_data_hash(&self) -> u64 {
1876 data_hash(&self.bytes.0)
1877 }
1878
1879 #[inline]
1881 pub fn pixel_kind(&self) -> TexturePixelKind {
1882 self.pixel_kind
1883 }
1884
1885 #[inline]
1887 pub fn base_level(&self) -> usize {
1888 self.base_level
1889 }
1890
1891 #[inline]
1895 pub fn set_base_level(&mut self, base_level: usize) {
1896 self.base_level = base_level;
1897 }
1898
1899 #[inline]
1901 pub fn max_level(&self) -> usize {
1902 self.max_level
1903 }
1904
1905 #[inline]
1909 pub fn set_max_level(&mut self, max_level: usize) {
1910 self.max_level = max_level;
1911 }
1912
1913 #[inline]
1915 pub fn min_lod(&self) -> f32 {
1916 self.min_lod
1917 }
1918
1919 #[inline]
1922 pub fn set_min_lod(&mut self, min_lod: f32) {
1923 self.sampler_properties_modifications += 1;
1924 self.min_lod = min_lod;
1925 }
1926
1927 #[inline]
1929 pub fn max_lod(&self) -> f32 {
1930 self.max_lod
1931 }
1932
1933 #[inline]
1936 pub fn set_max_lod(&mut self, max_lod: f32) {
1937 self.sampler_properties_modifications += 1;
1938 self.max_lod = max_lod;
1939 }
1940
1941 #[inline]
1944 pub fn lod_bias(&self) -> f32 {
1945 self.lod_bias
1946 }
1947
1948 #[inline]
1954 pub fn set_lod_bias(&mut self, lod_bias: f32) {
1955 self.sampler_properties_modifications += 1;
1956 self.lod_bias = lod_bias;
1957 }
1958
1959 pub fn data(&self) -> &[u8] {
1961 &self.bytes
1962 }
1963
1964 pub fn data_of_type<T: Sized>(&self) -> Option<&[T]> {
1978 if let Some(pixel_size) = self.pixel_kind.size_in_bytes() {
1979 if pixel_size == std::mem::size_of::<T>() {
1980 return Some(transmute_slice(&self.bytes));
1981 }
1982 }
1983 None
1984 }
1985
1986 #[inline]
1988 pub fn mip_level_data(&self, mip: usize) -> &[u8] {
1989 let mip_begin = mip
1990 .checked_sub(1)
1991 .map(|prev| mip_byte_offset(self.kind, self.pixel_kind, prev))
1992 .unwrap_or_default();
1993 let mip_end = mip_byte_offset(self.kind, self.pixel_kind, mip);
1994 &self.bytes[mip_begin..mip_end]
1995 }
1996
1997 pub fn mip_level_data_of_type<T: Sized>(&self, mip: usize) -> Option<&[T]> {
2008 if let Some(pixel_size) = self.pixel_kind.size_in_bytes() {
2009 if pixel_size == std::mem::size_of::<T>() {
2010 return Some(transmute_slice(self.mip_level_data(mip)));
2011 }
2012 }
2013 None
2014 }
2015
2016 #[inline]
2018 pub fn is_render_target(&self) -> bool {
2019 self.is_render_target
2020 }
2021
2022 #[inline]
2027 pub fn set_anisotropy_level(&mut self, anisotropy: f32) {
2028 self.sampler_properties_modifications += 1;
2029 self.anisotropy = anisotropy.max(1.0);
2030 }
2031
2032 #[inline]
2034 pub fn anisotropy_level(&self) -> f32 {
2035 self.anisotropy
2036 }
2037
2038 #[inline]
2041 pub fn modify(&mut self) -> TextureDataRefMut<'_> {
2042 TextureDataRefMut { texture: self }
2043 }
2044}
2045
2046pub struct TextureDataRefMut<'a> {
2049 texture: &'a mut Texture,
2050}
2051
2052impl Drop for TextureDataRefMut<'_> {
2053 fn drop(&mut self) {
2054 self.texture.modifications_counter += 1;
2055 }
2056}
2057
2058impl Deref for TextureDataRefMut<'_> {
2059 type Target = Texture;
2060
2061 fn deref(&self) -> &Self::Target {
2062 self.texture
2063 }
2064}
2065
2066impl DerefMut for TextureDataRefMut<'_> {
2067 fn deref_mut(&mut self) -> &mut Self::Target {
2068 self.texture
2069 }
2070}
2071
2072impl TextureDataRefMut<'_> {
2073 pub fn data_mut(&mut self) -> &mut [u8] {
2075 &mut self.texture.bytes
2076 }
2077
2078 pub fn data_mut_of_type<T: Sized>(&mut self) -> Option<&mut [T]> {
2085 if let Some(pixel_size) = self.texture.pixel_kind.size_in_bytes() {
2086 if pixel_size == std::mem::size_of::<T>() {
2087 return Some(transmute_slice_mut(&mut self.texture.bytes));
2088 }
2089 }
2090 None
2091 }
2092}