1 /*
2  * mape - C4 Landscape.txt editor
3  *
4  * Copyright (c) 2005-2009, Armin Burgmeier
5  *
6  * Distributed under the terms of the ISC license; see accompanying file
7  * "COPYING" for details.
8  *
9  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
10  * See accompanying file "TRADEMARK" for details.
11  *
12  * To redistribute this file separately, substitute the full license texts
13  * for the above references.
14  */
15 
16 #include <string.h>
17 
18 #include "mape/cpp-handles/mapgen-handle.h"
19 #include "mape/cpp-handles/material-handle.h"
20 #include "mape/cpp-handles/texture-handle.h"
21 #include "mape/mapgen.h"
22 
23 /* Declare private API */
24 C4GroupHandle*
25 _mape_group_get_handle(MapeGroup* group);
26 
27 
28 C4MaterialMapHandle*
29 _mape_material_map_get_handle(MapeMaterialMap* map);
30 
31 C4TextureMapHandle*
32 _mape_texture_map_get_handle(MapeTextureMap* map);
33 
mape_mapgen_error_quark()34 static GQuark mape_mapgen_error_quark()
35 {
36   return g_quark_from_static_string("MAPE_MAPGEN_ERROR");
37 }
38 
mape_mapgen_read_color(guint8 * dest,MapeMaterialMap * material_map,MapeTextureMap * texture_map,unsigned int matnum)39 static void mape_mapgen_read_color(guint8* dest,
40                                    MapeMaterialMap* material_map,
41                                    MapeTextureMap* texture_map,
42                                    unsigned int matnum)
43 {
44   const gchar* texture_name;
45   const gchar* material_name;
46   const gchar* first_tex_separator;
47   const MapeMaterial* material;
48   gchar* own_texture_name;
49   guint32 color;
50 
51   if(matnum == 0)
52   {
53     /* Sky */
54     dest[matnum * 4 + 1] = 100;
55     dest[matnum * 4 + 2] = 100;
56     dest[matnum * 4 + 3] = 255;
57   }
58   else
59   {
60     texture_name = mape_texture_map_get_texture_name_from_mapping(
61       texture_map,
62       matnum
63     );
64 
65     own_texture_name = NULL;
66     if(texture_name != NULL)
67     {
68       /* When the texture is animated, the texture name consists of more than
69        * one texture, separated with a '-' character. In this case, we simply
70        * use the first one for display. */
71       first_tex_separator = strchr(texture_name, '-');
72       if(first_tex_separator != NULL)
73       {
74         own_texture_name = g_strndup(
75           texture_name,
76           first_tex_separator - texture_name
77         );
78 
79         texture_name = own_texture_name;
80       }
81 
82       /* Make sure the texture exists */
83       if(!mape_texture_map_lookup_texture(texture_map, texture_name))
84       {
85         material_name = mape_texture_map_get_material_name_from_mapping(
86           texture_map,
87           matnum
88         );
89 
90         material = mape_material_map_get_material_by_name(
91           material_map,
92           material_name
93         );
94 
95         /* It can happen that the material does not exist; this happens when
96          * a material-texture specification with texture set and invalid
97          * material occurs, such as "E-rough". In this case we display sky,
98          * since this is what happens when the texture specification is
99          * omitted (in which case no entry in the texmap is created, and
100          * matnum=0). */
101         if(!material)
102         {
103           dest[matnum * 4 + 1] = 100;
104           dest[matnum * 4 + 2] = 100;
105           dest[matnum * 4 + 3] = 255;
106           texture_name = NULL;
107         }
108         else
109         {
110           texture_name = mape_material_get_texture_overlay(material);
111         }
112       }
113     }
114 
115     if(texture_name != NULL)
116     {
117       color = mape_texture_map_get_average_texture_color(
118         texture_map,
119         texture_name
120       );
121 
122       dest[matnum * 4 + 1] = (color      ) & 0xff;
123       dest[matnum * 4 + 2] = (color >>  8) & 0xff;
124       dest[matnum * 4 + 3] = (color >> 16) & 0xff;
125     }
126 
127     g_free(own_texture_name);
128   }
129 }
130 
131 /*
132  * Public API.
133  */
134 
135 /**
136  * mape_mapgen_init:
137  * @error: Location to store error information, if any.
138  *
139  * Initializes the map generator.
140  *
141  * Returns: %TRUE on success or %FALSE on error.
142  */
143 gboolean
mape_mapgen_init(GError ** error)144 mape_mapgen_init(GError** error)
145 {
146   c4_mapgen_handle_init_script_engine();
147   return TRUE;
148 }
149 
150 /**
151  * mape_mapgen_deinit():
152  *
153  * Deinitializes the map generator.
154  */
155 void
mape_mapgen_deinit()156 mape_mapgen_deinit()
157 {
158   c4_mapgen_handle_deinit_script_engine();
159 }
160 
161 /**
162  * mape_mapgen_set_root_group:
163  * @group: The root group.
164  *
165  * Sets the root group for the map generator. This group is used to lookup the
166  * Library_Map definition.
167  */
168 void
mape_mapgen_set_root_group(MapeGroup * group)169 mape_mapgen_set_root_group(MapeGroup* group)
170 {
171   MapeGroup* objects;
172   MapeGroup* libraries;
173   MapeGroup* map;
174   GError* error;
175 
176   error = NULL;
177   if(!error)
178     objects = mape_group_open_child(group, "Objects.ocd", &error);
179   if(!error)
180     libraries = mape_group_open_child(objects, "Libraries.ocd", &error);
181   if(!error)
182     map = mape_group_open_child(libraries, "Map.ocd", &error);
183 
184   /* TODO: Error reporting? */
185   if(error == NULL)
186     c4_mapgen_handle_set_map_library(_mape_group_get_handle(map));
187 
188   if(error != NULL)
189   {
190     fprintf(stderr, "Failed to load Objects.ocd/Libraries.ocd/Map.ocd/Script.c: %s\n", error->message);
191     g_error_free(error);
192   }
193 }
194 
195 /**
196  * mape_mapgen_render:
197  *
198  * @filename: The filename of the file that is being parsed. This is only used
199  * for display purposes.
200  * @source: The map generator source code for the map to generate.
201  * @type: Specifies how the text in @source should be interpreted. Must not be
202  * %MAPE_MAPGEN_NONE.
203  * @script_path: Path to the script source for algo=script overlays, or %NULL.
204  * @material_map: The material map containing the materials to be used during
205  * map generation.
206  * @texture_map: The texture map containing the textures to be used during map
207  * generation.
208  * @width: The width of the map to generate.
209  * @height: The height of the map to generate.
210  * @error: Location to store error information, if any, or %NULL.
211  *
212  * Renders the map described by @source with the C4MapCreatorS2 into a pixbuf.
213  * The pixel color depends on the texture at the corresponding position and is
214  * determined by the average color of that texture.
215  *
216  * If the source contains one or more algo=script overlays and @script_path is
217  * %NULL, an error is generated. Otherwise, the file at @script_path is opened
218  * and used to look up the relevant script functions.
219  *
220  * In case an error occurs, for example when the map generator source code is
221  * not valid, @error is set and the function returns %NULL.
222  *
223  * Return Value: A #GdkPixbuf with the generated map, or %NULL. Free with
224  * g_object_unref().
225  **/
226 GdkPixbuf*
mape_mapgen_render(const gchar * filename,const gchar * source,MapeMapgenType type,const gchar * script_path,MapeMaterialMap * material_map,MapeTextureMap * texture_map,guint width,guint height,GError ** error)227 mape_mapgen_render(const gchar* filename,
228                    const gchar* source,
229                    MapeMapgenType type,
230                    const gchar* script_path,
231                    MapeMaterialMap* material_map,
232                    MapeTextureMap* texture_map,
233                    guint width,
234                    guint height,
235                    GError** error)
236 {
237   C4MapgenHandle* handle;
238   const char* error_message;
239   unsigned int out_width;
240   unsigned int out_height;
241   GdkPixbuf* pixbuf;
242   guint8* out_p;
243   const unsigned char* in_p;
244   guint out_rowstride;
245   unsigned int in_rowstride;
246   guint datawidth;
247   guint8 matclrs[256 * 4];
248   unsigned int x, y;
249   unsigned int matnum;
250 
251   switch(type)
252   {
253   case MAPE_MAPGEN_LANDSCAPE_TXT:
254     handle = c4_mapgen_handle_new(
255       filename,
256       source,
257       script_path,
258       _mape_material_map_get_handle(material_map),
259       _mape_texture_map_get_handle(texture_map),
260       width,
261       height
262     );
263 
264     break;
265   case MAPE_MAPGEN_MAP_C:
266     handle = c4_mapgen_handle_new_script(
267       filename,
268       source,
269       _mape_material_map_get_handle(material_map),
270       _mape_texture_map_get_handle(texture_map),
271       width,
272       height
273     );
274 
275     break;
276   default:
277     handle = NULL;
278     g_assert_not_reached();
279     break;
280   }
281 
282   error_message = c4_mapgen_handle_get_error(handle);
283   if(error_message)
284   {
285     g_set_error(
286       error,
287       mape_mapgen_error_quark(),
288       MAPE_MAPGEN_ERROR_COMPILE,
289       "%s",
290       error_message
291     );
292 
293     c4_mapgen_handle_free(handle);
294     return NULL;
295   }
296 
297   out_width = c4_mapgen_handle_get_width(handle);
298   out_height = c4_mapgen_handle_get_height(handle);
299 
300   pixbuf = gdk_pixbuf_new(
301     GDK_COLORSPACE_RGB,
302     FALSE,
303     8,
304     out_width,
305     out_height
306   );
307 
308   if(pixbuf == NULL)
309   {
310     g_set_error(
311       error,
312       mape_mapgen_error_quark(),
313       MAPE_MAPGEN_ERROR_MEMORY,
314       "Insufficient memory is available"
315     );
316 
317     c4_mapgen_handle_free(handle);
318     return NULL;
319   }
320 
321   out_p = gdk_pixbuf_get_pixels(pixbuf);
322   in_p = c4_mapgen_handle_get_map(handle);
323   out_rowstride = gdk_pixbuf_get_rowstride(pixbuf);
324   in_rowstride = c4_mapgen_handle_get_rowstride(handle);
325   datawidth = gdk_pixbuf_get_width(pixbuf) * 3;
326   memset(matclrs, 0, sizeof(matclrs) );
327 
328   for(y = 0; y < out_height; ++y)
329   {
330     for(x = 0; x < out_width; ++x)
331     {
332       matnum = *in_p;
333 
334       if(matclrs[matnum * 4] == 0)
335       {
336         mape_mapgen_read_color(
337           matclrs,
338           material_map,
339           texture_map,
340           matnum
341         );
342 
343         /* Color has been loaded */
344         matclrs[matnum * 4] = 1;
345       }
346 
347       out_p[0] = matclrs[matnum * 4 + 1];
348       out_p[1] = matclrs[matnum * 4 + 2];
349       out_p[2] = matclrs[matnum * 4 + 3];
350       ++in_p;
351       out_p += 3;
352     }
353 
354     in_p += in_rowstride - out_width;
355     out_p += out_rowstride - datawidth;
356   }
357 
358   c4_mapgen_handle_free(handle);
359   return pixbuf;
360 }
361 
362 /* vim:set et sw=2 ts=2: */
363