test.cpp

C++ source for Expanding & shrinking sphere (blobs2), 592 lines. Raw file

#define WIN32_LEAN_AND_MEAN

#include <math.h>

#include	"qe_ogl.h"
#include "Pattern.h"
#include "mmsystem.h"


void oglcube(double x1, double y1, double z1, double x2, double y2, double z2, double r, double g, double b)
{
/*
  double t = 7 + 2*sin(GetTickCount()*3.14159/16400);

  double dd = sqrt(x1*x1+y1*y1+z1*z1);
  double d = sin(t*dd);
//  if(d < 0) return;

  d += 1.3;

  r *= d; g *= d; b *= d;
*/

  glColor3f(r, g, b);

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x1, y1, z1);
    glVertex3f(x1, y1, z2);
    glVertex3f(x2, y1, z2);
    glVertex3f(x2, y1, z1);
  glEnd();

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x2, y2, z1);
    glVertex3f(x2, y2, z2);
    glVertex3f(x1, y2, z2);
    glVertex3f(x1, y2, z1);
  glEnd();

  glColor3f(r*0.95, g*0.95, b*0.95);

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x1, y2, z1);
    glVertex3f(x1, y2, z2);
    glVertex3f(x1, y1, z2);
    glVertex3f(x1, y1, z1);
  glEnd();

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x2, y1, z1);
    glVertex3f(x2, y1, z2);
    glVertex3f(x2, y2, z2);
    glVertex3f(x2, y2, z1);
  glEnd();

  glColor3f(r*0.90, g*0.90, b*0.90);

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x2, y1, z1);
    glVertex3f(x2, y2, z1);
    glVertex3f(x1, y2, z1);
    glVertex3f(x1, y1, z1);
  glEnd();

  glBegin(GL_TRIANGLE_FAN);
    glVertex3f(x1, y1, z2);
    glVertex3f(x1, y2, z2);
    glVertex3f(x2, y2, z2);
    glVertex3f(x2, y1, z2);
  glEnd();

}



//---- ISOOBJECT -------------------------------------------------------------//

class IsoObject
{
private:

  double size_x, size_y, size_z;  //  Size of bounding box
  double scale;                   //  Size of cubes

  double f_size;

  //---- f(x, y, z) - isosurface function

  double f(double x, double y, double z)
  {
    return x*x + y*y + z*z - f_size*f_size;
  }

  //---- IsSurface(x, y, z) - returns whether this cube contains a portion of the surface of the object

  bool IsSurface(double x, double y, double z)
  {
    bool p0 = f(x      , y      , z      ) < 0;
    bool p1 = f(x+scale, y      , z      ) < 0;
    bool p2 = f(x      , y+scale, z      ) < 0;
    bool p3 = f(x+scale, y+scale, z      ) < 0;
    bool p4 = f(x      , y      , z+scale) < 0;
    bool p5 = f(x+scale, y      , z+scale) < 0;
    bool p6 = f(x      , y+scale, z+scale) < 0;
    bool p7 = f(x+scale, y+scale, z+scale) < 0;

    return !(p0 == p1 && p0 == p2 && p0 == p3 && p0 == p4 && p0 == p5 && p0 == p6 && p0 == p7);
  }

public:

  //---- IsoObject() - sets default values

  IsoObject()
  {
    SetSize(30, 30, 30);
    SetScale(1.0);
  }

  //---- SetSize(x, y, z) - sets the dimensions of the bounding box

  void SetSize(double x, double y, double z)
  {
    size_x = x; size_y = y; size_z = z;
  }

  //---- SetScale(scale) - sets the size of the cubes to divide the object into to render

  void SetScale(double s)
  {
    scale = s;
  }

  //---- Draw() - Draws the object in the current OpenGL context

  void Draw()
  {
    int i, j, k;

    //- Number of cubes we can fit into each dimension

    int cx = size_x / scale;
    int cy = size_y / scale;
    int cz = size_z / scale;

    //- Initial point

    double px = -0.5 * cx * scale;
    double py = -0.5 * cy * scale;
    double pz = -0.5 * cz * scale;

    //- Vary object

    f_size = 10.0 + 6.0*sin(GetTickCount()*0.0003);

    //- TEMP: Draw bounding box

//    oglcube(px, py, pz, -px, -py, -pz, 0.4, 0.6, 0.8);

    //- Fill with cubes to show boundary

    double s = scale * 0.1;

    double x, y, z;

    z = pz;

    for(k=0; k<cz; k++)
    {
      y = py;

      for(j=0; j<cy; j++)
      {
        x = px;

        for(i=0; i<cx; i++)
        {
          if(IsSurface(x, y, z)) oglcube(x+s, y+s, z+s, x+scale-s, y+scale-s, z+scale-s, (1.0*i)/cx, (1.0*j)/cy, (1.0*k)/cz);
          x += scale;
        }
        y += scale;
      }
      z += scale;
    }

  }



};



//----------------------------------------------------------------------------//



//---- QUEUE

class Queue
{
  #define QUEUE_SIZE 10000

public:

  struct {
    int x, y, z;
  } queue[QUEUE_SIZE];

  int queue_start;
  int queue_num;
  int queue_next;

  Queue()
  {
    Init();
  }

  void Init()
  {
    queue_start = 0;
    queue_num = 0;
  }

  bool Push(int x, int y, int z)
  {
    if(queue_num < QUEUE_SIZE)
    {
      int index = (queue_start + queue_num) % QUEUE_SIZE;
      queue[index].x = x;
      queue[index].y = y;
      queue[index].z = z;
      queue_num ++;
      return true;
    }
    return false;
  }

  bool Pop(int *x, int *y, int *z)
  {
    if(queue_num > 0)
    {
      queue_num --;
      int index = (queue_start + queue_num) % QUEUE_SIZE;
      *x = queue[index].x;
      *y = queue[index].y;
      *z = queue[index].z;
      return true;
    }
    return false;
  }

  bool Contains(int x, int y, int z)
  {
    for(int i=0; i<queue_num; i++)
    {
      int index = (queue_start + i) % QUEUE_SIZE;
      if(queue[index].x == x && queue[index].y == y && queue[index].z == z) return true;
    }
    return false;
  }

  bool IsEmpty()
  {
    return queue_num == 0;
  }

  int Num()
  {
    return queue_num;
  }

  void First()
  {
    queue_next = 0;
  }

  bool Next(int *x, int *y, int *z)
  {
    if(queue_next >= queue_num) return false;
    int index = (queue_start + queue_next) % QUEUE_SIZE;
    *x = queue[index].x;
    *y = queue[index].y;
    *z = queue[index].z;
    queue_next ++;
    return true;
  }

};



//---- CACHE

class Cache
{
public:

  #define CACHE_SIZE 10000

  struct {
    bool inuse;
    int x, y, z;
    double v;
  } cache[CACHE_SIZE];

  Cache()
  {
    Flush();
  }

  bool Flush()
  {
    int i;
    for(i=0; i<CACHE_SIZE; i++) cache[i].inuse = false;
  }

  inline int Hash(int x, int y, int z)
  {
    return (x<<2)^(y<<5)^(z);//( ((5*x*x)^(7*y*y)^(3*z*z))+((z&0x07FC5BFB)*0x376F)+((y&0x0FCF77F7)*0x8721)+((x&0x0F559CFD)*0xFC7B) ) & 0x00FFFFFF;
  }

  bool Get(int x, int y, int z, double *v)
  {
    int h = Hash(x, y, z) % CACHE_SIZE;

    if(cache[h].inuse && cache[h].x == x && cache[h].y == y && cache[h].z == z)
    {
      *v = cache[h].v;
      return true;
    }
    return false;
  }

  void Set(int x, int y, int z, double v)
  {
    int h = Hash(x, y, z) % CACHE_SIZE;

    cache[h].inuse = true;
    cache[h].x = x;
    cache[h].y = y;
    cache[h].z = z;
    cache[h].v = v;
  }

};



//---- DATA

double r1=0, r2=0;
BOOL DrawMe = TRUE;

double scale = 0.05;

PATTERN_A   *a1, *a2, *a3, *a4, *a5;
PATTERN_RGB *rgb1, *rgb2, *rgb3, *rgb4, *rgb5;

Queue *q[3];
Queue drawqueue;

int maxcubes = 55;

bool restart = true;



//---- STEPPING CUBES



double bx[10] = {- 0, -15,  17, -15, -12, - 9,  15,  17, - 1,  13};
double by[10] = {  0,  11, - 2,   4,   1,   5, - 5, -12, -11, - 3};
double bz[10] = {- 0,   3,   9,   9, - 5,  11, - 3, - 2,   9,  13};

Cache fcache;



double f(int x, int y, int z)
{
/*
  double d = sqrt(x*x + y*y + z*z);
  if(d < 0.1) d = 0.1;
  return d - 20;
*/

  int i;
  double d, dd=0;

  if(!fcache.Get(x, y, z, &dd))
  {
    for(i=0; i<10; i++)
    {
      d = sqrt((x-bx[i])*(x-bx[i]) + (y-by[i])*(y-by[i]) + (z-bz[i])*(z-bz[i]));
      if(d < 0.1) d = 0.1;
      d = 1.0/d;
      dd += d;
    }
    fcache.Set(x, y, z, dd);
  }

  return dd - 0.65;

}



bool edgecube(int x, int y, int z)
{
  bool p0 = f(x  , y  , z) < 0;
  bool p1 = f(x+1, y  , z) < 0;
  bool p2 = f(x  , y+1, z) < 0;
  bool p3 = f(x+1, y+1, z) < 0;
  bool p4 = f(x  , y  , z+1) < 0;
  bool p5 = f(x+1, y  , z+1) < 0;
  bool p6 = f(x  , y+1, z+1) < 0;
  bool p7 = f(x+1, y+1, z+1) < 0;

  return !(p0 == p1 && p0 == p2 && p0 == p3 && p0 == p4 && p0 == p5 && p0 == p6 && p0 == p7);
}



void queuecube(int x, int y, int z)
{
  if(q[0]->Contains(x, y, z) || q[1]->Contains(x, y, z) || q[2]->Contains(x, y, z)) return;
  if(!edgecube(x, y, z)) return;
  drawqueue.Push(x, y, z);
  q[1]->Push(x, y, z);
}



void draw_setup()
{
  int i;

  //  Clear queues

  for(i=0; i<3; i++) q[i]->Init();

  drawqueue.Init();

  //  Start from middle of blobs and find

  int x = 0, y = 0, z = 0;

  while(!edgecube(x, y, z) && y < 20) y++;
  if(y >= 80) return;

  //  Enqueue this item

  q[0]->Push(x, y, z);
  drawqueue.Push(x, y, z);
}



bool draw_iterate()
{
  int x, y, z;

  if(!q[0]->IsEmpty())
  {
    //  Add all neighboring edge cubes to stack 1

    q[1]->Init();

    q[0]->First();

    while(q[0]->Next(&x, &y, &z))
    {
      //  Check all the cubes around this one
      int i, j, k;
      for(i=-1; i<=1; i++) for(j=-1; j<=1; j++) for(k=-1; k<=1; k++) if(i+j+k == 1 || i+j+k == -1)
        queuecube(x+i, y+j, z+k);
    }

    //  Shuffle queues so that q[0] is the neighbour

    Queue *qq = q[0];
    q[0] = q[1];
    q[1] = q[2];
    q[2] = qq;

    //  If we've collected the maximum no of cubes then stop

    if(drawqueue.Num() >= maxcubes) q[0]->Init();
  }

  return !q[0]->IsEmpty();
}



void draw_render()
{
  int i;
  int x, y, z;

  //- Draw cubes

  drawqueue.First();

  while(drawqueue.Next(&x, &y, &z))
  {
    oglcube(x*scale, y*scale, z*scale, (x+1)*scale, (y+1)*scale, (z+1)*scale, 0.8, 0.4, 0.0);
  }
}



void drawsteppingcubes()
{
  draw_setup();

  while(draw_iterate());

  draw_render();
}



void cube(int x, int y, int z)
{
  oglcube(x*scale, y*scale, z*scale, (x+1)*scale, (y+1)*scale, (z+1)*scale, 0.8, 0.4, 0.0);
}


void draw()
{
  int i, j, k;

/*
  static int p = 0;
  p++;

  int w = 20;

  for(k = -w; k < w; k++)
    for(j = -w; j < w; j++)
      for(i = -w; i < w; i++)
      {
//        if( ((i+j+k+p)&31) < 1 ) cube(i, j, k);

        double t = sin(i*3.14159/17+p*3.14159/31 + 0.12451) +
                   sin(j*3.14159/16+p*3.14159/54 + 1.89214) +
                   sin(k*3.14159/15+p*3.14159/47 + 1.00298);
        if(t > 0 && t < 0.25) cube(i, j, k);

      }
*/

  drawsteppingcubes();
}



//---- MAIN

void Initialise()
{
  for(int i=0; i<3; i++) q[i] = new Queue();
}


IsoObject iso1;

void GameDraw()
{
  iso1.Draw();
}



void GameMain()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	glTranslatef(0.f, 0.f, -50.f);

	glRotatef(-r2*0.2, 1.0, 0.0, 0.0);
	glRotatef(-r1*0.2, 0.0, 1.0, 0.0);

	GameDraw();
	SwapBuffers(gl_dc);
}