/* aya1.c - Don Yang (uguu.org) 10/03/06 */ #include #include #include #include #include #include #define WINDOW_TITLE "Aya" /* Window title */ #define PIXEL_ASPECT 1.0 /* Pixel width/height ratio */ #define TEXTURE_SIZE 64 /* Texture resolution */ #define SPRITE_SIZE 0.07 /* Sprite size (0.5 * width) */ #define MAX_EMITTER_AGE 1.5 #define MAX_RADIUS 2.1 /* Constants */ static double PI; static double ColorCurveR[] = { 0.9, 0.98, 1.0, 0.0, -1.0 }; static double ColorCurveGB[] = { 0.32, 0.98, 0.36, 0.12, 0.54, 0.12, 0.58, 0.98, 0.9, 0.98, 1.0, 0.0, -1.0 }; static double ColorCurveA[] = { 0.56, 0.99, 0.58, 0.70, 0.9, 0.0, -1.0 }; /* Graphics objects */ static unsigned char TextureImage[TEXTURE_SIZE * TEXTURE_SIZE * 4]; static GLuint TextureHandle; static GLuint SpriteHandle; /* Global clock, updated exactly once per frame */ static double CurrentTime; /* Emitter type. All sprites come from emitters */ typedef struct { double t0, x, y, r0, r1; } Emitter; static Emitter GlobalEmitter; #define INTERPOLATE(t, x0, x1) ((t) * ((x1) - (x0)) + (x0)) #define RANDOM(x0, x1) \ ((x0) + ((x1) - (x0)) * (double)(rand() & 0x3fff) / 16383.0) /* Prototypes */ static void Init(void); static void InitTexture(void); static void RenderFirstFrame(void); static void Render(void); static void Animate(void); static void Reshape(/*@unused@*/int w, /*@unused@*/int h); static void Quit(/*@unused@*/unsigned char c, /*@unused@*/int u, /*@unused@*/int v); static double StepFunc(double t, double *curve); static void UpdateClock(void); static void InitSprites(Emitter *emitter); static void DrawSprites(Emitter *emitter); int main(int argc, char **argv) { glutInit(&argc, argv); Init(); glutMainLoop(); return 0; } static void Init(void) { PI = atan2(0.0, -1.0); InitTexture(); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); glutSetWindow(glutCreateWindow(WINDOW_TITLE)); glutDisplayFunc(RenderFirstFrame); glutIdleFunc(Animate); glutReshapeFunc(Reshape); glutKeyboardFunc(Quit); } static void InitTexture(void) { double x, y, r, c; int i, j, k; unsigned char *p; p = memset(TextureImage, 0, TEXTURE_SIZE * TEXTURE_SIZE * 4); for(i = 0; i < TEXTURE_SIZE; i++) { x = ((double)i - (TEXTURE_SIZE / 2)) / (TEXTURE_SIZE / 2); for(j = 0; j < TEXTURE_SIZE; j++) { y = ((double)j - (TEXTURE_SIZE / 2)) / (TEXTURE_SIZE / 2); r = sqrt(x * x + y * y); /* red */ c = StepFunc(r, ColorCurveR); k = (int)(255 * c); if( k > 255 ) k = 255; *(p++) = (unsigned char)k; /* green + blue */ c = StepFunc(r, ColorCurveGB); k = (int)(255 * c); if( k > 255 ) k = 255; *(p++) = (unsigned char)k; *(p++) = (unsigned char)k; /* alpha */ c = StepFunc(r, ColorCurveA); k = (int)(255 * c); if( k > 255 ) k = 255; *(p++) = (unsigned char)k; } } } static void RenderFirstFrame(void) { /* Initialize clock */ UpdateClock(); srand((unsigned int)CurrentTime); /* Textures */ glGenTextures(1, &TextureHandle); glBindTexture(GL_TEXTURE_2D, TextureHandle); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEXTURE_SIZE, TEXTURE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)TextureImage); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); /* Sprites */ glNewList(SpriteHandle = glGenLists(1), GL_COMPILE); glBegin(GL_TRIANGLE_FAN); glTexCoord2d(0.0, 0.0); glVertex2d(-SPRITE_SIZE, -SPRITE_SIZE); glTexCoord2d(1.0, 0.0); glVertex2d( SPRITE_SIZE, -SPRITE_SIZE); glTexCoord2d(1.0, 1.0); glVertex2d( SPRITE_SIZE, SPRITE_SIZE); glTexCoord2d(0.0, 1.0); glVertex2d(-SPRITE_SIZE, SPRITE_SIZE); glEnd(); glEndList(); InitSprites(&GlobalEmitter); glutDisplayFunc(Render); Render(); } static void Render(void) { int width, height; UpdateClock(); glDrawBuffer(GL_BACK); glClear(GL_COLOR_BUFFER_BIT); /* Set projection */ width = glutGet(GLUT_WINDOW_WIDTH); height = glutGet(GLUT_WINDOW_HEIGHT); glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if( width > height ) { gluOrtho2D((-width * PIXEL_ASPECT) / height, (width * PIXEL_ASPECT) / height, -1.0, 1.0); } else { gluOrtho2D(-1.0, 1.0, -height / (width * PIXEL_ASPECT), height / (width * PIXEL_ASPECT)); } /* Draw objects */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); DrawSprites(&GlobalEmitter); glutSwapBuffers(); glFlush(); } static void Animate(void) { glutPostRedisplay(); } static void Reshape(/*@unused@*/int w, /*@unused@*/int h) { glutPostRedisplay(); } static void Quit(/*@unused@*/unsigned char c, /*@unused@*/int u, /*@unused@*/int v) { glFlush(); glDeleteLists(SpriteHandle, 1); glDeleteTextures(1, &TextureHandle); exit(EXIT_SUCCESS); } static double StepFunc(double t, double *curve) { double x0, y0, x1, y1; x0 = *curve++; y0 = *curve++; if( t < x0 ) return y0; for(;;) { x1 = *curve++; if( x1 < x0 ) break; y1 = *curve++; if( t < x1 ) return (y1 - y0) * (t - x0) / (x1 - x0) + y0; x0 = x1; y0 = y1; } return y0; } static void UpdateClock(void) { struct timeval t; gettimeofday(&t, NULL); CurrentTime = (double)t.tv_sec + (double)(t.tv_usec / 1000000.0); } static void InitSprites(Emitter *emitter) { emitter->t0 = CurrentTime; emitter->r0 = emitter->r1 = RANDOM(0.0, PI * 2.0); if( (rand() & 3) == 0 ) emitter->r1 += RANDOM(-PI / 3.0, PI / 3.0); emitter->x = RANDOM(-0.5, 0.5); emitter->y = RANDOM(-0.5, 0.5); } static void DrawSprites(Emitter *emitter) { double age = (CurrentTime - emitter->t0) / MAX_EMITTER_AGE; double ring_age, radius, angle, x, y; int i, j; for(i = 0; i < 23; i++) { ring_age = age - (i / 23.0); if( ring_age < 0.0 ) continue; radius = ring_age * MAX_RADIUS; for(j = 0; j < 32; j++) { angle = INTERPOLATE(ring_age, emitter->r0 + j * PI * 2.0 / 32.0, emitter->r1 + j * PI * 2.0 / 32.0); if( (i & 1) != 0 ) angle += PI * 2.0 / 64.0; x = radius * cos(angle) + emitter->x; y = radius * sin(angle) + emitter->y; glPushMatrix(); glTranslated(x, y, 0.0); glCallList(SpriteHandle); glPopMatrix(); } } if( age > 2.2 ) InitSprites(emitter); }