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