/* forest.c - Don Yang (uguu.org) Cell type interpretation: bits 0-4: plant type xxx00 = flower bits 2-3 = color other = tree bits 0-1 = size bit 1 = fruits bit 2 = round/cone bits 5-6: X jitter bits 7-8: Z jitter The flowers are pretty abstract looking, I know ^_^; Takes too much effort to draw better ones... 05/30/03 */ /*@ -realcompare @*/ #include"global.h" #include"forest.h" #include"util.h" /* Colors */ static const GLfloat BranchAmbient[4] = {0.1f, 0.1f, 0.0f, 1.0f}; static const GLfloat BranchDiffuse[4] = {0.3f, 0.2f, 0.1f, 1.0f}; static const GLfloat LeafAmbient[4] = {0.1f, 0.2f, 0.1f, 1.0f}; static const GLfloat LeafDiffuse[4] = {0.0f, 0.8f, 0.2f, 1.0f}; static const GLfloat FruitAmbient[4] = {0.15f, 0.1f, 0.1f, 1.0f}; static const GLfloat FruitDiffuse[4] = {0.0f, 0.3f, 0.0f, 1.0f}; static const GLfloat PetalAmbient[4][4] = { {0.2f, 0.1f, 0.1f, 1.0f}, {0.1f, 0.2f, 0.1f, 1.0f}, {0.1f, 0.1f, 0.2f, 1.0f}, {0.15f, 0.15f, 0.15f, 1.0f} }; static const GLfloat PetalDiffuse[4][4] = { {1.0f, 0.8f, 0.8f, 1.0f}, {0.8f, 1.0f, 0.8f, 1.0f}, {0.8f, 0.8f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f} }; static const GLfloat DSpecular[4] = {0.2f, 0.2f, 0.2f, 1.0f}; static const GLfloat DEmission[4] = {0.0f, 0.0f, 0.0f, 1.0f}; static const GLfloat NSpecular[4] = {0.0f, 0.0f, 0.0f, 1.0f}; static const GLfloat NEmission[4] = {0.0f, 0.0f, 0.02f, 1.0f}; /* Geometry parameters */ static GLfloat BaseJitter[4][4] = { {0.0f, 0.1f, -0.1f, 0.0f}, {0.1f, 0.3f, -0.1f, -0.3f}, {0.3f, 0.9f, -0.3f, -0.9f}, {0.4f, 1.2f, -0.4f, -1.2f} }; /* Display lists */ static GLuint ObjPlantBase = 0, ObjOutlineBase = 0; /* Local functions */ static void DrawBlob(double ax, double ay, double bx, double by, double cx, double cy); static void DrawBlobFruits(double ax, double ay, double bx, double by, double cx, double cy, int count); static void DrawCone(double r, double h, double y); static void DrawConeFruits(double r, double h, double y, int count); static void DrawFruit(double x, double y, double z, double nx, double ny, double nz); static void DrawPetals(double a1, double a2, double x, double y, double z, double r1, double r2, double r3); static void DrawStem(double ax, double ay, double az, double bx, double by, double bz, double r); static void DrawTrunk(double h1, double h2, double r1, double r2); static void InitLargeTiles(void); static void InitSmallTiles(void); /******************************************************* DrawForestOutline */ void DrawForestOutline(unsigned int type) { glTranslatef(BaseJitter[type & 3][(type >> 5) & 3], 0.0f, BaseJitter[type & 3][(type >> 7) & 3]); glCallList(ObjOutlineBase + (type & 31)); } /* DrawForestOutline() */ /********************************************************** DrawForestTile */ void DrawForestTile(unsigned int type) { glTranslatef(BaseJitter[type & 3][(type >> 5) & 3], 0.0f, BaseJitter[type & 3][(type >> 7) & 3]); glCallList(ObjPlantBase + (type & 31)); } /* DrawForestTile() */ /********************************************************* InitForestTiles */ void InitForestTiles(void) { ObjPlantBase = glGenLists(32); ObjOutlineBase = glGenLists(32); InitSmallTiles(); InitLargeTiles(); } /* InitForestTiles() */ /************************************************************ SetupForestD */ void SetupForestD(void) { glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, DSpecular); glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, DEmission); } /* SetupForestD() */ /************************************************************ SetupForestN */ void SetupForestN(void) { glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, NSpecular); glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, NEmission); } /* SetupForestN() */ /******************************************************* UninitForestTiles */ void UninitForestTiles(void) { if( ObjPlantBase != 0 ) glDeleteLists(ObjPlantBase, 32); if( ObjOutlineBase != 0 ) glDeleteLists(ObjOutlineBase, 32); } /* UninitForestTiles() */ /**************************************************************** DrawBlob */ static void DrawBlob(double ax, double ay, double bx, double by, double cx, double cy) { double controlx[5], controly[5]; double ox[TREE_RES2], oy[TREE_RES2], nx[TREE_RES2], ny[TREE_RES2]; double p1, p2, p3, p4, q1, q2, q3, q4, c0, c1, c2, c3, c4; int i, j; assert(bx > ax && bx > cx); assert(by > ay && by < (ay + cy) / 2.0); assert(cy > ay); /* Create profile curve */ controlx[0] = 0.0; controly[0] = ay; controlx[1] = ax; controly[1] = ay; controlx[2] = bx; controly[2] = by; controlx[3] = cx; controly[3] = cy; controlx[4] = 0.0; controly[4] = cy; for(i = 0; i < TREE_RES2; i++) { /* Expontiate */ p1 = 1 - (q1 = ((double)(i + 1)) / (TREE_RES2 + 1)); p4 = p1 * (p3 = p1 * (p2 = p1 * p1)); q4 = q1 * (q3 = q1 * (q2 = q1 * q1)); /* Curve points */ ox[i] = controlx[0] * p4 + controlx[1] * (c1 = p3 * q1 * 4.0) + controlx[2] * (c2 = p2 * q2 * 6.0) + controlx[3] * (c3 = p1 * q3 * 4.0) + controlx[4] * q4; oy[i] = controly[0] * p4 + controly[1] * c1 + controly[2] * c2 + controly[3] * c3 + controly[4] * q4; assert(ox[i] >= 0.0 && oy[i] >= ay && oy[i] <= cy); /* Curve normals */ ny[i] = - controlx[0] * (c0 = -4.0 * p3) - controlx[1] * (c1 = -12.0 * p2 * q1 + 4.0 * p3) - controlx[2] * (c2 = -12.0 * p1 * q2 + 12.0 * p2 * q1) - controlx[3] * (c3 = -4.0 * q3 + 12.0 * p1 * q2) - controlx[4] * (c4 = 4.0 * q3); nx[i] = controly[0] * c0 + controly[1] * c1 + controly[2] * c2 + controly[3] * c3 + controly[4] * c4; } /* Bottom cap */ glBegin(GL_TRIANGLE_FAN); glNormal3d(0.0,-1.0, 0.0); glVertex3d(0.0, ay, 0.0); for(i = 0; i < TREE_RES1; i++) { c0 = (double)(2 * i) * PI / TREE_RES1; p1 = cos(c0); q1 = sin(c0); glNormal3d(nx[0] * p1, ny[0], nx[0] * q1); glVertex3d(ox[0] * p1, oy[0], ox[0] * q1); } glNormal3d(nx[0], ny[0], 0.0); glVertex3d(ox[0], oy[0], 0.0); glEnd(); /* Middle */ for(i = 0; i < TREE_RES2 - 1; i++) { glBegin(GL_QUAD_STRIP); for(j = 0; j < TREE_RES1; j++) { c0 = (double)(2 * j) * PI / TREE_RES1; p1 = cos(c0); q1 = sin(c0); glNormal3d(nx[i] * p1, ny[i], nx[i] * q1); glVertex3d(ox[i] * p1, oy[i], ox[i] * q1); glNormal3d(nx[i + 1] * p1, ny[i + 1], nx[i + 1] * q1); glVertex3d(ox[i + 1] * p1, oy[i + 1], ox[i + 1] * q1); } glNormal3d(nx[i], ny[i], 0.0); glVertex3d(ox[i], oy[i], 0.0); glNormal3d(nx[i + 1], ny[i + 1], 0.0); glVertex3d(ox[i + 1], oy[i + 1], 0.0); glEnd(); } /* Top cap */ glBegin(GL_TRIANGLE_FAN); glNormal3d(0.0, 1.0, 0.0); glVertex3d(0.0, cy, 0.0); for(i = 0; i < TREE_RES1; i++) { c0 = (double)(-2 * i) * PI / TREE_RES1; p1 = cos(c0); q1 = sin(c0); glNormal3d(nx[TREE_RES2 - 1] * p1, ny[TREE_RES2 - 1], nx[TREE_RES2 - 1] * q1); glVertex3d(ox[TREE_RES2 - 1] * p1, oy[TREE_RES2 - 1], ox[TREE_RES2 - 1] * q1); } glNormal3d(nx[TREE_RES2 - 1], ny[TREE_RES2 - 1], 0.0); glVertex3d(ox[TREE_RES2 - 1], oy[TREE_RES2 - 1], 0.0); glEnd(); } /* DrawBlob() */ /********************************************************** DrawBlobFruits */ static void DrawBlobFruits(double ax, double ay, double bx, double by, double cx, double cy, int count) { double controlx[5], controly[5]; double ox, oy, nx, ny, t; double p1, p2, p3, p4, q1, q2, q3, q4, c0, c1, c2, c3, c4; int i; controlx[0] = 0.0; controly[0] = ay; controlx[1] = ax; controly[1] = ay; controlx[2] = bx; controly[2] = by; controlx[3] = cx; controly[3] = cy; controlx[4] = 0.0; controly[4] = cy; for(i = 0; i < count; i++) { /* Expontiate */ t = Random(0.0, 1.0); p1 = 1 - (q1 = t); p4 = p1 * (p3 = p1 * (p2 = p1 * p1)); q4 = q1 * (q3 = q1 * (q2 = q1 * q1)); /* Curve points */ ox = controlx[0] * p4 + controlx[1] * (c1 = p3 * q1 * 4.0) + controlx[2] * (c2 = p2 * q2 * 6.0) + controlx[3] * (c3 = p1 * q3 * 4.0) + controlx[4] * q4; oy = controly[0] * p4 + controly[1] * c1 + controly[2] * c2 + controly[3] * c3 + controly[4] * q4; /* Curve normals */ ny = - controlx[0] * (c0 = -4.0 * p3) - controlx[1] * (c1 = -12.0 * p2 * q1 + 4.0 * p3) - controlx[2] * (c2 = -12.0 * p1 * q2 + 12.0 * p2 * q1) - controlx[3] * (c3 = -4.0 * q3 + 12.0 * p1 * q2) - controlx[4] * (c4 = 4.0 * q3); nx = controly[0] * c0 + controly[1] * c1 + controly[2] * c2 + controly[3] * c3 + controly[4] * c4; t = Random(0.0, 2.0 * PI); DrawFruit(ox * cos(t), oy, ox * sin(t), nx * cos(t), ny, nx * sin(t)); } } /* DrawBlobFruits() */ /**************************************************************** DrawCone */ static void DrawCone(double r, double h, double y) { double a; int i; /* Side faces */ glBegin(GL_TRIANGLE_FAN); glNormal3d(0.0, 1.0, 0.0); glVertex3d(0.0, y + h, 0.0); for(i = 0; i < TREE_RES1; i++) { a = -2.0 * PI * i / (double)TREE_RES1; glNormal3d(h * cos(a), r, h * sin(a)); glVertex3d(r * cos(a), y, r * sin(a)); } glNormal3d(h, r, 0.0); glVertex3d(r, y, 0.0); glEnd(); #if 0 /* Same as above, but apex normals are not shared */ glBegin(GL_QUAD_STRIP); for(i = 0; i < TREE_RES1; i++) { a = 2.0 * PI * i / (double)TREE_RES1; glNormal3d(h * cos(a), r, h * sin(a)); glVertex3d(r * cos(a), y, r * sin(a)); glVertex3d(0.0, y + h, 0.0); } glNormal3d(h, r, 0.0); glVertex3d(r, y, 0.0); glVertex3d(0.0, y + h, 0.0); glEnd(); #endif /* Bottom face */ glBegin(GL_TRIANGLE_FAN); glNormal3d(0.0, -1.0, 0.0); glVertex3d(0.0, y, 0.0); for(i = 0; i < TREE_RES1; i++) { a = 2.0 * PI * i / (double)TREE_RES1; glVertex3d(r * cos(a), y, r * sin(a)); } glVertex3d(r, y, 0.0); glEnd(); } /* DrawCone() */ /********************************************************** DrawConeFruits */ static void DrawConeFruits(double r, double h, double y, int count) { double a, b, r0; int i; for(i = 0; i < count; i++) { a = Random(0.0, 2.0 * PI); b = Random(0.1, 0.9); r0 = b * r; DrawFruit(r0 * cos(a), y + (1.0 - b) * h, r0 * sin(a), h * cos(a), r, h * sin(a)); } } /* DrawConeFruits() */ /*************************************************************** DrawFruit */ static void DrawFruit(double x, double y, double z, double nx, double ny, double nz) { static const double size = 0.25; static const int resolution = 6; double ax, ay, az, bx, by, bz, a; int i; /* Find two vectors perpendicular to normal */ if( fabs(nx) > fabs(nz) ) Cross(nx, ny, nz, 0.0, 0.0, (nx > 0.0) ? -1.0 : 1.0, &ax, &ay, &az); else Cross(nx, ny, nz, (nz > 0.0) ? 1.0 : -1.0, 0.0, 0.0, &ax, &ay, &az); Cross(nx, ny, nz, ax, ay, az, &bx, &by, &bz); Normalize(&ax, &ay, &az, size); Normalize(&bx, &by, &bz, size); glBegin(GL_TRIANGLE_FAN); glNormal3d(nx, ny, nz); glVertex3d(x, y, z); for(i = 0; i < 8; i++) { a = 2.0 * PI * (double)i / (double)resolution; glVertex3d(x + cos(a) * ax + sin(a) * bx, y + cos(a) * ay + sin(a) * by, z + cos(a) * az + sin(a) * bz); } glVertex3d(x + ax, y + ay, z + az); glEnd(); } /* DrawFruit() */ /************************************************************** DrawPetals */ static void DrawPetals(double a1, double a2, double x, double y, double z, double r1, double r2, double r3) { double p[PETAL_COUNT][4][3], n[PETAL_COUNT][2][3]; double a, d1, d2, d3, dx, dy, dz; int i, j; d1 = Random(0.0, PI); d2 = PI / (double)PETAL_COUNT - 0.04; d3 = PI / (double)PETAL_COUNT - 0.1; dx = dy = 0.0; dz = 0.2; RotatePoint(a1, a2, &dx, &dy, &dz); x += dx; y += dy; z += dz; for(i = 0; i < PETAL_COUNT; i++) { a = 2.0 * PI * (double)i / (double)PETAL_COUNT + d1; p[i][0][0] = r1 * cos(a - d2); p[i][0][1] = r1 * sin(a - d2); p[i][0][2] = 0.0; p[i][1][0] = r1 * cos(a + d2); p[i][1][1] = r1 * sin(a + d2); p[i][1][2] = 0.0; p[i][2][0] = r2 * cos(a + d3); p[i][2][1] = r2 * sin(a + d3); p[i][2][2] = r3; p[i][3][0] = r2 * cos(a - d3); p[i][3][1] = r2 * sin(a - d3); p[i][3][2] = r3; n[i][0][0] = -cos(a); n[i][0][1] = -sin(a); n[i][0][2] = 1.0; n[i][1][0] = 0.1 * cos(a); n[i][1][1] = 0.1 * sin(a); n[i][1][2] = 1.0; for(j = 0; j < 4; j++) { RotatePoint(a1, a2, &p[i][j][0], &p[i][j][1], &p[i][j][2]); p[i][j][0] += x; p[i][j][1] += y; p[i][j][2] += z; } for(j = 0; j < 2; j++) RotatePoint(a1, a2, &n[i][j][0], &n[i][j][1], &n[i][j][2]); } glBegin(GL_QUADS); for(i = 0; i < PETAL_COUNT; i++) { glNormal3dv(n[i][0]); glVertex3dv(p[i][0]); glVertex3dv(p[i][1]); glNormal3dv(n[i][1]); glVertex3dv(p[i][2]); glVertex3dv(p[i][3]); } glEnd(); } /* DrawPetals() */ /**************************************************************** DrawStem */ static void DrawStem(double ax, double ay, double az, double bx, double by, double bz, double r) { static const int res = 3; double ux, uy, uz, vx, vy, vz, a; int i; Cross(bx - ax, by - ay, bz - az, 1.0, 0.0, 0.0, &ux, &uy, &uz); Cross(bx - ax, by - ay, bz - az, ux, uy, uz, &vx, &vy, &vz); a = r / sqrt(ux * ux + uy * uy + uz * uz); ux *= a; uy *= a; uz *= a; a = r / sqrt(vx * vx + vy * vy + vz * vz); vx *= a; vy *= a; vz *= a; glBegin(GL_QUAD_STRIP); for(i = 0; i < res; i++) { a = 2.0 * PI * (double)i / (double)res; glNormal3d(ux * cos(a) + vx * sin(a), uy * cos(a) + vy * sin(a), uz * cos(a) + vz * sin(a)); glVertex3d(bx + ux * cos(a) + vx * sin(a), by + uy * cos(a) + vy * sin(a), bz + uz * cos(a) + vz * sin(a)); glVertex3d(ax + ux * cos(a) + vx * sin(a), ay + uy * cos(a) + vy * sin(a), az + uz * cos(a) + vz * sin(a)); } glNormal3d(ux, uy, uz); glVertex3d(bx + ux, by + uy, bz + uz); glVertex3d(ax + ux, ay + uy, az + uz); glEnd(); } /* DrawStem() */ /*************************************************************** DrawTrunk */ static void DrawTrunk(double h1, double h2, double r1, double r2) { double a, nr, ny; int i; nr = h1 - h2; ny = r2 - r1; glBegin(GL_QUAD_STRIP); for(i = 0; i < TREE_RES1; i++) { a = 2.0 * PI * i / (double)TREE_RES1; glNormal3d(nr * cos(a), ny, nr * sin(a)); glVertex3d(r2 * cos(a), h2, r2 * sin(a)); glVertex3d(r1 * cos(a), h1, r1 * sin(a)); } glNormal3d(nr, ny, 0.0); glVertex3d(r2, h2, 0.0); glVertex3d(r1, h1, 0.0); glEnd(); } /* DrawTrunk() */ /********************************************************** InitLargeTiles */ static void InitLargeTiles(void) { static const int fruitcount = 6; double r1, r2, h, a, size; double ax, ay, bx, by, cx, cy; int i, j; for(i = 0; i < 32; i++) { if( (i & 3) == 0 ) continue; size = (double)(i & 3); if( (i & 4) == 0 ) { /* Round trees */ r1 = Random(0.4, 0.6); r2 = r1 + Random(0.0, 0.15); h = Random(0.7, size + 0.7); ax = Random(size + 0.51, size + 0.8); bx = Random(size + 1.0, size + 1.5); cx = Random(size + 0.3, size + 0.5); ay = Random(h - 0.2, h - 0.1); cy = Random(size + h + 2.0, size + h + 3.5); by = Random(ay + 0.1, ay + (cy - ay) / 3.0); /* Tree object */ glNewList(ObjPlantBase + i, GL_COMPILE); glMaterialfv(GL_FRONT, GL_AMBIENT, BranchAmbient); glMaterialfv(GL_FRONT, GL_DIFFUSE, BranchDiffuse); DrawTrunk(h, 0.0, r1, r2); glMaterialfv(GL_FRONT, GL_AMBIENT, LeafAmbient); glMaterialfv(GL_FRONT, GL_DIFFUSE, LeafDiffuse); DrawBlob(ax, ay, bx, by, cx, cy); if( (i & 3) > 1 ) { glMaterialfv(GL_FRONT, GL_AMBIENT, FruitAmbient); glMaterialfv(GL_FRONT, GL_DIFFUSE, FruitDiffuse); DrawBlobFruits(ax, ay, bx, by, cx, cy, fruitcount); } glEndList(); /* Tree outline */ r1 += TREE_OUTLINE; r2 += TREE_OUTLINE; ax += TREE_OUTLINE; bx += TREE_OUTLINE; cx += TREE_OUTLINE; ay -= TREE_OUTLINE; cy += TREE_OUTLINE; glNewList(ObjOutlineBase + i, GL_COMPILE); glBegin(GL_TRIANGLE_FAN); glNormal3d(0.0, -1.0, 0.0); for(j = 0; j < TREE_RES1; j++) { a = 2.0 * PI * j / (double)TREE_RES1; glVertex3d(r2 * cos(a), -TREE_OUTLINE, r2 * sin(a)); } glVertex3d(r2, -TREE_OUTLINE, 0.0); glEnd(); DrawTrunk(h + TREE_OUTLINE, -TREE_OUTLINE, r1, r2); DrawBlob(ax, ay, bx, by, cx, cy); glEndList(); } else /* (i & 4) != 0 */ { /* Cone trees */ r1 = Random(0.2, 0.6); r2 = r1 + Random(0.0, 0.1); h = Random(0.5, size + 0.5); ax = Random(size - 0.1, size + 0.3); bx = Random(size + 1.0, size + 5.0); /* Tree object */ glNewList(ObjPlantBase + i, GL_COMPILE); glMaterialfv(GL_FRONT, GL_AMBIENT, BranchAmbient); glMaterialfv(GL_FRONT, GL_DIFFUSE, BranchDiffuse); DrawTrunk(h, 0.0, r1, r2); glMaterialfv(GL_FRONT, GL_AMBIENT, LeafAmbient); glMaterialfv(GL_FRONT, GL_DIFFUSE, LeafDiffuse); DrawCone(ax, bx, h); if( (i & 3) > 1 ) { glMaterialfv(GL_FRONT, GL_AMBIENT, FruitAmbient); glMaterialfv(GL_FRONT, GL_DIFFUSE, FruitDiffuse); DrawConeFruits(ax, bx, h, fruitcount); } glEndList(); /* Tree outline */ r1 += TREE_OUTLINE; r2 += TREE_OUTLINE; ax += TREE_OUTLINE; bx += TREE_OUTLINE * 3.0; glNewList(ObjOutlineBase + i, GL_COMPILE); glBegin(GL_TRIANGLE_FAN); glNormal3d(0.0, -1.0, 0.0); for(j = 0; j < TREE_RES1; j++) { a = 2.0 * PI * j / (double)TREE_RES1; glVertex3d(r2 * cos(a), -TREE_OUTLINE, r2 * sin(a)); } glVertex3d(r2, -TREE_OUTLINE, 0.0); glEnd(); DrawTrunk(h + TREE_OUTLINE, -TREE_OUTLINE, r1, r2); DrawCone(ax, bx, h - TREE_OUTLINE); glEndList(); } } } /* InitLargeTiles() */ /********************************************************** InitSmallTiles */ static void InitSmallTiles(void) { double ax, ay, az, bx, by, bz, a1, a2; unsigned int i; for(i = 0; i < 32; i += 4) { ax = Random(-0.1, 0.1); ay = 0.0; az = Random(-0.1, 0.1); bx = Random(-0.3, 0.3); by = Random(1.8, 2.2); bz = Random(-0.3, 0.3); a1 = Random(0.0, 2.0 * PI); a2 = Random(-0.2 * PI, 0.05 * PI); /* Object */ glNewList(ObjPlantBase + i, GL_COMPILE); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, LeafAmbient); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, LeafDiffuse); DrawStem(ax, ay, az, bx, by, bz, 0.05); glDisable(GL_CULL_FACE); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, PetalAmbient[(i>>2) & 3]); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, PetalDiffuse[(i>>2) & 3]); DrawPetals(a1, a2, bx, by, bz, 0.3, 1.0, 0.2); glEnable(GL_CULL_FACE); glEndList(); /* No outline */ glNewList(ObjOutlineBase + i, GL_COMPILE); glEndList(); } } /* InitSmallTiles() */