1 /*
2  * Copyright (C) 2002  Terence M. Welsh
3  * Ported to Linux by Tugrul Galatali <tugrul@galatali.com>
4  *
5  * Helios is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Helios is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 
19 // Helios screensaver
20 
21 #include <math.h>
22 #include <stdio.h>
23 #include <GL/gl.h>
24 #include <GL/glu.h>
25 #include <GL/glx.h>
26 
27 #include "driver.h"
28 #include "Implicit/impCubeVolume.h"
29 #include "Implicit/impCrawlPoint.h"
30 #include "Implicit/impSphere.h"
31 #include "loadTexture.h"
32 #include "rgbhsl.h"
33 #include "rsDefines.h"
34 #include "rsRand.h"
35 #include "rsMath/rsMatrix.h"
36 #include "rsMath/rsQuat.h"
37 #include "rsMath/rsVec.h"
38 
39 const char *hack_name = "helios";
40 
41 #define TEXSIZE 256
42 
43 extern unsigned char *spheremap;
44 extern unsigned int spheremap_size;
45 extern unsigned int spheremap_compressedsize;
46 
47 #define LIGHTSIZE 64
48 
49 class particle;
50 class emitter;
51 class attracter;
52 class ion;
53 
54 // Global variables
55 int readyToDraw = 0;
56 unsigned char lightTexture[LIGHTSIZE][LIGHTSIZE];
57 float elapsedTime = 0.0f;
58 emitter *elist;
59 attracter *alist;
60 ion *ilist;
61 rsVec newRgb;
62 float billboardMat[16];
63 int pfd_swap_exchange;
64 
65 // Parameters edited in the dialog box
66 int dIons;
67 int dSize;
68 int dEmitters;
69 int dAttracters;
70 int dSpeed;
71 int dCameraspeed;
72 int dSurface;
73 int dWireframe;
74 int dBlur;
75 
76 impCubeVolume *volume;
77 impSurface *surface;
78 impSphere *spheres;
79 
80 class particle {
81       public:
82 	rsVec pos;
83 	rsVec rgb;
84 	float size;
85 };
86 
87 class emitter:public particle {
88       public:
89 	rsVec oldpos;
90 	rsVec targetpos;
91 
92 	  emitter ();
~emitter()93 	 ~emitter () {
94 	};
settargetpos(rsVec target)95 	void settargetpos (rsVec target) {
96 		oldpos = pos;
97 		targetpos = target;
98 	};
interppos(float n)99 	void interppos (float n) {
100 		pos = oldpos * (1.0f - n) + targetpos * n;
101 	};
update()102 	void update () {
103 	};
104 };
105 
emitter()106 emitter::emitter ()
107 {
108 	pos = rsVec (rsRandf (1000.0f) - 500.0f, rsRandf (1000.0f) - 500.0f, rsRandf (1000.0f) - 500.0f);
109 }
110 
111 class attracter:public particle {
112       public:
113 	rsVec oldpos;
114 	rsVec targetpos;
115 
116 	  attracter ();
~attracter()117 	 ~attracter () {
118 	};
settargetpos(rsVec target)119 	void settargetpos (rsVec target) {
120 		oldpos = pos;
121 		targetpos = target;
122 	};
interppos(float n)123 	void interppos (float n) {
124 		pos = oldpos * (1.0f - n) + targetpos * n;
125 	};
update()126 	void update () {
127 	};
128 };
129 
attracter()130 attracter::attracter ()
131 {
132 	pos = rsVec (rsRandf (1000.0f) - 500.0f, rsRandf (1000.0f) - 500.0f, rsRandf (1000.0f) - 500.0f);
133 }
134 
135 class ion:public particle {
136       public:
137 	float speed;
138 
139 	  ion ();
~ion()140 	 ~ion () {
141 	};
142 	void start ();
143 	void update ();
144 	void draw ();
145 };
146 
ion()147 ion::ion ()
148 {
149 	float temp;
150 
151 	pos = rsVec (0.0f, 0.0f, 0.0f);
152 	rgb = rsVec (0.0f, 0.0f, 0.0f);
153 	temp = rsRandf (2.0f) + 0.4f;
154 	size = float (dSize) * temp;
155 	speed = float (dSpeed) * 12.0f / temp;
156 }
157 
158 void
start()159   ion::start ()
160 {
161 	int i = rsRandi (dEmitters);
162 	float offset = elapsedTime * speed;
163 
164 	pos = elist[i].pos;
165 
166 	switch (rsRandi (14)) {
167 	case 0:
168 		pos[0] += offset;
169 		break;
170 	case 1:
171 		pos[0] -= offset;
172 		break;
173 	case 2:
174 		pos[1] += offset;
175 		break;
176 	case 3:
177 		pos[1] -= offset;
178 		break;
179 	case 4:
180 		pos[2] += offset;
181 		break;
182 	case 5:
183 		pos[2] -= offset;
184 		break;
185 	case 6:
186 		pos[0] += offset;
187 		pos[1] += offset;
188 		pos[2] += offset;
189 		break;
190 	case 7:
191 		pos[0] -= offset;
192 		pos[1] += offset;
193 		pos[2] += offset;
194 		break;
195 	case 8:
196 		pos[0] += offset;
197 		pos[1] -= offset;
198 		pos[2] += offset;
199 		break;
200 	case 9:
201 		pos[0] -= offset;
202 		pos[1] -= offset;
203 		pos[2] += offset;
204 		break;
205 	case 10:
206 		pos[0] += offset;
207 		pos[1] += offset;
208 		pos[2] -= offset;
209 		break;
210 	case 11:
211 		pos[0] -= offset;
212 		pos[1] += offset;
213 		pos[2] -= offset;
214 		break;
215 	case 12:
216 		pos[0] += offset;
217 		pos[1] -= offset;
218 		pos[2] -= offset;
219 		break;
220 	case 13:
221 		pos[0] -= offset;
222 		pos[1] -= offset;
223 		pos[2] -= offset;
224 	}
225 
226 	rgb = newRgb;
227 }
228 
update()229 void ion::update ()
230 {
231 	int i;
232 	int startOver = 0;
233 	static float startOverDistance;
234 	static rsVec force, tempvec;
235 	static float length, temp;
236 
237 	force = rsVec (0.0f, 0.0f, 0.0f);
238 	for (i = 0; i < dEmitters; i++) {
239 		tempvec = pos - elist[i].pos;
240 		length = tempvec.normalize ();
241 		if (length > 11000.0f)
242 			startOver = 1;
243 		if (length <= 1.0f)
244 			temp = 1.0f;
245 		else
246 			temp = 1.0f / length;
247 		tempvec *= temp;
248 		force += tempvec;
249 	}
250 	startOverDistance = speed * elapsedTime;
251 	for (i = 0; i < dAttracters; i++) {
252 		tempvec = alist[i].pos - pos;
253 		length = tempvec.normalize ();
254 		if (length < startOverDistance)
255 			startOver = 1;
256 		if (length <= 1.0f)
257 			temp = 1.0f;
258 		else
259 			temp = 1.0f / length;
260 		tempvec *= temp;
261 		force += tempvec;
262 	}
263 
264 	// Start this ion at an emitter if it gets too close to an attracter
265 	// or too far from an emitter
266 	if (startOver)
267 		start ();
268 	else {
269 		force.normalize ();
270 		pos += (force * elapsedTime * speed);
271 	}
272 }
273 
draw()274 void ion::draw ()
275 {
276 	glColor3f (rgb[0], rgb[1], rgb[2]);
277 	glPushMatrix ();
278 	glTranslatef (pos[0] * billboardMat[0] + pos[1] * billboardMat[4] + pos[2] * billboardMat[8],
279 		      pos[0] * billboardMat[1] + pos[1] * billboardMat[5] + pos[2] * billboardMat[9],
280 		      pos[0] * billboardMat[2] + pos[1] * billboardMat[6] + pos[2] * billboardMat[10]);
281 	glScalef (size, size, size);
282 	glCallList (1);
283 	glPopMatrix ();
284 }
285 
setTargets(int whichTarget)286 void setTargets (int whichTarget)
287 {
288 	int i;
289 
290 	switch (whichTarget) {
291 	case 0:		// random
292 		for (i = 0; i < dEmitters; i++)
293 			elist[i].settargetpos (rsVec (rsVec (rsRandf (1000.0f) - 500.0f, rsRandf (1000.0f) - 500.0f, rsRandf (1000.0f) - 500.0f)));
294 		for (i = 0; i < dAttracters; i++)
295 			alist[i].settargetpos (rsVec (rsVec (rsRandf (1000.0f) - 500.0f, rsRandf (1000.0f) - 500.0f, rsRandf (1000.0f) - 500.0f)));
296 		break;
297 	case 1:
298 		{		// line (all emitters on one side, all attracters on the other)
299 			float position = -500.0f, change = 1000.0f / float (dEmitters + dAttracters - 1);
300 
301 			for (i = 0; i < dEmitters; i++) {
302 				elist[i].settargetpos (rsVec (rsVec (position, position * 0.5f, 0.0f)));
303 				position += change;
304 			}
305 			for (i = 0; i < dAttracters; i++) {
306 				alist[i].settargetpos (rsVec (rsVec (position, position * 0.5f, 0.0f)));
307 				position += change;
308 			}
309 			break;
310 		}
311 	case 2:
312 		{		// line (emitters and attracters staggered)
313 			float change;
314 
315 			if (dEmitters > dAttracters) {
316 				change = 1000.0f / float (dEmitters * 2 - 1);
317 			} else {
318 				change = 1000.0f / float (dAttracters * 2 - 1);
319 			}
320 			float position = -500.0f;
321 
322 			for (i = 0; i < dEmitters; i++) {
323 				elist[i].settargetpos (rsVec (rsVec (position, position * 0.5f, 0.0f)));
324 				position += change * 2.0f;
325 			}
326 			position = -500.0f + change;
327 			for (i = 0; i < dAttracters; i++) {
328 				alist[i].settargetpos (rsVec (rsVec (position, position * 0.5f, 0.0f)));
329 				position += change * 2.0f;
330 			}
331 			break;
332 		}
333 	case 3:
334 		{		// 2 lines (parallel)
335 			float change = 1000.0f / float (dEmitters * 2 - 1);
336 			float position = -500.0f;
337 			float height = -525.0f + float (dEmitters * 25);
338 
339 			for (i = 0; i < dEmitters; i++) {
340 				elist[i].settargetpos (rsVec (rsVec (position, height, -50.0f)));
341 				position += change * 2.0f;
342 			}
343 			change = 1000.0f / float (dAttracters * 2 - 1);
344 
345 			position = -500.0f;
346 			height = 525.0f - float (dAttracters * 25);
347 
348 			for (i = 0; i < dAttracters; i++) {
349 				alist[i].settargetpos (rsVec (rsVec (position, height, 50.0f)));
350 				position += change * 2.0f;
351 			}
352 			break;
353 		}
354 	case 4:
355 		{		// 2 lines (skewed)
356 			float change = 1000.0f / float (dEmitters * 2 - 1);
357 			float position = -500.0f;
358 			float height = -525.0f + float (dEmitters * 25);
359 
360 			for (i = 0; i < dEmitters; i++) {
361 				elist[i].settargetpos (rsVec (rsVec (position, height, 0.0f)));
362 				position += change * 2.0f;
363 			}
364 			change = 1000.0f / float (dAttracters * 2 - 1);
365 
366 			position = -500.0f;
367 			height = 525.0f - float (dAttracters * 25);
368 
369 			for (i = 0; i < dAttracters; i++) {
370 				alist[i].settargetpos (rsVec (rsVec (10.0f, height, position)));
371 				position += change * 2.0f;
372 			}
373 			break;
374 		}
375 	case 5:		// random distribution across a plane
376 		for (i = 0; i < dEmitters; i++)
377 			elist[i].settargetpos (rsVec (rsVec (rsRandf (1000.0f) - 500.0f, 0.0f, rsRandf (1000.0f) - 500.0f)));
378 		for (i = 0; i < dAttracters; i++)
379 			alist[i].settargetpos (rsVec (rsVec (rsRandf (1000.0f) - 500.0f, 0.0f, rsRandf (1000.0f) - 500.0f)));
380 		break;
381 	case 6:
382 		{		// random distribution across 2 planes
383 			float height = -525.0f + float (dEmitters * 25);
384 
385 			for (i = 0; i < dEmitters; i++)
386 				elist[i].settargetpos (rsVec (rsVec (rsRandf (1000.0f) - 500.0f, height, rsRandf (1000.0f) - 500.0f)));
387 
388 			height = 525.0f - float (dAttracters * 25);
389 
390 			for (i = 0; i < dAttracters; i++)
391 				alist[i].settargetpos (rsVec (rsVec (rsRandf (1000.0f) - 500.0f, height, rsRandf (1000.0f) - 500.0f)));
392 
393 			break;
394 		}
395 	case 7:
396 		{		// 2 rings (1 inside and 1 outside)
397 			float angle = 0.5f, cosangle, sinangle;
398 			float change = PIx2 / float (dEmitters);
399 
400 			for (i = 0; i < dEmitters; i++) {
401 				angle += change;
402 				cosangle = cos (angle) * 200.0f;
403 				sinangle = sin (angle) * 200.0f;
404 				elist[i].settargetpos (rsVec (rsVec (cosangle, sinangle, 0.0f)));
405 			}
406 			angle = 1.5f;
407 			change = PIx2 / float (dAttracters);
408 
409 			for (i = 0; i < dAttracters; i++) {
410 				angle += change;
411 				cosangle = cos (angle) * 500.0f;
412 				sinangle = sin (angle) * 500.0f;
413 				alist[i].settargetpos (rsVec (rsVec (cosangle, sinangle, 0.0f)));
414 			}
415 			break;
416 		}
417 	case 8:
418 		{		// ring (all emitters on one side, all attracters on the other)
419 			float angle = 0.5f, cosangle, sinangle;
420 			float change = PIx2 / float (dEmitters + dAttracters);
421 
422 			for (i = 0; i < dEmitters; i++) {
423 				angle += change;
424 				cosangle = cos (angle) * 500.0f;
425 				sinangle = sin (angle) * 500.0f;
426 				elist[i].settargetpos (rsVec (rsVec (cosangle, sinangle, 0.0f)));
427 			}
428 			for (i = 0; i < dAttracters; i++) {
429 				angle += change;
430 				cosangle = cos (angle) * 500.0f;
431 				sinangle = sin (angle) * 500.0f;
432 				alist[i].settargetpos (rsVec (rsVec (cosangle, sinangle, 0.0f)));
433 			}
434 			break;
435 		}
436 	case 9:
437 		{		// ring (emitters and attracters staggered)
438 			float change;
439 
440 			if (dEmitters > dAttracters) {
441 				change = PIx2 / float (dEmitters * 2);
442 			} else {
443 				change = PIx2 / float (dAttracters * 2);
444 			}
445 			float angle = 0.5f, cosangle, sinangle;
446 
447 			for (i = 0; i < dEmitters; i++) {
448 				cosangle = cos (angle) * 500.0f;
449 				sinangle = sin (angle) * 500.0f;
450 				elist[i].settargetpos (rsVec (rsVec (cosangle, sinangle, 0.0f)));
451 				angle += change * 2.0f;
452 			}
453 			angle = 0.5f + change;
454 			for (i = 0; i < dAttracters; i++) {
455 				cosangle = cos (angle) * 500.0f;
456 				sinangle = sin (angle) * 500.0f;
457 				alist[i].settargetpos (rsVec (rsVec (cosangle, sinangle, 0.0f)));
458 				angle += change * 2.0f;
459 			}
460 			break;
461 		}
462 	case 10:		// 2 points
463 		for (i = 0; i < dEmitters; i++)
464 			elist[i].settargetpos (rsVec (rsVec (500.0f, 100.0f, 50.0f)));
465 		for (i = 0; i < dAttracters; i++)
466 			alist[i].settargetpos (rsVec (rsVec (-500.0f, -100.0f, -50.0f)));
467 		break;
468 	}
469 }
470 
surfaceFunction(float * position)471 float surfaceFunction (float *position)
472 {
473 	static int i;
474 	static float value;
475 	static int points = dEmitters + dAttracters;
476 
477 	value = 0.0f;
478 	for (i = 0; i < points; i++)
479 		value += spheres[i].value (position);
480 
481 	return (value);
482 }
483 
hack_draw(xstuff_t * XStuff,double currentTime,float frameTime)484 void hack_draw (xstuff_t * XStuff, double currentTime, float frameTime)
485 {
486 	int i;
487 	static int ionsReleased = 0;
488 	static float releaseTime = 0.0f;
489 	Display *dpy = XStuff->display;
490 #ifdef BENCHMARK
491 	static int a = 1;
492 #endif
493 
494 	Window window = XStuff->window;
495 
496 	elapsedTime = frameTime;
497 
498 #ifdef BENCHMARK
499 	if (a++ == 1000)
500 		exit(0);
501 	elapsedTime = 0.1f;
502 #endif
503 
504 	// Camera movements
505 	// first do translation (distance from center)
506 	static float oldCameraDistance;
507 	static float cameraDistance;
508 	static float targetCameraDistance = -1000.0f;
509 	static float preCameraInterp = PI;
510 	float cameraInterp;
511 
512 	preCameraInterp += float (dCameraspeed) * elapsedTime * 0.01f;
513 
514 	cameraInterp = 0.5f - (0.5f * cos (preCameraInterp));
515 	cameraDistance = (1.0f - cameraInterp) * oldCameraDistance + cameraInterp * targetCameraDistance;
516 
517 	if (preCameraInterp >= PI) {
518 		oldCameraDistance = targetCameraDistance;
519 		targetCameraDistance = -rsRandf (1300.0f) - 200.0f;
520 		preCameraInterp = 0.0f;
521 	}
522 
523 	glMatrixMode (GL_MODELVIEW);
524 	glLoadIdentity ();
525 	glTranslatef (0.0, 0.0, cameraDistance);
526 
527 	// then do rotation
528 	static rsVec radialVel = rsVec (0.0f, 0.0f, 0.0f);
529 	static rsVec targetRadialVel = radialVel;
530 	static rsQuat rotQuat = rsQuat (0.0f, 0.0f, 0.0f, 1.0f);
531 
532 	rsVec radialVelDiff = targetRadialVel - radialVel;
533 	float changeRemaining = radialVelDiff.normalize ();
534 	float change = float (dCameraspeed) * 0.0002f * elapsedTime;
535 
536 	if (changeRemaining > change) {
537 		radialVelDiff *= change;
538 		radialVel += radialVelDiff;
539 	} else {
540 		radialVel = targetRadialVel;
541 		if (rsRandi (2)) {
542 			targetRadialVel = rsVec (rsRandf (1.0f), rsRandf (1.0f), rsRandf (1.0f));
543 			targetRadialVel.normalize ();
544 			targetRadialVel *= float (dCameraspeed) * rsRandf (0.002f);
545 		} else
546 			targetRadialVel = rsVec (0.0f, 0.0f, 0.0f);
547 	}
548 
549 	rsVec tempRadialVel = radialVel;
550 	float angle = tempRadialVel.normalize ();
551 
552 	rsQuat radialQuat;
553 
554 	radialQuat.make (angle, tempRadialVel[0], tempRadialVel[1], tempRadialVel[2]);
555 	rotQuat.preMult (radialQuat);
556 	rsMatrix rotMat;
557 
558 	rotMat.fromQuat (rotQuat);
559 
560 	// make billboard matrix for rotating particles when they are drawn
561 	rotMat.get (billboardMat);
562 
563 	// Calculate new color
564 	static rsVec oldHsl, newHsl = rsVec (rsRandf (1.0f), 1.0f, 1.0f), targetHsl;
565 	static float colorInterp = 1.0f, colorChange;
566 
567 	colorInterp += elapsedTime * colorChange;
568 	if (colorInterp >= 1.0f) {
569 		if (!rsRandi (3) && dIons >= 100)	// change color suddenly
570 			newHsl = rsVec (rsRandf (1.0f), 1.0f - (rsRandf (1.0f) * rsRandf (1.0f)), 1.0f);
571 		oldHsl = newHsl;
572 		targetHsl = rsVec (rsRandf (1.0f), 1.0f - (rsRandf (1.0f) * rsRandf (1.0f)), 1.0f);
573 		colorInterp = 0.0f;
574 		// amount by which to change colorInterp each second
575 		colorChange = rsRandf (0.005f * float (dSpeed)) + (0.002f * float (dSpeed));
576 	} else {
577 		float diff = targetHsl[0] - oldHsl[0];
578 
579 		if (diff < -0.5f || (diff > 0.0f && diff < 0.5f))
580 			newHsl[0] = oldHsl[0] + colorInterp * diff;
581 		else
582 			newHsl[0] = oldHsl[0] - colorInterp * diff;
583 		diff = targetHsl[1] - oldHsl[1];
584 		newHsl[1] = oldHsl[1] + colorInterp * diff;
585 		if (newHsl[0] < 0.0f)
586 			newHsl[0] += 1.0f;
587 		if (newHsl[0] > 1.0f)
588 			newHsl[0] -= 1.0f;
589 		hsl2rgb (newHsl[0], newHsl[1], 1.0f, newRgb[0], newRgb[1], newRgb[2]);
590 	}
591 
592 	// Release ions
593 	if (ionsReleased < dIons) {
594 		releaseTime -= elapsedTime;
595 		while (ionsReleased < dIons && releaseTime <= 0.0f) {
596 			ilist[ionsReleased].start ();
597 			ionsReleased++;
598 			// all ions released after 2 minutes
599 			releaseTime += 120.0f / float (dIons);
600 		}
601 	}
602 	// Set interpolation value for emitters and attracters
603 	static float wait = 0.0f;
604 	static float preinterp = PI, interp;
605 	static float interpconst = 0.001f;
606 
607 	wait -= elapsedTime;
608 	if (wait <= 0.0f) {
609 		preinterp += elapsedTime * float (dSpeed) * interpconst;
610 
611 		interp = 0.5f - (0.5f * cos (preinterp));
612 	}
613 	if (preinterp >= PI) {
614 		// select new taget points (not the same pattern twice in a row)
615 		static int newTarget = 0, lastTarget;
616 
617 		lastTarget = newTarget;
618 		newTarget = rsRandi (10);
619 		if (newTarget == lastTarget)
620 			newTarget++;
621 		setTargets (newTarget);
622 		preinterp = 0.0f;
623 		interp = 0.0f;
624 		wait = 10.0f;	// pause after forming each new pattern
625 		interpconst = 0.001f;
626 		if (!rsRandi (4))	// interpolate really fast sometimes
627 			interpconst = 0.1f;
628 	}
629 	// Update particles
630 	for (i = 0; i < dEmitters; i++) {
631 		elist[i].interppos (interp);
632 		elist[i].update ();
633 	}
634 	for (i = 0; i < dAttracters; i++) {
635 		alist[i].interppos (interp);
636 		alist[i].update ();
637 	}
638 	for (i = 0; i < ionsReleased; i++)
639 		ilist[i].update ();
640 
641 	// Calculate surface
642 	if (dSurface) {
643 		for (i = 0; i < dEmitters; i++)
644 			spheres[i].setPosition (elist[i].pos[0], elist[i].pos[1], elist[i].pos[2]);
645 		for (i = 0; i < dAttracters; i++)
646 			spheres[dEmitters + i].setPosition (alist[i].pos[0], alist[i].pos[1], alist[i].pos[2]);
647 
648 		impCrawlPointVector cpv;
649 		for(i=0; i<dEmitters+dAttracters; i++)
650 			spheres[i].addCrawlPoint(cpv);
651 
652 		surface->reset ();
653 
654 		static float valuetrig = 0.0f;
655 		valuetrig += elapsedTime;
656 
657 		volume->setSurfaceValue(0.45f + 0.05f * cosf(valuetrig));
658 		volume->makeSurface(cpv);
659 	}
660 	// Draw
661 	// clear the screen
662 	if (dBlur) {		// partially
663 		glMatrixMode (GL_PROJECTION);
664 		glPushMatrix ();
665 			glLoadIdentity();
666 			glOrtho(0.0, 1.0, 0.0, 1.0, 1.0, -1.0);
667 			glMatrixMode(GL_MODELVIEW);
668 			glPushMatrix();
669 				glLoadIdentity();
670 				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
671 				glColor4f(0.0f, 0.0f, 0.0f, 0.5f - (float(sqrtf(sqrtf(float(dBlur)))) * 0.15495f));
672 				glBegin(GL_TRIANGLE_STRIP);
673 					glVertex3f(0.0f, 0.0f, 0.0f);
674 					glVertex3f(1.0f, 0.0f, 0.0f);
675 					glVertex3f(0.0f, 1.0f, 0.0f);
676 					glVertex3f(1.0f, 1.0f, 0.0f);
677 			glEnd();
678 		glPopMatrix();
679 		glMatrixMode(GL_PROJECTION);
680 		glPopMatrix();
681 	} else			// completely
682 		glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
683 
684 	// Draw ions
685 	glMatrixMode(GL_MODELVIEW);
686 	glBlendFunc (GL_ONE, GL_ONE);
687 	glBindTexture (GL_TEXTURE_2D, 1);
688 	for (i = 0; i < ionsReleased; i++)
689 		ilist[i].draw ();
690 
691 	// Draw surfaces
692 	float brightFactor = 0;
693 	float surfaceColor[3] = {
694 		0.0f,
695 		0.0f,
696 		0.0f
697 	};
698 
699 	if (dSurface) {
700 		glBindTexture (GL_TEXTURE_2D, 2);
701 		glEnable (GL_TEXTURE_GEN_S);
702 		glEnable (GL_TEXTURE_GEN_T);
703 		// find color for surfaces
704 		if (dIons >= 100) {
705 			if (dWireframe)
706 				brightFactor = 2.0f / (float (dBlur + 30) * float (dBlur + 30));
707 			else
708 				brightFactor = 4.0f / (float (dBlur + 30) * float (dBlur + 30));
709 			for (i = 0; i < 100; i++) {
710 				surfaceColor[0] += ilist[i].rgb[0] * brightFactor;
711 				surfaceColor[1] += ilist[i].rgb[1] * brightFactor;
712 				surfaceColor[2] += ilist[i].rgb[2] * brightFactor;
713 			}
714 			glColor3fv (surfaceColor);
715 		} else {
716 			if (dWireframe)
717 				brightFactor = 200.0f / (float (dBlur + 30) * float (dBlur + 30));
718 			else
719 				brightFactor = 400.0f / (float (dBlur + 30) * float (dBlur + 30));
720 			glColor3f (newRgb[0] * brightFactor, newRgb[1] * brightFactor, newRgb[2] * brightFactor);
721 		}
722 		// draw the surface
723 		glPushMatrix ();
724 		glMultMatrixf (billboardMat);
725 		if (dWireframe) {
726 			glDisable (GL_TEXTURE_2D);
727 			surface->draw_wireframe ();
728 			glEnable (GL_TEXTURE_2D);
729 		} else
730 			surface->draw ();
731 		glPopMatrix ();
732 		glDisable (GL_TEXTURE_GEN_S);
733 		glDisable (GL_TEXTURE_GEN_T);
734 	}
735 	// If graphics card does a true buffer swap instead of a copy swap
736 	// then everything must get drawn on both buffers
737 	if (dBlur & pfd_swap_exchange) {
738 		glXSwapBuffers (dpy, window);
739 		// wglSwapLayerBuffers(hdc, WGL_SWAP_MAIN_PLANE);
740 		glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
741 		glColor4f (0.0f, 0.0f, 0.0f, 0.5f - (float (sqrt (sqrt (double (dBlur)))) * 0.15495f));
742 
743 		glPushMatrix ();
744 		glLoadIdentity ();
745 		glBegin (GL_TRIANGLE_STRIP);
746 		glVertex3f (-5.0f, -4.0f, -3.0f);
747 		glVertex3f (5.0f, -4.0f, -3.0f);
748 		glVertex3f (-5.0f, 4.0f, -3.0f);
749 		glVertex3f (5.0f, 4.0f, -3.0f);
750 		glEnd ();
751 		glPopMatrix ();
752 
753 		// Draw ions
754 		glBlendFunc (GL_ONE, GL_ONE);
755 		glBindTexture (GL_TEXTURE_2D, 1);
756 		for (i = 0; i < ionsReleased; i++)
757 			ilist[i].draw ();
758 
759 		// Draw surfaces
760 		if (dSurface) {
761 			glBindTexture (GL_TEXTURE_2D, 2);
762 			glEnable (GL_TEXTURE_GEN_S);
763 			glEnable (GL_TEXTURE_GEN_T);
764 			if (dIons >= 100)
765 				glColor3fv (surfaceColor);
766 			else
767 				glColor3f (newRgb[0] * brightFactor, newRgb[1] * brightFactor, newRgb[2] * brightFactor);
768 			glPushMatrix ();
769 			glMultMatrixf (billboardMat);
770 			if (dWireframe) {
771 				glDisable (GL_TEXTURE_2D);
772 				surface->draw_wireframe ();
773 				glEnable (GL_TEXTURE_2D);
774 			} else
775 				surface->draw ();
776 			glPopMatrix ();
777 			glDisable (GL_TEXTURE_GEN_S);
778 			glDisable (GL_TEXTURE_GEN_T);
779 		}
780 	}
781 }
782 
hack_reshape(xstuff_t * XStuff)783 void hack_reshape (xstuff_t * XStuff)
784 {
785 	glViewport (0, 0, XStuff->windowWidth, XStuff->windowHeight);
786 
787 	glMatrixMode (GL_PROJECTION);
788 	glLoadIdentity ();
789 	gluPerspective (60.0, float (XStuff->windowWidth) / float (XStuff->windowHeight), 0.1, 10000.0f);
790 }
791 
hack_init(xstuff_t * XStuff)792 void hack_init (xstuff_t * XStuff)
793 {
794 	int i, j;
795 	float x, y, temp;
796 	unsigned char *tex;
797 
798 	// Window initialization
799 	hack_reshape (XStuff);
800 
801 	glDisable (GL_DEPTH_TEST);
802 	glEnable (GL_BLEND);
803 	glLightModeli (GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
804 
805 	// Clear the buffers and test for type of buffer swapping
806 	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
807 	glXSwapBuffers (XStuff->display, XStuff->window);	// wglSwapLayerBuffers(hdc, WGL_SWAP_MAIN_PLANE);
808 	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
809 	unsigned char pixel[1] = { 255 };
810 
811 	glRasterPos2i (0, 0);
812 	glDrawPixels (1, 1, GL_RED, GL_UNSIGNED_BYTE, pixel);
813 	glXSwapBuffers (XStuff->display, XStuff->window);	// wglSwapLayerBuffers(hdc, WGL_SWAP_MAIN_PLANE);
814 	glReadPixels (0, 0, 1, 1, GL_RED, GL_UNSIGNED_BYTE, pixel);
815 
816 	if (pixel[0] == 0) {	// Color was swapped out of the back buffer
817 		pfd_swap_exchange = 1;
818 	} else {		// Color remains in back buffer
819 		pfd_swap_exchange = 0;
820 	}
821 
822 	// Init light texture
823 	for (i = 0; i < LIGHTSIZE; i++) {
824 		for (j = 0; j < LIGHTSIZE; j++) {
825 			x = float (i - LIGHTSIZE / 2) / float (LIGHTSIZE / 2);
826 			y = float (j - LIGHTSIZE / 2) / float (LIGHTSIZE / 2);
827 			temp = 1.0f - float (sqrt ((x * x) + (y * y)));
828 
829 			if (temp > 1.0f)
830 				temp = 1.0f;
831 			if (temp < 0.0f)
832 				temp = 0.0f;
833 			lightTexture[i][j] = char (255.0f * temp * temp);
834 		}
835 	}
836 
837 	glBindTexture (GL_TEXTURE_2D, 1);
838 	glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
839 	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
840 	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
841 	gluBuild2DMipmaps (GL_TEXTURE_2D, 1, LIGHTSIZE, LIGHTSIZE, GL_LUMINANCE, GL_UNSIGNED_BYTE, lightTexture);
842 	glBindTexture (GL_TEXTURE_2D, 2);
843 	glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
844 	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
845 	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
846 
847 	LOAD_TEXTURE (tex, spheremap, spheremap_compressedsize, spheremap_size)
848 		gluBuild2DMipmaps (GL_TEXTURE_2D, 3, TEXSIZE, TEXSIZE, GL_RGB, GL_UNSIGNED_BYTE, tex);
849 	FREE_TEXTURE (tex)
850 
851 		glTexGeni (GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
852 	glTexGeni (GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
853 	glEnable (GL_TEXTURE_2D);
854 
855 	// Initialize light display list
856 	glNewList (1, GL_COMPILE);
857 	glBindTexture (GL_TEXTURE_2D, 1);
858 	glBegin (GL_TRIANGLES);
859 	glTexCoord2f (0.0f, 0.0f);
860 	glVertex3f (-0.5f, -0.5f, 0.0f);
861 	glTexCoord2f (1.0f, 0.0f);
862 	glVertex3f (0.5f, -0.5f, 0.0f);
863 	glTexCoord2f (1.0f, 1.0f);
864 	glVertex3f (0.5f, 0.5f, 0.0f);
865 	glTexCoord2f (0.0f, 0.0f);
866 	glVertex3f (-0.5f, -0.5f, 0.0f);
867 	glTexCoord2f (1.0f, 1.0f);
868 	glVertex3f (0.5f, 0.5f, 0.0f);
869 	glTexCoord2f (0.0f, 1.0f);
870 	glVertex3f (-0.5f, 0.5f, 0.0f);
871 	glEnd ();
872 	glEndList ();
873 
874 	// Initialize particles
875 	elist = new emitter[dEmitters];
876 	alist = new attracter[dAttracters];
877 	ilist = new ion[dIons];
878 
879 	// Initialize surface
880 	if (dSurface) {
881 		volume = new impCubeVolume;
882 		volume->init (50, 50, 50, 35.0f);
883 		volume->function = surfaceFunction;
884 		surface = volume->getSurface();
885 
886 		spheres = new impSphere[dEmitters + dAttracters];
887 
888 		float sphereScaleFactor = 1.0f / sqrtf (double (2 * dEmitters + dAttracters));
889 		for (i = 0; i < dEmitters; i++)
890 			spheres[i].setThickness (400.0f * sphereScaleFactor);
891 		for (i = 0; i < dAttracters; i++)
892 			spheres[i + dEmitters].setThickness (200.0f * sphereScaleFactor);
893 	}
894 }
895 
hack_cleanup(xstuff_t * XStuff)896 void hack_cleanup (xstuff_t * XStuff)
897 {
898 	glDeleteLists (1, 1);
899 }
900 
hack_handle_opts(int argc,char ** argv)901 void hack_handle_opts (int argc, char **argv)
902 {
903 	dIons = 1500;
904 	dSize = 10;
905 	dEmitters = 3;
906 	dAttracters = 3;
907 	dSpeed = 10;
908 	dCameraspeed = 10;
909 	dSurface = 1;
910 	dWireframe = 0;
911 	dBlur = 10;
912 
913 	while (1) {
914 		int c;
915 
916 #ifdef HAVE_GETOPT_H
917 		static struct option long_options[] = {
918 			{"help", 0, 0, 'h'},
919 			DRIVER_OPTIONS_LONG {"ions", 1, 0, 'i'},
920 			{"size", 1, 0, 's'},
921 			{"emitters", 1, 0, 'e'},
922 			{"attracters", 1, 0, 'a'},
923 			{"speed", 1, 0, 'S'},
924 			{"cameraspeed", 1, 0, 'c'},
925 			{"surface", 0, 0, 'u'},
926 			{"no-surface", 0, 0, 'U'},
927 			{"blur", 1, 0, 'b'},
928 			{"wireframe", 0, 0, 'w'},
929 			{"no-wireframe", 0, 0, 'W'},
930 			{0, 0, 0, 0}
931 		};
932 
933 		c = getopt_long (argc, argv, DRIVER_OPTIONS_SHORT "hi:s:e:a:S:c:uUb:wW", long_options, NULL);
934 #else
935 		c = getopt (argc, argv, DRIVER_OPTIONS_SHORT "hi:s:e:a:S:c:uUb:wW");
936 #endif
937 		if (c == -1)
938 			break;
939 
940 		switch (c) {
941 			DRIVER_OPTIONS_CASES case 'h':printf ("%s:"
942 #ifndef HAVE_GETOPT_H
943 							      " Not built with GNU getopt.h, long options *NOT* enabled."
944 #endif
945 							      "\n" DRIVER_OPTIONS_HELP "\t--ions/-i <arg>\n" "\t--size/-s <arg>\n" "\t--emitters/-e <arg>\n"
946 							      "\t--attracters/-a <arg>\n" "\t--speed/-S <arg>\n" "\t--cameraspeed/-c <arg>\n" "\t--surface/-u\n"
947 							      "\t--no-surface/-U\n" "\t--blur/-b <arg>\n" "\t--wireframe/-w\n" "\t--no-wireframe/-W\n", argv[0]);
948 			exit (1);
949 		case 'i':
950 			dIons = strtol_minmaxdef (optarg, 10, 0, 30000, 1, 1500, "--ions: ");
951 			break;
952 		case 's':
953 			dSize = strtol_minmaxdef (optarg, 10, 1, 100, 1, 10, "--size: ");
954 			break;
955 		case 'e':
956 			dEmitters = strtol_minmaxdef (optarg, 10, 1, 10, 1, 3, "--emitters: ");
957 			break;
958 		case 'a':
959 			dAttracters = strtol_minmaxdef (optarg, 10, 1, 10, 1, 3, "--attracters: ");
960 			break;
961 		case 'S':
962 			dSpeed = strtol_minmaxdef (optarg, 10, 1, 100, 1, 10, "--speed: ");
963 			break;
964 		case 'c':
965 			dCameraspeed = strtol_minmaxdef (optarg, 10, 0, 100, 1, 10, "--cameraspeed: ");
966 			break;
967 		case 'u':
968 			dSurface = 1;
969 			break;
970 		case 'U':
971 			dSurface = 0;
972 			break;
973 		case 'b':
974 			dBlur = strtol_minmaxdef (optarg, 10, 0, 100, 1, 10, "--blur: ");
975 			break;
976 		case 'w':
977 			dWireframe = 1;
978 			break;
979 		case 'W':
980 			dWireframe = 0;
981 			break;
982 		}
983 	}
984 }
985