/* yomiko6.c - Don Yang (uguu.org) 03/20/02 */ #include #include #include #include #include #include #define PAPER_SIZE 5.0 #define MAX_PAPER_COUNT 2048 #define MIN_PAPER_COUNT 128 #define DEFAULT_PAPER_COUNT 512 #define WORLD_SIZE (64.0 * PAPER_SIZE) #define DRIFT_PERIOD 10.0 #define DRIFT_DIST (PAPER_SIZE / 2.0) #define CUT_SPEED (4.0 * 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.0 * WORLD_SIZE) #define CAMERA_CYCLE1_TIME 50000 #define CAMERA_CYCLE2_TIME 20000 #define TIME_PER_FRAME 31 #define WINDOW_TITLE "yomiko" #define ROTATE_RESOLUTION 128 #define ROTATE_RESOLUTION_F 128.0 #define Interpolate(fmin, fmax, t) \ ((((float)((fmax) - (fmin))) * (float)(t)) + (fmin)) #define Random(rmin, rmax) \ Interpolate(rmin, rmax, ((float)(rand() & 4095)) / 4095.0) typedef struct { float v[4][3], n[3]; } Obj; typedef struct { float x, y, z, x0, y0, z0, vx, vy, vz; int rx, ry, rx0, ry0; } ObjM; float PI, CameraAngle, CameraPosition, dw, dh, dx[2], aa, ab, ac, ba, bb, bc, ca, cb, cc, cx, sx, cy, sy, t, u, v, f[11][4] = { {0.8, 0.8, 0.8, 1.0}, {0.8, 0.8, 1.0, 1.0}, {0.8, 1.0, 0.8, 1.0}, {1.0, 0.8, 0.8, 1.0}, {1.0, 1.0, 0.8, 1.0}, {1.0, 0.8, 1.0, 1.0}, {0.8, 1.0, 1.0, 1.0}, {1.0, 1.0, 1.0, 1.0}, {0.0, 1.0, 1.0, 0.0}, {0.0,-1.0,-1.0, 0.0}, {0.0, 0.0, 0.0, 0.0} }, vertices[4][3] = { {-PAPER_SIZE, 0.0, -PAPER_SIZE}, {-PAPER_SIZE, 0.0, PAPER_SIZE}, { PAPER_SIZE, 0.0, PAPER_SIZE}, { PAPER_SIZE, 0.0, -PAPER_SIZE} }; int StartTime, CurrentTime, PreviousTime, TimeOffset, WindowWidth, WindowHeight, DrawOutline, PaperCount, AnimateStartTime, AnimateChangeTime, CutAngle, i, j, k; struct timeb z; Obj Poly[ROTATE_RESOLUTION][ROTATE_RESOLUTION], *r; ObjM Paper[MAX_PAPER_COUNT], Paper0[MAX_PAPER_COUNT], *p, *q; void (*AnimateStateFunc)(void); void Animate_TransitionToCut(void); void Wrap(void) { for(; p->x < -WORLD_SIZE; p->x += 2.0 * WORLD_SIZE); for(; p->z < -WORLD_SIZE; p->z += 2.0 * WORLD_SIZE); for(; p->x > WORLD_SIZE; p->x -= 2.0 * WORLD_SIZE); for(; p->z > WORLD_SIZE; p->z -= 2.0 * WORLD_SIZE); } void Cut(int count) { dx[0] = CUT_SPEED * Poly[ROTATE_RESOLUTION / 4][CutAngle].v[0][0]; dx[1] = CUT_SPEED * Poly[ROTATE_RESOLUTION / 4][CutAngle].v[0][2]; for(i = 0; i < count; i++) { p->x = p->x0 + dx[0] * t; p->z = p->z0 + dx[1] * t; Wrap(); p++; } } void Drift(int count) { dx[0] = sin(PI * t * DRIFT_PERIOD); dx[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; Wrap(); if( p->y < -WORLD_SIZE ) { p->y0 += 2.0 * WORLD_SIZE; p->x0 = Random(-WORLD_SIZE, WORLD_SIZE); p->z0 = Random(-WORLD_SIZE, WORLD_SIZE); p->vx = Random(-0.3 * WORLD_SIZE, 0.3 * WORLD_SIZE); p->vz = Random(-0.3 * WORLD_SIZE, 0.3 * WORLD_SIZE); p->vy = Random(-2.0 * WORLD_SIZE, -WORLD_SIZE); } if( (i & 2) != 0 ) { p->x += Poly[0][p->ry].v[0][0] * dx[i & 1] * DRIFT_DIST; p->z += Poly[0][p->ry].v[0][2] * dx[i & 1] * DRIFT_DIST; p->rx = ((int)(0.08 * dx[i & 1] * ROTATE_RESOLUTION) + ROTATE_RESOLUTION) % ROTATE_RESOLUTION; } else { p->x -= (Poly[0][p->ry].v[0][0] * dx[i & 1] * DRIFT_DIST); p->z -= (Poly[0][p->ry].v[0][2] * dx[i & 1] * DRIFT_DIST); p->rx = ((int)(-0.08 * dx[i & 1] * ROTATE_RESOLUTION) + ROTATE_RESOLUTION) % ROTATE_RESOLUTION; } p++; } } void Freeze(void) { p = Paper; 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++; } } void Animate(void) { i = 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 = (((float)((CurrentTime % CAMERA_CYCLE1_TIME) * 2)) / (float)CAMERA_CYCLE1_TIME) * PI; CameraPosition = sin( (((float)((CurrentTime % CAMERA_CYCLE2_TIME) * 2)) / (float)CAMERA_CYCLE2_TIME) * PI); t = (CurrentTime - AnimateStartTime) / (float)(AnimateChangeTime - AnimateStartTime); p = Paper; AnimateStateFunc(); glutPostRedisplay(); } void Animate_Drift(void) { if( CurrentTime > AnimateChangeTime ) { Drift(MAX_PAPER_COUNT); Freeze(); CutAngle = rand() % ROTATE_RESOLUTION; AnimateStartTime = CurrentTime; AnimateChangeTime = CurrentTime + 1000 + (rand() % ANIMATE_PHASE_ERROR); AnimateStateFunc = Animate_TransitionToCut; } else { Drift(PaperCount); } } void Animate_TransitionToDrift(void) { if( CurrentTime > AnimateChangeTime ) { Drift(MAX_PAPER_COUNT); Freeze(); AnimateStartTime = CurrentTime; AnimateChangeTime = CurrentTime + ANIMATE_PHASE_TIME * 2 + (rand() % ANIMATE_PHASE_ERROR); AnimateStateFunc = Animate_Drift; } else { Drift(PaperCount); u = t; t = -0.5 * t * t + t + 1.0; p = Paper0; Cut(PaperCount); p = Paper; q = Paper0; for(i = 0; i < PaperCount; i++) { p->x = Interpolate(q->x, p->x, u); p->y = Interpolate(q->y, p->y, u); p->z = Interpolate(q->z, p->z, u); if( (i & 1) != 0 ) p->rx = (int)Interpolate(q->rx0, 0, u); else p->rx = (int)Interpolate(q->rx0, ROTATE_RESOLUTION, u); p->rx %= ROTATE_RESOLUTION; if( (i & 2) != 0 ) p->ry = (int)Interpolate(q->ry0, p->ry0, u); else p->ry = (int)Interpolate(q->ry0, p->ry0 + ROTATE_RESOLUTION, u); p->ry %= ROTATE_RESOLUTION; p++; q++; } } } void Animate_Cut(void) { if( CurrentTime > AnimateChangeTime ) { Cut(MAX_PAPER_COUNT); Freeze(); memcpy(Paper0, Paper, MAX_PAPER_COUNT * sizeof(ObjM)); p = Paper; for(i = 0; i < MAX_PAPER_COUNT; i++) { p->vx = Random(-0.5 * WORLD_SIZE, 0.5 * WORLD_SIZE); p->vz = Random(-0.5 * WORLD_SIZE, 0.5 * WORLD_SIZE); p->vy = 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(PaperCount); } } void Animate_Still(void) { if( CurrentTime > AnimateChangeTime ) { AnimateStartTime = CurrentTime; AnimateChangeTime = CurrentTime + ANIMATE_PHASE_TIME + (rand() % ANIMATE_PHASE_ERROR); AnimateStateFunc = Animate_Cut; } } void Animate_TransitionToCut(void) { 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; } } void Render(void) { ftime(&z); PreviousTime = CurrentTime; CurrentTime = (z.time - TimeOffset) * 1000 + z.millitm; glDrawBuffer(GL_BACK); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, WindowWidth, WindowHeight); 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, f[5]); glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, f[6]); 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, f[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); 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++; } glEnable(GL_LIGHTING); } glutSwapBuffers(); glFlush(); } void Reshape(int w, int h) { WindowWidth = w; WindowHeight = h; if( w > h ) { dw = w / (float)h; dh = 1.0; } else { dw = 1.0; dh = h / (float)w; } glutPostRedisplay(); } void Quit(unsigned char c, int u, int v) { exit(EXIT_SUCCESS); } int main(int argc, char **argv) { glutInit(&argc, argv); PI = atan2(0.0, -1.0); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); glutSetWindow(glutCreateWindow(WINDOW_TITLE)); glutDisplayFunc(Render); glutIdleFunc(Animate); glutReshapeFunc(Reshape); glutKeyboardFunc(Quit); glEnable(GL_NORMALIZE); glEnable(GL_DEPTH_TEST); glShadeModel(GL_FLAT); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_DIFFUSE, f[7]); glLightfv(GL_LIGHT0, GL_SPECULAR, f[7]); glColor4fv(f[10]); r = &Poly[0][0]; for(i = 0; i < ROTATE_RESOLUTION; i++) { for(j = 0; j < ROTATE_RESOLUTION; j++) { memcpy(r->v, vertices, sizeof(float) * 4 * 3); cx = cos((2.0 * PI * i) / ROTATE_RESOLUTION_F); sx = sin((2.0 * PI * i) / ROTATE_RESOLUTION_F); cy = cos((2.0 * PI * j) / ROTATE_RESOLUTION_F); sy = sin((2.0 * PI * j) / 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(k = 0; k < 4; k++) { t = aa * r->v[k][0] + ab * r->v[k][1] + ac * r->v[k][2]; u = ba * r->v[k][0] + bb * r->v[k][1] + bc * r->v[k][2]; v = ca * r->v[k][0] + cb * r->v[k][1] + cc * r->v[k][2]; r->v[k][0] = t; r->v[k][1] = u; r->v[k][2] = v; } r->n[0] = ab; r->n[1] = bb; r->n[2] = cb; r++; } } DrawOutline = 0; PaperCount = DEFAULT_PAPER_COUNT; p = Paper; for(i = 0; i < MAX_PAPER_COUNT; i++) { p->x0 = Random(-WORLD_SIZE, WORLD_SIZE); p->y0 = Random(-WORLD_SIZE, WORLD_SIZE); p->z0 = Random(-WORLD_SIZE, WORLD_SIZE); p->vx = Random(-0.3 * WORLD_SIZE, 0.3 * WORLD_SIZE); p->vz = Random(-0.3 * WORLD_SIZE, 0.3 * WORLD_SIZE); p->vy = Random(-2.0 * WORLD_SIZE, -WORLD_SIZE); p->rx0 = p->rx = rand() % ROTATE_RESOLUTION; p->ry0 = p->ry = rand() % ROTATE_RESOLUTION; p++; } ftime(&z); srand(TimeOffset = z.time); CurrentTime = StartTime = z.millitm; PreviousTime = CurrentTime - TIME_PER_FRAME; CameraAngle = 0.0; AnimateStartTime = CurrentTime; AnimateChangeTime = CurrentTime + ANIMATE_PHASE_TIME + (rand() % ANIMATE_PHASE_ERROR); AnimateStateFunc = Animate_Drift; glutMainLoop(); return 0; }