Atlas: Terrain painting
This was SVN commit r3092.
This commit is contained in:
parent
a679206ca1
commit
faaee7d1b5
@ -407,15 +407,6 @@ float CTerrain::FlattenArea(float x0,float x1,float z0,float z1)
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void CTerrain::RaiseVertex(int x, int z, int amount)
|
|
||||||
{
|
|
||||||
// Ignore out-of-bounds vertices
|
|
||||||
if ((unsigned)x >= m_MapSize || (unsigned)z >= m_MapSize)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_Heightmap[x + z*m_MapSize] = (u16)clamp(m_Heightmap[x + z*m_MapSize] + amount, 0, 65535);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CTerrain::MakeDirty(int x0, int z0, int x1, int z1)
|
void CTerrain::MakeDirty(int x0, int z0, int x1, int z1)
|
||||||
{
|
{
|
||||||
// flag vertex data as dirty for affected patches, and rebuild bounds of these patches
|
// flag vertex data as dirty for affected patches, and rebuild bounds of these patches
|
||||||
@ -431,3 +422,14 @@ void CTerrain::MakeDirty(int x0, int z0, int x1, int z1)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CTerrain::MakeDirty()
|
||||||
|
{
|
||||||
|
for (u32 j = 0; j < m_MapSizePatches; j++) {
|
||||||
|
for (u32 i = 0; i < m_MapSizePatches; i++) {
|
||||||
|
CPatch* patch = GetPatch(i,j);
|
||||||
|
patch->CalcBounds();
|
||||||
|
patch->SetDirty(RENDERDATA_UPDATE_VERTICES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -68,11 +68,10 @@ public:
|
|||||||
// the average height of the flattened area
|
// the average height of the flattened area
|
||||||
float FlattenArea(float x0,float x1,float z0,float z1);
|
float FlattenArea(float x0,float x1,float z0,float z1);
|
||||||
|
|
||||||
// raise a given vertex, clamped to min/max height; ignored if out of bounds
|
|
||||||
void RaiseVertex(int x, int y, int amount);
|
|
||||||
|
|
||||||
// mark a specific square of tiles as dirty - use this after modifying the heightmap
|
// mark a specific square of tiles as dirty - use this after modifying the heightmap
|
||||||
void MakeDirty(int x0, int z0, int x1, int z1);
|
void MakeDirty(int x0, int z0, int x1, int z1);
|
||||||
|
// mark the entire map as dirty
|
||||||
|
void MakeDirty();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// delete any data allocated by this terrain
|
// delete any data allocated by this terrain
|
||||||
|
@ -236,6 +236,7 @@ extern float fmaxf(float a, float b);
|
|||||||
# define STL_HASH_MULTIMAP __gnu_cxx::hash_multimap
|
# define STL_HASH_MULTIMAP __gnu_cxx::hash_multimap
|
||||||
# define STL_HASH_SET __gnu_cxx::hash_set
|
# define STL_HASH_SET __gnu_cxx::hash_set
|
||||||
# define STL_HASH_MULTISET __gnu_cxx::hash_multiset
|
# define STL_HASH_MULTISET __gnu_cxx::hash_multiset
|
||||||
|
# define STL_HASH_VALUE __gnu_cxx::hash
|
||||||
# define STL_SLIST __gnu_cxx::slist
|
# define STL_SLIST __gnu_cxx::slist
|
||||||
|
|
||||||
// Hack: GCC Doesn't have a hash instance for std::string included (and it looks
|
// Hack: GCC Doesn't have a hash instance for std::string included (and it looks
|
||||||
@ -260,12 +261,14 @@ namespace __gnu_cxx
|
|||||||
# define STL_HASH_MULTIMAP stdext::hash_multimap
|
# define STL_HASH_MULTIMAP stdext::hash_multimap
|
||||||
# define STL_HASH_SET stdext::hash_set
|
# define STL_HASH_SET stdext::hash_set
|
||||||
# define STL_HASH_MULTISET stdext::hash_multiset
|
# define STL_HASH_MULTISET stdext::hash_multiset
|
||||||
|
# define STL_HASH_VALUE stdext::hash_value
|
||||||
// VC6 and anything else (most likely name)
|
// VC6 and anything else (most likely name)
|
||||||
# else
|
# else
|
||||||
# define STL_HASH_MAP std::hash_map
|
# define STL_HASH_MAP std::hash_map
|
||||||
# define STL_HASH_MULTIMAP std::hash_multimap
|
# define STL_HASH_MULTIMAP std::hash_multimap
|
||||||
# define STL_HASH_SET std::hash_set
|
# define STL_HASH_SET std::hash_set
|
||||||
# define STL_HASH_MULTISET std::hash_multiset
|
# define STL_HASH_MULTISET std::hash_multiset
|
||||||
|
# define STL_HASH_VALUE std::hash_value
|
||||||
# endif // MSC_VERSION >= 1300
|
# endif // MSC_VERSION >= 1300
|
||||||
#endif // !__GNUC__
|
#endif // !__GNUC__
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "General/Datafile.h"
|
#include "General/Datafile.h"
|
||||||
#include "ScenarioEditor/Tools/Common/Brushes.h"
|
#include "ScenarioEditor/Tools/Common/Brushes.h"
|
||||||
#include "ScenarioEditor/Tools/Common/Tools.h"
|
#include "ScenarioEditor/Tools/Common/Tools.h"
|
||||||
|
#include "ScenarioEditor/Tools/Common/MiscState.h"
|
||||||
|
|
||||||
#include "GameInterface/Messages.h"
|
#include "GameInterface/Messages.h"
|
||||||
|
|
||||||
@ -22,8 +23,9 @@ TerrainSidebar::TerrainSidebar(wxWindow* parent)
|
|||||||
{
|
{
|
||||||
wxSizer* sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Elevation tools"));
|
wxSizer* sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Elevation tools"));
|
||||||
sizer->Add(new ToolButton(this, _("Modify"), _T("AlterElevation"), wxSize(50,20)));
|
sizer->Add(new ToolButton(this, _("Modify"), _T("AlterElevation"), wxSize(50,20)));
|
||||||
sizer->Add(new ToolButton(this, _("Smooth"), _T(""), wxSize(50,20)));
|
// sizer->Add(new ToolButton(this, _("Smooth"), _T(""), wxSize(50,20)));
|
||||||
sizer->Add(new ToolButton(this, _("Sample"), _T(""), wxSize(50,20)));
|
// sizer->Add(new ToolButton(this, _("Sample"), _T(""), wxSize(50,20)));
|
||||||
|
sizer->Add(new ToolButton(this, _("Paint"), _T("PaintTerrain"), wxSize(50,20)));
|
||||||
m_MainSizer->Add(sizer);
|
m_MainSizer->Add(sizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,17 +56,19 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnSelected()
|
void OnDisplay()
|
||||||
{
|
{
|
||||||
if (m_Loaded)
|
if (m_Loaded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const int imageWidth = 64;
|
m_TextureNames.Clear();
|
||||||
const int imageHeight = 32;
|
|
||||||
|
|
||||||
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
wxListCtrl* list = new wxListCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_ICON | wxLC_SINGLE_SEL | wxLC_AUTOARRANGE);
|
wxListCtrl* list = new wxListCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_ICON | wxLC_SINGLE_SEL | wxLC_AUTOARRANGE);
|
||||||
|
|
||||||
|
const int imageWidth = 64;
|
||||||
|
const int imageHeight = 32;
|
||||||
wxImageList* imglist = new wxImageList(imageWidth, imageHeight, false, 0);
|
wxImageList* imglist = new wxImageList(imageWidth, imageHeight, false, 0);
|
||||||
|
|
||||||
AtlasMessage::qGetTerrainGroupPreviews qry(m_Name.c_str(), imageWidth, imageHeight);
|
AtlasMessage::qGetTerrainGroupPreviews qry(m_Name.c_str(), imageWidth, imageHeight);
|
||||||
@ -76,12 +80,20 @@ public:
|
|||||||
wxImage img (imageWidth, imageHeight, it->imagedata);
|
wxImage img (imageWidth, imageHeight, it->imagedata);
|
||||||
imglist->Add(wxBitmap(img));
|
imglist->Add(wxBitmap(img));
|
||||||
|
|
||||||
// Add spaces into the name, so Windows doesn't just say
|
wxListItem item;
|
||||||
// "grass_..." in the list ctrl for every terrain
|
|
||||||
wxString name = it->name.c_str();
|
|
||||||
name.Replace(_T("_"), _T(" "));
|
|
||||||
|
|
||||||
list->InsertItem(i, name, i);
|
wxString name = it->name.c_str();
|
||||||
|
m_TextureNames.Add(name);
|
||||||
|
// Add spaces into the displayed name, so Windows doesn't just say
|
||||||
|
// "grass_..." in the list ctrl for every terrain
|
||||||
|
name.Replace(_T("_"), _T(" "));
|
||||||
|
item.SetText(name);
|
||||||
|
|
||||||
|
item.SetData(i);
|
||||||
|
item.SetId(i);
|
||||||
|
item.SetImage(i);
|
||||||
|
|
||||||
|
list->InsertItem(item);
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
list->AssignImageList(imglist, wxIMAGE_LIST_NORMAL);
|
list->AssignImageList(imglist, wxIMAGE_LIST_NORMAL);
|
||||||
@ -93,11 +105,24 @@ public:
|
|||||||
m_Loaded = true;
|
m_Loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OnSelect(wxListEvent& evt)
|
||||||
|
{
|
||||||
|
g_SelectedTexture = m_TextureNames[evt.GetData()];
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_Loaded;
|
bool m_Loaded;
|
||||||
wxString m_Name;
|
wxString m_Name;
|
||||||
|
wxArrayString m_TextureNames;
|
||||||
|
|
||||||
|
DECLARE_EVENT_TABLE();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BEGIN_EVENT_TABLE(TextureNotebookPage, wxPanel)
|
||||||
|
EVT_LIST_ITEM_SELECTED(wxID_ANY, TextureNotebookPage::OnSelect)
|
||||||
|
END_EVENT_TABLE();
|
||||||
|
|
||||||
|
|
||||||
class TextureNotebook : public wxNotebook
|
class TextureNotebook : public wxNotebook
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -124,7 +149,7 @@ protected:
|
|||||||
{
|
{
|
||||||
if (event.GetSelection() != -1)
|
if (event.GetSelection() != -1)
|
||||||
{
|
{
|
||||||
static_cast<TextureNotebookPage*>(GetPage(event.GetSelection()))->OnSelected();
|
static_cast<TextureNotebookPage*>(GetPage(event.GetSelection()))->OnDisplay();
|
||||||
}
|
}
|
||||||
event.Skip();
|
event.Skip();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
#include "MiscState.h"
|
||||||
|
|
||||||
|
wxString g_SelectedTexture = _T("grass1_spring");
|
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef MISCSTATE_H__
|
||||||
|
#define MISCSTATE_H__
|
||||||
|
|
||||||
|
extern wxString g_SelectedTexture;
|
||||||
|
|
||||||
|
#endif // MISCSTATE_H__
|
122
source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PaintTerrain.cpp
Normal file
122
source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PaintTerrain.cpp
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
#include "Common/Tools.h"
|
||||||
|
#include "Common/Brushes.h"
|
||||||
|
#include "Common/MiscState.h"
|
||||||
|
#include "GameInterface/Messages.h"
|
||||||
|
|
||||||
|
using AtlasMessage::Position;
|
||||||
|
|
||||||
|
class PaintTerrain : public StateDrivenTool<PaintTerrain>
|
||||||
|
{
|
||||||
|
DECLARE_DYNAMIC_CLASS(PaintTerrain);
|
||||||
|
|
||||||
|
Position m_Pos;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PaintTerrain()
|
||||||
|
{
|
||||||
|
SetState(&Waiting);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OnEnable(PaintTerrain*)
|
||||||
|
{
|
||||||
|
// TODO: multiple independent brushes?
|
||||||
|
g_Brush_Elevation.MakeActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDisable(PaintTerrain*)
|
||||||
|
{
|
||||||
|
POST_MESSAGE(BrushPreview(false, Position()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct sWaiting : public State
|
||||||
|
{
|
||||||
|
bool OnMouse(PaintTerrain* obj, wxMouseEvent& evt)
|
||||||
|
{
|
||||||
|
if (evt.LeftDown())
|
||||||
|
{
|
||||||
|
obj->m_Pos = Position(evt.GetPosition());
|
||||||
|
SET_STATE(PaintingHigh);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (evt.RightDown())
|
||||||
|
{
|
||||||
|
obj->m_Pos = Position(evt.GetPosition());
|
||||||
|
SET_STATE(PaintingLow);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (evt.Moving())
|
||||||
|
{
|
||||||
|
POST_MESSAGE(BrushPreview(true, Position(evt.GetPosition())));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Waiting;
|
||||||
|
|
||||||
|
|
||||||
|
struct sPainting_common : public State
|
||||||
|
{
|
||||||
|
void OnEnter(PaintTerrain* obj)
|
||||||
|
{
|
||||||
|
Paint(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnLeave(PaintTerrain*)
|
||||||
|
{
|
||||||
|
ScenarioEditor::GetCommandProc().FinaliseLastCommand();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OnMouse(PaintTerrain* obj, wxMouseEvent& evt)
|
||||||
|
{
|
||||||
|
if (IsMouseUp(evt))
|
||||||
|
{
|
||||||
|
SET_STATE(Waiting);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (evt.Dragging())
|
||||||
|
{
|
||||||
|
wxPoint pos = evt.GetPosition();
|
||||||
|
obj->m_Pos = Position(pos);
|
||||||
|
Paint(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Paint(PaintTerrain* obj)
|
||||||
|
{
|
||||||
|
POST_MESSAGE(BrushPreview(true, obj->m_Pos));
|
||||||
|
POST_COMMAND(PaintTerrain, (obj->m_Pos, g_SelectedTexture.c_str(), GetPriority()));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool IsMouseUp(wxMouseEvent& evt) = 0;
|
||||||
|
virtual int GetPriority() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sPaintingHigh : public sPainting_common
|
||||||
|
{
|
||||||
|
bool IsMouseUp(wxMouseEvent& evt) { return evt.LeftUp(); }
|
||||||
|
int GetPriority() { return AtlasMessage::ePaintTerrainPriority::HIGH; }
|
||||||
|
}
|
||||||
|
PaintingHigh;
|
||||||
|
|
||||||
|
struct sPaintingLow : public sPainting_common
|
||||||
|
{
|
||||||
|
bool IsMouseUp(wxMouseEvent& evt) { return evt.RightUp(); }
|
||||||
|
int GetPriority() { return AtlasMessage::ePaintTerrainPriority::LOW; }
|
||||||
|
}
|
||||||
|
PaintingLow;
|
||||||
|
};
|
||||||
|
|
||||||
|
IMPLEMENT_DYNAMIC_CLASS(PaintTerrain, StateDrivenTool<PaintTerrain>);
|
90
source/tools/atlas/GameInterface/DeltaArray.h
Normal file
90
source/tools/atlas/GameInterface/DeltaArray.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#ifndef DELTAARRAY_H__
|
||||||
|
#define DELTAARRAY_H__
|
||||||
|
|
||||||
|
template<typename T> class DeltaArray2D
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
T get(int x, int y);
|
||||||
|
void set(int x, int y, const T& val);
|
||||||
|
|
||||||
|
void OverlayWith(const DeltaArray2D<T>& overlayer);
|
||||||
|
void Undo();
|
||||||
|
void Redo();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual T getOld(int x, int y) = 0;
|
||||||
|
virtual void setNew(int x, int y, const T& val) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct hashfunc {
|
||||||
|
enum {
|
||||||
|
bucket_size = 4,
|
||||||
|
min_buckets = 8
|
||||||
|
};
|
||||||
|
size_t operator()(const std::pair<int, int>& p) const {
|
||||||
|
return STL_HASH_VALUE(p.first << 16) + STL_HASH_VALUE(p.second);
|
||||||
|
}
|
||||||
|
bool operator()(const std::pair<int, int>& a, const std::pair<int, int>& b) const {
|
||||||
|
return (a < b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// TODO: more efficient representation
|
||||||
|
typedef STL_HASH_MAP<std::pair<int, int>, std::pair<T, T>, hashfunc> Data; // map of <x,y> -> <old_val, new_val>
|
||||||
|
Data m_Data;
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T DeltaArray2D<T>::get(int x, int y)
|
||||||
|
{
|
||||||
|
Data::iterator it = m_Data.find(std::make_pair(x, y));
|
||||||
|
if (it == m_Data.end())
|
||||||
|
return getOld(x, y);
|
||||||
|
else
|
||||||
|
return it->second.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void DeltaArray2D<T>::set(int x, int y, const T& val)
|
||||||
|
{
|
||||||
|
Data::iterator it = m_Data.find(std::make_pair(x, y));
|
||||||
|
if (it == m_Data.end())
|
||||||
|
m_Data.insert(std::make_pair(std::make_pair(x, y), std::make_pair(getOld(x, y), val)));
|
||||||
|
else
|
||||||
|
it->second.second = val;
|
||||||
|
setNew(x, y, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void DeltaArray2D<T>::OverlayWith(const DeltaArray2D<T>& overlayer)
|
||||||
|
{
|
||||||
|
for (Data::const_iterator it = overlayer.m_Data.begin(); it != overlayer.m_Data.end(); ++it)
|
||||||
|
{
|
||||||
|
Data::iterator it2 = m_Data.find(it->first);
|
||||||
|
if (it2 == m_Data.end())
|
||||||
|
m_Data.insert(*it);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//debug_assert(it2->second.second == it->second.first);
|
||||||
|
it2->second.second = it->second.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void DeltaArray2D<T>::Undo()
|
||||||
|
{
|
||||||
|
for (Data::iterator it = m_Data.begin(); it != m_Data.end(); ++it)
|
||||||
|
setNew(it->first.first, it->first.second, it->second.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void DeltaArray2D<T>::Redo()
|
||||||
|
{
|
||||||
|
for (Data::iterator it = m_Data.begin(); it != m_Data.end(); ++it)
|
||||||
|
setNew(it->first.first, it->first.second, it->second.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DELTAARRAY_H__
|
@ -6,37 +6,60 @@
|
|||||||
|
|
||||||
#include "graphics/Terrain.h"
|
#include "graphics/Terrain.h"
|
||||||
#include "ps/Game.h"
|
#include "ps/Game.h"
|
||||||
|
#include "maths/MathUtil.h"
|
||||||
|
|
||||||
#include "../Brushes.h"
|
#include "../Brushes.h"
|
||||||
|
#include "../DeltaArray.h"
|
||||||
|
|
||||||
namespace AtlasMessage {
|
namespace AtlasMessage {
|
||||||
|
|
||||||
|
|
||||||
BEGIN_COMMAND(AlterElevation)
|
BEGIN_COMMAND(AlterElevation)
|
||||||
|
|
||||||
// TODO: much more efficient version of this, and without the memory leaks
|
class TerrainArray : public DeltaArray2D<u16>
|
||||||
u16* OldTerrain;
|
{
|
||||||
u16* NewTerrain;
|
public:
|
||||||
|
void Init()
|
||||||
|
{
|
||||||
|
m_Heightmap = g_Game->GetWorld()->GetTerrain()->GetHeightMap();
|
||||||
|
m_VertsPerSide = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RaiseVertex(int x, int y, int amount)
|
||||||
|
{
|
||||||
|
// Ignore out-of-bounds vertices
|
||||||
|
if ((unsigned)x >= m_VertsPerSide || (unsigned)y >= m_VertsPerSide)
|
||||||
|
return;
|
||||||
|
|
||||||
|
set(x,y, (u16)clamp(get(x,y) + amount, 0, 65535));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
u16 getOld(int x, int y)
|
||||||
|
{
|
||||||
|
return m_Heightmap[y*m_VertsPerSide + x];
|
||||||
|
}
|
||||||
|
void setNew(int x, int y, const u16& val)
|
||||||
|
{
|
||||||
|
m_Heightmap[y*m_VertsPerSide + x] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
u16* m_Heightmap;
|
||||||
|
size_t m_VertsPerSide;
|
||||||
|
};
|
||||||
|
|
||||||
|
TerrainArray m_TerrainDelta;
|
||||||
|
|
||||||
void Construct()
|
void Construct()
|
||||||
{
|
{
|
||||||
OldTerrain = NewTerrain = NULL;
|
m_TerrainDelta.Init();
|
||||||
}
|
}
|
||||||
void Destruct()
|
void Destruct()
|
||||||
{
|
{
|
||||||
delete OldTerrain;
|
|
||||||
delete NewTerrain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Do()
|
void Do()
|
||||||
{
|
{
|
||||||
|
|
||||||
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
|
|
||||||
|
|
||||||
int verts = terrain->GetVerticesPerSide()*terrain->GetVerticesPerSide();
|
|
||||||
OldTerrain = new u16[verts];
|
|
||||||
memcpy2(OldTerrain, terrain->GetHeightMap(), verts*sizeof(u16));
|
|
||||||
|
|
||||||
int amount = (int)d->amount;
|
int amount = (int)d->amount;
|
||||||
|
|
||||||
// If the framerate is very high, 'amount' is often very
|
// If the framerate is very high, 'amount' is often very
|
||||||
@ -62,33 +85,27 @@ BEGIN_COMMAND(AlterElevation)
|
|||||||
// TODO: proper variable raise amount (store floats in terrain delta array?)
|
// TODO: proper variable raise amount (store floats in terrain delta array?)
|
||||||
float b = g_CurrentBrush.Get(dx, dy);
|
float b = g_CurrentBrush.Get(dx, dy);
|
||||||
if (b)
|
if (b)
|
||||||
terrain->RaiseVertex(x0+dx, y0+dy, amount*b);
|
m_TerrainDelta.RaiseVertex(x0+dx, y0+dy, amount*b);
|
||||||
}
|
}
|
||||||
|
|
||||||
terrain->MakeDirty(x0, y0, x0+g_CurrentBrush.m_W, y0+g_CurrentBrush.m_H);
|
g_Game->GetWorld()->GetTerrain()->MakeDirty(x0, y0, x0+g_CurrentBrush.m_W, y0+g_CurrentBrush.m_H);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Undo()
|
void Undo()
|
||||||
{
|
{
|
||||||
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
|
m_TerrainDelta.Undo();
|
||||||
if (! NewTerrain)
|
g_Game->GetWorld()->GetTerrain()->MakeDirty();
|
||||||
{
|
|
||||||
int verts = terrain->GetVerticesPerSide()*terrain->GetVerticesPerSide();
|
|
||||||
NewTerrain = new u16[verts];
|
|
||||||
memcpy2(NewTerrain, terrain->GetHeightMap(), verts*sizeof(u16));
|
|
||||||
}
|
|
||||||
terrain->SetHeightMap(OldTerrain); // CTerrain duplicates the data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Redo()
|
void Redo()
|
||||||
{
|
{
|
||||||
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
|
m_TerrainDelta.Redo();
|
||||||
terrain->SetHeightMap(NewTerrain); // CTerrain duplicates the data
|
g_Game->GetWorld()->GetTerrain()->MakeDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MergeWithSelf(cAlterElevation* prev)
|
void MergeWithSelf(cAlterElevation* prev)
|
||||||
{
|
{
|
||||||
std::swap(prev->NewTerrain, NewTerrain);
|
prev->m_TerrainDelta.OverlayWith(m_TerrainDelta);
|
||||||
}
|
}
|
||||||
|
|
||||||
END_COMMAND(AlterElevation);
|
END_COMMAND(AlterElevation);
|
||||||
|
@ -70,7 +70,7 @@ MESSAGEHANDLER(GenerateMap)
|
|||||||
|
|
||||||
// Cover terrain with default texture
|
// Cover terrain with default texture
|
||||||
// TODO: split into fCoverWithTexture
|
// TODO: split into fCoverWithTexture
|
||||||
CTextureEntry* texentry = g_TexMan.FindTexture("grass1_spring.dds"); // TODO: make default customisable
|
CTextureEntry* texentry = g_TexMan.FindTexture("grass1_spring"); // TODO: make default customisable
|
||||||
Handle tex = texentry ? texentry->GetHandle() : 0;
|
Handle tex = texentry ? texentry->GetHandle() : 0;
|
||||||
|
|
||||||
int patches = terrain->GetPatchesPerSide();
|
int patches = terrain->GetPatchesPerSide();
|
||||||
@ -81,7 +81,10 @@ MESSAGEHANDLER(GenerateMap)
|
|||||||
|
|
||||||
for (int z = 0; z < PATCH_SIZE; ++z)
|
for (int z = 0; z < PATCH_SIZE; ++z)
|
||||||
for (int x = 0; x < PATCH_SIZE; ++x)
|
for (int x = 0; x < PATCH_SIZE; ++x)
|
||||||
|
{
|
||||||
patch->m_MiniPatches[z][x].Tex1 = tex;
|
patch->m_MiniPatches[z][x].Tex1 = tex;
|
||||||
|
patch->m_MiniPatches[z][x].Tex1Priority = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,14 @@
|
|||||||
|
|
||||||
#include "graphics/TextureManager.h"
|
#include "graphics/TextureManager.h"
|
||||||
#include "graphics/TextureEntry.h"
|
#include "graphics/TextureEntry.h"
|
||||||
|
#include "graphics/Terrain.h"
|
||||||
|
#include "ps/Game.h"
|
||||||
|
#include "ps/World.h"
|
||||||
|
#include "lib/ogl.h"
|
||||||
|
#include "lib/res/graphics/ogl_tex.h"
|
||||||
|
|
||||||
#include "../Brushes.h"
|
#include "../Brushes.h"
|
||||||
|
#include "../DeltaArray.h"
|
||||||
|
|
||||||
namespace AtlasMessage {
|
namespace AtlasMessage {
|
||||||
|
|
||||||
@ -18,31 +24,207 @@ QUERYHANDLER(GetTerrainGroups)
|
|||||||
msg->groupnames.push_back(CStrW(it->first));
|
msg->groupnames.push_back(CStrW(it->first));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool CompareTerrain(const sTerrainGroupPreview& a, const sTerrainGroupPreview& b)
|
||||||
|
{
|
||||||
|
return (wcscmp(a.name.c_str(), b.name.c_str()) < 0);
|
||||||
|
}
|
||||||
|
|
||||||
QUERYHANDLER(GetTerrainGroupPreviews)
|
QUERYHANDLER(GetTerrainGroupPreviews)
|
||||||
{
|
{
|
||||||
CTerrainGroup* group = g_TexMan.FindGroup(CStrW(msg->groupname));
|
CTerrainGroup* group = g_TexMan.FindGroup(CStrW(msg->groupname));
|
||||||
for (std::vector<CTextureEntry*>::const_iterator it = group->GetTerrains().begin(); it != group->GetTerrains().end(); ++it)
|
for (std::vector<CTextureEntry*>::const_iterator it = group->GetTerrains().begin(); it != group->GetTerrains().end(); ++it)
|
||||||
{
|
{
|
||||||
msg->previews.push_back(AtlasMessage::sTerrainGroupPreview());
|
msg->previews.push_back(sTerrainGroupPreview());
|
||||||
msg->previews.back().name = CStrW((*it)->GetTag());
|
msg->previews.back().name = CStrW((*it)->GetTag());
|
||||||
|
|
||||||
u32 c = (*it)->GetBaseColor();
|
|
||||||
unsigned char* buf = (unsigned char*)malloc(msg->imagewidth*msg->imageheight*3);
|
unsigned char* buf = (unsigned char*)malloc(msg->imagewidth*msg->imageheight*3);
|
||||||
|
|
||||||
// TODO: An actual preview of the texture. (There's no need to shrink
|
// It's not good to shrink the entire texture to fit the small preview
|
||||||
// the entire texture to fit, since it's the small details in the
|
// window, since it's the fine details in the texture that are
|
||||||
// texture that are interesting, so we could just crop a chunk out of
|
// interesting; so just go down one mipmap level, then crop a chunk
|
||||||
// the middle.)
|
// out of the middle.
|
||||||
|
|
||||||
|
// Read the size of the texture. (Usually loads the texture from
|
||||||
|
// disk, which is slow.)
|
||||||
|
GLint w, h;
|
||||||
|
int level = 1; // level 0 is the original size
|
||||||
|
ogl_tex_bind((*it)->GetHandle());
|
||||||
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_WIDTH, &w);
|
||||||
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_HEIGHT, &h);
|
||||||
|
|
||||||
|
if (w < msg->imagewidth || h < msg->imageheight)
|
||||||
|
{
|
||||||
|
// Oops, too small to preview - just use a flat colour
|
||||||
|
u32 c = (*it)->GetBaseColor();
|
||||||
for (int i = 0; i < msg->imagewidth*msg->imageheight; ++i)
|
for (int i = 0; i < msg->imagewidth*msg->imageheight; ++i)
|
||||||
{
|
{
|
||||||
buf[i*3+0] = (c>>16) & 0xff;
|
buf[i*3+0] = (c>>16) & 0xff;
|
||||||
buf[i*3+1] = (c>>8) & 0xff;
|
buf[i*3+1] = (c>>8) & 0xff;
|
||||||
buf[i*3+2] = (c>>0) & 0xff;
|
buf[i*3+2] = (c>>0) & 0xff;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Read the whole texture into a new buffer
|
||||||
|
unsigned char* texdata = new unsigned char[w*h*3];
|
||||||
|
glGetTexImage(GL_TEXTURE_2D, level, GL_RGB, GL_UNSIGNED_BYTE, texdata);
|
||||||
|
|
||||||
|
// Extract the middle section (as a representative preview),
|
||||||
|
// and copy into buf
|
||||||
|
unsigned char* texdata_ptr = texdata + (w*(h - msg->imageheight)/2 + (w - msg->imagewidth)/2) * 3;
|
||||||
|
unsigned char* buf_ptr = buf;
|
||||||
|
for (int y = 0; y < msg->imageheight; ++y)
|
||||||
|
{
|
||||||
|
memcpy(buf_ptr, texdata_ptr, msg->imagewidth*3);
|
||||||
|
buf_ptr += msg->imagewidth*3;
|
||||||
|
texdata_ptr += w*3;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] texdata;
|
||||||
|
}
|
||||||
|
|
||||||
msg->previews.back().imagedata = buf;
|
msg->previews.back().imagedata = buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort the list alphabetically by name
|
||||||
|
std::sort(msg->previews.begin(), msg->previews.end(), CompareTerrain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
BEGIN_COMMAND(PaintTerrain)
|
||||||
|
|
||||||
|
struct TerrainTile
|
||||||
|
{
|
||||||
|
TerrainTile(Handle t, int p) : tex(t), priority(p) {}
|
||||||
|
Handle tex;
|
||||||
|
int priority;
|
||||||
|
};
|
||||||
|
class TerrainArray : public DeltaArray2D<TerrainTile>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Init()
|
||||||
|
{
|
||||||
|
m_Terrain = g_Game->GetWorld()->GetTerrain();
|
||||||
|
m_VertsPerSide = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PaintTile(int x, int y, Handle tex, int priority)
|
||||||
|
{
|
||||||
|
// Ignore out-of-bounds tiles
|
||||||
|
if ((unsigned)x >= m_VertsPerSide-1 || (unsigned)y >= m_VertsPerSide-1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
set(x,y, TerrainTile(tex, priority == ePaintTerrainPriority::HIGH ? 1 : -1));
|
||||||
|
/* (TODO: fix all this)
|
||||||
|
// Priority system: If the new tile should have a high priority,
|
||||||
|
// set it to one plus the maximum priority of all surrounding tiles
|
||||||
|
// TODO: the blending system is broken when adjacent tiles have
|
||||||
|
// the same priority but different textures
|
||||||
|
int greatestSame = 0;
|
||||||
|
int greatestDiff = 0;
|
||||||
|
int scale = (priority == ePaintTerrainPriority::HIGH ? +1 : -1);
|
||||||
|
CMiniPatch* tile;
|
||||||
|
#define TILE(dx, dy) \
|
||||||
|
tile = m_Terrain->GetTile(x dx, y dy); \
|
||||||
|
if (tile) { \
|
||||||
|
if (tile->Tex1 == tex && tile->Tex1Priority*scale > greatestSame) \
|
||||||
|
greatestSame = tile->Tex1Priority*scale; \
|
||||||
|
else if (tile->Tex1 != tex && tile->Tex1Priority*scale > greatestDiff) \
|
||||||
|
greatestDiff = tile->Tex1Priority*scale; \
|
||||||
|
}
|
||||||
|
TILE(-1, -1) TILE(+0, -1) TILE(+1, -1)
|
||||||
|
TILE(-1, +0) TILE(+1, +0)
|
||||||
|
TILE(-1, +1) TILE(+0, +1) TILE(+1, +1)
|
||||||
|
#undef TILE
|
||||||
|
// If the greatest priority is of the same texture as this one,
|
||||||
|
// give this one the same priority (to minimise confusion in the
|
||||||
|
// blending system)
|
||||||
|
if (greatestSame > greatestDiff)
|
||||||
|
{
|
||||||
|
tile = m_Terrain->GetTile(x,y);
|
||||||
|
// Don't bother updating tiles that are exactly the same
|
||||||
|
// (e.g. when dragging big brushes around)
|
||||||
|
if (tile->Tex1 != tex || tile->Tex1Priority != greatestSame)
|
||||||
|
set(x,y, TerrainTile(tex, greatestSame*scale));
|
||||||
|
}
|
||||||
|
// Set this texture to have a priority greater than the surrounding ones
|
||||||
|
else
|
||||||
|
set(x,y, TerrainTile(tex, (greatestDiff+1)*scale));
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
TerrainTile getOld(int x, int y)
|
||||||
|
{
|
||||||
|
CMiniPatch* mp = m_Terrain->GetTile(x, y);
|
||||||
|
return TerrainTile(mp->Tex1, mp->Tex1Priority);
|
||||||
|
}
|
||||||
|
void setNew(int x, int y, const TerrainTile& val)
|
||||||
|
{
|
||||||
|
CMiniPatch* mp = m_Terrain->GetTile(x, y);
|
||||||
|
mp->Tex1 = val.tex;
|
||||||
|
mp->Tex1Priority = val.priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
CTerrain* m_Terrain;
|
||||||
|
size_t m_VertsPerSide;
|
||||||
|
};
|
||||||
|
|
||||||
|
TerrainArray m_TerrainDelta;
|
||||||
|
|
||||||
|
void Construct()
|
||||||
|
{
|
||||||
|
m_TerrainDelta.Init();
|
||||||
|
}
|
||||||
|
void Destruct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Do()
|
||||||
|
{
|
||||||
|
|
||||||
|
d->pos.GetWorldSpace(g_CurrentBrush.m_Centre);
|
||||||
|
|
||||||
|
int x0, y0;
|
||||||
|
g_CurrentBrush.GetBottomRight(x0, y0);
|
||||||
|
|
||||||
|
CTextureEntry* texentry = g_TexMan.FindTexture(CStrW(d->texture));
|
||||||
|
if (! texentry)
|
||||||
|
{
|
||||||
|
debug_warn("Can't find texentry"); // TODO: nicer error handling
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Handle texture = texentry->GetHandle();
|
||||||
|
|
||||||
|
for (int dy = 0; dy < g_CurrentBrush.m_H; ++dy)
|
||||||
|
for (int dx = 0; dx < g_CurrentBrush.m_W; ++dx)
|
||||||
|
{
|
||||||
|
if (g_CurrentBrush.Get(dx, dy) > 0.5f) // TODO: proper solid brushes
|
||||||
|
m_TerrainDelta.PaintTile(x0+dx, y0+dy, texture, d->priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_Game->GetWorld()->GetTerrain()->MakeDirty(x0, y0, x0+g_CurrentBrush.m_W, y0+g_CurrentBrush.m_H);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Undo()
|
||||||
|
{
|
||||||
|
m_TerrainDelta.Undo();
|
||||||
|
g_Game->GetWorld()->GetTerrain()->MakeDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Redo()
|
||||||
|
{
|
||||||
|
m_TerrainDelta.Redo();
|
||||||
|
g_Game->GetWorld()->GetTerrain()->MakeDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MergeWithSelf(cPaintTerrain* prev)
|
||||||
|
{
|
||||||
|
prev->m_TerrainDelta.OverlayWith(m_TerrainDelta);
|
||||||
|
}
|
||||||
|
|
||||||
|
END_COMMAND(PaintTerrain);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -115,6 +115,13 @@ COMMAND(AlterElevation, MERGE,
|
|||||||
((float, amount))
|
((float, amount))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
struct ePaintTerrainPriority { enum { HIGH, LOW }; };
|
||||||
|
COMMAND(PaintTerrain, MERGE,
|
||||||
|
((Position, pos))
|
||||||
|
((std::wstring, texture))
|
||||||
|
((int, priority))
|
||||||
|
);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "MessagesSetup.h"
|
#include "MessagesSetup.h"
|
||||||
|
Loading…
Reference in New Issue
Block a user