@@ -140,6 +140,7 @@ void TerrCell::createPrimBuffer( GFXPrimitiveBufferHandle *primBuffer )
}
}


// Now add indices for the 'skirts'.
// These could probably be reduced to a loop.

@@ -435,40 +436,48 @@ void TerrCell::_updateVertexBuffer()

TerrVertex *vert = mVertexBuffer.lock();

Point2I gridPt;
Point2F point;
F32 height;
Point3F normal;

const TerrainFile *file = mTerrain->getFile();

Point2I gridPt = Point2I::Zero;
for ( U32 y = 0; y < smVBStride; y++ )
{
gridPt.y = mPoint.y + y * stepSize;
for ( U32 x = 0; x < smVBStride; x++ )
{
// We clamp here to keep the geometry from reading across
// one side of the height map to the other causing walls
// around the edges of the terrain.
gridPt.x = mClamp( mPoint.x + x * stepSize, 0, blockSize - 1 );
gridPt.y = mClamp( mPoint.y + y * stepSize, 0, blockSize - 1 );
gridPt.x = mPoint.x + x * stepSize;

// Corrected calc the position for height.
const Point2I p(
(gridPt.x < blockSize) ? gridPt.x : (blockSize - 1),
(gridPt.y < blockSize) ? gridPt.y : (blockSize - 1)
);
F32 height;
file->getHeight( &height, p );

// Setup this point.
point.x = (F32)gridPt.x * squareSize;
point.y = (F32)gridPt.y * squareSize;
height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) );
vert->point.x = point.x;
vert->point.y = point.y;
vert->point.x = (F32)gridPt.x * squareSize;
vert->point.y = (F32)gridPt.y * squareSize;
vert->point.z = height;

// Get the normal.
mTerrain->getSmoothNormal( point, &normal, true, false );
vert->normal = normal;
const Point2F pn(
(F32)p.x * squareSize,
(F32)p.y * squareSize
);
mTerrain->getSmoothNormal( pn, &vert->normal, true, false );

// Get the tangent z.
vert->tangentZ = fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) ) - height;
const Point2I p1( p.x + 1, p.y );
F32 height1;
file->getHeight( &height1, p1 );
vert->tangentZ = height1 - height;

// Test the empty state for this vert.
if ( file->isEmptyAt( gridPt.x, gridPt.y ) )
if ( file->isEmptyAt( p.x, p.y ) )
{
mHasEmpty = true;
mEmptyVertexList.push_back( vbcounter );
@@ -479,8 +488,12 @@ void TerrCell::_updateVertexBuffer()
}
}


// Add verts for 'skirts' around/beneath the edge verts of this cell.
// This could probably be reduced to a loop...

Point2F point = Point2F::Zero;
Point3F normal = Point3F::Zero;

const F32 skirtDepth = mSize / smMinCellSize * mTerrain->getSquareSize();

@@ -492,7 +505,7 @@ void TerrCell::_updateVertexBuffer()

point.x = (F32)gridPt.x * squareSize;
point.y = (F32)gridPt.y * squareSize;
height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) );
const F32 height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) );
vert->point.x = point.x;
vert->point.y = point.y;
vert->point.z = height - skirtDepth;
@@ -516,7 +529,7 @@ void TerrCell::_updateVertexBuffer()

point.x = (F32)gridPt.x * squareSize;
point.y = (F32)gridPt.y * squareSize;
height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) );
const F32 height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) );
vert->point.x = point.x;
vert->point.y = point.y;
vert->point.z = height - skirtDepth;
@@ -540,7 +553,7 @@ void TerrCell::_updateVertexBuffer()

point.x = (F32)gridPt.x * squareSize;
point.y = (F32)gridPt.y * squareSize;
height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) );
const F32 height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) );
vert->point.x = point.x;
vert->point.y = point.y;
vert->point.z = height - skirtDepth;
@@ -564,7 +577,7 @@ void TerrCell::_updateVertexBuffer()

point.x = (F32)gridPt.x * squareSize;
point.y = (F32)gridPt.y * squareSize;
height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) );
const F32 height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) );
vert->point.x = point.x;
vert->point.y = point.y;
vert->point.z = height - skirtDepth;
@@ -580,7 +593,6 @@ void TerrCell::_updateVertexBuffer()
++vert;
}

AssertFatal( vbcounter == smVBSize, "bad" );
mVertexBuffer.unlock();
}

@@ -827,29 +839,33 @@ void TerrCell::_updateMaterials()
{
PROFILE_SCOPE( TerrCell_UpdateMaterials );

const U32 blockSize = mTerrain->getBlockSize();

// This should really only be called for cells of smMinCellSize,
// in which case stepSize is always one.
U32 stepSize = mSize / smMinCellSize;
const U32 stepSize = mSize / smMinCellSize;
mMaterials = 0;
U8 index;
U32 x, y;

const TerrainFile *file = mTerrain->getFile();

// Step thru the samples in the map then.
for ( y = 0; y < smVBStride; y++ )
// Calc 'gridPt' by analogy with _updateVertexBuffer().
Point2I gridPt = Point2I::Zero;
for ( U32 y = 0; y < smVBStride; y++ )
{
for ( x = 0; x < smVBStride; x++ )
gridPt.y = mPoint.y + y * stepSize;
for ( U32 x = 0; x < smVBStride; x++ )
{
index = file->getLayerIndex( mPoint.x + x * stepSize,
mPoint.y + y * stepSize );
gridPt.x = mPoint.x + x * stepSize;
const U32 lx = (gridPt.x < blockSize) ? gridPt.x : (blockSize - 1);
const U32 ly = (gridPt.y < blockSize) ? gridPt.y : (blockSize - 1);
const U8 index = file->getLayerIndex( lx, ly );

// Skip empty layers and anything that doesn't fit
// the 64bit material flags.
if ( index == U8_MAX || index > 63 )
continue;

mMaterials |= (U64)((U64)1<<index);
mMaterials |= (U64)(1<<index);
}
}

@@ -647,7 +647,7 @@ bool TerrainBlock::castRayI(const Point3F &start, const Point3F &end, RayInfo *i

info->object = this;

if(start.x == end.x && start.y == end.y)
if(start.x == end.x || start.y == end.y)
{
if (end.z == start.z)
return false;
@@ -657,14 +657,14 @@ bool TerrainBlock::castRayI(const Point3F &start, const Point3F &end, RayInfo *i
return false;

F32 t = (height - start.z) / (end.z - start.z);
if(t < 0 || t > 1)
if(t < 0.0f || t > 1.0f)
return false;
info->t = t;

return true;
}

F32 invBlockWorldSize = 1 / getWorldBlockSize();
F32 invBlockWorldSize = 1.0f / getWorldBlockSize();

Point3F pStart(start.x * invBlockWorldSize, start.y * invBlockWorldSize, start.z);
Point3F pEnd(end.x * invBlockWorldSize, end.y * invBlockWorldSize, end.z);
@@ -683,7 +683,7 @@ bool TerrainBlock::castRayI(const Point3F &start, const Point3F &end, RayInfo *i
}
else
{
invDeltaX = 1 / (pEnd.x - pStart.x);
invDeltaX = 1.0f / (pEnd.x - pStart.x);
calcInterceptX = calcInterceptV;
if(pEnd.x < pStart.x)
dx = -1;
@@ -700,7 +700,7 @@ bool TerrainBlock::castRayI(const Point3F &start, const Point3F &end, RayInfo *i
}
else
{
invDeltaY = 1 / (pEnd.y - pStart.y);
invDeltaY = 1.0f / (pEnd.y - pStart.y);
calcInterceptY = calcInterceptV;
if(pEnd.y < pStart.y)
dy = -1;
@@ -717,7 +717,8 @@ bool TerrainBlock::castRayI(const Point3F &start, const Point3F &end, RayInfo *i
F32 nextXInt = calcInterceptX(pStart.x, invDeltaX, (F32)(blockX + (dx == 1)));
F32 nextYInt = calcInterceptY(pStart.y, invDeltaY, (F32)(blockY + (dy == 1)));

F32 intersectT = 1;

F32 intersectT = 1.0f;

if(nextXInt < intersectT)
intersectT = nextXInt;
@@ -742,7 +743,7 @@ bool TerrainBlock::castRayI(const Point3F &start, const Point3F &end, RayInfo *i
}

startT = intersectT;
if(intersectT >= 1)
if(intersectT >= 1.0f)
break;
if(nextXInt < nextYInt)
blockX += dx;
@@ -514,10 +514,14 @@ bool TerrainBlock::getHeight( const Point2F &pos, F32 *height ) const
if ( sq->flags & TerrainSquare::Empty )
return false;

F32 zBottomLeft = fixedToFloat( mFile->getHeight( x, y ) );
F32 zBottomRight = fixedToFloat( mFile->getHeight( x + 1, y ) );
F32 zTopLeft = fixedToFloat( mFile->getHeight( x, y + 1 ) );
F32 zTopRight = fixedToFloat( mFile->getHeight( x + 1, y + 1 ) );
F32 zBottomLeft, zBottomRight, zTopLeft, zTopRight;
mFile->getHeight4(
&zBottomLeft, &zBottomRight, &zTopLeft, &zTopRight,
Point2I( x, y ),
Point2I( x + 1, y ),
Point2I( x, y + 1 ),
Point2I( x + 1, y + 1 )
);

if ( sq->flags & TerrainSquare::Split45 )
{
@@ -565,10 +569,15 @@ bool TerrainBlock::getNormal( const Point2F &pos, Point3F *normal, bool normaliz
if ( skipEmpty && sq->flags & TerrainSquare::Empty )
return false;

F32 zBottomLeft = fixedToFloat( mFile->getHeight( x, y ) );
F32 zBottomRight = fixedToFloat( mFile->getHeight( x + 1, y ) );
F32 zTopLeft = fixedToFloat( mFile->getHeight( x, y + 1 ) );
F32 zTopRight = fixedToFloat( mFile->getHeight( x + 1, y + 1 ) );

F32 zBottomLeft, zBottomRight, zTopLeft, zTopRight;
mFile->getHeight4(
&zBottomLeft, &zBottomRight, &zTopLeft, &zTopRight,
Point2I( x, y ),
Point2I( x + 1, y ),
Point2I( x, y + 1 ),
Point2I( x + 1, y + 1 )
);

if ( sq->flags & TerrainSquare::Split45 )
{
@@ -620,10 +629,14 @@ bool TerrainBlock::getSmoothNormal( const Point2F &pos,
if ( skipEmpty && sq->flags & TerrainSquare::Empty )
return false;

F32 h1 = fixedToFloat( mFile->getHeight( x + 1, y ) );
F32 h2 = fixedToFloat( mFile->getHeight( x, y + 1 ) );
F32 h3 = fixedToFloat( mFile->getHeight( x - 1, y ) );
F32 h4 = fixedToFloat( mFile->getHeight( x, y - 1 ) );
F32 h1, h2, h3, h4;
mFile->getHeight4(
&h1, &h2, &h3, &h4,
Point2I( x + 1, y ),
Point2I( x, y + 1 ),
Point2I( x - 1, y ),
Point2I( x, y - 1 )
);

normal->set( h3 - h1, h4 - h2, mSquareSize * 2.0f );

@@ -657,10 +670,14 @@ bool TerrainBlock::getNormalAndHeight( const Point2F &pos, Point3F *normal, F32
if ( sq->flags & TerrainSquare::Empty )
return false;

F32 zBottomLeft = fixedToFloat( mFile->getHeight(x, y) );
F32 zBottomRight = fixedToFloat( mFile->getHeight(x + 1, y) );
F32 zTopLeft = fixedToFloat( mFile->getHeight(x, y + 1) );
F32 zTopRight = fixedToFloat( mFile->getHeight(x + 1, y + 1) );
F32 zBottomLeft, zBottomRight, zTopLeft, zTopRight;
mFile->getHeight4(
&zBottomLeft, &zBottomRight, &zTopLeft, &zTopRight,
Point2I( x, y ),
Point2I( x + 1, y ),
Point2I( x, y + 1 ),
Point2I( x + 1, y + 1 )
);

if ( sq->flags & TerrainSquare::Split45 )
{
@@ -729,10 +746,14 @@ bool TerrainBlock::getNormalHeightMaterial( const Point2F &pos,
if ( sq->flags & TerrainSquare::Empty )
return false;

F32 zBottomLeft = fixedToFloat( mFile->getHeight(x, y) );
F32 zBottomRight = fixedToFloat( mFile->getHeight(x + 1, y) );
F32 zTopLeft = fixedToFloat( mFile->getHeight(x, y + 1) );
F32 zTopRight = fixedToFloat( mFile->getHeight(x + 1, y + 1) );
F32 zBottomLeft, zBottomRight, zTopLeft, zTopRight;
mFile->getHeight4(
&zBottomLeft, &zBottomRight, &zTopLeft, &zTopRight,
Point2I( x, y ),
Point2I( x + 1, y ),
Point2I( x, y + 1 ),
Point2I( x + 1, y + 1 )
);

matName = mFile->getMaterialName( xm, ym );

@@ -46,6 +46,7 @@
#endif


TerrainBlock* getTerrainUnderWorldPoint(const Point3F&);

class GBitmap;
class TerrainBlock;
@@ -114,7 +114,7 @@ void TerrainFile::_buildGridMap()
for ( S32 i = mGridLevels; i >= 0; i-- )
{
mGridMap[i] = sq;
sq += 1 << ( 2 * ( mGridLevels - i ) );
sq += (size_t)(1 << ( 2 * ( mGridLevels - i ) ));
}

for( S32 i = mGridLevels; i >= 0; i-- )
@@ -41,6 +41,18 @@ class FileStream;
class GBitmap;


/// Conversion from 11.5 fixed point to floating point.
inline F32 fixedToFloat( U16 val )
{
return F32(val) * 0.03125f;
}

/// Conversion from floating point to 11.5 fixed point.
inline U16 floatToFixed( F32 val )
{
return U16(val * 32.0f + 0.5f);
}

///
struct TerrainSquare
{
@@ -177,10 +189,22 @@ class TerrainFile

void setHeight( U32 x, U32 y, U16 height );

void setHeight( U32 i, U16 height );

const U16* getHeightAddress( U32 x, U32 y ) const;

U16 getHeight( U32 x, U32 y ) const;

void getHeight( F32* h, const Point2I& ) const;

void getHeight4(
F32* a, F32* b, F32* c, F32* d,
const Point2I& pa,
const Point2I& pb,
const Point2I& pc,
const Point2I& pd
) const;

U16 getMaxHeight() const { return mGridMap[mGridLevels]->maxHeight; }

/// Returns the constant heightmap vector.
@@ -191,13 +215,52 @@ class TerrainFile

/// Check if the given point is valid within the (non-tiled) terrain file.
bool isPointInTerrain( U32 x, U32 y ) const;

private:
// Clamp X and Y to the size of terrain.
void clamp( U32* x, U32* y ) const;
inline static void error(char c, U32 v ) {
static const char* s = "TerrainFile Coord '%c == %d' out of range. Fix it in the algorithm.";
#if 1
// only first error
static bool first = true;
if ( first ) {
Con::errorf( s, c, v );
first = false;
}
#else
// all errors
Con::errorf( s, c, v );
#endif
}
};

// @todo ! Need AssertFatal() and debug all clients.
// @todo ! Verify all methods of TerrainFile.
inline void TerrainFile::clamp( U32* x, U32* y ) const
{
#if 0
// old clamp
*x %= mSize;
*y %= mSize;
#else


if (*x >= mSize) {
error( 'x', *x );
*x = mSize - 1;
}
if (*y >= mSize) {
error( 'y', *y );
*y = mSize - 1;
}
#endif
}


inline TerrainSquare* TerrainFile::findSquare( U32 level, U32 x, U32 y ) const
{
x %= mSize;
y %= mSize;
clamp( &x, &y );
x >>= level;
y >>= level;

@@ -206,36 +269,59 @@ inline TerrainSquare* TerrainFile::findSquare( U32 level, U32 x, U32 y ) const

inline void TerrainFile::setHeight( U32 x, U32 y, U16 height )
{
x %= mSize;
y %= mSize;
mHeightMap[ x + ( y * mSize ) ] = height;
clamp( &x, &y );
setHeight( x + y * mSize, height );
}

inline void TerrainFile::setHeight( U32 i, U16 height )
{
mHeightMap[ i ] = height;
}

inline const U16* TerrainFile::getHeightAddress( U32 x, U32 y ) const
{
x %= mSize;
y %= mSize;
clamp( &x, &y );
return &mHeightMap[ x + ( y * mSize ) ];
}

inline U16 TerrainFile::getHeight( U32 x, U32 y ) const
{
x %= mSize;
y %= mSize;
clamp( &x, &y );
return mHeightMap[ x + ( y * mSize ) ];
}

inline void TerrainFile::getHeight( F32* h, const Point2I& p ) const
{
const Point2I pp(
mClamp( p.x, 0, mSize - 1 ),
mClamp( p.y, 0, mSize - 1 )
);
*h = fixedToFloat( getHeight( (U32)pp.x, (U32)pp.y ) );
}

inline void TerrainFile::getHeight4(
F32* a, F32* b, F32* c, F32* d,
const Point2I& pa,
const Point2I& pb,
const Point2I& pc,
const Point2I& pd
) const
{
getHeight( a, pa );
getHeight( b, pb );
getHeight( c, pc );
getHeight( d, pd );
}

inline U8 TerrainFile::getLayerIndex( U32 x, U32 y ) const
{
x %= mSize;
y %= mSize;
clamp( &x, &y );
return mLayerMap[ x + ( y * mSize ) ];
}

inline void TerrainFile::setLayerIndex( U32 x, U32 y, U8 index )
{
x %= mSize;
y %= mSize;
clamp( &x, &y );
mLayerMap[ x + ( y * mSize ) ] = index;
}

@@ -249,8 +335,7 @@ inline BaseMatInstance* TerrainFile::getMaterialMapping( U32 index ) const

inline StringTableEntry TerrainFile::getMaterialName( U32 x, U32 y) const
{
x %= mSize;
y %= mSize;
clamp( &x, &y );
const U8 &index = mLayerMap[ x + ( y * mSize ) ];

if ( index < mMaterials.size() )
@@ -260,17 +345,6 @@ inline StringTableEntry TerrainFile::getMaterialName( U32 x, U32 y) const
}


/// Conversion from 11.5 fixed point to floating point.
inline F32 fixedToFloat( U16 val )
{
return F32(val) * 0.03125f;
}

/// Conversion from floating point to 11.5 fixed point.
inline U16 floatToFixed( F32 val )
{
return U16(val * 32.0 + 0.5f);
}

inline bool TerrainFile::isPointInTerrain( U32 x, U32 y ) const
{
@@ -170,10 +170,9 @@ bool TerrainBlock::_initBaseShader()
desc.zDefined = true;
desc.zWriteEnable = false;
desc.zEnable = false;
desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendOne );
desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha );
desc.cullDefined = true;
desc.cullMode = GFXCullNone;
desc.colorWriteAlpha = false;
mBaseShaderSB = GFX->createStateBlock( desc );

return true;
@@ -252,8 +251,6 @@ void TerrainBlock::_updateBaseTexture(bool writeToCache)
mBaseTarget->attachTexture( GFXTextureTarget::Color0, blendTex );
GFX->setActiveRenderTarget( mBaseTarget );

GFX->clear( GFXClearTarget, ColorI(0,0,0,255), 1.0f, 0 );

GFX->setTexture( 0, mLayerTex );
mBaseShaderConsts->setSafe( mBaseLayerSizeConst, (F32)mLayerTex->getWidth() );

@@ -402,3 +402,4 @@ function generateProceduralTerrainMask()
Canvas.popDialog(ProceduralTerrainPainterGui);
ETerrainEditor.autoMaterialLayer($TPPHeightMin, $TPPHeightMax, $TPPSlopeMin, $TPPSlopeMax, $TPPCoverage);
}

@@ -1025,6 +1025,7 @@
barTitle = "Terrain";

item[0] = "Smooth Heightmap" TAB "" TAB "ETerrainEditor.onSmoothHeightmap();";
item[1] = "Solder Edges" TAB "" TAB "ETerrainEditor.onSolderEdges();";
};
}

@@ -347,7 +347,14 @@ function onNeedRelight()
%action.smooth( %this.getActiveTerrain(), 1.0, 1 );
%action.addToManager( Editor.getUndoManager() );
}

function TerrainEditor::onSolderEdges( %this )
{
// # Work with all terrains on the loaded level.
%action = new TerrainSolderEdgesAction();
%action.solder();
%action.addToManager( Editor.getUndoManager() );
ETerrainEditor.isDirty = true;
}
function TerrainEditor::onMaterialUndo( %this )
{
// Update the gui to reflect the current materials.
@@ -1025,6 +1025,7 @@
barTitle = "Terrain";

item[0] = "Smooth Heightmap" TAB "" TAB "ETerrainEditor.onSmoothHeightmap();";
item[1] = "Solder Edges" TAB "" TAB "ETerrainEditor.onSolderEdges();";
};
}

@@ -347,7 +347,14 @@ function onNeedRelight()
%action.smooth( %this.getActiveTerrain(), 1.0, 1 );
%action.addToManager( Editor.getUndoManager() );
}

function TerrainEditor::onSolderEdges( %this )
{
// # Work with all terrains on the loaded level.
%action = new TerrainSolderEdgesAction();
%action.solder();
%action.addToManager( Editor.getUndoManager() );
ETerrainEditor.isDirty = true;
}
function TerrainEditor::onMaterialUndo( %this )
{
// Update the gui to reflect the current materials.