Beispiel #1
0
//-----------------------------------------------------------------------------
// set day or night
void Landscape::setDayLight(
  BOOL daylight)
{
  m_daylight = daylight;

  m_sky->enableFog(true);
  if (m_daylight)
  {
    m_lightColor = mgPoint3(0.8, 0.8, 0.8);
    m_fogColor = mgPoint3(0.8, 0.8, 0.8);
  }
  else 
  {
    m_lightColor = mgPoint3(0.2, 0.2, 0.2);
    m_fogColor = mgPoint3(0.2, 0.2, 0.2);
  }

  m_sky->enableSkyBox(m_daylight);
  m_sky->enableSun(m_daylight);

  m_sky->enableStars(!m_daylight);
  m_sky->enableMoon(!m_daylight);

  m_eyeChanged = true;
}
Beispiel #2
0
//--------------------------------------------------------------
// constructor
Planet::Planet(
  const mgOptionsFile& options)
{
  // load the planet cubemap texture
  mgString xminName, xmaxName, yminName, ymaxName, zminName, zmaxName;
  options.getFileName("planet-xmin", options.m_sourceFileName, "", xminName);
  options.getFileName("planet-xmax", options.m_sourceFileName, "", xmaxName);
  options.getFileName("planet-ymin", options.m_sourceFileName, "", yminName);
  options.getFileName("planet-ymax", options.m_sourceFileName, "", ymaxName);
  options.getFileName("planet-zmin", options.m_sourceFileName, "", zminName);
  options.getFileName("planet-zmax", options.m_sourceFileName, "", zmaxName);

  m_surfaceTexture = loadCubeMap(xminName, xmaxName, yminName, ymaxName, zminName, zmaxName);

  // load the clouds cubemap texture
  options.getFileName("clouds-xmin", options.m_sourceFileName, "", xminName);
  options.getFileName("clouds-xmax", options.m_sourceFileName, "", xmaxName);
  options.getFileName("clouds-ymin", options.m_sourceFileName, "", yminName);
  options.getFileName("clouds-ymax", options.m_sourceFileName, "", ymaxName);
  options.getFileName("clouds-zmin", options.m_sourceFileName, "", zminName);
  options.getFileName("clouds-zmax", options.m_sourceFileName, "", zmaxName);

  m_cloudsTexture = loadCubeMap(xminName, xmaxName, yminName, ymaxName, zminName, zmaxName);

  compileShader(options);

  // create the vertex buffer
  glGenVertexArrays(1, &m_vertexArray);
  glBindVertexArray(m_vertexArray);

  glGenBuffers(1, &m_vertexBuffer);
  glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);

  glEnableVertexAttribArray(0);
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexPlanet), (const GLvoid*) offsetof(VertexPlanet, m_px));
  glEnableVertexAttribArray(1);
  glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexPlanet), (const GLvoid*) offsetof(VertexPlanet, m_mx));

  m_eyePt = mgPoint3(3500, 0, 0);
  m_eyeRotX = 0.0;
  m_eyeRotY = 90.0;
  m_eyeMatrix.rotateYDeg(m_eyeRotY);
  m_eyeMatrix.rotateXDeg(m_eyeRotX);

  m_specularColor = mgPoint3(0.4, 0.4, 0.4);
  m_lightColor = mgPoint3(0.6, 0.6, 0.6);
  m_lightAmbient = mgPoint3(0.2, 0.2, 0.2);
  m_matColor = mgPoint4(1, 1, 1, 1);
}
Beispiel #3
0
//--------------------------------------------------------------
// constructor
SolarSystem::SolarSystem(
  const mgOptionsFile& options)
{
  m_planetDayLen = options.getDouble("planetDayLen", 600.0); // seconds
  if (m_planetDayLen == 0.0)
    m_planetDayLen = 1e10;  // infinity

  m_moonDayLen = options.getDouble("moonDayLen", 300.0); // seconds
  if (m_moonDayLen == 0.0)
    m_moonDayLen = 1e10;  // infinity

  m_ringDayLen = options.getDouble("ringDayLen", 200.0);  // seconds
  if (m_ringDayLen == 0.0)
    m_ringDayLen = 1e10;  // infinity

  m_moonMonthLen = options.getDouble("moonMonthLen", 1000.0);  // seconds
  if (m_moonMonthLen == 0.0)
    m_moonMonthLen = 1e10;  // infinity

  m_beltMonthLen = options.getDouble("beltMonthLen", 1000.0);  // seconds
  if (m_beltMonthLen == 0.0)
    m_beltMonthLen = 1e10;  // infinity

  m_planet = new Planet(options, PLANET_RADIUS);
//  m_belt = new Belt(options, BELT_RADIUS);
  m_moon = new Moon(options, MOON_RADIUS);
  m_ring = new Ring(options, RING_RADIUS, RING_WALL_HEIGHT, RING_WALL_WIDTH);

  m_planetDay = 90.0;
  m_moonDay = 0.0;
  m_ringDay = 0.0;

  m_moonMonth = 30.0;
  m_beltMonth = 0.0;

  m_coordSystem = COORDS_PLANET;
  m_coordPosn = mgPoint3(0.0, 2.0, 0.0);
  m_eyeRotX = 0.0;
  m_eyeRotY = 0.0;
  m_eyeRotZ = 0.0;

  mgDebug("Planet radius = %g km (%g miles)", PLANET_RADIUS/1000.0, PLANET_RADIUS/1600.0);
  double val = 4*PI*PLANET_RADIUS*PLANET_RADIUS;
  mgDebug("Surface area of planet = %g million sq km (%g million sq mi)", val/1e12, (val/1e12)/1.609);

  mgDebug("Moon radius = %g km (%g miles)", MOON_RADIUS/1000.0, MOON_RADIUS/1600.0);
  val = 4*PI*MOON_RADIUS*MOON_RADIUS;
  mgDebug("Surface area of moon = %g million sq km (%g million sq mi)", val/1e12, (val/1e12)/1.609);

  mgDebug("Moon distance = %g km (%g miles)", MOON_DISTANCE/1000.0, MOON_DISTANCE/1600.0);

  mgDebug("Ring radius = %g km (%g miles)", RING_RADIUS/1000.0, RING_RADIUS/1600.0);
  val = 2*PI*RING_RADIUS*RING_WIDTH;
  mgDebug("Surface area of ring = %g million sq km (%g million sq mi)", val/1e12, (val/1e12)/1.609);
  mgDebug("Ring width = %g m (%g ft)", RING_WIDTH, RING_WIDTH*3.16);
  mgDebug("Ring wall height = %g m (%g ft)", RING_WALL_HEIGHT, RING_WALL_HEIGHT*3.16);
  mgDebug("Ring wall width = %g m (%g ft)", RING_WALL_WIDTH, RING_WALL_WIDTH*3.16);
}
Beispiel #4
0
//--------------------------------------------------------------
// initialize application
void SeaOfMemes::appInit()
{
  // get the important directories
  mgString shaderDir;
  m_options.getFileName("shaderDir", m_options.m_sourceFileName, "docs/shaders", shaderDir);
  mgString uiDir;
  m_options.getFileName("uiDir", m_options.m_sourceFileName, "docs/ui", uiDir);
  mgString fontDir;
  m_options.getFileName("fontDir", m_options.m_sourceFileName, "docs/fonts", fontDir);

  mgInitDisplayServices(shaderDir, fontDir);

  mgDisplay->setDPI(m_options.getInteger("dpi", 0));
  mgDisplay->setFOV(m_options.getDouble("FOV", 60.0));

  // load cursors
  loadCursor();

  setDeskMode(true);

  // take units from option
  m_unitsMetric = m_options.getBoolean("metricUnits", true);

  // create the sky
  m_matColor = mgPoint3(1, 1, 1);
  m_lightDir = mgPoint3(1.0, 0.25, 0.0);
  m_lightDir.normalize();

  m_sky = new NebulaSky(m_options);
  m_sky->setSunDir(m_lightDir);

  m_world = new SolarSystem(m_options);

  m_helpUI = new HelpUI(m_options);
  m_helpUI->setDebugApp(this);

  m_speedUI = new SpeedUI(m_options);
  m_speedUI->setUnits(m_unitsMetric ? UNITS_KM : UNITS_MILES);

  appCreateBuffers();

  // initialize movement
  initMovement();
  m_lastAnimate = 0.0;
}
Beispiel #5
0
//--------------------------------------------------------------
// return end of array
void mgPoint3Array::top(
  mgPoint3& elm) const
{
  if (m_elementCount == 0)
  {
    elm = mgPoint3(0, 0, 0);
    return;
  }
    
  elm = m_elements[m_elementCount-1];
}
Beispiel #6
0
//--------------------------------------------------------------
// return element of array
void mgPoint3Array::getAt(
  int index,
  mgPoint3& elm) const
{
  if (index < 0 || index >= m_elementCount)
  {
    elm = mgPoint3(0, 0, 0);
    return;
  }
  elm = m_elements[index];
}
Beispiel #7
0
//--------------------------------------------------------------
// constructor
Planet::Planet(
  const mgOptionsFile& options)
{
  // load the planet cubemap texture
  mgString xminName, xmaxName, yminName, ymaxName, zminName, zmaxName;
  options.getFileName("planet-xmin", options.m_sourceFileName, "", xminName);
  options.getFileName("planet-xmax", options.m_sourceFileName, "", xmaxName);
  options.getFileName("planet-ymin", options.m_sourceFileName, "", yminName);
  options.getFileName("planet-ymax", options.m_sourceFileName, "", ymaxName);
  options.getFileName("planet-zmin", options.m_sourceFileName, "", zminName);
  options.getFileName("planet-zmax", options.m_sourceFileName, "", zmaxName);

  m_surfaceTexture = loadCubeMap(xminName, xmaxName, yminName, ymaxName, zminName, zmaxName);

  // load the clouds cubemap texture
  options.getFileName("clouds-xmin", options.m_sourceFileName, "", xminName);
  options.getFileName("clouds-xmax", options.m_sourceFileName, "", xmaxName);
  options.getFileName("clouds-ymin", options.m_sourceFileName, "", yminName);
  options.getFileName("clouds-ymax", options.m_sourceFileName, "", ymaxName);
  options.getFileName("clouds-zmin", options.m_sourceFileName, "", zminName);
  options.getFileName("clouds-zmax", options.m_sourceFileName, "", zmaxName);

  m_cloudsTexture = loadCubeMap(xminName, xmaxName, yminName, ymaxName, zminName, zmaxName);

  compileShader(options);

  // create the vertex buffer
  glGenBuffers(1, &m_vertexBuffer);
  glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);

  m_eyePt = mgPoint3(3500, 0, 0);
  m_eyeRotX = 0.0;
  m_eyeRotY = 90.0;
  m_eyeMatrix.rotateYDeg(m_eyeRotY);
  m_eyeMatrix.rotateXDeg(m_eyeRotX);

  m_specularColor = mgPoint3(0.4, 0.4, 0.4);
  m_lightColor = mgPoint3(0.6, 0.6, 0.6);
  m_lightAmbient = mgPoint3(0.2, 0.2, 0.2);
  m_matColor = mgPoint4(1, 1, 1, 1);
}
Beispiel #8
0
//--------------------------------------------------------------
// remove from end of array
void mgPoint3Array::pop(
  mgPoint3& elm)
{
  if (m_elementCount == 0)
  {
    elm = mgPoint3(0, 0, 0);
    return;
  }
    
  elm = m_elements[m_elementCount-1];
  m_elementCount--;
}
Beispiel #9
0
//--------------------------------------------------------------
// set element of array
void mgPoint3Array::setAt(
  int index, 
  const mgPoint3& elm)
{
  grow(index);
  // pad to position of new element
  for (int i = m_elementCount; i < index; i++)
    m_elements[i] = mgPoint3(0, 0, 0);
    
  // set new element
  m_elements[index] = elm;
  m_elementCount = max(m_elementCount, index+1);
}
Beispiel #10
0
//--------------------------------------------------------------
// add a branch
void Colonization::addBranch(
  const mgPoint3& pt,
  int parent)
{
  Branch* branch = new Branch();
  branch->m_pt = pt;
  branch->m_parent = parent;
  branch->m_growDir = mgPoint3(0, 0, 0);
  branch->m_growCount = 0;
  branch->m_area = TWIG_AREA;

  m_branches.add(branch);
}
Beispiel #11
0
//--------------------------------------------------------------
// constructor
ChunkWorld::ChunkWorld(
  const mgOptionsFile& options)
{
  BrickBlob::staticInit();
  loadShaders();

  // read the brick set to use
  mgString fileName;
  options.getFileName("brickSet", options.m_sourceFileName, "bricks.xml", fileName);

  BrickSetFile brickSetFile(fileName);
  readBrickSet(brickSetFile);
  loadWaterTexture(brickSetFile);

  m_textureArray = mgDisplay->loadTextureArray(m_textureFiles);

  m_displayMemLimit = options.getInteger("displayMemory", 200); 
  m_displayMemLimit *= 1024*1024; // megabytes

  m_systemMemLimit = options.getInteger("systemMemory", 400); 
  m_systemMemLimit *= 1024*1024; // megabytes

  m_threadCount = options.getInteger("threadCount", 1); 
#ifdef EMSCRIPTEN
  m_threadCount = 0;
#endif
  m_viewDistance = options.getInteger("viewDistance", 300);

  // since the view list goes up as the cube of distance, limit it
  m_viewDistance = min(450, m_viewDistance);

  m_sortCount = options.getInteger("sortCount", 5);

  options.getPoint("torchColor", mgPoint3(0.5, 0.5, 0.1), m_torchColor);

  mgDebug("thread count: %d", m_threadCount);

  m_viewList = NULL;

  createViewList(m_viewDistance);
  createHorizon();
  createWater();

  m_displayMemUsed = 0;
  m_systemMemUsed = 0;
  m_shutdown = false;
  m_chunksChanged = false;

  createWorkers();
}
Beispiel #12
0
//-----------------------------------------------------------------------------
// call a function
void TestCube::debugCallFunction(
  const char* funcName,
  mgStringArray& args,
  mgString& reply) 
{
  // process function call
  if (_stricmp(funcName, "reset") == 0)
  {
    m_eyePt = mgPoint3(0, 2.5, -3.5);
    m_eyeRotX = 0.0;
    m_eyeRotY = 0.0;
    m_eyeRotZ = 0.0;
    m_eyeChanged = true;
    reply = "ok";
  }

  else MovementApp::debugCallFunction(funcName, args, reply);
}
Beispiel #13
0
//--------------------------------------------------------------------
// initialize movement state
void SeaOfMemes::initMovement()
{
  m_forwardSince = INVALID_TIME;
  m_backwardSince = INVALID_TIME;
  m_leftSince = INVALID_TIME;
  m_rightSince = INVALID_TIME;
  m_upSince = INVALID_TIME;
  m_downSince = INVALID_TIME;
  m_turnLeftSince = INVALID_TIME;
  m_turnRightSince = INVALID_TIME;

  m_turnSpeed = 120.0/1000;             // degrees per ms
  m_landingMode = false;
  m_targetSpeed = MAX_AVATAR_SPEED;
  m_avatarSpeed = m_targetSpeed;

  m_world->setCoordSystem(COORDS_SPACE);
  m_world->setCoordPosn(mgPoint3(PLANET_RADIUS*2, 0.0, 0.0));

//  m_world->setCoordSystem(COORDS_RING);
//  m_world->setCoordPosn(mgPoint3(RING_RADIUS-1000, 0.0, 0.0));

//  m_world->setCoordSystem(COORDS_PLANET);
//  double angle = 45*PI/180;
//  double ht = PLANET_RADIUS+1000.0;
//  m_world->setCoordPosn(mgPoint3(ht*sin(angle), ht*cos(angle), 0.0));

//  m_world->setCoordSystem(COORDS_MOON);
//  double angle = 45*PI/180;
//  double ht = MOON_RADIUS+1000.0;
//  m_world->setCoordPosn(mgPoint3(ht*sin(angle), ht*cos(angle), 0.0));

  m_world->setEyeAngle(0.0, 0.0);

  if (m_speedUI != NULL)
  {
    m_speedUI->setSpeed(m_avatarSpeed);
    m_speedUI->setLanding(false);
  }

  m_eyeChanged = true;
}
Beispiel #14
0
//--------------------------------------------------------------------
// initialize movement state
void GuiTestAll::initMovement()
{
  m_forwardSince = INVALID_TIME;
  m_backwardSince = INVALID_TIME;
  m_leftSince = INVALID_TIME;
  m_rightSince = INVALID_TIME;
  m_upSince = INVALID_TIME;
  m_downSince = INVALID_TIME;
  m_turnLeftSince = INVALID_TIME;
  m_turnRightSince = INVALID_TIME;

  m_turnSpeed = 120.0/1000;             // degrees per ms
  m_moveSpeed = 2.5/1000;               // units per ms // 100.0/1000;

  m_eyePt = mgPoint3(0, 2.5, -3.5);
  m_eyeRotX = 0.0;
  m_eyeRotY = 0.0;
  m_eyeRotZ = 0.0;

  m_eyeChanged = true;
}
Beispiel #15
0
//--------------------------------------------------------------
// initialize application
void Crafty::appInit()
{
  MovementApp::appInit();

  mgPlatform->setWindowTitle("Crafty, Part 58");

  // get lighting and material
  m_options.getPoint("matColor", mgPoint3(1,1,1), m_matColor);
  m_options.getPoint("sunlightColor", mgPoint3(0.8, 0.8, 0.8), m_sunlightColor);
  m_options.getPoint("moonlightColor", mgPoint3(0.1, 0.1, 0.1), m_moonlightColor);
  m_options.getPoint("torchlightColor", mgPoint3(0.5, 0.5, 0.5), m_torchlightColor);

  // since the view list goes up as the cube of distance, limit it
  int viewDistance = m_options.getInteger("viewDistance", 300);
  viewDistance = min(450, viewDistance);
  m_fogMaxDist = viewDistance;

  // create the sky
  m_lightDir = mgPoint3(0.0, 0.5, 1.0);
  m_sky = new StarrySky(m_options);
  m_sky->setMoonDir(m_lightDir);
  m_sky->setSunDir(m_lightDir);
  m_sky->setFogInten(1.0, 1.0);
  m_sky->setFogDist(1000.0, m_fogMaxDist);

  m_world = new ChunkWorld(m_options);
  m_world->setTorchColor(m_torchlightColor);
  m_world->setFogHeight(FOG_BOT_HEIGHT, FOG_TOP_HEIGHT);
  m_world->setFogInten(1.0, 1.0);
  m_world->setFogDist(m_fogMaxDist);

  setDaylight(true);
  setFoggy(true);

  m_eyePt =  mgPoint3(721, 72, -179);

  m_eyeRotX = -1.32; // -33;
  m_eyeRotY = -111.56; // -29;
  m_eyeRotZ = 0;

  setDeskMode(true);

  // create the help pane
  m_help = new HelpUI(m_options);
  setUI(m_help);
}
Beispiel #16
0
//--------------------------------------------------------------------
// render the view
void Crafty::appViewDraw()
{
  // draw the sky
  mgDisplay->setFrontAndBack(0.25, 16384);

  mgMatrix4 identity;
  m_eyeMatrix.loadIdentity();
  m_eyeMatrix.rotateZDeg(m_eyeRotZ);
  m_eyeMatrix.rotateYDeg(m_eyeRotY);
  m_eyeMatrix.rotateXDeg(m_eyeRotX);

  mgDisplay->setEyeMatrix(m_eyeMatrix);
  mgDisplay->setEyePt(mgPoint3(0,0,0));

  mgDisplay->setModelTransform(identity);
  mgDisplay->setCulling(true);

  m_sky->setFogHeight(FOG_BOT_HEIGHT - m_eyePt.y, FOG_TOP_HEIGHT - m_eyePt.y);
  m_sky->render();

  // reset state after any changes in sky render
  mgDisplay->setLightDir(m_lightDir.x, m_lightDir.y, m_lightDir.z);
  mgDisplay->setMatColor(m_matColor.x, m_matColor.y, m_matColor.z);
  mgDisplay->setLightAmbient(0.0, 0.0, 0.0);
  mgDisplay->setTransparent(false);

  mgDisplay->setEyeMatrix(m_eyeMatrix);
  mgDisplay->setEyePt(m_eyePt);
  mgDisplay->setModelTransform(identity);

  m_world->render();

  mgDisplay->setTransparent(true);

  m_world->renderTransparent();

  mgDisplay->setTransparent(false);
}
Beispiel #17
0
//--------------------------------------------------------------------
// render the view
void SeaOfMemes::appViewDraw()
{
  // draw the sky
  mgDisplay->setFrontAndBack(0.25, 16384);

  mgPoint3 eyePt;
  m_world->getEyePt(eyePt);
  mgMatrix4 eyeMatrix;
  m_world->getEyeMatrix(eyeMatrix);

  mgDisplay->setEyeMatrix(eyeMatrix);
  mgDisplay->setEyePt(mgPoint3(0,0,0));

  mgMatrix4 identity;
  mgDisplay->setModelTransform(identity);
  mgDisplay->setCulling(true);

  m_sky->render();

  m_world->setSunDir(m_lightDir);

  m_world->render();
}
Beispiel #18
0
//-----------------------------------------------------------------------------
// create ring
void Wreck::createRing(
  mgMatrix4& xform,
  int ringSteps,
  int circleSteps)
{
  mgBezier ringSpline;
  ringSpline.addVertex(mgPoint3(  0.0,  4.0,  0.0),     mgPoint3(  0.0+13,  4.0,       0.0));
  ringSpline.addVertex(mgPoint3( 13.0,  0.0,  0.0),     mgPoint3( 13.0,     0.0+4,     0.0));
  ringSpline.addVertex(mgPoint3(  0.0, -4.0,  0.0),     mgPoint3(  0.0+13, -4.0,       0.0));

  double ringLen = ringSpline.getLength();

  mgVertexTA* points = new mgVertexTA[(ringSteps+1) * (circleSteps+1)];

  mgPoint3 ringPt, xpt;
  for (int i = 0; i <= circleSteps; i++)
  {
    double angle = (i*2*PI)/circleSteps;
    for (int j = 0; j <= ringSteps; j++)
    {
      ringSpline.splinePt((ringLen * j)/ringSteps, ringPt);

      mgVertexTA* v = &points[i*(ringSteps+1)+j];
      mgPoint3 pt(cos(angle) * ringPt.x, 
                  sin(angle) * ringPt.x,
                  ringPt.y);
      xform.mapPt(pt, xpt);
      v->setPoint(xpt.x, xpt.y, xpt.z);

      v->setTexture(xpt.z/100 + 2*j/(double) ringSteps, 2*i/(double) circleSteps, 0);
    }
  }

  // set all the normals
  setNormals(points, circleSteps+1, ringSteps+1, true);

  int vertexBase = m_vertexes->getLength();
  for (int i = 0; i < (circleSteps+1)*(ringSteps+1); i++)
    m_vertexes->addVertex(&points[i]);

  m_indexes->addGrid(vertexBase, ringSteps+1, circleSteps, ringSteps, true);

  delete points;
}
Beispiel #19
0
//-----------------------------------------------------------------------------
// create vertex and index buffers
void Wreck::createSpine(
  int sideSteps,
  int outSteps)
{
  mgBezier outSpline;
  outSpline.addVertex(mgPoint3(  0.0, 10.0,   9.5),    mgPoint3(  0.0,    10.0+10,   9.5-3));
  outSpline.addVertex(mgPoint3( 10.0, 40.0,   0.0),    mgPoint3( 10.0-10, 40.0,      0.0));
  outSpline.addVertex(mgPoint3( 30.0, 10.0,   0.0),    mgPoint3( 30.0-10, 10.0,      0.0));
  outSpline.addVertex(mgPoint3( 60.0, 10.0,   0.0),    mgPoint3( 60.0,    10.0,      0.0));
  outSpline.addVertex(mgPoint3(180.0, 10.0,   0.0),    mgPoint3(180.0,    10.0,      0.0));
  outSpline.addVertex(mgPoint3(180.0, 10.0,   0.0),    mgPoint3(180.0-10, 10.0,      0.0));
  outSpline.addVertex(mgPoint3(210.0, 30.0,   5.0),    mgPoint3(210.0-10, 30.0,      5.0));
  outSpline.addVertex(mgPoint3(240.0, 30.0,   5.0),    mgPoint3(240.0-10, 30.0,      5.0));
  outSpline.addVertex(mgPoint3(235.0, 17.5,  17.0),    mgPoint3(235.0,    17.5+5,   17.0-5));

  mgBezier sideSpline;
  double left = 150*PI/180.0;
  double lx = 10*cos(left);  
  double ly = 10*sin(left);

  double right = 30*PI/180.0;
  double rx = 10*cos(right);
  double ry = 10*sin(right);

  sideSpline.addVertex(mgPoint3(0.0,    0.0,    0.0),       mgPoint3(0.0-1,   0.0+1,    0.0));
  sideSpline.addVertex(mgPoint3(lx+2,   ly+1,   0.0),       mgPoint3(lx+2,    ly+1-2,   0.0));
  sideSpline.addVertex(mgPoint3(0.0,    10.0,   0.0),       mgPoint3(0-2.5,   10,       0.0));
  sideSpline.addVertex(mgPoint3(rx-2,   ry+1,   0.0),       mgPoint3(rx-2,    ry+1+2,   0.0));
  sideSpline.addVertex(mgPoint3(0.0,    0.0,    0.0),       mgPoint3(0.0+1,   0.0+1,    0.0));

  for (int i = 0; i < 3; i++)
  {
    mgMatrix4 rotate;
    rotate.rotateZDeg(i*120);
    buildSurface(sideSpline, 0.1, sideSteps, outSpline, 1.0, outSteps, rotate, false, 0);
  }
}
Beispiel #20
0
//--------------------------------------------------------------
// constructor
Tower::Tower(
  const mgOptionsFile& options,
  BOOL lights)
{
  m_lights = lights;
  m_shellIndexes = NULL;
  m_shellVertexes = NULL;
  m_officeIndexes = NULL;
  m_officeVertexes = NULL;
  m_glassIndexes = NULL;

  // load ball textures.  must all be same size.
  mgString fileName;
  options.getFileName("tower-shell", options.m_sourceFileName, "tower-body.jpg", fileName);
  m_shellTexture = mgDisplay->loadTexture(fileName);

  options.getFileName("tower-glass", options.m_sourceFileName, "tower-glass.jpg", fileName);
  m_glassTexture = mgDisplay->loadTexture(fileName);

  mgStringArray fileList;
  options.getFileName("tower-wall", options.m_sourceFileName, "tower-wall.jpg", fileName);
  fileList.add(fileName);
  options.getFileName("tower-light", options.m_sourceFileName, "tower-light.jpg", fileName);
  fileList.add(fileName);
  options.getFileName("tower-floor", options.m_sourceFileName, "tower-floor.jpg", fileName);
  fileList.add(fileName);
  options.getFileName("tower-wall-dark", options.m_sourceFileName, "tower-wall.jpg", fileName);
  fileList.add(fileName);
  options.getFileName("tower-light-dark", options.m_sourceFileName, "tower-light.jpg", fileName);
  fileList.add(fileName);
  options.getFileName("tower-floor-dark", options.m_sourceFileName, "tower-floor.jpg", fileName);
  fileList.add(fileName);
  options.getFileName("tower-frame", options.m_sourceFileName, "tower-frame.jpg", fileName);
  fileList.add(fileName);

  m_officeTextures = mgDisplay->loadTextureArray(fileList);

  m_floorSpline.addVertex(mgPoint3(-30.0,   0.0,  0.0),     mgPoint3(-30.0,      0.0+10,    0.0));
  m_floorSpline.addVertex(mgPoint3(  0.0,  10.0,  0.0),     mgPoint3(  0.0-30,  10.0,       0.0));
  m_floorSpline.addVertex(mgPoint3( 30.0,   0.0,  0.0),     mgPoint3( 30.0,      0.0+10,    0.0));
  m_floorSpline.addVertex(mgPoint3(  0.0, -10.0,  0.0),     mgPoint3(  0.0+30, -10.0,       0.0));
  m_floorSpline.addVertex(mgPoint3(-30.0,   0.0,  0.0),     mgPoint3(-30.0,      0.0-10,    0.0));

  m_floorLen = m_floorSpline.getLength();

  m_aptSpline.addVertex(mgPoint3(  0.0,   1.5,  0.0),     mgPoint3( 0.0+0,      1.5,     0.0));
  m_aptSpline.addVertex(mgPoint3(  3.0,   1.5,  0.0),     mgPoint3(  3.0-2.5,   1.5,     0.0));
  m_aptSpline.addVertex(mgPoint3(  5.0,   7.5,  0.0),     mgPoint3(  5.0,       7.5-2.5, 0.0));
  m_aptSpline.addVertex(mgPoint3(  5.0,   7.5,  0.0),     mgPoint3(  5.0,       7.5-7.5, 0.0));
  m_aptSpline.addVertex(mgPoint3( 35.0,  15.0,  0.0),     mgPoint3( 35.0-30,    15.0,    0.0));
  m_aptSpline.addVertex(mgPoint3( 65.0,   7.5,  0.0),     mgPoint3( 65.0,       7.5+7.5, 0.0));
  m_aptSpline.addVertex(mgPoint3( 65.0,   0.0,  0.0),     mgPoint3( 65.0,       0.0,     0.0));
  
  m_aptLen = m_aptSpline.getLength();

  /* 
    The spline class returns points based on a 'distance' which is a sum of all the
    segments that define the spline curve, not the actual length of the curve.
    To get regularly spaced points for windows, we search for the input distances
    that produce the correct x values (used for height here).  The portions of
    the shape above and below the floors are then generated from the first and
    last distance point making the floors.
  */

  m_floorDists = new double[APT_STEPS+1];

  // find the floor heights
  for (int i = 0; i <= FLOORS*3; i++)
  {
    m_floorDists[15+i] = findSplineX(m_aptSpline, BOTTOM_FLOOR+i, 12, m_aptLen/2);
  }

  // fill in the rest of the tower
  double bottom = m_floorDists[15];
  double top = m_floorDists[15+FLOORS*3];
  for (int i = 0; i < 15; i++)
  {
    m_floorDists[i] = (bottom*i)/15;
    m_floorDists[16+FLOORS*3+i] = top + (m_aptLen - top)*(i+1)/15.0;
  }
}
Beispiel #21
0
//--------------------------------------------------------------
// get avatar orientation under coordinate system
void SolarSystem::getCoordBasis(
  int coordSystem,
  mgMatrix4& basis)
{
  // get position of moon
  double angle = (2*PI*m_moonMonth)/360;
  mgPoint3 moonCenter(MOON_DISTANCE*sin(angle), 0, MOON_DISTANCE*cos(angle));

  // get coordinate system based on object
  mgPoint3 xaxis(1.0, 0.0, 0.0);
  mgPoint3 yaxis(0.0, 1.0, 0.0);
  mgPoint3 zaxis(0.0, 0.0, 1.0);

  switch (coordSystem)
  {
    case COORDS_PLANET:
      // y axis points away from center of planet
      yaxis = m_coordPosn;
      yaxis.normalize();

      // construct z axis orthonal to y
      zaxis = yaxis;
      zaxis.cross(mgPoint3(0.0, 1.0, 0.0));
      zaxis.normalize();

      // construct x axis
      xaxis = yaxis;
      xaxis.cross(zaxis);
      xaxis.normalize();
      break;

    case COORDS_MOON:
      // y axis points away from center of moon
      yaxis = m_coordPosn;
      yaxis.normalize();

      // construct z axis orthonal to y
      zaxis = yaxis;
      zaxis.cross(mgPoint3(0.0, 1.0, 0.0));
      zaxis.normalize();

      // construct x axis
      xaxis = yaxis;
      xaxis.cross(zaxis);
      xaxis.normalize();
      break;

    case COORDS_RING:
      // y axis points towards center of ring
      yaxis.x = -m_coordPosn.x;
      yaxis.z = -m_coordPosn.z;
      yaxis.y = 0.0;
      yaxis.normalize();

      // x axis is across the ring
      xaxis.x = 0.0; xaxis.y = 1.0; xaxis.z = 0.0;

      // construct z axis
      zaxis = xaxis;
      zaxis.cross(yaxis);
      zaxis.normalize();
      break;

    case COORDS_BELT:
      break;

    case COORDS_SPACE:
      // use default axis
      break;
  }

  // apply object surface orientation
  basis._11 = xaxis.x;
  basis._21 = xaxis.y;
  basis._31 = xaxis.z;
  basis._12 = yaxis.x;
  basis._22 = yaxis.y;
  basis._32 = yaxis.z;
  basis._13 = zaxis.x;
  basis._23 = zaxis.y;
  basis._33 = zaxis.z;
}
Beispiel #22
0
//--------------------------------------------------------------
// initialize application
void Landscape::appInit()
{
  MovementApp::appInit();
  mgPlatform->setWindowTitle("Landscape, Part 58");

  // create the help pane
  m_help = new HelpUI(m_options);
  setUI(m_help);

  // load textures
  loadTextures();

  // load shaders
  mgVertex::loadShader("litTexture");
  mgVertex::loadShader("unlitTexture");
  mgVertexTA::loadShader("litTextureArray");
  VertexTerrain::loadShader("terrain");
  mgVertex::loadShader("water");

  // create the sky
  m_lightDir = mgPoint3(0.0, 0.5, 1.0);
  m_lightColor = mgPoint3(0.8, 0.8, 0.8);
  m_lightAmbient = mgPoint3(0.2, 0.2, 0.2);

  m_fogColor = mgPoint3(0.8, 0.8, 0.8);
  m_fogMaxDist = 13000.0;
  m_fogTopHeight = 2000.0; // WATER_LEVEL + 128.0;// m_fogMaxDist;
  m_fogBotHeight = WATER_LEVEL; 
  m_fogBotInten = 1.0; 
  m_fogTopInten = 0.8;

  m_sky = new StarrySky(m_options);
  m_sky->setFogInten(m_fogBotInten, m_fogTopInten);

  m_sky->setMoonDir(m_lightDir);
  m_sky->setSunDir(m_lightDir);

  setDayLight(true);

  m_maxCount = 0;  // number of terrain chunks in use

  // generate the initial terrain
  for (int x = 0; x <= 2; x++)
  {
    for (int z = 0; z <= 2; z++)
    {
      Terrain* chunk = new Terrain(
        (x-1) * HORIZON_SIZE - HORIZON_SIZE/2, 
        (z-1) * HORIZON_SIZE - HORIZON_SIZE/2,
        HORIZON_SIZE, HORIZON_SIZE);

      chunk->createHeightmap();
      m_terrain[x][z] = chunk;
    }
  }

  // subdivide terrain around the eye
  checkTerrainResolution();

  m_mapView = false;
  m_terrainGrid = GRID_TERRAIN;
  m_mapGrid = GRID_CHUNKS;

  m_eyeHeight = 1.75;
  m_eyePt = mgPoint3(7012.1, 358.22, -6287.94); // nice area for initial coordinate

  double ht = Terrain::heightAtPt(m_eyePt.x, m_eyePt.z);
  ht = max(ht, WATER_LEVEL);
  m_eyePt.y = ht + m_eyeHeight;

  m_eyeRotX = 4.4;
  m_eyeRotY = 2.84;
  m_eyeRotZ = 0.0;

  // init threads
  m_threadCount = m_options.getInteger("threadCount", 1); 

  m_buildLock = mgOSLock::create();

  m_shutdown = false;
  if (m_threadCount > 0)
  {
    m_buildEvent = mgOSEvent::create();

    m_buildThreads = mgOSThread::create(m_threadCount, buildThreadProc, 
      mgOSThread::PRIORITY_LOW, this, NULL);
  }
}
Beispiel #23
0
//--------------------------------------------------------------------
// render the view
void Landscape::appViewDraw()
{
  mgPoint3 renderEyePt(m_eyePt);
  if (m_mapView)
    renderEyePt.y = MAPVIEW_HEIGHT;

  // draw the sky
  mgDisplay->setFrontAndBack(0.25, 16384);

  mgMatrix4 identity;
  m_eyeMatrix.loadIdentity();
  if (m_mapView)
  {
    m_eyeMatrix.rotateZDeg(m_eyeRotZ);
    m_eyeMatrix.rotateYDeg(m_eyeRotY);
    m_eyeMatrix.rotateXDeg(-90);
  }
  else
  {
    m_eyeMatrix.rotateZDeg(m_eyeRotZ);
    m_eyeMatrix.rotateYDeg(m_eyeRotY);
    m_eyeMatrix.rotateXDeg(m_eyeRotX);
  }

  mgDisplay->setEyeMatrix(m_eyeMatrix);
  mgDisplay->setEyePt(mgPoint3(0,0,0));

  mgDisplay->setModelTransform(identity);
  mgDisplay->setCulling(true);
  
  m_sky->setFogColor(m_fogColor);
  m_sky->setFogHeight(m_fogBotHeight - renderEyePt.y, m_fogTopHeight - renderEyePt.y);
  m_sky->setFogDist(HORIZON_SIZE, m_fogMaxDist);
  m_sky->render();

  // reset state after any changes in sky render
  mgDisplay->setLightDir(m_lightDir.x, m_lightDir.y, m_lightDir.z);
  mgDisplay->setMatColor(1.0, 1.0, 1.0);
  mgDisplay->setLightColor(m_lightColor.x, m_lightColor.y, m_lightColor.z);
  mgDisplay->setLightAmbient(m_lightAmbient.x, m_lightAmbient.y, m_lightAmbient.z);
  mgDisplay->setTransparent(false);

  mgDisplay->setEyeMatrix(m_eyeMatrix);
  mgDisplay->setEyePt(mgPoint3(0,0,0));  // we use relative coordinates in render

  if (mgDisplay->getDepthBits() == 32)
  {
    mgDisplay->setFrontAndBack(0.25, 65536*4);

    renderTerrain();
  }
  else if (mgDisplay->getDepthBits() >= 24)
  {
    mgDisplay->setFrontAndBack(256, 65536*4);

    renderTerrain();

    mgDisplay->setFrontAndBack(0.25, 256);
    mgDisplay->clearBuffer(MG_DEPTH_BUFFER);

    renderTerrain();
  }
  else
  {
    mgDisplay->setFrontAndBack(4096, 65536*4);

    renderTerrain();

    mgDisplay->setFrontAndBack(32, 4096);
    mgDisplay->clearBuffer(MG_DEPTH_BUFFER);

    renderTerrain();

    mgDisplay->setFrontAndBack(0.25, 32);
    mgDisplay->clearBuffer(MG_DEPTH_BUFFER);

    renderTerrain();
  }
}
Beispiel #24
0
//--------------------------------------------------------------
// create buffers ready to send to display
void Earth::createBuffers()
{
  const int HEAD_STRIPS = 16;
  const int HEAD_FACES = 32;

  // three points times two triangles times grid size
  m_farVertexes = mgVertex::newBuffer(3*2*HEAD_STRIPS * HEAD_FACES);

  mgVertex tl, tr, bl, br;

  // create a sphere
  for (int i = 1; i <= HEAD_STRIPS; i++)
  {
    double topAngle = (PI*(i-1))/HEAD_STRIPS;
    double topRadius = sin(topAngle);
    double topY = cos(topAngle);
                                    
    double botAngle = (PI*i)/HEAD_STRIPS;
    double botRadius = sin(botAngle);
    double botY = cos(botAngle);
                                    
    for (int j = 1; j <= HEAD_FACES; j++)
    {
      double leftAngle = (2.0*PI*(j-1))/HEAD_FACES;
      tl.setPoint(topRadius * cos(leftAngle), topY, topRadius * sin(leftAngle));
      bl.setPoint(botRadius * cos(leftAngle), botY, botRadius * sin(leftAngle));

      double rightAngle = (2.0*PI*j)/HEAD_FACES;
      tr.setPoint(topRadius * cos(rightAngle), topY, topRadius * sin(rightAngle));
      br.setPoint(botRadius * cos(rightAngle), botY, botRadius * sin(rightAngle));

      // set texture coordinates
      double topTx = (i-1); // /(double)HEAD_STRIPS;
      double botTx = i; // /(double)HEAD_STRIPS;
      double leftTx = (j-1); // /(double)HEAD_FACES;
      double rightTx = j; // /(double)HEAD_FACES;

      tl.setTexture(leftTx, topTx);
      tr.setTexture(rightTx, topTx);
      bl.setTexture(leftTx, botTx);
      br.setTexture(rightTx, botTx);

      // use vertexes as normals, since they are from origin
      mgPoint3 normal;
      normal = mgPoint3(tl.m_px, tl.m_py, tl.m_pz);
      normal.normalize();
      tl.setNormal(normal.x, normal.y, normal.z);

      normal = mgPoint3(tr.m_px, tr.m_py, tr.m_pz);
      normal.normalize();
      tr.setNormal(normal.x, normal.y, normal.z);

      normal = mgPoint3(bl.m_px, bl.m_py, bl.m_pz);
      normal.normalize();
      bl.setNormal(normal.x, normal.y, normal.z);

      normal = mgPoint3(br.m_px, br.m_py, br.m_pz);
      normal.normalize();
      br.setNormal(normal.x, normal.y, normal.z);

      tl.addTo(m_farVertexes);
      tr.addTo(m_farVertexes);
      bl.addTo(m_farVertexes);

      bl.addTo(m_farVertexes);
      tr.addTo(m_farVertexes);
      br.addTo(m_farVertexes);
    }
  }
}
Beispiel #25
0
//--------------------------------------------------------------
// render the planet
void Planet::render(
  int graphicsWidth,
  int graphicsHeight,
  double angle)
{
  // if shader didn't compile, nothing we can do
  if (m_planetShader == 0)
    return;

  double atmos = 20.0;
  double radius = 1200.0;

  double eyeDist = m_eyePt.length()/radius;
  double a = 1.0;
  if (eyeDist < a)
    return;  // below surface, nothing to do 
  double b = sqrt(eyeDist*eyeDist - a*a);
  double h = (a*b)/eyeDist;
  double m = (a*a)/eyeDist;

  h += atmos/radius;

  // x axis from planet center towards eye
  mgPoint3 xaxis(m_eyePt);
  xaxis.normalize();

  // build y axis
  mgPoint3 yaxis(xaxis);
  yaxis.cross(mgPoint3(0.0, 1.0, 0.0));
  yaxis.normalize();

  mgPoint3 zaxis(yaxis);
  zaxis.cross(xaxis);
  zaxis.normalize();

  mgMatrix4 transform;
  transform._11 = xaxis.x;
  transform._12 = xaxis.y;
  transform._13 = xaxis.z;
  transform._21 = yaxis.x;
  transform._22 = yaxis.y;
  transform._23 = yaxis.z;
  transform._31 = zaxis.x;
  transform._32 = zaxis.y;
  transform._33 = zaxis.z;
  
  VertexPlanet tl, tr, bl, br;

  mgPoint3 pt;
  transform.mapPt(m, -h, h, pt.x, pt.y, pt.z);
  tl.setPoint(radius*pt.x, radius*pt.y, radius*pt.z);

  transform.mapPt(m, h, h, pt.x, pt.y, pt.z);
  tr.setPoint(radius*pt.x, radius*pt.y, radius*pt.z);

  transform.mapPt(m, -h, -h, pt.x, pt.y, pt.z);
  bl.setPoint(radius*pt.x, radius*pt.y, radius*pt.z);

  transform.mapPt(m, h, -h, pt.x, pt.y, pt.z);
  br.setPoint(radius*pt.x, radius*pt.y, radius*pt.z);

  // inverse of world transform
  mgMatrix4 model;
  model.rotateYDeg(-angle);

  mgPoint3 lightDir(1.0, 0.25, 0.0);
  lightDir.normalize();

  mgPoint3 modelLightDir;
  model.mapPt(lightDir, modelLightDir);
  transform.multiply(model);

  mgPoint3 modelEye; 
  transform.mapPt(eyeDist, 0.0, 0.0, modelEye.x, modelEye.y, modelEye.z);

  transform.mapPt(m, -h, h, pt.x, pt.y, pt.z);
  tl.setModelPoint(pt.x, pt.y, pt.z);

  transform.mapPt(m, h, h, pt.x, pt.y, pt.z);
  tr.setModelPoint(pt.x, pt.y, pt.z);

  transform.mapPt(m, -h, -h, pt.x, pt.y, pt.z);
  bl.setModelPoint(pt.x, pt.y, pt.z);

  transform.mapPt(m, h, -h, pt.x, pt.y, pt.z);
  br.setModelPoint(pt.x, pt.y, pt.z);

  mgMatrix4 viewMatrix;
  viewMatrix.translate(-m_eyePt.x, -m_eyePt.y, -m_eyePt.z);
  viewMatrix.multiply(m_eyeMatrix);

  mgMatrix4 worldProjection;
  createProjection(worldProjection, graphicsWidth, graphicsHeight);

  mgMatrix4 mvpMatrix(viewMatrix);
  mvpMatrix.multiply(worldProjection);

  setupShader(mvpMatrix, modelEye, modelLightDir);

  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_CUBE_MAP, m_surfaceTexture); 
  glActiveTexture(GL_TEXTURE1);
  glBindTexture(GL_TEXTURE_CUBE_MAP, m_cloudsTexture); 

  glBindVertexArray(m_vertexArray);
  glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);

  VertexPlanet data[6];
  data[0] = tl;
  data[1] = tr;
  data[2] = bl;
  data[3] = bl;
  data[4] = tr;
  data[5] = br;
  int vertexSize = sizeof(VertexPlanet);
  int count = 6;
  glBufferData(GL_ARRAY_BUFFER, vertexSize * count, data, GL_DYNAMIC_DRAW);

  glDrawArrays(GL_TRIANGLES, 0, count);

  glBindBuffer(GL_ARRAY_BUFFER, 0);
  glBindVertexArray(0);
  glActiveTexture(GL_TEXTURE0);
}
Beispiel #26
0
//--------------------------------------------------------------------
// render the view
void DontHitMe::appViewDraw()
{
  // draw the sky
  if (m_sky != NULL)
  {
    mgDisplay->setTransparent(true);
    mgDisplay->setZEnable(false);

    mgDisplay->setEyeMatrix(m_eyeMatrix);
    mgDisplay->setEyePt(mgPoint3(0,0,0));

    mgMatrix4 identity;
    mgDisplay->setModelTransform(identity);

    m_sky->render();

    mgDisplay->setTransparent(false);
    mgDisplay->setZEnable(true);
  }

  mgDisplay->setEyeMatrix(m_eyeMatrix);
  mgDisplay->setEyePt(m_eyePt);

  mgDisplay->setLightColor(0.8, 0.8, 0.8);

  // draw the planet
  if (m_planet != NULL)
    m_planet->render();

  // draw the wreck
  if (m_wreck != NULL)
  {
    mgMatrix4 model;
    model.translate(0, 0, -120);  // center of ship
    model.rotateYDeg(20);
    model.rotateZDeg(10);
    model.translate(215, 0, 30);  // near asteroid
    mgDisplay->setModelTransform(model);

    m_wreck->render();
  }

  // draw the fallen towers
  if (m_tower1 != NULL)
  {
    mgMatrix4 model;
    model.rotateYDeg(-80);
    model.rotateXDeg(10);
    model.rotateZDeg(-60);
    model.translate(190, 40, 40);
    mgDisplay->setModelTransform(model);

    m_tower1->render();
  }

  if (m_tower2 != NULL)
  {
    mgMatrix4 model;
    model.rotateYDeg(90);
    model.rotateXDeg(-5);
    model.rotateZDeg(-105);
    model.translate(205, -20, 20);
    mgDisplay->setModelTransform(model);

    m_tower2->render();
  }

/*
  // draw the prickles
  for (int i = 0; i < m_prickles.length(); i++)
  {
    Prickle* prickle = (Prickle*) m_prickles[i];
    prickle->render();
  }
*/

  if (m_showingIntro)
  {
    if (m_intro != NULL)
      m_intro->render();
  }

  else
  {
    // draw the saucer
    if (m_saucer != NULL)
    {
      double saucerScale = 0.5;
      double ballRadius = 0.5*saucerScale;
      mgMatrix4 ballModel;
      ballModel.scale(saucerScale);
      ballModel.translate(0.0, ballRadius + TUBE_RADIUS, 0.0);
  //    ballModel.rotateZDeg(m_ballRotate);
  //    ballModel.rotateXDeg(m_ballRoll);
      ballModel.multiply(m_ballMatrix);
      ballModel.translate(m_ballOrigin);

      mgDisplay->setModelTransform(ballModel);

      m_saucer->render();
      mgDisplay->setCulling(true);
    }

    // draw the track
    if (m_tube != NULL)
    {
      mgDisplay->setTransparent(true);
      mgMatrix4 tubeModel;
      mgDisplay->setModelTransform(tubeModel);
      mgDisplay->setTexture(m_tubeTexture);
      m_tube->render();
      mgDisplay->setTransparent(false);
    }
  }
}
Beispiel #27
0
//--------------------------------------------------------------
// do a step of colonization.  return true if added branch
BOOL Colonization::colonize()
{
  BOOL active = false;

  int branchCount = m_branches.length();

  // for each leaf, find the closest branch point within the cutoff radius
  for (int i = m_leaves.length()-1; i >= 0; i--)
  {
    Leaf* leaf = getLeaf(i);
    if (!leaf->m_active)
      continue;

    leaf->m_closest = -1;
    double closestDist = INT_MAX;

    for (int j = 0; j < branchCount; j++)
    {
      Branch* branch = getBranch(j);
      // calculate distance
      mgPoint3 dir(leaf->m_pt);
      dir.subtract(branch->m_pt);
      double dist = dir.length();
      if (dist < MIN_DISTANCE)
      {
        leaf->m_active = false;
        break; 
      }
      if (dist > MAX_DISTANCE)
        continue;

      if (dist < closestDist)
      {
        leaf->m_closest = j;
        closestDist = dist;
      }
    }

    // update growth of closest branch
    if (leaf->m_closest != -1)
    {
      Branch* branch = getBranch(leaf->m_closest);

      // calculate vector to branch
      mgPoint3 dir(leaf->m_pt);
      dir.subtract(branch->m_pt);
      dir.normalize();
//      double dist = dir.length();
//      dist = pow(dist, 1.5);

      // add normalized vector
      branch->m_growDir.add(dir);
      branch->m_growCount++;
    }
  }

  // for each branch
  for (int j = 0; j < branchCount; j++)
  {
    Branch* branch = getBranch(j);
    // if there's growth, add a new branch
    if (branch->m_growCount != 0)
    {
      // normalize the growth direction
      branch->m_growDir.scale(1.0/branch->m_growCount);
      branch->m_growDir.normalize();
      branch->m_growDir.scale(GROW_DISTANCE);

      // create a new branch
      Branch* twig = new Branch();
      twig->m_pt = branch->m_pt;
      twig->m_pt.add(branch->m_growDir);
      twig->m_parent = j;
      twig->m_growDir = mgPoint3(0,0,0);
      twig->m_growCount = 0;
      twig->m_area = TWIG_AREA;

      m_branches.add(twig);

      // reset growth vector on branch
      branch->m_growDir = mgPoint3(0,0,0);
      branch->m_growCount = 0;
      active = true;
    }
  }

  return active;
}