12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664 |
- //-----------------------------------------------------------------------------
- // 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/terrData.h"
- #include "terrain/terrCollision.h"
- #include "terrain/terrCell.h"
- #include "terrain/terrRender.h"
- #include "terrain/terrMaterial.h"
- #include "terrain/terrCellMaterial.h"
- #include "gui/worldEditor/terrainEditor.h"
- #include "math/mathIO.h"
- #include "core/stream/fileStream.h"
- #include "core/stream/bitStream.h"
- #include "console/consoleTypes.h"
- #include "sim/netConnection.h"
- #include "core/util/safeDelete.h"
- #include "T3D/objectTypes.h"
- #include "renderInstance/renderPassManager.h"
- #include "scene/sceneRenderState.h"
- #include "materials/materialManager.h"
- #include "materials/baseMatInstance.h"
- #include "gfx/gfxTextureManager.h"
- #include "gfx/gfxCardProfile.h"
- #include "gfx/gfxAPI.h"
- #include "core/resourceManager.h"
- #include "T3D/physics/physicsPlugin.h"
- #include "T3D/physics/physicsBody.h"
- #include "T3D/physics/physicsCollision.h"
- #include "console/engineAPI.h"
- #include "core/util/safeRelease.h"
- #include "T3D/assets/TerrainMaterialAsset.h"
- using namespace Torque;
- IMPLEMENT_CO_NETOBJECT_V1(TerrainBlock);
- ConsoleDocClass( TerrainBlock,
- "@brief Represent a terrain object in a Torque 3D level\n\n"
- "@tsexample\n"
- "new TerrainBlock(theTerrain)\n"
- "{\n"
- " terrainFile = \"art/terrains/Deathball Desert_0.ter\";\n"
- " squareSize = \"2\";\n"
- " tile = \"0\";\n"
- " baseTexSize = \"1024\";\n"
- " screenError = \"16\";\n"
- " position = \"-1024 -1024 179.978\";\n"
- " rotation = \"1 0 0 0\";\n"
- " scale = \"1 1 1\";\n"
- " isRenderEnabled = \"true\";\n"
- " canSaveDynamicFields = \"1\";\n"
- "};\n"
- "@endtsexample\n\n"
- "@see TerrainMaterial\n\n"
- "@ingroup Terrain\n"
- );
- Signal<void(U32,TerrainBlock*,const Point2I& ,const Point2I&)> TerrainBlock::smUpdateSignal;
- F32 TerrainBlock::smLODScale = 1.0f;
- F32 TerrainBlock::smDetailScale = 1.0f;
- //RBP - Global function declared in Terrdata.h
- TerrainBlock* getTerrainUnderWorldPoint(const Point3F & wPos)
- {
- // Cast a ray straight down from the world position and see which
- // Terrain is the closest to our starting point
- Point3F startPnt = wPos;
- Point3F endPnt = wPos + Point3F(0.0f, 0.0f, -10000.0f);
- S32 blockIndex = -1;
- F32 nearT = 1.0f;
- SimpleQueryList queryList;
- gServerContainer.findObjects( TerrainObjectType, SimpleQueryList::insertionCallback, &queryList);
- for (U32 i = 0; i < queryList.mList.size(); i++)
- {
- Point3F tStartPnt, tEndPnt;
- TerrainBlock* terrBlock = dynamic_cast<TerrainBlock*>(queryList.mList[i]);
- terrBlock->getWorldTransform().mulP(startPnt, &tStartPnt);
- terrBlock->getWorldTransform().mulP(endPnt, &tEndPnt);
- RayInfo ri;
- if (terrBlock->castRayI(tStartPnt, tEndPnt, &ri, true))
- {
- if (ri.t < nearT)
- {
- blockIndex = i;
- nearT = ri.t;
- }
- }
- }
- if (blockIndex > -1)
- return (TerrainBlock*)(queryList.mList[blockIndex]);
- return NULL;
- }
- ConsoleDocFragment _getTerrainUnderWorldPoint1(
- "@brief Gets the terrain block that is located under the given world point\n\n"
- "@param position The world space coordinate you wish to query at. Formatted as (\"x y z\")\n\n"
- "@return Returns the ID of the requested terrain block (0 if not found).\n\n"
- "@ingroup Terrain",
- NULL,
- "bool getTerrainUnderWorldPoint( Point3F position );"
- );
- ConsoleDocFragment _getTerrainUnderWorldPoint2(
- "@brief Takes a world point and find the \"highest\" terrain underneath it\n\n"
- "@param x The X coordinate in world space\n"
- "@param y The Y coordinate in world space\n\n"
- "@param z The Z coordinate in world space\n\n"
- "@return Returns the ID of the requested terrain block (0 if not found).\n\n"
- "@ingroup Terrain",
- NULL,
- "bool getTerrainUnderWorldPoint( F32 x, F32 y, F32 z);"
- );
- DefineEngineFunction( getTerrainUnderWorldPoint, S32, (const char* ptOrX, const char* y, const char* z), ("", ""),
- "(Point3F x/y/z) Gets the terrain block that is located under the given world point.\n"
- "@param x/y/z The world coordinates (floating point values) you wish to query at. "
- "These can be formatted as either a string (\"x y z\") or separately as (x, y, z)\n"
- "@return Returns the ID of the requested terrain block (0 if not found).\n\n"
- "@hide")
- {
- Point3F pos;
- if(!String::isEmpty(ptOrX) && String::isEmpty(y) && String::isEmpty(z))
- dSscanf(ptOrX, "%f %f %f", &pos.x, &pos.y, &pos.z);
- else if(!String::isEmpty(ptOrX) && !String::isEmpty(y) && !String::isEmpty(z))
- {
- pos.x = dAtof(ptOrX);
- pos.y = dAtof(y);
- pos.z = dAtof(z);
- }
- TerrainBlock* terrain = getTerrainUnderWorldPoint(pos);
- if(terrain != NULL)
- {
- return terrain->getId();
- }
- return 0;
- }
- typedef TerrainBlock::BaseTexFormat baseTexFormat;
- DefineEnumType(baseTexFormat);
- ImplementEnumType(baseTexFormat,
- "Description\n"
- "@ingroup ?\n\n")
- { TerrainBlock::NONE, "NONE", "No cached terrain.\n" },
- { TerrainBlock::DDS, "DDS", "Cache the terrain in a DDS format.\n" },
- { TerrainBlock::PNG, "PNG", "Cache the terrain in a PNG format.\n" },
- EndImplementEnumType;
- TerrainBlock::TerrainBlock()
- : mLightMap( NULL ),
- mLightMapSize( 256 ),
- mCRC( 0 ),
- mMaxDetailDistance( 0.0f ),
- mBaseTexScaleConst( NULL ),
- mBaseTexIdConst( NULL ),
- mBaseLayerSizeConst(NULL),
- mDetailsDirty( false ),
- mLayerTexDirty( false ),
- mBaseTexSize( 1024 ),
- mBaseTexFormat( TerrainBlock::DDS ),
- mCell( NULL ),
- mBaseMaterial( NULL ),
- mDefaultMatInst( NULL ),
- mSquareSize( 1.0f ),
- mPhysicsRep( NULL ),
- mScreenError( 16 ),
- mCastShadows( true ),
- mZoningDirty( false ),
- mUpdateBasetex ( true ),
- mDetailTextureArray( NULL ),
- mMacroTextureArray( NULL ),
- mOrmTextureArray( NULL ),
- mNormalTextureArray( NULL )
- {
- mTypeMask = TerrainObjectType | StaticObjectType | StaticShapeObjectType;
- mNetFlags.set(Ghostable | ScopeAlways);
- mIgnoreZodiacs = false;
- zode_primBuffer = 0;
- mTerrainAsset = StringTable->EmptyString();
- mTerrainAssetId = StringTable->EmptyString();
- }
- TerrainBlock::~TerrainBlock()
- {
- // Kill collision
- mTerrainConvexList.nukeList();
- SAFE_DELETE(mLightMap);
- mLightMapTex = NULL;
- #ifdef TORQUE_TOOLS
- TerrainEditor* editor = dynamic_cast<TerrainEditor*>(Sim::findObject("ETerrainEditor"));
- if (editor)
- editor->detachTerrain(this);
- #endif
- deleteZodiacPrimitiveBuffer();
- SAFE_RELEASE(mDetailTextureArray);
- SAFE_RELEASE(mMacroTextureArray);
- SAFE_RELEASE(mNormalTextureArray);
- SAFE_RELEASE(mOrmTextureArray);
- }
- void TerrainBlock::_onTextureEvent( GFXTexCallbackCode code )
- {
- if ( code == GFXZombify )
- {
- if ( mBaseTex.isValid() &&
- mBaseTex->isRenderTarget() )
- mBaseTex = NULL;
- mLayerTex = NULL;
- mLightMapTex = NULL;
- }
- }
- bool TerrainBlock::_setSquareSize( void *obj, const char *index, const char *data )
- {
- TerrainBlock *terrain = static_cast<TerrainBlock*>( obj );
- F32 newSqaureSize = dAtof( data );
- if ( !mIsEqual( terrain->mSquareSize, newSqaureSize ) )
- {
- terrain->mSquareSize = newSqaureSize;
- if ( terrain->isServerObject() && terrain->isProperlyAdded() )
- terrain->_updateBounds();
- terrain->setMaskBits( HeightMapChangeMask | SizeMask );
- }
- return false;
- }
- bool TerrainBlock::_setBaseTexSize( void *obj, const char *index, const char *data )
- {
- TerrainBlock *terrain = static_cast<TerrainBlock*>( obj );
- // NOTE: We're limiting the base texture size to
- // 2048 as anything greater in size becomes too
- // large to generate for many cards.
- //
- // If you want to remove this limit feel free, but
- // prepare for problems if you don't ship the baked
- // base texture with your installer.
- //
- S32 texSize = mClamp( dAtoi( data ), 0, 2048 );
- if ( terrain->mBaseTexSize != texSize )
- {
- terrain->mBaseTexSize = texSize;
- terrain->setMaskBits( MaterialMask );
- }
- return false;
- }
- bool TerrainBlock::_setBaseTexFormat(void *obj, const char *index, const char *data)
- {
- TerrainBlock *terrain = static_cast<TerrainBlock*>(obj);
- EngineEnumTable eTable = _baseTexFormat::_sEnumTable;
- for (U8 i = 0; i < eTable.getNumValues(); i++)
- {
- if (strcasecmp(eTable[i].mName, data) == 0)
- {
- terrain->mBaseTexFormat = (BaseTexFormat)eTable[i].mInt;
- terrain->_updateMaterials();
- if (terrain->isServerObject()) return false;
- terrain->_updateLayerTexture();
- // If the cached base texture is older that the terrain file or
- // it doesn't exist then generate and cache it.
- String baseCachePath = terrain->_getBaseTexCacheFileName();
- if (Platform::compareModifiedTimes(baseCachePath, terrain->mTerrainAsset->getTerrainFilePath()) < 0 && terrain->mUpdateBasetex)
- terrain->_updateBaseTexture(true);
- break;
- }
- }
- return false;
- }
- bool TerrainBlock::_setLightMapSize( void *obj, const char *index, const char *data )
- {
- TerrainBlock *terrain = static_cast<TerrainBlock*>(obj);
- // Handle inspector value decrements correctly
- U32 mapSize = dAtoi( data );
- if ( mapSize == terrain->mLightMapSize-1 )
- mapSize = terrain->mLightMapSize/2;
- // Limit the lightmap size, and ensure it is a power of 2
- const U32 maxTextureSize = GFX->getCardProfiler()->queryProfile( "maxTextureSize", 1024 );
- mapSize = mClamp( getNextPow2( mapSize ), 0, maxTextureSize );
- if ( terrain->mLightMapSize != mapSize )
- {
- terrain->mLightMapSize = mapSize;
- terrain->setMaskBits( MaterialMask );
- }
- return false;
- }
- bool TerrainBlock::setFile( const FileName &terrFileName )
- {
- if ( mTerrainAsset && mTerrainAsset->getTerrainFilePath() == terrFileName )
- return mFile != NULL;
- Resource<TerrainFile> file = ResourceManager::get().load( terrFileName );
- if( !file )
- return false;
-
- setFile( file );
- setMaskBits( FileMask | HeightMapChangeMask );
-
- return true;
- }
- void TerrainBlock::setFile(const Resource<TerrainFile>& terr)
- {
- if (mFile)
- {
- GFXTextureManager::removeEventDelegate(this, &TerrainBlock::_onTextureEvent);
- MATMGR->getFlushSignal().remove(this, &TerrainBlock::_onFlushMaterials);
- }
- mFile = terr;
- if (!mFile)
- {
- Con::errorf("TerrainBlock::setFile() - No valid terrain file!");
- return;
- }
- if (terr->mNeedsResaving)
- {
- if (Platform::messageBox("Update Terrain File", "You appear to have a Terrain file in an older format. Do you want Torque to update it?", MBOkCancel, MIQuestion) == MROk)
- {
- mFile->save(terr->mFilePath.getFullPath());
- mFile->mNeedsResaving = false;
- }
- }
- if (terr->mFileVersion != TerrainFile::FILE_VERSION || terr->mNeedsResaving)
- {
- Con::errorf(" *********************************************************");
- Con::errorf(" *********************************************************");
- Con::errorf(" *********************************************************");
- Con::errorf(" PLEASE RESAVE THE TERRAIN FILE FOR THIS MISSION! THANKS!");
- Con::errorf(" *********************************************************");
- Con::errorf(" *********************************************************");
- Con::errorf(" *********************************************************");
- }
- _updateBounds();
- resetWorldBox();
- setRenderTransform(mObjToWorld);
- if (isClientObject())
- {
- if (mCRC != terr.getChecksum())
- {
- NetConnection::setLastError("Your terrain file doesn't match the version that is running on the server.");
- return;
- }
- clearLightMap();
- // Init the detail layer rendering helper.
- _updateMaterials();
- _updateLayerTexture();
- // If the cached base texture is older that the terrain file or
- // it doesn't exist then generate and cache it.
- String baseCachePath = _getBaseTexCacheFileName();
- if (Platform::compareModifiedTimes(baseCachePath, mTerrainAsset->getTerrainFilePath()) < 0 && mUpdateBasetex)
- _updateBaseTexture(true);
- // The base texture should have been cached by now... so load it.
- mBaseTex.set(baseCachePath, &GFXStaticTextureSRGBProfile, "TerrainBlock::mBaseTex");
- GFXTextureManager::addEventDelegate(this, &TerrainBlock::_onTextureEvent);
- MATMGR->getFlushSignal().notify(this, &TerrainBlock::_onFlushMaterials);
- // Build the terrain quadtree.
- _rebuildQuadtree();
- // Preload all the materials.
- mCell->preloadMaterials();
- mZoningDirty = true;
- SceneZoneSpaceManager::getZoningChangedSignal().notify(this, &TerrainBlock::_onZoningChanged);
- }
- else
- mCRC = terr.getChecksum();
- }
- bool TerrainBlock::setTerrainAsset(const StringTableEntry terrainAssetId)
- {
- if (TerrainAsset::getAssetById(terrainAssetId, &mTerrainAsset))
- {
- //Special exception case. If we've defaulted to the 'no shape' mesh, don't save it out, we'll retain the original ids/paths so it doesn't break
- //the TSStatic
- if (!mTerrainAsset.isNull())
- {
- mTerrFileName = StringTable->EmptyString();
- }
- setFile(mTerrainAsset->getTerrainResource());
- setMaskBits(-1);
- return true;
- }
- return false;
- }
- bool TerrainBlock::save(const char *filename)
- {
- return mFile->save(filename);
- }
- bool TerrainBlock::saveAsset()
- {
- if (!mTerrainAsset.isNull() && mTerrainAsset->isAssetValid())
- {
- mTerrainAsset->clearAssetDependencyFields("terrainMaterailAsset");
- AssetQuery* pAssetQuery = new AssetQuery();
- pAssetQuery->registerObject();
- AssetDatabase.findAssetType(pAssetQuery, "TerrainMaterialAsset");
- TerrainBlock* terr = static_cast<TerrainBlock*>(getClientObject());
- if (!terr)
- {
- Con::warnf("No active client terrain while trying to save asset. Could be a server action, but should check to be sure!");
- terr = this;
- }
- for (U32 i = 0; i < pAssetQuery->mAssetList.size(); i++)
- {
- //Acquire it so we can check it for matches
- AssetPtr<TerrainMaterialAsset> terrMatAsset = pAssetQuery->mAssetList[i];
- for (U32 m = 0; m < terr->mFile->mMaterials.size(); m++)
- {
- StringTableEntry intMatName = terr->mFile->mMaterials[m]->getInternalName();
- StringTableEntry assetMatDefName = terrMatAsset->getMaterialDefinitionName();
- if (assetMatDefName == intMatName)
- {
- mTerrainAsset->addAssetDependencyField("terrainMaterailAsset", terrMatAsset.getAssetId());
- }
- }
- terrMatAsset.clear();
- }
- pAssetQuery->destroySelf();
- bool saveAssetSuccess = mTerrainAsset->saveAsset();
- if (!saveAssetSuccess)
- return false;
- return mFile->save(mTerrainAsset->getTerrainFilePath());
- }
- return false;
- }
- bool TerrainBlock::_setTerrainFile( void *obj, const char *index, const char *data )
- {
- //TerrainBlock* terrain = static_cast<TerrainBlock*>( obj )->setFile( FileName( data ) );
- TerrainBlock* terrain = static_cast<TerrainBlock*>(obj);
- StringTableEntry file = StringTable->insert(data);
- if (file != StringTable->EmptyString())
- {
- StringTableEntry assetId = TerrainAsset::getAssetIdByFilename(file);
- if (assetId != StringTable->EmptyString())
- {
- if (terrain->setTerrainAsset(assetId))
- {
- terrain->mTerrainAssetId = assetId;
- terrain->mTerrFileName = StringTable->EmptyString();
- return false;
- }
- }
- else
- {
- terrain->mTerrainAsset = StringTable->EmptyString();
- }
- }
- return true;
- }
- bool TerrainBlock::_setTerrainAsset(void* obj, const char* index, const char* data)
- {
- TerrainBlock* terr = static_cast<TerrainBlock*>(obj);// ->setFile(FileName(data));
- terr->mTerrainAssetId = StringTable->insert(data);
- return terr->setTerrainAsset(terr->mTerrainAssetId);
- }
- void TerrainBlock::_updateBounds()
- {
- if ( !mFile )
- return; // quick fix to stop crashing when deleting terrainblocks
- // Setup our object space bounds.
- mBounds.minExtents.set( 0.0f, 0.0f, 0.0f );
- mBounds.maxExtents.set( getWorldBlockSize(), getWorldBlockSize(), 0.0f );
- getMinMaxHeight( &mBounds.minExtents.z, &mBounds.maxExtents.z );
- // Set our mObjBox to be equal to mBounds
- if ( mObjBox.maxExtents != mBounds.maxExtents ||
- mObjBox.minExtents != mBounds.minExtents )
- {
- mObjBox = mBounds;
- resetWorldBox();
- }
- }
- void TerrainBlock::_onZoningChanged( SceneZoneSpaceManager *zoneManager )
- {
- const SceneManager* sm = getSceneManager();
- if (mCell == NULL || (sm != NULL && sm->getZoneManager() != NULL && zoneManager != sm->getZoneManager()))
- return;
- mZoningDirty = true;
- }
- void TerrainBlock::setHeight( const Point2I &pos, F32 height )
- {
- U16 ht = floatToFixed( height );
- mFile->setHeight( pos.x, pos.y, ht );
- // Note: We do not update the grid here as this could
- // be called several times in a loop. We depend on the
- // caller doing a grid update when he is done.
- }
- F32 TerrainBlock::getHeight( const Point2I &pos )
- {
- U16 ht = mFile->getHeight( pos.x, pos.y );
- return fixedToFloat( ht );
- }
- void TerrainBlock::updateGridMaterials( const Point2I &minPt, const Point2I &maxPt )
- {
- if ( mCell )
- {
- // Tell the terrain cell that something changed.
- const RectI gridRect( minPt, maxPt - minPt );
- mCell->updateGrid( gridRect, true );
- }
- // We mark us as dirty... it will be updated
- // before the next time we render the terrain.
- mLayerTexDirty = true;
- // Signal anyone that cares that the opacity was changed.
- smUpdateSignal.trigger( LayersUpdate, this, minPt, maxPt );
- }
- Point2I TerrainBlock::getGridPos( const Point3F &worldPos ) const
- {
- Point3F terrainPos = worldPos;
- getWorldTransform().mulP( terrainPos );
- F32 squareSize = ( F32 ) getSquareSize();
- F32 halfSquareSize = squareSize / 2.0;
- F32 x = ( terrainPos.x + halfSquareSize ) / squareSize;
- F32 y = ( terrainPos.y + halfSquareSize ) / squareSize;
- Point2I gridPos( ( S32 ) mFloor( x ), ( S32 ) mFloor( y ) );
- return gridPos;
- }
- void TerrainBlock::updateGrid( const Point2I &minPt, const Point2I &maxPt, bool updateClient )
- {
- // On the client we just signal everyone that the height
- // map has changed... the server does the actual changes.
- if ( isClientObject() )
- {
- PROFILE_SCOPE( TerrainBlock_updateGrid_Client );
- // This depends on the client getting this call 'after' the server.
- // Which is currently the case.
- _updateBounds();
- mZoningDirty = true;
- smUpdateSignal.trigger( HeightmapUpdate, this, minPt, maxPt );
- // Tell the terrain cell that the height changed.
- const RectI gridRect( minPt, maxPt - minPt );
- mCell->updateGrid( gridRect );
- // Rebuild the physics representation.
- if ( mPhysicsRep )
- {
- // Delay the update by a few milliseconds so
- // that we're not rebuilding during an active
- // editing operation.
- mPhysicsRep->queueCallback( 500, Delegate<void()>( this, &TerrainBlock::_updatePhysics ) );
- }
- return;
- }
- // Now on the server we rebuild the
- // affected area of the grid map.
- mFile->updateGrid( minPt, maxPt );
- // Fix up the bounds.
- _updateBounds();
- // Rebuild the physics representation.
- if ( mPhysicsRep )
- {
- // Delay the update by a few milliseconds so
- // that we're not rebuilding during an active
- // editing operation.
- mPhysicsRep->queueCallback( 500, Delegate<void()>( this, &TerrainBlock::_updatePhysics ) );
- }
- // Signal again here for any server side observers.
- smUpdateSignal.trigger( HeightmapUpdate, this, minPt, maxPt );
- // If this is a server object and the client update
- // was requested then try to use the local connection
- // pointer to do it.
- if ( updateClient && getClientObject() )
- ((TerrainBlock*)getClientObject())->updateGrid( minPt, maxPt, false );
- }
- bool TerrainBlock::getHeight( const Point2F &pos, F32 *height ) const
- {
- PROFILE_SCOPE( TerrainBlock_getHeight );
- F32 invSquareSize = 1.0f / mSquareSize;
- F32 xp = pos.x * invSquareSize;
- F32 yp = pos.y * invSquareSize;
- S32 x = S32(xp);
- S32 y = S32(yp);
- xp -= (F32)x;
- yp -= (F32)y;
- const U32 blockMask = mFile->mSize - 1;
- if ( x & ~blockMask || y & ~blockMask )
- return false;
-
- x &= blockMask;
- y &= blockMask;
-
- const TerrainSquare *sq = mFile->findSquare( 0, x, y );
- 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 ) );
- if ( sq->flags & TerrainSquare::Split45 )
- {
- if (xp>yp)
- // bottom half
- *height = zBottomLeft + xp * (zBottomRight-zBottomLeft) + yp * (zTopRight-zBottomRight);
- else
- // top half
- *height = zBottomLeft + xp * (zTopRight-zTopLeft) + yp * (zTopLeft-zBottomLeft);
- }
- else
- {
- if (1.0f-xp>yp)
- // bottom half
- *height = zBottomRight + (1.0f-xp) * (zBottomLeft-zBottomRight) + yp * (zTopLeft-zBottomLeft);
- else
- // top half
- *height = zBottomRight + (1.0f-xp) * (zTopLeft-zTopRight) + yp * (zTopRight-zBottomRight);
- }
- return true;
- }
- bool TerrainBlock::getNormal( const Point2F &pos, Point3F *normal, bool normalize, bool skipEmpty ) const
- {
- PROFILE_SCOPE( TerrainBlock_getNormal );
- F32 invSquareSize = 1.0f / mSquareSize;
- F32 xp = pos.x * invSquareSize;
- F32 yp = pos.y * invSquareSize;
- S32 x = S32(xp);
- S32 y = S32(yp);
- xp -= (F32)x;
- yp -= (F32)y;
-
- const U32 blockMask = mFile->mSize - 1;
- if ( x & ~blockMask || y & ~blockMask )
- return false;
-
- x &= blockMask;
- y &= blockMask;
-
- const TerrainSquare *sq = mFile->findSquare( 0, x, y );
- 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 ) );
- if ( sq->flags & TerrainSquare::Split45 )
- {
- if (xp>yp)
- // bottom half
- normal->set(zBottomLeft-zBottomRight, zBottomRight-zTopRight, mSquareSize);
- else
- // top half
- normal->set(zTopLeft-zTopRight, zBottomLeft-zTopLeft, mSquareSize);
- }
- else
- {
- if (1.0f-xp>yp)
- // bottom half
- normal->set(zBottomLeft-zBottomRight, zBottomLeft-zTopLeft, mSquareSize);
- else
- // top half
- normal->set(zTopLeft-zTopRight, zBottomRight-zTopRight, mSquareSize);
- }
- if (normalize)
- normal->normalize();
- return true;
- }
- bool TerrainBlock::getSmoothNormal( const Point2F &pos,
- Point3F *normal,
- bool normalize,
- bool skipEmpty ) const
- {
- PROFILE_SCOPE( TerrainBlock_getSmoothNormal );
- F32 invSquareSize = 1.0f / mSquareSize;
- F32 xp = pos.x * invSquareSize;
- F32 yp = pos.y * invSquareSize;
- S32 x = S32(xp);
- S32 y = S32(yp);
-
- const U32 blockMask = mFile->mSize - 1;
- if ( x & ~blockMask || y & ~blockMask )
- return false;
-
- x &= blockMask;
- y &= blockMask;
-
- const TerrainSquare *sq = mFile->findSquare( 0, x, y );
- 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 ) );
- normal->set( h3 - h1, h4 - h2, mSquareSize * 2.0f );
- if ( normalize )
- normal->normalize();
- return true;
- }
- bool TerrainBlock::getNormalAndHeight( const Point2F &pos, Point3F *normal, F32 *height, bool normalize ) const
- {
- PROFILE_SCOPE( TerrainBlock_getNormalAndHeight );
- F32 invSquareSize = 1.0f / mSquareSize;
- F32 xp = pos.x * invSquareSize;
- F32 yp = pos.y * invSquareSize;
- S32 x = S32(xp);
- S32 y = S32(yp);
- xp -= (F32)x;
- yp -= (F32)y;
- const U32 blockMask = mFile->mSize - 1;
- if ( x & ~blockMask || y & ~blockMask )
- return false;
-
- x &= blockMask;
- y &= blockMask;
-
- const TerrainSquare *sq = mFile->findSquare( 0, x, y );
- 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) );
- if ( sq->flags & TerrainSquare::Split45 )
- {
- if (xp>yp)
- {
- // bottom half
- normal->set(zBottomLeft-zBottomRight, zBottomRight-zTopRight, mSquareSize);
- *height = zBottomLeft + xp * (zBottomRight-zBottomLeft) + yp * (zTopRight-zBottomRight);
- }
- else
- {
- // top half
- normal->set(zTopLeft-zTopRight, zBottomLeft-zTopLeft, mSquareSize);
- *height = zBottomLeft + xp * (zTopRight-zTopLeft) + yp * (zTopLeft-zBottomLeft);
- }
- }
- else
- {
- if (1.0f-xp>yp)
- {
- // bottom half
- normal->set(zBottomLeft-zBottomRight, zBottomLeft-zTopLeft, mSquareSize);
- *height = zBottomRight + (1.0f-xp) * (zBottomLeft-zBottomRight) + yp * (zTopLeft-zBottomLeft);
- }
- else
- {
- // top half
- normal->set(zTopLeft-zTopRight, zBottomRight-zTopRight, mSquareSize);
- *height = zBottomRight + (1.0f-xp) * (zTopLeft-zTopRight) + yp * (zTopRight-zBottomRight);
- }
- }
- if (normalize)
- normal->normalize();
- return true;
- }
- bool TerrainBlock::getNormalHeightMaterial( const Point2F &pos,
- Point3F *normal,
- F32 *height,
- StringTableEntry &matName ) const
- {
- PROFILE_SCOPE( TerrainBlock_getNormalHeightMaterial );
- F32 invSquareSize = 1.0f / mSquareSize;
- F32 xp = pos.x * invSquareSize;
- F32 yp = pos.y * invSquareSize;
- S32 x = S32(xp);
- S32 y = S32(yp);
- S32 xm = S32(mFloor( xp + 0.5f ));
- S32 ym = S32(mFloor( yp + 0.5f ));
- xp -= (F32)x;
- yp -= (F32)y;
- const U32 blockMask = mFile->mSize - 1;
- if ( x & ~blockMask || y & ~blockMask )
- return false;
-
- x &= blockMask;
- y &= blockMask;
-
- const TerrainSquare *sq = mFile->findSquare( 0, x, y );
- 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) );
- matName = mFile->getMaterialName( xm, ym );
- if ( sq->flags & TerrainSquare::Split45 )
- {
- if (xp>yp)
- {
- // bottom half
- normal->set(zBottomLeft-zBottomRight, zBottomRight-zTopRight, mSquareSize);
- *height = zBottomLeft + xp * (zBottomRight-zBottomLeft) + yp * (zTopRight-zBottomRight);
- }
- else
- {
- // top half
- normal->set(zTopLeft-zTopRight, zBottomLeft-zTopLeft, mSquareSize);
- *height = zBottomLeft + xp * (zTopRight-zTopLeft) + yp * (zTopLeft-zBottomLeft);
- }
- }
- else
- {
- if (1.0f-xp>yp)
- {
- // bottom half
- normal->set(zBottomLeft-zBottomRight, zBottomLeft-zTopLeft, mSquareSize);
- *height = zBottomRight + (1.0f-xp) * (zBottomLeft-zBottomRight) + yp * (zTopLeft-zBottomLeft);
- }
- else
- {
- // top half
- normal->set(zTopLeft-zTopRight, zBottomRight-zTopRight, mSquareSize);
- *height = zBottomRight + (1.0f-xp) * (zTopLeft-zTopRight) + yp * (zTopRight-zBottomRight);
- }
- }
- normal->normalize();
- return true;
- }
- U32 TerrainBlock::getMaterialCount() const
- {
- return mFile->mMaterials.size();
- }
- void TerrainBlock::addMaterial( const String &name, U32 insertAt )
- {
- TerrainMaterial *mat = TerrainMaterial::findOrCreate( name );
- StringTableEntry newMatName = StringTable->insert(name.c_str());
- if ( insertAt == -1 )
- {
- //Check to ensure we're not trying to add one that already exists, as that'd be kinda dumb
- for (U32 i = 0; i < mFile->mMaterials.size(); i++)
- {
- if (mFile->mMaterials[i]->getInternalName() == newMatName)
- return;
- }
- mFile->mMaterials.push_back( mat );
- mFile->_initMaterialInstMapping();
- //now we update our asset
- if (mTerrainAsset)
- {
- StringTableEntry terrMatName = StringTable->insert(name.c_str());
- AssetQuery* aq = new AssetQuery();
- U32 foundCount = AssetDatabase.findAssetType(aq, "TerrainMaterialAsset");
- for (U32 i = 0; i < foundCount; i++)
- {
- TerrainMaterialAsset* terrMatAsset = AssetDatabase.acquireAsset<TerrainMaterialAsset>(aq->mAssetList[i]);
- if (terrMatAsset && terrMatAsset->getMaterialDefinitionName() == terrMatName)
- {
- //Do iterative logic to find the next available slot and write to it with our new mat field
- mTerrainAsset->setDataField(StringTable->insert("terrainMaterialAsset"), nullptr, aq->mAssetList[i]);
- }
- }
- }
- }
- else
- {
- // TODO: Insert and reindex!
- }
- mDetailsDirty = true;
- mLayerTexDirty = true;
- }
- void TerrainBlock::removeMaterial( U32 index )
- {
- // Cannot delete if only one layer.
- if ( mFile->mMaterials.size() == 1 )
- return;
- mFile->mMaterials.erase( index );
- mFile->_initMaterialInstMapping();
- for ( S32 i = 0; i < mFile->mLayerMap.size(); i++ )
- {
- if ( mFile->mLayerMap[i] >= index &&
- mFile->mLayerMap[i] != 0 )
- {
- mFile->mLayerMap[i]--;
- }
- }
- mDetailsDirty = true;
- mLayerTexDirty = true;
- }
- void TerrainBlock::updateMaterial( U32 index, const String &name )
- {
- if ( index >= mFile->mMaterials.size() )
- return;
- mFile->mMaterials[ index ] = TerrainMaterial::findOrCreate( name );
- mFile->_initMaterialInstMapping();
- mDetailsDirty = true;
- mLayerTexDirty = true;
- }
- TerrainMaterial* TerrainBlock::getMaterial( U32 index ) const
- {
- if ( index >= mFile->mMaterials.size() )
- return NULL;
- return mFile->mMaterials[ index ];
- }
- void TerrainBlock::deleteAllMaterials()
- {
- mFile->mMaterials.clear();
- mFile->mMaterialInstMapping.clearMatInstList();
- }
- const char* TerrainBlock::getMaterialName( U32 index ) const
- {
- if ( index < mFile->mMaterials.size() )
- return mFile->mMaterials[ index ]->getInternalName();
- return NULL;
- }
- void TerrainBlock::setLightMap( GBitmap *newLightMap )
- {
- SAFE_DELETE( mLightMap );
- mLightMap = newLightMap;
- mLightMapTex = NULL;
- }
- void TerrainBlock::clearLightMap()
- {
- if ( !mLightMap )
- mLightMap = new GBitmap( mLightMapSize, mLightMapSize, 0, GFXFormatR8G8B8 );
- mLightMap->fillWhite();
- mLightMapTex = NULL;
- }
- GFXTextureObject* TerrainBlock::getLightMapTex()
- {
- if ( mLightMapTex.isNull() && mLightMap )
- {
- mLightMapTex.set( mLightMap,
- &GFXStaticTextureProfile,
- false,
- "TerrainBlock::getLightMapTex()" );
- }
- return mLightMapTex;
- }
- void TerrainBlock::onEditorEnable()
- {
- }
- void TerrainBlock::onEditorDisable()
- {
- }
- bool TerrainBlock::onAdd()
- {
- if(!Parent::onAdd())
- return false;
- Resource<TerrainFile> terr;
- if (!mTerrainAsset.isNull())
- {
- terr = mTerrainAsset->getTerrainResource();
- if (terr == NULL)
- {
- if (isClientObject())
- NetConnection::setLastError("Unable to load terrain asset: %s", mTerrainAsset.getAssetId());
- return false;
- }
- setFile(terr);
- }
-
- addToScene();
- _updatePhysics();
- return true;
- }
- String TerrainBlock::_getBaseTexCacheFileName() const
- {
- Torque::Path basePath( mTerrainAsset->getTerrainFilePath() );
- basePath.setFileName( basePath.getFileName() + "_basetex" );
- basePath.setExtension( formatToExtension(mBaseTexFormat) );
- return basePath.getFullPath();
- }
- void TerrainBlock::_rebuildQuadtree()
- {
- SAFE_DELETE( mCell );
- // Recursively build the cells.
- mCell = TerrCell::init( this );
- // Build the shared PrimitiveBuffer.
- mCell->createPrimBuffer( &mPrimBuffer );
- deleteZodiacPrimitiveBuffer();
- }
- void TerrainBlock::_updatePhysics()
- {
- if ( !PHYSICSMGR )
- return;
- SAFE_DELETE( mPhysicsRep );
- PhysicsCollision *colShape;
- // If we can steal the collision shape from the local server
- // object then do so as it saves us alot of cpu time and memory.
- //
- // TODO: We should move this sharing down into TerrFile where
- // it probably belongs.
- //
- if ( getServerObject() )
- {
- TerrainBlock *serverTerrain = (TerrainBlock*)getServerObject();
- colShape = serverTerrain->mPhysicsRep->getColShape();
- }
- else
- {
- // Get empty state of each vert
- bool *holes = new bool[ getBlockSize() * getBlockSize() ];
- for ( U32 row = 0; row < getBlockSize(); row++ )
- for ( U32 column = 0; column < getBlockSize(); column++ )
- holes[ row + (column * getBlockSize()) ] = mFile->isEmptyAt( row, column );
- colShape = PHYSICSMGR->createCollision();
- colShape->addHeightfield( mFile->getHeightMap().address(), holes, getBlockSize(), mSquareSize, MatrixF::Identity );
- delete [] holes;
- }
- PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
- mPhysicsRep = PHYSICSMGR->createBody();
- mPhysicsRep->init( colShape, 0, 0, this, world );
- mPhysicsRep->setTransform( getTransform() );
- }
- void TerrainBlock::onRemove()
- {
- removeFromScene();
- SceneZoneSpaceManager::getZoningChangedSignal().remove( this, &TerrainBlock::_onZoningChanged );
- SAFE_DELETE( mPhysicsRep );
- if ( isClientObject() )
- {
- mBaseTex = NULL;
- mLayerTex = NULL;
- SAFE_DELETE( mBaseMaterial );
- SAFE_DELETE( mDefaultMatInst );
- SAFE_DELETE( mCell );
- mPrimBuffer = NULL;
- mBaseShader = NULL;
- GFXTextureManager::removeEventDelegate( this, &TerrainBlock::_onTextureEvent );
- MATMGR->getFlushSignal().remove( this, &TerrainBlock::_onFlushMaterials );
- }
- Parent::onRemove();
- }
- void TerrainBlock::prepRenderImage( SceneRenderState* state )
- {
- PROFILE_SCOPE(TerrainBlock_prepRenderImage);
-
- // If we need to update our cached
- // zone state then do it now.
- if ( mZoningDirty )
- {
- mZoningDirty = false;
- mCell->updateZoning( getSceneManager()->getZoneManager() );
- }
- _renderBlock( state );
- }
- void TerrainBlock::setTransform(const MatrixF & mat)
- {
- Parent::setTransform( mat );
- // Update world-space OBBs.
- if( mCell )
- {
- mCell->updateOBBs();
- mZoningDirty = true;
- }
- if ( mPhysicsRep )
- mPhysicsRep->setTransform( mat );
- setRenderTransform( mat );
- setMaskBits( TransformMask );
- if(isClientObject())
- smUpdateSignal.trigger( HeightmapUpdate, this, Point2I::Zero, Point2I::Max );
- }
- void TerrainBlock::setScale( const VectorF &scale )
- {
- // We disable scaling... we never scale!
- Parent::setScale( VectorF::One );
- }
- void TerrainBlock::initPersistFields()
- {
- addGroup( "Media" );
- addProtectedField("terrainAsset", TypeTerrainAssetId, Offset(mTerrainAssetId, TerrainBlock),
- &TerrainBlock::_setTerrainAsset, &defaultProtectedGetFn,
- "The source terrain data asset.");
- addProtectedField( "terrainFile", TypeStringFilename, Offset( mTerrFileName, TerrainBlock ),
- &TerrainBlock::_setTerrainFile, &defaultProtectedGetFn,
- "The source terrain data file." );
- endGroup( "Media" );
- addGroup( "Misc" );
- addField( "castShadows", TypeBool, Offset( mCastShadows, TerrainBlock ),
- "Allows the terrain to cast shadows onto itself and other objects.");
-
- addProtectedField( "squareSize", TypeF32, Offset( mSquareSize, TerrainBlock ),
- &TerrainBlock::_setSquareSize, &defaultProtectedGetFn,
- "Indicates the spacing between points on the XY plane on the terrain." );
- addProtectedField( "baseTexSize", TypeS32, Offset( mBaseTexSize, TerrainBlock ),
- &TerrainBlock::_setBaseTexSize, &defaultProtectedGetFn,
- "Size of base texture size per meter." );
- addProtectedField("baseTexFormat", TYPEID<baseTexFormat>(), Offset(mBaseTexFormat, TerrainBlock),
- &TerrainBlock::_setBaseTexFormat, &defaultProtectedGetFn,
- "");
- addProtectedField( "lightMapSize", TypeS32, Offset( mLightMapSize, TerrainBlock ),
- &TerrainBlock::_setLightMapSize, &defaultProtectedGetFn,
- "Light map dimensions in pixels." );
- addField( "screenError", TypeS32, Offset( mScreenError, TerrainBlock ), "Not yet implemented." );
-
- addField( "updateBasetex", TypeBool, Offset( mUpdateBasetex, TerrainBlock ), "Whether or not to update the Base Texture" );
- endGroup( "Misc" );
- addGroup("AFX");
- addField("ignoreZodiacs", TypeBool, Offset(mIgnoreZodiacs, TerrainBlock));
- endGroup("AFX");
- Parent::initPersistFields();
- removeField( "scale" );
- Con::addVariable( "$TerrainBlock::debugRender", TypeBool, &smDebugRender, "Triggers debug rendering of terrain cells\n\n"
- "@ingroup Terrain");
-
- Con::addVariable( "$pref::Terrain::lodScale", TypeF32, &smLODScale, "A global LOD scale used to tweak the default terrain screen error value.\n\n"
- "@ingroup Terrain");
- Con::addVariable( "$pref::Terrain::detailScale", TypeF32, &smDetailScale, "A global detail scale used to tweak the material detail distances.\n\n"
- "@ingroup Terrain");
- }
- void TerrainBlock::inspectPostApply()
- {
- Parent::inspectPostApply();
- setMaskBits( MiscMask );
- }
- U32 TerrainBlock::packUpdate(NetConnection* con, U32 mask, BitStream *stream)
- {
- U32 retMask = Parent::packUpdate( con, mask, stream );
-
- if ( stream->writeFlag( mask & TransformMask ) )
- mathWrite( *stream, getTransform() );
- if ( stream->writeFlag( mask & SizeMask ) )
- stream->write( mSquareSize );
- stream->writeFlag( mCastShadows );
-
- if ( stream->writeFlag( mask & MaterialMask ) )
- {
- stream->write( mBaseTexSize );
- stream->write( mLightMapSize );
- }
- if ( stream->writeFlag( mask & FileMask ) )
- {
- stream->write(mCRC);
- stream->writeString( mTerrainAsset.getAssetId() );
- }
- stream->writeFlag( mask & HeightMapChangeMask );
- if ( stream->writeFlag( mask & MiscMask ) )
- stream->write( mScreenError );
- stream->writeInt(mBaseTexFormat, 32);
-
- stream->writeFlag(mUpdateBasetex);
- stream->writeFlag(mIgnoreZodiacs);
- return retMask;
- }
- void TerrainBlock::unpackUpdate(NetConnection* con, BitStream *stream)
- {
- Parent::unpackUpdate( con, stream );
-
- if ( stream->readFlag() ) // TransformMask
- {
- MatrixF mat;
- mathRead( *stream, &mat );
- setTransform( mat );
- }
- if ( stream->readFlag() ) // SizeMask
- stream->read( &mSquareSize );
- mCastShadows = stream->readFlag();
- bool baseTexSizeChanged = false;
- if ( stream->readFlag() ) // MaterialMask
- {
- U32 baseTexSize;
- stream->read( &baseTexSize );
- if ( mBaseTexSize != baseTexSize )
- {
- mBaseTexSize = baseTexSize;
- baseTexSizeChanged = true;
- }
- U32 lightMapSize;
- stream->read( &lightMapSize );
- if ( mLightMapSize != lightMapSize )
- {
- mLightMapSize = lightMapSize;
- if ( isProperlyAdded() )
- {
- SAFE_DELETE( mLightMap );
- clearLightMap();
- }
- }
- }
- if (stream->readFlag()) // FileMask
- {
- stream->read(&mCRC);
- char buffer[256];
- stream->readString(buffer);
- setTerrainAsset(StringTable->insert(buffer));
- }
- if (baseTexSizeChanged && isProperlyAdded())
- _updateBaseTexture(NONE);
- if ( stream->readFlag() && isProperlyAdded() ) // HeightMapChangeMask
- {
- _updateBounds();
- _rebuildQuadtree();
- _updatePhysics();
- mDetailsDirty = true;
- mLayerTexDirty = true;
- }
- if ( stream->readFlag() ) // MiscMask
- stream->read( &mScreenError );
- mBaseTexFormat = (BaseTexFormat)stream->readInt(32);
-
- mUpdateBasetex = stream->readFlag();
- mIgnoreZodiacs = stream->readFlag();
- }
- void TerrainBlock::getMinMaxHeight( F32 *minHeight, F32 *maxHeight ) const
- {
- // We can get the bound height from the last grid level.
- const TerrainSquare *sq = mFile->findSquare( mFile->mGridLevels, 0, 0 );
- *minHeight = fixedToFloat( sq->minHeight );
- *maxHeight = fixedToFloat( sq->maxHeight );
- }
- void TerrainBlock::getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList)
- {
- if (!mTerrainAsset.isNull())
- usedAssetsList->push_back_unique(mTerrainAsset->getAssetId());
- }
- //-----------------------------------------------------------------------------
- // Console Methods
- //-----------------------------------------------------------------------------
- bool TerrainBlock::renameTerrainMaterial(StringTableEntry oldMatName, StringTableEntry newMatName)
- {
- TerrainMaterial* newMat = TerrainMaterial::findOrCreate(newMatName);
- if (!newMat)
- return false;
- U32 terrainMaterialCount = mFile->mMaterials.size();
- for (U32 i = 0; i < terrainMaterialCount; i++)
- {
- if (mFile->mMaterials[i]->getInternalName() == oldMatName)
- {
- TerrainMaterial* oldMat = mFile->mMaterials[i];
- mFile->mMaterials[i] = newMat;
- }
- }
- return true;
- }
- DefineEngineMethod( TerrainBlock, save, bool, ( const char* fileName),,
- "@brief Saves the terrain block's terrain file to the specified file name.\n\n"
- "@param fileName Name and path of file to save terrain data to.\n\n"
- "@return True if file save was successful, false otherwise")
- {
- char filename[256];
- dStrcpy(filename,fileName,256);
- char *ext = dStrrchr(filename, '.');
- if (!ext || dStricmp(ext, ".ter") != 0)
- dStrcat(filename, ".ter", 256);
- return static_cast<TerrainBlock*>(object)->save(filename);
- }
- DefineEngineMethod(TerrainBlock, saveAsset, bool, (), ,
- "@brief Saves the terrain block's terrain file to the specified file name.\n\n"
- "@param fileName Name and path of file to save terrain data to.\n\n"
- "@return True if file save was successful, false otherwise")
- {
- return static_cast<TerrainBlock*>(object)->saveAsset();
- }
- DefineEngineMethod( TerrainBlock, setMaterialsDirty, void, (),, "")
- {
- static_cast<TerrainBlock*>(object)->setMaterialsDirty();
- }
- //ConsoleMethod(TerrainBlock, save, bool, 3, 3, "(string fileName) - saves the terrain block's terrain file to the specified file name.")
- //{
- // char filename[256];
- // dStrcpy(filename,argv[2],256);
- // char *ext = dStrrchr(filename, '.');
- // if (!ext || dStricmp(ext, ".ter") != 0)
- // dStrcat(filename, ".ter", 256);
- // return static_cast<TerrainBlock*>(object)->save(filename);
- //}
- ConsoleDocFragment _getTerrainHeight1(
- "@brief Gets the terrain height at the specified position\n\n"
- "@param position The world space point, minus the z (height) value. Formatted as (\"x y\")\n\n"
- "@return Returns the terrain height at the given point as an F32 value.\n\n"
- "@ingroup Terrain",
- NULL,
- "bool getTerrainHeight( Point2I position );"
- );
- ConsoleDocFragment _getTerrainHeight2(
- "@brief Gets the terrain height at the specified position\n\n"
- "@param x The X coordinate in world space\n"
- "@param y The Y coordinate in world space\n\n"
- "@return Returns the terrain height at the given point as an F32 value.\n\n"
- "@ingroup Terrain",
- NULL,
- "bool getTerrainHeight( F32 x, F32 y);"
- );
- DefineEngineFunction( getTerrainHeight, F32, (const char* ptOrX, const char* y), (""), "(Point2 pos) - gets the terrain height at the specified position."
- "@param pos The world space point, minus the z (height) value\n Can be formatted as either (\"x y\") or (x,y)\n"
- "@return Returns the terrain height at the given point as an F32 value.\n"
- "@hide")
- {
- F32 height = 0.0f;
- Point2F pos;
- if(!String::isEmpty(ptOrX) && String::isEmpty(y))
- dSscanf(ptOrX, "%f %f", &pos.x, &pos.y);
- else if(!String::isEmpty(ptOrX) && !String::isEmpty(y))
- {
- pos.x = dAtof(ptOrX);
- pos.y = dAtof(y);
- }
- TerrainBlock * terrain = getTerrainUnderWorldPoint(Point3F(pos.x, pos.y, 5000.0f));
- if(terrain && terrain->isServerObject())
- {
- Point3F offset;
- terrain->getTransform().getColumn(3, &offset);
- pos -= Point2F(offset.x, offset.y);
- terrain->getHeight(pos, &height);
- }
- return height;
- }
- ConsoleDocFragment _getTerrainHeightBelowPosition1(
- "@brief Takes a world point and find the \"highest\" terrain underneath it\n\n"
- "@param position The world space point, minus the z (height) value. Formatted as (\"x y\")\n\n"
- "@return Returns the closest terrain height below the given point as an F32 value.\n\n"
- "@ingroup Terrain",
- NULL,
- "bool getTerrainHeightBelowPosition( Point2I position );"
- );
- ConsoleDocFragment _getTerrainHeightBelowPosition2(
- "@brief Takes a world point and find the \"highest\" terrain underneath it\n\n"
- "@param x The X coordinate in world space\n"
- "@param y The Y coordinate in world space\n\n"
- "@return Returns the closest terrain height below the given point as an F32 value.\n\n"
- "@ingroup Terrain",
- NULL,
- "bool getTerrainHeightBelowPosition( F32 x, F32 y);"
- );
- DefineEngineFunction( getTerrainHeightBelowPosition, F32, (const char* ptOrX, const char* y, const char* z), ("", ""),
- "(Point3F pos) - gets the terrain height at the specified position."
- "@param pos The world space point. Can be formatted as either (\"x y z\") or (x,y,z)\n"
- "@note This function is useful if you simply want to grab the terrain height underneath an object.\n"
- "@return Returns the terrain height at the given point as an F32 value.\n"
- "@hide")
- {
- F32 height = 0.0f;
- Point3F pos;
- if(!String::isEmpty(ptOrX) && String::isEmpty(y) && String::isEmpty(z))
- dSscanf(ptOrX, "%f %f %f", &pos.x, &pos.y, &pos.z);
- else if(!String::isEmpty(ptOrX) && !String::isEmpty(y) && !String::isEmpty(z))
- {
- pos.x = dAtof(ptOrX);
- pos.y = dAtof(y);
- pos.z = dAtof(z);
- }
- TerrainBlock * terrain = getTerrainUnderWorldPoint(pos);
-
- Point2F nohghtPos(pos.x, pos.y);
- if(terrain)
- {
- if(terrain->isServerObject())
- {
- Point3F offset;
- terrain->getTransform().getColumn(3, &offset);
- nohghtPos -= Point2F(offset.x, offset.y);
- terrain->getHeight(nohghtPos, &height);
- }
- }
-
- return height;
- }
- const U16* TerrainBlock::getZodiacPrimitiveBuffer()
- {
- if (!zode_primBuffer && !mIgnoreZodiacs)
- TerrCell::createZodiacPrimBuffer(&zode_primBuffer);
- return zode_primBuffer;
- }
- void TerrainBlock::deleteZodiacPrimitiveBuffer()
- {
- if (zode_primBuffer != 0)
- {
- delete [] zode_primBuffer;
- zode_primBuffer = 0;
- }
- }
- DefineEngineMethod(TerrainBlock, getTerrain, String, (), , "Returns the terrain file used for this terrain block, either via the asset or the filename assigned, which ever is valid")
- {
- return object->getTerrain();
- }
- DefineEngineMethod(TerrainBlock, getTerrainAsset, String, (), , "Returns the assetId used for this terrain block")
- {
- return object->getTerrainAssetId();
- }
- DefineEngineMethod(TerrainBlock, setTerrain, bool, (const char* terrain), , "Terrain assignment.first tries asset then flat file.")
- {
- return object->_setTerrain(StringTable->insert(terrain));
- }
- DefineEngineMethod(TerrainBlock, getTerrainMaterialCount, S32, (), , "Gets the number of terrain materials for this block")
- {
- return object->getTerrainMaterialCount();
- }
- DefineEngineMethod(TerrainBlock, getTerrainMaterialName, const char*, (S32 index), , "Gets the number of terrain materials for this block")
- {
- if (index < 0 || index >= object->getTerrainMaterialCount())
- return StringTable->EmptyString();
- return object->getTerrainMaterialName(index);
- }
- DefineEngineMethod(TerrainBlock, renameTerrainMaterial, bool, (const char* oldMaterialName, const char* newMaterialName), , "Updates the terrain material from the original to the new name in the file. Mostly used for import/conversions.")
- {
- return object->renameTerrainMaterial(StringTable->insert(oldMaterialName), StringTable->insert(newMaterialName));
- }
|