/*
**	Command & Conquer Generals Zero Hour(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

////////////////////////////////////////////////////////////////////////////////
//																																						//
//  (c) 2001-2003 Electronic Arts Inc.																				//
//																																						//
////////////////////////////////////////////////////////////////////////////////

// FILE: W3DTerrainVisual.cpp /////////////////////////////////////////////////////////////////////
// W3D implementation details for visual aspects of terrain
// Author: Colin Day, April 2001
///////////////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#include "Common/GameState.h"
#include "Common/GlobalData.h"
#include "Common/PerfTimer.h"
#include "Common/MapReaderWriterInfo.h"
#include "Common/ThingTemplate.h"
#include "Common/WellKnownKeys.h"
#include "Common/TerrainTypes.h"
#include "Common/Xfer.h"
#include "Common/UnitTimings.h" //Contains the DO_UNIT_TIMINGS define jba.		 
#include "Common/QuickTrig.h"

#include "GameClient/Drawable.h"
#include "GameClient/ClientRandomValue.h"

#include "GameLogic/Object.h"
#include "GameLogic/GameLogic.h"

#include "W3DDevice/GameClient/W3DScene.h"
#include "W3DDevice/GameClient/W3DTerrainVisual.h"
#include "W3DDevice/GameClient/WorldHeightMap.h"
#include "W3DDevice/GameClient/W3DWater.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
#include "W3DDevice/GameClient/W3DDebugIcons.h"
#include "W3DDevice/GameClient/W3DTerrainTracks.h"
#include "W3DDevice/GameClient/W3DGranny.h"
#include "W3DDevice/GameClient/W3DShadow.h"
#include "W3DDevice/GameClient/heightmap.h"
#include "W3DDevice/GameClient/FlatHeightmap.h"
#include "W3DDevice/GameClient/W3DSmudge.h"
#include "W3DDevice/GameClient/Module/W3DModelDraw.h"
#include "WW3D2/Light.h"
#include "WW3D2/RendObj.h"
#include "WW3D2/ColType.h"
#include "WW3D2/ColTest.h"
#include "WW3D2/assetmgr.h"



class TestSeismicFilter : public SeismicSimulationFilterBase
{

  virtual SeismicSimStatusCode filterCallback( WorldHeightMapInterfaceClass *heightMap, const SeismicSimulationNode *node )
  {


    Int life = node->m_life;

    if ( heightMap == NULL )
      return SEISMIC_STATUS_INVALID;


    if ( life == 0 )
      return SEISMIC_STATUS_ACTIVE;
    if ( life < 15 )
    {
      // ADD HEIGHT BECAUSE THE EXPLOSION IS PUSHING DIRT UP

      Real magnitude = node->m_magnitude;

      Real offsScalar =  magnitude / (Real)life; // real-life, get it?
      Int radius = node->m_radius;
      Int border = heightMap->getBorderSize();
      Int centerX = node->m_center.x + border ;
      Int centerY = node->m_center.y + border ;
  
      UnsignedInt workspaceWidth = radius*2;
      Real *workspace = NEW( Real[ sqr(workspaceWidth) ] );
      Real *workspaceEnd = workspace + sqr(workspaceWidth);


      for ( Real *t = workspace; t < workspaceEnd; ++t ) *t = 0.0f;// clear the workspace

      for (Int x = 0; x < radius; ++x)
      {
        for (Int y = 0; y < radius; ++y)
        {

          Real distance = sqrt( sqr(x) + sqr(y) );//Pythagoras
      
          if ( distance < radius )
          {
            Real distScalar = cos( ( distance / radius * (PI/2) ) );
            Real height = (offsScalar * distScalar); 

            workspace[ (radius + x) +  workspaceWidth * (radius + y) ] = height + heightMap->getBilinearSampleSeismicZVelocity( centerX + x,  centerY + y ) ;//kaleidoscope
            
            if ( x != 0 ) // non-zero test prevents cross-shaped double stamp 
            {
      			  workspace[ (radius - x) + workspaceWidth * (radius + y) ] = height + heightMap->getBilinearSampleSeismicZVelocity( centerX - x,  centerY + y ) ;
              if ( y != 0 )
                workspace[ (radius - x) + workspaceWidth * (radius - y) ] =  height + heightMap->getBilinearSampleSeismicZVelocity( centerX - x,  centerY - y ) ;
            }
            if ( y != 0 )
      			  workspace[ (radius + x) + workspaceWidth * (radius - y) ] = height + heightMap->getBilinearSampleSeismicZVelocity( centerX + x,  centerY - y ) ;
          }
        }
      }

      // stuff the values from the workspace into the heightmap's velocities
      for (x = 0; x < workspaceWidth; ++x)
        for (Int y = 0; y < workspaceWidth; ++y)
    			heightMap->setSeismicZVelocity( centerX - radius + x, centerY - radius + y,  workspace[  x + workspaceWidth * y ]  );

      delete [] workspace;

      return SEISMIC_STATUS_ACTIVE;
    }
    else
      return SEISMIC_STATUS_ZERO_ENERGY;
  }

  virtual Real applyGravityCallback( Real velocityIn )
  {
    Real velocityOut = velocityIn;
    velocityOut -= 1.5f;
    return velocityOut;
  }


};
static TestSeismicFilter testSeismicFilter;




//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DTerrainVisual::W3DTerrainVisual()
{

	m_terrainRenderObject = NULL;
	m_waterRenderObject = NULL;
	TheWaterRenderObj = NULL;
	
  m_logicHeightMap   = NULL;
  
#ifdef DO_SEISMIC_SIMULATIONS
  m_clientHeightMap = NULL;
#endif











}  // end W3DTerrainVisual

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DTerrainVisual::~W3DTerrainVisual()
{
	// release our render object
	if (TheTerrainRenderObject == m_terrainRenderObject) {
		TheTerrainRenderObject = NULL;
	}

	if (TheTerrainTracksRenderObjClassSystem)
	{
		delete TheTerrainTracksRenderObjClassSystem;
		TheTerrainTracksRenderObjClassSystem=NULL;
	}

#ifdef	INCLUDE_GRANNY_IN_BUILD
	if (TheGrannyRenderObjSystem)
	{
		delete TheGrannyRenderObjSystem;
		TheGrannyRenderObjSystem=NULL;
	}
#endif

	if (TheW3DShadowManager)
	{	
		delete TheW3DShadowManager;
		TheW3DShadowManager=NULL;
	}

	if (TheSmudgeManager)
	{
		delete TheSmudgeManager;
		TheSmudgeManager=NULL;
	}

	REF_PTR_RELEASE( m_waterRenderObject );
	TheWaterRenderObj=NULL;
	REF_PTR_RELEASE( m_terrainRenderObject );
	REF_PTR_RELEASE( m_logicHeightMap );

#ifdef DO_SEISMIC_SIMULATIONS
  REF_PTR_RELEASE( m_clientHeightMap );
#endif
}  // end ~W3DTerrainVisual

//-------------------------------------------------------------------------------------------------
/** init */
//-------------------------------------------------------------------------------------------------
void W3DTerrainVisual::init( void )
{

	// extend
	TerrainVisual::init();
	// create a new render object for W3D
	m_terrainRenderObject = NEW_REF( HeightMapRenderObjClass, () );
	m_terrainRenderObject->Set_Collision_Type( PICK_TYPE_TERRAIN );
	TheTerrainRenderObject = m_terrainRenderObject;

	// initialize track drawing system
	TheTerrainTracksRenderObjClassSystem = NEW TerrainTracksRenderObjClassSystem;
	TheTerrainTracksRenderObjClassSystem->init(W3DDisplay::m_3DScene);

#ifdef	INCLUDE_GRANNY_IN_BUILD
	// initialize Granny model drawing system
	TheGrannyRenderObjSystem = NEW GrannyRenderObjSystem;
#endif

	// initialize object shadow drawing system
	TheW3DShadowManager = NEW W3DShadowManager;
 	TheW3DShadowManager->init();
	
	// create a water plane render object
	TheWaterRenderObj=m_waterRenderObject = NEW_REF( WaterRenderObjClass, () );
	m_waterRenderObject->init(TheGlobalData->m_waterPositionZ, TheGlobalData->m_waterExtentX, TheGlobalData->m_waterExtentY, W3DDisplay::m_3DScene, (WaterRenderObjClass::WaterType)TheGlobalData->m_waterType);	//create a water plane that's 128x128 units
	m_waterRenderObject->Set_Position(Vector3(TheGlobalData->m_waterPositionX,TheGlobalData->m_waterPositionY,TheGlobalData->m_waterPositionZ));	//place water in world

	// create smudge rendering system.
	TheSmudgeManager = NEW(W3DSmudgeManager);
	TheSmudgeManager->init();

#ifdef DO_UNIT_TIMINGS
#pragma MESSAGE("********************* WARNING- Doing UNIT TIMINGS. ")
#else 
		if (TheGlobalData->m_waterType == WaterRenderObjClass::WATER_TYPE_1_FB_REFLECTION)
		{	// add water render object to the pre-pass scene (to be rendered before main scene)
 			//W3DDisplay::m_prePass3DScene->Add_Render_Object( m_waterRenderObject);
		}
		else
		{	// add water render object to the post-pass scene (to be rendered after main scene)
			W3DDisplay::m_3DScene->Add_Render_Object( m_waterRenderObject);
		}
#endif
	if (TheGlobalData->m_useCloudPlane)
		m_waterRenderObject->toggleCloudLayer(true);
	else
		m_waterRenderObject->toggleCloudLayer(false);

	// set the vertex animated water properties
	Int waterSettingIndex = 0;  // use index 0 settings by default
	TheTerrainVisual->setWaterGridHeightClamps( NULL, 
																							TheGlobalData->m_vertexWaterHeightClampLow[ waterSettingIndex ], 
																							TheGlobalData->m_vertexWaterHeightClampHi[ waterSettingIndex ] );
	TheTerrainVisual->setWaterTransform( NULL, 
																			 TheGlobalData->m_vertexWaterAngle[ waterSettingIndex ], 
																			 TheGlobalData->m_vertexWaterXPosition[ waterSettingIndex ], 
																			 TheGlobalData->m_vertexWaterYPosition[ waterSettingIndex ], 
																			 TheGlobalData->m_vertexWaterZPosition[ waterSettingIndex ] );
	TheTerrainVisual->setWaterGridResolution( NULL, 
																						TheGlobalData->m_vertexWaterXGridCells[ waterSettingIndex ], 
																						TheGlobalData->m_vertexWaterYGridCells[ waterSettingIndex ], 
																						TheGlobalData->m_vertexWaterGridSize[ waterSettingIndex ] );
	TheTerrainVisual->setWaterAttenuationFactors( NULL, 
																								TheGlobalData->m_vertexWaterAttenuationA[ waterSettingIndex ], 
																								TheGlobalData->m_vertexWaterAttenuationB[ waterSettingIndex ], 
																								TheGlobalData->m_vertexWaterAttenuationC[ waterSettingIndex ], 
																								TheGlobalData->m_vertexWaterAttenuationRange[ waterSettingIndex ] );	
	m_isWaterGridRenderingEnabled = FALSE;

#ifdef DO_SEISMIC_SIMULATIONS
  m_seismicSimulationList.clear();
#endif

}  // end init

//-------------------------------------------------------------------------------------------------
/** reset */
//-------------------------------------------------------------------------------------------------
void W3DTerrainVisual::reset( void )
{

	// extend
	TerrainVisual::reset();

	m_terrainRenderObject->reset();

	if (TheW3DShadowManager)
		TheW3DShadowManager->Reset();

	if (TheSmudgeManager)
		TheSmudgeManager->reset();

	if (TheTerrainTracksRenderObjClassSystem)
		TheTerrainTracksRenderObjClassSystem->Reset();

	// reset water render object if present
	if( m_waterRenderObject )
	{
		for (Int i=0; i<5; i++)
		{
			//check if this texture was ever changed from default
			if (m_currentSkyboxTexNames[i] != m_initialSkyboxTexNames[i])
			{
				m_waterRenderObject->replaceSkyboxTexture(m_currentSkyboxTexNames[i], m_initialSkyboxTexNames[i]);
				m_currentSkyboxTexNames[i]=m_initialSkyboxTexNames[i];	//update current state to new texture
			}
		}

		m_waterRenderObject->reset();
	}

#ifdef DO_SEISMIC_SIMULATIONS
  m_seismicSimulationList.clear();
#endif
  
}  // end reset

//-------------------------------------------------------------------------------------------------
/** update */
//-------------------------------------------------------------------------------------------------
void W3DTerrainVisual::update( void )
{

	// extend
	TerrainVisual::update();

#ifdef DO_SEISMIC_SIMULATIONS
  handleSeismicSimulations();
#endif
	// if we have a water render object, it has an update method
	if( m_waterRenderObject )
		m_waterRenderObject->update();


}  // end update


#ifdef DO_SEISMIC_SIMULATIONS

void W3DTerrainVisual::addSeismicSimulation( const SeismicSimulationNode& sim )
{
    // HERE WOULD BE A GREAT PLACE FOR AN IDIOT TEST:
  // REJECT SIMULATION NODES THAT ARE OFF SCREEN!!!!!!!!!!
    // HERE WOULD BE A GREAT PLACE FOR AN IDIOT TEST:
  // REJECT SIMULATION NODES THAT ARE OFF SCREEN!!!!!!!!!!


  m_seismicSimulationList.push_back( sim );
}



void W3DTerrainVisual::handleSeismicSimulations( void )
{
  if ( ! m_clientHeightMap || ! m_logicHeightMap || ! m_terrainRenderObject )
    return;


  if ( ! m_seismicSimulationList.empty() )
  {
    SeismicSimulationListIt it = m_seismicSimulationList.begin();
    
    
    m_clientHeightMap->clearSeismicUpdateFlags();


    while ( it != m_seismicSimulationList.end() )
	  {
      SeismicSimulationNode *ssn = &*it;

      if ( ssn )
      {
        SeismicSimulationFilterBase::SeismicSimStatusCode code = ssn->handleFilterCallback( m_clientHeightMap );
        DEBUG_ASSERTCRASH( code != SeismicSimulationFilterBase::SEISMIC_STATUS_INVALID, ("Trouble in the Seismic simulator.") );
      
        switch ( code ) 
        {
          case SeismicSimulationFilterBase::SEISMIC_STATUS_ACTIVE:
          {
        	  break;
          }
          case SeismicSimulationFilterBase::SEISMIC_STATUS_ZERO_ENERGY:
          {
        	  break;
          }
        }

        Int border = m_clientHeightMap->getBorderSizeInline();

        // Now we apply some gravity to the dirt, so it falls back to its "original" height
        UnsignedInt fallCount = 0;
        for (Int x = border+ssn->m_region.lo.x; x < border+ssn->m_region.hi.x; ++x)
        {
          for (Int y = border+ssn->m_region.lo.y; y < border+ssn->m_region.hi.y; ++y)
          {
            if ( ! m_clientHeightMap->getSeismicUpdateFlag( x, y ) )
            {
              UnsignedByte heightOfOriginal = m_logicHeightMap->getHeight( x, y ); // LOGIC, YES DEFINITELY THE LOGIC

              Real oldSpeed = m_clientHeightMap->getSeismicZVelocity( x, y );
              Real newSpeed = ssn->applyGravity( oldSpeed );// - 0.5f;
              
              m_clientHeightMap->setSeismicZVelocity( x, y, newSpeed );

              Int heightToUse = m_clientHeightMap->getHeight( x, y ) + newSpeed ;


              if (heightToUse <= heightOfOriginal)
              {
                heightToUse = heightOfOriginal;
                m_clientHeightMap->setSeismicZVelocity( x, y, 0.0f ); //poof! the dirt hit ground level so stop "falling"
              }
              else
              {
                ++fallCount;
                
                if ( heightToUse > 255 )
                  heightToUse = 255;

              }
    			    m_clientHeightMap->setRawHeight( x, y, heightToUse ); 
              m_clientHeightMap->setSeismicUpdateFlag( x, y, TRUE );
            }
            
          }
        }

        if ( fallCount == 0 )
          ssn->m_clean = TRUE;

      }

      ++it;

	  }
  }





}

  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void W3DTerrainVisual::updateSeismicSimulations( void )
{

	if (m_logicHeightMap==NULL) 
		return;

	if (m_clientHeightMap==NULL) 
		return;

  if (m_terrainRenderObject==NULL)
    return;

  if ( ! m_seismicSimulationList.empty() )
  {
    SeismicSimulationListIt it = m_seismicSimulationList.begin();

    // First we run through the list and do our business for each region

	  while ( it != m_seismicSimulationList.end() )
	  {
      SeismicSimulationNode *hur = &*it;
      if ( hur )
      {
        Int border = m_clientHeightMap->getBorderSizeInline();

		    TheTerrainRenderObject->updateBlock(
          hur->m_region.lo.x + border, 
          hur->m_region.lo.y + border, 
          hur->m_region.hi.x + border, 
          hur->m_region.hi.y + border, 
          m_clientHeightMap, 
          0);
      }

      ++it;

	  }
    // Then we check to see if these need to get erased from the list
    it = m_seismicSimulationList.begin();
	  while ( it != m_seismicSimulationList.end() )
	  {
      SeismicSimulationNode *hur = &*it;
      if ( hur->m_clean )
      {
		    it = m_seismicSimulationList.erase( it );
      }
      else
        ++it; 

	  }

  }
}


#endif //#defined DO_SEISMIC_SIMULATIONS
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



//-------------------------------------------------------------------------------------------------
/** load method for W3D visual terrain */
//-------------------------------------------------------------------------------------------------
Bool W3DTerrainVisual::load( AsciiString filename )
{
	
#if 0	
	// (gth) Testing exclusion list asset releasing
	DynamicVectorClass<StringClass> exclusion_list(8000);
	
	WW3DAssetManager::Get_Instance()->Create_Asset_List(exclusion_list);

	exclusion_list.Add(StringClass("avcomanche"));
	exclusion_list.Add(StringClass("avcomanche_d"));
	exclusion_list.Add(StringClass("ptdogwood08"));
	exclusion_list.Add(StringClass("ptdogwood01_b"));
	exclusion_list.Add(StringClass("ptpalm01"));
	exclusion_list.Add(StringClass("ptpalm01_b"));
	exclusion_list.Add(StringClass("avhummer"));
	exclusion_list.Add(StringClass("avhummer_d"));
	exclusion_list.Add(StringClass("avleopard"));
	exclusion_list.Add(StringClass("avleopard_d"));

	WW3DAssetManager::Get_Instance()->Free_Assets_With_Exclusion_List(exclusion_list);
#endif

	// enhancing functionality specific for W3D terrain
	if( TerrainVisual::load( filename ) == FALSE )
		return FALSE;  // failed

	// open the terrain file
	CachedFileInputStream fileStrm;
	if( !fileStrm.open(filename) )
	{

		REF_PTR_RELEASE( m_terrainRenderObject );
		return FALSE;

	}  // end if

	if( m_terrainRenderObject == NULL )
		return FALSE;


  ChunkInputStream *pStrm = &fileStrm;

  // allocate new height map data to read from file
  REF_PTR_RELEASE( m_logicHeightMap );
	m_logicHeightMap = NEW WorldHeightMap(pStrm);




#ifdef DO_SEISMIC_SIMULATIONS

  fileStrm.close();
  fileStrm.open(filename);
  pStrm = &fileStrm;

	REF_PTR_RELEASE( m_clientHeightMap );
  m_clientHeightMap = NEW WorldHeightMap( pStrm );

#endif








	// Add any lights loaded by map.
	MapObject *pMapObj = MapObject::getFirstMapObject();
	while (pMapObj) 
	{
		Dict *d = pMapObj->getProperties();
		if (pMapObj->isLight()) 
		{ 
			Coord3D loc = *pMapObj->getLocation();
			if (loc.z < 0) {
				Vector3 vec;
				loc.z = m_terrainRenderObject->getHeightMapHeight(loc.x, loc.y, NULL);
				loc.z += d->getReal(TheKey_lightHeightAboveTerrain);
			}
			// It is a light, and handled at the device level.  jba.
			LightClass* lightP = NEW_REF(LightClass, (LightClass::POINT));

			RGBColor c;
			c.setFromInt(d->getInt(TheKey_lightAmbientColor));
			lightP->Set_Ambient( Vector3( c.red, c.green, c.blue ) );

			c.setFromInt(d->getInt(TheKey_lightDiffuseColor));
			lightP->Set_Diffuse( Vector3(  c.red, c.green, c.blue) );

			lightP->Set_Position(Vector3(loc.x, loc.y, loc.z));

			lightP->Set_Far_Attenuation_Range(d->getReal(TheKey_lightInnerRadius), d->getReal(TheKey_lightOuterRadius));
 			W3DDisplay::m_3DScene->Add_Render_Object(lightP);
			REF_PTR_RELEASE( lightP );
		}
		pMapObj = pMapObj->getNext();
	}


	RefRenderObjListIterator *it = W3DDisplay::m_3DScene->createLightsIterator();
	// apply the heightmap to the terrain render object

#ifdef DO_SEISMIC_SIMULATIONS
	m_terrainRenderObject->initHeightData( m_clientHeightMap->getDrawWidth(), 
																				 m_clientHeightMap->getDrawHeight(),
																				 m_clientHeightMap,
																				 it);
#else
	m_terrainRenderObject->initHeightData( m_logicHeightMap->getDrawWidth(), 
																				 m_logicHeightMap->getDrawHeight(),
																				 m_logicHeightMap,
																				 it);
#endif


	if (it) {
	 W3DDisplay::m_3DScene->destroyLightsIterator(it);
	 it = NULL;
	}
	// add our terrain render object to the scene
	W3DDisplay::m_3DScene->Add_Render_Object( m_terrainRenderObject );

#if defined _DEBUG || defined _INTERNAL
	// Icon drawing utility object for pathfinding.
	W3DDebugIcons *icons = NEW W3DDebugIcons;
 	W3DDisplay::m_3DScene->Add_Render_Object( icons );
	icons->Release_Ref(); // belongs to scene.
#endif

#ifdef DO_UNIT_TIMINGS
#pragma MESSAGE("********************* WARNING- Doing UNIT TIMINGS. ")
#else 
	if (m_waterRenderObject)
	{
		W3DDisplay::m_3DScene->Add_Render_Object( m_waterRenderObject);
		m_waterRenderObject->enableWaterGrid(false);
		m_waterRenderObject->updateMapOverrides();
	}
#endif

	pMapObj = MapObject::getFirstMapObject();
	while (pMapObj) 
	{
		Dict *d = pMapObj->getProperties();
		if (pMapObj->isScorch()) {
			const Coord3D *pos = pMapObj->getLocation();
			Vector3 loc(pos->x, pos->y, pos->z);
			Real radius = d->getReal(TheKey_objectRadius);
			Scorches type = (Scorches)d->getInt(TheKey_scorchType);
			m_terrainRenderObject->addScorch(loc, radius, type);
		}
		pMapObj = pMapObj->getNext();
	}

	// reset water render object if present
	if( m_waterRenderObject )
	{
		m_waterRenderObject->load();
	}

	return TRUE;  // success

}  // end load

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::enableWaterGrid( Bool enable )
{

	//Get default water type
	m_isWaterGridRenderingEnabled = enable;

	// make the changes in the water render object
	if( m_waterRenderObject )
		m_waterRenderObject->enableWaterGrid( enable );

}  // end enableWaterGrid

//-------------------------------------------------------------------------------------------------
/** intersect the ray with the terrain, if a hit occurs TRUE is returned
	* and the result point on the terrain is returned in "result" */
//-------------------------------------------------------------------------------------------------
Bool W3DTerrainVisual::intersectTerrain( Coord3D *rayStart, 
																				 Coord3D *rayEnd, 
																				 Coord3D *result )
{
	Bool hit = FALSE;

	// sanity
	if( rayStart == NULL || rayEnd == NULL )
		return hit;

	if( m_terrainRenderObject )
	{
		CastResultStruct res;
		LineSegClass lineSeg( Vector3( rayStart->x, rayStart->y, rayStart->z ),
													Vector3( rayEnd->x, rayEnd->y, rayEnd->z ) );
		RayCollisionTestClass rayTest( lineSeg, &res );

		hit = m_terrainRenderObject->Cast_Ray( rayTest );
		if( hit && result )
		{
			Vector3 point = rayTest.Result->ContactPoint;

			result->x = point.X;
			result->y = point.Y;
			result->z = point.Z;	

		}  // end if
		
	}  // end if

	// return hit result
	return hit;

}  // end intersectTerrain

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DTerrainVisual::getTerrainColorAt( Real x, Real y, RGBColor *pColor )
{

#ifdef DO_SEISMIC_SIMULATIONS
  if( m_clientHeightMap )
		m_clientHeightMap->getTerrainColorAt( x, y, pColor );
#else
  if( m_logicHeightMap )
		m_logicHeightMap->getTerrainColorAt( x, y, pColor );
#endif

}  // end getTerrainColorAt

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
TerrainType *W3DTerrainVisual::getTerrainTile( Real x, Real y )
{
	TerrainType *tile = NULL;


#ifdef DO_SEISMIC_SIMULATIONS
	if( m_clientHeightMap )
	{
		AsciiString tileName = m_clientHeightMap->getTerrainNameAt( x, y );
		tile = TheTerrainTypes->findTerrain( tileName );
	}  // end if
#else
  if( m_logicHeightMap )
	{
		AsciiString tileName = m_logicHeightMap->getTerrainNameAt( x, y );
		tile = TheTerrainTypes->findTerrain( tileName );
	}  // end if
#endif




	return tile;

}  // end getTerrainTile

// ------------------------------------------------------------------------------------------------
/** set min/max height values allowed in water grid pointed to by waterTable */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::setWaterGridHeightClamps( const WaterHandle *waterTable, 
																								 Real minZ, Real maxZ )
{

	if( m_waterRenderObject )
		m_waterRenderObject->setGridHeightClamps( minZ, maxZ );

}  // end setWaterGridHeightClamps

// ------------------------------------------------------------------------------------------------
/** adjust fallof parameters for grid change method */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::setWaterAttenuationFactors( const WaterHandle *waterTable, 
																									 Real a, Real b, Real c, Real range )
{

	if( m_waterRenderObject )
		m_waterRenderObject->setGridChangeAttenuationFactors( a, b, c, range );

}  // end setWaterAttenuationFactors

// ------------------------------------------------------------------------------------------------
/** set the water table position and orientation in world space */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::setWaterTransform( const WaterHandle *waterTable, 
																					Real angle, Real x, Real y, Real z )
{

	if( m_waterRenderObject )
		m_waterRenderObject->setGridTransform( angle, x, y, z );

}  // end setWaterTransform

// ------------------------------------------------------------------------------------------------
/** set water table transform by matrix */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::setWaterTransform( const Matrix3D *transform )
{

	if( m_waterRenderObject )
		m_waterRenderObject->setGridTransform( transform );
			
}  // end setWaterTransform

// ------------------------------------------------------------------------------------------------
/** get the water transform matrix */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::getWaterTransform( const WaterHandle *waterTable, Matrix3D *transform )
{

	if( m_waterRenderObject )
		m_waterRenderObject->getGridTransform( transform );

}  // end getWaterTransform

// ------------------------------------------------------------------------------------------------
/** water grid resolution spacing */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::setWaterGridResolution( const WaterHandle *waterTable,
																							 Real gridCellsX, Real gridCellsY, Real cellSize )
{

	if( m_waterRenderObject )
		m_waterRenderObject->setGridResolution( gridCellsX, gridCellsY, cellSize );

}  // end setWaterGridResolution

// ------------------------------------------------------------------------------------------------
/** get water grid resolution spacing */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::getWaterGridResolution( const WaterHandle *waterTable,	
																							 Real *gridCellsX, Real *gridCellsY, Real *cellSize )
{

	if( m_waterRenderObject )
		m_waterRenderObject->getGridResolution( gridCellsX, gridCellsY, cellSize );

}  // end getWaterGridResolution

// ------------------------------------------------------------------------------------------------
/** adjust the water grid in world coords by the delta */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::changeWaterHeight( Real x, Real y, Real delta )
{

	if( m_waterRenderObject )
		m_waterRenderObject->changeGridHeight( x, y, delta );

}  // end changeWaterHeight

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::addWaterVelocity( Real worldX, Real worldY, 
																				 Real velocity, Real preferredHeight )
{

	if( m_waterRenderObject )
		m_waterRenderObject->addVelocity( worldX, worldY, velocity, preferredHeight );

}  // end addWaterVelocity

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
Bool W3DTerrainVisual::getWaterGridHeight( Real worldX, Real worldY, Real *height)
{
	Real gridX, gridY;

	if (m_isWaterGridRenderingEnabled && m_waterRenderObject &&
		m_waterRenderObject->worldToGridSpace(worldX, worldY, gridX, gridY))
	{	//point falls within grid, return correct height
		m_waterRenderObject->getGridVertexHeight(REAL_TO_INT(gridX),REAL_TO_INT(gridY),height);
		return TRUE;
	}
	return FALSE;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::setRawMapHeight(const ICoord2D *gridPos, Int height)
{
  // This method writes to the m_logicHeightMap member, 
  // since m_logicHeightMap is the true, golden standard to which m_clientHeightMap
  // interpolates during or after its Seismic simulation displaces it..
  // THIS IS TRUE ONLY WHEN DO_SEISMIC_SIMULATIONS is defined M Lorenzen, 8/23/03

	if (m_logicHeightMap) 
  {
		Int x = gridPos->x+m_logicHeightMap->getBorderSizeInline();
		Int y = gridPos->y+m_logicHeightMap->getBorderSizeInline();
 		//if (m_logicHeightMap->getHeight(x,y) != height) //ML changed to prevent scissoring with roads
 		if (m_logicHeightMap->getHeight(x,y) > height) 
		{
			m_logicHeightMap->setRawHeight(x, y, height);
			m_terrainRenderObject->staticLightingChanged(); // OOH! this could benefit from the new Seismic update code


#ifdef DO_SEISMIC_SIMULATIONS 
      if ( m_clientHeightMap )
      {
        if ( height < m_clientHeightMap->getHeight( x,y ) )
          m_clientHeightMap->setRawHeight( x, y, height ); // if the client map is heigher than this height, it will fall down to it anyway!
      }
#endif

		}
	}
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
Int W3DTerrainVisual::getRawMapHeight(const ICoord2D *gridPos)
{
	if (m_logicHeightMap) 
  {
		Int x = gridPos->x+m_logicHeightMap->getBorderSizeInline();
		Int y = gridPos->y+m_logicHeightMap->getBorderSizeInline();
 		//if (m_logicHeightMap->getHeight(x,y) != height) //ML changed to prevent scissoring with roads
    return m_logicHeightMap->getHeight(x,y) ;
	}
  return 0;

}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::addFactionBibDrawable(Drawable *factionBuilding, Bool highlight, Real extra)
{
#ifdef DO_SEISMIC_SIMULATIONS 
	if (m_clientHeightMap)
#else
	if (m_logicHeightMap)
#endif
  {
		const Matrix3D * mtx = factionBuilding->getTransformMatrix();
		Vector3 corners[4];
		Coord3D pos;
		pos.set(0,0,0);
		Real exitWidth = factionBuilding->getTemplate()->getFactoryExitWidth();
		Real extraWidth = factionBuilding->getTemplate()->getFactoryExtraBibWidth() + extra;
		const GeometryInfo info = factionBuilding->getTemplate()->getTemplateGeometryInfo();
		Real sizeX = info.getMajorRadius();
		Real sizeY = info.getMinorRadius();
		if (info.getGeomType() != GEOMETRY_BOX) {
			sizeY = sizeX;
		}
		corners[0].Set(pos.x, pos.y, pos.z);
		corners[0].X -= sizeX+extraWidth;
		corners[0].Y -= sizeY+extraWidth;
		corners[1].Set(pos.x, pos.y, pos.z);
		corners[1].X += sizeX+exitWidth+extraWidth;
		corners[1].Y -= sizeY+extraWidth;
		corners[2].Set(pos.x, pos.y, pos.z);
		corners[2].X += sizeX+exitWidth+extraWidth;
		corners[2].Y += sizeY+extraWidth;
		corners[3].Set(pos.x, pos.y, pos.z);
		corners[3].X -= sizeX+extraWidth;
		corners[3].Y += sizeY+extraWidth;
		mtx->Transform_Vector(*mtx, corners[0], &corners[0]);
		mtx->Transform_Vector(*mtx, corners[1], &corners[1]);
		mtx->Transform_Vector(*mtx, corners[2], &corners[2]);
		mtx->Transform_Vector(*mtx, corners[3], &corners[3]);
		m_terrainRenderObject->addTerrainBibDrawable(corners, factionBuilding->getID(), highlight);
	}
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::addFactionBib(Object *factionBuilding, Bool highlight, Real extra)
{
#ifdef DO_SEISMIC_SIMULATIONS
	if (m_clientHeightMap) 
#else
	if (m_logicHeightMap) 
#endif
  {
		const Matrix3D * mtx = factionBuilding->getTransformMatrix();
		Vector3 corners[4];
		Coord3D pos;
		pos.set(0,0,0);
		Real exitWidth = factionBuilding->getTemplate()->getFactoryExitWidth();
		Real extraWidth = factionBuilding->getTemplate()->getFactoryExtraBibWidth() + extra;
		const GeometryInfo info = factionBuilding->getGeometryInfo();
		Real sizeX = info.getMajorRadius();
		Real sizeY = info.getMinorRadius();
		if (info.getGeomType() != GEOMETRY_BOX) {
			sizeY = sizeX;
		}
		corners[0].Set(pos.x, pos.y, pos.z);
		corners[0].X -= sizeX+extraWidth;
		corners[0].Y -= sizeY+extraWidth;
		corners[1].Set(pos.x, pos.y, pos.z);
		corners[1].X += sizeX+exitWidth+extraWidth;
		corners[1].Y -= sizeY+extraWidth;
		corners[2].Set(pos.x, pos.y, pos.z);
		corners[2].X += sizeX+exitWidth+extraWidth;
		corners[2].Y += sizeY+extraWidth;
		corners[3].Set(pos.x, pos.y, pos.z);
		corners[3].X -= sizeX+extraWidth;
		corners[3].Y += sizeY+extraWidth;
		mtx->Transform_Vector(*mtx, corners[0], &corners[0]);
		mtx->Transform_Vector(*mtx, corners[1], &corners[1]);
		mtx->Transform_Vector(*mtx, corners[2], &corners[2]);
		mtx->Transform_Vector(*mtx, corners[3], &corners[3]);
		m_terrainRenderObject->addTerrainBib(corners, factionBuilding->getID(), highlight);
	}
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::removeFactionBibDrawable(Drawable *factionBuilding)
{
	if (m_terrainRenderObject) {
		m_terrainRenderObject->removeTerrainBibDrawable(factionBuilding->getID());
	}
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::removeFactionBib(Object *factionBuilding)
{
	if (m_terrainRenderObject) {
		m_terrainRenderObject->removeTerrainBib(factionBuilding->getID());
	}
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::removeAllBibs(void)
{
	if (m_terrainRenderObject) {
		m_terrainRenderObject->removeAllTerrainBibs();
	}
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::removeBibHighlighting(void)
{
	if (m_terrainRenderObject) {
		m_terrainRenderObject->removeTerrainBibHighlighting();
	}
}
 
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::removeTreesAndPropsForConstruction(const Coord3D* pos, 
																				const GeometryInfo& geom,
																				Real angle)
{
	if (m_terrainRenderObject) {
		m_terrainRenderObject->removeTreesAndPropsForConstruction(pos, geom, angle);
	}
}
 
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::addProp(const ThingTemplate *tTemplate, const Coord3D *pos, Real angle)
{
	ModelConditionFlags state;
	state.clear();
	if (TheGlobalData->m_weather == WEATHER_SNOWY)
	{
		state.set(MODELCONDITION_SNOW);
	}
	if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT)
	{
		state.set(MODELCONDITION_NIGHT);
	}
	AsciiString modelName;
	Real scale = tTemplate->getAssetScale();
	const ModuleInfo& mi = tTemplate->getDrawModuleInfo();
	if (mi.getCount() > 0)
	{
		const ModuleData* mdd = mi.getNthData(0);
		const W3DModelDrawModuleData* md = mdd ? mdd->getAsW3DModelDrawModuleData() : NULL;
		if (md)
		{
			modelName = md->getBestModelNameForWB(state);
		}
	}
	if (m_terrainRenderObject && modelName.isNotEmpty()) {
		m_terrainRenderObject->addProp(1, *pos, angle, scale, modelName);
	}
}
 
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::setTerrainTracksDetail(void)
{
	if (TheTerrainTracksRenderObjClassSystem)
		TheTerrainTracksRenderObjClassSystem->setDetail();
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::setShoreLineDetail(void)
{
	if (m_terrainRenderObject) 
		m_terrainRenderObject->setShoreLineDetail();
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
/// Replace the skybox texture
void W3DTerrainVisual::replaceSkyboxTextures(const AsciiString *oldTexName[5], const AsciiString *newTexName[5])
{
	if (m_waterRenderObject)
	{
		for (Int i=0; i<5; i++)
		{
			//check if this texture was never changed before and is still using the default art.
			if (m_initialSkyboxTexNames[i].isEmpty())
			{	m_initialSkyboxTexNames[i]=*oldTexName[i];
				m_currentSkyboxTexNames[i]=*oldTexName[i];
			}

			if (m_currentSkyboxTexNames[i] != *newTexName[i])
			{	m_waterRenderObject->replaceSkyboxTexture(m_currentSkyboxTexNames[i], *newTexName[i]);
				m_currentSkyboxTexNames[i]=*newTexName[i];	//update current state to new texture
			}
		}
	}
}

// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::crc( Xfer *xfer )
{

	// extend base class
	TerrainVisual::crc( xfer );

}  // end CRC

// ------------------------------------------------------------------------------------------------
/** Xfer
	* Version Info:
	* 1: Initial version 
	* 2: Add height map heights.
	* 3: Add client side trees & props. jba.
*/
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::xfer( Xfer *xfer )
{

	// version
	XferVersion currentVersion = 3;
	XferVersion version = currentVersion;
	xfer->xferVersion( &version, currentVersion );

	// extend base class
	TerrainVisual::xfer( xfer );

	// flag for whether or not the water grid is enabled
	Bool gridEnabled = m_isWaterGridRenderingEnabled;
	xfer->xferBool( &gridEnabled );
	if( gridEnabled != m_isWaterGridRenderingEnabled )
	{

		DEBUG_CRASH(( "W3DTerrainVisual::xfer - m_isWaterGridRenderingEnabled mismatch\n" ));
		throw SC_INVALID_DATA;

	}  // end if

	// xfer grid data if enabled
	if( gridEnabled )
		xfer->xferSnapshot( m_waterRenderObject );

/*
	{

		// grid width and height
		Int width = getGridWidth();
		Int height = getGridheight();
		xfer->xferInt( &width );
		xfer->xferInt( &height );
		if( width != getGridWidth() )
		{

			DEBUG_CRASH(( "W3DTerainVisual::xfer - grid width mismatch '%d' should be '%d'\n",
										width, getGridWidth() ));
			throw SC_INVALID_DATA;

		}  // end if
		if( height != getGridHeight() )
		{

			DEBUG_CRASH(( "W3DTerainVisual::xfer - grid height mismatch '%d' should be '%d'\n",
										height, getGridHeight() ));
			throw SC_INVALID_DATA;

		}  // end if

		// write data for each grid

	}  // end if
*/

	// Write out the terrain height data.
	if (version >= 2) {
		UnsignedByte *data = m_logicHeightMap->getDataPtr();
		Int len = m_logicHeightMap->getXExtent()*m_logicHeightMap->getYExtent();
		Int xferLen = len;
		xfer->xferInt(&xferLen);
		if (len!=xferLen) {
			DEBUG_CRASH(("Bad height map length."));
			if (len>xferLen) {
				len = xferLen;
			}
		}
		xfer->xferUser(data, len);	
		if (xfer->getXferMode() == XFER_LOAD)	
    {	
			// Update the display height map.
			m_terrainRenderObject->staticLightingChanged();
		}
	}

	if (version >= 3) {
		xfer->xferSnapshot(m_terrainRenderObject);
	}



         // XFER //
         // X  R //
         // X  R //
         // XFER //
 /////   /USE CLIENT HEIGHT MAPCLIENT HEIGHT MAP
//   //  /USE CLIENT HEIGHT MAPCLIENT HEIGHT MAP
//    ////USE CLIENT HEIGHT MAPCLIENT HEIGHT MAP----------------------------
//   //  /USE CLIENT HEIGHT MAPCLIENT HEIGHT MAP                            ^
 /////   /USE CLIENT HEIGHT MAPCLIENT HEIGHT MAP                            0
         // XFER //
         // X  R //
         // X  R //
         // XFER //





}  // end xfer

// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::loadPostProcess( void )
{

	// extend base class
	TerrainVisual::loadPostProcess();

}  // end loadPostProcess

