mirror of
https://github.com/luanti-org/luanti.git
synced 2025-11-19 16:15:19 +01:00
Introduce array textures for node rendering (#16574)
This commit is contained in:
@@ -14,17 +14,25 @@
|
||||
#include "texturepaths.h"
|
||||
#include "util/thread.h"
|
||||
|
||||
|
||||
// Represents a to-be-generated texture for queuing purposes
|
||||
struct TextureRequest
|
||||
{
|
||||
std::string image;
|
||||
video::E_TEXTURE_TYPE type = video::ETT_2D;
|
||||
std::vector<std::string> images;
|
||||
|
||||
void print(std::ostream &to) const {
|
||||
to << "image=\"" << image << "\"";
|
||||
if (images.size() == 1) {
|
||||
to << "image=\"" << images[0] << "\"";
|
||||
} else {
|
||||
to << "images={";
|
||||
for (auto &image : images)
|
||||
to << "\"" << image << "\" ";
|
||||
to << "}";
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const TextureRequest &other) const {
|
||||
return image == other.image;
|
||||
return type == other.type && images == other.images;
|
||||
}
|
||||
bool operator!=(const TextureRequest &other) const {
|
||||
return !(*this == other);
|
||||
@@ -72,6 +80,9 @@ public:
|
||||
|
||||
video::ITexture* getTexture(const std::string &name, u32 *id = nullptr);
|
||||
|
||||
video::ITexture *addArrayTexture(
|
||||
const std::vector<std::string> &images, u32 *id = nullptr);
|
||||
|
||||
bool needFilterForMesh() const {
|
||||
return mesh_filter_needed;
|
||||
}
|
||||
@@ -104,6 +115,8 @@ public:
|
||||
|
||||
video::SColor getTextureAverageColor(const std::string &name);
|
||||
|
||||
core::dimension2du getTextureDimensions(const std::string &image);
|
||||
|
||||
void setImageCaching(bool enabled);
|
||||
|
||||
private:
|
||||
@@ -137,6 +150,9 @@ private:
|
||||
// Generate standard texture
|
||||
u32 generateTexture(const std::string &name);
|
||||
|
||||
// Generate array texture
|
||||
u32 generateArrayTexture(const std::vector<std::string> &names);
|
||||
|
||||
// Thread-safe cache of what source images are known (true = known)
|
||||
MutexedMap<std::string, bool> m_source_image_existence;
|
||||
|
||||
@@ -270,15 +286,97 @@ u32 TextureSource::getTextureId(const std::string &name)
|
||||
return n->second;
|
||||
}
|
||||
|
||||
TextureRequest req{name};
|
||||
TextureRequest req{video::ETT_2D, {name}};
|
||||
|
||||
return processRequestQueued(req);
|
||||
}
|
||||
|
||||
video::ITexture *TextureSource::addArrayTexture(
|
||||
const std::vector<std::string> &images, u32 *ret_id)
|
||||
{
|
||||
if (images.empty())
|
||||
return NULL;
|
||||
|
||||
TextureRequest req{video::ETT_2D_ARRAY, images};
|
||||
|
||||
u32 id = processRequestQueued(req);
|
||||
if (ret_id)
|
||||
*ret_id = id;
|
||||
return getTexture(id);
|
||||
}
|
||||
|
||||
u32 TextureSource::processRequest(const TextureRequest &req)
|
||||
{
|
||||
// No different types yet (TODO)
|
||||
return generateTexture(req.image);
|
||||
if (req.type == video::ETT_2D) {
|
||||
assert(req.images.size() == 1);
|
||||
return generateTexture(req.images[0]);
|
||||
}
|
||||
|
||||
if (req.type == video::ETT_2D_ARRAY) {
|
||||
assert(!req.images.empty());
|
||||
return generateArrayTexture(req.images);
|
||||
}
|
||||
|
||||
errorstream << "TextureSource::processRequest(): unknown type "
|
||||
<< (int)req.type << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 TextureSource::generateArrayTexture(const std::vector<std::string> &images)
|
||||
{
|
||||
std::set<std::string> source_image_names;
|
||||
std::vector<video::IImage*> imgs;
|
||||
const auto &drop_imgs = [&imgs] () {
|
||||
for (auto *img : imgs) {
|
||||
if (img)
|
||||
img->drop();
|
||||
}
|
||||
imgs.clear();
|
||||
};
|
||||
for (auto &name : images) {
|
||||
video::IImage *img = getOrGenerateImage(name, source_image_names);
|
||||
if (!img) {
|
||||
// Since the caller needs to make sure of the dimensions beforehand
|
||||
// anyway, this should not ever happen. So the "unhelpful" error is ok.
|
||||
errorstream << "generateArrayTexture(): one of " << images.size()
|
||||
<< " images failed to generate, aborting." << std::endl;
|
||||
drop_imgs();
|
||||
return 0;
|
||||
}
|
||||
imgs.push_back(img);
|
||||
}
|
||||
assert(!imgs.empty());
|
||||
|
||||
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
|
||||
sanity_check(driver);
|
||||
assert(driver->queryFeature(video::EVDF_TEXTURE_2D_ARRAY));
|
||||
|
||||
MutexAutoLock lock(m_textureinfo_cache_mutex);
|
||||
const u32 id = m_textureinfo_cache.size();
|
||||
std::string name;
|
||||
{ // automatically choose a name
|
||||
char buf[64];
|
||||
porting::mt_snprintf(buf, sizeof(buf), "array#%u %ux%ux%u", id,
|
||||
imgs[0]->getDimension().Width, imgs[0]->getDimension().Height,
|
||||
imgs.size());
|
||||
name = buf;
|
||||
}
|
||||
|
||||
video::ITexture *tex = driver->addArrayTexture(name, imgs.data(), imgs.size());
|
||||
drop_imgs();
|
||||
|
||||
if (!tex) {
|
||||
warningstream << "generateArrayTexture(): failed to upload texture \""
|
||||
<< name << "\"" << std::endl;
|
||||
}
|
||||
|
||||
// Add texture to caches (add NULL textures too)
|
||||
|
||||
TextureInfo ti{video::ETT_2D_ARRAY, name, images, tex, std::move(source_image_names)};
|
||||
m_textureinfo_cache.emplace_back(std::move(ti));
|
||||
m_name_to_id[name] = id;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
u32 TextureSource::generateTexture(const std::string &name)
|
||||
@@ -302,7 +400,6 @@ u32 TextureSource::generateTexture(const std::string &name)
|
||||
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
|
||||
sanity_check(driver);
|
||||
|
||||
// passed into texture info for dynamic media tracking
|
||||
std::set<std::string> source_image_names;
|
||||
video::IImage *img = getOrGenerateImage(name, source_image_names);
|
||||
|
||||
@@ -314,12 +411,16 @@ u32 TextureSource::generateTexture(const std::string &name)
|
||||
guiScalingCache(io::path(name.c_str()), driver, img);
|
||||
img->drop();
|
||||
}
|
||||
if (!tex) {
|
||||
warningstream << "generateTexture(): failed to upload texture \""
|
||||
<< name << "\"" << std::endl;
|
||||
}
|
||||
|
||||
// Add texture to caches (add NULL textures too)
|
||||
|
||||
MutexAutoLock lock(m_textureinfo_cache_mutex);
|
||||
|
||||
u32 id = m_textureinfo_cache.size();
|
||||
const u32 id = m_textureinfo_cache.size();
|
||||
TextureInfo ti{video::ETT_2D, name, {name}, tex, std::move(source_image_names)};
|
||||
m_textureinfo_cache.emplace_back(std::move(ti));
|
||||
m_name_to_id[name] = id;
|
||||
@@ -535,6 +636,8 @@ void TextureSource::rebuildTexture(video::IVideoDriver *driver, TextureInfo &ti)
|
||||
video::SColor TextureSource::getTextureAverageColor(const std::string &name)
|
||||
{
|
||||
assert(std::this_thread::get_id() == m_main_thread);
|
||||
if (name.empty())
|
||||
return {0, 0, 0, 0};
|
||||
|
||||
std::set<std::string> unused;
|
||||
auto *image = getOrGenerateImage(name, unused);
|
||||
@@ -547,6 +650,23 @@ video::SColor TextureSource::getTextureAverageColor(const std::string &name)
|
||||
return c;
|
||||
}
|
||||
|
||||
core::dimension2du TextureSource::getTextureDimensions(const std::string &name)
|
||||
{
|
||||
assert(std::this_thread::get_id() == m_main_thread);
|
||||
|
||||
core::dimension2du ret;
|
||||
if (!name.empty()) {
|
||||
std::set<std::string> unused;
|
||||
auto *image = getOrGenerateImage(name, unused);
|
||||
if (image) {
|
||||
ret = image->getDimension();
|
||||
image->drop();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TextureSource::setImageCaching(bool enabled)
|
||||
{
|
||||
m_image_cache_enabled = enabled;
|
||||
|
||||
Reference in New Issue
Block a user