1
0
forked from 0ad/0ad

Adds eyedropper tool to Atlas terrain brush (Shift+Click to activate). Fixes #1122.

Adds preview box showing currently selected terrain.

This was SVN commit r11528.
This commit is contained in:
historic_bruno 2012-04-17 02:43:52 +00:00
parent 772b38e6be
commit 5cfeccea09
9 changed files with 325 additions and 79 deletions

View File

@ -373,6 +373,9 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterfac
// wxLog::SetTraceMask(wxTraceMessages); // wxLog::SetTraceMask(wxTraceMessages);
g_SelectedTexture = _T("grass1_spring");
g_SelectedTexture.NotifyObservers();
SetOpenFilename(_T("")); SetOpenFilename(_T(""));
#if defined(__WXMSW__) #if defined(__WXMSW__)

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games. /* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -45,7 +45,6 @@ private:
TextureNotebook* m_Textures; TextureNotebook* m_Textures;
}; };
enum enum
{ {
ID_Passability = 1, ID_Passability = 1,
@ -60,6 +59,118 @@ static wxWindow* Tooltipped(wxWindow* window, const wxString& tip)
return window; return window;
} }
// Add spaces into the displayed name so there are more wrapping opportunities
static wxString FormatTextureName(wxString name)
{
if (name.Len())
name[0] = wxToupper(name[0]);
name.Replace(_T("_"), _T(" "));
return name;
}
//////////////////////////////////////////////////////////////////////////
class TexturePreviewPanel : public wxPanel
{
private:
static const int imageWidth = 120;
static const int imageHeight = 40;
public:
TexturePreviewPanel(wxWindow* parent)
: wxPanel(parent, wxID_ANY), m_Timer(this)
{
m_Conn = g_SelectedTexture.RegisterObserver(0, &TexturePreviewPanel::OnTerrainChange, this);
m_Sizer = new wxStaticBoxSizer(wxVERTICAL, this, _T("Texture"));
SetSizer(m_Sizer);
// Use placeholder bitmap for now
m_Sizer->Add(new wxStaticBitmap(this, wxID_ANY, wxNullBitmap), wxSizerFlags(1).Expand());
}
void LoadPreview()
{
if (m_TextureName.IsEmpty())
{
// If we haven't got a texture yet, copy the global
m_TextureName = g_SelectedTexture;
}
Freeze();
m_Sizer->Clear(true);
AtlasMessage::qGetTerrainTexturePreview qry(m_TextureName.wc_str(), imageWidth, imageHeight);
qry.Post();
AtlasMessage::sTerrainTexturePreview preview = qry.preview;
// Check for invalid/missing texture - shouldn't happen
if (!wxString(qry.preview->name.c_str()).IsEmpty())
{
// Construct the wrapped-text label
wxStaticText* label = new wxStaticText(this, wxID_ANY, FormatTextureName(*qry.preview->name), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
label->Wrap(m_Sizer->GetSize().GetX());
unsigned char* buf = (unsigned char*)(malloc(preview.imageData.GetSize()));
// imagedata.GetBuffer() gives a Shareable<unsigned char>*, which
// is stored the same as a unsigned char*, so we can just copy it.
memcpy(buf, preview.imageData.GetBuffer(), preview.imageData.GetSize());
wxImage img(qry.preview->imageWidth, qry.preview->imageHeight, buf);
wxStaticBitmap* bitmap = new wxStaticBitmap(this, wxID_ANY, wxBitmap(img), wxDefaultPosition, wxSize(qry.preview->imageWidth, qry.preview->imageHeight), wxBORDER_SIMPLE);
m_Sizer->Add(bitmap, wxSizerFlags(1).Align(wxALIGN_CENTRE));
m_Sizer->Add(label, wxSizerFlags().Expand());
// We have to force the sidebar to layout manually
GetParent()->Layout();
if (preview.loaded && m_Timer.IsRunning())
{
m_Timer.Stop();
}
else if (!preview.loaded && !m_Timer.IsRunning())
{
m_Timer.Start(2000);
}
}
Layout();
Thaw();
}
void OnTerrainChange(const wxString& texture)
{
// Check if texture really changed, to avoid doing this too often
if (texture != m_TextureName)
{
// Load new texture preview
m_TextureName = texture;
LoadPreview();
}
}
void OnTimer(wxTimerEvent& WXUNUSED(evt))
{
LoadPreview();
}
private:
ObservableScopedConnection m_Conn;
wxSizer* m_Sizer;
wxTimer m_Timer;
wxString m_TextureName;
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(TexturePreviewPanel, wxPanel)
EVT_TIMER(wxID_ANY, TexturePreviewPanel::OnTimer)
END_EVENT_TABLE();
//////////////////////////////////////////////////////////////////////////
TerrainSidebar::TerrainSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer) : TerrainSidebar::TerrainSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer) :
Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer) Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer)
{ {
@ -84,7 +195,7 @@ TerrainSidebar::TerrainSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebar
wxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Texture tools")); wxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Texture tools"));
wxSizer* gridSizer = new wxGridSizer(3); wxSizer* gridSizer = new wxGridSizer(3);
gridSizer->Add(Tooltipped(new ToolButton(scenarioEditor.GetToolManager(), this, _("Paint"), _T("PaintTerrain")), gridSizer->Add(Tooltipped(new ToolButton(scenarioEditor.GetToolManager(), this, _("Paint"), _T("PaintTerrain")),
_("Brush with left mouse button to paint texture dominantly,\nright mouse button to paint submissively")), wxSizerFlags().Expand()); _("Brush with left mouse button to paint texture dominantly,\nright mouse button to paint submissively.\nShift-left-click for eyedropper tool")), wxSizerFlags().Expand());
gridSizer->Add(Tooltipped(new ToolButton(scenarioEditor.GetToolManager(), this, _("Replace"), _T("ReplaceTerrain")), gridSizer->Add(Tooltipped(new ToolButton(scenarioEditor.GetToolManager(), this, _("Replace"), _T("ReplaceTerrain")),
_("Replace all of a terrain texture with a new one")), wxSizerFlags().Expand()); _("Replace all of a terrain texture with a new one")), wxSizerFlags().Expand());
gridSizer->Add(Tooltipped(new ToolButton(scenarioEditor.GetToolManager(), this, _("Fill"), _T("FillTerrain")), gridSizer->Add(Tooltipped(new ToolButton(scenarioEditor.GetToolManager(), this, _("Fill"), _T("FillTerrain")),
@ -97,6 +208,10 @@ TerrainSidebar::TerrainSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebar
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////
// Brush settings // Brush settings
wxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Brush")); wxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Brush"));
m_TexturePreview = new TexturePreviewPanel(this);
sizer->Add(m_TexturePreview, wxSizerFlags(1).Expand());
g_Brush_Elevation.CreateUI(this, sizer); g_Brush_Elevation.CreateUI(this, sizer);
m_MainSizer->Add(sizer, wxSizerFlags().Expand().Border(wxTOP, 10)); m_MainSizer->Add(sizer, wxSizerFlags().Expand().Border(wxTOP, 10));
} }
@ -145,6 +260,7 @@ void TerrainSidebar::OnFirstDisplay()
m_PassabilityChoice->Append(passClasses[i].c_str()); m_PassabilityChoice->Append(passClasses[i].c_str());
static_cast<TerrainBottomBar*>(m_BottomBar)->LoadTerrain(); static_cast<TerrainBottomBar*>(m_BottomBar)->LoadTerrain();
m_TexturePreview->LoadPreview();
} }
void TerrainSidebar::OnPassabilityChoice(wxCommandEvent& evt) void TerrainSidebar::OnPassabilityChoice(wxCommandEvent& evt)
@ -245,7 +361,7 @@ public:
AtlasMessage::qGetTerrainGroupPreviews qry((std::wstring)m_Name.wc_str(), imageWidth, imageHeight); AtlasMessage::qGetTerrainGroupPreviews qry((std::wstring)m_Name.wc_str(), imageWidth, imageHeight);
qry.Post(); qry.Post();
std::vector<AtlasMessage::sTerrainGroupPreview> previews = *qry.previews; std::vector<AtlasMessage::sTerrainTexturePreview> previews = *qry.previews;
bool allLoaded = true; bool allLoaded = true;
@ -254,15 +370,10 @@ public:
if (!previews[i].loaded) if (!previews[i].loaded)
allLoaded = false; allLoaded = false;
// Construct the wrapped-text label
wxString name = previews[i].name.c_str(); wxString name = previews[i].name.c_str();
// Add spaces into the displayed name so there are more wrapping opportunities // Construct the wrapped-text label
wxString labelText = name; wxStaticText* label = new wxStaticText(m_ScrolledPanel, wxID_ANY, FormatTextureName(name), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
if (labelText.Len())
labelText[0] = wxToupper(labelText[0]);
labelText.Replace(_T("_"), _T(" "));
wxStaticText* label = new wxStaticText(m_ScrolledPanel, wxID_ANY, labelText, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
label->Wrap(imageWidth); label->Wrap(imageWidth);
unsigned char* buf = (unsigned char*)(malloc(previews[i].imageData.GetSize())); unsigned char* buf = (unsigned char*)(malloc(previews[i].imageData.GetSize()));
@ -303,6 +414,7 @@ public:
wxButton* button = wxDynamicCast(evt.GetEventObject(), wxButton); wxButton* button = wxDynamicCast(evt.GetEventObject(), wxButton);
wxString name = static_cast<wxStringClientData*>(button->GetClientObject())->GetData(); wxString name = static_cast<wxStringClientData*>(button->GetClientObject())->GetData();
g_SelectedTexture = name; g_SelectedTexture = name;
g_SelectedTexture.NotifyObservers();
if (m_LastTerrainSelection) if (m_LastTerrainSelection)
m_LastTerrainSelection->SetBackgroundColour(wxNullColour); m_LastTerrainSelection->SetBackgroundColour(wxNullColour);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games. /* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -17,6 +17,8 @@
#include "../Common/Sidebar.h" #include "../Common/Sidebar.h"
class TexturePreviewPanel;
class TerrainSidebar : public Sidebar class TerrainSidebar : public Sidebar
{ {
public: public:
@ -31,6 +33,8 @@ private:
void OnResizeMap(wxCommandEvent& evt); void OnResizeMap(wxCommandEvent& evt);
wxChoice* m_PassabilityChoice; wxChoice* m_PassabilityChoice;
TexturePreviewPanel* m_TexturePreview;
DECLARE_EVENT_TABLE(); DECLARE_EVENT_TABLE();
}; };

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games. /* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -19,6 +19,6 @@
#include "MiscState.h" #include "MiscState.h"
wxString g_SelectedTexture = _T("grass1_spring"); Observable<wxString> g_SelectedTexture;
Observable<std::vector<AtlasMessage::ObjectID> > g_SelectedObjects; Observable<std::vector<AtlasMessage::ObjectID> > g_SelectedObjects;

View File

@ -25,7 +25,7 @@ namespace AtlasMessage
typedef unsigned int ObjectID; typedef unsigned int ObjectID;
} }
extern wxString g_SelectedTexture; extern Observable<wxString> g_SelectedTexture;
extern Observable<std::vector<AtlasMessage::ObjectID> > g_SelectedObjects; extern Observable<std::vector<AtlasMessage::ObjectID> > g_SelectedObjects;

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games. /* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -31,10 +31,18 @@ class PaintTerrain : public StateDrivenTool<PaintTerrain>
Position m_Pos; Position m_Pos;
// Brush for eyedropper preview
// (it's confusing if we use the arbitrarily sized paint brush)
Brush m_EyedropperBrush;
static const wxKeyCode EYEDROPPER_HOTKEY = WXK_SHIFT;
public: public:
PaintTerrain() PaintTerrain()
{ {
SetState(&Waiting); SetState(&Waiting);
m_EyedropperBrush.SetSquare(2);
} }
@ -49,9 +57,21 @@ public:
POST_MESSAGE(BrushPreview, (false, Position())); POST_MESSAGE(BrushPreview, (false, Position()));
} }
struct sWaiting : public State struct sWaiting : public State
{ {
bool OnKey(PaintTerrain* obj, wxKeyEvent& evt, KeyEventType type)
{
if (type == KEY_DOWN && evt.GetKeyCode() == EYEDROPPER_HOTKEY)
{
SET_STATE(Eyedropper);
return true;
}
else
{
return false;
}
}
bool OnMouse(PaintTerrain* obj, wxMouseEvent& evt) bool OnMouse(PaintTerrain* obj, wxMouseEvent& evt)
{ {
if (evt.LeftDown()) if (evt.LeftDown())
@ -135,6 +155,56 @@ public:
int GetPriority() { return AtlasMessage::ePaintTerrainPriority::LOW; } int GetPriority() { return AtlasMessage::ePaintTerrainPriority::LOW; }
} }
PaintingLow; PaintingLow;
struct sEyedropper : public State
{
void OnEnter(PaintTerrain* obj)
{
obj->m_EyedropperBrush.MakeActive();
}
void OnLeave(PaintTerrain* WXUNUSED(obj))
{
g_Brush_Elevation.MakeActive();
}
bool OnKey(PaintTerrain* obj, wxKeyEvent& evt, KeyEventType type)
{
if (type == KEY_UP && evt.GetKeyCode() == EYEDROPPER_HOTKEY)
{
SET_STATE(Waiting);
return true;
}
else
{
return false;
}
}
bool OnMouse(PaintTerrain* WXUNUSED(obj), wxMouseEvent& evt)
{
if (evt.ButtonDown() || evt.Dragging())
{
POST_MESSAGE(BrushPreview, (true, evt.GetPosition()));
AtlasMessage::qGetTerrainTexture qry(evt.GetPosition());
qry.Post();
g_SelectedTexture = wxString(qry.texture.c_str());
g_SelectedTexture.NotifyObservers();
return true;
}
else if (evt.Moving())
{
POST_MESSAGE(BrushPreview, (true, Position(evt.GetPosition())));
return true;
}
else
{
return false;
}
}
}
Eyedropper;
}; };
IMPLEMENT_DYNAMIC_CLASS(PaintTerrain, StateDrivenTool<PaintTerrain>); IMPLEMENT_DYNAMIC_CLASS(PaintTerrain, StateDrivenTool<PaintTerrain>);

View File

@ -51,74 +51,81 @@ QUERYHANDLER(GetTerrainGroups)
msg->groupNames = groupNames; msg->groupNames = groupNames;
} }
static bool CompareTerrain(const sTerrainGroupPreview& a, const sTerrainGroupPreview& b) static bool CompareTerrain(const sTerrainTexturePreview& a, const sTerrainTexturePreview& b)
{ {
return (wcscmp(a.name.c_str(), b.name.c_str()) < 0); return (wcscmp(a.name.c_str(), b.name.c_str()) < 0);
} }
static sTerrainTexturePreview GetPreview(CTerrainTextureEntry* tex, int width, int height)
{
sTerrainTexturePreview preview;
preview.name = tex->GetTag().FromUTF8();
std::vector<unsigned char> buf (width*height*3);
#if !CONFIG2_GLES
// 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.)
tex->GetTexture()->Bind();
int level = 1; // level 0 is the original size
int w = std::max(1, (int)tex->GetTexture()->GetWidth() >> level);
int h = std::max(1, (int)tex->GetTexture()->GetHeight() >> level);
if (w >= width && h >= height)
{
// 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 - height)/2 + (w - width)/2) * 3;
unsigned char* buf_ptr = &buf[0];
for (ssize_t y = 0; y < height; ++y)
{
memcpy(buf_ptr, texdata_ptr, width*3);
buf_ptr += width*3;
texdata_ptr += w*3;
}
delete[] texdata;
}
else
#endif
{
// Too small to preview, or glGetTexImage not supported (on GLES)
// Just use a flat color instead
u32 c = tex->GetBaseColor();
for (ssize_t i = 0; i < width*height; ++i)
{
buf[i*3+0] = (c>>16) & 0xff;
buf[i*3+1] = (c>>8) & 0xff;
buf[i*3+2] = (c>>0) & 0xff;
}
}
preview.loaded = tex->GetTexture()->IsLoaded();
preview.imageWidth = width;
preview.imageHeight = height;
preview.imageData = buf;
return preview;
}
QUERYHANDLER(GetTerrainGroupPreviews) QUERYHANDLER(GetTerrainGroupPreviews)
{ {
std::vector<sTerrainGroupPreview> previews; std::vector<sTerrainTexturePreview> previews;
CTerrainGroup* group = g_TexMan.FindGroup(CStrW(*msg->groupName).ToUTF8()); CTerrainGroup* group = g_TexMan.FindGroup(CStrW(*msg->groupName).ToUTF8());
for (std::vector<CTerrainTextureEntry*>::const_iterator it = group->GetTerrains().begin(); it != group->GetTerrains().end(); ++it) for (std::vector<CTerrainTextureEntry*>::const_iterator it = group->GetTerrains().begin(); it != group->GetTerrains().end(); ++it)
{ {
previews.push_back(sTerrainGroupPreview()); previews.push_back(GetPreview(*it, msg->imageWidth, msg->imageHeight));
previews.back().name = (*it)->GetTag().FromUTF8(); }
std::vector<unsigned char> buf (msg->imageWidth*msg->imageHeight*3);
#if !CONFIG2_GLES
// 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.)
(*it)->GetTexture()->Bind();
int level = 1; // level 0 is the original size
int w = std::max(1, (int)(*it)->GetTexture()->GetWidth() >> level);
int h = std::max(1, (int)(*it)->GetTexture()->GetHeight() >> level);
if (w >= msg->imageWidth && h >= msg->imageHeight)
{
// 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[0];
for (ssize_t 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;
}
else
#endif
{
// Too small to preview, or glGetTexImage not supported (on GLES)
// Just use a flat color instead
u32 c = (*it)->GetBaseColor();
for (ssize_t 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;
}
}
previews.back().loaded = (*it)->GetTexture()->IsLoaded();
previews.back().imageWidth = msg->imageWidth;
previews.back().imageHeight = msg->imageHeight;
previews.back().imageData = buf;
}
// Sort the list alphabetically by name // Sort the list alphabetically by name
std::sort(previews.begin(), previews.end(), CompareTerrain); std::sort(previews.begin(), previews.end(), CompareTerrain);
@ -139,6 +146,42 @@ QUERYHANDLER(GetTerrainPassabilityClasses)
} }
} }
QUERYHANDLER(GetTerrainTexture)
{
ssize_t x, y;
g_CurrentBrush.m_Centre = msg->pos->GetWorldSpace();
g_CurrentBrush.GetCentre(x, y);
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
CMiniPatch* tile = terrain->GetTile(x, y);
if (tile)
{
CTerrainTextureEntry* tex = tile->GetTextureEntry();
msg->texture = tex->GetTag().FromUTF8();
}
else
{
msg->texture = std::wstring();
}
}
QUERYHANDLER(GetTerrainTexturePreview)
{
CTerrainTextureEntry* tex = g_TexMan.FindTexture(CStrW(*msg->name).ToUTF8());
if (tex)
{
msg->preview = GetPreview(tex, msg->imageWidth, msg->imageHeight);
}
else
{
sTerrainTexturePreview noPreview;
noPreview.name = std::wstring();
noPreview.imageHeight = 0;
noPreview.imageWidth = 0;
msg->preview = noPreview;
}
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
namespace { namespace {

View File

@ -260,7 +260,7 @@ QUERY(GetTerrainGroups,
); );
#ifndef MESSAGES_SKIP_STRUCTS #ifndef MESSAGES_SKIP_STRUCTS
struct sTerrainGroupPreview struct sTerrainTexturePreview
{ {
Shareable<std::wstring> name; Shareable<std::wstring> name;
Shareable<bool> loaded; Shareable<bool> loaded;
@ -268,7 +268,7 @@ struct sTerrainGroupPreview
Shareable<int> imageHeight; Shareable<int> imageHeight;
Shareable<std::vector<unsigned char> > imageData; // RGB*width*height Shareable<std::vector<unsigned char> > imageData; // RGB*width*height
}; };
SHAREABLE_STRUCT(sTerrainGroupPreview); SHAREABLE_STRUCT(sTerrainTexturePreview);
#endif #endif
QUERY(GetTerrainGroupPreviews, QUERY(GetTerrainGroupPreviews,
@ -276,7 +276,7 @@ QUERY(GetTerrainGroupPreviews,
((int, imageWidth)) ((int, imageWidth))
((int, imageHeight)) ((int, imageHeight))
, ,
((std::vector<sTerrainGroupPreview>, previews)) ((std::vector<sTerrainTexturePreview>, previews))
); );
QUERY(GetTerrainPassabilityClasses, QUERY(GetTerrainPassabilityClasses,
@ -284,6 +284,14 @@ QUERY(GetTerrainPassabilityClasses,
((std::vector<std::wstring>, classNames)) ((std::vector<std::wstring>, classNames))
); );
QUERY(GetTerrainTexturePreview,
((std::wstring, name))
((int, imageWidth))
((int, imageHeight))
,
((sTerrainTexturePreview, preview))
);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
#ifndef MESSAGES_SKIP_STRUCTS #ifndef MESSAGES_SKIP_STRUCTS
@ -483,6 +491,12 @@ COMMAND(FillTerrain, NOMERGE,
((std::wstring, texture)) ((std::wstring, texture))
); );
QUERY(GetTerrainTexture,
((Position, pos))
,
((std::wstring, texture))
);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
QUERY(PickObject, QUERY(PickObject,

View File

@ -118,7 +118,7 @@ SHAREABLE_PRIMITIVE(void*);
// Shareable containers must have shareable contents - but it's easy to forget // Shareable containers must have shareable contents - but it's easy to forget
// to declare them, so make sure the errors are almost readable, like: // to declare them, so make sure the errors are almost readable, like:
// "use of undefined type 'REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE<T,__formal> // "use of undefined type 'REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE<T,__formal>
// with [ T=AtlasMessage::sTerrainGroupPreview, __formal=false ]" // with [ T=AtlasMessage::sTerrainTexturePreview, __formal=false ]"
// //
// (Implementation based on boost/static_assert) // (Implementation based on boost/static_assert)
template <typename T, bool> struct REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE; template <typename T, bool> struct REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE;