forked from 0ad/0ad
Speedups terrain painting tab in Atlas by asynchronous texture loading.
Tested By: Silier, Stan Differential Revision: https://code.wildfiregames.com/D4405 This was SVN commit r26142.
This commit is contained in:
parent
ae32055c9b
commit
e4455a8e8f
@ -30,6 +30,7 @@
|
||||
#include "lib/tex/tex.h"
|
||||
#include "lib/utf8.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/CStrInternStatic.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/XML/Xeromyces.h"
|
||||
#include "renderer/Renderer.h"
|
||||
@ -96,6 +97,8 @@ CTerrainTextureEntry::CTerrainTextureEntry(CTerrainPropertiesPtr properties, con
|
||||
name = relativePath.Value;
|
||||
}
|
||||
samplers.emplace_back(name, terrainTexturePath);
|
||||
if (name == str_baseTex.string())
|
||||
m_DiffuseTexturePath = terrainTexturePath;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -53,6 +53,11 @@ public:
|
||||
// mapping world-space (x,y,z,1) coordinates onto (u,v,0,1) texcoords
|
||||
const float* GetTextureMatrix() const;
|
||||
|
||||
// Used in Atlas to retrieve a texture for previews. Can't use textures
|
||||
// directly because they're required on CPU side. Another solution is to
|
||||
// retrieve path from diffuse texture from material.
|
||||
const VfsPath& GetDiffuseTexturePath() const { return m_DiffuseTexturePath; }
|
||||
|
||||
// Get mipmap color in BGRA format
|
||||
u32 GetBaseColor()
|
||||
{
|
||||
@ -66,6 +71,8 @@ private:
|
||||
// Tag = file name stripped of path and extension (grass_dark_1)
|
||||
CStr m_Tag;
|
||||
|
||||
VfsPath m_DiffuseTexturePath;
|
||||
|
||||
// The property sheet used by this texture
|
||||
CTerrainPropertiesPtr m_pProperties;
|
||||
|
||||
|
@ -663,6 +663,10 @@ void ScenarioEditor::OnClose(wxCloseEvent& event)
|
||||
|
||||
m_FileHistory.SaveToSubDir(*wxConfigBase::Get());
|
||||
|
||||
// We notify all clients that might interact with the game after its
|
||||
// shutdown to prevent accessing invalid state.
|
||||
m_SectionLayout.OnShutdown();
|
||||
|
||||
POST_MESSAGE(Shutdown, ());
|
||||
|
||||
qExit().Post();
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2020 Wildfire Games.
|
||||
/* Copyright (C) 2021 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -184,6 +184,12 @@ public:
|
||||
m_Pages[i].bar->OnMapReload();
|
||||
}
|
||||
|
||||
void OnShutdown()
|
||||
{
|
||||
for (size_t i = 0; i < m_Pages.size(); ++i)
|
||||
m_Pages[i].bar->OnShutdown();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void OnPageChanged(SidebarPage oldPage, SidebarPage newPage)
|
||||
@ -312,3 +318,8 @@ void SectionLayout::OnMapReload()
|
||||
{
|
||||
m_SidebarBook->OnMapReload();
|
||||
}
|
||||
|
||||
void SectionLayout::OnShutdown()
|
||||
{
|
||||
m_SidebarBook->OnShutdown();
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2017 Wildfire Games.
|
||||
/* Copyright (C) 2021 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -52,6 +52,7 @@ public:
|
||||
void SelectPage(const wxString& classname);
|
||||
|
||||
void OnMapReload();
|
||||
void OnShutdown();
|
||||
|
||||
private:
|
||||
SidebarBook* m_SidebarBook;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* Copyright (C) 2021 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -32,6 +32,8 @@ public:
|
||||
|
||||
virtual void OnMapReload() {}
|
||||
|
||||
virtual void OnShutdown() {}
|
||||
|
||||
protected:
|
||||
ScenarioEditor& m_ScenarioEditor;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2020 Wildfire Games.
|
||||
/* Copyright (C) 2021 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -26,6 +26,8 @@
|
||||
|
||||
#include "GameInterface/Messages.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <unordered_map>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/listctrl.h>
|
||||
#include <wx/image.h>
|
||||
@ -33,6 +35,15 @@
|
||||
#include <wx/busyinfo.h>
|
||||
#include <wx/notebook.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const int PREVIEW_RELOAD_DELAY_MILLISECONDS = 2000;
|
||||
const int PREVIEW_RELOAD_TIMEOUT_DELAY_MILLISECONDS = 200;
|
||||
const float PREVIEW_RELOAD_TIMEOUT_THRESHOLD_SECONDS = 0.1f;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class TextureNotebook;
|
||||
|
||||
class TerrainBottomBar : public wxPanel
|
||||
@ -40,6 +51,7 @@ class TerrainBottomBar : public wxPanel
|
||||
public:
|
||||
TerrainBottomBar(ScenarioEditor& scenarioEditor, wxWindow* parent);
|
||||
void LoadTerrain();
|
||||
void OnShutdown();
|
||||
private:
|
||||
TextureNotebook* m_Textures;
|
||||
};
|
||||
@ -130,7 +142,7 @@ public:
|
||||
}
|
||||
else if (!preview.loaded && !m_Timer.IsRunning())
|
||||
{
|
||||
m_Timer.Start(2000);
|
||||
m_Timer.Start(PREVIEW_RELOAD_DELAY_MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,6 +261,11 @@ TerrainSidebar::TerrainSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebar
|
||||
m_BottomBar = new TerrainBottomBar(scenarioEditor, bottomBarContainer);
|
||||
}
|
||||
|
||||
void TerrainSidebar::OnShutdown()
|
||||
{
|
||||
static_cast<TerrainBottomBar*>(m_BottomBar)->OnShutdown();
|
||||
}
|
||||
|
||||
void TerrainSidebar::OnFirstDisplay()
|
||||
{
|
||||
AtlasMessage::qGetTerrainPassabilityClasses qry;
|
||||
@ -314,66 +331,114 @@ public:
|
||||
|
||||
wxBusyInfo busy (_("Loading terrain previews"));
|
||||
|
||||
AtlasMessage::qGetTerrainGroupTextures query((std::wstring)m_Name.wc_str());
|
||||
query.Post();
|
||||
m_Textures = *query.names;
|
||||
|
||||
LayoutButtons();
|
||||
ReloadPreviews();
|
||||
}
|
||||
|
||||
void ReloadPreviews()
|
||||
void LayoutButtons()
|
||||
{
|
||||
Freeze();
|
||||
|
||||
m_ScrolledPanel->DestroyChildren();
|
||||
m_ItemSizer->Clear();
|
||||
|
||||
m_LastTerrainSelection = NULL; // clear any reference to deleted button
|
||||
m_LastTerrainSelection = nullptr; // clear any reference to deleted button
|
||||
|
||||
AtlasMessage::qGetTerrainGroupPreviews qry((std::wstring)m_Name.wc_str(), imageWidth, imageHeight);
|
||||
qry.Post();
|
||||
|
||||
std::vector<AtlasMessage::sTerrainTexturePreview> previews = *qry.previews;
|
||||
|
||||
bool allLoaded = true;
|
||||
|
||||
for (size_t i = 0; i < previews.size(); ++i)
|
||||
for (const std::wstring& textureName : m_Textures)
|
||||
{
|
||||
if (!previews[i].loaded)
|
||||
allLoaded = false;
|
||||
|
||||
wxString name = previews[i].name.c_str();
|
||||
|
||||
// Construct the wrapped-text label
|
||||
wxStaticText* label = new wxStaticText(m_ScrolledPanel, wxID_ANY, FormatTextureName(name), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
|
||||
wxStaticText* label = new wxStaticText(m_ScrolledPanel, wxID_ANY, FormatTextureName(textureName), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
|
||||
label->Wrap(imageWidth);
|
||||
|
||||
unsigned char* buf = (unsigned char*)(malloc(previews[i].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, previews[i].imageData.GetBuffer(), previews[i].imageData.GetSize());
|
||||
wxImage img (imageWidth, imageHeight, buf);
|
||||
wxImage image(imageWidth, imageHeight);
|
||||
wxBitmapButton* button = new wxBitmapButton(m_ScrolledPanel, wxID_ANY, wxBitmap(image));
|
||||
|
||||
wxButton* button = new wxBitmapButton(m_ScrolledPanel, wxID_ANY, wxBitmap(img));
|
||||
// Store the texture name in the clientdata slot
|
||||
button->SetClientObject(new wxStringClientData(name));
|
||||
button->SetClientObject(new wxStringClientData(textureName));
|
||||
|
||||
wxSizer* imageSizer = new wxBoxSizer(wxVERTICAL);
|
||||
imageSizer->Add(button, wxSizerFlags().Center());
|
||||
imageSizer->Add(label, wxSizerFlags().Proportion(1).Center());
|
||||
m_ItemSizer->Add(imageSizer, wxSizerFlags().Expand());
|
||||
|
||||
m_PreviewButtons.emplace(textureName, PreviewButton{button, false});
|
||||
}
|
||||
|
||||
m_ScrolledPanel->Fit();
|
||||
Layout();
|
||||
|
||||
Thaw();
|
||||
}
|
||||
|
||||
void ReloadPreviews()
|
||||
{
|
||||
bool allLoaded = true;
|
||||
bool timeout = false;
|
||||
const std::chrono::high_resolution_clock::time_point reloadingStart =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
for (const std::wstring& textureName : m_Textures)
|
||||
{
|
||||
const auto it = m_PreviewButtons.find(textureName);
|
||||
if (it == m_PreviewButtons.end() || it->second.loaded)
|
||||
continue;
|
||||
|
||||
if (timeout)
|
||||
{
|
||||
// Mark allLoaded only in case we have a real not loaded texture, and not
|
||||
// because we have an exceeded timeout.
|
||||
allLoaded = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
AtlasMessage::qGetTerrainTexturePreview previewQuery(textureName, imageWidth, imageHeight);
|
||||
previewQuery.Post();
|
||||
AtlasMessage::sTerrainTexturePreview preview = previewQuery.preview;
|
||||
|
||||
if (!preview.loaded)
|
||||
allLoaded = false;
|
||||
else
|
||||
it->second.loaded = true;
|
||||
|
||||
if (preview.imageData.GetSize())
|
||||
{
|
||||
unsigned char* buffer = reinterpret_cast<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(buffer, preview.imageData.GetBuffer(), preview.imageData.GetSize());
|
||||
wxImage image(imageWidth, imageHeight, buffer);
|
||||
it->second.button->SetBitmap(wxBitmap(image));
|
||||
}
|
||||
|
||||
// We need to load at least one preview so check for timeout inside real
|
||||
// loading.
|
||||
const std::chrono::high_resolution_clock::time_point now =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
const std::chrono::duration<float> delta = now - reloadingStart;
|
||||
if (delta.count() > PREVIEW_RELOAD_TIMEOUT_THRESHOLD_SECONDS)
|
||||
timeout = true;
|
||||
}
|
||||
|
||||
// If not all textures were loaded yet, run a timer to reload the previews
|
||||
// every so often until they've all finished
|
||||
// every so often until they've all finished.
|
||||
if (allLoaded && m_Timer.IsRunning())
|
||||
{
|
||||
m_Timer.Stop();
|
||||
m_PreviewButtons.clear();
|
||||
}
|
||||
else if (!allLoaded && !m_Timer.IsRunning())
|
||||
else if (!allLoaded)
|
||||
{
|
||||
m_Timer.Start(2000);
|
||||
if (timeout)
|
||||
{
|
||||
// In case we didn't have enough time to load all previews
|
||||
// start after a minimum delay to not freeze the whole UI.
|
||||
m_Timer.Start(PREVIEW_RELOAD_TIMEOUT_DELAY_MILLISECONDS);
|
||||
}
|
||||
else
|
||||
m_Timer.Start(PREVIEW_RELOAD_DELAY_MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,6 +474,12 @@ public:
|
||||
ReloadPreviews();
|
||||
}
|
||||
|
||||
void OnShutdown()
|
||||
{
|
||||
if (m_Timer.IsRunning())
|
||||
m_Timer.Stop();
|
||||
}
|
||||
|
||||
private:
|
||||
ScenarioEditor& m_ScenarioEditor;
|
||||
bool m_Loaded;
|
||||
@ -418,6 +489,14 @@ private:
|
||||
wxGridSizer* m_ItemSizer;
|
||||
wxButton* m_LastTerrainSelection; // button that was last selected, so we can undo its coloring
|
||||
|
||||
std::vector<std::wstring> m_Textures;
|
||||
struct PreviewButton
|
||||
{
|
||||
wxBitmapButton* button;
|
||||
bool loaded;
|
||||
};
|
||||
std::unordered_map<std::wstring, PreviewButton> m_PreviewButtons;
|
||||
|
||||
DECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
@ -466,6 +545,12 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void OnShutdown()
|
||||
{
|
||||
for (size_t index = 0; index < GetPageCount(); ++index)
|
||||
static_cast<TextureNotebookPage*>(GetPage(index))->OnShutdown();
|
||||
}
|
||||
|
||||
protected:
|
||||
void OnPageChanged(wxNotebookEvent& event)
|
||||
{
|
||||
@ -502,3 +587,8 @@ void TerrainBottomBar::LoadTerrain()
|
||||
{
|
||||
m_Textures->LoadTerrain();
|
||||
}
|
||||
|
||||
void TerrainBottomBar::OnShutdown()
|
||||
{
|
||||
m_Textures->OnShutdown();
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2020 Wildfire Games.
|
||||
/* Copyright (C) 2021 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -24,8 +24,10 @@ class TerrainSidebar : public Sidebar
|
||||
public:
|
||||
TerrainSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer);
|
||||
|
||||
void OnShutdown() override;
|
||||
|
||||
protected:
|
||||
virtual void OnFirstDisplay();
|
||||
void OnFirstDisplay() override;
|
||||
|
||||
private:
|
||||
void OnPassabilityChoice(wxCommandEvent& evt);
|
||||
|
@ -27,7 +27,8 @@
|
||||
#include "graphics/Terrain.h"
|
||||
#include "ps/Game.h"
|
||||
#include "ps/World.h"
|
||||
#include "lib/ogl.h"
|
||||
#include "lib/tex/tex.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "simulation2/components/ICmpPathfinder.h"
|
||||
#include "simulation2/components/ICmpTerrain.h"
|
||||
@ -42,6 +43,93 @@
|
||||
namespace AtlasMessage
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
sTerrainTexturePreview MakeEmptyTerrainTexturePreview()
|
||||
{
|
||||
sTerrainTexturePreview preview{};
|
||||
preview.name = std::wstring();
|
||||
preview.loaded = false;
|
||||
preview.imageHeight = 0;
|
||||
preview.imageWidth = 0;
|
||||
preview.imageData = {};
|
||||
return preview;
|
||||
}
|
||||
|
||||
bool CompareTerrain(const sTerrainTexturePreview& a, const sTerrainTexturePreview& b)
|
||||
{
|
||||
return (wcscmp(a.name.c_str(), b.name.c_str()) < 0);
|
||||
}
|
||||
|
||||
sTerrainTexturePreview GetPreview(CTerrainTextureEntry* tex, size_t width, size_t height)
|
||||
{
|
||||
sTerrainTexturePreview preview;
|
||||
preview.name = tex->GetTag().FromUTF8();
|
||||
|
||||
const size_t previewBPP = 3;
|
||||
std::vector<u8> buffer(width * height * previewBPP);
|
||||
|
||||
// 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.
|
||||
|
||||
std::shared_ptr<u8> fileData;
|
||||
size_t fileSize;
|
||||
Tex texture;
|
||||
const bool canUsePreview =
|
||||
!tex->GetDiffuseTexturePath().empty() &&
|
||||
g_VFS->LoadFile(tex->GetDiffuseTexturePath(), fileData, fileSize) == INFO::OK &&
|
||||
texture.decode(fileData, fileSize) == INFO::OK &&
|
||||
// Check that we can fit the texture into the preview size before any transform.
|
||||
texture.m_Width >= width && texture.m_Height >= height &&
|
||||
// Transform to a single format that we can process.
|
||||
texture.transform_to((texture.m_Flags) & ~(TEX_DXT | TEX_MIPMAPS | TEX_GREY | TEX_BGR)) == INFO::OK &&
|
||||
(texture.m_Bpp == 24 || texture.m_Bpp == 32);
|
||||
if (canUsePreview)
|
||||
{
|
||||
size_t level = 0;
|
||||
while ((texture.m_Width >> (level + 1)) >= width && (texture.m_Height >> (level + 1)) >= height)
|
||||
++level;
|
||||
// Extract the middle section (as a representative preview),
|
||||
// and copy into buffer.
|
||||
u8* data = texture.get_data();
|
||||
const size_t dataShiftX = ((texture.m_Width - width) / 2) >> level;
|
||||
const size_t dataShiftY = ((texture.m_Height - height) / 2) >> level;
|
||||
for (size_t y = 0; y < height; ++y)
|
||||
for (size_t x = 0; x < width; ++x)
|
||||
{
|
||||
const size_t bufferOffset = (y * width + x) * previewBPP;
|
||||
const size_t dataOffset = (((y << level) + dataShiftY) * texture.m_Width + (x << level) + dataShiftX) * texture.m_Bpp / 8;
|
||||
buffer[bufferOffset + 0] = data[dataOffset + 0];
|
||||
buffer[bufferOffset + 1] = data[dataOffset + 1];
|
||||
buffer[bufferOffset + 2] = data[dataOffset + 2];
|
||||
}
|
||||
preview.loaded = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Too small to preview. Just use a flat color instead.
|
||||
const u32 baseColor = tex->GetBaseColor();
|
||||
for (size_t i = 0; i < width * height; ++i)
|
||||
{
|
||||
buffer[i * previewBPP + 0] = (baseColor >> 16) & 0xff;
|
||||
buffer[i * previewBPP + 1] = (baseColor >> 8) & 0xff;
|
||||
buffer[i * previewBPP + 2] = (baseColor >> 0) & 0xff;
|
||||
}
|
||||
preview.loaded = tex->GetTexture()->IsLoaded();
|
||||
}
|
||||
|
||||
preview.imageWidth = width;
|
||||
preview.imageHeight = height;
|
||||
preview.imageData = buffer;
|
||||
|
||||
return preview;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
QUERYHANDLER(GetTerrainGroups)
|
||||
{
|
||||
const CTerrainTextureManager::TerrainGroupMap &groups = g_TexMan.GetGroups();
|
||||
@ -51,70 +139,18 @@ QUERYHANDLER(GetTerrainGroups)
|
||||
msg->groupNames = groupNames;
|
||||
}
|
||||
|
||||
static bool CompareTerrain(const sTerrainTexturePreview& a, const sTerrainTexturePreview& b)
|
||||
QUERYHANDLER(GetTerrainGroupTextures)
|
||||
{
|
||||
return (wcscmp(a.name.c_str(), b.name.c_str()) < 0);
|
||||
}
|
||||
std::vector<std::wstring> names;
|
||||
|
||||
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)
|
||||
CTerrainGroup* group = g_TexMan.FindGroup(CStrW(*msg->groupName).ToUTF8());
|
||||
if (group)
|
||||
{
|
||||
// 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;
|
||||
for (std::vector<CTerrainTextureEntry*>::const_iterator it = group->GetTerrains().begin(); it != group->GetTerrains().end(); ++it)
|
||||
names.emplace_back((*it)->GetTag().FromUTF8());
|
||||
}
|
||||
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;
|
||||
std::sort(names.begin(), names.end());
|
||||
msg->names = names;
|
||||
}
|
||||
|
||||
QUERYHANDLER(GetTerrainGroupPreviews)
|
||||
@ -170,23 +206,15 @@ 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.loaded = false;
|
||||
noPreview.imageHeight = 0;
|
||||
noPreview.imageWidth = 0;
|
||||
msg->preview = noPreview;
|
||||
}
|
||||
msg->preview = MakeEmptyTerrainTexturePreview();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace {
|
||||
namespace
|
||||
{
|
||||
|
||||
struct TerrainTile
|
||||
{
|
||||
|
@ -315,6 +315,11 @@ QUERY(GetTerrainGroups,
|
||||
((std::vector<std::wstring>, groupNames))
|
||||
);
|
||||
|
||||
QUERY(GetTerrainGroupTextures,
|
||||
((std::wstring, groupName)),
|
||||
((std::vector<std::wstring>, names))
|
||||
);
|
||||
|
||||
#ifndef MESSAGES_SKIP_STRUCTS
|
||||
struct sTerrainTexturePreview
|
||||
{
|
||||
@ -322,7 +327,7 @@ struct sTerrainTexturePreview
|
||||
Shareable<bool> loaded;
|
||||
Shareable<int> imageWidth;
|
||||
Shareable<int> imageHeight;
|
||||
Shareable<std::vector<unsigned char> > imageData; // RGB*width*height
|
||||
Shareable<std::vector<unsigned char>> imageData; // RGB*width*height
|
||||
};
|
||||
SHAREABLE_STRUCT(sTerrainTexturePreview);
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user