#include "../headers/MVector3.h"
#include "../headers/MVector4.h"
#include "../headers/GeMesh.h"
#include "../headers/GeActor.h"
#include "../headers/GeBlendableMesh.h"
#include "../headers/FBXConverter.h"
#include "../headers/PhysXWrapper.h"

KFbxGeometryConverter* FBXConverter :: geomConverter = NULL;

GeLight* FBXConverter :: light(KFbxNode* node, KFbxXMatrix matrix) {
	GeLight* ret = NULL;
	KFbxLight* light = (KFbxLight*)node->GetNodeAttribute();
	KFbxColor color;
	light->GetDefaultColor(color);
	MColor4 col4((float)color.mRed, (float)color.mGreen, (float)color.mBlue, (float)color.mAlpha);
	//col4 *= light->GetDefaultIntensity()/200.0f;
	KFbxLight::ELightType type = light->GetLightType();
	if (type == KFbxLight::ePOINT) {
		ret = new GeLight();
		KFbxVector4 pos = matrix.GetT();
		ret->setPosition((float)pos[0], (float)pos[1], (float)pos[2]);
		ret->setDiffuse(col4.r, col4.g, col4.b, col4.a);
		ret->setSpecular(col4.r, col4.g, col4.b, col4.a);
		ret->setAttenuation(GL_CONSTANT_ATTENUATION, 1.0f); // * 100/light->GetDefaultIntensity()
		ret->setAttenuation(GL_LINEAR_ATTENUATION, (float)(0.001 * 100/light->GetDefaultIntensity()));
		ret->setAttenuation(GL_QUADRATIC_ATTENUATION, (float)(0.01 * 100/light->GetDefaultIntensity()));
		//ret->show();
	}
	return ret;
}

void FBXConverter :: getGeometry(KFbxMesh* pMesh, KFbxXMatrix matrix, GeGeometry* geometry, GeTexture** tex) {
	*tex = NULL;

	/* get Texture Mapping Mode and UV Coordinates */
	double textureScaleU, textureScaleV;
	KFbxVector2* lUVArray = NULL;
	KFbxLayerElement::EMappingMode lTextureMappingMode;

	int layerindex = pMesh->GetLayerIndex(0,KFbxLayerElement::eDIFFUSE_TEXTURES);
	if (layerindex != -1) {
		lUVArray = pMesh->GetTextureUV();
		lTextureMappingMode =
			pMesh->GetLayer(layerindex)->GetUVs()->GetMappingMode();
		if ((lTextureMappingMode == KFbxLayerElement::eBY_CONTROL_POINT) ||
			(lTextureMappingMode == KFbxLayerElement::eBY_POLYGON_VERTEX)  ) {
			KFbxLayerElementTexture* lTextureLayer = pMesh->GetLayer(layerindex)->
				GetDiffuseTextures();
			if ((lTextureLayer != NULL) && (lTextureLayer->GetDirectArray().GetCount() > 0) &&
				(lTextureLayer->GetMappingMode() == KFbxLayerElement::eALL_SAME) &&
				(lTextureLayer->GetReferenceMode() != KFbxLayerElement::eINDEX))
			{
				textureScaleU = lTextureLayer->GetDirectArray().GetAt(0)->GetScaleU();
				textureScaleV = lTextureLayer->GetDirectArray().GetAt(0)->GetScaleV();
				KString file = lTextureLayer->GetDirectArray().GetAt(0)->GetFileName();
				file = *(new KString("textures/"))+file.GetToken(file.GetTokenCount("\\")-1,"\\");
				*tex = GeTexture::getTexture(file.Buffer());
			}
		}
		else {
			printf("Unable to handle Mapping Mode: %d!\n", lTextureMappingMode);
		}
	}

	/* copy geometry data */
	int lPolygonVertexCount = pMesh->GetPolygonVertexCount();
	int cpCount = pMesh->GetControlPointsCount();

	pMesh->BuildMeshEdgeArray();

	//memcpy(geometry->transformation, (double*)matrix, sizeof(double)*16);
	geometry->vertices = new GLdouble[lPolygonVertexCount*3];
	geometry->normals = new GLdouble[lPolygonVertexCount*3];
	geometry->edgeCount = pMesh->GetMeshEdgeCount();
	geometry->edges = new GeEdge[geometry->edgeCount];
	memset(geometry->edges, -1, sizeof(GeEdge)*geometry->edgeCount);
	if (*tex!=NULL) {
		geometry->texcoord = new GLdouble[lPolygonVertexCount*2];
	}
	else {
		geometry->texcoord = NULL;
	}
    geometry->vertexcount = lPolygonVertexCount;
	geometry->polygon_vertex_count = lPolygonVertexCount;	

	/* copy geometry data per polygon vertex and calculate the radius */
	/* of a bounding sphere on the center (not the origin)            */
	KFbxVector4* pVertexArray = pMesh->GetControlPoints();
	KFbxVector4 normal;
	int index = 0, lCurrentUVIndex, polyindex;
	GLdouble vertex[3];
	
	for (int i=0; i < pMesh->GetPolygonCount(); i++) {
		polyindex = index;
		for (int j=0; j < 3; j++) {
			int vindex = pMesh->GetPolygonVertex(i,j);
			KFbxVector4 v = pVertexArray[vindex];
			vertex[0] = (NxReal)(v[0]*matrix[0] + v[1]*matrix[4] + v[2]*matrix[8]  + matrix[12]);
			vertex[1] = (NxReal)(v[0]*matrix[1] + v[1]*matrix[5] + v[2]*matrix[9]  + matrix[13]);
			vertex[2] = (NxReal)(v[0]*matrix[2] + v[1]*matrix[6] + v[2]*matrix[10] + matrix[14]);

			memcpy(geometry->vertices + index*3, (GLdouble *)vertex, 3*sizeof(GLdouble));
		
			//pMesh->GetPolygonVertexNormal(i, j, normal);
			KFbxXMatrix transNormMat(matrix);
			KFbxVector4 invScale = transNormMat.GetS();
			invScale[0] = 1/invScale[0];
			invScale[1] = 1/invScale[1];
			invScale[2] = 1/invScale[2];
			
			transNormMat.SetS(invScale);
			transNormMat.SetT(KFbxVector4());

			KFbxVector4 n;
			pMesh->GetPolygonVertexNormal(i, j, n);
			normal[0] = (NxReal)(n[0]*transNormMat[0] + n[1]*transNormMat[4] + n[2]*transNormMat[8]  + transNormMat[12]);
			normal[1] = (NxReal)(n[0]*transNormMat[1] + n[1]*transNormMat[5] + n[2]*transNormMat[9]  + transNormMat[13]);
			normal[2] = (NxReal)(n[0]*transNormMat[2] + n[1]*transNormMat[6] + n[2]*transNormMat[10] + transNormMat[14]);
			normal[3] = 0;
			normal.Normalize();
			
			memcpy(geometry->normals + index*3, (GLdouble *)normal, 3*sizeof(GLdouble));

			int eindex = pMesh->GetMeshEdgeIndexForPolygon(i, j);
			if (geometry->edges[eindex].vertexIndex[0] >= 0) {
				geometry->edges[eindex].triIndex[1] = i;
			}
			else {
				geometry->edges[eindex].vertexIndex[0] = polyindex+j;
				geometry->edges[eindex].vertexIndex[1] = polyindex+(1+j)%3;
				geometry->edges[eindex].triIndex[0] = i;
			}
			//pMesh->GetMeshEdgeVertices(int pEdgeIndex, int &pStartVertexIndex, int &pEndVertexIndex)	

			if (*tex != NULL) {
				if (lTextureMappingMode == KFbxLayerElement::eBY_POLYGON_VERTEX) {
					lCurrentUVIndex = pMesh->GetTextureUVIndex(i, j);
				}
				else { // KFbxLayerElement::eBY_CONTROL_POINT
					lCurrentUVIndex = vindex;
				}
				geometry->texcoord[index*2] = lUVArray[lCurrentUVIndex][0]*textureScaleU;
				geometry->texcoord[index*2+1] = lUVArray[lCurrentUVIndex][1]*textureScaleV;
			}
			index ++;
		}
	}
}

GeMaterial FBXConverter :: getMaterial(KFbxMesh* pMesh) {
	/* set the material if available */
	GeMaterial material;
	int layerindex = pMesh->GetLayerIndex(0,KFbxLayerElement::eMATERIAL);
	if (layerindex != -1) {
		KFbxLayerElementMaterial* lMaterialLayer = pMesh->GetLayer(layerindex)->GetMaterials();
		if (lMaterialLayer != NULL) {
			KFbxSurfacePhong* lMaterial = (KFbxSurfacePhong*)lMaterialLayer->GetDirectArray().mArray[0];
			double ambientColor[4];
			double ambientFactor;
			double diffuseColor[4];
			double diffuseFactor;
			double specularColor[4];
			double specularFactor;
			double shininess;
			double transparency;
			lMaterial->GetAmbientColor().Get(ambientColor, EFbxType::eDOUBLE4);
			lMaterial->GetAmbientFactor().Get(&ambientFactor, EFbxType::eDOUBLE1);
			lMaterial->GetDiffuseColor().Get(diffuseColor, EFbxType::eDOUBLE4);
			lMaterial->GetDiffuseFactor().Get(&diffuseFactor, EFbxType::eDOUBLE1);
			lMaterial->GetSpecularColor().Get(specularColor, EFbxType::eDOUBLE4);
			lMaterial->GetSpecularFactor().Get(&specularFactor, EFbxType::eDOUBLE1);
			lMaterial->GetShininess().Get(&shininess, EFbxType::eDOUBLE1);

			material.ambient = ambientColor;
			material.ambient *= ambientFactor;
			material.diffuse = diffuseColor;
			material.diffuse *= diffuseFactor;
			material.specular = specularColor;
			material.specular *= specularFactor;
			material.shininess = (GLfloat)shininess;

			lMaterial->GetTransparencyFactor().Get(&transparency, EFbxType::eDOUBLE1);
			material.diffuse.a = (float)(1.0-transparency);
		}
	}
	return material;
}

Trigger* FBXConverter :: trigger(KFbxNode* node, KFbxXMatrix matrix) {
	GeGeometry* geometry = new GeGeometry;
	GeTexture* texture = NULL;
	KFbxMesh* pMesh = (KFbxMesh*) node->GetNodeAttribute();
	string name = node->GetNameOnly();
	NxActor *actor = NULL;
	Trigger* ret = NULL;

	string action = name.substr(2, name.length()-4);
	if(actionMap.count(action) > 0) {
		matrix = matrix * getGeometricTransformation(node);

		getGeometry(pMesh, matrix, geometry, &texture);

		/* calculate the bounding box, the center and the bounding sphere */
		NxVec3 center, min, max;
		double radius = 0.0f;

		min.x = (float)geometry->vertices[0];
		min.y = (float)geometry->vertices[1];
		min.z = (float)geometry->vertices[2];
		max = min;
		for (unsigned int i=1; i<geometry->polygon_vertex_count; i++) {
			double* vertex = geometry->vertices + i*3;
			
			if ((float)vertex[0] > max[0]) max[0] = (float)vertex[0];
			if ((float)vertex[1] > max[1]) max[1] = (float)vertex[1];
			if ((float)vertex[2] > max[2]) max[2] = (float)vertex[2];
			
			if ((float)vertex[0] < min[0]) min[0] = (float)vertex[0];
			if ((float)vertex[1] < min[1]) min[1] = (float)vertex[1];
			if ((float)vertex[2] < min[2]) min[2] = (float)vertex[2];
		}
		center = (min+max)/2;
		max = max - center;

		for (unsigned int i=0; i<geometry->polygon_vertex_count; i++) {
			double* v = geometry->vertices + i*3;
			double distance = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
			if (distance > radius) radius = distance;
		}

		delete(geometry->vertices);
		delete(geometry->normals);
		if (geometry->texcoord != NULL) {
			delete(texture);
			delete(geometry->texcoord);
		}
		delete(geometry);

		/* setup PhysX Actor */
		NxShapeDesc* shapeDesc;
		NxActorDesc actorDesc;

		if (name[1] == 'S') { // sphere shape
			NxSphereShapeDesc* sphereDesc = new NxSphereShapeDesc();
			sphereDesc->radius = sphereDesc->radius;
			sphereDesc->localPose = sphereDesc->localPose;
			shapeDesc = sphereDesc;
		}
		else if (((name[1] != 'M') || ((shapeDesc = cookMesh(pMesh, matrix)) == NULL)) &&
				 ((name[1] != 'C') || ((shapeDesc = cookConvex(pMesh, matrix)) == NULL)) )
		{
			NxBoxShapeDesc* bboxDesc = new NxBoxShapeDesc();
			bboxDesc->dimensions = NxVec3((NxReal)max[0],(NxReal)max[1],(NxReal)max[2]);
			NxQuat q;
			q.fromAngleAxis(180, NxVec3(1, 0, 0));
			bboxDesc->localPose.M.fromQuat(q);
			bboxDesc->localPose.t = NxVec3((NxReal)center[0], (NxReal)center[1], (NxReal)center[2]);
			shapeDesc = bboxDesc;
		}

		shapeDesc->group = SHAPE_TRIGGER;
		shapeDesc->shapeFlags |= NX_TRIGGER_ON_ENTER | NX_TRIGGER_ON_LEAVE;
		actorDesc.shapes.push_back(shapeDesc);
		actorDesc.group = TYPENO_TRIGGER;
		actorDesc.body = NULL;

		ret = new Trigger();
		ret->actionCallback = actionMap[action];
		ret->id = name.substr(name.length()-2);
		actorDesc.userData = (void*)ret;

		if (actorDesc.isValid()) {
			actor = physx.scene->createActor(actorDesc);
			ret->actor = actor;
		}
		else {
			printf("Invalid ActorDesc!\n");
			exit(0);
		}
		delete shapeDesc;
	}

	return ret;
}

ActorPrototype FBXConverter :: mesh(KFbxNode* node, KFbxXMatrix matrix, bool staticActor) {
	GeGeometry* geometry = new GeGeometry;
	ActorPrototype ret;
	GeTexture* texture = NULL;
	matrix = matrix * getGeometricTransformation(node);
	
    KFbxMesh* pMesh = (KFbxMesh*) node->GetNodeAttribute();

	getGeometry(pMesh, matrix, geometry, &texture);

	/* calculate the bounding box, the center and the bounding sphere */
	NxVec3 center, min, max;
	double radius = 0.0f;

	min.x = (float)geometry->vertices[0];
	min.y = (float)geometry->vertices[1];
	min.z = (float)geometry->vertices[2];
	max = min;
	for (unsigned int i=1; i<geometry->polygon_vertex_count; i++) {
		double* vertex = geometry->vertices + i*3;
		
		if ((float)vertex[0] > max[0]) max[0] = (float)vertex[0];
		if ((float)vertex[1] > max[1]) max[1] = (float)vertex[1];
		if ((float)vertex[2] > max[2]) max[2] = (float)vertex[2];
		
		if ((float)vertex[0] < min[0]) min[0] = (float)vertex[0];
		if ((float)vertex[1] < min[1]) min[1] = (float)vertex[1];
		if ((float)vertex[2] < min[2]) min[2] = (float)vertex[2];
	}
	center = (min+max)/2;
	max = max - center;

	for (unsigned int i=0; i<geometry->polygon_vertex_count; i++) {
		double* v = geometry->vertices + i*3;
		double distance = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
		if (distance > radius) radius = distance;
	}

	/* create mesh and its texture if available */
	int type = 0;
	bool castShadow = false;
	KString name = node->GetNameOnly();
	if (name[0] == 'W') {
		type = TYPENO_WALL;
	} 
	else if (name[0] == 'S') {
		type = TYPENO_WALL;
		castShadow = true;
	}
	else if (name[0] == 'I') {
		type = TYPENO_ITEM;
	}

	/* setup PhysX Actor */
	ret.actorDesc = new NxActorDesc();
	NxSphereShapeDesc* sphereDesc = new NxSphereShapeDesc();
	NxActor *actor;
	NxShapeDesc* meshDesc;
	NxBodyDesc* bodyDesc = new NxBodyDesc();

	//bboxDesc.density = 10.0f;

	sphereDesc->shapeFlags |= NX_SF_DISABLE_RESPONSE;
	sphereDesc->shapeFlags &= ~NX_SF_VISUALIZATION;
	sphereDesc->group = SHAPE_SPHERE;
	sphereDesc->radius = (NxReal)radius;
	sphereDesc->localPose.t = NxVec3((NxReal)center[0], (NxReal)center[1], (NxReal)center[2]);
	sphereDesc->mass = 0;

	if (staticActor) ret.actorDesc->body = NULL;
	else {
		bodyDesc->linearDamping = 2.0f;
		ret.actorDesc->body = bodyDesc;
	}
	
    ret.actorDesc->shapes.pushBack(sphereDesc);

	if (name[1] == 'S') { // sphere shape
		NxSphereShapeDesc* sphereDesc2 = new NxSphereShapeDesc();
		sphereDesc2->radius = sphereDesc->radius;
		sphereDesc2->localPose = sphereDesc->localPose;
		sphereDesc2->group = SHAPE_OTHER;
		ret.actorDesc->shapes.pushBack(sphereDesc2);
	}
	else if (name[1] == 'H') {
		getShapes(node, ret.actorDesc, matrix);
	}
	else if ((name[1] == 'M') && ((meshDesc = cookMesh(pMesh, matrix)) != NULL)) { // triangle mesh shape
		meshDesc->group = SHAPE_OTHER;
		ret.actorDesc->shapes.pushBack(meshDesc);
	}
	else if ((name[1] == 'C') && ((meshDesc = cookConvex(pMesh, matrix)) != NULL)) { // convex mesh shape
		meshDesc->group = SHAPE_OTHER;
		ret.actorDesc->shapes.pushBack(meshDesc);
	}
	else if (name[1] != 'N'){ // box shape
		NxBoxShapeDesc* bboxDesc = new NxBoxShapeDesc();
		bboxDesc->dimensions = NxVec3((NxReal)max[0],(NxReal)max[1],(NxReal)max[2]);
		NxQuat q;
		q.fromAngleAxis(180, NxVec3(1, 0, 0));
		bboxDesc->localPose.M.fromQuat(q);
		bboxDesc->localPose.t = NxVec3((NxReal)center[0], (NxReal)center[1], (NxReal)center[2]);
		bboxDesc->group = SHAPE_OTHER;
		ret.actorDesc->shapes.pushBack(bboxDesc);
	}
	else { // no shape
		ret.actorDesc->flags |= NX_AF_DISABLE_RESPONSE;
	}

	ret.actorDesc->density = 1.0f;
	//ret.actorDesc->globalPose.t = NxVec3((NxReal)center[0],(NxReal)center[1],(NxReal)center[2]);
	ret.actorDesc->group = type;

	if (ret.actorDesc->isValid()) {
		actor = physx.scene->createActor(*(ret.actorDesc));
	}
	else {
		printf("Invalid ActorDesc!\n");
		exit(0);
	}
	
	/* instantiate the class */
	GeMaterial material = getMaterial(pMesh);

	if (material.diffuse.a != 1.0) {
		ret.actor = new GeBlendableMesh(geometry, actor);
	}
	else {
		ret.actor = new GeActor(geometry, actor);
	}
	ret.actor->material = material;
	ret.actor->actor->userData = ret.actor;
	ret.actor->castShadow = castShadow; 

	if (texture != NULL) {
		ret.actor->setTexture(texture);
	}
	if(!staticActor) {
		ret.actor->actor->putToSleep();
	}

	importChilds(node, ret.actor);

	return ret;
}

void FBXConverter :: importChilds(KFbxNode* node, GeActor* actor) {
	int cnum = node->GetChildCount();
	KFbxNode* cur = NULL;
	KFbxNodeAttribute* attr = NULL;
	KFbxMesh* pMesh = NULL;
	KFbxXMatrix mat1, mat2, mat3;
	GeGeometry* geometry = NULL;
	GeTexture* texture = NULL;
	GeMesh* mesh = NULL;

	for(int i=0; i<cnum; i++) {
		cur = node->GetChild(i);
		attr = cur->GetNodeAttribute();
		if ((attr != NULL) && (attr->GetAttributeType() == KFbxNodeAttribute::eMESH)) {
			const char* name = cur->GetName();
			if (name[0] == 'M') {
				printf("SubMesh found: %s\n", cur->GetName());
				geomConverter->TriangulateInPlace(cur);

				pMesh = NULL;
				pMesh = (KFbxMesh*) cur->GetNodeAttribute();
				mat1 = getGeometricTransformation(cur);
				mat2 = cur->GetGlobalFromDefaultTake();
				mat3 = getLocalTransformation(cur);
				geometry = new GeGeometry();
				texture = NULL;
				mesh = NULL;

				getGeometry(pMesh, mat1*mat2, geometry, &texture);

				mesh = new GeMesh(geometry);
				mesh->material = getMaterial(pMesh);

				if (name[2] == 'R') {
					if (name[3] == 'X'){
						mesh->setAnimator(ANIMATOR_ROT_X);
					}
					else if (name[3] == 'Y'){
						mesh->setAnimator(ANIMATOR_ROT_Y);
					}
					else if (name[3] == 'Z'){
						mesh->setAnimator(ANIMATOR_ROT_Z);
					}
				}

				actor->push_back(mesh);
			}
		}
	}
}

KFbxXMatrix FBXConverter :: getLocalTransformation(KFbxNode* pNode) {
	KFbxVector4 lT, lR, lS;
	KFbxXMatrix ret;
	lT = pNode->GetLocalTFromDefaultTake();
	lR = pNode->GetLocalRFromDefaultTake();
	lS = pNode->GetLocalSFromDefaultTake();

	ret.SetT(lT);
	ret.SetR(lR);
	ret.SetS(lS);

	return ret;
}

KFbxXMatrix FBXConverter :: getGeometricTransformation(KFbxNode* pNode) {
	KFbxVector4 lT, lR, lS;
	KFbxXMatrix ret;

	lT = pNode->GetGeometricTranslation(KFbxNode::eSOURCE_SET);
	lR = pNode->GetGeometricRotation(KFbxNode::eSOURCE_SET);
	lS = pNode->GetGeometricScaling(KFbxNode::eSOURCE_SET);

	ret.SetT(lT);
	ret.SetR(lR);
	ret.SetS(lS);

	return ret;
}

NxConvexShapeDesc* FBXConverter :: cookConvex(KFbxMesh* mesh, KFbxXMatrix mat) {
	NxConvexMeshDesc meshDesc;
	NxConvexShapeDesc* shapeDesc;
	NxVec3* vertexbuffer;
	GLdouble* M = (double *) mat;

	KFbxVector4 * pVertexArray = mesh->GetControlPoints();
	int cpCount = mesh->GetControlPointsCount();
	vertexbuffer = new NxVec3[cpCount];

	for (int i=0; i<cpCount; i++) {
		KFbxVector4 v = pVertexArray[i];
		vertexbuffer[i].x = (NxReal)(v[0]*M[0] + v[1]*M[4] + v[2]*M[8]  + M[12]);
		vertexbuffer[i].y = (NxReal)(v[0]*M[1] + v[1]*M[5] + v[2]*M[9]  + M[13]);
		vertexbuffer[i].z = (NxReal)(v[0]*M[2] + v[1]*M[6] + v[2]*M[10] + M[14]);
	}

	meshDesc.flags = NX_CF_COMPUTE_CONVEX;
	meshDesc.numVertices = cpCount;
	meshDesc.points = vertexbuffer;
	meshDesc.pointStrideBytes = sizeof(NxVec3);

	shapeDesc = physx.cookConvexMesh(meshDesc);
	delete vertexbuffer;
	return shapeDesc;
}

NxTriangleMeshShapeDesc * FBXConverter :: cookMesh(KFbxMesh* mesh, KFbxXMatrix mat) {
	NxTriangleMeshDesc meshDesc;
	NxTriangleMeshShapeDesc* shapeDesc;
	NxVec3* vertexbuffer;
	GLdouble* M = (double *) mat;
	
	KFbxVector4 * pVertexArray = mesh->GetControlPoints();
	int cpCount = mesh->GetControlPointsCount();
	vertexbuffer = new NxVec3[cpCount];

	for (int i=0; i<cpCount; i++) {
		KFbxVector4 v = pVertexArray[i];
		vertexbuffer[i].x = (NxReal)(v[0]*M[0] + v[1]*M[4] + v[2]*M[8]  + M[12]);
		vertexbuffer[i].y = (NxReal)(v[0]*M[1] + v[1]*M[5] + v[2]*M[9]  + M[13]);
		vertexbuffer[i].z = (NxReal)(v[0]*M[2] + v[1]*M[6] + v[2]*M[10] + M[14]);
	}

	//meshDesc.flags = NX_MF_FLIPNORMALS;
	meshDesc.numVertices = cpCount;
	meshDesc.numTriangles = mesh->GetPolygonCount();

	meshDesc.points = vertexbuffer;
	meshDesc.pointStrideBytes = sizeof(NxVec3);

	meshDesc.triangles = mesh->GetPolygonVertices();
	meshDesc.triangleStrideBytes = 3*sizeof(int);

	shapeDesc = physx.cookTriangleMesh(meshDesc);
/*	shapeDesc->localPose.M.setColumnMajorStride4(geom->transformation);
*/
	delete vertexbuffer;
	return shapeDesc;
}

void FBXConverter :: getShapes(KFbxNode* node, NxActorDesc* actorDesc, KFbxXMatrix globalPose) {
	int cnum = node->GetChildCount();
	KFbxNode* cur = NULL;
	KFbxNodeAttribute* attr = NULL;
	KFbxVector4 center, min, max;

	KFbxXMatrix globalPoseInv = globalPose.Inverse();
	for(int i=0; i<cnum; i++) {
		cur = node->GetChild(i);
		attr = cur->GetNodeAttribute();
		if ((attr != NULL) && (attr->GetAttributeType() == KFbxNodeAttribute::eMESH)) {
			const char* name = cur->GetName();
			if (name[1] != 'N') {
				geomConverter->TriangulateInPlace(cur);

				KFbxMesh* pMesh = (KFbxMesh*) cur->GetNodeAttribute();
				KFbxXMatrix mat = getGeometricTransformation(cur)*cur->GetGlobalFromDefaultTake(); //*globalPoseInv; // *cur->GetGlobalFromDefaultTake()
				KFbxQuaternion q = mat.GetQ();
				KFbxVector4 * pVertexArray = pMesh->GetControlPoints();
				int cpCount = pMesh->GetControlPointsCount();
				KFbxVector4 center, min, max, t;
				NxQuat nxq;
				NxMat34 localPose;

				t = mat.GetT();

				nxq.x = (NxReal)q.mData[0];
				nxq.y = (NxReal)q.mData[1];
				nxq.z = (NxReal)q.mData[2];
				nxq.w = (NxReal)q.mData[3];
				mat.SetR(KFbxVector4());
				//mat.SetT(KFbxVector4());
				
				min = pVertexArray[0];
				max = min;
				for (int i=0; i<cpCount; i++) {
					KFbxVector4 vertex = pVertexArray[i];
					
					if (vertex[0] > max[0]) max[0] = vertex[0];
					if (vertex[1] > max[1]) max[1] = vertex[1];
					if (vertex[2] > max[2]) max[2] = vertex[2];
					
					if (vertex[0] < min[0]) min[0] = vertex[0];
					if (vertex[1] < min[1]) min[1] = vertex[1];
					if (vertex[2] < min[2]) min[2] = vertex[2];
				}
				center = mat.MultS((min+max)/2);
				max = mat.MultS(max) - center;
				
				localPose.M.fromQuat(nxq);
				//localPose.t = NxVec3((NxReal)t[0], (NxReal)t[1], (NxReal)t[2]);
				
				/*
				mesh = FBXConverter::mesh(cur, mat, (name[0] != 'I'));
				if (mesh != NULL) {
					mesh->actor->setGlobalPose(nxmat);
					ret->push_back(mesh);
				}
				*/

				if (name[1] == 'S') { // sphere shape
					double radius = 0.0;
					for (int i=0; i<cpCount; i++) {
						double distance = pVertexArray[i].Length();
						if (distance > radius) radius = distance;
					}
					NxSphereShapeDesc* sphereDesc = new NxSphereShapeDesc();
					sphereDesc->group = SHAPE_OTHER;
					sphereDesc->radius = (NxReal)radius;
					localPose.t = NxVec3((NxReal)(t[0]+center[0]), (NxReal)(t[1]+center[1]), (NxReal)(t[2]+center[2]));
					sphereDesc->localPose = localPose;
					actorDesc->shapes.pushBack(sphereDesc);
				}
				else if (name[1] == 'M') {
					NxShapeDesc* shapeDesc = cookMesh(pMesh, mat);
					shapeDesc->group = SHAPE_OTHER;
					shapeDesc->localPose = localPose;
					actorDesc->shapes.pushBack(shapeDesc);
				}
				else if (name[1] == 'C') {
					NxShapeDesc* shapeDesc = cookConvex(pMesh, mat);
					shapeDesc->group = SHAPE_OTHER;
					shapeDesc->localPose = localPose;
					actorDesc->shapes.pushBack(shapeDesc);
				}
				else { // box shape
					NxBoxShapeDesc* bboxDesc = new NxBoxShapeDesc();
					bboxDesc->group = SHAPE_OTHER;
					bboxDesc->dimensions = NxVec3((NxReal)max[0],(NxReal)max[1],(NxReal)max[2]);
					localPose.t = NxVec3((NxReal)(t[0]+center[0]), (NxReal)(t[1]+center[1]), (NxReal)(t[2]+center[2]));
					bboxDesc->localPose = localPose;
					actorDesc->shapes.pushBack(bboxDesc);
				}
			}
		}
	}
}

