Reworked texture, material, mineral and whatever handling
[oweals/minetest.git] / src / irrlichtwrapper.cpp
1 #include "irrlichtwrapper.h"
2 #include "constants.h"
3 #include "string.h"
4 #include "strfnd.h"
5
6 IrrlichtWrapper::IrrlichtWrapper(IrrlichtDevice *device)
7 {
8         m_main_thread = get_current_thread_id();
9         m_device_mutex.Init();
10         m_device = device;
11 }
12
13 void IrrlichtWrapper::Run()
14 {
15         /*
16                 Fetch textures
17         */
18         if(m_get_texture_queue.size() > 0)
19         {
20                 GetRequest<std::string, video::ITexture*, u8, u8>
21                                 request = m_get_texture_queue.pop();
22
23                 dstream<<"got texture request with key="
24                                 <<request.key<<std::endl;
25
26                 GetResult<std::string, video::ITexture*, u8, u8>
27                                 result;
28                 result.key = request.key;
29                 result.callers = request.callers;
30                 result.item = getTextureDirect(request.key);
31
32                 request.dest->push_back(result);
33         }
34 }
35
36 video::ITexture* IrrlichtWrapper::getTexture(const std::string &spec)
37 {
38         if(spec == "")
39                 return NULL;
40         
41         video::ITexture *t = m_texturecache.get(spec);
42         if(t != NULL)
43                 return t;
44         
45         if(get_current_thread_id() == m_main_thread)
46         {
47                 dstream<<"Getting texture directly: spec="
48                                 <<spec<<std::endl;
49                                 
50                 t = getTextureDirect(spec);
51         }
52         else
53         {
54                 // We're gonna ask the result to be put into here
55                 ResultQueue<std::string, video::ITexture*, u8, u8> result_queue;
56                 
57                 // Throw a request in
58                 m_get_texture_queue.add(spec, 0, 0, &result_queue);
59                 
60                 dstream<<"Waiting for texture from main thread: "
61                                 <<spec<<std::endl;
62                 
63                 try
64                 {
65                         // Wait result for a second
66                         GetResult<std::string, video::ITexture*, u8, u8>
67                                         result = result_queue.pop_front(1000);
68                 
69                         // Check that at least something worked OK
70                         assert(result.key == spec);
71
72                         t = result.item;
73                 }
74                 catch(ItemNotFoundException &e)
75                 {
76                         dstream<<"Waiting for texture timed out."<<std::endl;
77                         t = NULL;
78                 }
79         }
80
81         // Add to cache and return
82         m_texturecache.set(spec, t);
83         return t;
84 }
85
86 /*
87         Non-thread-safe functions
88 */
89
90 /*
91         Texture modifier functions
92 */
93
94 // blitted_name = eg. "mineral_coal.png"
95 video::ITexture * make_blitname(const std::string &blitted_name,
96                 video::ITexture *original,
97                 const char *newname, video::IVideoDriver* driver)
98 {
99         if(original == NULL)
100                 return NULL;
101         
102         // Size of the base image
103         core::dimension2d<u32> dim(16, 16);
104         // Position to copy the blitted to in the base image
105         core::position2d<s32> pos_base(0, 0);
106         // Position to copy the blitted from in the blitted image
107         core::position2d<s32> pos_other(0, 0);
108
109         video::IImage *baseimage = driver->createImage(original, pos_base, dim);
110         assert(baseimage);
111
112         video::IImage *blittedimage = driver->createImageFromFile(porting::getDataPath(blitted_name.c_str()).c_str());
113         assert(blittedimage);
114         
115         // Then copy the right part of blittedimage to baseimage
116         
117         blittedimage->copyToWithAlpha(baseimage, v2s32(0,0),
118                         core::rect<s32>(pos_other, dim),
119                         video::SColor(255,255,255,255),
120                         NULL);
121         
122         blittedimage->drop();
123
124         // Create texture from resulting image
125
126         video::ITexture *newtexture = driver->addTexture(newname, baseimage);
127
128         baseimage->drop();
129
130         return newtexture;
131 }
132
133 video::ITexture * make_crack(u16 progression, video::ITexture *original,
134                 const char *newname, video::IVideoDriver* driver)
135 {
136         if(original == NULL)
137                 return NULL;
138         
139         // Size of the base image
140         core::dimension2d<u32> dim(16, 16);
141         // Size of the crack image
142         //core::dimension2d<u32> dim_crack(16, 16 * CRACK_ANIMATION_LENGTH);
143         // Position to copy the crack to in the base image
144         core::position2d<s32> pos_base(0, 0);
145         // Position to copy the crack from in the crack image
146         core::position2d<s32> pos_other(0, 16 * progression);
147
148         video::IImage *baseimage = driver->createImage(original, pos_base, dim);
149         assert(baseimage);
150
151         video::IImage *crackimage = driver->createImageFromFile(porting::getDataPath("crack.png").c_str());
152         assert(crackimage);
153         
154         // Then copy the right part of crackimage to baseimage
155         
156         crackimage->copyToWithAlpha(baseimage, v2s32(0,0),
157                         core::rect<s32>(pos_other, dim),
158                         video::SColor(255,255,255,255),
159                         NULL);
160         
161         crackimage->drop();
162
163         // Create texture from resulting image
164
165         video::ITexture *newtexture = driver->addTexture(newname, baseimage);
166
167         baseimage->drop();
168
169         return newtexture;
170 }
171
172 #if 0
173 video::ITexture * make_sidegrass(video::ITexture *original,
174                 const char *newname, video::IVideoDriver* driver)
175 {
176         if(original == NULL)
177                 return NULL;
178         
179         // Size of the base image
180         core::dimension2d<u32> dim(16, 16);
181         // Position to copy the grass to in the base image
182         core::position2d<s32> pos_base(0, 0);
183         // Position to copy the grass from in the grass image
184         core::position2d<s32> pos_other(0, 0);
185
186         video::IImage *baseimage = driver->createImage(original, pos_base, dim);
187         assert(baseimage);
188
189         video::IImage *grassimage = driver->createImageFromFile(porting::getDataPath("grass_side.png").c_str());
190         assert(grassimage);
191         
192         // Then copy the right part of grassimage to baseimage
193         
194         grassimage->copyToWithAlpha(baseimage, v2s32(0,0),
195                         core::rect<s32>(pos_other, dim),
196                         video::SColor(255,255,255,255),
197                         NULL);
198         
199         grassimage->drop();
200
201         // Create texture from resulting image
202
203         video::ITexture *newtexture = driver->addTexture(newname, baseimage);
204
205         baseimage->drop();
206
207         return newtexture;
208 }
209 #endif
210
211 video::ITexture * make_progressbar(float value, video::ITexture *original,
212                 const char *newname, video::IVideoDriver* driver)
213 {
214         if(original == NULL)
215                 return NULL;
216         
217         core::position2d<s32> pos_base(0, 0);
218         core::dimension2d<u32> dim = original->getOriginalSize();
219
220         video::IImage *baseimage = driver->createImage(original, pos_base, dim);
221         assert(baseimage);
222         
223         core::dimension2d<u32> size = baseimage->getDimension();
224
225         u32 barheight = 1;
226         u32 barpad_x = 1;
227         u32 barpad_y = 1;
228         u32 barwidth = size.Width - barpad_x*2;
229         v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
230
231         u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
232
233         video::SColor active(255,255,0,0);
234         video::SColor inactive(255,0,0,0);
235         for(u32 x0=0; x0<barwidth; x0++)
236         {
237                 video::SColor *c;
238                 if(x0 < barvalue_i)
239                         c = &active;
240                 else
241                         c = &inactive;
242                 u32 x = x0 + barpos.X;
243                 for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
244                 {
245                         baseimage->setPixel(x,y, *c);
246                 }
247         }
248         
249         video::ITexture *newtexture = driver->addTexture(newname, baseimage);
250
251         baseimage->drop();
252
253         return newtexture;
254 }
255
256 /*
257         Texture fetcher/maker function, called always from the main thread
258 */
259
260 video::ITexture* IrrlichtWrapper::getTextureDirect(const std::string &spec)
261 {
262         if(spec == "")
263                 return NULL;
264
265         video::IVideoDriver* driver = m_device->getVideoDriver();
266         
267         /*
268                 Input (spec) is something like this:
269                 "/usr/share/minetest/stone.png[[mod:mineral0[[mod:crack3"
270         */
271         
272         video::ITexture* t = NULL;
273         std::string modmagic = "[[mod:";
274         Strfnd f(spec);
275         std::string path = f.next(modmagic);
276         t = driver->getTexture(path.c_str());
277         std::string texture_name = path;
278         while(f.atend() == false)
279         {
280                 std::string mod = f.next(modmagic);
281                 texture_name += modmagic + mod;
282                 dstream<<"Making texture \""<<texture_name<<"\""<<std::endl;
283                 /*if(mod == "sidegrass")
284                 {
285                         t = make_sidegrass(t, texture_name.c_str(), driver);
286                 }
287                 else*/
288                 if(mod.substr(0, 9) == "blitname:")
289                 {
290                         //t = make_sidegrass(t, texture_name.c_str(), driver);
291                         t = make_blitname(mod.substr(9), t, texture_name.c_str(), driver);
292                 }
293                 else if(mod.substr(0,5) == "crack")
294                 {
295                         u16 prog = stoi(mod.substr(5));
296                         t = make_crack(prog, t, texture_name.c_str(), driver);
297                 }
298                 else if(mod.substr(0,11) == "progressbar")
299                 {
300                         float value = stof(mod.substr(11));
301                         t = make_progressbar(value, t, texture_name.c_str(), driver);
302                 }
303                 else
304                 {
305                         dstream<<"Invalid texture mod: \""<<mod<<"\""<<std::endl;
306                 }
307         }
308         return t;
309
310 #if 0
311         video::ITexture* t = NULL;
312         const char *modmagic = "[[mod:";
313         const s32 modmagic_len = 6;
314         enum{
315                 READMODE_PATH,
316                 READMODE_MOD
317         } readmode = READMODE_PATH;
318         s32 specsize = spec.size()+1;
319         char *strcache = (char*)malloc(specsize);
320         assert(strcache);
321         char *path = NULL;
322         s32 length = 0;
323         // Next index of modmagic to be found
324         s32 modmagic_i = 0;
325         u32 i=0;
326         for(;;)
327         {
328                 strcache[length++] = spec[i];
329                 
330                 bool got_modmagic = false;
331
332                 /*
333                         Check modmagic
334                 */
335                 if(spec[i] == modmagic[modmagic_i])
336                 {
337                         modmagic_i++;
338                         if(modmagic_i == modmagic_len)
339                         {
340                                 got_modmagic = true;
341                                 modmagic_i = 0;
342                                 length -= modmagic_len;
343                         }
344                 }
345                 else
346                         modmagic_i = 0;
347                 
348                 // Set i to be the length of read string
349                 i++;
350
351                 if(got_modmagic || i >= spec.size())
352                 {
353                         strcache[length] = '\0';
354                         // Now our string is in strcache, ending in \0
355                         
356                         if(readmode == READMODE_PATH)
357                         {
358                                 // Get initial texture (strcache is path)
359                                 assert(t == NULL);
360                                 t = driver->getTexture(strcache);
361                                 readmode = READMODE_MOD;
362                                 path = strcache;
363                                 strcache = (char*)malloc(specsize);
364                                 assert(strcache);
365                         }
366                         else
367                         {
368                                 dstream<<"Parsing mod \""<<strcache<<"\""<<std::endl;
369                                 // The name of the result of adding this mod.
370                                 // This doesn't have to be fast so std::string is used.
371                                 std::string name(path);
372                                 name += "[[mod:";
373                                 name += strcache;
374                                 dstream<<"Name of modded texture is \""<<name<<"\""
375                                                 <<std::endl;
376                                 // Sidegrass
377                                 if(strcmp(strcache, "sidegrass") == 0)
378                                 {
379                                         t = make_sidegrass(t, name.c_str(), driver);
380                                 }
381                                 else
382                                 {
383                                         dstream<<"Invalid texture mod"<<std::endl;
384                                 }
385                         }
386
387                         length = 0;
388                 }
389
390                 if(i >= spec.size())
391                         break;
392         }
393
394         /*if(spec.mod == NULL)
395         {
396                 dstream<<"IrrlichtWrapper::getTextureDirect: Loading texture "
397                                 <<spec.path<<std::endl;
398                 return driver->getTexture(spec.path.c_str());
399         }
400
401         dstream<<"IrrlichtWrapper::getTextureDirect: Loading and modifying "
402                         "texture "<<spec.path<<" to make "<<spec.name<<std::endl;
403
404         video::ITexture *base = driver->getTexture(spec.path.c_str());
405         video::ITexture *result = spec.mod->make(base, spec.name.c_str(), driver);
406
407         delete spec.mod;*/
408         
409         if(strcache)
410                 free(strcache);
411         if(path)
412                 free(path);
413         
414         return t;
415 #endif
416 }
417
418