/***********************************************************************/
/* Open Visualization Data Explorer                                    */
/* (C) Copyright IBM Corp. 1989,1999                                   */
/* ALL RIGHTS RESERVED                                                 */
/* This code licensed under the                                        */
/*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
/***********************************************************************/

#include <dxconfig.h>


#include "hwDeclarations.h"
#include "hwList.h"
#include "hwClipped.h"
#include "hwMatrix.h"
#include "hwMemory.h"
#include "hwPortLayer.h"

#include "hwGather.h"
#include "hwXfield.h"
#include "hwWindow.h"
#include "hwObjectHash.h"
#include "hwSort.h"

#include "hwDebug.h"

static Error _gatherRecurse(dxObject object, dxObject *newObject, 
				gatherO gather, attributeP attributes,
	       			void *globals);

typedef struct clipPlanesS *clipPlaneP;

struct clipPlanesS
{
  int		n;
  Point		*p;
  Vector	*v;
  clipPlaneP 	next;
} clipPlanesT;

typedef struct gatherS
{
  long int	flags;
  listO		lights;
  RGBColor	ambientColor;
  materialO 	lastUsedMaterial;
  dxObject	object;     /* modified copy of original object */
  dxObject     	clipObject; /* modified copy of object doing tranp clip */
  clipPlaneP	clipStack;
} gatherT, *gatherP;

static gatherP
_getHwGatherData(gatherO go)
{
    return (gatherP)_dxf_getHwObjectData((dxObject)go);
}

Error
_dxf_pushHwGatherClipPlanes(gatherO go, int n, Point *p, Vector *v)
{
    int i;
    gatherP gop;
    clipPlaneP clips = NULL;

    if (! go)
	return ERROR;

    gop = _getHwGatherData(go);

    clips = (clipPlaneP)DXAllocateZero(sizeof(clipPlanesT));
    if (! clips)
	goto error;
    
    clips->n = n;

    if (n > 0)
    {
	clips->p = (Point *)DXAllocateZero(n*sizeof(Point));
	if (! clips->p)
	    goto error;

	clips->v = (Point *)DXAllocateZero(n*sizeof(Vector));
	if (! clips->v)
	    goto error;

	for (i = 0; i < n; i++)
	{
	    clips->p[i] = p[i];
	    clips->v[i] = v[i];
	}
    }

    clips->next = gop->clipStack;
    gop->clipStack = clips;

    return OK;

error:
    if (clips)
    {
	DXFree((Pointer)clips->p);
	DXFree((Pointer)clips->v);
	DXFree((Pointer)clips);
    }

    return ERROR;
}

		
Error
_dxf_getHwGatherClipPlanes(gatherO go, int *nClips, Point **p, Vector **v)
{
    gatherP gop;
    clipPlaneP clips;

    *p = NULL;
    *v = NULL;

    if (! go)
	return ERROR;

    gop = _getHwGatherData(go);
    
    *nClips = 0;
    for (clips = gop->clipStack; clips; clips = clips->next)
	*nClips += clips->n;


    if (*nClips)
    {
	int i, j;

	*p = (Point *)DXAllocate((*nClips)*sizeof(Point));
	*v = (Point *)DXAllocate((*nClips)*sizeof(Point));
	if (! *p || ! *v)
	    goto error;

	for (j = 0, clips = gop->clipStack; clips; clips = clips->next)
	    for (i = 0; i < clips->n; i++, j++)
	    {
		(*p)[j] = clips->p[i];
		(*v)[j] = clips->v[i];
	    }
    }

    return OK;

error:

    DXFree((Pointer)*p);
    DXFree((Pointer)*v);
    return ERROR;
}



static Error
__popHwGatherClipPlanes(gatherP gop)
{
    clipPlaneP top;

    if (! gop)
	 return OK;
    
    if (NULL == (top = gop->clipStack))
	return ERROR;

    gop->clipStack = top->next;

    DXFree((Pointer)top->p);
    DXFree((Pointer)top->v);
    DXFree((Pointer)top);

    return OK;
}

		
Error
_dxf_popHwGatherClipPlanes(gatherO go)
{
    gatherP gop;

    if (! go)
	return ERROR;

    gop = _getHwGatherData(go);

    return __popHwGatherClipPlanes(gop);
    

    return OK;
}

		
Error
_dxf_getHwGatherFlags(gatherO go, int *flags)
{
    gatherP gop;

    if (! go)
	return ERROR;

    gop = _getHwGatherData(go);
    
    if (!gop)
	return ERROR;

    if (flags)
        *flags = gop->flags;

    return OK;
}

Error
_dxf_setHwGatherFlags(gatherO go, int flags)
{
    gatherP gop;

    if (! go)
	return ERROR;

    gop = _getHwGatherData(go);
    
    if (!gop)
	return ERROR;

    gop->flags = flags;

    return OK;
}

Error
_dxf_getHwGatherLights(gatherO go, listO *list)
{
    gatherP gop;

    if (! go)
	return ERROR;

    gop = _getHwGatherData(go);
    
    if (!gop)
	return ERROR;

    if (list)
        *list = gop->lights;

    return OK;
}

Error
_dxf_setHwGatherLights(gatherO go, listO list)
{
    gatherP gop;

    if (! go)
	return ERROR;

    gop = _getHwGatherData(go);
    
    if (!gop)
	return ERROR;

    if (gop->lights)
    {
	DXDelete((dxObject)gop->lights);
	gop->lights = NULL;
    }
    
    if (list)
	gop->lights = (listO)DXReference((dxObject)list);

    return OK;
}

Error
_dxf_getHwGatherAmbientColor(gatherO go, RGBColor *color)
{
    gatherP gop;

    if (! go)
	return ERROR;

    gop = _getHwGatherData(go);
    
    if (!gop)
	return ERROR;

    if (color)
        *color = gop->ambientColor;

    return OK;
}

Error
_dxf_setHwGatherAmbientColor(gatherO go, RGBColor color)
{
    gatherP gop;

    if (! go)
	return ERROR;

    gop = _getHwGatherData(go);
    
    if (!gop)
	return ERROR;

    gop->ambientColor = color;

    return OK;
}

Error
_dxf_getHwGatherMaterial(gatherO go, materialO *mat)
{
    gatherP gop;

    if (! go)
	return ERROR;

    gop = _getHwGatherData(go);
    
    if (!gop)
	return ERROR;

    if (mat)
        *mat = gop->lastUsedMaterial;

    return OK;
}

Error
_dxf_setHwGatherMaterial(gatherO go, materialO mat)
{
    gatherP gop;

    if (! go)
	return ERROR;

    gop = _getHwGatherData(go);
    
    if (!gop)
	return ERROR;

    if (gop->lastUsedMaterial)
    {
	DXDelete((dxObject)gop->lastUsedMaterial);
	gop->lastUsedMaterial = NULL;
    }

    DXReference((dxObject)mat);
    gop->lastUsedMaterial = mat;

    return OK;
}

Error
_dxf_getHwGatherObject(gatherO go, dxObject *object)
{
    gatherP gop;

    if (! go)
	return ERROR;

    gop = _getHwGatherData(go);
    
    if (!gop)
	return ERROR;

    if (object)
        *object = gop->object;

    return OK;
}

Error
_dxf_setHwGatherObject(gatherO go, dxObject object)
{
    gatherP gop;

    if (! go)
	return ERROR;

    gop = _getHwGatherData(go);
    
    if (!gop)
	return ERROR;

    if (gop->object)
    {
	DXDelete((dxObject)gop->object);
	gop->object = NULL;
    }

    if (object)
	gop->object = (dxObject)DXReference(object);

    return OK;
}

Error
_dxf_getHwGatherClipObject(gatherO go, dxObject *clipObject)
{
    gatherP gop;

    if (! go)
	return ERROR;

    gop = _getHwGatherData(go);
    
    if (!gop)
	return ERROR;

    if (clipObject)
        *clipObject = gop->clipObject;

    return OK;
}

Error
_dxf_setHwGatherClipObject(gatherO go, dxObject clipObject)
{
    gatherP gop;

    if (! go)
	return ERROR;

    gop = _getHwGatherData(go);
    
    if (!gop)
	return ERROR;

    if (gop->clipObject)
    {
	DXDelete((dxObject)gop->clipObject);
	gop->clipObject = NULL;
    }

    if (clipObject)
	gop->clipObject = (dxObject)DXReference(clipObject);

    return OK;
}

static Error 
_deleteGather(Pointer arg)
{
  gatherP gp = (gatherP)arg;

  ENTRY(("_dxf_deleteGather(0x%x)", arg));
  
  if (gp)
  {
      if (gp->lights)
      {
	  DXDelete((dxObject)gp->lights);
	  gp->lights = NULL;
      }

      if (gp->lastUsedMaterial)
      {
	  DXDelete((dxObject)gp->lastUsedMaterial);
	  gp->lastUsedMaterial = NULL;
      }

      if(gp->object)
      {
	  DXDelete(gp->object);
	  gp->object = NULL;
      }

      if(gp->clipObject)
      {
	  DXDelete(gp->clipObject);
	  gp->clipObject = NULL;
      }

      while(__popHwGatherClipPlanes(gp));

  }
    
  DXFree(gp);
  gp = NULL;

  tdmCheckAllocTable(3);

  EXIT(("OK"));
  return OK;
}

gatherO
_dxf_newHwGather(void)
{
  gatherP	ret;
  gatherO	go;

  ENTRY(("_dxf_newGather()"));
  
  ret = (gatherP)DXAllocateZero(sizeof(gatherT));
  if(!ret)
    goto error;

  ret->lights = _dxf_newList(8, (int(*)(void*))DXDelete);
  if(!ret->lights)
    goto error;

  go = (gatherO)_dxf_newHwObject(HW_CLASS_GATHER,
				(Pointer)ret, _deleteGather);
  if (!go)
    goto error;

  EXIT(("ret = 0x%x", ret));
  return go;

 error:
  if(ret) {
    DXFree(ret);
    ret = NULL;
  }

  EXIT(("ERROR"));
  DXErrorReturn(ERROR_NO_MEMORY,"");
}

/*
 * Gather
 *
 * NOTE: This function should only do View Independent and Medium Indepenant
 * functions. (No camera or medium is available)
 *
 * Gathers together camera independent lights 
 * Gathers a single clip object for transparent fields.
 * Creates a new object heirarchy with:
 *	fields replaced with xfields (which contain accumulated model xforms)
 *	SCREEN_WORLD screen objects replaced with SCREEN_VIEWPORT screen objects
 *	modeling transforms carried down through clip and render objects
 * 	XForms objects removed
 * count the total number of connections in all transparent fields.
 *
 * Sets global flags indicating:
 *	if transparent fields exist in the object
 *	if Volume fields exist in the object
 *	if a single regular volume is the only transparent object ?????
 *	if Clip objects exist in the object
 *
*/

Error
_dxf_gather(dxObject object, gatherO gather, void *globals)
{
  DEFGLOBALDATA(globals);
  DEFPORT(PORT_HANDLE);
  attributeP 	attributes = NULL;
  dxObject	newObject, savenew;
  int		flags;

  ENTRY(("_dxf_gather(0x%x, 0x%x, 0x%x)", object, gather, globals));

  attributes = _dxf_newAttribute(NULL);

  if (!object) {
    EXIT(("object == NULL"));
    return OK;
  }

  /*
   * Initialize (or create) the mesh and texture hash tables.
   * During the gather traversal, whenever we hit a triangle- or
   * quad-strippable field, look in the mesh hash table to see if 
   * there already is one.  Same for textures.  In the initialization
   * process, the hit flag for every object (of either kind) is
   * set to 0.  When an object is accessed on this traversal, its
   * hit flag is set to 1.  At the end of the traversal, the hash
   * tables are traversed, and any object with a hit flag of 0 
   * (indicating that it wasn't needed in this rendering) is discarded.
   */
  if (! MESHHASH) MESHHASH = _dxf_InitObjectHash();
  else _dxf_ResetObjectHash(MESHHASH);
    
  if (! TEXTUREHASH) TEXTUREHASH = _dxf_InitObjectHash();
  else _dxf_ResetObjectHash(TEXTUREHASH);

  _dxf_clearSortList(SORTLIST);

  if(!(savenew = newObject = DXCopy(object,COPY_ATTRIBUTES))) goto error;
  if(!(_gatherRecurse(object, &newObject, gather, attributes, globals))) 
    goto error;

  if(savenew != newObject)
    DXDelete(savenew);

  if (! _dxf_getHwGatherFlags(gather, &flags))
    goto error;

  if(!(flags & CONTAINS_LIGHT)) 
  {
    listO lo;
    Vector direction = {-1.0, 0.0, 1.0};
    RGBColor color = {1.0, 1.0, 1.0};
    Light	newLight;

    if(!(newLight = DXNewCameraDistantLight(direction,color)))
       goto error;

    DXReference((dxObject)newLight);

    if (! _dxf_getHwGatherLights(gather, &lo))
	goto error;

    if (! _dxf_listAppendItem(lo, (Pointer)newLight))
	goto error;

    color.r = color.g = color.b = 0.2;
    if (! _dxf_setHwGatherAmbientColor(gather, color))
	goto error;
  } 

  if (! _dxf_setHwGatherObject(gather, newObject))
      goto error;

  if (attributes)
    _dxf_deleteAttribute(attributes);

  /*
   * Now delete all the un-hit objects from the mesh and
   * texture hashes
   */
  MESHHASH = _dxf_CullObjectHash(MESHHASH);
  TEXTUREHASH = _dxf_CullObjectHash(TEXTUREHASH);

  EXIT(("OK"));
  return OK;

 error:

  if(attributes)
    _dxf_deleteAttribute(attributes);

  EXIT(("ERROR"));
  return ERROR;
}

#define OPACITY(offset)							\
  *(float *)((xf->omap) ?                                               \
      DXGetArrayEntry(xf->omap,                                         \
          *(char *) DXGetArrayEntry(xf->opacities, offset, &iscratch),  \
          fscratch) :                                                   \
      DXGetArrayEntry(xf->opacities,                                    \
          offset,                                                       \
          fscratch))

static Error
_gatherField(Field f, 
	     dxObject *newObject,
	     gatherO gather, 
	     attributeP attributes,
	     void *globals)
{
  DEFGLOBALDATA(globals);
  DEFPORT(PORT_HANDLE);
  xfieldP	xf;
  hwFlags	attFlags;
  hwFlags	servicesFlags;

  ENTRY(("_gatherField(0x%x, 0x%x, 0x%x, 0x%x, 0x%x)",
	 f, newObject, gather, attributes, globals));
  
  if (DXEmptyField(f)) {
    *newObject = NULL;
    EXIT(("OK: f is empty"));
    return OK;
  }
	  
  servicesFlags = _dxf_SERVICES_FLAGS();

  if(_dxf_isFlagsSet(servicesFlags,SF_VOLUME_BOUNDARY))
    {
      dxObject	connections;
      char	*elementType = NULL;
      int	one = 1;
      
      if(connections = DXGetComponentValue(f,"connections")) {
	if(!DXGetStringAttribute(connections,"element type",&elementType))
	  DXErrorGoto(ERROR_BAD_PARAMETER, "#13070");
	
	if(!strcmp(elementType,"tetrahedra") || !strcmp(elementType,"cubes")) {
	  Array validity;
	  dxObject newField;
	  ModuleInput inputs[2];
	  ModuleOutput outputs[1];
	  
	  if (!(validity = (Array)DXNewArray(TYPE_INT, CATEGORY_REAL, 0)))
	    goto error;

	  if (!DXAddArrayData((Array)validity, 0, 1, (Pointer)&one))
	    goto error;
	  
	  DXReference((dxObject)validity);

	  DXSetModuleInput(inputs[0],"input",(dxObject)f);
	  DXSetModuleInput(inputs[1],"validity",(dxObject)validity);
	  DXSetModuleOutput(outputs[0],"output",&newField);

	  if(!DXCallModule("ShowBoundary",2,inputs,1,outputs))
	  {
	      if (DXGetError() == ERROR_NONE)
		    DXSetError(ERROR_INTERNAL,
		    "ShowBoundary or DXCallModule failed, no error code set");
	      DXDelete((dxObject)validity);
	      goto error;
	  } 

	  DXDelete((dxObject)validity);
	  DXReference((dxObject)newField);
		    
	  if (!_gatherRecurse(newField,newObject, gather, 
			      attributes, globals))
	  {
	    DXDelete(newField);
	    goto error;
	  }
	  
	  DXDelete(newField);
	  
	  EXIT(("OK"));
	  return OK;
	}
	
      }
    }

  /* allocate xfield */
  *newObject = (dxObject)_dxf_newXfieldO(f,attributes,globals);
  if (! *newObject)
    goto error;

  xf = _dxf_getXfieldData((xfieldO)*newObject);
  if (! xf)
    goto error;

  attFlags = _dxf_attributeFlags(_dxf_xfieldAttributes(xf));

  if (!(_dxf_isFlagsSet(attFlags,(CONTAINS_TRANSPARENT | CONTAINS_VOLUME))))
  {
    EXIT(("OK: neither transparent nor volume field"));
    return OK;
  }
  
  /*
   * OK we're into the transparent fields (and volumes) now
   */

  if(_dxf_isFlagsSet(attFlags,IN_SCREEN_OBJECT)) {
    EXIT(("ERROR"));
    DXErrorReturn(ERROR_BAD_PARAMETER,
		  "Transparent fields not allowed in screen object");
  }
  
  /*
  _dxf_setFlags(attFlags,CONTAINS_TRANSPARENT);
  */

  switch (xf->opacitiesDep)
  {
      case dep_field:
      {
	  int	 i;
	  float  fscratch[1];
	  int    iscratch;

	  if (OPACITY(0))
	      for (i = 0; i < xf->nconnections; i++)
		  if (!xf->invCntns || !DXIsElementInvalid(xf->invCntns,i))
		      _dxf_Insert_Translucent(SORTLIST, (void *)xf, i);
	  break;
      }

      case dep_connections:
      {
	  int	 i;
	  float  fscratch[1];
	  int    iscratch;

	  for (i = 0; i < xf->nconnections; i++)
 	      if (!xf->invCntns || !DXIsElementInvalid(xf->invCntns,i))
		  if (OPACITY(i))
		      _dxf_Insert_Translucent(SORTLIST, (void *)xf, i);
	  break;
      }

      case dep_positions:
      {
	  int	 i, j;
	  float  fscratch[1];
	  int    iscratch;
	  int	 *c, cscr[8];

	  for (i = 0; i < xf->nconnections; i++)
	  {
 	      if (!xf->invCntns || !DXIsElementInvalid(xf->invCntns,i))
	      {
		  c = (int*)DXGetArrayEntry(xf->connections,i,cscr);
		  for (j = 0; j < xf->posPerConn; j++)
		      if (OPACITY(c[j]))
		      {
			  _dxf_Insert_Translucent(SORTLIST, (void *)xf, i);
			  break;
		      }
	      }
	  }
	  break;
      }

      case dep_none:
      default:
	  break;
  }

  EXIT(("OK"));
  return OK;

 error:

  EXIT(("ERROR"));
  return ERROR;
}

static Error
_gatherRecurse(dxObject object,
	       dxObject *newObject,
	       gatherO gather,
	       attributeP attributes,
	       void *globals)
{
  DEFGLOBALDATA(globals);
  DEFPORT(PORT_HANDLE);
  dxObject 	subObject,tmpObject,tmpObject1, attr;
  int 		i;
  attributeP 	new = NULL;
  Class		class,groupClass;
  hwFlags	newFlags;

  ENTRY(("_gatherRecurse(0x%x, 0x%x, 0x%x, 0x%x, 0x%x)",
	 object, newObject, gather, attributes, globals));

  attr = DXGetAttribute(object, "visible");
  if (attr)
  {
      int flag;
      DXExtractInteger(attr, &flag);
      if (flag == 0)
      {
	  *newObject = NULL;
	  return OK;
      }
  }
  
  if (!(new = _dxf_parameters((dxObject)object, attributes))) goto error;
  if (!(class = DXGetObjectClass(object))) goto error;
  newFlags = _dxf_attributeFlags(new);
  /*
   * XXX check for empty dxObject
   */

  switch (class) {
  case CLASS_GROUP:
  {
    *newObject = DXCopy(object,COPY_ATTRIBUTES);
    if(!(groupClass = DXGetGroupClass((Group)object))) goto error;
    switch(groupClass)
    {
	case CLASS_COMPOSITEFIELD:
	{
	    _dxf_setFlags(newFlags,IGNORE_PARAMS);
	    i = 0;
	    while (NULL != 
		(subObject = DXGetEnumeratedMember((Group)object, i, NULL)))
	    {
	        dxObject tmpObject;

	        if (!_gatherRecurse(subObject, &tmpObject, 
					gather, new, globals))
		    goto error;

		if (tmpObject)
		{
		    if (!DXSetMember((Group)*newObject,NULL,tmpObject))
			goto error;
	        }

		i++;
	    }
	    break;
	}

	case CLASS_SERIES:
	{
	    float p;
	    int j;
	    _dxf_clearFlags(newFlags, IGNORE_PARAMS);
	    i = j = 0;
	    while (NULL != 
		(subObject = DXGetSeriesMember((Series)object, i, &p)))
	    {
	        dxObject tmpObject;

	        if (!_gatherRecurse(subObject, &tmpObject, 
					gather, new, globals))
		    goto error;

		if (tmpObject)
		{
		    if (!DXSetSeriesMember((Series)*newObject, j++, p, tmpObject))
			goto error;
	        }

		i++;
	    }
	    break;
	}

	default:
	{
	    char *name;
	    _dxf_clearFlags(newFlags, IGNORE_PARAMS);
	    i = 0;
	    while (NULL != 
		(subObject = DXGetEnumeratedMember((Group)object, i, &name)))
	    {
	        dxObject tmpObject;

	        if (!_gatherRecurse(subObject, &tmpObject, 
					gather, new, globals))
		    goto error;

		if (tmpObject)
		{
		    if (!DXSetMember((Group)*newObject, name, tmpObject))
			goto error;
	        }

		i++;
	    }
	    break;
	}
    }

    break;
  }
  case CLASS_FIELD:
    if(!_gatherField((Field)object,newObject,gather,new,globals))
      goto error;
    break;
  case CLASS_SCREEN:
    {
      int		positionUnits, z;
      float		matrix[4][4];
      float		ident[4][4];
      int		flags;
      

      COPYMATRIX(ident,identity);

      if(!(DXGetScreenInfo((dxScreen)object,&subObject,&positionUnits,&z)))
	goto error;

      /* 
       * Set the modeling matrix to identity for recursion
       */
      _dxf_attributeMatrix(attributes,matrix);
      _dxf_setAttributeMatrix(new, ident);
      _dxf_setFlags(newFlags,IN_SCREEN_OBJECT);

      if (positionUnits != SCREEN_STATIONARY)
      {
	if (z)
	  new->ff = 1.0 / 1000000;
	
	new->fuzz *= new->ff;
      }

      if (!_gatherRecurse(subObject, &tmpObject, gather, new, globals))
	goto error;

      if(tmpObject) {

	*newObject = (dxObject)_dxf_newHwScreen((dxScreen)object,
						(dxObject) tmpObject,
						NULL,matrix,
						0,0);
	if (! *newObject)
	   goto error;
 
	if (! _dxf_getHwGatherFlags(gather, &flags))
	  goto error;

	flags |= CONTAINS_SCREEN;

        if (! _dxf_setHwGatherFlags(gather, flags))
	  goto error;

      }
      else
      {
	*newObject = NULL;
      }
    }
    break;
  case CLASS_CLIPPED:
    {
      dxObject 		clipObject;
      float		m[4][4],atm[4][4];
      double		dm[4][4],datm[4][4];
      static Vector 	X={1.0,0.0,0.0},Y={0.0,1.0,0.0},Z={0.0,0.0,1.0};
      Array		attr;
      Pointer		attP;
      Point		pt,min,max,newMin,newMax;
      Vector		normal;
      float		fpt[3],fnormal[3];
      clippedO		cpo;


      /*
       * validity checks 
       */
      if(_dxf_isFlagsSet(newFlags,IN_SCREEN_OBJECT)) {
	EXIT(("ERROR"));
	DXErrorReturn(ERROR_BAD_PARAMETER,
		      "Clipped objects not allowed in screen object");
      }

      if(_dxf_isFlagsSet(newFlags,BEING_CLIPPED)) {
	EXIT(("ERROR"));
	DXErrorReturn(ERROR_BAD_PARAMETER,
		      "Clipped objects cannot be clipped.");
      }

      if(_dxf_isFlagsSet(newFlags,IN_CLIP_OBJECT)) {
	EXIT(("ERROR"));
	DXErrorReturn(ERROR_BAD_PARAMETER,
		      "Nested clipped objects not allowed.");
      }

      DXGetClippedInfo((Clipped)object, &subObject, &clipObject);

      _dxf_setFlags(newFlags,BEING_CLIPPED);
      if (!_gatherRecurse(subObject, &tmpObject, gather, new, globals))
	goto error;
      _dxf_clearFlags(newFlags,BEING_CLIPPED);

      if(tmpObject) {

	/* Get the current modeling transform and adjointTranspose */
	_dxf_attributeMatrix(new,m);
	COPYMATRIX(dm,m);
	_dxfAdjointTranspose(datm,dm);
	
	if((attr = (Array)DXGetAttribute(clipObject,"clipplane normal")))
	{
	  Point  *cp_pt;
	  Vector *cp_nrm;

	  attP = (Vector *)DXGetConstantArrayData(attr);
	  if(!attP)
	    DXErrorGoto(ERROR_BAD_PARAMETER,
		"clip plane has invalid normal");

	  normal = *(Vector*)attP;

	  attr = (Array)DXGetAttribute(clipObject,"clipplane point");
	  if (!attr)
	      DXErrorGoto(ERROR_BAD_PARAMETER,
	  	  "clip plane contains no point");

	  attP = (Point *)DXGetConstantArrayData(attr);
	  if(!attP)
	      DXErrorGoto(ERROR_BAD_PARAMETER,
		  "clip plane has invalid point");

	  pt = *(Vector*)attP;
	  
	  cpo = _dxf_newHwClipped(1, tmpObject);
	  if (! cpo)
	      goto error;
	   
	  APPLY3((float *)&pt,dm);
	  APPLY3((float *)&normal,datm);

	  if (! _dxf_getHwClippedInfo(cpo, &cp_pt, &cp_nrm, NULL, NULL))
	      goto error;

	  /* Negate the sense of the normal (undoes same in DXClipPlane)
	     and normalize */

	  *cp_pt  = pt;
	  *cp_nrm = DXNormalize(DXNeg(normal));
	  
	  *newObject = (dxObject)cpo;

	}
	else if (attr = (Array)DXGetAttribute(clipObject,"clipbox min"))
	{
	  Point  *cp_pts;
	  Vector *cp_nrms;

	  attP = (Point *)DXGetConstantArrayData(attr);
	  if(!attP)
	      DXErrorGoto(ERROR_BAD_PARAMETER,
		  "clip box has invalid min");

	  min = *(Point *)attP;

	  attr = (Array)DXGetAttribute(clipObject,"clipbox max");
	  if(!attr)
	      DXErrorGoto(ERROR_BAD_PARAMETER,
		  "clip box contains no max");

	  attP = (Point *)DXGetConstantArrayData(attr);
	  if(!attP)
	      DXErrorGoto(ERROR_BAD_PARAMETER,
		  "clip box has invalid max");

	  max = *(Point *)attP;
	  
	  newMin.x = min.x < max.x ? min.x : max.x;
	  newMin.y = min.y < max.y ? min.y : max.y;
	  newMin.z = min.z < max.z ? min.z : max.z;
	  
	  newMax.x = min.x > max.x ? min.x : max.x;
	  newMax.y = min.y > max.y ? min.y : max.y;
	  newMax.z = min.z > max.z ? min.z : max.z;
	  
	  APPLY3((float*)&newMin,dm);
	  APPLY3((float*)&newMax,dm);
	  APPLY3((float*)&X,datm);
	  APPLY3((float*)&Y,datm);
	  APPLY3((float*)&Z,datm);

	  cpo = _dxf_newHwClipped(6, tmpObject);
	  if (! cpo)
	      goto error;

	  if (! _dxf_getHwClippedInfo(cpo, &cp_pts, &cp_nrms, NULL, NULL))
	      goto error;

	  cp_pts[0] = newMin;
	  cp_nrms[0] = X;
	  
	  cp_pts[1] = newMin;
	  cp_nrms[1] = Y;
	  
	  cp_pts[2] = newMin;
	  cp_nrms[2] = Z;
	  
	  cp_pts[3] = newMax;
	  cp_nrms[3] = DXNeg(X);
	  
	  cp_pts[4] = newMax;
	  cp_nrms[4] = DXNeg(Y);
	  
	  cp_pts[5] = newMax;
	  cp_nrms[5] = DXNeg(Z);
	  
	  *newObject = (dxObject)cpo;
	}
	else 
	{
	  *newObject = NULL;
	  DXErrorGoto(ERROR_BAD_PARAMETER,"unrecognized clip object");
	}
	
	
      _dxf_setFlags(newFlags,CONTAINS_CLIP_OBJECT);

      } else
	*newObject = NULL;
    }
    break;
  case CLASS_XFORM:
    {
      dxMatrix	matrix;
      float	tmp[4][4],tmp1[4][4],tmp2[4][4];
      /*
       * remove XForms from tree, propagate Xforms to Fields
       */

      if (!DXGetXformInfo((Xform)object, &subObject, &matrix)) {
	EXIT(("DXGetXformInfo returns NULL"));
	return NULL;
      }
      
      CONVERTMATRIX(tmp,matrix);
      _dxf_attributeMatrix(new,tmp1);
      MULTMATRIX(tmp2,tmp,tmp1);
      _dxf_setAttributeMatrix(new,tmp2);
      if (!_gatherRecurse(subObject, newObject, gather, new, globals))
	goto error;
    }
    break;
  case CLASS_LIGHT:
    {
      float	direction[3];
      RGBColor	color;
      int       flags;

      *newObject = NULL;

      if(_dxf_isFlagsSet(newFlags,IN_SCREEN_OBJECT)) {
	EXIT(("ERROR"));
	DXErrorReturn(ERROR_BAD_PARAMETER,
		      "Light objects not allowed in Screen object");
      }

      if(DXQueryCameraDistantLight((Light)object,(Vector*)direction,&color))
      {
	listO lights;

	if (! _dxf_getHwGatherLights(gather, &lights))
	    goto error;

	 /* only first 8 lights are used */
	if(_dxf_listSize(lights) >= 8) {
	  DXWarning("#5195") ;
	  break;
	}

	DXReference(object);

	if(! _dxf_listAppendItem(lights,(Pointer) object))
	  goto error;

      }
      else if (DXQueryDistantLight((Light)object,(Vector*)direction,&color))
      {
	listO lights;

	if (! _dxf_getHwGatherLights(gather, &lights))
	    goto error;

	if(_dxf_listSize(lights) >= 8) {
	  DXWarning("#5195") ;
	  break;
	}

	DXReference(object);

	if(! _dxf_listAppendItem(lights,(Pointer) object))
	  goto error;
      }
      else if(DXQueryAmbientLight((Light)object,&color)) 
      {
	RGBColor gcolor;

	if (! _dxf_getHwGatherAmbientColor(gather, &gcolor))
	    goto error;

	gcolor.r += color.r;
	gcolor.g += color.g;
	gcolor.b += color.b;

	if (! _dxf_setHwGatherAmbientColor(gather, gcolor))
	    goto error;

      } 
      else
      {
	PRINT(("invalid light"));
	DXErrorGoto(ERROR_INVALID_DATA, "#13750");
      }

      if (! _dxf_getHwGatherFlags(gather, &flags))
	  goto error;

      flags |= CONTAINS_LIGHT;

      if (! _dxf_setHwGatherFlags(gather, flags))
	  goto error;

    }
    break;
  default:
    break;
  }

  if(new)
    _dxf_deleteAttribute(new);

  EXIT(("OK"));
  return OK;

 error:

  if(new)
    _dxf_deleteAttribute(new);

  EXIT(("OK"));
  return ERROR;
}

