/*

  MindLink

  Coded by Gautier "Impulse" PORTET

  09/97 - 12/97

  (c) 1997-2002 KNIGHTS 

  not so fast code
  ugly design
  buggy module player
  but here is it: win32/linux versions
  
  
  Contact me:
  gautier@tlk.fr
*/

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <allegro.h>
#include "data.h"


//#define MIDAS

#define USE_JGMOD
//#define USE_MINIFMOD

#ifdef USE_JGMOD
#include <jgmod.h>
JGMOD *module;
#endif

unsigned short TunnelLookup[320*200];
unsigned short TunnelLookup2[320*200];
unsigned short PlasmaLookup[320*200];
unsigned short PlasmaLookup2[320*200];

unsigned char _image[320*200];
unsigned char _map[256*256];

BITMAP * buffer;
DATAFILE *data;

int mode=GFX_AUTODETECT_WINDOWED;

//int iVBLframe,VBLdelta,__old;
int count;

volatile int VBLframe;

PALETTE *palWater;
COLOR_MAP *tableWater,*tableGlenz;

RLE_SPRITE *text[11];


void Fatal(char* str)
{
  printf("Fatal : %s\n", str);
  exit(0);
}

/* all possible methods to draw a face */
enum K3DTfilling
{
  PLOT=0,
  WIREFRAME,
  FLAT,
  GOURAUD,
  GOURAUD_RGB,
  TEXTURE,
  TEXTURE_MASK,
  TEXTURE_SHADED,
  TEXTURE_CORRECTED,
  TEXTURE_MASK_CORRECTED,
  TEXTURE_SHADED_CORRECTED
};

/* type of sorting - bytesort is the fastest.*/
enum K3DTsorting
{
  NONE=0,
  QSORT,
  BYTESORT,
  FORCE_BACKFACE=128
};

/* the method used to compute faces light */
enum K3DTcolorModel
{
//  NONE=0,
  Z=1,
  NORMAL
};


typedef struct K3DTRendering
{
  enum K3DTfilling filling;
  enum K3DTsorting sorting;
  enum K3DTcolorModel  colorModel;
} K3DTRendering;


/* a 3D point / a vector ( u & v : for the texture mapping, c: color )  */
typedef struct K3DTVertex
{
  fixed x, y, z;
  fixed u,v;
  int color;
} K3DTVertex;

/* a face [WARNING K3D can ONLY HANDLE TRIANGLES !!]*/
typedef struct K3DTFace
{
  int v1, v2, v3;
  int color;
  BITMAP * map;
  char visible;
  int z; // *****
} K3DTFace;

/* the positions and rotations ( used as the place of an object or its speed )*/
typedef struct K3DTPlace
{
  fixed x, y, z;
  fixed rx, ry, rz;
} K3DTPlace;

/* an object : 2 arrays with vertexes & faces + its location & orientation */
typedef struct K3DTObject
{
  int                         vertexes,faces;
  struct K3DTVertex          *vertex;
  struct K3DTVertex          *rotated;
  struct K3DTVertex          *projected;
  struct K3DTFace            *face;
  struct K3DTPlace            place;
  struct K3DTRendering        rendering;
  fixed                       scale;
  fixed                       clipping;
} K3DTObject;

/* an array of objects */
typedef struct K3DTWorld
{
  int objects;
  struct K3DTObject *object;
  struct K3DTPlace place;
} K3DTWorld;

unsigned char faceOrder[256],faceDepth[256];


void K3DInit(void)
{
}

void K3DInitObject(struct K3DTObject * obj)
{
  obj->scale = itofix(1);
  obj->vertexes = 0;
  obj->faces = 0;
  obj->vertex = NULL;
  obj->face = NULL;
  obj->clipping = itofix(5);
  obj->rendering.sorting = NONE;
  obj->rendering.colorModel = NORMAL;
  obj->rendering.filling = FLAT;
  
}

void K3DInitWorld(struct K3DTWorld * obj)
{
  obj->objects = 0;
  obj->object = NULL;
}

void K3DDeleteObject(struct K3DTObject * obj)
{
  if(obj->vertex != NULL)
    free(obj->vertex);
  if(obj->face != NULL)
    free(obj->face);
}

void K3DDeleteWorld(struct K3DTWorld * obj)
{
  int i;
  
  if(obj->object != NULL)
    for(i = 0;i < obj->objects;i++)
    K3DDeleteObject(&obj->object[i]);
}

  /* internal helper : load a file line */
  int LoadLine(FILE * f, char * line)
  {
  char c;
  int i = 0;
  
  line[0] = 0;
  do
  {
    c = fgetc(f);
    if(c == EOF)
      return 0;
    if(c == '\n')
      return 1;
    line[i++] = c;
    line[i] = 0;
  }
  while(1);
}

int K3DLoadObjectASC(char * file, struct K3DTObject * obj)
{
  
  FILE * f;
  int i,fin;
  char line[250],temp[255];
  long   faces,vertexes;
  
  line[0] = 0;
  f = fopen(file, "rb");
  if(f == NULL) return 0;
  do
  {
    /* until a tri-mesh found */
    do
    {
      /* search for all objects */
      LoadLine(f, line);
    } while(strncmp(line, "Named object:", 13) != 0);
    LoadLine(f, line);
  } while(strncmp(line, "Tri-mesh", 8) != 0);
  
  /* Tri-mesh, Vertices: 410     Faces: 718 */
  /*                   ^ here we go  */
  strcpy(line, strchr(line, ':')+2);
  strncpy(temp, line, strchr(line, 'F')-line);
  /*410     Faces: 718 */
  /*<------> get this  */
  temp[strchr(line, 'F')-line] = 0;
  vertexes = atoi(temp);
  
  strcpy(line, strchr(line, ':')+2);
  faces = atoi(line);
  
  obj->vertex = (struct K3DTVertex*) malloc(vertexes*sizeof(struct K3DTVertex));
  obj->rotated = (struct K3DTVertex*) malloc(vertexes*sizeof(struct K3DTVertex));
  obj->projected = (struct K3DTVertex*) malloc(vertexes*sizeof(struct K3DTVertex));
  obj->face = (struct K3DTFace*) malloc(faces*sizeof(struct K3DTFace));
  
  LoadLine(f, line);
  i = 0;
  fin = 0;
  do
  {
    /* load all vertexes */
    LoadLine(f, line);
    //      printf("\r%s",line);
    if(strncmp(line, "Face list:", 10) != 0)
    {
      /* convert vertex */
      
      if((strstr(line, "X:")) != NULL)
      {
        /* Get the x,y,z vertexes */
        strcpy(line, strstr(line, "X:")+3);
        strcpy(temp, line);
        temp[strstr(line, "Y:")-line] = 0;
        obj->vertex[i].x = itofix(atoi(temp));
        
        strcpy(line, strstr(line, "Y:")+3);
        strcpy(temp, line);
        temp[strstr(line, "Z:")-line] = 0;
        obj->vertex[i].y = itofix(atoi(temp));
        
        strcpy(line, strstr(line, "Z:")+3);
        strcpy(temp, line);
        obj->vertex[i].z = itofix(atoi(temp));
        i++;
        
      }
      else
      {
        }
      
    }
    else
      fin = 1;
    
  } while(!fin);
  
  if(i != vertexes)
  {
    printf("Error while parsing vertexes (%d found,%ld expexted)", i, vertexes);
    exit(1);
  }
  
  for(i = 0;i < faces;i++)
  {
    do
    {
      if(!LoadLine(f, line))
      {
        printf("Unexpected EOF\n");
        exit(2);
      }
    }
    while((strstr(line, "Face")) == NULL);
    /*Face 717:    A:407 B:236 C:231 AB:1 BC:1 CA:1*/
    
    /* Get the face's vertexes */
    strcpy(line, strstr(line, "A:")+2);
    strcpy(temp, line);
    obj->face[i].v1 = atoi(temp);
    
    strcpy(line, strstr(line, "B:")+2);
    strcpy(temp, line);
    obj->face[i].v2 = atoi(temp);
    
    strcpy(line, strstr(line, "C:")+2);
    strcpy(temp, line);
    obj->face[i].v3 = atoi(temp);
    
  }
  
  obj->vertexes = vertexes;
  obj->faces = faces;
  
  fclose(f);
  return 1;
}

void K3DSetObjectRendering(struct K3DTObject *obj, int f, int s, int c)
{
  obj->rendering.filling = f;
  obj->rendering.sorting = s;
  obj->rendering.colorModel = c;
}

void K3DSetObjectMap(struct K3DTObject *obj, BITMAP* map)
{
  int i;
  
  for(i = 0;i < obj->faces;i++)
    obj->face[i].map = map;
}

void K3DEnvMapObject(struct K3DTObject *obj)
{
  int i;
  
  //  if(obj->rendering.colorModel==Z)
{
  for(i = 0;i < obj->vertexes;i++)
  {
    obj->projected[i].u = obj->rotated[i].x-obj->rotated[i].z;
    obj->projected[i].v = obj->rotated[i].y-obj->rotated[i].z;
  }
}
}

void K3DPlaceObject(struct K3DTObject *obj, fixed x, fixed y, fixed z, fixed rx, fixed ry, fixed rz)
{
  obj->place.x = x;
  obj->place.y = y;
  obj->place.z = z;
  obj->place.rx = rx;
  obj->place.ry = ry;
  obj->place.rz = rz;
}

int qsort_FaceCompare(const void * f1, const void * f2)
{
  return ((((struct K3DTFace*)f2)->z)-(((struct K3DTFace*)f1)->z));
}

int byteSort(char * array, char * dest, int size)
{
  static int lookup[256];
  static int lookup2[256];
  int i;
  
  for(i = 255;i >= 0;i--)
    lookup[i] = 0;
  for(i = size;i >= 0;i--)
    lookup[array[i]]++;
  lookup2[0] = lookup[0];
  for(i = 1;i < 255;i++)
    lookup2[i] = lookup[i]+lookup2[i-1];
  for(i = size;i >= 0;i--)
    dest[lookup2[array[i]]] = array[i];
}

void K3DRotateObject(struct K3DTObject *obj)
{
  MATRIX matrix;
  int i;
  
  get_transformation_matrix(&matrix, obj->scale, obj->place.rx, obj->place.ry, obj->place.rz, obj->place.x, obj->place.y, obj->place.z);
  //  get_translation_matrix(&matrix, obj->place.x, obj->place.y, obj->place.z);
  
  for(i = 0;i < obj->vertexes;i++)
  {
    apply_matrix(&matrix, obj->vertex[i].x, obj->vertex[i].y, obj->vertex[i].z, &obj->rotated[i].x, &obj->rotated[i].y, &obj->rotated[i].z);
  }
  for(i = 0;i < obj->faces;i++)
  {
    faceDepth[i] = fixtoi(obj->rotated[obj->face[i].v1].z+obj->rotated[obj->face[i].v2].z+obj->rotated[obj->face[i].v3].z)/4;
    obj->face[i].z = obj->rotated[obj->face[i].v1].z+obj->rotated[obj->face[i].v2].z+obj->rotated[obj->face[i].v3].z;
    obj->face[i].color = 255-(faceDepth[i]/2);
    if((obj->rotated[obj->face[i].v1].z < obj->clipping) ||
        (obj->rotated[obj->face[i].v2].z < obj->clipping) ||
        (obj->rotated[obj->face[i].v3].z < obj->clipping))
      obj->face[i].visible = 0;
    else
      obj->face[i].visible = 1;
    
  }
  if(obj->rendering.colorModel == Z)
  {
    for(i = 0;i < obj->faces;i++)
    {
      obj->face[i].color = 255-(obj->face[i].z>>16)/8;
      obj->projected[obj->face[i].v1].color =
        obj->projected[obj->face[i].v2].color =
        obj->projected[obj->face[i].v3].color = obj->face[i].color;
    }
  }
  
  if(obj->rendering.sorting == QSORT)
  {
    qsort(obj->face, obj->faces, sizeof(K3DTFace), qsort_FaceCompare);
  }
  if(obj->rendering.sorting == BYTESORT)
  {
    byteSort(faceDepth, faceOrder, obj->faces);
  }
}

void K3DProjectObject(struct K3DTObject *obj)
{
  int i;
  
  for(i = 0;i < obj->vertexes;i++)
  {
    persp_project(obj->rotated[i].x, obj->rotated[i].y, obj->rotated[i].z, &obj->projected[i].x, &obj->projected[i].y);
    //    obj->projected[i].z = obj->rotated[i].z;
  }
}

void K3DDrawObject(BITMAP * buffer, struct K3DTObject *obj)
{
  int i,c;
  
  if(obj->rendering.filling == PLOT)
  {
    for(i = 0;i < obj->vertexes;i++)                                         /*(obj->projected[i].y>>15)*/
    {
      c = 120+(obj->projected[i].y>>16);
      putpixel(buffer, (obj->projected[i].x>>16), (obj->projected[i].y>>16), c);//(obj->projected[i].y>>16));
      putpixel(buffer, (obj->projected[i].x>>16)+1, (obj->projected[i].y>>16), c);//(obj->projected[i].y>>16));
      putpixel(buffer, (obj->projected[i].x>>16), (obj->projected[i].y>>16)+1, c);//(obj->projected[i].y>>16));
      putpixel(buffer, (obj->projected[i].x>>16)+1, (obj->projected[i].y>>16)+1, c);//(obj->projected[i].y>>16));
    }
  }
  else
    switch(obj->rendering.filling)
    {
      case FLAT:
      for(i = 0;i < obj->faces;i++)
      {
        if(obj->face[i].visible)
          triangle(buffer, 
            obj->projected[obj->face[i].v1].x>>16, 
            obj->projected[obj->face[i].v1].y>>16, 
            obj->projected[obj->face[i].v2].x>>16, 
            obj->projected[obj->face[i].v2].y>>16, 
            obj->projected[obj->face[i].v3].x>>16, 
            obj->projected[obj->face[i].v3].y>>16, 
            obj->face[i].color);
      }
      break;
      case GOURAUD:
      for(i = 0;i < obj->faces;i++)
      {
        if(obj->face[i].visible)
          triangle3d(buffer, 
            POLYTYPE_GCOL, 
            NULL, 
            (V3D*)&obj->projected[obj->face[i].v1], 
            (V3D*)&obj->projected[obj->face[i].v2], 
            (V3D*)&obj->projected[obj->face[i].v3]
            );
      }
      break;
      case TEXTURE:
      for(i = 0;i < obj->faces;i++)
      {
        if(obj->face[i].visible)
          triangle3d(buffer, 
            POLYTYPE_ATEX, 
            obj->face[i].map, 
            (V3D*)&obj->projected[obj->face[i].v1], 
            (V3D*)&obj->projected[obj->face[i].v2], 
            (V3D*)&obj->projected[obj->face[i].v3]
            );
      }
      break;
      case TEXTURE_CORRECTED:
      for(i = 0;i < obj->faces;i++)
      {
        if(obj->face[i].visible)
          triangle3d(buffer, 
            POLYTYPE_PTEX, 
            obj->face[i].map, 
            (V3D*)&obj->projected[obj->face[i].v1], 
            (V3D*)&obj->projected[obj->face[i].v2], 
            (V3D*)&obj->projected[obj->face[i].v3]
            );
      }
      break;
    }
  
}
void K3DRenderObject(BITMAP * buffer, struct K3DTObject *obj)
{
  K3DRotateObject(obj);
  K3DProjectObject(obj);
  K3DDrawObject(buffer, obj);
}

void DrawObject(struct K3DTObject *obj);


void my_timer_handler()
{
	VBLframe++;
}
END_OF_FUNCTION(my_timer_handler);


void WaitVBL()
{
	static int yop=0;
	RGB color;
	/*
	float delta,diff;
	
	delta = fVBLframe- (float)retrace_count;
	fVBLframe += delta / 10.0f;
	*/
  if(key[KEY_TILDE])
  {
    color.r = 40;
    set_color(0, &color);
    vsync();
    color.r = color.g = color.b = 0;
    set_color(0, &color);
  }

  
  VBLframe = retrace_count;
//    VBLframe = yop++;
  }

void MIDASerror(void)
{
    exit(EXIT_FAILURE);
}

/*
void MIDAS_CALL prevr(void)
{
  VBLframe++;
}
*/

int GetPosition(void)
{
	if(key[KEY_ESC])
		return 0xffff;
#ifdef USE_JGMOD
	return mi.trk*256+mi.pos;
#endif
}


void Load(void)
{
  void draw_bar(int pos)
  {
		rectfill(screen, 192+128+16*(pos/32), 270, 14+192+128+16*(pos/32), 286, 32);
  }
  
  
  RGB color;
  register int i,x,y;
  
  PALETTE pal;
  
  BITMAP *titre = data[BMP_winText].dat;
  //load_bitmap("gfx/winlogo.bmp", pal);
  BITMAP *menu = data[BMP_winMenu].dat;//load_bitmap("gfx/win.bmp", pal);
  //  pal          = data[PAL_winMenu].dat;
  memcpy(pal, data[PAL_winMenu].dat, sizeof(pal));
  
  
  set_gfx_mode(mode, 640, 480, 0, 0);
  
  for(x = 0;x < 330;x++)
    for(y = 0;y < 36;y++)
    putpixel(menu, x, y, getpixel(menu, x, y)+64);
  //      menu->line[y][x] = menu->line[y][x] +64;
  
  for(i = 0;i < 16;i++)
  {
    pal[i+64].r = pal[i].r;
    pal[i+64].g = pal[i].g;
    pal[i+64].b = pal[i].b;
  }
  pal[255].r = 63;
  pal[255].g = 63;
  pal[255].b = 63;
  for(i = 0;i < 64;i++)
  {
    color.r = color.g = color.b = 0;
    set_color(i, &color);
  }
  
  vsync();
  
  for(x = 0;x < 640;x++)
    for(y = 0;y < 480;y++)
  {
    i = (i+(rand()>>29)+((y*2)/15))/2;
    if(i < 1) i = 1;
    if(i > 63) i = 63;
    _putpixel(screen, x, y, i);
  }
  vsync();
  for(i = 0;i < 64;i++)
  {
    pal[i].r = pal[i].g = 0;
    pal[i].b = 64-i;
  }
  
  rectfill(screen, 154, 169, 484, 309, 64+7);
  blit(menu, screen, 1, 1, 154, 169, 330, 36);
  line(screen, 154, 169, 154, 309, 64+15);
  line(screen, 484, 309, 154, 309, 64+8);
  line(screen, 484, 169, 484, 309, 64+8);
  
  line(screen, 190, 268, 190, 288, 64+8);
  line(screen, 190, 268, 449, 268, 64+8);
  line(screen, 449, 268, 449, 288, 64+15);
  line(screen, 190, 288, 449, 288, 64+15);
  text_mode(-1);
  textout_centre(screen, font, "MindLink 1.0", 321, 215, 64+0);
  textout_centre(screen, font, "Building", 321, 240, 64+0);
  
  draw_sprite(screen, titre, 1, 1);
  set_palette(pal);
  vsync();
  
  
  for(x = 0;x < 320;x++)
  {
    if(x%20)
      rectfill(screen, 192+16*(x/40), 270, 14+192+16*(x/40), 286, 32);
    
		//rest(1);
		
    for(y = 0;y < 200;y++)
    {
      fixed n,m;
      
      if((y-100) != 0)
        TunnelLookup[320*y+x] = (
          /*(random()%2)+*/fixtoi(fatan(fdiv(itofix(x-160), itofix(y-100)))))<<9;
      else
        TunnelLookup[320*y+x] = (128)<<8;
      
      PlasmaLookup[320*y+x] = TunnelLookup[320*y+x];
      
      //         TunnelLookup[320*y+x] += x;
      n = itofix(x-160);
      m = itofix(y-100);

      PlasmaLookup2[320*y+x] = TunnelLookup2[320*y+x] = TunnelLookup[320*y+x]/256;

      if(sqrt((int)(x-160)*(x-160)+(y-100)*(y-100)) != 0)
      {
        TunnelLookup[320*y+x] += (4096)/(int)sqrt((x-160)*(x-160)+(y-100)*(y-100));
        //+ (random()%2);
        PlasmaLookup[320*y+x] += (int)sqrt((x-160)*(x-160)+(y-100)*(y-100));
        TunnelLookup2[320*y+x]+= 256*TunnelLookup[320*y+x];
        PlasmaLookup2[320*y+x]+= 256*PlasmaLookup[320*y+x];
        //+ (random()%2);
      }

    }
  }
  {
    PALETTE pal;

    memcpy(pal,data[PAL_turtle].dat,sizeof(PALETTE));
    for(i = 0;i < 128;i++)
    {
      // picture palette
      pal[i+128].r = pal[i].r;
      pal[i+128].g = pal[i].g;
      pal[i+128].b = pal[i].b;
    
      // water gradient
      pal[i].r = pal[i].g = i/1;
      pal[i].b = 2*i/1;
    }
    tableWater = malloc(sizeof(COLOR_MAP));
    create_trans_table(tableWater, pal, 128, 128, 128, draw_bar);
  }
  {
    /* load texts */
    for(i=0;i<11;i++)
    {
      BITMAP *_text;
      _text   = create_bitmap(320,27);
      blit(data[BMP_text].dat,_text,0,i*27,0,0,320,27);
      text[i] = get_rle_sprite(_text);
    }
  }

  tableGlenz = malloc(sizeof(COLOR_MAP));
  for(i = 0xffff;i >= 0;i--)
  {
    int c;
    c = ((i&255)+(i>>9));
    if(c > 255)
      c = 255;
    *((unsigned char*)tableGlenz+i) = c;
  }
  //  while(!key[KEY_ESC])
  //  {}
  
}

// not used: not working :)
void Bump(void)
{
  RGB color;
  int i,x,y,frame,delta,old;
  int lx,ly,dx,dy,vx,vy,c,x1,y1,x2,y2,tx,ty;
  
  BITMAP * map,*buffer,*deltas,*lightMap;
  PALETTE pal;
  
  
  
  map = load_bitmap("height.pcx", pal);
  buffer = create_bitmap(320, 200);
  deltas = create_bitmap(640, 200);
  lightMap = create_bitmap(256, 256);
  
  set_gfx_mode(mode, 320, 200, 0, 0);
  
  clear(screen);
  clear(buffer);
  
  set_palette(pal);
  
  for(y = 1;y < 198;y++)
    for(x = 1;x < 318;x++)
  {
    *(deltas->line[y]+2*x) = *(map->line[y]+x+1)-*(map->line[y]+x-1);;
    *(deltas->line[y]+2*x+1) = *(map->line[y+1]+x)-*(map->line[y-1]+x);;
  }
  
  for(y = 0;y < 256;y++)
    for(x = 0;x < 256;x++)
  {
    int tx,ty,dist;
    
    tx = x-127;
    ty = y-127;
    dist = sqrt(tx*tx+ty*ty);
    if(dist < 64)
      putpixel(lightMap, x, y, 
        /*(random()>>28)+*/(char)(254-(dist*4))+1);
    else
      putpixel(lightMap, x, y, 1);
    if(getpixel(lightMap, x, y) < 1)
      putpixel(lightMap, x, y, 1);
  }
  
  for(;;)
  {
    color.r = color.g = color.b = 00;
    set_color(0, &color);
    vsync();
    color.r = 20;
    //    set_color(0, &color);
    delta = retrace_count-old;
    old = retrace_count;
    frame += delta;
    
    frame++;
    
    x1 = 0;
    //+fixtoi(fmul(fsin(itofix(frame&255)),itofix(160)));
    y1 = 0;
    //+fixtoi(fmul(fcos(itofix(frame&255)),itofix(100)));
    x2 = x1+256;
    y2 = y1+256;
    tx = x1;
    ty = y1;
    if(x1 < buffer->cl) x1 = buffer->cl;
    if(y1 < buffer->ct) y1 = buffer->ct;
    if(x1 > buffer->cr) x1 = buffer->cr;
    if(y1 > buffer->cb) y1 = buffer->cb;
    if(x2 < buffer->cl) x2 = buffer->cl;
    if(y2 < buffer->ct) y2 = buffer->ct;
    if(x2 > buffer->cr) x2 = buffer->cr;
    if(y2 > buffer->cb) y2 = buffer->cb;
    
    //       clear(buffer);
    rectfill(buffer, 0, 0, x1, y2, 0);
    rectfill(buffer, 0, y2, x2, 199, 0);
    rectfill(buffer, x1, 0, 319, y1, 0);
    rectfill(buffer, x2, y1, 319, 199, 0);
    
    
    for(y = y1+1;y < y2-1;y++)
    {
      vx = x-tx;
      vy = (int)y-ty;
      for(x = x1+1;x < x2;x++)
      {
        //          vx=x-tx;
        dx = (char) *(deltas->line[y]+2*x);
        dy = (char) *(deltas->line[y]+2*x+1);
        c = (char) *(lightMap->line[(dy+vy)&255]+((dx+(vx++))&255));
        
        *(buffer->line[y]+x) = c;
      }
    }
    
    //       rect(buffer,x1,y1,x2,y2,250);
    
    blit(buffer, screen, 0, 0, 0, 0, 320, 200);
    
    if(key[KEY_ESC])
      break;
  }
  
  
  /*
  dx = *(map->line[y]+x+1)-*(map->line[y]+x-1);
  vx = x-mouse_x;
  c = abs(vx-dx);
  dy = *(map->line[y+1]+x)-*(map->line[y-1]+x);
  vy = y-mouse_y;
  c += abs(vy-dy);
  
  c = 255-c;
  c = -c;
  if(c<=0) c = 1;
  if(c>255) c = 255;
  
  *(buffer->line[y]+x) = c;
  */
  
  
  
  
}

typedef struct TDeformTableParam
{
   int amplitude;
   int freq;
   int phase;
}TDeformTableParam;

typedef struct TDeformTable
{
   TDeformTableParam param[4];
   int multiplier;
   int table[256];
}TDeformTable;

void DoDeformTable(TDeformTable* deform, int position)
{
  int i,c;
  
  // Compute deformation lookup
  for(i = 255;i >= 0;i--)
  {
    c = (
      fsin(itofix(((((i+position)*deform->param[0].freq)>>16)+
      deform->param[0].phase)&255))+
      fsin(itofix(((((i+position)*deform->param[1].freq)>>16)+
      deform->param[1].phase)&255))+
      fsin(itofix(((((i+position)*deform->param[2].freq)>>16)+
      deform->param[2].phase)&255))+
      fsin(itofix(((((i+position)*deform->param[3].freq)>>16)+
      deform->param[3].phase)&255)));
    
    // clipping
    deform->table[i] = 128+(fixtoi(c*128/6));
  }
}

static void DrawGlenzTunnel(BITMAP* buffer, unsigned short* lookup, BITMAP* map, BITMAP* picture, 
                     int x, int y, TDeformTable * deform)
{
  int offset;
  int i;

  unsigned char* pPicture = _image;

  // offset in map
  offset = x*256+y;
  
  for(i = 0;i < 320*200;i++)
  {
  	unsigned char pixel;
  	unsigned short address;
  	unsigned char save;
  	unsigned int def;
  	address = lookup[i];
  	save = address>>8;
  	address+=offset;
  	def=deform->table[save];
  	address+=def;
  	pixel = *(_map+(address&0xffff));
	*(char*)(buffer->dat+i) = *pPicture++ + pixel;
  }
  
  // draw the tunnel on the buffer
//		*(char*)(buffer->dat+i) = *pPicture++ + *(_map+((lookup[i]+deform->table[lookup[i]&255]+offset)&0xffff));
//  	pixel =	*(_map+(((lookup[i]&0xff00)+deform->table[lookup[i]&0xff]+offset)&0xffff))
	/*
  asm volatile ("
    pushl %%ebp
    movl %%ecx,%%ebp
    movl $320*200/1,%%ecx
    glenz_tunnel_loop:
    xorl %%ebx,%%ebx
    movl (%%edx,%%ecx,2),%%eax // lookup
    movb %%ah,%%bl    // *** // sauve lookup haut
    addl %%ebp,%%eax // add offset
    movl (%%esi,%%ebx,4),%%ebx // charge 4o de deform
    addl %%ebx,%%eax // on ajoute a lookup+offset
    andl $0xffff,%%eax
    movb __map(%%eax),%%al
    movb __image(%%ecx),%%bl
    //        movb $64,%%bl
    addb %%bl,%%al
    movb %%al,(%%edi,%%ecx)
    
    decl %%ecx
    jns  glenz_tunnel_loop
    popl %%ebp
    "
    :
  : "c" (offset),"D" (buffer->dat), "S" (&deform->table), "d" (lookup)
    : "eax","ebx","ecx","edx","esi","edi"
    );
  */
}

void PartTunnel(void)
{
  TDeformTable tunnelDeform;
  
  BITMAP* map,* picture;
  int i;
  
  map = data[BMP_map1].dat;
  picture = data[BMP_glenzK].dat;
  set_palette(data[PAL_glenz1].dat);
  
  // Init global buffers
  for(i = 0;i < 320*200;i++)
    *(unsigned char*)(_image+i) = 30;
  for(i = 0;i < 256*256;i++)
    *(unsigned char*)(_map+i) = *((unsigned char*)(map->dat+i))/2;
  
  tunnelDeform.param[0].amplitude =
    tunnelDeform.param[1].amplitude =
    tunnelDeform.param[2].amplitude =
    tunnelDeform.param[3].amplitude = 0;
  tunnelDeform.param[0].freq = 0x0000;
  tunnelDeform.param[1].freq = 0x0000;
  tunnelDeform.param[2].freq = 0x0000;
  tunnelDeform.param[3].freq = 0x0000;
  
  while(GetPosition() < 0x1000)
  {
    DoDeformTable(&tunnelDeform, VBLframe);
    DrawGlenzTunnel(buffer, TunnelLookup, map, picture, VBLframe, VBLframe*2, &tunnelDeform);
    draw_trans_rle_sprite(buffer,text[7],0,200-28);
    WaitVBL();
    blit(buffer, screen, 0, 0, 0, 0, 320, 200);
    tunnelDeform.param[0].phase = 64+VBLframe;
    tunnelDeform.param[1].phase = 192+VBLframe;
    //    tunnelDeform.param[1].phase=tunnelDeform.table[(2*VBLframe)&255]+VBLframe;
    tunnelDeform.param[2].phase = tunnelDeform.table[VBLframe&255]+VBLframe;
    tunnelDeform.param[3].phase = 274+VBLframe;
    
    /* if F1 pressed, wait a moment */
    while(key[KEY_F1])
    {
      }
    
    /* exit if ESC pressed */
    //    if(key[KEY_ESC])
    //      break;
  }
  
  tunnelDeform.param[0].amplitude =
    tunnelDeform.param[1].amplitude =
    tunnelDeform.param[2].amplitude =
    tunnelDeform.param[3].amplitude = 1;
  tunnelDeform.param[0].freq = 0x20000;
  tunnelDeform.param[1].freq = 0x30000;
  tunnelDeform.param[2].freq = 0x40000;
  tunnelDeform.param[3].freq = 0x10000;
  
  while(GetPosition() < 0x1100)
  {
    DoDeformTable(&tunnelDeform, VBLframe);
    DrawGlenzTunnel(buffer, TunnelLookup, map, picture, VBLframe*0, VBLframe*0, &tunnelDeform);
    draw_trans_rle_sprite(buffer,text[8],0,200-28);
    WaitVBL();
    blit(buffer, screen, 0, 0, 0, 0, 320, 200);
    tunnelDeform.param[0].phase = 64+VBLframe;
    tunnelDeform.param[1].phase = 192+VBLframe;
    //    tunnelDeform.param[1].phase=tunnelDeform.table[(2*VBLframe)&255]+VBLframe;
    tunnelDeform.param[2].phase = tunnelDeform.table[VBLframe&255]+VBLframe;
    tunnelDeform.param[3].phase = 274+VBLframe;
  }
  tunnelDeform.param[0].freq = 0x20000;
  tunnelDeform.param[1].freq = 0x70000;
  tunnelDeform.param[2].freq = 0x40000;
  tunnelDeform.param[3].freq = 0x10000;
  while(GetPosition() < 0x1200)
  {
    DoDeformTable(&tunnelDeform, VBLframe);
    DrawGlenzTunnel(buffer, TunnelLookup, map, picture, VBLframe, VBLframe*1, &tunnelDeform);
    draw_trans_rle_sprite(buffer,text[9],0,200-28);
    WaitVBL();
    blit(buffer, screen, 0, 0, 0, 0, 320, 200);
    tunnelDeform.param[0].phase = 64+VBLframe;
    tunnelDeform.param[1].phase = 192+VBLframe;
    //    tunnelDeform.param[1].phase=tunnelDeform.table[(2*VBLframe)&255]+VBLframe;
    tunnelDeform.param[2].phase = tunnelDeform.table[VBLframe&255]+VBLframe;
    tunnelDeform.param[3].phase = 274+VBLframe;
    
  }
  tunnelDeform.param[0].freq = 0x20000;
  tunnelDeform.param[1].freq = 0x10000;
  tunnelDeform.param[2].freq = 0x20000;
  tunnelDeform.param[3].freq = 0x10000;
  while(GetPosition() < 0x1300)
  {
    DoDeformTable(&tunnelDeform, VBLframe);
    DrawGlenzTunnel(buffer, TunnelLookup, map, picture, VBLframe, VBLframe*2, &tunnelDeform);
    draw_trans_rle_sprite(buffer,text[10],0,200-28);
    WaitVBL();
    blit(buffer, screen, 0, 0, 0, 0, 320, 200);
    tunnelDeform.param[0].phase = 64+VBLframe;
    tunnelDeform.param[1].phase = 192+VBLframe;
    //    tunnelDeform.param[1].phase=tunnelDeform.table[(2*VBLframe)&255]+VBLframe;
    tunnelDeform.param[2].phase = tunnelDeform.table[VBLframe&255]+VBLframe;
    tunnelDeform.param[3].phase = 274+VBLframe;
    
  }
  
  for(i = 0;i < 320*200;i++)
    *(unsigned char*)(_image+i) = *((unsigned char*)(picture->dat+i))/2;
  
  
  tunnelDeform.param[0].freq = 0x20000;
  tunnelDeform.param[1].freq = 0x10000;
  tunnelDeform.param[2].freq = 0x10000;
  tunnelDeform.param[3].freq = 0x20000;
  while(GetPosition() < 0x1400)
  {
    DoDeformTable(&tunnelDeform, VBLframe);
    DrawGlenzTunnel(buffer, TunnelLookup2, map, picture, VBLframe*2, VBLframe*0, &tunnelDeform);
    WaitVBL();
    blit(buffer, screen, 0, 0, 0, 0, 320, 200);
    
    tunnelDeform.param[0].phase = -VBLframe;
    tunnelDeform.param[1].phase = 192+VBLframe;
    tunnelDeform.param[2].phase = 48+VBLframe*2;
    tunnelDeform.param[3].phase = 64-VBLframe;
  }
}

void PartInternalBlob(void)
{
  K3DTObject obj;
  BITMAP *map,* texte;
  PALETTE pal;
  int i;
  
  clear(screen);
  K3DInit();
  K3DInitObject(&obj);
  K3DLoadObjectASC("blob.asc", &obj);
  set_projection_viewport(0, 0, SCREEN_W, SCREEN_H);
  map = data[BMP_map1].dat;
  memcpy(pal, data[PAL_glenz1].dat, sizeof(PALETTE));
  texte = data[BMP_after].dat;
  
  //  map    = load_bitmap("texture3.pcx",pal);
  K3DSetObjectRendering(&obj, TEXTURE, NONE, Z);
  K3DPlaceObject(&obj, 0, 0, itofix(100), 0, 0, 0);
  K3DSetObjectMap(&obj, map);
  
  K3DRotateObject(&obj);
  K3DProjectObject(&obj);
  K3DEnvMapObject(&obj);
  
  color_map = tableGlenz;
  //  obj.scale=ftofix(0.3);
  set_palette(pal);
  while(GetPosition() < 0x0300)
//	while(0)
  {
    K3DPlaceObject(&obj, 0, itofix(-50), 
      itofix(50)+70*fsin(itofix((1*VBLframe)&255)), 
      (VBLframe<<14)&0xffffff, 
      (VBLframe<<14)&0xffffff, 
      (VBLframe<<14)&0xffffff);
    
    
    if(GetPosition() == 0x0100)
      texte = data[BMP_lasy].dat;
    if(GetPosition() == 0x0200)
      texte = data[BMP_back].dat;
    draw_sprite(buffer, texte, 0, 0);
    //    blit(text,buffer,0,0,0,0,320,32);
    WaitVBL();
    blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
    clear(buffer);
    K3DRotateObject(&obj);
    for(i = 0;i < obj.vertexes;i++)
    {
      obj.rotated[i].x += 20*fsin(itofix((2*i+3*VBLframe)&255));
      obj.rotated[i].y += 20*fsin(itofix((5*i+2*VBLframe)&255));
    }
    K3DProjectObject(&obj);
    //    K3DEnvMapObject(&obj);
    K3DDrawObject(buffer, &obj);


    if(key[KEY_ESC])
      break;
  }
  K3DDeleteObject(&obj);
}

void Part2Blob(void)
{
  K3DTObject obj;
  BITMAP *map;
  PALETTE pal;
  int i;
  
  clear(screen);
  K3DInit();
  K3DInitObject(&obj);
  K3DLoadObjectASC("blob.asc", &obj);
  set_projection_viewport(0, 0, SCREEN_W, SCREEN_H);
  map = data[BMP_map1].dat;
  memcpy(pal, data[PAL_glenz1].dat, sizeof(PALETTE));
  
  K3DSetObjectRendering(&obj, TEXTURE, NONE, Z);
  K3DPlaceObject(&obj, 0, 0, itofix(100), 0, 0, 0);
  K3DSetObjectMap(&obj, map);
  
  K3DRotateObject(&obj);
  K3DProjectObject(&obj);
  K3DEnvMapObject(&obj);
  
  
  //  obj.scale=ftofix(0.3);
  set_palette(pal);
  while(GetPosition() < 0x0a00)
  {
    /* internal blob */
    K3DPlaceObject(&obj, 0, 0, 
      itofix(40)+50*fsin(itofix((4*VBLframe)&255)),
      (VBLframe<<14)&0xffffff, 
      (VBLframe<<14)&0xffffff, 
      (VBLframe<<14)&0xffffff);
    
    WaitVBL();
    blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
    clear(buffer);
    K3DRotateObject(&obj);
    for(i = 0;i < obj.vertexes;i++)
    {
      obj.rotated[i].x += 24*fsin(itofix((7*i+8*VBLframe)&255))+
        14*fsin(itofix((6*i+2*VBLframe)&255));
      obj.rotated[i].y += 10*fsin(itofix((1*i+2*VBLframe)&255))+
        44*fsin(itofix((2*i+5*VBLframe)&255));
    }
    K3DProjectObject(&obj);
    //    K3DEnvMapObject(&obj);
    K3DSetObjectRendering(&obj, TEXTURE, NONE, Z);
    K3DDrawObject(buffer, &obj);
    
    /* little blob */
    K3DPlaceObject(&obj, 0, 0, 
      itofix(500)+50*fcos(itofix((4*VBLframe)&255)), 
      (VBLframe<<14)&0xffffff, 
      (VBLframe<<14)&0xffffff, 
      (VBLframe<<14)&0xffffff);
    
    K3DRotateObject(&obj);
    for(i = 0;i < obj.vertexes;i++)
    {
      //      obj.rotated[i].x += 14*fsin(itofix((1*i+1*VBLframe)&255))+
      //                          24*fsin(itofix((2*i+1*VBLframe)&255));
      //      obj.rotated[i].y += 20*fsin(itofix((3*i+3*VBLframe)&255))+
      //                          14*fsin(itofix((7*i+2*VBLframe)&255));
      obj.rotated[i].z += 50*fsin(itofix((3*i+5*VBLframe)&255))+
        34*fsin(itofix((7*i+8*VBLframe)&255));
    }
    
    K3DProjectObject(&obj);
    //    K3DEnvMapObject(&obj);
    
    if(GetPosition() < 0xa00)
      K3DSetObjectRendering(&obj, TEXTURE, QSORT, Z);
    if(GetPosition() < 0x800)
      K3DSetObjectRendering(&obj, GOURAUD, QSORT, Z);
    if(GetPosition() < 0x700)
      K3DSetObjectRendering(&obj, FLAT, QSORT, Z);
    K3DDrawObject(buffer, &obj);

    i = GetPosition();

    if((i<0x700) && (i>0x600))
      draw_trans_rle_sprite(buffer,text[0],0,200-28);
    if((i<0x800) && (i>0x700))
      draw_trans_rle_sprite(buffer,text[1],0,200-28);
    if((i<0x900) && (i>0x800))
      draw_trans_rle_sprite(buffer,text[2],0,200-28);
    if((i<0xa00) && (i>0x900))
      draw_trans_rle_sprite(buffer,text[3],0,200-28);

    if(key[KEY_ESC])
      break;
  }
  K3DDeleteObject(&obj);
}


static void DrawWater(BITMAP * buffer, BITMAP * old)
{
  // compute water data
	/*
  asm volatile("
    pushl %%ebp
    xorl %%ebx,%%ebx
    xorl %%ecx,%%ecx
    xorl %%edx,%%edx
    movl  $320*150,%%ebp
    
    water_loop:
    // pixel loop
    xorl %%eax,%%eax
    
    // add the S,W,E,N pixels
    movb -320(%%esi),%%al
    movb -1(%%esi),%%bl
    movb 1(%%esi),%%cl
    movb 320(%%esi),%%dl
    
    // add them and div by 2
    addl %%ebx,%%eax
    addl %%edx,%%ecx
    addl %%ecx,%%eax
    shrl $1,%%eax
    andl $0xff,%%eax
    movb (%%edi),%%ah
    
    // search into the lookup
    movb __map(%%eax),%%bl
    
    // plot it
    movb %%bl,(%%edi)
    
    incl %%edi
    incl %%esi
    dec %%ebp
    jnz water_loop
    
    popl %%ebp
    ":
  : "D"(buffer->dat+320*25), "S"(old->dat+320*25)
    : "eax","ebx","ecx","edx","esi","edi"
    );
  */
  
  int i;
  unsigned int b;
  char * table = _map;
  for(i=640;i<320*197;i++)
  {
		b = *((unsigned char*)old->dat+i-1)+
			*((unsigned char*)old->dat+i+1)+
			*((unsigned char*)old->dat+i-320)+
			*((unsigned char*)old->dat+i+320);
		b/=2;
		b&=255;
		b=_map[256*b+(*((unsigned char*)buffer->dat+i))*1];
		*((unsigned char*)buffer->dat+i) = b;
  }
  rest(20);
}

void PartWater(void)
{
  BITMAP *old,*_buffer;
  BITMAP *tortue;
  PALETTE pal;
  BITMAP *temp;
  
  
  int x,y;
  int i,j;
  
  // create and clear 2 buffers
  _buffer = create_bitmap(320, 202);
  clear(_buffer);
  old = create_bitmap(320, 202);
  clear(old);
  
  srand(154);
  
  // load and change a picture
  tortue = data[BMP_turtle].dat;
  memcpy(pal,data[PAL_turtle].dat,sizeof(PALETTE));
  for(x = 0;x < 172;x++)
    for(y = 0;y < 256;y++)
    _putpixel(tortue, x, y, _getpixel(tortue, x, y)+128);
  
  for(i = 0;i < 128;i++)
  {
    // picture palette
    pal[i+128].r = pal[i].r;
    pal[i+128].g = pal[i].g;
    pal[i+128].b = pal[i].b;
    
    // water gradient
    pal[i].r = pal[i].g = i/1;
    pal[i].b = 2*i/1;
  }
  set_palette(pal);
  
  color_map = tableWater;
  
  
  // calculate the lookup
  for(x = 0;x < 256;x++)
    for(y = 0;y < 256;y++)
  {
    j = y-x;
    j -= j/16;
    if(j < 0)
      j = 0;
    _map[x+256*y] = j;
  }
  
  VBLframe = retrace_count = 200;
  x = 0;
  x =-300;
  while(GetPosition() < 0x1700)
  {
    // swap new and old buffer
    temp = _buffer;
    _buffer = old;
    old = temp;
    
    if(GetPosition() == 0x1500)
    {
      x = -175;
    }
    if ((x>-176) && (x<=0))
      x++;

    // put the picture
    //      blit(tortue,buffer,x,(fsin((itofix((4*VBLframe)&255)))/2340)+27,0,0,172,256);
    blit(_buffer, buffer, 0, 0, 0, 0, 320, 200);
    draw_trans_sprite(buffer, tortue, x, (fsin((itofix((10*VBLframe)&255)))/2340)-27);
    
    WaitVBL();
    //      vsync();
    
    // blit into screen
    blit(buffer, screen, 0, 0, 0, 0, 320, 200);
    
    // adjust contours
    line(old, 0, 0, 319, 0, 0);
    line(old, 0, 0, 0, 199, 0);
    line(old, 319, 0, 319, 199, 0);
    line(old, 0, 199, 319, 199, 0);
    
    // draw water plot motion
    _putpixel(old, 160+100*cos(2*3.14159*VBLframe/256), 100+50*sin(4*3.14159*VBLframe/256), 127);
    putpixel(old, rand()%319, 25+rand()%150, 127);
    
    // and calculate it
    DrawWater(_buffer, old);
    
    if(key[KEY_ESC])
      break;
  }
}


void PartMetaballs(void)
{
  K3DTObject obj;
  BITMAP *map,*blob,*back;
  PALETTE pal;
  int i,c;
  
  clear(screen);
  K3DInit();
  K3DInitObject(&obj);
  K3DLoadObjectASC("k.asc", &obj);
  blob = create_bitmap(64, 64);
  clear(blob);
  for(i = 0;i < 32;i++)
  {
    c = 256-8*((i*i)/16);
    if(c > 255)
      c = 255;
    if(c < 0)
      c = 0;
    circle(blob, 32, 32, i, c);
    circle(blob, 31, 31, i, c);
    circle(blob, 31, 32, i, c);
    circle(blob, 32, 31, i, c);
  }
  
  set_projection_viewport(0, 0, SCREEN_W, SCREEN_H);
  map = data[BMP_map1].dat;
  back = data[BMP_glenzMind].dat;
  memcpy(pal, data[PAL_glenz3].dat, sizeof(PALETTE));
  
  K3DSetObjectRendering(&obj, TEXTURE, QSORT, NONE);
  K3DPlaceObject(&obj, 0, 0, itofix(100), 0, 0, 0);
  K3DSetObjectMap(&obj, map);
  
  K3DRotateObject(&obj);
  K3DProjectObject(&obj);
  K3DEnvMapObject(&obj);
  
  color_map = tableGlenz;
  
  //  obj.scale=ftofix(0.3);
  set_palette(pal);
  while(GetPosition() < 0x0c00)
  {
    K3DPlaceObject(&obj, 0, 0, 
      itofix(120)+30*fsin(itofix((1*VBLframe)&255)), 
      itofix(64), 0, ((256-VBLframe)<<17)&0xffffff);
    //                   (VBLframe<<16)&0xffffff,
    //                   (VBLframe<<16)&0xffffff);
    
    
    WaitVBL();
    stretch_blit(buffer, buffer, 0, 0, SCREEN_W, SCREEN_H, 0, 0, (SCREEN_W/4), (SCREEN_H/4));
    rect(buffer, 0, 0, SCREEN_W/4, SCREEN_H/4, 128);
    blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
    blit(back, buffer, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
    //    clear(buffer);
    K3DRotateObject(&obj);
    K3DProjectObject(&obj);
    K3DEnvMapObject(&obj);
    
    c = 8;
    for(i = 0;i < c;i++)
      draw_trans_sprite(buffer, blob, 
        160-32+fixtoi(60*fsin(itofix((VBLframe+i*256/c)&255)))+fixtoi(80*fcos(itofix((VBLframe*2+i*256/c)&255)))
        , 100-32+fixtoi(70*fcos(itofix((VBLframe*3+i*256/c)&255)))+fixtoi(40*fsin(itofix((VBLframe+i*256*c)&255))));
    
    K3DDrawObject(buffer, &obj);
    
    
    c = 4;
    for(i = 0;i < c;i++)
      draw_trans_sprite(buffer, blob, 
        160-32+fixtoi(60*fsin(itofix((VBLframe+i*256/c)&255)))+fixtoi(80*fcos(itofix((VBLframe+i*256/c)&255)))
        , 100-32+fixtoi(80*fcos(itofix((VBLframe+i*256/c)&255)))+fixtoi(30*fsin(itofix((VBLframe*2+i*256*c)&255))));
    

    draw_trans_rle_sprite(buffer,text[4],0,200-28);

    if(key[KEY_ESC])
      break;
  }
  K3DDeleteObject(&obj);
}

void PartTunnel3d(void)
{
  K3DTObject obj;
  BITMAP *map,*tv;
  PALETTE pal;
  
  clear(screen);
  K3DInit();
  K3DInitObject(&obj);
  K3DLoadObjectASC("tunnel.asc", &obj);
  obj.clipping = itofix(2);
  
  set_projection_viewport(0, 0, SCREEN_W, SCREEN_H);
  map = data[BMP_map1].dat;
  memcpy(pal, data[PAL_glenz2].dat, sizeof(PALETTE));
  
  tv = create_bitmap(80, 50);
  
  K3DSetObjectRendering(&obj, TEXTURE, NONE, Z);
  K3DPlaceObject(&obj, 0, 0, itofix(100), 0, 0, 0);
  K3DSetObjectMap(&obj, map);
  
  K3DRotateObject(&obj);
  K3DProjectObject(&obj);
  K3DEnvMapObject(&obj);
  
  //  obj.scale=ftofix(0.3);
  set_palette(pal);
  while(GetPosition() < 0x0d00)
  {
    K3DPlaceObject(&obj, 
      0, 
      0, 
      0, 
      (VBLframe<<16)&0xffffff, 
      (VBLframe<<16)&0xffffff, 
      (VBLframe<<16)&0xffffff);
    
    
    blit(tv, buffer, 0, 0, 0, 0, 80, 50);
    rotate_sprite(buffer, tv, 320-80, 200-50, VBLframe<<16);
    //    rect(buffer,0,0,SCREEN_W/4,SCREEN_H/4,128);
    line(buffer, 0, 50, 319, 50, 128);
    line(buffer, 80, 0, 80, 199, 128);
    
    stretch_blit(buffer, tv, 0, 0, SCREEN_W, SCREEN_H, 0, 0, (SCREEN_W/4), (SCREEN_H/4));
    WaitVBL();
    blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
    clear(buffer);
    K3DRotateObject(&obj);
    K3DProjectObject(&obj);
    //    K3DEnvMapObject(&obj);
    
    K3DDrawObject(buffer, &obj);
    
    while(key[KEY_F1])
    {
      }
    
    draw_trans_rle_sprite(buffer,text[5],0,200-28);

    if(key[KEY_ESC])
      break;
  }
  K3DDeleteObject(&obj);
}

void PartTunnel3d2(void)
{
  K3DTObject obj;
  BITMAP *map;
  PALETTE pal;
  int i;
  
  clear(screen);
  K3DInit();
  K3DInitObject(&obj);
  K3DLoadObjectASC("tube.asc", &obj);
  
  set_projection_viewport(0, 0, SCREEN_W, SCREEN_H);
  map = data[BMP_map1].dat;
  memcpy(pal, data[PAL_glenz3].dat, sizeof(PALETTE));
  
  K3DSetObjectRendering(&obj, TEXTURE, NONE, NONE);
  K3DPlaceObject(&obj, 0, 0, itofix(100), 0, 0, 0);
  K3DSetObjectMap(&obj, map);
  
  K3DRotateObject(&obj);
  K3DProjectObject(&obj);
  K3DEnvMapObject(&obj);
  
  VBLframe = retrace_count =-200;
  obj.clipping = itofix(40);
  //  obj.scale=ftofix(0.3);
  set_palette(pal);
  while(GetPosition() < 0x0f00)
  {
    WaitVBL();
    blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
    clear(buffer);
    
    for(i = 4;i >= 0;i--)
    {
      K3DPlaceObject(&obj, 
        400*65536*sin(2*3.1415927*VBLframe/256), 
        0, 
        itofix((512*i-(VBLframe*14))&2047), 
        itofix(64), 
        //                     ((i*32-VBLframe)<<16)&0xffffff,
        128*65536*sin(2*3.1415927*(VBLframe+i*9)/256), 
        itofix(0));
      K3DRotateObject(&obj);
      K3DProjectObject(&obj);
      //      K3DEnvMapObject(&obj);
      
      K3DDrawObject(buffer, &obj);
    }
    while(key[KEY_F1])
    {
    }

    if (GetPosition() < 0x0d00)
      draw_trans_rle_sprite(buffer,text[5],0,200-28);
    else
      draw_trans_rle_sprite(buffer,text[6],0,200-28);
    
    if(key[KEY_ESC])
      break;
  }
  K3DDeleteObject(&obj);
}

void PartPlasma(void)
{
  TDeformTable tunnelDeform;
  
  BITMAP* map,* picture;
  PALETTE pal,black,begin;
  int i,fade;
  
  map = data[BMP_map1].dat;
  picture = data[BMP_glenzK].dat;
  set_palette(data[PAL_glenz1].dat);

  memcpy(begin,data[PAL_glenz1].dat,sizeof(PALETTE));
  for(i = 0;i < 256;i++)
    black[i].r=black[i].g=black[i].b=0;


  // Init global buffers
  for(i = 0;i < 256*256;i++)
    *(unsigned char*)(_map+i) = *((unsigned char*)(map->dat+i))/2;
  for(i = 0;i < 320*200;i++)
    *(unsigned char*)(_image+i) = 30;
  
  tunnelDeform.param[0].freq = 0x10000;
  tunnelDeform.param[1].freq = 0x10000;
  tunnelDeform.param[2].freq = 0x10000;
  tunnelDeform.param[3].freq = 0x10000;
  
  while(GetPosition() < 0x1800)
  {
    DoDeformTable(&tunnelDeform, VBLframe);
    DrawGlenzTunnel(buffer, PlasmaLookup, map, picture,
                    tunnelDeform.table[(VBLframe+194)&255],
                    tunnelDeform.table[(2*VBLframe)&255], &tunnelDeform);
    WaitVBL();
    blit(buffer, screen, 0, 0, 0, 0, 320, 200);
    tunnelDeform.param[0].phase = 64+VBLframe;
    tunnelDeform.param[1].phase = tunnelDeform.table[(5*VBLframe)&255];
    tunnelDeform.param[2].phase = tunnelDeform.table[VBLframe&255]+VBLframe;
    tunnelDeform.param[3].phase = 192+VBLframe;
    
  }
  tunnelDeform.param[0].freq = 0x10000;
  tunnelDeform.param[1].freq = 0x20000;
  tunnelDeform.param[2].freq = 0x20000;
  tunnelDeform.param[3].freq = 0x10000;
  while(GetPosition() < 0x1900)
  {
    DoDeformTable(&tunnelDeform, VBLframe);
    DrawGlenzTunnel(buffer, PlasmaLookup, map, picture,
                    tunnelDeform.table[(VBLframe+194)&255],
                    tunnelDeform.table[(2*VBLframe)&255], &tunnelDeform);
    WaitVBL();
    blit(buffer, screen, 0, 0, 0, 0, 320, 200);
    tunnelDeform.param[0].phase = 64+VBLframe;
    tunnelDeform.param[1].phase = tunnelDeform.table[(2*VBLframe)&255];
    tunnelDeform.param[2].phase = tunnelDeform.table[VBLframe&255]+VBLframe;
    tunnelDeform.param[3].phase = 274+VBLframe;
    
  }
  while(GetPosition() < 0x1a00)
  {
    DoDeformTable(&tunnelDeform, VBLframe);
    DrawGlenzTunnel(buffer, PlasmaLookup2, map, picture,
                    tunnelDeform.table[(VBLframe+194)&255],
                    tunnelDeform.table[(2*VBLframe)&255], &tunnelDeform);
    WaitVBL();
    blit(buffer, screen, 0, 0, 0, 0, 320, 200);
    tunnelDeform.param[0].phase = 64+VBLframe;
    tunnelDeform.param[1].phase = tunnelDeform.table[(2*VBLframe)&255];
    tunnelDeform.param[2].phase = VBLframe;
    tunnelDeform.param[3].phase = 274+VBLframe;
  }
  tunnelDeform.param[3].freq = 0x30000;
  while(GetPosition() < 0x1c00)
  {
    DoDeformTable(&tunnelDeform, VBLframe);
    DrawGlenzTunnel(buffer, PlasmaLookup, map, picture,
                    tunnelDeform.table[(VBLframe+194)&255],
                    tunnelDeform.table[(2*VBLframe)&255], &tunnelDeform);
    WaitVBL();
    blit(buffer, screen, 0, 0, 0, 0, 320, 200);
    tunnelDeform.param[0].phase = 64+VBLframe;
    tunnelDeform.param[1].phase = tunnelDeform.table[(2*VBLframe)&255];
    tunnelDeform.param[2].phase = VBLframe;
    tunnelDeform.param[3].phase = 274+VBLframe;
  }
  for(i = 0;i < 320*200;i++)
    *(unsigned char*)(_image+i) = 70+*((unsigned char*)(((BITMAP*)data[BMP_think].dat)->dat+i))/2;
  while(GetPosition() < 0x1d00)
  {
    DoDeformTable(&tunnelDeform, VBLframe);
    DrawGlenzTunnel(buffer, PlasmaLookup2, map, picture,
                    tunnelDeform.table[(VBLframe+194)&255],
                    tunnelDeform.table[(2*VBLframe)&255], &tunnelDeform);
    WaitVBL();
    blit(buffer, screen, 0, 0, 0, 0, 320, 200);
    tunnelDeform.param[0].phase = 64+VBLframe;
    tunnelDeform.param[1].phase = tunnelDeform.table[(2*VBLframe)&255];
    tunnelDeform.param[2].phase = VBLframe;
    tunnelDeform.param[3].phase = 274+VBLframe;
  }
  fade=0;
  while(GetPosition() < 0x1e00)
  {
    DoDeformTable(&tunnelDeform, VBLframe);
    DrawGlenzTunnel(buffer, PlasmaLookup, map, picture,
                    tunnelDeform.table[(VBLframe+194)&255],
                    tunnelDeform.table[(2*VBLframe)&255], &tunnelDeform);
    WaitVBL();
    if (fade<64*3)
      fade_interpolate(begin, black, pal, fade/3, 0, 255);
    vsync();
    fade++;
    set_palette(pal);

    blit(buffer, screen, 0, 0, 0, 0, 320, 200);
    tunnelDeform.param[0].phase = 64+VBLframe;
    tunnelDeform.param[1].phase = tunnelDeform.table[(2*VBLframe)&255];
    tunnelDeform.param[2].phase = VBLframe;
    tunnelDeform.param[3].phase = 274+VBLframe;
  }
  
}

void PartEnd(void)
{
  K3DTObject obj;
  int i;
  PALETTE black,pal;

  K3DInit();
  K3DInitObject(&obj);
  K3DLoadObjectASC("blob.asc", &obj);
  set_projection_viewport(0, 0, SCREEN_W, SCREEN_H);

  K3DSetObjectRendering(&obj, FLAT, QSORT, Z);
  K3DPlaceObject(&obj, 0, 0, itofix(100), 0, 0, 0);
  K3DSetObjectMap(&obj, data[BMP_map1].dat);
  
  K3DRotateObject(&obj);
  K3DProjectObject(&obj);
  K3DEnvMapObject(&obj);

  for(i=0;i<255;i++)
    black[i].r=black[i].g=black[i].b=0;


  obj.scale=ftofix(0.5);
	#ifdef USE_JGMOD
	goto_mod_track(0x1f);
	#endif
  color_map = tableGlenz;
  set_palette(data[PAL_glenz2].dat);
  VBLframe=retrace_count=-100;
  for(i=VBLframe;i<800*2;i=VBLframe)
  {
    WaitVBL();
//    vsync();
    blit(buffer,screen,0,0,0,0,320,200);
    blit(data[BMP_bumpBack].dat,buffer,0,0,0,0,320,200);

//    K3DPlaceObject(&obj,itofix(250),itofix(0),itofix(300),itofix(64),itofix(0),itofix(i));
    K3DPlaceObject(&obj,itofix(250)+100*fsin(itofix(i&255)),itofix((i&1023)-512),itofix(300),itofix(i),itofix(0),itofix(i));
    K3DRenderObject(buffer, &obj);

    K3DPlaceObject(&obj,itofix(250)+100*fcos(itofix(i&255)),itofix(((i+512)&1023)-512),itofix(300),itofix(i),itofix(0),itofix(i));
    K3DRenderObject(buffer, &obj);

    draw_trans_rle_sprite(buffer,data[BMP_scroll].dat,160,-i/2);

    if (key[KEY_ESC])
      return;
  }
  for(i=0;i<64*2;i++)
  {
    fade_interpolate(data[PAL_glenz1].dat, black, pal, i/2, 0, 255);
    WaitVBL();
    vsync();
    set_palette(pal);
    blit(buffer,screen,0,0,0,0,320,200);
//    MIDASsetMusicVolume(play,64-(i/2));
//    blit(data[BMP_bumpBack].dat,buffer,0,0,0,0,320,200);
    blit(data[BMP_kEnd].dat,buffer,0,0,0,0,320,200);

//    K3DPlaceObject(&obj,itofix(250),itofix(0),itofix(300),itofix(64),itofix(0),itofix(i));
    K3DPlaceObject(&obj,itofix(250)+100*fsin(itofix(VBLframe&255)),itofix(((VBLframe)&1023)-512),itofix(300),itofix(VBLframe),itofix(0),itofix(VBLframe));
    K3DRenderObject(buffer, &obj);

    K3DPlaceObject(&obj,itofix(250)+100*fcos(itofix(VBLframe&255)),itofix(((VBLframe+512)&1023)-512),itofix(300),itofix(VBLframe),itofix(0),itofix(VBLframe));
    K3DRenderObject(buffer, &obj);

    if (key[KEY_ESC])
      return;
  }

  K3DDeleteObject(&obj);
}

void Demo(void)
{
  set_gfx_mode(mode, 320, 200, 0, 0);
  buffer = create_bitmap(SCREEN_W, SCREEN_H);


  PartInternalBlob();

  {
  PALETTE pal;
  fade_out(12);
  memcpy(pal,data[PAL_kIntro].dat,sizeof(PALETTE));
  blit(data[BMP_kIntro].dat,screen,0,0,0,0,320,200);
  //    set_palette(pal);
  fade_in(pal,2);
  while(GetPosition()<0x0400)
  {
  WaitVBL();
  }
  fade_out(12);
  memcpy(pal,data[PAL_mindIntro].dat,sizeof(PALETTE));
  blit(data[BMP_mindIntro].dat,screen,0,0,0,0,320,200);
  //    set_palette(pal);
  fade_in(pal,2);
  while(GetPosition()<0x043e)
  {
  WaitVBL();
  }
  fade_out(12);
  }
  

  Part2Blob();
  
  PartMetaballs();
  
  PartTunnel3d();
  
  PartTunnel3d2();

  PartTunnel();

  PartWater();
	
  PartPlasma();

  PartEnd();

}



int main(int argc, char ** argv)
{
  char buf[80];
  int refreshRate;
  
  printf("MindLink (c) KNIGHTS 1997\n");
  printf("  ( Run with any parameter to run sound setup )\n");
  printf("  Initializing.\n");
  allegro_init();

  if (argc>1)
  	mode = GFX_AUTODETECT_FULLSCREEN;
  
  /* load the datafile into memory */
  printf("  UnPacking.");
  fflush(stdout);
  data = load_datafile("mindlink.dat");
  if(!data)
  {
    allegro_exit();
    printf("  Error loading example.dat!\n\n");
    #ifdef MIDAS
      if(!MIDASclose())
      MIDASerror();
    #endif
      exit(1);
  }
  
  install_keyboard();
  
  printf(".");
  fflush(stdout);
  install_timer();
    

	install_timer ();	
	install_keyboard ();
	reserve_voices (20, -1);	

#ifdef USE_JGMOD

	if (install_sound (DIGI_AUTODETECT, MIDI_NONE, NULL) < 0)
	{
		printf ("Error initializing sound card");
		return 1;
	}

	set_volume(128,0);

	if (install_mod (20) < 0)	
	{
		printf ("Error setting digi voices");
		return 1;
	}
#endif
	
    Load();
  
    
//	install_int_ex(my_timer_handler,BPS_TO_TIMER(70));
#ifdef USE_JGMOD

	module = load_mod ("mindlink.xm");
	if (module == NULL)
	{
		printf ("Error reading mindlink.xm");
		return 1;
	}
	play_mod (module, TRUE);
	set_mod_volume(128);
#endif
    
    Demo();
 
  
  #ifdef MIDAS
    if(!MIDASremoveTimerCallbacks())
    MIDASerror();
  if(!MIDASstopModule(play))
    MIDASerror();
  if(!MIDASfreeModule(module))
    MIDASerror();
  
  if(!MIDASclose())
    MIDASerror();
  #endif
#ifdef USE_JGMOD
  stop_mod ();
	destroy_mod (module);
#endif
  
    /* unload the datafile when we are finished with it */
    unload_datafile(data);
  
  set_gfx_mode(GFX_TEXT, 640, 480, 0, 0);
  printf("\n       ______                                                             _____\n");
  printf("______\\\\     \\______________________________________  ___________________/    /\n");
  printf("\\      \\\\                                           |/ \"I've Got The Poison\" / \n");
  printf(" \\      |\\     .______.    ._______.   .______.     |_. .___.   .____.  .___/  \n");
  printf("  |     |/     /|     \\     |     |    / _____     _    /_  |    \\  /       \\  \n");
  printf("  |      _____/_|     _     |     |    \\_\\          |     |_|     >>\\____    \\ \n");
  printf("  |             \\\\   |      |     |\\        //      |    //    \\\\//           \\\n");
  printf("  |_____|\\_____  >>__|\\_____|_____| \\______<<  ____/|___<<  __________________/\n");
  printf(" !Fuck'Em All! \\//                          \\\\/          \\\\/      !SenseRofTHN! \n");
  
  printf("\nMindLink 1.1 (GPL) [%s %s]\n\n", __DATE__, __TIME__);
  printf("http://knights.planet-d.net gautier@tlk.fr\n");
  printf("                     \n");
  printf("\n");
  
  
  
  return 0;
}
END_OF_MAIN()
