Files
DoomRTX/neo/engine/renderer/draw_terrain.cpp
T
2026-05-28 17:15:52 -07:00

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();
}