12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
- // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
- // Copyright (C) 2015 Faust Logic, Inc.
- //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
- #include "platform/platform.h"
- #include "terrain/terrCell.h"
- #include "math/util/frustum.h"
- #include "terrain/terrData.h"
- #include "terrain/terrCellMaterial.h"
- #include "scene/sceneRenderState.h"
- #include "lighting/lightManager.h"
- #include "gfx/gfxDrawUtil.h"
- GFXImplementVertexFormat( TerrVertex )
- {
- addElement( "POSITION", GFXDeclType_Float3 );
- addElement( "NORMAL", GFXDeclType_Float3 );
- addElement( "TangentZ", GFXDeclType_Float, 0 );
- addElement( "Empty", GFXDeclType_Float, 1 );
- };
- const U32 TerrCell::smMinCellSize = 64;
- const U32 TerrCell::smVBStride = TerrCell::smMinCellSize + 1; // 129
- const U32 TerrCell::smVBSize = ( TerrCell::smVBStride * TerrCell::smVBStride ) +
- ( TerrCell::smVBStride * 4 ); // 17,157
- const U32 TerrCell::smPBSize = ( TerrCell::smMinCellSize * TerrCell::smMinCellSize * 6 ) +
- ( TerrCell::smMinCellSize * 4 * 6 ); // 101,376
- const U32 TerrCell::smTriCount = TerrCell::smPBSize / 3; // 33,792
- TerrCell::TerrCell()
- : mTriCount( 0 ),
- mHasEmpty( false ),
- mMaterial( NULL ),
- mMaterials( 0 ),
- mIsInteriorOnly( false ),
- mSize(smMinCellSize),
- mLevel(0),
- mTerrain(NULL),
- mRadius(0.5f)
- {
- dMemset( mChildren, 0, sizeof( mChildren ) );
- zode_vertexBuffer = 0;
- }
- TerrCell::~TerrCell()
- {
- SAFE_DELETE( mMaterial );
- for ( U32 i=0; i < 4; i++ )
- SAFE_DELETE( mChildren[i] );
- deleteZodiacVertexBuffer();
- }
- void TerrCell::createPrimBuffer( GFXPrimitiveBufferHandle *primBuffer )
- {
- PROFILE_SCOPE( TerrCell_AllocPrimBuffer );
- primBuffer->set( GFX, smPBSize, 1, GFXBufferTypeStatic, "TerrCell" );
-
- // We don't use the primitive for normal clipmap
- // rendering, but it is used for the shadow pass.
- GFXPrimitive *prim = primBuffer->getPointer()->mPrimitiveArray;
- prim->type = GFXTriangleList;
- prim->numPrimitives = smTriCount;
- prim->numVertices = smVBSize;
- //
- // The vertex pattern for the terrain is as
- // follows...
- //
- // 0----1----2.....n
- // |\ | /|
- // | \ | / |
- // | \ | / |
- // | \|/ |
- // n----n----n
- // | /|\ |
- // | / | \ |
- // | / | \ |
- // |/ | \|
- // n----n----n
- //
- // Lock and fill it up!
- U16 *idxBuff;
- primBuffer->lock( &idxBuff );
- U32 counter = 0;
- U32 maxIndex = 0;
- for ( U32 y = 0; y < smMinCellSize; y++ )
- {
- const U32 yTess = y % 2;
- for ( U32 x = 0; x < smMinCellSize; x++ )
- {
- U32 index = ( y * smVBStride ) + x;
-
- const U32 xTess = x % 2;
- if ( ( xTess == 0 && yTess == 0 ) ||
- ( xTess != 0 && yTess != 0 ) )
- {
- idxBuff[0] = index + 0;
- idxBuff[1] = index + smVBStride;
- idxBuff[2] = index + smVBStride + 1;
- idxBuff[3] = index + 0;
- idxBuff[4] = index + smVBStride + 1;
- idxBuff[5] = index + 1;
- }
- else
- {
- idxBuff[0] = index + 1;
- idxBuff[1] = index;
- idxBuff[2] = index + smVBStride;
- idxBuff[3] = index + 1;
- idxBuff[4] = index + smVBStride;
- idxBuff[5] = index + smVBStride + 1;
- }
- idxBuff += 6;
- maxIndex = index + 1 + smVBStride;
- counter += 6;
- }
- }
- // Now add indices for the 'skirts'.
- // These could probably be reduced to a loop.
- // Temporaries that hold triangle indices.
- // Top/Bottom - 0,1
- U32 t0, t1, b0, b1;
- // Top edge skirt...
- // Index to the first vert of the top row.
- U32 startIndex = 0;
- // Index to the first vert of the skirt under the top row.
- U32 skirtStartIdx = smVBStride * smVBStride;
- // Step to go one vert to the right.
- U32 step = 1;
- for ( U32 i = 0; i < smMinCellSize; i++ )
- {
- t0 = startIndex + i * step;
- t1 = t0 + step;
- b0 = skirtStartIdx + i;
- b1 = skirtStartIdx + i + 1;
- idxBuff[0] = b0;
- idxBuff[1] = t0;
- idxBuff[2] = t1;
- idxBuff[3] = b1;
- idxBuff[4] = b0;
- idxBuff[5] = t1;
- idxBuff += 6;
- maxIndex = b1;
- counter += 6;
- }
- // Bottom edge skirt...
- // Index to the first vert of the bottom row.
- startIndex = smVBStride * smVBStride - smVBStride;
- // Index to the first vert of the skirt under the bottom row.
- skirtStartIdx = startIndex + smVBStride * 2;
- // Step to go one vert to the right.
- step = 1;
-
- for ( U32 i = 0; i < smMinCellSize; i++ )
- {
- t0 = startIndex + ( i * step );
- t1 = t0 + step;
- b0 = skirtStartIdx + i;
- b1 = skirtStartIdx + i + 1;
- idxBuff[0] = t1;
- idxBuff[1] = t0;
- idxBuff[2] = b0;
- idxBuff[3] = t1;
- idxBuff[4] = b0;
- idxBuff[5] = b1;
- idxBuff += 6;
- maxIndex = b1;
- counter += 6;
- }
- // Left edge skirt...
- // Index to the first vert of the left column.
- startIndex = 0;
- // Index to the first vert of the skirt under the left column.
- skirtStartIdx = smVBStride * smVBStride + smVBStride * 2;
- // Step to go one vert down.
- step = smVBStride;
- for ( U32 i = 0; i < smMinCellSize; i++ )
- {
- t0 = startIndex + ( i * step );
- t1 = t0 + step;
- b0 = skirtStartIdx + i;
- b1 = skirtStartIdx + i + 1;
- idxBuff[0] = t1;
- idxBuff[1] = t0;
- idxBuff[2] = b0;
- idxBuff[3] = t1;
- idxBuff[4] = b0;
- idxBuff[5] = b1;
- idxBuff += 6;
- maxIndex = b1;
- counter += 6;
- }
- // Right edge skirt...
- // Index to the first vert of the right column.
- startIndex = smVBStride - 1;
- // Index to the first vert of the skirt under the right column.
- skirtStartIdx = smVBStride * smVBStride + smVBStride * 3;
- // Step to go one vert down.
- step = smVBStride;
- for ( U32 i = 0; i < smMinCellSize; i++ )
- {
- t0 = startIndex + ( i * step );
- t1 = t0 + step;
- b0 = skirtStartIdx + i;
- b1 = skirtStartIdx + i + 1;
- idxBuff[0] = b0;
- idxBuff[1] = t0;
- idxBuff[2] = t1;
- idxBuff[3] = b1;
- idxBuff[4] = b0;
- idxBuff[5] = t1;
- idxBuff += 6;
- maxIndex = b1;
- counter += 6;
- }
- primBuffer->unlock();
- }
- TerrCell* TerrCell::init( TerrainBlock *terrain )
- {
- // Just create the root cell and call the inner init.
- TerrCell *root = new TerrCell;
- root->_init( terrain,
- Point2I( 0, 0 ),
- terrain->getBlockSize(),
- 0 );
- // Set initial states of OBBs.
- root->updateOBBs();
- return root;
- }
- void TerrCell::_init( TerrainBlock *terrain,
- const Point2I &point,
- U32 size,
- U32 level )
- {
- PROFILE_SCOPE( TerrCell_Init );
- mTerrain = terrain;
- mPoint = point;
- mSize = size;
- mLevel = level;
- // Generate a VB (and maybe a PB) for this cell, unless we are the Root cell.
- if ( level > 0 )
- {
- _updateVertexBuffer();
- _updatePrimitiveBuffer();
- }
-
- if ( mSize <= smMinCellSize )
- {
- // Update our bounds and materials... the
- // parent will use it to update itself.
- _updateBounds();
- _updateMaterials();
- return;
- }
- // Create our children and update our
- // bounds and materials from them.
- const U32 childSize = mSize / 2;
- const U32 childLevel = mLevel + 1;
- mChildren[0] = new TerrCell;
- mChildren[0]->_init( mTerrain,
- Point2I( mPoint.x, mPoint.y ),
- childSize,
- childLevel );
- mBounds = mChildren[0]->getBounds();
- mMaterials = mChildren[0]->getMaterials();
- mChildren[1] = new TerrCell;
- mChildren[1]->_init( mTerrain,
- Point2I( mPoint.x + childSize, mPoint.y ),
- childSize,
- childLevel );
- mBounds.intersect( mChildren[1]->getBounds() );
- mMaterials |= mChildren[1]->getMaterials();
- mChildren[2] = new TerrCell;
- mChildren[2]->_init( mTerrain,
- Point2I( mPoint.x, mPoint.y + childSize ),
- childSize,
- childLevel );
- mBounds.intersect( mChildren[2]->getBounds() );
- mMaterials |= mChildren[2]->getMaterials();
- mChildren[3] = new TerrCell;
- mChildren[3]->_init( mTerrain,
- Point2I( mPoint.x + childSize, mPoint.y + childSize ),
- childSize,
- childLevel );
- mBounds.intersect( mChildren[3]->getBounds() );
- mMaterials |= mChildren[3]->getMaterials();
- mRadius = mBounds.len() * 0.5f;
- _updateOBB();
- }
- void TerrCell::updateGrid( const RectI &gridRect, bool opacityOnly )
- {
- PROFILE_SCOPE( TerrCell_UpdateGrid );
- // If we have a VB... then update it.
- if ( mVertexBuffer.isValid() && !opacityOnly )
- _updateVertexBuffer();
- // Update our PB, if any
- _updatePrimitiveBuffer();
- // If we don't have children... then we're
- // a leaf at the bottom of the cell quadtree
- // and we should just update our bounds.
- if ( !mChildren[0] )
- {
- if ( !opacityOnly )
- _updateBounds();
- _updateMaterials();
- return;
- }
- // Otherwise, we must call updateGrid on our children
- // and then update our bounds/materials AFTER to contain them.
- mMaterials = 0;
- for ( U32 i = 0; i < 4; i++ )
- {
- TerrCell *cell = mChildren[i];
- // The overlap test doesn't hit shared edges
- // so grow it a bit when we create it.
- const RectI cellRect( cell->mPoint.x - 1,
- cell->mPoint.y - 1,
- cell->mSize + 2,
- cell->mSize + 2 );
- // We do an overlap and containment test as it
- // properly handles zero sized rects.
- if ( cellRect.contains( gridRect ) ||
- cellRect.overlaps( gridRect ) )
- cell->updateGrid( gridRect, opacityOnly );
- // Update the bounds from our children.
- if ( !opacityOnly )
- {
- if ( i == 0 )
- mBounds = mChildren[i]->getBounds();
- else
- mBounds.intersect( mChildren[i]->getBounds() );
- mRadius = mBounds.len() * 0.5f;
- }
- // Update the material flags.
- mMaterials |= mChildren[i]->getMaterials();
- }
- if ( mMaterial )
- mMaterial->init( mTerrain, mMaterials );
- }
- void TerrCell::_updateVertexBuffer()
- {
- PROFILE_SCOPE( TerrCell_UpdateVertexBuffer );
- // Start off with no empty squares
- mHasEmpty = false;
- mEmptyVertexList.clear();
- mVertexBuffer.set( GFX, smVBSize, GFXBufferTypeStatic );
- const F32 squareSize = mTerrain->getSquareSize();
- const U32 blockSize = mTerrain->getBlockSize();
- const U32 stepSize = mSize / smMinCellSize;
- U32 vbcounter = 0;
- TerrVertex *vert = mVertexBuffer.lock();
- Point2I gridPt;
- Point2F point;
- F32 height;
- Point3F normal;
-
- const TerrainFile *file = mTerrain->getFile();
- for ( U32 y = 0; y < smVBStride; y++ )
- {
- 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 );
- // 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.z = height;
- // Get the normal.
- mTerrain->getSmoothNormal( point, &normal, true, false );
- vert->normal = normal;
- // Get the tangent z.
- vert->tangentZ = fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) ) - height;
- // Test the empty state for this vert.
- if ( file->isEmptyAt( gridPt.x, gridPt.y ) )
- {
- mHasEmpty = true;
- mEmptyVertexList.push_back( vbcounter );
- }
- vbcounter++;
- ++vert;
- }
- }
- // Add verts for 'skirts' around/beneath the edge verts of this cell.
- // This could probably be reduced to a loop...
-
- const F32 skirtDepth = mSize / smMinCellSize * mTerrain->getSquareSize();
- // Top edge skirt
- for ( U32 i = 0; i < smVBStride; i++ )
- {
- gridPt.x = mClamp( mPoint.x + i * stepSize, 0, blockSize - 1 );
- gridPt.y = mClamp( mPoint.y, 0, blockSize - 1 );
-
- 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.z = height - skirtDepth;
- // Get the normal.
- mTerrain->getNormal( point, &normal, true, false );
- vert->normal = normal;
- // Get the tangent.
- vert->tangentZ = height - fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) );
- vbcounter++;
- ++vert;
- }
- // Bottom edge skirt
- for ( U32 i = 0; i < smVBStride; i++ )
- {
- gridPt.x = mClamp( mPoint.x + i * stepSize, 0, blockSize - 1 );
- gridPt.y = mClamp( mPoint.y + smMinCellSize * stepSize, 0, blockSize - 1 );
- 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.z = height - skirtDepth;
- // Get the normal.
- mTerrain->getNormal( point, &normal, true, false );
- vert->normal = normal;
- // Get the tangent.
- vert->tangentZ = height - fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) );
- vbcounter++;
- ++vert;
- }
- // Left edge skirt
- for ( U32 i = 0; i < smVBStride; i++ )
- {
- gridPt.x = mClamp( mPoint.x, 0, blockSize - 1 );
- gridPt.y = mClamp( mPoint.y + i * stepSize, 0, blockSize - 1 );
- 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.z = height - skirtDepth;
- // Get the normal.
- mTerrain->getNormal( point, &normal, true, false );
- vert->normal = normal;
- // Get the tangent.
- vert->tangentZ = height - fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) );
- vbcounter++;
- ++vert;
- }
- // Right edge skirt
- for ( U32 i = 0; i < smVBStride; i++ )
- {
- gridPt.x = mClamp( mPoint.x + smMinCellSize * stepSize, 0, blockSize - 1 );
- gridPt.y = mClamp( mPoint.y + i * stepSize, 0, blockSize - 1 );
- 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.z = height - skirtDepth;
- // Get the normal.
- mTerrain->getNormal( point, &normal, true, false );
- vert->normal = normal;
- // Get the tangent.
- vert->tangentZ = height - fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) );
- vbcounter++;
- ++vert;
- }
- AssertFatal( vbcounter == smVBSize, "bad" );
- mVertexBuffer.unlock();
- deleteZodiacVertexBuffer();
- }
- void TerrCell::_updatePrimitiveBuffer()
- {
- PROFILE_SCOPE( TerrCell_UpdatePrimitiveBuffer );
- if ( !mHasEmpty )
- {
- if ( mPrimBuffer.isValid() )
- {
- // There are no more empty squares for this cell, so
- // get rid of the primitive buffer to use the standard one.
- mPrimBuffer = NULL;
- }
- return;
- }
- // Build our custom primitive buffer. We're setting it to the maximum allowed
- // size, but should be just shy of it depending on the number of empty squares
- // in this cell. We could calculate it, but note that it would be different
- // from mEmptyVertexList.size() as that can include vertices on the edges that
- // are really considered part of another cell's squares. So we take a slightly
- // larger buffer over running through the calculation.
- mPrimBuffer.set( GFX, smPBSize, 1, GFXBufferTypeStatic, "TerrCell" );
- GFXPrimitive *prim = mPrimBuffer.getPointer()->mPrimitiveArray;
- prim->type = GFXTriangleList;
- prim->numVertices = smVBSize;
- mTriCount = 0;
- // Lock and fill it up!
- U16 *idxBuff;
- mPrimBuffer.lock( &idxBuff );
- U32 counter = 0;
- U32 maxIndex = 0;
- for ( U32 y = 0; y < smMinCellSize; y++ )
- {
- const U32 yTess = y % 2;
- for ( U32 x = 0; x < smMinCellSize; x++ )
- {
- U32 index = ( y * smVBStride ) + x;
-
- // Should this square be skipped?
- if ( _isVertIndexEmpty(index) )
- continue;
- const U32 xTess = x % 2;
- if ( ( xTess == 0 && yTess == 0 ) ||
- ( xTess != 0 && yTess != 0 ) )
- {
- idxBuff[0] = index + 0;
- idxBuff[1] = index + smVBStride;
- idxBuff[2] = index + smVBStride + 1;
- idxBuff[3] = index + 0;
- idxBuff[4] = index + smVBStride + 1;
- idxBuff[5] = index + 1;
- }
- else
- {
- idxBuff[0] = index + 1;
- idxBuff[1] = index;
- idxBuff[2] = index + smVBStride;
- idxBuff[3] = index + 1;
- idxBuff[4] = index + smVBStride;
- idxBuff[5] = index + smVBStride + 1;
- }
- idxBuff += 6;
- maxIndex = index + 1 + smVBStride;
- counter += 6;
- mTriCount += 2;
- }
- }
- // Now add indices for the 'skirts'.
- // These could probably be reduced to a loop.
- // Temporaries that hold triangle indices.
- // Top/Bottom - 0,1
- U32 t0, t1, b0, b1;
- // Top edge skirt...
- // Index to the first vert of the top row.
- U32 startIndex = 0;
- // Index to the first vert of the skirt under the top row.
- U32 skirtStartIdx = smVBStride * smVBStride;
- // Step to go one vert to the right.
- U32 step = 1;
- for ( U32 i = 0; i < smMinCellSize; i++ )
- {
- t0 = startIndex + i * step;
-
- // Should this square be skipped?
- if ( _isVertIndexEmpty(t0) )
- continue;
- t1 = t0 + step;
- b0 = skirtStartIdx + i;
- b1 = skirtStartIdx + i + 1;
- idxBuff[0] = b0;
- idxBuff[1] = t0;
- idxBuff[2] = t1;
- idxBuff[3] = b1;
- idxBuff[4] = b0;
- idxBuff[5] = t1;
- idxBuff += 6;
- maxIndex = b1;
- counter += 6;
- mTriCount += 2;
- }
- // Bottom edge skirt...
- // Index to the first vert of the bottom row.
- startIndex = smVBStride * smVBStride - smVBStride;
- // Index to the first vert of the skirt under the bottom row.
- skirtStartIdx = startIndex + smVBStride * 2;
- // Step to go one vert to the right.
- step = 1;
-
- for ( U32 i = 0; i < smMinCellSize; i++ )
- {
- t0 = startIndex + ( i * step );
-
- // Should this square be skipped? We actually need to test
- // the vertex one row down as it defines the empty state
- // for this square.
- if ( _isVertIndexEmpty( t0 - smVBStride ) )
- continue;
- t1 = t0 + step;
- b0 = skirtStartIdx + i;
- b1 = skirtStartIdx + i + 1;
- idxBuff[0] = t1;
- idxBuff[1] = t0;
- idxBuff[2] = b0;
- idxBuff[3] = t1;
- idxBuff[4] = b0;
- idxBuff[5] = b1;
- idxBuff += 6;
- maxIndex = b1;
- counter += 6;
- mTriCount += 2;
- }
- // Left edge skirt...
- // Index to the first vert of the left column.
- startIndex = 0;
- // Index to the first vert of the skirt under the left column.
- skirtStartIdx = smVBStride * smVBStride + smVBStride * 2;
- // Step to go one vert down.
- step = smVBStride;
- for ( U32 i = 0; i < smMinCellSize; i++ )
- {
- t0 = startIndex + ( i * step );
-
- // Should this square be skipped?
- if ( _isVertIndexEmpty(t0) )
- continue;
- t1 = t0 + step;
- b0 = skirtStartIdx + i;
- b1 = skirtStartIdx + i + 1;
- idxBuff[0] = t1;
- idxBuff[1] = t0;
- idxBuff[2] = b0;
- idxBuff[3] = t1;
- idxBuff[4] = b0;
- idxBuff[5] = b1;
- idxBuff += 6;
- maxIndex = b1;
- counter += 6;
- mTriCount += 2;
- }
- // Right edge skirt...
- // Index to the first vert of the right column.
- startIndex = smVBStride - 1;
- // Index to the first vert of the skirt under the right column.
- skirtStartIdx = smVBStride * smVBStride + smVBStride * 3;
- // Step to go one vert down.
- step = smVBStride;
- for ( U32 i = 0; i < smMinCellSize; i++ )
- {
- t0 = startIndex + ( i * step );
-
- // Should this square be skipped? We actually need to test
- // the vertex one column to the left as it defines the empty
- // state for this square.
- if ( _isVertIndexEmpty( t0 - 1 ) )
- continue;
- t1 = t0 + step;
- b0 = skirtStartIdx + i;
- b1 = skirtStartIdx + i + 1;
- idxBuff[0] = b0;
- idxBuff[1] = t0;
- idxBuff[2] = t1;
- idxBuff[3] = b1;
- idxBuff[4] = b0;
- idxBuff[5] = t1;
- idxBuff += 6;
- maxIndex = b1;
- counter += 6;
- mTriCount += 2;
- }
- mPrimBuffer.unlock();
- prim->numPrimitives = mTriCount;
- }
- void TerrCell::_updateMaterials()
- {
- PROFILE_SCOPE( TerrCell_UpdateMaterials );
- // This should really only be called for cells of smMinCellSize,
- // in which case stepSize is always one.
- 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++ )
- {
- for ( x = 0; x < smVBStride; x++ )
- {
- index = file->getLayerIndex( mPoint.x + x * stepSize,
- mPoint.y + y * stepSize );
-
- // 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);
- }
- }
- if ( mMaterial )
- mMaterial->init( mTerrain, mMaterials );
- }
- void TerrCell::_updateBounds()
- {
- PROFILE_SCOPE( TerrCell_UpdateBounds );
- const F32 squareSize = mTerrain->getSquareSize();
- // This should really only be called for cells of smMinCellSize,
- // in which case stepSize is always one.
- const U32 stepSize = mSize / smMinCellSize;
- // Prepare to expand the bounds.
- mBounds.minExtents.set( F32_MAX, F32_MAX, F32_MAX );
- mBounds.maxExtents.set( -F32_MAX, -F32_MAX, -F32_MAX );
- Point3F vert;
- Point2F texCoord;
- const TerrainFile *file = mTerrain->getFile();
- for ( U32 y = 0; y < smVBStride; y++ )
- {
- for ( U32 x = 0; x < smVBStride; x++ )
- {
- // Setup this point.
- vert.x = (F32)( mPoint.x + x * stepSize ) * squareSize;
- vert.y = (F32)( mPoint.y + y * stepSize ) * squareSize;
- vert.z = fixedToFloat( file->getHeight( mPoint.x + x,
- mPoint.y + y ) );
- // HACK: Call it twice to deal with the inverted
- // inital bounds state... shouldn't be a perf issue.
- mBounds.extend( vert );
- mBounds.extend( vert );
- }
- }
- mRadius = mBounds.len() * 0.5;
- _updateOBB();
- }
- void TerrCell::_updateOBB()
- {
- mOBB.set( mTerrain->getTransform(), mBounds );
- }
- void TerrCell::updateOBBs()
- {
- _updateOBB();
- // Update children.
- if( mChildren[ 0 ] )
- for( U32 i = 0; i < 4; ++ i )
- mChildren[ i ]->updateOBBs();
- }
- void TerrCell::updateZoning( const SceneZoneSpaceManager *zoneManager )
- {
- PROFILE_SCOPE( TerrCell_UpdateZoning );
- mZoneOverlap.setSize( zoneManager->getNumZones() );
- mZoneOverlap.clear();
- mIsInteriorOnly = true;
- if ( mChildren[0] == NULL )
- {
- Box3F worldBounds( mBounds );
- mTerrain->getTransform().mul( worldBounds );
- Vector<U32> zones;
- zoneManager->findZones( worldBounds, zones );
- for ( U32 i=0; i < zones.size(); i++ )
- {
- // Set overlap bit for zone except it's the outdoor zone.
- if( zones[ i ] != SceneZoneSpaceManager::RootZoneId )
- mZoneOverlap.set( zones[i] );
- else
- mIsInteriorOnly = false;
- }
- return;
- }
- for ( U32 i = 0; i < 4; i++ )
- {
- TerrCell *cell = mChildren[i];
- cell->updateZoning( zoneManager );
- mZoneOverlap.combineOR( cell->getZoneOverlap() );
- mIsInteriorOnly &= cell->mIsInteriorOnly;
- }
- }
- void TerrCell::cullCells( const SceneRenderState *state,
- const Point3F &objLodPos,
- Vector<TerrCell*> *outCells )
- {
- // If we have a VB and no children then just add
- // ourselves to the results and return.
- if ( mVertexBuffer.isValid() && !mChildren[0] )
- {
- outCells->push_back( this );
- return;
- }
- const F32 screenError = mTerrain->getScreenError();
- const BitVector &zoneState = state->getCullingState().getZoneVisibilityFlags();
- for ( U32 i = 0; i < 4; i++ )
- {
- TerrCell *cell = mChildren[i];
- // Test cell visibility for interior zones.
-
- const bool visibleInside = !cell->getZoneOverlap().empty() ? zoneState.testAny( cell->getZoneOverlap() ) : false;
- // Test cell visibility for outdoor zone, but only
- // if we need to.
- bool visibleOutside = false;
- if( !mIsInteriorOnly && !visibleInside )
- {
- U32 outdoorZone = SceneZoneSpaceManager::RootZoneId;
- visibleOutside = !state->getCullingState().isCulled( cell->mOBB, &outdoorZone, 1 );
- }
- // Skip cell if neither visible indoors nor outdoors.
- if( !visibleInside && !visibleOutside )
- continue;
- // Lod based on screen error...
- // If far enough, just add this child cells vb ( skipping its children ).
- F32 dist = cell->getDistanceTo( objLodPos );
- F32 errorMeters = ( cell->mSize / smMinCellSize ) * mTerrain->getSquareSize();
- U32 errorPixels = mCeil( state->projectRadius( dist, errorMeters ) );
- if ( errorPixels < screenError )
- {
- if ( cell->mVertexBuffer.isValid() )
- outCells->push_back( cell );
- }
- else
- cell->cullCells( state, objLodPos, outCells );
- }
- }
- void TerrCell::getRenderPrimitive( GFXPrimitive *prim,
- GFXVertexBufferHandleBase *vertBuff,
- GFXPrimitiveBufferHandle *primBuff ) const
- {
- *vertBuff = mVertexBuffer;
- // Only supply a primitive buffer if we're using our own
- // due to empty squares.
- bool useStaticPrimBuffer = true;
- if ( mPrimBuffer.isValid() )
- {
- useStaticPrimBuffer = false;
- *primBuff = mPrimBuffer;
- }
-
- prim->type = GFXTriangleList;
- prim->startVertex = 0;
- prim->minIndex = 0;
- prim->startIndex = 0;
- prim->numVertices = smVBSize;
- if ( useStaticPrimBuffer )
- {
- // Use the standard primitive buffer count
- prim->numPrimitives = smTriCount;
- }
- else
- {
- // Use our triangle count that matches out primitive buffer
- prim->numPrimitives = mTriCount;
- }
- }
- void TerrCell::renderBounds() const
- {
- LinearColorF color;
- color.interpolate( ColorI::RED, ColorI::GREEN, (F32)mLevel / 3.0f );
- GFXStateBlockDesc desc;
- desc.setZReadWrite( true, false );
- desc.fillMode = GFXFillWireframe;
-
- GFX->getDrawUtil()->drawCube( desc, mBounds, color.toColorI());
- }
- void TerrCell::preloadMaterials()
- {
- PROFILE_SCOPE( TerrCell_PreloadMaterials );
- // If we have a VB then we need a material.
- if ( mVertexBuffer.isValid() )
- {
- TerrainCellMaterial *material = getMaterial();
- material->getReflectMat();
- if ( GFX->getPixelShaderVersion() > 2.0f &&
- String::compare( LIGHTMGR->getId(), "BLM" ) != 0)
- material->getDeferredMat();
- }
- for ( U32 i = 0; i < 4; i++ )
- if ( mChildren[i] )
- mChildren[i]->preloadMaterials();
- }
- TerrainCellMaterial* TerrCell::getMaterial()
- {
- if ( !mMaterial )
- {
- mMaterial = new TerrainCellMaterial;
- mMaterial->init( mTerrain, mMaterials );
- }
- return mMaterial;
- }
- void TerrCell::deleteMaterials()
- {
- SAFE_DELETE( mMaterial );
- for ( U32 i = 0; i < 4; i++ )
- if ( mChildren[i] )
- mChildren[i]->deleteMaterials();
- }
- const Point3F* TerrCell::getZodiacVertexBuffer()
- {
- if (!zode_vertexBuffer)
- createZodiacVertexBuffer();
- return zode_vertexBuffer;
- }
- void TerrCell::createZodiacPrimBuffer(U16** zode_primBuffer)
- {
- if (*zode_primBuffer != 0)
- delete [] *zode_primBuffer;
- *zode_primBuffer = new U16[TerrCell::smMinCellSize*TerrCell::smMinCellSize*6];
- // Lock and fill it up!
- U16* idxBuff = *zode_primBuffer;
- U32 counter = 0;
- U32 maxIndex = 0;
- for ( U32 y = 0; y < smMinCellSize; y++ )
- {
- const U32 yTess = y % 2;
- for ( U32 x = 0; x < smMinCellSize; x++ )
- {
- U32 index = ( y * smVBStride ) + x;
-
- const U32 xTess = x % 2;
- if ( ( xTess == 0 && yTess == 0 ) ||
- ( xTess != 0 && yTess != 0 ) )
- {
- idxBuff[0] = index + 0;
- idxBuff[1] = index + smVBStride;
- idxBuff[2] = index + smVBStride + 1;
- idxBuff[3] = index + 0;
- idxBuff[4] = index + smVBStride + 1;
- idxBuff[5] = index + 1;
- }
- else
- {
- idxBuff[0] = index + 1;
- idxBuff[1] = index;
- idxBuff[2] = index + smVBStride;
- idxBuff[3] = index + 1;
- idxBuff[4] = index + smVBStride;
- idxBuff[5] = index + smVBStride + 1;
- }
- idxBuff += 6;
- maxIndex = index + 1 + smVBStride;
- counter += 6;
- }
- }
- }
- void TerrCell::createZodiacVertexBuffer()
- {
- const F32 squareSize = mTerrain->getSquareSize();
- const U32 blockSize = mTerrain->getBlockSize();
- const U32 stepSize = mSize / smMinCellSize;
- if (zode_vertexBuffer)
- delete [] zode_vertexBuffer;
- zode_vertexBuffer = new Point3F[smVBStride*smVBStride];
- Point3F* vert = zode_vertexBuffer;
- Point2I gridPt;
- Point2F point;
- F32 height;
-
- const TerrainFile *file = mTerrain->getFile();
- for ( U32 y = 0; y < smVBStride; y++ )
- {
- 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 );
- // Setup this point.
- point.x = (F32)gridPt.x * squareSize;
- point.y = (F32)gridPt.y * squareSize;
- height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) );
- vert->x = point.x;
- vert->y = point.y;
- vert->z = height;
- ++vert;
- }
- }
- }
- void TerrCell::deleteZodiacVertexBuffer()
- {
- if (zode_vertexBuffer)
- {
- delete [] zode_vertexBuffer;
- zode_vertexBuffer = 0;
- }
- }
|