/*
 GNU Maverik - a system for managing display and interaction in 
               Virtual Environment applications.
 Copyright (C) 1999 Advanced Interfaces Group

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA


 The authors can be contacted via:
 www   - http://aig.cs.man.ac.uk
 email - maverik@aig.cs.man.ac.uk
 mail  - Advanced Interfaces Group, Room 2.90, Computer Science Building, 
         University of Manchester, Manchester, M13 9PL, UK
*/


/* VRML97 parser uses the free CyberVRML97 for C++ library */
/* by Satoshi Konno (http://www.cyber.koganei.tokyo.jp/top/index.html) */



#include "maverik.h"
#include "mav_vrml97.h"
#include "SceneGraph.h"
#include <stdio.h>
#include <math.h>

#ifdef MAV_IRIX5
/* Dont have these defined on our Irix 5 version of CC */
bool false=0;
bool true=!false;
#endif


int mavlib_matScaleWarn= MAV_FALSE;

/* Casts to convert generic node into a specfic type. Note that is*Node retuns true if it is */
/* an instance node which refers to the queried note type. So, these casts need to either */
/* cast the supplied node, or the node it references. */

Node *mavlib_getReferenceNode(Node *n)
{
  Node *rv= n->getReferenceNode();
  while (rv->getReferenceNode()) rv= rv->getReferenceNode();
  return rv;
}

ShapeNode *mavlib_castToShapeNode(Node *n)
{
  ShapeNode *rv= (ShapeNode *) n;
  if (n->isInstanceNode()) rv= (ShapeNode *) mavlib_getReferenceNode(n);
  return rv;
}

TransformNode *mavlib_castToTransformNode(Node *n)
{
  TransformNode *rv= (TransformNode *) n;
  if (n->isInstanceNode()) rv= (TransformNode *) mavlib_getReferenceNode(n);
  return rv;
}

SphereNode *mavlib_castToSphereNode(Node *n)
{
  SphereNode *rv= (SphereNode *) n;
  if (n->isInstanceNode()) rv= (SphereNode *) mavlib_getReferenceNode(n);
  return rv;
}

BoxNode *mavlib_castToBoxNode(Node *n)
{
  BoxNode *rv= (BoxNode *) n;
  if (n->isInstanceNode()) rv= (BoxNode *) mavlib_getReferenceNode(n);
  return rv;
}

CylinderNode *mavlib_castToCylinderNode(Node *n)
{
  CylinderNode *rv= (CylinderNode *) n;
  if (n->isInstanceNode()) rv= (CylinderNode *) mavlib_getReferenceNode(n);
  return rv;
}

ConeNode *mavlib_castToConeNode(Node *n)
{
  ConeNode *rv= (ConeNode *) n;
  if (n->isInstanceNode()) rv= (ConeNode *) mavlib_getReferenceNode(n);
  return rv;
}

IndexedFaceSetNode *mavlib_castToIndexedFaceSetNode(Node *n)
{
  IndexedFaceSetNode *rv= (IndexedFaceSetNode *) n;
  if (n->isInstanceNode()) rv= (IndexedFaceSetNode *) mavlib_getReferenceNode(n);
  return rv;
}



/* Routine to generate a box */

MAV_object *mavlib_dealWithBoxNode(BoxNode *n, MAV_surfaceParams *sp, MAV_matrix m)
{
  MAV_box *b;

  if (mav_opt_output==MAV_VERBOSE) fprintf(stderr,"Building box\n");

  if (fabs(m.mat[0][0]-m.mat[1][1])>0.01 || fabs(m.mat[0][0]-m.mat[2][2])>0.01) mavlib_matScaleWarn= MAV_TRUE;

  b= (MAV_box *) mav_malloc(sizeof(MAV_box));
  b->size.x= n->getX();
  b->size.y= n->getY();
  b->size.z= n->getZ();
  b->matrix= m;
  b->sp= sp;

  return mav_objectNew(mav_class_box, b);
}



/* Routine to generate a sphere */

MAV_object *mavlib_dealWithSphereNode(SphereNode *n, MAV_surfaceParams *sp, MAV_matrix m)
{
  MAV_sphere *s;

  if (mav_opt_output==MAV_VERBOSE) fprintf(stderr,"Building sphere\n");

  if (fabs(m.mat[0][0]-m.mat[1][1])>0.01 || fabs(m.mat[0][0]-m.mat[2][2])>0.01) mavlib_matScaleWarn= MAV_TRUE;

  s= (MAV_sphere *) mav_malloc(sizeof(MAV_sphere));
  s->radius= n->getRadius();
  s->nverts= 0;
  s->nchips= 0;
  s->matrix= m;
  s->sp= sp;

  return mav_objectNew(mav_class_sphere, s);
}



/* Routine to generate a cylinder */

MAV_object *mavlib_dealWithCylinderNode(CylinderNode *n, MAV_surfaceParams *sp, MAV_matrix m)
{
  MAV_cylinder *cyl;

  if (mav_opt_output==MAV_VERBOSE) fprintf(stderr,"Building cylinder\n");

  if (fabs(m.mat[0][0]-m.mat[1][1])>0.01 || fabs(m.mat[0][0]-m.mat[2][2])>0.01) mavlib_matScaleWarn= MAV_TRUE;

  cyl= (MAV_cylinder *) mav_malloc(sizeof(MAV_cylinder));
  cyl->radius= n->getRadius();
  cyl->height= n->getHeight();
  cyl->nverts= 0;
  cyl->endcap= MAV_TRUE;
  cyl->matrix= mav_matrixMult(m, mav_matrixSet(0,-90,0,0,0,0));
  cyl->sp= sp;  

  return mav_objectNew(mav_class_cylinder, cyl);
}



/* Routine to generate a cone */

MAV_object *mavlib_dealWithConeNode(ConeNode *n, MAV_surfaceParams *sp, MAV_matrix m)
{
  MAV_cone *cone;

  if (mav_opt_output==MAV_VERBOSE) fprintf(stderr,"Building cone\n");

  if (fabs(m.mat[0][0]-m.mat[1][1])>0.01 || fabs(m.mat[0][0]-m.mat[2][2])>0.01) mavlib_matScaleWarn= MAV_TRUE;

  cone= (MAV_cone *) mav_malloc(sizeof(MAV_cone));
  cone->rt= 0.0;
  cone->rb= n->getBottomRadius();
  cone->height= n->getHeight();
  cone->nverts= 0;
  cone->endcap= MAV_TRUE;
  cone->matrix= mav_matrixMult(m, mav_matrixSet(0,-90,0,0,0,0));
  cone->sp= sp;  

  return mav_objectNew(mav_class_cone, cone);
}



/* Routine to generate an indexed face set (a facet) */

MAV_object *mavlib_dealWithIndexedFaceSetNode(IndexedFaceSetNode *n, MAV_surfaceParams *sp, MAV_matrix m)
{ 
  int i, j, idx, idxbk, nf=0, nv=0;
  CoordinateNode *pts= NULL; 
  NormalNode *norms= NULL;
  MAV_facet *facet;
  float v[3];
  MAV_vector v1, v2;
  
  /* Get the coordinates node */
  pts= n->getCoordinateNodes();

  /* Get the normal node */
  norms= n->getNormalNodes();

  if (!pts) {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: can not find the indexed face set coordinate node, ignoring\n");
    return NULL;
  }

  /* Ensure that its convex */
  if (!n->getConvex()) {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: indexed face set is concave, ignoring\n");
    return NULL;
  }

  /* Misc traps */
  if (n->getColorNodes()) if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: colour not yet supported, ignoring\n");
  if (n->getTextureNode()) if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: texture not yet supported, ignoring\n");

  /* Work with a local copy of the coord indexes for efficiency */
  int coordNum= n->getNCoordIndexes();
  if (coordNum==0) {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: indexed face set contains no indices, ignoring\n");
    return NULL;
  }    
  int *coordIdx= new int[coordNum];

  /* Copy indexes and count number of faces and vertices */
  for (i=0; i<coordNum; i++) {
    coordIdx[i]= n->getCoordIndex(i);
    if (coordIdx[i]==-1)
    {
      nf++;
    }
    else
    {
      nv++;
    }
  }

  /* Last face may be implicit rather than have a -1 */
  if (coordIdx[coordNum-1]!=-1) nf++;

  /* Create facet to represent the indexed face set */
  if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Building facet with %i faces and %i vertices\n", nf, nv); 

  if (fabs(m.mat[0][0]-m.mat[1][1])>0.01 || fabs(m.mat[0][0]-m.mat[2][2])>0.01) mavlib_matScaleWarn= MAV_TRUE;

  facet= (MAV_facet *) mav_malloc(sizeof(MAV_facet));
  facet->npolys= nf;
  facet->np= (int *) mav_malloc(facet->npolys*sizeof(int));
  facet->norm= (MAV_vector **) mav_malloc(facet->npolys*sizeof(MAV_vector *));
  facet->vert= (MAV_vector **) mav_malloc(facet->npolys*sizeof(MAV_vector *));
  facet->tex= (MAV_texCoord **) mav_malloc(facet->npolys*sizeof(MAV_texCoord *));
  facet->sp= (MAV_surfaceParams **) mav_malloc(facet->npolys*sizeof(MAV_surfaceParams *));
  facet->matrix= m;

  MAV_vector *perfacenormal= new MAV_vector[facet->npolys];

  idx=0;
  for (i=0; i<facet->npolys; i++) {

    /* Count number of vertices in this face */
    nv=0;
    idxbk= idx;
    while (!(idx==coordNum || coordIdx[idx]==-1)) {
      nv++;
      idx++;
    }

    if (nv>=3)
    {
      /* Malloc of room for the vertices */
      facet->np[i]= nv;
      facet->norm[i]= (MAV_vector *) mav_malloc(facet->np[i]*sizeof(MAV_vector));
      facet->vert[i]= (MAV_vector *) mav_malloc(facet->np[i]*sizeof(MAV_vector));
      facet->tex[i]= (MAV_texCoord *) mav_malloc(facet->np[i]*sizeof(MAV_texCoord));
      facet->sp[i]= sp;

      /* Store the vertices */
      nv=0;
      idx= idxbk;
      while (!(idx==coordNum || coordIdx[idx]==-1)) {
	
	/* Ensure that the index is valid */
	if (coordIdx[idx] >= pts->getNPoints()) 
	{
	  if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: coordinate index too big, setting to max\n");
	  pts->getPoint(pts->getNPoints()-1, v);
	}
	else
	{
	  pts->getPoint(coordIdx[idx], v);
	}

	/* Account for vertex ordering */
	if (n->getCCW())
	{
	  facet->vert[i][nv].x= v[0];
	  facet->vert[i][nv].y= v[1];
	  facet->vert[i][nv].z= v[2];    
	}
	else
	{
	  facet->vert[i][facet->np[i]-1-nv].x= v[0];
	  facet->vert[i][facet->np[i]-1-nv].y= v[1];
	  facet->vert[i][facet->np[i]-1-nv].z= v[2];    
	}

	nv++;
	idx++;
      }

      /* Store the normals */
      if (norms)  // Normals supplied
      {
	if (n->getNormalPerVertex()) // Defined per-vertex
	{
	  nv=0;
	  idx= idxbk;
	  while (!(idx==coordNum || coordIdx[idx]==-1)) {
	
	    if (n->getNNormalIndexes()==0) // Implicit ordering (same as coordIndex)
	    {	
	      /* Ensure that the index is valid */
	      if (coordIdx[idx] >= norms->getNVectors())
	      {
		if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: coordinate index too big, setting to max\n");
		norms->getVector(norms->getNVectors()-1, v);
	      }
	      else
	      {
		norms->getVector(coordIdx[idx], v);
	      }
	    }
	    else // Explicit ordering (suppled in normalIndex)
	    {
	      /* Ensure that the index is valid */
	      if (n->getNormalIndex(idx) >= norms->getNVectors())
	      {
		if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: normal index too big, setting to max\n");
		norms->getVector(norms->getNVectors()-1, v);
	      }
	      else
	      {
		norms->getVector(n->getNormalIndex(idx), v);
	      }
	    }

	    /* Account for vertex ordering */
	    if (n->getCCW())
	    {
	      facet->norm[i][nv].x= v[0];
	      facet->norm[i][nv].y= v[1];
	      facet->norm[i][nv].z= v[2];    
	    }
	    else
	    {
	      facet->norm[i][facet->np[i]-1-nv].x= v[0];
	      facet->norm[i][facet->np[i]-1-nv].y= v[1];
	      facet->norm[i][facet->np[i]-1-nv].z= v[2];    
	    }

	    nv++;
	    idx++;
	  }
	}
	else // Defined per-face
	{
	  if (n->getNNormalIndexes()==0) // Implicit ordering
	  {
	    /* Get normal - ensure index is valid */
	    if (i >= norms->getNVectors()) 
	    {
	      if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: more faces than supplied normals, setting to max\n");
	      norms->getVector(norms->getNVectors()-1, v);
	    }
	    else
	    {
	      norms->getVector(i, v);
	    }

	    /* Set to all vertices in face */
	    for (nv=0; nv<facet->np[i]; nv++) {
	      facet->norm[i][nv].x= v[0];
	      facet->norm[i][nv].y= v[1];
	      facet->norm[i][nv].z= v[2];
	    }
	  }
	  else // Explicit ordering
	  {
	    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: indexed per-face normals not supported yet, ignoring\n");
	  }
	}
      }
      else // Normals need to be generated
      {
	/* For now calculate a per-face normal and store in tmp array */
	j=0;
	do {
	  /* Find 2 non-parallel edge vectors */
	  if (j==facet->np[i]-2)
	  {
	    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: can't find surface normal, ignoring\n");
	    facet->np[i]=0;
	    v1.x= 1;
	    v1.y= 0;
	    v1.z= 0;
	    v2.x= 0;
	    v2.y= 1;
	    v2.z= 0;
	  }
	  else
	  {
	    v1= mav_vectorNormalise(mav_vectorSub(facet->vert[i][j+2],facet->vert[i][j+1]));
	    v2= mav_vectorNormalise(mav_vectorSub(facet->vert[i][j],facet->vert[i][j+1]));
	  }
	  j++;
	} while (fabs(mav_vectorDotProduct(v1, v2))==1.0);

	/* Cross product them to get normal and store */
	perfacenormal[i]= mav_vectorNormalise(mav_vectorCrossProduct(v1,v2));
      }
    }    
    else
    {
      if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: face with less than 3 vertices, ignoring\n");
      facet->np[i]=0;
    }

    idx++;
  }

  /* Smooth vertex normals */
  if (!norms) {
    bool *ptslist= new bool[pts->getNPoints()];
    for (i=0; i<pts->getNPoints(); i++) ptslist[i]= false;

    /* Find which vertices are referenced */
    for (i=0; i<coordNum; i++) if (coordIdx[i]!=-1 && coordIdx[i]<pts->getNPoints()) ptslist[coordIdx[i]]= true;
    
    /* For each point defined */
    for (i=0; i<pts->getNPoints(); i++) {
      if (ptslist[i]) {

	/* Count how many faces this point appears in */
	int numf=0;
	nf=0;
	for (idx=0; idx<coordNum; idx++) {
	  if (coordIdx[idx]==-1) nf++;
	  if (coordIdx[idx]==i && facet->np[nf]) numf++;
	}

	/* Store a list of the facet faces and vertex numbers for this point */
	int *theface= new int[numf];
	int *thevertex= new int[numf];

	nf=0;
	nv=0;
	int fc=0;
	for (idx=0; idx<coordNum; idx++) {
	  if (coordIdx[idx]==-1) {
	    nf++;
	    nv=idx+1;
	  }

	  if (coordIdx[idx]==i && facet->np[nf]) {
	    theface[fc]= nf;
	    if (n->getCCW())
	    {
	      thevertex[fc]= idx-nv;
	    }
	    else
	    {
	      thevertex[fc]= facet->np[nf]-1-(idx-nv);
	    }
	    fc++;
	  }
	}

	/* Calculate the normal for each face the point appears in */
	for (nf=0; nf<numf; nf++) {

	  MAV_vector accnorm;
	  accnorm.x=0;
	  accnorm.y=0;
	  accnorm.z=0;

	  /* If angle between faces is less than crease angle, then update face normal */
	  for (nv=0; nv<numf; nv++) {
	    float aval= mav_vectorDotProduct(perfacenormal[theface[nf]], perfacenormal[theface[nv]]);
	    if (aval>1.0) aval=1.0;
	    if (acos(aval)<=n->getCreaseAngle()+0.01) accnorm= mav_vectorAdd(accnorm, perfacenormal[theface[nv]]);
	  }

	  facet->norm[theface[nf]][thevertex[nf]]= mav_vectorNormalise(accnorm);
	}

	delete theface;
	delete thevertex;
      }
    }

    delete ptslist;
  }

  delete coordIdx;
  delete perfacenormal;

  return mav_objectNew(mav_class_facet, facet);
}



/* Routine to build a material */

MAV_surfaceParams *mavlib_dealWithMaterialNode(MaterialNode *n)
{  
  float ambientInt, diffuse[3], spec[3], emis[3], shin, trans;
  MAV_surfaceParams *rv= mav_sp_default;
  int i;

  /* Get definition of material */
  ambientInt= n->getAmbientIntensity();
  n->getDiffuseColor(diffuse);
  n->getSpecularColor(spec);
  n->getEmissiveColor(emis);
  shin= n->getShininess();
  trans= n->getTransparency();

  /* Try to find a matching index */
  i= mav_paletteMaterialIndexMatchGet(mav_palette_default,
			   diffuse[0]*ambientInt, diffuse[1]*ambientInt, diffuse[2]*ambientInt, 1.0-trans, 
			   diffuse[0], diffuse[1], diffuse[2], 1.0-trans, spec[0], spec[1], spec[2], 1.0-trans, 
			   emis[0], emis[1], emis[2], 1.0-trans, shin*128.0);
  
  if (i>=0) 
  {
    /* You spawny get! construct and return the surface parameters */
    rv= mav_surfaceParamsNew(MAV_MATERIAL, 0, i, 0);
  }
  else
  {
    /* No joy, find empty material index to use */
    i= mav_paletteMaterialIndexEmptyGet(mav_palette_default);

    if (i>=0) {
      /* Set this material */
      mav_paletteMaterialSet(mav_palette_default, i,
			   diffuse[0]*ambientInt, diffuse[1]*ambientInt, diffuse[2]*ambientInt, 1.0-trans, 
			   diffuse[0], diffuse[1], diffuse[2], 1.0-trans, spec[0], spec[1], spec[2], 1.0-trans, 
			   emis[0], emis[1], emis[2], 1.0-trans, shin*128.0);

      /* Construct and return the surface parameters */
      rv= mav_surfaceParamsNew(MAV_MATERIAL, 0, i, 0);
    }
  }

  return rv; 
}



/* Routine to deal with an appearance node (i.e. a material definition) */

MAV_surfaceParams *mavlib_dealWithAppearanceNode(AppearanceNode *n)
{
  MaterialNode *mn= n->getMaterialNodes();
  MAV_surfaceParams *rv= mav_sp_default;

  /* Deal with material node if it exists */
  if (mn)
  {
    rv= mavlib_dealWithMaterialNode(mn);
  }
  else
  {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: no material node defined in appearance - dont know if this is significant\n");
  }

  return rv;
}



/* Routine to deal with a shape node (i.e. an appearance and a geometric object) */

MAV_object *mavlib_dealWithShapeNode(ShapeNode *n, MAV_matrix m)
{
  AppearanceNode *an= n->getAppearanceNodes();
  Node *gn= n->getGeometryNode();
  MAV_surfaceParams *sp= mav_sp_default;
  MAV_object *rv= NULL;

  /* Deal with appearance node if it exists */
  if (an)
  {
    sp= mavlib_dealWithAppearanceNode(an);
  }
  else
  {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: no appearance node defined in shape - dont know if this is significant\n");
  }

  /* Deal with geometric shape */
  if (gn)
  {
    if (gn->isIndexedFaceSetNode()) 
    {
      rv= mavlib_dealWithIndexedFaceSetNode(mavlib_castToIndexedFaceSetNode(gn), sp, m);
    } 
    else if (gn->isBoxNode()) 
    {
      rv= mavlib_dealWithBoxNode(mavlib_castToBoxNode(gn), sp, m);
    } 
    else if (gn->isSphereNode()) 
    {
      rv= mavlib_dealWithSphereNode(mavlib_castToSphereNode(gn), sp, m);
    } 
    else if (gn->isCylinderNode()) 
    {
      rv= mavlib_dealWithCylinderNode(mavlib_castToCylinderNode(gn), sp, m);
    } 
    else if (gn->isConeNode()) 
    {
      rv= mavlib_dealWithConeNode(mavlib_castToConeNode(gn), sp, m);
    } 
    else 
    {
      if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: geometry node type %s not supported yet, ignoring\n", gn->getType());
    }
  }
  else
  {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: no geometry node defined in shape - dont know if this is significant\n");
  }

  return rv;
}



/* Routine to deal with a transform node (i.e. a matrix definition) */

MAV_matrix mavlib_dealWithTransformNode(TransformNode *n)
{
  MAV_matrix m, trans, cent, rot, scaleOrient, scale, mscaleOrient, mcent;
  float c[3], rt[4], sc[3], sco[4], tx[4];

  /* Get the transform definition */
  n->getCenter(c);
  n->getRotation(rt);
  n->getScale(sc);
  n->getScaleOrientation(sco);
  n->getTranslation(tx);

  /* Load up the matrices (See VRML97 transform node spec) */
  trans= mav_matrixSet(0,0,0, tx[0], tx[1], tx[2]);
  cent= mav_matrixSet(0,0,0, c[0], c[1], c[2]);
  rot= mav_matrixQuaternionConvert(mav_quaternionSet(mav_vectorSet(rt[0], rt[1], rt[2]), MAV_RAD2DEG(rt[3])));
  scaleOrient= mav_matrixQuaternionConvert(mav_quaternionSet(mav_vectorSet(sco[0], sco[1], sco[2]), MAV_RAD2DEG(sco[3])));
  scale= MAV_ID_MATRIX;
  scale.mat[0][0]= sc[0];
  scale.mat[1][1]= sc[1];
  scale.mat[2][2]= sc[2];
  mscaleOrient= mav_matrixInverse(scaleOrient);
  mcent= mav_matrixInverse(cent);

  /* Construct the overall matrix */
  m= trans;
  m= mav_matrixMult(m, cent);
  m= mav_matrixMult(m, rot);
  m= mav_matrixMult(m, scaleOrient);
  m= mav_matrixMult(m, scale);
  m= mav_matrixMult(m, mscaleOrient);
  m= mav_matrixMult(m, mcent);

  return m;
}



/* Routine to deal with a node */

MAV_object *mavlib_dealWithNode(Node *n, int depth, MAV_matrix matin, MAV_matrix *matout)
{
  MAV_object *o= NULL;
  MAV_matrix newm;

  //  printf("[depth %i] [name %s] [type %s] [mat %f %f %f %f\n", depth, n->getName(), n->getType());

  /* Output matrix is same as input matrix (may be changed lated by transform node) */
  *matout= matin;

  /* Only concerned with shape and tranform nodes */
  if (n->isShapeNode()) 
  {
    o= mavlib_dealWithShapeNode(mavlib_castToShapeNode(n), matin);
  }
  else if (n->isTransformNode())
  {
    newm= mavlib_dealWithTransformNode(mavlib_castToTransformNode(n));
    *matout= mav_matrixMult(matin, newm);
  }

  return o;
}



/* Routine to traverse and parse nodes and their children */

#define MAXOBJS 1000
MAV_object *mavlib_nodeObj[MAXOBJS];
int mavlib_nodeObjCount;

int mavlib_parseNode(Node *n, int depth, MAV_matrix m)
{
  MAV_object *o;
  MAV_matrix matout;

  while (n) {
    /* Deal with this node */
    o= mavlib_dealWithNode(n, depth, m, &matout);

    /* Store the object if it generated one */
    if (o) {
      if (mavlib_nodeObjCount<MAXOBJS) 
      {
	mavlib_nodeObj[mavlib_nodeObjCount]=o;
	mavlib_nodeObjCount++;
      }
      else
      {
	if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: maximum number of object exceeded, ignoring\n");
      }
    }

    /* Parse its children */
    mavlib_parseNode(n->getChildNodes(), depth+1, matout);

    /* Move onto next node */
    n= n->next();
  }

  return MAV_TRUE;
}



/* Routine to read a VRML97 file and generate a composite for it */

int mav_compositeReadVRML97(char *filename, MAV_composite *c, MAV_matrix m)
{
  SceneGraph sceneGraph;
  int otbk, i;
 
  /* Initialise composite */
  c->numobj=0;

  if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Parsing VRML97 file %s...", filename);

  /* Construct scene graph */
  sceneGraph.load(filename, false);

  if (!sceneGraph.isOK()) {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: in VRML97 file at line %d [%s]\n", sceneGraph.getErrorLineNumber(), sceneGraph.getErrorLineString());
    return MAV_FALSE;
  }

  if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "done\nBuilding objects...\n");

  /* create objects without table entries */
  otbk= mav_opt_objectTables;
  mav_opt_objectTables= MAV_FALSE;

  /* Parse nodes and build objects */
  mavlib_nodeObjCount= 0;
  mavlib_matScaleWarn= MAV_FALSE;
  mavlib_parseNode(sceneGraph.getNodes(), 0, m);
  if (mavlib_matScaleWarn && mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: non-uniformly scaled matrix, may lead to intersection problems\n");

  /* restore object table value */
  mav_opt_objectTables= otbk;

  /* Build composite */
  c->numobj= mavlib_nodeObjCount;
  c->obj= (MAV_object **) mav_malloc(c->numobj*sizeof(MAV_object *));
  for (i=0; i<c->numobj; i++) c->obj[i]= mavlib_nodeObj[i];
  if (mav_opt_compositeSetMatrix) c->matrix= MAV_ID_MATRIX;
  c->filename= strdup(filename);

  /* Calculate and store its BB */
  mav_compositeCalcBB(c);

  if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "done\n");

  return MAV_TRUE;
}



/* Routines to initialise the module */

char *mav_VRML97ModuleID(void)
{
  return "VRML97";
}

int mav_VRML97ModuleInit(void)
{
  /* Add the new module */
  mav_moduleNew(mav_VRML97ModuleID);

  /* Add VRML97 to list of supported composite file formats */
  if (mav_compositeFormat[2].defined && mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: file format 2 already reserved, overwriting\n"); 
  mav_compositeFormat[2].defined= MAV_TRUE;
  mav_compositeFormat[2].ext= ".wrl";
  mav_compositeFormat[2].fn= mav_compositeReadVRML97;

  return 1;
}
