* 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:
parent
22377bb255
commit
d3e48d8c31
|
|
@ -82,6 +82,7 @@ public:
|
|||
GLint m_texture5;
|
||||
GLint m_texture6;
|
||||
GLint m_texture7;
|
||||
GLint m_textureSkyBox;
|
||||
Vector4 m_colour;
|
||||
GLenum m_blend_src, m_blend_dst;
|
||||
GLenum m_depthfunc;
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ public:
|
|||
virtual void DecRef() = 0;
|
||||
// get/set the qtexture_t* Radiant uses to represent this shader object
|
||||
virtual qtexture_t* getTexture() const = 0;
|
||||
virtual qtexture_t* getSkyBox() = 0;
|
||||
virtual qtexture_t* getDiffuse() const = 0;
|
||||
virtual qtexture_t* getBump() const = 0;
|
||||
virtual qtexture_t* getSpecular() const = 0;
|
||||
|
|
|
|||
|
|
@ -32,8 +32,9 @@ class LoadImageCallback
|
|||
public:
|
||||
void* m_environment;
|
||||
LoadFunc m_func;
|
||||
bool m_skybox;
|
||||
|
||||
LoadImageCallback( void* environment, LoadFunc func ) : m_environment( environment ), m_func( func ){
|
||||
LoadImageCallback( void* environment, LoadFunc func, bool skybox = false ) : m_environment( environment ), m_func( func ), m_skybox( skybox ){
|
||||
}
|
||||
Image* loadImage( const char* name ) const {
|
||||
return m_func( m_environment, name );
|
||||
|
|
|
|||
|
|
@ -279,6 +279,7 @@ public:
|
|||
ShaderParameters m_params;
|
||||
|
||||
TextureExpression m_textureName;
|
||||
TextureExpression m_skyBox;
|
||||
TextureExpression m_diffuse;
|
||||
TextureExpression m_bump;
|
||||
ShaderValue m_heightmapScale;
|
||||
|
|
@ -838,6 +839,7 @@ class CShader : public IShader
|
|||
CopiedString m_Name;
|
||||
|
||||
qtexture_t* m_pTexture;
|
||||
qtexture_t* m_pSkyBox;
|
||||
qtexture_t* m_notfound;
|
||||
qtexture_t* m_pDiffuse;
|
||||
float m_heightmapScale;
|
||||
|
|
@ -860,6 +862,7 @@ public:
|
|||
m_blendFunc( BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA ),
|
||||
m_bInUse( false ){
|
||||
m_pTexture = 0;
|
||||
m_pSkyBox = 0;
|
||||
m_pDiffuse = 0;
|
||||
m_pBump = 0;
|
||||
m_pSpecular = 0;
|
||||
|
|
@ -893,6 +896,13 @@ public:
|
|||
qtexture_t* getTexture() const {
|
||||
return m_pTexture;
|
||||
}
|
||||
qtexture_t* getSkyBox() override {
|
||||
/* load skybox if only used */
|
||||
if( m_pSkyBox == nullptr && !m_template.m_skyBox.empty() )
|
||||
m_pSkyBox = GlobalTexturesCache().capture( LoadImageCallback( 0, GlobalTexturesCache().defaultLoader().m_func, true ), m_template.m_skyBox.c_str() );
|
||||
|
||||
return m_pSkyBox;
|
||||
}
|
||||
qtexture_t* getDiffuse() const {
|
||||
return m_pDiffuse;
|
||||
}
|
||||
|
|
@ -966,6 +976,10 @@ public:
|
|||
GlobalTexturesCache().release( m_notfound );
|
||||
}
|
||||
|
||||
if ( m_pSkyBox != 0 ) {
|
||||
GlobalTexturesCache().release( m_pSkyBox );
|
||||
}
|
||||
|
||||
unrealiseLighting();
|
||||
}
|
||||
|
||||
|
|
@ -1220,6 +1234,18 @@ bool ShaderTemplate::parseQuake3( Tokeniser& tokeniser ){
|
|||
|
||||
RETURN_FALSE_IF_FAIL( Tokeniser_getFloat( tokeniser, m_AlphaRef ) );
|
||||
}
|
||||
else if ( string_equal_nocase( token, "skyparms" ) ) {
|
||||
const char* sky = tokeniser.getToken();
|
||||
|
||||
if ( sky == 0 ) {
|
||||
Tokeniser_unexpectedError( tokeniser, sky, "#skyparms" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !string_equal( sky, "-" ) ){
|
||||
m_skyBox = sky;
|
||||
}
|
||||
}
|
||||
else if ( string_equal_nocase( token, "cull" ) ) {
|
||||
const char* cull = tokeniser.getToken();
|
||||
|
||||
|
|
|
|||
|
|
@ -2091,7 +2091,8 @@ void CamWnd::Cam_Draw(){
|
|||
| RENDER_LIGHTING
|
||||
| RENDER_TEXTURE
|
||||
| RENDER_SMOOTH
|
||||
| RENDER_SCALED;
|
||||
| RENDER_SCALED
|
||||
| RENDER_PROGRAM;
|
||||
break;
|
||||
case cd_lighting:
|
||||
globalstate |= RENDER_FILL
|
||||
|
|
|
|||
|
|
@ -365,6 +365,69 @@ public:
|
|||
GLSLDepthFillProgram g_depthFillGLSL;
|
||||
|
||||
|
||||
class GLSLSkyboxProgram : public GLProgram
|
||||
{
|
||||
public:
|
||||
GLhandleARB m_program;
|
||||
GLint u_view_origin;
|
||||
|
||||
GLSLSkyboxProgram() : m_program( 0 ){
|
||||
}
|
||||
|
||||
void create(){
|
||||
// create program
|
||||
m_program = glCreateProgramObjectARB();
|
||||
|
||||
// create shader
|
||||
{
|
||||
StringOutputStream filename( 256 );
|
||||
createShader( m_program, filename( GlobalRadiant().getAppPath(), "gl/skybox_vp.glsl" ), GL_VERTEX_SHADER_ARB );
|
||||
createShader( m_program, filename( GlobalRadiant().getAppPath(), "gl/skybox_fp.glsl" ), GL_FRAGMENT_SHADER_ARB );
|
||||
}
|
||||
|
||||
GLSLProgram_link( m_program );
|
||||
GLSLProgram_validate( m_program );
|
||||
|
||||
glUseProgramObjectARB( m_program );
|
||||
|
||||
u_view_origin = glGetUniformLocationARB( m_program, "u_view_origin" );
|
||||
|
||||
glUseProgramObjectARB( 0 );
|
||||
|
||||
GlobalOpenGL_debugAssertNoErrors();
|
||||
}
|
||||
|
||||
void destroy(){
|
||||
glDeleteObjectARB( m_program );
|
||||
m_program = 0;
|
||||
}
|
||||
|
||||
void enable(){
|
||||
glUseProgramObjectARB( m_program );
|
||||
|
||||
GlobalOpenGL_debugAssertNoErrors();
|
||||
|
||||
debug_string( "enable skybox" );
|
||||
}
|
||||
|
||||
void disable(){
|
||||
glUseProgramObjectARB( 0 );
|
||||
|
||||
GlobalOpenGL_debugAssertNoErrors();
|
||||
|
||||
debug_string( "disable skybox" );
|
||||
}
|
||||
|
||||
void setParameters( const Vector3& viewer, const Matrix4& localToWorld, const Vector3& origin, const Vector3& colour, const Matrix4& world2light ){
|
||||
glUniform3fARB( u_view_origin, viewer.x(), viewer.y(), viewer.z() );
|
||||
|
||||
GlobalOpenGL_debugAssertNoErrors();
|
||||
}
|
||||
};
|
||||
|
||||
GLSLSkyboxProgram g_skyboxGLSL;
|
||||
|
||||
|
||||
// ARB path
|
||||
|
||||
void createProgram( const char* filename, GLenum type ){
|
||||
|
|
@ -790,6 +853,9 @@ inline bool OpenGLState_less( const OpenGLState& self, const OpenGLState& other
|
|||
if ( self.m_texture7 != other.m_texture7 ) {
|
||||
return self.m_texture7 < other.m_texture7;
|
||||
}
|
||||
if ( self.m_textureSkyBox != other.m_textureSkyBox ) {
|
||||
return self.m_textureSkyBox < other.m_textureSkyBox;
|
||||
}
|
||||
//! Sort by state bit-vector.
|
||||
if ( self.m_state != other.m_state ) {
|
||||
return self.m_state < other.m_state;
|
||||
|
|
@ -809,6 +875,7 @@ void OpenGLState_constructDefault( OpenGLState& state ){
|
|||
state.m_texture5 = 0;
|
||||
state.m_texture6 = 0;
|
||||
state.m_texture7 = 0;
|
||||
state.m_textureSkyBox = 0;
|
||||
|
||||
state.m_colour[0] = 1;
|
||||
state.m_colour[1] = 1;
|
||||
|
|
@ -1158,7 +1225,7 @@ class OpenGLShaderCache final : public ShaderCache, public TexturesCacheObserver
|
|||
|
||||
bool m_lightingEnabled;
|
||||
bool m_lightingSupported;
|
||||
bool m_useShaderLanguage;
|
||||
const bool m_useShaderLanguage;
|
||||
|
||||
public:
|
||||
OpenGLShaderCache() :
|
||||
|
|
@ -1302,8 +1369,9 @@ public:
|
|||
}
|
||||
debug_string( "end rendering" );
|
||||
|
||||
OpenGLState reset = current; /* popmatrix after RENDER_TEXT */
|
||||
reset.m_state = current.m_state & ~RENDER_TEXT;
|
||||
OpenGLState reset = current; /* reset some states */
|
||||
reset.m_state = current.m_state & ~RENDER_TEXT; /* popmatrix after RENDER_TEXT */
|
||||
reset.m_program = nullptr; /* disable shader */
|
||||
OpenGLState_apply( reset, current, globalstate );
|
||||
}
|
||||
void realise(){
|
||||
|
|
@ -1320,6 +1388,9 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
if( lightingSupported() )
|
||||
g_skyboxGLSL.create();
|
||||
|
||||
for ( Shaders::iterator i = m_shaders.begin(); i != m_shaders.end(); ++i )
|
||||
{
|
||||
if ( !( *i ).value.empty() ) {
|
||||
|
|
@ -1347,6 +1418,8 @@ public:
|
|||
g_depthFillARB.destroy();
|
||||
}
|
||||
}
|
||||
if( GlobalOpenGL().contextValid && lightingSupported() )
|
||||
g_skyboxGLSL.destroy();
|
||||
}
|
||||
}
|
||||
bool realised(){
|
||||
|
|
@ -1645,7 +1718,7 @@ void OpenGLState_apply( const OpenGLState& self, OpenGLState& current, unsigned
|
|||
if ( program != current.m_program ) {
|
||||
if ( current.m_program != 0 ) {
|
||||
current.m_program->disable();
|
||||
glColor4fv( vector4_to_array( current.m_colour ) );
|
||||
//why? glColor4fv( vector4_to_array( current.m_colour ) );
|
||||
debug_colour( "cleaning program" );
|
||||
}
|
||||
|
||||
|
|
@ -1888,6 +1961,16 @@ void OpenGLState_apply( const OpenGLState& self, OpenGLState& current, unsigned
|
|||
}
|
||||
|
||||
|
||||
if( current.m_textureSkyBox != self.m_textureSkyBox ){
|
||||
if ( GlobalOpenGL().GL_1_3() ) {
|
||||
glActiveTexture( GL_TEXTURE0 );
|
||||
glClientActiveTexture( GL_TEXTURE0 );
|
||||
}
|
||||
glBindTexture( GL_TEXTURE_CUBE_MAP, self.m_textureSkyBox );
|
||||
GlobalOpenGL_debugAssertNoErrors();
|
||||
current.m_textureSkyBox = self.m_textureSkyBox;
|
||||
}
|
||||
|
||||
if ( state & RENDER_TEXTURE && self.m_colour[3] != current.m_colour[3] ) {
|
||||
debug_colour( "setting alpha" );
|
||||
glColor4f( 1,1,1,self.m_colour[3] );
|
||||
|
|
@ -1932,6 +2015,11 @@ void OpenGLState_apply( const OpenGLState& self, OpenGLState& current, unsigned
|
|||
void Renderables_flush( OpenGLStateBucket::Renderables& renderables, OpenGLState& current, unsigned int globalstate, const Vector3& viewer ){
|
||||
const Matrix4* transform = 0;
|
||||
glPushMatrix();
|
||||
|
||||
if ( current.m_program != 0 && current.m_textureSkyBox != 0 && globalstate & RENDER_PROGRAM ) {
|
||||
current.m_program->setParameters( viewer, g_matrix4_identity, g_vector3_identity, g_vector3_identity, g_matrix4_identity );
|
||||
}
|
||||
|
||||
for ( OpenGLStateBucket::Renderables::const_iterator i = renderables.begin(); i != renderables.end(); ++i )
|
||||
{
|
||||
//qglLoadMatrixf(i->m_transform);
|
||||
|
|
@ -2421,6 +2509,19 @@ void OpenGLShader::construct( const char* name ){
|
|||
bumpPass.m_blend_src = GL_ONE;
|
||||
bumpPass.m_blend_dst = GL_ONE;
|
||||
}
|
||||
// g_ShaderCache->lightingSupported() as in GLSL is available
|
||||
else if( m_shader->getSkyBox() != nullptr && m_shader->getSkyBox()->texture_number != 0 && g_ShaderCache->lightingSupported() )
|
||||
{
|
||||
state.m_texture = m_shader->getTexture()->texture_number;
|
||||
state.m_textureSkyBox = m_shader->getSkyBox()->texture_number;
|
||||
|
||||
state.m_state = RENDER_FILL | RENDER_CULLFACE | RENDER_TEXTURE | RENDER_DEPTHTEST | RENDER_DEPTHWRITE | RENDER_COLOURWRITE | RENDER_PROGRAM;
|
||||
state.m_colour.vec3() = m_shader->getTexture()->color;
|
||||
state.m_colour[3] = 1.0f;
|
||||
state.m_sort = OpenGLState::eSortFullbright;
|
||||
|
||||
state.m_program = &g_skyboxGLSL;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.m_texture = m_shader->getTexture()->texture_number;
|
||||
|
|
@ -2461,7 +2562,7 @@ void OpenGLShader::construct( const char* name ){
|
|||
break;
|
||||
}
|
||||
}
|
||||
reinterpret_cast<Vector3&>( state.m_colour ) = m_shader->getTexture()->color;
|
||||
state.m_colour.vec3() = m_shader->getTexture()->color;
|
||||
state.m_colour[3] = 1.0f;
|
||||
|
||||
if ( ( m_shader->getFlags() & QER_TRANS ) != 0 ) {
|
||||
|
|
|
|||
|
|
@ -209,6 +209,7 @@ void R_ResampleTexture( const void *indata, int inwidth, int inheight, void *out
|
|||
oldy = yi;
|
||||
}
|
||||
memcpy( out, row1, outwidth4 );
|
||||
out += outwidth4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -297,6 +298,7 @@ void R_ResampleTexture( const void *indata, int inwidth, int inheight, void *out
|
|||
oldy = yi;
|
||||
}
|
||||
memcpy( out, row1, outwidth3 );
|
||||
out += outwidth3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(); } );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1313,8 +1313,8 @@ void BackgroundImage::render( const VIEWTYPE viewtype ){
|
|||
glDisable( GL_DEPTH_TEST );
|
||||
|
||||
glBindTexture( GL_TEXTURE_2D, _tex );
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
|
||||
|
||||
glBegin( GL_QUADS );
|
||||
|
||||
|
|
|
|||
8
setup/data/tools/gl/skybox_fp.glsl
Normal file
8
setup/data/tools/gl/skybox_fp.glsl
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
uniform samplerCube skybox;
|
||||
|
||||
void main()
|
||||
{
|
||||
//doing rotation/flip to fix skybox orientation
|
||||
gl_FragColor = textureCube( skybox, vec3( -gl_TexCoord[0].y, gl_TexCoord[0].z, gl_TexCoord[0].x ) );
|
||||
}
|
||||
8
setup/data/tools/gl/skybox_vp.glsl
Normal file
8
setup/data/tools/gl/skybox_vp.glsl
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
uniform vec3 u_view_origin;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_TexCoord[0] = vec4( ( gl_Vertex.xyz - u_view_origin ), 1 );
|
||||
gl_Position = ftransform();
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user