* render Q3 shader based skyboxes

logic: load 6 skybox textures when shader gets used by scene, don't unload dynamically, just on 'flush'
texture browser only uses normal preview image and doesn't trigger potentially heavy box loading
also fix R_ResampleTexture for [2+x upscaling
This commit is contained in:
Garux
2022-07-18 10:05:19 +03:00
parent 22377bb255
commit d3e48d8c31
11 changed files with 219 additions and 21 deletions

View File

@@ -33,6 +33,7 @@
#include "container/hashfunc.h"
#include "container/cache.h"
#include "generic/callback.h"
#include "stream/stringstream.h"
#include "stringio.h"
#include "image.h"
@@ -334,19 +335,67 @@ typedef std::pair<LoadImageCallback, CopiedString> TextureKey;
void qtexture_realise( qtexture_t& texture, const TextureKey& key ){
texture.texture_number = 0;
if ( !string_empty( key.second.c_str() ) ) {
Image* image = key.first.loadImage( key.second.c_str() );
if ( image != 0 ) {
LoadTextureRGBA( &texture, image->getRGBAPixels(), image->getWidth(), image->getHeight() );
texture.surfaceFlags = image->getSurfaceFlags();
texture.contentFlags = image->getContentFlags();
texture.value = image->getValue();
image->release();
globalOutputStream() << "Loaded Texture: \"" << key.second << "\"\n";
GlobalOpenGL_debugAssertNoErrors();
if( !key.first.m_skybox ){
Image* image = key.first.loadImage( key.second.c_str() );
if ( image != 0 ) {
LoadTextureRGBA( &texture, image->getRGBAPixels(), image->getWidth(), image->getHeight() );
texture.surfaceFlags = image->getSurfaceFlags();
texture.contentFlags = image->getContentFlags();
texture.value = image->getValue();
image->release();
globalOutputStream() << "Loaded Texture: \"" << key.second << "\"\n";
GlobalOpenGL_debugAssertNoErrors();
}
else
{
globalErrorStream() << "Texture load failed: \"" << key.second << "\"\n";
}
}
else
{
globalErrorStream() << "Texture load failed: \"" << key.second << "\"\n";
else {
Image *images[6]{};
/* load in order, so that Q3 cubemap is seamless in openGL, but rotated & flipped; fix misorientation in shader later */
const char *suffixes[] = { "_ft", "_bk", "_up", "_dn", "_rt", "_lf" };
for( int i = 0; i < 6; ++i ){
images[i] = key.first.loadImage( StringOutputStream( 64 )( key.second, suffixes[i] ) );
}
if( std::all_of( images, images + std::size( images ), []( const Image *img ){ return img != nullptr; } ) ){
glGenTextures( 1, &texture.texture_number );
glBindTexture( GL_TEXTURE_CUBE_MAP, texture.texture_number );
glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP, GL_FALSE );
glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0 );
glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 0); //this or mipmaps are required for samplerCube to work
// fix non quadratic, varying sizes; GL_TEXTURE_CUBE_MAP requires this
unsigned int size = 0;
for( const auto img : images )
size = std::max( { size, img->getWidth(), img->getHeight() } );
for( int i = 0; i < 6; ++i ){
const Image& img = *images[i];
byte *pix = img.getRGBAPixels();
if( img.getWidth() != size || img.getHeight() != size ){
pix = static_cast<byte*>( malloc( size * size * 4 ) );
R_ResampleTexture( img.getRGBAPixels(), img.getWidth(), img.getHeight(), pix, size, size, 4 );
}
glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, g_texture_globals.texture_components, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, pix );
if( pix != img.getRGBAPixels() )
free( pix );
}
glBindTexture( GL_TEXTURE_CUBE_MAP, 0 );
globalOutputStream() << "Loaded Skybox: \"" << key.second << "\"\n";
GlobalOpenGL_debugAssertNoErrors();
}
else
{
globalErrorStream() << "Skybox load failed: \"" << key.second << "\"\n";
}
std::for_each_n( images, std::size( images ), []( Image *img ){ if( img != nullptr ) img->release(); } );
}
}
}