1604 lines
40 KiB
C++
1604 lines
40 KiB
C++
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Crytek Engine Source File.
|
|
// Copyright (C), Crytek Studios, 2002.
|
|
// -------------------------------------------------------------------------
|
|
// File name: vegetationmap.cpp
|
|
// Version: v1.00
|
|
// Created: 31/7/2002 by Timur.
|
|
// Compilers: Visual Studio.NET
|
|
// Description:
|
|
// -------------------------------------------------------------------------
|
|
// History:
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "StdAfx.h"
|
|
#include "VegetationMap.h"
|
|
#include "Heightmap.h"
|
|
#include "Layer.h"
|
|
#include "VegetationBrush.h"
|
|
|
|
#include <I3DEngine.h>
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CVegetationMap implementation.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#pragma pack(push,1)
|
|
// Structure of vegetation object instance in file.
|
|
struct SVegInst
|
|
{
|
|
Vec3 pos;
|
|
float scale;
|
|
uchar objectIndex;
|
|
uchar brightness;
|
|
uchar flags;
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
#define MAX_TERRAIN_SIZE 1024
|
|
#define MIN_MASK_VALUE 32
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Undo object for CBaseObject.
|
|
class CUndoVegInstance : public IUndoObject
|
|
{
|
|
public:
|
|
CUndoVegInstance( CVegetationInstance *obj )
|
|
{
|
|
// Stores the current state of this object.
|
|
assert( obj != 0 );
|
|
m_obj = obj;
|
|
m_obj->AddRef();
|
|
|
|
ZeroStruct(m_redo);
|
|
m_undo.pos = m_obj->pos;
|
|
m_undo.scale = m_obj->scale;
|
|
m_undo.objectIndex = m_obj->object->GetIndex();
|
|
m_undo.brightness = m_obj->brightness;
|
|
m_undo.flags = m_obj->flags;
|
|
}
|
|
~CUndoVegInstance()
|
|
{
|
|
m_obj->Release();
|
|
}
|
|
protected:
|
|
virtual int GetSize() { return sizeof(*this); }
|
|
virtual const char* GetDescription() { return "Vegetation Modify"; };
|
|
|
|
virtual void Undo( bool bUndo )
|
|
{
|
|
if (bUndo)
|
|
{
|
|
m_redo.pos = m_obj->pos;
|
|
m_redo.scale = m_obj->scale;
|
|
m_redo.objectIndex = m_obj->object->GetIndex();
|
|
m_redo.brightness = m_obj->brightness;
|
|
m_redo.flags = m_obj->flags;
|
|
}
|
|
m_obj->scale = m_undo.scale;
|
|
m_obj->brightness = m_undo.brightness;
|
|
m_obj->flags = m_undo.flags;
|
|
GetIEditor()->GetVegetationMap()->MoveInstance( m_obj,m_undo.pos );
|
|
}
|
|
virtual void Redo()
|
|
{
|
|
m_obj->scale = m_redo.scale;
|
|
m_obj->brightness = m_redo.brightness;
|
|
m_obj->flags = m_redo.flags;
|
|
GetIEditor()->GetVegetationMap()->MoveInstance( m_obj,m_redo.pos );
|
|
}
|
|
private:
|
|
CVegetationInstance *m_obj;
|
|
SVegInst m_undo;
|
|
SVegInst m_redo;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Undo object for CBaseObject.
|
|
class CUndoVegInstanceCreate : public IUndoObject
|
|
{
|
|
public:
|
|
CUndoVegInstanceCreate( CVegetationInstance *obj,bool bDeleted )
|
|
{
|
|
// Stores the current state of this object.
|
|
assert( obj != 0 );
|
|
m_obj = obj;
|
|
m_obj->AddRef();
|
|
m_bDeleted = bDeleted;
|
|
}
|
|
~CUndoVegInstanceCreate()
|
|
{
|
|
m_obj->Release();
|
|
}
|
|
protected:
|
|
virtual int GetSize() { return sizeof(*this); }
|
|
virtual const char* GetDescription() { return "Vegetation Create"; };
|
|
|
|
virtual void Undo( bool bUndo )
|
|
{
|
|
CVegetationMap *vegMap = GetIEditor()->GetVegetationMap();
|
|
if (m_bDeleted)
|
|
{
|
|
vegMap->AddObjInstance( m_obj );
|
|
vegMap->MoveInstance( m_obj,m_obj->pos );
|
|
}
|
|
else
|
|
vegMap->DeleteObjInstance( m_obj );
|
|
}
|
|
virtual void Redo()
|
|
{
|
|
CVegetationMap *vegMap = GetIEditor()->GetVegetationMap();
|
|
if (!m_bDeleted)
|
|
{
|
|
vegMap->AddObjInstance( m_obj );
|
|
vegMap->MoveInstance( m_obj,m_obj->pos );
|
|
}
|
|
else
|
|
vegMap->DeleteObjInstance( m_obj );
|
|
}
|
|
private:
|
|
CVegetationInstance *m_obj;
|
|
bool m_bDeleted;
|
|
};
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CVegetationMap::CVegetationMap()
|
|
{
|
|
m_numSectors = 0;
|
|
m_sectors = 0;
|
|
m_worldToSector = 0;
|
|
|
|
m_minimalDistance = 0.1f;
|
|
m_numInstances = 0;
|
|
m_bUpdateOnPaintBrightness = true;
|
|
|
|
m_I3DEngine = GetIEditor()->Get3DEngine();
|
|
|
|
// Initialize the random number generator
|
|
srand( GetTickCount() );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CVegetationMap::~CVegetationMap()
|
|
{
|
|
Clear();
|
|
|
|
if (m_sectors)
|
|
free( m_sectors );
|
|
}
|
|
|
|
void CVegetationMap::Clear()
|
|
{
|
|
ClearObjects();
|
|
}
|
|
|
|
void CVegetationMap::ClearSectors()
|
|
{
|
|
// Delete all objects in sectors.
|
|
// Iterator over all sectors.
|
|
CVegetationInstance *next;
|
|
for (int i = 0; i < m_numSectors*m_numSectors; i++)
|
|
{
|
|
SectorInfo *si = &m_sectors[i];
|
|
// Iterate on every object in sector.
|
|
for (CVegetationInstance *obj = si->first; obj; obj = next)
|
|
{
|
|
next = obj->next;
|
|
obj->Release();
|
|
}
|
|
si->first = 0;
|
|
}
|
|
m_numInstances = 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::Allocate( CHeightmap *heightmap )
|
|
{
|
|
m_I3DEngine = GetIEditor()->Get3DEngine();
|
|
m_heightmap = heightmap;
|
|
|
|
int terrainSize = m_heightmap->GetUnitSize() * max(m_heightmap->GetWidth(),m_heightmap->GetHeight());
|
|
|
|
int sectorSize;
|
|
int numSectors;
|
|
|
|
if (terrainSize < MAX_TERRAIN_SIZE)
|
|
{
|
|
sectorSize = 1;
|
|
}
|
|
else
|
|
{
|
|
sectorSize = terrainSize / MAX_TERRAIN_SIZE;
|
|
}
|
|
assert( sectorSize != 0 );
|
|
numSectors = terrainSize / sectorSize;
|
|
|
|
if (sectorSize != m_sectorSize || numSectors != m_numSectors || !m_sectors)
|
|
{
|
|
ClearSectors();
|
|
if (m_sectors)
|
|
free( m_sectors );
|
|
|
|
m_numSectors = numSectors;
|
|
m_sectorSize = sectorSize;
|
|
// allocate sectors map.
|
|
int sz = sizeof(SectorInfo)*m_numSectors*m_numSectors;
|
|
m_sectors = (SectorInfo*)malloc( sz );
|
|
memset( m_sectors,0,sz );
|
|
}
|
|
|
|
m_mapSize = terrainSize;
|
|
m_worldToSector = 1.0f / m_sectorSize;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CVegetationMap::GetNumSectors() const
|
|
{
|
|
return m_numSectors;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CVegetationMap::GetSize() const
|
|
{
|
|
return m_numSectors * m_sectorSize;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::PlaceObjectsOnTerrain()
|
|
{
|
|
if (!m_I3DEngine)
|
|
return;
|
|
|
|
// Clear all objects from 3d Engine.
|
|
m_I3DEngine->RemoveAllStaticObjects();
|
|
|
|
// Iterator over all sectors.
|
|
for (int i = 0; i < m_numSectors*m_numSectors; i++)
|
|
{
|
|
SectorInfo *si = &m_sectors[i];
|
|
// Iterate on every object in sector.
|
|
for (CVegetationInstance *obj = si->first; obj; obj = obj->next)
|
|
{
|
|
if (!obj->object->IsHidden())
|
|
{
|
|
// Stick vegetation to terrain.
|
|
obj->pos.z = m_I3DEngine->GetTerrainElevation(obj->pos.x,obj->pos.y);
|
|
m_I3DEngine->AddStaticObject( obj->object->GetId(),obj->pos,obj->scale,obj->brightness );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::RemoveObjectsFromTerrain()
|
|
{
|
|
if (m_I3DEngine)
|
|
m_I3DEngine->RemoveAllStaticObjects();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::HideObject( CVegetationObject *object,bool bHide )
|
|
{
|
|
if (object->IsHidden() == bHide)
|
|
return;
|
|
|
|
object->SetHidden(bHide);
|
|
|
|
if (object->GetNumInstances() > 0)
|
|
{
|
|
// Iterate over all sectors.
|
|
for (int i = 0; i < m_numSectors*m_numSectors; i++)
|
|
{
|
|
SectorInfo *si = &m_sectors[i];
|
|
// Iterate on every object in sector.
|
|
for (CVegetationInstance *obj = si->first; obj; obj = obj->next)
|
|
{
|
|
if (obj->object == object)
|
|
{
|
|
// Remove/Add to terrain.
|
|
if (bHide)
|
|
m_I3DEngine->RemoveStaticObject( -1,obj->pos );
|
|
else
|
|
{
|
|
// Stick vegetation to terrain.
|
|
obj->pos.z = m_I3DEngine->GetTerrainElevation(obj->pos.x,obj->pos.y);
|
|
m_I3DEngine->AddStaticObject( obj->object->GetId(),obj->pos,obj->scale,obj->brightness );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CVegetationMap::SectorLink( CVegetationInstance *obj,SectorInfo *sector )
|
|
{
|
|
/*
|
|
if (sector->first)
|
|
{
|
|
// Add to the end of current list.
|
|
CVegetationInstance *head = sector->first;
|
|
CVegetationInstance *tail = head->prev;
|
|
obj->prev = tail;
|
|
obj->next = 0;
|
|
|
|
tail->next = obj;
|
|
head->prev = obj; // obj is now last object.
|
|
}
|
|
else
|
|
{
|
|
sector->first = obj;
|
|
obj->prev = obj;
|
|
obj->next = 0;
|
|
}
|
|
*/
|
|
if (sector->first)
|
|
{
|
|
// Add to the end of current list.
|
|
CVegetationInstance *head = sector->first;
|
|
obj->prev = 0;
|
|
obj->next = head;
|
|
head->prev = obj;
|
|
sector->first = obj;
|
|
}
|
|
else
|
|
{
|
|
sector->first = obj;
|
|
obj->prev = 0;
|
|
obj->next = 0;
|
|
}
|
|
}
|
|
|
|
void CVegetationMap::SectorUnlink( CVegetationInstance *obj,SectorInfo *sector )
|
|
{
|
|
if (obj == sector->first) // if head of list.
|
|
{
|
|
sector->first = obj->next;
|
|
if (sector->first)
|
|
sector->first->prev = 0;
|
|
}
|
|
else
|
|
{
|
|
//assert( obj->prev != 0 );
|
|
if (obj->prev)
|
|
obj->prev->next = obj->next;
|
|
if (obj->next)
|
|
obj->next->prev = obj->prev;
|
|
}
|
|
}
|
|
|
|
void CVegetationMap::AddObjInstance( CVegetationInstance *obj )
|
|
{
|
|
SectorInfo *si = GetSector(obj->pos);
|
|
if (!si)
|
|
{
|
|
obj->next = obj->prev = 0;
|
|
return;
|
|
}
|
|
obj->AddRef();
|
|
|
|
CVegetationObject *object = obj->object;
|
|
// Add object to end of the list of instances in sector.
|
|
// Increase number of instances.
|
|
object->SetNumInstances( object->GetNumInstances() + 1 );
|
|
m_numInstances++;
|
|
|
|
SectorLink( obj,si );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CVegetationInstance* CVegetationMap::CreateObjInstance( CVegetationObject *object,const Vec3 &pos )
|
|
{
|
|
SectorInfo *si = GetSector(pos);
|
|
if (!si)
|
|
return 0;
|
|
|
|
CVegetationInstance *obj = new CVegetationInstance;
|
|
obj->m_refCount = 1; // Starts with 1 reference.
|
|
//obj->AddRef();
|
|
obj->pos = pos;
|
|
obj->scale = 1;
|
|
obj->object = object;
|
|
obj->brightness = 255;
|
|
obj->flags = 0;
|
|
|
|
if (CUndo::IsRecording())
|
|
CUndo::Record( new CUndoVegInstanceCreate(obj,false) );
|
|
|
|
// Add object to end of the list of instances in sector.
|
|
// Increase number of instances.
|
|
object->SetNumInstances( object->GetNumInstances() + 1 );
|
|
m_numInstances++;
|
|
|
|
SectorLink( obj,si );
|
|
return obj;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::DeleteObjInstance( CVegetationInstance *obj,SectorInfo *sector )
|
|
{
|
|
if (CUndo::IsRecording())
|
|
CUndo::Record( new CUndoVegInstanceCreate(obj,true) );
|
|
|
|
m_I3DEngine->RemoveStaticObject( -1, obj->pos );
|
|
SectorUnlink(obj,sector);
|
|
obj->object->SetNumInstances( obj->object->GetNumInstances() - 1 );
|
|
m_numInstances--;
|
|
assert( m_numInstances >= 0 );
|
|
obj->Release();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::DeleteObjInstance( CVegetationInstance *obj )
|
|
{
|
|
SectorInfo *sector = GetSector( obj->pos );
|
|
if (sector)
|
|
DeleteObjInstance( obj,sector );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CVegetationInstance* CVegetationMap::GetNearestInstance( const Vec3 &pos,float radius )
|
|
{
|
|
// check all sectors intersected by radius.
|
|
float r = radius*m_worldToSector;
|
|
float px = pos.x*m_worldToSector;
|
|
float py = pos.y*m_worldToSector;
|
|
int sx1 = ftoi(px-r); sx1 = max(sx1,0);
|
|
int sx2 = ftoi(px+r); sx2 = min(sx2,m_numSectors-1);
|
|
int sy1 = ftoi(py-r); sy1 = max(sy1,0);
|
|
int sy2 = ftoi(py+r); sy2 = min(sy2,m_numSectors-1);
|
|
|
|
CVegetationInstance *nearest = 0;
|
|
float minDist = FLT_MAX;
|
|
|
|
for (int y = sy1; y <= sy2; y++)
|
|
{
|
|
for (int x = sx1; x <= sx2; x++)
|
|
{
|
|
// For each sector check if any object is nearby.
|
|
SectorInfo *si = GetSector(x,y);
|
|
if (si->first)
|
|
{
|
|
for (CVegetationInstance *obj = si->first; obj; obj = obj->next)
|
|
{
|
|
if (fabs(obj->pos.x-pos.x) < radius && fabs(obj->pos.y-pos.y) < radius)
|
|
{
|
|
float dist = GetSquaredDistance(pos,obj->pos);
|
|
if (dist < minDist)
|
|
{
|
|
minDist = dist;
|
|
nearest = obj;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nearest;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::GetObjectInstances( float x1,float y1,float x2,float y2,std::vector<CVegetationInstance*> &instances )
|
|
{
|
|
instances.reserve(100);
|
|
// check all sectors intersected by radius.
|
|
int sx1 = ftoi(x1*m_worldToSector); sx1 = max(sx1,0);
|
|
int sx2 = ftoi(x2*m_worldToSector); sx2 = min(sx2,m_numSectors-1);
|
|
int sy1 = ftoi(y1*m_worldToSector); sy1 = max(sy1,0);
|
|
int sy2 = ftoi(y2*m_worldToSector); sy2 = min(sy2,m_numSectors-1);
|
|
for (int y = sy1; y <= sy2; y++)
|
|
{
|
|
for (int x = sx1; x <= sx2; x++)
|
|
{
|
|
// For each sector check if any object is nearby.
|
|
SectorInfo *si = GetSector(x,y);
|
|
if (si->first)
|
|
{
|
|
for (CVegetationInstance *obj = si->first; obj; obj = obj->next)
|
|
{
|
|
if (obj->pos.x >= x1 && obj->pos.x <= x2 && obj->pos.y >= y1 && obj->pos.y <= y2)
|
|
{
|
|
instances.push_back(obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::GetAllInstances( std::vector<CVegetationInstance*> &instances )
|
|
{
|
|
int k = 0;
|
|
instances.resize( m_numInstances );
|
|
for (int i = 0; i < m_numSectors*m_numSectors; i++)
|
|
{
|
|
// Iterate on every object in sector.
|
|
for (CVegetationInstance *obj = m_sectors[i].first; obj; obj = obj->next)
|
|
{
|
|
instances[k++] = obj;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CVegetationMap::IsPlaceEmpty( const Vec3 &pos,float radius,CVegetationInstance *ignore )
|
|
{
|
|
// check all sectors intersected by radius.
|
|
if (pos.x < 0 || pos.y < 0 || pos.x > m_mapSize || pos.y > m_mapSize)
|
|
return false;
|
|
|
|
// check all sectors intersected by radius.
|
|
float r = radius*m_worldToSector;
|
|
float px = pos.x*m_worldToSector;
|
|
float py = pos.y*m_worldToSector;
|
|
int sx1 = ftoi(px-r); sx1 = max(sx1,0);
|
|
int sx2 = ftoi(px+r); sx2 = min(sx2,m_numSectors-1);
|
|
int sy1 = ftoi(py-r); sy1 = max(sy1,0);
|
|
int sy2 = ftoi(py+r); sy2 = min(sy2,m_numSectors-1);
|
|
for (int y = sy1; y <= sy2; y++)
|
|
{
|
|
for (int x = sx1; x <= sx2; x++)
|
|
{
|
|
// For each sector check if any object is within this radius.
|
|
SectorInfo *si = GetSector(x,y);
|
|
if (si->first)
|
|
{
|
|
for (CVegetationInstance *obj = si->first; obj; obj = obj->next)
|
|
{
|
|
if (obj != ignore && fabs(obj->pos.x-pos.x) < radius && fabs(obj->pos.y-pos.y) < radius)
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::MoveInstance( CVegetationInstance* obj,const Vec3 &newPos )
|
|
{
|
|
if (!IsPlaceEmpty(newPos,m_minimalDistance,obj))
|
|
return;
|
|
|
|
if (obj->pos != newPos)
|
|
RecordUndo( obj );
|
|
|
|
// Then delete object.
|
|
m_I3DEngine->RemoveStaticObject( -1, obj->pos );
|
|
|
|
SectorInfo *from = GetSector(obj->pos);
|
|
SectorInfo *to = GetSector(newPos);
|
|
if (from != to)
|
|
{
|
|
// Relink object between sectors.
|
|
SectorUnlink( obj,from );
|
|
if (to)
|
|
{
|
|
SectorLink( obj,to );
|
|
}
|
|
}
|
|
|
|
obj->pos = newPos;
|
|
// Stick vegetation to terrain.
|
|
obj->pos.z = m_I3DEngine->GetTerrainElevation(obj->pos.x,obj->pos.y);
|
|
m_I3DEngine->AddStaticObject( obj->object->GetId(),newPos,obj->scale,obj->brightness );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CVegetationMap::CanPlace( CVegetationObject *object,const Vec3 &pos,float radius )
|
|
{
|
|
// check all sectors intersected by radius.
|
|
if (pos.x < 0 || pos.y < 0 || pos.x > m_mapSize || pos.y > m_mapSize)
|
|
return false;
|
|
|
|
float r = radius*m_worldToSector;
|
|
float px = pos.x*m_worldToSector;
|
|
float py = pos.y*m_worldToSector;
|
|
int sx1 = ftoi(px-r); sx1 = max(sx1,0);
|
|
int sx2 = ftoi(px+r); sx2 = min(sx2,m_numSectors-1);
|
|
int sy1 = ftoi(py-r); sy1 = max(sy1,0);
|
|
int sy2 = ftoi(py+r); sy2 = min(sy2,m_numSectors-1);
|
|
for (int y = sy1; y <= sy2; y++)
|
|
{
|
|
for (int x = sx1; x <= sx2; x++)
|
|
{
|
|
// For each sector check if any object is within this radius.
|
|
SectorInfo *si = GetSector(x,y);
|
|
if (si->first)
|
|
{
|
|
for (CVegetationInstance *obj = si->first; obj; obj = obj->next)
|
|
{
|
|
// Only check objects that we need.
|
|
if (obj->object == object)
|
|
{
|
|
if (fabs(obj->pos.x-pos.x) < radius && fabs(obj->pos.y-pos.y) < radius)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (fabs(obj->pos.x-pos.x) < m_minimalDistance && fabs(obj->pos.y-pos.y) < m_minimalDistance)
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CVegetationInstance* CVegetationMap::PlaceObjectInstance( const Vec3 &worldPos,CVegetationObject* object )
|
|
{
|
|
float fScale = object->CalcVariableSize();
|
|
// Check if this place is empty.
|
|
if (CanPlace( object,worldPos,m_minimalDistance))
|
|
{
|
|
CVegetationInstance *obj = CreateObjInstance( object,worldPos );
|
|
if (obj)
|
|
{
|
|
obj->scale = fScale;
|
|
|
|
// Stick vegetation to terrain.
|
|
obj->pos.z = m_I3DEngine->GetTerrainElevation(obj->pos.x,obj->pos.y);
|
|
m_I3DEngine->AddStaticObject( object->GetId(),obj->pos,obj->scale,obj->brightness );
|
|
}
|
|
return obj;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//! Remove any object at specified location.
|
|
void CVegetationMap::RemoveObjectInstance( const Vec3 &worldPos )
|
|
{
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::PaintBrush( CRect &rc,bool bCircle,CVegetationObject* object )
|
|
{
|
|
assert( object != 0 );
|
|
|
|
GetIEditor()->SetModifiedFlag();
|
|
|
|
Vec3 p(0,0,0);
|
|
|
|
int unitSize = m_heightmap->GetUnitSize();
|
|
|
|
int mapSize = m_numSectors*m_sectorSize;
|
|
|
|
bool bProgress = rc.right-rc.left >= mapSize;
|
|
CWaitProgress wait("Distributing objects",bProgress);
|
|
|
|
// Intersect with map rectangle.
|
|
// Offset by 1 from each side.
|
|
float brushRadius2 = (rc.right-rc.left)*(rc.right-rc.left)/4;
|
|
rc &= CRect( 1,1,mapSize-2,mapSize-2 );
|
|
|
|
float AltMin = object->GetElevationMin();
|
|
float AltMax = object->GetElevationMax();
|
|
float SlopeMin = object->GetSlopeMin();
|
|
float SlopeMax = object->GetSlopeMax();
|
|
|
|
float density = object->GetDensity();
|
|
if (density <= 0)
|
|
density = m_minimalDistance;
|
|
|
|
int area = rc.Width() * rc.Height();
|
|
int count = area / (density*density);
|
|
|
|
// Limit from too much objects.
|
|
if (count > area)
|
|
count = area;
|
|
|
|
int j = 0;
|
|
|
|
float x1 = rc.left;
|
|
float y1 = rc.top;
|
|
float width2 = (rc.right-rc.left)/2.0f;
|
|
float height2 = (rc.bottom-rc.top)/2.0f;
|
|
|
|
float cx = (rc.right+rc.left)/2.0f;
|
|
float cy = (rc.bottom+rc.top)/2.0f;
|
|
|
|
// Calculate the vegetation for every point in the area marked by the brush
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (bProgress)
|
|
{
|
|
j++;
|
|
if (j > 200)
|
|
{
|
|
if (!wait.Step( 100*i/count ))
|
|
break;
|
|
}
|
|
}
|
|
|
|
float x = x1 + (frand()+1)*width2;
|
|
float y = y1 + (frand()+1)*height2;
|
|
|
|
// Skip all coordinates outside the brush circle
|
|
if (bCircle) {
|
|
if (((x-cx)*(x-cx) + (y-cy)*(y-cy)) > brushRadius2)
|
|
continue;
|
|
}
|
|
|
|
// Get the height of the current point
|
|
// swap x/y
|
|
int hy = ftoi(x/unitSize);
|
|
int hx = ftoi(y/unitSize);
|
|
|
|
float currHeight = m_heightmap->GetXY(hx,hy);
|
|
// Check if height valie is withing brush min/max altitude.
|
|
if (currHeight < AltMin || currHeight > AltMax)
|
|
continue;
|
|
|
|
// Calculate the slope for this spot
|
|
float slope = m_heightmap->GetSlope( hx,hy );
|
|
|
|
// Check if slope is withing brush min/max slope.
|
|
if (slope < SlopeMin || slope > SlopeMax)
|
|
continue;
|
|
|
|
p.x = x;
|
|
p.y = y;
|
|
float fScale = object->CalcVariableSize();
|
|
float placeRadius = fScale*object->GetObjectSize()*0.5f;
|
|
// Check if this place is empty.
|
|
if (!CanPlace( object,p,placeRadius ))
|
|
continue;
|
|
|
|
p.z = m_I3DEngine->GetTerrainElevation(p.x,p.y);
|
|
CVegetationInstance *obj = CreateObjInstance( object,p );
|
|
if (obj)
|
|
{
|
|
obj->scale = fScale;
|
|
m_I3DEngine->AddStaticObject( object->GetId(),p,fScale,obj->brightness );
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::PaintBrightness( float x,float y,float w,float h,uchar brightness,uchar brightness_shadowmap )
|
|
{
|
|
// Find sector range from world positions.
|
|
int startSectorX = ftoi(x*m_worldToSector);
|
|
int startSectorY = ftoi(y*m_worldToSector);
|
|
int endSectorX = ftoi((x + w)*m_worldToSector);
|
|
int endSectorY = ftoi((y + h)*m_worldToSector);
|
|
|
|
// Clamp start and end sectors to valid range.
|
|
if (startSectorX < 0)
|
|
startSectorX = 0;
|
|
if (startSectorY < 0)
|
|
startSectorY = 0;
|
|
if (endSectorX >= m_numSectors)
|
|
endSectorX = m_numSectors-1;
|
|
if (endSectorY >= m_numSectors)
|
|
endSectorY = m_numSectors-1;
|
|
|
|
float x2 = x+w;
|
|
float y2 = y+h;
|
|
|
|
// Iterate all sectors in range.
|
|
for (int sy = startSectorY; sy <= endSectorY; sy++)
|
|
{
|
|
for (int sx = startSectorX; sx <= endSectorX; sx++)
|
|
{
|
|
// Iterate all objects in sector.
|
|
SectorInfo *si = &m_sectors[sy*m_numSectors + sx];
|
|
if (!si->first)
|
|
continue;
|
|
for (CVegetationInstance *obj = si->first; obj; obj = obj->next)
|
|
{
|
|
if (obj->pos.x >= x && obj->pos.x < x2 && obj->pos.y >= y && obj->pos.y <= y2)
|
|
{
|
|
bool bNeedUpdate = false;
|
|
if (!obj->object->IsPrecalcShadow())
|
|
{
|
|
if (obj->brightness != brightness_shadowmap)
|
|
bNeedUpdate = true;
|
|
// If object is not casting precalculated shadow (small grass etc..) affect it by shadow map.
|
|
obj->brightness = brightness_shadowmap;
|
|
}
|
|
else
|
|
{
|
|
if (obj->brightness != brightness)
|
|
bNeedUpdate = true;
|
|
obj->brightness = brightness;
|
|
}
|
|
|
|
if (m_bUpdateOnPaintBrightness && bNeedUpdate)
|
|
{
|
|
RepaintInstance( obj );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::ClearBrush( CRect &rc,bool bCircle,CVegetationObject* object )
|
|
{
|
|
GetIEditor()->SetModifiedFlag();
|
|
|
|
Vec3 p(0,0,0);
|
|
|
|
int unitSize = m_heightmap->GetUnitSize();
|
|
|
|
int mapSize = m_numSectors*m_sectorSize;
|
|
|
|
// Intersect with map rectangle.
|
|
// Offset by 1 from each side.
|
|
float brushRadius2 = (rc.right-rc.left)*(rc.right-rc.left)/4;
|
|
float cx = (rc.right+rc.left)/2;
|
|
float cy = (rc.bottom+rc.top)/2;
|
|
|
|
float x1 = rc.left;
|
|
float y1 = rc.top;
|
|
float x2 = rc.right;
|
|
float y2 = rc.bottom;
|
|
|
|
// check all sectors intersected by radius.
|
|
int sx1 = ftoi(x1*m_worldToSector);
|
|
int sx2 = ftoi(x2*m_worldToSector);
|
|
int sy1 = ftoi(y1*m_worldToSector);
|
|
int sy2 = ftoi(y2*m_worldToSector);
|
|
sx1 = max(sx1,0);
|
|
sx2 = min(sx2,m_numSectors-1);
|
|
sy1 = max(sy1,0);
|
|
sy2 = min(sy2,m_numSectors-1);
|
|
|
|
CVegetationInstance *next = 0;
|
|
for (int y = sy1; y <= sy2; y++)
|
|
{
|
|
for (int x = sx1; x <= sx2; x++)
|
|
{
|
|
// For each sector check if any object is within this radius.
|
|
SectorInfo *si = GetSector(x,y);
|
|
if (si->first)
|
|
{
|
|
for (CVegetationInstance *obj = si->first; obj; obj = next)
|
|
{
|
|
next = obj->next;
|
|
if (obj->object != object && object != NULL)
|
|
continue;
|
|
|
|
if (bCircle)
|
|
{
|
|
// Skip objects outside the brush circle
|
|
if (((obj->pos.x-cx)*(obj->pos.x-cx) + (obj->pos.y-cy)*(obj->pos.y-cy)) > brushRadius2)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// Withing rectangle.
|
|
if (obj->pos.x < x1 || obj->pos.x > x2 || obj->pos.y < y1 || obj->pos.y > y2)
|
|
continue;
|
|
}
|
|
|
|
// Then delete object.
|
|
DeleteObjInstance( obj,si );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::ClearMask( const CString &maskFile )
|
|
{
|
|
// Swap x/y
|
|
Vec3 p(0,0,0);
|
|
|
|
CLayer layer;
|
|
layer.SetAutoGen(false);
|
|
layer.SetSmooth(false);
|
|
if (!layer.LoadMask( maskFile ))
|
|
{
|
|
CString str;
|
|
str.Format( "Error loading mask file %s",(const char*)maskFile );
|
|
AfxGetMainWnd()->MessageBox( str,"Warning",MB_OK|MB_ICONEXCLAMATION );
|
|
return;
|
|
}
|
|
int layerSize = m_numSectors;
|
|
layer.UpdateLayerMask16( 0,layerSize,layerSize,false );
|
|
if (!layer.IsValid())
|
|
return;
|
|
|
|
GetIEditor()->SetModifiedFlag();
|
|
CWaitProgress wait( "Clearing mask" );
|
|
|
|
for (int y = 0; y < layerSize; y++)
|
|
{
|
|
if (!wait.Step( 100*y/layerSize ))
|
|
break;
|
|
for (int x = 0; x < layerSize; x++)
|
|
{
|
|
if (layer.GetLayerMaskPoint(x,y) > MIN_MASK_VALUE)
|
|
{
|
|
// Find sector.
|
|
// Swap X/Y.
|
|
SectorInfo *si = &m_sectors[y + x*m_numSectors];
|
|
// Delete all instances in this sectors.
|
|
CVegetationInstance *next = 0;
|
|
for (CVegetationInstance *obj = si->first; obj; obj = next)
|
|
{
|
|
next = obj->next;
|
|
DeleteObjInstance( obj,si );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CVegetationObject* CVegetationMap::CreateObject( CVegetationObject *prev )
|
|
{
|
|
int id = -1;
|
|
// Generate New id.
|
|
// Cannot create more then 256 objects.
|
|
for (int i = 0; i < 256; i++)
|
|
{
|
|
if (m_usedIds.find(i) == m_usedIds.end())
|
|
{
|
|
id = i;
|
|
break;
|
|
}
|
|
}
|
|
if (id < 0)
|
|
{
|
|
// Free id not found, created more then 256 objects
|
|
AfxMessageBox( _T("Vegetation objects limit is reached.\r\nMaximum of 256 vegetation objects are supported."),MB_OK|MB_ICONWARNING );
|
|
return 0;
|
|
}
|
|
|
|
// Mark id as used.
|
|
m_usedIds.insert(id);
|
|
|
|
CVegetationObject *obj = new CVegetationObject( id,this );
|
|
if (prev)
|
|
obj->CopyFrom( *prev );
|
|
|
|
m_objects.push_back( obj );
|
|
return obj;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::RemoveObject( CVegetationObject *object )
|
|
{
|
|
// Free id for this object.
|
|
m_usedIds.erase( object->GetId() );
|
|
|
|
if (object->GetNumInstances() > 0)
|
|
{
|
|
CVegetationInstance *next = 0;
|
|
// Delete this object in sectors.
|
|
for (int i = 0; i < m_numSectors*m_numSectors; i++)
|
|
{
|
|
SectorInfo *si = &m_sectors[i];
|
|
// Iterate on every object in sector.
|
|
for (CVegetationInstance *obj = si->first; obj; obj = next)
|
|
{
|
|
next = obj->next;
|
|
if (obj->object == object)
|
|
{
|
|
DeleteObjInstance( obj,si );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Objects::iterator it = std::find(m_objects.begin(),m_objects.end(),object);
|
|
if (it != m_objects.end())
|
|
m_objects.erase( it );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::ReplaceObject( CVegetationObject *pOldObject,CVegetationObject *pNewObject )
|
|
{
|
|
if (pOldObject->GetNumInstances() > 0)
|
|
{
|
|
pNewObject->SetNumInstances( pNewObject->GetNumInstances() + pOldObject->GetNumInstances() );
|
|
CVegetationInstance *next = 0;
|
|
// Delete this object in sectors.
|
|
for (int i = 0; i < m_numSectors*m_numSectors; i++)
|
|
{
|
|
SectorInfo *si = &m_sectors[i];
|
|
// Iterate on every object in sector.
|
|
for (CVegetationInstance *obj = si->first; obj; obj = next)
|
|
{
|
|
next = obj->next;
|
|
if (obj->object == pOldObject)
|
|
{
|
|
obj->object = pNewObject;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
RemoveObject( pOldObject );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::ClearObjects()
|
|
{
|
|
ClearSectors();
|
|
m_objects.clear();
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::RepaintInstance( CVegetationInstance *obj )
|
|
{
|
|
m_I3DEngine->RemoveStaticObject( -1, obj->pos );
|
|
m_I3DEngine->AddStaticObject( obj->object->GetId(),obj->pos,obj->scale,obj->brightness );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::RepositionObject( CVegetationObject *object )
|
|
{
|
|
// Iterator over all sectors.
|
|
for (int i = 0; i < m_numSectors*m_numSectors; i++)
|
|
{
|
|
SectorInfo *si = &m_sectors[i];
|
|
// Iterate on every object in sector.
|
|
for (CVegetationInstance *obj = si->first; obj; obj = obj->next)
|
|
{
|
|
if (obj->object == object)
|
|
{
|
|
RepaintInstance( obj );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::ScaleObjectInstances( CVegetationObject *object,float fScale )
|
|
{
|
|
// Iterator over all sectors.
|
|
for (int i = 0; i < m_numSectors*m_numSectors; i++)
|
|
{
|
|
SectorInfo *si = &m_sectors[i];
|
|
// Iterate on every object in sector.
|
|
for (CVegetationInstance *obj = si->first; obj; obj = obj->next)
|
|
{
|
|
if (obj->object == object)
|
|
{
|
|
obj->scale *= fScale;
|
|
RepaintInstance( obj );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::Serialize( CXmlArchive &xmlAr )
|
|
{
|
|
int i;
|
|
if (xmlAr.bLoading)
|
|
{
|
|
CLogFile::WriteLine("Loading Vegetation Map...");
|
|
|
|
ClearObjects();
|
|
|
|
CWaitProgress progress( _T("Loading Static Objects") );
|
|
|
|
XmlNodeRef mainNode = xmlAr.root->findChild( "VegetationMap" );
|
|
if (mainNode)
|
|
{
|
|
XmlNodeRef objects = mainNode->findChild( "Objects" );
|
|
if (objects)
|
|
{
|
|
int numObjects = objects->getChildCount();
|
|
for (i = 0; i < numObjects; i++)
|
|
{
|
|
if (!progress.Step( 100*i/numObjects ))
|
|
break;
|
|
CVegetationObject *obj = CreateObject();
|
|
if (obj)
|
|
obj->Serialize( objects->getChild(i),xmlAr.bLoading );
|
|
}
|
|
}
|
|
}
|
|
|
|
SerializeInstances( xmlAr );
|
|
|
|
LoadOldStuff( xmlAr );
|
|
|
|
// Now display all objects on terrain.
|
|
PlaceObjectsOnTerrain();
|
|
}
|
|
else
|
|
{
|
|
// Storing
|
|
CLogFile::WriteLine("Saving Vegetation Map...");
|
|
|
|
//xmlAr.pNamedData->AddDataBlock( "StatObjectsArray",m_map.GetData(),m_width*m_height );
|
|
|
|
XmlNodeRef mainNode = xmlAr.root->newChild( "VegetationMap" );
|
|
|
|
// Save objects.
|
|
XmlNodeRef objects = mainNode->newChild( "Objects" );
|
|
for (i = 0; i < GetObjectCount(); i++)
|
|
{
|
|
XmlNodeRef obj = objects->newChild( "Object" );
|
|
GetObject(i)->Serialize(obj,xmlAr.bLoading);
|
|
}
|
|
|
|
// Store objects.
|
|
SerializeInstances( xmlAr );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::SerializeInstances( CXmlArchive &xmlAr,CRect *saveRect )
|
|
{
|
|
int i;
|
|
if (xmlAr.bLoading)
|
|
{
|
|
Vec3 posofs(0,0,0);
|
|
// Loading.
|
|
if (!saveRect)
|
|
{
|
|
ClearSectors();
|
|
}
|
|
else
|
|
{
|
|
posofs.x = saveRect->left;
|
|
posofs.y = saveRect->top;
|
|
}
|
|
|
|
int numObjects = m_objects.size();
|
|
int arraySize;
|
|
void *pData = 0;
|
|
if (!xmlAr.pNamedData->GetDataBlock( "VegetationInstancesArray",pData,arraySize ))
|
|
return;
|
|
SVegInst *array = (SVegInst*)pData;
|
|
int numInst = arraySize / sizeof(SVegInst);
|
|
for (int i = 0; i < numInst; i++)
|
|
{
|
|
if (array[i].objectIndex >= numObjects)
|
|
continue;
|
|
CVegetationObject *object = m_objects[array[i].objectIndex];
|
|
CVegetationInstance *obj = CreateObjInstance( object,array[i].pos+posofs );
|
|
if (obj)
|
|
{
|
|
obj->scale = array[i].scale;
|
|
obj->brightness = array[i].brightness;
|
|
obj->flags = array[i].flags;
|
|
}
|
|
}
|
|
|
|
//assert( m_numInstances == numInst );
|
|
}
|
|
else
|
|
{
|
|
// Saving.
|
|
if (m_numInstances == 0)
|
|
return;
|
|
int arraySize = sizeof(SVegInst)*m_numInstances;
|
|
SVegInst *array = (SVegInst*)malloc( arraySize );
|
|
|
|
int k = 0;
|
|
|
|
// Assign indices to objects.
|
|
for (i = 0; i < GetObjectCount(); i++)
|
|
{
|
|
GetObject(i)->SetIndex(i);
|
|
}
|
|
|
|
float x1,y1,x2,y2;
|
|
if (saveRect)
|
|
{
|
|
x1 = saveRect->left;
|
|
y1 = saveRect->top;
|
|
x2 = saveRect->right;
|
|
y2 = saveRect->bottom;
|
|
}
|
|
|
|
// Fill array.
|
|
for (i = 0; i < m_numSectors*m_numSectors; i++)
|
|
{
|
|
SectorInfo *si = &m_sectors[i];
|
|
// Iterate on every object in sector.
|
|
for (CVegetationInstance *obj = si->first; obj; obj = obj->next)
|
|
{
|
|
if (saveRect)
|
|
{
|
|
if (obj->pos.x < x1 || obj->pos.x > x2 || obj->pos.y < y1 || obj->pos.y > y2)
|
|
continue;
|
|
}
|
|
array[k].pos = obj->pos;
|
|
array[k].scale = obj->scale;
|
|
array[k].brightness = obj->brightness;
|
|
array[k].flags = obj->flags;
|
|
array[k].objectIndex = obj->object->GetIndex();
|
|
k++;
|
|
}
|
|
}
|
|
// Save this array.
|
|
xmlAr.pNamedData->AddDataBlock( "VegetationInstancesArray",array,k*sizeof(SVegInst) );
|
|
|
|
free( array );
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::LoadOldStuff( CXmlArchive &xmlAr )
|
|
{
|
|
XmlNodeRef mainNode = xmlAr.root->findChild( "VegetationMap" );
|
|
if (!mainNode)
|
|
return;
|
|
|
|
int i;
|
|
// Backward compatability loading of brushes.
|
|
XmlNodeRef brushes = mainNode->findChild( "Brushes" );
|
|
if (brushes)
|
|
{
|
|
for (i = 0; i < brushes->getChildCount(); i++)
|
|
{
|
|
CVegetationBrush br;
|
|
br.Serialize( brushes->getChild(i),xmlAr.bLoading );
|
|
}
|
|
}
|
|
|
|
void *pData = 0;
|
|
int nSize;
|
|
if (xmlAr.pNamedData->GetDataBlock( "StatObjectsArray",pData,nSize ))
|
|
{
|
|
if (nSize != 2048*2048)
|
|
return;
|
|
|
|
int numObjects = m_objects.size();
|
|
CVegetationObject* usedObjects[256];
|
|
ZeroStruct(usedObjects);
|
|
|
|
for (i = 0; i < m_objects.size(); i++)
|
|
{
|
|
usedObjects[m_objects[i]->GetIndex()] = m_objects[i];
|
|
}
|
|
|
|
|
|
Vec3 pos;
|
|
int i = 0;
|
|
uchar *pMap = (uchar*)pData;
|
|
for (int y = 0; y < 2048; y++)
|
|
{
|
|
for (int x = 0; x < 2048; x++,i++)
|
|
{
|
|
i = x + 2048*y;
|
|
if (!pMap[i])
|
|
continue;
|
|
|
|
unsigned int objIndex = pMap[i] - 1;
|
|
if (!usedObjects[objIndex])
|
|
continue;
|
|
|
|
//Swap x/y
|
|
pos.x = y;
|
|
pos.y = x;
|
|
pos.z = m_I3DEngine->GetTerrainElevation(pos.x,pos.y);
|
|
CVegetationInstance *obj = CreateObjInstance( usedObjects[objIndex],pos );
|
|
if (obj)
|
|
{
|
|
obj->scale = usedObjects[objIndex]->CalcVariableSize();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//! Generate shadows from static objects and place them in shadow map bitarray.
|
|
void CVegetationMap::GenerateShadowMap( CByteImage &shadowmap,float shadowAmmount,const Vec3 &sunVector )
|
|
{
|
|
// if(!m_pTerrain)
|
|
// return;
|
|
|
|
int width = shadowmap.GetWidth();
|
|
int height = shadowmap.GetHeight();
|
|
int i =0;
|
|
|
|
//@FIXME: Hardcoded.
|
|
int sectorSizeInMeters = 64;
|
|
|
|
int unitSize = m_heightmap->GetUnitSize();
|
|
int numSectors = (m_heightmap->GetWidth() * unitSize) / sectorSizeInMeters;
|
|
|
|
int sectorSize = shadowmap.GetWidth()/numSectors;
|
|
int sectorSize2 = sectorSize*2;
|
|
assert( sectorSize > 0 );
|
|
|
|
uint shadowValue = shadowAmmount;
|
|
|
|
bool bProgress = width >= 2048;
|
|
CWaitProgress wait( "Calculating Objects Shadows",bProgress );
|
|
|
|
unsigned char *sectorImage = (unsigned char*)malloc(sectorSize*sectorSize*3);
|
|
unsigned char *sectorImage2 = (unsigned char*)malloc(sectorSize2*sectorSize2*3);
|
|
|
|
for (int y = 0; y < numSectors; y++)
|
|
{
|
|
if (bProgress)
|
|
{
|
|
if (!wait.Step( y*100/numSectors ))
|
|
break;
|
|
}
|
|
|
|
for (int x = 0; x < numSectors; x++)
|
|
{
|
|
m_I3DEngine->MakeSectorLightMap( y*sectorSizeInMeters,x*sectorSizeInMeters,sectorImage2,sectorSize2 );
|
|
|
|
// Scale sector image down and store into the shadow map.
|
|
{
|
|
int pos;
|
|
uint color;
|
|
int x1 = x*sectorSize;
|
|
int y1 = y*sectorSize;
|
|
for (int j = 0; j < sectorSize; j++)
|
|
{
|
|
int sx1 = x1+(sectorSize-j-1);
|
|
for (int i = 0; i < sectorSize; i++)
|
|
{
|
|
pos = (i + j*sectorSize2)*2*3;
|
|
color = (shadowValue*
|
|
((uint)
|
|
(255-sectorImage2[pos]) +
|
|
(255-sectorImage2[pos+3]) +
|
|
(255-sectorImage2[pos+sectorSize2*3]) +
|
|
(255-sectorImage2[pos+sectorSize2*3+3])
|
|
)) >> 10;
|
|
// color = color*shadowValue >> 8;
|
|
// swap x/y
|
|
//color = (255-sectorImage2[(i+j*sectorSize)*3]);
|
|
shadowmap.ValueAt(sx1,y1+i) = color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
free( sectorImage );
|
|
free( sectorImage2 );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CVegetationMap::ExportObject( CVegetationObject *object,XmlNodeRef &node,CRect *saveRect )
|
|
{
|
|
int numSaved = 0;
|
|
assert( object != 0 );
|
|
object->Serialize( node,false );
|
|
if (object->GetNumInstances() > 0)
|
|
{
|
|
float x1,y1,x2,y2;
|
|
if (saveRect)
|
|
{
|
|
x1 = saveRect->left;
|
|
y1 = saveRect->top;
|
|
x2 = saveRect->right;
|
|
y2 = saveRect->bottom;
|
|
}
|
|
// Export instances.
|
|
XmlNodeRef instancesNode = node->newChild("Instances");
|
|
// Iterator over all sectors.
|
|
for (int i = 0; i < m_numSectors*m_numSectors; i++)
|
|
{
|
|
// Iterate on every object in sector.
|
|
for (CVegetationInstance *obj = m_sectors[i].first; obj; obj = obj->next)
|
|
{
|
|
if (obj->object == object)
|
|
{
|
|
if (saveRect)
|
|
{
|
|
if (obj->pos.x < x1 || obj->pos.x > x2 || obj->pos.y < y1 || obj->pos.y > y2)
|
|
continue;
|
|
}
|
|
numSaved++;
|
|
XmlNodeRef inst = instancesNode->newChild( "Instance" );
|
|
inst->setAttr( "Pos",obj->pos );
|
|
if (obj->scale != 1)
|
|
inst->setAttr( "Scale",obj->scale );
|
|
if (obj->brightness != 255)
|
|
inst->setAttr( "Brightness",(int)obj->brightness );
|
|
if (obj->flags != 0)
|
|
inst->setAttr( "Flags",(int)obj->flags );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return numSaved;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::ImportObject( XmlNodeRef &node,const Vec3& offset )
|
|
{
|
|
CVegetationObject *object = CreateObject();
|
|
if (!object)
|
|
return;
|
|
object->Serialize( node,true );
|
|
|
|
// Check if object with this GUID. already exists.
|
|
for (int i = 0; i < GetObjectCount(); i++)
|
|
{
|
|
CVegetationObject *pOldObject = GetObject(i);
|
|
if (pOldObject == object)
|
|
continue;
|
|
if (pOldObject->GetGUID() == object->GetGUID())
|
|
{
|
|
ReplaceObject( pOldObject,object );
|
|
/*
|
|
// 2 objects have same GUID;
|
|
CString msg;
|
|
msg.Format( _T("Vegetation Object %s %s already exists in the level!.\r\nOverride existing object?"),
|
|
GuidUtil::ToString(pOldObject->GetGUID()),(const char*)pOldObject->GetFileName() );
|
|
|
|
if (AfxMessageBox( msg,MB_YESNO|MB_ICONWARNING ) == IDYES)
|
|
{
|
|
ReplaceObject( pOldObject,object );
|
|
}
|
|
else
|
|
{
|
|
RemoveObject( object );
|
|
object = pOldObject;
|
|
}
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
|
|
Vec3 pos(0,0,0);
|
|
float scale = 1;
|
|
int brightness = 255;
|
|
int flags = 0;
|
|
|
|
XmlNodeRef instancesNode = node->findChild("Instances");
|
|
if (instancesNode)
|
|
{
|
|
int numChilds = instancesNode->getChildCount();
|
|
for (int i = 0; i < numChilds; i++)
|
|
{
|
|
pos.Set(0,0,0);
|
|
scale = 1;
|
|
brightness = 255;
|
|
flags = 0;
|
|
|
|
XmlNodeRef inst = instancesNode->getChild(i);
|
|
inst->getAttr( "Pos",pos );
|
|
inst->getAttr( "Scale",scale );
|
|
inst->getAttr( "Brightness",brightness );
|
|
inst->getAttr( "Flags",flags );
|
|
|
|
pos += offset;
|
|
CVegetationInstance *obj = GetNearestInstance( pos,0.01f );
|
|
if (obj && obj->pos == pos)
|
|
{
|
|
// Delete pevious object at same position.
|
|
DeleteObjInstance( obj );
|
|
}
|
|
obj = CreateObjInstance( object,pos );
|
|
if (obj)
|
|
{
|
|
obj->scale = scale;
|
|
obj->brightness = brightness;
|
|
obj->flags = flags;
|
|
// Stick vegetation to terrain.
|
|
obj->pos.z = m_I3DEngine->GetTerrainElevation(obj->pos.x,obj->pos.y);
|
|
}
|
|
}
|
|
}
|
|
RepositionObject(object);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::DrawToTexture( uint *texture,int texWidth,int texHeight,int srcX,int srcY )
|
|
{
|
|
assert( texture != 0 );
|
|
|
|
for (int y = 0; y < texHeight; y++)
|
|
{
|
|
int trgYOfs = y*texWidth;
|
|
for (int x = 0; x < texWidth; x++)
|
|
{
|
|
int sx = x + srcX;
|
|
int sy = y + srcY;
|
|
// Swap X/Y
|
|
SectorInfo *si = &m_sectors[sy + sx*m_numSectors];
|
|
if (si->first)
|
|
texture[x+trgYOfs] = 0xFFFFFFFF;
|
|
else
|
|
texture[x+trgYOfs] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CVegetationMap::WorldToSector( float worldCoord ) const
|
|
{
|
|
return ftoi(worldCoord*m_worldToSector);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::UnloadObjectsGeometry()
|
|
{
|
|
for (int i = 0; i < GetObjectCount(); i++)
|
|
{
|
|
GetObject(i)->UnloadObject();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::ExportBlock( const CRect &subRc,CXmlArchive &ar )
|
|
{
|
|
XmlNodeRef mainNode = ar.root->newChild( "VegetationMap" );
|
|
mainNode->setAttr( "X1",subRc.left );
|
|
mainNode->setAttr( "Y1",subRc.top );
|
|
mainNode->setAttr( "X2",subRc.right );
|
|
mainNode->setAttr( "Y2",subRc.bottom );
|
|
|
|
CRect rect = subRc;
|
|
for (int i = 0; i < GetObjectCount(); i++)
|
|
{
|
|
XmlNodeRef vegObjNode = mainNode->createNode( "VegetationObject" );
|
|
CVegetationObject *pObject = GetObject(i);
|
|
int numSaved = ExportObject( pObject,vegObjNode,&rect );
|
|
if (numSaved > 0)
|
|
{
|
|
mainNode->addChild( vegObjNode );
|
|
}
|
|
}
|
|
|
|
//SerializeInstances( ar,&rect );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::ImportBlock( CXmlArchive &ar,CPoint placeOffset )
|
|
{
|
|
XmlNodeRef mainNode = ar.root->findChild( "VegetationMap" );
|
|
if (!mainNode)
|
|
return;
|
|
|
|
CRect subRc( 0,0,1,1 );
|
|
mainNode->getAttr( "X1",subRc.left );
|
|
mainNode->getAttr( "Y1",subRc.top );
|
|
mainNode->getAttr( "X2",subRc.right );
|
|
mainNode->getAttr( "Y2",subRc.bottom );
|
|
|
|
subRc.OffsetRect(placeOffset);
|
|
|
|
// Clear all vegetation instances in this rectangle.
|
|
ClearBrush( subRc,false,NULL );
|
|
|
|
// Serialize vegitation objects.
|
|
for (int i = 0; i < mainNode->getChildCount(); i++)
|
|
{
|
|
XmlNodeRef vegObjNode = mainNode->getChild(i);
|
|
ImportObject( vegObjNode,Vec3(placeOffset.x,placeOffset.y,0) );
|
|
}
|
|
// Clear object in this rectangle.
|
|
/*
|
|
CRect rect(placeOffset.x,placeOffset.y,placeOffset.x,placeOffset.y);
|
|
SerializeInstances( ar,&rect );
|
|
*/
|
|
|
|
// Now display all objects on terrain.
|
|
PlaceObjectsOnTerrain();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::RecordUndo( CVegetationInstance *obj )
|
|
{
|
|
if (CUndo::IsRecording())
|
|
CUndo::Record( new CUndoVegInstance(obj) );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CVegetationMap::Validate( CErrorReport &report )
|
|
{
|
|
for (int i = 0; i < GetObjectCount(); i++)
|
|
{
|
|
GetObject(i)->Validate( report );
|
|
}
|
|
} |