/*
  Maze editor: 3D model class
  Copyright (C) 1998 by Jorrit Tyberghein
  Written by Andrew Zabolotny <bit@eltech.ru>

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

  This library 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
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "sysdef.h"
#include "me_draft.h"
#include "me_model.h"
#include "me_util.h"
#include "csutil/util.h"
#include "cssys/common/system.h"
#include "csengine/thingtpl.h"
#include "csengine/cssprite.h"
#include "csengine/triangle.h"
#include "csengine/polygon.h"
#include "csengine/pol2d.h"
#include "csobject/nameobj.h"
#include "csengine/dumper.h"

//----------------------------------------------------// 3D model utils //----//

mz3DModel::csVertexVector::~csVertexVector ()
{
  DeleteAll ();
}

bool mz3DModel::csVertexVector::FreeItem (csSome Item)
{
  CHK (delete (mz3DVertex *)Item);
  return true;
}

//----------------------------------------------------// 3D model class //----//
mz3DModel::mz3DModel (csModelType iType, char *iFileName)
  : V (8, 8), P (16, 16), L (8, 8), A (8, 8), FrameName (8, 8)
{

  static int rm_ind = 0, st_ind = 0, tg_ind = 0, sp_ind = 0;
  int count = 0;
  Type = iType;
  Color = -1;
  Name = FileName = NULL;
  Texture = NULL;
  Frame = -1;
  SetFrame (0);
  sector_ind = thing_ind = sprite_ind = 0;

  // Generate a name for model
  char tmp [16];
  // The (char *) is necessary or compilation breaks in GCC 2.95 -MDL
  char *format = (char *) (iType == csmtRoom ? (sector_ind = count =  rm_ind++), "o%d" :
		 iType == csmtSector ? (sector_ind = count =  st_ind++), "s%d" :
                 iType == csmtThing ? (thing_ind = count = tg_ind++), "t%d" :
                 iType == csmtSprite ? (count = sp_ind++), "s%d" :
                 "m%lX");
  sprintf (tmp, format, count);
  SetName (tmp);

  if (iFileName)
    SetFileName (iFileName);
}


mz3DModel::~mz3DModel ()
{
  SetName (NULL);
  SetFileName (NULL);
}

void mz3DModel::SetName (const char *iName)
{
  if (Name)
    delete [] Name;
  Name = strnew (iName);
}

void mz3DModel::SetFileName (char *iFileName)
{
  if (FileName)
    delete [] FileName;
  FileName = strnew (iFileName);
}

void mz3DModel::SetFrameName (int iFrame, char *iName)
{
  if ((iFrame < 0)
   || (iFrame > Frames ()))
    return;

  if (iFrame < FrameName.Length ())
    FrameName.FreeItem (FrameName [iFrame]);
  FrameName [iFrame] = strnew (iName);
}

bool mz3DModel::SetFrame (int iFrame, char *iFrameName)
{
  if ((Type != csmtSprite)
   && (iFrame != 0))
    return false;
  if ((iFrame < 0)
   || (iFrame > V.Length ()))
    return false;
  int oldFrame = Frame;
  Frame = iFrame;
  if (iFrameName)
    SetFrameName (Frame, iFrameName);
  if (Frame == V.Length ())
  {
    V.Push (new csVertexVector (64, 64));
    if (!iFrameName)
    {
      char tmp [16];
      sprintf (tmp, "f%d", Frame);
      SetFrameName (Frame, tmp);
    } /* endif */
    // duplicate previous frame, if any
    if (oldFrame >= 0)
    {
      int vc = Vertices (oldFrame);
      for (int vertex = 0; vertex < vc; vertex++)
        ((csVertexVector *)V [Frame])->Push (new mz3DVertex (Vertex (vertex, oldFrame)));
    } /* endif */
  }

  // duplicate selection
  if (oldFrame >= 0)
  {
    int vc = Vertices (oldFrame);
    for (int vertex = 0; vertex < vc; vertex++)
      Vertex (vertex).selected = Vertex (vertex, oldFrame).selected;
  } /* endif */
  return true;
}

void mz3DModel::DeleteFrame (int iFrame)
{
  if ((iFrame < 0) || (iFrame >= Frames ()))
    return;
  for (int i = A.Length () - 1; i >= 0; i--)
  {
    mzSpriteAction *sa = (mzSpriteAction *)A [i];
    sa->NotifyFrameDeleted ((char *)FrameName [iFrame]);
  } /* endfor */
  V.Delete (iFrame);
  FrameName.Delete (iFrame);
  if (Frame >= V.Length ())
    Frame = V.Length () - 1;
  if (Frame < 0)
    SetFrame (0);
}

bool mz3DModel::InsertVertex (mz3DVertex *iVertex, int *iNum)
{
  int i;
  for (i = Vertices () - 1; i >= 0; i--)
    if (Vertex (i) == *iVertex)
    {
      if (iNum)
        *iNum = i;
      CHK (delete iVertex);
      return false;
    } /* endif */
  if (iNum)
    *iNum = Vertices ();
  for (i = Frames () - 1; i >= 0; i--)
    ((csVertexVector *)V [i])->Push (iVertex);
  return true;
}

bool mz3DModel::DeleteVertex (int iVertex)
{
  if ((iVertex < 0)
   || (iVertex >= Vertices ()))
    return false;

  int i;
  for (i = Frames () - 1; i >= 0; i--)
    ((csVertexVector *)V [i])->Delete (iVertex);
  for (i = Polygons () - 1; i >= 0; i--)
    if (!Polygon (i).NotifyVertexDeleted (iVertex))
      DeletePolygon (i);
  return true;
}

bool mz3DModel::InsertPolygon (mz3DPolygon *iPolygon, int *iNum)
{
  for (int i = P.Length () - 1; i >= 0; i--)
    if (Polygon (i) == *iPolygon)
    {
      if (iNum)
        *iNum = i;
      delete iPolygon;
      return false;
    } /* endif */
  if (iNum)
    *iNum = P.Length ();
  iPolygon->parent = this;
  P.Push (iPolygon);
  return true;
}

bool mz3DModel::InsertSector (csSector *iSector, int *iNum)
{

  if (iNum)
    *iNum = S.Length ();
//  iSector->parent = this;
  S.Push (iSector);
  return true;
}

bool mz3DModel::InsertLight (mz3DLight *iLight, int *iNum) {
  for (int i = L.Length () - 1; i >= 0; i--)
  
    if (Light (i) == *iLight) {
      if (iNum)
	*iNum = i;
      delete iLight;
      return false;
    }
  if (iNum)
    *iNum = L.Length ();
  iLight->parent = this;
  L.Push (iLight);
  return true;
}


bool mz3DModel::InsertThing (csThing *iThing, int *iNum)
{

  if (iNum)
    *iNum = S.Length ();
//  iSector->parent = this;
  T.Push (iThing);
  return true;
}

bool mz3DModel::DeletePolygon (int iPolygon)
{
  if ((iPolygon < 0)
   || (iPolygon >= Polygons ()))
    return false;

  P.Delete (iPolygon);
  return true;
}

bool mz3DModel::DeleteSector (int iSector)
{
  if ((iSector < 0)
   || (iSector >= Sectors ()))
    return false;

  S.Delete (iSector);
  return true;
}

bool mz3DModel::DeleteThing (int iThing)
{
  if ((iThing < 0)
   || (iThing >= Sectors ()))
    return false;

  T.Delete (iThing);
  return true;
}

bool mz3DModel::InsertAction (mzSpriteAction *iAction, int *iNum)
{
  for (int i = Actions () - 1; i >= 0; i--)
    if (Action (i) == *iAction)
    {
      if (iNum)
        *iNum = i;
      delete iAction;
      return false;
    } /* endif */
  if (iNum)
    *iNum = A.Length ();
  A.Push (iAction);
  return true;
}

bool mz3DModel::DeleteAction (int iAction)
{
  if ((iAction < 0)
   || (iAction >= Actions ()))
    return false;

  A.Delete (iAction);
  return true;
}

void mz3DModel::Clear (bool iVertices, bool iFaces, bool iLights, bool iFrames,
  bool iActions)
{
  if (iVertices)
  {
    P.DeleteAll ();
    for (int i = Frames () - 1; i >= 0; i--)
      ((csVertexVector *)V [i])->DeleteAll ();
  }
  if (iFaces)
    P.DeleteAll ();
  if (iLights)
    L.DeleteAll ();
  if (iFrames)
  {
    V.DeleteAll ();
    FrameName.DeleteAll ();
    Frame = -1;
    SetFrame (0);
  }
  if (iActions)
    A.DeleteAll ();
}

bool mz3DModel::Save (bool (*Put) (Archive *ar, void *iStream, char *iData, int iSize), Archive *ar, void *iStream)
{
#define PUTS(ar, s)					\
  {						\
    int _len = strlen (s);			\
    if  (!Put (ar, iStream, s, _len))		\
	return false;  \
  }

/*#define PUTS(s)					\
  {						\
    int _len = strlen (s);			\
    if (!Put (iStream, s, _len))		\
      return false;				\
  }*/

  char *header;
  switch (Type)
  {
    case csmtRoom:
      header = "  ROOM '%s' (\n";
      break;
    case csmtSector:
      header = "  Sector '%s' (\n";
      break;
    case csmtThing:
      header = "  THING '%s' (\n";
      break;
    case csmtSprite:
      header = "  SPRITE '%s' (\n";
      break;
    default:
      return false;
  } /* endswitch */

  char buff [512];
  sprintf (buff, header, Name);
  PUTS (ar, buff);

  int i, j;
  char tmp[30];
//    mz3DPolygon &p ;

  /* Strange kludge - the old way of generating vertex data was removing the
     last digit on the output string. This is an obscure work-around to fix it. */
#define removezero(dest, src)                                        \
  sprintf (tmp, "%.2f", src);                                          \
  while (tmp[strlen(tmp)-1] == '0')                                  \
    tmp[strlen(tmp)-1] = 0;                                          \
  if (tmp[strlen(tmp)-1] == '.') tmp[strlen(tmp)-1] = 0;             \
  strcat (dest,tmp);

  mz3DPolygon &p = Polygon (0);
  csTextureHandle *tp = (p.Texture);
  csSector*  sec;
//  csThing*  thg;

  switch (Type)
  {
    case csmtRoom:
	strcpy (buff, "    FLOOR_CEIL ((");
	removezero(buff, Vertex(0).x)
	strcat (buff,",");
	removezero (buff, Vertex(0).z)
    strcat (buff, ")(");
	removezero(buff, Vertex(2).x)
	strcat (buff,",");
	removezero (buff, Vertex(1).z)
    strcat (buff, ")(");
	removezero(buff, Vertex(1).x)
	strcat (buff,",");
	removezero (buff, Vertex(2).z)
    strcat (buff, ")(");
	removezero(buff, Vertex(3).x)
	strcat (buff,",");
	removezero (buff, Vertex(3).z)
	strcat (buff, "))\n");
	PUTS(ar, buff);
	memset (buff, 0, sizeof (buff));

	strcpy (buff, "    FLOOR_HEIGHT (");
	removezero(buff, Vertex(0).y)
    strcat (buff, ")\n");
	PUTS(ar, buff);
	memset (buff, 0, sizeof (buff));

	strcpy (buff, "    HEIGHT (");
	removezero(buff, Vertex(4).y)
    strcat (buff, ")\n");
	PUTS(ar, buff);
	memset (buff, 0, sizeof (buff));

	sprintf (buff, "        TEXTURE ('%s')\n",csNameObject::GetName(*tp));
	PUTS (ar, buff);
	sprintf (buff, " TEX'down'(TEXTURE ('%s'))\n",csNameObject::GetName(*tp));
	PUTS (ar, buff);
	sprintf (buff, " TEX'up' (TEXTURE ('%s'))\n",csNameObject::GetName(*tp));
	PUTS (ar, buff);

	break;

    case csmtSector:
    // handling of thing output inside a sector
      sec = (csSector*)S[ sector_ind ];
//  System->Printf (MSG_DEBUG_0, "Saving sector %d \n", sector_ind);      

/*      for (thg = sec->GetFirstThing(); thg!=NULL; thg = (csThing *)thg->GetNext()) {
  System->Printf (MSG_DEBUG_0, "sector %d with thing %s\n", sector_ind, csNameObject::GetName(*thg));      
	sprintf(buff, "THING '%s' ( TEMPLATE (%s))\n", 
	  csNameObject::GetName(*thg), csNameObject::GetName(*(thg->ParentTemplate)));
	PUTS(ar, buff);
      }*/
    case csmtThing:
      strcpy (buff, "    TEXLEN (4)\n");
      PUTS (ar, buff);
      for (i = 0; i < Vertices (); i++)
      {
	memset (buff, 0, sizeof (buff));
	strcpy (buff, "    VERTEX (");
	removezero (buff, Vertex(i).x)
	strcat (buff,",");
	removezero (buff, Vertex(i).y)
	strcat (buff,",");
	removezero (buff, Vertex(i).z)
        strcat (buff, ")\n");
	PUTS (ar, buff);
      } /* endfor */
      for (i = 0; i < Polygons (); i++)
      {
        mz3DPolygon &p = Polygon (i);

        sprintf (buff, "    POLYGON '%s' (\n        VERTICES (", p.Name);
		int len = p.Vertices();
        for (j = 0; j < len; j++)
        {
          char *format;
          if (j == 0)
            format = "%d";
          else
            format = ",%d";
		  if (Type == csmtSector)
			sprintf (tmp, format, p [j]);
		  else
  			sprintf (tmp, format, p [len-j-1]);
          strcat (buff, tmp);
        } /* vertex endfor */
        strcat (buff, ")\n");
		PUTS (ar, buff);
		// Handle portal
		if (p.portal != NULL)
		{
			sprintf (buff, " PORTAL (%s)", p.portal);
			PUTS (ar, buff);
			if (p.SpaceWarp != NULL)
			{
				csMatrix3 m = p.SpaceWarp->GetO2T ();
				csVector3 v = p.SpaceWarp->GetO2TTranslation ();
				sprintf (buff, "\n        WARP (MATRIX (");
				removezero (buff, m.m11);
				strcat (buff,",");
				removezero (buff, m.m12);
				strcat (buff,",");
				removezero (buff, m.m13);
				strcat (buff,",");
				removezero (buff, m.m21);
				strcat (buff,",");
				removezero (buff, m.m22);
				strcat (buff,",");
				removezero (buff, m.m23);
				strcat (buff,",");
				removezero (buff, m.m31);
				strcat (buff,",");
				removezero (buff, m.m32);
				strcat (buff,",");
				removezero (buff, m.m33);
				strcat (buff,") V (");
				removezero (buff, v.x);
				strcat (buff,",");
				removezero (buff, v.y);
				strcat (buff,",");
				removezero (buff, v.z);
				strcat (buff,"))\n");
	    
				PUTS (ar, buff);
				// Temporary hack
				PUTS (ar, "        ALPHA (50)\n)\n");
			}
			else {
				if (p.mirror) {
					sprintf(buff, ")  WARP (MIRROR()) ALPHA (");
					removezero(buff, p.alpha);
					strcat(buff, ")");
					PUTS(ar, buff);
				}
			// end the portal expression
			//  sprintf (buff, " )\n");
			//  PUTS (ar, buff);
			}
		} // end if

	csTextureHandle *tp = (p.Texture);
	sprintf (buff, "        TEXNR ('%s')\n",csNameObject::GetName(*tp));
	PUTS (ar, buff);

        strcpy (buff, "    )\n");
        PUTS (ar, buff);
      } /* polygons endfor */

    // Save Lights
	  if (Type == csmtSector)
	// we save the lighting information which belong to that sector
    for (i = 0; i < Lights (); i++)
    {
		mz3DLight &l = Light (i);

		strcpy (buff, "    LIGHT (");
		removezero (buff, l.Position.x)
		strcat (buff,",");
		removezero (buff, l.Position.y)
		strcat (buff,",");
		removezero (buff, l.Position.z)
		strcat (buff,":");
		removezero (buff, l.Radius)
		strcat (buff,",");
		removezero (buff, l.Color.red)
		strcat (buff,",");
		removezero (buff, l.Color.green)
		strcat (buff,",");
		removezero (buff, l.Color.blue)
        sprintf (tmp, ",%d)\n",l.Dynamic);
		strcat (buff, tmp);
		PUTS (ar, buff);
      }
      break;
    case csmtSprite:
      if (Texture)
        sprintf (buff, "    TEXNR ('%s')\n", csNameObject::GetName(*Texture));
      PUTS (ar, buff);
      for (i = 0; i < Frames (); i++)
      {
        sprintf (buff, "    FRAME '%s' (", GetFrameName (i));
        PUTS (ar, buff);
        for (j = 0; j < Vertices (); j++)
        {
          if (j)
            strcpy (buff, " V(");
          else
            strcpy (buff, "V(");
          ftoa (&buff [strlen (buff)], Vertex (j, i).x);
          strcat (buff, ",");
          ftoa (&buff [strlen (buff)], Vertex (j, i).y);
          strcat (buff, ",");
          ftoa (&buff [strlen (buff)], Vertex (j, i).z);
          strcat (buff, ":");
          ftoa (&buff [strlen (buff)], Vertex (j, i).y);
          strcat (buff, ",");
          ftoa (&buff [strlen (buff)], Vertex (j, i).y);
          strcat (buff, ")");
          PUTS (ar, buff);
        } /* endfor */
        PUTS (ar, ")\n");
      } /* endfor */
      for (i = 0; i < Actions (); i++)
      {
        mzSpriteAction &a = Action (i);
        sprintf (buff, "    ACTION '%s' (", a.Name);
        PUTS (ar, buff);
        for (j = 0; j < a.Frames (); j++)
        {
          sprintf (buff, "F(%s,%d) ", a.FrameName (j), a.Delay (j));
          if (j == a.Frames () - 1)
            buff [strlen (buff) - 1] = 0;
          PUTS (ar, buff);
        } /* endfor */
        PUTS (ar, ")\n");
      } /* endfor */
      for (i = 0; i < Polygons (); i++)
      {
        mz3DPolygon &p = Polygon (i);
        sprintf (buff, "    TRIANGLE (%d,%d,%d)\n", p [0], p [1], p [2]);
        PUTS (ar, buff);
      } /* endfor */
      break;
    default:
      break;
  } /* endswitch */

  PUTS (ar, "  )\n\n");
  return true;
}

void mz3DModel::Draw (mzCameraView *Canvas)
{
#if 0
  csVector3* old_tr3;
  int num = Polygons();
  // Private integer 2D point structure
  struct Point { int x, y, xm, ym; bool selected; };
  int i, j, k;

  switch (Type) {
  case csmtSector:
//  System->Printf (MSG_DEBUG_0, "Drawing sector\n");      
      break;
  case csmtThing:
//  System->Printf (MSG_DEBUG_0, "Drawing thing\n");      
      break;
  case csmtSprite:
//  System->Printf (MSG_DEBUG_0, "Drawing sprite\n");      
      break;
  default:
      // error, return to calling function 
      return;
  }
  csRenderView* d = Canvas->rview;
  csPolygon3D* p;
  G3DPolygonDP g3dpoly;
  csPortal* po;
  csVector3* verts;
  csVector2* vertices;
  int num_verts;
  bool use_z_buf = true;
  csSector *curr_sector = NULL;
  csThing *curr_thing = NULL;
  csSprite3D *curr_sprite = NULL;
  CHK (Point *vert = new Point [Vertices ()]);
  CHK (vertices = new csVector2 [200]);

  //Dumper::dump(Canvas->cam);

  int offset_x = 0, offset_y = 0;
  csVector2* orig_triangle;

  // determine the nature of the object going to be rendered
  switch (Type) {
  case csmtSector:
    curr_sector = (csSector*)S[sector_ind]; 
    break;
  case csmtThing:
    curr_thing = (csThing*)T[thing_ind]; 
    break;
  case csmtSprite:
    curr_sprite = (csSprite3D*)Sp[sprite_ind]; 
    break;
  }
  Canvas->LocalToGlobal(offset_x, offset_y);
//  System->Printf (MSG_DEBUG_0, "canvas xmin %d ymin %d\n", offset_x, offset_y);      
  // hack to place the camera window at right bottom
  offset_y = 0;
  for (i = 0 ; i < num ; i++)
  {
    // Dumper::dump(sec); 
//    System->Printf (MSG_DEBUG_0, "Drawing polygon %d\n", i);
    p = (csPolygon3D*)Polygon(i).csp;
    switch (Type) {
    case csmtSector:
      curr_sector->NewTransformation (old_tr3); 
      curr_sector->TransformWorld2Cam (*d);
      break;
    case csmtThing:
      curr_thing->NewTransformation (old_tr3); 
      curr_thing->TransformWorld2Cam (*d);
      break;
    case csmtSprite:
      // TODO should do transformation accordingly 
      break;
    }
    curr_sector = Polygon( i ).sector;
    
// perform debug dump
//    if (i == 0)     { Dumper::dump(sec); }
    num_verts = p->GetNumVertices();
    if (p->GetUVCoords () || p->UseFlatColor ())
    {
      orig_triangle = new csVector2 [3];
      //System->Printf (MSG_DEBUG_0, "use flat color or UV coord\n", i);
      //@@@ NOT OPTIMAL AT ALL!
      //@@@@@@@@
      float iz;
      if (p->Vcam (0).z) iz = Canvas->cam->aspect/p->Vcam (0).z;
      else iz = 1000000.;
  //System->Printf (MSG_DEBUG_0, "canvas camera aspect %f p->Vcam (0).z %f\n", Canvas->cam->aspect, p->Vcam (0).z);      
  //System->Printf (MSG_DEBUG_0, "p->Vcam(0).x  %f p->Vcam(0).y %f iz %f\n", p->Vcam(0).x, p->Vcam(0).y, iz);      
      Canvas->world2win (p->Vwor(0), vert [0].x,  vert[0].y);
      //orig_triangle[0].x = (float)vert [0].x;
      //orig_triangle[0].y = (float)vert [0].y;
      // we convert to screen coordinates and then map it into same dimensions as window
      orig_triangle[0].x = Canvas->bound.Width() * (p->Vcam (0).x * iz + csWorld::shift_x) / csWorld::frame_width + offset_x;
      orig_triangle[0].y = Canvas->bound.Height() * (p->Vcam (0).y * iz + csWorld::shift_y) / csWorld::frame_height  + offset_y;
      if (p->Vcam (1).z) iz = Canvas->cam->aspect/p->Vcam (1).z;
      else iz = 1000000.;
  //System->Printf (MSG_DEBUG_0, "p->Vcam(1).x  %f p->Vcam(1).y %f \n", p->Vcam(1).x, p->Vcam(1).y);      
      Canvas->world2win (p->Vwor(1), vert [1].x,  vert[1].y);
      //orig_triangle[1].x = (float)vert [1].x;
      //orig_triangle[1].y = (float)vert [1].y;
      orig_triangle[1].x = Canvas->bound.Width() * (p->Vcam (1).x * iz + csWorld::shift_x) / csWorld::frame_width  + offset_x;
      orig_triangle[1].y = Canvas->bound.Height() * (p->Vcam (1).y * iz + csWorld::shift_y) / csWorld::frame_height  + offset_y;
      if (p->Vcam (2).z) iz = Canvas->cam->aspect/p->Vcam (2).z;
      else iz = 1000000.;
 // System->Printf (MSG_DEBUG_0, "p->Vcam(2).x  %f p->Vcam(2).y %f \n", p->Vcam(2).x, p->Vcam(2).y);      
      Canvas->world2win (p->Vwor(2), vert [2].x,  vert[2].y);
      //orig_triangle[2].x = (float)vert [2].x;
      //orig_triangle[2].y = (float)vert [2].y;
      orig_triangle[2].x = Canvas->bound.Width() * (p->Vcam (2).x * iz + csWorld::shift_x) / csWorld::frame_width  + offset_x;
      orig_triangle[2].y = Canvas->bound.Height() * (p->Vcam (2).y * iz + csWorld::shift_y) / csWorld::frame_height  + offset_y;
  //System->Printf (MSG_DEBUG_0, "orig triangle[0]  %f %f \n", orig_triangle[0].x , orig_triangle[0].y );
  //System->Printf (MSG_DEBUG_0, "orig triangle[1]  %f %f \n", orig_triangle[1].x , orig_triangle[1].y );
  //System->Printf (MSG_DEBUG_0, "orig triangle[2]  %f %f \n", orig_triangle[2].x , orig_triangle[2].y );
    }

    float iz;
//  System->Printf (MSG_DEBUG_0, "csWorld::shift_x %f csWorld::shift_y %f\n", csWorld::shift_x, csWorld::shift_y );
//  System->Printf (MSG_DEBUG_0, "Number of vertices in this polygon %d\n", num_verts);

    for (j=0; j<num_verts; j++) {
      // compute the perspective project vertices
      // currently we draw it in initialized screen coordinate, later
      // should reshape it to camera view space
      if (p->Vcam (j).z) iz = Canvas->cam->aspect/p->Vcam (j).z;
      else iz = 1000000.;
      vertices[j].x = Canvas->bound.Width() * (p->Vcam (j).x * iz + csWorld::shift_x) / csWorld::frame_width  + offset_x;
      vertices[j].y = Canvas->bound.Height() * (p->Vcam (j).y * iz + csWorld::shift_y) / csWorld::frame_height + offset_y;
//  System->Printf (MSG_DEBUG_0, "vertices[j].x %f vertices[j].y %f\n", vertices[j].x , vertices[j].y );
    }
//    if ( !p->dont_draw &&
 //      p->ClipToPlane (d->do_clip_plane ? &d->clip_plane : (csPlane*)NULL, d->GetOrigin (),
//	 	verts, num_verts) && 
  //       p->DoPerspective (*d, verts, num_verts, &csPolygon2D::clipped, orig_triangle, d->IsMirrored ())  &&
    //     csPolygon2D::clipped.ClipAgainst (d->view) )
    {

//  System->Printf (MSG_DEBUG_0, "trying to get portal\n", i);
      po = p->GetPortal ();
      if (csSector::do_portals && po)
      {
	bool filtered = false;
	// is_this_fog is true if this sector is fogged.
	bool is_this_fog = curr_sector->HasFog ();

        // If there is filtering (alpha mapping or something like that) we need to keep the
        // 2D polygon and texture plane so that it can be drawn after the sector has been drawn.
        // We can't just use 'clipped' because calling Portal->Draw will (possibly)
        // reuse the clipped global variable.
        // The texture plane needs to be kept because this polygon may be rendered again
        // (through mirrors) possibly overwriting the plane.
        csPolygon2D* keep_clipped = NULL;
        csPolyPlane* keep_plane = NULL;

        long do_transp;
        d->g3d->GetRenderState (G3DRENDERSTATE_TRANSPARENCYENABLE, do_transp);
	if (do_transp)
          filtered = p->IsTransparent ();
              
        if (filtered || is_this_fog)
        {
          CHK (keep_clipped = new csPolygon2D (csPolygon2D::clipped));
          CHK (keep_plane = new csPolyPlane (*(p->GetPlane ())));
        }
              
        // Draw through the portal. If this fails we draw the original polygon
        // instead. Drawing through a portal can fail because we have reached
        // the maximum number that a sector is drawn (for mirrors).
	// Note that use_z_buf is given to this routine as well but this
	// parameter has nothing to do with the Z buffer but with the fact that
	// we have a portal in the middle of a sector. use_z_buf happens to be
	// true for Things and false for sectors so this works now.
        if (po->Draw (&csPolygon2D::clipped, &p->GetPlane ()->GetCameraPlane (), use_z_buf, *d))
        {
	  if (filtered)
	    keep_clipped->DrawFilled (d->g3d, p, keep_plane, d->IsMirrored (),
		use_z_buf, orig_triangle);
//	  System->Printf (MSG_DEBUG_0, "drawing keep clipped draw-filled \n");
	  if (is_this_fog) keep_clipped->AddFogPolygon (d->g3d, p, keep_plane, d->IsMirrored (),
		curr_sector->GetID (), CS_FOG_BACK);
        }
        else {
//	  System->Printf (MSG_DEBUG_0, "drawing no clipped draw-filled \n");
	  csPolygon2D::clipped.DrawFilled (d->g3d, p, p->GetPlane (), d->IsMirrored (),
		use_z_buf, orig_triangle);
	}
        // Cleanup.
        if (keep_clipped)
        {
          CHK (delete keep_clipped);
          CHK (delete keep_plane);
        }
      }
      else {
//	System->Printf (MSG_DEBUG_0, "drawing usual / clipped draw-filled \n");

//       csPolygon2D::clipped.DrawFilled (d->g3d, p, p->GetPlane (), d->IsMirrored (),
//     	  use_z_buf, orig_triangle);
//  if (use_z_buf)
// This is a test version of csPolygon2D::clipped.DrawFilled 
{
  System->piG3D->SetRenderState (G3DRENDERSTATE_ZBUFFERTESTENABLE, true);
  System->piG3D->SetRenderState (G3DRENDERSTATE_ZBUFFERFILLENABLE, true);
  memset (&g3dpoly, 0, sizeof (G3DPolygon));
  g3dpoly.num = num_verts;
  g3dpoly.txt_handle = p->GetTextureHandle ();
  g3dpoly.inv_aspect = csCamera::inv_aspect;

  if (p->GetUVCoords () || p->UseFlatColor ())
  {
//   System->Printf (MSG_DEBUG_0, "UV coord or Flat color!\n");
    csColor* po_colors = p->GetColors ();
    g3dpoly.flat_color_r = p->GetFlatColor ().red;
    g3dpoly.flat_color_g = p->GetFlatColor ().green;
    g3dpoly.flat_color_b = p->GetFlatColor ().blue;
    if (p->UseFlatColor ()) g3dpoly.txt_handle = NULL;

    // We are going to use DrawPolygonQuick.
    g3dpoly.pi_triangle = orig_triangle;
    g3dpoly.pi_tritexcoords[0].z = 1. / p->Vcam (0).z;
    g3dpoly.pi_tritexcoords[1].z = 1. / p->Vcam (1).z;
    g3dpoly.pi_tritexcoords[2].z = 1. / p->Vcam (2).z;
    if (g3dpoly.txt_handle)
    {
     g3dpoly.pi_tritexcoords[0].u = p->GetUVCoords ()[0].x;
      g3dpoly.pi_tritexcoords[0].v = p->GetUVCoords ()[0].y;
      g3dpoly.pi_tritexcoords[1].u = p->GetUVCoords ()[1].x;
      g3dpoly.pi_tritexcoords[1].v = p->GetUVCoords ()[1].y;
      g3dpoly.pi_tritexcoords[2].u = p->GetUVCoords ()[2].x;
      g3dpoly.pi_tritexcoords[2].v = p->GetUVCoords ()[2].y;
    }
    if (po_colors)
    {
//  System->Printf (MSG_DEBUG_0, "Colors are being copied\n");

      g3dpoly.pi_tritexcoords[0].r = po_colors[0].red;
      g3dpoly.pi_tritexcoords[0].g = po_colors[0].green;
      g3dpoly.pi_tritexcoords[0].b = po_colors[0].blue;
      g3dpoly.pi_tritexcoords[1].r = po_colors[1].red;
      g3dpoly.pi_tritexcoords[1].g = po_colors[1].green;
      g3dpoly.pi_tritexcoords[1].b = po_colors[1].blue;
      g3dpoly.pi_tritexcoords[2].r = po_colors[2].red;
      g3dpoly.pi_tritexcoords[2].g = po_colors[2].green;
      g3dpoly.pi_tritexcoords[2].b = po_colors[2].blue;
    }
    for (k = 0 ; k < num_verts ; k++)
    {
      g3dpoly.vertices[k].sx = vertices[k].x;
      g3dpoly.vertices[k].sy = vertices[k].y;
    }
    CHK (g3dpoly.pi_texcoords = new G3DPolygon::poly_texture_def [64]);
    PreparePolygonQuick (&g3dpoly, po_colors != NULL);
    // send to graphics pipeline
    Canvas->Polygon(g3dpoly, po_colors != NULL);
/*    if (System->piG3D->BeginDraw (CSDRAW_3DGRAPHICS) != S_OK) return;
    PreparePolygonQuick (&g3dpoly, po_colors != NULL);
    System->piG3D->StartPolygonQuick (g3dpoly.txt_handle, po_colors != NULL);
    System->piG3D->DrawPolygonQuick (g3dpoly, po_colors != NULL);
    System->piG3D->FinishPolygonQuick ();
    System->piG3D->FinishDraw();
    CHK (delete [] g3dpoly.pi_texcoords);*/
  }
}
      }

    }
    switch (Type) {
    case csmtSector:
      curr_sector->RestoreTransformation (old_tr3); 
      break;
    case csmtThing:
      curr_thing->RestoreTransformation (old_tr3); 
      break;
    case csmtSprite:
      // TODO should do transformation accordingly 
      break;
    }
  }
  CHK (delete [] vert);
#else
  (void)Canvas;
#endif
}

void mz3DModel::Draw (mzDraftEditor *Canvas, bool iActive, bool iModified,
  bool iCopy)
{
  // Private integer 2D point structure
  struct Point { int x, y, xm, ym; bool selected; };
  int i, j;
  CHK (Point *vert = new Point [Vertices ()]);

  if (iModified)
  {
    for (i = 0; i < Vertices (); i++)
      vert [i].selected = Vertex (i).selected;
    for (i = 0; i < Polygons (); i++)
    {
      mz3DPolygon &p = Polygon (i);
      if (p.selected)
        for (j = p.Vertices () - 1; j >= 0; j--)
          vert [p [j]].selected = true;
    } /* endfor */
  } /* endif */

  // First project all vertices of the model
  for (i = 0; i < Vertices (); i++)
  {
    Canvas->world2win (Vertex (i), vert [i].x, vert [i].y);
    if (iModified && vert [i].selected)
      Canvas->world2winM (Vertex (i), vert [i].xm, vert [i].ym);
    else
    {
      vert [i].xm = vert [i].x;
      vert [i].ym = vert [i].y;
    } /* endif */
  }

  // First project and paint all vertices of the model
  csVector2 poly [MAX_POLYGON_VERTICES];
  csVector2 polym [MAX_POLYGON_VERTICES];
  bool drawsel = false;
  do
  {
    for (i = 0; i < Polygons (); i++)
    {
      mz3DPolygon &CurrentPoly = Polygon (i);
      if (drawsel != CurrentPoly.selected)
        continue;

      int vert_in_poly = CurrentPoly.Vertices ();
      for (j = 0; j < vert_in_poly; j++)
      {
        Point &p = vert [CurrentPoly [j]];
        poly [j].Set (p.x, p.y);
        polym [j].Set (p.xm, p.ym);
      } /* endfor */

      int color;
      if (iActive)
      {
        if (CurrentPoly.selected)
          color = Canvas->palette [CSPAL_DRAFT_SELPOLY];
        else
        {
          color = CurrentPoly.Color;
          if (color < 0)
            color = Color;
          if (color < 0)
            color = Canvas->palette [CSPAL_DRAFT_ACTPOLY];
        }
      }
      else
        color = Canvas->palette [CSPAL_DRAFT_POLY];
      Canvas->SetColor (CSPAL_DRAFT_SCRATCHPAD, color);

      for (j = 0; j < vert_in_poly; j++)
      {
        int prev = (j == 0 ? vert_in_poly - 1 : j - 1);
        if (!iModified || !iActive) {
          Canvas->Line ((int)poly [prev].x, (int)poly [prev].y,
            (int)poly [j].x, (int)poly [j].y, CSPAL_DRAFT_SCRATCHPAD);
	} else
        {
          if (iCopy)
            Canvas->Line ((int)poly [prev].x, (int)poly [prev].y,
              (int)poly [j].x, (int)poly [j].y, CSPAL_DRAFT_ACTPOLY);
          int linecolor = CSPAL_DRAFT_SCRATCHPAD;
          if (iActive
           && (vert [CurrentPoly [j]].selected
            || vert [CurrentPoly [prev]].selected))
            linecolor = CSPAL_DRAFT_MODIFIED;
          Canvas->Line ((int)polym [prev].x, (int)polym [prev].y,
            (int)polym [j].x, (int)polym [j].y, linecolor);
        } /* endif */
      } /* endfor */
    } /* endfor */
    drawsel = !drawsel;
  } while (drawsel); /* enddo */

  // And now paint all vertices of the model
  for (i = 0; i < Vertices (); i++)
  {
    int x = vert [i].x, y = vert [i].y;
    int xm = vert [i].xm, ym = vert [i].ym;
    int col = iActive ? (Vertex (i).selected ? CSPAL_DRAFT_SELVERTEX
                                             : CSPAL_DRAFT_VERTEX)
                      : CSPAL_DRAFT_INACTVERTEX;
    if (!iModified || !iActive)
      if (F_DRAFT_CROSSVERTEX)
      {
        Canvas->Line (x-1, y-1, x+1, y+1, col);
        Canvas->Line (x+1, y-1, x-1, y+1, col);
      }
      else
        Canvas->Line (x, y, x, y, col);
    else
    {
      if (iCopy)
        if (F_DRAFT_CROSSVERTEX)
        {
          Canvas->Line (x-1, y-1, x+1, y+1, CSPAL_DRAFT_VERTEX);
          Canvas->Line (x+1, y-1, x-1, y+1, CSPAL_DRAFT_VERTEX);
        }
        else
          Canvas->Line (x, y, x, y, CSPAL_DRAFT_VERTEX);
      if (F_DRAFT_CROSSVERTEX)
      {
        Canvas->Line (xm-1, ym-1, xm+1, ym+1, CSPAL_DRAFT_VERTEX);
        Canvas->Line (xm+1, ym-1, xm-1, ym+1, CSPAL_DRAFT_VERTEX);
      }
      else
        Canvas->Line (xm, ym, xm, ym, col);
    } /* endif */
  } /* endfor */
  CHK (delete [] vert);
}

void mz3DModel::Modify (mzDraftEditor *Canvas, bool iCopy)
{
  // Private integer 2D point structure
  int i, j;
  CHK (bool *vert = new bool [Vertices ()]);

  // Find which vertices should be modified
  for (i = 0; i < Vertices (); i++)
    vert [i] = Vertex (i).selected;
  for (i = 0; i < Polygons (); i++)
  {
    mz3DPolygon &p = Polygon (i);
    if (p.selected)
      for (j = p.Vertices () - 1; j >= 0; j--)
        vert [p [j]] = true;
  } /* endfor */

  if (iCopy)
  {
    CHK (int *vertidx = new int [Vertices ()]);
    int nvert = Vertices ();
    for (i = 0; i < nvert; i++)
      if (vert [i])
      {
        mz3DVertex *v = new mz3DVertex (Vertex (i));
        Vertex (i).selected = false;
        Canvas->Modify (*v);
        InsertVertex (v, &vertidx [i]);
        Vertex (vertidx [i]).selected = true;
      }
      else
        vertidx [i] = i;
    int npoly = Polygons ();
    for (i = 0; i < npoly; i++)
      if (Polygon (i).selected)
      {
        mz3DPolygon *p = new mz3DPolygon (Polygon (i));
        Polygon (i).selected = false;
        for (j = 0; j < p->Vertices (); j++)
          (*p) [j] = vertidx [(*p) [j]];
        int polyidx;
        InsertPolygon (p, &polyidx);
        Polygon (polyidx).selected = true;
      } /* endfor */
    CHK (delete [] vertidx);
    CHK (delete [] vert);
    return;
  } /* endif */

  // Modify all selected vertices of the model
  for (i = 0; i < Vertices (); i++)
    if (vert [i])
      Canvas->Modify (Vertex (i));
  CHK (delete [] vert);
}

bool mz3DModel::HasSelection ()
{
  int i;
  // Find which vertices should be modified
  for (i = 0; i < Vertices (); i++)
    if (Vertex (i).selected)
      return true;
  for (i = 0; i < Polygons (); i++)
    if (Polygon (i).selected)
      return true;
  return false;
}

/*
bool mz3DModel::Load (csLight* iLight) {
  Clear (true, true, true, true, true);
  char name[256];
  strncpy (name, iLight->GetName (), sizeof (name));
  int i, sl = strlen (name) - 1;
  for (i = sl; i >= 0; i--)
    if ((name [i] >= '0') && (name [i] <= '9'))
      sl = i;
    else if (name [i] == '#')
    {
      name [i] = 0;
      break;
    }
    else
      break;
  SetName (name);
  return Join (iLight, true);
};

bool mz3DModel::Join (csLight* iLight)
{
  mz3DLight temp;

  temp.Color.Set (iLight.red, iLight.green, iLight.blue);
  temp.Position = iLight.GetCenter ();
  temp.
  int i, j;
  int *vert = new int [iThing->GetNumVertices ()];
  for (i = 0; i < iThing->get_num_vertices (); i++)
  {
    CHK (mz3DVertex *v = new mz3DVertex (iThing->vtex (i)));
    InsertVertex (v, &vert [i]);
  } 
  for (i = 0; i < iThing->GetNumPolygons (); i++)
  {
    csPolygonTemplate *pt = iThing->GetPolygon (i);
    CHK (mz3DPolygon *p = new mz3DPolygon (this));
    p->SetName (pt->get_name ());
    // Copy the world source Thing polygon to dest Polygon
    // Vertices:
    for (j = 0; j < pt->get_num_vertices (); j++)
      (*p) [j] = vert [pt->get_vertices_idx () [j]];

    // Texture:
    if (pt->get_texture ())
      p->Texture = new csTextureHandle (*(pt->get_texture ()));

    // Insert polygon into 3d model's list
    InsertPolygon (p);

  }

  CHK (delete [] vert);
  return true;
}
*/
bool mz3DModel::Load (csSector* iSector)
{
  Clear (true, true, true, true, true);
  char name [256];
  strncpy (name, csNameObject::GetName(*iSector), sizeof (name));
  InsertSector(iSector);
  int i, sl = strlen (name) - 1;
  for (i = sl; i >= 0; i--)
    if ((name [i] >= '0') && (name [i] <= '9'))
      sl = i;
    else if (name [i] == '#')
    {
      name [i] = 0;
      break;
    }
    else
      break;
  SetName (name);
  return Join (iSector, true);
}

bool mz3DModel::Join (csSector *iSector, bool iForce)
{
  if (!iForce)
  {
    // Check if this sector is a part of this room
    const char *SectName = csNameObject::GetName(*iSector);
    int nl = strlen (Name);
    int sl = strlen (SectName);
    if (nl > sl)
      return false;			// csSector name should be based on Name
    if (strncmp (Name, SectName, nl) != 0)
      return false;			// Not a subset of this room
    if (SectName [nl] != '#')
      return false;			// No '#' after basename
    while (++nl < sl)
      if ((SectName [nl] < '0') || (SectName [nl] > '9'))
        return false;
  } /* endif */

  int i, j;
  int *vert = new int [iSector->GetNumVertices ()];
  for (i = 0; i < iSector->GetNumVertices (); i++)
  {
    CHK (mz3DVertex *v = new mz3DVertex (iSector->Vobj (i)));
    InsertVertex (v, &vert [i]);
  } /* endfor */
  for (i = 0; i < iSector->GetNumPolygons (); i++)
  {
    csPolygon3D *pt = (csPolygon3D *)iSector->GetPolygon (i);
    CHK (mz3DPolygon *p = new mz3DPolygon (this));
    p->SetName (csNameObject::GetName(*pt));
    for (j = 0; j < pt->GetVertices ().GetNumVertices (); j++)
      (*p) [j] = vert [pt->GetVertices ().GetVertexIndices () [j]];

    // Texture:
    if (pt->GetTexture ())      
      p->Texture = new csTextureHandle (*(pt->GetTexture ()));

    // Portal:
    if (pt->GetPortal ())
    {
      CHK (p->portal = strnew (csNameObject::GetName(*(pt->GetPortal()->GetSector()))));
      // SpaceWarp:
      if (pt->GetPortal ()->IsSpaceWarped ())
      {
        CHK (p->SpaceWarp = new csReversibleTransform ());
	bool warp = false;
	pt->GetPortal ()->WarpSpace(*(p->SpaceWarp), warp);
      }
	  
    }
    InsertPolygon (p);
  } /* endfor */

  for (i = 0; i < iSector->lights.Length (); i++) {
    csStatLight *lg = (csStatLight *)iSector->lights [i];
    CHK (mz3DLight *l = new mz3DLight (this));
    char tmp [50];
    sprintf (tmp, "S%sL%d", Name, i);
    l->SetName (tmp);
    l->Color = lg->GetColor ();
    l->Position = lg->GetCenter ();
    l->Radius = lg->GetRadius ();
    InsertLight (l);
  }
  return true;
}

bool mz3DModel::Load (csThingTemplate* iThing)
{
  Clear (true, true, true, true, true);
  SetName (csNameObject::GetName(*iThing));
  return Join (iThing);
}

bool mz3DModel::Join (csThingTemplate* iThing)
{
  int i, j;
  int *vert = new int [iThing->GetNumVertices ()];
  for (i = 0; i < iThing->GetNumVertices (); i++)
  {
    CHK (mz3DVertex *v = new mz3DVertex (iThing->Vtex (i)));
    InsertVertex (v, &vert [i]);
  } /* endfor */
  for (i = 0; i < iThing->GetNumPolygon (); i++)
  {
    csPolygonTemplate *pt = iThing->GetPolygon (i);
    CHK (mz3DPolygon *p = new mz3DPolygon (this));
    p->SetName (pt->GetName ());
    // Copy the world source Thing polygon to dest Polygon
    // Vertices:
    int len = pt->GetNumVertices ();
    for (j = 0; j < len; j++)
      (*p) [j] = vert [pt->GetVerticesIdx () [len - j - 1]];

    // Texture:
    if (pt->GetTexture ())
      p->Texture = new csTextureHandle (*(pt->GetTexture ()));

    // Insert polygon into 3d model's list
    InsertPolygon (p);

  } /* endfor */

  CHK (delete [] vert);
  return true;
}

bool mz3DModel::Load (csSpriteTemplate *iSprite)
{
  Clear (true, true, true, true, true);
  SetName (csNameObject::GetName(*iSprite));
  SetTexture (iSprite->GetTexture ());
  return Join (iSprite);
}

bool mz3DModel::Join (csSpriteTemplate *iSprite)
{
  int *vert = new int [iSprite->GetNumVertices ()];
  for (int frame = 0; frame < iSprite->GetNumFrames (); frame++)
  {
    csFrame* f = iSprite->GetFrame (frame);
    SetFrame (frame, f->GetName ());
    for (int vertex = 0; vertex < iSprite->GetNumVertices (); vertex++)
    {
      if (frame == 0)
      {
        CHK (mz3DVertex *v = new mz3DVertex (f->GetVertex (vertex)));
        InsertVertex (v, &vert [vertex]);
      }
      else
        Vertex (vert [vertex]) = f->GetVertex (vertex);
      Vertex (vert [vertex]).SetTexel (f->GetTexel (vertex));
    } /* endfor */
  } /* endfor */

  csTriangleMesh* mesh = iSprite->GetBaseMesh ();
  csTriangle* triangles = mesh->GetTriangles ();
  for (int triangle = 0; triangle < mesh->GetNumTriangles (); triangle++)
  {
    CHK (mz3DPolygon *p = new mz3DPolygon (this));
    p->SetName (NULL);
    (*p) [0] = vert [triangles [triangle].a];
    (*p) [1] = vert [triangles [triangle].b];
    (*p) [2] = vert [triangles [triangle].c];
    InsertPolygon (p, NULL);
  } /* endfor */
  CHK (delete [] vert);
  SetFrame (0);
  return true;
}

void mz3DModel::SelectAllVertices (int iCode)
{
  for (int i = Vertices () - 1; i >= 0; i--)
  {
    switch (iCode)
    {
      case cscmdMzVertexSelect:
      case cscmdMzVertexSelectAll:
        Vertex (i).selected = true;
        break;
      case cscmdMzVertexDeselect:
      case cscmdMzVertexDeselectAll:
        Vertex (i).selected = false;
        break;
      case cscmdMzVertexInvert:
      case cscmdMzVertexInvertAll:
        Vertex (i).selected = !Vertex (i).selected;
        break;
    } /* endswitch */
  } /* endfor */
}

void mz3DModel::SelectAllPolygons (int iCode)
{
  for (int i = Polygons () - 1; i >= 0; i--)
  {
    switch (iCode)
    {
      case cscmdMzPolySelect:
      case cscmdMzPolySelectAll:
        Polygon (i).selected = true;
        break;
      case cscmdMzPolyDeselect:
      case cscmdMzPolyDeselectAll:
        Polygon (i).selected = false;
        break;
      case cscmdMzPolyInvert:
      case cscmdMzPolyInvertAll:
        Polygon (i).selected = !Polygon (i).selected;
        break;
    } /* endswitch */
  } /* endfor */
}

void mz3DModel::DelSelectedVertices ()
{
  for (int i = Vertices () - 1; i >= 0; i--)
    if (Vertex (i).selected)
      DeleteVertex (i);
}

void mz3DModel::DelSelectedPolygons ()
{
  for (int i = Polygons () - 1; i >= 0; i--)
    if (Polygon (i).selected)
      DeletePolygon (i);
}

void mz3DModel::GetBoundingBox (csVector3 &iMin, csVector3 &iMax, bool iOnlySelected)
{
  // Private integer 2D point structure
  int i, j;
  CHK (bool *vert = new bool [Vertices ()]);

  if (iOnlySelected)
  {
    // Find which vertices should be modified
    for (i = 0; i < Vertices (); i++)
      vert [i] = Vertex (i).selected;
    for (i = 0; i < Polygons (); i++)
    {
      mz3DPolygon &p = Polygon (i);
      if (p.selected)
        for (j = p.Vertices () - 1; j >= 0; j--)
          vert [p [j]] = true;
    } /* endfor */
  } /* endif */

  iMin.Set (+9999999999., +9999999999., +9999999999.);
  iMax.Set (-9999999999., -9999999999., -9999999999.);

  for (i = Vertices () - 1; i >= 0; i--)
  {
    if (!iOnlySelected || vert [i])
    {
      mz3DVertex &v = Vertex (i);
      if (v.x < iMin.x) iMin.x = v.x;
      if (v.x > iMax.x) iMax.x = v.x;
      if (v.y < iMin.y) iMin.y = v.y;
      if (v.y > iMax.y) iMax.y = v.y;
      if (v.z < iMin.z) iMin.z = v.z;
      if (v.z > iMax.z) iMax.z = v.z;
    } /* endif */
  } /* endfor */
  CHK (delete [] vert);
}

void mz3DModel::GetBoundingBox (mzDraftEditor *Canvas, csVector3 &iMin,
  csVector3 &iMax, bool iOnlySelected)
{
  // Private integer 2D point structure
  int i, j;
  CHK (bool *vert = new bool [Vertices ()]);

  if (iOnlySelected)
  {
    // Find which vertices should be modified
    for (i = 0; i < Vertices (); i++)
      vert [i] = Vertex (i).selected;
    for (i = 0; i < Polygons (); i++)
    {
      mz3DPolygon &p = Polygon (i);
      if (p.selected)
        for (j = p.Vertices () - 1; j >= 0; j--)
          vert [p [j]] = true;
    } /* endfor */
  } /* endif */

  iMin.Set (+9999999999., +9999999999., +9999999999.);
  iMax.Set (-9999999999., -9999999999., -9999999999.);

  for (i = Vertices () - 1; i >= 0; i--)
  {
    if (!iOnlySelected || vert [i])
    {
      csVector3 v;
      Canvas->world2win (Vertex (i), v);
      if (v.x < iMin.x) iMin.x = v.x;
      if (v.x > iMax.x) iMax.x = v.x;
      if (v.y < iMin.y) iMin.y = v.y;
      if (v.y > iMax.y) iMax.y = v.y;
      if (v.z < iMin.z) iMin.z = v.z;
      if (v.z > iMax.z) iMax.z = v.z;
    } /* endif */
  } /* endfor */
}

void mz3DModel::GetTextures (csStrVector &iTexList)
{
  int vtx;
  switch (Type) 
  {
    case csmtSprite:
      AddNewTexture (iTexList, Texture);
      break;
    case csmtThing:
    case csmtRoom:
	case csmtSector:
      if ((vtx = Polygons ()) != 0) {
	for (int i = 0; i < vtx; i++) {
	  AddNewTexture (iTexList, Polygon (i).Texture);
	}
      }
      break;
  default:
    break;
  }
	
}

void mz3DModel::AddNewTexture (csStrVector &iTexList, csTextureHandle *iTexture)
{
  if (!iTexture)
    return;

  char buff [256];
  sprintf (buff, "TEXTURE '%s' (", csNameObject::GetName(*iTexture));
#if 0
  if (iTexture->GetTransparent () >= 0)
  {
    int tr, tg, tb;
    iTexture->GetTransparent (tr, tg, tb);
    sprintf ((char *)&buff [strlen (buff)], "TRANSPARENT(%d,%d,%d)", tr, tg, tb);
  } /* endif */
#endif
  strcat (buff, ")");
  iTexList.Push (strnew (buff));
  coating = iTexture;
}
