From faaee7d1b5ca943caf718ed35546e71b4db7ffbb Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Sat, 5 Nov 2005 04:59:54 +0000 Subject: [PATCH] Atlas: Terrain painting This was SVN commit r3092. --- source/graphics/Terrain.cpp | 20 +- source/graphics/Terrain.h | 5 +- source/lib/sysdep/sysdep.h | 5 +- .../Sections/Terrain/Terrain.cpp | 47 +++- .../ScenarioEditor/Tools/Common/MiscState.cpp | 5 + .../ScenarioEditor/Tools/Common/MiscState.h | 6 + .../ScenarioEditor/Tools/PaintTerrain.cpp | 122 +++++++++++ source/tools/atlas/GameInterface/DeltaArray.h | 90 ++++++++ .../GameInterface/Handlers/Elevation.cpp | 69 +++--- .../atlas/GameInterface/Handlers/Map.cpp | 5 +- .../Handlers/TerrainHandlers.cpp | 202 +++++++++++++++++- source/tools/atlas/GameInterface/Messages.h | 7 + 12 files changed, 522 insertions(+), 61 deletions(-) create mode 100644 source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.cpp create mode 100644 source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.h create mode 100644 source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PaintTerrain.cpp create mode 100644 source/tools/atlas/GameInterface/DeltaArray.h diff --git a/source/graphics/Terrain.cpp b/source/graphics/Terrain.cpp index 8a53305794..1dfee0e535 100755 --- a/source/graphics/Terrain.cpp +++ b/source/graphics/Terrain.cpp @@ -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) { // 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); + } + } +} diff --git a/source/graphics/Terrain.h b/source/graphics/Terrain.h index 3ff74fa0ba..c1ffbe26b6 100755 --- a/source/graphics/Terrain.h +++ b/source/graphics/Terrain.h @@ -68,11 +68,10 @@ public: // the average height of the flattened area 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 void MakeDirty(int x0, int z0, int x1, int z1); + // mark the entire map as dirty + void MakeDirty(); private: // delete any data allocated by this terrain diff --git a/source/lib/sysdep/sysdep.h b/source/lib/sysdep/sysdep.h index c943d5cef3..2d26ededac 100755 --- a/source/lib/sysdep/sysdep.h +++ b/source/lib/sysdep/sysdep.h @@ -236,6 +236,7 @@ extern float fmaxf(float a, float b); # define STL_HASH_MULTIMAP __gnu_cxx::hash_multimap # define STL_HASH_SET __gnu_cxx::hash_set # define STL_HASH_MULTISET __gnu_cxx::hash_multiset +# define STL_HASH_VALUE __gnu_cxx::hash # define STL_SLIST __gnu_cxx::slist // Hack: GCC Doesn't have a hash instance for std::string included (and it looks @@ -247,7 +248,7 @@ namespace __gnu_cxx size_t operator()(const std::string& __x) const { return __stl_hash_string(__x.c_str()); - } + } }; } @@ -260,12 +261,14 @@ namespace __gnu_cxx # define STL_HASH_MULTIMAP stdext::hash_multimap # define STL_HASH_SET stdext::hash_set # define STL_HASH_MULTISET stdext::hash_multiset +# define STL_HASH_VALUE stdext::hash_value // VC6 and anything else (most likely name) # else # define STL_HASH_MAP std::hash_map # define STL_HASH_MULTIMAP std::hash_multimap # define STL_HASH_SET std::hash_set # define STL_HASH_MULTISET std::hash_multiset +# define STL_HASH_VALUE std::hash_value # endif // MSC_VERSION >= 1300 #endif // !__GNUC__ diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp index 9bcd8fb7a4..32307793b9 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp @@ -7,6 +7,7 @@ #include "General/Datafile.h" #include "ScenarioEditor/Tools/Common/Brushes.h" #include "ScenarioEditor/Tools/Common/Tools.h" +#include "ScenarioEditor/Tools/Common/MiscState.h" #include "GameInterface/Messages.h" @@ -22,8 +23,9 @@ TerrainSidebar::TerrainSidebar(wxWindow* parent) { wxSizer* sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Elevation tools")); 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, _("Sample"), _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, _("Paint"), _T("PaintTerrain"), wxSize(50,20))); m_MainSizer->Add(sizer); } @@ -54,17 +56,19 @@ public: { } - void OnSelected() + void OnDisplay() { if (m_Loaded) return; - const int imageWidth = 64; - const int imageHeight = 32; + m_TextureNames.Clear(); wxSizer* sizer = new wxBoxSizer(wxVERTICAL); 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); AtlasMessage::qGetTerrainGroupPreviews qry(m_Name.c_str(), imageWidth, imageHeight); @@ -76,12 +80,20 @@ public: wxImage img (imageWidth, imageHeight, it->imagedata); imglist->Add(wxBitmap(img)); - // Add spaces into the name, so Windows doesn't just say - // "grass_..." in the list ctrl for every terrain - wxString name = it->name.c_str(); - name.Replace(_T("_"), _T(" ")); + wxListItem item; - 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; } list->AssignImageList(imglist, wxIMAGE_LIST_NORMAL); @@ -93,11 +105,24 @@ public: m_Loaded = true; } + void OnSelect(wxListEvent& evt) + { + g_SelectedTexture = m_TextureNames[evt.GetData()]; + } + private: bool m_Loaded; 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 { public: @@ -124,7 +149,7 @@ protected: { if (event.GetSelection() != -1) { - static_cast(GetPage(event.GetSelection()))->OnSelected(); + static_cast(GetPage(event.GetSelection()))->OnDisplay(); } event.Skip(); } diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.cpp new file mode 100644 index 0000000000..98483bac04 --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.cpp @@ -0,0 +1,5 @@ +#include "stdafx.h" + +#include "MiscState.h" + +wxString g_SelectedTexture = _T("grass1_spring"); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.h new file mode 100644 index 0000000000..85ec3e4301 --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.h @@ -0,0 +1,6 @@ +#ifndef MISCSTATE_H__ +#define MISCSTATE_H__ + +extern wxString g_SelectedTexture; + +#endif // MISCSTATE_H__ diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PaintTerrain.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PaintTerrain.cpp new file mode 100644 index 0000000000..61f7d14b79 --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PaintTerrain.cpp @@ -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 +{ + 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); diff --git a/source/tools/atlas/GameInterface/DeltaArray.h b/source/tools/atlas/GameInterface/DeltaArray.h new file mode 100644 index 0000000000..f0b87d8935 --- /dev/null +++ b/source/tools/atlas/GameInterface/DeltaArray.h @@ -0,0 +1,90 @@ +#ifndef DELTAARRAY_H__ +#define DELTAARRAY_H__ + +template class DeltaArray2D +{ +public: + T get(int x, int y); + void set(int x, int y, const T& val); + + void OverlayWith(const DeltaArray2D& 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& p) const { + return STL_HASH_VALUE(p.first << 16) + STL_HASH_VALUE(p.second); + } + bool operator()(const std::pair& a, const std::pair& b) const { + return (a < b); + } + }; + // TODO: more efficient representation + typedef STL_HASH_MAP, std::pair, hashfunc> Data; // map of -> + Data m_Data; +}; + +////////////////////////////////////////////////////////////////////////// + +template +T DeltaArray2D::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 +void DeltaArray2D::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 +void DeltaArray2D::OverlayWith(const DeltaArray2D& 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 +void DeltaArray2D::Undo() +{ + for (Data::iterator it = m_Data.begin(); it != m_Data.end(); ++it) + setNew(it->first.first, it->first.second, it->second.first); +} + +template +void DeltaArray2D::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__ diff --git a/source/tools/atlas/GameInterface/Handlers/Elevation.cpp b/source/tools/atlas/GameInterface/Handlers/Elevation.cpp index 21a31fe33b..3f1d2113c7 100644 --- a/source/tools/atlas/GameInterface/Handlers/Elevation.cpp +++ b/source/tools/atlas/GameInterface/Handlers/Elevation.cpp @@ -6,37 +6,60 @@ #include "graphics/Terrain.h" #include "ps/Game.h" +#include "maths/MathUtil.h" #include "../Brushes.h" +#include "../DeltaArray.h" namespace AtlasMessage { - BEGIN_COMMAND(AlterElevation) - // TODO: much more efficient version of this, and without the memory leaks - u16* OldTerrain; - u16* NewTerrain; + class TerrainArray : public DeltaArray2D + { + 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() { - OldTerrain = NewTerrain = NULL; + m_TerrainDelta.Init(); } void Destruct() { - delete OldTerrain; - delete NewTerrain; } 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; // 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?) float b = g_CurrentBrush.Get(dx, dy); 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() { - CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); - if (! NewTerrain) - { - int verts = terrain->GetVerticesPerSide()*terrain->GetVerticesPerSide(); - NewTerrain = new u16[verts]; - memcpy2(NewTerrain, terrain->GetHeightMap(), verts*sizeof(u16)); - } - terrain->SetHeightMap(OldTerrain); // CTerrain duplicates the data + m_TerrainDelta.Undo(); + g_Game->GetWorld()->GetTerrain()->MakeDirty(); } void Redo() { - CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); - terrain->SetHeightMap(NewTerrain); // CTerrain duplicates the data + m_TerrainDelta.Redo(); + g_Game->GetWorld()->GetTerrain()->MakeDirty(); } void MergeWithSelf(cAlterElevation* prev) { - std::swap(prev->NewTerrain, NewTerrain); + prev->m_TerrainDelta.OverlayWith(m_TerrainDelta); } END_COMMAND(AlterElevation); diff --git a/source/tools/atlas/GameInterface/Handlers/Map.cpp b/source/tools/atlas/GameInterface/Handlers/Map.cpp index e85b7226c7..2cf9b71712 100644 --- a/source/tools/atlas/GameInterface/Handlers/Map.cpp +++ b/source/tools/atlas/GameInterface/Handlers/Map.cpp @@ -70,7 +70,7 @@ MESSAGEHANDLER(GenerateMap) // Cover terrain with default texture // 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; int patches = terrain->GetPatchesPerSide(); @@ -81,7 +81,10 @@ MESSAGEHANDLER(GenerateMap) for (int z = 0; z < PATCH_SIZE; ++z) for (int x = 0; x < PATCH_SIZE; ++x) + { patch->m_MiniPatches[z][x].Tex1 = tex; + patch->m_MiniPatches[z][x].Tex1Priority = 0; + } } } diff --git a/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp index 9084a5f273..3227db2885 100644 --- a/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp @@ -6,8 +6,14 @@ #include "graphics/TextureManager.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 "../DeltaArray.h" namespace AtlasMessage { @@ -18,31 +24,207 @@ QUERYHANDLER(GetTerrainGroups) 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) { CTerrainGroup* group = g_TexMan.FindGroup(CStrW(msg->groupname)); for (std::vector::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()); - u32 c = (*it)->GetBaseColor(); unsigned char* buf = (unsigned char*)malloc(msg->imagewidth*msg->imageheight*3); - // TODO: An actual preview of the texture. (There's no need to shrink - // the entire texture to fit, since it's the small details in the - // texture that are interesting, so we could just crop a chunk out of - // the middle.) - for (int i = 0; i < msg->imagewidth*msg->imageheight; ++i) + // It's not good to shrink the entire texture to fit the small preview + // window, since it's the fine details in the texture that are + // interesting; so just go down one mipmap level, then crop a chunk + // 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) { - buf[i*3+0] = (c>>16) & 0xff; - buf[i*3+1] = (c>>8) & 0xff; - buf[i*3+2] = (c>>0) & 0xff; + // Oops, too small to preview - just use a flat colour + u32 c = (*it)->GetBaseColor(); + for (int i = 0; i < msg->imagewidth*msg->imageheight; ++i) + { + buf[i*3+0] = (c>>16) & 0xff; + buf[i*3+1] = (c>>8) & 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; } + // 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 + { + 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); + + } diff --git a/source/tools/atlas/GameInterface/Messages.h b/source/tools/atlas/GameInterface/Messages.h index 1727f82d12..bc7e41df79 100644 --- a/source/tools/atlas/GameInterface/Messages.h +++ b/source/tools/atlas/GameInterface/Messages.h @@ -115,6 +115,13 @@ COMMAND(AlterElevation, MERGE, ((float, amount)) ); +struct ePaintTerrainPriority { enum { HIGH, LOW }; }; +COMMAND(PaintTerrain, MERGE, + ((Position, pos)) + ((std::wstring, texture)) + ((int, priority)) + ); + ////////////////////////////////////////////////////////////////////////// #include "MessagesSetup.h"