//------------------------------------------------------------------------------
// File: LWO.cpp
//
// Desc: Class for Newtek LightWave [6] Object loader
//
//
// Copyright (C) 2001 Alexander Sokolov A.K.A. Z-Mechanic
// E-mail: cis@vsmpo.ru
//------------------------------------------------------------------------------

#pragma warn -csu

#include <lwo.h>

//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
LWO::LWO()
{
  kernel = new KERNEL;

  LWO_ImgNames = new TStringList;
  LWO_Pnts = NULL;
  LWO_Poly = NULL;
  LWO_BBox = NULL;
  LWO_Text = NULL;
  LWO_Surf = NULL;
  LWO_Vmap = NULL;
  LWO_Normals = NULL;
}

//------------------------------------------------------------------------------
// Destructor
//------------------------------------------------------------------------------
LWO::~LWO()
{
  //sequence of deleting is very important !!!
  delete LWO_ImgNames;
  //freeing UVmaps
  for (int i = 0; i < kernel->Size( LWO_Vmap ); i++)
  {
    kernel->Free( LWO_Vmap[i].name );
    //delete columns in VMap matrix
    for (int j = 0; j < kernel->Size( LWO_Pnts ); j++)
      kernel->Free( LWO_Vmap[i].data[j] );
    kernel->Free( LWO_Vmap[i].data );
  }
  //freeing surfaces
  for (int i = 0; i < kernel->Size( LWO_Surf ); i++) {
    kernel->Free( LWO_Surf[i].name );
    kernel->Free( LWO_Surf[i].prop );
  }
  kernel->Free( LWO_Vmap );
  kernel->Free( LWO_Pnts );
  kernel->Free( LWO_Poly );
  kernel->Free( LWO_BBox );
  kernel->Free( LWO_Text );
  kernel->Free( LWO_Surf );
  kernel->Free( LWO_Normals );

  delete kernel;
}

//------------------------------------------------------------------------------
// Load model from file
//------------------------------------------------------------------------------
void LWO::LoadFromFile(AnsiString FileName)
{
  const char                 Module[] = "LWO::LoadFromFile";

  FILE                       *mdl;
  char                       byte_4[] = "----";
  char                       byte_2[] = "--";
  unsigned int               pos, cnt;
  unsigned char              ch;
  AnsiString                 str;
  unsigned int               datasize;
  unsigned int               filepos;
  unsigned short             texture_index;

  _fmode = O_BINARY;

  glb_Desc( "LWO::LoadFromFile now processing LW file: '" + FileName + "'" );
  glb_Func( "" );

  if ( ( mdl = fopen( FileName.c_str(), "r" ) ) == NULL )
    PutErrorMessage( Module, "Can't open file" );

  //get LWO object indentificator
  fread( byte_4, 4, 1, mdl );
  if ( CompareChunk( "FORM", byte_4 ) == false )
    PutErrorMessage( Module, "File is not LW model (can't find FORM chunk)" );

  //get file data size
  fread( byte_4, 4, 1, mdl );
  datasize = ChunkToUInt( byte_4 );

  //get LWO data indentificator
  fread( byte_4, 4, 1, mdl );
  if ( CompareChunk( "LWO2", byte_4 ) == false )
    PutErrorMessage( Module, "File is not LW [6] model (can't find LWO2 chunk)" );

  //processing all chunks from file
  do {
    //get chunk stream
    fread( byte_4, 4, 1, mdl );
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    //we get surface name stream
    if ( CompareChunk( "TAGS", byte_4 ) == true ) {
      glb_Func( "Processing TAGS chunk" );
      fread( byte_4, 4, 1, mdl );
      cnt = ChunkToUInt( byte_4 );
      str = "";
      for (pos = 1; pos <= cnt; pos++)
      {
        fread( &ch, 1, 1, mdl );
        if ( (byte) ch != 0 )
          str = str + (char) ch;
        else
          if ( str != "" ) {
            //allocate memory for new surface properties
            int index = kernel->Add( LWO_Surf );
            //setup texture properties
            LWO_Surf[index].count = 0;
            LWO_Surf[index].prop = NULL;
            //put texture name to current position
            kernel->Allocate( LWO_Surf[index].name, str.Length() + 1 );
            strcpy( LWO_Surf[index].name, str.c_str() );
            str = "";
          }
      }
      goto proc_FileChunk;    //prevent done processing
    }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    //we get image name stream
    if ( CompareChunk( "CLIP", byte_4 ) == true ) {
      glb_Func( "Processing CLIP chunk" );
      fread( byte_4, 4, 1, mdl );
      //skip image index
      fread( byte_4, 4, 1, mdl );
      //read clip type (must be STIL)
      fread( byte_4, 4, 1, mdl );
      if ( CompareChunk( "STIL", byte_4 ) == false )
        PutErrorMessage( Module, "Model contains animated file surface (not support)" );
      //read image name size
      fread( byte_2, 2, 1, mdl );
      cnt = ChunkToUShort( byte_2 );
      str = "";
      for (pos = 1; pos <= cnt; pos++)
      {
        fread( &ch, 1, 1, mdl );
        if ( (byte) ch != 0 )
          str = str + (char) ch;
        else
          if ( str != "" ) {
            str = str.Insert( "/", 3 );
            LWO_ImgNames->Add( str );
            str = "";
          }
      }
      goto proc_FileChunk;    //prevent done processing
    }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    //we get layer stream (simple skip it)
    if ( CompareChunk( "LAYR", byte_4 ) == true ) {
      glb_Func( "Processing LAYR chunk" );
      fread( byte_4, 4, 1, mdl );
      cnt = ChunkToUInt( byte_4 );
      for (pos = 1; pos <= cnt; pos++)
      {
        fread( &ch, 1, 1, mdl );
        if ( pos <= 16 && ch != 0 )
          PutErrorMessage( Module, "File is multilayer model (not support)" );
      }
      goto proc_FileChunk;    //prevent done processing
    }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    //we get bounding box stream
    if ( CompareChunk( "BBOX", byte_4 ) == true ) {
      glb_Func( "Processing BBOX chunk" );
      fread( byte_4, 4, 1, mdl );
      //get points count
      kernel->Allocate( LWO_BBox, 2 );
      fread( byte_4, 4, 1, mdl );
      LWO_BBox[0].x = ChunkToFloat( byte_4 );
      fread( byte_4, 4, 1, mdl );
      LWO_BBox[0].y = ChunkToFloat( byte_4 );
      fread( byte_4, 4, 1, mdl );
      LWO_BBox[0].z = ChunkToFloat( byte_4 );
      fread( byte_4, 4, 1, mdl );
      LWO_BBox[1].x = ChunkToFloat( byte_4 );
      fread( byte_4, 4, 1, mdl );
      LWO_BBox[1].y = ChunkToFloat( byte_4 );
      fread( byte_4, 4, 1, mdl );
      LWO_BBox[1].z = ChunkToFloat( byte_4 );
      goto proc_FileChunk;    //prevent done processing
    }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    //we get points stream
    if ( CompareChunk( "PNTS", byte_4 ) == true ) {
      glb_Func( "Processing PNTS chunk" );
      fread( byte_4, 4, 1, mdl );
      //get points count
      cnt = ChunkToUInt( byte_4 ) / 12;
      kernel->Allocate( LWO_Pnts, cnt );

      for (pos = 1; pos <= cnt; pos++)
      {
        fread( byte_4, 4, 1, mdl );
        LWO_Pnts[pos - 1].x = ChunkToFloat( byte_4 );
        fread( byte_4, 4, 1, mdl );
        LWO_Pnts[pos - 1].y = ChunkToFloat( byte_4 );
        fread( byte_4, 4, 1, mdl );
        LWO_Pnts[pos - 1].z = ChunkToFloat( byte_4 );
      }
      goto proc_FileChunk;    //prevent done processing
    }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    //we get polygons stream
    if ( CompareChunk ( "POLS", byte_4 ) == true ) {
      glb_Func( "Processing POLS chunk" );
      LWO_PolCnt = 0;
      fread( byte_4, 4, 1, mdl );
      cnt = ChunkToUInt( byte_4 );
      //skip FACE or CURV chunk
      cnt = cnt - 4;

      //read data type which contains POLS stream
      fread( byte_4, 4, 1, mdl );
      if ( CompareChunk( "FACE", byte_4 ) == false )
        PutErrorMessage( Module, "Data type except FACE is present in POLS stream" );

      //get points count
      cnt = cnt / 2;
      kernel->Allocate( LWO_Poly, cnt );
      unsigned int           polygon_size = 0;    //don't change 0 to anything
      unsigned int           already_read = 1;    //don't change 1 to anything

      for (pos = 1; pos <= cnt; pos++)
      {
        fread( byte_2, 2, 1, mdl );
        if ( already_read > polygon_size ) {
          polygon_size = ChunkToUShort( byte_2 );
          already_read = 0;
          LWO_PolCnt++;
        }
        LWO_Poly[pos - 1] = ChunkToUShort( byte_2 );
        already_read++;
      }
      goto proc_FileChunk;    //prevent done processing
    }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    //we get texture index stream
    if ( CompareChunk( "PTAG", byte_4 ) == true ) {
      glb_Func( "Processing PTAG chunk" );
      fread( byte_4, 4, 1, mdl );
      cnt = ChunkToUInt( byte_4 );
      //skip SURF or other chunk
      cnt = cnt - 4;

      //read data type which contains POLS stream
      fread( byte_4, 4, 1, mdl );
      if ( CompareChunk( "SURF", byte_4 ) == false )
        PutErrorMessage( Module, "Data type except SURF is present in PTAG stream" );

      //get textures index count
      cnt = cnt / 4;
      kernel->Allocate( LWO_Text, cnt );

      LWO_PolCnt = cnt;  //??
      if ( cnt != LWO_PolCnt )
        PutErrorMessage( Module, "Polygon count is not equal texture destination" );

      unsigned short         polygon_index;
      for (pos = 1; pos <= cnt; pos++)
      {
        //get polygon index
        fread( byte_2, 2, 1, mdl );
        polygon_index = ChunkToUShort( byte_2 );
        //get texture index and set it to polygon index
        fread( byte_2, 2, 1, mdl );
        LWO_Text[polygon_index] = ChunkToUShort( byte_2 );
      }
      goto proc_FileChunk;    //prevent done processing
    }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    //we get UV texture coordinates stream
    if ( CompareChunk( "VMAP", byte_4 ) == true ) {
      glb_Func( "Processing VMAP chunk" );
      fread( byte_4, 4, 1, mdl );
      cnt = ChunkToUInt( byte_4 );
      //skip TXUV id chunk
      cnt = cnt - 4;

      //read data type which contains VMAP stream
      fread( byte_4, 4, 1, mdl );
      if ( CompareChunk ( "TXUV", byte_4 ) == false )
        PutErrorMessage( Module, "Data type except TXUV is present in VMAP stream" );

      //add new UVmap tag to global UVmap array
      int index = kernel->Add( LWO_Vmap );

      //get UVmap name prefix (length)
      fread( byte_2, 2, 1, mdl );

      //reading UVmap name
      str = "";
      for (pos = 1; pos <= 255; pos++)
      {
        fread( &ch, 1, 1, mdl );
        cnt--;
        if ( (byte) ch != 0 ) {
          str = str + (char) ch;
        } else {
          //upload one byte to chunk align for 2 when length of string is even
          if (pos % 2 != 0) {
            fread( &ch, 1, 1, mdl );
            cnt--;
          }
          break;    //string loaded -> exiting
        }
      }

      //put UVmap name to current position
      kernel->Allocate( LWO_Vmap[index].name, str.Length() + 1 );
      strcpy( LWO_Vmap[index].name, str.c_str() );

      //get points count (2 bytes point index + 4 bytes X + 4 bytes Y)
      cnt = cnt / 10;
      //allocate UVmap matrix row
      kernel->Allocate( LWO_Vmap[index].data, kernel->Size( LWO_Pnts ) );
      //allocate one column in UVmap matrix
      for (int i = 0; i < kernel->Size( LWO_Pnts ); i++)
        kernel->Allocate( LWO_Vmap[index].data[i], 1 );

      for (pos = 1; pos <= cnt; pos++)
      {
        fread( byte_2, 2, 1, mdl );
        int point = ChunkToUShort( byte_2 );
        fread( byte_4, 4, 1, mdl );
        LWO_Vmap[index].data[point][0].x = ChunkToFloat( byte_4 );
        fread( byte_4, 4, 1, mdl );
        LWO_Vmap[index].data[point][0].y = ChunkToFloat( byte_4 );
      }

      LWO_Vmap[index].uv_full = false;    //matrix may be trucated
      goto proc_FileChunk;    //prevent done processing
    }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    //we get UV advanced texture coordinates stream
    if ( CompareChunk( "VMAD", byte_4 ) == true ) {
      glb_Func( "Processing VMAD chunk" );
      fread( byte_4, 4, 1, mdl );
      cnt = ChunkToUInt( byte_4 );
      //skip TXUV id chunk
      cnt = cnt - 4;

      //read data type which contains VMAP stream
      fread( byte_4, 4, 1, mdl );
      if ( CompareChunk ( "TXUV", byte_4 ) == false )
        PutErrorMessage( Module, "Data type except TXUV is present in VMAD stream" );

      //get UVmap name prefix (length)
      fread( byte_2, 2, 1, mdl );

      //reading UVmap name
      str = "";
      for (pos = 1; pos <= 255; pos++)
      {
        fread( &ch, 1, 1, mdl );
        cnt--;
        if ( (byte) ch != 0 ) {
          str = str + (char) ch;
        } else {
          //upload one byte to chunk align for 2 when length of string is even
          if (pos % 2 != 0) {
            fread( &ch, 1, 1, mdl );
            cnt--;
          }
          break;    //string loaded -> exiting
        }
      }

      int index = -1;
      //get UVmap matrix index by name
      for (int i = 0; i < kernel->Size( LWO_Vmap ); i++)
        if (strcmp( LWO_Vmap[i].name, str.c_str() ) == 0) {
          index = i;
          break;
        }
      if (index == -1)
        PutErrorMessage( Module, "Can't find VMap matrix needs VMAD stream" );

      AddString( kernel->Size( LWO_Pnts ) );

      //allocate need columns in UVmap matrix
      for (int i = 0; i < kernel->Size( LWO_Pnts ); i++)
      {
        kernel->Resize( LWO_Vmap[index].data[i], LWO_PolCnt );
        //set all values of UVmap matrix to same as first line
        for (int j = 1; j < LWO_PolCnt; j++)
          LWO_Vmap[index].data[i][j] = LWO_Vmap[index].data[i][0];
      }

      //get points count (4 bytes point index + 4 bytes X + 4 bytes Y)
      cnt = cnt / 12;

      for (pos = 1; pos <= cnt; pos++)
      {
        fread( byte_2, 2, 1, mdl );
        int point = ChunkToUShort( byte_2 );
        fread( byte_2, 2, 1, mdl );
        int polygon = ChunkToUShort( byte_2 );
        fread( byte_4, 4, 1, mdl );
        LWO_Vmap[index].data[point][polygon].x = ChunkToFloat( byte_4 );
        fread( byte_4, 4, 1, mdl );
        LWO_Vmap[index].data[point][polygon].y = ChunkToFloat( byte_4 );
      }

      LWO_Vmap[index].uv_full = true;    //matrix is full

      goto proc_FileChunk;    //prevent done processing
    }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    //we get surface stream
    if ( CompareChunk( "SURF", byte_4 ) == true ) {
      glb_Func( "Processing SURF chunk" );
      fread( byte_4, 4, 1, mdl );
      int sub_pos = ftell( mdl );
      int sub_len = ChunkToUInt( byte_4 );
      str = "";
      //load texture name
      do {
        fread( &ch, 1, 1, mdl );
        if ( (byte) ch != 0 )
          str = str + (char) ch;
      } while ( ch != 0);
      //skip zero bytes after texture name
      for (pos = 1; pos <= 3 - (str.Length() % 2); pos++)
        fread( &ch, 1, 1, mdl );

      //get texture index
      for (int i = 0; i < kernel->Size( LWO_Surf ); i++)
        if (strcmp( str.c_str(), LWO_Surf[i].name ) == 0) {
          texture_index = i;
          break;
        }

      do {
        //get chunk stream
        fread( byte_4, 4, 1, mdl );
        //...color
        if ( CompareChunk( "COLR", byte_4 ) == true ) {
          //read chunk size
          fread( byte_2, 2, 1, mdl );
          //read color data
          fread( byte_4, 4, 1, mdl );
          LWO_Surf[texture_index].color.r = ChunkToFloat( byte_4 );
          fread( byte_4, 4, 1, mdl );
          LWO_Surf[texture_index].color.g = ChunkToFloat( byte_4 );
          fread( byte_4, 4, 1, mdl );
          LWO_Surf[texture_index].color.b = ChunkToFloat( byte_4 );
          //read 2 zero bytes
          fread( byte_2, 2, 1, mdl );
          goto proc_SurfChunk;
        }
        //...diffuse
        if ( CompareChunk( "DIFF", byte_4 ) == true ) {
          //read chunk size
          fread( byte_2, 2, 1, mdl );
          //read diffuse data
          fread( byte_4, 4, 1, mdl );
          LWO_Surf[texture_index].diffuse.r = LWO_Surf[texture_index].color.r * ChunkToFloat( byte_4 );
          LWO_Surf[texture_index].diffuse.g = LWO_Surf[texture_index].color.g * ChunkToFloat( byte_4 );
          LWO_Surf[texture_index].diffuse.b = LWO_Surf[texture_index].color.b * ChunkToFloat( byte_4 );
          //read 2 zero bytes
          fread( byte_2, 2, 1, mdl );
          goto proc_SurfChunk;
        }

        //...other properties
        if ( CompareChunk( "BLOK", byte_4 ) == true ) {
          //add multi-texture and set it count
          int subtex_index = kernel->Add( LWO_Surf[texture_index].prop );
          LWO_Surf[texture_index].count++;
          //read chunk size
          fread( byte_2, 2, 1, mdl );
          int sub_sub_len = ChunkToUShort( byte_2 );
          int sub_sub_pos = ftell( mdl );

          do {
            fread( byte_4, 4, 1, mdl );

            //...image index
            if ( CompareChunk( "IMAG", byte_4 ) == true ) {
              //skip chunk size
              fread( byte_2, 2, 1, mdl );
              //read image index
              fread( byte_2, 2, 1, mdl );
              LWO_Surf[texture_index].prop[subtex_index].image = ChunkToUShort( byte_2 ) - 1;
              goto proc_BlokChunk;    //prevent done processing
            }
            //...UVmap name
            if ( CompareChunk( "VMAP", byte_4 ) == true ) {
              //get UVmap name length
              fread( byte_2, 2, 1, mdl );
              int name_length = ChunkToUShort( byte_2 );

              //reading UVmap name
              str = "";
              for (pos = 1; pos <= name_length; pos++)
              {
                fread( &ch, 1, 1, mdl );
                if ( (byte) ch != 0 )
                  str = str + (char) ch;
              }
              //find and set UVmap index assigned to texture
              for (int i = 0; i < kernel->Size( LWO_Vmap ); i++)
                if (strcmp( str.c_str(), LWO_Vmap[i].name ) == 0) {
                  LWO_Surf[texture_index].prop[subtex_index].uv_map = i;
                  break;
                }
              goto proc_BlokChunk;    //prevent done processing
            }

            //skip unsupported sub_sub_chunk
            fread( byte_2, 2, 1, mdl );
            pos = ChunkToUShort( byte_2 );
            for (int add_cnt = 1; add_cnt <= pos; add_cnt++)
              fread( &ch, 1, 1, mdl );

            proc_BlokChunk:
          } while ( sub_sub_len != ftell( mdl ) - sub_sub_pos );
          goto proc_SurfChunk;
        }

        //skip unsupported sub_chunk
        fread( byte_2, 2, 1, mdl );
        pos = ChunkToUShort( byte_2 );
        for (int add_cnt = 1; add_cnt <= pos; add_cnt++)
          fread( &ch, 1, 1, mdl );

        proc_SurfChunk:
      } while ( sub_len != ftell( mdl ) - sub_pos );
      goto proc_FileChunk;    //prevent done processing
    }

    //skip unsupported chunk
    fread( byte_4, 4, 1, mdl );
    pos = ChunkToUInt( byte_4 );
    for (int add_cnt = 1; add_cnt <= pos; add_cnt++)
      fread( &ch, 1, 1, mdl );

    proc_FileChunk:
  } while ( ftell( mdl ) != datasize + 8);

  fclose(mdl);
  // NOW LW OBJECT LOADING COMPLETED !!!

  //DON'T EXCHANGE FOLLOWING TWO LINES
  Optimize();       //process optimization
  CalcNormals();    //calculate normal to each vector

  glb_Desc( "" );
  glb_Func( "" );
}

//------------------------------------------------------------------------------
// Return polygon count in model
//------------------------------------------------------------------------------
unsigned short LWO::PolygonCount()
{
  return LWO_PolCnt;
}

//------------------------------------------------------------------------------
// Return surface count in model
//------------------------------------------------------------------------------
unsigned short LWO::SurfaceCount()
{
  return kernel->Size( LWO_Surf );
}

//------------------------------------------------------------------------------
// Return pointer to points array
//------------------------------------------------------------------------------
point_3D* LWO::Points()
{
  return LWO_Pnts;
}

//------------------------------------------------------------------------------
// Return pointer to polygons array
//------------------------------------------------------------------------------
unsigned short* LWO::Polygons()
{
  return LWO_Poly;
}

//------------------------------------------------------------------------------
// Return pointer to bounding box two 3D point array
//------------------------------------------------------------------------------
point_3D* LWO::BoundingBox()
{
  return LWO_BBox;
}

//------------------------------------------------------------------------------
// Return pointer to texture index array
//------------------------------------------------------------------------------
unsigned short* LWO::Textures()
{
  return LWO_Text;
}

//------------------------------------------------------------------------------
// Return pointer to surface properties array
//------------------------------------------------------------------------------
LWO::lwo_surf* LWO::Surfaces()
{
  return LWO_Surf;
}

//------------------------------------------------------------------------------
// Return pointer to UVmap points coordinates array
//------------------------------------------------------------------------------
LWO::lwo_uv_tag* LWO::UVmap()
{
  return LWO_Vmap;
}

//------------------------------------------------------------------------------
// Return pointer to images array
//------------------------------------------------------------------------------
TStringList* LWO::Images()
{
  return LWO_ImgNames;
}

//------------------------------------------------------------------------------
// Return pointer to array of vector normals
//------------------------------------------------------------------------------
point_3D* LWO::Normals()
{
  return LWO_Normals;
}

//==============================================================================
// Compares two chunks
//==============================================================================
bool LWO::CompareChunk(char *mustbe, char *whatget)
{
  if ( strcmp( mustbe, whatget ) == 0 )
    return true;
  else
    return false;
}

//==============================================================================
// Convert chunk (4 bytes) to unsigned long
//==============================================================================
unsigned int LWO::ChunkToUInt(char *input)
{
  union {
    unsigned int               temp;
    unsigned char              byte_4[4];
  } swp;

  swp.byte_4[0] = input[3];
  swp.byte_4[1] = input[2];
  swp.byte_4[2] = input[1];
  swp.byte_4[3] = input[0];

  return swp.temp;
}

//==============================================================================
// Convert chunk (4 bytes) to float
//==============================================================================
float LWO::ChunkToFloat(char *input)
{
  union {
    float                      temp;
    unsigned char              byte_4[4];
  } swp;

  swp.byte_4[0] = input[3];
  swp.byte_4[1] = input[2];
  swp.byte_4[2] = input[1];
  swp.byte_4[3] = input[0];

  return swp.temp;
}

//==============================================================================
// Convert chunk (2 bytes) to unsigned short
//==============================================================================
unsigned short LWO::ChunkToUShort(char *input)
{
  union {
    unsigned short             temp;
    unsigned char              byte_2[2];
  } swp;

  swp.byte_2[0] = input[1];
  swp.byte_2[1] = input[0];

  return swp.temp;
}

//==============================================================================
// Calculate normals for all polygons in object
//==============================================================================
void LWO::CalcNormals()
{
  kernel->Allocate( LWO_Normals, LWO_PolCnt );

  unsigned int             polpos = 0;

  for (int poly = 0; poly < LWO_PolCnt; poly++)
  {
    unsigned short cnt = LWO_Poly[polpos];    //keep points count in current polygon

    //calculate normal when 3 or more points is present
    if (cnt >= 3)
      LWO_Normals[poly] = GetNormal( LWO_Pnts[LWO_Poly[polpos + 1]], LWO_Pnts[LWO_Poly[polpos + 2]], LWO_Pnts[LWO_Poly[polpos + 3]] );
    else {
      LWO_Normals[poly].x = 0;
      LWO_Normals[poly].y = 0;
      LWO_Normals[poly].z = 0;
    }

    polpos = polpos + cnt + 1;
  }
}

//==============================================================================
// Optimize LW object by polygon point count
//==============================================================================
void LWO::Optimize()
{
  unsigned short             *tmp_Poly;
  unsigned short             *tmp_Text;

  kernel->Allocate( tmp_Poly, kernel->Size( LWO_Poly ) );
  kernel->Allocate( tmp_Text, kernel->Size( LWO_Text ) );

  unsigned int             copy_polpos = 0, copy_texpos = 0;
  //cycle by count of point in polygon
  for (int pcip = 1; pcip <= 5; pcip++)
  {
    unsigned int             polpos = 0;
    Optimizer[pcip - 1] = 0;
    //cycle by polygon count
    for (int poly = 0; poly < LWO_PolCnt; poly++)
    {
      unsigned short cnt = LWO_Poly[polpos];    //keep points count in current polygon

      //when requested polygon contains need point count
      if ( (cnt == pcip && pcip <= 4) || (cnt >= 5 && pcip == 5) ) {
        Optimizer[pcip - 1]++;    
        //process polygon & points
        memcpy( tmp_Poly + copy_polpos, LWO_Poly + polpos, (cnt + 1) * sizeof( unsigned short ) );
        copy_polpos = copy_polpos + cnt + 1;
        //process texture index
        tmp_Text[copy_texpos] = LWO_Text[poly];
        copy_texpos++;
      }
      polpos = polpos + cnt + 1;
    }
  }

  //copy optimized buffers to actual buffers and destroy optimized buffers
  memcpy( LWO_Poly, tmp_Poly, kernel->Size( LWO_Poly )* sizeof( unsigned short ) );
  kernel->Free( tmp_Poly );
  memcpy( LWO_Text, tmp_Text, kernel->Size( LWO_Text )* sizeof( unsigned short ) );
  kernel->Free( tmp_Text );
}

