1 /*
2  * XPilotNG/SDL, an SDL/OpenGL XPilot client.
3  *
4  * Copyright (C) 2003-2004 Juha Lindstr�m <juhal@users.sourceforge.net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 
21 #include "xpclient_sdl.h"
22 
23 #include "images.h"
24 #include "sdlpaint.h"
25 
26 static image_t *images = NULL;
27 static int num_images = 0, max_images = 0;
28 static int first_texture = 0;
29 
pow2_ceil(int t)30 static int pow2_ceil(int t)
31 {
32     int r = 1;
33     while (r < t) r <<= 1;
34     return r;
35 }
36 
Image_init(image_t * img)37 static int Image_init(image_t *img)
38 {
39     int i, x, y;
40     xp_picture_t pic;
41     RGB_COLOR c;
42 
43     if (img->state != IMG_STATE_UNINITIALIZED)
44 	return -1;
45 
46     if (Picture_init(&pic,
47 		     img->filename,
48 		     img->num_frames * (img->rotate ? 1 : -1)) == -1) {
49 	img->state = IMG_STATE_ERROR;
50 	return -1;
51     }
52     img->name = 0;
53     img->width = pic.width * img->num_frames;
54     img->height = pic.height;
55     img->frame_width = img->width / img->num_frames;
56     img->data_width = pow2_ceil(img->width);
57     img->data_height = pow2_ceil(img->height);
58 
59     warn("Loaded image %s: w=%d, h=%d, fw=%d, dw=%d, dh=%d",
60 	 img->filename, img->width, img->height, img->frame_width,
61 	 img->data_width, img->data_height);
62 
63     img->data = XCALLOC(unsigned int, img->data_width * img->data_height);
64     if (img->data == NULL) {
65         error("Failed to allocate memory for: %s size %dx%d",
66               img->filename, img->data_width, img->data_height);
67 	img->state = IMG_STATE_ERROR;
68 	return -1;
69     }
70 
71     for (i = 0; i < img->num_frames; i++) {
72 	for (y = 0; y < img->height; y++) {
73 	    for (x = 0; x < img->frame_width; x++) {
74 		/* the pixels needs to be mirrored over x-axis because
75 		 * of the used OpenGL projection */
76 		c = Picture_get_pixel(&pic, i, x, img->height - y - 1);
77 		if (c)
78 		    c |= 0xff000000; /* alpha */
79 		img->data[(x + img->frame_width * i) + (y * img->data_width)]
80 		    = c;
81 	    }
82 	}
83     }
84 
85     glGenTextures(1, &img->name);
86     glBindTexture(GL_TEXTURE_2D, img->name);
87     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img->data_width, img->data_height,
88                  0, GL_RGBA, GL_UNSIGNED_BYTE, img->data);
89     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
90                     GL_NEAREST);
91     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
92                     GL_NEAREST);
93 
94     img->state = IMG_STATE_READY;
95     return 0;
96 }
97 
Image_free(image_t * img)98 static void Image_free(image_t *img)
99 {
100     XFREE(img->filename);
101     if (img->state == IMG_STATE_READY) {
102 	glDeleteTextures(1, &img->name);
103 	/* this causes a Segmentation Fault for some reason
104 	free(img->data);
105 	*/
106     }
107     img->state = IMG_STATE_UNINITIALIZED;
108 }
109 
Image_get(int ind)110 image_t *Image_get(int ind) {
111 
112     image_t *img;
113 
114     if (ind >= num_images)
115 	return NULL;
116     img = &images[ind];
117     if (img == NULL)
118 	return NULL;
119     if (img->state == IMG_STATE_UNINITIALIZED)
120 	Image_init(img);
121     if (img->state != IMG_STATE_READY)
122 	return NULL;
123     return img;
124 }
125 
Image_get_texture(int ind)126 image_t *Image_get_texture(int ind)
127 {
128     return Image_get(first_texture + ind);
129 }
130 
Image_use_texture(int ind)131 void Image_use_texture(int ind)
132 {
133     image_t *img = Image_get_texture(ind);
134 
135     glEnable(GL_TEXTURE_2D);
136     glEnable(GL_BLEND);
137 
138     if (img == NULL) {
139 	warn("Texture %d is undefined.\n", ind);
140 	return;
141     }
142 
143     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
144     glBindTexture(GL_TEXTURE_2D, img->name);
145     glColor4ub(255, 255, 255, 255);
146 }
147 
Image_no_texture(void)148 void Image_no_texture(void)
149 {
150     /*glDisable(GL_BLEND);*/
151     glDisable(GL_TEXTURE_2D);
152 }
153 
Image_paint(int ind,int x,int y,int frame,int c)154 void Image_paint(int ind, int x, int y, int frame, int c)
155 {
156     Image_paint_area(ind, x, y, frame, NULL, c);
157 }
158 
Image_paint_area(int ind,int x,int y,int frame,irec_t * r,int c)159 void Image_paint_area(int ind, int x, int y, int frame, irec_t *r, int c)
160 {
161     image_t *img;
162     irec_t whole;
163     float tx1, ty1, tx2, ty2;
164 
165     img = Image_get(ind);
166     if (img == NULL)
167 	return;
168 
169     if (r == NULL) {
170 	whole.x = 0;
171 	whole.y = 0;
172 	whole.w = img->frame_width;
173 	whole.h = img->height;
174 	r = &whole;
175     }
176 
177     tx1 = ((float)frame * img->frame_width + r->x) / img->data_width;
178     ty1 = ((float)r->y) / img->data_height;
179     tx2 = ((float)frame * img->frame_width + r->x + r->w) / img->data_width;
180     ty2 = ((float)r->y + r->h) / (img->data_height);
181 
182     glBindTexture(GL_TEXTURE_2D, img->name);
183     glEnable(GL_TEXTURE_2D);
184     glEnable(GL_BLEND);
185     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
186     set_alphacolor(c);
187 
188     glBegin(GL_QUADS);
189     glTexCoord2f(tx1, ty1); glVertex2i(x    	, y 	    );
190     glTexCoord2f(tx2, ty1); glVertex2i(x + r->w , y 	    );
191     glTexCoord2f(tx2, ty2); glVertex2i(x + r->w , y + r->h  );
192     glTexCoord2f(tx1, ty2); glVertex2i(x    	, y + r->h  );
193     glEnd();
194 
195     glDisable(GL_BLEND);
196     glDisable(GL_TEXTURE_2D);
197 }
198 
Image_paint_rotated(int ind,int x,int y,int dir,int color)199 void Image_paint_rotated(int ind, int x, int y, int dir, int color)
200 {
201     image_t *img;
202     irec_t whole;
203     float tx1, ty1, tx2, ty2;
204 
205     img = Image_get(ind);
206     if (img == NULL)
207 	return;
208 
209     whole.x = 0;
210     whole.y = 0;
211     whole.w = img->frame_width;
212     whole.h = img->height;
213 
214     tx1 = 0.0;
215     ty1 = 0.0;
216     tx2 = img->frame_width / (double)img->data_width;
217     ty2 = img->height / (double)img->data_height;
218 
219     glPushMatrix();
220     glTranslatef((GLfloat)(x), (GLfloat)(y), 0.0);
221     glRotatef(360.0 * dir / (double)TABLE_SIZE, 0.0, 0.0, 1.0);
222 
223     glBindTexture(GL_TEXTURE_2D, img->name);
224     glEnable(GL_TEXTURE_2D);
225     glEnable(GL_BLEND);
226     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
227     /* Linear Filtering */
228     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
229     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
230     set_alphacolor(color);
231 
232     glBegin(GL_QUADS);
233     glTexCoord2f(tx1, ty1); glVertex2f( -whole.w / 2.0 ,    -whole.h / 2.0  );
234     glTexCoord2f(tx2, ty1); glVertex2f( whole.w / 2.0  ,    -whole.h / 2.0  );
235     glTexCoord2f(tx2, ty2); glVertex2f( whole.w / 2.0  ,    whole.h / 2.0   );
236     glTexCoord2f(tx1, ty2); glVertex2f( -whole.w / 2.0 ,    whole.h / 2.0   );
237     glEnd();
238 
239     glDisable(GL_BLEND);
240     glDisable(GL_TEXTURE_2D);
241 
242     glPopMatrix();
243 }
244 
245 
Images_init(void)246 int Images_init(void)
247 {
248 #define DEF_IMG(name, count) Bitmap_add(name, count, false);
249 
250     DEF_IMG("holder1.ppm", 1);
251     DEF_IMG("holder2.ppm", 1);
252     DEF_IMG("ball_gray.ppm", 1);
253     DEF_IMG("ship_friend.ppm", 1); /* 128 fails in some OpenGL drivers */
254     DEF_IMG("ship_friend.ppm", 1); /* I guess texture gets too wide (4096) */
255     DEF_IMG("ship_enemy.ppm", 1);
256     DEF_IMG("bullet.ppm", -16);
257     DEF_IMG("bullet_blue.ppm", -16);
258     DEF_IMG("base_down.ppm", 1);
259     DEF_IMG("base_left.ppm", 1);
260     DEF_IMG("base_up.ppm", 1);
261     DEF_IMG("base_right.ppm", 1);
262     DEF_IMG("fuelcell.ppm", 1);
263     DEF_IMG("fuel2.ppm", -16);
264     DEF_IMG("allitems.ppm", -30);
265     DEF_IMG("cannon_down.ppm", 1);
266     DEF_IMG("cannon_left.ppm", 1);
267     DEF_IMG("cannon_up.ppm", 1);
268     DEF_IMG("cannon_right.ppm", 1);
269     DEF_IMG("sparks.ppm", -8);
270     DEF_IMG("paused.ppm", -2);
271     DEF_IMG("refuel.ppm", -4);
272     DEF_IMG("wormhole.ppm", 1);
273     DEF_IMG("mine_team.ppm", 1);
274     DEF_IMG("mine_other.ppm", 1);
275     DEF_IMG("concentrator.ppm", 1);
276     DEF_IMG("plus.ppm", 1);
277     DEF_IMG("minus.ppm", 1);
278     DEF_IMG("checkpoint.ppm", -2);
279     DEF_IMG("meter.ppm", -2);
280     DEF_IMG("asteroidconcentrator.ppm", 1);
281     DEF_IMG("shield.ppm", 1);
282     DEF_IMG("acwise_grav.ppm", -6);
283     DEF_IMG("cwise_grav.ppm", -6);
284     DEF_IMG("missile.ppm", 1);
285     DEF_IMG("asteroid.ppm", 1);
286     DEF_IMG("target.ppm", 1);
287     DEF_IMG("huditems.ppm", -30);
288 
289     first_texture = num_images;
290 
291 #undef DEF_IMG
292     return 0;
293 }
294 
295 
Images_cleanup(void)296 void Images_cleanup(void)
297 {
298     int i;
299 
300     if (images == NULL)
301 	return;
302 
303     for (i = 0; i < num_images; i++)
304 	Image_free(images + i);
305 
306     XFREE(images);
307 }
308 
309 
Bitmap_add(const char * filename,int count,bool scalable)310 int Bitmap_add(const char *filename, int count, bool scalable)
311 {
312     image_t img;
313 
314     img.filename   = xp_strdup(filename);
315     img.num_frames = ABS(count);
316     img.rotate     = count > 1;
317     img.state      = IMG_STATE_UNINITIALIZED;
318     STORE(image_t, images, num_images, max_images, img);
319 
320     return num_images - 1;
321 }
322 
323 
324