Ticket #1122: atlas_eyedropper-03202012.patch
File atlas_eyedropper-03202012.patch, 20.3 KB (added by , 12 years ago) |
---|
-
source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp
372 372 wxSystemOptions::SetOption(_T("osx.openfiledialog.always-show-types"), 1); // has global effect 373 373 374 374 // wxLog::SetTraceMask(wxTraceMessages); 375 376 g_SelectedTexture = _T("grass1_spring"); 377 g_SelectedTexture.NotifyObservers(); 375 378 376 379 SetOpenFilename(_T("")); 377 380 -
source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp
1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2012 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … 45 45 TextureNotebook* m_Textures; 46 46 }; 47 47 48 49 48 enum 50 49 { 51 50 ID_Passability = 1, … … 60 59 return window; 61 60 } 62 61 62 // Add spaces into the displayed name so there are more wrapping opportunities 63 static wxString FormatTextureName(wxString name) 64 { 65 if (name.Len()) 66 name[0] = wxToupper(name[0]); 67 name.Replace(_T("_"), _T(" ")); 68 69 return name; 70 } 71 72 ////////////////////////////////////////////////////////////////////////// 73 74 class TexturePreviewPanel : public wxPanel 75 { 76 private: 77 static const int imageWidth = 120; 78 static const int imageHeight = 40; 79 80 public: 81 TexturePreviewPanel(wxWindow* parent) 82 : wxPanel(parent, wxID_ANY), m_Timer(this) 83 { 84 m_Conn = g_SelectedTexture.RegisterObserver(0, &TexturePreviewPanel::OnTerrainChange, this); 85 m_Sizer = new wxStaticBoxSizer(wxVERTICAL, this, _T("Texture")); 86 SetSizer(m_Sizer); 87 88 // Use placeholder bitmap for now 89 m_Sizer->Add(new wxStaticBitmap(this, wxID_ANY, wxNullBitmap), wxSizerFlags(1).Expand()); 90 } 91 92 void LoadPreview() 93 { 94 if (m_TextureName.IsEmpty()) 95 { 96 // If we haven't got a texture yet, copy the global 97 m_TextureName = g_SelectedTexture; 98 } 99 100 Freeze(); 101 102 m_Sizer->Clear(true); 103 104 AtlasMessage::qGetTerrainTexturePreview qry(m_TextureName.wc_str(), imageWidth, imageHeight); 105 qry.Post(); 106 107 AtlasMessage::sTerrainTexturePreview preview = qry.preview; 108 109 // Check for invalid/missing texture - shouldn't happen 110 if (!wxString(qry.preview->name.c_str()).IsEmpty()) 111 { 112 // Construct the wrapped-text label 113 wxStaticText* label = new wxStaticText(this, wxID_ANY, FormatTextureName(*qry.preview->name), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER); 114 label->Wrap(m_Sizer->GetSize().GetX()); 115 116 unsigned char* buf = (unsigned char*)(malloc(preview.imageData.GetSize())); 117 // imagedata.GetBuffer() gives a Shareable<unsigned char>*, which 118 // is stored the same as a unsigned char*, so we can just copy it. 119 memcpy(buf, preview.imageData.GetBuffer(), preview.imageData.GetSize()); 120 wxImage img(qry.preview->imageWidth, qry.preview->imageHeight, buf); 121 122 wxStaticBitmap* bitmap = new wxStaticBitmap(this, wxID_ANY, wxBitmap(img), wxDefaultPosition, wxSize(qry.preview->imageWidth, qry.preview->imageHeight), wxBORDER_SIMPLE); 123 m_Sizer->Add(bitmap, wxSizerFlags(1).Align(wxALIGN_CENTRE)); 124 m_Sizer->Add(label, wxSizerFlags().Expand()); 125 126 // We have to force the sidebar to layout manually 127 GetParent()->Layout(); 128 129 if (preview.loaded && m_Timer.IsRunning()) 130 { 131 m_Timer.Stop(); 132 } 133 else if (!preview.loaded && !m_Timer.IsRunning()) 134 { 135 m_Timer.Start(2000); 136 } 137 } 138 139 Layout(); 140 Thaw(); 141 } 142 143 void OnTerrainChange(const wxString& texture) 144 { 145 // Check if texture really changed, to avoid doing this too often 146 if (texture != m_TextureName) 147 { 148 // Load new texture preview 149 m_TextureName = texture; 150 LoadPreview(); 151 } 152 } 153 154 void OnTimer(wxTimerEvent& WXUNUSED(evt)) 155 { 156 LoadPreview(); 157 } 158 159 private: 160 ObservableScopedConnection m_Conn; 161 wxSizer* m_Sizer; 162 wxTimer m_Timer; 163 wxString m_TextureName; 164 165 DECLARE_EVENT_TABLE(); 166 }; 167 168 BEGIN_EVENT_TABLE(TexturePreviewPanel, wxPanel) 169 EVT_TIMER(wxID_ANY, TexturePreviewPanel::OnTimer) 170 END_EVENT_TABLE(); 171 172 ////////////////////////////////////////////////////////////////////////// 173 63 174 TerrainSidebar::TerrainSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer) : 64 175 Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer) 65 176 { … … 84 195 wxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Texture tools")); 85 196 wxSizer* gridSizer = new wxGridSizer(3); 86 197 gridSizer->Add(Tooltipped(new ToolButton(scenarioEditor.GetToolManager(), this, _("Paint"), _T("PaintTerrain")), 87 _("Brush with left mouse button to paint texture dominantly,\nright mouse button to paint submissively ")), wxSizerFlags().Expand());198 _("Brush with left mouse button to paint texture dominantly,\nright mouse button to paint submissively.\nShift-left-click for eyedropper tool")), wxSizerFlags().Expand()); 88 199 gridSizer->Add(Tooltipped(new ToolButton(scenarioEditor.GetToolManager(), this, _("Replace"), _T("ReplaceTerrain")), 89 200 _("Replace all of a terrain texture with a new one")), wxSizerFlags().Expand()); 90 201 gridSizer->Add(Tooltipped(new ToolButton(scenarioEditor.GetToolManager(), this, _("Fill"), _T("FillTerrain")), … … 97 208 ///////////////////////////////////////////////////////////////////////// 98 209 // Brush settings 99 210 wxSizer* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Brush")); 211 212 m_TexturePreview = new TexturePreviewPanel(this); 213 sizer->Add(m_TexturePreview, wxSizerFlags(1).Expand()); 214 100 215 g_Brush_Elevation.CreateUI(this, sizer); 101 216 m_MainSizer->Add(sizer, wxSizerFlags().Expand().Border(wxTOP, 10)); 102 217 } … … 145 260 m_PassabilityChoice->Append(passClasses[i].c_str()); 146 261 147 262 static_cast<TerrainBottomBar*>(m_BottomBar)->LoadTerrain(); 263 m_TexturePreview->LoadPreview(); 148 264 } 149 265 150 266 void TerrainSidebar::OnPassabilityChoice(wxCommandEvent& evt) … … 245 361 AtlasMessage::qGetTerrainGroupPreviews qry((std::wstring)m_Name.wc_str(), imageWidth, imageHeight); 246 362 qry.Post(); 247 363 248 std::vector<AtlasMessage::sTerrain GroupPreview> previews = *qry.previews;364 std::vector<AtlasMessage::sTerrainTexturePreview> previews = *qry.previews; 249 365 250 366 bool allLoaded = true; 251 367 … … 254 370 if (!previews[i].loaded) 255 371 allLoaded = false; 256 372 257 // Construct the wrapped-text label258 373 wxString name = previews[i].name.c_str(); 259 374 260 // Add spaces into the displayed name so there are more wrapping opportunities 261 wxString labelText = name; 262 if (labelText.Len()) 263 labelText[0] = wxToupper(labelText[0]); 264 labelText.Replace(_T("_"), _T(" ")); 265 wxStaticText* label = new wxStaticText(m_ScrolledPanel, wxID_ANY, labelText, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER); 375 // Construct the wrapped-text label 376 wxStaticText* label = new wxStaticText(m_ScrolledPanel, wxID_ANY, FormatTextureName(name), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER); 266 377 label->Wrap(imageWidth); 267 378 268 379 unsigned char* buf = (unsigned char*)(malloc(previews[i].imageData.GetSize())); … … 303 414 wxButton* button = wxDynamicCast(evt.GetEventObject(), wxButton); 304 415 wxString name = static_cast<wxStringClientData*>(button->GetClientObject())->GetData(); 305 416 g_SelectedTexture = name; 417 g_SelectedTexture.NotifyObservers(); 306 418 307 419 if (m_LastTerrainSelection) 308 420 m_LastTerrainSelection->SetBackgroundColour(wxNullColour); -
source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.h
1 /* Copyright (C) 201 1Wildfire Games.1 /* Copyright (C) 2012 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … 17 17 18 18 #include "../Common/Sidebar.h" 19 19 20 class TexturePreviewPanel; 21 20 22 class TerrainSidebar : public Sidebar 21 23 { 22 24 public: … … 31 33 void OnResizeMap(wxCommandEvent& evt); 32 34 33 35 wxChoice* m_PassabilityChoice; 36 TexturePreviewPanel* m_TexturePreview; 34 37 35 38 DECLARE_EVENT_TABLE(); 36 39 }; 40 -
source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.cpp
1 /* Copyright (C) 20 09Wildfire Games.1 /* Copyright (C) 2012 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … 19 19 20 20 #include "MiscState.h" 21 21 22 wxString g_SelectedTexture = _T("grass1_spring");22 Observable<wxString> g_SelectedTexture; 23 23 24 24 Observable<std::vector<AtlasMessage::ObjectID> > g_SelectedObjects; -
source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/MiscState.h
25 25 typedef unsigned int ObjectID; 26 26 } 27 27 28 extern wxStringg_SelectedTexture;28 extern Observable<wxString> g_SelectedTexture; 29 29 30 30 extern Observable<std::vector<AtlasMessage::ObjectID> > g_SelectedObjects; 31 31 -
source/tools/atlas/AtlasUI/ScenarioEditor/Tools/PaintTerrain.cpp
1 /* Copyright (C) 20 09Wildfire Games.1 /* Copyright (C) 2012 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … 31 31 32 32 Position m_Pos; 33 33 34 // Brush for eyedropper preview 35 // (it's confusing if we use the arbitrarily sized paint brush) 36 Brush m_EyedropperBrush; 37 38 static const wxKeyCode EYEDROPPER_HOTKEY = WXK_SHIFT; 39 34 40 public: 35 41 PaintTerrain() 36 42 { 37 43 SetState(&Waiting); 44 45 m_EyedropperBrush.SetSquare(2); 38 46 } 39 47 40 48 … … 49 57 POST_MESSAGE(BrushPreview, (false, Position())); 50 58 } 51 59 52 53 60 struct sWaiting : public State 54 61 { 62 bool OnKey(PaintTerrain* obj, wxKeyEvent& evt, KeyEventType type) 63 { 64 if (type == KEY_DOWN && evt.GetKeyCode() == EYEDROPPER_HOTKEY) 65 { 66 SET_STATE(Eyedropper); 67 return true; 68 } 69 else 70 { 71 return false; 72 } 73 } 74 55 75 bool OnMouse(PaintTerrain* obj, wxMouseEvent& evt) 56 76 { 57 77 if (evt.LeftDown()) … … 135 155 int GetPriority() { return AtlasMessage::ePaintTerrainPriority::LOW; } 136 156 } 137 157 PaintingLow; 158 159 struct sEyedropper : public State 160 { 161 void OnEnter(PaintTerrain* obj) 162 { 163 obj->m_EyedropperBrush.MakeActive(); 164 } 165 166 void OnLeave(PaintTerrain* WXUNUSED(obj)) 167 { 168 g_Brush_Elevation.MakeActive(); 169 } 170 171 bool OnKey(PaintTerrain* obj, wxKeyEvent& evt, KeyEventType type) 172 { 173 if (type == KEY_UP && evt.GetKeyCode() == EYEDROPPER_HOTKEY) 174 { 175 SET_STATE(Waiting); 176 return true; 177 } 178 else 179 { 180 return false; 181 } 182 } 183 184 bool OnMouse(PaintTerrain* WXUNUSED(obj), wxMouseEvent& evt) 185 { 186 if (evt.LeftDown() || evt.Dragging()) 187 { 188 POST_MESSAGE(BrushPreview, (true, evt.GetPosition())); 189 AtlasMessage::qGetTerrainTexture qry(evt.GetPosition()); 190 qry.Post(); 191 192 g_SelectedTexture = wxString(qry.texture.c_str()); 193 g_SelectedTexture.NotifyObservers(); 194 return true; 195 } 196 else if (evt.Moving()) 197 { 198 POST_MESSAGE(BrushPreview, (true, Position(evt.GetPosition()))); 199 return true; 200 } 201 else 202 { 203 return false; 204 } 205 } 206 } 207 Eyedropper; 138 208 }; 139 209 140 210 IMPLEMENT_DYNAMIC_CLASS(PaintTerrain, StateDrivenTool<PaintTerrain>); -
source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp
51 51 msg->groupNames = groupNames; 52 52 } 53 53 54 static bool CompareTerrain(const sTerrain GroupPreview& a, const sTerrainGroupPreview& b)54 static bool CompareTerrain(const sTerrainTexturePreview& a, const sTerrainTexturePreview& b) 55 55 { 56 56 return (wcscmp(a.name.c_str(), b.name.c_str()) < 0); 57 57 } 58 58 59 QUERYHANDLER(GetTerrainGroupPreviews)59 static sTerrainTexturePreview GetPreview(CTerrainTextureEntry* tex, int width, int height) 60 60 { 61 std::vector<sTerrainGroupPreview> previews; 61 sTerrainTexturePreview preview; 62 preview.name = tex->GetTag().FromUTF8(); 62 63 63 CTerrainGroup* group = g_TexMan.FindGroup(CStrW(*msg->groupName).ToUTF8()); 64 for (std::vector<CTerrainTextureEntry*>::const_iterator it = group->GetTerrains().begin(); it != group->GetTerrains().end(); ++it) 65 { 66 previews.push_back(sTerrainGroupPreview()); 67 previews.back().name = (*it)->GetTag().FromUTF8(); 64 std::vector<unsigned char> buf (width*height*3); 68 65 69 std::vector<unsigned char> buf (msg->imageWidth*msg->imageHeight*3);70 71 66 #if !CONFIG2_GLES 72 73 74 75 67 // It's not good to shrink the entire texture to fit the small preview 68 // window, since it's the fine details in the texture that are 69 // interesting; so just go down one mipmap level, then crop a chunk 70 // out of the middle. 76 71 77 78 79 (*it)->GetTexture()->Bind();80 81 int w = std::max(1, (int)(*it)->GetTexture()->GetWidth() >> level);82 int h = std::max(1, (int)(*it)->GetTexture()->GetHeight() >> level);72 // Read the size of the texture. (Usually loads the texture from 73 // disk, which is slow.) 74 tex->GetTexture()->Bind(); 75 int level = 1; // level 0 is the original size 76 int w = std::max(1, (int)tex->GetTexture()->GetWidth() >> level); 77 int h = std::max(1, (int)tex->GetTexture()->GetHeight() >> level); 83 78 84 if (w >= msg->imageWidth && h >= msg->imageHeight) 79 if (w >= width && h >= height) 80 { 81 // Read the whole texture into a new buffer 82 unsigned char* texdata = new unsigned char[w*h*3]; 83 glGetTexImage(GL_TEXTURE_2D, level, GL_RGB, GL_UNSIGNED_BYTE, texdata); 84 85 // Extract the middle section (as a representative preview), 86 // and copy into buf 87 unsigned char* texdata_ptr = texdata + (w*(h - height)/2 + (w - width)/2) * 3; 88 unsigned char* buf_ptr = &buf[0]; 89 for (ssize_t y = 0; y < height; ++y) 85 90 { 86 // Read the whole texture into a new buffer 87 unsigned char* texdata = new unsigned char[w*h*3]; 88 glGetTexImage(GL_TEXTURE_2D, level, GL_RGB, GL_UNSIGNED_BYTE, texdata); 91 memcpy(buf_ptr, texdata_ptr, width*3); 92 buf_ptr += width*3; 93 texdata_ptr += w*3; 94 } 89 95 90 // Extract the middle section (as a representative preview), 91 // and copy into buf 92 unsigned char* texdata_ptr = texdata + (w*(h - msg->imageHeight)/2 + (w - msg->imageWidth)/2) * 3; 93 unsigned char* buf_ptr = &buf[0]; 94 for (ssize_t y = 0; y < msg->imageHeight; ++y) 95 { 96 memcpy(buf_ptr, texdata_ptr, msg->imageWidth*3); 97 buf_ptr += msg->imageWidth*3; 98 texdata_ptr += w*3; 99 } 100 101 delete[] texdata; 102 } 103 else 96 delete[] texdata; 97 } 98 else 104 99 #endif 100 { 101 // Too small to preview, or glGetTexImage not supported (on GLES) 102 // Just use a flat color instead 103 u32 c = tex->GetBaseColor(); 104 for (ssize_t i = 0; i < width*height; ++i) 105 105 { 106 // Too small to preview, or glGetTexImage not supported (on GLES) 107 // Just use a flat color instead 108 u32 c = (*it)->GetBaseColor(); 109 for (ssize_t i = 0; i < msg->imageWidth*msg->imageHeight; ++i) 110 { 111 buf[i*3+0] = (c>>16) & 0xff; 112 buf[i*3+1] = (c>>8) & 0xff; 113 buf[i*3+2] = (c>>0) & 0xff; 114 } 106 buf[i*3+0] = (c>>16) & 0xff; 107 buf[i*3+1] = (c>>8) & 0xff; 108 buf[i*3+2] = (c>>0) & 0xff; 115 109 } 116 117 previews.back().loaded = (*it)->GetTexture()->IsLoaded();118 previews.back().imageWidth = msg->imageWidth;119 previews.back().imageHeight = msg->imageHeight;120 previews.back().imageData = buf;121 110 } 122 111 112 preview.loaded = tex->GetTexture()->IsLoaded(); 113 preview.imageWidth = width; 114 preview.imageHeight = height; 115 preview.imageData = buf; 116 117 return preview; 118 } 119 120 QUERYHANDLER(GetTerrainGroupPreviews) 121 { 122 std::vector<sTerrainTexturePreview> previews; 123 124 CTerrainGroup* group = g_TexMan.FindGroup(CStrW(*msg->groupName).ToUTF8()); 125 for (std::vector<CTerrainTextureEntry*>::const_iterator it = group->GetTerrains().begin(); it != group->GetTerrains().end(); ++it) 126 { 127 previews.push_back(GetPreview(*it, msg->imageWidth, msg->imageHeight)); 128 } 129 123 130 // Sort the list alphabetically by name 124 131 std::sort(previews.begin(), previews.end(), CompareTerrain); 125 132 msg->previews = previews; … … 139 146 } 140 147 } 141 148 149 QUERYHANDLER(GetTerrainTexture) 150 { 151 ssize_t x, y; 152 g_CurrentBrush.m_Centre = msg->pos->GetWorldSpace(); 153 g_CurrentBrush.GetCentre(x, y); 154 155 CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); 156 CMiniPatch* tile = terrain->GetTile(x, y); 157 if (tile) 158 { 159 CTerrainTextureEntry* tex = tile->GetTextureEntry(); 160 msg->texture = tex->GetTag().FromUTF8(); 161 } 162 else 163 { 164 msg->texture = std::wstring(); 165 } 166 } 167 168 QUERYHANDLER(GetTerrainTexturePreview) 169 { 170 CTerrainTextureEntry* tex = g_TexMan.FindTexture(CStrW(*msg->name).ToUTF8()); 171 if (tex) 172 { 173 msg->preview = GetPreview(tex, msg->imageWidth, msg->imageHeight); 174 } 175 else 176 { 177 sTerrainTexturePreview noPreview; 178 noPreview.name = std::wstring(); 179 noPreview.imageHeight = 0; 180 noPreview.imageWidth = 0; 181 msg->preview = noPreview; 182 } 183 } 184 142 185 ////////////////////////////////////////////////////////////////////////// 143 186 144 187 namespace { -
source/tools/atlas/GameInterface/Messages.h
260 260 ); 261 261 262 262 #ifndef MESSAGES_SKIP_STRUCTS 263 struct sTerrain GroupPreview263 struct sTerrainTexturePreview 264 264 { 265 265 Shareable<std::wstring> name; 266 266 Shareable<bool> loaded; … … 268 268 Shareable<int> imageHeight; 269 269 Shareable<std::vector<unsigned char> > imageData; // RGB*width*height 270 270 }; 271 SHAREABLE_STRUCT(sTerrain GroupPreview);271 SHAREABLE_STRUCT(sTerrainTexturePreview); 272 272 #endif 273 273 274 274 QUERY(GetTerrainGroupPreviews, … … 276 276 ((int, imageWidth)) 277 277 ((int, imageHeight)) 278 278 , 279 ((std::vector<sTerrain GroupPreview>, previews))279 ((std::vector<sTerrainTexturePreview>, previews)) 280 280 ); 281 281 282 282 QUERY(GetTerrainPassabilityClasses, … … 284 284 ((std::vector<std::wstring>, classNames)) 285 285 ); 286 286 287 QUERY(GetTerrainTexturePreview, 288 ((std::wstring, name)) 289 ((int, imageWidth)) 290 ((int, imageHeight)) 291 , 292 ((sTerrainTexturePreview, preview)) 293 ); 294 287 295 ////////////////////////////////////////////////////////////////////////// 288 296 289 297 #ifndef MESSAGES_SKIP_STRUCTS … … 482 490 ((std::wstring, texture)) 483 491 ); 484 492 493 QUERY(GetTerrainTexture, 494 ((Position, pos)) 495 , 496 ((std::wstring, texture)) 497 ); 498 485 499 ////////////////////////////////////////////////////////////////////////// 486 500 487 501 QUERY(PickObject, -
source/tools/atlas/GameInterface/Shareable.h
118 118 // Shareable containers must have shareable contents - but it's easy to forget 119 119 // to declare them, so make sure the errors are almost readable, like: 120 120 // "use of undefined type 'REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE<T,__formal> 121 // with [ T=AtlasMessage::sTerrain GroupPreview, __formal=false ]"121 // with [ T=AtlasMessage::sTerrainTexturePreview, __formal=false ]" 122 122 // 123 123 // (Implementation based on boost/static_assert) 124 124 template <typename T, bool> struct REQUIRE_TYPE_TO_BE_SHAREABLE_FAILURE;