/* yomiko1.c - Don Yang (uguu.org) 03/19/02 */ #include #include #include #include #include #include #include #define PAPER_SIZE 5.0f #define MAX_PAPER_COUNT 2048 #define MIN_PAPER_COUNT 128 #define DEFAULT_PAPER_COUNT 512 #define WORLD_SIZE (64.0f * PAPER_SIZE) #define DRIFT_PERIOD 10.0 #define DRIFT_DIST (PAPER_SIZE / 2.0) #define CUT_SPEED (4.0f * WORLD_SIZE) #define ANIMATE_PHASE_TIME 12000 #define ANIMATE_PHASE_ERROR 1024 #define PERSP_SCALE 1.0 #define PERSP_NEAR 10.0 #define PERSP_FAR 2000.0 #define CAMERA_DISTANCE (2.0f * WORLD_SIZE) #define CAMERA_CYCLE1_TIME 50000 #define CAMERA_CYCLE2_TIME 20000 #define TIME_PER_FRAME 31 #define WINDOW_WIDTH 640 #define WINDOW_HEIGHT 480 #define WINDOW_POS_X 100 #define WINDOW_POS_Y 100 #define WINDOW_TITLE "yomiko" #define ROTATE_RESOLUTION 128 #define ROTATE_RESOLUTION_F 128.0 #define Interpolate(fmin, fmax, t) \ ((((double)((fmax) - (fmin))) * (double)(t)) + (fmin)) #define Random(rmin, rmax) \ Interpolate(rmin, rmax, ((double)(rand() & 4095)) / 4095.0) typedef struct { GLfloat v[4][3], n[3]; } Obj; typedef struct { GLfloat x, y, z; GLfloat x0, y0, z0; GLfloat vx, vy, vz; int rx, ry, rx0, ry0; } ObjM; static double PI, CameraAngle, CameraPosition; static long StartTime, CurrentTime, PreviousTime, TimeOffset; static int Window, DrawOutline; static Obj Poly[ROTATE_RESOLUTION][ROTATE_RESOLUTION]; static ObjM Paper[MAX_PAPER_COUNT], Paper0[MAX_PAPER_COUNT]; static int PaperCount; static long AnimateStartTime, AnimateChangeTime; static int CutAngle; static void (*AnimateStateFunc)(void); static void Animate(void); static void Animate_TransitionToDrift(void); static void Animate_Drift(void); static void Animate_TransitionToCut(void); static void Animate_Still(void); static void Animate_Cut(void); static void Cut(ObjM *p, int count, double t); static void Drift(ObjM *p, int count, double t); static void Freeze(ObjM *p); static void Initialize(void); static void Render(void); static void Reshape(int w, int h); static void Rotate(Obj *obj, int rx, int ry); static void Quit(unsigned char c, int u, int v); int main(int argc, char **argv) { glutInit(&argc, argv); Initialize(); glutMainLoop(); return 0; } static void Animate(void) { int i; i = (int)(CurrentTime - PreviousTime - TIME_PER_FRAME); if( abs(i) > 4 ) { PaperCount -= i / 4; if( PaperCount < MIN_PAPER_COUNT ) PaperCount = MIN_PAPER_COUNT; if( PaperCount > MAX_PAPER_COUNT ) PaperCount = MAX_PAPER_COUNT; } if( i < TIME_PER_FRAME / 2 && PaperCount == MAX_PAPER_COUNT ) DrawOutline = 1; else DrawOutline = 0; CameraAngle = (((double)((CurrentTime % CAMERA_CYCLE1_TIME) * 2)) / (double)CAMERA_CYCLE1_TIME) * PI; CameraPosition = sin( (((double)((CurrentTime % CAMERA_CYCLE2_TIME) * 2)) / (double)CAMERA_CYCLE2_TIME) * PI); AnimateStateFunc(); glutSetWindow(Window); glutPostRedisplay(); } static void Animate_TransitionToDrift(void) { ObjM *p, *q; int i; double t; t = ((double)(CurrentTime - AnimateStartTime)) / ((double)(AnimateChangeTime - AnimateStartTime)); if( CurrentTime > AnimateChangeTime ) { Drift(Paper, MAX_PAPER_COUNT, t); Freeze(Paper); AnimateStartTime = CurrentTime; AnimateChangeTime = CurrentTime + ANIMATE_PHASE_TIME * 2 + (rand() % ANIMATE_PHASE_ERROR); AnimateStateFunc = Animate_Drift; } else { Cut(Paper0, PaperCount, -0.5 * t * t + t + 1.0); Drift(Paper, PaperCount, t); p = Paper; q = Paper0; for(i = 0; i < PaperCount; i++) { p->x = (GLfloat)Interpolate(q->x, p->x, t); p->y = (GLfloat)Interpolate(q->y, p->y, t); p->z = (GLfloat)Interpolate(q->z, p->z, t); if( (i & 1) != 0 ) p->rx = (int)Interpolate(q->rx0, 0, t); else p->rx = (int)Interpolate(q->rx0, ROTATE_RESOLUTION, t); p->rx %= ROTATE_RESOLUTION; if( (i & 2) != 0 ) p->ry = (int)Interpolate(q->ry0, p->ry0, t); else p->ry = (int)Interpolate(q->ry0, p->ry0 + ROTATE_RESOLUTION, t); p->ry %= ROTATE_RESOLUTION; p++; q++; } } } static void Animate_Drift(void) { double t; t = ((double)(CurrentTime - AnimateStartTime)) / ((double)(AnimateChangeTime - AnimateStartTime)); if( CurrentTime > AnimateChangeTime ) { Drift(Paper, MAX_PAPER_COUNT, t); Freeze(Paper); CutAngle = rand() % ROTATE_RESOLUTION; AnimateStartTime = CurrentTime; AnimateChangeTime = CurrentTime + 1000 + (rand() % ANIMATE_PHASE_ERROR); AnimateStateFunc = Animate_TransitionToCut; } else { Drift(Paper, PaperCount, t); } } static void Animate_TransitionToCut(void) { ObjM *p; double t; int i; t = ((double)(CurrentTime - AnimateStartTime)) / ((double)(AnimateChangeTime - AnimateStartTime)); p = Paper; for(i = 0; i < PaperCount; i++) { if( (i & 1) != 0 ) { p->rx = ((int)Interpolate(p->rx0, ROTATE_RESOLUTION / 4, t)) % ROTATE_RESOLUTION; } else { p->rx = ((int)Interpolate(p->rx0, 5 * ROTATE_RESOLUTION / 4, t)) % ROTATE_RESOLUTION; } if( (i & 2) != 0 ) { p->ry = ((int)Interpolate(p->ry0, CutAngle, t)) % ROTATE_RESOLUTION; } else { p->ry = ((int)Interpolate(p->ry0, CutAngle + ROTATE_RESOLUTION, t)) % ROTATE_RESOLUTION; } p++; } if( CurrentTime > AnimateChangeTime ) { p = Paper; for(i = 0; i < MAX_PAPER_COUNT; i++) { p->rx = p->rx0 = ROTATE_RESOLUTION / 4; p->ry = p->ry0 = CutAngle; p++; } AnimateStartTime = CurrentTime; AnimateChangeTime = CurrentTime + 1000; AnimateStateFunc = Animate_Still; } } static void Animate_Still(void) { if( CurrentTime > AnimateChangeTime ) { AnimateStartTime = CurrentTime; AnimateChangeTime = CurrentTime + ANIMATE_PHASE_TIME + (rand() % ANIMATE_PHASE_ERROR); AnimateStateFunc = Animate_Cut; } } static void Animate_Cut(void) { ObjM *p; double dtime; int i; dtime = ((double)(CurrentTime - AnimateStartTime)) / ((double)(AnimateChangeTime - AnimateStartTime)); if( CurrentTime > AnimateChangeTime ) { Cut(Paper, MAX_PAPER_COUNT, dtime); Freeze(Paper); memcpy(Paper0, Paper, MAX_PAPER_COUNT * sizeof(ObjM)); p = Paper; for(i = 0; i < MAX_PAPER_COUNT; i++) { p->vx = (GLfloat)Random(-0.5 * WORLD_SIZE, 0.5 * WORLD_SIZE); p->vz = (GLfloat)Random(-0.5 * WORLD_SIZE, 0.5 * WORLD_SIZE); p->vy = (GLfloat)Random(-2.0 * WORLD_SIZE, -1.0 * WORLD_SIZE); p->rx0 = 0; p->ry0 = rand() % ROTATE_RESOLUTION; p++; } AnimateStartTime = CurrentTime; AnimateChangeTime = CurrentTime + ANIMATE_PHASE_TIME + (rand() % ANIMATE_PHASE_ERROR); AnimateStateFunc = Animate_TransitionToDrift; } else { Cut(Paper, PaperCount, dtime); } } static void Cut(ObjM *p, int count, double t) { GLfloat dx, dz; int i; dx = CUT_SPEED * Poly[ROTATE_RESOLUTION / 4][CutAngle].v[0][0]; dz = CUT_SPEED * Poly[ROTATE_RESOLUTION / 4][CutAngle].v[0][2]; for(i = 0; i < count; i++) { p->x = p->x0 + (GLfloat)(dx * t); p->z = p->z0 + (GLfloat)(dz * t); while( p->x < -WORLD_SIZE ) p->x += 2.0 * WORLD_SIZE; while( p->x > WORLD_SIZE ) p->x -= 2.0 * WORLD_SIZE; while( p->z < -WORLD_SIZE ) p->z += 2.0 * WORLD_SIZE; while( p->z > WORLD_SIZE ) p->z -= 2.0 * WORLD_SIZE; p++; } } static void Drift(ObjM *p, int count, double t) { double d[2]; int i; d[0] = sin(PI * t * DRIFT_PERIOD); d[1] = sin(PI * t * DRIFT_PERIOD * 1.7); for(i = 0; i < count; i++) { p->x = p->x0 + p->vx * t * 3.0; p->y = p->y0 + p->vy * t * 3.0; p->z = p->z0 + p->vz * t * 3.0; while( p->x < -WORLD_SIZE ) p->x += 2.0 * WORLD_SIZE; while( p->x > WORLD_SIZE ) p->x -= 2.0 * WORLD_SIZE; while( p->z < -WORLD_SIZE ) p->z += 2.0 * WORLD_SIZE; while( p->z > WORLD_SIZE ) p->z -= 2.0 * WORLD_SIZE; if( p->y < -WORLD_SIZE ) { p->y0 += 2.0 * WORLD_SIZE; p->x0 = (GLfloat)Random(-WORLD_SIZE, WORLD_SIZE); p->z0 = (GLfloat)Random(-WORLD_SIZE, WORLD_SIZE); p->vx = (GLfloat)Random(-0.3 * WORLD_SIZE, 0.3 * WORLD_SIZE); p->vz = (GLfloat)Random(-0.3 * WORLD_SIZE, 0.3 * WORLD_SIZE); p->vy = (GLfloat)Random(-2.0 * WORLD_SIZE, -WORLD_SIZE); } if( (i & 2) != 0 ) { p->x += (GLfloat)(Poly[0][p->ry].v[0][0] * d[i & 1] * DRIFT_DIST); p->z += (GLfloat)(Poly[0][p->ry].v[0][2] * d[i & 1] * DRIFT_DIST); p->rx = ((int)(0.08 * d[i & 1] * ROTATE_RESOLUTION) + ROTATE_RESOLUTION) % ROTATE_RESOLUTION; } else { p->x -= (GLfloat)(Poly[0][p->ry].v[0][0] * d[i & 1] * DRIFT_DIST); p->z -= (GLfloat)(Poly[0][p->ry].v[0][2] * d[i & 1] * DRIFT_DIST); p->rx = ((int)(-0.08 * d[i & 1] * ROTATE_RESOLUTION) + ROTATE_RESOLUTION) % ROTATE_RESOLUTION; } p++; } } static void Freeze(ObjM *p) { int i; for(i = 0; i < MAX_PAPER_COUNT; i++) { p->x0 = p->x; p->y0 = p->y; p->z0 = p->z; p->rx0 = p->rx; p->ry0 = p->ry; p++; } } static void Initialize(void) { static GLfloat diffuse[4] = {1.0f, 1.0f, 1.0f, 1.0f}; static GLfloat specular[4] = {1.0f, 1.0f, 1.0f, 1.0f}; static GLfloat vertices[4][3] = { {-PAPER_SIZE, 0.0f, -PAPER_SIZE}, {-PAPER_SIZE, 0.0f, PAPER_SIZE}, { PAPER_SIZE, 0.0f, PAPER_SIZE}, { PAPER_SIZE, 0.0f, -PAPER_SIZE} }; int x, y; struct timeb t; PI = atan2(0.0, -1.0); srand((unsigned)time(NULL)); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT); glutInitWindowPosition(WINDOW_POS_X, WINDOW_POS_Y); Window = glutCreateWindow(WINDOW_TITLE); glutSetWindowTitle(WINDOW_TITLE); glutSetWindow(Window); glutDisplayFunc(Render); glutIdleFunc(Animate); glutReshapeFunc(Reshape); glutKeyboardFunc(Quit); glDisable(GL_BLEND); glDisable(GL_ALPHA_TEST); glEnable(GL_NORMALIZE); glEnable(GL_DEPTH_TEST); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glShadeModel(GL_FLAT); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, specular); glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); for(x = 0; x < ROTATE_RESOLUTION; x++) { for(y = 0; y < ROTATE_RESOLUTION; y++) { memcpy(Poly[x][y].v, vertices, sizeof(GLfloat) * 4 * 3); Rotate(&Poly[x][y], x, y); } } DrawOutline = 0; PaperCount = DEFAULT_PAPER_COUNT; for(x = 0; x < MAX_PAPER_COUNT; x++) { Paper[x].x0 = (GLfloat)Random(-WORLD_SIZE, WORLD_SIZE); Paper[x].y0 = (GLfloat)Random(-WORLD_SIZE, WORLD_SIZE); Paper[x].z0 = (GLfloat)Random(-WORLD_SIZE, WORLD_SIZE); Paper[x].vx = (GLfloat)Random(-0.3 * WORLD_SIZE, 0.3 * WORLD_SIZE); Paper[x].vz = (GLfloat)Random(-0.3 * WORLD_SIZE, 0.3 * WORLD_SIZE); Paper[x].vy = (GLfloat)Random(-2.0 * WORLD_SIZE, -WORLD_SIZE); Paper[x].rx0 = Paper[x].rx = rand() % ROTATE_RESOLUTION; Paper[x].ry0 = Paper[x].ry = rand() % ROTATE_RESOLUTION; } (void)ftime(&t); TimeOffset = (long)t.time; CurrentTime = StartTime = (long)t.millitm; PreviousTime = CurrentTime - TIME_PER_FRAME; CameraAngle = 0.0; AnimateStartTime = CurrentTime; AnimateChangeTime = CurrentTime + ANIMATE_PHASE_TIME + (rand() % ANIMATE_PHASE_ERROR); AnimateStateFunc = Animate_Drift; } static void Render(void) { static GLfloat ambient[8][4] = { {0.8f, 0.8f, 0.8f, 1.0f}, {1.0f, 0.8f, 0.8f, 1.0f}, {0.8f, 1.0f, 0.8f, 1.0f}, {1.0f, 1.0f, 0.8f, 1.0f}, {0.8f, 0.8f, 1.0f, 1.0f}, {1.0f, 0.8f, 1.0f, 1.0f}, {0.8f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f} }; static GLfloat position[4] = {0.0f, 1.0f, 1.0f, 0.0f}; static GLfloat direction[4] = {0.0f, -1.0f, -1.0f, 0.0f}; struct timeb t; double dw, dh; int w, h, i, j, k; ObjM *p; (void)ftime(&t); PreviousTime = CurrentTime; CurrentTime = (t.time - TimeOffset) * 1000 + t.millitm; glutSetWindow(Window); glDrawBuffer(GL_BACK); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, w = glutGet(GLUT_WINDOW_WIDTH), h = glutGet(GLUT_WINDOW_HEIGHT)); if( w > h ) { dw = (double)w / (double)h; dh = 1.0; } else { dw = 1.0; dh = (double)h / (double)w; } glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-dw * PERSP_SCALE, dw * PERSP_SCALE, -dh * PERSP_SCALE, dh * PERSP_SCALE, PERSP_NEAR, PERSP_FAR); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glLightfv(GL_LIGHT0, GL_POSITION, position); glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, direction); gluLookAt( CAMERA_DISTANCE * sin(CameraAngle), CAMERA_DISTANCE * 0.5 * CameraPosition, CAMERA_DISTANCE * cos(CameraAngle), 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); for(k = 0; k < 8; k++) { glLightfv(GL_LIGHT0, GL_AMBIENT, ambient[k]); glBegin(GL_QUADS); p = &Paper[k]; for(i = k; i < PaperCount; i += 8) { glNormal3fv(Poly[p->rx][p->ry].n); for(j = 0; j < 4; j++) { glVertex3f( Poly[p->rx][p->ry].v[j][0] + p->x, Poly[p->rx][p->ry].v[j][1] + p->y, Poly[p->rx][p->ry].v[j][2] + p->z); } p += 8; } glEnd(); } if( DrawOutline != 0 ) { glDisable(GL_LIGHTING); glColor4f(0.0f, 0.0f, 0.0f, 1.0f); p = Paper; for(i = 0; i < PaperCount; i++) { glBegin(GL_LINE_LOOP); for(j = 0; j < 4; j++) { glVertex3f( Poly[p->rx][p->ry].v[j][0] + p->x, Poly[p->rx][p->ry].v[j][1] + p->y, Poly[p->rx][p->ry].v[j][2] + p->z); } glEnd(); p++; } glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glEnable(GL_LIGHTING); } glutSwapBuffers(); glFlush(); } static void Reshape(int w, int h) { glutSetWindow(Window); glutPostRedisplay(); } static void Rotate(Obj *obj, int rx, int ry) { double aa, ab, ac, ba, bb, bc, ca, cb, cc, cx, sx, cy, sy, x, y, z; int i; cx = cos(2.0 * PI * ((double)rx) / ROTATE_RESOLUTION_F); sx = sin(2.0 * PI * ((double)rx) / ROTATE_RESOLUTION_F); cy = cos(2.0 * PI * ((double)ry) / ROTATE_RESOLUTION_F); sy = sin(2.0 * PI * ((double)ry) / ROTATE_RESOLUTION_F); aa = cy; ab = -sx * sy; ac = cx * sy; ba = 0; bb = cx; bc = -sx; ca = -sy; cb = sx * cy; cc = cx * cy; for(i = 0; i < 4; i++) { x = aa * obj->v[i][0] + ab * obj->v[i][1] + ac * obj->v[i][2]; y = ba * obj->v[i][0] + bb * obj->v[i][1] + bc * obj->v[i][2]; z = ca * obj->v[i][0] + cb * obj->v[i][1] + cc * obj->v[i][2]; obj->v[i][0] = (GLfloat)x; obj->v[i][1] = (GLfloat)y; obj->v[i][2] = (GLfloat)z; } obj->n[0] = (GLfloat)ab; obj->n[1] = (GLfloat)bb; obj->n[2] = (GLfloat)cb; } static void Quit(unsigned char c, int u, int v) { glFlush(); exit(EXIT_SUCCESS); }