work-in-progress texture atlas optimization

This commit is contained in:
Perttu Ahola 2011-02-10 02:13:03 +02:00
parent 949383a2f7
commit 1704badc30
21 changed files with 1496 additions and 271 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -75,6 +75,7 @@ set(minetest_SRCS
guiPauseMenu.cpp guiPauseMenu.cpp
irrlichtwrapper.cpp irrlichtwrapper.cpp
client.cpp client.cpp
tile.cpp
main.cpp main.cpp
) )

@ -118,9 +118,9 @@ void Environment::step(float dtime)
/* /*
Apply water resistance Apply water resistance
*/ */
if(player->in_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; if(speed.Y < -max_down) speed.Y = -max_down;
f32 max = 2.5*BS; f32 max = 2.5*BS;

@ -21,6 +21,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define IIRRLICHTWRAPPER_HEADER #define IIRRLICHTWRAPPER_HEADER
#include "common_irrlicht.h" #include "common_irrlicht.h"
#include "texture.h"
/*
NOTE: This is deprecated and should be removed completely
*/
/* /*
IrrlichtWrapper prototype. IrrlichtWrapper prototype.
@ -38,15 +43,18 @@ public:
{ {
} }
// Should be called only from the main thread
virtual IrrlichtDevice* getDevice(){ return NULL; }
virtual u32 getTime() virtual u32 getTime()
{ {
return 0; 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 std::string getTextureName(textureid_t id){ return ""; }
virtual video::ITexture* getTexture(const std::string &name){ return NULL; } 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: private:
}; };

@ -96,13 +96,17 @@ InventoryItem* InventoryItem::deSerialize(std::istream &is)
#ifndef SERVER #ifndef SERVER
video::ITexture * MapBlockObjectItem::getImage() video::ITexture * MapBlockObjectItem::getImage()
{ {
//TODO
if(m_inventorystring.substr(0,3) == "Rat") if(m_inventorystring.substr(0,3) == "Rat")
//return g_device->getVideoDriver()->getTexture(porting::getDataPath("rat.png").c_str()); //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") if(m_inventorystring.substr(0,4) == "Sign")
//return g_device->getVideoDriver()->getTexture(porting::getDataPath("sign.png").c_str()); //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; return NULL;
} }

@ -122,12 +122,9 @@ public:
#ifndef SERVER #ifndef SERVER
video::ITexture * getImage() video::ITexture * getImage()
{ {
/*if(m_content >= USEFUL_CONTENT_COUNT) //TODO
//return g_irrlicht->getTexture(content_features(m_content).inventory_texture);
return NULL; return NULL;
return g_irrlicht->getTexture(g_content_inventory_texture_paths[m_content]);*/
return g_irrlicht->getTexture(content_features(m_content).inventory_texture);
} }
#endif #endif
std::string getText() std::string getText()
@ -264,7 +261,9 @@ public:
name = "cloud.png"; name = "cloud.png";
// Get such a texture // Get such a texture
return g_irrlicht->getTexture(name); //return g_irrlicht->getTexture(name);
//TODO
return NULL;
} }
#endif #endif
std::string getText() std::string getText()
@ -351,10 +350,12 @@ public:
std::ostringstream os; std::ostringstream os;
os<<"[progressbar"<<value_f; os<<"[progressbar"<<value_f;
TextureSpec spec; /*TextureSpec spec;
spec.addTid(g_irrlicht->getTextureId(basename)); spec.addTid(g_irrlicht->getTextureId(basename));
spec.addTid(g_irrlicht->getTextureId(os.str())); 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 /*// Make texture name for the new texture with a progress bar
float value_f = (float)toolprogress / (float)maxprogress; float value_f = (float)toolprogress / (float)maxprogress;

@ -11,8 +11,22 @@ IrrlichtWrapper::IrrlichtWrapper(IrrlichtDevice *device)
m_device = device; m_device = device;
} }
IrrlichtWrapper::~IrrlichtWrapper()
{
#if 0
// Clear image cache
for(core::map<std::string, video::IImage*>::Iterator
i = m_imagecache.getIterator();
i.atEnd() == false; i++)
{
i.getNode()->getValue()->drop();
}
#endif
}
void IrrlichtWrapper::Run() void IrrlichtWrapper::Run()
{ {
#if 0
/* /*
Fetch textures Fetch textures
*/ */
@ -34,6 +48,7 @@ void IrrlichtWrapper::Run()
request.dest->push_back(result); request.dest->push_back(result);
} }
#endif
} }
void IrrlichtWrapper::Shutdown(bool shutdown) void IrrlichtWrapper::Shutdown(bool shutdown)
@ -41,6 +56,18 @@ void IrrlichtWrapper::Shutdown(bool shutdown)
m_running = !shutdown; m_running = !shutdown;
} }
IrrlichtDevice* IrrlichtWrapper::getDevice()
{
if(get_current_thread_id() != m_main_thread)
{
dstream<<"WARNING: IrrlichtWrapper::getDevice() called "
"not from main thread"<<std::endl;
return NULL;
}
return m_device;
}
#if 0
textureid_t IrrlichtWrapper::getTextureId(const std::string &name) textureid_t IrrlichtWrapper::getTextureId(const std::string &name)
{ {
u32 id = m_namecache.getId(name); u32 id = m_namecache.getId(name);
@ -55,9 +82,9 @@ std::string IrrlichtWrapper::getTextureName(textureid_t id)
return name; return name;
} }
video::ITexture* IrrlichtWrapper::getTexture(const std::string &name) video::ITexture* IrrlichtWrapper::getTexture(const std::string &filename)
{ {
TextureSpec spec(getTextureId(name)); TextureSpec spec(getTextureId(filename));
return getTexture(spec); return getTexture(spec);
} }
@ -163,11 +190,35 @@ video::ITexture* IrrlichtWrapper::getTextureDirect(const TextureSpec &spec)
texture_name += name; texture_name += name;
texture_name += "]"; texture_name += "]";
/*
Try to get image from image cache
*/
{
core::map<std::string, video::IImage*>::Node *n;
n = m_imagecache.find(texture_name);
if(n != NULL)
{
video::IImage *image = n->getValue();
core::dimension2d<u32> dim = image->getDimension();
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
image->copyTo(baseimg);
dstream<<"INFO: getTextureDirect(): Loaded \""
<<texture_name<<"\" from image cache"
<<std::endl;
// Do not process any further.
continue;
}
}
// Stuff starting with [ are special commands
if(name[0] != '[') if(name[0] != '[')
{ {
// A normal texture; load it from a file // A normal texture; load it from a file
std::string path = porting::getDataPath(name.c_str()); std::string path = porting::getDataPath(name.c_str());
dstream<<"getTextureDirect(): Loading path \""<<path dstream<<"INFO: getTextureDirect(): Loading path \""<<path
<<"\""<<std::endl; <<"\""<<std::endl;
// DEBUG // DEBUG
@ -192,7 +243,7 @@ video::ITexture* IrrlichtWrapper::getTextureDirect(const TextureSpec &spec)
// If base image is NULL, load as base. // If base image is NULL, load as base.
if(baseimg == NULL) if(baseimg == NULL)
{ {
dstream<<"Setting "<<name<<" as base"<<std::endl; dstream<<"INFO: Setting "<<name<<" as base"<<std::endl;
/* /*
Copy it this way to get an alpha channel. Copy it this way to get an alpha channel.
Otherwise images with alpha cannot be blitted on Otherwise images with alpha cannot be blitted on
@ -209,7 +260,7 @@ video::ITexture* IrrlichtWrapper::getTextureDirect(const TextureSpec &spec)
// Else blit on base. // Else blit on base.
else else
{ {
dstream<<"Blitting "<<name<<" on base"<<std::endl; dstream<<"INFO: Blitting "<<name<<" on base"<<std::endl;
// Size of the copied area // Size of the copied area
core::dimension2d<u32> dim = image->getDimension(); core::dimension2d<u32> dim = image->getDimension();
//core::dimension2d<u32> dim(16,16); //core::dimension2d<u32> dim(16,16);
@ -229,28 +280,86 @@ video::ITexture* IrrlichtWrapper::getTextureDirect(const TextureSpec &spec)
else else
{ {
// A special texture modification // A special texture modification
dstream<<"getTextureDirect(): generating \""<<name<<"\"" dstream<<"INFO: getTextureDirect(): generating \""<<name<<"\""
<<std::endl; <<std::endl;
if(name.substr(0,6) == "[crack") if(name.substr(0,6) == "[crack")
{ {
u16 progression = stoi(name.substr(6)); u16 progression = stoi(name.substr(6));
// Size of the base image // Size of the base image
core::dimension2d<u32> dim(16, 16); core::dimension2d<u32> dim_base = baseimg->getDimension();
// Crack will be drawn at this size
u32 cracksize = 16;
// Size of the crack image // Size of the crack image
//core::dimension2d<u32> dim_crack(16, 16 * CRACK_ANIMATION_LENGTH); core::dimension2d<u32> dim_crack(cracksize,cracksize);
// Position to copy the crack to in the base image
core::position2d<s32> pos_base(0, 0);
// Position to copy the crack from in the crack image // Position to copy the crack from in the crack image
core::position2d<s32> pos_other(0, 16 * progression); core::position2d<s32> pos_other(0, 16 * progression);
video::IImage *crackimage = driver->createImageFromFile( video::IImage *crackimage = driver->createImageFromFile(
porting::getDataPath("crack.png").c_str()); porting::getDataPath("crack.png").c_str());
crackimage->copyToWithAlpha(baseimg, v2s32(0,0),
core::rect<s32>(pos_other, dim), if(crackimage)
{
/*crackimage->copyToWithAlpha(baseimg, v2s32(0,0),
core::rect<s32>(pos_other, dim_base),
video::SColor(255,255,255,255),
NULL);*/
for(u32 y0=0; y0<dim_base.Height/dim_crack.Height; y0++)
for(u32 x0=0; x0<dim_base.Width/dim_crack.Width; x0++)
{
// Position to copy the crack to in the base image
core::position2d<s32> pos_base(x0*cracksize, y0*cracksize);
crackimage->copyToWithAlpha(baseimg, pos_base,
core::rect<s32>(pos_other, dim_crack),
video::SColor(255,255,255,255), video::SColor(255,255,255,255),
NULL); NULL);
}
crackimage->drop(); 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="<<w0<<" h="<<h0<<std::endl;
core::dimension2d<u32> dim(w0,h0);
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
while(sf.atend() == false)
{
u32 x = stoi(sf.next(","));
u32 y = stoi(sf.next("="));
std::string filename = sf.next(":");
dstream<<"INFO: Adding \""<<filename
<<"\" to combined ("<<x<<","<<y<<")"
<<std::endl;
video::IImage *img = driver->createImageFromFile(
porting::getDataPath(filename.c_str()).c_str());
if(img)
{
core::dimension2d<u32> dim = img->getDimension();
dstream<<"INFO: Size "<<dim.Width
<<"x"<<dim.Height<<std::endl;
core::position2d<s32> 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<s32>(v2s32(0,0), dim),
video::SColor(255,255,255,255),
NULL);
img2->drop();
}
else
{
dstream<<"WARNING: img==NULL"<<std::endl;
}
}
}
else if(name.substr(0,12) == "[progressbar") else if(name.substr(0,12) == "[progressbar")
{ {
float value = stof(name.substr(12)); float value = stof(name.substr(12));
@ -262,12 +371,31 @@ video::ITexture* IrrlichtWrapper::getTextureDirect(const TextureSpec &spec)
" texture: \""<<name<<"\""<<std::endl; " texture: \""<<name<<"\""<<std::endl;
} }
} }
/*
Add to image cache
*/
if(baseimg != NULL)
{
core::map<std::string, video::IImage*>::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 no resulting image, return NULL
if(baseimg == NULL) if(baseimg == NULL)
{ {
dstream<<"getTextureDirect(): baseimg is NULL (attempted to" dstream<<"WARNING: getTextureDirect(): baseimg is NULL (attempted to"
" create texture \""<<texture_name<<"\""<<std::endl; " create texture \""<<texture_name<<"\""<<std::endl;
return NULL; return NULL;
} }
@ -281,9 +409,8 @@ video::ITexture* IrrlichtWrapper::getTextureDirect(const TextureSpec &spec)
// Create texture from resulting image // Create texture from resulting image
t = driver->addTexture(texture_name.c_str(), baseimg); t = driver->addTexture(texture_name.c_str(), baseimg);
baseimg->drop();
dstream<<"getTextureDirect(): created texture \""<<texture_name dstream<<"INFO: getTextureDirect(): created texture \""<<texture_name
<<"\""<<std::endl; <<"\""<<std::endl;
return t; return t;
@ -321,5 +448,5 @@ void make_progressbar(float value, video::IImage *image)
} }
} }
} }
#endif

@ -31,6 +31,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <jmutexautolock.h> #include <jmutexautolock.h>
#include <string> #include <string>
/*
NOTE: This is deprecated and should be removed completely
*/
/* /*
A thread-safe texture pointer cache. 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 threads, because texture pointers have to be handled in
background threads. background threads.
*/ */
#if 0 #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<std::string, video::ITexture*>::Node *n;
n = m_textures.find(name);
if(n != NULL)
return n->getValue();
return NULL;
}
private:
core::map<std::string, video::ITexture*> m_textures;
JMutex m_mutex;
};
#endif
/* /*
A thread-safe texture pointer cache A thread-safe texture pointer cache
*/ */
@ -116,6 +83,7 @@ private:
core::map<TextureSpec, video::ITexture*> m_textures; core::map<TextureSpec, video::ITexture*> m_textures;
JMutex m_mutex; JMutex m_mutex;
}; };
#endif
/* /*
A thread-safe wrapper for irrlicht, to be accessed from A thread-safe wrapper for irrlicht, to be accessed from
@ -124,6 +92,8 @@ private:
Queues tasks to be done in the main thread. Queues tasks to be done in the main thread.
Also caches texture specification strings to ids and textures. Also caches texture specification strings to ids and textures.
TODO: Remove this and move all texture functionality to TextureSource
*/ */
class IrrlichtWrapper : public IIrrlichtWrapper class IrrlichtWrapper : public IIrrlichtWrapper
@ -135,12 +105,16 @@ public:
IrrlichtWrapper(IrrlichtDevice *device); IrrlichtWrapper(IrrlichtDevice *device);
~IrrlichtWrapper();
// Run queued tasks // Run queued tasks
void Run(); void Run();
// Shutdown wrapper; this disables queued texture fetching // Shutdown wrapper; this disables queued texture fetching
void Shutdown(bool shutdown); void Shutdown(bool shutdown);
IrrlichtDevice* getDevice();
/* /*
These are called from other threads These are called from other threads
*/ */
@ -152,6 +126,7 @@ public:
return m_device->getTimer()->getRealTime(); return m_device->getTimer()->getRealTime();
} }
#if 0
/* /*
Format of a texture name: Format of a texture name:
"stone.png" (filename in image data directory) "stone.png" (filename in image data directory)
@ -167,20 +142,18 @@ public:
// The reverse of the above // The reverse of the above
std::string getTextureName(textureid_t id); std::string getTextureName(textureid_t id);
// Gets a texture based on a filename // 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) // Gets a texture based on a TextureSpec (a textureid_t is fine too)
video::ITexture* getTexture(const TextureSpec &spec); video::ITexture* getTexture(const TextureSpec &spec);
#endif
private: private:
/* /*
Non-thread-safe variants of stuff, for internal use 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 // Constructs a texture according to spec
video::ITexture* getTextureDirect(const TextureSpec &spec); //video::ITexture* getTextureDirect(const TextureSpec &spec);
/* /*
Members Members
@ -195,14 +168,19 @@ private:
JMutex m_device_mutex; JMutex m_device_mutex;
IrrlichtDevice *m_device; IrrlichtDevice *m_device;
#if 0
// Queued texture fetches (to be processed by the main thread) // Queued texture fetches (to be processed by the main thread)
RequestQueue<TextureSpec, video::ITexture*, u8, u8> m_get_texture_queue; RequestQueue<TextureSpec, video::ITexture*, u8, u8> m_get_texture_queue;
// Cache of textures by spec // Cache of textures by spec
TextureCache m_texturecache; TextureCache m_texturecache;
// Cached or generated source images by texture name
core::map<std::string, video::IImage*> m_imagecache;
// A mapping from texture id to string spec // A mapping from texture id to string spec
MutexedIdGenerator<std::string> m_namecache; MutexedIdGenerator<std::string> m_namecache;
#endif
}; };
#endif #endif

@ -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 different directions and then only those drawn that need to be
- Also an 1-dimensional tile map would be nice probably - 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: Gaming ideas:
------------- -------------
@ -363,8 +366,13 @@ Doing now (most important at the top):
#include "guiMainMenu.h" #include "guiMainMenu.h"
#include "mineral.h" #include "mineral.h"
#include "noise.h" #include "noise.h"
#include "tile.h"
IrrlichtWrapper *g_irrlicht; // TODO: Remove this
IrrlichtWrapper *g_irrlicht = NULL;
// This makes textures
TextureSource *g_texturesource = NULL;
MapDrawControl draw_control; MapDrawControl draw_control;
@ -1643,6 +1651,7 @@ int main(int argc, char *argv[])
g_device = device; g_device = device;
g_irrlicht = new IrrlichtWrapper(device); g_irrlicht = new IrrlichtWrapper(device);
g_texturesource = new TextureSource(device);
/* /*
Speed tests (done after irrlicht is loaded to get timer) 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(); 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); //driver->setMinHardwareBufferVertexCount(50);
@ -2061,6 +2071,11 @@ int main(int argc, char *argv[])
*/ */
g_irrlicht->Run(); g_irrlicht->Run();
/*
Process TextureSource's queue
*/
g_texturesource->processQueue();
/* /*
Random calculations Random calculations
*/ */

@ -20,15 +20,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef MAIN_HEADER #ifndef MAIN_HEADER
#define MAIN_HEADER #define MAIN_HEADER
#include "irrlichtwrapper.h"
// Settings // Settings
#include "utility.h"
extern Settings g_settings; extern Settings g_settings;
// A thread safe wrapper to irrlicht // A thread safe wrapper to irrlicht
// On a server build, this is always NULL. // On a server build, this is always NULL.
// TODO: Remove this
#include "irrlichtwrapper.h"
extern IrrlichtWrapper *g_irrlicht; extern IrrlichtWrapper *g_irrlicht;
// This makes and maps textures
#include "tile.h"
extern TextureSource *g_texturesource;
// Debug streams // Debug streams
#include <fstream> #include <fstream>

@ -1974,7 +1974,7 @@ double tree_amount_2d(u64 seed, v2s16 p)
{ {
double noise = noise2d_perlin( double noise = noise2d_perlin(
0.5+(float)p.X/250, 0.5+(float)p.Y/250, 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; double zeroval = -0.3;
if(noise < zeroval) if(noise < zeroval)
return 0; return 0;
@ -2021,9 +2021,9 @@ double base_rock_level_2d(u64 seed, v2s16 p)
base = base2;*/ base = base2;*/
#if 1 #if 1
// Higher ground level // Higher ground level
double higher = (double)WATER_LEVEL + 13. + 50. * noise2d_perlin( double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
0.5+(float)p.X/500., 0.5+(float)p.Y/500., 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
seed+85039, 6, 0.69); seed+85039, 5, 0.69);
//higher = 30; // For debugging //higher = 30; // For debugging
// Limit higher to at least base // Limit higher to at least base
@ -2042,7 +2042,7 @@ double base_rock_level_2d(u64 seed, v2s16 p)
//double b = 20; //double b = 20;
// Offset to more low // Offset to more low
double a_off = -0.3; double a_off = -0.15;
// High/low selector // High/low selector
/*double a = 0.5 + b * (a_off + noise2d_perlin( /*double a = 0.5 + b * (a_off + noise2d_perlin(
0.5+(float)p.X/500., 0.5+(float)p.Y/500., 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
@ -2414,12 +2414,12 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
/* /*
Make dungeons Make dungeons
*/ */
//u32 dungeons_count = relative_volume / 600000; u32 dungeons_count = relative_volume / 600000;
/*u32 bruises_count = relative_volume * stone_surface_max_y / 40000000; u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
if(stone_surface_max_y < WATER_LEVEL) if(stone_surface_max_y < WATER_LEVEL)
bruises_count = 0;*/ bruises_count = 0;
u32 dungeons_count = 0; /*u32 dungeons_count = 0;
u32 bruises_count = 0; u32 bruises_count = 0;*/
for(u32 jj=0; jj<dungeons_count+bruises_count; jj++) for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
{ {
s16 min_tunnel_diameter = 2; s16 min_tunnel_diameter = 2;
@ -5116,6 +5116,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
timecheck_counter++; timecheck_counter++;
if(timecheck_counter > 50) if(timecheck_counter > 50)
{ {
timecheck_counter = 0;
int time2 = time(0); int time2 = time(0);
if(time2 > time1 + 4) if(time2 > time1 + 4)
{ {
@ -5167,71 +5168,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
continue; 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 #if 1
/* /*
Update expired mesh Update expired mesh
@ -5324,6 +5260,11 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
// Render transparent on transparent pass and likewise. // Render transparent on transparent pass and likewise.
if(transparent == is_transparent_pass) 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->setMaterial(buf->getMaterial());
driver->drawMeshBuffer(buf); driver->drawMeshBuffer(buf);
vertex_count += buf->getVertexCount(); vertex_count += buf->getVertexCount();

@ -513,6 +513,8 @@ public:
// For debug printing // For debug printing
virtual void PrintInfo(std::ostream &out); virtual void PrintInfo(std::ostream &out);
bool isSavingEnabled(){ return m_map_saving_enabled; }
private: private:
// Seed used for all kinds of randomness // Seed used for all kinds of randomness
u64 m_seed; u64 m_seed;

@ -273,13 +273,32 @@ void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
video::SColor c = video::SColor(alpha,li,li,li); video::SColor c = video::SColor(alpha,li,li,li);
face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c, face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
core::vector2d<f32>(0,1));
face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
core::vector2d<f32>(abs_scale,1)); core::vector2d<f32>(abs_scale,1));
face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
core::vector2d<f32>(0,1));
face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c, face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
core::vector2d<f32>(abs_scale,0));
face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
core::vector2d<f32>(0,0)); core::vector2d<f32>(0,0));
face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
core::vector2d<f32>(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<f32>(x0+w*abs_scale, y0+h));
face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
core::vector2d<f32>(x0, y0+h));
face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
core::vector2d<f32>(x0, y0));
face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
core::vector2d<f32>(x0+w*abs_scale, y0));
face.tile = tile; face.tile = tile;
//DEBUG //DEBUG
@ -318,11 +337,26 @@ TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
} }
if(mod.type == NODEMOD_CRACK) 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; std::ostringstream os;
os<<"[crack"<<mod.param; os<<orig_name<<"^[crack"<<mod.param;
//os<<orig_name<<"^[progressbar0.5";
//os<<"mese.png";
// Get new texture
u32 new_id = g_texturesource->getTextureId(os.str());
textureid_t tid = g_irrlicht->getTextureId(os.str()); dstream<<"MapBlock::getNodeTile(): Switching from "
spec.spec.addTid(tid); <<orig_name<<" to "<<os.str()<<" ("
<<orig_id<<" to "<<new_id<<")"<<std::endl;
spec.texture = g_texturesource->getTexture(new_id);
} }
} }
@ -407,6 +441,8 @@ void MapBlock::updateFastFaceRow(
TileSpec tile1_next; TileSpec tile1_next;
u8 light_next = 0; 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) if(j != length - 1)
{ {
p_next = p + translate_dir; p_next = p + translate_dir;
@ -426,7 +462,31 @@ void MapBlock::updateFastFaceRow(
continuous_tiles_count++; 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 Create a face if there should be one
@ -684,10 +744,6 @@ void MapBlock::updateMesh(u32 daynight_ratio)
Convert FastFaces to SMesh Convert FastFaces to SMesh
*/ */
scene::SMesh *mesh_new = NULL;
mesh_new = new scene::SMesh();
MeshCollector collector; MeshCollector collector;
if(fastfaces_new.size() > 0) if(fastfaces_new.size() > 0)
@ -709,7 +765,8 @@ void MapBlock::updateMesh(u32 daynight_ratio)
const u16 indices[] = {0,1,2,2,3,0}; 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) if(texture == NULL)
continue; continue;
@ -717,11 +774,6 @@ void MapBlock::updateMesh(u32 daynight_ratio)
f.tile.applyMaterialOptions(material); 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); 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_BILINEAR_FILTER, false);
material_water1.setFlag(video::EMF_FOG_ENABLE, true); material_water1.setFlag(video::EMF_FOG_ENABLE, true);
material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; 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 // New-style leaves material
video::SMaterial material_leaves1; 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_BILINEAR_FILTER, false);
material_leaves1.setFlag(video::EMF_FOG_ENABLE, true); material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; 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; z<MAP_BLOCKSIZE; z++) for(s16 z=0; z<MAP_BLOCKSIZE; z++)
for(s16 y=0; y<MAP_BLOCKSIZE; y++) for(s16 y=0; y<MAP_BLOCKSIZE; y++)
@ -804,7 +864,8 @@ void MapBlock::updateMesh(u32 daynight_ratio)
//material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
material.MaterialType material.MaterialType
= video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
if(dir == v3s16(0,-1,0)) //TODO
/*if(dir == v3s16(0,-1,0))
material.setTexture(0, material.setTexture(0,
g_irrlicht->getTexture("torch_on_floor.png")); g_irrlicht->getTexture("torch_on_floor.png"));
else if(dir == v3s16(0,1,0)) else if(dir == v3s16(0,1,0))
@ -816,7 +877,7 @@ void MapBlock::updateMesh(u32 daynight_ratio)
g_irrlicht->getTexture("torch_on_floor.png")); g_irrlicht->getTexture("torch_on_floor.png"));
else else
material.setTexture(0, material.setTexture(0,
g_irrlicht->getTexture("torch.png")); g_irrlicht->getTexture("torch.png"));*/
u16 indices[] = {0,1,2,2,3,0}; u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector // Add to mesh collector
@ -974,14 +1035,18 @@ void MapBlock::updateMesh(u32 daynight_ratio)
video::S3DVertex vertices[4] = video::S3DVertex vertices[4] =
{ {
/*video::S3DVertex(-BS/2,-BS/2,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,-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, 1,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, 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 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,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, 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++) for(s32 i=0; i<4; i++)
@ -1092,10 +1165,18 @@ void MapBlock::updateMesh(u32 daynight_ratio)
video::S3DVertex vertices[4] = 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,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, 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++) for(s32 i=0; i<4; i++)
@ -1120,10 +1201,18 @@ void MapBlock::updateMesh(u32 daynight_ratio)
{ {
video::S3DVertex vertices[4] = 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,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, 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) if(j == 0)
@ -1173,6 +1262,9 @@ void MapBlock::updateMesh(u32 daynight_ratio)
Add stuff from collector to mesh Add stuff from collector to mesh
*/ */
scene::SMesh *mesh_new = NULL;
mesh_new = new scene::SMesh();
collector.fillMesh(mesh_new); collector.fillMesh(mesh_new);
/* /*
@ -1191,13 +1283,25 @@ void MapBlock::updateMesh(u32 daynight_ratio)
mesh_new = NULL; mesh_new = NULL;
} }
if(mesh_new)
{
#if 0
// Usually 1-700 faces and 1-7 materials
std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
<<"and uses "<<mesh_new->getMeshBufferCount()
<<" materials (meshbuffers)"<<std::endl;
#endif
// Use VBO for mesh (this just would set this for ever buffer) // Use VBO for mesh (this just would set this for ever buffer)
// This will lead to infinite memory usage because or irrlicht. // This will lead to infinite memory usage because or irrlicht.
//mesh_new->setHardwareMappingHint(scene::EHM_STATIC); //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
/*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces " /*
<<"and uses "<<mesh_new->getMeshBufferCount() NOTE: If that is enabled, some kind of a queue to the main
<<" materials (meshbuffers)"<<std::endl;*/ thread should be made which would call irrlicht to delete
the hardware buffer and then delete the mesh
*/
}
/* /*
Replace the mesh Replace the mesh

@ -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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#include "common_irrlicht.h"
#include "mapnode.h" #include "mapnode.h"
#include "tile.h" #include "tile.h"
#include "porting.h" #include "porting.h"
@ -31,6 +32,20 @@ ContentFeatures::~ContentFeatures()
delete translate_to; 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]; struct ContentFeatures g_content_features[256];
ContentFeatures & content_features(u8 i) ContentFeatures & content_features(u8 i)
@ -40,56 +55,75 @@ ContentFeatures & content_features(u8 i)
void init_mapnode(IIrrlichtWrapper *irrlicht) void init_mapnode(IIrrlichtWrapper *irrlicht)
{ {
// Read some settings
bool new_style_water = g_settings.getBool("new_style_water"); bool new_style_water = g_settings.getBool("new_style_water");
bool new_style_leaves = g_settings.getBool("new_style_leaves"); 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; u8 i;
ContentFeatures *f = NULL; ContentFeatures *f = NULL;
i = CONTENT_STONE; i = CONTENT_STONE;
f = &g_content_features[i]; f = &g_content_features[i];
f->setAllTextures(irrlicht->getTextureId("stone.png")); f->setAllTextures("stone.png");
f->param_type = CPT_MINERAL; f->param_type = CPT_MINERAL;
f->is_ground_content = true; f->is_ground_content = true;
i = CONTENT_GRASS; i = CONTENT_GRASS;
f = &g_content_features[i]; f = &g_content_features[i];
f->setAllTextures(TextureSpec(irrlicht->getTextureId("mud.png"), f->setAllTextures("mud.png^grass_side.png");
irrlicht->getTextureId("grass_side.png"))); f->setTexture(0, "grass.png");
f->setTexture(0, irrlicht->getTextureId("grass.png")); f->setTexture(1, "mud.png");
f->setTexture(1, irrlicht->getTextureId("mud.png")); //f->setInventoryTexture(irrlicht->getTextureId("grass.png"));
f->setInventoryTexture(irrlicht->getTextureId("grass.png"));
f->param_type = CPT_MINERAL; f->param_type = CPT_MINERAL;
f->is_ground_content = true; f->is_ground_content = true;
i = CONTENT_GRASS_FOOTSTEPS; i = CONTENT_GRASS_FOOTSTEPS;
f = &g_content_features[i]; f = &g_content_features[i];
f->setAllTextures(TextureSpec(irrlicht->getTextureId("mud.png"), //f->setInventoryTexture(irrlicht->getTextureId("grass_footsteps.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->param_type = CPT_MINERAL; f->param_type = CPT_MINERAL;
f->is_ground_content = true; f->is_ground_content = true;
i = CONTENT_MUD; i = CONTENT_MUD;
f = &g_content_features[i]; f = &g_content_features[i];
f->setAllTextures(irrlicht->getTextureId("mud.png")); f->setAllTextures("mud.png");
f->param_type = CPT_MINERAL; f->param_type = CPT_MINERAL;
f->is_ground_content = true; f->is_ground_content = true;
i = CONTENT_SAND; i = CONTENT_SAND;
f = &g_content_features[i]; f = &g_content_features[i];
f->setAllTextures(irrlicht->getTextureId("sand.png")); f->setAllTextures("sand.png");
f->param_type = CPT_MINERAL; f->param_type = CPT_MINERAL;
f->is_ground_content = true; f->is_ground_content = true;
i = CONTENT_TREE; i = CONTENT_TREE;
f = &g_content_features[i]; f = &g_content_features[i];
f->setAllTextures(irrlicht->getTextureId("tree.png")); f->setAllTextures("tree.png");
f->setTexture(0, irrlicht->getTextureId("tree_top.png")); f->setTexture(0, "tree_top.png");
f->setTexture(1, irrlicht->getTextureId("tree_top.png")); f->setTexture(1, "tree_top.png");
f->setInventoryTexture(irrlicht->getTextureId("tree_top.png")); //f->setInventoryTexture(irrlicht->getTextureId("tree_top.png"));
f->param_type = CPT_MINERAL; f->param_type = CPT_MINERAL;
f->is_ground_content = true; f->is_ground_content = true;
@ -105,36 +139,33 @@ void init_mapnode(IIrrlichtWrapper *irrlicht)
} }
else 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; i = CONTENT_COALSTONE;
f = &g_content_features[i]; f = &g_content_features[i];
//f->translate_to = new MapNode(CONTENT_STONE, MINERAL_COAL); //f->translate_to = new MapNode(CONTENT_STONE, MINERAL_COAL);
f->setAllTextures(TextureSpec(irrlicht->getTextureId("coal.png"), /*f->setAllTextures(TextureSpec(irrlicht->getTextureId("coal.png"),
irrlicht->getTextureId("mineral_coal.png"))); irrlicht->getTextureId("mineral_coal.png")));*/
f->setAllTextures("stone.png^mineral_coal.png");
f->is_ground_content = true; f->is_ground_content = true;
i = CONTENT_WOOD; i = CONTENT_WOOD;
f = &g_content_features[i]; 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; f->is_ground_content = true;
i = CONTENT_MESE; i = CONTENT_MESE;
f = &g_content_features[i]; 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; f->is_ground_content = true;
i = CONTENT_CLOUD; i = CONTENT_CLOUD;
f = &g_content_features[i]; 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; f->is_ground_content = true;
i = CONTENT_AIR; i = CONTENT_AIR;
@ -150,7 +181,7 @@ void init_mapnode(IIrrlichtWrapper *irrlicht)
i = CONTENT_WATER; i = CONTENT_WATER;
f = &g_content_features[i]; f = &g_content_features[i];
f->setInventoryTexture(irrlicht->getTextureId("water.png")); //f->setInventoryTexture(irrlicht->getTextureId("water.png"));
f->param_type = CPT_LIGHT; f->param_type = CPT_LIGHT;
f->light_propagates = true; f->light_propagates = true;
f->solidness = 0; // Drawn separately, makes no faces f->solidness = 0; // Drawn separately, makes no faces
@ -162,21 +193,23 @@ void init_mapnode(IIrrlichtWrapper *irrlicht)
i = CONTENT_WATERSOURCE; i = CONTENT_WATERSOURCE;
f = &g_content_features[i]; f = &g_content_features[i];
f->setInventoryTexture(irrlicht->getTextureId("water.png")); //f->setInventoryTexture(irrlicht->getTextureId("water.png"));
if(new_style_water) if(new_style_water)
{ {
f->solidness = 0; // drawn separately, makes no faces f->solidness = 0; // drawn separately, makes no faces
} }
else // old style else // old style
{ {
f->setAllTextures(irrlicht->getTextureId("water.png"), WATER_ALPHA); f->solidness = 1;
TileSpec t; TileSpec t;
t.spec = TextureSpec(irrlicht->getTextureId("water.png")); if(g_texturesource)
t.texture = g_texturesource->getTexture("water.png");
t.alpha = WATER_ALPHA; t.alpha = WATER_ALPHA;
t.material_type = MATERIAL_ALPHA_VERTEX; t.material_type = MATERIAL_ALPHA_VERTEX;
t.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING; t.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
f->setAllTiles(t); f->setAllTiles(t);
f->solidness = 1;
} }
f->param_type = CPT_LIGHT; f->param_type = CPT_LIGHT;
f->light_propagates = true; f->light_propagates = true;
@ -188,7 +221,6 @@ void init_mapnode(IIrrlichtWrapper *irrlicht)
i = CONTENT_TORCH; i = CONTENT_TORCH;
f = &g_content_features[i]; f = &g_content_features[i];
f->setInventoryTexture(irrlicht->getTextureId("torch_on_floor.png"));
f->param_type = CPT_LIGHT; f->param_type = CPT_LIGHT;
f->light_propagates = true; f->light_propagates = true;
f->solidness = 0; // drawn separately, makes no faces f->solidness = 0; // drawn separately, makes no faces
@ -224,13 +256,23 @@ TileSpec MapNode::getTile(v3s16 dir)
else else
spec = content_features(d).tiles[dir_i]; 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; u8 mineral = param & 0x1f;
// Add mineral block texture std::string mineral_texture_name = mineral_block_texture(mineral);
textureid_t tid = mineral_block_texture(mineral); if(mineral_texture_name != "")
if(tid != 0) {
spec.spec.addTid(tid); 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; return spec;

@ -133,8 +133,10 @@ struct ContentFeatures
*/ */
TileSpec tiles[6]; TileSpec tiles[6];
// TODO: Somehow specify inventory image
//std::string inventory_image_path; //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 is_ground_content; //TODO: Remove, use walkable instead
bool light_propagates; bool light_propagates;
@ -167,27 +169,36 @@ struct ContentFeatures
~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) if(alpha != 255)
{ {
tiles[i].alpha = alpha; tiles[i].alpha = alpha;
tiles[i].material_type = MATERIAL_ALPHA_VERTEX; 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++) 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) 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; inventory_texture = spec;
} }*/
/*void setInventoryImage(std::string imgname) /*void setInventoryImage(std::string imgname)
{ {

@ -27,7 +27,8 @@ const char *mineral_filenames[MINERAL_COUNT] =
"mineral_iron.png" "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) void init_mineral(IIrrlichtWrapper *irrlicht)
{ {
@ -35,14 +36,17 @@ void init_mineral(IIrrlichtWrapper *irrlicht)
{ {
if(mineral_filenames[i] == NULL) if(mineral_filenames[i] == NULL)
continue; 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) if(mineral >= MINERAL_COUNT)
return 0; return "";
return mineral_textures[mineral]; return mineral_textures[mineral];
} }

@ -40,7 +40,8 @@ void init_mineral(IIrrlichtWrapper *irrlicht);
#define MINERAL_COUNT 3 #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) inline CraftItem * getDiggedMineralItem(u8 mineral)
{ {

@ -1642,6 +1642,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
os<<name<<L","; os<<name<<L",";
} }
os<<L"}"; os<<L"}";
if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
os<<" WARNING: Map saving is disabled."<<std::endl;
// Send message // Send message
SendChatMessage(peer_id, os.str()); SendChatMessage(peer_id, os.str());
} }

@ -20,6 +20,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef TEXTURE_HEADER #ifndef TEXTURE_HEADER
#define TEXTURE_HEADER #define TEXTURE_HEADER
// This file now contains all that was here
#include "tile.h"
// TODO: Remove this
typedef u16 textureid_t;
#if 0
#include "common_irrlicht.h" #include "common_irrlicht.h"
//#include "utility.h" //#include "utility.h"
#include "debug.h" #include "debug.h"
@ -122,3 +130,5 @@ struct TextureSpec
}; };
#endif #endif
#endif

@ -18,10 +18,770 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/ */
#include "tile.h" #include "tile.h"
//#include "porting.h" #include "debug.h"
// For IrrlichtWrapper
//#include "main.h"
//#include <string>
// 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<std::string, u32, u8, u8>
request = m_get_texture_queue.pop();
dstream<<"INFO: TextureSource::processQueue(): "
<<"got texture request with "
<<"name="<<request.key
<<std::endl;
GetResult<std::string, u32, u8, u8>
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="<<name<<std::endl;
{
/*
See if texture already exists
*/
JMutexAutoLock lock(m_atlaspointer_cache_mutex);
core::map<std::string, u32>::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="<<name<<std::endl;
// We're gonna ask the result to be put into here
ResultQueue<std::string, u32, u8, u8> 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="
<<name<<std::endl;
try
{
// Wait result for a second
GetResult<std::string, u32, u8, u8>
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."<<std::endl;
return 0;
}
}
dstream<<"WARNING: getTextureId(): Failed"<<std::endl;
return 0;
}
// Draw a progress bar on the image
void make_progressbar(float value, video::IImage *image);
/*
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,
video::IVideoDriver* driver);
/*
Generates an image from a full string like
"stone.png^mineral_coal.png^[crack0".
This is used by buildMainAtlas().
*/
video::IImage* generate_image_from_scratch(std::string name,
video::IVideoDriver* driver);
/*
This method generates all the textures
*/
u32 TextureSource::getTextureIdDirect(const std::string &name)
{
dstream<<"INFO: getTextureIdDirect(): name="<<name<<std::endl;
// Empty name means texture 0
if(name == "")
{
dstream<<"INFO: getTextureIdDirect(): name is empty"<<std::endl;
return 0;
}
/*
Calling only allowed from main thread
*/
if(get_current_thread_id() != m_main_thread)
{
dstream<<"ERROR: TextureSource::getTextureIdDirect() "
"called not from main thread"<<std::endl;
return 0;
}
/*
See if texture already exists
*/
{
JMutexAutoLock lock(m_atlaspointer_cache_mutex);
core::map<std::string, u32>::Node *n;
n = m_name_to_id.find(name);
if(n != NULL)
{
dstream<<"INFO: getTextureIdDirect(): name="<<name
<<" found in cache"<<std::endl;
return n->getValue();
}
}
dstream<<"INFO: getTextureIdDirect(): name="<<name
<<" NOT found in cache. Creating it."<<std::endl;
/*
Get the base image
*/
char separator = '^';
/*
This is set to the id of the base image.
If left 0, there is no base image and a completely new image
is made.
*/
u32 base_image_id = 0;
// Find last meta separator in name
s32 last_separator_position = -1;
for(s32 i=name.size()-1; i>=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="<<base_image_name<<std::endl;
base_image_id = getTextureIdDirect(base_image_name);
}
dstream<<"base_image_id="<<base_image_id<<std::endl;
video::IVideoDriver* driver = m_device->getVideoDriver();
assert(driver);
video::ITexture *t = NULL;
/*
An image will be built from files and then converted into a texture.
*/
video::IImage *baseimg = NULL;
// If a base image was found, copy it to baseimg
if(base_image_id != 0)
{
JMutexAutoLock lock(m_atlaspointer_cache_mutex);
SourceAtlasPointer ap = m_atlaspointer_cache[base_image_id];
video::IImage *image = ap.atlas_img;
core::dimension2d<u32> dim = ap.intsize;
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
core::position2d<s32> pos_to(0,0);
core::position2d<s32> pos_from = ap.intpos;
image->copyTo(
baseimg, // target
v2s32(0,0), // position in target
core::rect<s32>(pos_from, dim) // from
);
dstream<<"INFO: getTextureIdDirect(): Loaded \""
<<base_image_name<<"\" from image cache"
<<std::endl;
}
/*
Parse out the last part of the name of the image and act
according to it
*/
std::string last_part_of_name = name.substr(last_separator_position+1);
dstream<<"last_part_of_name="<<last_part_of_name<<std::endl;
// Generate image according to part of name
if(generate_image(last_part_of_name, baseimg, driver) == false)
{
dstream<<"INFO: getTextureIdDirect(): "
"failed to generate \""<<last_part_of_name<<"\""
<<std::endl;
return 0;
}
// If no resulting image, return NULL
if(baseimg == NULL)
{
dstream<<"WARNING: getTextureIdDirect(): baseimg is NULL (attempted to"
" create texture \""<<name<<"\""<<std::endl;
return 0;
}
// Create texture from resulting image
t = driver->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="<<name
<<": succesfully returning id="<<id<<std::endl;
return id;
}
std::string TextureSource::getTextureName(u32 id)
{
JMutexAutoLock lock(m_atlaspointer_cache_mutex);
if(id >= m_atlaspointer_cache.size())
{
dstream<<"WARNING: TextureSource::getTextureName(): id="<<id
<<" >= m_atlaspointer_cache.size()="
<<m_atlaspointer_cache.size()<<std::endl;
return "";
}
return m_atlaspointer_cache[id].name;
}
AtlasPointer TextureSource::getTexture(u32 id)
{
JMutexAutoLock lock(m_atlaspointer_cache_mutex);
if(id >= m_atlaspointer_cache.size())
return AtlasPointer(0, NULL);
return m_atlaspointer_cache[id].a;
}
void TextureSource::buildMainAtlas()
{
dstream<<"TextureSource::buildMainAtlas()"<<std::endl;
//return; // Disable (for testing)
video::IVideoDriver* driver = m_device->getVideoDriver();
assert(driver);
JMutexAutoLock lock(m_atlaspointer_cache_mutex);
// Create an image of the right size
core::dimension2d<u32> 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<std::string> 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<s32> pos_in_atlas(0,0);
for(u32 i=0; i<sourcelist.size(); i++)
{
std::string name = sourcelist[i];
/*video::IImage *img = driver->createImageFromFile(
porting::getDataPath(name.c_str()).c_str());
if(img == NULL)
continue;
core::dimension2d<u32> 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<u32> dim = img2->getDimension();
// Tile it a few times in the X direction
u16 xwise_tiling = 16;
for(u32 j=0; j<xwise_tiling; j++)
{
// Copy the copy to the atlas
img2->copyToWithAlpha(atlas_img,
pos_in_atlas + v2s32(j*dim.Width,0),
core::rect<s32>(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; i<sourcelist.size(); i++)
{
std::string name = sourcelist[i];
u32 id = m_name_to_id[name];
m_atlaspointer_cache[id].a.atlas = t;
}
/*
Write image to file so that it can be inspected
*/
driver->writeImageToFile(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="<<name<<std::endl;
/*
Get the base image
*/
video::IImage *baseimg = NULL;
char separator = '^';
// Find last meta separator in name
s32 last_separator_position = -1;
for(s32 i=name.size()-1; i>=0; i--)
{
if(name[i] == separator)
{
last_separator_position = i;
break;
}
}
/*dstream<<"INFO: generate_image_from_scratch(): "
<<"last_separator_position="<<last_separator_position
<<std::endl;*/
/*
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: generate_image_from_scratch(): Calling itself recursively"
" to get base image, name="<<base_image_name<<std::endl;
baseimg = generate_image_from_scratch(base_image_name, driver);
}
/*
Parse out the last part of the name of the image and act
according to it
*/
std::string last_part_of_name = name.substr(last_separator_position+1);
dstream<<"last_part_of_name="<<last_part_of_name<<std::endl;
// Generate image according to part of name
if(generate_image(last_part_of_name, baseimg, driver) == false)
{
dstream<<"INFO: generate_image_from_scratch(): "
"failed to generate \""<<last_part_of_name<<"\""
<<std::endl;
return NULL;
}
return baseimg;
}
bool generate_image(std::string part_of_name, video::IImage *& baseimg,
video::IVideoDriver* driver)
{
// Stuff starting with [ are special commands
if(part_of_name[0] != '[')
{
// A normal texture; load it from a file
std::string path = porting::getDataPath(part_of_name.c_str());
dstream<<"INFO: getTextureIdDirect(): Loading path \""<<path
<<"\""<<std::endl;
video::IImage *image = driver->createImageFromFile(path.c_str());
if(image == NULL)
{
dstream<<"WARNING: Could not load image \""<<part_of_name
<<"\" from path \""<<path<<"\""
<<" while building texture"<<std::endl;
return false;
}
// If base image is NULL, load as base.
if(baseimg == NULL)
{
dstream<<"INFO: Setting "<<part_of_name<<" as base"<<std::endl;
/*
Copy it this way to get an alpha channel.
Otherwise images with alpha cannot be blitted on
images that don't have alpha in the original file.
*/
core::dimension2d<u32> dim = image->getDimension();
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
image->copyTo(baseimg);
image->drop();
}
// Else blit on base.
else
{
dstream<<"INFO: Blitting "<<part_of_name<<" on base"<<std::endl;
// Size of the copied area
core::dimension2d<u32> dim = image->getDimension();
//core::dimension2d<u32> dim(16,16);
// Position to copy the blitted to in the base image
core::position2d<s32> pos_to(0,0);
// Position to copy the blitted from in the blitted image
core::position2d<s32> pos_from(0,0);
// Blit
image->copyToWithAlpha(baseimg, pos_to,
core::rect<s32>(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 \""<<part_of_name<<"\""
<<std::endl;
if(part_of_name.substr(0,6) == "[crack")
{
if(baseimg == NULL)
{
dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
<<"for part_of_name="<<part_of_name
<<", cancelling."<<std::endl;
return false;
}
u16 progression = stoi(part_of_name.substr(6));
// Size of the base image
core::dimension2d<u32> dim_base = baseimg->getDimension();
// Crack will be drawn at this size
u32 cracksize = 16;
// Size of the crack image
core::dimension2d<u32> dim_crack(cracksize,cracksize);
// Position to copy the crack from in the crack image
core::position2d<s32> 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<s32>(pos_other, dim_base),
video::SColor(255,255,255,255),
NULL);*/
for(u32 y0=0; y0<dim_base.Height/dim_crack.Height; y0++)
for(u32 x0=0; x0<dim_base.Width/dim_crack.Width; x0++)
{
// Position to copy the crack to in the base image
core::position2d<s32> pos_base(x0*cracksize, y0*cracksize);
crackimage->copyToWithAlpha(baseimg, pos_base,
core::rect<s32>(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="<<w0<<" h="<<h0<<std::endl;
core::dimension2d<u32> dim(w0,h0);
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
while(sf.atend() == false)
{
u32 x = stoi(sf.next(","));
u32 y = stoi(sf.next("="));
std::string filename = sf.next(":");
dstream<<"INFO: Adding \""<<filename
<<"\" to combined ("<<x<<","<<y<<")"
<<std::endl;
video::IImage *img = driver->createImageFromFile(
porting::getDataPath(filename.c_str()).c_str());
if(img)
{
core::dimension2d<u32> dim = img->getDimension();
dstream<<"INFO: Size "<<dim.Width
<<"x"<<dim.Height<<std::endl;
core::position2d<s32> 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<s32>(v2s32(0,0), dim),
video::SColor(255,255,255,255),
NULL);
img2->drop();
}
else
{
dstream<<"WARNING: img==NULL"<<std::endl;
}
}
}
else if(part_of_name.substr(0,12) == "[progressbar")
{
if(baseimg == NULL)
{
dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
<<"for part_of_name="<<part_of_name
<<", cancelling."<<std::endl;
return false;
}
float value = stof(part_of_name.substr(12));
make_progressbar(value, baseimg);
}
// "[noalpha:filename.png"
// Use an image without it's alpha channel
else if(part_of_name.substr(0,8) == "[noalpha")
{
if(baseimg != NULL)
{
dstream<<"WARNING: getTextureIdDirect(): baseimg!=NULL "
<<"for part_of_name="<<part_of_name
<<", cancelling."<<std::endl;
return false;
}
std::string filename = part_of_name.substr(9);
std::string path = porting::getDataPath(filename.c_str());
dstream<<"INFO: getTextureIdDirect(): Loading path \""<<path
<<"\""<<std::endl;
video::IImage *image = driver->createImageFromFile(path.c_str());
if(image == NULL)
{
dstream<<"WARNING: getTextureIdDirect(): Loading path \""
<<path<<"\" failed"<<std::endl;
}
else
{
core::dimension2d<u32> dim = image->getDimension();
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
// Set alpha to full
for(u32 y=0; y<dim.Height; y++)
for(u32 x=0; x<dim.Width; x++)
{
video::SColor c = image->getPixel(x,y);
c.setAlpha(255);
image->setPixel(x,y,c);
}
// Blit
image->copyTo(baseimg);
image->drop();
}
}
else
{
dstream<<"WARNING: getTextureIdDirect(): Invalid "
" modification: \""<<part_of_name<<"\""<<std::endl;
}
}
return true;
}
void make_progressbar(float value, video::IImage *image)
{
if(image == NULL)
return;
core::dimension2d<u32> 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; x0<barwidth; x0++)
{
video::SColor *c;
if(x0 < barvalue_i)
c = &active;
else
c = &inactive;
u32 x = x0 + barpos.X;
for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
{
image->setPixel(x,y, *c);
}
}
}

@ -21,10 +21,208 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define TILE_HEADER #define TILE_HEADER
#include "common_irrlicht.h" #include "common_irrlicht.h"
//#include "utility.h" #include "threads.h"
#include "texture.h" #include "utility.h"
#include <string> #include <string>
/*
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<SourceAtlasPointer> m_atlaspointer_cache;
// Maps a texture name to an index in the former.
core::map<std::string, u32> 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<std::string, u32, u8, u8> m_get_texture_queue;
};
enum MaterialType{ enum MaterialType{
MATERIAL_ALPHA_NONE, MATERIAL_ALPHA_NONE,
MATERIAL_ALPHA_VERTEX, MATERIAL_ALPHA_VERTEX,
@ -38,12 +236,17 @@ enum MaterialType{
/* /*
This fully defines the looks of a tile. This fully defines the looks of a tile.
The SMaterial of a tile is constructed according to this. The SMaterial of a tile is constructed according to this.
TODO: Change this to use an AtlasPointer
*/ */
struct TileSpec struct TileSpec
{ {
TileSpec(): TileSpec():
texture(0),
alpha(255), alpha(255),
material_type(MATERIAL_ALPHA_NONE), material_type(MATERIAL_ALPHA_NONE),
// Use this so that leaves don't need a separate material
//material_type(MATERIAL_ALPHA_SIMPLE),
material_flags( material_flags(
MATERIAL_FLAG_BACKFACE_CULLING MATERIAL_FLAG_BACKFACE_CULLING
) )
@ -53,7 +256,7 @@ struct TileSpec
bool operator==(TileSpec &other) bool operator==(TileSpec &other)
{ {
return ( return (
spec == other.spec && texture == other.texture &&
alpha == other.alpha && alpha == other.alpha &&
material_type == other.material_type && material_type == other.material_type &&
material_flags == other.material_flags material_flags == other.material_flags
@ -80,8 +283,14 @@ struct TileSpec
material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING) ? true : false; material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING) ? true : false;
} }
// Specification of texture // NOTE: Deprecated, i guess?
TextureSpec spec; 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 // Vertex alpha
u8 alpha; u8 alpha;
// Material type // Material type