1 /*
2 * This file is released under the GNU General Public Licence
3 *
4 * authors:
5 * Richard Ashbury <richard.asbury@btinternet.com>
6 * Jean-Christophe Hoelt <jeko@ios-software.com>
7 */
8
9 /////////////////////////////////////////////////////////////////////////////
10 //
11 // Corona.cpp : Implementation of CCorona
12 //
13 /////////////////////////////////////////////////////////////////////////////
14
15 #include "corona.h"
16 #include <cstdlib>
17 #include <cmath>
18 using namespace std;
19
20
21 /////////////////////////////////////////////////////////////////////////////
22 // Corona::Corona
23 // Constructor
24
Corona()25 Corona::Corona()
26 {
27 m_clrForeground = 0x0000FF;
28 m_swirltime = 0;
29 m_testing = false;
30 m_silent = false;
31 m_avg = 1;
32 m_oldval = 0;
33 m_pos = 0;
34
35 m_image = 0;
36 m_real_image = 0;
37 m_deltafield = 0;
38 m_width = -1;
39 m_height = -1;
40 m_real_height = -1;
41 nbParticules = 1000;
42 m_reflArray = 0;
43 m_waveloop = 0.0;
44 m_nPreset = PRESET_CORONA;
45
46 m_particles = (Particle*)calloc (nbParticules, sizeof(Particle));
47 // Create particles in random positions
48 for (int i = nbParticules - 1; i >= 0; --i)
49 {
50 Particle *it = m_particles + i;
51 it->x = random(0, 1);
52 it->y = random(0, 1);
53 it->xvel = it->yvel = 0;
54 }
55 // Set up the background swirling effect
56 chooseRandomSwirl();
57 }
58
59 /////////////////////////////////////////////////////////////////////////////
60 // Corona::~Corona
61 // Destructor
62
~Corona()63 Corona::~Corona()
64 {
65 if (m_real_image) free(m_real_image);
66 if (m_deltafield) free(m_deltafield);
67 }
68
random(double min,double max) const69 double Corona::random(double min, double max) const {
70 return rand() * (max - min) / RAND_MAX + min;
71 }
72
setUpSurface(int width,int height)73 bool Corona::setUpSurface(int width, int height) {
74 // Delete any image that might have previously been allocated
75 if (m_real_image) free(m_real_image);
76 if (m_deltafield) free(m_deltafield);
77 if (m_reflArray) free(m_reflArray);
78
79 // Fill in the size details in the BitmapInfo structure
80 m_width = width;
81 m_height = (height*4) / 5;
82 m_real_height = height;
83
84 // Allocate the image data
85 m_real_image = (unsigned char *)calloc(1,width*height+1);
86 if (m_real_image == 0) return false;
87 m_image = m_real_image + m_width * (m_real_height - m_height);
88 m_reflArray = (int*)calloc(1,m_real_height - m_height + 1);
89
90 // Allocate the delta-field memory, and initialise it
91 m_deltafield = (unsigned char**)malloc(m_width * m_height * sizeof(unsigned char*));
92
93 for (int x = 0; x < m_width; ++x) {
94 for (int y = 0; y < m_height; ++y) {
95 setPointDelta(x, y);
96 }
97 }
98
99 // Change the number of particles
100 int newsize = (int) (::sqrt(m_width * m_height) * 3.0);
101 if (newsize < 2000) newsize = 2000;
102 int oldsize = (int) nbParticules;
103 nbParticules = newsize;
104 m_particles = (Particle*)realloc (m_particles, sizeof(Particle) * newsize);
105 for (int i = oldsize; i < newsize; ++i) {
106 m_particles[i].x = random(0, 1);
107 m_particles[i].y = random(0, 1);
108 m_particles[i].xvel = m_particles[i].yvel = 0;
109 }
110
111 return true;
112 }
113
drawLine(int x0,int y0,int x1,int y1,unsigned char col)114 void Corona::drawLine(int x0, int y0, int x1, int y1, unsigned char col)
115 {
116 int incx = (x1 > x0) ? 1 : -1;
117 int incy = (y1 > y0) ? m_width : -m_width;
118 int dincx = 2 * abs(y1 - y0);
119 int dincy = 2 * abs(x1 - x0);
120 unsigned char* p = &(m_image[x0 + y0 * m_width]);
121 unsigned char* const end = &(m_image[m_width + (m_height - 1) * m_width]);
122 unsigned char* const start = m_image;
123 int n, d; // n is the "pixel counter"
124
125 // Always draw at least one pixel
126 if (start <= p && p < end) *p = col;
127
128 if (abs(x1 - x0) > abs(y1 - y0)) {
129 d = x0 - x1;
130 for (n = abs(x1 - x0); n > 0; --n, p += incx) {
131 if (start <= p && p < end) *p = col;
132 d += dincx;
133 if (d > 0) {
134 p += incy;
135 d -= dincy;
136 }
137 }
138 }
139 else {
140 d = y0 - y1;
141 for (n = abs(y1 - y0); n > 0; --n, p += incy) {
142 if (start <= p && p < end) *p = col;
143 d += dincy;
144 if (d > 0) {
145 p += incx;
146 d -= dincx;
147 }
148 }
149 }
150 }
151
152
chooseRandomSwirl()153 void Corona::chooseRandomSwirl()
154 {
155 m_swirl.x = random(0.2, 0.8);
156 m_swirl.y = random(0.2, 0.8);
157 m_swirl.tightness = random(-0.01, 0.01);
158 m_swirl.pull = random(1.0, 1.04);
159 }
160
setPointDelta(int x,int y)161 void Corona::setPointDelta(int x, int y)
162 {
163 double tx = ((double) x / m_width) - m_swirl.x;
164 double ty = ((double) y / m_height) - m_swirl.y;
165 double d = tx * tx + ty * ty;
166 double ds = ::sqrt(d);
167 double ang = atan2(ty, tx) + m_swirl.tightness / (d + 0.01);
168 int dx = (int) ((ds * m_swirl.pull * cos(ang) - tx) * m_width) + rand() % 5 - 2;
169 int dy = (int) ((ds * m_swirl.pull * sin(ang) - ty) * m_height) + rand() % 5 - 2;
170 if (x + dx < 0) dx = -dx - x;
171 if (x + dx >= m_width) dx = 2 * m_width - 2 * x - dx - 1;
172 if (y + dy < 0) dy = -dy - y;
173 if (y + dy >= m_height) dy = 2 * m_height - 2 * y - dy - 1;
174 m_deltafield[x + y * m_width] = &(m_image[x + dx + (y + dy) * m_width]);
175 }
176
applyDeltaField(bool heavy)177 void Corona::applyDeltaField(bool heavy)
178 {
179 if (heavy) {
180 for (int y = 0; y < m_height; ++y) {
181 unsigned char *s = &(m_image[y * m_width]);
182 unsigned char **p = &(m_deltafield[y * m_width]);
183 for (int x = 0; x < m_width; ++x, ++s, ++p) {
184 *s = (*s + *(*p)) >> 1;
185 if (*s >= 2) *s -= 2;
186 }
187 }
188 }
189 else {
190 for (int y = 0; y < m_height; ++y) {
191 unsigned char *s = &(m_image[y * m_width]);
192 unsigned char **p = &(m_deltafield[y * m_width]);
193 for (int x = 0; x < m_width; ++x, ++s, ++p) {
194 *s = (*s + **p) >> 1;
195 if (*s >= 1) *s -= 1;
196 }
197 }
198 }
199 }
200
getBeatVal(TimedLevel * tl)201 int Corona::getBeatVal(TimedLevel *tl)
202 {
203 int total = 0;
204 for (int i = 50; i < 250; ++i) {
205 int n = tl->frequency[0][i];
206 total += n;
207 }
208 total /= 3;
209
210 m_avg = 0.9 * m_avg + 0.1 * total;
211 if (m_avg < 1000) m_avg = 1000;
212
213 if (total > m_avg * 1.2 && tl->timeStamp - tl->lastbeat > 750000) {
214 m_avg = total;
215 tl->lastbeat = tl->timeStamp;
216 if (total > 2500) return 2500;
217 else return total;
218 }
219 else return 0;
220 }
221
drawParticules()222 void Corona::drawParticules()
223 {
224 int p;
225 for (p = 0; p < nbParticules; ++p) {
226 Particle *it = m_particles + p;
227 int x = (int) (it->x * m_width);
228 int y = (int) (it->y * m_height);
229 int xv = (int) (it->xvel * m_width);
230 int yv = (int) (it->yvel * m_height);
231 drawLine(x, y, x - xv, y - yv, 255);
232 }
233 }
234
drawParticulesWithShift()235 void Corona::drawParticulesWithShift()
236 {
237 int p;
238 for (p = 0; p < nbParticules; ++p) {
239 Particle *it = m_particles + p;
240 int x = (int) (it->x * m_width);
241 int y = (int) (it->y * m_height);
242 int xv = (int) (it->xvel * m_width);
243 int yv = (int) (it->yvel * m_height);
244 double l = (xv * xv + yv * yv);
245 if (l > 10.0 * 10.0) {
246 l = ::sqrt(l);
247 double dl = 10 / (l + 0.01);
248 xv = (int) (xv * dl);
249 yv = (int) (yv * dl);
250 }
251 drawLine(x, y, x - xv, y - yv, 255);
252 }
253 }
254
getAvgParticlePos(double & x,double & y) const255 void Corona::getAvgParticlePos(double& x, double& y) const
256 {
257 x = y = 0;
258 for (int i = 0; i < 10; ++i) {
259 int r = rand() % nbParticules;
260 x += m_particles[r].x;
261 y += m_particles[r].y;
262 }
263 x /= 10;
264 y /= 10;
265 }
266
267 #define REFL_MIN_WIDTH 3.0
268 #define REFL_INC_WIDTH 0.08
269
genReflectedWaves(double loop)270 void Corona::genReflectedWaves(double loop)
271 {
272 double fdec = 0.0;
273 double floop = 0.0;
274 double fwidth = (m_real_height - m_height) * REFL_INC_WIDTH + REFL_MIN_WIDTH;
275 double REFL_MAX_WIDTH = fwidth;
276
277 m_reflArray = new int[m_real_height - m_height + 1];
278
279 for (int i = 0; i < (m_real_height - m_height); ++i)
280 {
281 double fincr = (3.1415 / 2.0) * (1.0 - (fwidth - REFL_MIN_WIDTH) / REFL_MAX_WIDTH);
282 floop += fincr;
283
284 fwidth -= REFL_INC_WIDTH;
285 fdec = fwidth * sin(floop + loop);
286 m_reflArray[i] = (int)fdec;
287 }
288 }
289
drawReflected()290 void Corona::drawReflected()
291 {
292 genReflectedWaves(m_waveloop);
293 int offsetDest = (m_real_height - m_height - 1) * m_width;
294 int offsetSrc = (m_real_height - m_height) * m_width;
295
296 for (int i = m_real_height - m_height; i--;)
297 {
298 int idec = m_reflArray[i];
299
300 for (int x = m_width; x--;)
301 {
302 int out = m_real_image[(offsetSrc++) + idec];
303 m_real_image[offsetDest++] = out;
304 }
305
306 offsetDest -= m_width * 2;
307 offsetSrc += m_width;
308 }
309 }
310
blurImage()311 void Corona::blurImage()
312 {
313 for (int y = 1; y < m_real_height - 1; ++y) {
314 m_real_image[y * m_width] = 0;
315 for (int x = 1; x < m_width - 1; ++x) {
316 int n = x + y * m_width;
317 int val = m_real_image[n + 1];
318 val += m_real_image[n - 1];
319 val += m_real_image[n - m_width];
320 val += m_real_image[n + m_width];
321 val >>= 2;
322 m_real_image[n] = val & 0xff;
323 }
324 }
325 }
326
update(TimedLevel * pLevels)327 void Corona::update(TimedLevel *pLevels)
328 {
329 // Check for a beat
330 int beatval = getBeatVal(pLevels);
331 if (beatval > 1000)
332 {
333 int total = 0;
334 for (int i = 0; i < 512; ++i)
335 total += 2 * pLevels->frequency[0][i];
336 double currval = 1.0 - exp(-total / 40000.0);
337 m_oldval = (m_oldval + currval) / 2.0;
338
339 double tx, ty;
340 getAvgParticlePos(tx, ty);
341 // If most of the particles are low down, use a launch
342 if (ty < 0.2 && rand() % 4 != 0) {
343 int p;
344 double bv = m_oldval * 5.0;
345 for (p = 0; p < nbParticules; ++p)
346 {
347 Particle *it = m_particles + p;
348 if (it->y < 0.1) {
349 double x = (it->x - tx) / bv;
350 it->yvel += 0.01 * bv * exp(-1000.0 * x * x);
351 }
352 }
353 }
354 else
355 { // Otherwise use a swirl
356 tx += random(-0.1, 0.1);
357 ty += random(-0.1, 0.1);
358 double bv = 0.009 * m_oldval;
359 double bv2 = 0.0036 * m_oldval;
360 if (rand() % 2 == 0) bv = -bv;
361 m_movement.x = tx;
362 m_movement.y = ty;
363 m_movement.tightness = random(0.8 * bv, bv);
364 m_movement.pull = random(1 - bv2, 1 - 0.2 * bv2);
365 m_swirltime = 1;
366 }
367
368 pLevels->lastbeat = pLevels->currentTimeMs;
369 }
370
371 // Deal with the particles
372 int p;
373 for (p = 0; p < nbParticules; ++p)
374 {
375 Particle *it = m_particles + p;
376 // Apply gravity
377 it->yvel -= 0.0006; // the gravity value
378
379 // If there's an active swirl, swirl around it
380 if (m_swirltime > 0) {
381 double dx = it->x - m_movement.x;
382 double dy = it->y - m_movement.y;
383 double d = dx * dx + dy * dy;
384 double ds = ::sqrt(d);
385 double ang = atan2(dy, dx) + m_movement.tightness / (d + 0.01);
386 it->xvel += (ds * m_movement.pull * cos(ang) - dx);
387 it->yvel += (ds * m_movement.pull * sin(ang) - dy);
388 }
389
390 // Gitter
391 it->xvel += random(-0.0002, 0.0002);
392 it->yvel += random(-0.0002, 0.0002);
393
394 // Clamp the velocity
395 if (it->xvel < -0.1 ) it->xvel = -0.1;
396 if (it->xvel > 0.1 ) it->xvel = 0.1;
397 if (it->yvel < -0.1 ) it->yvel = -0.1;
398 if (it->yvel > 0.1 ) it->yvel = 0.1;
399
400 // Randomly move the particle once in a while
401 if (rand() % (nbParticules / 5) == 0)
402 {
403 it->x = random(0, 1);
404 it->y = random(0, 1);
405 it->xvel = it->yvel = 0;
406 }
407
408 // Move and bounce the particle
409 it->x += it->xvel;
410 it->y += it->yvel;
411 if (it->x < 0) { it->x = -it->x; it->xvel *= -0.25; it->yvel *= 0.25; }
412 if (it->y < 0) { it->y = -it->y; it->xvel *= 0.25; it->yvel *= -0.25; }
413 if (it->x > 1) { it->x = 2.0 - it->x; it->xvel *= -0.25; it->yvel *= 0.25; }
414 if (it->y > 1) { it->y = 2.0 - it->y; it->xvel *= 0.25; it->yvel = 0; }
415 }
416
417 if (m_swirltime > 0) --m_swirltime;
418
419 // Randomly change the delta field
420 if (rand() % 200 == 0) chooseRandomSwirl();
421
422 // Animate the waves
423 m_waveloop += 0.6;
424
425 // drawing.
426 if (m_image != 0)
427 {
428 // Draw the particles on the bitmap
429 drawParticules();
430
431 // Apply the deltafield and update a few of its points
432 applyDeltaField((m_nPreset == PRESET_BLAZE) && m_width * m_height < 150000);
433
434 int n = (m_width * m_height) / 100;
435 for (int i = 0; i < n; ++i)
436 setPointDelta(rand() % m_width, rand() % m_height);
437
438 // If on the blaze preset, draw the particles again
439 if (m_nPreset == PRESET_BLAZE)
440 drawParticules();
441
442 drawReflected();
443
444 // Blur the bitmap
445 blurImage();
446
447 // If on the blaze preset, draw the particles one last time
448 if (m_nPreset == PRESET_BLAZE)
449 drawParticulesWithShift();
450 }
451 }
452
453