Files
FC1/Editor/TerrainBeachGen.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

612 lines
17 KiB
C++

////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: terrainbeachgen.cpp
// Version: v1.00
// Created: 1/7/2003 by Vladimir.
// Compilers: Visual Studio.NET
// Description:
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "TerrainBeachGen.h"
#include "Heightmap.h"
//min size of water area with beaches
#define MIN_UNITS_IN_WATER_AREA 8
#define BOTTOM_LEVEL (-10000)
//////////////////////////////////////////////////////////////////////////
CTerrainBeachGenerator::CTerrainBeachGenerator(CHeightmap * pTerrain)
{
memset(this,0,sizeof(*this));
m_pFile = 0;
m_nMaxAreaSize = 0;
m_pTerrain = pTerrain;
m_pHeightmapData = pTerrain->GetData();
SSectorInfo ssi;
pTerrain->GetSectorsInfo( ssi );
m_sectorSize = ssi.sectorSize;
m_unitSize = ssi.unitSize;
m_terrainSize = ssi.sectorSize*ssi.numSectors;
//m_sectorTableSize = 32;
m_sectorTableSize = ssi.numSectors;
m_heightmapSize = pTerrain->GetHeight();
m_waterLevel = pTerrain->GetWaterLevel();
m_fShoreSize = 2;
m_arrBeachMap.Allocate(m_terrainSize/m_unitSize+1);
m_WaterAreaMap.Allocate(m_terrainSize/m_unitSize+1);
}
//////////////////////////////////////////////////////////////////////////
void CTerrainBeachGenerator::Generate( CFile &file )
{
m_pFile = &file;
int nAreasFound = MarkWaterAreas();
int i,x,y;
std::vector<CTerrainSectorBeachInfo*> sectors;
sectors.resize( m_sectorTableSize*m_sectorTableSize );
for (x = 0; x < m_sectorTableSize; x++)
{
for (y = 0; y < m_sectorTableSize; y++)
{
CTerrainSectorBeachInfo *pSector = new CTerrainSectorBeachInfo;
sectors[x + y*m_sectorTableSize] = pSector;
pSector->m_nOriginX = x * m_sectorSize;
pSector->m_nOriginY = y * m_sectorSize;
}
}
for (x = 0; x < m_sectorTableSize; x++)
{
for (y = 0; y < m_sectorTableSize; y++)
{
MakeBeachStage1( sectors[x + y*m_sectorTableSize] );
}
}
for (x = 0; x < m_sectorTableSize; x++)
{
for (y = 0; y < m_sectorTableSize; y++)
{
MakeBeachStage2( sectors[x + y*m_sectorTableSize] );
}
}
for (i = 0; i < m_sectorTableSize*m_sectorTableSize; i++)
{
delete sectors[i];
}
}
//////////////////////////////////////////////////////////////////////////
float CTerrainBeachGenerator::GetZSafe(int x, int y)
{
if (x>=0 && y>=0 && x<m_terrainSize && y<m_terrainSize)
{
x /= m_unitSize;
y /= m_unitSize;
float z = m_pHeightmapData[(y*m_heightmapSize + x)];
if (z > BOTTOM_LEVEL)
return z;
}
return BOTTOM_LEVEL;
}
//////////////////////////////////////////////////////////////////////////
float CTerrainBeachGenerator::GetZSafe(float fx, float fy)
{
int x = ftoi(fx);
int y = ftoi(fy);
if (x>=0 && y>=0 && x<m_terrainSize && y<m_terrainSize)
{
x /= m_unitSize;
y /= m_unitSize;
float z = m_pHeightmapData[(y*m_heightmapSize+x)/m_unitSize];
if (z > BOTTOM_LEVEL)
return z;
}
return BOTTOM_LEVEL;
}
//////////////////////////////////////////////////////////////////////////
float CTerrainBeachGenerator::GetZApr(float x1, float y1)
{
float dDownLandZ;
if( x1<1 || y1<1 || x1>=m_terrainSize || y1>=m_terrainSize )
dDownLandZ = BOTTOM_LEVEL;
else
{
// convert into hmap space
x1 /= m_unitSize;
y1 /= m_unitSize;
int nX = ftoi(x1);
int nY = ftoi(y1);
//int nX = (int)x1;
//int nY = (int)y1;
float dx1 = x1 - nX;
float dy1 = y1 - nY;
float dDownLandZ0 =
(1.f-dx1) * m_pHeightmapData[nX + nY*m_heightmapSize] +
( dx1) * m_pHeightmapData[nX+1 + nY*m_heightmapSize];
float dDownLandZ1 =
(1.f-dx1) * m_pHeightmapData[nX + (nY+1)*m_heightmapSize] +
( dx1) * m_pHeightmapData[nX+1 + (nY+1)*m_heightmapSize];
dDownLandZ = (1-dy1) * dDownLandZ0 + ( dy1) * dDownLandZ1;
if(dDownLandZ < BOTTOM_LEVEL)
dDownLandZ = BOTTOM_LEVEL;
}
return dDownLandZ;
}
//////////////////////////////////////////////////////////////////////////
int CTerrainBeachGenerator::MarkWaterAreas()
{
ushort nMaxAreaId = 0;
int nSize = m_terrainSize/m_unitSize;
for(int x=0; x<nSize; x++)
{
for(int y=0; y<nSize; y++)
{
bool in_water = GetZSafe(x*m_unitSize,y*m_unitSize) < m_waterLevel;
m_WaterAreaMap[x][y] = in_water ? 1 : 0;
}
}
m_lstWaterAreaSizeTable.clear();
for(int x=0; x<nSize; x++)
for(int y=0; y<nSize; y++)
{
while(m_lstWaterAreaSizeTable.size()<=m_WaterAreaMap[x][y])
m_lstWaterAreaSizeTable.push_back(int(0));
m_lstWaterAreaSizeTable[m_WaterAreaMap[x][y]] ++;
}
return 1;
}
//////////////////////////////////////////////////////////////////////////
void CTerrainBeachGenerator::MakeBeachStage1( CTerrainSectorBeachInfo *pSector )
{
for(int x=pSector->m_nOriginX; x<=pSector->m_nOriginX+m_sectorSize; x+=m_unitSize)
for(int y=pSector->m_nOriginY; y<=pSector->m_nOriginY+m_sectorSize; y+=m_unitSize)
{
bool in_water = GetZSafe(x,y) < m_waterLevel;
bool beach = 0;
for(int _x=x-m_unitSize; _x<=x+m_unitSize; _x+=m_unitSize)
for(int _y=y-m_unitSize; _y<=y+m_unitSize; _y+=m_unitSize)
{
bool _in_water = (GetZSafe(_x,_y) < m_waterLevel)
&& (_x>0 && _y>0 && _x<m_terrainSize && _y<m_terrainSize)
&& (m_lstWaterAreaSizeTable[(m_WaterAreaMap[_x/m_unitSize][_y/m_unitSize])] > MIN_UNITS_IN_WATER_AREA);
if(in_water != _in_water && (x==_x || y==_y))
{
beach = true;
break;
}
}
m_arrBeachMap[x/m_unitSize][y/m_unitSize].beach = beach;
m_arrBeachMap[x/m_unitSize][y/m_unitSize].in_water = in_water;
}
}
void CTerrainBeachGenerator::MakeBeachStage2( CTerrainSectorBeachInfo *pSector )
{
struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F * m_pVertBufferBeach=0;
int m_nVertNumBeach=0;
/*
if(!m_bBeachPresent)
{
if(hFileToSave)
GetSystem()->GetIPak()->FPrintf(hFileToSave, "BeachInfo(%8d)",GetSecIndex());
// m_nVertNumBeach is zero = no beach here
if(hFileToSave)
GetSystem()->GetIPak()->FWrite(&m_nVertNumBeach,1,sizeof(m_nVertNumBeach),hFileToSave);
return;
}
*/
int group;
for(group=0; group<MAX_BEACH_GROUPS; group++)
pSector->m_arrlistBeachVerts[group].clear();
for(int x=pSector->m_nOriginX; x<=pSector->m_nOriginX+m_sectorSize; x+=m_unitSize)
for(int y=pSector->m_nOriginY; y<=pSector->m_nOriginY+m_sectorSize; y+=m_unitSize)
{
if (m_arrBeachMap[x/m_unitSize][y/m_unitSize].beach &&
!m_arrBeachMap[x/m_unitSize][y/m_unitSize].in_water)
{
Vec3d water_dir(0,0,0);
for(int _x=x-m_unitSize; _x<=x+m_unitSize; _x+=m_unitSize)
for(int _y=y-m_unitSize; _y<=y+m_unitSize; _y+=m_unitSize)
if(_x>=0 && _y>=0 && _x<m_terrainSize && _y<m_terrainSize)
if(m_arrBeachMap[_x/m_unitSize][_y/m_unitSize].in_water)
water_dir += Vec3d(float(_x-x),float(_y-y),0);
water_dir.Normalize();
Vec3d border_pos((float)x,(float)y,m_waterLevel);
CTerrainSectorBeachInfo::BeachPairStruct pair;
pair.water_dir = water_dir;
water_dir/=100;
int t=0;
while(GetZApr(border_pos.x,border_pos.y)>m_waterLevel && t<100)
{
border_pos += water_dir;
t++;
}
// assert(t<100);
/* if( border_pos.x>=m_nOriginX &&
border_pos.y>=m_nOriginY &&
border_pos.x<(m_nOriginX+m_sectorSize) &&
border_pos.y<(m_nOriginY+m_sectorSize) )*/
{
pair.pos = border_pos ;//+ Vec3d(0,0,0.01f);
pSector->m_lstUnsortedBeachVerts.push_back(pair);
}
/* else
{
if(GetSectorFromPoint(border_pos.x,border_pos.y) == this
}*/
}
}
if(!pSector->m_lstUnsortedBeachVerts.size())
{
CString str;
str.Format( "BeachInfo(%8d)",GetSecIndex(pSector) );
m_pFile->Write( (const char*)str,str.GetLength() );
// m_nVertNumBeach is zero = no beach here
m_pFile->Write( &m_nVertNumBeach,sizeof(m_nVertNumBeach) );
return;
}
for(group=0; group<MAX_BEACH_GROUPS; group++)
{
int prev_closest_id = -1;
pSector->m_arrlistBeachVerts[group].clear();
// search for free starting point
int first;
for(first=0; first<pSector->m_lstUnsortedBeachVerts.size(); first++)
if(pSector->m_lstUnsortedBeachVerts[first].busy < 2)
break;
if(first>=pSector->m_lstUnsortedBeachVerts.size())
break; // no more free points
pSector->m_arrlistBeachVerts[group].push_back(pSector->m_lstUnsortedBeachVerts[first]);
pSector->m_lstUnsortedBeachVerts[first].busy ++;
for(int pass=0; pass<pSector->m_lstUnsortedBeachVerts.size(); pass++)
{
// find totaly free point closest to last result point
int closest_id = -1;
{
float closest_dist = 100000;
for(int j=0; j<pSector->m_lstUnsortedBeachVerts.size(); j++)
if(pSector->m_lstUnsortedBeachVerts[j].busy==0)
{
float dist = GetDistance( pSector->m_arrlistBeachVerts[group].back().pos, pSector->m_lstUnsortedBeachVerts[j].pos);
if(dist<closest_dist && dist<4 && dist!=0)
{
closest_dist = dist;
closest_id = j;
}
}
}
if(closest_id>=0)
{ // if found add into current group
if(prev_closest_id>=0)
pSector->m_lstUnsortedBeachVerts[prev_closest_id].busy++;
prev_closest_id = closest_id;
pSector->m_arrlistBeachVerts[group].push_back(pSector->m_lstUnsortedBeachVerts[closest_id]);
pSector->m_lstUnsortedBeachVerts[closest_id].busy ++;
}
else
{
if(prev_closest_id>=0)
pSector->m_lstUnsortedBeachVerts[prev_closest_id].busy++;
prev_closest_id = closest_id;
closest_id = -1;
{ // try to find semi free point as last
float closest_dist = 100000;
for(int j=0; j<pSector->m_lstUnsortedBeachVerts.size(); j++)
if(pSector->m_lstUnsortedBeachVerts[j].busy==1)
{
// skip point if it is already used in this group
int k;
for(k=0; k<pSector->m_arrlistBeachVerts[group].size(); k++)
if(GetDistance(pSector->m_arrlistBeachVerts[group][k].pos,pSector->m_lstUnsortedBeachVerts[j].pos) == 0)
break;
if(k<pSector->m_arrlistBeachVerts[group].size())
continue;
float dist = GetDistance(pSector->m_arrlistBeachVerts[group].back().pos,pSector->m_lstUnsortedBeachVerts[j].pos);
if(dist<closest_dist && dist<4 && dist!=0)
{
closest_dist = dist;
closest_id = j;
}
}
}
if(closest_id>=0)
{ // if found add into current group
pSector->m_arrlistBeachVerts[group].push_back(pSector->m_lstUnsortedBeachVerts[closest_id]);
pSector->m_lstUnsortedBeachVerts[closest_id].busy ++;
}
if(pSector->m_arrlistBeachVerts[group].size()==1)
pSector->m_arrlistBeachVerts[group].clear();
break;
}
}
}
// for(int u=0; u<UnsortedBeachVerts.size(); u++)
// CSystem::GetRenderer()->DrawLabel(UnsortedBeachVerts[u].pos,1,"%d",UnsortedBeachVerts[u].busy);
// smooth
for(group=0; group<MAX_BEACH_GROUPS; group++)
{
int numv = pSector->m_arrlistBeachVerts[group].size();
for(int i=1; i<numv-1; i++)
{
Vec3d dir1 = pSector->m_arrlistBeachVerts[group][i+1].pos - pSector->m_arrlistBeachVerts[group][i].pos;
dir1.Normalize();
Vec3d dir2 = pSector->m_arrlistBeachVerts[group][i-1].pos - pSector->m_arrlistBeachVerts[group][i].pos;
dir2.Normalize();
Vec3d dir;
if(dir1.Dot(dir2)>-0.99f)
dir = dir1 + dir2;
else
dir = dir1.Cross(Vec3d(0,0,1));
dir.Normalize();
if(dir.Dot(pSector->m_arrlistBeachVerts[group][i ].water_dir)<0)
dir = -dir;
pSector->m_arrlistBeachVerts[group][i ].water_dir = dir;
}
}
for( group=0; group<MAX_BEACH_GROUPS; group++)
{
int numv = pSector->m_arrlistBeachVerts[group].size();
for( int i=0; i<numv-1; i++)
{
CTerrainSectorBeachInfo::BeachPairStruct * p1 = &pSector->m_arrlistBeachVerts[group][i ];
CTerrainSectorBeachInfo::BeachPairStruct * p2 = &pSector->m_arrlistBeachVerts[group][i+1];
int _x = int((p1->pos + p1->water_dir*2).x);
int _y = int((p1->pos + p1->water_dir*2).y);
// int nAreaID = m_WaterAreaMap[_x/m_unitSize][_y/m_unitSize];
float fBeachSize = (float)m_fShoreSize;//1.f+8.f*((float)m_lstWaterAreaSizeTable[nAreaID]/(float)m_nMaxAreaSize);
// left
p1->pos1 = p1->pos;
int l=0;
while(p1->pos1.z<m_waterLevel+0.1f*fBeachSize && l<70)
{
p1->pos1 = p1->pos1 - pSector->m_arrlistBeachVerts[group][i].water_dir*0.05f;
p1->pos1.z = GetZApr(p1->pos1.x,p1->pos1.y);
l++;
}
p1->pos2 = p1->pos + p1->water_dir*fBeachSize;
p1->posm = p1->pos + Vec3d(0,0,0.2f*fBeachSize/4);
// right
p2->pos1 = p2->pos;
l=0;
while(p2->pos1.z<m_waterLevel+0.1f*fBeachSize && l<70)
{
p2->pos1 = p2->pos1 - p2->water_dir*0.05f;
p2->pos1.z = GetZApr(p2->pos1.x,p2->pos1.y);
l++;
}
p2->pos2 = p2->pos + p2->water_dir*fBeachSize;
p2->posm = p2->pos + Vec3d(0,0,0.2f*fBeachSize/4);
}
}
pSector->m_lstUnsortedBeachVerts.clear();
/////////////////////////////////////////////////////////////////////////////////////////////
// stage 3
/////////////////////////////////////////////////////////////////////////////////////////////
/*
if(!m_bBeachPresent)
{
if(hFileToSave)
GetSystem()->GetIPak()->FPrintf(hFileToSave, "BeachInfo(%8d)",GetSecIndex());
// m_nVertNumBeach is zero = no beach here
if(hFileToSave)
GetSystem()->GetIPak()->FWrite(&m_nVertNumBeach,1,sizeof(m_nVertNumBeach),hFileToSave);
return;
}
*/
// count elements
int nElements = 0;
for( group=0; group<MAX_BEACH_GROUPS; group++)
{
int numv = pSector->m_arrlistBeachVerts[group].size();
for( int i=0; i<numv-1; i++)
nElements ++;
}
if(!nElements)
{
//m_bBeachPresent = false;
{
CString str;
str.Format( "BeachInfo(%8d)",GetSecIndex(pSector) );
m_pFile->Write( (const char*)str,str.GetLength() );
// m_nVertNumBeach is zero = no beach here
m_pFile->Write( &m_nVertNumBeach,sizeof(m_nVertNumBeach) );
return;
}
}
// make buffer
m_nVertNumBeach = 6*nElements;
m_pVertBufferBeach = new struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F[m_nVertNumBeach];
std::vector<ushort> lstBeachIndices;
// tmp buff
struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F verts[6];
// color
Vec3d vTerrainColor(1,1,1); //= GetSystem()->GetI3DEngine()->GetWorldColor();//*GetSystem()->GetI3DEngine()->GetWorldBrightnes();
for(int i=0; i<6; i++)
{
verts[i].color.bcolor[0]=(byte)min(255,vTerrainColor[0]*512.0f);
verts[i].color.bcolor[1]=(byte)min(255,vTerrainColor[1]*512.0f);
verts[i].color.bcolor[2]=(byte)min(255,vTerrainColor[2]*512.0f);
verts[i].color.bcolor[3]=0;
}
verts[2].color.bcolor[3]=255;
verts[3].color.bcolor[3]=255;
// tex coord
verts[0].st[0]=0; verts[0].st[1]=1.0f;
verts[1].st[0]=1; verts[1].st[1]=1.0f;
verts[2].st[0]=0; verts[2].st[1]=0.5f;
verts[3].st[0]=1; verts[3].st[1]=0.5f;
verts[4].st[0]=0; verts[4].st[1]=0.0f;
verts[5].st[0]=1; verts[5].st[1]=0.0f;
// make final buffer
int nPos=0;
int nIndex=0;
for( group=0; group<MAX_BEACH_GROUPS; group++)
{
int numv = pSector->m_arrlistBeachVerts[group].size();
for( int i=0; i<numv-1; i++)
{
CTerrainSectorBeachInfo::BeachPairStruct * p1 = &pSector->m_arrlistBeachVerts[group][i ];
CTerrainSectorBeachInfo::BeachPairStruct * p2 = &pSector->m_arrlistBeachVerts[group][i+1];
// pos
verts[0].xyz = p1->pos1;
verts[1].xyz = p2->pos1;
verts[2].xyz = p1->posm;
verts[3].xyz = p2->posm;
verts[4].xyz = p1->pos2;
verts[5].xyz = p2->pos2;
// draw
memcpy(&m_pVertBufferBeach[nPos],verts,sizeof(verts));
nPos+=6;
lstBeachIndices.push_back(nIndex+0);
lstBeachIndices.push_back(nIndex+1);
lstBeachIndices.push_back(nIndex+2);
lstBeachIndices.push_back(nIndex+3);
lstBeachIndices.push_back(nIndex+2);
lstBeachIndices.push_back(nIndex+1);
lstBeachIndices.push_back(nIndex+2);
lstBeachIndices.push_back(nIndex+3);
lstBeachIndices.push_back(nIndex+4);
lstBeachIndices.push_back(nIndex+5);
lstBeachIndices.push_back(nIndex+4);
lstBeachIndices.push_back(nIndex+3);
nIndex+=6;
}
}
// save calculated data into file
// header
CString str;
str.Format( "BeachInfo(%8d)",GetSecIndex(pSector) );
m_pFile->Write( (const char*)str,str.GetLength() );
// m_nVertNumBeach is zero = no beach here
m_pFile->Write( &m_nVertNumBeach,sizeof(m_nVertNumBeach) );
m_pFile->Write( m_pVertBufferBeach,m_nVertNumBeach*sizeof(struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F) );
// indices
int nIndCount = lstBeachIndices.size();
m_pFile->Write(&nIndCount,sizeof(nIndCount) );
m_pFile->Write(&lstBeachIndices[0],nIndCount*sizeof(ushort) );
/*
// make vertex buffer
if(m_pLeafBufferBeach)
GetRenderer()->DeleteLeafBuffer(m_pLeafBufferBeach);
m_pLeafBufferBeach = GetRenderer()->CreateLeafBufferInitialized(
m_pVertBufferBeach, m_nVertNumBeach, VERTEX_FORMAT_P3F_COL4UB_TEX2F,
lstBeachIndices.GetElements(), lstBeachIndices.size(), R_PRIMV_TRIANGLES, "WaterBeach");
m_pLeafBufferBeach->SetChunk( m_pTerrain->m_pSHShore,
0, m_nVertNumBeach,
0, lstBeachIndices.size());
assert(m_pLeafBufferBeach);
*/
delete [] m_pVertBufferBeach; m_pVertBufferBeach=0;
}