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,
65};
66use image::{ColorType, DynamicImage, ImageError, ImageFormat, Pixel};
67use serde::{Deserialize, Serialize};
68use std::sync::LazyLock;
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, AsRefStr, EnumString, VariantNames, TypeUuidProvider)]
83#[type_uuid(id = "542eb785-875b-43ce-b73a-a25024535f48")]
84pub enum TextureKind {
85 Line {
87 length: u32,
89 },
90 Rectangle {
92 width: u32,
94 height: u32,
96 },
97 Cube {
99 size: u32,
101 },
102 Volume {
104 width: u32,
106 height: u32,
108 depth: u32,
110 },
111}
112
113impl TextureKind {
114 #[inline]
116 pub fn line_length(&self) -> Option<u32> {
117 if let Self::Line { length } = self {
118 Some(*length)
119 } else {
120 None
121 }
122 }
123
124 #[inline]
126 pub fn rectangle_size(&self) -> Option<Vector2<u32>> {
127 if let Self::Rectangle { width, height } = self {
128 Some(Vector2::new(*width, *height))
129 } else {
130 None
131 }
132 }
133
134 #[inline]
136 pub fn cube_size(&self) -> Option<u32> {
137 if let Self::Cube { size } = self {
138 Some(*size)
139 } else {
140 None
141 }
142 }
143
144 #[inline]
146 pub fn volume_size(&self) -> Option<Vector3<u32>> {
147 if let Self::Volume {
148 width,
149 height,
150 depth,
151 } = self
152 {
153 Some(Vector3::new(*width, *height, *depth))
154 } else {
155 None
156 }
157 }
158}
159
160impl Default for TextureKind {
161 fn default() -> Self {
162 Self::Rectangle {
163 width: 0,
164 height: 0,
165 }
166 }
167}
168
169impl Visit for TextureKind {
170 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
171 let mut region = visitor.enter_region(name)?;
172
173 let mut id = match self {
174 TextureKind::Line { .. } => 0,
175 TextureKind::Rectangle { .. } => 1,
176 TextureKind::Cube { .. } => 2,
177 TextureKind::Volume { .. } => 3,
178 };
179 id.visit("Id", &mut region)?;
180 if region.is_reading() {
181 *self = match id {
182 0 => TextureKind::Line { length: 0 },
183 1 => TextureKind::Rectangle {
184 width: 0,
185 height: 0,
186 },
187 2 => TextureKind::Cube { size: 0 },
188 3 => TextureKind::Volume {
189 width: 0,
190 height: 0,
191 depth: 0,
192 },
193 _ => {
194 return VisitResult::Err(VisitError::User(format!(
195 "Invalid texture kind {id}!"
196 )))
197 }
198 };
199 }
200 match self {
201 TextureKind::Line { length } => {
202 length.visit("Length", &mut region)?;
203 }
204 TextureKind::Rectangle { width, height } => {
205 width.visit("Width", &mut region)?;
206 height.visit("Height", &mut region)?;
207 }
208 TextureKind::Cube { size } => {
209 size.visit("Width", &mut region)?;
211 }
212 TextureKind::Volume {
213 width,
214 height,
215 depth,
216 } => {
217 width.visit("Width", &mut region)?;
218 height.visit("Height", &mut region)?;
219 depth.visit("Depth", &mut region)?;
220 }
221 }
222
223 Ok(())
224 }
225}
226
227#[derive(Default, Clone, Reflect)]
229pub struct TextureBytes(Vec<u8>);
230
231impl Visit for TextureBytes {
232 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
233 self.0.visit(name, visitor)
234 }
235}
236
237impl Debug for TextureBytes {
238 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
239 write!(f, "Texture has {} bytes", self.0.len())
240 }
241}
242
243impl From<Vec<u8>> for TextureBytes {
244 fn from(bytes: Vec<u8>) -> Self {
245 Self(bytes)
246 }
247}
248
249impl Deref for TextureBytes {
250 type Target = Vec<u8>;
251
252 fn deref(&self) -> &Self::Target {
253 &self.0
254 }
255}
256
257impl DerefMut for TextureBytes {
258 fn deref_mut(&mut self) -> &mut Self::Target {
259 &mut self.0
260 }
261}
262
263#[derive(Debug, Clone, Reflect)]
265pub struct Texture {
266 kind: TextureKind,
267 bytes: TextureBytes,
268 pixel_kind: TexturePixelKind,
269 minification_filter: TextureMinificationFilter,
270 magnification_filter: TextureMagnificationFilter,
271 s_wrap_mode: TextureWrapMode,
272 t_wrap_mode: TextureWrapMode,
273 r_wrap_mode: TextureWrapMode,
274 base_level: usize,
275 max_level: usize,
276 min_lod: f32,
277 max_lod: f32,
278 lod_bias: f32,
279 mip_count: u32,
280 anisotropy: f32,
281 modifications_counter: u64,
282 sampler_properties_modifications: u64,
283 is_render_target: bool,
284 #[doc(hidden)]
285 #[reflect(hidden)]
286 pub cache_index: Arc<AtomicIndex>,
287}
288
289impl TypeUuidProvider for Texture {
290 fn type_uuid() -> Uuid {
291 uuid!("02c23a44-55fa-411a-bc39-eb7a5eadf15c")
292 }
293}
294
295impl ResourceData for Texture {
296 fn type_uuid(&self) -> Uuid {
297 <Self as TypeUuidProvider>::type_uuid()
298 }
299
300 fn save(&mut self, path: &Path) -> Result<(), Box<dyn std::error::Error>> {
301 let color_type = match self.pixel_kind {
302 TexturePixelKind::R8 => ColorType::L8,
303 TexturePixelKind::Luminance8 => ColorType::L8,
304 TexturePixelKind::RGB8 | TexturePixelKind::SRGB8 => ColorType::Rgb8,
305 TexturePixelKind::RGBA8 | TexturePixelKind::SRGBA8 => ColorType::Rgba8,
306 TexturePixelKind::RG8 => ColorType::La8,
307 TexturePixelKind::LuminanceAlpha8 => ColorType::La8,
308 TexturePixelKind::R16 => ColorType::L16,
309 TexturePixelKind::Luminance16 => ColorType::L16,
310 TexturePixelKind::RG16 => ColorType::La16,
311 TexturePixelKind::LuminanceAlpha16 => ColorType::La16,
312 TexturePixelKind::RGB16 => ColorType::Rgb16,
313 TexturePixelKind::RGBA16 => ColorType::Rgba16,
314 TexturePixelKind::RGB32F => ColorType::Rgb32F,
315 TexturePixelKind::RGBA32F => ColorType::Rgba32F,
316 TexturePixelKind::DXT1RGB
317 | TexturePixelKind::DXT1RGBA
318 | TexturePixelKind::DXT3RGBA
319 | TexturePixelKind::DXT5RGBA
320 | TexturePixelKind::R8RGTC
321 | TexturePixelKind::RG8RGTC
322 | TexturePixelKind::BGR8
323 | TexturePixelKind::BGRA8
324 | TexturePixelKind::RGB16F
325 | TexturePixelKind::R32F
326 | TexturePixelKind::R16F => return Err(Box::new(TextureError::UnsupportedFormat)),
327 };
328 if let TextureKind::Rectangle { width, height } = self.kind {
329 Ok(image::save_buffer(
330 path,
331 self.bytes.as_ref(),
332 width,
333 height,
334 color_type,
335 )?)
336 } else {
337 Err(Box::new(TextureError::UnsupportedFormat))
338 }
339 }
340
341 fn can_be_saved(&self) -> bool {
342 true
343 }
344
345 fn try_clone_box(&self) -> Option<Box<dyn ResourceData>> {
346 Some(Box::new(self.clone()))
347 }
348}
349
350impl Visit for Texture {
351 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
352 let mut region = visitor.enter_region(name)?;
353
354 let mut kind = self.pixel_kind.id();
355 kind.visit("KindId", &mut region)?;
356 if region.is_reading() {
357 self.pixel_kind = TexturePixelKind::new(kind)?;
358 }
359
360 self.minification_filter
361 .visit("MinificationFilter", &mut region)?;
362 self.magnification_filter
363 .visit("MagnificationFilter", &mut region)?;
364 self.anisotropy.visit("Anisotropy", &mut region)?;
365 self.s_wrap_mode.visit("SWrapMode", &mut region)?;
366 self.t_wrap_mode.visit("TWrapMode", &mut region)?;
367 self.t_wrap_mode.visit("RWrapMode", &mut region)?;
368 self.mip_count.visit("MipCount", &mut region)?;
369 self.kind.visit("Kind", &mut region)?;
370 let mut bytes_view = PodVecView::from_pod_vec(&mut self.bytes);
371 bytes_view.visit("Data", &mut region)?;
372 self.base_level.visit("BaseLevel", &mut region)?;
373 self.max_level.visit("MaxLevel", &mut region)?;
374 self.min_lod.visit("MinLod", &mut region)?;
375 self.max_lod.visit("MaxLod", &mut region)?;
376 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
673pub static PLACEHOLDER: LazyLock<BuiltInResource<Texture>> = LazyLock::new(|| {
675 BuiltInResource::new(
676 "__PlaceholderTexture",
677 embedded_data_source!("default.png"),
678 |data| {
679 TextureResource::load_from_memory(
680 uuid!("58b0e112-a21a-481f-b305-a2dc5a8bea1f"),
681 ResourceKind::External,
682 data,
683 Default::default(),
684 )
685 .unwrap()
686 },
687 )
688});
689
690pub static PURE_COLOR: LazyLock<BuiltInResource<Texture>> = LazyLock::new(|| {
692 BuiltInResource::new(
693 "__PureColorTexture",
694 embedded_data_source!("pure_color.png"),
695 |data| {
696 TextureResource::load_from_memory(
697 uuid!("9709eef2-305c-44da-91e5-6f293d74408a"),
698 ResourceKind::External,
699 data,
700 Default::default(),
701 )
702 .unwrap()
703 },
704 )
705});
706
707pub type TextureResource = Resource<Texture>;
709
710pub trait TextureResourceExtension: Sized {
712 fn new_render_target(width: u32, height: u32) -> Self;
716
717 fn new_cube_render_target(resolution: u32) -> Self;
721
722 fn new_render_target_with_format(width: u32, height: u32, pixel_kind: TexturePixelKind)
726 -> Self;
727
728 fn load_from_memory(
746 resource_uuid: Uuid,
747 kind: ResourceKind,
748 data: &[u8],
749 import_options: TextureImportOptions,
750 ) -> Result<Self, TextureError>;
751
752 fn from_bytes(
755 resource_uuid: Uuid,
756 kind: TextureKind,
757 pixel_kind: TexturePixelKind,
758 bytes: Vec<u8>,
759 resource_kind: ResourceKind,
760 ) -> Option<Self>;
761
762 fn deep_clone(&self) -> Self;
765}
766
767impl TextureResourceExtension for TextureResource {
768 fn new_render_target(width: u32, height: u32) -> Self {
769 Self::new_render_target_with_format(width, height, TexturePixelKind::RGBA8)
770 }
771
772 fn new_cube_render_target(size: u32) -> Self {
773 Resource::new_ok(
774 Default::default(),
775 Default::default(),
776 Texture {
777 kind: TextureKind::Cube { size },
779 bytes: Default::default(),
780 pixel_kind: TexturePixelKind::RGBA8,
781 minification_filter: TextureMinificationFilter::Linear,
782 magnification_filter: TextureMagnificationFilter::Linear,
783 s_wrap_mode: TextureWrapMode::Repeat,
784 t_wrap_mode: TextureWrapMode::Repeat,
785 r_wrap_mode: TextureWrapMode::Repeat,
786 base_level: 0,
787 max_level: 1000,
788 min_lod: -1000.0,
789 max_lod: 1000.0,
790 lod_bias: 0.0,
791 mip_count: 1,
792 anisotropy: 1.0,
793 modifications_counter: 0,
794 sampler_properties_modifications: 1,
795 is_render_target: true,
796 cache_index: Default::default(),
797 },
798 )
799 }
800
801 fn new_render_target_with_format(
802 width: u32,
803 height: u32,
804 pixel_kind: TexturePixelKind,
805 ) -> Self {
806 Resource::new_ok(
807 Default::default(),
808 Default::default(),
809 Texture {
810 kind: TextureKind::Rectangle { width, height },
812 bytes: Default::default(),
813 pixel_kind,
814 minification_filter: TextureMinificationFilter::Linear,
815 magnification_filter: TextureMagnificationFilter::Linear,
816 s_wrap_mode: TextureWrapMode::Repeat,
817 t_wrap_mode: TextureWrapMode::Repeat,
818 r_wrap_mode: TextureWrapMode::Repeat,
819 base_level: 0,
820 max_level: 1000,
821 min_lod: -1000.0,
822 max_lod: 1000.0,
823 lod_bias: 0.0,
824 mip_count: 1,
825 anisotropy: 1.0,
826 modifications_counter: 0,
827 sampler_properties_modifications: 1,
828 is_render_target: true,
829 cache_index: Default::default(),
830 },
831 )
832 }
833
834 fn load_from_memory(
835 resource_uuid: Uuid,
836 kind: ResourceKind,
837 data: &[u8],
838 import_options: TextureImportOptions,
839 ) -> Result<Self, TextureError> {
840 Ok(Resource::new_ok(
841 resource_uuid,
842 kind,
843 Texture::load_from_memory(data, import_options)?,
844 ))
845 }
846
847 fn from_bytes(
848 resource_uuid: Uuid,
849 kind: TextureKind,
850 pixel_kind: TexturePixelKind,
851 bytes: Vec<u8>,
852 resource_kind: ResourceKind,
853 ) -> Option<Self> {
854 Some(Resource::new_ok(
855 resource_uuid,
856 resource_kind,
857 Texture::from_bytes(kind, pixel_kind, bytes)?,
858 ))
859 }
860
861 fn deep_clone(&self) -> Self {
862 let kind = self.header().kind;
863 let data = self.data_ref().clone();
864 Resource::new_ok(Uuid::new_v4(), kind, data)
865 }
866}
867
868#[derive(
871 Copy,
872 Clone,
873 Debug,
874 Hash,
875 PartialOrd,
876 PartialEq,
877 Deserialize,
878 Serialize,
879 Reflect,
880 VariantNames,
881 EnumString,
882 AsRefStr,
883 Visit,
884 Eq,
885)]
886#[repr(u32)]
887#[derive(Default)]
888pub enum TextureMagnificationFilter {
889 Nearest = 0,
892
893 #[default]
896 Linear = 1,
897}
898
899uuid_provider!(TextureMagnificationFilter = "824f5b6c-8957-42db-9ebc-ef2a5dece5ab");
900
901#[derive(
904 Copy,
905 Clone,
906 Debug,
907 Hash,
908 PartialOrd,
909 PartialEq,
910 Deserialize,
911 Serialize,
912 Reflect,
913 VariantNames,
914 EnumString,
915 AsRefStr,
916 Visit,
917 Eq,
918)]
919#[repr(u32)]
920#[derive(Default)]
921pub enum TextureMinificationFilter {
922 Nearest = 0,
925
926 NearestMipMapNearest = 1,
930
931 NearestMipMapLinear = 2,
936
937 Linear = 3,
940
941 LinearMipMapNearest = 4,
945
946 #[default]
951 LinearMipMapLinear = 5,
952}
953
954uuid_provider!(TextureMinificationFilter = "0ec9e072-6d0a-47b2-a9c2-498cac4de22b");
955
956impl TextureMinificationFilter {
957 pub fn is_using_mip_mapping(self) -> bool {
959 match self {
960 TextureMinificationFilter::Nearest | TextureMinificationFilter::Linear => false,
961 TextureMinificationFilter::NearestMipMapNearest
962 | TextureMinificationFilter::LinearMipMapLinear
963 | TextureMinificationFilter::NearestMipMapLinear
964 | TextureMinificationFilter::LinearMipMapNearest => true,
965 }
966 }
967}
968
969#[derive(
971 Copy,
972 Clone,
973 Debug,
974 Hash,
975 PartialOrd,
976 PartialEq,
977 Deserialize,
978 Serialize,
979 Reflect,
980 VariantNames,
981 EnumString,
982 AsRefStr,
983 Visit,
984 Eq,
985)]
986#[repr(u32)]
987#[derive(Default)]
988pub enum TextureWrapMode {
989 #[default]
992 Repeat = 0,
993
994 ClampToEdge = 1,
997
998 ClampToBorder = 2,
1002
1003 MirroredRepeat = 3,
1007
1008 MirrorClampToEdge = 4,
1011}
1012
1013uuid_provider!(TextureWrapMode = "e360d139-4374-4323-a66d-d192809d9d87");
1014
1015#[derive(
1017 Copy,
1018 Clone,
1019 PartialEq,
1020 Eq,
1021 Debug,
1022 Reflect,
1023 Visit,
1024 AsRefStr,
1025 EnumString,
1026 VariantNames,
1027 TypeUuidProvider,
1028)]
1029#[type_uuid(id = "dcca9b9c-dd1e-412c-922f-074703d35781")]
1030#[repr(u32)]
1031pub enum TexturePixelKind {
1032 R8 = 0,
1034
1035 RGB8 = 1,
1037
1038 RGBA8 = 2,
1040
1041 RG8 = 3,
1043
1044 R16 = 4,
1046
1047 RG16 = 5,
1049
1050 BGR8 = 6,
1052
1053 BGRA8 = 7,
1055
1056 RGB16 = 8,
1058
1059 RGBA16 = 9,
1061
1062 DXT1RGB = 10,
1064
1065 DXT1RGBA = 11,
1067
1068 DXT3RGBA = 12,
1070
1071 DXT5RGBA = 13,
1073
1074 R8RGTC = 14,
1076
1077 RG8RGTC = 15,
1079
1080 RGB32F = 16,
1082
1083 RGBA32F = 17,
1085
1086 Luminance8 = 18,
1093
1094 LuminanceAlpha8 = 19,
1101
1102 Luminance16 = 20,
1109
1110 LuminanceAlpha16 = 21,
1117
1118 RGB16F = 22,
1120
1121 R32F = 23,
1123
1124 R16F = 24,
1126
1127 SRGBA8 = 25,
1128 SRGB8 = 26,
1129}
1130
1131impl TexturePixelKind {
1132 fn new(id: u32) -> Result<Self, String> {
1133 match id {
1134 0 => Ok(Self::R8),
1135 1 => Ok(Self::RGB8),
1136 2 => Ok(Self::RGBA8),
1137 3 => Ok(Self::RG8),
1138 4 => Ok(Self::R16),
1139 5 => Ok(Self::RG16),
1140 6 => Ok(Self::BGR8),
1141 7 => Ok(Self::BGRA8),
1142 8 => Ok(Self::RGB16),
1143 9 => Ok(Self::RGBA16),
1144 10 => Ok(Self::DXT1RGB),
1145 11 => Ok(Self::DXT1RGBA),
1146 12 => Ok(Self::DXT3RGBA),
1147 13 => Ok(Self::DXT5RGBA),
1148 14 => Ok(Self::R8RGTC),
1149 15 => Ok(Self::RG8RGTC),
1150 16 => Ok(Self::RGB32F),
1151 17 => Ok(Self::RGBA32F),
1152 18 => Ok(Self::Luminance8),
1153 19 => Ok(Self::LuminanceAlpha8),
1154 20 => Ok(Self::Luminance16),
1155 21 => Ok(Self::LuminanceAlpha16),
1156 22 => Ok(Self::RGB16F),
1157 23 => Ok(Self::R32F),
1158 24 => Ok(Self::R16F),
1159 25 => Ok(Self::SRGBA8),
1160 26 => Ok(Self::SRGB8),
1161 _ => Err(format!("Invalid texture kind {id}!")),
1162 }
1163 }
1164
1165 fn id(self) -> u32 {
1166 self as u32
1167 }
1168
1169 pub fn size_in_bytes(&self) -> Option<usize> {
1172 match self {
1173 Self::R8 | Self::Luminance8 => Some(1),
1174 Self::RGB8 | Self::SRGB8 | Self::BGR8 => Some(3),
1175 Self::RGBA8
1176 | Self::SRGBA8
1177 | Self::RG16
1178 | Self::BGRA8
1179 | Self::LuminanceAlpha16
1180 | Self::R32F => Some(4),
1181 Self::RG8 | Self::R16 | Self::LuminanceAlpha8 | Self::Luminance16 | Self::R16F => {
1182 Some(2)
1183 }
1184 Self::RGB16 | Self::RGB16F => Some(6),
1185 Self::RGBA16 => Some(8),
1186 Self::RGB32F => Some(12),
1187 Self::RGBA32F => Some(16),
1188 Self::DXT1RGB
1191 | Self::DXT1RGBA
1192 | Self::DXT3RGBA
1193 | Self::DXT5RGBA
1194 | Self::R8RGTC
1195 | Self::RG8RGTC => None,
1196 }
1197 }
1198}
1199
1200#[derive(Debug)]
1202pub enum TextureError {
1203 UnsupportedFormat,
1205 Io(std::io::Error),
1207 Image(image::ImageError),
1209 FileLoadError(FileError),
1211}
1212
1213impl Display for TextureError {
1214 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1215 match self {
1216 TextureError::UnsupportedFormat => {
1217 write!(f, "Unsupported format!")
1218 }
1219 TextureError::Io(v) => {
1220 write!(f, "An i/o error has occurred: {v}")
1221 }
1222 TextureError::Image(v) => {
1223 write!(f, "Image loading error {v}")
1224 }
1225 TextureError::FileLoadError(v) => {
1226 write!(f, "A file load error has occurred {v:?}")
1227 }
1228 }
1229 }
1230}
1231
1232impl std::error::Error for TextureError {}
1233
1234impl From<FileError> for TextureError {
1235 fn from(v: FileError) -> Self {
1236 Self::FileLoadError(v)
1237 }
1238}
1239
1240impl From<image::ImageError> for TextureError {
1241 fn from(v: ImageError) -> Self {
1242 Self::Image(v)
1243 }
1244}
1245
1246impl From<std::io::Error> for TextureError {
1247 fn from(v: Error) -> Self {
1248 Self::Io(v)
1249 }
1250}
1251
1252fn ceil_div_4(x: u32) -> u32 {
1253 x.div_ceil(4)
1254}
1255
1256#[derive(
1264 Copy,
1265 Clone,
1266 Deserialize,
1267 Serialize,
1268 PartialEq,
1269 Eq,
1270 Debug,
1271 Reflect,
1272 VariantNames,
1273 EnumString,
1274 AsRefStr,
1275)]
1276#[repr(u32)]
1277#[derive(Default)]
1278pub enum CompressionOptions {
1279 #[default]
1281 NoCompression = 0,
1282
1283 Speed = 1,
1289
1290 Quality = 2,
1296}
1297
1298uuid_provider!(CompressionOptions = "fbdcc081-d0b8-4b62-9925-2de6c013fbf5");
1299
1300fn transmute_slice<T>(bytes: &[u8]) -> &'_ [T] {
1301 unsafe {
1304 std::slice::from_raw_parts(
1305 bytes.as_ptr() as *const T,
1306 bytes.len() / std::mem::size_of::<T>(),
1307 )
1308 }
1309}
1310
1311fn transmute_slice_mut<T>(bytes: &mut [u8]) -> &'_ mut [T] {
1312 unsafe {
1315 std::slice::from_raw_parts_mut(
1316 bytes.as_ptr() as *mut T,
1317 bytes.len() / std::mem::size_of::<T>(),
1318 )
1319 }
1320}
1321
1322fn compress_bc1<T: tbc::color::ColorRgba8>(bytes: &[u8], width: usize, height: usize) -> Vec<u8> {
1323 tbc::encode_image_bc1_conv_u8::<T>(transmute_slice::<T>(bytes), width, height)
1324}
1325
1326fn compress_bc3<T: tbc::color::ColorRgba8>(bytes: &[u8], width: usize, height: usize) -> Vec<u8> {
1327 tbc::encode_image_bc3_conv_u8::<T>(transmute_slice::<T>(bytes), width, height)
1328}
1329
1330fn compress_r8_bc4<T: tbc::color::ColorRed8>(bytes: &[u8], width: usize, height: usize) -> Vec<u8> {
1331 tbc::encode_image_bc4_r8_conv_u8::<T>(transmute_slice::<T>(bytes), width, height)
1332}
1333
1334fn compress_rg8_bc4<T: tbc::color::ColorRedGreen8>(
1335 bytes: &[u8],
1336 width: usize,
1337 height: usize,
1338) -> Vec<u8> {
1339 tbc::encode_image_bc4_rg8_conv_u8::<T>(transmute_slice::<T>(bytes), width, height)
1340}
1341
1342fn data_hash(data: &[u8]) -> u64 {
1343 let mut hasher = FxHasher::default();
1344 data.hash(&mut hasher);
1345 hasher.finish()
1346}
1347
1348fn try_compress(
1349 pixel_kind: TexturePixelKind,
1350 bytes: &[u8],
1351 w: usize,
1352 h: usize,
1353 compression: CompressionOptions,
1354) -> Option<(Vec<u8>, TexturePixelKind)> {
1355 match (pixel_kind, compression) {
1356 (TexturePixelKind::RGB8, CompressionOptions::Speed) => Some((
1357 compress_bc1::<tbc::color::Rgb8>(bytes, w, h),
1358 TexturePixelKind::DXT1RGB,
1359 )),
1360 (TexturePixelKind::RGB8, CompressionOptions::Quality) => Some((
1361 compress_bc3::<tbc::color::Rgb8>(bytes, w, h),
1362 TexturePixelKind::DXT5RGBA,
1363 )),
1364 (TexturePixelKind::RGBA8, CompressionOptions::Speed) => Some((
1365 compress_bc1::<tbc::color::Rgba8>(bytes, w, h),
1366 TexturePixelKind::DXT1RGBA,
1367 )),
1368 (TexturePixelKind::RGBA8, CompressionOptions::Quality) => Some((
1369 compress_bc3::<tbc::color::Rgba8>(bytes, w, h),
1370 TexturePixelKind::DXT5RGBA,
1371 )),
1372 (TexturePixelKind::R8, CompressionOptions::Speed)
1373 | (TexturePixelKind::R8, CompressionOptions::Quality)
1374 | (TexturePixelKind::Luminance8, CompressionOptions::Speed)
1375 | (TexturePixelKind::Luminance8, CompressionOptions::Quality) => Some((
1376 compress_r8_bc4::<tbc::color::Red8>(bytes, w, h),
1377 TexturePixelKind::R8RGTC,
1378 )),
1379 (TexturePixelKind::RG8, CompressionOptions::Speed)
1380 | (TexturePixelKind::RG8, CompressionOptions::Quality)
1381 | (TexturePixelKind::LuminanceAlpha8, CompressionOptions::Speed)
1382 | (TexturePixelKind::LuminanceAlpha8, CompressionOptions::Quality) => Some((
1383 compress_rg8_bc4::<tbc::color::RedGreen8>(bytes, w, h),
1384 TexturePixelKind::RG8RGTC,
1385 )),
1386 _ => None,
1387 }
1388}
1389
1390fn bytes_in_mip_level(kind: TextureKind, pixel_kind: TexturePixelKind, mip: usize) -> u32 {
1391 let pixel_count = match kind {
1392 TextureKind::Line { length } => length.shr(mip),
1393 TextureKind::Rectangle { width, height } => width.shr(mip) * height.shr(mip),
1394 TextureKind::Cube { size } => 6 * size.shr(mip).pow(2),
1395 TextureKind::Volume {
1396 width,
1397 height,
1398 depth,
1399 } => width.shr(mip) * height.shr(mip) * depth.shr(mip),
1400 };
1401 match pixel_kind {
1402 TexturePixelKind::R8 | TexturePixelKind::Luminance8 => pixel_count,
1404 TexturePixelKind::R16
1405 | TexturePixelKind::LuminanceAlpha8
1406 | TexturePixelKind::Luminance16
1407 | TexturePixelKind::RG8
1408 | TexturePixelKind::R16F => 2 * pixel_count,
1409 TexturePixelKind::RGB8 | TexturePixelKind::SRGB8 | TexturePixelKind::BGR8 => {
1410 3 * pixel_count
1411 }
1412 TexturePixelKind::RGBA8
1413 | TexturePixelKind::SRGBA8
1414 | TexturePixelKind::BGRA8
1415 | TexturePixelKind::RG16
1416 | TexturePixelKind::LuminanceAlpha16
1417 | TexturePixelKind::R32F => 4 * pixel_count,
1418 TexturePixelKind::RGB16 | TexturePixelKind::RGB16F => 6 * pixel_count,
1419 TexturePixelKind::RGBA16 => 8 * pixel_count,
1420 TexturePixelKind::RGB32F => 12 * pixel_count,
1421 TexturePixelKind::RGBA32F => 16 * pixel_count,
1422
1423 TexturePixelKind::DXT1RGB
1425 | TexturePixelKind::DXT1RGBA
1426 | TexturePixelKind::DXT3RGBA
1427 | TexturePixelKind::DXT5RGBA
1428 | TexturePixelKind::R8RGTC
1429 | TexturePixelKind::RG8RGTC => {
1430 let block_size = match pixel_kind {
1431 TexturePixelKind::DXT1RGB
1432 | TexturePixelKind::DXT1RGBA
1433 | TexturePixelKind::R8RGTC => 8,
1434 TexturePixelKind::DXT3RGBA
1435 | TexturePixelKind::DXT5RGBA
1436 | TexturePixelKind::RG8RGTC => 16,
1437 _ => unreachable!(),
1438 };
1439 match kind {
1440 TextureKind::Line { length } => ceil_div_4(length) * block_size,
1441 TextureKind::Rectangle { width, height } => {
1442 ceil_div_4(width) * ceil_div_4(height) * block_size
1443 }
1444 TextureKind::Cube { size } => 6 * ceil_div_4(size).pow(2) * block_size,
1445 TextureKind::Volume {
1446 width,
1447 height,
1448 depth,
1449 } => ceil_div_4(width) * ceil_div_4(height) * ceil_div_4(depth) * block_size,
1450 }
1451 }
1452 }
1453}
1454
1455fn mip_byte_offset(kind: TextureKind, pixel_kind: TexturePixelKind, mut mip: usize) -> usize {
1456 let mut offset = 0;
1458 loop {
1459 offset += bytes_in_mip_level(kind, pixel_kind, mip) as usize;
1460 mip = mip.saturating_sub(1);
1461 if mip == 0 {
1462 break;
1463 }
1464 }
1465 offset
1466}
1467
1468fn convert_pixel_type_enum(pixel_kind: TexturePixelKind) -> fr::PixelType {
1469 match pixel_kind {
1470 TexturePixelKind::R8 | TexturePixelKind::Luminance8 => fr::PixelType::U8,
1471 TexturePixelKind::RGB8 | TexturePixelKind::BGR8 => fr::PixelType::U8x3,
1472 TexturePixelKind::RGBA8 | TexturePixelKind::BGRA8 => fr::PixelType::U8x4,
1473 TexturePixelKind::RG8 | TexturePixelKind::LuminanceAlpha8 => fr::PixelType::U8x2,
1474 TexturePixelKind::R16 | TexturePixelKind::Luminance16 => fr::PixelType::U16,
1475 TexturePixelKind::RG16 | TexturePixelKind::LuminanceAlpha16 => fr::PixelType::U16x2,
1476 TexturePixelKind::RGB16 => fr::PixelType::U16x3,
1477 TexturePixelKind::RGBA16 => fr::PixelType::U16x4,
1478 TexturePixelKind::R32F => fr::PixelType::F32,
1479 _ => unreachable!(),
1480 }
1481}
1482
1483fn flip_green_channel<'a, P>(pixels: impl Iterator<Item = &'a mut P>)
1484where
1485 P: Pixel + 'a,
1486{
1487 for pixel in pixels {
1488 let green = &mut pixel.channels_mut()[1];
1489 let inverted = P::Subpixel::max_value() - *green;
1490 *green = inverted;
1491 }
1492}
1493
1494impl Texture {
1495 pub fn load_from_memory(
1517 data: &[u8],
1518 import_options: TextureImportOptions,
1519 ) -> Result<Self, TextureError> {
1520 if let Ok(dds) = ddsfile::Dds::read(&mut Cursor::new(data)) {
1525 let d3dformat = dds
1526 .get_d3d_format()
1527 .ok_or(TextureError::UnsupportedFormat)?;
1528 let mip_count = dds.get_num_mipmap_levels();
1529 let mut bytes = dds.data;
1530
1531 let pixel_kind = match d3dformat {
1533 D3DFormat::DXT1 => TexturePixelKind::DXT1RGBA,
1534 D3DFormat::DXT3 => TexturePixelKind::DXT3RGBA,
1535 D3DFormat::DXT5 => TexturePixelKind::DXT5RGBA,
1536 D3DFormat::L8 | D3DFormat::A8 => TexturePixelKind::R8,
1537 D3DFormat::L16 => TexturePixelKind::R16,
1538 D3DFormat::R8G8B8 => TexturePixelKind::RGB8,
1539 D3DFormat::A8L8 => TexturePixelKind::RG8,
1540 D3DFormat::A8R8G8B8 => {
1541 TexturePixelKind::RGBA8
1554 }
1555 D3DFormat::G16R16 => {
1556 assert_eq!(bytes.len() % 4, 0);
1558 for chunk in bytes.chunks_exact_mut(4) {
1559 let gh = chunk[0];
1561 let gl = chunk[1];
1562 let rh = chunk[2];
1564 let rl = chunk[3];
1565 chunk[0] = rh;
1567 chunk[1] = rl;
1568 chunk[2] = gh;
1569 chunk[3] = gl;
1570 }
1571 TexturePixelKind::RG16
1572 }
1573 _ => return Err(TextureError::UnsupportedFormat),
1574 };
1575
1576 Ok(Self {
1577 pixel_kind,
1578 modifications_counter: 0,
1579 minification_filter: import_options.minification_filter,
1580 magnification_filter: import_options.magnification_filter,
1581 s_wrap_mode: import_options.s_wrap_mode,
1582 t_wrap_mode: import_options.t_wrap_mode,
1583 r_wrap_mode: import_options.r_wrap_mode,
1584 base_level: import_options.base_level,
1585 max_level: import_options.max_level,
1586 min_lod: import_options.min_lod,
1587 max_lod: import_options.max_lod,
1588 anisotropy: import_options.anisotropy,
1589 mip_count,
1590 bytes: bytes.into(),
1591 kind: if dds.header.caps2 & Caps2::CUBEMAP == Caps2::CUBEMAP {
1592 TextureKind::Cube {
1593 size: dds.header.width,
1594 }
1595 } else if dds.header.caps2 & Caps2::VOLUME == Caps2::VOLUME {
1596 TextureKind::Volume {
1597 width: dds.header.width,
1598 height: dds.header.height,
1599 depth: dds.header.depth.unwrap(),
1600 }
1601 } else {
1602 TextureKind::Rectangle {
1603 width: dds.header.width,
1604 height: dds.header.height,
1605 }
1606 },
1607 is_render_target: false,
1608 cache_index: Default::default(),
1609 lod_bias: import_options.lod_bias,
1610 sampler_properties_modifications: 1,
1611 })
1612 } else {
1613 let mut dyn_img = image::load_from_memory(data)
1615 .or_else(|_| image::load_from_memory_with_format(data, ImageFormat::Tga))?;
1619
1620 let width = dyn_img.width();
1621 let height = dyn_img.height();
1622
1623 if import_options.flip_green_channel {
1624 match dyn_img {
1625 DynamicImage::ImageRgb8(ref mut img) => flip_green_channel(img.pixels_mut()),
1626 DynamicImage::ImageRgba8(ref mut img) => flip_green_channel(img.pixels_mut()),
1627 DynamicImage::ImageRgb16(ref mut img) => flip_green_channel(img.pixels_mut()),
1628 DynamicImage::ImageRgba16(ref mut img) => flip_green_channel(img.pixels_mut()),
1629 DynamicImage::ImageRgb32F(ref mut img) => flip_green_channel(img.pixels_mut()),
1630 DynamicImage::ImageRgba32F(ref mut img) => flip_green_channel(img.pixels_mut()),
1631 _ => (),
1632 }
1633 }
1634
1635 let src_pixel_kind = match dyn_img {
1636 DynamicImage::ImageLuma8(_) => TexturePixelKind::Luminance8,
1637 DynamicImage::ImageLumaA8(_) => TexturePixelKind::LuminanceAlpha8,
1638 DynamicImage::ImageRgb8(_) => TexturePixelKind::RGB8,
1639 DynamicImage::ImageRgba8(_) => TexturePixelKind::RGBA8,
1640 DynamicImage::ImageLuma16(_) => TexturePixelKind::Luminance16,
1641 DynamicImage::ImageLumaA16(_) => TexturePixelKind::LuminanceAlpha16,
1642 DynamicImage::ImageRgb16(_) => TexturePixelKind::RGB16,
1643 DynamicImage::ImageRgba16(_) => TexturePixelKind::RGBA16,
1644 DynamicImage::ImageRgb32F(_) => TexturePixelKind::RGB32F,
1645 DynamicImage::ImageRgba32F(_) => TexturePixelKind::RGBA32F,
1646 _ => return Err(TextureError::UnsupportedFormat),
1647 };
1648 let mut final_pixel_kind = src_pixel_kind;
1649
1650 let mut mip_count = 0;
1651 let mut bytes = Vec::with_capacity(
1652 width as usize * height as usize * src_pixel_kind.size_in_bytes().unwrap_or(4),
1653 );
1654
1655 if import_options.minification_filter.is_using_mip_mapping() {
1656 let src_pixel_type = convert_pixel_type_enum(src_pixel_kind);
1657 let mut level_width = width;
1658 let mut level_height = height;
1659 let mut current_level = fr::images::Image::from_vec_u8(
1660 level_width,
1661 level_height,
1662 dyn_img.as_bytes().to_vec(),
1663 src_pixel_type,
1664 )
1665 .map_err(|_| TextureError::UnsupportedFormat)?;
1666
1667 while level_width != 0 && level_height != 0 {
1668 if mip_count != 0 {
1669 let mut dst_img =
1670 fr::images::Image::new(level_width, level_height, src_pixel_type);
1671
1672 let mut resizer = fr::Resizer::new();
1673
1674 resizer
1675 .resize(
1676 ¤t_level,
1677 &mut dst_img,
1678 Some(&ResizeOptions {
1679 algorithm: fr::ResizeAlg::Convolution(
1680 import_options.mip_filter.into_filter_type(),
1681 ),
1682 cropping: Default::default(),
1683 mul_div_alpha: true,
1684 }),
1685 )
1686 .expect("Pixel types must match!");
1687
1688 current_level = dst_img;
1689 }
1690
1691 mip_count += 1;
1692
1693 if import_options.compression == CompressionOptions::NoCompression {
1694 bytes.extend_from_slice(current_level.buffer())
1695 } else if let Some((compressed_data, new_pixel_kind)) = try_compress(
1696 src_pixel_kind,
1697 current_level.buffer(),
1698 level_width as usize,
1699 level_height as usize,
1700 import_options.compression,
1701 ) {
1702 final_pixel_kind = new_pixel_kind;
1703 bytes.extend_from_slice(&compressed_data);
1704 } else {
1705 bytes.extend_from_slice(current_level.buffer())
1706 }
1707
1708 level_width = level_width.checked_shr(1).unwrap_or_default();
1709 level_height = level_height.checked_shr(1).unwrap_or_default();
1710 }
1711 } else {
1712 mip_count = 1;
1713
1714 if import_options.compression == CompressionOptions::NoCompression {
1715 bytes.extend_from_slice(dyn_img.as_bytes());
1716 } else if let Some((compressed_data, new_pixel_kind)) = try_compress(
1717 src_pixel_kind,
1718 dyn_img.as_bytes(),
1719 width as usize,
1720 height as usize,
1721 import_options.compression,
1722 ) {
1723 final_pixel_kind = new_pixel_kind;
1724 bytes.extend_from_slice(&compressed_data);
1725 } else {
1726 bytes.extend_from_slice(dyn_img.as_bytes())
1727 }
1728 }
1729
1730 Ok(Self {
1731 pixel_kind: final_pixel_kind,
1732 kind: TextureKind::Rectangle { width, height },
1733 modifications_counter: 0,
1734 bytes: bytes.into(),
1735 mip_count,
1736 minification_filter: import_options.minification_filter,
1737 magnification_filter: import_options.magnification_filter,
1738 s_wrap_mode: import_options.s_wrap_mode,
1739 t_wrap_mode: import_options.t_wrap_mode,
1740 r_wrap_mode: import_options.r_wrap_mode,
1741 base_level: import_options.base_level,
1742 max_level: import_options.max_level,
1743 min_lod: import_options.min_lod,
1744 max_lod: import_options.max_lod,
1745 anisotropy: import_options.anisotropy,
1746 is_render_target: false,
1747 cache_index: Default::default(),
1748 lod_bias: import_options.lod_bias,
1749 sampler_properties_modifications: 1,
1750 })
1751 }
1752 }
1753
1754 pub(crate) async fn load_from_file<P: AsRef<Path>>(
1761 path: P,
1762 io: &dyn ResourceIo,
1763 import_options: TextureImportOptions,
1764 ) -> Result<Self, TextureError> {
1765 let data = io.load_file(path.as_ref()).await?;
1766 Self::load_from_memory(&data, import_options)
1767 }
1768
1769 pub fn from_bytes(
1775 kind: TextureKind,
1776 pixel_kind: TexturePixelKind,
1777 bytes: Vec<u8>,
1778 ) -> Option<Self> {
1779 if bytes_in_mip_level(kind, pixel_kind, 0) != bytes.len() as u32 {
1780 None
1781 } else {
1782 Some(Self {
1783 kind,
1784 modifications_counter: 0,
1785 bytes: bytes.into(),
1786 pixel_kind,
1787 ..Default::default()
1788 })
1789 }
1790 }
1791
1792 #[inline]
1794 pub fn set_minification_filter(&mut self, filter: TextureMinificationFilter) {
1795 self.minification_filter = filter;
1796 self.sampler_properties_modifications += 1;
1797 }
1798
1799 #[inline]
1801 pub fn minification_filter(&self) -> TextureMinificationFilter {
1802 self.minification_filter
1803 }
1804
1805 #[inline]
1807 pub fn set_magnification_filter(&mut self, filter: TextureMagnificationFilter) {
1808 self.magnification_filter = filter;
1809 self.sampler_properties_modifications += 1;
1810 }
1811
1812 #[inline]
1814 pub fn magnification_filter(&self) -> TextureMagnificationFilter {
1815 self.magnification_filter
1816 }
1817
1818 #[inline]
1820 pub fn set_s_wrap_mode(&mut self, s_wrap_mode: TextureWrapMode) {
1821 self.s_wrap_mode = s_wrap_mode;
1822 self.sampler_properties_modifications += 1;
1823 }
1824
1825 #[inline]
1827 pub fn s_wrap_mode(&self) -> TextureWrapMode {
1828 self.s_wrap_mode
1829 }
1830
1831 #[inline]
1833 pub fn set_t_wrap_mode(&mut self, t_wrap_mode: TextureWrapMode) {
1834 self.t_wrap_mode = t_wrap_mode;
1835 self.sampler_properties_modifications += 1;
1836 }
1837
1838 #[inline]
1840 pub fn t_wrap_mode(&self) -> TextureWrapMode {
1841 self.t_wrap_mode
1842 }
1843
1844 #[inline]
1846 pub fn set_r_wrap_mode(&mut self, r_wrap_mode: TextureWrapMode) {
1847 self.r_wrap_mode = r_wrap_mode;
1848 self.sampler_properties_modifications += 1;
1849 }
1850
1851 pub fn sampler_modifications_count(&self) -> u64 {
1852 self.sampler_properties_modifications
1853 }
1854
1855 #[inline]
1857 pub fn r_wrap_mode(&self) -> TextureWrapMode {
1858 self.r_wrap_mode
1859 }
1860
1861 #[inline]
1863 pub fn mip_count(&self) -> u32 {
1864 self.mip_count
1865 }
1866
1867 #[inline]
1869 pub fn kind(&self) -> TextureKind {
1870 self.kind
1871 }
1872
1873 #[inline]
1875 pub fn modifications_count(&self) -> u64 {
1876 self.modifications_counter
1877 }
1878
1879 #[inline]
1881 pub fn calculate_data_hash(&self) -> u64 {
1882 data_hash(&self.bytes.0)
1883 }
1884
1885 #[inline]
1887 pub fn pixel_kind(&self) -> TexturePixelKind {
1888 self.pixel_kind
1889 }
1890
1891 #[inline]
1893 pub fn base_level(&self) -> usize {
1894 self.base_level
1895 }
1896
1897 #[inline]
1901 pub fn set_base_level(&mut self, base_level: usize) {
1902 self.base_level = base_level;
1903 }
1904
1905 #[inline]
1907 pub fn max_level(&self) -> usize {
1908 self.max_level
1909 }
1910
1911 #[inline]
1915 pub fn set_max_level(&mut self, max_level: usize) {
1916 self.max_level = max_level;
1917 }
1918
1919 #[inline]
1921 pub fn min_lod(&self) -> f32 {
1922 self.min_lod
1923 }
1924
1925 #[inline]
1928 pub fn set_min_lod(&mut self, min_lod: f32) {
1929 self.sampler_properties_modifications += 1;
1930 self.min_lod = min_lod;
1931 }
1932
1933 #[inline]
1935 pub fn max_lod(&self) -> f32 {
1936 self.max_lod
1937 }
1938
1939 #[inline]
1942 pub fn set_max_lod(&mut self, max_lod: f32) {
1943 self.sampler_properties_modifications += 1;
1944 self.max_lod = max_lod;
1945 }
1946
1947 #[inline]
1950 pub fn lod_bias(&self) -> f32 {
1951 self.lod_bias
1952 }
1953
1954 #[inline]
1960 pub fn set_lod_bias(&mut self, lod_bias: f32) {
1961 self.sampler_properties_modifications += 1;
1962 self.lod_bias = lod_bias;
1963 }
1964
1965 pub fn data(&self) -> &[u8] {
1967 &self.bytes
1968 }
1969
1970 pub fn data_of_type<T: Sized>(&self) -> Option<&[T]> {
1984 if let Some(pixel_size) = self.pixel_kind.size_in_bytes() {
1985 if pixel_size == std::mem::size_of::<T>() {
1986 return Some(transmute_slice(&self.bytes));
1987 }
1988 }
1989 None
1990 }
1991
1992 #[inline]
1994 pub fn mip_level_data(&self, mip: usize) -> &[u8] {
1995 let mip_begin = mip
1996 .checked_sub(1)
1997 .map(|prev| mip_byte_offset(self.kind, self.pixel_kind, prev))
1998 .unwrap_or_default();
1999 let mip_end = mip_byte_offset(self.kind, self.pixel_kind, mip);
2000 &self.bytes[mip_begin..mip_end]
2001 }
2002
2003 pub fn mip_level_data_of_type<T: Sized>(&self, mip: usize) -> Option<&[T]> {
2014 if let Some(pixel_size) = self.pixel_kind.size_in_bytes() {
2015 if pixel_size == std::mem::size_of::<T>() {
2016 return Some(transmute_slice(self.mip_level_data(mip)));
2017 }
2018 }
2019 None
2020 }
2021
2022 #[inline]
2024 pub fn is_render_target(&self) -> bool {
2025 self.is_render_target
2026 }
2027
2028 #[inline]
2033 pub fn set_anisotropy_level(&mut self, anisotropy: f32) {
2034 self.sampler_properties_modifications += 1;
2035 self.anisotropy = anisotropy.max(1.0);
2036 }
2037
2038 #[inline]
2040 pub fn anisotropy_level(&self) -> f32 {
2041 self.anisotropy
2042 }
2043
2044 #[inline]
2047 pub fn modify(&mut self) -> TextureDataRefMut<'_> {
2048 TextureDataRefMut { texture: self }
2049 }
2050}
2051
2052pub struct TextureDataRefMut<'a> {
2055 texture: &'a mut Texture,
2056}
2057
2058impl Drop for TextureDataRefMut<'_> {
2059 fn drop(&mut self) {
2060 self.texture.modifications_counter += 1;
2061 }
2062}
2063
2064impl Deref for TextureDataRefMut<'_> {
2065 type Target = Texture;
2066
2067 fn deref(&self) -> &Self::Target {
2068 self.texture
2069 }
2070}
2071
2072impl DerefMut for TextureDataRefMut<'_> {
2073 fn deref_mut(&mut self) -> &mut Self::Target {
2074 self.texture
2075 }
2076}
2077
2078impl TextureDataRefMut<'_> {
2079 pub fn data_mut(&mut self) -> &mut [u8] {
2081 &mut self.texture.bytes
2082 }
2083
2084 pub fn data_mut_of_type<T: Sized>(&mut self) -> Option<&mut [T]> {
2091 if let Some(pixel_size) = self.texture.pixel_kind.size_in_bytes() {
2092 if pixel_size == std::mem::size_of::<T>() {
2093 return Some(transmute_slice_mut(&mut self.texture.bytes));
2094 }
2095 }
2096 None
2097 }
2098}