/****************************************************************************************

	Explode.cpp

	Copyright (c)1990 - 1999 MetaCreations. All rights reserved.

	Author:	Guillaume Chermette

	Date:	08/23/99

	Revision History

	when		who			what & why
	=========== ===========	=============================================================
	
****************************************************************************************/

#include "Explode.h"
#include "MCCountedPtrHelper.h"
#include "COMUtilities.h"
#include "IShUtilities.h"
#include "I3DShUtilities.h"


const MCGUID CLSID_Explode(R_CLSID_Explode);


void Extension3DInit(IMCUnknown* utilities)
{
	// Perform your dll initialisation there
}

void Extension3DCleanup()
{
	// Perform any nec clean-up here
}

TBasicUnknown* MakeCOMObject (const MCCLSID& classId) // This method instantiates the object Explode
{
	TBasicUnknown* res = NULL;

	if (classId == CLSID_Explode)
		res = new Explode;

	return res;
}

int16 Explode::GetResID()
{
	return 143;		// This is the view ID in the resource file
}

// Constructor / Destructor of the C++ Object :
Explode::Explode() {
  
  // Data initialisation :
	fData.fMinSize = 1.0; 
	fData.fExplosionFactor = 0.0;
	fData.fFloor = 0.0;
	fData.fGravityCoef = 9.81f;
	fData.fApplyGravity = false;

	fData.fBoundingBox.fMin[0] = 0.0;
	fData.fBoundingBox.fMin[1] = 0.0;
	fData.fBoundingBox.fMin[2] = 0.0;
	fData.fBoundingBox.fMax[0] = 0.0;
	fData.fBoundingBox.fMax[1] = 0.0;
	fData.fBoundingBox.fMax[2] = 0.0;

	fFlags.SetMasked(TModifierFlags::kDisableBackFacing);
}
  
void* Explode::GetExtensionDataBuffer()
{
  return &fData;					// used by the shell to set the new parameters
}
  
void Explode::SetBoundingBox(const TBBox3D& bbox)
{
  fData.fBoundingBox = bbox;
}

MCCOMErr Explode::DeformPoint(const TVector3& point, TVector3& result)
{
  TVector3 Translate;
  int i;

  Translate[0] = point[0] * fData.fExplosionFactor;
  Translate[1] = point[1] * fData.fExplosionFactor;
  if (!fData.fApplyGravity)
    Translate[2] = point[2] * fData.fExplosionFactor;
  else {
    if (point[2] <= fData.fFloor) 
      Translate[2] = 0.0;
    else 
	  Translate[2] = point[2] * fData.fExplosionFactor - fData.fGravityCoef * (fData.fExplosionFactor * fData.fExplosionFactor);
	if (point[2] + Translate[2] < fData.fFloor)
	  Translate[2] = fData.fFloor - point[2];
  }
  for (i=0; i<3; i++)
  	result[i] = point[i] + Translate[i];
  
  return MC_S_OK;
}

// EVX calls for Mac/EVX case
/*extern "C" {

	void  OLXIteratorReset(IShIterator* iter);
	void* OLXIteratorNext(IShIterator* iter);
	void* OLXIteratorGetInfo(IShIterator* iter,short index);
	void  OLXIteratorDelete(IShIterator* iter);
} */

// Split a Facet between point p1 and p3
//   p1____________ p3
//     \    |    /
//      \ f1|f2 /
//       \  |  /
//        \ | /
//         \|/
//          p2
void SplitFacet(const TFacet3D *f0, TFacet3D *f1, TFacet3D *f2, int16 p1, int16 p2, int16 p3) {

  f1->fVertices[0] = f0->fVertices[p1]; // Copy normal/UV/point
  f1->fVertices[1] = f0->fVertices[p2];
  f1->fVertices[2].fVertex[0] = (f0->fVertices[p1].fVertex[0] + f0->fVertices[p3].fVertex[0]) / 2.0;
  f1->fVertices[2].fVertex[1] = (f0->fVertices[p1].fVertex[1] + f0->fVertices[p3].fVertex[1]) / 2.0;
  f1->fVertices[2].fVertex[2] = (f0->fVertices[p1].fVertex[2] + f0->fVertices[p3].fVertex[2]) / 2.0;
  f1->fVertices[2].fNormal[0] = (f0->fVertices[p1].fNormal[0] + f0->fVertices[p3].fNormal[0]) / 2.0;
  f1->fVertices[2].fNormal[1] = (f0->fVertices[p1].fNormal[1] + f0->fVertices[p3].fNormal[1]) / 2.0;
  f1->fVertices[2].fNormal[2] = (f0->fVertices[p1].fNormal[2] + f0->fVertices[p3].fNormal[2]) / 2.0;
  f1->fVertices[2].fUV[0] = (f0->fVertices[p1].fUV[0] + f0->fVertices[p3].fUV[0]) / 2;
  f1->fVertices[2].fUV[1] = (f0->fVertices[p1].fUV[1] + f0->fVertices[p3].fUV[1]) / 2;
  f1->fUVSpace = f2->fUVSpace = f0->fUVSpace;
  f2->fVertices[0] = f0->fVertices[p2];
  f2->fVertices[1] = f0->fVertices[p3];
  f2->fVertices[2] = f1->fVertices[2];
}

// The criteria to split a facet is the longest size on the X axis
void SplitFacetX(const TFacet3D *aFacet, TFacet3D *f1,TFacet3D *f2) {

  int16 p1,p2,p3;
  real dx1,dx2,dx3;

  dx1 = aFacet->fVertices[1].fVertex[0] - aFacet->fVertices[0].fVertex[0];
  dx2 = aFacet->fVertices[2].fVertex[0] - aFacet->fVertices[1].fVertex[0];
  dx3 = aFacet->fVertices[0].fVertex[0] - aFacet->fVertices[2].fVertex[0];

  if (dx1 < 0.0) dx1 = -dx1;
  if (dx2 < 0.0) dx2 = -dx2;
  if (dx3 < 0.0) dx3 = -dx3;

  if ((dx1>=dx2)&&(dx1>=dx3)) {
    p1=1;p2=2;p3=0;
    // split the facet between p1 and p3
  }
  else if ((dx2>=dx1)&&(dx2>=dx3)) {
	p1=2;p2=0;p3=1;
	// split the facet between p1 and p3
  }
  else {
    p1=0;p2=1;p3=2;
    // split the facet between p1 and p3
  }
  SplitFacet(aFacet,f1,f2,p1,p2,p3);  
}

// The criteria to split a facet is the longest size on the Y axis
void SplitFacetY(const TFacet3D *aFacet, TFacet3D *f1, TFacet3D *f2) {

  int16 p1,p2,p3;
  real dy1,dy2,dy3;

  dy1 = aFacet->fVertices[1].fVertex[1] - aFacet->fVertices[0].fVertex[1];
  dy2 = aFacet->fVertices[2].fVertex[1] - aFacet->fVertices[1].fVertex[1];
  dy3 = aFacet->fVertices[0].fVertex[1] - aFacet->fVertices[2].fVertex[1];

  if (dy1<0.0) dy1 = -dy1;
  if (dy2<0.0) dy2 = -dy2;
  if (dy3<0.0) dy3 = -dy3;

  if ((dy1>=dy2)&&(dy1>=dy3)) {
    p1=1;p2=2;p3=0;
    // split the facet between p1 and p3
    }
  else if ((dy2>=dy1)&&(dy2>=dy3)) {
    p1=2;p2=0;p3=1;
    // split the facet between p1 and p3
    }
  else {
    p1=0;p2=1;p3=2;
    // split the facet between p1 and p3
    }
  SplitFacet(aFacet,f1,f2,p1,p2,p3);  
  }

// The criteria to split a facet is the longest size on the Z axis
void SplitFacetZ(const TFacet3D* aFacet, TFacet3D* f1, TFacet3D* f2) {

  int16 p1,p2,p3;
  real dz1,dz2,dz3;

  dz1 = aFacet->fVertices[1].fVertex[2] - aFacet->fVertices[0].fVertex[2];
  dz2 = aFacet->fVertices[2].fVertex[2] - aFacet->fVertices[1].fVertex[2];
  dz3 = aFacet->fVertices[0].fVertex[2] - aFacet->fVertices[2].fVertex[2];

  if (dz1<0.0) dz1 = -dz1;
  if (dz2<0.0) dz2 = -dz2;
  if (dz3<0.0) dz3 = -dz3;

  if ((dz1>=dz2)&&(dz1>=dz3)) {
    p1=1;p2=2;p3=0;
    // split the facet between p1 and p3
    }
  else if ((dz2>=dz1)&&(dz2>=dz3)) {
    p1=2;p2=0;p3=1;
    // split the facet between p1 and p3
    }
  else {
    p1=0;p2=1;p3=2;
    // split the facet between p1 and p3
    }
  SplitFacet(aFacet,f1,f2,p1,p2,p3);  
  }

// Verify Size of the facet to split it if necessary and move it.
void Explode::explodeFacet(const TFacet3D* aFacet, FacetMeshAccumulator* accu, int16 level) {

  int16 i,j;
  real sx,sy,sz;
  // Calcul size of the Facet
  TBBox3D facetbb;

  for(i=0;i<3;i++) facetbb.fMin[i] = facetbb.fMax[i] = aFacet->fVertices[0].fVertex[i];
  for(i=1;i<3;i++) {
    for (j=0;j<3;j++) {
      if (facetbb.fMin[j] > aFacet->fVertices[i].fVertex[j])
        facetbb.fMin[j] = aFacet->fVertices[i].fVertex[j];
      if (facetbb.fMax[j] < aFacet->fVertices[i].fVertex[j])
        facetbb.fMax[j] = aFacet->fVertices[i].fVertex[j];
      }
    }
  sx = facetbb.fMax[0]-facetbb.fMin[0];
  sy = facetbb.fMax[1]-facetbb.fMin[1];
  sz = facetbb.fMax[2]-facetbb.fMin[2];

  //if (fData.fMinSize<1.0/16.0 || level>SPLITMAX) {
  if (fData.fMinSize<0.1 || level > SPLITMAX) {
	  accu->AccumulateFacet(aFacet);
  }
  else if (sx>fData.fMinSize) {
    TFacet3D f1,f2;
    SplitFacetX(aFacet, &f1, &f2);
    explodeFacet(&f1, accu, level+1);
    explodeFacet(&f2, accu, level+1);    
  }
  else if (sy>fData.fMinSize) {
    TFacet3D f1,f2;
    SplitFacetY(aFacet, &f1, &f2);
    explodeFacet(&f1, accu, level+1);
    explodeFacet(&f2, accu, level+1);
  }
  else if (sz>fData.fMinSize) {
    TFacet3D f1,f2;
    SplitFacetZ(aFacet, &f1, &f2);
    explodeFacet(&f1, accu, level+1);
    explodeFacet(&f2, accu, level+1);
    }
  else {
    TFacet3D movedFacet=*aFacet;
    moveFacet(&movedFacet);
	accu->AccumulateFacet(&movedFacet);
  }
}

void Explode::moveFacet(TFacet3D *aFacet) {

  TVector3 BaryCentre;
  TVector3 Translate;
  int i;

  for (i=0; i<3; i++) {
		BaryCentre[i] = aFacet->fVertices[0].fVertex[i] + aFacet->fVertices[1].fVertex[i]
						+ aFacet->fVertices[2].fVertex[i];
		BaryCentre[i] /= 3.0;
  }
  Translate[0] = BaryCentre[0] * fData.fExplosionFactor;
  Translate[1] = BaryCentre[1] * fData.fExplosionFactor;
  if (!fData.fApplyGravity)
      Translate[2] = BaryCentre[2] * fData.fExplosionFactor;
  else {
    if (BaryCentre[2] <= fData.fFloor) 
      Translate[2] = 0.0;
    else {
      Translate[2] = BaryCentre[2] * fData.fExplosionFactor - fData.fGravityCoef * (fData.fExplosionFactor * fData.fExplosionFactor);
      }
    if (BaryCentre[2]+Translate[2] < fData.fFloor) {
      Translate[2] = fData.fFloor - BaryCentre[2];
      }
    }
	for (i=0; i<3; i++) {
		aFacet->fVertices[0].fVertex[i] += Translate[i];
		aFacet->fVertices[1].fVertex[i] += Translate[i];
		aFacet->fVertices[2].fVertex[i] += Translate[i];
		}
  }

MCCOMErr Explode::DeformFacetMesh(real lod,FacetMesh* in, FacetMesh** outMesh) {

	FacetMeshAccumulator accu;
	FacetMeshFacetIterator iter;
	iter.Initialize(in);
	for (iter.First(); iter.More(); iter.Next()) {
		explodeFacet(&iter.GetFacet(), &accu);
	}
	accu.MakeFacetMesh(outMesh);
  return MC_S_OK;
}

MCCOMErr Explode::DeformBBox(const TBBox3D &in, TBBox3D &out) {

	DeformPoint(in.fMin, out.fMin);
	DeformPoint(in.fMax, out.fMax);
	return MC_S_OK;
}
