mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 08:11:10 +02:00
579 lines
17 KiB
C++
579 lines
17 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Shared terrain source model
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "TerrainModel.h"
|
|
|
|
idTerrainPatch::idTerrainPatch() {
|
|
name.Clear();
|
|
material.Clear();
|
|
origin.Zero();
|
|
width = TERRAIN_DEFAULT_VERTS;
|
|
height = TERRAIN_DEFAULT_VERTS;
|
|
cellSize = TERRAIN_DEFAULT_CELL_SIZE;
|
|
paintWidth = TERRAIN_DEFAULT_PAINT_SIZE;
|
|
paintHeight = TERRAIN_DEFAULT_PAINT_SIZE;
|
|
paintTexture = 0;
|
|
paintTextureDirty = true;
|
|
paintDirtyMinX = 0;
|
|
paintDirtyMinY = 0;
|
|
paintDirtyMaxX = paintWidth - 1;
|
|
paintDirtyMaxY = paintHeight - 1;
|
|
bounds.Clear();
|
|
}
|
|
|
|
int idTerrainPatch::SampleIndex( int x, int y ) const {
|
|
return y * width + x;
|
|
}
|
|
|
|
int idTerrainPatch::PaintIndex( int x, int y ) const {
|
|
return y * paintWidth + x;
|
|
}
|
|
|
|
float idTerrainPatch::SampleHeight( int x, int y ) const {
|
|
return heights[SampleIndex( x, y )];
|
|
}
|
|
|
|
void idTerrainPatch::SetHeight( int x, int y, float height ) {
|
|
heights[SampleIndex( x, y )] = height;
|
|
}
|
|
|
|
bool idTerrainPatch::SetHeightBySample( int sample, float height ) {
|
|
if ( sample < 0 || sample >= heights.Num() ) {
|
|
return false;
|
|
}
|
|
heights[sample] = height;
|
|
return true;
|
|
}
|
|
|
|
idVec4 idTerrainPatch::SamplePaintWeight( int x, int y ) const {
|
|
return paintWeights[PaintIndex( x, y )];
|
|
}
|
|
|
|
void idTerrainPatch::SetPaintWeight( int x, int y, const idVec4 &weight ) {
|
|
paintWeights[PaintIndex( x, y )] = NormalizeLayerWeight( weight );
|
|
MarkPaintTextureDirtyRect( x, y, x, y );
|
|
}
|
|
|
|
bool idTerrainPatch::SetPaintWeightBySample( int sample, const idVec4 &weight ) {
|
|
if ( sample < 0 || sample >= paintWeights.Num() || paintWidth <= 0 ) {
|
|
return false;
|
|
}
|
|
paintWeights[sample] = NormalizeLayerWeight( weight );
|
|
MarkPaintTextureDirtyRect( sample % paintWidth, sample / paintWidth, sample % paintWidth, sample / paintWidth );
|
|
return true;
|
|
}
|
|
|
|
idVec4 idTerrainPatch::BaseLayerWeight() {
|
|
return idVec4( 1.0f, 0.0f, 0.0f, 0.0f );
|
|
}
|
|
|
|
idVec4 idTerrainPatch::NormalizeLayerWeight( const idVec4 &weight ) {
|
|
idVec4 normalized;
|
|
normalized.x = weight.x > 0.0f ? weight.x : 0.0f;
|
|
normalized.y = weight.y > 0.0f ? weight.y : 0.0f;
|
|
normalized.z = weight.z > 0.0f ? weight.z : 0.0f;
|
|
normalized.w = weight.w > 0.0f ? weight.w : 0.0f;
|
|
|
|
const float total = normalized.x + normalized.y + normalized.z + normalized.w;
|
|
if ( total <= 0.0001f ) {
|
|
return BaseLayerWeight();
|
|
}
|
|
|
|
const float invTotal = 1.0f / total;
|
|
normalized.x *= invTotal;
|
|
normalized.y *= invTotal;
|
|
normalized.z *= invTotal;
|
|
normalized.w *= invTotal;
|
|
return normalized;
|
|
}
|
|
|
|
float idTerrainPatch::WorldWidth() const {
|
|
return ( width - 1 ) * cellSize;
|
|
}
|
|
|
|
float idTerrainPatch::WorldHeight() const {
|
|
return ( height - 1 ) * cellSize;
|
|
}
|
|
|
|
float idTerrainPatch::BilinearHeight( float u, float v ) const {
|
|
u = idMath::ClampFloat( 0.0f, 1.0f, u );
|
|
v = idMath::ClampFloat( 0.0f, 1.0f, v );
|
|
|
|
const float fx = u * ( width - 1 );
|
|
const float fy = v * ( height - 1 );
|
|
const int x0 = idMath::ClampInt( 0, width - 1, (int)floorf( fx ) );
|
|
const int y0 = idMath::ClampInt( 0, height - 1, (int)floorf( fy ) );
|
|
const int x1 = idMath::ClampInt( 0, width - 1, x0 + 1 );
|
|
const int y1 = idMath::ClampInt( 0, height - 1, y0 + 1 );
|
|
const float tx = fx - (float)x0;
|
|
const float ty = fy - (float)y0;
|
|
|
|
const float h00 = SampleHeight( x0, y0 );
|
|
const float h10 = SampleHeight( x1, y0 );
|
|
const float h01 = SampleHeight( x0, y1 );
|
|
const float h11 = SampleHeight( x1, y1 );
|
|
const float h0 = h00 + ( h10 - h00 ) * tx;
|
|
const float h1 = h01 + ( h11 - h01 ) * tx;
|
|
return h0 + ( h1 - h0 ) * ty;
|
|
}
|
|
|
|
idVec3 idTerrainPatch::VertexUV( float u, float v ) const {
|
|
idVec3 p;
|
|
p.x = origin.x + WorldWidth() * u;
|
|
p.y = origin.y + WorldHeight() * v;
|
|
p.z = origin.z + BilinearHeight( u, v );
|
|
return p;
|
|
}
|
|
|
|
idVec3 idTerrainPatch::Vertex( int x, int y ) const {
|
|
idVec3 v;
|
|
v.x = origin.x + x * cellSize;
|
|
v.y = origin.y + y * cellSize;
|
|
v.z = origin.z + SampleHeight( x, y );
|
|
return v;
|
|
}
|
|
|
|
idVec3 idTerrainPatch::VertexNormal( int x, int y ) const {
|
|
const int x0 = idMath::ClampInt( 0, width - 1, x - 1 );
|
|
const int x1 = idMath::ClampInt( 0, width - 1, x + 1 );
|
|
const int y0 = idMath::ClampInt( 0, height - 1, y - 1 );
|
|
const int y1 = idMath::ClampInt( 0, height - 1, y + 1 );
|
|
const idVec3 sx = Vertex( x1, y ) - Vertex( x0, y );
|
|
const idVec3 sy = Vertex( x, y1 ) - Vertex( x, y0 );
|
|
idVec3 normal = sx.Cross( sy );
|
|
if ( normal.LengthSqr() <= 0.0001f ) {
|
|
normal.Set( 0.0f, 0.0f, 1.0f );
|
|
} else {
|
|
normal.Normalize();
|
|
}
|
|
return normal;
|
|
}
|
|
|
|
void idTerrainPatch::UpdateBounds() {
|
|
bounds.Clear();
|
|
if ( width <= 0 || height <= 0 ) {
|
|
bounds.AddPoint( origin );
|
|
return;
|
|
}
|
|
for ( int y = 0; y < height; y++ ) {
|
|
for ( int x = 0; x < width; x++ ) {
|
|
bounds.AddPoint( Vertex( x, y ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void idTerrainPatch::EnsureLayerData( const char *defaultMaterial ) {
|
|
if ( material.IsEmpty() ) {
|
|
material = defaultMaterial && defaultMaterial[0] ? defaultMaterial : "textures/common/caulk";
|
|
}
|
|
if ( layerMaterials[0].IsEmpty() ) {
|
|
layerMaterials[0] = material;
|
|
}
|
|
material = layerMaterials[0];
|
|
|
|
if ( width < 2 ) {
|
|
width = 2;
|
|
}
|
|
if ( height < 2 ) {
|
|
height = 2;
|
|
}
|
|
const int heightSampleCount = width * height;
|
|
const int oldHeightCount = heights.Num();
|
|
heights.SetNum( heightSampleCount );
|
|
for ( int i = oldHeightCount; i < heightSampleCount; i++ ) {
|
|
heights[i] = 0.0f;
|
|
}
|
|
|
|
if ( paintWidth < 2 ) {
|
|
paintWidth = TERRAIN_DEFAULT_PAINT_SIZE;
|
|
}
|
|
if ( paintHeight < 2 ) {
|
|
paintHeight = TERRAIN_DEFAULT_PAINT_SIZE;
|
|
}
|
|
|
|
const int paintSampleCount = paintWidth * paintHeight;
|
|
if ( paintSampleCount <= 0 ) {
|
|
paintWeights.Clear();
|
|
return;
|
|
}
|
|
|
|
const int oldCount = paintWeights.Num();
|
|
paintWeights.SetNum( paintSampleCount );
|
|
if ( oldCount != paintSampleCount ) {
|
|
MarkPaintTextureDirty();
|
|
}
|
|
for ( int i = 0; i < paintSampleCount; i++ ) {
|
|
if ( i >= oldCount ) {
|
|
paintWeights[i] = BaseLayerWeight();
|
|
} else {
|
|
paintWeights[i] = NormalizeLayerWeight( paintWeights[i] );
|
|
}
|
|
}
|
|
UpdateBounds();
|
|
}
|
|
|
|
void idTerrainPatch::ClearLayerWeight( int layer ) {
|
|
if ( layer < 0 || layer >= TERRAIN_MAX_LAYERS ) {
|
|
return;
|
|
}
|
|
for ( int i = 0; i < paintWeights.Num(); i++ ) {
|
|
idVec4 weight = paintWeights[i];
|
|
weight[layer] = 0.0f;
|
|
paintWeights[i] = NormalizeLayerWeight( weight );
|
|
}
|
|
MarkPaintTextureDirty();
|
|
}
|
|
|
|
bool idTerrainPatch::SetLayerMaterials( const idStr newLayerMaterials[TERRAIN_MAX_LAYERS], const char *defaultMaterial ) {
|
|
bool changed = false;
|
|
for ( int layer = 0; layer < TERRAIN_MAX_LAYERS; layer++ ) {
|
|
idStr materialName = newLayerMaterials[layer];
|
|
materialName.StripLeading( ' ' );
|
|
materialName.StripTrailing( ' ' );
|
|
if ( layer == 0 && materialName.IsEmpty() ) {
|
|
materialName = defaultMaterial && defaultMaterial[0] ? defaultMaterial : "textures/common/caulk";
|
|
}
|
|
if ( layerMaterials[layer].Icmp( materialName.c_str() ) == 0 ) {
|
|
continue;
|
|
}
|
|
layerMaterials[layer] = materialName;
|
|
if ( layer > 0 && materialName.IsEmpty() ) {
|
|
ClearLayerWeight( layer );
|
|
}
|
|
changed = true;
|
|
}
|
|
material = layerMaterials[0];
|
|
return changed;
|
|
}
|
|
|
|
void idTerrainPatch::MarkPaintTextureDirty() {
|
|
paintTextureDirty = true;
|
|
paintDirtyMinX = 0;
|
|
paintDirtyMinY = 0;
|
|
paintDirtyMaxX = paintWidth > 0 ? paintWidth - 1 : 0;
|
|
paintDirtyMaxY = paintHeight > 0 ? paintHeight - 1 : 0;
|
|
}
|
|
|
|
void idTerrainPatch::ClearPaintTextureDirtyRect() {
|
|
paintTextureDirty = false;
|
|
paintDirtyMinX = paintWidth;
|
|
paintDirtyMinY = paintHeight;
|
|
paintDirtyMaxX = -1;
|
|
paintDirtyMaxY = -1;
|
|
}
|
|
|
|
void idTerrainPatch::MarkPaintTextureDirtyRect( int minX, int minY, int maxX, int maxY ) {
|
|
if ( paintWidth <= 0 || paintHeight <= 0 ) {
|
|
return;
|
|
}
|
|
|
|
minX = idMath::ClampInt( 0, paintWidth - 1, minX );
|
|
maxX = idMath::ClampInt( 0, paintWidth - 1, maxX );
|
|
minY = idMath::ClampInt( 0, paintHeight - 1, minY );
|
|
maxY = idMath::ClampInt( 0, paintHeight - 1, maxY );
|
|
if ( maxX < minX || maxY < minY ) {
|
|
return;
|
|
}
|
|
|
|
if ( !paintTextureDirty || paintDirtyMaxX < paintDirtyMinX || paintDirtyMaxY < paintDirtyMinY ) {
|
|
paintDirtyMinX = minX;
|
|
paintDirtyMinY = minY;
|
|
paintDirtyMaxX = maxX;
|
|
paintDirtyMaxY = maxY;
|
|
} else {
|
|
if ( minX < paintDirtyMinX ) {
|
|
paintDirtyMinX = minX;
|
|
}
|
|
if ( minY < paintDirtyMinY ) {
|
|
paintDirtyMinY = minY;
|
|
}
|
|
if ( maxX > paintDirtyMaxX ) {
|
|
paintDirtyMaxX = maxX;
|
|
}
|
|
if ( maxY > paintDirtyMaxY ) {
|
|
paintDirtyMaxY = maxY;
|
|
}
|
|
}
|
|
paintTextureDirty = true;
|
|
}
|
|
|
|
idTerrainModel::idTerrainModel() {
|
|
}
|
|
|
|
idTerrainModel::~idTerrainModel() {
|
|
Clear();
|
|
}
|
|
|
|
void idTerrainModel::Clear() {
|
|
terrains.DeleteContents( true );
|
|
}
|
|
|
|
int idTerrainModel::NumTerrains() const {
|
|
return terrains.Num();
|
|
}
|
|
|
|
idTerrainPatch *idTerrainModel::Terrain( int index ) {
|
|
if ( index < 0 || index >= terrains.Num() ) {
|
|
return NULL;
|
|
}
|
|
return terrains[index];
|
|
}
|
|
|
|
const idTerrainPatch *idTerrainModel::Terrain( int index ) const {
|
|
if ( index < 0 || index >= terrains.Num() ) {
|
|
return NULL;
|
|
}
|
|
return terrains[index];
|
|
}
|
|
|
|
idList<idTerrainPatch *> &idTerrainModel::Terrains() {
|
|
return terrains;
|
|
}
|
|
|
|
const idList<idTerrainPatch *> &idTerrainModel::Terrains() const {
|
|
return terrains;
|
|
}
|
|
|
|
static float TerrainModel_SnapToGrid( float value, float gridSize ) {
|
|
if ( gridSize <= 0.0f ) {
|
|
gridSize = 1.0f;
|
|
}
|
|
return floorf( value / gridSize + ( value >= 0.0f ? 0.5f : -0.5f ) ) * gridSize;
|
|
}
|
|
|
|
idTerrainPatch *idTerrainModel::AllocDefaultTerrain( const idVec3 ¢er, const char *defaultMaterial, float gridSize ) {
|
|
idTerrainPatch *terrain = new idTerrainPatch;
|
|
terrain->name = va( "terrain_%i", terrains.Num() + 1 );
|
|
terrain->material = defaultMaterial && defaultMaterial[0] ? defaultMaterial : "textures/common/caulk";
|
|
terrain->layerMaterials[0] = terrain->material;
|
|
const float worldSize = ( terrain->width - 1 ) * terrain->cellSize;
|
|
terrain->origin.x = TerrainModel_SnapToGrid( center.x - worldSize * 0.5f, gridSize );
|
|
terrain->origin.y = TerrainModel_SnapToGrid( center.y - worldSize * 0.5f, gridSize );
|
|
terrain->origin.z = TerrainModel_SnapToGrid( center.z, gridSize );
|
|
terrain->heights.SetNum( terrain->width * terrain->height );
|
|
for ( int i = 0; i < terrain->heights.Num(); i++ ) {
|
|
terrain->heights[i] = 0.0f;
|
|
}
|
|
terrain->EnsureLayerData( defaultMaterial );
|
|
return terrain;
|
|
}
|
|
|
|
void idTerrainModel::AddTerrain( idTerrainPatch *terrain ) {
|
|
if ( terrain ) {
|
|
terrains.Append( terrain );
|
|
}
|
|
}
|
|
|
|
idStr idTerrainModel::FileNameForMap( const char *mapFileName ) {
|
|
idStr fileName = mapFileName ? mapFileName : "";
|
|
fileName.StripFileExtension();
|
|
fileName.SetFileExtension( ".terrain" );
|
|
return fileName;
|
|
}
|
|
|
|
bool idTerrainModel::SaveFile( const char *filename ) {
|
|
if ( !filename || !filename[0] ) {
|
|
return false;
|
|
}
|
|
|
|
const bool osPath = strstr( filename, ":" ) != NULL;
|
|
idFile *file = osPath ? fileSystem->OpenExplicitFileWrite( filename ) : fileSystem->OpenFileWrite( filename );
|
|
if ( !file ) {
|
|
common->Warning( "idTerrainModel::SaveFile: couldn't write %s", filename );
|
|
return false;
|
|
}
|
|
|
|
file->WriteFloatString( "terrainAuthoring 3 {\n" );
|
|
for ( int i = 0; i < terrains.Num(); i++ ) {
|
|
idTerrainPatch *terrain = terrains[i];
|
|
if ( !terrain ) {
|
|
continue;
|
|
}
|
|
terrain->EnsureLayerData( "textures/common/caulk" );
|
|
|
|
file->WriteFloatString( "\tterrain {\n" );
|
|
file->WriteFloatString( "\t\tname \"%s\"\n", terrain->name.c_str() );
|
|
file->WriteFloatString( "\t\tmaterial \"%s\"\n", terrain->material.c_str() );
|
|
for ( int layer = 0; layer < TERRAIN_MAX_LAYERS; layer++ ) {
|
|
if ( layer == 0 || !terrain->layerMaterials[layer].IsEmpty() ) {
|
|
file->WriteFloatString( "\t\tlayer %i \"%s\"\n", layer, terrain->layerMaterials[layer].c_str() );
|
|
}
|
|
}
|
|
file->WriteFloatString( "\t\torigin ( %f %f %f )\n", terrain->origin.x, terrain->origin.y, terrain->origin.z );
|
|
file->WriteFloatString( "\t\tsize %i %i\n", terrain->width, terrain->height );
|
|
file->WriteFloatString( "\t\tcellSize %f\n", terrain->cellSize );
|
|
file->WriteFloatString( "\t\tpaintSize %i %i\n", terrain->paintWidth, terrain->paintHeight );
|
|
file->WriteFloatString( "\t\theights {\n" );
|
|
for ( int y = 0; y < terrain->height; y++ ) {
|
|
file->WriteFloatString( "\t\t\t" );
|
|
for ( int x = 0; x < terrain->width; x++ ) {
|
|
file->WriteFloatString( "%f ", terrain->SampleHeight( x, y ) );
|
|
}
|
|
file->WriteFloatString( "\n" );
|
|
}
|
|
file->WriteFloatString( "\t\t}\n" );
|
|
file->WriteFloatString( "\t\tpaintWeights {\n" );
|
|
for ( int y = 0; y < terrain->paintHeight; y++ ) {
|
|
file->WriteFloatString( "\t\t\t" );
|
|
for ( int x = 0; x < terrain->paintWidth; x++ ) {
|
|
const idVec4 weights = terrain->SamplePaintWeight( x, y );
|
|
file->WriteFloatString( "%f %f %f %f ", weights.x, weights.y, weights.z, weights.w );
|
|
}
|
|
file->WriteFloatString( "\n" );
|
|
}
|
|
file->WriteFloatString( "\t\t}\n" );
|
|
file->WriteFloatString( "\t}\n" );
|
|
}
|
|
file->WriteFloatString( "}\n" );
|
|
fileSystem->CloseFile( file );
|
|
return true;
|
|
}
|
|
|
|
bool idTerrainModel::LoadFile( const char *filename, const char *defaultMaterial ) {
|
|
Clear();
|
|
if ( !filename || !filename[0] ) {
|
|
return false;
|
|
}
|
|
|
|
const char *fallbackMaterial = defaultMaterial && defaultMaterial[0] ? defaultMaterial : "textures/common/caulk";
|
|
const bool osPath = strstr( filename, ":" ) != NULL;
|
|
idLexer src( LEXFL_NOSTRINGCONCAT | LEXFL_NODOLLARPRECOMPILE | LEXFL_ALLOWPATHNAMES );
|
|
src.LoadFile( filename, osPath );
|
|
if ( !src.IsLoaded() ) {
|
|
return false;
|
|
}
|
|
|
|
idToken token;
|
|
if ( !src.ReadToken( &token ) || token.Icmp( "terrainAuthoring" ) ) {
|
|
return false;
|
|
}
|
|
src.ReadToken( &token );
|
|
if ( token != "{" ) {
|
|
src.ExpectTokenString( "{" );
|
|
}
|
|
|
|
while ( src.ReadToken( &token ) ) {
|
|
if ( token == "}" ) {
|
|
break;
|
|
}
|
|
if ( token.Icmp( "terrain" ) ) {
|
|
break;
|
|
}
|
|
|
|
idTerrainPatch *terrain = new idTerrainPatch;
|
|
terrain->name = va( "terrain_%i", terrains.Num() + 1 );
|
|
terrain->material = fallbackMaterial;
|
|
terrain->layerMaterials[0] = terrain->material;
|
|
terrain->MarkPaintTextureDirty();
|
|
|
|
src.ExpectTokenString( "{" );
|
|
while ( src.ReadToken( &token ) ) {
|
|
if ( token == "}" ) {
|
|
break;
|
|
}
|
|
if ( !token.Icmp( "name" ) ) {
|
|
src.ExpectAnyToken( &token );
|
|
terrain->name = token;
|
|
} else if ( !token.Icmp( "material" ) ) {
|
|
src.ExpectAnyToken( &token );
|
|
terrain->material = token;
|
|
terrain->layerMaterials[0] = token;
|
|
} else if ( !token.Icmp( "layer" ) ) {
|
|
const int layer = src.ParseInt();
|
|
src.ExpectAnyToken( &token );
|
|
if ( layer >= 0 && layer < TERRAIN_MAX_LAYERS ) {
|
|
terrain->layerMaterials[layer] = token;
|
|
if ( layer == 0 ) {
|
|
terrain->material = token;
|
|
}
|
|
}
|
|
} else if ( !token.Icmp( "origin" ) ) {
|
|
src.Parse1DMatrix( 3, terrain->origin.ToFloatPtr() );
|
|
} else if ( !token.Icmp( "size" ) ) {
|
|
terrain->width = src.ParseInt();
|
|
terrain->height = src.ParseInt();
|
|
if ( terrain->width < 2 ) {
|
|
terrain->width = 2;
|
|
}
|
|
if ( terrain->height < 2 ) {
|
|
terrain->height = 2;
|
|
}
|
|
} else if ( !token.Icmp( "cellSize" ) ) {
|
|
terrain->cellSize = src.ParseFloat();
|
|
if ( terrain->cellSize <= 0.0f ) {
|
|
terrain->cellSize = TERRAIN_DEFAULT_CELL_SIZE;
|
|
}
|
|
} else if ( !token.Icmp( "paintSize" ) ) {
|
|
terrain->paintWidth = src.ParseInt();
|
|
terrain->paintHeight = src.ParseInt();
|
|
if ( terrain->paintWidth < 2 ) {
|
|
terrain->paintWidth = 2;
|
|
}
|
|
if ( terrain->paintHeight < 2 ) {
|
|
terrain->paintHeight = 2;
|
|
}
|
|
} else if ( !token.Icmp( "heights" ) ) {
|
|
src.ExpectTokenString( "{" );
|
|
terrain->heights.SetNum( terrain->width * terrain->height );
|
|
for ( int i = 0; i < terrain->heights.Num(); i++ ) {
|
|
terrain->heights[i] = 0.0f;
|
|
}
|
|
int sample = 0;
|
|
while ( src.ReadToken( &token ) ) {
|
|
if ( token == "}" ) {
|
|
break;
|
|
}
|
|
if ( sample < terrain->heights.Num() ) {
|
|
terrain->heights[sample++] = (float)atof( token.c_str() );
|
|
}
|
|
}
|
|
} else if ( !token.Icmp( "weights" ) || !token.Icmp( "paintWeights" ) ) {
|
|
const bool legacyWeights = !token.Icmp( "weights" );
|
|
src.ExpectTokenString( "{" );
|
|
if ( legacyWeights && terrain->paintWidth == TERRAIN_DEFAULT_PAINT_SIZE && terrain->paintHeight == TERRAIN_DEFAULT_PAINT_SIZE ) {
|
|
terrain->paintWidth = terrain->width;
|
|
terrain->paintHeight = terrain->height;
|
|
}
|
|
terrain->paintWeights.SetNum( terrain->paintWidth * terrain->paintHeight );
|
|
for ( int i = 0; i < terrain->paintWeights.Num(); i++ ) {
|
|
terrain->paintWeights[i] = idTerrainPatch::BaseLayerWeight();
|
|
}
|
|
|
|
int sample = 0;
|
|
int component = 0;
|
|
idVec4 weight;
|
|
weight.Zero();
|
|
while ( src.ReadToken( &token ) ) {
|
|
if ( token == "}" ) {
|
|
break;
|
|
}
|
|
weight[component++] = (float)atof( token.c_str() );
|
|
if ( component == 4 ) {
|
|
if ( sample < terrain->paintWeights.Num() ) {
|
|
terrain->paintWeights[sample] = idTerrainPatch::NormalizeLayerWeight( weight );
|
|
}
|
|
sample++;
|
|
component = 0;
|
|
weight.Zero();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
terrain->EnsureLayerData( fallbackMaterial );
|
|
terrain->MarkPaintTextureDirty();
|
|
terrain->UpdateBounds();
|
|
terrains.Append( terrain );
|
|
}
|
|
|
|
return true;
|
|
}
|