4ad647194e0f3413f7252d5b171d6a702a1d2d57
[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_running = true;
9         m_main_thread = get_current_thread_id();
10         m_device_mutex.Init();
11         m_device = device;
12 }
13
14 void IrrlichtWrapper::Run()
15 {
16         /*
17                 Fetch textures
18         */
19         if(m_get_texture_queue.size() > 0)
20         {
21                 GetRequest<TextureSpec, video::ITexture*, u8, u8>
22                                 request = m_get_texture_queue.pop();
23
24                 dstream<<"got texture request with"
25                                 <<" key.tids[0]="<<request.key.tids[0]
26                                 <<" [1]="<<request.key.tids[1]
27                                 <<std::endl;
28
29                 GetResult<TextureSpec, video::ITexture*, u8, u8>
30                                 result;
31                 result.key = request.key;
32                 result.callers = request.callers;
33                 result.item = getTextureDirect(request.key);
34
35                 request.dest->push_back(result);
36         }
37 }
38
39 void IrrlichtWrapper::Shutdown(bool shutdown)
40 {
41         m_running = !shutdown;
42 }
43
44 textureid_t IrrlichtWrapper::getTextureId(const std::string &name)
45 {
46         u32 id = m_namecache.getId(name);
47         return id;
48 }
49
50 std::string IrrlichtWrapper::getTextureName(textureid_t id)
51 {
52         std::string name("");
53         m_namecache.getValue(id, name);
54         // In case it was found, return the name; otherwise return an empty name.
55         return name;
56 }
57
58 video::ITexture* IrrlichtWrapper::getTexture(const std::string &name)
59 {
60         TextureSpec spec(getTextureId(name));
61         return getTexture(spec);
62 }
63
64 video::ITexture* IrrlichtWrapper::getTexture(const TextureSpec &spec)
65 {
66         if(spec.empty())
67                 return NULL;
68         
69         video::ITexture *t = m_texturecache.get(spec);
70         if(t != NULL)
71                 return t;
72         
73         if(get_current_thread_id() == m_main_thread)
74         {
75                 dstream<<"Getting texture directly: spec.tids[0]="
76                                 <<spec.tids[0]<<std::endl;
77                                 
78                 t = getTextureDirect(spec);
79         }
80         else
81         {
82                 // If irrlicht has shut down, just return NULL
83                 if(m_running == false)
84                         return NULL;
85
86                 // We're gonna ask the result to be put into here
87                 ResultQueue<TextureSpec, video::ITexture*, u8, u8> result_queue;
88                 
89                 // Throw a request in
90                 m_get_texture_queue.add(spec, 0, 0, &result_queue);
91                 
92                 dstream<<"Waiting for texture from main thread: spec.tids[0]="
93                                 <<spec.tids[0]<<std::endl;
94                 
95                 try
96                 {
97                         // Wait result for a second
98                         GetResult<TextureSpec, video::ITexture*, u8, u8>
99                                         result = result_queue.pop_front(1000);
100                 
101                         // Check that at least something worked OK
102                         assert(result.key == spec);
103
104                         t = result.item;
105                 }
106                 catch(ItemNotFoundException &e)
107                 {
108                         dstream<<"Waiting for texture timed out."<<std::endl;
109                         t = NULL;
110                 }
111         }
112
113         // Add to cache and return
114         m_texturecache.set(spec, t);
115         return t;
116 }
117
118 // Draw a progress bar on the image
119 void make_progressbar(float value, video::IImage *image);
120
121 /*
122         Texture fetcher/maker function, called always from the main thread
123 */
124
125 video::ITexture* IrrlichtWrapper::getTextureDirect(const TextureSpec &spec)
126 {
127         // This would result in NULL image
128         if(spec.empty())
129                 return NULL;
130         
131         // Don't generate existing stuff
132         video::ITexture *t = m_texturecache.get(spec);
133         if(t != NULL)
134         {
135                 dstream<<"WARNING: Existing stuff requested from "
136                                 "getTextureDirect()"<<std::endl;
137                 return t;
138         }
139         
140         video::IVideoDriver* driver = m_device->getVideoDriver();
141
142         /*
143                 An image will be built from files and then converted into a texture.
144         */
145         video::IImage *baseimg = NULL;
146
147         /*
148                 Irrlicht requires a name for every texture, with which it
149                 will be stored internally in irrlicht.
150         */
151         std::string texture_name;
152
153         for(u32 i=0; i<TEXTURE_SPEC_TEXTURE_COUNT; i++)
154         {
155                 textureid_t tid = spec.tids[i];
156                 if(tid == 0)
157                         continue;
158
159                 std::string name = getTextureName(tid);
160                 
161                 // Add something to the name so that it is a unique identifier.
162                 texture_name += "[";
163                 texture_name += name;
164                 texture_name += "]";
165
166                 if(name[0] != '[')
167                 {
168                         // A normal texture; load it from a file
169                         std::string path = porting::getDataPath(name.c_str());
170                         dstream<<"getTextureDirect(): Loading path \""<<path
171                                         <<"\""<<std::endl;
172                         
173                         // DEBUG
174                         /*{
175                                 dstream<<"DEBUG CODE: Loading base image "
176                                                 "directly to texture"<<std::endl;
177                                 t = driver->getTexture(path.c_str());
178                                 driver->renameTexture(t, texture_name.c_str());
179                                 return t;
180                         }*/
181                         
182                         video::IImage *image = driver->createImageFromFile(path.c_str());
183
184                         if(image == NULL)
185                         {
186                                 dstream<<"WARNING: Could not load image \""<<name
187                                                 <<"\" from path \""<<path<<"\""
188                                                 <<" while building texture"<<std::endl;
189                                 continue;
190                         }
191
192                         // If base image is NULL, load as base.
193                         if(baseimg == NULL)
194                         {
195                                 dstream<<"Setting "<<name<<" as base"<<std::endl;
196                                 /*
197                                         Copy it this way to get an alpha channel.
198                                         Otherwise images with alpha cannot be blitted on 
199                                         images that don't have alpha in the original file.
200                                 */
201                                 // This is a deprecated method
202                                 //baseimg = driver->createImage(video::ECF_A8R8G8B8, image);
203                                 core::dimension2d<u32> dim = image->getDimension();
204                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
205                                 image->copyTo(baseimg);
206                                 image->drop();
207                                 //baseimg = image;
208                         }
209                         // Else blit on base.
210                         else
211                         {
212                                 dstream<<"Blitting "<<name<<" on base"<<std::endl;
213                                 // Size of the copied area
214                                 core::dimension2d<u32> dim = image->getDimension();
215                                 //core::dimension2d<u32> dim(16,16);
216                                 // Position to copy the blitted to in the base image
217                                 core::position2d<s32> pos_to(0,0);
218                                 // Position to copy the blitted from in the blitted image
219                                 core::position2d<s32> pos_from(0,0);
220                                 // Blit
221                                 image->copyToWithAlpha(baseimg, pos_to,
222                                                 core::rect<s32>(pos_from, dim),
223                                                 video::SColor(255,255,255,255),
224                                                 NULL);
225                                 // Drop image
226                                 image->drop();
227                         }
228                 }
229                 else
230                 {
231                         // A special texture modification
232                         dstream<<"getTextureDirect(): generating \""<<name<<"\""
233                                         <<std::endl;
234                         if(name.substr(0,6) == "[crack")
235                         {
236                                 u16 progression = stoi(name.substr(6));
237                                 // Size of the base image
238                                 core::dimension2d<u32> dim(16, 16);
239                                 // Size of the crack image
240                                 //core::dimension2d<u32> dim_crack(16, 16 * CRACK_ANIMATION_LENGTH);
241                                 // Position to copy the crack to in the base image
242                                 core::position2d<s32> pos_base(0, 0);
243                                 // Position to copy the crack from in the crack image
244                                 core::position2d<s32> pos_other(0, 16 * progression);
245
246                                 video::IImage *crackimage = driver->createImageFromFile(
247                                                 porting::getDataPath("crack.png").c_str());
248                                 crackimage->copyToWithAlpha(baseimg, v2s32(0,0),
249                                                 core::rect<s32>(pos_other, dim),
250                                                 video::SColor(255,255,255,255),
251                                                 NULL);
252                                 crackimage->drop();
253                         }
254                         else if(name.substr(0,12) == "[progressbar")
255                         {
256                                 float value = stof(name.substr(12));
257                                 make_progressbar(value, baseimg);
258                         }
259                         else
260                         {
261                                 dstream<<"WARNING: getTextureDirect(): Invalid "
262                                                 " texture: \""<<name<<"\""<<std::endl;
263                         }
264                 }
265         }
266
267         // If no resulting image, return NULL
268         if(baseimg == NULL)
269         {
270                 dstream<<"getTextureDirect(): baseimg is NULL (attempted to"
271                                 " create texture \""<<texture_name<<"\""<<std::endl;
272                 return NULL;
273         }
274         
275         /*// DEBUG: Paint some pixels
276         video::SColor c(255,255,0,0);
277         baseimg->setPixel(1,1, c);
278         baseimg->setPixel(1,14, c);
279         baseimg->setPixel(14,1, c);
280         baseimg->setPixel(14,14, c);*/
281
282         // Create texture from resulting image
283         t = driver->addTexture(texture_name.c_str(), baseimg);
284         baseimg->drop();
285
286         dstream<<"getTextureDirect(): created texture \""<<texture_name
287                         <<"\""<<std::endl;
288
289         return t;
290
291 }
292
293 void make_progressbar(float value, video::IImage *image)
294 {
295         if(image == NULL)
296                 return;
297         
298         core::dimension2d<u32> size = image->getDimension();
299
300         u32 barheight = 1;
301         u32 barpad_x = 1;
302         u32 barpad_y = 1;
303         u32 barwidth = size.Width - barpad_x*2;
304         v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
305
306         u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
307
308         video::SColor active(255,255,0,0);
309         video::SColor inactive(255,0,0,0);
310         for(u32 x0=0; x0<barwidth; x0++)
311         {
312                 video::SColor *c;
313                 if(x0 < barvalue_i)
314                         c = &active;
315                 else
316                         c = &inactive;
317                 u32 x = x0 + barpos.X;
318                 for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
319                 {
320                         image->setPixel(x,y, *c);
321                 }
322         }
323 }
324
325