/* * * Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like. Any license provided herein, whether implied or * otherwise, applies only to this software file. Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA 94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ * */ // // Implementation of cool backgrounds from Drew Olbrich // Modified by Gavin Bell // #include #include #include #include #include #include #include #include #include #include #include #include #include "Background.h" #define ABS(a) ((a) < 0.0 ? -(a) : (a)) #define CLAMP(a, b, c) ((b) > (a) ? (b) : ((a) > (c) ? (c) : (a))) #define SMOOTH(a) ((a)*(a)*(3.0 - 2.0*(a))) static const int HASH_SIZE=751; // two of my favorite prime numbers static const int HASH_VALUE=911; static const int32_t BIG_NOISE=100000; // noise repeats this often // // Constructor. The background is always the same number of rows and // columns (that number is the size argument that gets passed). // FlashBackground::FlashBackground(int size) { // // Must create hash table... // hash_table = new double[HASH_SIZE]; srand48(getpid()); int i; for (i = 0; i < HASH_SIZE; i++) hash_table[i] = drand48(); // // And must create the scene graph // meshSize = size; int nverts = (size+1)*(size+1); // Set up a 0.0 to 1.0 view SoOrthographicCamera *oc = new SoOrthographicCamera; // // The LEAVE_ALONE viewport mapping is used because we don't // really care if the background is squished/stretched, but it // MUST always fill up the entire viewport. // oc->viewportMapping = SoCamera::LEAVE_ALONE; oc->position.setValue(0.5, 0.5, 1); oc->height = 0.9; oc->aspectRatio = 1.0; oc->farDistance = 1001.0; oc->focalDistance = 999.0; // Defaults for other stuff are OK. sceneGraph->addChild(oc); coordinates = new SoCoordinate3; // // Pre-allocate enough room in the coordinates node for the // vertices // coordinates->point.insertSpace(0, nverts); sceneGraph->addChild(coordinates); colors = new SoBaseColor; colors->rgb.insertSpace(0, nverts); sceneGraph->addChild(colors); SoMaterialBinding *mb = new SoMaterialBinding; mb->value = SoMaterialBinding::PER_VERTEX_INDEXED; sceneGraph->addChild(mb); SoLightModel *lm = new SoLightModel; lm->model = SoLightModel::BASE_COLOR; sceneGraph->addChild(lm); SoPickStyle *ps = new SoPickStyle; ps->style = SoPickStyle::UNPICKABLE; sceneGraph->addChild(ps); SoQuadMesh *qm = new SoQuadMesh; qm->verticesPerRow = meshSize+1; qm->verticesPerColumn = meshSize+1; sceneGraph->addChild(qm); // // Pre-calculate some different shapes and colors. These are // linearly interpolated in a random order to get the nice // animation. It takes a significant time to calculate some of // the shape/color functions; pre-calculating them avoids annoying // jumps in the animation. // shapeTable = new SbVec3f*[NUM_SHAPES]; for (i = 0; i < NUM_SHAPES; i++) { shapeTable[i] = new SbVec3f[nverts]; calculateShape((Shape)i, shapeTable[i]); } schemeTable = new SbColor*[NUM_SHAPES]; for (i = 0; i < NUM_SCHEMES; i++) { schemeTable[i] = new SbColor[nverts]; calculateColor((Shade)(lrand48()%NUM_SHADES), (Scheme)i, schemeTable[i]); } // // Start out with a random shape and color // currentShape = (Shape)(lrand48()%NUM_SHAPES); currentScheme = (Scheme)(lrand48()%NUM_SCHEMES); nextShape = (Shape)(lrand48()%NUM_SHAPES); nextScheme = (Scheme)(lrand48()%NUM_SCHEMES); // // And start at time 0.0 // updateSceneShape(0.0); updateSceneColor(0.0); } FlashBackground::~FlashBackground() { // // Must get rid of all the stuff allocated or ref'ed in the // constructor. Note: the sceneGraph member variable is allocated // and freed in the Background base class. // delete[] hash_table; int i; for (i = 0; i < NUM_SHAPES; i++) { delete[] shapeTable[i]; } delete[] shapeTable; for (i = 0; i < NUM_SCHEMES; i++) { delete[] schemeTable[i]; } delete[] schemeTable; } // // Noise() - returns noise at integer boundaries // // Warning! This only works for positive values! // double FlashBackground::Noise(uint32_t x, uint32_t y, uint32_t z) { uint32_t index; double result; index = x; index = index*HASH_VALUE + y; index = index*HASH_VALUE + z; result = hash_table[index % HASH_SIZE]; // add a little high order noise to make it look more convincing index = HASH_VALUE + x*1000; index = index*HASH_VALUE + y*1000; index = index*HASH_VALUE + z*1000; result += hash_table[index % HASH_SIZE]; if (result >= 1.0) result -= 1.0; return result; } // // Noise() - the noise function // double FlashBackground::Noise(SbVec3f &p) { uint32_t x0, y0, z0; uint32_t x1, y1, z1; double sx, sy, sz; double tx, ty, tz; double n; double result; SbVec3f q = p; if (q[0] < 0.0) q[0] += BIG_NOISE; q[0] = fmod(q[0], (double) BIG_NOISE); if (q[1] < 0.0) q[1] += BIG_NOISE; q[1] = fmod(q[1], (double) BIG_NOISE); if (q[2] < 0.0) q[2] += BIG_NOISE; q[2] = fmod(q[2], (double) BIG_NOISE); x0 = (uint32_t) q[0]; y0 = (uint32_t) q[1]; z0 = (uint32_t) q[2]; x1 = (x0 + 1) % BIG_NOISE; y1 = (y0 + 1) % BIG_NOISE; z1 = (z0 + 1) % BIG_NOISE; sx = SMOOTH(q[0] - x0); sy = SMOOTH(q[1] - y0); sz = SMOOTH(q[2] - z0); tx = 1.0 - sx; ty = 1.0 - sy; tz = 1.0 - sz; n = Noise(x0, y0, z0); result = tx*ty*tz*n; n = Noise(x0, y0, z1); result += tx*ty*sz*n; n = Noise(x0, y1, z0); result += tx*sy*tz*n; n = Noise(x0, y1, z1); result += tx*sy*sz*n; n = Noise(x1, y0, z0); result += sx*ty*tz*n; n = Noise(x1, y0, z1); result += sx*ty*sz*n; n = Noise(x1, y1, z0); result += sx*sy*tz*n; n = Noise(x1, y1, z1); result += sx*sy*sz*n; return result; } // // Turbulence() - the turbulence function // // The variable 'octaves' holds the number of octaves of noise // we want to make the turbulence out of. // double FlashBackground::Turbulence(SbVec3f &p, int octaves) { double scale = 1.0; double inv_scale = 1.0; double result = 0.0; double max_total = 0.0; int i; SbVec3f q; for (i = 0; i < octaves; i++) { max_total += inv_scale; q = p * scale; result += Noise(q)*inv_scale; if (i != octaves - 1) { scale *= 2.0; inv_scale *= 0.5; } } result /= max_total; /* scale it to [0..1] */ return result; } void FlashBackground::shapeFunction(Shape shape, SbVec3f &xy) { double dx, dy; double l; double theta; switch (shape) { case SHAPE_CLOUDS : xy[1] = (0.5 - xy[1])*2.0; xy[1] = xy[1]*xy[1]*xy[1]; xy[1] = 1.0 - (xy[1]/2.0 + 0.5); break; case SHAPE_FLAT : break; case SHAPE_SWIRL : dx = 0.5 - xy[0]; dy = 0.5 - xy[1]; l = sqrt(dx*dx + dy*dy); if (dx != 0.0 || dy != 0.0) { theta = atan2(dy, dx); xy[0] = 0.5 + cos(theta + l*5.0)*l*1.5; xy[1] = 0.5 + sin(theta + l*5.0)*l*1.5; } break; case SHAPE_PINCH : dx = xy[0] - 0.5; dy = xy[1] - 0.5; l = sqrt(dx*dx + dy*dy); xy[0] = 0.5 + dx*l*l*4.0; xy[1] = 0.5 + dy*l*l*4.0; break; case SHAPE_WAVY : xy[1] += sin(xy[0]*15.0)*0.06; xy[1] = 0.5 + (xy[1] - 0.5)*1.3; break; case SHAPE_SCRUNCH : xy[1] = xy[1]*xy[1]*xy[1]; break; case SHAPE_WALLS : dx = (1.0 - ABS(xy[0] - 0.5)*2.0); dx = 1.0 - exp(dx); if (xy[1] != 1.0) xy[1] += xy[1]*dx*0.8; break; case SHAPE_PULL : dx = (xy[0] - 0.5)*2.0; dx *= 4.0; dx = 1.0/(1.0 + dx*dx); xy[1] += xy[1]*dx; break; case NUM_SHAPES: fprintf(stderr, "BLEUCH: NUM_SHAPES CASE REACHED\n"); break; } } double FlashBackground::shadeFunction(Shade shade, double i, double j) { double gray; double result; SbVec3f v; switch (shade) { case SHADE_BARS : j *= meshSize; gray = (meshSize - j)*120/meshSize + ((int) (j*4129) % 30); result = gray/255.0; break; case SHADE_STATIC : v.setValue(i*20.0, j*20.0, 0.0); result = drand48()*0.5 + 0.25; result *= (1.0 - j)*0.8; break; case SHADE_FOG : v.setValue(i*6.0, j*6.0, 0.0); result = Turbulence(v, 5); result *= (1.0 - j)*0.8; break; case SHADE_MARBLE : v.setValue(i*5.0, j*5.0, 0.0); result = sin(i*30.0 + 15.0*Turbulence(v, 5))*0.5 + 0.5; result = result*0.5 + 0.25; result *= (1.0 - j)*0.8; break; case SHADE_HAZY : j *= meshSize; gray = (meshSize - j)*120/meshSize + ((int) (j*4129) % 30); result = gray/255.0; v.setValue(i*4.5, j*6.0, 0.0); v[0] += v[1]/3.1; result *= Turbulence(v, 4); break; case NUM_SHADES: fprintf(stderr, "BLEUCH: NUM_SHADES CASE REACHED\n"); break; } return result; } void FlashBackground::colorFunction(Scheme cscheme, SbColor &c, double y) { switch (cscheme) { case COLOR_GRAY : break; case COLOR_NOISE : c[0] += (drand48()*2.0 - 1.0)*0.03; c[1] += (drand48()*2.0 - 1.0)*0.03; c[2] += (drand48()*2.0 - 1.0)*0.03; break; case COLOR_PURPLE : c[0] = c[0]; c[1] = SMOOTH(c[1]); c[2] = c[2]; break; case COLOR_SUNSET : c[0] = c[0]*3.0 - 0.5; c[1] = c[1]*c[1]*c[1]*2.5; c[2] = c[2]*(1.0 - y); break; case COLOR_ABYSS : c[0] = c[0]*c[0]*c[0]*3.0 - 0.5; c[1] = c[1]*c[1]*3.0 - 0.5; c[2] = c[2]*3.0 - 0.5; break; case NUM_SCHEMES: fprintf(stderr, "BLEUCH: NUM_SCHEMES CASE REACHED\n"); break; } c[0] = CLAMP(c[0], 0.0, 1.0); c[1] = CLAMP(c[1], 0.0, 1.0); c[2] = CLAMP(c[2], 0.0, 1.0); } void FlashBackground::calculateShape(Shape shape, SbVec3f *coords) { for (int i = 0; i < meshSize+1; i++) { double y = (double)i / (double)(meshSize); for (int j = 0; j < meshSize+1; j++) { int index = i*(meshSize+1)+j; double x = (double)j / (double)(meshSize); coords[index].setValue(x, y, -999.0); shapeFunction(shape, coords[index]); if (j > 0 && j < meshSize) coords[index][1] += drand48()*x*0.015; } } } void FlashBackground::calculateColor(Shade shade, Scheme cscheme, SbColor *clrs) { for (int i = 0; i < meshSize+1; i++) { double y = (double)i / (double)(meshSize); for (int j = 0; j < meshSize+1; j++) { int index = i*(meshSize+1)+j; double x = (double)j / (double)(meshSize); double gray = shadeFunction(shade, x, y); clrs[index][0] = gray; clrs[index][1] = gray; clrs[index][2] = gray; colorFunction(cscheme, clrs[index], 1.0 - x); } } } // // Once we figure out which two shapes we are interpolating // (currentShape and the shape after currentShape) these two routines // actually do the interpolation. If we wanted to speed this up, // these are the best candidates for optimization effort. // void FlashBackground::updateSceneShape(double t) { SbVec3f *coords = coordinates->point.startEditing(); for (int i = 0; i < meshSize+1; i++) { for (int j = 0; j < meshSize+1; j++) { int index = i*(meshSize+1)+j; coords[index] = (1.0 - t) * shapeTable[currentShape][index] + t * shapeTable[nextShape][index]; } } coordinates->point.finishEditing(); } void FlashBackground::updateSceneColor(double t) { SbColor *clrs = colors->rgb.startEditing(); for (int i = 0; i < meshSize+1; i++) { for (int j = 0; j < meshSize+1; j++) { int index = i*(meshSize+1)+j; SbVec3f workAround = (1.0 - t) * schemeTable[currentScheme][index] + t * schemeTable[nextScheme][index]; clrs[index] = workAround.getValue(); } } colors->rgb.finishEditing(); } // // This is called repeatedly to animate between the various shape and // color schemes. It is assumed that time does not go backwards! If // real-world time is fed directly to this function, shapes will // change at a rate of one per second. You can multiply or divide to // get faster or slower changes (I find a change every 8 seconds to be // pretty nice). // void FlashBackground::animateShape(double time) { static double lastTime = (-1.0); if (lastTime == (-1.0)) { lastTime = time; } double t = time - lastTime; while (t > 1.0) { // Choose a new next shape currentShape = nextShape; nextShape = (Shape)(lrand48()%NUM_SHAPES); t -= 1.0; lastTime += 1.0; } updateSceneShape(t); } void FlashBackground::animateColor(double time) { static double lastTime = (-1.0); if (lastTime == (-1.0)) { lastTime = time; } double t = time - lastTime; while (t > 1.0) { // Choose a new next color scheme currentScheme = nextScheme; nextScheme = (Scheme)(lrand48()%NUM_SCHEMES); t -= 1.0; lastTime += 1.0; } updateSceneColor(t); }