DEADSOFTWARE

gl: implement texture filtering
[d2df-sdl.git] / src / game / renders / opengl / r_draw.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 {$INCLUDE ../../../shared/a_modes.inc}
16 unit r_draw;
18 interface
20 uses
21 g_animations,
22 r_textures
23 ;
25 procedure r_Draw_SetFilter (img: TGLTexture; enable: Boolean);
27 procedure r_Draw_Texture (img: TGLTexture; x, y, w, h: Integer; flip: Boolean; r, g, b, a: Byte; blend: Boolean);
28 procedure r_Draw_TextureRepeat (img: TGLTexture; x, y, w, h: Integer; flip: Boolean; r, g, b, a: Byte; blend: Boolean);
29 procedure r_Draw_TextureRepeatRotate (img: TGLTexture; x, y, w, h: Integer; flip: Boolean; r, g, b, a: Byte; blend: Boolean; rx, ry, angle: Integer);
31 procedure r_Draw_MultiTextureRepeat (m: TGLMultiTexture; const anim: TAnimState; backanim: Boolean; x, y, w, h: Integer; flip: Boolean; r, g, b, a: Byte; blend: Boolean);
32 procedure r_Draw_MultiTextureRepeatRotate (m: TGLMultiTexture; const anim: TAnimState; backanim: Boolean; x, y, w, h: Integer; flip: Boolean; r, g, b, a: Byte; blend: Boolean; rx, ry, angle: Integer);
34 procedure r_Draw_Filter (l, t, r, b: Integer; rr, gg, bb, aa: Byte);
35 procedure r_Draw_Rect (l, t, r, b: Integer; rr, gg, bb, aa: Byte);
36 procedure r_Draw_FillRect (l, t, r, b: Integer; rr, gg, bb, aa: Byte);
37 procedure r_Draw_InvertRect (l, t, r, b: Integer; rr, gg, bb, aa: Byte);
38 procedure r_Draw_Line (x0, y0, x1, y1: Integer; rr, gg, bb, aa: Byte);
40 procedure r_Draw_Text (const text: AnsiString; x, y: Integer; r, g, b, a: Byte; f: TGLFont);
41 procedure r_Draw_GetTextSize (const text: AnsiString; f: TGLFont; out w, h: Integer);
43 procedure r_Draw_Setup (sw, sh, gw, gh: Integer);
44 procedure r_Draw_SetRect (l, t, r, b: Integer);
45 procedure r_Draw_GetRect (out l, t, r, b: Integer);
47 procedure r_Draw_EnableTexture2D (enable: Boolean);
48 procedure r_Draw_SetColor (r, g, b, a: Byte);
50 implementation
52 uses
53 {$I ../../../nogl/noGLuses.inc}
54 SysUtils, Classes, Math,
55 e_log, utils
56 ;
58 const
59 NTR = $FF;
60 NTG = $00;
61 NTB = $00;
62 NTA = $FF;
64 var
65 sl, st, sr, sb: Integer;
66 ScreenWidth, ScreenHeight: Integer;
67 GameWidth, GameHeight: Integer;
69 enableTexture2D: Boolean;
70 curR, curG, curB, curA: Byte;
72 procedure r_Draw_EnableTexture2D (enable: Boolean);
73 begin
74 if enable <> enableTexture2D then
75 begin
76 if enable then glEnable(GL_TEXTURE_2D) else glDisable(GL_TEXTURE_2D);
77 enableTexture2D := enable;
78 end;
79 end;
81 procedure r_Draw_SetColor (r, g, b, a: Byte);
82 begin
83 if (r <> curR) or (g <> curG) or (b <> curB) or (curA <> a) then
84 begin
85 glColor4ub(r, g, b, a);
86 curR := r;
87 curG := g;
88 curB := b;
89 curA := a;
90 end;
91 end;
93 procedure r_Draw_Setup (sw, sh, gw, gh: Integer);
94 begin
95 ASSERT((sw >= 0) and (sh >= 0)); // screen/window size
96 ASSERT((gw >= 0) and (gh >= 0)); // virtual screen size
97 ScreenWidth := sw;
98 ScreenHeight := sh;
99 GameWidth := gw;
100 GameHeight := gh;
101 glScissor(0, 0, sw, sh);
102 glViewport(0, 0, sw, sh);
103 glMatrixMode(GL_PROJECTION);
104 glLoadIdentity;
105 glOrtho(0, gw, gh, 0, 0, 1);
106 glMatrixMode(GL_MODELVIEW);
107 glLoadIdentity;
108 glEnable(GL_SCISSOR_TEST);
109 r_Draw_SetRect(0, 0, gw - 1, gh - 1);
110 end;
112 procedure DrawQuad (x, y, w, h: Integer);
113 begin
114 glBegin(GL_QUADS);
115 glVertex2i(x + w, y);
116 glVertex2i(x, y);
117 glVertex2i(x, y + h);
118 glVertex2i(x + w, y + h);
119 glEnd();
120 end;
122 procedure DrawTextureError (x, y, w, h: Integer);
123 var w2, h2: Integer;
124 begin
125 w2 := w div 2; h2 := h div 2;
126 r_Draw_FillRect(x, y, x + w2, y + h2, 255, 0, 255, 255);
127 r_Draw_FillRect(x + w2, y, x + w, y + h2, 255, 255, 255, 255);
128 r_Draw_FillRect(x + w2, y + h2, x + w, y + h, 255, 0, 255, 255);
129 r_Draw_FillRect(x, y + h2, x + w2, y + h, 255, 255, 255, 255);
130 if (w > 2) and (h > 2) then
131 r_Draw_Rect(x, y, x + w, y + h, 0, 255, 0, 255);
132 end;
134 procedure r_Draw_SetFilter (img: TGLTexture; enable: Boolean);
135 begin
136 ASSERT(img <> nil);
137 img.filter := enable;
138 end;
140 procedure DrawTile (tile: TGLAtlasNode; x, y, w, h: Integer; flip: Boolean; rr, gg, bb, aa: Byte; blend, filter: Boolean);
141 var nw, nh, ax, bx, ay, by: GLfloat; l, t, r, b: Integer;
142 begin
143 if tile = nil then
144 begin
145 DrawTextureError(x, y, w, h);
146 end
147 else
148 begin
149 nw := tile.base.w;
150 nh := tile.base.h;
151 ax := IfThen(flip, tile.l, tile.r + 1) / nw;
152 bx := IfThen(flip, tile.r + 1, tile.l) / nh;
153 ay := (tile.t) / nw;
154 by := (tile.b + 1) / nh;
155 l := x; t := y; r := x + w; b := y + h;
156 r_Textures_GL_Bind(tile.id);
157 if filter <> tile.base.filter then
158 begin
159 if filter then
160 begin
161 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
162 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
163 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
164 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
165 end
166 else
167 begin
168 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
169 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
170 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
171 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
172 end;
173 tile.base.filter := filter;
174 end;
175 r_Draw_SetColor(rr, gg, bb, aa);
176 r_Draw_EnableTexture2D(true);
177 if blend then glBlendFunc(GL_SRC_ALPHA, GL_ONE) else glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
178 glEnable(GL_BLEND);
179 glBegin(GL_QUADS);
180 glTexCoord2f(ax, ay); glVertex2i(r, t);
181 glTexCoord2f(bx, ay); glVertex2i(l, t);
182 glTexCoord2f(bx, by); glVertex2i(l, b);
183 glTexCoord2f(ax, by); glVertex2i(r, b);
184 glEnd();
185 end
186 end;
188 procedure DrawHWTexture (gltex: GLint; nw, nh, x, y, w, h: Integer; flip: Boolean; rr, gg, bb, aa: Byte; blend: Boolean);
189 var ax, bx, ay, by: GLfloat; l, t, r, b: Integer;
190 begin
191 ax := IfThen(flip, 0, w) / nw;
192 bx := IfThen(flip, w, 0) / nh;
193 ay := 0 / nw;
194 by := h / nh;
195 l := x; t := y; r := x + w; b := y + h;
196 r_Textures_GL_Bind(gltex);
197 r_Draw_SetColor(rr, gg, bb, aa);
198 r_Draw_EnableTexture2D(true);
199 if blend then glBlendFunc(GL_SRC_ALPHA, GL_ONE) else glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
200 glEnable(GL_BLEND);
201 glBegin(GL_QUADS);
202 glTexCoord2f(ax, ay); glVertex2i(r, t);
203 glTexCoord2f(bx, ay); glVertex2i(l, t);
204 glTexCoord2f(bx, by); glVertex2i(l, b);
205 glTexCoord2f(ax, by); glVertex2i(r, b);
206 glEnd();
207 end;
209 procedure r_Draw_Texture (img: TGLTexture; x, y, w, h: Integer; flip: Boolean; r, g, b, a: Byte; blend: Boolean);
210 var i, j, first, last, step: Integer; n: TGLAtlasNode;
211 begin
212 ASSERT(w >= 0);
213 ASSERT(h >= 0);
214 if img = nil then
215 DrawTextureError(x, y, w, h)
216 else
217 begin
218 if flip then first := img.cols - 1 else first := 0;
219 if flip then last := -1 else last := img.cols;
220 if flip then step := -1 else step := +1;
221 glPushMatrix;
222 glTranslatef(x, y, 0);
223 glScalef(w / img.width, h / img.height, 1);
224 for j := 0 to img.lines - 1 do
225 begin
226 i := first;
227 repeat
228 n := img.GetTile(i, j);
229 ASSERT(n <> nil);
230 DrawTile(n, 0, 0, n.width, n.height, flip, r, g, b, a, blend, img.filter);
231 glTranslatef(n.width, 0, 0);
232 i := i + step;
233 until i = last;
234 glTranslatef(-img.width, n.height, 0);
235 end;
236 glPopMatrix;
237 end
238 end;
240 function r_Draw_IsHWRepeatable (img: TGLTexture): Boolean;
241 var n: TGLAtlasNode; a: TGLAtlas;
242 begin
243 ASSERT(img <> nil);
244 result := false;
245 if (img.cols = 1) and (img.lines = 1) then
246 begin
247 n := img.GetTile(0, 0);
248 if (n.width = img.width) and (n.height = img.height) then
249 begin
250 a := n.base;
251 result := (a.GetWidth() = img.width) and (a.GetHeight() = img.height)
252 end;
253 end;
254 end;
256 procedure r_Draw_TextureRepeat (img: TGLTexture; x, y, w, h: Integer; flip: Boolean; r, g, b, a: Byte; blend: Boolean);
257 var i, j: Integer;
258 begin
259 ASSERT(w >= 0);
260 ASSERT(h >= 0);
261 if img = nil then
262 DrawTextureError(x, y, w, h)
263 else if r_Draw_IsHWRepeatable(img) then
264 DrawHWTexture(img.GetTile(0, 0).base.id, img.width, img.height, x, y, w, h, flip, r, g, b, a, blend)
265 else
266 for j := 0 to (h - 1) div img.height do
267 for i := 0 to (w - 1) div img.width do
268 r_Draw_Texture(img, x + i * img.width, y + j * img.height, img.width, img.height, flip, r, g, b, a, blend);
269 end;
271 procedure r_Draw_TextureRepeatRotate (img: TGLTexture; x, y, w, h: Integer; flip: Boolean; r, g, b, a: Byte; blend: Boolean; rx, ry, angle: Integer);
272 begin
273 ASSERT(w >= 0);
274 ASSERT(h >= 0);
275 if a <> 0 then
276 begin
277 glPushMatrix;
278 glTranslatef(x + rx, y + ry, 0);
279 glRotatef(angle, 0, 0, 1);
280 glTranslatef(-(x + rx), -(y + ry), 0);
281 r_Draw_TextureRepeat(img, x, y, w, h, flip, r, g, b, a, blend);
282 glPopMatrix;
283 end
284 else
285 r_Draw_TextureRepeat(img, x, y, w, h, flip, r, g, b, a, blend);
286 end;
288 procedure r_Draw_MultiTextureRepeat (m: TGLMultiTexture; const anim: TAnimState; backanim: Boolean; x, y, w, h: Integer; flip: Boolean; r, g, b, a: Byte; blend: Boolean);
289 var img: TGLTexture; frame: LongInt;
290 begin
291 ASSERT(anim.IsValid());
292 if m = nil then
293 DrawTextureError(x, y, w, h)
294 else
295 begin
296 g_Anim_GetFrameFromState(anim, backanim, frame);
297 ASSERT(frame >= 0);
298 ASSERT(frame < m.count);
299 img := m.GetTexture(frame);
300 r_Draw_TextureRepeat(img, x, y, w, h, flip, r, g, b, a, blend);
301 end
302 end;
304 procedure r_Draw_MultiTextureRepeatRotate (m: TGLMultiTexture; const anim: TAnimState; backanim: Boolean; x, y, w, h: Integer; flip: Boolean; r, g, b, a: Byte; blend: Boolean; rx, ry, angle: Integer);
305 begin
306 ASSERT(w >= 0);
307 ASSERT(h >= 0);
308 if a <> 0 then
309 begin
310 glPushMatrix;
311 glTranslatef(x + rx, y + ry, 0);
312 glRotatef(angle, 0, 0, 1);
313 glTranslatef(-(x + rx), -(y + ry), 0);
314 r_Draw_MultiTextureRepeat(m, anim, backanim, x, y, w, h, flip, r, g, b, a, blend);
315 glPopMatrix;
316 end
317 else
318 r_Draw_MultiTextureRepeat(m, anim, backanim, x, y, w, h, flip, r, g, b, a, blend);
319 end;
321 procedure r_Draw_Rect (l, t, r, b: Integer; rr, gg, bb, aa: Byte);
322 begin
323 ASSERT(l <= r);
324 ASSERT(t <= b);
325 if (l < r) and (t < b) then
326 begin
327 glEnable(GL_BLEND);
328 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
329 r_Draw_EnableTexture2D(false);
330 r_Draw_SetColor(rr, gg, bb, aa);
331 glBegin(GL_QUADS);
332 (* top *)
333 glVertex2i(l, t);
334 glVertex2i(r, t);
335 glVertex2i(r, t+1);
336 glVertex2i(l, t+1);
337 (* bottom *)
338 glVertex2i(l, b-1);
339 glVertex2i(r, b-1);
340 glVertex2i(r, b);
341 glVertex2i(l, b);
342 (* left *)
343 glVertex2i(l, t+1);
344 glVertex2i(l+1, t+1);
345 glVertex2i(l+1, b-1);
346 glVertex2i(l, b-1);
347 (* right *)
348 glVertex2i(r-1, t+1);
349 glVertex2i(r, t+1);
350 glVertex2i(r, b-1);
351 glVertex2i(r-1, b-1);
352 glEnd;
353 end;
354 end;
356 procedure r_Draw_FillRect (l, t, r, b: Integer; rr, gg, bb, aa: Byte);
357 begin
358 ASSERT(l <= r);
359 ASSERT(t <= b);
360 if (l < r) and (t < b) then
361 begin
362 glEnable(GL_BLEND);
363 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
364 r_Draw_EnableTexture2D(false);
365 r_Draw_SetColor(rr, gg, bb, aa);
366 glBegin(GL_QUADS);
367 glVertex2i(l, t);
368 glVertex2i(r, t);
369 glVertex2i(r, b);
370 glVertex2i(l, b);
371 glEnd;
372 end;
373 end;
375 procedure r_Draw_Filter (l, t, r, b: Integer; rr, gg, bb, aa: Byte);
376 begin
377 ASSERT(l <= r);
378 ASSERT(t <= b);
379 if (l < r) and (t < b) then
380 begin
381 glEnable(GL_BLEND);
382 glBlendFunc(GL_ZERO, GL_SRC_COLOR);
383 r_Draw_EnableTexture2D(false);
384 r_Draw_SetColor(rr, gg, bb, aa);
385 glBegin(GL_QUADS);
386 glVertex2i(l, t);
387 glVertex2i(r, t);
388 glVertex2i(r, b);
389 glVertex2i(l, b);
390 glEnd;
391 end;
392 end;
394 procedure r_Draw_InvertRect (l, t, r, b: Integer; rr, gg, bb, aa: Byte);
395 begin
396 ASSERT(l <= r);
397 ASSERT(t <= b);
398 if (l < r) and (t < b) then
399 begin
400 glEnable(GL_BLEND);
401 glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
402 r_Draw_EnableTexture2D(false);
403 r_Draw_SetColor(rr, gg, bb, aa);
404 glBegin(GL_QUADS);
405 glVertex2i(l, t);
406 glVertex2i(r, t);
407 glVertex2i(r, b);
408 glVertex2i(l, b);
409 glEnd;
410 end;
411 end;
413 procedure r_Draw_Line (x0, y0, x1, y1: Integer; rr, gg, bb, aa: Byte);
414 begin
415 glEnable(GL_BLEND);
416 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
417 r_Draw_EnableTexture2D(false);
418 r_Draw_SetColor(rr, gg, bb, aa);
419 glLineWidth(1);
420 glBegin(GL_LINES);
421 glVertex2i(x0, y0);
422 glVertex2i(x1, y1);
423 glEnd;
424 end;
426 procedure r_Draw_Text (const text: AnsiString; x, y: Integer; r, g, b, a: Byte; f: TGLFont);
427 var i, xoff, spc: Integer; t: TGLTexture; ch: AnsiChar;
428 begin
429 xoff := x; spc := MAX(0, f.GetSpace());
430 for i := 1 to Length(text) do
431 begin
432 ch := text[i];
433 t := f.GetChar(ch);
434 if t <> nil then
435 r_Draw_Texture(t, xoff, y, t.width, t.height, false, r, g, b, a, false);
436 Inc(xoff, f.GetWidth(ch) + spc);
437 end;
438 end;
440 procedure r_Draw_GetTextSize (const text: AnsiString; f: TGLFont; out w, h: Integer);
441 var i, spc, len: Integer;
442 begin
443 w := 0;
444 h := f.GetMaxHeight();
445 len := Length(text);
446 if len > 0 then
447 begin
448 spc := MAX(0, f.GetSpace());
449 for i := 1 to len - 1 do
450 Inc(w, f.GetWidth(text[i]) + spc);
451 Inc(w, f.GetWidth(text[len]));
452 end;
453 end;
455 procedure r_Draw_SetRect (l, t, r, b: Integer);
456 var x, y, w, h: Integer;
457 begin
458 ASSERT(l <= r);
459 ASSERT(t <= b);
460 x := l * ScreenWidth div GameWidth;
461 y := t * ScreenHeight div GameHeight;
462 w := (r - l + 1) * ScreenWidth div GameWidth;
463 h := (b - t + 1) * ScreenHeight div GameHeight;
464 glScissor(x, ScreenHeight - h - y, w, h);
465 sl := l; st := t; sr := r; sb := b;
466 end;
468 procedure r_Draw_GetRect (out l, t, r, b: Integer);
469 begin
470 l := sl; t := st; r := sr; b := sb;
471 end;
473 end.