// This is world.cc CVS version: $Id: world.cc,v 1.4 2000/02/28 21:49:58 andreaha Exp $
#include "eng.h"

/************************************************************************/
/* World                                                                */
/************************************************************************/

/*************
  World::World()

  World constructor, initializes a new
  World object, checking host system and
  initializing all data. Precalculates
  some tables, generates default material,
  sets default values.

  ************/
World::World() {
  dpush1("World::World()");

  setConstants();

  print("\n");
  debug("===================================================================");
  debug(" Bitto 3D Engine v1.0b                                             ");
  debug(" (c) Copyright 1999 INF development                                ");
  debug("-------------------------------------------------------------------");
  debug2("Debug output for world [%#010x]:", (int )this);

  int a = 1;
  char *b = (char *)&a;
  if (*b) {
    debug("System is littleendian.");
    endian = LITTLE;
  } else {
    debug("System is bigendian.");
    endian = BIG;
  }

  /* array list ptrs */

  trigonlist = NULL;
  sortedlist = NULL;
  renderlist = NULL;

  /* boolean operators */

  initcalled = FALSE;
  errorsignal = FALSE;

  /* diverse element counters */

  numoftrigons = 0;
  numofobjects = 0;
  numoflights = 0;
  numofmaterials = 0;
  numofcameras = 0;

  /* render info */                                 
  rendx = 0;                                     /* standard rendering */
  rendy = 0;                                     /* 256 x 256          */
  rendbpp = 0;                                   /* 32 bit rendering   */
  rendbuffer = NULL;
  vrtscr = NULL;
  precy = NULL;
  yval = NULL;
  rendernum = 0;
  visibleobjects = 0;
  antialias = 0;
  setantialias = 0;

  /* memory counters */

  memused = 0;
  memtotal = sizeof(World);

  objectmem = 0;
  trigonmem = 0;
  materialmem = 0;
  lightmem = 0;
  cameramem = 0;

  ambient.set(30, 15, 7);

  /* default function calls */

  for (int i = 0; i < 193; i++)
    precc[i] = (unsigned char)((float )i * (float )(1.0 / 0.75)); 

  genDefaultMaterial();

  signal(SIGINT,  &trapCtrlBreak);
  signal(SIGSEGV, &trapSegFault);
  signal(SIGILL,  &trapSegFault);
  signal(SIGBUS,  &trapSegFault);
  signal(SIGABRT, &trapSegFault);

  setAntiAlias(FALSE);

  setRend(256, 256, 8);

  dpop();
}


/*************
  World::~World()

  World destructor, reports scene info, frees
  all objects, materials, lights and cameras,
  reports memory leaks and frees all unreleased
  memory.

  ************/
World::~World() {
  dpush1("World::~World()");

  if (errorsignal) {
      print("\r===================================\n");
  }

  print2("\r| Rendered frames: %-6i         |\n", frame);
  print2("\r| Average fps:     %-6.2f         |\n", (float )frame /(float )(time(0)-starttime));
  print("\r===================================\n\n");

  debug("Deleting World.");

  if (trigonlist != NULL) freeMem(trigonlist);
  if (sortedlist != NULL) freeMem(sortedlist);
  if (renderlist != NULL) freeMem(renderlist);
  if (rendbuffer != NULL) freeMem(rendbuffer);
  if (precy != NULL) freeMem(precy);
  if (yval != NULL)  freeMem(yval);

  if (rendbpp != 32)
    if (vrtscr != NULL) freeMem(vrtscr);
  
  debug("Deleting materials");
  matlist.kill();

  debug("Deleting objects");
  objlist.kill();

  debug("Deleting lights");
  lightlist.kill();

  debug("Deleting cameras");
  camlist.kill();

  if (memused > 0) {
    if (memused > 8192)
      printf("\n Memory leak! %ik memory unreleased.", memused>>10);
    else
      printf("\n Memory leak! %i bytes memory unreleased.", memused);
  }

  if (errorsignal == 0) {
    debug("Exited with no errors.\n");
  } else if (errorsignal == 1) {
    debug("Exited with 1 error.\n");
  } else debug2("Exited with %i errors.\n", errorsignal);

  if (errorsignal != 0) {
    dpop();
    exit(0);
  }

  print("\n\n");

  dpop();
}

/*************
  void *World::getMem(size_t memsize)

  param: memsize == size in bytes of memory chunk.

  Returns: pointer to memory area.

  Links all grabbed memory to list, freeing
  all memory on world deletion, reports all
  memory leaks.

  ************/
void *World::getMem(size_t memsize) {
  dpush2("World::getMem(%i)", memsize);

  MemChunk *m = new MemChunk(this, memsize);

  if (m->memptr == NULL) {
    error2("Out of memory. Please wait while freeing %3.1fmb", (float)memused / (1024.0*1024.0));
    dpop();
    exit(0);
  }

  memlist.addel(m);

  memset(m->memptr, 0, memsize);

  debug2(" total: %6ik",memused>>10);

#ifndef DEBUG
  print2("\r Allocated %s memory.                  ", util.bytes(memused));
#endif

  dpop();
  return m->memptr;
}

/*************
  void World::freeMem(void *memptr)

  param: memptr == pointer to memory, returned by getMem(x).

  Checks memory linked list and frees memory.

  ************/
void World::freeMem(void *memptr) {
  dpush2("World::freeMem(%#010x)",(int)memptr);

  MemChunk *step = memlist.getFirst();

  if (step == NULL) {
    dpop();
    return;
  }

  while ((step != NULL) && (step->memptr != memptr))
    step = memlist.getNext(); 
 
  memlist.remel(step);

  debug2(" total: %6ik",memused>>10);

#ifndef DEBUG
  print2("\r Allocated %s memory.                  ", util.bytes(memused));
  if (memused == 0) {
    print("\r All memory freed.                      \n");
  }
#endif

  dpop();
}

/*************
  World::Object *World::newObject(char *c)
  World::Object *World::newObject(char *c, float f, float g)

  param: c == name of mesh file, f = object mul, g = texture mul.

  Loads a mesh file into world.

  Example:
  World *w = new World;
  World::Object *o = w->newObject("mesh.asc", 1.0, 256.0);

  Using 256 as last parameter causes texture to be mapped
  from 0-255 alongst each axis.
 
  ************/
World::Object *World::newObject(char *c) {  
  dpush2("World::newObject(\"%s\")", c);

  if (initcalled) {
    print2("World->init() has been called. Object \"%s\" rejected.\n",c);
    dpop();
    exit(0);
  }

  dpop();
  return newObject(c, 1, 256);
}
World::Object *World::newObject(char *c, float f, float g) {
  dpush4("World::newObject(\"%s\", %3.1f, %3.1f)",c,f,g);

  if (initcalled) {
    print2("World->init() has been called. Object \"%s\" rejected.\n",c);
    dpop();
    exit(0);
  }

  debug("===========================================================================");
  debug2("Loading object [%s] ", c);

  Object *o = new Object(this, c, f, g);

  objectmem += sizeof(Object);
  memtotal += sizeof(Object);
  numofobjects++;

  objlist.addel(o);
  
  debug2("Object [%s] ok.", c);
  debug("---------------------------------------------------------------------------");

  dpop();
  return o;
}

/*************
  World::Material *World::newMaterial(char *filen)

  param: filen == name of pixture file.

  Returns: Pointer to new Material object.

  The function auto-detects the material file type,
  loads, converts and scales the texture appropriately.
  All textures are assumed to be square 256x256. If
  they are not, they are scaled.

  Example:
  World *w = new World;
  World::Material *m = w->newMaterial("map.pcx");

  ************/
World::Material *World::newMaterial(char *filen) {
  dpush2("World::newMaterial(\"%s\")", filen);

  if (initcalled) {
    print2("World->init() has been called. Material \"%s\" rejected.\n",filen);
    dpop();
    exit(0);
  }

  debug("===========================================================================");
  debug2("Loading material [%s] ", filen);

  Material *m = new Material(this);

  materialmem += sizeof(Material);
  memtotal += sizeof(Material);
  numofmaterials++;

  if (m->importFile(filen) == ERROR) {
    error2("Error loading material [%s]", filen);
    dpop();
    exit(0);
  }
  
  matlist.addel(m);

  debug2("Material [%s] ok. ", filen); 
  debug("---------------------------------------------------------------------------");

  dpop();
  return m;
}


/*************
  World::Light *World::newLight() # calls newLight(1000,1,1,1)
  World::Light *World::newLight(i,red,green,blue)

  param: i = range in world space, red,green,blue are
  color values (0 >= x >= 1).

  Returns: Pointer to new Light object.

  Example:
  World *w = new World;
  World::Light *l = w->newLight(400, 1, 0, 0);

  ************/
World::Light *World::newLight() {
  dpush1("World::newLight()");

  if (initcalled) {
    print("World->init() has been called. Light rejected.\n");
    dpop();
    exit(0);
  }


  dpop();
  return newLight(1000, 1.0, 1.0, 1.0);         /* Create white light, range 1000 */
}
World::Light *World::newLight(float i, float red, float green, float blue) {
  dpush5("World::newLight(%3.1f, %3.1f, %3.1f, %3.1f)",i,red,green,blue);

  if (initcalled) {
    print("World->init() has been called. Light rejected.\n");
    dpop();
    exit(0);
  }

  Light *l = new Light(i, red, green, blue);

  lightmem += sizeof(Light);
  memtotal += sizeof(Light);
  numoflights++;

  lightlist.addel(l);

  l->myworld = this;

  dpop();
  return l;
}

/*************
  World::Camera *World::newCamera()

  Returns: pointer to a new camera
  object.

  Example:
  World *w = new World;
  World::Camera *c = w->newCamera();

  ************/
World::Camera *World::newCamera() {
  dpush1("World::newCamera()");

  if (initcalled) {
    print("World->init() has been called. Camera rejected.\n");
    dpop();
    exit(0);
  }

  Camera *c = new Camera;

  cameramem += sizeof(Camera);
  memtotal += sizeof(Camera);
  numofcameras++;

  camlist.addel(c);

  setActiveCamera(c);
  c->myworld = this;

  dpop();
  return c;
}

/*************
  void World::setActiveCamera(Camera *c)

  param: c == camera to be set active.

  The 'active' camera is the camera used
  when rendering the scene.

  ************/
void World::setActiveCamera(Camera *c) {
  dpush2("World::setActiveCamera(%#010x)",(int)c);

  if (activecamera)
    activecamera->on = FALSE;
  activecamera = c;
  c->on = TRUE;

  dpop();
}

/*************
  void World::genDefaultMaterial()

  Generates a default 256x256 material
  with typical no-flags-set. The material
  is an ugly grid with the letters
  "UNMAPPED" writtin in the middle.

  ************/
void World::genDefaultMaterial() {
  dpush1("World::genDefaultMaterial()");

  char text[8][50];
  int white = ((255<<24) + (255<<16) + (255<<8) + 255);
  defaultmat = new Material(this);
  int *d = defaultmat->txt;

  matlist.addel(defaultmat);
  materialmem += sizeof(Material);
  memtotal += sizeof(Material);
  numofmaterials++;

  strcpy(text[7], "                                              ");
  strcpy(text[6], " X  X X   X XX XX  XX  XXXX XXXX XXXX XXX     ");
  strcpy(text[5], " X  X XX  X X X X X  X X  X X  X X    X  X    ");
  strcpy(text[4], " X  X X X X X   X XXXX XXXX XXXX XXXX X  X    ");
  strcpy(text[3], " X  X X  XX X   X X  X X    X    X    X  X    ");
  strcpy(text[2], " X  X X   X X   X X  X X    X    X    X  X    ");
  strcpy(text[1], " XXXX X   X X   X X  X X    X    XXXX XXX     ");
  strcpy(text[0], "                                              ");

  for (int y = 0; y < 256; y++)
    for (int x = 0; x < 256; x++) {
      if ((y > 30) && (y < 226) && (x > 30) && (x < 226)) {
	*d++ = (text[(y/2) % 8][(x/2) % 47] == 'X') ? white : 0;
      } else {
	if ((y&7) == 0) *d++ = white;
	else if ((x&7) == 0) *d++ = white;
	else *d++ = 0;
      }
    }

  /* Create MIP maps */
  antiFlip(defaultmat->txt, defaultmat->txtm1, 128, 128);
  antiFlip(defaultmat->txtm1, defaultmat->txtm2, 64, 64);
  antiFlip(defaultmat->txtm2, defaultmat->txtm3, 32, 32);
  antiFlip(defaultmat->txtm3, defaultmat->txtm4, 16, 16);

  dpop();
}

/*************
  void World::init()

  Closes world for all import. No objects or
  cameras can be loaded / generated after this
  function has been called. This is because of
  the static allocation of 'trigonlist', 'sortedlist'
  and 'renderlist'.

  ************/
void World::init() {
  dpush1("World::init()");

  int worldsize;
  initcalled = TRUE;

  trigonlist = (Trigon **)getMem(numoftrigons * sizeof(Trigon **));  
  sortedlist = (Trigon **)getMem(numoftrigons * sizeof(Trigon **));  
  renderlist = (Trigon **)getMem(numoftrigons * sizeof(Trigon **));  

  memtotal += numoftrigons * sizeof(Trigon **) * 3;

  worldsize = sizeof(World) + vrtscrsize + rendbuffersize
                            + numoftrigons * sizeof(Trigon **) * 3;

  if (numoftrigons == 0) {
    error("No polys, nothing to render");
    dpop();
    exit(0);
  }
  if (numofcameras == 0) {
    error("No cameras, nothing to render");
    dpop();
    exit(0);
  }

  print ("\n\n-----------------------------------\n");
  print ("| World closed, initiation begins |\n");
  print ("===================================\n");
  print2("| World:             %s |\n", util.bytes(worldsize));
  print3("| Materials: %-6i  %s |\n", numofmaterials, util.bytes(materialmem));
  print3("| Objects:   %-6i  %s |\n", numofobjects,   util.bytes(objectmem));
  print3("| Lights:    %-6i  %s |\n", numoflights,    util.bytes(lightmem));
  print3("| Cameras:   %-6i  %s |\n", numofcameras,   util.bytes(cameramem));
  print3("| Trigons:   %-6i  %s |\n", numoftrigons,   util.bytes(trigonmem));
  print ("-----------------------------------\n");
  print2("| World memory total:%s |\n", util.bytes(memtotal));
  print ("===================================\n");

  /*******/

  /* Check for wrapping error */
  wrappingCorrection();  

  /* Reset object */
  reset();

  /* This is to correct an old error     */
  /* preventing the first rendered frame */
  /* from to be wrong.                   */
  render();
  reset();

  dpop();
}

/*************
  void World::setRend(xres, yres, bpp)

  Changes render conditions, reallocates
  memory and recalculates lookup tables.

  Render-X and Render-Y can be changed in
  real-time, as can Render-BPP (for what
  use that is).
  
  ************/
void World::setRend(int xres, int yres, int bpp) {
  dpush4("World::setRend(%i, %i, %i)",xres,yres,bpp);

  if (setantialias == TRUE) {
    preProcess = &antiFlip;
    antialias = TRUE;
  } else {
    preProcess = &doNothing;
    antialias = FALSE;
  }

  viewx = (xres + 7) & ~7; /* Round up */
  viewy = (yres + 7) & ~7;

  if (xres < 8) {
    error("Render (x <= 0)");
  }
  if (yres < 8) {
    error("Render (y <= 0)");
  }

  rendx = viewx * (antialias + 1);
  rendy = viewy * (antialias + 1);

  screensize = viewx * viewy;
  rendbuffersize = rendx * rendy * sizeof(int);

  midx = rendx / 2;
  midy = rendy / 2;
  maxx = rendx - 1;
  maxy = rendy - 1;

  lowerscanline = maxx;
  upperscanline = 0;

  hmul = 1.0;
  vmul = 1.0;

  if (yval       != NULL) freeMem(yval);            /* trigonfiller y values */
  if (precy      != NULL) freeMem(precy);           /* precalc Y values */
  if (rendbuffer != NULL) freeMem(rendbuffer);      /* free memory */
  if (rendbpp    != 32)                             /* vrtscr == rendbuf*/
    if (vrtscr   != NULL) freeMem(vrtscr);          /* if rendbpp == 32 */
  else
    vrtscr = NULL;

  rendbpp = bpp;

  switch (bpp) {                                    /* check for valid BPP */
  case 8:
    vrtscrsize = screensize * sizeof(char);
    blit = &(blit32_to_8c);
    break;
  case 15:
    vrtscrsize = screensize * sizeof(short);
    blit = &(blit32_to_15);
    break;
  case 16:
    vrtscrsize = screensize * sizeof(short);
    blit = &(blit32_to_16);
    break;
  case 24:
    vrtscrsize = screensize * 3 * sizeof(char);
    blit = &(blit32_to_24);
    break;
  case 32:
    vrtscrsize = screensize * sizeof(int);
    blit = &(blit32_to_32);
    break;
  default: error2("Invalid render depth: %i bpp", bpp);
    return;
  }

  debug2("Rendbuffersize: %i bytes.", rendbuffersize);

  precy = (int *)getMem(rendy * sizeof(int));
  rendbuffer = (int *)getMem(rendbuffersize);

  if (bpp == 32)
    vrtscr = (void *)rendbuffer;
  else
    vrtscr = (void *)getMem(vrtscrsize);

  for (int i = 0; i < rendy; i++)
    precy[i] = i * rendx;

  yval = (Yvalc *)getMem(rendy * sizeof(Yvalc));

  memset(rendbuffer, 0, rendbuffersize);
  memset(vrtscr, 0, vrtscrsize);

  Camera *c = camlist.getFirst();
  while (c) {
    if (c->focus) {
      freeMem(c->addbuffer);
      c->addbuffer = (unsigned int *)getMem(rendbuffersize * sizeof(int));
    }
    c = camlist.getNext();
  }

  debug("Render resolution/depth changed.");
  dpop();
}

/*************
  void World::render() # calls render(vrtscr)
  void World::render(void *screen)
  
  param: screen == output virtual screen

  Renders scene onto 'screen'.

  ************/
void World::render() {
  dpush1("World::render()");

  render(rendbuffer);                              /* simply passes renderbuffer */

  dpop();
}
void World::render(void *screen) {
  dpush2("World::render(%#010x)", (int)screen);
  debug2("Rendering frame #%i.\n", frame);

  int upper, lower;

  if (activecamera == NULL) {
    dpop();
    return;
  }

  if (initcalled == 0) {
    debug("Init not called. Forcing init.");
    init();
  }

  if (frame == 0)
    starttime = time(0);

  rendernum = 0;
  frame++;

  if (upperscanline < lowerscanline) {
    upper = upperscanline * rendx;
    lower = upper + ((maxy - lowerscanline) * rendx);
  } else {
    upper = 0;
    lower = 0;
  }

//  memset((char *)screen + upper, 0, rendbuffersize - lower);
  memset(screen, 0, rendbuffersize);

  lowerscanline = 0;
  upperscanline = maxx;

  rotate();

  cullBackFaces();

  makeRenderList();

  sort();

  for (int i = rendernum - 1; i >= 0; i--)
    renderlist[i]->drawIt((int *)screen);

  (this->*preProcess)(screen, screen, viewx, viewy);
 
  reset();
  dpop();
}

/*************
  void World::blitTo(void *)

  Draws 

  ***********/
void World::blitTo(void *screen) {
  dpush2("World::blitTo(%#010x)", (int )screen);
  debug("Blitting.\n");

  (this->*blit)(screen);
  dpop();
}

/*************
  void World::reset()

  Reset world, set all objects
  back to original positions.

  ************/
void World::reset() {
  dpush1("World::reset()");
  debug("Resetting.");

  World::Light *l;
  World::Object *o;

  l = lightlist.getFirst();
  while (l) {
    l->posr.set(&l->pos);
    l = lightlist.getNext();
  }

  o = objlist.getFirst();
  while (o) {
    if (o->visible)
      o->reset();
    o = objlist.getNext();
  }

  dpop();
}


/*************
  void World::sort()

  Sorts all polys in renderlist using
  Radix sort (bucket sort / B-trees).
  Sort key is (a.z+b.z+c.z) / 4.

  ************/
void World::sort() {
  dpush1("World::sort()");
  debug("Sorting.\n");

  static int numbers[256];
  short int a, b, d;

  if (rendernum == 0) {
    debug("Nothing to sort.");
  }

  /* clear numbers array */
  memset(numbers, 0, sizeof(numbers));
  /* count number of each byte-value */
  for (int i = 0; i < rendernum; i++)
    numbers[renderlist[i]->z & 255]++;

  /* convert from numbers to offset in list to sort */
  a = 0;
  for (int i = 0; i < 256; i++) {
    b = numbers[i];
    numbers[i] = a;
    a += b;
  }

  /* sort according to low byte (least significant) */
  for (int i = 0; i < rendernum; i++) {
    a = renderlist[i]->z & 255;
    d = numbers[a];
    sortedlist[d] = renderlist[i];
    numbers[a]++;
  }

  /* now sort by high byte (most significant) */
  memset(numbers, 0, sizeof(numbers));

  /* count number of each byte-value */
  for (int i = 0; i < rendernum; i++)
    numbers[(sortedlist[i]->z & 0xff00) >> 8]++;

  /* convert from numbers to offset in list to sort */
  a = 0;
  for (int i = 0; i < 256; i++) {
    b = numbers[i];
    numbers[i] = a;
    a += b;
  }

  /* sort according to high byte */
  for (int i = 0; i < rendernum; i++) {
    a = (sortedlist[i]->z & 0xff00) >> 8; 
    d = numbers[a];
    renderlist[d] = sortedlist[i];
    numbers[a]++;
  }

  dpop();
}

/*************
  void World::cullBackFaces()

  Removes all single-sided faces which are
  faced away from camera view. Check is made
  using a 2D face normal, checking if the 
  length of this normal is positive or negative.

  ************/
void World::cullBackFaces() {
  dpush1("World::cullBackFaces()");
  debug("Culling.\n");

  float c;
  float x1, x2, x3;
  float y1, y2, y3;

  World::Trigon *t;
  World::Object *o;

  o = objlist.getFirst();

  while (o != NULL) {
    if (o->visible) {
      t = o->trigonlist.getFirst();

      while ((t) && (t->twosided == 0)) {

	/*
	  FEIL:
	  I første frame er alle x1, x2, x3 like.
	  Hvorfor er det tilfelle?
	  Det gjør at c = 0, og t->visible = false.
	 */

        x1 = t->ap->posr->x;                       /* get perspective coords */
        y1 = t->ap->posr->y;
        x2 = t->bp->posr->x;
        y2 = t->bp->posr->y;
        x3 = t->cp->posr->x;
        y3 = t->cp->posr->y;

        c  = ((x1-x2) * (y3-y2)) - ((y1-y2) * (x3-x2));  /* 2d,generate normal */

        if (c <= 0)
          t->visible = FALSE;
	else
	  t->visible = TRUE;

        t = o->trigonlist.getNext();
      }
    }
    o = objlist.getNext();
  }

  dpop();
}

/*************
  void World::wrappingCorrection()

  For valid 3D objects with wrapping flags
  implemented, this function attempts to
  correct all wrapping errors. If there are
  no wrapping flags (for instance ASC), all
  polys are assumed to have this error.

  ************/
void World::wrappingCorrection() {  
  dpush1("World::wrappingCorrection()");
  debug("Correcting wrapping error.\n");

  World::Trigon *p;
  World::Object *o = objlist.getFirst();
  World::Vertex *v[3];
  int genL = 0;

  while (o) {

    /*
      if (o->filltype > __ZBUFF) zflag = __TRUE;
      */
    
    if (!genL)
    if ((o->filltype&PHONG)||
	(o->filltype&GOURAUD)) {
      genLookUp();
      genL = TRUE;
    }

    p = o->trigonlist.getFirst();

    while (p != NULL) {

      p->ua = p->ap->u;
      p->va = p->ap->v;
      p->ub = p->bp->u;
      p->vb = p->bp->v;
      p->uc = p->cp->u;
      p->vc = p->cp->v;

      v[0] = p->ap;
      v[1] = p->bp;
      v[2] = p->cp;

      if (o->loadtype == Object::FILEASC)
        p->flags = ~0;
     
      /*
         Check for wrapping error
      */

      /* hva har sluttet å virke???*/

      if (p->flags & 8) {  // possible u wrap


        int little  = (v[0]      ->u > v[1]->u) ?      1 : 0;
            little  = (v[little] ->u < v[2]->u) ? little : 2;
        int big     = (v[0]      ->u < v[1]->u) ?      1 : 0;
            big     = (v[big]    ->u > v[2]->u) ?    big : 2;

        if ((v[big]->u - v[little]->u) > (0.8 * o->txtscale)) {
          if (p->ua < (0.5 * o->txtscale)) {
	    p->ua    += o->txtscale;
	    p->ap->u += o->txtscale;
	  }
          if (p->ub < (0.5 * o->txtscale)) {
	    p->ub    += o->txtscale;
	    p->bp->u += o->txtscale;
	  }
	  if (p->uc < (0.5 * o->txtscale)) {
	    p->uc    += o->txtscale; 
	    p->cp->u += o->txtscale;
	  }
        }
      }

      if (p->flags & 16) { // possible v wrap

        int little  = (v[0]      ->v > v[1]->v) ?      1 : 0;
            little  = (v[little] ->v < v[2]->v) ? little : 2;
        int big     = (v[0]      ->v < v[1]->v) ?      1 : 0;
            big     = (v[big]    ->v > v[2]->v) ?    big : 2;

        if ((v[big]->v - v[little]->v) > (0.8 * o->txtscale)) {
          if (p->va < (0.5 * o->txtscale)) {
	    p->va    += o->txtscale;
	    p->ap->v += o->txtscale;
	  }
          if (p->vb < (0.5 * o->txtscale)) {
	    p->vb    += o->txtscale;
	    p->bp->v += o->txtscale;
	  }
	  if (p->vc < (0.5 * o->txtscale)) {
	    p->vc    += o->txtscale; 
	    p->cp->v += o->txtscale;
	  }
        }
      }

      p = o->trigonlist.getNext();
    }
    o = objlist.getNext();
  }

  dpop();
}

/*************
  void World::makeRenderList()

  Creates a list of all trigons to be
  drawn. Trigons must be:
    1) Inside FOV (visible)
    2) Not a backface if singesided.

  The functions collects data from
  'World::cullBackFaces()'.

  ************/
void World::makeRenderList() {
  dpush1("World::makeRenderList()");
  debug("Making renderlist.\n");

  Trigon *t;
  Object *o;

  /* make renderlist
     conditions:
     1) inside FOV
     2) not a backface */

  o = objlist.getFirst();
  while (o) {
    if (o->visible) {
      debug2("  Visible object: [%s]", o->name);
      t = o->trigonlist.getFirst();
      while (t != NULL) {
        if (t->visible && (!t->lostPoly())) {
          t->z = CEIL((t->ap->posr->z +             /* generate average poly Z */
                      t->bp->posr->z +
                      t->cp->posr->z) * 0.25);
          renderlist[rendernum++] = t;              /* if poly is visible */
        }
        t = o->trigonlist.getNext();
      }
    }
    o = objlist.getNext();
  }
  dpop();
}

/*************
  void World::rotate()

  This function creates all matrices, builds
  the object hierarchy, then transformes all
  visible objects. Lights are also calculated,
  gouraud values updated, and perspective FOV
  division is done.

  ************/
void World::rotate() {
  dpush1("World::rotate()");
  debug("Rotating.\n");

  float vx, vy, vz;
  float vdx, vdy;
  float divz;

  World::Object *o;
  World::Light  *l;
  World::Vector *v;
  World::Vector *vr;
  World::Vector *n;
  World::Vector *nr;

  vdx = viewdistancex = FOV2FOC((float )rendx, DEG2RAD(activecamera->fov)) * hmul;
  vdy = viewdistancey = FOV2FOC((float )rendy, DEG2RAD(activecamera->fov)) * vmul;

  /* with this camera's view */

  activecamera->createMatrix();

  /*-----------------------------------------*/
  /*  Create all object matrices             */
  o = objlist.getFirst();
  while (o != NULL) {
    o->createMatrix();
    o = objlist.getNext();
  }

  /*-----------------------------------------*/
  /*  Create all light matrices              */
  l = lightlist.getFirst();
  while (l != NULL) {
    l->createMatrix();
    l = lightlist.getNext();
  }

  /*-----------------------------------------*/
  /*  Eliminate objects outside FOV          */
  eliminateLostObjects(); 

  /*-----------------------------------------*/
  /*Rotate visible objects & project vertices*/
  o = objlist.getFirst();

  while (o) {
    if (o->visible) {

      v = &o->vertvectors[0];
      vr = &o->vertvectorsr[0];

      n = &o->normals[0];
      nr = &o->normalsr[0];

      for (int i = 0; i < o->numofverts; i++) {
	
      	vr->genTransform(&o->matrix);

        vx = n->x;
        vy = n->y;
        vz = n->z;

	nr->x = (vx * o->matrix.m[0])
              + (vy * o->matrix.m[1])
              + (vz * o->matrix.m[2]);
	nr->y = (vx * o->matrix.m[4])
              + (vy * o->matrix.m[5])
              + (vz * o->matrix.m[6]);
	nr->z = (vx * o->matrix.m[8])
              + (vy * o->matrix.m[9])
              + (vz * o->matrix.m[10]);

	n++; nr++; v++; vr++;
      }
    }
    o = objlist.getNext();
  }

  /* light rotation */
  l = lightlist.getFirst();
  while (l) {
    l->posr.genTransform(&l->matrix);
    l = lightlist.getNext();
  }

  /* gouraud mapping */
  o = objlist.getFirst();
  while (o) {
    if (o->visible) {
      if ((o->filltype & GOURAUD)||
	  (o->filltype & PLAIN))
	o->calcGouraudValues();
    }
    o = objlist.getNext();
  }

  /* -------------------------- */
  /* perform perspective muls   */

  debug("  transformations...");
  
  o = objlist.getFirst();
  while (o) {
    if (o->visible) {

      vr = &o->vertvectorsr[0];

      for (int i = 0; i < o->numofverts; i++) {
        divz = (float )1.0 / vr->z;
	vr->x = (vr->x * vdx * divz) + midx;
	vr->y = (vr->y * vdy * divz) + midy;
	o->vertices[i].cyr = CEIL(vr->y);
	vr++;
      }
    }
    o->checkForClip();
    o = objlist.getNext();
  }

  dpop();
}

/*************
  void World::eliminateLostObjects()

  Checks is objects' bounding boxes are
  partially and/or completely inside the render
  area (FOV). If the object is outside this area,
  there is no way it will be drawn. The 'visible'
  flag is set to 'FALSE', and the object is
  disregarded in all later transformations.

  ************/
void World::eliminateLostObjects() {
  dpush1("World::eliminateLostObjects()");
  debug("Eliminating lost objects.\n");

  float      divz;
  float      xrad;
  float      yrad;
  float      nx, ny;
  float      vx, vy, vz;
  float      vdx, vdy;
  Object     *o;
  Vector     *v;

  visibleobjects = 0;

  vdx = viewdistancex;
  vdy = viewdistancey;

  o = objlist.getFirst();
  while (o) {
    if (!o->on)
      o->visible = FALSE;
    else {

      o->visible = TRUE;

      v = o->transBsph();
      vx = v->x;
      vy = v->y;
      vz = v->z;

      o->bspherer.set(vx, vy, vz);

      if (vz == 0) {
	error("Division by zero in eliminatelost");
	return;
      }

      divz = (float )1.0 / vz;
      nx = (vx * vdx * divz) + midx;
      ny = (vy * vdy * divz) + midy;

      xrad = o->bsrad * vdx * divz;
      yrad = o->bsrad * vdy * divz;

      if (!o->on) o->visible = FALSE;
      if ((nx + xrad) < 0)    o->visible = FALSE;
      if ((nx - xrad) > maxx) o->visible = FALSE;
      if ((ny + yrad) < 0)    o->visible = FALSE;
      if ((ny - yrad) > maxy) o->visible = FALSE; 
      if (o->visible) visibleobjects++;
    }
    o = objlist.getNext();
  }
  dpop();
}

/****/ /*************/ /****/
/****/ /* BLITTING  */ /****/
/****/ /*************/ /****/

/*************
  void World::blit32_to_32(void *screen)

  param: screen == output virtual memory.

  Does nothing, as 'rendbuffer' is already
  a 32bit image and virtualscreen == rendbuffer.

  ************/
void World::blit32_to_32(void *screen) {
  dpush1("World::blit32_to_32()");

  memcpy(screen, rendbuffer, rendbuffersize);

  dpop();
}

/*************
  void World::blit32_to_24(void *screen)

  param: screen == output virtual memory.

  Reads a 32bit r|g|b|a image from 'rendbuffer',
  one byte at a time, skipping the alpha byte.

  ************/
void World::blit32_to_24(void *screen) {
  dpush1("World::blit32_to_24()");

  register char *from = (char *)rendbuffer;
  register char *to   = (char *)screen;
  register char r, g, b;

  switch (endian) {
  case LITTLE:
    for (unsigned int i = 0; i < screensize; i++) {
      from++;    
      r = *from++;
      g = *from++;
      b = *from++;
      *to++ = b;
      *to++ = g;
      *to++ = r;
    } break;
  case BIG:
    for (unsigned int i = 0; i < screensize; i++) {
      *to++ = *from++;
      *to++ = *from++;
      *to++ = *from++;
      from++;    
    }
  }

  dpop();
}

/*************
  void World::blit32_to_16(void *screen)

  param: screen == output virtual screen

  Converts a 32bit r|g|b|a image from 'rendbuffer'
  to a 16bit r|g|b. 16bit is 5+6+5, with the green
  component having one more bit resolution.

  ************/
void World::blit32_to_16(void *screen) {
  dpush1("World::blit32_to_16()");

  register unsigned int *from = (unsigned int *)rendbuffer;
  register unsigned int *to   = (unsigned int *)screen;
  register unsigned int f, g;

  switch (endian) {
  case LITTLE:
    for (unsigned int i = 0; i < screensize; i += 2) {
      f = *from++;
      g = *from++;
      *to++ = (((f>>16)&0x0000f800)|((f>>13)&0x000007e0)|((f>>11)&0x0000001f))|
               ((g     &0xf8000000)|((g<< 3)&0x07e00000)|((g<<5)&0x001f0000));
    } break;
  case BIG:
    for (unsigned int i = 0; i < screensize; i += 2) {
      f = *from++;
      g = *from++;
      *to++ = (((g>>16)&0x0000f800)|((g>>13)&0x000007e0)|((g>>11)&0x0000001f))|
               ((f     &0xf8000000)|((f<< 3)&0x07e00000)|((f<<5)&0x001f0000));
    } break;
  }

  dpop();
}

/*************
  void World::blit32_to_15(void *screen)

  param: screen == output virtual screen

  Converts a 32bit r|g|b|a image from 'rendbuffer'
  to a 15bit r|g|b. 15bit is 5+5+5 bits.

  ************/
void World::blit32_to_15(void *screen) {
  dpush1("World::blit32_to_15()");

  short *from = (short *)rendbuffer;
  short *to   = (short *)screen;
  short red, green, blue;

  for (unsigned int i = 0; i < screensize; i++) {
    red = *from++ >> 8;
    green = (red >> 8) >> 3;                    /* mask 8 bits green */
    red =  (red & 255) >> 3;                    /* mask 8 bits red */
    blue = (*from++ >> 24) >> 3;                /* mask 8 bits blue */

    *to++ = red | (green << 5) | (blue << 10);    /* merge colors */
  }

  dpop();
}

/*************
  void World::blit32_to_8c(void *screen)

  param: screen == output virtual screen

  Converts a 32bit r|g|b|a image from 'rendbuffer'
  to 8bit b|g|r output using 3+3+2 palette. Palette
  generating:

  for (int r = 0; r < 8; ++r)
    for (int g = 0; g < 8; ++g)
      for (int b = 0; b < 4; ++b)
        SetPaletteColor((r << 5) | (g << 2) | b,
                        r * 32, g * 32, b * 64);       

  ************/
void World::blit32_to_8c(void *screen) {
  dpush1("World::blit32_to_8c()");

  register unsigned int *from  = (unsigned int *)rendbuffer;
  register unsigned int *to   = (unsigned int *)screen;
  register unsigned int in1, in2, in3, in4;

  switch (endian) {
  case LITTLE:
    for (unsigned int i = 0; i < screensize; i += 4) {
      in4 = *from++;
      in3 = *from++;
      in2 = *from++;
      in1 = *from++;
	*to++ = ((in4>>14)&0x00000003)|((in4>>19)&0x0000001c)|((in4>>24)&0x000000e0)|
	        ((in3>> 6)&0x00000300)|((in3>>11)&0x00001c00)|((in3>>16)&0x0000e000)|
	        ((in2<< 2)&0x00030000)|((in2>> 3)&0x001c0000)|((in2>> 8)&0x00e00000)|
	        ((in1<<10)&0x03000000)|((in1<< 5)&0x1c000000)|( in1     &0xe0000000);
    } break;
  case BIG:
    for (unsigned int i = 0; i < screensize; i += 4) {
      in1 = *from++;
      in2 = *from++;
      in3 = *from++;
      in4 = *from++;
	*to++ = ((in4>>14)&0x00000003)|((in4>>19)&0x0000001c)|((in4>>24)&0x000000e0)|
	        ((in3>> 6)&0x00000300)|((in3>>11)&0x00001c00)|((in3>>16)&0x0000e000)|
	        ((in2<< 2)&0x00030000)|((in2>> 3)&0x001c0000)|((in2>> 8)&0x00e00000)|
	        ((in1<<10)&0x03000000)|((in1<< 5)&0x1c000000)|( in1     &0xe0000000);

    } break;
  }

  dpop();
}



/*************
  void World::blit32_to_8g(void *screen)

  param: screen == output virtual screen
  
  Converts a 32bit r|g|b|a image in 'rendbuffer'
  to a 256 color greyscale image using simple average.
  Two ways to do this: Either use a full 256 color palette,
  scaling (a+b+c)/4 with * 1.33, or to scale down
  the palette.

  precc[] contains all chars from 0-192 mult with 1.33.

  ************/
void World::blit32_to_8g(void *screen) {
  dpush1("World::blit32_to_8g()");

  register unsigned char *from = (unsigned char *)rendbuffer;
  register unsigned char *to   = (unsigned char *)screen;
  register unsigned char red, green, blue;

  switch (endian) {
  case LITTLE:
    for (unsigned int i = 0; i < screensize; i++) {
      red = *from++   >> 2;
      green = *from++ >> 2;
      blue = *from++  >> 2;
      from++;
      *to++ = precc[red + green + blue];
//    *to++ = red + green + blue;
    } break;
  case BIG:
    for (unsigned int i = 0; i < screensize; i++) {
      from++;
      blue = *from++  >> 2;
      green = *from++ >> 2;
      red = *from++   >> 2;
      *to++ = precc[red + green + blue];
//    *to++ = red + green + blue;
    } break;
  }

  dpop();
}

/*************
  void World::setAntiAlias(int i)

  param: i == TRUE / FALSE

  Sets 'setantialias' flag, so the next time
  'setres' is called, the image will be rendered
  with two pixel supersampling.

  ************/
void World::setAntiAlias(int i) {
  dpush2("World::setAntiAlias(%i)", i);

  switch (i) {
  case TRUE: setantialias = TRUE;
    debug("Antialias flag is set. Must call setRend to init.");
    break;
  default: setantialias = FALSE;
  }

  dpop();
}
 

/*************
  void World::antiFlip(void *, void *, int, int);

  param: Output buffer, min size 1/4 of rendbuffersize.

  Resamples one 32bit r|g|b|a image using simple average.
  Red and blue are mask out together, as 0xff * 4 can
  never overflow more that two bits. Alpha byte is here
  not averaged, as it would overflow and would therefore
  have to be seperately averaged.

  ************/
void World::antiFlip(void *frombuf, void *tobuf, int vx, int vy) {
  dpush1("World::antiFlip()");

  unsigned int *from = (unsigned int *)frombuf;
  unsigned int *to  = (unsigned int *)tobuf;
  register unsigned int ul, ur, ll, lr;
  register unsigned int rx, ry;
  /*  register unsigned int red, green, blue; */

  static int REDMASK   = 0xff000000;
  static int GREENMASK = 0x00ff0000;
  static int BLUEMASK  = 0x0000ff00;
  
  rx = vx << 1;
  ry = vy << 1;

  for (int y = 0; y < vy; y++) {
    for (int x = 0; x < vx; x++) {

      ul = *from;
      ur = *(from + 1);
      ll = *(from + rx);
      lr = *(from + rx + 1);
      /*      
      red  = ((ul & REDMASK)>>2) + ((ur & REDMASK)>>2) +
	     ((ll & REDMASK)>>2) + ((lr & REDMASK)>>2);
      red &= REDMASK;

      green  = (ul & GREENMASK) + (ur & GREENMASK) +
	       (ll & GREENMASK) + (lr & GREENMASK);
      green >>= 2;
      green &= GREENMASK;

      blue  = (ul & BLUEMASK) + (ur & BLUEMASK) +
 	      (ll & BLUEMASK) + (lr & BLUEMASK);
      blue >>= 2;
      blue &= BLUEMASK;
      
      *to++ = red | green | blue;
      */
      
      *to++ = ((((ul&REDMASK)>>2)+((ur&REDMASK)>>2)
	       +((ll&REDMASK)>>2)+((lr&REDMASK)>>2))&REDMASK)|
              ((((ul&GREENMASK)+(ur&GREENMASK)+
   	         (ll&GREENMASK)+(lr&GREENMASK))>>2)&GREENMASK)|
              ((((ul&BLUEMASK)+(ur&BLUEMASK)+
   	         (ll&BLUEMASK)+(lr&BLUEMASK))>>2)&BLUEMASK);
		 
      
      from += 2;
    }
    from += rx;
  }

  dpop();
}

/*************
  void World::floydSteinbergDither()

  Dithers a 32bit image to 8bit using 
  Floyd - Steinberg.

  ************/
void World::floydSteinbergDither() {
  dpush1("World::floydSteinbergDither()");

  unsigned char *dithered = (unsigned char *)getMem(vrtscrsize);
  unsigned char *errormap = (unsigned char *)getMem(vrtscrsize);
  char error;
  char in;
  char *d = (char *)dithered;
  int drawsize = vrtscrsize - viewx;

  memcpy(dithered, vrtscr, vrtscrsize);

  for (int i = 0; i < drawsize; i++) {
    in = *d;

    if (in >= 3)
      error = in - 255;
    else
      error = in;

    *(d+1) += 0;
  }

  freeMem(errormap);
  freeMem(dithered);
  dpop();
}

/*************
  void World::doNothing(void *, void *, int, int)

  Does nothing. Used when render-depth
  is invalid, and otherwise just so
  pointer-to-member functions are
  initialized.

  ************/
void World::doNothing(void *, void *, int, int) {
  dpush1("World::doNothing()");

  dpop();
}

/*************
  void *World::getVrtScr()

  returns: A void pointer to
  the internal virtual screen.

  ************/
void *World::getVrtScr() {
  dpush1("World::getVrtScr()");

  dpop();
  return vrtscr;
}


/*************
  void World::genLookUp()

  Generates lookup table for combining
  color components of texture with light.

  ************/
void World::genLookUp() {
  dpush1("World::genLookUp()");

  int *l = lookup;

  for (int i = 0; i < 256; i++)
    for (int j = 0; j < 256; j++)
      *l++ = ((i * j)>>7) > 255 ? 255 : ((i * j)>>7);

  dpop();
}

/*************
  void World::saveBufAsPPM24(char *filename)

  Saves last rendered frame as PPM 6, using
  raw encoding.

  ************/
void World::saveBufAsPPM24(char *filename) {
  dpush2("World::saveBufAsPPM24(%s)", filename);

  FILE *fp;
  char *buf;

  buf = (char *)getMem(vrtscrsize);

  blit32_to_24(buf);

  if ((fp = fopen(filename, "wb"))) {
    fprintf(fp, "P6\n%i %i\n255\n", viewx, viewy);
    fwrite(buf, 1, vrtscrsize*3, fp);
    fclose(fp);
  } else {
    printf("Error: Can't write to %s.\n", filename);
  }

  freeMem(buf);
  dpop();
}

/*************
  World::dumpSizes()

  Dumps all world structure sizes.

  ************/
void World::dumpSizes() {
  dpush1("World::dumpSizes()");

  printf("Dumping world structure sizes: \n\n");
  printf(" World              | %-3i bytes.\n", sizeof(World));
  printf(" World::Vector      | %-3i bytes.\n", sizeof(World::Vector));
  printf(" World::Normal      | %-3i bytes.\n", sizeof(World::Normal));
  printf(" World::Vertex      | %-3i bytes.\n", sizeof(World::Vertex));
  printf(" World::Material    | %-3i bytes.\n", sizeof(World::Material));
  printf(" World::Trigon      | %-3i bytes.\n", sizeof(World::Trigon));
  printf(" World::Object      | %-3i bytes.\n", sizeof(World::Object));
  printf(" World::Camera      | %-3i bytes.\n", sizeof(World::Camera));
  printf(" World::Light       | %-3i bytes.\n", sizeof(World::Light));

  dpop();
}

/* world printout */
void World::printOut() {
  dpush1("World::printOut()");

  print2("World [%#010x] printout:\n", (int)this);

  dpop();
}

/* set all world constants */
void World::setConstants() {
  dpush1("World::setConstants()");


  dpop();
}

