From 8f1d5d34a6191346d7f8cb5e012053bcd19795c4 Mon Sep 17 00:00:00 2001 From: Kahrl Date: Wed, 3 Jul 2013 02:22:39 +0200 Subject: [PATCH] Move generateTextureFromMesh to TextureSource to fix a texture leak TextureSource has a list of textures to delete (m_texture_trash) so this provides a proper, non-hacky way to delete RTT textures. Also, the prior, hacky way of deleting them seems to be broken (see pull request #803). To avoid header file clutter by repeating the same long list of arguments over and over again, store the arguments of generateTextureFromMesh in a struct called TextureFromMeshParams. Also fix issue #782 (Only use bilinear (and others) on item textures when settings allow it). --- src/itemdef.cpp | 60 +++--------- src/mesh.cpp | 77 --------------- src/mesh.h | 16 --- src/tile.cpp | 256 +++++++++++++++++++++++++++++++----------------- src/tile.h | 23 +++++ 5 files changed, 202 insertions(+), 230 deletions(-) diff --git a/src/itemdef.cpp b/src/itemdef.cpp index b582aef78..238ff58c0 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -229,7 +229,6 @@ public: #ifndef SERVER m_main_thread = get_current_thread_id(); - m_driver = NULL; #endif clear(); } @@ -246,13 +245,6 @@ public: delete cc; } - if (m_driver != NULL) { - for (unsigned int i = 0; i < m_extruded_textures.size(); i++) { - m_driver->removeTexture(m_extruded_textures[i]); - } - m_extruded_textures.clear(); - } - m_driver = NULL; #endif for (std::map::iterator iter = m_item_definitions.begin(); iter != m_item_definitions.end(); @@ -307,9 +299,6 @@ public: return m_item_definitions.find(name) != m_item_definitions.end(); } #ifndef SERVER -private: - static video::IVideoDriver * m_driver; - static std::vector m_extruded_textures; public: ClientCached* createClientCachedDirect(const std::string &name, IGameDef *gamedef) const @@ -416,31 +405,25 @@ public: */ if(cc->inventory_texture == NULL) { - core::dimension2d dim(64,64); - std::string rtt_texture_name = "INVENTORY_" + TextureFromMeshParams params; + params.mesh = node_mesh; + params.dim.set(64, 64); + params.rtt_texture_name = "INVENTORY_" + def->name + "_RTT"; - v3f camera_position(0, 1.0, -1.5); - camera_position.rotateXZBy(45); - v3f camera_lookat(0, 0, 0); - core::CMatrix4 camera_projection_matrix; + params.delete_texture_on_shutdown = true; + params.camera_position.set(0, 1.0, -1.5); + params.camera_position.rotateXZBy(45); + params.camera_lookat.set(0, 0, 0); // Set orthogonal projection - camera_projection_matrix.buildProjectionMatrixOrthoLH( + params.camera_projection_matrix.buildProjectionMatrixOrthoLH( 1.65, 1.65, 0, 100); + params.ambient_light.set(1.0, 0.2, 0.2, 0.2); + params.light_position.set(10, 100, -50); + params.light_color.set(1.0, 0.5, 0.5, 0.5); + params.light_radius = 1000; - video::SColorf ambient_light(0.2,0.2,0.2); - v3f light_position(10, 100, -50); - video::SColorf light_color(0.5,0.5,0.5); - f32 light_radius = 1000; - - cc->inventory_texture = generateTextureFromMesh( - node_mesh, device, dim, rtt_texture_name, - camera_position, - camera_lookat, - camera_projection_matrix, - ambient_light, - light_position, - light_color, - light_radius); + cc->inventory_texture = + tsrc->generateTextureFromMesh(params); // render-to-target didn't work if(cc->inventory_texture == NULL) @@ -449,13 +432,6 @@ public: tsrc->getTexture(f.tiledef[0].name); } } - else - { - if (m_driver == 0) - m_driver = driver; - - m_extruded_textures.push_back(cc->inventory_texture); - } /* Use the node mesh as the wield mesh @@ -681,9 +657,3 @@ IWritableItemDefManager* createItemDefManager() { return new CItemDefManager(); } - -#ifndef SERVER -//TODO very very very dirty hack! -video::IVideoDriver * CItemDefManager::m_driver = 0; -std::vector CItemDefManager::m_extruded_textures; -#endif diff --git a/src/mesh.cpp b/src/mesh.cpp index fd35a3a06..a98cb8bf4 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -23,7 +23,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include -#include // In Irrlicht 1.8 the signature of ITexture::lock was changed from // (bool, u32) to (E_TEXTURE_LOCK_MODE, u32). @@ -407,79 +406,3 @@ void setMeshColorByNormalXYZ(scene::IMesh *mesh, } } } - -video::ITexture *generateTextureFromMesh(scene::IMesh *mesh, - IrrlichtDevice *device, - core::dimension2d dim, - std::string texture_name, - v3f camera_position, - v3f camera_lookat, - core::CMatrix4 camera_projection_matrix, - video::SColorf ambient_light, - v3f light_position, - video::SColorf light_color, - f32 light_radius) -{ - video::IVideoDriver *driver = device->getVideoDriver(); - if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false) - { - static bool warned = false; - if(!warned) - { - errorstream<<"generateTextureFromMesh(): EVDF_RENDER_TO_TARGET" - " not supported."<addRenderTargetTexture( - dim, texture_name.c_str(), video::ECF_A8R8G8B8); - if(rtt == NULL) - { - errorstream<<"generateTextureFromMesh(): addRenderTargetTexture" - " returned NULL."<setRenderTarget(rtt, false, true, video::SColor(0,0,0,0)); - - // Get a scene manager - scene::ISceneManager *smgr_main = device->getSceneManager(); - assert(smgr_main); - scene::ISceneManager *smgr = smgr_main->createNewSceneManager(); - assert(smgr); - - scene::IMeshSceneNode* meshnode = smgr->addMeshSceneNode(mesh, NULL, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true); - meshnode->setMaterialFlag(video::EMF_LIGHTING, true); - meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true); - meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, true); - - scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0, - camera_position, camera_lookat); - // second parameter of setProjectionMatrix (isOrthogonal) is ignored - camera->setProjectionMatrix(camera_projection_matrix, false); - - smgr->setAmbientLight(ambient_light); - smgr->addLightSceneNode(0, light_position, light_color, light_radius); - - // Render scene - driver->beginScene(true, true, video::SColor(0,0,0,0)); - smgr->drawAll(); - driver->endScene(); - - // NOTE: The scene nodes should not be dropped, otherwise - // smgr->drop() segfaults - /*cube->drop(); - camera->drop(); - light->drop();*/ - // Drop scene manager - smgr->drop(); - - // Unset render target - driver->setRenderTarget(0, false, true, 0); - - return rtt; -} diff --git a/src/mesh.h b/src/mesh.h index cf46e7f8f..a89bea385 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -69,20 +69,4 @@ void setMeshColorByNormalXYZ(scene::IMesh *mesh, const video::SColor &colorY, const video::SColor &colorZ); -/* - Render a mesh to a texture. - Returns NULL if render-to-texture failed. -*/ -video::ITexture *generateTextureFromMesh(scene::IMesh *mesh, - IrrlichtDevice *device, - core::dimension2d dim, - std::string texture_name, - v3f camera_position, - v3f camera_lookat, - core::CMatrix4 camera_projection_matrix, - video::SColorf ambient_light, - v3f light_position, - video::SColorf light_color, - f32 light_radius); - #endif diff --git a/src/tile.cpp b/src/tile.cpp index da286fda9..1361d57e5 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "tile.h" +#include "irrlichttypes_extrabloated.h" #include "debug.h" #include "main.h" // for g_settings #include "filesys.h" @@ -238,7 +239,7 @@ public: return NULL; } // Primarily fetches from cache, secondarily tries to read from filesystem - video::IImage* getOrLoad(const std::string &name, IrrlichtDevice *device) + video::IImage* getOrLoad(const std::string &name, IrrlichtDevice *m_device) { std::map::iterator n; n = m_images.find(name); @@ -246,7 +247,7 @@ public: n->second->grab(); // Grab for caller return n->second; } - video::IVideoDriver* driver = device->getVideoDriver(); + video::IVideoDriver* driver = m_device->getVideoDriver(); std::string path = getTexturePath(name.c_str()); if(path == ""){ infostream<<"SourceImageCache::getOrLoad(): No path found for \"" @@ -378,6 +379,22 @@ public: // Shall be called from the main thread. void rebuildImagesAndTextures(); + // Render a mesh to a texture. + // Returns NULL if render-to-texture failed. + // Shall be called from the main thread. + video::ITexture* generateTextureFromMesh( + const TextureFromMeshParams ¶ms); + + // Generates an image from a full string like + // "stone.png^mineral_coal.png^[crack0". + // Shall be called from the main thread. + video::IImage* generateImageFromScratch(std::string name); + + // Generate image based on a string like "stone.png" or "[crack0". + // if baseimg is NULL, it is created. Otherwise stuff is made on it. + // Shall be called from the main thread. + bool generateImage(std::string part_of_name, video::IImage *& baseimg); + private: // The id of the thread that is allowed to use irrlicht directly @@ -406,6 +423,11 @@ private: // Textures that have been overwritten with other ones // but can't be deleted because the ITexture* might still be used std::list m_texture_trash; + + // Cached settings needed for making textures from meshes + bool m_setting_trilinear_filter; + bool m_setting_bilinear_filter; + bool m_setting_anisotropic_filter; }; IWritableTextureSource* createTextureSource(IrrlichtDevice *device) @@ -425,6 +447,13 @@ TextureSource::TextureSource(IrrlichtDevice *device): // Add a NULL TextureInfo as the first index, named "" m_textureinfo_cache.push_back(TextureInfo("")); m_name_to_id[""] = 0; + + // Cache some settings + // Note: Since this is only done once, the game must be restarted + // for these settings to take effect + m_setting_trilinear_filter = g_settings->getBool("trilinear_filter"); + m_setting_bilinear_filter = g_settings->getBool("bilinear_filter"); + m_setting_anisotropic_filter = g_settings->getBool("anisotropic_filter"); } TextureSource::~TextureSource() @@ -538,20 +567,6 @@ core::dimension2d imageTransformDimension(u32 transform, core::dimension2d< // Apply transform to image data void imageTransform(u32 transform, video::IImage *src, video::IImage *dst); -/* - Generate image based on a string like "stone.png" or "[crack0". - if baseimg is NULL, it is created. Otherwise stuff is made on it. -*/ -bool generate_image(std::string part_of_name, video::IImage *& baseimg, - IrrlichtDevice *device, SourceImageCache *sourcecache); - -/* - Generates an image from a full string like - "stone.png^mineral_coal.png^[crack0". -*/ -video::IImage* generate_image_from_scratch(std::string name, - IrrlichtDevice *device, SourceImageCache *sourcecache); - /* This method generates all the textures */ @@ -685,7 +700,7 @@ u32 TextureSource::getTextureIdDirect(const std::string &name) //infostream<<"last_part_of_name=\""<getVideoDriver()); m_source_image_existence.set(name, true); } - + void TextureSource::rebuildImagesAndTextures() { JMutexAutoLock lock(m_textureinfo_cache_mutex); @@ -800,8 +815,7 @@ void TextureSource::rebuildImagesAndTextures() // Recreate textures for(u32 i=0; iname, m_device, &m_sourcecache); + video::IImage *img = generateImageFromScratch(ti->name); // Create texture from resulting image video::ITexture *t = NULL; if(img) @@ -816,13 +830,90 @@ void TextureSource::rebuildImagesAndTextures() } } -video::IImage* generate_image_from_scratch(std::string name, - IrrlichtDevice *device, SourceImageCache *sourcecache) +video::ITexture* TextureSource::generateTextureFromMesh( + const TextureFromMeshParams ¶ms) { - /*infostream<<"generate_image_from_scratch(): " + video::IVideoDriver *driver = m_device->getVideoDriver(); + assert(driver); + + if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false) + { + static bool warned = false; + if(!warned) + { + errorstream<<"TextureSource::generateTextureFromMesh(): " + <<"EVDF_RENDER_TO_TARGET not supported."<addRenderTargetTexture( + params.dim, params.rtt_texture_name.c_str(), + video::ECF_A8R8G8B8); + if(rtt == NULL) + { + errorstream<<"TextureSource::generateTextureFromMesh(): " + <<"addRenderTargetTexture returned NULL."<setRenderTarget(rtt, false, true, video::SColor(0,0,0,0)); + + // Get a scene manager + scene::ISceneManager *smgr_main = m_device->getSceneManager(); + assert(smgr_main); + scene::ISceneManager *smgr = smgr_main->createNewSceneManager(); + assert(smgr); + + scene::IMeshSceneNode* meshnode = smgr->addMeshSceneNode(params.mesh, NULL, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true); + meshnode->setMaterialFlag(video::EMF_LIGHTING, true); + meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true); + meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter); + meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter); + meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter); + + scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0, + params.camera_position, params.camera_lookat); + // second parameter of setProjectionMatrix (isOrthogonal) is ignored + camera->setProjectionMatrix(params.camera_projection_matrix, false); + + smgr->setAmbientLight(params.ambient_light); + smgr->addLightSceneNode(0, + params.light_position, + params.light_color, + params.light_radius); + + // Render scene + driver->beginScene(true, true, video::SColor(0,0,0,0)); + smgr->drawAll(); + driver->endScene(); + + // NOTE: The scene nodes should not be dropped, otherwise + // smgr->drop() segfaults + /*cube->drop(); + camera->drop(); + light->drop();*/ + // Drop scene manager + smgr->drop(); + + // Unset render target + driver->setRenderTarget(0, false, true, 0); + + if(params.delete_texture_on_shutdown) + m_texture_trash.push_back(rtt); + + return rtt; +} + +video::IImage* TextureSource::generateImageFromScratch(std::string name) +{ + /*infostream<<"generateImageFromScratch(): " "\""<getVideoDriver(); + + video::IVideoDriver *driver = m_device->getVideoDriver(); assert(driver); /* @@ -835,12 +926,6 @@ video::IImage* generate_image_from_scratch(std::string name, // Find last meta separator in name s32 last_separator_position = name.find_last_of(separator); - //if(last_separator_position == std::npos) - // last_separator_position = -1; - - /*infostream<<"generate_image_from_scratch(): " - <<"last_separator_position="<getVideoDriver(); + video::IVideoDriver* driver = m_device->getVideoDriver(); assert(driver); // Stuff starting with [ are special commands if(part_of_name.size() == 0 || part_of_name[0] != '[') { - video::IImage *image = sourcecache->getOrLoad(part_of_name, device); + video::IImage *image = m_sourcecache.getOrLoad(part_of_name, m_device); if(image == NULL) { if(part_of_name != ""){ - errorstream<<"generate_image(): Could not load image \"" + errorstream<<"generateImage(): Could not load image \"" <getOrLoad( - "crack_anylength.png", device); + video::IImage *img_crack = m_sourcecache.getOrLoad( + "crack_anylength.png", m_device); if(img_crack && progression >= 0) { @@ -1080,7 +1159,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, infostream<<"Adding \""<getOrLoad(filename, device); + video::IImage *img = m_sourcecache.getOrLoad(filename, m_device); if(img) { core::dimension2d dim = img->getDimension(); @@ -1111,7 +1190,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, { if(baseimg == NULL) { - errorstream<<"generate_image(): baseimg==NULL " + errorstream<<"generateImage(): baseimg==NULL " <<"for part_of_name=\""<getMeshBuffer(4)->getMaterial().setTexture(0, texture_left); cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left); - core::dimension2d dim(64,64); - std::string rtt_texture_name = part_of_name + "_RTT"; - - v3f camera_position(0, 1.0, -1.5); - camera_position.rotateXZBy(45); - v3f camera_lookat(0, 0, 0); - core::CMatrix4 camera_projection_matrix; + TextureFromMeshParams params; + params.mesh = cube; + params.dim.set(64, 64); + params.rtt_texture_name = part_of_name + "_RTT"; + // We will delete the rtt texture ourselves + params.delete_texture_on_shutdown = false; + params.camera_position.set(0, 1.0, -1.5); + params.camera_position.rotateXZBy(45); + params.camera_lookat.set(0, 0, 0); // Set orthogonal projection - camera_projection_matrix.buildProjectionMatrixOrthoLH( + params.camera_projection_matrix.buildProjectionMatrixOrthoLH( 1.65, 1.65, 0, 100); - video::SColorf ambient_light(0.2,0.2,0.2); - v3f light_position(10, 100, -50); - video::SColorf light_color(0.5,0.5,0.5); - f32 light_radius = 1000; - - video::ITexture *rtt = generateTextureFromMesh( - cube, device, dim, rtt_texture_name, - camera_position, - camera_lookat, - camera_projection_matrix, - ambient_light, - light_position, - light_color, - light_radius); + params.ambient_light.set(1.0, 0.2, 0.2, 0.2); + params.light_position.set(10, 100, -50); + params.light_color.set(1.0, 0.5, 0.5, 0.5); + params.light_radius = 1000; + + video::ITexture *rtt = generateTextureFromMesh(params); // Drop mesh cube->drop(); @@ -1324,19 +1397,18 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, if(rtt == NULL) { - baseimg = generate_image_from_scratch( - imagename_top, device, sourcecache); + baseimg = generateImageFromScratch(imagename_top); return true; } // Create image of render target - video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim); + video::IImage *image = driver->createImage(rtt, v2s32(0,0), params.dim); assert(image); - //cleanup texture + // Cleanup texture driver->removeTexture(rtt); - baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); + baseimg = driver->createImage(video::ECF_A8R8G8B8, params.dim); if(image) { @@ -1358,7 +1430,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, if(baseimg == NULL) baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16)); - video::IImage *img = sourcecache->getOrLoad(filename, device); + video::IImage *img = m_sourcecache.getOrLoad(filename, m_device); if(img) { core::dimension2d dim = img->getDimension(); @@ -1392,7 +1464,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, u32 frame_index = stoi(sf.next(":")); if(baseimg == NULL){ - errorstream<<"generate_image(): baseimg!=NULL " + errorstream<<"generateImage(): baseimg!=NULL " <<"for part_of_name=\""<createImage(video::ECF_A8R8G8B8, frame_size); if(!img){ - errorstream<<"generate_image(): Could not create image " + errorstream<<"generateImage(): Could not create image " <<"for part_of_name=\""< dim; + std::string rtt_texture_name; + bool delete_texture_on_shutdown; + v3f camera_position; + v3f camera_lookat; + core::CMatrix4 camera_projection_matrix; + video::SColorf ambient_light; + v3f light_position; + video::SColorf light_color; + f32 light_radius; +}; + /* TextureSource creates and caches textures. */ @@ -78,6 +97,8 @@ public: virtual IrrlichtDevice* getDevice() {return NULL;} virtual bool isKnownSourceImage(const std::string &name)=0; + virtual video::ITexture* generateTextureFromMesh( + const TextureFromMeshParams ¶ms)=0; }; class IWritableTextureSource : public ITextureSource @@ -100,6 +121,8 @@ public: virtual void processQueue()=0; virtual void insertSourceImage(const std::string &name, video::IImage *img)=0; virtual void rebuildImagesAndTextures()=0; + virtual video::ITexture* generateTextureFromMesh( + const TextureFromMeshParams ¶ms)=0; }; IWritableTextureSource* createTextureSource(IrrlichtDevice *device);