/*===========================================================================
 *
 * DFPixel_D3D.CPP - Dave Humphrey (uesp@m0use.net), 23 November 2000
 *
 *=========================================================================*/

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


#undef  __FUNC__
#define __FUNC__ "CDFPixelBuilderD3D::CDFPixelBuilderD3D()"
/*===========================================================================
 *
 * Class CDFPixelBuilderD3D Constructor
 * 
 *=========================================================================*/
CDFPixelBuilderD3D::CDFPixelBuilderD3D() {
  pBuilder = NULL;
  pFrame = NULL;
  pParentFrame = NULL;
 }
/*===========================================================================
 *		End of Class CDFPixelBuilderD3D Constructor
 *=========================================================================*/


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

	/* Release the Direct3D objects */
  if (pBuilder != NULL && pFrame != NULL) {
    pFrame->DeleteVisual(pBuilder);
    if (pParentFrame != NULL) pParentFrame->DeleteChild(pFrame);
   }

  RELEASE(pBuilder);
  RELEASE(pFrame);
  pParentFrame = NULL;

	/* Call the base class method */
  CDFPixelBuilder::Destroy();
 }
/*===========================================================================
 *		End of Class CDFPixelBuilderD3D Destructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFPixelBuilderD3D::CreateLandscape()"
/*===========================================================================
 *
 * Class CDFPixelBuilderD3D Method - boolean CreateLandscape (void)
 *
 * Creates the Direct3D landscape objects for the current land pixel.
 * Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDFPixelBuilderD3D::CreateLandscape (void) {
  HRESULT Result;

	/* Don't do anything if the builder object already exists */
  if (pBuilder != NULL) return (TRUE);

  PROFILE_INC();
  START_PROFILE(CreateArrays);

  	/* Generate the landscape vertex/face arrays */
  CreateArrays();

  END_PROFILE(CreateArrays);
  START_PROFILE(MakeBuilder);

	/* Create a mesh builder object from the D3D object as required */
  Result = MakeBuilder();
  if (FAILED(Result)) return (FALSE);

  END_PROFILE(MakeBuilder);
  START_PROFILE(AddBuilder);

      	/* Add the builder mesh to the frame */
  Result = pFrame->AddVisual(pBuilder);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to add landscape object to the landscape frame!");
    return (FALSE);
   }

  END_PROFILE(AddBuilder);
  START_PROFILE(CreateLocation);

	/* Translate the frame to the desired world position (X-Z only).  The origin
	 * pixel always sits at (X=0, Z=0) */
  //XTranslation = D3DVAL(((float) (XCounter - DFLAND_PIXELS_ARRAYHALFSIZE)) * DFLAND_PIXEL_COORSIZE);
  //ZTranslation = D3DVAL(((float) (YCounter - DFLAND_PIXELS_ARRAYHALFSIZE)) * DFLAND_PIXEL_COORSIZE);
  //(*ppFramePtr)->SetPosition(pParentFrame, XTranslation, 0, ZTranslation);
	
	/* Create any locations in the land pixel */
  Result = CreateLocation();

  END_PROFILE(CreateLocation);
  PROFILE_DEC();

  return ((boolean) Result);
 }
/*===========================================================================
 *		End of Class Method CDFPixelBuilderD3D::CreateLandscape()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFPixelBuilderD3D::CreateLocation()"
/*===========================================================================
 *
 * Class CDFPixelBuilderD3D Method - boolean CreateLocation (void)
 *
 * Creates any locations in the land pixel. Returns FALSE on any error.
 * Protected class method.
 *
 *=========================================================================*/
boolean CDFPixelBuilderD3D::CreateLocation (void) {
  CD3DFrame*		pBlockFrame = NULL;
  CDFRMBFileD3D*	pRMBFile;
  CDFMapPItemRecord*    pPItem;
  dfmap_pitem_footer_t* pFooter;
  HRESULT               Result;
  d3dvalue_t		XPos;
  d3dvalue_t		ZPos;
  int			XCounter;
  int			YCounter;
  int			Counter;
  
	/* Ignore if no location in pixel or the location is already created */
  if (LocationIndex < 0 || CreatedLocation) return (TRUE);

  PROFILE_INC();
  START_PROFILE(ReadRegion);

	/* Read in the region data */
  Result = DFMaps.ReadRegion(RegionIndex, DFMAP_REGION_ALL);
  if (!Result) return (FALSE);

  END_PROFILE(ReadRegion);
  START_PROFILE(GetItems);

	/* Attempt to get the location PItem data */
  pPItem = DFMaps.GetMapPItem (RegionIndex, LocationIndex);
  if (pPItem == NULL) return (FALSE);
  pFooter = pPItem->GetFooter();

  END_PROFILE(GetItems);
  START_PROFILE(BlockLoop);
  PROFILE_INC();

	/* Load and add all valid blocks */
  for (YCounter = 0, Counter = 0; YCounter < pFooter->BlockHeight; YCounter++) {
    for (XCounter = 0; XCounter < pFooter->BlockWidth; XCounter++, Counter++) {

		/* Release the previous block frame, if any */
      RELEASE(pBlockFrame);

      START_PROFILE(CreateBlock);

		/* Create the temporary reference frame */
      Result = GetD3DRM()->CreateFrame(pFrame, &pBlockFrame);

      if (FAILED(Result)) {
        SET_D3D_ERROR2(Result, "Failed to create block record frame!");
        return (FALSE);
       }
    
      END_PROFILE(CreateBlock);
      START_PROFILE(GetRMBFile);

		/* Load the current block information */
      pRMBFile = DFBlocks.GetRMBFileD3D ((int) pFooter->BlockFileIndex[Counter], 
    				         (int) pFooter->BlockFileChar[Counter], 
				         (int) pFooter->BlockFileNumber[Counter]);

		/* TODO: Error loading block, just continue for now */
      if (pRMBFile == NULL) continue;

      END_PROFILE(GetRMBFile);
      START_PROFILE(CreateD3DBlock);

		/* Attempt to create and add the block to the frame */
      Result = pRMBFile->CreateD3DBlock(DFBLK_FLAG_DEFAULT, pBlockFrame);

      END_PROFILE(CreateD3DBlock);
      START_PROFILE(SetPosition);

		/* Move the frame to the appropiate position */
      XPos = D3DVAL(XCounter*2000.0 + (pPItem->GetXPosition() % (long) DFLAND_PIXEL_COORSIZE));
      ZPos = D3DVAL(YCounter*2000.0 + (pPItem->GetZPosition() % (long) DFLAND_PIXEL_COORSIZE));
      pBlockFrame->SetPosition(pFrame, XPos, -300.0, ZPos);

      END_PROFILE(SetPosition);
     }
   }

  PROFILE_DEC();
  END_PROFILE(BlockLoop);
  PROFILE_DEC();

	/* Update the location creation flag */
  SetCreatedLocation(TRUE);
  RELEASE(pBlockFrame);
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFPixelBuilderD3D::CreateLocation()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFPixelBuilderD3D::MakeBuilder()"
/*===========================================================================
 *
 * Class CDFPixelBuilderD3D Method - boolean MakeBuilder (void);
 *
 * Makes a Direct3D builer object from the current land data.  Returns FALSE
 * on any error. Protected class member.
 *
 *=========================================================================*/
boolean CDFPixelBuilderD3D::MakeBuilder (void) {
  CD3DTexture*  pTexture = NULL;
  CD3DMaterial*	pMaterial = NULL;
  CD3DWrap*	pWrap = NULL;
  HRESULT	Result;
  float		FaceSize;

	/* Ensure valid objects */
  ASSERT(GetD3DRM()   != NULL);
  ASSERT(pParentFrame != NULL);

  PROFILE_INC();
  START_PROFILE(CreateFrame);

  	/* Create a destination frame from the D3D object */
  Result = GetD3DRM()->CreateFrame(pParentFrame, &pFrame);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to create landscape frame!"); 
    return (FALSE);
   }

  END_PROFILE(CreateFrame);
  START_PROFILE(CreateBuilder);

	/* Create a mesh builder object from the D3D object */
  Result = GetD3DRM()->CreateMeshBuilder(&pBuilder);

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

  END_PROFILE(CreateBuilder);
  START_PROFILE(AddFaces);

	/* Attempt to build the object using the create vertex/face arrays */
  Result = pBuilder->AddFaces(NumVertices, (D3DVECTOR*)GetVertices(),  0, NULL, GetFaceData(), NULL);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to add faces to the mesh builder!"); 
    return (FALSE);
   }

  END_PROFILE(AddFaces);
  START_PROFILE(GenerateNormals);

	/* Create the normals for the faces */
  Result = pBuilder->GenerateNormals();

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to generate landscape normals!");
    return (FALSE);
   }

  END_PROFILE(GenerateNormals);
  START_PROFILE(SetColor);

	/* Set the default color of the mesh */
  Result = pBuilder->SetColorRGB(D3DVAL(0.9), D3DVAL(0.9), D3DVAL(0.9));

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to set the builder mesh color!");
    return (FALSE);
   }

  END_PROFILE(SetColor);
  START_PROFILE(CreateMaterial);

  	/* Create a default material */
  Result = GetD3DRM()->CreateMaterial(D3DVAL(10.0), &pMaterial);

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

  END_PROFILE(CreateMaterial);
  START_PROFILE(SetMaterial);

	/* Set the mesh material */
  Result = pBuilder->SetMaterial(pMaterial);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to set the mesh builder material!"); 
    RELEASE(pMaterial);
    return (FALSE);
   }

  END_PROFILE(SetMaterial);
  START_PROFILE(CreateWrap);

	/* Create a wrap  */
  FaceSize = (float) ((float) DFLAND_PIXEL_COORSIZE / (float) VertexSize);

  Result = GetD3DRM()->CreateWrap(D3DRMWRAP_FLAT, NULL, 
			      D3DVAL(0.0), 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(1.0), 
			      D3DVAL(1.0/FaceSize), D3DVAL(1.0/FaceSize), 
			      &pWrap);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to create a Direct3D wrap object!"); 
    return (FALSE);
   }

  END_PROFILE(CreateWrap);
  START_PROFILE(GetTexture);

  if (GetSmallPixel() < 3)
    pTexture = DFGetD3DTexture(GetClimateType(), 0);
  else
    pTexture = DFGetD3DTexture(GetClimateType(), 2);

  END_PROFILE(GetTexture);

  if (pTexture != NULL) {
    START_PROFILE(ApplyWrap);

    Result = pWrap->Apply((CD3DObject*) pBuilder);

    if (FAILED(Result)) {
      SET_D3D_ERROR2(Result, "Failed to apply wrap to landscape mesh!");
      RELEASE(pMaterial);
      RELEASE(pWrap);
      return (FALSE);
     } 

    END_PROFILE(ApplyWrap);
    START_PROFILE(SetTexture);

    Result = pBuilder->SetTexture(pTexture);

    if (FAILED(Result)) {
      SET_D3D_ERROR2(Result, "Failed to apply texture to landscape mesh!");
      RELEASE(pMaterial);
      RELEASE(pWrap);
      return (FALSE);
     } 

    END_PROFILE(SetTexture);
   }

  PROFILE_DEC();

  	/* Return success */
  RELEASE(pMaterial);
  RELEASE(pWrap);
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFPixelBuilderD3D::MakeBuilder()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFPixelBuilderD3D::SetFramePosition()"
/*===========================================================================
 *
 * Class CDFPixelBuilderD3D Method - boolean SetFramePosition (XPos, YPos, ZPos);
 *
 * Set the position of the land pixel Direct3D frame.  Returns FALSE on
 * any error.
 *
 *=========================================================================*/
boolean CDFPixelBuilderD3D::SetFramePosition (const float XPos, const float YPos, const float ZPos) {
  HRESULT Result;

	/* Ignore if no frames have been created yet */
  if (pFrame == NULL || pParentFrame == NULL) return (TRUE);

	/* Attempt to set the frame position */
  Result = pFrame->SetPosition(pParentFrame, XPos, YPos, ZPos);
 
  if (FAILED(Result)) { 
    SET_D3D_ERROR2(Result, "Failed to set the landscape pixel frame position!");
    return (FALSE);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFPixelBuilderD3D::SetFramePosition()
 *=========================================================================*/