mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 08:11:10 +02:00
235 lines
8.4 KiB
C++
235 lines
8.4 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Render-world terrain drawing
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "tr_local.h"
|
|
|
|
static byte RB_TerrainWeightToByte( float value ) {
|
|
return (byte)idMath::ClampInt( 0, 255, (int)floorf( idMath::ClampFloat( 0.0f, 1.0f, value ) * 255.0f + 0.5f ) );
|
|
}
|
|
|
|
static void RB_TerrainFillWeightPixels( idTerrainPatch *terrain, int minX, int minY, int uploadWidth, int uploadHeight, idList<byte> &pixels ) {
|
|
pixels.SetNum( uploadWidth * uploadHeight * 4 );
|
|
for ( int y = 0; y < uploadHeight; y++ ) {
|
|
for ( int x = 0; x < uploadWidth; x++ ) {
|
|
const int sample = terrain->PaintIndex( minX + x, minY + y );
|
|
const int pixel = ( y * uploadWidth + x ) * 4;
|
|
const idVec4 weights = idTerrainPatch::NormalizeLayerWeight( terrain->paintWeights[sample] );
|
|
pixels[pixel + 0] = RB_TerrainWeightToByte( weights.x );
|
|
pixels[pixel + 1] = RB_TerrainWeightToByte( weights.y );
|
|
pixels[pixel + 2] = RB_TerrainWeightToByte( weights.z );
|
|
pixels[pixel + 3] = RB_TerrainWeightToByte( weights.w );
|
|
}
|
|
}
|
|
}
|
|
|
|
static GLuint RB_TerrainUpdateWeightTexture( idTerrainPatch *terrain ) {
|
|
terrain->EnsureLayerData( "textures/common/caulk" );
|
|
|
|
bool newTexture = false;
|
|
if ( terrain->paintTexture == 0 ) {
|
|
glGenTextures( 1, &terrain->paintTexture );
|
|
terrain->MarkPaintTextureDirty();
|
|
newTexture = true;
|
|
}
|
|
if ( !terrain->paintTextureDirty ) {
|
|
return terrain->paintTexture;
|
|
}
|
|
|
|
const int minX = idMath::ClampInt( 0, terrain->paintWidth - 1, terrain->paintDirtyMinX );
|
|
const int minY = idMath::ClampInt( 0, terrain->paintHeight - 1, terrain->paintDirtyMinY );
|
|
const int maxX = idMath::ClampInt( 0, terrain->paintWidth - 1, terrain->paintDirtyMaxX );
|
|
const int maxY = idMath::ClampInt( 0, terrain->paintHeight - 1, terrain->paintDirtyMaxY );
|
|
const bool validRect = maxX >= minX && maxY >= minY;
|
|
const bool fullUpload = newTexture || !validRect ||
|
|
( minX == 0 && minY == 0 && maxX == terrain->paintWidth - 1 && maxY == terrain->paintHeight - 1 );
|
|
const int uploadX = fullUpload ? 0 : minX;
|
|
const int uploadY = fullUpload ? 0 : minY;
|
|
const int uploadWidth = fullUpload ? terrain->paintWidth : ( maxX - minX + 1 );
|
|
const int uploadHeight = fullUpload ? terrain->paintHeight : ( maxY - minY + 1 );
|
|
|
|
GL_SelectTexture( 0 );
|
|
glBindTexture( GL_TEXTURE_2D, terrain->paintTexture );
|
|
backEnd.glState.tmu[backEnd.glState.currenttmu].current2DMap = terrain->paintTexture;
|
|
backEnd.glState.tmu[backEnd.glState.currenttmu].textureType = TT_2D;
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
|
|
|
|
idList<byte> pixels;
|
|
RB_TerrainFillWeightPixels( terrain, uploadX, uploadY, uploadWidth, uploadHeight, pixels );
|
|
if ( fullUpload ) {
|
|
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, terrain->paintWidth, terrain->paintHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.Ptr() );
|
|
} else {
|
|
glTexSubImage2D( GL_TEXTURE_2D, 0, uploadX, uploadY, uploadWidth, uploadHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels.Ptr() );
|
|
}
|
|
|
|
terrain->ClearPaintTextureDirtyRect();
|
|
return terrain->paintTexture;
|
|
}
|
|
|
|
static const idMaterial *RB_TerrainLayerMaterial( const char *materialName ) {
|
|
if ( !materialName || !materialName[0] ) {
|
|
return NULL;
|
|
}
|
|
const idMaterial *material = declManager->FindMaterial( materialName, false );
|
|
if ( !material || material->TestMaterialFlag( MF_DEFAULTED ) ) {
|
|
return NULL;
|
|
}
|
|
return material;
|
|
}
|
|
|
|
static idImage *RB_TerrainLayerDiffuseImage( const char *materialName ) {
|
|
const idMaterial *material = RB_TerrainLayerMaterial( materialName );
|
|
if ( !material ) {
|
|
return NULL;
|
|
}
|
|
idImage *image = material->GetDiffuseImage( NULL );
|
|
return image ? image : material->GetEditorImage();
|
|
}
|
|
|
|
static idImage *RB_TerrainLayerNormalImage( const char *materialName ) {
|
|
idImage *flatNormal = globalImages->flatNormalMap ? globalImages->flatNormalMap : globalImages->whiteImage;
|
|
const idMaterial *material = RB_TerrainLayerMaterial( materialName );
|
|
if ( !material ) {
|
|
return flatNormal;
|
|
}
|
|
const shaderStage_t *bumpStage = material->GetBumpStage();
|
|
if ( !bumpStage || !bumpStage->texture.image ) {
|
|
return flatNormal;
|
|
}
|
|
return bumpStage->texture.image;
|
|
}
|
|
|
|
static void RB_TerrainBindLayerTextures( idTerrainPatch *terrain ) {
|
|
terrain->EnsureLayerData( "textures/common/caulk" );
|
|
|
|
idImage *baseImage = RB_TerrainLayerDiffuseImage( terrain->layerMaterials[0].c_str() );
|
|
if ( !baseImage ) {
|
|
baseImage = globalImages->whiteImage;
|
|
}
|
|
|
|
for ( int layer = 0; layer < TERRAIN_MAX_LAYERS; layer++ ) {
|
|
idImage *image = RB_TerrainLayerDiffuseImage( terrain->layerMaterials[layer].c_str() );
|
|
if ( !image ) {
|
|
image = baseImage;
|
|
}
|
|
GL_SelectTexture( 0 );
|
|
image->Bind();
|
|
glTerrainLayerTextureQD3D12( layer, image->texnum );
|
|
|
|
idImage *normalImage = RB_TerrainLayerNormalImage( terrain->layerMaterials[layer].c_str() );
|
|
if ( !normalImage ) {
|
|
normalImage = globalImages->flatNormalMap ? globalImages->flatNormalMap : globalImages->whiteImage;
|
|
}
|
|
normalImage->Bind();
|
|
glTerrainLayerNormalTextureQD3D12( layer, normalImage->texnum );
|
|
}
|
|
|
|
glTerrainWeightTextureQD3D12( RB_TerrainUpdateWeightTexture( terrain ) );
|
|
}
|
|
|
|
static void RB_TerrainClearLayerTextureBindings() {
|
|
glTerrainBlendEnableQD3D12( GL_FALSE );
|
|
glTerrainBlendOpacityQD3D12( 1.0f );
|
|
glTerrainWeightTextureQD3D12( 0 );
|
|
for ( int layer = 0; layer < TERRAIN_MAX_LAYERS; layer++ ) {
|
|
glTerrainLayerTextureQD3D12( layer, 0 );
|
|
glTerrainLayerNormalTextureQD3D12( layer, 0 );
|
|
}
|
|
}
|
|
|
|
static void RB_TerrainSubmitVertex( const idTerrainPatch *terrain, int x, int y ) {
|
|
idVec3 vertex = terrain->Vertex( x, y );
|
|
idVec3 normal = terrain->VertexNormal( x, y );
|
|
const float u = (float)x / (float)( terrain->width - 1 );
|
|
const float v = (float)y / (float)( terrain->height - 1 );
|
|
const float layerS = terrain->WorldWidth() * u / 256.0f;
|
|
const float layerT = terrain->WorldHeight() * v / 256.0f;
|
|
const int paintX = idMath::ClampInt( 0, terrain->paintWidth - 1, (int)floorf( u * (float)( terrain->paintWidth - 1 ) + 0.5f ) );
|
|
const int paintY = idMath::ClampInt( 0, terrain->paintHeight - 1, (int)floorf( v * (float)( terrain->paintHeight - 1 ) + 0.5f ) );
|
|
const idVec4 weights = idTerrainPatch::NormalizeLayerWeight( terrain->SamplePaintWeight( paintX, paintY ) );
|
|
|
|
glColor4fv( weights.ToFloatPtr() );
|
|
glNormal3fv( normal.ToFloatPtr() );
|
|
glMultiTexCoord2fARB( GL_TEXTURE0_ARB, layerS, layerT );
|
|
glMultiTexCoord2fARB( GL_TEXTURE1_ARB, u, v );
|
|
glVertex3fv( vertex.ToFloatPtr() );
|
|
}
|
|
|
|
static bool RB_TerrainCullPatch( const idTerrainPatch *terrain, const viewDef_t *viewDef ) {
|
|
if ( !terrain || !viewDef ) {
|
|
return true;
|
|
}
|
|
return R_CullLocalBox( terrain->bounds, viewDef->worldSpace.modelMatrix, 5, viewDef->frustum );
|
|
}
|
|
|
|
static void RB_TerrainDrawPatch( idTerrainPatch *terrain ) {
|
|
if ( !terrain || terrain->width < 2 || terrain->height < 2 ) {
|
|
return;
|
|
}
|
|
|
|
RB_TerrainBindLayerTextures( terrain );
|
|
|
|
glTerrainBlendEnableQD3D12( GL_TRUE );
|
|
glTerrainBlendOpacityQD3D12( 1.0f );
|
|
GL_State( GLS_DEPTHFUNC_LESS );
|
|
GL_Cull( CT_TWO_SIDED );
|
|
GL_SelectTexture( 0 );
|
|
glEnable( GL_TEXTURE_2D );
|
|
glEnable( GL_DEPTH_TEST );
|
|
glDepthMask( GL_TRUE );
|
|
glEnable( GL_BLEND );
|
|
|
|
for ( int y = 0; y < terrain->height - 1; y++ ) {
|
|
glBegin( GL_TRIANGLE_STRIP );
|
|
for ( int x = 0; x < terrain->width; x++ ) {
|
|
RB_TerrainSubmitVertex( terrain, x, y );
|
|
RB_TerrainSubmitVertex( terrain, x, y + 1 );
|
|
}
|
|
glEnd();
|
|
}
|
|
|
|
RB_TerrainClearLayerTextureBindings();
|
|
}
|
|
|
|
void RB_DrawTerrain( const viewDef_t *viewDef ) {
|
|
if ( r_skipTerrain.GetBool() || !viewDef || !viewDef->renderWorld || !viewDef->viewEntitys ) {
|
|
return;
|
|
}
|
|
|
|
idRenderWorldLocal *world = viewDef->renderWorld;
|
|
if ( world->terrainModel.NumTerrains() <= 0 ) {
|
|
return;
|
|
}
|
|
|
|
RB_LogComment( "---------- RB_DrawTerrain ----------\n" );
|
|
|
|
glMatrixMode( GL_MODELVIEW );
|
|
glLoadMatrixf( viewDef->worldSpace.modelViewMatrix );
|
|
backEnd.currentSpace = &viewDef->worldSpace;
|
|
|
|
for ( int i = 0; i < world->terrainModel.NumTerrains(); i++ ) {
|
|
idTerrainPatch *terrain = world->terrainModel.Terrain( i );
|
|
if ( !terrain || RB_TerrainCullPatch( terrain, viewDef ) ) {
|
|
continue;
|
|
}
|
|
RB_TerrainDrawPatch( terrain );
|
|
}
|
|
|
|
GL_Cull( CT_FRONT_SIDED );
|
|
glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
|
|
glEnable( GL_BLEND );
|
|
GL_SelectTexture( 0 );
|
|
globalImages->BindNull();
|
|
GL_ClearStateDelta();
|
|
}
|