partly working chunk-based map generator (doesn't save properly, spawn is pretty...
[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                         video::IImage *image = driver->createImageFromFile(path.c_str());
173
174                         if(image == NULL)
175                         {
176                                 dstream<<"WARNING: Could not load image \""<<name
177                                                 <<"\" from path \""<<path<<"\""
178                                                 <<" while building texture"<<std::endl;
179                                 continue;
180                         }
181
182                         // If base image is NULL, load as base.
183                         if(baseimg == NULL)
184                         {
185                                 dstream<<"Setting "<<name<<" as base"<<std::endl;
186                                 /*
187                                         Copy it this way to get an alpha channel.
188                                         Otherwise images with alpha cannot be blitted on 
189                                         images that don't have alpha in the original file.
190                                 */
191                                 // This is a deprecated method
192                                 //baseimg = driver->createImage(video::ECF_A8R8G8B8, image);
193                                 core::dimension2d<u32> dim = image->getDimension();
194                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
195                                 image->copyTo(baseimg);
196                                 image->drop();
197                                 //baseimg = image;
198                         }
199                         // Else blit on base.
200                         else
201                         {
202                                 dstream<<"Blitting "<<name<<" on base"<<std::endl;
203                                 // Size of the copied area
204                                 core::dimension2d<u32> dim = image->getDimension();
205                                 //core::dimension2d<u32> dim(16,16);
206                                 // Position to copy the blitted to in the base image
207                                 core::position2d<s32> pos_to(0,0);
208                                 // Position to copy the blitted from in the blitted image
209                                 core::position2d<s32> pos_from(0,0);
210                                 // Blit
211                                 image->copyToWithAlpha(baseimg, pos_to,
212                                                 core::rect<s32>(pos_from, dim),
213                                                 video::SColor(255,255,255,255),
214                                                 NULL);
215                                 // Drop image
216                                 image->drop();
217                         }
218                 }
219                 else
220                 {
221                         // A special texture modification
222                         dstream<<"getTextureDirect(): generating \""<<name<<"\""
223                                         <<std::endl;
224                         if(name.substr(0,6) == "[crack")
225                         {
226                                 u16 progression = stoi(name.substr(6));
227                                 // Size of the base image
228                                 core::dimension2d<u32> dim(16, 16);
229                                 // Size of the crack image
230                                 //core::dimension2d<u32> dim_crack(16, 16 * CRACK_ANIMATION_LENGTH);
231                                 // Position to copy the crack to in the base image
232                                 core::position2d<s32> pos_base(0, 0);
233                                 // Position to copy the crack from in the crack image
234                                 core::position2d<s32> pos_other(0, 16 * progression);
235
236                                 video::IImage *crackimage = driver->createImageFromFile(
237                                                 porting::getDataPath("crack.png").c_str());
238                                 crackimage->copyToWithAlpha(baseimg, v2s32(0,0),
239                                                 core::rect<s32>(pos_other, dim),
240                                                 video::SColor(255,255,255,255),
241                                                 NULL);
242                                 crackimage->drop();
243                         }
244                         else if(name.substr(0,12) == "[progressbar")
245                         {
246                                 float value = stof(name.substr(12));
247                                 make_progressbar(value, baseimg);
248                         }
249                         else
250                         {
251                                 dstream<<"WARNING: getTextureDirect(): Invalid "
252                                                 " texture: \""<<name<<"\""<<std::endl;
253                         }
254                 }
255         }
256
257         // If no resulting image, return NULL
258         if(baseimg == NULL)
259         {
260                 dstream<<"getTextureDirect(): baseimg is NULL (attempted to"
261                                 " create texture \""<<texture_name<<"\""<<std::endl;
262                 return NULL;
263         }
264         
265         /*// DEBUG: Paint some pixels
266         video::SColor c(255,255,0,0);
267         baseimg->setPixel(1,1, c);
268         baseimg->setPixel(1,14, c);
269         baseimg->setPixel(14,1, c);
270         baseimg->setPixel(14,14, c);*/
271
272         // Create texture from resulting image
273         t = driver->addTexture(texture_name.c_str(), baseimg);
274         baseimg->drop();
275
276         dstream<<"getTextureDirect(): created texture \""<<texture_name
277                         <<"\""<<std::endl;
278
279         return t;
280
281 }
282
283 void make_progressbar(float value, video::IImage *image)
284 {
285         if(image == NULL)
286                 return;
287         
288         core::dimension2d<u32> size = image->getDimension();
289
290         u32 barheight = 1;
291         u32 barpad_x = 1;
292         u32 barpad_y = 1;
293         u32 barwidth = size.Width - barpad_x*2;
294         v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
295
296         u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
297
298         video::SColor active(255,255,0,0);
299         video::SColor inactive(255,0,0,0);
300         for(u32 x0=0; x0<barwidth; x0++)
301         {
302                 video::SColor *c;
303                 if(x0 < barvalue_i)
304                         c = &active;
305                 else
306                         c = &inactive;
307                 u32 x = x0 + barpos.X;
308                 for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
309                 {
310                         image->setPixel(x,y, *c);
311                 }
312         }
313 }
314
315