minor gitignore additions
[summerhack] / src / 3dengfx / src / dsys / fx.cpp
1 /*
2 This file is part of the 3dengfx demo system.
3
4 Copyright (c) 2004, 2005 John Tsiombikas <nuclear@siggraph.org>
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 "3dengfx_config.h"
22
23 #include <list>
24 #include <algorithm>
25 #include "fx.hpp"
26 #include "dsys.hpp"
27 #include "script.h"
28 #include "3dengfx/3dengfx.hpp"
29 #include "common/err_msg.h"
30
31
32 static bool str_to_color(const char *str, Color *col);
33
34
35 void dsys::radial_blur(Texture *tex, float ammount, const Vector2 &origin, bool additive) {
36         Vector2 c1(0.0f, 0.0f), c2(1.0f, 1.0f);
37
38         set_alpha_blending(true);
39         if(additive) {
40                 set_blend_func(BLEND_SRC_ALPHA, BLEND_ONE);
41         } else {
42                 set_blend_func(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA);
43         }
44         
45         ammount += 1.0f;
46         int quad_count = (int)(ammount * 20.0f);
47         float dscale = (ammount - 1.0f) / (float)quad_count;
48         float scale = 1.0f;
49         for(int i=0; i<quad_count; i++) {
50                 Vector2 v1 = c1, v2 = c2;
51                 v1 -= origin;
52                 v1 *= scale;
53                 v1 += origin;
54
55                 v2 -= origin;
56                 v2 *= scale;
57                 v2 += origin;
58                 
59                 float alpha = (float)((quad_count-1) - i) / (float)quad_count;
60                 dsys::overlay(tex, v1, v2, Color(1.0f, 1.0f, 1.0f, alpha), 0, false);
61                 scale += dscale;
62         }
63
64         set_alpha_blending(false);
65 }
66
67 void dsys::dir_blur(Texture *tex, float ammount, int dir) {
68         Vector2 c1(0.0f, 1.0f), c2(1.0f, 0.0f);
69         
70         ammount *= 0.5f;
71         int quad_count = (int)(ammount * 100.0f);
72         float offs_inc = ammount / (float)(quad_count/2);
73         float offs = 0.0f;
74         for(int i=0; i<quad_count/2; i++) {
75                 Vector2 off_vec = dir == BLUR_DIR_X ? Vector2(offs, 0) : Vector2(0, offs);
76
77                 float alpha = 1.0f - offs / ammount;
78                 dsys::overlay(tex, c1 + off_vec, c2 + off_vec, Color(1.0f, 1.0f, 1.0f, alpha));
79                 //dsys::overlay(tex, c1 - off_vec, c2 - off_vec, Color(1.0f, 1.0f, 1.0f, alpha));
80                 offs += offs_inc;
81         }
82 }
83
84 /*
85 static Vector2 *blur_pos;
86 static int pos_count;
87
88 void dsys::blur(Texture *tex, float ammount, bool additive) {
89         //additive = true;
90
91         set_alpha_blending(true);
92         if(additive) {
93                 set_blend_func(BLEND_SRC_ALPHA, BLEND_ONE);
94         } else {
95                 set_blend_func(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA);
96         }
97
98         int quad_count = (int)(ammount * 700.0f);
99
100         if(quad_count != pos_count) {
101                 if(blur_pos) delete [] blur_pos;
102                 blur_pos = new Vector2[quad_count];
103
104                 for(int i=0; i<quad_count; i++) {
105                         blur_pos[i] = Vector2(frand(1.0f) - 0.5f, frand(1.0f) - 0.5f);
106                         blur_pos[i] *= ammount;
107                 }
108                 pos_count = quad_count;
109         }
110         
111         for(int i=0; i<quad_count; i++) {
112                 float dist = blur_pos[i].length_sq();
113                 float alpha = 1.0f / dist;
114                 dsys::overlay(tex, Vector2(0, 0) + blur_pos[i], Vector2(1, 1) + blur_pos[i], Color(1.0f, 1.0f, 1.0f, alpha), false);
115         }
116
117         set_alpha_blending(false);
118 }
119 */
120
121 void dsys::overlay(Texture *tex, const Vector2 &corner1, const Vector2 &corner2, const Color &color, GfxProg *pprog, bool handle_blending) {
122         glMatrixMode(GL_MODELVIEW);
123         glPushMatrix();
124         glLoadIdentity();
125
126         float aspect = (float)fxwt::screenx / fxwt::screeny;
127         float offs = (1.333333333f - aspect) / 2.0f;
128         glMatrixMode(GL_PROJECTION);
129         glPushMatrix();
130         glLoadIdentity();
131         glOrtho(offs, 1.0 - offs, 1.0, 0.0, 0.0, 1.0);
132
133         set_lighting(false);
134         set_zbuffering(false);
135         set_backface_culling(false);
136         if(handle_blending) {
137                 set_alpha_blending(true);
138                 set_blend_func(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA);
139         }
140
141         if(tex) {
142                 enable_texture_unit(0);
143                 disable_texture_unit(1);
144                 set_texture_unit_color(0, TOP_REPLACE, TARG_TEXTURE, TARG_COLOR);
145                 set_texture_unit_alpha(0, TOP_MODULATE, TARG_TEXTURE, TARG_COLOR);
146                 set_texture_coord_index(0, 0);
147                 set_texture(0, tex);
148                 set_texture_addressing(0, TEXADDR_CLAMP, TEXADDR_CLAMP);
149
150                 for(int i=0; i<4; i++) {
151                         if(tex == dsys::tex[i]) {
152                                 glMatrixMode(GL_TEXTURE);
153                                 load_matrix_gl(dsys::tex_mat[i]);
154                                 break;
155                         }
156                 }
157         }
158
159         if(pprog) {
160                 set_gfx_program(pprog);
161         }
162         
163         glBegin(GL_QUADS);
164         glColor4f(color.r, color.g, color.b, color.a);
165         glTexCoord2f(0.0f, 1.0f);
166         glVertex3f(corner1.x, corner1.y, -0.5);
167         glTexCoord2f(1.0f, 1.0f);
168         glVertex3f(corner2.x, corner1.y, -0.5);
169         glTexCoord2f(1.0f, 0.0f);
170         glVertex3f(corner2.x, corner2.y, -0.5);
171         glTexCoord2f(0.0f, 0.0f);
172         glVertex3f(corner1.x, corner2.y, -0.5);
173         glEnd();
174
175         if(pprog) {
176                 set_gfx_program(0);
177         }
178         
179         if(tex) {
180                 for(int i=0; i<4; i++) {
181                         if(tex == dsys::tex[i]) {
182                                 glMatrixMode(GL_TEXTURE);
183                                 load_matrix_gl(Matrix4x4::identity_matrix);
184                                 break;
185                         }
186                 }
187                 set_texture_addressing(0, TEXADDR_WRAP, TEXADDR_WRAP);
188                 disable_texture_unit(0);
189         }
190         if(handle_blending) set_alpha_blending(false);
191         set_backface_culling(true);
192         set_zbuffering(true);
193         set_lighting(true);
194
195         glMatrixMode(GL_PROJECTION);
196         glPopMatrix();
197         glMatrixMode(GL_MODELVIEW);
198         glPopMatrix();
199 }
200
201 void dsys::negative(const Vector2 &corner1, const Vector2 &corner2) {
202         set_alpha_blending(true);
203         set_blend_func(BLEND_ONE_MINUS_DST_COLOR, BLEND_ZERO);
204         dsys::overlay(0, corner1, corner2, Color(1.0f, 1.0f, 1.0f, 1.0f), 0, false);
205         set_alpha_blending(false);
206 }
207
208 void dsys::flash(unsigned long time, unsigned long when, unsigned long dur, const Color &col) {
209         long start = when - dur/2;
210         long end = when + dur/2;
211         
212         if((long)time >= start && (long)time < end) {
213                 scalar_t t = (scalar_t)time / 1000.0;
214                 scalar_t dt = (scalar_t)dur / 1000.0;
215                 scalar_t wt = (scalar_t)when / 1000.0;
216                 scalar_t half_dt = dt / 2.0;
217                 
218                 scalar_t alpha = cos(pi * (t - wt) / half_dt);
219
220                 dsys::overlay(0, Vector3(0,0), Vector3(1,1), Color(col.r, col.g, col.b, alpha));
221         }
222 }
223
224
225
226 // ------------- Image Effect manager --------------
227
228 using std::list;
229 using namespace dsys;
230
231 static list<ImageFx*> fx_list;
232
233 void dsys::add_image_fx(ImageFx *fx) {
234         fx_list.push_back(fx);
235 }
236
237 void dsys::remove_image_fx(ImageFx *fx) {
238         fx_list.erase(find(fx_list.begin(), fx_list.end(), fx));
239 }
240
241 void dsys::apply_image_fx(unsigned long time) {
242         list<ImageFx*>::iterator iter = fx_list.begin();
243
244         while(iter != fx_list.end()) {
245                 (*iter++)->apply(time);
246         }
247 }
248
249
250 // -------------- Image Effect class --------------
251
252 ImageFx::ImageFx() {
253         time = 0;
254         duration = 0;
255 }
256
257 ImageFx::~ImageFx() {}
258
259 bool ImageFx::parse_script_args(const char **args) {
260         long t, d;
261
262         if(!args[0] || (t = str_to_time(args[0])) == -1) {
263                 return false;
264         }
265
266         if(!args[1] || (d = str_to_time(args[1])) == -1) {
267                 return false;
268         }
269
270         time = t;
271         duration = d;
272         return true;
273 }
274
275 void ImageFx::set_time(unsigned long time) {
276         this->time = time;
277 }
278
279 void ImageFx::set_duration(unsigned long dur) {
280         duration = dur;
281 }
282
283
284 // ------------- Negative (inverse video) effect ---------------
285
286 FxNegative::~FxNegative() {}
287
288 void FxNegative::apply(unsigned long time) {
289         if(time < this->time || time > this->time + duration) return;
290
291         negative();
292 }
293
294 // ------------ Screen Flash -------------
295
296 FxFlash::~FxFlash() {}
297
298 FxFlash::FxFlash() {
299         color = Color(1.0, 1.0, 1.0);
300 }
301
302 bool FxFlash::parse_script_args(const char **args) {
303         if(!ImageFx::parse_script_args(args)) {
304                 return false;
305         }
306
307         if(args[2]) {
308                 if(!str_to_color(args[2], &color)) return false;
309         }
310
311         return true;
312 }
313
314 void FxFlash::set_color(const Color &col) {
315         color = col;
316 }
317
318 void FxFlash::apply(unsigned long time) {
319         flash(time, this->time, duration, color);
320 }
321
322
323 // ------------- Fade in/out -------------
324
325 FxFade::FxFade() {
326         color1 = Color(0.0, 0.0, 0.0);
327         color2 = Color(1.0, 1.0, 1.0);
328         tex1 = tex2 = 0;
329 }
330
331 FxFade::~FxFade() {}
332
333 bool FxFade::parse_script_args(const char **args) {
334         if(!ImageFx::parse_script_args(args)) {
335                 return false;
336         }
337
338         if(args[2]) {
339                 if(!str_to_color(args[2], &color1)) return false;
340         }
341
342         if(args[3] && !(args[3][0] == '0' && args[3][1] == 0)) {
343                 // texture register? (t0, t1, t2, t3)
344                 if(args[3][0] == 't' && isdigit(args[3][1]) && !args[3][2]) {
345                         int reg = args[3][1] - '0';
346                         if(reg > 3) return false;
347                         tex1 = dsys::tex[reg];
348                 } else {        // or a texture from file?
349                         if(!(tex1 = get_texture(args[3]))) {
350                                 return false;
351                         }
352                 }
353         }
354
355         if(args[4]) {
356                 if(!str_to_color(args[4], &color2)) return false;
357         }
358
359         if(args[5] && !(args[5][0] == '0' && args[5][1] == 0)) {
360                 // texture register? (t0, t1, t2, t3)
361                 if(args[5][0] == 't' && isdigit(args[5][1]) && !args[5][2]) {
362                         int reg = args[5][1] - '0';
363                         if(reg > 3) return false;
364                         tex2 = dsys::tex[reg];
365                 } else {        // or a texture from file?
366                         if(!(tex2 = get_texture(args[5]))) {
367                                 return false;
368                         }
369                 }
370         }
371
372         return true;
373 }
374
375 void FxFade::apply(unsigned long time) {
376         if(time >= this->time && time < this->time + duration) {
377                 float fsec = (float)(time - this->time) / 1000.0;
378                 float t = fsec / ((float)duration / 1000.0);
379
380                 if(tex1) {
381                         set_texture(0, tex1);
382                         enable_texture_unit(0);
383                 }
384                 
385                 if(tex2) {
386                         set_texture(1, tex2);
387                         enable_texture_unit(1);
388
389                         set_texture_constant(1, t);
390                         set_texture_unit_color(1, TOP_LERP, TARG_PREV, TARG_TEXTURE, TARG_CONSTANT);
391                 }
392
393                 set_alpha_blending(true);
394                 set_blend_func(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA);
395                 set_lighting(false);
396
397                 Color col = color1 + (color2 - color1) * t;
398                 col.a = color1.a + (color2.a - color1.a) * t;
399                 
400                 draw_scr_quad(Vector2(0, 0), Vector2(1, 1), col);
401
402                 set_lighting(true);
403                 set_alpha_blending(false);
404
405                 if(tex1) disable_texture_unit(0);
406                 if(tex2) disable_texture_unit(1);
407         }
408 }
409
410 // ------------ Image Overlay ------------
411
412
413 FxOverlay::FxOverlay() {
414         tex = 0;
415         shader = 0;
416 }
417
418 FxOverlay::~FxOverlay() {
419         delete shader;
420 }
421
422 bool FxOverlay::parse_script_args(const char **args) {
423         if(!ImageFx::parse_script_args(args)) {
424                 return false;
425         }
426         if(!args[2]) return false;
427
428         // texture register? (t0, t1, t2, t3)
429         if(args[2][0] == 't' && isdigit(args[2][1]) && !args[2][2]) {
430                 int reg = args[2][1] - '0';
431                 if(reg > 3) return false;
432                 tex = dsys::tex[reg];
433         } else {        // or a texture from file?
434                 if(!(tex = get_texture(args[2]))) {
435                         return false;
436                 }
437         }
438
439         // check if a shader is specified
440         if(args[3]) {
441                 Shader sdr = get_shader(args[3], PROG_PIXEL);
442                 if(!sdr) {
443                         error("failed loading shader %s", args[3]);
444                         return false;
445                 }
446
447                 shader = new GfxProg(0, sdr);
448                 shader->link();
449         }
450         return true;
451 }
452
453 void FxOverlay::set_texture(Texture *tex) {
454         this->tex = tex;
455 }
456
457 void FxOverlay::set_shader(GfxProg *sdr) {
458         shader = sdr;
459 }
460
461 void FxOverlay::apply(unsigned long time) {
462         if(time >= this->time && time < this->time + duration) {
463                 if(shader) {
464                         float fsec = (float)(time - this->time) / 1000.0;
465                         shader->set_parameter("time", fsec);
466
467                         float t = fsec / ((float)duration / 1000.0f);
468                         shader->set_parameter("t", t);
469
470                         float ease = (t < 0.25 || t >= 0.75) ? fabs(sin(t * 2.0 * pi)) : 1.0;
471                         shader->set_parameter("ease", ease);
472
473                         float ease_sin = sin(t * pi);
474                         shader->set_parameter("ease_sin", ease_sin);
475                 }
476                 overlay(tex, Vector2(0, 0), Vector2(1, 1), Color(1, 1, 1), shader);
477         }
478 }
479
480
481
482 // just a little helper function to get a color out of an <r,g,b> string
483 static bool str_to_color(const char *str, Color *col) {
484         // get red
485         if(*str++ != '<') return false;
486         if(!isdigit(*str) && *str != '.') return false;
487         col->r = atof(str);
488
489         while(isdigit(*str) || *str == '.') str++;
490         if(*str++ != ',') return false;
491
492         // get green
493         if(!isdigit(*str) && *str != '.') return false;
494         col->g = atof(str);
495
496         while(isdigit(*str) || *str == '.') str++;
497         if(*str++ != ',') return false;
498
499         // get blue
500         if(!isdigit(*str) && *str != '.') return false;
501         col->b = atof(str);
502         
503         while(isdigit(*str) || *str == '.') str++;
504         if(*str != ',') {
505                 col->a = 1.0;
506                 return *str == '>' ? true : false;
507         }
508
509         // get alpha
510         if(!isdigit(*++str) && *str != '.') return false;
511         col->a = atof(str);
512         
513         while(isdigit(*str) || *str == '.') str++;
514         return *str == '>' ? true : false;
515 }