/*===========================================================================
 *
 * ARCH_D3D.CPP - Dave Humphrey (uesp@m0use.net), 31 October 2000
 *
 *=========================================================================*/

	/* Include Files */
#include "arch_d3d.h"
#include "dfcommon_d3d.h"
#include "profile.h"


	/* Initialize class static members */
boolean    CDF3DObjectD3D::LoadObjectNormals = TRUE;	
d3dvalue_t CDF3DObjectD3D::PointScale = DF_DEFAULT_3DOBJECT_SCALE;	


#undef  __FUNC__
#define __FUNC__ "CDF3DObjectD3D::CDF3DObjectD3D()"
/*===========================================================================
 *
 * Class CDF3DObjectD3D Constructor
 *
 *=========================================================================*/
CDF3DObjectD3D::CDF3DObjectD3D() {
  pD3DBuilder = NULL;
 }
/*===========================================================================
 *		End of Class CDF3DObjectD3D Constructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDF3DObjectD3D::Destroy()"
/*===========================================================================
 *
 * Class CDF3DObjectD3D Destructor
 *
 *=========================================================================*/
void CDF3DObjectD3D::Destroy () {

	/* Release allocated objects */
  RELEASE(pD3DBuilder);

	/* Call base class method */
  CDF3DObject::Destroy();
 }
/*===========================================================================
 *		End of Class CDF3DObjectD3D Destructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDF3DObjectD3D::GetD3DBuilder()"
/*===========================================================================
 *
 * Class CDF3DObjectD3D Method - CD3DMeshBuilder* GetD3DBuilder (void);
 *
 * Retrieves the 3D objects D3D builder.  Creates it if it doesn't currently
 * exist.  Returns NULL on any error.
 *
 *=========================================================================*/
CD3DMeshBuilder* CDF3DObjectD3D::GetD3DBuilder (void) {
  boolean Result;

	/* Attempt to make the builder if it doesn't exist */
  if (pD3DBuilder == NULL) {
    Result = MakeMeshBuilder();
    if (!Result) return (NULL);
   }

  return (pD3DBuilder);
 }
/*===========================================================================
 *		End of Class Method CDF3DObjectD3D::GetD3DBuilder()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDF3DObjectD3D::MakeBuildData()"
/*===========================================================================
 *
 * Class CDF3DObjectD3D Method - DWORD* MakeBuildData (void);
 *
 * Creates the build data array associated with the 3d object.  Always returns
 * a valid pointer unless there are no planes in the object. Protected
 * class method.
 *
 *=========================================================================*/
DWORD* CDF3DObjectD3D::MakeBuildData (void) {
  dfarch_plane_t* pPlaneData;
  int		  LoopCounter;
  int		  PointCounter;
  int		  IndexDivide = 12;
  long		  DataIndex = 0;
  long		  NumPlanePoints;
  long		  BuildDataSize;
  DWORD*	  pBuildData;

  START_PROFILE(MakeBuildData);

	/* Count the number of plane points to ensure there's at least one */
  NumPlanePoints = CountPlanePoints();
  if (NumPlanePoints <= 0) return (NULL);

	/* Special case for version v2.5 objects */
  if (IsVersion("v2.5")) IndexDivide = 36;

	/* Determine the size of the build array */
  if (LoadObjectNormals)
    BuildDataSize = GetNumPlanesAllocated() + NumPlanePoints*2 + 2;
  else
    BuildDataSize = GetNumPlanesAllocated() + NumPlanePoints + 2;

	/* Allocate the build data array */
  CreatePointerArray(pBuildData, DWORD, BuildDataSize);

	/* Add all the allocated planes in the object */
  for (LoopCounter = 0, DataIndex = 0; LoopCounter < GetNumPlanesAllocated(); LoopCounter++) { 
    pPlaneData = GetPlane(LoopCounter);

		/* Set the number of points/normals indices which follow */
    pBuildData[DataIndex] = pPlaneData->PointCount;
    DataIndex++;

		/* Add each point in the currnt plane */
    for (PointCounter = 0; PointCounter < pPlaneData->PointCount; PointCounter++) {
      pBuildData[DataIndex] = (pPlaneData->SubRecords[PointCounter].PointOffset)/IndexDivide;
      DataIndex++;

		/* Don't interlace the normals if this option is off */
      if (LoadObjectNormals) {
        pBuildData[DataIndex] = LoopCounter;
        DataIndex++;
       }
     }
   }

	/* Terminate the build data array */
  pBuildData[DataIndex] = 0;
  DataIndex++;

	 /* Check the array index */
  if (DataIndex > BuildDataSize) {
    SET_EXT_ERROR3(ERR_OVERFLOW, "Build data array index exceeded by %ld!", DataIndex - BuildDataSize);
   }

  END_PROFILE(MakeBuildData);

  return (pBuildData);
 }
/*===========================================================================
 *		End of Class Method CDF3DObjectD3D::MakeBuildData()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDF3DObjectD3D::MakeNormals()"
/*===========================================================================
 *
 * Class CDF3DObjectD3D Method - d3dvector_t* MakeNormals (void);
 *
 * Creates the normals array associated with the 3d object.  Always returns
 * a valid pointer unless there are no planes in the object or if the 
 * LoadObjectNormals is FALSE. Protected class method.
 *
 *=========================================================================*/
d3dvector_t* CDF3DObjectD3D::MakeNormals (void) {
  dfarch_point_t* pPoints;
  int		  LoopCounter;
  d3dvector_t*	  pNormals;

  START_PROFILE(MakeNormals);

	/* Ensure a valid number of points */
  if (GetNumPlanesAllocated() <= 0 || !LoadObjectNormals) {
    SET_EXT_ERROR(ERR_NULL);
    return (NULL);
   }

	/* Allocate the normals array */
  CreatePointerArray(pNormals, d3dvector_t, GetNumPlanesAllocated());
  pPoints = GetNormals();

	/* Setup the normals array */
  for (LoopCounter = 0; LoopCounter < GetNumPlanesAllocated(); LoopCounter++) {
    pNormals[LoopCounter] = PointToNormal(*pPoints);
    pPoints++;
   } 

  END_PROFILE(MakeNormals);

  return (pNormals);
 }
/*===========================================================================
 *		End of Class Method CDF3DObjectD3D::MakeNormals()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDF3DObjectD3D::MakeTextures()"
/*===========================================================================
 *
 * Class CDF3DObjectD3D Method - boolean MakeTextures (void);
 *
 * Assigns textures to the builder object.  Protected
 * class method.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDF3DObjectD3D::MakeTextures (void) {
  CD3DTexture*    pTexture;
  CD3DFaceArray*  pFaceArray = NULL;
  CD3DFace*	  pFace = NULL;
  CD3DWrap*       pWrap = NULL;
  d3dpalentry_t*  pPalEntry;
  d3dvector_t	  FaceNormal;
  d3dvector_t 	  FaceVertex;
  d3dvalue_t	  UTexture;
  d3dvalue_t	  VTexture;
  HRESULT	  Result;
  uint		  LoopCounter;
  int		  VertexCounter;
  dfarch_plane_t* pPlane;
  int		  ColorIndex;

  START_PROFILE(MakeTextures);

  	/* Get the D3D face array object */
  Result = pD3DBuilder->GetFaces(&pFaceArray);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to get face array for Direct3D builder object!");
    return (FALSE);
   }

  Result = GetD3DRM()->CreateWrap(D3DRMWRAP_SPHERE, NULL, 
			      D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0),
			      D3DVAL(1.0), D3DVAL(0.0), D3DVAL(0.0),
			      D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0), 
			      D3DVAL(0.0), D3DVAL(1.0), 
			      D3DVAL(1.0), D3DVAL(1.0), 
			      &pWrap);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to create wrap");
   }
  else
    pWrap->Apply(pD3DBuilder);          

  	/* Set the texture for each face */
  for (LoopCounter = 0; LoopCounter < pFaceArray->GetSize() && LoopCounter < (uint) NumPlanesAllocated; LoopCounter++) {
    pPlane = GetPlane(LoopCounter);
    if (pPlane == NULL) continue;
       
		/* Get the current face object */
    Result = pFaceArray->GetElement(LoopCounter, &pFace);

    if (FAILED(Result)) {
      SET_D3D_ERROR3(Result, "Failed to get face %d from face array!", LoopCounter);
      goto DF3D_MAKETEXTURE_ERROR;
     }

     		/* Special case for textures 0/1 for now */
    if (pPlane->TextureIndex <= 1) { 

			/* Compute color index and attempt to retrieve D3D palette entry for it*/
      ColorIndex = pPlane->TextureIndex*128 + pPlane->SubImageIndex;
      pPalEntry = DFStandardPaletteD3D.GetD3DPalEntry(ColorIndex);

      if (pPalEntry == NULL) {
        SET_D3D_ERROR3(Result, "Failed to get D3D palette entry %d from standard palette!", ColorIndex);
        RELEASE(pFace);
        continue;
       }

			/* Set the face color */
      pFace->SetColorRGB(D3DVAL(pPalEntry->red)/256, D3DVAL(pPalEntry->green)/256, D3DVAL(pPalEntry->blue)/256);
      RELEASE(pFace);
      continue;
     }

		/* Load/retreive the appropiate texture image */
    pTexture = DFGetD3DTexture(pPlane->TextureIndex, pPlane->SubImageIndex);		

    if (pTexture == NULL) {
      RELEASE(pFace);
      continue;
     }

    if (pWrap) {
      Result = pWrap->Apply(pFace);
      if (FAILED(Result)) SET_D3D_ERROR2(Result, "Failed to apply wrap to face");
     }

    //SystemLog.Printf ("Texture Coordinates for Face %d", LoopCounter);
   
		/* Set the face texture */
    Result = pFace->SetTexture(pTexture);
    pWrap->Apply(pFace);

    if (FAILED(Result)) {
      SET_D3D_ERROR4(Result, "Failed to set the face texture (%d, %d)!", pPlane->TextureIndex, pPlane->SubImageIndex);
     }
/*
   for (VertexCounter = 0; VertexCounter < pFace->GetVertexCount(); VertexCounter++) {
      Result = pFace->GetVertex(VertexCounter, &FaceVertex, &FaceNormal);
      if (FAILED(Result)) SET_D3D_ERROR2(Result, "Failed to get vertex");
      FaceNormal.y = (float)rand()/(float)RAND_MAX;
      FaceNormal.z = (float)rand()/(float)RAND_MAX;
      Result = pFace->SetTextureCoordinates(VertexCounter, FaceNormal.y, FaceNormal.z);
      if (FAILED(Result)) SET_D3D_ERROR2(Result, "Failed to set texture coordinates");
      pFace->GetTextureCoordinates(VertexCounter, &UTexture, &VTexture);
      SystemLog.Printf ("\tVertex %d: %f, %f", VertexCounter, UTexture, VTexture);
     } //*/

    RELEASE(pFace);
   } 

  END_PROFILE(MakeTextures);

	/* Cleanup and return success */
  RELEASE(pFaceArray);
  RELEASE(pFace);
  RELEASE(pWrap);
  return (TRUE);
  
DF3D_MAKETEXTURE_ERROR:
  RELEASE(pFaceArray);
  RELEASE(pFace);
  RELEASE(pWrap);

  return (FALSE);
 }
/*===========================================================================
 *		End of Class Method CDF3DObjectD3D::MakeTextures()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDF3DObjectD3D::MakeVectors()"
/*===========================================================================
 *
 * Class CDF3DObjectD3D Method - d3dvector_t* MakeVectors (void);
 *
 * Creates the vector array associated with the 3d object.  Always returns
 * a valid pointer unless there are no points in the object.  Protected
 * class method.
 *
 *=========================================================================*/
d3dvector_t* CDF3DObjectD3D::MakeVectors (void) {
  dfarch_point_t* pPoints;
  int		  LoopCounter;
  d3dvector_t*	  pVertices;

  START_PROFILE(MakeVectors);

	/* Ensure a valid number of points */
  if (GetNumPoints() <= 0) {
    SET_EXT_ERROR(ERR_NULL);
    return (NULL);
   }

	/* Allocate the vertices array */
  CreatePointerArray(pVertices, d3dvector_t, GetNumPoints());
  pPoints = GetPoints();

	/* Setup the vertex array */
  for (LoopCounter = 0; LoopCounter < GetNumPoints(); LoopCounter++) {
    pVertices[LoopCounter] = PointToScaledVector(*pPoints);
    pPoints++;
   } 

  END_PROFILE(MakeVectors);

  return (pVertices);
 }
/*===========================================================================
 *		End of Class Method CDF3DObjectD3D::MakeVectors()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDF3DObjectD3D::MakeMeshBuilder()"
/*===========================================================================
 *
 * Class CDF3DObjectD3D Method - boolean MakeMeshBuilder (void);
 *
 * Make a D3DRM Builder object from the 3D object. Returns the D3D error
 * code on error, or D3DRM_OK on success.  Protected class method.
 *
 *=========================================================================*/
boolean CDF3DObjectD3D::MakeMeshBuilder (void) { 
  d3dvector_t* pVertices = NULL;
  d3dvector_t* pNormals = NULL;
  DWORD*       pBuildData = NULL;
  HRESULT      Result;

  START_PROFILE(MakeMeshBuilder);
  PROFILE_INC();

	/* Ensure a valid input objects */
  ASSERT(GetD3DRM() != NULL);
  	
	/* Output debug information on object to log file */
#ifdef _DEBUG
  //Dump(SystemLog.GetFileHandle());
#endif

	/* Create a mesh builder object */
  Result = GetD3DRM()->CreateMeshBuilder(&pD3DBuilder);

  if (FAILED(Result)) {  
    SET_D3D_ERROR2(Result, "Failed to create the Builder2 object!");
    return (FALSE);
   }

	/* Create the vertices, normals, and build data arrays */
  pVertices = MakeVectors();
  pNormals = MakeNormals();
  pBuildData = MakeBuildData();

	/* Add the faces to the mesh builder */
  if (LoadObjectNormals)
    Result = pD3DBuilder->AddFaces(GetNumPoints(), pVertices, GetNumPlanesAllocated(), pNormals, pBuildData, NULL);
  else
    Result = pD3DBuilder->AddFaces(GetNumPoints(), pVertices, 0, NULL, pBuildData, NULL);
    
  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to add faces to the Builder2 object!");
    RELEASE(pD3DBuilder);  
    return (FALSE);
   }

	/* Make the textures for the object */
  Result = MakeTextures();

	/* Unallocate the temporary array data */
  DestroyPointer(pVertices);
  DestroyPointer(pNormals);
  DestroyPointer(pBuildData);

  PROFILE_DEC();
  END_PROFILE(MakeMeshBuilder);

  return (boolean) (Result);
 }
/*===========================================================================
 *		End of Class Method CDF3DObjectD3D::MakeMeshBuilder()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDF3DObjectD3D::Create3DObject()"
/*===========================================================================
 *
 * Class CDFArchD3D Method - void Create3DObject (RecordIndex);
 *
 * Creates the given object array.  Protected class method.
 *
 *=========================================================================*/
void CDFArchD3D::Create3DObject (const int RecordIndex) {

	/* Delete the object if it exists */
  DestroyPointer(p3DObjects[RecordIndex]);

	/* Alloate the new object */
  CreatePointer(p3DObjects[RecordIndex], CDF3DObjectD3D);
 }
/*===========================================================================
 *		End of Class Method CDFArchD3D::Create3DObject()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDF3DObjectD3D::GetD3DBuilder()"
/*===========================================================================
 *
 * Class CDFArchD3D Method - CD3DMeshBuilder* GetD3DBuilder(ValueID);
 *
 * Returns the specified Direct3D builder object or NULL on error.  
 *
 *=========================================================================*/
CD3DMeshBuilder* CDFArchD3D::GetD3DBuilder (const long ValueID) {
  return GetD3DBuilder(FindRecordIndex(ValueID));
 }
/*===========================================================================
 *		End of Class Method CDFArchD3D::Create3DObject()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDF3DObjectD3D::GetD3DBuilder()"
/*===========================================================================
 *
 * Class CDFArchD3D Method - CD3DMeshBuilder* GetD3DBuilder(Index);
 *
 * Returns the specified Direct3D builder object or NULL on error.  
 *
 *=========================================================================*/
CD3DMeshBuilder* CDFArchD3D::GetD3DBuilder (const int Index) {
  CDF3DObjectD3D* p3DObject;

	/* Ensure the object is loaded */
  p3DObject = GetD3DObject(Index);
  if (p3DObject == NULL) return (NULL);

	/* Return the builder object */
  return (p3DObject->GetD3DBuilder());
 }
/*===========================================================================
 *		End of Class Method CDFArchD3D::Create3DObject()
 *=========================================================================*/