
// g3dsystm.cpp
//
// Copyright (c) 1996 by Toshiaki Tsuji, all rights reserved.

#include "stdgfx.h"
#include "g3dsystm.h"

#include "g3d3ds.h"
#include "g3dasc.h"
#include "g3dgem.h"
#include "g3dgeo.h"

G3DSYSTEM::G3DSYSTEM ()
  {
    G3DDraw = new G3DDRAW ();

    World = NULL;
    MaterialLib = NULL;

    VisibleShapes = NULL;
    ShadeFlags = SHADE_GOURAUD;
    FaceFlags = FACE_TEXTURE;
    DepthCueing = FALSE;
    DepthScale = (float)1.0;
    BlendTable = NULL;
    HazeScale = (float)1.0;
    HazeTable = NULL;
    DoHaze = FALSE;
  } // End of Constructor for G3DSYSTEM 

G3DSYSTEM::~G3DSYSTEM ()
  {
    if (G3DDraw!=NULL)
      delete G3DDraw;
    G3DDraw = NULL;

    if (VisibleShapes!=NULL)
      delete VisibleShapes;
    VisibleShapes = NULL;  

    if (World!=NULL)
      delete World;
    World = NULL;

    if (BlendTable!=NULL)
      delete BlendTable;
    BlendTable = NULL;
      
    if (HazeTable!=NULL)
      delete HazeTable;
    HazeTable = NULL;
      
    if (MaterialLib!=NULL)
      delete MaterialLib;
    MaterialLib = NULL;
  } // End of Destructor for G3DSYSTEM 

VOID G3DSYSTEM::SetShadeFlags ( LONG Flags )
  {
    ShadeFlags = Flags;  
  } // End of SetShadeFlags for G3DSYSTEM

VOID G3DSYSTEM::SetFaceFlags ( LONG Flags )
  {
    FaceFlags = Flags;  
  } // End of SetFaceFlags for G3DSYSTEM
  
VOID G3DSYSTEM::SetDepthCueing ( BOOLEAN OnOff, float Scale )
  {
    DepthCueing = OnOff;
    DepthScale = Scale;  
  } // End of SetDepthCueing for G3DSYSTEM

VOID G3DSYSTEM::SetBlendTable ( COLORTABLE *Table )
  {
    BlendTable = Table;  
  } // End of SetBlendTable for G3DSYSTEM

VOID G3DSYSTEM::SetHazing ( BOOLEAN OnOff, float Scale, COLORTABLE *Table )
  {
    DoHaze = OnOff;
    HazeScale = Scale;
    HazeTable = Table;  
  } // End of SetHazing for G3DSYSTEM

VOID G3DSYSTEM::AllocateShapeTable ( LONG Num )
  {
    if (VisibleShapes!=NULL)
      delete VisibleShapes;
    VisibleShapes = new G3DSHAPE* [Num];        
  } // End of AllocateShapeTable for G3DSYSTEM

LONG G3DSYSTEM::CountNumShapes ()
  {
    if (World==NULL)
      return 0;
    return World->CountNumShapes ();    
  } // End of CountNumShapes for G3DSYSTEM

VOID G3DSYSTEM::SetDestination ( IMAGE *Dest )
  {
    if (G3DDraw!=NULL)
      {
        G3DDraw->SetDestination ( Dest );  
      } // End if  
  } // End of SetDestination for G3DSYSTEM

VOID G3DSYSTEM::SetViewDistance ( float ViewDistance )
  {
    if (G3DDraw!=NULL)
      {
        G3DDraw->SetViewDistance ( ViewDistance );  
      } // End if  
  } // End of SetViewDistance for G3DSYSTEM

VOID G3DSYSTEM::SetScreenCenter ( LONG CenterX, LONG CenterY )
  {
    if (G3DDraw!=NULL)
      {
        G3DDraw->SetScreenCenter ( CenterX, CenterY );  
      } // End if  
  } // End of SetScreenCenter for G3DSYSTEM

VOID G3DSYSTEM::SetMaterialLib ( G3DMATERIALLIB *NewLib )
  {
    MaterialLib = NewLib;  
  } // End of SetMaterialLib for G3DSYSTEM
  
G3DMATERIAL* G3DSYSTEM::FindMaterialByName ( STRING Name )
  {
    if (MaterialLib==NULL)
      return NULL;
      
    return MaterialLib->FindMaterialByName ( Name );  
  } // End of FindMaterialByName for G3DSYSTEM

G3DMATERIAL* G3DSYSTEM::FindMaterialByID ( LONG SearchID )
  {
    if (MaterialLib==NULL)
      return NULL;

    return MaterialLib->FindMaterialByID ( SearchID );  
  } // End of FindMaterialByID for G3DSYSTEM

G3DMATERIAL* G3DSYSTEM::FindMaterialByIndex ( LONG Index )
  {
    if (MaterialLib==NULL)
      return NULL;

    if ((Index<0)||(Index>=MaterialLib->GetNumMaterials()))
      return NULL;
      
    return MaterialLib->GetMaterial ( Index );  
  } // End of FindMaterialByIndex for G3DSYSTEM

VOID G3DSYSTEM::SetWorld ( G3DWORLD *NewWorld, BOOLEAN DeleteOld )
  {
    if ((World!=NULL)&&(DeleteOld))
      delete World;        
    World = NewWorld;  
  } // End of SetWorld for G3DSYSTEM

int CompareShape ( const void *Elem1, const void *Elem2 )
  {
    G3DSHAPE *Shape1 = (*(G3DSHAPE**)Elem1);  
    G3DSHAPE *Shape2 = (*(G3DSHAPE**)Elem2);

    if (Shape1->MinZ>Shape2->MaxZ)
      return -1;
    if (Shape1->MaxZ<Shape2->MinZ)
      return 1;

    if (Shape1->MinZ>Shape2->MinZ)
      return -1;
    if (Shape1->MinZ<Shape2->MinZ)
      return 1;
      
    if (Shape1->MaxZ>Shape2->MaxZ)
      return -1;
    if (Shape1->MaxZ<Shape2->MaxZ)
      return 1;
              
    return 0;  
  } // End of CompareShape

VOID G3DSYSTEM::PainterSort ()
  {
    qsort ( VisibleShapes, NumVisibleShapes, sizeof(G3DSHAPE*),
            CompareShape ); 
  } // End of PainterSort for G3DSYSTEM

BOOLEAN G3DSYSTEM::Init ()
  {
    BOOLEAN Result;  
    if (World==NULL)
      return FAILURE;
    Result = World->Init ();

    if (Result)
      {
        LONG NumShapes;
        NumShapes = CountNumShapes ();
        AllocateShapeTable ( NumShapes*2 );  
      } // End if
    return Result;  
  } // End of Init for G3DSYSTEM

G3DOBJECT* G3DSYSTEM::FindObjectByName ( STRING SearchName )
  {
    if (World==NULL)
      return NULL;
    return World->FindObjectByName ( SearchName );    
  } // End of FindObjectByName for G3DSYSTEM 

G3DOBJECT* G3DSYSTEM::FindObjectByID ( LONG SearchID )
  {
    if (World==NULL)
      return NULL;
    return World->FindObjectByID ( SearchID );    
  } // End of FindObjectByID for G3DSYSTEM 

INT G3DSYSTEM::Get3DFileType ( FILEHANDLE f )
  {
    SHORT ID;
    
    if (File.Read ( f, &ID, 2  )==FAILURE)  
      return -1;

    // Check for 3DS file
    if (ID==CHUNK_PRIMARY)  // 3DS File?
      {
        return FILE_3DS;
      } // End if

    CHAR TypeStr[128];

    // Check for ASC file
    File.Seek ( f, 0, FROM_BEGIN );
    if (File.Read ( f, TypeStr, 7 )==FAILURE)
      return -1;
    if (strncmp ( TypeStr, "Ambient", 7)==0)
      return FILE_ASC;    

    // Check for GEO file
    File.Seek ( f, 0, FROM_BEGIN );
    if (File.Read ( f, TypeStr, 4 )==FAILURE)
      return -1;
    if (strncmp ( TypeStr, "3DG1", 4)==0)
      return FILE_GEO;    

    return FILE_GEM;  // Default to GEM file
  } // End of Get3DFileType for G3DSYSTEM
  
BOOLEAN G3DSYSTEM::Load3DFile ( STRING FileName, LINKEDLIST<G3DOBJECT*> *ObjectList,
                                BOOLEAN ClockWise, double Scale )
  {
    FILEHANDLE f;
    BOOLEAN Result;
    
    f = File.Open ( FileName, OPEN_READ | OPEN_BINARY );
    if (f==NULL)
      return FAILURE;

    INT Type;

    Type = Get3DFileType ( f );
    File.Seek ( f, 0, FROM_BEGIN );

    G3DFILETOOL *G3DFileTool=NULL;
    switch (Type)
      {
        case FILE_ASC :
          G3DFileTool = new G3DASCTOOL ();
          break; 
        case FILE_3DS :
          G3DFileTool = new G3D3DSTOOL ();
          break;
        case FILE_GEM :
          G3DFileTool = new G3DGEMTOOL ();
          break;
        case FILE_GEO :
          G3DFileTool = new G3DGEOTOOL ();
          break;
        default :
          G3DFileTool = NULL;
          break;
      } // End switch

    if (G3DFileTool!=NULL)
      {
        Result = G3DFileTool->Load ( f, ObjectList, ClockWise, Scale );
        delete G3DFileTool;
      } // End if  
    else
      Result = FAILURE;
        
    File.Close ( f );
    return Result;
  } // End of Load3DFile for G3DSYSTEM                             

BOOLEAN G3DSYSTEM::Save3DFile ( STRING FileName, LINKEDLIST<G3DOBJECT*> *ObjectList,
                                BOOLEAN ClockWise, double Scale, INT Type )
  {
    FILEHANDLE f;
    BOOLEAN Result;
    
    f = File.Open ( FileName, OPEN_WRITE | OPEN_BINARY );
    if (f==NULL)
      return FAILURE;

    File.Seek ( f, 0, FROM_BEGIN );

    G3DFILETOOL *G3DFileTool=NULL;
    switch (Type)
      {
        case FILE_ASC :
          G3DFileTool = new G3DASCTOOL ();
          break; 
        case FILE_3DS :
          G3DFileTool = new G3D3DSTOOL ();
          break;
        case FILE_GEM :
          G3DFileTool = new G3DGEMTOOL ();
          break;
        case FILE_GEO :
          G3DFileTool = new G3DGEOTOOL ();
          break;
        default :
          G3DFileTool = NULL;
          break;
      } // End switch

    if (G3DFileTool!=NULL)
      {
        Result = G3DFileTool->Save ( f, ObjectList, ClockWise, Scale );
        delete G3DFileTool;
      } // End if  
    else
      Result = FAILURE;
        
    File.Close ( f );
    return Result;
  } // End of Save3DFile for G3DSYSTEM
                                
VOID G3DSYSTEM::SetViewPort ( LONG x1, LONG y1, LONG x2, LONG y2 )
  {
    if (G3DDraw!=NULL)
      G3DDraw->SetViewPort ( x1, y1, x2, y2 );  
  } // End of SetViewPort for G3DSYSTEM
  
VOID G3DSYSTEM::SetNearClipZ ( float Z )
  {
    if (G3DDraw!=NULL)
      G3DDraw->SetNearClipZ ( Z );  
  } // End of SetNearClipZ for G3DSYSTEM

FLPVECTOR3D G3DSYSTEM::ComputeNextPos ( FLPVECTOR3D StartP, FLPVECTOR3D Angle, 
                                        FLPVECTOR3D Vector )
  {
    G3DMATRIX Matrix;
    
    InitMatrix ( Matrix );
    RotateXYZ ( Matrix, Angle.x, Angle.y, Angle.z );

    FLPVECTOR3D EndP;
    float x,y,z;
    
    x = Vector.x*Matrix[0][0]+
         Vector.y*Matrix[1][0]+    
          Vector.z*Matrix[2][0];    
    y = Vector.x*Matrix[0][1]+
         Vector.y*Matrix[1][1]+    
          Vector.z*Matrix[2][1];    
    z = Vector.x*Matrix[0][2]+
         Vector.y*Matrix[1][2]+    
          Vector.z*Matrix[2][2];

    EndP.x = StartP.x + x;
    EndP.y = StartP.y + y; 
    EndP.z = StartP.z + z;
    
    return EndP;
  } // End of ComputeNextPos for G3DENGINE
  
LONG G3DSYSTEM::CheckCollision ( FLPVECTOR3D StartPt, FLPVECTOR3D EndPt,
                                 COLLIDEDATA *CollideList, LONG MaxNum,
                                 float CollideDist, float Gap )
  {
    if (World==NULL)
      return 0;

    LONG Count;

    Count = World->CheckCollision ( StartPt, EndPt, CollideList, MaxNum, CollideDist, Gap );
    return Count;   
  } // End of CheckColiision for G3DSYSTEM
                                 
VOID G3DSYSTEM::ShowView ( G3DCAMERA *Camera )
  {
    if (Camera==NULL)
      return;
    if (World==NULL)
      return;
          
    InitMatrix ( ViewMatrix );

    FLPVECTOR3D CamAngle,CamPos;
    CamAngle = Camera->GetAngle ();
    CamPos = Camera->GetWorldPosition ();
    Translate ( ViewMatrix, -CamPos.x, -CamPos.y, -CamPos.z );
    RotateYXZ ( ViewMatrix, -CamAngle.x, -CamAngle.y, -CamAngle.z );

    CAMERADATA CameraData;
    CameraData.CameraPoint = CamPos;
    CameraData.NumVisibleShapes = 0;
    CameraData.VisibleShapes = VisibleShapes;
    CameraData.CenterX = (float)G3DDraw->GetCenterX();
    CameraData.CenterY = (float)G3DDraw->GetCenterY();
    CameraData.ViewDistance = G3DDraw->GetViewDistance();
    CameraData.ViewPort = G3DDraw->GetViewPort ();
    CameraData.NearClipZ = G3DDraw->GetNearClipZ ();
    CameraData.ShadeFlags = ShadeFlags;
    CameraData.FaceFlags = FaceFlags;
    CameraData.DepthCueing = DepthCueing;
    CameraData.DepthScale = DepthScale;
    
    if (HazeTable!=NULL)
      {
        CameraData.DoHaze = DoHaze;
        CameraData.HazeScale = HazeScale;
        CameraData.HazeLevels = HazeTable->GetNumLevels();
      } // End if
    else  
      {
        CameraData.DoHaze = FALSE;
        CameraData.HazeScale = HazeScale;
        CameraData.HazeLevels = 0;
      } // End if

    World->TransformWorldToCamera ( ViewMatrix, &CameraData );

    NumVisibleShapes = CameraData.NumVisibleShapes;
    
    PainterSort ();

    LONG i;
    G3DDraw->ShadeFlags = ShadeFlags;
    G3DDraw->FaceFlags = FaceFlags;
    G3DDraw->BlendTable = BlendTable;
    G3DDraw->DoHaze = DoHaze;
    G3DDraw->HazeTable = HazeTable;
    
    for (i=0;i<NumVisibleShapes;i++)
      {
        G3DDraw->DrawShape ( VisibleShapes[i] );
      } // End for
  } // End of ShowView for G3DSYSTEM

// For benchmark purpose
VOID G3DSYSTEM::TestDrawTriangle ( FXPPOINT2D *Points, ANIMIMAGE *Texture,
                                   LONG Intensity, LONG Color, LONG DrawShadeFlags,
                                   LONG DrawFaceFlags, COLORTABLE *BlendTBL, COLORTABLE *ShadeTBL,
                                   COLORTABLE *HazeTBL )
  {
    G3DDraw->TestDrawTriangle ( Points, Texture, Intensity, Color, DrawShadeFlags,
                                DrawFaceFlags,  BlendTBL, ShadeTBL, HazeTBL );
  } // End of TestDrawTriangle for G3DSYSTEM

VOID G3DSYSTEM::TestDrawTriangle2 ( FXPPOINT2D *Points, ANIMIMAGE *Texture,
                                    LONG Intensity, LONG Color, LONG DrawShadeFlags,
                                    LONG DrawFaceFlags, COLORTABLE *BlendTBL, COLORTABLE *ShadeTBL,
                                    COLORTABLE *HazeTBL )
  {
    G3DDraw->TestDrawTriangle2 ( Points, Texture, Intensity, Color, DrawShadeFlags,
                                 DrawFaceFlags,  BlendTBL, ShadeTBL, HazeTBL );
  } // End of TestDrawTriangle2 for G3DSYSTEM

