utility.h: Change Buffer's interface to be more compatible with SharedBuffer's interf...
[oweals/minetest.git] / src / tile.h
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #ifndef TILE_HEADER
21 #define TILE_HEADER
22
23 #include "common_irrlicht.h"
24 #include "threads.h"
25 #include "utility.h"
26 #include <string>
27
28 /*
29         tile.{h,cpp}: Texture handling stuff.
30 */
31
32 /*
33         Gets the path to a texture by first checking if the texture exists
34         in texture_path and if not, using the data path.
35
36         Checks all supported extensions by replacing the original extension.
37
38         If not found, returns "".
39
40         Utilizes a thread-safe cache.
41 */
42 std::string getTexturePath(const std::string &filename);
43
44 /*
45         Specifies a texture in an atlas.
46
47         This is used to specify single textures also.
48
49         This has been designed to be small enough to be thrown around a lot.
50 */
51 struct AtlasPointer
52 {
53         u32 id; // Texture id
54         video::ITexture *atlas; // Atlas in where the texture is
55         v2f pos; // Position in atlas
56         v2f size; // Size in atlas
57         u16 tiled; // X-wise tiling count. If 0, width of atlas is width of image.
58
59         AtlasPointer(
60                         u16 id_,
61                         video::ITexture *atlas_=NULL,
62                         v2f pos_=v2f(0,0),
63                         v2f size_=v2f(1,1),
64                         u16 tiled_=1
65                 ):
66                 id(id_),
67                 atlas(atlas_),
68                 pos(pos_),
69                 size(size_),
70                 tiled(tiled_)
71         {
72         }
73
74         bool operator==(const AtlasPointer &other)
75         {
76                 return (
77                         id == other.id
78                 );
79                 /*return (
80                         id == other.id &&
81                         atlas == other.atlas &&
82                         pos == other.pos &&
83                         size == other.size &&
84                         tiled == other.tiled
85                 );*/
86         }
87
88         float x0(){ return pos.X; }
89         float x1(){ return pos.X + size.X; }
90         float y0(){ return pos.Y; }
91         float y1(){ return pos.Y + size.Y; }
92 };
93
94 /*
95         An internal variant of the former with more data.
96 */
97 struct SourceAtlasPointer
98 {
99         std::string name;
100         AtlasPointer a;
101         video::IImage *atlas_img; // The source image of the atlas
102         // Integer variants of position and size
103         v2s32 intpos;
104         v2u32 intsize;
105
106         SourceAtlasPointer(
107                         const std::string &name_,
108                         AtlasPointer a_=AtlasPointer(0, NULL),
109                         video::IImage *atlas_img_=NULL,
110                         v2s32 intpos_=v2s32(0,0),
111                         v2u32 intsize_=v2u32(0,0)
112                 ):
113                 name(name_),
114                 a(a_),
115                 atlas_img(atlas_img_),
116                 intpos(intpos_),
117                 intsize(intsize_)
118         {
119         }
120 };
121
122 /*
123         Implementation (to be used as a no-op on the server)
124 */
125 class ITextureSource
126 {
127 public:
128         ITextureSource(){}
129         virtual ~ITextureSource(){}
130         virtual u32 getTextureId(const std::string &name){return 0;}
131         virtual u32 getTextureIdDirect(const std::string &name){return 0;}
132         virtual std::string getTextureName(u32 id){return "";}
133         virtual AtlasPointer getTexture(u32 id){return AtlasPointer(0);}
134         virtual AtlasPointer getTexture(const std::string &name)
135                 {return AtlasPointer(0);}
136         virtual video::ITexture* getTextureRaw(const std::string &name)
137                 {return NULL;}
138 };
139
140 /*
141         Creates and caches textures.
142 */
143 class TextureSource : public ITextureSource
144 {
145 public:
146         TextureSource(IrrlichtDevice *device);
147         ~TextureSource();
148
149         /*
150                 Processes queued texture requests from other threads.
151
152                 Shall be called from the main thread.
153         */
154         void processQueue();
155         
156         /*
157                 Example case:
158                 Now, assume a texture with the id 1 exists, and has the name
159                 "stone.png^mineral1".
160                 Then a random thread calls getTextureId for a texture called
161                 "stone.png^mineral1^crack0".
162                 ...Now, WTF should happen? Well:
163                 - getTextureId strips off stuff recursively from the end until
164                   the remaining part is found, or nothing is left when
165                   something is stripped out
166
167                 But it is slow to search for textures by names and modify them
168                 like that?
169                 - ContentFeatures is made to contain ids for the basic plain
170                   textures
171                 - Crack textures can be slow by themselves, but the framework
172                   must be fast.
173
174                 Example case #2:
175                 - Assume a texture with the id 1 exists, and has the name
176                   "stone.png^mineral1" and is specified as a part of some atlas.
177                 - Now MapBlock::getNodeTile() stumbles upon a node which uses
178                   texture id 1, and finds out that NODEMOD_CRACK must be applied
179                   with progression=0
180                 - It finds out the name of the texture with getTextureName(1),
181                   appends "^crack0" to it and gets a new texture id with
182                   getTextureId("stone.png^mineral1^crack0")
183
184         */
185         
186         /*
187                 Gets a texture id from cache or
188                 - if main thread, from getTextureIdDirect
189                 - if other thread, adds to request queue and waits for main thread
190         */
191         u32 getTextureId(const std::string &name);
192         
193         /*
194                 Example names:
195                 "stone.png"
196                 "stone.png^crack2"
197                 "stone.png^blit:mineral_coal.png"
198                 "stone.png^blit:mineral_coal.png^crack1"
199
200                 - If texture specified by name is found from cache, return the
201                   cached id.
202                 - Otherwise generate the texture, add to cache and return id.
203                   Recursion is used to find out the largest found part of the
204                   texture and continue based on it.
205
206                 The id 0 points to a NULL texture. It is returned in case of error.
207         */
208         u32 getTextureIdDirect(const std::string &name);
209
210         /*
211                 Finds out the name of a cached texture.
212         */
213         std::string getTextureName(u32 id);
214
215         /*
216                 If texture specified by the name pointed by the id doesn't
217                 exist, create it, then return the cached texture.
218
219                 Can be called from any thread. If called from some other thread
220                 and not found in cache, the call is queued to the main thread
221                 for processing.
222         */
223         AtlasPointer getTexture(u32 id);
224         
225         AtlasPointer getTexture(const std::string &name)
226         {
227                 return getTexture(getTextureId(name));
228         }
229         
230         // Gets a separate texture
231         video::ITexture* getTextureRaw(const std::string &name)
232         {
233                 AtlasPointer ap = getTexture(name);
234                 return ap.atlas;
235         }
236
237 private:
238         /*
239                 Build the main texture atlas which contains most of the
240                 textures.
241                 
242                 This is called by the constructor.
243         */
244         void buildMainAtlas();
245         
246         // The id of the thread that is allowed to use irrlicht directly
247         threadid_t m_main_thread;
248         // The irrlicht device
249         IrrlichtDevice *m_device;
250         
251         // A texture id is index in this array.
252         // The first position contains a NULL texture.
253         core::array<SourceAtlasPointer> m_atlaspointer_cache;
254         // Maps a texture name to an index in the former.
255         core::map<std::string, u32> m_name_to_id;
256         // The two former containers are behind this mutex
257         JMutex m_atlaspointer_cache_mutex;
258         
259         // Main texture atlas. This is filled at startup and is then not touched.
260         video::IImage *m_main_atlas_image;
261         video::ITexture *m_main_atlas_texture;
262
263         // Queued texture fetches (to be processed by the main thread)
264         RequestQueue<std::string, u32, u8, u8> m_get_texture_queue;
265 };
266
267 enum MaterialType{
268         MATERIAL_ALPHA_NONE,
269         MATERIAL_ALPHA_VERTEX,
270         MATERIAL_ALPHA_SIMPLE, // >127 = opaque
271         MATERIAL_ALPHA_BLEND,
272 };
273
274 // Material flags
275 #define MATERIAL_FLAG_BACKFACE_CULLING 0x01
276
277 /*
278         This fully defines the looks of a tile.
279         The SMaterial of a tile is constructed according to this.
280 */
281 struct TileSpec
282 {
283         TileSpec():
284                 texture(0),
285                 alpha(255),
286                 //material_type(MATERIAL_ALPHA_NONE),
287                 // Use this so that leaves don't need a separate material
288                 material_type(MATERIAL_ALPHA_SIMPLE),
289                 material_flags(
290                         //0 // <- DEBUG, Use the one below
291                         MATERIAL_FLAG_BACKFACE_CULLING
292                 )
293         {
294         }
295
296         bool operator==(TileSpec &other)
297         {
298                 return (
299                         texture == other.texture &&
300                         alpha == other.alpha &&
301                         material_type == other.material_type &&
302                         material_flags == other.material_flags
303                 );
304         }
305         
306         // Sets everything else except the texture in the material
307         void applyMaterialOptions(video::SMaterial &material) const
308         {
309                 if(alpha != 255 && material_type != MATERIAL_ALPHA_VERTEX)
310                         dstream<<"WARNING: TileSpec: alpha != 255 "
311                                         "but not MATERIAL_ALPHA_VERTEX"
312                                         <<std::endl;
313
314                 if(material_type == MATERIAL_ALPHA_NONE)
315                         material.MaterialType = video::EMT_SOLID;
316                 else if(material_type == MATERIAL_ALPHA_VERTEX)
317                         material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
318                 else if(material_type == MATERIAL_ALPHA_SIMPLE)
319                         material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
320                 else if(material_type == MATERIAL_ALPHA_BLEND)
321                         material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
322
323                 material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING) ? true : false;
324         }
325         
326         // NOTE: Deprecated, i guess?
327         void setTexturePos(u8 tx_, u8 ty_, u8 tw_, u8 th_)
328         {
329                 texture.pos = v2f((float)tx_/256.0, (float)ty_/256.0);
330                 texture.size = v2f(((float)tw_ + 1.0)/256.0, ((float)th_ + 1.0)/256.0);
331         }
332         
333         AtlasPointer texture;
334         // Vertex alpha
335         u8 alpha;
336         // Material type
337         u8 material_type;
338         // Material flags
339         u8 material_flags;
340 };
341
342 #endif