Blob Blame Raw

#include <math.h>
#include <GL/gl.h>
#include <helianthus.h>


#include "matrix.inc.h"


typedef struct {
  int colors[6];
  Matrix position;
  Vector3 mousePos[6];
} Cube;


const double colors[7][4] = {
  { 0.25, 0.25, 0.25, 1 }, // gray
  { 1   , 1   , 1   , 1 }, // white
  { 0   , 0.5 , 0   , 1 }, // green
  { 1   , 0.5 , 0   , 1 }, // orange
  { 1   , 1   , 0   , 1 }, // yellow
  { 0   , 0   , 1   , 1 }, // blue
  { 1   , 0   , 0   , 1 }, // red
};

Cube cubes[27];

Matrix position;
double mx, my;

int turning;
Vector3 turnAxis;
double turnLayer;
double turnAngle;


void randomTurn() {
  vector3Set(&turnAxis, 0, 0, 0);
  turnAxis.a[randomNumber(0, 2)] = randomNumber(0, 1)*2 - 1;
  turnLayer = randomNumber(-1, 1);
  turnAngle = 0;
  turning = TRUE;
}


void captureMouse(Vector3 *position, double z) {
  vector3Set(position, 0, 0, 100);

  double mx = mouseX();
  double my = mouseY();
  double w = windowGetWidth();
  double h = windowGetHeight();

  mx = mx/w*2 - 1;
  my = 1 - my/h*2;
  Vector4 p0, p1;
  vector4Set(&p0, mx, my, -1, 1);
  vector4Set(&p1, mx, my,  1, 1);

  Matrix back, transform, projection, modelview;
  glGetDoublev(GL_PROJECTION_MATRIX, projection.a);
  glGetDoublev(GL_MODELVIEW_MATRIX, modelview.a);
  matrixMultMatrix(&transform, &modelview, &projection);
  matrixInvert(&back, &transform);

  vector4MultMatrix(&p0, &p0, &back);
  vector4MultMatrix(&p1, &p1, &back);
  if (fabs(p0.w) <= 1e-6 || fabs(p1.w) <= 1e-6)
    return;
  vector4PespectiveDivide(&p0, &p0);
  vector4PespectiveDivide(&p1, &p1);

  double dz = p1.z - p0.z;
  if (fabs(dz) <= 1e-6)
    return;

  double l = (z - p0.z)/dz;
  double x = (p1.x - p0.x)*l + p0.x;
  double y = (p1.y - p0.y)*l + p0.y;

  vector4Set(&p0, x, y, z, 1);
  vector4MultMatrix(&p0, &p0, &transform);
  if (fabs(p0.w) <= 1e-6)
    return;
  vector4PespectiveDivide(&p0, &p0);
  vector3Set(position, x, y, p0.z);
}


void drawCube(Cube *cube) {
  const double s = 0.5;
  const double ss = 0.8*s;
  const double sq2 = sqrt(2);
  const double sq3 = sqrt(3);

  glPushMatrix();
  if (turning && fabs(vector3Dot(&cube->position.ow.xyz, &turnAxis) - turnLayer) < 0.5)
    glRotated(turnAngle, turnAxis.x, turnAxis.y, turnAxis.z);
  glMultMatrixd(cube->position.a);

  for(int i = 0; i < 6; ++i) {
    glColor4dv(colors[cube->colors[i]]);
    glBegin(GL_QUADS);
    glNormal3d(0, 0, -1);
    glVertex3d(-ss, -ss, -s);
    glVertex3d(-ss,  ss, -s);
    glVertex3d( ss,  ss, -s);
    glVertex3d( ss, -ss, -s);
    glEnd();

    glColor4dv(colors[0]);
    glBegin(GL_QUADS);
    glNormal3d(-sq2, 0, -sq2);
    glVertex3d(-s,  -ss, -ss);
    glVertex3d(-s,   ss, -ss);
    glVertex3d(-ss,  ss,  -s);
    glVertex3d(-ss, -ss,  -s);
    glNormal3d(0, -sq2, -sq2);
    glVertex3d(-ss,  -s, -ss);
    glVertex3d(-ss, -ss,  -s);
    glVertex3d( ss, -ss,  -s);
    glVertex3d( ss,  -s, -ss);
    glEnd();

    glBegin(GL_TRIANGLES);
    glNormal3d(-sq3, -sq3, -sq3);
    glVertex3d(-ss, -ss, -s);
    glVertex3d(-ss, -s, -ss);
    glVertex3d(-s, -ss, -ss);
    glEnd();
    if (i == 0) {
      glBegin(GL_TRIANGLES);
      glNormal3d(-sq3, sq3, -sq3);
      glVertex3d(-s, ss, -ss);
      glVertex3d(-ss, s, -ss);
      glVertex3d(-ss, ss, -s);
      glEnd();
    }
    if (i == 1) {
      glBegin(GL_TRIANGLES);
      glNormal3d(sq3, -sq3, -sq3);
      glVertex3d(s, -ss, -ss);
      glVertex3d(ss, -s, -ss);
      glVertex3d(ss, -ss, -s);
      glEnd();
    }

    glRotated(90, i%2, -((i+1)%2), 0);
  }
  glPopMatrix();
}


void init() {
  background(colorByName("black"));

  Cube *c = cubes;
  for(int ix = -1; ix <= 1; ++ix)
  for(int iy = -1; iy <= 1; ++iy)
  for(int iz = -1; iz <= 1; ++iz, ++c) {
    if (iz < 0) c->colors[0] = 0+1;
    if (ix > 0) c->colors[1] = 1+1;
    if (iy > 0) c->colors[2] = 2+1;
    if (iz > 0) c->colors[3] = 3+1;
    if (ix < 0) c->colors[4] = 4+1;
    if (iy < 0) c->colors[5] = 5+1;
    c->position.ox.x = c->position.oy.y = c->position.oz.z = c->position.ow.w = 1;
    c->position.ow.x = ix;
    c->position.ow.y = iy;
    c->position.ow.z = iz;
  }

  matrixSetIdentity(&position);
  randomTurn();
}


void draw() {
  saveState();

  if (mouseDown("right")) {
    double ay = mouseX() - mx;
    double ax = mouseY() - my;
    Matrix rotation;
    matrixSetRotation(&rotation, ax, 1, 0, 0);
    matrixMultMatrix(&position, &position, &rotation);
    matrixSetRotation(&rotation, ay, 0, 1, 0);
    matrixMultMatrix(&position, &position, &rotation);
  }
  if (mouseDown("middle")) {
    double az = mouseX() - mx;
    Matrix rotation;
    matrixSetRotation(&rotation, az, 0, 0, 1);
    matrixMultMatrix(&position, &position, &rotation);
  }
  mx = mouseX(); my = mouseY();

  if (turning) {
    turnAngle += windowGetFrameTime()*90;
    if (turnAngle > 90) {
      turnAngle = 90;
      Matrix turningMatrix;
      matrixSetRotation(&turningMatrix, turnAngle, turnAxis.x, turnAxis.y, turnAxis.z);
      for(int i = 0; i < 27; ++i) {
        if (fabs(vector3Dot(&cubes[i].position.ow.xyz, &turnAxis) - turnLayer) < 0.5)
          matrixMultMatrix(&cubes[i].position, &cubes[i].position, &turningMatrix);
      }
      turning = FALSE;
      randomTurn();
    }
  }

  glClear(GL_DEPTH_BUFFER_BIT);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_CULL_FACE);
  glEnable(GL_COLOR_MATERIAL);
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);

  Matrix perspective;
  matrixSetPerspective(&perspective, 100.0, windowGetWidth()/(double)windowGetHeight(), 0.01, 100);
  glMatrixMode(GL_PROJECTION);
  glLoadMatrixd(perspective.a);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glTranslated(0, 0, -5);
  glMultMatrixd(position.a);

  for(int i = 0; i < 27; ++i)
    drawCube(&cubes[i]);

  glDisable(GL_DEPTH_TEST);
  restoreState();
}


int main() {
  windowSetVariableFrameRate();
  windowSetResizable(TRUE);
  windowSetInit(&init);
  windowSetDraw(&draw);
  windowRun();
}