
#include "scene.h"

#include "assert.h"
#include <fstream.h>
#include <stdio.h>

#include "jlstring.h"

#include "texture.h"
#include "raycastnode.h"
#include "sphere.h"
#include "box.h"
#include "vertexlist.h"
#include "indexed_polygon.h"
#include "kdtree.h"
#include "globalstate.h"

#ifndef NDEBUG
#define LOG_SCENE
#endif
#include "log.h"
#include "scenelog.h"

//inventor reader; these are in iv/include
#include <QvDB.h>
#include <QvInput.h>
#include <QvNode.h>
#include <QvState.h>

//our hook-ins to inventor reader
#include <QvTraverse.h>
#include <QvCoordinate3.h>
#include <QvFaceSet.h>
#include <QvIndexedFaceSet.h>
#include <QvMaterial.h>
#include <QvElement.h>
#include <QvState.h>
#include <QvBaseColor.h>
#include <QvCube.h>
#include "myvertexlist.h"

#define MAX_LINE_LEN 300 //for the file readers

//problem: there's a list of vertices for indexed face sets to be kept 
//track of, of unknown and probably gargantuan size.
//solution: some constant size (VLIST_ALLOC_SIZE) list is initially alloced
//   if more than that # of vertices come in, more vertex lists are made.
//     -the list is stored in m_vlists; the total # is m_num_vertices
//     -the current list is m_cur_VL

#define VLIST_ALLOC_SIZE 10000
int m_num_vertices = 0; //total # of vertices made so far. All ObjHandler subclasses gotta increment this appropriately.
JPAList <MyVertexList> m_vlists; //all the vertex lists we've made; a list of lists
MyVertexList *m_cur_VL = NULL; //the latest; the one we're currently adding to.
int FindCoord3Start (QvCoordinate3 *coord);
Vec4 GetCurColor (QvState *state);

//problem: in inventor files we have lots of lists, each indexed independently
//solution: associate each SoCoordinate3 with some absolute index into 
//          the big list.
struct CoordAssoc {
  QvCoordinate3 *_coord;
  int _offset;
};
JPAList <CoordAssoc> m_coord_assoc;

//p: it takes a long time to read in the file
//s: status reports every second, via a timer.
//   scene_input_hnd is the timer callback and m_linenum says how many lines we've processed so far.

//  subp: we could finish reading; days later, the timer gets called.
//  subs: m_still_reading, a bool to tell the timer callback if it's still valid

int m_still_reading = 0; //are we still reading? do we need to set the timer for progress reports?
int m_linenum = 0; //current line number of the file that we're reading

//scene_input_hnd provides progress reports when reading in the scene file
void scene_input_hnd (int)
{
  if (m_still_reading)
    {
      itimerval oldval;
      itimerval newval;
      newval.it_interval.tv_sec = newval.it_interval.tv_usec = 0;
      newval.it_value.tv_sec = 1;
      newval.it_value.tv_usec = 0;
      
      signal(SIGVTALRM, scene_input_hnd); //needed so we get called again
      if (0 != setitimer (ITIMER_VIRTUAL, &newval, &oldval))
	cerr << "error settin' timer." << endl;
      
      //tell the score
      assert (truly_GS->_scene._objhandler);
      truly_GS->_scene._objhandler->Report();
    }
}

Scene::Scene() : _root (NULL), _objlist (NULL), _objhandler (NULL) {

	Vec4 ambient_color (0.2, 0.2, 0.2);
	Vec4 light_color (0.6, 0.6, 0.6);

	_L.SetAmbientLight(ambient_color);
#if 0
	_L.AddDirectionalLight(Vec4(-2, 3, 1), light_color);
	_L.AddDirectionalLight(Vec4(1, 1, 3), light_color);
#endif
}

Scene::~Scene() {

//	_root->Kill();
}

Scene& Scene::CopyFrom(const Scene &) {

	cerr << "Error: Scene::CopyFrom() called" << endl;
	exit(-1);

	return *this;
}

/*
ostream& operator<<(ostream &co, const Scene &S) {

	return co;
}

istream& operator>>(istream &ci, Scene &S) {

	return ci;
}
*/


void Scene::Load(const JLString &filename, Vec4 &goodR, Vec4 &goodD, Vec4 &goodU) {

  delete _root;
  delete _objlist;

  char *fname = filename.GetString();

  //if the bbox has already been computed, it'll be in file.bbox.
  //if not, we have to compute it.
  char *bboxname = new char [strlen (fname) + 10];
  strcpy (bboxname, fname);
  strcat (bboxname, ".bbox");
  
  Bounds3d bbox;
  ifstream iFile (bboxname, ios::in | ios::nocreate);
  if (iFile.good())
    {
      float x1, y1, z1, x2, y2, z2;
      iFile >> x1 >> y1 >> z1 >> x2 >> y2 >> z2;
      bbox.Set (Vec4 (x1, y1, z1), Vec4 (x2, y2, z2));
      iFile.close();
    }
  else
    {
      _objhandler = new BBoxCreator (this);
      cerr << "procuring bbox for scene file..." << endl;
      readFile (fname);
      bbox = ((BBoxCreator *)_objhandler)->bbox();
      ofstream oFile (bboxname, ios::out | ios::trunc);

      //avoid precision errors.
      bbox[0] -= Vec4 (.01, .01, .01);
      bbox[1] += Vec4 (.01, .01, .01);
      
      //write out the bbox
      oFile << bbox[0][0] << " " << bbox[0][1] << " " << bbox[0][2] << " ";
      oFile << bbox[1][0] << " " << bbox[1][1] << " " << bbox[1][2] << " ";
      oFile << endl;
      
      //and a helpful comment
      oFile << "bounding box for scene file " << fname << endl;
      oFile.close();
      delete _objhandler;
    }


  SCENELOG (SceneReadFile_Start, new SceneReadFile_Start_Entry (fname));
  cerr << "reading in the scene file for real..." << endl;
  _objhandler = new ObjCreator (this, bbox);
  readFile (fname);

  _objlist = ((ObjCreator *)_objhandler)->getObjList();
  cerr << "total: read in " << _objlist->Size() << " objects, " << m_num_vertices << " vertices." << endl;
  KDTree *k = ((ObjCreator *)_objhandler)->getKDTree();
  k->FinishSetup();
  _root = k;

  cerr << "made k-d tree from " << _objlist->Size() << " objects" << endl;
  cerr << "done loading scene" << endl;
  
  SCENELOG (SceneReadFile_End, new SceneReadFile_End_Entry (_objlist->Size()));
  
  //pick some good viewpoint - outside the scene, looking down on it, b/c
  //we're snooty viewers.
  Bounds3d bounds = _root->bbox();
  goodR = bounds[1] + .25 * (bounds[1] - bounds[0]);
  goodD = bounds[0] - bounds[1]; goodD.FastMakeUnit3();

  //pick smallest dimension as up vector; this seems to work well.
  Vec4 dim = bounds[1] - bounds[0];
  if (dim[0] < dim[1] && dim[0] < dim[2])
    goodU.Set (1,0,0);
  else if (dim[1] < dim[0] && dim[1] < dim[2])
    goodU.Set (0,1,0);
  else
    goodU.Set (0,0,1);
}

void Scene::readJUFF (char *fname)
{
  ifstream iFile(fname, ios::in);
  
  if (!iFile) {
    cerr << "unable to open file: " << fname << endl;
    exit(-1);
  }

  char ch;
  int done = 0;
  float wall_thickness = 0.0625f;
  float wall_height = 1.5f;

  char texname [100];
  char throwaway[100];

  m_linenum = 0;
  while (!done) {

    m_linenum ++;

    iFile >> ch;

    if (iFile.eof())
      break;

    switch (ch) {
    case '#': {
      iFile.getline (throwaway, 100);
      break;
    }
    case 'E': {
      done = 1;
    }
    break;

    case 'K': //k-d tree extent
      float x1, y1, z1, x2, y2, z2;
      iFile >> x1 >> y1 >> z1 >> x2 >> y2 >> z2;
      _objhandler->IncludePoint (Vec4 (x1,y1,z1));
      _objhandler->IncludePoint (Vec4 (x2,y2,z2));
      break;
    case 'N': {
      _objhandler->UseTex (NULL);
    }
    break;
    case 'T': {
      float sx, sy; //scale factors
      iFile >> texname >> sx >> sy;
      _objhandler->UseTex (texname, sx, sy);
    }
    break;
    case 'C': {
      float cr, cg, cb;
      iFile >> cr >> cg >> cb;
      _objhandler->UseColor (cr, cg, cb);
    }
    break;

    case 'L': { //point light 
      float x, y, z;
      float color[3];
      iFile >> x >> y >> z >> color[0] >> color[1] >> color[2];
      _objhandler->AddPointLight (Vec4 (x,y,z), Vec4 (color[0], color[1], color[2]));
    }
    break;
    case 'D':{ //directional light
      float x, y, z;
      float color[3];
      iFile >> x >> y >> z >> color[0] >> color[1] >> color[2];
      _objhandler->AddDirLight (Vec4 (x,y,z), Vec4 (color[0], color[1], color[2]));
    }
    break;

    case 'W': {
      float x1, y1, x2, y2;
      iFile >> x1 >> y1 >> x2 >> y2;
      _objhandler->MakeBox (Bounds3d (x1-wall_thickness,
		       x2+wall_thickness,
		       0,
		       wall_height,
		       y1-wall_thickness,
		       y2+wall_thickness));
    }
    break;

    case 'V': {
      float x, y, z;
      float nx, ny, nz;
      iFile >> x >> y >> z >> nx >> ny >> nz;
      _objhandler->MakeVertex (x, y, z);
    }
    break;

    case 'P': {
      int n, v1, v2, v3, v4;
      iFile >> n;

      if (n == 3) {
	iFile >> v1 >> v2 >> v3;
	_objhandler->MakePoly (3, v1, v2, v3);
      }
      else if (n == 4) {
	iFile >> v1 >> v2 >> v3 >> v4;
	_objhandler->MakePoly (4, v1, v2, v3, v4);
      }
      else
	{
	  cerr << "asking for " << n << " - sided polygon." << endl;
	  break;
	}
    }
    break;

    case 'S': {
      float x, y, z, r;
      iFile >> x >> y >> z >> r;
      _objhandler->MakeSphere (Vec4(x, y, z), r);
    }
    break;

    default:
      cerr << "Parsing error, unknown code: " << ch << endl;
      iFile.getline (throwaway, 100);
      break;
    }

  }
}

void getMaterialColor (char *mtlcolor, float &r, float &g, float &b)
{
  if (strcmp (mtlcolor, "brown") == 0)
    {
      r = g = .5; b = .0;
    }
  else if (strcmp (mtlcolor, "flltbrown") == 0)
    {
      r = g = .4; b = 0.;
    }
  else if (strcmp (mtlcolor, "bone") == 0)
    {
      r = g = b= 1.0;
    }
  else if (strcmp (mtlcolor, "grey") == 0)
    {
      r = g = b= .7;
    }
  else if (strcmp (mtlcolor, "silver") == 0)
    {
      r = g = b= .8;
    }
  else if (strcmp (mtlcolor, "dkdkgrey") == 0)
    {
      r = g = b= .3;
    }
  else if (strcmp (mtlcolor, "flwhite") == 0)
    {
      r = g = b= 1.;
    }
  else if (strcmp (mtlcolor, "flred") == 0)
    {
      r = 1.; g = b=0.;
    }
  else if (strcmp (mtlcolor, "red") == 0)
    {
      r = 1.; g = b=0.;
    }
  else if (strcmp (mtlcolor, "glass") == 0)
    {
      r = g = b= .5;
    }

  else
    {
      r = g = b = 1.0;
    }
}

void Scene::readOBJ (char *fname)
{
  ifstream iFile(fname, ios::in);
  
  if (!iFile) {
    cerr << "unable to open file: " << fname << endl;
    exit(-1);
  }

  //  VertexList *VL = new VertexList;
  
  char ch;
  int done = 0;

  char texname [100];
  char throwaway[MAX_LINE_LEN];

  m_linenum = 0;
  while (!done) {
    m_linenum++;

    ch = ' ';
    iFile >> ch;

    if (iFile.eof())
      break;

    switch (ch) {

    case 'v':
      float x, y, z;
      
      int check;
      check = iFile.peek ();
      if (check == 'n')
	{
	  cerr << "threw away vn line, unneeded" << endl;
	  iFile.getline (throwaway,MAX_LINE_LEN);
	}
      else
	{
	  iFile >> x >> y >> z;
	  _objhandler->MakeVertex (x, y, z);
	}
      break;
    case 'f':
      iFile.getline (throwaway, MAX_LINE_LEN); 
      int v1, v2, v3, v4, v5;
      int num;
      num = sscanf (throwaway, "%d %d %d %d %d", &v1, &v2, &v3, &v4, &v5);

      //obj files index their vertices starting at 1! thank you, marek!
      v1--; v2--;v3--;v4--;

      if (num < 3)
	{
	  cerr << "we seem to have a " << num << "-sided polygon at line " << m_linenum << ":" << throwaway << endl;
	  break;
	}

      _objhandler->MakePoly (num, v1, v2, v3, v4);
      break;

    case 'u': //usemtl
      char mtlcolor [MAX_LINE_LEN];
      iFile >> throwaway >> mtlcolor;
      float cr, cg, cb;
      getMaterialColor (mtlcolor, cr, cg,cb);
      _objhandler->UseColor (cr, cg, cb);
      break;

      //extensions to OBJ
    case 'E': {
      done = 1;
    }
    break;

    case 'N': {
      _objhandler->UseTex (NULL);
    }
    break;

    case 'g': //grouping; we don't use this
    case '#': //comments
    case 's': //I don't rightly know, but I don't use it.
      iFile.getline (throwaway, MAX_LINE_LEN);
      break;
    case 'T': {
      float sx, sy; //scale factors
      iFile >> texname >> sx >> sy;
      _objhandler->UseTex (texname, sx, sy);
    }
    break;

    case 'C': {
      float cr, cg, cb;
      iFile >> cr >> cg >> cb;
      _objhandler->UseColor (cr, cg, cb);
    }
    break;

    case 'L': { //point light 
      float x, y, z;
      float color[3];
      iFile >> x >> y >> z >> color[0] >> color[1] >> color[2];
      _objhandler->AddPointLight (Vec4 (x,y,z), Vec4 (color[0], color[1], color[2]));
    }
    break;

    case 'D':{ //directional light
      float x, y, z;
      float color[3];
      iFile >> x >> y >> z >> color[0] >> color[1] >> color[2];
      _objhandler->AddDirLight (Vec4 (x,y,z), Vec4 (color[0], color[1], color[2]));
    }
    break;
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
    default:
      iFile.getline (throwaway, MAX_LINE_LEN);
      cerr << "Throwing away line " << m_linenum << ":" << ch << throwaway << endl;
      break;
    }
  }
}


void Scene::readXPoly (char *fname)
{
  ifstream iFile(fname, ios::in);
  
  if (!iFile) {
    cerr << "unable to open file: " << fname << endl;
    exit(-1);
  }

  //  VertexList *VL = new VertexList;
  
  char ch;
  int done = 0;

  char throwaway[MAX_LINE_LEN];
  
  float v[4][3];
  int c[4][3];
  
  m_linenum = 0;
  while (!done) {
    m_linenum++;

    ch = ' ';
    iFile >> ch;

    if (iFile.eof())
      break;

    switch (ch) {

    case 'p':
      iFile.getline (throwaway, MAX_LINE_LEN); 
      int numret;
      numret = sscanf (throwaway, " c %d %d %d v %f %f %f c %d %d %d v %f %f %f c %d %d %d v %f %f %f c %d %d %d v %f %f %f e", &c[0][0], &c[0][1], &c[0][2], 
			   &v[0][0], &v[0][1], &v[0][2], 
			   &c[1][0], &c[1][1], &c[1][2], 
			   &v[1][0], &v[1][1], &v[1][2], 
			   &c[2][0], &c[2][1], &c[2][2], 
			   &v[2][0], &v[2][1], &v[2][2], 
			   &c[3][0], &c[3][1], &c[3][2], 
			   &v[3][0], &v[3][0], &v[3][1]);
      
      if (numret < 18)
	{
	  cerr << "failed on line " << throwaway << endl;
	  break;
	}
      
      int start;
      start = m_num_vertices;
      _objhandler->MakeVertex (v[0][0], v[0][1], v[0][2]);
      _objhandler->MakeVertex (v[1][0], v[1][1], v[1][2]);
      _objhandler->MakeVertex (v[2][0], v[2][1], v[2][2]);
      
      _objhandler->UseColor (float(c[0][0]) / 255., float(c[0][1]) / 255., float(c[0][2]) / 255.);

      if (numret > 18)
	{
	  _objhandler->MakeVertex (v[3][0], v[3][1], v[3][2]);
	  _objhandler->MakePoly (4, start, start + 1, start + 2, start+3);
	}
      else
	{
	  _objhandler->MakePoly (3, start, start + 1, start + 2);
	}
      
      break;
  default:
      iFile.getline (throwaway, MAX_LINE_LEN);
      cerr << "Throwing away line " << m_linenum << ":" << ch << throwaway << endl;
      break;
    }
  }
}

#if 0
Prim* Scene::RayCast(const Ray &ray, float &hit) const {

	return _root->Intersect(ray, hit);
}
#endif

#if 0
//XXX hack; depends on Load() for structure
ObjList *Scene::CreateObjectList()
{
  return new ObjList (* ((KDTree *)_root)->GetObjects());

#if 0
  //  RayCastableList &oldobjlist = ((Geometry *)_root)->GetObjects();

  ObjList *objlist = new ObjList();
  for (int i=0; i<oldobjlist.Size(); i++)
    {
      objlist->PlaceRayCastable (&oldobjlist[i]);
    }
    
  return objlist;
#endif
}

//XXX hack like CreateObjectList;
int Scene::numObjects()
{
  return ((KDTree *)_root)->GetObjects()->Size();
}
#endif

void Scene::readIV (char *fname)
{
    QvDB::init();

    QvInput	in;
    FILE *handle = fopen (fname, "r");
    if (!handle)
      {
	cerr << "couldn't open " << fname << endl;
	return;
      }
    in.setFilePointer (handle);
    QvNode	*root;

    if (QvDB::read(&in, root) && root != NULL)
	printf("Read was ok\n");
    else {
	printf("Read was bad\n");
	return;
    }
    
    m_linenum = 0;

    callbacks->registerCB ("QvCoordinate3", coord3CB, this);
    callbacks->registerCB ("QvIndexedFaceSet", ifacesetCB, this);
    callbacks->registerCB ("QvFaceSet", facesetCB, this);
    callbacks->registerCB ("QvCube", cubeCB, this);
    
    QvState state;
    cerr << "read in file; traversing scene graph." << endl;
    root->traverse(&state);
    cerr << "done reading iv file.";
}

//iv interface begins here

#define STATIC_CB_PROC(cb, handle) \
void Scene::cb (void *ptrnode, QvState *state, void *data) \
{ \
  Scene *scene = (Scene *)data; \
  scene->handle (ptrnode, state); \
}

STATIC_CB_PROC (coord3CB, coord3Handle);
STATIC_CB_PROC (ifacesetCB, ifacesetHandle);
STATIC_CB_PROC (facesetCB, facesetHandle);
STATIC_CB_PROC (cubeCB, cubeHandle);

void Scene::coord3Handle (void *ptrnode, QvState *state)
{
  QvCoordinate3 *coord = (QvCoordinate3 *)ptrnode;
  
  //associate this coord3 with its offset in the list
  CoordAssoc *assoc = new CoordAssoc;
  assoc->_coord = coord;
  assoc->_offset = m_num_vertices;
  m_coord_assoc.PlaceInList (assoc);
  
    //add to vertex list
  //    vecListOffset = VL->Size();
  //XXX need to associate a VL with each coordinate3

    for (int i = 0; i < coord->point.num; i++)
      {
	_objhandler->MakeVertex (coord->point.values[3*i],coord->point.values[3*i+1],coord->point.values[3*i+2]);
      }
}

void Scene::facesetHandle (void *ptrnode, QvState *state)
{
  QvFaceSet *faceset = (QvFaceSet *)ptrnode;

  QvElement *elt = state->getTopElement (QvState::Coordinate3Index);
  QvCoordinate3 *curcoord = (QvCoordinate3 *)elt->data;
  
  int absoffset = FindCoord3Start (curcoord);
  
  //XXX read this in, somewhere - materialbinding, or basecolor?!
  _objhandler->UseColor (GetCurColor (state));
  _objhandler->UseTex (NULL);

  int startindex = faceset->startIndex.value;
  
  int num = faceset->numVertices.num;
  cerr << "reading " << num << " faces from FaceSet" << endl;
  
  int cur = startindex;
  int numv = 0;
  for (int i = 0; i < num; i++)
    {
      numv = faceset->numVertices.values[i];
      if (numv == 3)
	{
	  _objhandler->MakePoly (3, absoffset + cur, absoffset + cur + 1, absoffset + cur + 2);  
	}
      else if (numv >= 4)
	{
	  _objhandler->MakePoly (4, absoffset + cur, absoffset + cur + 1, absoffset + cur + 2, absoffset + cur + 3);  
	  if (numv > 4)
	    cerr << "throwing away vertices" << endl;
	}
      else
	{
	  cerr << "FaceSet has face with " << numv << " vertices?!" << endl;
	}
      cur += numv;
    }
    
}

void Scene::ifacesetHandle (void *ptrnode, QvState *state)
{
  QvIndexedFaceSet *faceset = (QvIndexedFaceSet *)ptrnode;
  QvMFLong &coordIndex = faceset->coordIndex;

  QvElement *elt = state->getTopElement (QvState::Coordinate3Index);
  QvCoordinate3 *curcoord = (QvCoordinate3 *)elt->data;
  
  int absoffset = FindCoord3Start (curcoord);
  
  int v[5];

  //XXX read this in, somewhere - materialbinding, or basecolor?!
  _objhandler->UseColor (GetCurColor (state));
      _objhandler->UseTex (NULL);

    int num = coordIndex.num;
    cerr << "reading " << num << " coords from indexed face set" << endl;

    int vnum = 0; //current vertex # we're reading - 0-4
    int i = 0;
    while (i < num)
      {
	v[vnum] = coordIndex.values[i++];

	if (v[vnum] == QV_END_FACE_INDEX)
	  {
	    //send down the vertices
	    if (vnum == 4)
	      {
		_objhandler->MakePoly (4, absoffset + v[0], absoffset + v[1], absoffset + v[2], absoffset + v[3]);
	      }
	    else if (vnum == 3)
	      {
		_objhandler->MakePoly (3, absoffset + v[0], absoffset + v[1], absoffset + v[2]);
	      }
	    else
	      {
		cerr << "read " << vnum << " vertices??";
	      }
	    
	    vnum = 0;
	  }
	else
	  {
	    if (vnum == 4)
	      cerr << "got a >4-sided polygon; throwing stuff away." << endl;
	    else
	      vnum++;
	  } //if we haven't hit the end of the polygon yet
      }  //while i < num

}

void Scene::cubeHandle (void *ptrnode, QvState *state)
{
  QvCube *cube = (QvCube *)ptrnode;

  QvElement *elt = state->getTopElement (QvState::TransformationIndex);
  
  while (elt)
    {
      switch (elt->type) {
      case QvElement::NoOpTransform:
	break;
      case QvElement::MatrixTransform:
	cerr << "aiee, matrix transform!" << endl;
	break;
      case QvElement::Rotation:
	cerr << "aiee, rotation!" << endl;
	break;
      case QvElement::Scale:
	cerr << "aiee, scale!" << endl;
	break;
      case QvElement::Transform:
	cerr << "aiee, transform!" << endl;
	break;
      case QvElement::Translation:
	cerr << "ah, translation!" << endl;
	break;
      }
      elt = elt->next;
    }

  float cx, cy, cz;

  //XXX get from transformation
  cx = cy = cz = 0.0;

  //XXX read this in, somewhere - materialbinding, or basecolor?!
  
  _objhandler->UseColor (GetCurColor (state));
    _objhandler->UseTex (NULL);

  _objhandler->MakeBox (Bounds3d (cx - cube->width.value,
		   cx + cube->width.value,
		   cy - cube->height.value,
		   cy + cube->height.value,
		   cz - cube->depth.value,
		   cz + cube->depth.value));
}

void ObjCreator::IncludePoint (const Vec4 &pt)
{
  assert (_bbox.IncludesPoint (pt));
}

void ObjCreator::MakeBox (const Bounds3d &box)
{
  assert (_bbox.IncludesPoint (box[0]));
  assert (_bbox.IncludesPoint (box[1]));

  Box *B = new Box(box[0][0], box[0][1], box[0][2],
		   box[1][0], box[1][1], box [1][2]);
  B->SetColor(_color[0], _color[1], _color[2]);
  if (_tex) B->setTexture (_tex);
  if (_kdtree) _kdtree->Insert (B);
  _objlist->PlaceInList(B);
}

void ObjCreator::MakeSphere (const Vec4 &center, float r)
{
  assert (_bbox.IncludesPoint (center + Vec4 (r,r,r)));
  assert (_bbox.IncludesPoint (center - Vec4 (r,r,r)));

  Sphere *S = new Sphere(center, r);
  S->SetColor(_color[0], _color[1], _color[2]);
  if (_tex) S->setTexture (_tex);
  if (_kdtree) _kdtree->Insert (S);
  _objlist->PlaceInList(S);
}

void ObjCreator::MakeVertex (float x, float y, float z)
{
  assert (_bbox.IncludesPoint (Vec4 (x,y,z)));
  Vertex *V = new Vertex(Vec4(x, y, z),
		 Vec4());
  if (V)
    {
      assert (m_cur_VL->Size() <= VLIST_ALLOC_SIZE);

      if (m_cur_VL->Size() >= VLIST_ALLOC_SIZE)
	MakeNewVertexList();

      m_cur_VL->PlaceInList(V);
      m_num_vertices++;
    }
  else
    cerr << "ran out of memory makin' a vertex." << endl;
}

void ObjCreator::UseTex (char *texname, float sx, float sy)
{
  if (texname)
    _tex = Texture::load (texname, sx, sy);
  else
    _tex = NULL;
}

void ObjCreator::UseColor (float cr, float cg, float cb)
{
  _color.Set (cr, cg, cb);
}

void ObjCreator::UseColor (const Vec4 &color)
{
  _color = color;
}

void ObjCreator::MakePoly (int num, int v1, int v2, int v3, int v4)
{
  assert (num >= 3);

  Indexed_Polygon *P;
  if (num == 3)
    P = new Indexed_Polygon(GetVertex (v1), GetVertex (v2), GetVertex (v3));
  else
    {
      //XXX handle n-sided polygons
      P = new Indexed_Polygon(GetVertex (v1), GetVertex (v2), GetVertex (v3), GetVertex (v4));
      if (num > 4)
	{
	  //	  cerr << "throwing away " << num - 4 <<" vertices of a polygon!" << endl;
	  _num_throwaway_vertices+= (num - 4);
	}
    }

  if (P)
    {
      P->SetColor(_color[0], _color[1], _color[2]);
      if (_tex) P->setTexture (_tex);
      if (_kdtree) _kdtree->Insert (P);
      _objlist->PlaceInList(P);
    }
  else
    cerr << "failed to make a polygon!" << endl;
}

void ObjCreator::AddPointLight (const Vec4 & origin, const Vec4 & color)
{
  _scene->_L.AddPointLight (origin, color);
}

void ObjCreator::AddDirLight (const Vec4 & dir, const Vec4 & color)
{
  _scene->_L.AddDirectionalLight (dir, color);
}

void ObjCreator::Report (void)
{
  char scratch [200];

  sprintf (scratch, "%d objects %d vertices through line %d\n", _objlist->Size(), m_num_vertices, m_linenum);
  cerr << scratch;
}

void BBoxCreator::IncludePoint (const Vec4 &pt)
{
  _bbox.IncludePoint (pt);
  _num++;
}

void BBoxCreator::MakeVertex (float x, float y, float z)
{
  _bbox.IncludePoint (Vec4 (x,y,z));
  m_num_vertices++;
  _num++;
}

void BBoxCreator::MakeBox (const Bounds3d &box)
{
  _bbox.IncludePoint (box[0]);
  _bbox.IncludePoint (box[1]);
  _num++;
}

void BBoxCreator::MakeSphere (const Vec4 &center, float r)
{
  _bbox.IncludePoint (center + Vec4 (r,r,r));
  _bbox.IncludePoint (center - Vec4 (r,r,r));
  _num++;
}

void BBoxCreator::Report (void)
{
  char scratch [200];

  sprintf (scratch, "up to line %d, processed %d objects/vertices\n", m_linenum, _num);
  cerr << scratch;
}

void Scene::readFile (char *fname)
{
  itimerval oldval;
  itimerval newval;
  newval.it_interval.tv_sec = newval.it_interval.tv_usec = 0;
  newval.it_value.tv_sec = 1;
  newval.it_value.tv_usec = 0;

  signal(SIGVTALRM, scene_input_hnd);
  m_still_reading = 1;
  if (0 != setitimer (ITIMER_VIRTUAL, &newval, &oldval))
    cerr << "error settin' timer." << endl;

  if (strcmp (fname +strlen(fname) - 4, ".obj") == 0)
    {
      cerr << "treating " << fname << " as obj file format." << endl;
      readOBJ (fname);
    }
  else if ( (strcmp (fname +strlen(fname) - 3, ".iv") == 0) || (strcmp (fname +strlen(fname) - 4, ".wrl") == 0) )
    {
      cerr << "treating " << fname << " as iv/wrl file format." << endl;
      readIV (fname);
    }
  else if (strcmp (fname +strlen(fname) - 3, ".xp") == 0)
    {
      cerr << "treating " << fname << " as xpoly file format." << endl;
      readXPoly (fname);
    }
  else
    {
      cerr << "treating " << fname << " as JUFF file format." << endl;
      readJUFF (fname);
    }
  m_still_reading = 0;
}

KDTree * ObjCreator::getKDTree (void)
{
  //if we weren't going to build it incrementally...
  if (!_kdtree)
    {
      _kdtree = new KDTree (NULL, _objlist, _bbox);
    }
  if (_num_throwaway_vertices)
    {
      cerr << "had to ignore " << _num_throwaway_vertices << " vertices." << endl;
    }
  
  return _kdtree;
}

ObjCreator::ObjCreator (Scene *scene, Bounds3d bbox) : ObjHandler (scene), _tex (NULL), _color (1., 1., 1.), _objlist (new ObjList()), _bbox (bbox), _kdtree (NULL), _num_throwaway_vertices (0)
{
  //if it's made here, it'll be constructed incrementally - not as 
  //smart, but necessary for big files
  //otherwise it'll be constructed at the end - good for little files which
  //memory can handle.
#define MAKE_KDTREE_AT_END
#ifndef MAKE_KDTREE_AT_END
  _kdtree = new KDTree (NULL, NULL, bbox);
#endif

  m_num_vertices = 0;
  assert (m_vlists.Size() == 0);
  assert (!m_cur_VL);
  MakeNewVertexList();
}

void ObjCreator::MakeNewVertexList (void)
{
  //  cerr << "making new vertex list" << endl;
  m_cur_VL = new MyVertexList (m_num_vertices, VLIST_ALLOC_SIZE);
  m_vlists.PlaceInList (m_cur_VL);
}

Vertex *ObjCreator::GetVertex (int num)
{
  assert (m_cur_VL);
  //first try the current list
  if (m_cur_VL->Contains (num))
    {
      return m_cur_VL->AbsIndex (num);
    }

  //otherwise we have to root through our lists
  void *place;
  MyVertexList *vl;
  m_vlists.Reset (place);
  while (place)
    {
      vl = m_vlists.Peek (place);
      m_vlists.Next (place);
      if ( vl->Contains (num))
	return vl->AbsIndex (num);
    }
  cerr << "bad vertex index num " << num << endl;
  assert (0); //couldn't find the desired vertex!
  return NULL;
}

int FindCoord3Start (QvCoordinate3 *coord)
{
  void *place;
  m_coord_assoc.Reset (place);
  CoordAssoc *assoc;
  while (place)
    {
      assoc = m_coord_assoc.Peek (place);
      if (assoc->_coord == coord)
	return assoc->_offset;
      m_coord_assoc.Next (place);
    }
  cerr << "couldn't find associated VL for coord3!" << endl;
  assert (0);
  return 0;
}

Vec4 GetCurColor (QvState *state)
{
  QvElement *elt = state->getTopElement (QvState::MaterialIndex);  
  
  if (elt->type == QvElement::Material)
    {
      QvMaterial *bc = (QvMaterial *)elt->data;
      
      return Vec4 (bc->diffuseColor.values[0], bc->diffuseColor.values[1], bc->diffuseColor.values[2]);
    }
  else if (elt->type == QvElement::BaseColor)
    {
      QvBaseColor *bc = (QvBaseColor *)elt->data;
      
      return Vec4 (bc->rgb.values[0], bc->rgb.values[1], bc->rgb.values[2]);
    }
  else
    {
      cerr << "unknown material elt type" << endl;
      return Vec4 (1., 1., 1.);
    }
}

BBoxCreator::BBoxCreator (Scene *scene) : ObjHandler (scene), _num (0) 
{
  m_num_vertices = 0;
}


