#[cfg(image_enabled)]
use rin_graphics::image;
use rin_gl as gl;
use rin_gl::types::*;
use rin_util as util;
use rin_material::texture::{
TextureRef, CubemapRef, SamplerRef, Sampler, Wrap, Filter, TextureCreationFlags
};
use densevec::KeyedDenseVec;
use std::ops::{Index, IndexMut};
use std::collections::HashMap;
use std::path::Path;
#[cfg(feature="async")]
use rin_util::async_fs;
#[cfg(feature="async")]
use std::pin::Pin;
#[cfg(feature="async")]
use futures::{Future, FutureExt, channel::mpsc::*, stream::StreamExt};
#[cfg(feature = "async")]
use rinecs::{CreationProxy, CreationSystemAsync};
#[cfg(feature = "async")]
use crate::renderer::resources::ResourceFuture;
use glin::Image as _;
#[cfg(feature = "async")]
struct TextureInfo{
creation_flags: TextureCreationFlags,
texture_ref: ResourceFuture<TextureRef>,
name: String,
}
#[cfg(feature="async")]
struct TextureFuture<T>{
future: Pin<Box<dyn Future<Output = (util::Result<T>, TextureInfo)>>>,
}
#[cfg(feature = "async")]
struct CubemapInfo{
creation_flags: TextureCreationFlags,
cubemap_ref: ResourceFuture<CubemapRef>,
color_format: glin::fbo::ColorFormat,
name: String,
}
#[cfg(feature="async")]
struct CubemapFuture<T>{
future: Pin<Box<dyn Future<Output = (util::Result<T>, CubemapInfo)>>>,
}
#[cfg(feature="async")]
struct CubemapLevelsFuture<T>{
future: Pin<Box<dyn Future<Output = (util::Result<Vec<T>>, CubemapInfo)>>>,
}
#[cfg(feature="async")]
enum ResourceType{
#[cfg(image_ty = "image")]
Hdr(TextureFuture<rin_graphics::Hdr>),
#[cfg(image_enabled)]
Image(TextureFuture<rin_graphics::Image>),
#[cfg(dds)]
Dds(CubemapFuture<rin_graphics::Dds>),
#[cfg(image_ty = "image")]
HdrCubemap(CubemapFuture<rin_graphics::Hdr>),
#[cfg(image_enabled)]
ImageCubemap(CubemapFuture<rin_graphics::Image>),
#[cfg(image_ty = "image")]
HdrLevelsCubemap(CubemapLevelsFuture<rin_graphics::Hdr>),
#[cfg(image_enabled)]
ImageLevelsCubemap(CubemapLevelsFuture<rin_graphics::Image>),
}
pub trait Image: glin::Image {
fn premultiply_alpha(&mut self);
}
#[cfg(image_ty = "freeimage")]
impl Image for rin_graphics::Image {
fn premultiply_alpha(&mut self){
use std::u8;
use std::u16;
let num_channels = self.num_components();
match self.ty(){
image::Type::BITMAP if self.is_transparent() => {
for p in self.pixels_mut::<u8>().chunks_mut(num_channels){
let a = p[num_channels - 1] as f32 / u8::MAX as f32;
for i in 0..num_channels - 1 {
p[i] = (p[i] as f32 * a) as u8
}
}
}
image::Type::RGBA16 => {
for p in self.pixels_mut::<u16>().chunks_mut(4){
let a = p[3] as f32 / u16::MAX as f32;
p[0] = (p[0] as f32 * a) as u16;
p[1] = (p[1] as f32 * a) as u16;
p[2] = (p[2] as f32 * a) as u16;
}
}
image::Type::RGBAF => {
for p in self.pixels_mut::<f32>().chunks_mut(4){
let a = p[3];
p[0] *= a;
p[1] *= a;
p[2] *= a;
}
}
_ => ()
}
}
}
#[cfg(image_ty = "image")]
impl Image for rin_graphics::Image {
fn premultiply_alpha(&mut self){
use std::u8;
let num_channels = self.num_components();
match self {
rin_graphics::Image::ImageLumaA8(img) => {
for p in img.pixels_mut() {
let a = p[1] as f32 / u8::MAX as f32;
p[0] = (p[0] as f32 * a) as u8;
}
}
rin_graphics::Image::ImageLumaA16(img) => {
for p in img.pixels_mut() {
let a = p[1] as f32 / u16::MAX as f32;
p[0] = (p[0] as f32 * a) as u16;
}
}
rin_graphics::Image::ImageRgba8(img) => {
for p in img.pixels_mut() {
let a = p[3] as f32 / u8::MAX as f32;
p[0] = (p[0] as f32 * a) as u8;
p[1] = (p[1] as f32 * a) as u8;
p[2] = (p[2] as f32 * a) as u8;
}
}
rin_graphics::Image::ImageRgba16(img) => {
for p in img.pixels_mut() {
let a = p[3] as f32 / u16::MAX as f32;
p[0] = (p[0] as f32 * a) as u16;
p[1] = (p[1] as f32 * a) as u16;
p[2] = (p[2] as f32 * a) as u16;
}
}
rin_graphics::Image::ImageBgra8(img) => {
for p in img.pixels_mut() {
let a = p[3] as f32 / u8::MAX as f32;
p[0] = (p[0] as f32 * a) as u8;
p[1] = (p[1] as f32 * a) as u8;
p[2] = (p[2] as f32 * a) as u8;
}
}
_ => ()
}
}
}
#[cfg(image_ty = "image")]
impl Image for image::ImageBuffer<image::Rgba<u8>, std::vec::Vec<u8>>{
fn premultiply_alpha(&mut self){
for p in self.pixels_mut() {
let a = p[3] as f32 / u8::MAX as f32;
p[0] = (p[0] as f32 * a) as u8;
p[1] = (p[1] as f32 * a) as u8;
p[2] = (p[2] as f32 * a) as u8;
}
}
}
#[cfg(image_ty = "image")]
impl Image for image::ImageBuffer<image::Rgb<u8>, std::vec::Vec<u8>>{
fn premultiply_alpha(&mut self){
}
}
#[cfg(image_ty = "image")]
impl Image for image::ImageBuffer<image::LumaA<u8>, std::vec::Vec<u8>>{
fn premultiply_alpha(&mut self){
for p in self.pixels_mut() {
let a = p[1] as f32 / u8::MAX as f32;
p[0] = (p[0] as f32 * a) as u8;
}
}
}
#[cfg(image_ty = "image")]
impl Image for image::ImageBuffer<image::Luma<u8>, std::vec::Vec<u8>>{
fn premultiply_alpha(&mut self){
}
}
#[cfg(image_ty = "image")]
impl Image for image::ImageBuffer<image::Rgba<u16>, std::vec::Vec<u16>>{
fn premultiply_alpha(&mut self){
for p in self.pixels_mut() {
let a = p[3] as f32 / u16::MAX as f32;
p[0] = (p[0] as f32 * a) as u16;
p[1] = (p[1] as f32 * a) as u16;
p[2] = (p[2] as f32 * a) as u16;
}
}
}
#[cfg(image_ty = "image")]
impl Image for image::ImageBuffer<image::Rgb<u16>, std::vec::Vec<u16>>{
fn premultiply_alpha(&mut self){
}
}
#[cfg(image_ty = "image")]
impl Image for image::ImageBuffer<image::LumaA<u16>, std::vec::Vec<u16>>{
fn premultiply_alpha(&mut self){
for p in self.pixels_mut() {
let a = p[1] as f32 / u16::MAX as f32;
p[0] = (p[0] as f32 * a) as u16;
}
}
}
#[cfg(image_ty = "image")]
impl Image for image::ImageBuffer<image::Luma<u16>, std::vec::Vec<u16>>{
fn premultiply_alpha(&mut self){
}
}
#[cfg(image_ty = "image")]
impl Image for image::ImageBuffer<image::Rgba<f32>, std::vec::Vec<f32>>{
fn premultiply_alpha(&mut self){
for p in self.pixels_mut() {
let a = p[3];
p[0] = p[0] * a;
p[1] = p[1] * a;
p[2] = p[2] * a;
}
}
}
#[cfg(image_ty = "image")]
impl Image for image::ImageBuffer<image::Rgb<f32>, std::vec::Vec<f32>>{
fn premultiply_alpha(&mut self){
}
}
#[cfg(image_ty = "image")]
impl Image for image::ImageBuffer<image::LumaA<f32>, std::vec::Vec<f32>>{
fn premultiply_alpha(&mut self){
for p in self.pixels_mut() {
p[0] = p[0] as f32 * p[1];
}
}
}
#[cfg(image_ty = "image")]
impl Image for image::ImageBuffer<image::Luma<f32>, std::vec::Vec<f32>>{
fn premultiply_alpha(&mut self){
}
}
#[cfg(image_ty = "image")]
impl Image for rin_graphics::Hdr {
fn premultiply_alpha(&mut self){
}
}
#[cfg(dds)]
impl Image for rin_graphics::Dds {
fn premultiply_alpha(&mut self){
unimplemented!()
}
}
#[cfg(image_ty="libhdr")]
impl Image for libhdr::Hdr {
fn premultiply_alpha(&mut self){
}
}
pub struct TexturesPool{
textures: KeyedDenseVec<TextureRef, glin::Texture>,
cubemaps: KeyedDenseVec<CubemapRef, glin::CubeMap>,
samplers: KeyedDenseVec<SamplerRef, glin::Sampler>,
name_texture_index: HashMap<String, TextureRef>,
name_cubemap_index: HashMap<String, CubemapRef>,
name_sampler_index: HashMap<String, SamplerRef>,
#[cfg(feature="async")]
async_sender: UnboundedSender<ResourceType>,
}
impl TexturesPool{
#[cfg(feature = "async")]
pub fn new() -> (TexturesPool, TexturesPoolUpdater){
#[cfg(feature = "async")]
let (async_sender, async_receiver) = unbounded();
let pool = TexturesPool{
textures: KeyedDenseVec::new(),
cubemaps: KeyedDenseVec::new(),
samplers: KeyedDenseVec::new(),
name_texture_index: HashMap::new(),
name_cubemap_index: HashMap::new(),
name_sampler_index: HashMap::new(),
async_sender,
};
let updater = TexturesPoolUpdater{
async_receiver,
};
(pool, updater)
}
#[cfg(not(feature = "async"))]
pub fn new() -> TexturesPool {
TexturesPool{
textures: KeyedDenseVec::new(),
cubemaps: KeyedDenseVec::new(),
samplers: KeyedDenseVec::new(),
name_texture_index: HashMap::new(),
name_cubemap_index: HashMap::new(),
name_sampler_index: HashMap::new(),
}
}
pub fn insert_texture(&mut self, texture: glin::Texture) -> TextureRef{
if let Ok(format) = glin::texture::DataFormat::default_from(&texture) {
log::trace!("Registering texture with {}Mb", format.bytes().unwrap() / 1024 / 1024 );
}
self.textures.insert_key_gen(texture)
}
pub fn insert_cubemap(&mut self, cubemap: glin::CubeMap) -> CubemapRef{
self.cubemaps.insert_key_gen(cubemap)
}
pub fn insert_sampler<R>(&mut self, sampler: Sampler, renderer: &glin::Context<R>) -> SamplerRef
where R: glin::RenderSurface
{
self.samplers.insert_key_gen(self.to_gl_sampler(sampler, renderer))
}
pub fn remove_texture(&mut self, texture: TextureRef) -> Option<glin::Texture>{
self.textures.remove(texture)
}
pub fn remove_cubemap(&mut self, cubemap: CubemapRef) -> Option<glin::CubeMap>{
self.cubemaps.remove(cubemap)
}
pub fn remove_sampler(&mut self, sampler: SamplerRef) -> Option<glin::Sampler>{
self.samplers.remove(sampler)
}
pub fn insert_named_texture(&mut self, name: &str, texture: glin::Texture) -> TextureRef{
if let Ok(format) = glin::texture::DataFormat::default_from(&texture) {
log::trace!("Registering texture {} with {}Mb", name, format.bytes().unwrap() / 1024 / 1024 );
}
if let Some(textureref) = self.name_texture_index.get(name){
self.textures.insert(*textureref, texture);
*textureref
}else{
let textureref = self.textures.insert_key_gen(texture);
self.name_texture_index.insert(name.to_owned(), textureref);
textureref
}
}
pub fn insert_named_cubemap(&mut self, name: &str, cubemap: glin::CubeMap) -> CubemapRef{
if let Some(cubemapref) = self.name_cubemap_index.get(name){
self.cubemaps.insert(*cubemapref, cubemap);
*cubemapref
}else{
let cubemapref = self.cubemaps.insert_key_gen(cubemap);
self.name_cubemap_index.insert(name.to_owned(), cubemapref);
cubemapref
}
}
pub fn insert_named_sampler<R: glin::RenderSurface>(&mut self,
name: &str,
sampler: Sampler,
renderer: &glin::Context<R>) -> SamplerRef
{
let sampler = self.to_gl_sampler(sampler, renderer);
if let Some(samplerref) = self.name_sampler_index.get(name){
self.samplers.insert(*samplerref, sampler);
*samplerref
}else{
let samplerref = self.samplers.insert_key_gen(sampler);
self.name_sampler_index.insert(name.to_owned(), samplerref);
samplerref
}
}
pub fn find_named_texture(&self, name: &str) -> Option<TextureRef>{
self.name_texture_index.get(name).cloned()
}
pub fn find_named_cubemap(&self, name: &str) -> Option<CubemapRef>{
self.name_cubemap_index.get(name).cloned()
}
pub fn find_named_sampler(&self, name: &str) -> Option<SamplerRef>{
self.name_sampler_index.get(name).cloned()
}
pub fn insert_texture_from_data_format<T,R>(&mut self,
format: glin::texture::LoadDataFormat,
creation_flags: TextureCreationFlags,
data: &[T],
renderer: &glin::Context<R>) -> Result<TextureRef, glin::Error>
where T: 'static, R: glin::RenderSurface
{
renderer.new_texture().from_data_format(format, data)
.map(|mut texture| {
if creation_flags.contains(TextureCreationFlags::MIPMAPS){
texture.generate_mipmaps();
}
self.insert_texture(texture)
})
}
pub fn insert_image<I,R>(&mut self,
img: &I,
creation_flags: TextureCreationFlags,
renderer: &glin::Context<R>) -> glin::Result<TextureRef>
where I: Image + Clone, R: glin::RenderSurface
{
let mut load_texture = |img: &I| if creation_flags.contains(TextureCreationFlags::MIPMAPS){
renderer
.new_texture()
.from_image_allocate_max_levels(img, gl::TEXTURE_2D)
.map(|mut texture| {
texture.generate_mipmaps();
self.insert_texture(texture)
})
}else{
renderer
.new_texture()
.from_image(img, gl::TEXTURE_2D)
.map(|texture| {
self.insert_texture(texture)
})
};
if creation_flags.contains(TextureCreationFlags::PREMULTIPLY_ALPHA){
let mut img = img.clone();
img.premultiply_alpha();
load_texture(&img)
}else{
load_texture(img)
}
}
#[cfg(feature="squish")]
fn compress<I,R>(img: &I, renderer: &glin::Context<R>) -> glin::Result<glin::Texture>
where I: glin::Image<DataType=u8>, R: glin::RenderSurface
{
let bytes = squish::Format::Bc1.compressed_size(img.width(0), img.height(0));
let mut output = vec![0; bytes];
squish::Format::Bc1.compress(
img.data(),
img.width(0),
img.height(0),
squish::Params::default(),
&mut output);
let data_format = glin::texture::CompressedDataFormat{
width: img.width(0) as u32,
height: img.height(0) as u32,
level: 0,
format: gl::COMPRESSED_RGBA_S3TC_DXT1_EXT,
.. Default::default()
};
let format = glin::texture::Format::new(
gl::COMPRESSED_RGBA_S3TC_DXT1_EXT,
img.width(0) as u32,
img.height(0) as u32,
);
renderer
.new_texture()
.from_format(format)
.and_then(|mut texture| {
texture.load_compressed_data(&output, data_format)?;
Ok(texture)
})
}
#[cfg(feature="squish")]
pub fn insert_compress_image<I,R>(&mut self,
img: &I,
creation_flags: TextureCreationFlags,
renderer: &glin::Context<R>) -> glin::Result<TextureRef>
where I: glin::Image<DataType=u8>, R: glin::RenderSurface
{
Self::compress(img, renderer).map(|mut texture| {
if creation_flags.contains(TextureCreationFlags::MIPMAPS){
texture.generate_mipmaps();
}
self.insert_texture(texture)
})
}
#[cfg(feature="squish")]
pub fn insert_compress_named_image<I,R>(&mut self,
name: &str,
img: &I,
creation_flags: TextureCreationFlags,
renderer: &glin::Context<R>) -> glin::Result<TextureRef>
where I: glin::Image<DataType=u8>, R: glin::RenderSurface
{
Self::compress(img, renderer).map(|mut texture| {
if creation_flags.contains(TextureCreationFlags::MIPMAPS){
texture.generate_mipmaps();
}
self.insert_named_texture(name, texture)
})
}
pub fn insert_named_texture_from_data_format<R,T>(&mut self,
name: &str,
format: glin::texture::LoadDataFormat,
creation_flags: TextureCreationFlags,
data: &[T],
renderer: &glin::Context<R>) -> glin::Result<TextureRef>
where T: 'static, R: glin::RenderSurface
{
renderer.new_texture().from_data_format(format, data)
.map(|mut texture| {
if creation_flags.contains(TextureCreationFlags::MIPMAPS){
texture.generate_mipmaps();
}
self.insert_named_texture(name, texture)
})
}
pub fn insert_named_image<I,R>(&mut self,
name: &str,
img: &I,
creation_flags: TextureCreationFlags,
renderer: &glin::Context<R>) -> glin::Result<TextureRef>
where
I: Image + Clone,
R: glin::RenderSurface
{
let mut load_texture = |img: &I| if creation_flags.contains(TextureCreationFlags::MIPMAPS){
renderer
.new_texture()
.from_image_allocate_max_levels(img, gl::TEXTURE_2D)
.map(|mut texture| {
texture.generate_mipmaps();
self.insert_named_texture(name, texture)
})
}else{
renderer
.new_texture()
.from_image(img, gl::TEXTURE_2D)
.map(|texture| {
self.insert_named_texture(name, texture)
})
};
if creation_flags.contains(TextureCreationFlags::PREMULTIPLY_ALPHA){
let mut img = img.clone();
img.premultiply_alpha();
load_texture(&img)
}else{
load_texture(&img)
}
}
fn to_gl_sampler<R>(&self, sampler: Sampler, renderer: &glin::Context<R>) -> glin::Sampler
where R: glin::RenderSurface
{
let mut glsampler = renderer.new_sampler();
let wrap = match sampler.wrap {
Wrap::Repeat => gl::REPEAT,
Wrap::Extend => gl::MIRRORED_REPEAT,
Wrap::Clip => gl::CLAMP_TO_EDGE,
};
glsampler.set_wrap_r(wrap);
glsampler.set_wrap_s(wrap);
glsampler.set_wrap_t(wrap);
let minfilter = match sampler.minfilter{
Filter::Nearest => gl::NEAREST,
Filter::Linear => gl::LINEAR,
Filter::NearestMipNearest => gl::NEAREST_MIPMAP_NEAREST,
Filter::NearestMipLinear => gl::NEAREST_MIPMAP_LINEAR,
Filter::LinearMipNearest => gl::LINEAR_MIPMAP_NEAREST,
Filter::LinearMipLinear => gl::LINEAR_MIPMAP_LINEAR,
};
let magfilter = match sampler.magfilter{
Filter::Nearest => gl::NEAREST,
Filter::Linear => gl::LINEAR,
Filter::NearestMipNearest => gl::NEAREST_MIPMAP_NEAREST,
Filter::NearestMipLinear => gl::NEAREST_MIPMAP_LINEAR,
Filter::LinearMipNearest => gl::LINEAR_MIPMAP_NEAREST,
Filter::LinearMipLinear => gl::LINEAR_MIPMAP_LINEAR,
};
glsampler.set_min_mag_filters(minfilter, magfilter);
glsampler
}
pub fn texture(&self, textureref: TextureRef) -> Option<&glin::Texture>{
self.textures.get(textureref)
}
pub fn texture_mut(&mut self, textureref: TextureRef) -> Option<&mut glin::Texture>{
self.textures.get_mut(textureref)
}
pub fn cubemap(&self, cubemapref: CubemapRef) -> Option<&glin::CubeMap>{
self.cubemaps.get(cubemapref)
}
pub fn cubemap_mut(&mut self, cubemapref: CubemapRef) -> Option<&mut glin::CubeMap>{
self.cubemaps.get_mut(cubemapref)
}
#[cfg(all(not(feature="web"), image_ty = "image"))]
fn preload_image<P: AsRef<Path>>(image: P, gl: &gl::Renderer) -> util::Result<glin::Texture> {
if let Some("hdr") = image.as_ref().extension().and_then(|s| s.to_str()){
let image = rin_graphics::Hdr::load(image.as_ref())
.map_err(|e| util::Error::with_cause("Error loading image", e))?;
gl.new_texture().from_image(&image, gl::TEXTURE_2D)
.map_err(|e| util::Error::with_cause("Error loading texture", e))
}else{
let mut image = image::load(image)
.map_err(|e| util::Error::with_cause("Error loading image", e))?;
gl.new_texture().from_image(&image, gl::TEXTURE_2D)
.map_err(|e| util::Error::with_cause("Error loading texture", e))
}
}
#[cfg(image_ty = "freeimage")]
fn preload_image<P: AsRef<Path>>(image: P, gl: &gl::Renderer) -> util::Result<glin::Texture> {
let image = image::load(image)
.and_then(|img| img.flip_vertical().map_err(|e| util::Error::with_cause("Error loading image", e)))?;
gl.new_texture().from_image(&image, gl::TEXTURE_2D)
.map_err(|e| util::Error::with_cause("Error loading texture", e))
}
#[cfg(all(feature = "async", image_enabled))]
fn image_future<'a>(path: &'a Path) -> impl Future<Output = util::Result<rin_graphics::Image>> {
let url = path.to_str().unwrap();
async_fs::read_file(url).map(|bytes| {
let bytes = bytes?;
image::load_from_memory(&bytes)
})
}
#[cfg(all(feature = "async", image_ty="image"))]
fn hdr_future<'a>(path: &'a Path) -> impl Future<Output = util::Result<rin_graphics::Hdr>> {
let url = path.to_str().unwrap();
async_fs::read_file(url).map(|bytes| {
let bytes = bytes?;
rin_graphics::Hdr::from_bytes(&bytes)
.map_err(|err| util::Error::with_cause("Error loading hdr image", err))
})
}
#[cfg(all(feature = "async", any(image_ty="image", image_ty="freeimage")))]
pub fn load_image_async<P>(
&mut self,
path: P,
creation_flags: TextureCreationFlags) -> ResourceFuture<TextureRef>
where P: AsRef<Path>,
{
let name = path.as_ref().to_str().unwrap().to_owned();
if let Some(texture_ref) = self.name_texture_index.get(&name){
ResourceFuture::with(Ok(*texture_ref))
}else{
let texture_ref = ResourceFuture::new();
let tex_info = TextureInfo{
creation_flags,
texture_ref: texture_ref.clone(),
name,
};
if let Some("hdr") = path.as_ref().extension().and_then(|s| s.to_str()){
#[cfg(image_ty = "image")]
{
let future = Self::hdr_future(path.as_ref()).map(|hdr| (hdr, tex_info)).boxed_local();
self.async_sender.unbounded_send(ResourceType::Hdr(TextureFuture{
future,
})).unwrap()
}
#[cfg(image_ty = "freeimage")]
{
let future = Self::image_future(path.as_ref()).map(|hdr| (hdr, tex_info)).boxed_local();
self.async_sender.unbounded_send(ResourceType::Image(TextureFuture{
future,
})).unwrap()
}
}else{
let future = Self::image_future(path.as_ref()).map(|hdr| (hdr, tex_info)).boxed_local();
self.async_sender.unbounded_send(ResourceType::Image(TextureFuture{
future,
})).unwrap()
};
texture_ref
}
}
#[cfg(all(not(feature="web"), any(image_ty="image", image_ty="freeimage")))]
pub fn load_image<P, R>(
&mut self,
path: P,
mut creation_flags: TextureCreationFlags,
renderer: &glin::Context<R>) -> util::Result<TextureRef>
where P: AsRef<Path>,
R: glin::RenderSurface
{
let mut img = image::load(path.as_ref())?;
if creation_flags.contains(TextureCreationFlags::PREMULTIPLY_ALPHA){
img.premultiply_alpha();
creation_flags.remove(TextureCreationFlags::PREMULTIPLY_ALPHA);
}
let path = path.as_ref().to_str().unwrap();
if let Some(textureref) = self.name_texture_index.get(path) {
let texture = &mut self.textures[*textureref];
if glin::Image::width(&img, 0) as u32 != texture.width()
|| glin::Image::height(&img, 0) as u32 != texture.height()
|| glin::Image::levels(&img) as u32 != texture.levels()
{
if creation_flags.contains(TextureCreationFlags::MIPMAPS){
*texture = renderer
.new_texture()
.from_image_allocate_max_levels(&img, gl::TEXTURE_2D)
.map_err(|err| util::Error::with_cause("Error loading image", err))?;
texture.generate_mipmaps();
}else{
*texture = renderer.new_texture().from_image(&img, gl::TEXTURE_2D)
.map_err(|err| util::Error::with_cause("Error loading image", err))?;
}
}else{
if creation_flags.contains(TextureCreationFlags::MIPMAPS)
&& texture.levels() < img.max_levels() as u32
{
*texture = renderer
.new_texture()
.from_image_allocate_max_levels(&img, gl::TEXTURE_2D)
.map_err(|err| util::Error::with_cause("Error loading image", err))?;
texture.generate_mipmaps();
}else{
texture.load_image(&img)
.map_err(|err| util::Error::with_cause("Error loading image", err))?;
texture.generate_mipmaps();
}
}
Ok(*textureref)
}else{
self.insert_named_image(path, &img, creation_flags, renderer)
.map_err(|err| util::Error::with_cause("Error loading image", err))
}
}
#[cfg(all(not(feature = "web"), dds))]
pub fn load_cubemap<P>(
&mut self,
path: P,
creation_flags: TextureCreationFlags,
renderer: &gl::Renderer) -> util::Result<CubemapRef>
where P: AsRef<Path>
{
let cubemap_img = rin_graphics::Dds::load(path.as_ref())
.map_err(|e| util::Error::with_cause("Error loading cubemap image", e))?;
let path_str = path.as_ref().to_str().unwrap();
if let Some(cubemapref) = self.name_cubemap_index.get(path_str){
let cubemap = &mut self.cubemaps[*cubemapref];
if cubemap_img.width() as u32 != cubemap.width()
|| cubemap_img.height() as u32 != cubemap.height()
|| cubemap_img.levels() as u32 != cubemap.levels()
{
*cubemap = renderer.new_cubemap().from_cubemap_image(&cubemap_img)
.map_err(|e| util::Error::with_cause("Error loading cubemap texture", e))?;
}else{
cubemap.load_image(&cubemap_img)
.map_err(|e| util::Error::with_cause("Error loading cubemap texture", e))?;
}
if creation_flags.contains(TextureCreationFlags::MIPMAPS){
cubemap.generate_mipmaps();
}
Ok(*cubemapref)
}else{
let mut cubemap = renderer.new_cubemap().from_cubemap_image(&cubemap_img)
.map_err(|e| util::Error::with_cause("Error loading cubemap texture", e))?;
if creation_flags.contains(TextureCreationFlags::MIPMAPS){
cubemap.generate_mipmaps();
}
Ok(self.insert_named_cubemap(path_str, cubemap))
}
}
#[cfg(all(feature = "async", dds))]
pub fn load_cubemap_async<P>(
&mut self,
path: P,
creation_flags: TextureCreationFlags) -> ResourceFuture<CubemapRef>
where P: AsRef<Path>
{
let url = path.as_ref().to_str().unwrap();
if let Some(cubemap_ref) = self.name_cubemap_index.get(url) {
ResourceFuture::with(Ok(*cubemap_ref))
}else{
let cubemap_ref = ResourceFuture::new();
let cubemap_info = CubemapInfo{
color_format: glin::fbo::ColorFormat::RGBA8,
creation_flags,
cubemap_ref: cubemap_ref.clone(),
name: url.to_owned(),
};
let future = async_fs::read_file(url).map(|bytes| {
let bytes = bytes?;
rin_graphics::Dds::from_bytes(&bytes)
.map_err(|err| util::Error::with_cause("Error loading dds", err))
}).map(|dds| (dds, cubemap_info)).boxed_local();
self.async_sender.unbounded_send(ResourceType::Dds(CubemapFuture{
future,
})).unwrap();
cubemap_ref
}
}
#[cfg(all(not(feature = "web"), image_enabled))]
pub fn load_equirectangular_cubemap<P>(
&mut self,
path: P,
creation_flags: TextureCreationFlags,
color_format: glin::fbo::ColorFormat,
renderer: &gl::Renderer) -> util::Result<CubemapRef>
where P: AsRef<Path>
{
let tex = Self::preload_image(path.as_ref(), renderer)?;
let path = path.as_ref().to_str().unwrap();
if let Some(cubemapref) = self.name_cubemap_index.get(path){
let cubemap = &mut self.cubemaps[*cubemapref];
if tex.height() as u32 != cubemap.width()
|| tex.height() as u32 != cubemap.height()
|| tex.levels() as u32 != cubemap.levels()
{
*cubemap = renderer.new_cubemap().from_format(glin::cubemap::Format{
internal_format: color_format as GLenum,
width: tex.height() as u32,
height: tex.height() as u32,
levels: 1,
}).map_err(|e| util::Error::with_cause("Error loading cubemap texture", e))?;
}
gl::image_based_light::copy_equirectangular_to_cubemap(&tex, cubemap, renderer);
Ok(*cubemapref)
}else{
let mut cubemap = gl::image_based_light::equirectangular_to_cubemap(
&tex,
tex.height() as u32,
color_format,
renderer);
if creation_flags.contains(TextureCreationFlags::MIPMAPS){
cubemap.generate_mipmaps();
}
Ok(self.insert_named_cubemap(path, cubemap))
}
}
#[cfg(all(feature = "async", any(image_ty="image", image_ty="freeimage")))]
pub fn load_equirectangular_cubemap_async<P>(
&mut self,
path: P,
creation_flags: TextureCreationFlags,
color_format: glin::fbo::ColorFormat) -> ResourceFuture<CubemapRef>
where P: AsRef<Path>
{
let url = path.as_ref().to_str().unwrap().to_owned();
if let Some(cubemap_ref) = self.name_cubemap_index.get(&url) {
ResourceFuture::with(Ok(*cubemap_ref))
}else{
let cubemap_ref = ResourceFuture::new();
let cubemap_info = CubemapInfo{
creation_flags,
color_format,
cubemap_ref: cubemap_ref.clone(),
name: url,
};
if let Some("hdr") = path.as_ref().extension().and_then(|s| s.to_str()){
#[cfg(image_ty = "image")]
{
let future = Self::hdr_future(path.as_ref()).map(|hdr| (hdr, cubemap_info)).boxed_local();
self.async_sender.unbounded_send(ResourceType::HdrCubemap(CubemapFuture{
future,
})).unwrap();
}
#[cfg(image_ty = "freeimage")]
{
let future = Self::image_future(path.as_ref()).map(|img| (img, cubemap_info)).boxed_local();
self.async_sender.unbounded_send(ResourceType::ImageCubemap(CubemapFuture{
future,
})).unwrap();
}
}else{
let future = Self::image_future(path.as_ref()).map(|img| (img, cubemap_info)).boxed_local();
self.async_sender.unbounded_send(ResourceType::ImageCubemap(CubemapFuture{
future,
})).unwrap();
}
cubemap_ref
}
}
#[cfg(all(not(feature="web"), any(image_ty="image", image_ty="freeimage")))]
pub fn load_equirectangular_levels_cubemap<P>(
&mut self,
paths: &[P],
creation_flags: TextureCreationFlags,
color_format: glin::fbo::ColorFormat,
renderer: &gl::Renderer) -> util::Result<CubemapRef>
where P: AsRef<Path>
{
let mut size = 0;
let levels = paths.iter().try_fold::<_, _, util::Result<_>>(vec![], |mut levels, path|{
let level = Self::preload_image(path, renderer)?;
if size == 0{
size = level.height() as u32;
}
levels.push(level);
Ok(levels)
})?;
let path = paths[0].as_ref().to_str().unwrap();
if let Some(cubemapref) = self.name_cubemap_index.get(path){
let cubemap = &mut self.cubemaps[*cubemapref];
if levels[0].height() as u32 != cubemap.width()
|| levels[0].height() as u32 != cubemap.height()
|| levels.len() as u32 != cubemap.levels()
{
*cubemap = renderer.new_cubemap().from_format(glin::cubemap::Format{
internal_format: color_format as GLenum,
width: levels[0].height() as u32,
height: levels[0].height() as u32,
levels: levels.len() as u32,
}).map_err(|e| util::Error::with_cause("Error loading cubemap texture", e))?;
}
gl::image_based_light::copy_equirectangular_levels_to_cubemap(
levels,
cubemap,
renderer);
Ok(*cubemapref)
}else{
let mut cubemap = gl::image_based_light::equirectangular_levels_to_cubemap(
levels,
size,
color_format,
renderer);
if creation_flags.contains(TextureCreationFlags::MIPMAPS){
cubemap.generate_mipmaps();
}
Ok(self.insert_named_cubemap(path, cubemap))
}
}
#[cfg(all(feature="async", any(image_ty="image", image_ty="freeimage")))]
pub fn load_equirectangular_levels_cubemap_async<P>(
&mut self,
paths: &[P],
creation_flags: TextureCreationFlags,
color_format: glin::fbo::ColorFormat) -> ResourceFuture<CubemapRef>
where P: AsRef<Path>
{
let url = paths[0].as_ref().to_str().unwrap();
if let Some(cubemap_ref) = self.name_cubemap_index.get(url){
ResourceFuture::with(Ok(*cubemap_ref))
}else{
let cubemap_ref = ResourceFuture::new();
let cubemap_info = CubemapInfo{
creation_flags,
color_format,
cubemap_ref: cubemap_ref.clone(),
name: url.to_owned(),
};
if let Some("hdr") = paths[0].as_ref().extension().and_then(|s| s.to_str()){
#[cfg(image_ty = "image")]
{
let futures = paths.into_iter().map(|path| {
Self::hdr_future(path.as_ref())
});
let future = futures::future::try_join_all(futures)
.map(|levels| (levels, cubemap_info))
.boxed_local();
self.async_sender.unbounded_send(ResourceType::HdrLevelsCubemap(CubemapLevelsFuture{
future,
})).unwrap();
}
#[cfg(image_ty = "freeimage")]
{
let futures = paths.into_iter().map(|path| {
Self::image_future(path.as_ref())
});
let future = futures::future::try_join_all(futures)
.map(|levels| (levels, cubemap_info))
.boxed_local();
self.async_sender.unbounded_send(ResourceType::ImageLevelsCubemap(CubemapLevelsFuture{
future,
})).unwrap();
}
}else{
let futures = paths.into_iter().map(|path| {
Self::image_future(path.as_ref())
});
let future = futures::future::try_join_all(futures)
.map(|levels| (levels, cubemap_info))
.boxed_local();
self.async_sender.unbounded_send(ResourceType::ImageLevelsCubemap(CubemapLevelsFuture{
future,
})).unwrap();
}
cubemap_ref
}
}
}
#[cfg(feature="async")]
pub struct TexturesPoolUpdater{
async_receiver: UnboundedReceiver<ResourceType>,
}
#[cfg(feature = "async")]
impl CreationSystemAsync for TexturesPoolUpdater{
#[cfg(feature="async")]
fn run(mut self, scene: CreationProxy) -> Pin<Box<dyn Future<Output = ()>>>{
async move{
while let Some(resource) = self.async_receiver.next().await {
match resource {
#[cfg(image_ty="image")]
ResourceType::Hdr(hdr) => {
let (hdr, mut tex_info) = hdr.future.await;
match hdr {
Ok(hdr) => {
let gl = scene.resource::<gl::Renderer>().unwrap();
let img = if tex_info.creation_flags.contains(TextureCreationFlags::MIPMAPS){
gl.new_texture().from_image_allocate_max_levels(&hdr, gl::TEXTURE_2D)
}else{
gl.new_texture().from_image(&hdr, gl::TEXTURE_2D)
};
match img {
Ok(mut texture) => {
if tex_info.creation_flags.contains(TextureCreationFlags::MIPMAPS){
texture.generate_mipmaps()
}
let mut textures_pool = scene.resource_mut::<TexturesPool>().unwrap();
let texture_ref = textures_pool.textures.insert_key_gen(texture);
tex_info.texture_ref.ready(Ok(texture_ref));
textures_pool.name_texture_index.insert(tex_info.name.clone(), texture_ref);
},
Err(err) => {
log::error!("{}", err);
let err = util::Error::with_cause("Error loading texture asynchornously", err);
tex_info.texture_ref.ready(Err(err));
},
}
}
Err(err) => {
log::error!("{}", err);
tex_info.texture_ref.ready(Err(err));
}
}
}
#[cfg(image_enabled)]
ResourceType::Image(img) => {
let (img, mut tex_info) = img.future.await;
match img {
Ok(mut img) => {
let gl = scene.resource::<gl::Renderer>().unwrap();
if tex_info.creation_flags.contains(TextureCreationFlags::PREMULTIPLY_ALPHA){
img.premultiply_alpha();
}
let img = if tex_info.creation_flags.contains(TextureCreationFlags::MIPMAPS){
gl.new_texture().from_image_allocate_max_levels(&img, gl::TEXTURE_2D)
}else{
gl.new_texture().from_image(&img, gl::TEXTURE_2D)
};
match img {
Ok(mut texture) => {
if tex_info.creation_flags.contains(TextureCreationFlags::MIPMAPS){
texture.generate_mipmaps()
}
let mut textures_pool = scene.resource_mut::<TexturesPool>().unwrap();
let texture_ref = textures_pool.textures.insert_key_gen(texture);
tex_info.texture_ref.ready(Ok(texture_ref));
textures_pool.name_texture_index.insert(tex_info.name.clone(), texture_ref);
},
Err(err) => {
log::error!("{}", err);
let err = util::Error::with_cause("Error loading texture asynchornously", err);
tex_info.texture_ref.ready(Err(err));
},
}
},
Err(err) => {
log::error!("{}", err);
tex_info.texture_ref.ready(Err(err));
}
}
}
#[cfg(dds)]
ResourceType::Dds(dds) => {
let (dds, mut cubemap_info) = dds.future.await;
match dds {
Ok(dds) => {
let gl = scene.resource::<gl::Renderer>().unwrap();
let img = if cubemap_info.creation_flags.contains(TextureCreationFlags::MIPMAPS){
gl.new_cubemap().from_cubemap_image_level_allocate_mips(&dds, 0)
}else{
gl.new_cubemap().from_cubemap_image(&dds)
};
match img {
Ok(mut cubemap) => {
if cubemap_info.creation_flags.contains(TextureCreationFlags::MIPMAPS){
cubemap.generate_mipmaps()
}
let mut textures_pool = scene.resource_mut::<TexturesPool>().unwrap();
let cubemap_ref = textures_pool.cubemaps.insert_key_gen(cubemap);
cubemap_info.cubemap_ref.ready(Ok(cubemap_ref));
textures_pool.name_cubemap_index.insert(cubemap_info.name.clone(), cubemap_ref);
}
Err(err) => {
log::error!("{}", err);
let err = util::Error::with_cause("Error loading cubemap asynchornously", err);
cubemap_info.cubemap_ref.ready(Err(err));
},
}
},
Err(err) => {
log::error!("{}", err);
cubemap_info.cubemap_ref.ready(Err(err));
}
}
}
#[cfg(image_ty="image")]
ResourceType::HdrCubemap(hdr) => {
let (hdr, mut cubemap_info) = hdr.future.await;
match hdr {
Ok(hdr) => {
let gl = scene.resource::<gl::Renderer>().unwrap();
let img = if cubemap_info.creation_flags.contains(TextureCreationFlags::MIPMAPS){
gl.new_texture().from_image_allocate_max_levels(&hdr, gl::TEXTURE_2D)
}else{
gl.new_texture().from_image(&hdr, gl::TEXTURE_2D)
};
match img {
Ok(texture) => {
let mut cubemap = gl.new_cubemap().from_format(glin::cubemap::Format{
internal_format: cubemap_info.color_format as GLenum,
width: texture.height() as u32,
height: texture.height() as u32,
levels: 1,
}).unwrap();
gl::image_based_light::copy_equirectangular_to_cubemap(
&texture,
&mut cubemap,
&gl);
if cubemap_info.creation_flags.contains(TextureCreationFlags::MIPMAPS){
cubemap.generate_mipmaps()
}
let mut textures_pool = scene.resource_mut::<TexturesPool>().unwrap();
let cubemap_ref = textures_pool.cubemaps.insert_key_gen(cubemap);
cubemap_info.cubemap_ref.ready(Ok(cubemap_ref));
textures_pool.name_cubemap_index.insert(cubemap_info.name.clone(), cubemap_ref);
},
Err(err) => {
log::error!("{}", err);
let err = util::Error::with_cause("Error loading cubemap asynchornously", err);
cubemap_info.cubemap_ref.ready(Err(err));
}
}
},
Err(err) => {
log::error!("{}", err);
cubemap_info.cubemap_ref.ready(Err(err));
}
}
}
#[cfg(any(image_ty="image", image_ty="freeimage"))]
ResourceType::ImageCubemap(img) => {
let (img, mut cubemap_info) = img.future.await;
match img {
Ok(img) => {
let gl = scene.resource::<gl::Renderer>().unwrap();
let img = if cubemap_info.creation_flags.contains(TextureCreationFlags::MIPMAPS){
gl.new_texture().from_image_allocate_max_levels(&img, gl::TEXTURE_2D)
}else{
gl.new_texture().from_image(&img, gl::TEXTURE_2D)
};
match img {
Ok(texture) => {
let mut cubemap = gl.new_cubemap().from_format(glin::cubemap::Format{
internal_format: cubemap_info.color_format as GLenum,
width: texture.height() as u32,
height: texture.height() as u32,
levels: 1,
}).unwrap();
gl::image_based_light::copy_equirectangular_to_cubemap(
&texture,
&mut cubemap,
&gl);
if cubemap_info.creation_flags.contains(TextureCreationFlags::MIPMAPS){
cubemap.generate_mipmaps()
}
let mut textures_pool = scene.resource_mut::<TexturesPool>().unwrap();
let cubemap_ref = textures_pool.cubemaps.insert_key_gen(cubemap);
cubemap_info.cubemap_ref.ready(Ok(cubemap_ref));
textures_pool.name_cubemap_index.insert(cubemap_info.name.clone(), cubemap_ref);
},
Err(err) => {
log::error!("{}", err);
let err = util::Error::with_cause("Error loading cubemap asynchornously", err);
cubemap_info.cubemap_ref.ready(Err(err));
}
}
},
Err(err) => {
log::error!("{}", err);
cubemap_info.cubemap_ref.ready(Err(err));
}
}
}
#[cfg(image_ty="image")]
ResourceType::HdrLevelsCubemap(hdr) => {
let (hdrs, mut cubemap_info) = hdr.future.await;
match hdrs{
Ok(hdrs) => {
let gl = scene.resource::<gl::Renderer>().unwrap();
let levels = hdrs.into_iter().try_fold::<_, _, util::Result<_>>(vec![], |mut levels, img|{
let tex = gl.new_texture().from_image(&img, gl::TEXTURE_2D)
.map_err(|err| util::Error::with_cause("Error loading texture", err))?;
levels.push(tex);
Ok(levels)
});
match levels {
Ok(levels) => {
let mut cubemap = gl.new_cubemap().from_format(glin::cubemap::Format{
internal_format: cubemap_info.color_format as GLenum,
width: levels[0].height(),
height: levels[0].height(),
levels: levels.len() as u32,
}).unwrap();
gl::image_based_light::copy_equirectangular_levels_to_cubemap(
&levels,
&mut cubemap,
&gl);
if cubemap_info.creation_flags.contains(TextureCreationFlags::MIPMAPS){
cubemap.generate_mipmaps()
}
let mut textures_pool = scene.resource_mut::<TexturesPool>().unwrap();
let cubemap_ref = textures_pool.cubemaps.insert_key_gen(cubemap);
cubemap_info.cubemap_ref.ready(Ok(cubemap_ref));
textures_pool.name_cubemap_index.insert(cubemap_info.name.clone(), cubemap_ref);
}
Err(err) => {
log::error!("{}", err);
let err = util::Error::with_cause("Error loading cubemap asynchornously", err);
cubemap_info.cubemap_ref.ready(Err(err));
}
}
}
Err(err) => {
log::error!("{}", err);
cubemap_info.cubemap_ref.ready(Err(err));
}
}
}
#[cfg(any(image_ty="image", image_ty="freeimage"))]
ResourceType::ImageLevelsCubemap(img) => {
let (imgs, mut cubemap_info) = img.future.await;
match imgs{
Ok(imgs) => {
let gl = scene.resource::<gl::Renderer>().unwrap();
let levels = imgs.into_iter().try_fold::<_, _, util::Result<_>>(vec![], |mut levels, img|{
let tex = gl.new_texture().from_image(&img, gl::TEXTURE_2D)
.map_err(|err| util::Error::with_cause("Error loading texture", err))?;
levels.push(tex);
Ok(levels)
});
match levels {
Ok(levels) => {
let mut cubemap = gl.new_cubemap().from_format(glin::cubemap::Format{
internal_format: cubemap_info.color_format as GLenum,
width: levels[0].height(),
height: levels[0].height(),
levels: levels.len() as u32,
}).unwrap();
gl::image_based_light::copy_equirectangular_levels_to_cubemap(
&levels,
&mut cubemap,
&gl);
if cubemap_info.creation_flags.contains(TextureCreationFlags::MIPMAPS){
cubemap.generate_mipmaps()
}
let mut textures_pool = scene.resource_mut::<TexturesPool>().unwrap();
let cubemap_ref = textures_pool.cubemaps.insert_key_gen(cubemap);
cubemap_info.cubemap_ref.ready(Ok(cubemap_ref));
textures_pool.name_cubemap_index.insert(cubemap_info.name.clone(), cubemap_ref);
}
Err(err) => {
log::error!("{}", err);
let err = util::Error::with_cause("Error loading cubemap asynchornously", err);
cubemap_info.cubemap_ref.ready(Err(err));
}
}
}
Err(err) => {
log::error!("{}", err);
cubemap_info.cubemap_ref.ready(Err(err));
}
}
}
}
}}.boxed_local()
}
}
impl Index<TextureRef> for TexturesPool{
type Output = glin::Texture;
fn index(&self, textureref: TextureRef) -> &glin::Texture{
&self.textures[textureref]
}
}
impl IndexMut<TextureRef> for TexturesPool{
fn index_mut(&mut self, textureref: TextureRef) -> &mut glin::Texture{
&mut self.textures[textureref]
}
}
impl Index<CubemapRef> for TexturesPool{
type Output = glin::CubeMap;
fn index(&self, cubemapref: CubemapRef) -> &glin::CubeMap{
&self.cubemaps[cubemapref]
}
}
impl IndexMut<CubemapRef> for TexturesPool{
fn index_mut(&mut self, cubemapref: CubemapRef) -> &mut glin::CubeMap{
&mut self.cubemaps[cubemapref]
}
}
impl Index<SamplerRef> for TexturesPool{
type Output = glin::Sampler;
fn index(&self, samplerref: SamplerRef) -> &glin::Sampler{
&self.samplers[samplerref]
}
}
impl IndexMut<SamplerRef> for TexturesPool{
fn index_mut(&mut self, samplerref: SamplerRef) -> &mut glin::Sampler{
&mut self.samplers[samplerref]
}
}