// WM-Dots the Window Maker 3d Rotating Dots Demo by Mark I Manning IV
// =======================================================================

// This demo is based on an old DOS demo I did a number of years ago.  The
// trick was to get it to work in an 64 x 64 window instead of the
// 320 x 200 of the original. (and in 16 instead of 256 colours :)

// Much of these sources are based on other dockable application sources.
// For this reason, and because it is the right thing to do, this file is
// distributed under the GPL.  Use it and abuse it as you will.

// Flames, critisizm and general nukings can be sent to....

//  i440r@mailcity.com

// I have included the sources (in 100% pure ASM) to the afore mentioned
// original demo.  These you can also consider covered by the GPL.

// Maybe someone can help me convert the bump mapping in the original to
// use only 16 colours :)  (actually, I have space in the master.xpm for
// 32 colours if I retain a 2 x 2 pixle size.  If i go to 1 x 1 i can
// fit 64 colours in there :)

// Yea... I know... Im supposed to be sparing of system resources like
// colour allocations but come on guys... this IS a demo after all :)
// Demos are supposed to be abusive of system resources eh??? :)

//  This source file is 100% TAB FREE. Use any editor/viewer you want
//  to view it with and set ANY size tabs and it will STILL look right.
//  TABS ARE EVIL!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

// Also Linus T is wrong. Indentations of 8 are an abomination and so is
// k&r source formatting.  So There!

// Apart from that... Linus for president :)
//                    or George W. Bush!!!

// -----------------------------------------------------------------------
// blah :)

// Version 0.1 beta     Released Saturday October 9 1999
//
//    To do...  Fix bug that causes core dump when the "cross" object
//               rotates outside window (its not a clipping problem).
//              Find out why the "cross" object is not cross shaped in
//               this demo when it is in the dos version.
//              Change pixle sizes to 1 x 1 for objects that are in
//               the distance.
//              Move all object definitions out of this file and into
//               some kind of rc file and allow user defined objects
//              Add greets to the credits display :)
//              Impliment better light shading (in object space?)
//              Prevent dark pixles overwriting light ones
//
//    Done...   Scrapped idea about different pixle sizez, it didn't look
//               as good as i thunked it would :)
//              Fixed adjust_angle() to be angle +128 not angle +127
//              Fixed cross by defining xyz as char instead of int
//              Fixed core dump problem with cross:  Oopts it was a
//               clipping problem, z clipping points behind camera! :)
//              Added size macro (prolly a bad name for it tho :)
//              Implimented better light shading but its still not
//               quite right yet...
//              Removed math.h from includes as im not using any
//               functions therein.  Saved a whopping 4 bytes :)
//              Added code to draw_point() to prevent dark pels from
//               being drawn over lighter pels.  Dark pels are further
//               away and should be BEHIND the lighter ones.  There is
//               almost no cpu bash here and I dont need to z sort pels
//
//
// Version 0.2 beta     Released Saturday October 9 1999
//
//    To do..   Still gotta move objects to rc file....
//
//
//
//    Done...

// -----------------------------------------------------------------------
// includes

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <X11/xpm.h>
#include <libdockapp/wmgeneral.h>
#include "master.xpm"
#include "sintab.c"             // sin() and cos() are crap!

// -----------------------------------------------------------------------
// defines

#define PEL_SIZE 2              // 1 or 2 only

#define MAX_POINTS 125          // max # pels in an object

#define MASK_WIDTH 64           // window bitmap mask dimentions
#define MASK_HEIGHT 64
#define MASK_SIZE MASK_WIDTH * MASK_HEIGHT

// adjust angle for cos table lookup.  cos = right angle further
// on in sin table, circle = 512 degrees...

#define adjust(angle) ((angle + 128) & 0x1ff)
#define size(array) ((sizeof(array) / sizeof(array[0])))

// -----------------------------------------------------------------------
// typedefs...

typedef struct                  // a point in 3d space
{
  char x, y, z;
}xyz;

typedef struct                  // a point in 2d space
{
  int x, y;
}xy;

typedef struct
{
  int counter;                  // count down to next speed/shape change
  int reset;                    // initial value of counter
  int *ptr;                     // pointer to item to modify
  int delta;                    // ammount to change item by
  int upper;                    // upper limit for item
  int lower;                    // lower limit for item
}modifier;

// -----------------------------------------------------------------------
// object structures...

xyz cube[]=
{
  {-35,-35,-35},{-35,-35,-15},{-35,-35, 05},{-35,-35, 25},{-35,-35, 45},
  {-35,-15,-35},{-35,-15,-15},{-35,-15, 05},{-35,-15, 25},{-35,-15, 45},
  {-35, 05,-35},{-35, 05,-15},{-35, 05, 05},{-35, 05, 25},{-35, 05, 45},
  {-35, 25,-35},{-35, 25,-15},{-35, 25, 05},{-35, 25, 25},{-35, 25, 45},
  {-35, 45,-35},{-35, 45,-15},{-35, 45, 05},{-35, 45, 25},{-35, 45, 45},
  {-15,-35,-35},{-15,-35,-15},{-15,-35, 05},{-15,-35, 25},{-15,-35, 45},
  {-15,-15,-35},{-15,-15,-15},{-15,-15, 05},{-15,-15, 25},{-15,-15, 45},
  {-15, 05,-35},{-15, 05,-15},{-15, 05, 05},{-15, 05, 25},{-15, 05, 45},
  {-15, 25,-35},{-15, 25,-15},{-15, 25, 05},{-15, 25, 25},{-15, 25, 45},
  {-15, 45,-35},{-15, 45,-15},{-15, 45, 05},{-15, 45, 25},{-15, 45, 45},
  { 05,-35,-35},{ 05,-35,-15},{ 05,-35, 05},{ 05,-35, 25},{ 05,-35, 45},
  { 05,-15,-35},{ 05,-15,-15},{ 05,-15, 05},{ 05,-15, 25},{ 05,-15, 45},
  { 05, 05,-35},{ 05, 05,-15},{ 05, 05, 05},{ 05, 05, 25},{ 05, 05, 45},
  { 05, 25,-35},{ 05, 25,-15},{ 05, 25, 05},{ 05, 25, 25},{ 05, 25, 45},
  { 05, 45,-35},{ 05, 45,-15},{ 05, 45, 05},{ 05, 45, 25},{ 05, 45, 45},
  { 25,-35,-35},{ 25,-35,-15},{ 25,-35, 05},{ 25,-35, 25},{ 25,-35, 45},
  { 25,-15,-35},{ 25,-15,-15},{ 25,-15, 05},{ 25,-15, 25},{ 25,-15, 45},
  { 25, 05,-35},{ 25, 05,-15},{ 25, 05, 05},{ 25, 05, 25},{ 25, 05, 45},
  { 25, 25,-35},{ 25, 25,-15},{ 25, 25, 05},{ 25, 25, 25},{ 25, 25, 45},
  { 25, 45,-35},{ 25, 45,-15},{ 25, 45, 05},{ 25, 45, 25},{ 25, 45, 45},
  { 45,-35,-35},{ 45,-35,-15},{ 45,-35, 05},{ 45,-35, 25},{ 45,-35, 45},
  { 45,-15,-35},{ 45,-15,-15},{ 45,-15, 05},{ 45,-15, 25},{ 45,-15, 45},
  { 45, 05,-35},{ 45, 05,-15},{ 45, 05, 05},{ 45, 05, 25},{ 45, 05, 45},
  { 45, 25,-35},{ 45, 25,-15},{ 45, 25, 05},{ 45, 25, 25},{ 45, 25, 45},
  { 45, 45,-35},{ 45, 45,-15},{ 45, 45, 05},{ 45, 45, 25},{ 45, 45, 45}
};

xyz star[]=
{
  {-35,-35, 05},{-35,-15, 05},{-35, 05, 05},{-35, 25, 05},{-35, 45, 05},
  {-15,-35, 05},{-15,-15, 05},{-15, 05, 05},{-15, 25, 05},{-15, 45, 05},
  { 05,-35, 05},{ 05,-15, 05},{ 05, 05, 05},{ 05, 25, 05},{ 05, 45, 05},
  { 25,-35, 05},{ 25,-15, 05},{ 25, 05, 05},{ 25, 25, 05},{ 25, 45, 05},
  { 45,-35, 05},{ 45,-15, 05},{ 45, 05, 05},{ 45, 25, 05},{ 45, 45, 05},
  { 05,-35,-35},{ 05,-35,-15},{ 05,-35, 25},{ 05,-35, 45},{-35, 05,-35},
  { 05,-15,-35},{ 05,-15,-15},{ 05,-15, 25},{ 05,-15, 45},{-15, 05,-35},
  { 05, 05,-35},{ 05, 05,-15},{ 05, 05, 25},{ 05, 05, 45},{ 05, 05,-35},
  { 05, 25,-35},{ 05, 25,-15},{ 05, 25, 25},{ 05, 25, 45},{ 25, 05,-35},
  { 05, 45,-35},{ 05, 45,-15},{ 05, 45, 25},{ 05, 45, 45},{ 45, 05,-35},
  {-35, 05,-15},{-35, 05, 25},{-35, 05, 45},{-15, 05,-15},{-15, 05, 25},
  {-15, 05, 45},{ 05, 05,-15},{ 05, 05, 25},{ 05, 05, 45},{ 25, 05,-15},
  { 25, 05, 25},{ 25, 05, 45},{ 45, 05,-15},{ 45, 05, 25},{ 45, 05, 45}
};

xyz dots[]=
{
  {-35,-35,-35},{-35,-35, 45},{ 45, 45,-35},{ 45, 45, 45},{ 5,-35, 5},
  {  5, 45,  5}
};

xyz square[]=
{
  {-35,-35,-35},{-35,-15,-35},{-35, 05,-35},{-35, 25,-35},{-35, 45,-35},
  {-15,-35,-35},{-15,-15,-35},{-15, 05,-35},{-15, 25,-35},{-15, 45,-35},
  { 05,-35,-35},{ 05,-15,-35},{ 05, 05,-35},{ 05, 25,-35},{ 05, 45,-35},
  { 25,-35,-35},{ 25,-15,-35},{ 25, 05,-35},{ 25, 25,-35},{ 25, 45,-35},
  { 45,-35,-35},{ 45,-15,-35},{ 45, 05,-35},{ 45, 25,-35},{ 45, 45,-35}
};

xyz cross[]=
{
  {0x00,0x00,0x19}, {0x00,0x05,0x19}, {0x00,0x14,0x01}, {0x00,0x32,0x00},
  {0x00,0x7D,0x00}, {0x00,0xC9,0x00}, {0x01,0x5F,0x01}, {0x01,0xAB,0x01},
  {0x01,0xF6,0x01}, {0x02,0x41,0x02}, {0x02,0x8D,0x02}, {0x02,0xD8,0x02},
  {0x03,0x23,0x03}, {0x03,0x05,0x04}, {0x03,0x6F,0x03}, {0x03,0xBA,0x03},
  {0x04,0x51,0x04}, {0x04,0x9C,0x04}, {0x04,0xE7,0x04}, {0x05,0x13,0x06},
  {0x05,0x32,0x05}, {0x05,0x7D,0x05}, {0x05,0xC8,0x05}, {0x06,0x5E,0x06},
  {0x06,0xA9,0x06}, {0x06,0xF4,0x06}, {0x07,0x3F,0x07}, {0x07,0x8A,0x07},
  {0x07,0xD5,0x07}, {0x08,0x00,0x09}, {0x08,0x20,0x08}, {0x08,0x6B,0x08},
  {0x08,0xB5,0x08}, {0x09,0x4B,0x09}, {0x09,0x95,0x09}, {0x09,0xE0,0x09},
  {0x0A,0x09,0x0B}, {0x0A,0x2A,0x0A}, {0x0A,0x75,0x0A}, {0x0A,0xBF,0x0A},
  {0x0B,0x54,0x0B}, {0x0D,0x07,0x26}, {0x0F,0x02,0x28}, {0x19,0x09,0x32},
  {0x1E,0x04,0x37}, {0x22,0x0B,0x3B}, {0x2C,0x06,0x45}, {0x2D,0x01,0x46},
  {0x39,0x08,0x52}, {0x3D,0x03,0x56}, {0x43,0x0A,0x5C}, {0x4B,0x00,0x64},
  {0x4B,0x05,0x64}, {0x58,0x07,0x71}, {0x5B,0x02,0x74}, {0x64,0x09,0x7C},
  {0x84,0x08,0x9C}, {0x88,0x03,0xA1}, {0x8D,0x0A,0xA6}, {0x96,0x00,0xAF},
  {0x96,0x05,0xAF}, {0xA3,0x07,0xBC}, {0xA6,0x02,0xBF}, {0xAE,0x09,0xC7},
  {0xB5,0x04,0xCE}, {0xC2,0x06,0xDB}, {0xC4,0x01,0xDD}, {0xCE,0x08,0xE7},
  {0xD3,0x03,0xEC}, {0xD8,0x0A,0xF1}, {0xE1,0x05,0xFA}, {0xE2,0x00,0xFB},
  {0xEE,0x07,0x07}, {0xF1,0x02,0x0A}, {0xF9,0x09,0x11}
};

// -----------------------------------------------------------------------
// object lists...

xyz *obj_list[]=                // pointers to each object
{
  (xyz *) &cube,
  (xyz *) &dots,
  (xyz *) &star,
  (xyz *) &square,
  (xyz *) &cross
};

int point_counts[]=             // number of points in each object
{
  size(cube),
  size(dots),
  size(star),
  size(square),
  size(cross)
};

#define NUM_OBJECTS size(obj_list)

// -----------------------------------------------------------------------
// i love global variables :)

// I hate 40 page functions with 50 levels if,and and but loop nesting

char *ProgName = NULL;
char *Version = "0.2 Beta";

int x_angle = 0;                // angles of rotation in each axis
int y_angle = 0;
int z_angle = 0;

short cos_x = 0;                // trig stuff
short cos_y = 0;
short cos_z = 0;
short sin_x = 0;
short sin_y = 0;
short sin_z = 0;

int x_off = 30;                 // world space position of object
int y_off = 30;
int z_off = 150;

int delta_x = 1;                // rotational speed of object
int delta_y = 1;
int delta_z = 1;

int itters = 1;                 // number of frames till shape change
int num_points = 0;             // number of points in object
int obj_number = 0;
xyz *object = NULL;             // pointer to current object

xy trail_1[125];                // frame histories
xy trail_2[125];
xy trail_3[125];

char *p_buff;                   // addr of pel buffer ( see draw_point() )

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

modifier w1 =                   // changes x rotation speed
{
   30,                          // count down counter
   34,                          // reset value for countdown counter
   &delta_x,                    // item to modify on count = 0
   2,                           // ammount to add to item
   8,                           // upper limit for item
   0,                           // lower limit for item
};

modifier w2 =                   // changes y rotation speed
{
  20, 20, &delta_y, -1, 6, 0
};

modifier w3 =                   // changes z rotation speed
{
  30, 30, &delta_z, 1, 7, 0
};

modifier w4 =                   // zooms object in / out of window
{
  4, 4, &z_off, -2, 200, 90
};

// modifier w5 =                // these two do some funky things with
// {                            // object space but they tend to make
//   10,10,&x_off,-3,30,10      // the objects small and look further
// };                           // away so im not using them
//
// modifier w6 =                // but go ahead and take a look at what
// {                            // they do :)
//   10,30,&y_off,-5,30,10
// };

// -----------------------------------------------------------------------
// draw a point at x/y in specified colour

void draw_point(int x, int y, int c)
{
  char *p;

  if(x != -1)                   // is point clipped ?
  {
    c <<= 1;                    // adjust c for xpm lookup of colour
    p = p_buff + (x + (MASK_WIDTH * y));

    if (*p > c)                 // if pel at *p is higher its darker so
    {                           // its ok to overwrite it
      *p = c;                   // remember pel colour at this position
      copyXPMArea(c, 65, PEL_SIZE, PEL_SIZE, x, y);
    }
  }
}

// -----------------------------------------------------------------------
// Erase previously drawn point (draw again in BLACK!!!)

void erase_point(int x, int y)
{
  if(x != -1)                   // is point clipped?
  {
    copyXPMArea(34, 65, PEL_SIZE, PEL_SIZE, x, y);
  }
}

// -----------------------------------------------------------------------
// Process pending X events

void do_pending(void)
{
  XEvent Event;

  while (XPending(display))             // for all pending events do...
  {
    XNextEvent(display, &Event);        // get event type

    switch (Event.type)                 // we are only interested in...
    {
      case Expose:                      // expose events and...
        RedrawWindow();
      break;

      case DestroyNotify:               // our own "pending" demise :)
        XCloseDisplay(display);
        exit(0);
      break;
    }
  }
}

// -----------------------------------------------------------------------
// clear frame history buffers

void clear_histories(void)
{
  int i = MAX_POINTS;

  while (i--)                           // for loops suck
  {
    trail_1[i].x = trail_1[i].y = -1;   // -1 = invalid coordinate
    trail_2[i].x = trail_2[i].y = -1;   // draw_point() ignores -1
    trail_3[i].x = trail_3[i].y = -1;
  }
}

// -----------------------------------------------------------------------
// erase points that are 3 frames old. shift frame histories

void do_trails(void)
{
  int i = MAX_POINTS;

  while (i--)
  {
    erase_point(trail_3[i].x, trail_3[i].y);

    trail_3[i].x = trail_2[i].x;        // shift points within history
    trail_2[i].x = trail_1[i].x;        //  buffers
    trail_1[i].x = -1;

    trail_3[i].y = trail_2[i].y;
    trail_2[i].y = trail_1[i].y;
    trail_1[i].y = -1;
  }
}

// -----------------------------------------------------------------------
// pre calculate sin and cosine values for x y and z angles of rotation

void wmdots_sincos(void)
{
  sin_x = sin_tab[x_angle];
  sin_y = sin_tab[y_angle];
  sin_z = sin_tab[z_angle];

  cos_x = sin_tab[adjust(x_angle)];
  cos_y = sin_tab[adjust(y_angle)];
  cos_z = sin_tab[adjust(z_angle)];
}

// -----------------------------------------------------------------------
// roatate object about x y and z axis (in object space)

void rotate(int *px, int *py, int *pz)
{
  int tx, ty, tz;                       // temp store

  if (x_angle)                          // rotate point about x axis...
  {
    ty = (*py * cos_x) - (*pz * sin_x);
    tz = (*py * sin_x) + (*pz * cos_x);

    *py = (ty >> 14);                   // sin table is scaled up so we
    *pz = (tz >> 14);                   // must re scale all results down
  }

  if (y_angle)                          // rotate point about y axis
  {
    tx = (*px * cos_y) - (*pz * sin_y);
    tz = (*px * sin_y) + (*pz * cos_y);

    *px = (tx >> 14);
    *pz = (tz >> 14);
  }

  if (z_angle)                          // rotate point about z axis
  {
    tx = (*px * cos_z) - (*py * sin_z);
    ty = (*px * sin_z) + (*py * cos_z);

    *px = (tx >> 14);
    *py = (ty >> 14);
  }
}

// -----------------------------------------------------------------------
// project point in 3d space onto plane in 2d space

void project(int px, int py, int pz, int *x, int *y)
{
  int tx, ty;                   // temp store...

  *x = *y = -1;                 // assume point is clipped

  if ((z_off + pz - 5) < 0)
  {
    return;
  }

  ty = ((y_off * py) / (z_off + pz)) + 30;

  if ((ty > 5) && (ty < 59))
  {
    tx = ((x_off * px) / (z_off + pz)) + 30;

    if ((tx > 5) && (tx < 59))
    {
      *x = tx;
      *y = ty;
    }
  }
}

// -----------------------------------------------------------------------
// draw one frame of object...

void do_frame(void)
{
  int px, py, pz;               // 3d coordinates to rotate
  int x, y, c;                  // 2d coordiantes of point and colour
  int i = num_points;           // loop counter
  int j = MASK_SIZE;
  char pel_buff[MASK_SIZE];     // frame buffer ( see draw_point() )
  p_buff = &pel_buff[0];

  while (j--)                   // erase pel buffer
  {
    pel_buff[j] = 32;
  }

  do_trails();                  // clear pels that are 3 frames old
  wmdots_sincos();                     // calculate all sin/cos values

  while(i--)
  {
    px = object[i].x;           // collect point from object
    py = object[i].y;
    pz = object[i].z;

    rotate(&px, &py, &pz);      // rotate this point about x/y and z axis
    project(px, py, pz, &x, &y); // projection = convert xyz to xy

    trail_1[i].x = x;           // store frame history for all pels
    trail_1[i].y = y;           //  of this frame

    c = (((z_off - 90) / 14) & 15) < 1;
    c -= ((-pz / 5) - 10);

    draw_point(x, y, c);
  }
}

// -----------------------------------------------------------------------
// adjust rotational speeds / distance between min and max for each

void modify(modifier *mod)
{
  mod->counter--;

  if (!mod->counter)
  {
    mod->counter = mod->reset;

    *mod->ptr += mod->delta;

    if (*mod->ptr >= mod->upper || *mod->ptr <= mod->lower)
    {
      mod->delta = -(mod->delta);
    }
  }
}

// -----------------------------------------------------------------------
// do the above on each of the 4 modifiers

void do_deltas(void)
{
  modify(&w1);                  // modify x rotational speed
  modify(&w2);                  // modify y rotational speed
  modify(&w3);                  // modify z rotational speed
  modify(&w4);                  // zoom object in and out
//  modify(&w5);		  // not used because they make objects
//  modify(&w6);		  // stay in the distance
}

// -----------------------------------------------------------------------
// adjust x y and z angles of ritation for next frame

void change_angles(void)
{
  x_angle += delta_x;
  y_angle += delta_y;
  z_angle += delta_z;

  x_angle &= 0x1ff;
  y_angle &= 0x1ff;
  z_angle &= 0x1ff;
}

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

void do_demo(void)
{
  while (1)                     // only way out is to die
  {
    while(--itters)             // countdown to next shape change...
    {                           //   shape change FORCED on entry
      change_angles();          // adjust angles of rotation
      do_frame();               // draw object
      do_deltas();              // modify rotation speeds etc

      RedrawWindow();           // lets see what weve drawn...
      do_pending();             // quit / expose
      usleep(50000);            // erm... coders never sleep :)
    }

    itters = 2500;              // should randomise this

    object = obj_list[obj_number];
    num_points = point_counts[obj_number++];

    if (obj_number == NUM_OBJECTS)
    {
      obj_number = 0;
    }
  }
}

// -----------------------------------------------------------------------
// display version info and credits

void credits(void)
{
  printf("\n");
  printf("WM-Dots DEMO? version %s\n",Version);
  printf("    by Mark I Manning IV\n\n");
  printf("greets to follow soon....\n\n");
}

// sgore for helping me get star office :)

// -----------------------------------------------------------------------
// scan command line args

void scan_args(int argc,char **argv)
{
  int i;

  for (i = 0; i < argc; i++)
  {
    char *arg = argv[i];

    if (*arg == '-')
    {
      switch (arg[1])
      {
        case 'v':
          credits();
          exit(0);
        break;

        default:
          ;
        break;
      }
    }
  }
}

// -----------------------------------------------------------------------
// main at BOTTOM of source so we have no EVIL forward refs!!

int main(int argc, char *argv[])
{
  char mask[MASK_SIZE];

  ProgName = argv[0];
  if (strlen(ProgName) >= 5)
  {
     ProgName += (strlen(ProgName) - 5);
  }

  scan_args(argc, argv);

  createXBMfromXPM(mask, master_xpm, MASK_WIDTH, MASK_HEIGHT);
  openXwindow(argc, argv, master_xpm, mask, MASK_WIDTH, MASK_HEIGHT);

  clear_histories();            // clear frame history buffers
  do_demo();                    // main = short and sweet :)

  return(0);                    // never gets executed but lets get rid
}                               //  of that stupid warning :)

// =======================================================================
