From 1704badc306fc8c7c6609aff9f809aee3ac00d3a Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Thu, 10 Feb 2011 02:13:03 +0200 Subject: [PATCH] work-in-progress texture atlas optimization --- data/water.png | Bin 1036 -> 1033 bytes src/CMakeLists.txt | 1 + src/environment.cpp | 4 +- src/iirrlichtwrapper.h | 12 +- src/inventory.cpp | 8 +- src/inventory.h | 19 +- src/irrlichtwrapper.cpp | 165 ++++++++- src/irrlichtwrapper.h | 66 ++-- src/main.cpp | 19 +- src/main.h | 9 +- src/map.cpp | 91 +---- src/map.h | 2 + src/mapblock.cpp | 190 +++++++--- src/mapnode.cpp | 124 ++++--- src/mapnode.h | 39 +- src/mineral.cpp | 12 +- src/mineral.h | 3 +- src/server.cpp | 2 + src/texture.h | 10 + src/tile.cpp | 770 +++++++++++++++++++++++++++++++++++++++- src/tile.h | 219 +++++++++++- 21 files changed, 1495 insertions(+), 270 deletions(-) diff --git a/data/water.png b/data/water.png index a5347f61da8c44cb67af6a7d115de442cde9cff8..a304059e3f37b31c3fec8e8d6a57186842aba751 100644 GIT binary patch delta 261 zcmV+g0s8)o2#E+GiBL{Q4GJ0x0000DNk~Le0000G0000G2m$~A0FaSrHnAZO1AoLx zL_t(2&y7+s3d1lAj9Y;rBt!5}FoZ7sLtoG@^w0f6w}3a3!2y#YO$U`j$VppG_T5>! zvu&!=z1k`3Jl1mzydXgCa-GIO(h%YB90Q@vG!9ZBCotZ0{TzdVB}J=~01uL2R9d&r z^A;e%ob<~Ok8rJx}~~Za>56DnRUs@I?t5Z{H-dwoJs$@n#}zMYKVE>R_cOl00000 LNkvXXu0mjf@XT#; delta 264 zcmV+j0r&oi2#g3JiBL{Q4GJ0x0000DNk~Le0000H0000G2m$~A0Dn^h2(cj#1AoU! zL_t(2&y7++3c^4P?CQWm=^n}+goWb8KllQ_;Gg}2w}Q9QLkq=2)k7o<*_E18I+-Ms z>ALE0t$u`$N%QCM&>Wh@(|I0yW-w3s1%VEm$DT>NNARZYByp*9in_water_stable) + if(player->in_water_stable || player->in_water) { - f32 max_down = 1.5*BS; + f32 max_down = 2.0*BS; if(speed.Y < -max_down) speed.Y = -max_down; f32 max = 2.5*BS; diff --git a/src/iirrlichtwrapper.h b/src/iirrlichtwrapper.h index 47ae21ee9..66f8a55c6 100644 --- a/src/iirrlichtwrapper.h +++ b/src/iirrlichtwrapper.h @@ -21,6 +21,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #define IIRRLICHTWRAPPER_HEADER #include "common_irrlicht.h" +#include "texture.h" + +/* + NOTE: This is deprecated and should be removed completely +*/ /* IrrlichtWrapper prototype. @@ -38,15 +43,18 @@ public: { } + // Should be called only from the main thread + virtual IrrlichtDevice* getDevice(){ return NULL; } + virtual u32 getTime() { return 0; } - virtual textureid_t getTextureId(const std::string &name){ return 0; } + /*virtual textureid_t getTextureId(const std::string &name){ return 0; } virtual std::string getTextureName(textureid_t id){ return ""; } virtual video::ITexture* getTexture(const std::string &name){ return NULL; } - virtual video::ITexture* getTexture(const TextureSpec &spec){ return NULL; } + virtual video::ITexture* getTexture(const TextureSpec &spec){ return NULL; }*/ private: }; diff --git a/src/inventory.cpp b/src/inventory.cpp index 5030d67d1..c29bb9470 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -96,13 +96,17 @@ InventoryItem* InventoryItem::deSerialize(std::istream &is) #ifndef SERVER video::ITexture * MapBlockObjectItem::getImage() { + //TODO + if(m_inventorystring.substr(0,3) == "Rat") //return g_device->getVideoDriver()->getTexture(porting::getDataPath("rat.png").c_str()); - return g_irrlicht->getTexture("rat.png"); + //return g_irrlicht->getTexture("rat.png"); + return NULL; if(m_inventorystring.substr(0,4) == "Sign") //return g_device->getVideoDriver()->getTexture(porting::getDataPath("sign.png").c_str()); - return g_irrlicht->getTexture("sign.png"); + //return g_irrlicht->getTexture("sign.png"); + return NULL; return NULL; } diff --git a/src/inventory.h b/src/inventory.h index e7c7adaee..ca07bf46d 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -122,12 +122,9 @@ public: #ifndef SERVER video::ITexture * getImage() { - /*if(m_content >= USEFUL_CONTENT_COUNT) - return NULL; - - return g_irrlicht->getTexture(g_content_inventory_texture_paths[m_content]);*/ - - return g_irrlicht->getTexture(content_features(m_content).inventory_texture); + //TODO + //return g_irrlicht->getTexture(content_features(m_content).inventory_texture); + return NULL; } #endif std::string getText() @@ -264,7 +261,9 @@ public: name = "cloud.png"; // Get such a texture - return g_irrlicht->getTexture(name); + //return g_irrlicht->getTexture(name); + //TODO + return NULL; } #endif std::string getText() @@ -351,10 +350,12 @@ public: std::ostringstream os; os<<"[progressbar"<getTextureId(basename)); spec.addTid(g_irrlicht->getTextureId(os.str())); - return g_irrlicht->getTexture(spec); + return g_irrlicht->getTexture(spec);*/ + //TODO + return NULL; /*// Make texture name for the new texture with a progress bar float value_f = (float)toolprogress / (float)maxprogress; diff --git a/src/irrlichtwrapper.cpp b/src/irrlichtwrapper.cpp index 4ad647194..4a1f2c413 100644 --- a/src/irrlichtwrapper.cpp +++ b/src/irrlichtwrapper.cpp @@ -11,8 +11,22 @@ IrrlichtWrapper::IrrlichtWrapper(IrrlichtDevice *device) m_device = device; } +IrrlichtWrapper::~IrrlichtWrapper() +{ +#if 0 + // Clear image cache + for(core::map::Iterator + i = m_imagecache.getIterator(); + i.atEnd() == false; i++) + { + i.getNode()->getValue()->drop(); + } +#endif +} + void IrrlichtWrapper::Run() { +#if 0 /* Fetch textures */ @@ -34,6 +48,7 @@ void IrrlichtWrapper::Run() request.dest->push_back(result); } +#endif } void IrrlichtWrapper::Shutdown(bool shutdown) @@ -41,6 +56,18 @@ void IrrlichtWrapper::Shutdown(bool shutdown) m_running = !shutdown; } +IrrlichtDevice* IrrlichtWrapper::getDevice() +{ + if(get_current_thread_id() != m_main_thread) + { + dstream<<"WARNING: IrrlichtWrapper::getDevice() called " + "not from main thread"<::Node *n; + n = m_imagecache.find(texture_name); + if(n != NULL) + { + video::IImage *image = n->getValue(); + + core::dimension2d dim = image->getDimension(); + baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); + image->copyTo(baseimg); + dstream<<"INFO: getTextureDirect(): Loaded \"" + < dim = image->getDimension(); //core::dimension2d dim(16,16); @@ -229,27 +280,85 @@ video::ITexture* IrrlichtWrapper::getTextureDirect(const TextureSpec &spec) else { // A special texture modification - dstream<<"getTextureDirect(): generating \""< dim(16, 16); + core::dimension2d dim_base = baseimg->getDimension(); + // Crack will be drawn at this size + u32 cracksize = 16; // Size of the crack image - //core::dimension2d dim_crack(16, 16 * CRACK_ANIMATION_LENGTH); - // Position to copy the crack to in the base image - core::position2d pos_base(0, 0); + core::dimension2d dim_crack(cracksize,cracksize); // Position to copy the crack from in the crack image core::position2d pos_other(0, 16 * progression); video::IImage *crackimage = driver->createImageFromFile( porting::getDataPath("crack.png").c_str()); - crackimage->copyToWithAlpha(baseimg, v2s32(0,0), - core::rect(pos_other, dim), - video::SColor(255,255,255,255), - NULL); - crackimage->drop(); + + if(crackimage) + { + /*crackimage->copyToWithAlpha(baseimg, v2s32(0,0), + core::rect(pos_other, dim_base), + video::SColor(255,255,255,255), + NULL);*/ + + for(u32 y0=0; y0 pos_base(x0*cracksize, y0*cracksize); + crackimage->copyToWithAlpha(baseimg, pos_base, + core::rect(pos_other, dim_crack), + video::SColor(255,255,255,255), + NULL); + } + + crackimage->drop(); + } + } + else if(name.substr(0,8) == "[combine") + { + // "[combine:16x128:0,0=stone.png:0,16=grass.png" + Strfnd sf(name); + sf.next(":"); + u32 w0 = stoi(sf.next("x")); + u32 h0 = stoi(sf.next(":")); + dstream<<"INFO: combined w="<createImageFromFile( + porting::getDataPath(filename.c_str()).c_str()); + if(img) + { + core::dimension2d dim = img->getDimension(); + dstream<<"INFO: Size "< pos_base(x, y); + video::IImage *img2 = + driver->createImage(video::ECF_A8R8G8B8, dim); + img->copyTo(img2); + img->drop(); + img2->copyToWithAlpha(baseimg, pos_base, + core::rect(v2s32(0,0), dim), + video::SColor(255,255,255,255), + NULL); + img2->drop(); + } + else + { + dstream<<"WARNING: img==NULL"<::Node *n; + n = m_imagecache.find(texture_name); + if(n != NULL) + { + video::IImage *img = n->getValue(); + if(img != baseimg) + { + img->drop(); + } + } + + m_imagecache[texture_name] = baseimg; + } } // If no resulting image, return NULL if(baseimg == NULL) { - dstream<<"getTextureDirect(): baseimg is NULL (attempted to" + dstream<<"WARNING: getTextureDirect(): baseimg is NULL (attempted to" " create texture \""<addTexture(texture_name.c_str(), baseimg); - baseimg->drop(); - dstream<<"getTextureDirect(): created texture \""< #include +/* + NOTE: This is deprecated and should be removed completely +*/ + /* A thread-safe texture pointer cache. @@ -38,45 +42,8 @@ with this program; if not, write to the Free Software Foundation, Inc., threads, because texture pointers have to be handled in background threads. */ -#if 0 -class TextureCache -{ -public: - TextureCache() - { - m_mutex.Init(); - assert(m_mutex.IsInitialized()); - } - - void set(std::string name, video::ITexture *texture) - { - if(texture == NULL) - return; - - JMutexAutoLock lock(m_mutex); - - m_textures[name] = texture; - } - - video::ITexture* get(const std::string &name) - { - JMutexAutoLock lock(m_mutex); - - core::map::Node *n; - n = m_textures.find(name); - - if(n != NULL) - return n->getValue(); - - return NULL; - } - -private: - core::map m_textures; - JMutex m_mutex; -}; -#endif +#if 0 /* A thread-safe texture pointer cache */ @@ -116,6 +83,7 @@ private: core::map m_textures; JMutex m_mutex; }; +#endif /* A thread-safe wrapper for irrlicht, to be accessed from @@ -124,6 +92,8 @@ private: Queues tasks to be done in the main thread. Also caches texture specification strings to ids and textures. + + TODO: Remove this and move all texture functionality to TextureSource */ class IrrlichtWrapper : public IIrrlichtWrapper @@ -134,6 +104,8 @@ public: */ IrrlichtWrapper(IrrlichtDevice *device); + + ~IrrlichtWrapper(); // Run queued tasks void Run(); @@ -141,6 +113,8 @@ public: // Shutdown wrapper; this disables queued texture fetching void Shutdown(bool shutdown); + IrrlichtDevice* getDevice(); + /* These are called from other threads */ @@ -151,7 +125,8 @@ public: { return m_device->getTimer()->getRealTime(); } - + +#if 0 /* Format of a texture name: "stone.png" (filename in image data directory) @@ -167,20 +142,18 @@ public: // The reverse of the above std::string getTextureName(textureid_t id); // Gets a texture based on a filename - video::ITexture* getTexture(const std::string &name); + video::ITexture* getTexture(const std::string &filename); // Gets a texture based on a TextureSpec (a textureid_t is fine too) video::ITexture* getTexture(const TextureSpec &spec); +#endif private: /* Non-thread-safe variants of stuff, for internal use */ - // DEPRECATED NO-OP - //video::ITexture* getTextureDirect(const std::string &spec); - // Constructs a texture according to spec - video::ITexture* getTextureDirect(const TextureSpec &spec); + //video::ITexture* getTextureDirect(const TextureSpec &spec); /* Members @@ -195,14 +168,19 @@ private: JMutex m_device_mutex; IrrlichtDevice *m_device; +#if 0 // Queued texture fetches (to be processed by the main thread) RequestQueue m_get_texture_queue; // Cache of textures by spec TextureCache m_texturecache; + // Cached or generated source images by texture name + core::map m_imagecache; + // A mapping from texture id to string spec MutexedIdGenerator m_namecache; +#endif }; #endif diff --git a/src/main.cpp b/src/main.cpp index ff24fbb87..1e30219c5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -97,6 +97,9 @@ SUGG: Meshes of blocks could be split into 6 meshes facing into different directions and then only those drawn that need to be - Also an 1-dimensional tile map would be nice probably +SUGG: Calculate lighting per vertex to get a lighting effect like in + bartwe's game + Gaming ideas: ------------- @@ -363,8 +366,13 @@ Doing now (most important at the top): #include "guiMainMenu.h" #include "mineral.h" #include "noise.h" +#include "tile.h" + +// TODO: Remove this +IrrlichtWrapper *g_irrlicht = NULL; -IrrlichtWrapper *g_irrlicht; +// This makes textures +TextureSource *g_texturesource = NULL; MapDrawControl draw_control; @@ -1643,6 +1651,7 @@ int main(int argc, char *argv[]) g_device = device; g_irrlicht = new IrrlichtWrapper(device); + g_texturesource = new TextureSource(device); /* Speed tests (done after irrlicht is loaded to get timer) @@ -1670,7 +1679,8 @@ int main(int argc, char *argv[]) video::IVideoDriver* driver = device->getVideoDriver(); /* - This changes the minimum allowed number of vertices in a VBO + This changes the minimum allowed number of vertices in a VBO. + Default is 500. */ //driver->setMinHardwareBufferVertexCount(50); @@ -2061,6 +2071,11 @@ int main(int argc, char *argv[]) */ g_irrlicht->Run(); + /* + Process TextureSource's queue + */ + g_texturesource->processQueue(); + /* Random calculations */ diff --git a/src/main.h b/src/main.h index e6bb1caac..9978c537a 100644 --- a/src/main.h +++ b/src/main.h @@ -20,15 +20,20 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef MAIN_HEADER #define MAIN_HEADER -#include "irrlichtwrapper.h" - // Settings +#include "utility.h" extern Settings g_settings; // A thread safe wrapper to irrlicht // On a server build, this is always NULL. +// TODO: Remove this +#include "irrlichtwrapper.h" extern IrrlichtWrapper *g_irrlicht; +// This makes and maps textures +#include "tile.h" +extern TextureSource *g_texturesource; + // Debug streams #include diff --git a/src/map.cpp b/src/map.cpp index 696019f3a..e4992618d 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1974,7 +1974,7 @@ double tree_amount_2d(u64 seed, v2s16 p) { double noise = noise2d_perlin( 0.5+(float)p.X/250, 0.5+(float)p.Y/250, - seed+2, 5, 0.6); + seed+2, 5, 0.66); double zeroval = -0.3; if(noise < zeroval) return 0; @@ -2021,9 +2021,9 @@ double base_rock_level_2d(u64 seed, v2s16 p) base = base2;*/ #if 1 // Higher ground level - double higher = (double)WATER_LEVEL + 13. + 50. * noise2d_perlin( - 0.5+(float)p.X/500., 0.5+(float)p.Y/500., - seed+85039, 6, 0.69); + double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin( + 0.5+(float)p.X/250., 0.5+(float)p.Y/250., + seed+85039, 5, 0.69); //higher = 30; // For debugging // Limit higher to at least base @@ -2042,7 +2042,7 @@ double base_rock_level_2d(u64 seed, v2s16 p) //double b = 20; // Offset to more low - double a_off = -0.3; + double a_off = -0.15; // High/low selector /*double a = 0.5 + b * (a_off + noise2d_perlin( 0.5+(float)p.X/500., 0.5+(float)p.Y/500., @@ -2414,12 +2414,12 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, /* Make dungeons */ - //u32 dungeons_count = relative_volume / 600000; - /*u32 bruises_count = relative_volume * stone_surface_max_y / 40000000; + u32 dungeons_count = relative_volume / 600000; + u32 bruises_count = relative_volume * stone_surface_max_y / 40000000; if(stone_surface_max_y < WATER_LEVEL) - bruises_count = 0;*/ - u32 dungeons_count = 0; - u32 bruises_count = 0; + bruises_count = 0; + /*u32 dungeons_count = 0; + u32 bruises_count = 0;*/ for(u32 jj=0; jj 50) { + timecheck_counter = 0; int time2 = time(0); if(time2 > time1 + 4) { @@ -5167,71 +5168,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) continue; } -#if 0 - v3s16 blockpos_nodes = block->getPosRelative(); - - // Block center position - v3f blockpos( - ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS, - ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS, - ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS - ); - - // Block position relative to camera - v3f blockpos_relative = blockpos - camera_position; - - // Distance in camera direction (+=front, -=back) - f32 dforward = blockpos_relative.dotProduct(camera_direction); - - // Total distance - f32 d = blockpos_relative.getLength(); - - if(m_control.range_all == false) - { - // If block is far away, don't draw it - if(d > m_control.wanted_range * BS) - continue; - } - - // Maximum radius of a block - f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS; - - // If block is (nearly) touching the camera, don't - // bother validating further (that is, render it anyway) - if(d > block_max_radius * 1.5) - { - // Cosine of the angle between the camera direction - // and the block direction (camera_direction is an unit vector) - f32 cosangle = dforward / d; - - // Compensate for the size of the block - // (as the block has to be shown even if it's a bit off FOV) - // This is an estimate. - cosangle += block_max_radius / dforward; - - // If block is not in the field of view, skip it - //if(cosangle < cos(FOV_ANGLE/2)) - if(cosangle < cos(FOV_ANGLE/2. * 4./3.)) - continue; - } -#endif -#if 0 - v3s16 blockpos_nodes = block->getPosRelative(); - - // Block center position - v3f blockpos( - ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS, - ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS, - ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS - ); - - // Block position relative to camera - v3f blockpos_relative = blockpos - camera_position; - - // Total distance - f32 d = blockpos_relative.getLength(); -#endif - #if 1 /* Update expired mesh @@ -5324,6 +5260,11 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) // Render transparent on transparent pass and likewise. if(transparent == is_transparent_pass) { + /* + This *shouldn't* hurt too much because Irrlicht + doesn't change opengl textures if the old + material is set again. + */ driver->setMaterial(buf->getMaterial()); driver->drawMeshBuffer(buf); vertex_count += buf->getVertexCount(); diff --git a/src/map.h b/src/map.h index b3ef47282..d18278834 100644 --- a/src/map.h +++ b/src/map.h @@ -513,6 +513,8 @@ public: // For debug printing virtual void PrintInfo(std::ostream &out); + bool isSavingEnabled(){ return m_map_saving_enabled; } + private: // Seed used for all kinds of randomness u64 m_seed; diff --git a/src/mapblock.cpp b/src/mapblock.cpp index e3e90a9a1..2ec63dbde 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -273,13 +273,32 @@ void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p, video::SColor c = video::SColor(alpha,li,li,li); face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c, - core::vector2d(0,1)); - face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c, core::vector2d(abs_scale,1)); + face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c, + core::vector2d(0,1)); face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c, + core::vector2d(0,0)); + face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c, core::vector2d(abs_scale,0)); + + /*float x0 = (float)tile.tx/256.0; + float y0 = (float)tile.ty/256.0; + float w = ((float)tile.tw + 1.0)/256.0; + float h = ((float)tile.th + 1.0)/256.0;*/ + + float x0 = tile.texture.pos.X; + float y0 = tile.texture.pos.Y; + float w = tile.texture.size.X; + float h = tile.texture.size.Y; + + face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c, + core::vector2d(x0+w*abs_scale, y0+h)); + face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c, + core::vector2d(x0, y0+h)); + face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c, + core::vector2d(x0, y0)); face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c, - core::vector2d(0,0)); + core::vector2d(x0+w*abs_scale, y0)); face.tile = tile; //DEBUG @@ -318,11 +337,26 @@ TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir, } if(mod.type == NODEMOD_CRACK) { + /* + Get texture id, translate it to name, append stuff to + name, get texture id + */ + // Get original texture name + u32 orig_id = spec.texture.id; + std::string orig_name = g_texturesource->getTextureName(orig_id); + // Create new texture name std::ostringstream os; - os<<"[crack"<getTextureId(os.str()); - spec.spec.addTid(tid); + os<getTextureId(os.str()); + + dstream<<"MapBlock::getNodeTile(): Switching from " + <getTexture(new_id); } } @@ -406,7 +440,9 @@ void MapBlock::updateFastFaceRow( TileSpec tile0_next; TileSpec tile1_next; u8 light_next = 0; - + + // If at last position, there is nothing to compare to and + // the face must be drawn anyway if(j != length - 1) { p_next = p + translate_dir; @@ -426,7 +462,31 @@ void MapBlock::updateFastFaceRow( continuous_tiles_count++; - if(next_is_different) + // This is set to true if the texture doesn't allow more tiling + bool end_of_texture = false; + /* + If there is no texture, it can be tiled infinitely. + If tiled==0, it means the texture can be tiled infinitely. + Otherwise check tiled agains continuous_tiles_count. + + This check has to be made for both tiles, because this is + a bit hackish and we know which one we're using only when + the decision to make the faces is made. + */ + if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0) + { + if(tile0.texture.tiled <= continuous_tiles_count) + end_of_texture = true; + } + if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0) + { + if(tile1.texture.tiled <= continuous_tiles_count) + end_of_texture = true; + } + + //end_of_texture = true; //DEBUG + + if(next_is_different || end_of_texture) { /* Create a face if there should be one @@ -684,10 +744,6 @@ void MapBlock::updateMesh(u32 daynight_ratio) Convert FastFaces to SMesh */ - scene::SMesh *mesh_new = NULL; - - mesh_new = new scene::SMesh(); - MeshCollector collector; if(fastfaces_new.size() > 0) @@ -708,8 +764,9 @@ void MapBlock::updateMesh(u32 daynight_ratio) FastFace &f = fastfaces_new[i]; const u16 indices[] = {0,1,2,2,3,0}; - - video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec); + + //video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec); + video::ITexture *texture = f.tile.texture.atlas; if(texture == NULL) continue; @@ -717,11 +774,6 @@ void MapBlock::updateMesh(u32 daynight_ratio) f.tile.applyMaterialOptions(material); - /*if(f.tile.alpha != 255) - material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; - else - material.MaterialType = video::EMT_SOLID;*/ - collector.append(material, f.vertices, 4, indices, 6); } } @@ -742,7 +794,11 @@ void MapBlock::updateMesh(u32 daynight_ratio) material_water1.setFlag(video::EMF_BILINEAR_FILTER, false); material_water1.setFlag(video::EMF_FOG_ENABLE, true); material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; - material_water1.setTexture(0, g_irrlicht->getTexture("water.png")); + //TODO + //material_water1.setTexture(0, g_irrlicht->getTexture("water.png")); + AtlasPointer pa_water1 = g_texturesource->getTexture( + g_texturesource->getTextureId("water.png")); + material_water1.setTexture(0, pa_water1.atlas); // New-style leaves material video::SMaterial material_leaves1; @@ -751,7 +807,11 @@ void MapBlock::updateMesh(u32 daynight_ratio) material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false); material_leaves1.setFlag(video::EMF_FOG_ENABLE, true); material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png")); + //TODO + //material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png")); + AtlasPointer pa_leaves1 = g_texturesource->getTexture( + g_texturesource->getTextureId("leaves.png")); + material_leaves1.setTexture(0, pa_leaves1.atlas); for(s16 z=0; zgetTexture("torch_on_floor.png")); else if(dir == v3s16(0,1,0)) @@ -816,7 +877,7 @@ void MapBlock::updateMesh(u32 daynight_ratio) g_irrlicht->getTexture("torch_on_floor.png")); else material.setTexture(0, - g_irrlicht->getTexture("torch.png")); + g_irrlicht->getTexture("torch.png"));*/ u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector @@ -974,14 +1035,18 @@ void MapBlock::updateMesh(u32 daynight_ratio) video::S3DVertex vertices[4] = { - /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/ - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1), + /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/ + video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, + pa_water1.x0(), pa_water1.y1()), + video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, + pa_water1.x1(), pa_water1.y1()), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, + pa_water1.x1(), pa_water1.y0()), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, + pa_water1.x0(), pa_water1.y0()), }; /* @@ -1048,10 +1113,18 @@ void MapBlock::updateMesh(u32 daynight_ratio) { video::S3DVertex vertices[4] = { - video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1), + /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1), video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/ + video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, + pa_water1.x0(), pa_water1.y1()), + video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, + pa_water1.x1(), pa_water1.y1()), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, + pa_water1.x1(), pa_water1.y0()), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, + pa_water1.x0(), pa_water1.y0()), }; for(s32 i=0; i<4; i++) @@ -1092,10 +1165,18 @@ void MapBlock::updateMesh(u32 daynight_ratio) video::S3DVertex vertices[4] = { - video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1), + /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1), video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/ + video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, + pa_water1.x0(), pa_water1.y1()), + video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, + pa_water1.x1(), pa_water1.y1()), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, + pa_water1.x1(), pa_water1.y0()), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, + pa_water1.x0(), pa_water1.y0()), }; for(s32 i=0; i<4; i++) @@ -1120,10 +1201,18 @@ void MapBlock::updateMesh(u32 daynight_ratio) { video::S3DVertex vertices[4] = { - video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1), + /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1), video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1), video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0), + video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/ + video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, + pa_leaves1.x0(), pa_leaves1.y1()), + video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, + pa_leaves1.x1(), pa_leaves1.y1()), + video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, + pa_leaves1.x1(), pa_leaves1.y0()), + video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, + pa_leaves1.x0(), pa_leaves1.y0()), }; if(j == 0) @@ -1173,6 +1262,9 @@ void MapBlock::updateMesh(u32 daynight_ratio) Add stuff from collector to mesh */ + scene::SMesh *mesh_new = NULL; + mesh_new = new scene::SMesh(); + collector.fillMesh(mesh_new); /* @@ -1191,13 +1283,25 @@ void MapBlock::updateMesh(u32 daynight_ratio) mesh_new = NULL; } - // Use VBO for mesh (this just would set this for ever buffer) - // This will lead to infinite memory usage because or irrlicht. - //mesh_new->setHardwareMappingHint(scene::EHM_STATIC); - - /*std::cout<<"MapBlock has "<getMeshBufferCount() - <<" materials (meshbuffers)"<getMeshBufferCount() + <<" materials (meshbuffers)"<setHardwareMappingHint(scene::EHM_STATIC); + + /* + NOTE: If that is enabled, some kind of a queue to the main + thread should be made which would call irrlicht to delete + the hardware buffer and then delete the mesh + */ + } /* Replace the mesh diff --git a/src/mapnode.cpp b/src/mapnode.cpp index e9a6ba904..653195ad4 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "common_irrlicht.h" #include "mapnode.h" #include "tile.h" #include "porting.h" @@ -31,6 +32,20 @@ ContentFeatures::~ContentFeatures() delete translate_to; } +void ContentFeatures::setTexture(u16 i, std::string name, u8 alpha) +{ + if(g_texturesource) + { + tiles[i].texture = g_texturesource->getTexture(name); + } + + if(alpha != 255) + { + tiles[i].alpha = alpha; + tiles[i].material_type = MATERIAL_ALPHA_VERTEX; + } +} + struct ContentFeatures g_content_features[256]; ContentFeatures & content_features(u8 i) @@ -40,56 +55,75 @@ ContentFeatures & content_features(u8 i) void init_mapnode(IIrrlichtWrapper *irrlicht) { + // Read some settings bool new_style_water = g_settings.getBool("new_style_water"); bool new_style_leaves = g_settings.getBool("new_style_leaves"); + + /* + Initialize content feature table + */ + + /* + Set initial material type to same in all tiles, so that the + same material can be used in more stuff. + This is set according to the leaves because they are the only + differing material to which all materials can be changed to + get this optimization. + */ + u8 initial_material_type = MATERIAL_ALPHA_SIMPLE; + /*if(new_style_leaves) + initial_material_type = MATERIAL_ALPHA_SIMPLE; + else + initial_material_type = MATERIAL_ALPHA_NONE;*/ + for(u16 i=0; i<256; i++) + { + ContentFeatures *f = &g_content_features[i]; + for(u16 j=0; j<6; j++) + f->tiles[j].material_type = initial_material_type; + } u8 i; ContentFeatures *f = NULL; i = CONTENT_STONE; f = &g_content_features[i]; - f->setAllTextures(irrlicht->getTextureId("stone.png")); + f->setAllTextures("stone.png"); f->param_type = CPT_MINERAL; f->is_ground_content = true; i = CONTENT_GRASS; f = &g_content_features[i]; - f->setAllTextures(TextureSpec(irrlicht->getTextureId("mud.png"), - irrlicht->getTextureId("grass_side.png"))); - f->setTexture(0, irrlicht->getTextureId("grass.png")); - f->setTexture(1, irrlicht->getTextureId("mud.png")); - f->setInventoryTexture(irrlicht->getTextureId("grass.png")); + f->setAllTextures("mud.png^grass_side.png"); + f->setTexture(0, "grass.png"); + f->setTexture(1, "mud.png"); + //f->setInventoryTexture(irrlicht->getTextureId("grass.png")); f->param_type = CPT_MINERAL; f->is_ground_content = true; i = CONTENT_GRASS_FOOTSTEPS; f = &g_content_features[i]; - f->setAllTextures(TextureSpec(irrlicht->getTextureId("mud.png"), - irrlicht->getTextureId("grass_side.png"))); - f->setTexture(0, irrlicht->getTextureId("grass_footsteps.png")); - f->setTexture(1, irrlicht->getTextureId("mud.png")); - f->setInventoryTexture(irrlicht->getTextureId("grass_footsteps.png")); + //f->setInventoryTexture(irrlicht->getTextureId("grass_footsteps.png")); f->param_type = CPT_MINERAL; f->is_ground_content = true; i = CONTENT_MUD; f = &g_content_features[i]; - f->setAllTextures(irrlicht->getTextureId("mud.png")); + f->setAllTextures("mud.png"); f->param_type = CPT_MINERAL; f->is_ground_content = true; i = CONTENT_SAND; f = &g_content_features[i]; - f->setAllTextures(irrlicht->getTextureId("sand.png")); + f->setAllTextures("sand.png"); f->param_type = CPT_MINERAL; f->is_ground_content = true; i = CONTENT_TREE; f = &g_content_features[i]; - f->setAllTextures(irrlicht->getTextureId("tree.png")); - f->setTexture(0, irrlicht->getTextureId("tree_top.png")); - f->setTexture(1, irrlicht->getTextureId("tree_top.png")); - f->setInventoryTexture(irrlicht->getTextureId("tree_top.png")); + f->setAllTextures("tree.png"); + f->setTexture(0, "tree_top.png"); + f->setTexture(1, "tree_top.png"); + //f->setInventoryTexture(irrlicht->getTextureId("tree_top.png")); f->param_type = CPT_MINERAL; f->is_ground_content = true; @@ -105,36 +139,33 @@ void init_mapnode(IIrrlichtWrapper *irrlicht) } else { - f->setAllTextures(irrlicht->getTextureId("leaves.png")); + f->setAllTextures("[noalpha:leaves.png"); } - /*{ - TileSpec t; - t.spec = TextureSpec(irrlicht->getTextureId("leaves.png")); - //t.material_type = MATERIAL_ALPHA_SIMPLE; - //t.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; - f->setAllTiles(t); - }*/ i = CONTENT_COALSTONE; f = &g_content_features[i]; //f->translate_to = new MapNode(CONTENT_STONE, MINERAL_COAL); - f->setAllTextures(TextureSpec(irrlicht->getTextureId("coal.png"), - irrlicht->getTextureId("mineral_coal.png"))); + /*f->setAllTextures(TextureSpec(irrlicht->getTextureId("coal.png"), + irrlicht->getTextureId("mineral_coal.png")));*/ + f->setAllTextures("stone.png^mineral_coal.png"); f->is_ground_content = true; i = CONTENT_WOOD; f = &g_content_features[i]; - f->setAllTextures(irrlicht->getTextureId("wood.png")); + //f->setAllTextures(irrlicht->getTextureId("wood.png")); + f->setAllTextures("wood.png"); f->is_ground_content = true; i = CONTENT_MESE; f = &g_content_features[i]; - f->setAllTextures(irrlicht->getTextureId("mese.png")); + //f->setAllTextures(irrlicht->getTextureId("mese.png")); + f->setAllTextures("mese.png"); f->is_ground_content = true; i = CONTENT_CLOUD; f = &g_content_features[i]; - f->setAllTextures(irrlicht->getTextureId("cloud.png")); + //f->setAllTextures(irrlicht->getTextureId("cloud.png")); + f->setAllTextures("cloud.png"); f->is_ground_content = true; i = CONTENT_AIR; @@ -150,7 +181,7 @@ void init_mapnode(IIrrlichtWrapper *irrlicht) i = CONTENT_WATER; f = &g_content_features[i]; - f->setInventoryTexture(irrlicht->getTextureId("water.png")); + //f->setInventoryTexture(irrlicht->getTextureId("water.png")); f->param_type = CPT_LIGHT; f->light_propagates = true; f->solidness = 0; // Drawn separately, makes no faces @@ -162,21 +193,23 @@ void init_mapnode(IIrrlichtWrapper *irrlicht) i = CONTENT_WATERSOURCE; f = &g_content_features[i]; - f->setInventoryTexture(irrlicht->getTextureId("water.png")); + //f->setInventoryTexture(irrlicht->getTextureId("water.png")); if(new_style_water) { f->solidness = 0; // drawn separately, makes no faces } else // old style { - f->setAllTextures(irrlicht->getTextureId("water.png"), WATER_ALPHA); + f->solidness = 1; + TileSpec t; - t.spec = TextureSpec(irrlicht->getTextureId("water.png")); + if(g_texturesource) + t.texture = g_texturesource->getTexture("water.png"); + t.alpha = WATER_ALPHA; t.material_type = MATERIAL_ALPHA_VERTEX; t.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING; f->setAllTiles(t); - f->solidness = 1; } f->param_type = CPT_LIGHT; f->light_propagates = true; @@ -188,7 +221,6 @@ void init_mapnode(IIrrlichtWrapper *irrlicht) i = CONTENT_TORCH; f = &g_content_features[i]; - f->setInventoryTexture(irrlicht->getTextureId("torch_on_floor.png")); f->param_type = CPT_LIGHT; f->light_propagates = true; f->solidness = 0; // drawn separately, makes no faces @@ -224,13 +256,23 @@ TileSpec MapNode::getTile(v3s16 dir) else spec = content_features(d).tiles[dir_i]; - if(content_features(d).param_type == CPT_MINERAL) + /* + If it contains some mineral, change texture id + */ + if(content_features(d).param_type == CPT_MINERAL && g_texturesource) { u8 mineral = param & 0x1f; - // Add mineral block texture - textureid_t tid = mineral_block_texture(mineral); - if(tid != 0) - spec.spec.addTid(tid); + std::string mineral_texture_name = mineral_block_texture(mineral); + if(mineral_texture_name != "") + { + u32 orig_id = spec.texture.id; + std::string texture_name = g_texturesource->getTextureName(orig_id); + //texture_name += "^blit:"; + texture_name += "^"; + texture_name += mineral_texture_name; + u32 new_id = g_texturesource->getTextureId(texture_name); + spec.texture = g_texturesource->getTexture(new_id); + } } return spec; diff --git a/src/mapnode.h b/src/mapnode.h index 0aaa4dc78..3de170598 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -132,9 +132,11 @@ struct ContentFeatures 5: front */ TileSpec tiles[6]; - + + // TODO: Somehow specify inventory image //std::string inventory_image_path; - TextureSpec inventory_texture; + //TextureSpec inventory_texture; + //u32 inventory_texture_id; bool is_ground_content; //TODO: Remove, use walkable instead bool light_propagates; @@ -167,27 +169,36 @@ struct ContentFeatures ~ContentFeatures(); - // Quickhands for simple materials - void setTexture(u16 i, const TextureSpec &spec, u8 alpha=255) + /* + Quickhands for simple materials + */ + + void setTexture(u16 i, std::string name, u8 alpha=255); + + void setAllTextures(std::string name, u8 alpha=255) { - tiles[i].spec = spec; + for(u16 i=0; i<6; i++) + { + setTexture(i, name, alpha); + } + } + + /*void setTexture(u16 i, AtlasPointer p, u8 alpha=255) + { + tiles[i].texture = p; if(alpha != 255) { tiles[i].alpha = alpha; tiles[i].material_type = MATERIAL_ALPHA_VERTEX; } } - void setAllTextures(const TextureSpec &spec, u8 alpha=255) + void setAllTextures(AtlasPointer p, u8 alpha=255) { for(u16 i=0; i<6; i++) { - setTexture(i, spec, alpha); + setTexture(i, p, alpha); } - - // Set this too so it can be left as is most times - if(inventory_texture.empty()) - inventory_texture = spec; - } + }*/ void setTile(u16 i, const TileSpec &tile) { @@ -201,10 +212,10 @@ struct ContentFeatures } } - void setInventoryTexture(const TextureSpec &spec) + /*void setInventoryTexture(const TextureSpec &spec) { inventory_texture = spec; - } + }*/ /*void setInventoryImage(std::string imgname) { diff --git a/src/mineral.cpp b/src/mineral.cpp index c333ae6f5..905f6497c 100644 --- a/src/mineral.cpp +++ b/src/mineral.cpp @@ -27,7 +27,8 @@ const char *mineral_filenames[MINERAL_COUNT] = "mineral_iron.png" }; -textureid_t mineral_textures[MINERAL_COUNT] = {0}; +//textureid_t mineral_textures[MINERAL_COUNT] = {0}; +std::string mineral_textures[MINERAL_COUNT]; void init_mineral(IIrrlichtWrapper *irrlicht) { @@ -35,14 +36,17 @@ void init_mineral(IIrrlichtWrapper *irrlicht) { if(mineral_filenames[i] == NULL) continue; - mineral_textures[i] = irrlicht->getTextureId(mineral_filenames[i]); + //mineral_textures[i] = irrlicht->getTextureId(mineral_filenames[i]); + //mineral_textures[i] = 0; + mineral_textures[i] = mineral_filenames[i]; } } -textureid_t mineral_block_texture(u8 mineral) +//textureid_t mineral_block_texture(u8 mineral) +std::string mineral_block_texture(u8 mineral) { if(mineral >= MINERAL_COUNT) - return 0; + return ""; return mineral_textures[mineral]; } diff --git a/src/mineral.h b/src/mineral.h index c663d9275..a181b89de 100644 --- a/src/mineral.h +++ b/src/mineral.h @@ -40,7 +40,8 @@ void init_mineral(IIrrlichtWrapper *irrlicht); #define MINERAL_COUNT 3 -textureid_t mineral_block_texture(u8 mineral); +//textureid_t mineral_block_texture(u8 mineral); +std::string mineral_block_texture(u8 mineral); inline CraftItem * getDiggedMineralItem(u8 mineral) { diff --git a/src/server.cpp b/src/server.cpp index 59d9acee4..f2078b9d0 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1642,6 +1642,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) os<isSavingEnabled() == false) + os<<" WARNING: Map saving is disabled."< +#include "debug.h" -// Nothing here +TextureSource::TextureSource(IrrlichtDevice *device): + m_device(device), + m_main_atlas_image(NULL), + m_main_atlas_texture(NULL) +{ + assert(m_device); + + m_atlaspointer_cache_mutex.Init(); + + m_main_thread = get_current_thread_id(); + + // Add a NULL AtlasPointer as the first index, named "" + m_atlaspointer_cache.push_back(SourceAtlasPointer("")); + m_name_to_id[""] = 0; + + // Build main texture atlas + buildMainAtlas(); +} + +TextureSource::~TextureSource() +{ +} + +void TextureSource::processQueue() +{ + /* + Fetch textures + */ + if(m_get_texture_queue.size() > 0) + { + GetRequest + request = m_get_texture_queue.pop(); + + dstream<<"INFO: TextureSource::processQueue(): " + <<"got texture request with " + <<"name="< + result; + result.key = request.key; + result.callers = request.callers; + result.item = getTextureIdDirect(request.key); + + request.dest->push_back(result); + } +} + +u32 TextureSource::getTextureId(const std::string &name) +{ + //dstream<<"INFO: getTextureId(): name="<::Node *n; + n = m_name_to_id.find(name); + if(n != NULL) + { + return n->getValue(); + } + } + + /* + Get texture + */ + if(get_current_thread_id() == m_main_thread) + { + return getTextureIdDirect(name); + } + else + { + dstream<<"INFO: getTextureId(): Queued: name="< result_queue; + + // Throw a request in + m_get_texture_queue.add(name, 0, 0, &result_queue); + + dstream<<"INFO: Waiting for texture from main thread, name=" + < + result = result_queue.pop_front(1000); + + // Check that at least something worked OK + assert(result.key == name); + + return result.item; + } + catch(ItemNotFoundException &e) + { + dstream<<"WARNING: Waiting for texture timed out."<::Node *n; + n = m_name_to_id.find(name); + if(n != NULL) + { + dstream<<"INFO: getTextureIdDirect(): name="<getValue(); + } + } + + dstream<<"INFO: getTextureIdDirect(): name="<=0; i--) + { + if(name[i] == separator) + { + last_separator_position = i; + break; + } + } + /* + If separator was found, construct the base name and make the + base image using a recursive call + */ + std::string base_image_name; + if(last_separator_position != -1) + { + // Construct base name + base_image_name = name.substr(0, last_separator_position); + dstream<<"INFO: getTextureIdDirect(): Calling itself recursively" + " to get base image, name="<addTexture(name.c_str(), baseimg); + + // If no texture + if(t == NULL) + return 0; + + /* + Add texture to caches + */ + + JMutexAutoLock lock(m_atlaspointer_cache_mutex); + + u32 id = m_atlaspointer_cache.size(); + AtlasPointer ap(id); + ap.atlas = t; + ap.pos = v2f(0,0); + ap.size = v2f(1,1); + ap.tiled = 0; + SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg->getDimension()); + m_atlaspointer_cache.push_back(nap); + m_name_to_id.insert(name, id); + + dstream<<"INFO: getTextureIdDirect(): name="<= m_atlaspointer_cache.size()=" + <= m_atlaspointer_cache.size()) + return AtlasPointer(0, NULL); + + return m_atlaspointer_cache[id].a; +} + +void TextureSource::buildMainAtlas() +{ + dstream<<"TextureSource::buildMainAtlas()"<getVideoDriver(); + assert(driver); + + JMutexAutoLock lock(m_atlaspointer_cache_mutex); + + // Create an image of the right size + core::dimension2d atlas_dim(1024,1024); + video::IImage *atlas_img = + driver->createImage(video::ECF_A8R8G8B8, atlas_dim); + + /* + A list of stuff to add. This should contain as much of the + stuff shown in game as possible, to minimize texture changes. + */ + + core::array sourcelist; + + sourcelist.push_back("stone.png"); + sourcelist.push_back("mud.png"); + sourcelist.push_back("sand.png"); + sourcelist.push_back("grass.png"); + sourcelist.push_back("mud.png"); + sourcelist.push_back("tree.png"); + sourcelist.push_back("tree_top.png"); + sourcelist.push_back("water.png"); + sourcelist.push_back("leaves.png"); + sourcelist.push_back("mud.png^grass_side.png"); + + sourcelist.push_back("stone.png^mineral_coal.png"); + sourcelist.push_back("stone.png^mineral_iron.png"); + sourcelist.push_back("mud.png^mineral_coal.png"); + sourcelist.push_back("mud.png^mineral_iron.png"); + sourcelist.push_back("sand.png^mineral_coal.png"); + sourcelist.push_back("sand.png^mineral_iron.png"); + + /* + First pass: generate almost everything + */ + core::position2d pos_in_atlas(0,0); + for(u32 i=0; icreateImageFromFile( + porting::getDataPath(name.c_str()).c_str()); + if(img == NULL) + continue; + + core::dimension2d dim = img->getDimension(); + // Make a copy with the right color format + video::IImage *img2 = + driver->createImage(video::ECF_A8R8G8B8, dim); + img->copyTo(img2); + img->drop();*/ + + // Generate image by name + video::IImage *img2 = generate_image_from_scratch(name, driver); + core::dimension2d dim = img2->getDimension(); + + // Tile it a few times in the X direction + u16 xwise_tiling = 16; + for(u32 j=0; jcopyToWithAlpha(atlas_img, + pos_in_atlas + v2s32(j*dim.Width,0), + core::rect(v2s32(0,0), dim), + video::SColor(255,255,255,255), + NULL); + } + + img2->drop(); + + /* + Add texture to caches + */ + + // Get next id + u32 id = m_atlaspointer_cache.size(); + + // Create AtlasPointer + AtlasPointer ap(id); + ap.atlas = NULL; // Set on the second pass + ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width, + (float)pos_in_atlas.Y/(float)atlas_dim.Height); + ap.size = v2f((float)dim.Width/(float)atlas_dim.Width, + (float)dim.Width/(float)atlas_dim.Height); + ap.tiled = xwise_tiling; + + // Create SourceAtlasPointer and add to containers + SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim); + m_atlaspointer_cache.push_back(nap); + m_name_to_id.insert(name, id); + + // Increment position + pos_in_atlas.Y += dim.Height; + } + + /* + Make texture + */ + video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img); + assert(t); + + /* + Second pass: set texture pointer in generated AtlasPointers + */ + for(u32 i=0; iwriteImageToFile(atlas_img, + porting::getDataPath("main_atlas.png").c_str()); +} + +video::IImage* generate_image_from_scratch(std::string name, + video::IVideoDriver* driver) +{ + dstream<<"INFO: generate_image_from_scratch(): " + "name="<=0; i--) + { + if(name[i] == separator) + { + last_separator_position = i; + break; + } + } + + /*dstream<<"INFO: generate_image_from_scratch(): " + <<"last_separator_position="<createImageFromFile(path.c_str()); + + if(image == NULL) + { + dstream<<"WARNING: Could not load image \""< dim = image->getDimension(); + baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); + image->copyTo(baseimg); + image->drop(); + } + // Else blit on base. + else + { + dstream<<"INFO: Blitting "< dim = image->getDimension(); + //core::dimension2d dim(16,16); + // Position to copy the blitted to in the base image + core::position2d pos_to(0,0); + // Position to copy the blitted from in the blitted image + core::position2d pos_from(0,0); + // Blit + image->copyToWithAlpha(baseimg, pos_to, + core::rect(pos_from, dim), + video::SColor(255,255,255,255), + NULL); + // Drop image + image->drop(); + } + } + else + { + // A special texture modification + + dstream<<"INFO: getTextureIdDirect(): generating special " + <<"modification \""< dim_base = baseimg->getDimension(); + // Crack will be drawn at this size + u32 cracksize = 16; + // Size of the crack image + core::dimension2d dim_crack(cracksize,cracksize); + // Position to copy the crack from in the crack image + core::position2d pos_other(0, 16 * progression); + + video::IImage *crackimage = driver->createImageFromFile( + porting::getDataPath("crack.png").c_str()); + + if(crackimage) + { + /*crackimage->copyToWithAlpha(baseimg, v2s32(0,0), + core::rect(pos_other, dim_base), + video::SColor(255,255,255,255), + NULL);*/ + + for(u32 y0=0; y0 pos_base(x0*cracksize, y0*cracksize); + crackimage->copyToWithAlpha(baseimg, pos_base, + core::rect(pos_other, dim_crack), + video::SColor(255,255,255,255), + NULL); + } + + crackimage->drop(); + } + } + else if(part_of_name.substr(0,8) == "[combine") + { + // "[combine:16x128:0,0=stone.png:0,16=grass.png" + Strfnd sf(part_of_name); + sf.next(":"); + u32 w0 = stoi(sf.next("x")); + u32 h0 = stoi(sf.next(":")); + dstream<<"INFO: combined w="<createImageFromFile( + porting::getDataPath(filename.c_str()).c_str()); + if(img) + { + core::dimension2d dim = img->getDimension(); + dstream<<"INFO: Size "< pos_base(x, y); + video::IImage *img2 = + driver->createImage(video::ECF_A8R8G8B8, dim); + img->copyTo(img2); + img->drop(); + img2->copyToWithAlpha(baseimg, pos_base, + core::rect(v2s32(0,0), dim), + video::SColor(255,255,255,255), + NULL); + img2->drop(); + } + else + { + dstream<<"WARNING: img==NULL"<createImageFromFile(path.c_str()); + + if(image == NULL) + { + dstream<<"WARNING: getTextureIdDirect(): Loading path \"" + < dim = image->getDimension(); + baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); + + // Set alpha to full + for(u32 y=0; ygetPixel(x,y); + c.setAlpha(255); + image->setPixel(x,y,c); + } + // Blit + image->copyTo(baseimg); + + image->drop(); + } + } + else + { + dstream<<"WARNING: getTextureIdDirect(): Invalid " + " modification: \""< size = image->getDimension(); + + u32 barheight = 1; + u32 barpad_x = 1; + u32 barpad_y = 1; + u32 barwidth = size.Width - barpad_x*2; + v2u32 barpos(barpad_x, size.Height - barheight - barpad_y); + + u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5); + + video::SColor active(255,255,0,0); + video::SColor inactive(255,0,0,0); + for(u32 x0=0; x0setPixel(x,y, *c); + } + } +} diff --git a/src/tile.h b/src/tile.h index a68731f55..b846b467b 100644 --- a/src/tile.h +++ b/src/tile.h @@ -21,10 +21,208 @@ with this program; if not, write to the Free Software Foundation, Inc., #define TILE_HEADER #include "common_irrlicht.h" -//#include "utility.h" -#include "texture.h" +#include "threads.h" +#include "utility.h" #include +/* + Specifies a texture in an atlas. + + This is used to specify single textures also. + + This has been designed to be small enough to be thrown around a lot. +*/ +struct AtlasPointer +{ + u32 id; // Texture id + video::ITexture *atlas; // Atlas in where the texture is + v2f pos; // Position in atlas + v2f size; // Size in atlas + u16 tiled; // X-wise tiling count. If 0, width of atlas is width of image. + + AtlasPointer( + u16 id_, + video::ITexture *atlas_=NULL, + v2f pos_=v2f(0,0), + v2f size_=v2f(1,1), + u16 tiled_=1 + ): + id(id_), + atlas(atlas_), + pos(pos_), + size(size_), + tiled(tiled_) + { + } + + bool operator==(const AtlasPointer &other) + { + return ( + id == other.id + ); + /*return ( + id == other.id && + atlas == other.atlas && + pos == other.pos && + size == other.size && + tiled == other.tiled + );*/ + } + + float x0(){ return pos.X; } + float x1(){ return pos.X + size.X; } + float y0(){ return pos.Y; } + float y1(){ return pos.Y + size.Y; } +}; + +/* + An internal variant of the former with more data. +*/ +struct SourceAtlasPointer +{ + std::string name; + AtlasPointer a; + video::IImage *atlas_img; // The source image of the atlas + // Integer variants of position and size + v2s32 intpos; + v2u32 intsize; + + SourceAtlasPointer( + const std::string &name_, + AtlasPointer a_=AtlasPointer(0, NULL), + video::IImage *atlas_img_=NULL, + v2s32 intpos_=v2s32(0,0), + v2u32 intsize_=v2u32(0,0) + ): + name(name_), + a(a_), + atlas_img(atlas_img_), + intpos(intpos_), + intsize(intsize_) + { + } +}; + +/* + Creates and caches textures. +*/ +class TextureSource +{ +public: + TextureSource(IrrlichtDevice *device); + ~TextureSource(); + + /* + Processes queued texture requests from other threads. + + Shall be called from the main thread. + */ + void processQueue(); + + /* + Example case: + Now, assume a texture with the id 1 exists, and has the name + "stone.png^mineral1". + Then a random thread calls getTextureId for a texture called + "stone.png^mineral1^crack0". + ...Now, WTF should happen? Well: + - getTextureId strips off stuff recursively from the end until + the remaining part is found, or nothing is left when + something is stripped out + + But it is slow to search for textures by names and modify them + like that? + - ContentFeatures is made to contain ids for the basic plain + textures + - Crack textures can be slow by themselves, but the framework + must be fast. + + Example case #2: + - Assume a texture with the id 1 exists, and has the name + "stone.png^mineral1" and is specified as a part of some atlas. + - Now MapBlock::getNodeTile() stumbles upon a node which uses + texture id 1, and finds out that NODEMOD_CRACK must be applied + with progression=0 + - It finds out the name of the texture with getTextureName(1), + appends "^crack0" to it and gets a new texture id with + getTextureId("stone.png^mineral1^crack0") + + */ + + /* + Gets a texture id from cache or + - if main thread, from getTextureIdDirect + - if other thread, adds to request queue and waits for main thread + */ + u32 getTextureId(const std::string &name); + + /* + Example names: + "stone.png" + "stone.png^crack2" + "stone.png^blit:mineral_coal.png" + "stone.png^blit:mineral_coal.png^crack1" + + - If texture specified by name is found from cache, return the + cached id. + - Otherwise generate the texture, add to cache and return id. + Recursion is used to find out the largest found part of the + texture and continue based on it. + + The id 0 points to a NULL texture. It is returned in case of error. + */ + u32 getTextureIdDirect(const std::string &name); + + /* + Finds out the name of a cached texture. + */ + std::string getTextureName(u32 id); + + /* + If texture specified by the name pointed by the id doesn't + exist, create it, then return the cached texture. + + Can be called from any thread. If called from some other thread + and not found in cache, the call is queued to the main thread + for processing. + */ + AtlasPointer getTexture(u32 id); + + AtlasPointer getTexture(const std::string &name) + { + return getTexture(getTextureId(name)); + } + +private: + /* + Build the main texture atlas which contains most of the + textures. + + This is called by the constructor. + */ + void buildMainAtlas(); + + // The id of the thread that is allowed to use irrlicht directly + threadid_t m_main_thread; + // The irrlicht device + IrrlichtDevice *m_device; + + // A texture id is index in this array. + // The first position contains a NULL texture. + core::array m_atlaspointer_cache; + // Maps a texture name to an index in the former. + core::map m_name_to_id; + // The two former containers are behind this mutex + JMutex m_atlaspointer_cache_mutex; + + // Main texture atlas. This is filled at startup and is then not touched. + video::IImage *m_main_atlas_image; + video::ITexture *m_main_atlas_texture; + + // Queued texture fetches (to be processed by the main thread) + RequestQueue m_get_texture_queue; +}; + enum MaterialType{ MATERIAL_ALPHA_NONE, MATERIAL_ALPHA_VERTEX, @@ -38,12 +236,17 @@ enum MaterialType{ /* This fully defines the looks of a tile. The SMaterial of a tile is constructed according to this. + + TODO: Change this to use an AtlasPointer */ struct TileSpec { TileSpec(): + texture(0), alpha(255), material_type(MATERIAL_ALPHA_NONE), + // Use this so that leaves don't need a separate material + //material_type(MATERIAL_ALPHA_SIMPLE), material_flags( MATERIAL_FLAG_BACKFACE_CULLING ) @@ -53,7 +256,7 @@ struct TileSpec bool operator==(TileSpec &other) { return ( - spec == other.spec && + texture == other.texture && alpha == other.alpha && material_type == other.material_type && material_flags == other.material_flags @@ -80,8 +283,14 @@ struct TileSpec material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING) ? true : false; } - // Specification of texture - TextureSpec spec; + // NOTE: Deprecated, i guess? + void setTexturePos(u8 tx_, u8 ty_, u8 tw_, u8 th_) + { + texture.pos = v2f((float)tx_/256.0, (float)ty_/256.0); + texture.size = v2f(((float)tw_ + 1.0)/256.0, ((float)th_ + 1.0)/256.0); + } + + AtlasPointer texture; // Vertex alpha u8 alpha; // Material type -- 2.25.1