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

	Imp.cpp

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

	Author:	Pierre-E & Mael

	Date:	7/13/99

	Revision History

	when		who			what & why
	=========== ===========	=============================================================
	08/20/99	cph			Put in Extension3DInit() and Extension3DCleanup() methods


	This file is an example of importer.
	it's a simple importer of facets from a text file.

	the file is a set of surfaces designed as follow :
	number of points
	1st point, x coordinate, y coordinate and z coordinate,
	2nd point, id
	etc...
	last point

	for example, a square is :
	4
	0 0 0
	0 1 0
	1 1 0
	1 0 0


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

#include "Imp.h"
#include "I3DShUtilities.h"
#include "COM3DUtilities.h"
#include "IShUtilities.h"
#include "COMSafeUtilities.h"
#include "I3DShShader.h"
#include "I3DShMasterGroup.h"
#include "I3DShFacetMesh.h"

const MCGUID CLSID_Imp ( R_CLSID_Imp );


void Extension3DInit(IMCUnknown* utilities)
{
	// Perform your dll initialization here
}

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


TBasicUnknown* MakeCOMObject(const MCCLSID& classId)	// This method instanciate
{														// the object COM
	TBasicUnknown* res = NULL;
	
	if (classId == CLSID_Imp) res = new Imp;
	
	return res;
}


Imp::Imp()
{
	fMasterGroup=NULL;
}
// I3DExImportFilter methods :


MCCOMErr Imp::DoImport(IMCFile * file, I3DShScene* scene, I3DShTreeElement* fatherTree)
{
	ThrowIfNil(scene);
	ThrowIfNil(file);

	TMCfstream*  stream = NULL;
	TMCCountedPtr<IMCUnknown> progressKey;

	TMCCountedPtr<I3DShMasterGroup> masterGroup;
	scene->QueryInterface(IID_I3DShMasterGroup,(void**)&masterGroup);

	fMasterGroup = masterGroup;

	try
	{
		//		*****     method to obtain stream:		******
		TMCString1023 fullPathName;
		file->GetFileFullPathName(fullPathName);
		stream = new TMCfstream(fullPathName.StrGet(),kAnsiTranscoder, TMCiostream::in);
		ThrowIfNil(stream);
		
		TMCString255 progressMsg;
		void* oldResources = NULL;
		gResourceUtilities->SetupComponentResources('3Din', 'COEA', &oldResources);
		gResourceUtilities->GetIndString(progressMsg, 130, 1);		// string  1 of STR# 130 (see Imp.r)
		gResourceUtilities->RestoreComponentResources(oldResources);

		gShellUtilities->BeginProgress(progressMsg, &progressKey);
		
		TMCCountedPtr<I3DShTreeElement> topTree;

		if (fatherTree == NULL)
		{
			TMCCountedPtr<I3DShGroup> 	topGroup;
			scene->CreateTreeRootIfNone();
			MCCOMErr err = scene->GetTreeRoot(&topGroup);
			topGroup->QueryInterface(IID_I3DShTreeElement,(void**)  &topTree);
		}
		else
		{
			gComponentUtilities->CoCreateInstance(CLSID_StandardGroup, NULL, MC_CLSCTX_INPROC_SERVER, IID_I3DShTreeElement, (void**) &topTree);
			ThrowIfNil(topTree);
			topTree->SetMasterGroup(masterGroup);
			fatherTree->InsertLast(topTree);
		}
		
		scene->CreateRenderingCameraIfNone('coni', (fatherTree == NULL));	// Create a conical rendering camera if none, and a Distant light if we not importing in an existing scene
		DoReadEasyFile(stream, scene, topTree);


		delete stream;
		gShellUtilities->EndProgress(progressKey);
	}
	
	catch (TMCException& exception)
	{
		delete stream; 
		gShellUtilities->EndProgress(progressKey);
		throw exception;
	}

	fMasterGroup=NULL;

	return MC_S_OK;
}


MCCOMErr ReadStreamVector3D(TMCfstream* stream, TVector3* res)
{
	(*stream) >> (*res)[0];
	(*stream) >> (*res)[1];
	(*stream) >> (*res)[2];
	
	return MC_S_OK;
}



TFacet3D* SetEasyFacet(TVector3 pA,TVector3 pB,TVector3 pC) {
	TFacet3D* facet=new TFacet3D;
	
	TVector3 vAB=pB-pA;
	TVector3 vAC=pC-pA;
	TVector3 vBC=pC-pB;
	
	facet->fVertices[0].fNormal=vAB ^ vAC;
	facet->fVertices[1].fNormal=vAB ^ vBC;
	facet->fVertices[2].fNormal=vAC ^ vBC;
	
	facet->fVertices[0].fVertex[0]=pA[0];
	facet->fVertices[0].fVertex[1]=pA[1];
	facet->fVertices[0].fVertex[2]=pA[2];
	facet->fVertices[1].fVertex[0]=pB[0];
	facet->fVertices[1].fVertex[1]=pB[1];
	facet->fVertices[1].fVertex[2]=pB[2];
	facet->fVertices[2].fVertex[0]=pC[0];
	facet->fVertices[2].fVertex[1]=pC[1];
	facet->fVertices[2].fVertex[2]=pC[2];
	
	facet->fVertices[0].fNormal.Normalize();
	facet->fVertices[1].fNormal.Normalize();
	facet->fVertices[2].fNormal.Normalize();
	
	facet->fReserved=0;
	return facet;
}


void Imp::DoReadEasyFile(TMCfstream* stream, I3DShScene* scene, I3DShTreeElement* topTree)
{
	long	nbPoints;
	int		i;
	
	TVector3			currentPoint;
	Triangle			trFacet;
	
	TMCCountedPtr<I3DShInstance> instance;
	TMCCountedPtr<I3DShTreeElement> instanceTree;
	
	TMCString31			objName;
	TMCString31			countstring;
	TMCString31			easystring;
	// DO NOIT USE HARD-CODED STRINGS : easystring.FromText("Easy",4);
	// Instead use CMP# resources; like this :
	void* oldResources = NULL;
	gResourceUtilities->SetupComponentResources('3Din', 'COEA', &oldResources);
	gResourceUtilities->GetIndString(easystring, 130, 2);		// string 2 of STR# 130 (see Imp.r)
	gResourceUtilities->RestoreComponentResources(oldResources);

	int32				counter=0;
	
	(*stream) >> nbPoints;
	
	try
	{
		while (nbPoints)
		{
			TMCCountedPtr<I3DShObject> shObject;
			TMCCountedPtr<I3DShShader> multiShader;
			TMCCountedPtr<I3DShShader> colorShader;
			TMCCountedPtr<I3DShShader> highlightShader;
			TMCCountedPtr<I3DShShader> shininessShader;
			TMCCountedPtr<I3DShShader> reflectionShader;
			TMCCountedPtr<I3DShShader> transparencyShader;
			TMCCountedPtr<I3DShMasterShader> masterShader;
			TMCCountedPtr<I3DShFacetMeshPrimitive> facetMeshPrimitive;

			gShell3DUtilities->CreatePrimitiveByID(kFacetMeshPrimitiveID, &shObject, IID_I3DShFacetMeshPrimitive, (void**) &facetMeshPrimitive);
			ThrowIfNil(facetMeshPrimitive);

			// fill in the mesh and give it to the primitive
			TMCCountedPtr<FacetMesh> theMesh;
			FacetMesh::Create(&theMesh);
			ThrowIfNil(theMesh);
			
			// Set the number of Points and Facets
			(theMesh->fVertices).SetElemCount (nbPoints);
			(theMesh->fuv).SetElemCount (nbPoints);
			(theMesh->fNormals).SetElemCount (nbPoints);

			(theMesh->fFacets).SetElemCount (nbPoints-2);
			(theMesh->fUVSpaceID).SetElemCount (nbPoints-2);

			// Initialize the two first points
			ThrowIfError(ReadStreamVector3D(stream,&currentPoint));
			(theMesh->fVertices)[0] = currentPoint;
			ThrowIfError(ReadStreamVector3D(stream,&currentPoint));
			(theMesh->fVertices)[1] = currentPoint;
			
			TVector3	normal;	

			// Create the whole FacetMesh
			for (i=2; i<nbPoints; i++)
			{
				ThrowIfError(ReadStreamVector3D(stream,&currentPoint));
				(theMesh->fVertices)[i] = currentPoint;
				trFacet.pt1 = 0;
				trFacet.pt2 = i-1;
				trFacet.pt3 = i;
				(theMesh->fFacets)[i-2] = trFacet;
				
				// Set all normals to the same value on each vertex of a facet
				normal = ((theMesh->fVertices)[trFacet.pt2] - (theMesh->fVertices)[trFacet.pt1]) ^ ((theMesh->fVertices)[trFacet.pt3] - (theMesh->fVertices)[trFacet.pt1]);
				normal.Normalize();
				(theMesh->fNormals).SetElem (i-1, normal);

				// Set all facets to the same uvspace (of index 0)
				(theMesh->fUVSpaceID).SetElem (i-2, 0);
			}
			// normal for the first and last point
			theMesh->fNormals[0] = normal;
			theMesh->fNormals[nbPoints-1] = normal;

			facetMeshPrimitive->SetNbrLOD(1); // Must be called before 'SetFacetMesh'
			facetMeshPrimitive->SetFacetMesh(0, 0, theMesh);
			

			// We need to compute the bounding box
			TBBox3D bbox;
			bbox.SetMin((theMesh->fVertices)[0]);
			bbox.SetMax((theMesh->fVertices)[0]);
			for (i=1; i<nbPoints; i++)
			{
				bbox.AddPoint((theMesh->fVertices)[i]);
			}

			facetMeshPrimitive->SetBoundingBox(bbox);

			// Set uv values for each vertex
			// note :	this is just an example; we use the bbox to compute the UV,
			//			without paying any attention to the connectivity of the vertices (which is probably better)
			real boxWidth = bbox.fMax.x - bbox.fMin.x;
			real boxHeight = bbox.fMax.y - bbox.fMin.y;
			for (i=0; i<nbPoints; i++)
			{
				TVector2	uv((theMesh->fVertices)[i].x/boxWidth, (theMesh->fVertices)[i].y/boxHeight);
				(theMesh->fuv).SetElem (i, uv);
			}
			
			TMCColorRGB defColor;
			defColor.R = 1.0;
			defColor.G = 0.0;
			defColor.B = 0.0;

			gShell3DUtilities->CreateColorShader(defColor, &colorShader);
			gShell3DUtilities->CreateValueShader(1.0, &highlightShader);
			gShell3DUtilities->CreateValueShader(1.0, &shininessShader);
			gShell3DUtilities->CreateValueShader(0.0, &reflectionShader);
			gShell3DUtilities->CreateValueShader(0.0, &transparencyShader);
			gShell3DUtilities->CreateMultiChannelShader(
							colorShader, highlightShader, shininessShader, NULL, reflectionShader,
							transparencyShader,	NULL, NULL, &multiShader);

			gShell3DUtilities->CreateMasterShader(multiShader, scene, &masterShader);

			countstring.FromInt32(++counter);
			objName = easystring + countstring;

			shObject->SetName(objName);
			masterShader->SetName(objName);

			// create tree instance for the facet mesh object
			gComponentUtilities->CoCreateInstance(CLSID_StandardInstance, NULL,
				MC_CLSCTX_INPROC_SERVER, IID_I3DShInstance, (void**) &instance);

			instance->Set3DObject(shObject);
			instance->SetShader (masterShader);
			instance->QueryInterface(IID_I3DShTreeElement, (void**) &instanceTree);

			MCAssert(fMasterGroup);
			instanceTree->SetMasterGroup(fMasterGroup);

			instanceTree->CenterHotPointOnElement();
			topTree->InsertLast(instanceTree);
			
			(*stream) >> nbPoints;
		}
	}
	catch (TMCException& exception)
	{
		throw exception;
	}
}
