Skip to content
Snippets Groups Projects
Coll.cpp 42.5 KiB
Newer Older
Gundalf's avatar
Gundalf committed
#include "pch.h"
#include "../NifProps/bhkRigidBodyInterface.h"
#include "obj/bhkListShape.h"
Tazpn's avatar
Tazpn committed
#include "obj/bhkConvexVerticesShape.h"
Tazpn's avatar
Tazpn committed
#include "obj/bhkTransformShape.h"
#include "obj/bhkSphereShape.h"
#include "obj/bhkBoxShape.h"
#include "obj/bhkCapsuleShape.h"
Tazpn's avatar
Tazpn committed
#include "obj/hkPackedNiTriStripsData.h"
#include "obj/bhkPackedNiTriStripsShape.h"
Tazpn's avatar
Tazpn committed
#include "obj/bhkMoppBvTreeShape.h"
Tazpn's avatar
Tazpn committed

Tazpn's avatar
Tazpn committed
#include "..\NifProps\bhkHelperFuncs.h"
#include "..\NifProps\bhkHelperInterface.h"

#ifdef _DEBUG
#include <assert.h>
#include <crtdbg.h>
#define ASSERT _ASSERTE
#else
#define ASSERT(x)
#endif

static Class_ID SCUBA_CLASS_ID(0x6d3d77ac, 0x79c939a9);
Tazpn's avatar
Tazpn committed
extern Class_ID BHKRIGIDBODYMODIFIER_CLASS_ID;
extern Class_ID BHKLISTOBJECT_CLASS_ID;
Tazpn's avatar
Tazpn committed
extern Class_ID bhkBoxObject_CLASS_ID;
extern Class_ID BHKCAPSULEOBJECT_CLASS_ID;
extern Class_ID bhkSphereObject_CLASS_ID;
extern Class_ID BHKPROXYOBJECT_CLASS_ID;
class HavokMoppCode
{
private:
	typedef int (__stdcall * fnGenerateMoppCode)(int nVerts, Vector3 const* verts, int nTris, Triangle const *tris);
	typedef int (__stdcall * fnRetrieveMoppCode)(int nBuffer, unsigned char *buffer);
	typedef int (__stdcall * fnRetrieveMoppScale)(float *value);
	typedef int (__stdcall * fnRetrieveMoppOrigin)(Vector3 *value);

	HMODULE hMoppLib;
	fnGenerateMoppCode GenerateMoppCode;
	fnRetrieveMoppCode RetrieveMoppCode;
	fnRetrieveMoppScale RetrieveMoppScale;
	fnRetrieveMoppOrigin RetrieveMoppOrigin;

public:
	HavokMoppCode() : hMoppLib(0), GenerateMoppCode(0), RetrieveMoppCode(0), RetrieveMoppScale(0), RetrieveMoppOrigin(0) {
	}
	~HavokMoppCode() {
		if (hMoppLib) FreeLibrary(hMoppLib);
	}
Tazpn's avatar
Tazpn committed

	bool Initialize()
	{
		if (hMoppLib == NULL)
		{
			hMoppLib = LoadLibraryA( "NifMopp.dll" );
			GenerateMoppCode = (fnGenerateMoppCode)GetProcAddress( hMoppLib, "GenerateMoppCode" );
			RetrieveMoppCode = (fnRetrieveMoppCode)GetProcAddress( hMoppLib, "RetrieveMoppCode" );
			RetrieveMoppScale = (fnRetrieveMoppScale)GetProcAddress( hMoppLib, "RetrieveMoppScale" );
			RetrieveMoppOrigin = (fnRetrieveMoppOrigin)GetProcAddress( hMoppLib, "RetrieveMoppOrigin" );
		}
		return ( NULL != GenerateMoppCode  && NULL != RetrieveMoppCode 
			&& NULL != RetrieveMoppScale && NULL != RetrieveMoppOrigin
			);
Tazpn's avatar
Tazpn committed
	}

	vector<Niflib::byte> CalculateMoppCode( vector<Niflib::Vector3> const & verts, vector<Niflib::Triangle> const & tris, Niflib::Vector3* origin, float* scale)
	{
		vector<Niflib::byte> code;
		if ( Initialize() )
		{
			int len = GenerateMoppCode( verts.size(), &verts[0], tris.size(), &tris[0] );
			if ( len > 0 )
			{
				code.resize( len );
				if ( 0 != RetrieveMoppCode( len , &code[0] ) )
				{
					if ( NULL != scale )
						RetrieveMoppScale(scale);
					if ( NULL != origin )
						RetrieveMoppOrigin(origin);
				}
				else
				{
					code.clear();
				}
			}
		}
		return code;
Tazpn's avatar
Tazpn committed
	}
Tazpn's avatar
Tazpn committed

static vector<Niflib::byte> ConstructHKMesh( NiTriBasedGeomRef shape, Niflib::Vector3& origin, float& scale)
Tazpn's avatar
Tazpn committed
{
	NiTriBasedGeomDataRef data = shape->GetData();
	return TheHavokCode.CalculateMoppCode(data->GetVertices(), data->GetTriangles(), &origin, &scale);
Tazpn's avatar
Tazpn committed
}

static vector<Niflib::byte> ConstructHKMesh( bhkPackedNiTriStripsShapeRef shape, Niflib::Vector3& origin, float& scale)
Tazpn's avatar
Tazpn committed
{
	hkPackedNiTriStripsDataRef data = shape->GetData();
	return TheHavokCode.CalculateMoppCode(data->GetVertices(), data->GetTriangles(), &origin, &scale);
Tazpn's avatar
Tazpn committed
}

/*
To mimic the "Reset Transform" and "Reset Scale" behavior, the following code snippet should help:



	Interface *ip = theResetScale.ip;
	TimeValue t = ip->GetTime();
	
	Control *tmControl = node->GetTMController();
	BOOL lookAt = tmControl->GetRollController() ? TRUE : FALSE;

	Matrix3 ntm = node->GetNodeTM(t);
	Matrix3 ptm = node->GetParentTM(t);
	Matrix3 rtm = ntm * Inverse(ptm);
	Matrix3 otm(1);
	Quat rot;

	// Grab the trans, and then set it to 0
	Point3 trans = rtm.GetTrans();
	rtm.NoTrans();
	
	// We're only doing scale - save out the 
	// rotation so we can put it back
	AffineParts parts;
	decomp_affine(rtm, &parts);
	rot = parts.q;

	// Build the offset tm
	otm.PreTranslate(node->GetObjOffsetPos()); 
	if (node->GetObjOffsetRot()!=IdentQuat()) {
	   	PreRotateMatrix(otm,node->GetObjOffsetRot());
		}

	Point3 tS(1,1,1);
	if ( node->GetObjOffsetScale().s != tS ) {
		ApplyScaling(otm,node->GetObjOffsetScale());
		}
	
	// Apply the relative tm to the offset
	otm = otm * rtm;
	decomp_affine(otm, &parts);
	node->SetObjOffsetPos(parts.t);	
	node->SetObjOffsetScale(ScaleValue(parts.k*parts.f,parts.u));

	// Now set the transform controller with a matrix 
	// that has no rotation or scale
	rtm.IdentityMatrix();
	rtm.SetTrans(trans);
	if (!lookAt) {
		PreRotateMatrix(rtm,rot);
		}

	// But first, want to keep children stationary.
	Matrix3 ttm = rtm*ptm;
	for (int i=0; iNumberOfChildren(); i++)  {
		Control *tmc  = node->GetChildNode(i)->GetTMController();
		Matrix3 oldtm = node->GetChildNode(i)->GetNodeTM(t);
		SetXFormPacket pk(oldtm,ttm);
		tmc->SetValue(t,&pk);
		}

  	SetXFormPacket pckt(rtm);
	tmControl->SetValue(t,&pckt);		



To mimic the "Align to world" behavior, the following code snippet should help:



    AffineParts parts;
    TimeValue currtime = m_pInterface->GetTime();
    Matrix3 m = pNode->GetNodeTM(currtime);
    decomp_affine(m, &parts); 
    if (rotobj) {
        // if "affect obj only" we move it simply thus:
        pNode->SetObjOffsetRot(Inverse(parts.q));
    } else {
        // otherwise, "affect pivot only" we would do:
        IdentityTM ident;
        Matrix3 wax = ident;
        wax.SetTrans(m.GetTrans());  // world aligned axis,  centered at pivot point
        pNode->Rotate(currtime, wax, Inverse(parts.q),TRUE,FALSE, PIV_PIVOT_ONLY);
    }
    m_pInterface->RedrawViews(m_pInterface->GetTime(),REDRAW_NORMAL,NULL);

*/

Gundalf's avatar
Gundalf committed
int Exporter::addVertex(vector<Vector3> &verts, vector<Vector3> &vnorms, const Point3 &pt, const Point3 &norm)
{
Tazpn's avatar
Tazpn committed
	for (unsigned int i=0; i<verts.size(); i++)
Gundalf's avatar
Gundalf committed
	{
		if (equal(verts[i], pt, mWeldThresh) &&
			equal(vnorms[i], norm, 0))
			return i;
	}
	
	verts.push_back(Vector3(pt.x, pt.y, pt.z));
	vnorms.push_back(Vector3(norm.x, norm.y, norm.z));

	return verts.size()-1;
}

void Exporter::addFace(Triangles &tris, vector<Vector3> &verts, vector<Vector3> &vnorms, 
Tazpn's avatar
Tazpn committed
			 int face, const int vi[3], Mesh *mesh, Matrix3& tm)
Gundalf's avatar
Gundalf committed
{
	Triangle tri;
	for (int i=0; i<3; i++)
	{
Tazpn's avatar
Tazpn committed
      Point3 pt = mesh->verts[ mesh->faces[ face ].v[ vi[i] ] ] * tm;
      Point3 norm = getVertexNormal(mesh, face, mesh->getRVertPtr(mesh->faces[ face ].v[ vi[i] ])) * tm;
Tazpn's avatar
Tazpn committed
		tri[i] = addVertex(verts, vnorms, pt, norm);
Gundalf's avatar
Gundalf committed
	}
	tris.push_back(tri);
}

Exporter::Result Exporter::exportCollision(NiNodeRef &parent, INode *node)
{
	if (isHandled(node) || (node->IsHidden() && !mExportHidden))
		return Exporter::Skip;

Tazpn's avatar
Tazpn committed
   ProgressUpdate(Collision, FormatText("'%s' Collision", node->GetName()));

	//bool coll = npIsCollision(node);
   bool coll = isCollision(node);
Gundalf's avatar
Gundalf committed

   bool local = !mFlattenHierarchy;
   NiNodeRef nodeParent = mFlattenHierarchy ? mNiRoot : parent;

Gundalf's avatar
Gundalf committed
	if (coll)
		markAsHandled(node);

		newParent = Exporter::findNode(node->GetParentNode());
Tazpn's avatar
Tazpn committed
		if (!newParent) {
			newParent = nodeParent;
Tazpn's avatar
Tazpn committed
		}
		//newParent = nodeParent; // always have collision one level up?
		bhkRigidBodyRef body = makeCollisionBody(node);
Tazpn's avatar
Tazpn committed
		if (body)
Tazpn's avatar
Tazpn committed
			bool hasTrans = (body->IsDerivedType(bhkRigidBodyT::TYPE));
			Matrix3 tm = getTransform(node, t, false); // has transform
			Matrix3 pm = TOMATRIX3(newParent->GetWorldTransform());
			tm = tm * Inverse(pm);

			Matrix44 rm4 = TOMATRIX4(tm, false);
			Vector3 trans; Matrix33 rm; float scale;
			rm4.Decompose(trans, rm, scale);
Tazpn's avatar
Tazpn committed
			QuaternionXYZW q = TOQUATXYZW(rm.AsQuaternion());
			body->SetRotation(q);
			body->SetTranslation(trans / Exporter::bhkScaleFactor);

			if (hasTrans) {
				tm = getTransform(node, t, false);
				tm.NoScale();
				tm.Invert();
			} else {
				tm = TOMATRIX3(newParent->GetWorldTransform());
				tm.NoScale();
				tm.Invert();
				//tm.IdentityMatrix();
            }
Tazpn's avatar
Tazpn committed
			bhkShapeRef shape = makeCollisionShape(node, tm, body);
			if (shape)
			{
				body->SetShape(DynamicCast<bhkShape>(shape));

				bhkCollisionObjectRef co = new bhkCollisionObject();
				co->SetBody(DynamicCast<NiObject>(body));
				newParent->SetCollisionObject(DynamicCast<NiCollisionObject>(co));
			}
	} else if (isCollisionGroup(node) && !mFlattenHierarchy) {
		newParent = makeNode(nodeParent, node);
   } else {
		newParent = Exporter::findNode(node->GetParentNode());
		if (!newParent)
			newParent = nodeParent;
	if (!newParent)
		newParent = nodeParent;

	for (int i=0; i<node->NumberOfChildren(); i++) 
	{
		Result result = exportCollision(newParent, node->GetChildNode(i));
		if (result!=Ok && result!=Skip)
			return result;
	}

	return Ok;
}


bhkRigidBodyRef Exporter::makeCollisionBody(INode *node)
{
	// get data from node
	int lyr = NP_DEFAULT_HVK_LAYER;
	int mtl = NP_DEFAULT_HVK_MATERIAL;
	int msys  = NP_DEFAULT_HVK_MOTION_SYSTEM;
	int qtype = NP_DEFAULT_HVK_QUALITY_TYPE;
	float mass = NP_DEFAULT_HVK_MASS;
	float lindamp = NP_DEFAULT_HVK_LINEAR_DAMPING;
	float angdamp = NP_DEFAULT_HVK_ANGULAR_DAMPING;
	float frict = NP_DEFAULT_HVK_FRICTION;
	float maxlinvel = NP_DEFAULT_HVK_MAX_LINEAR_VELOCITY;
	float maxangvel = NP_DEFAULT_HVK_MAX_ANGULAR_VELOCITY;
	float resti = NP_DEFAULT_HVK_RESTITUTION;
	float pendepth = NP_DEFAULT_HVK_PENETRATION_DEPTH;
	Vector3 center(0,0,0);
	BOOL transenable = TRUE;

	if (bhkRigidBodyInterface *irb = (bhkRigidBodyInterface *)node->GetObjectRef()->GetInterface(BHKRIGIDBODYINTERFACE_DESC))
	{
		mass = irb->GetMass(0);
		frict = irb->GetFriction(0);
		resti = irb->GetRestitution(0);
		lyr = irb->GetLayer(0);
		msys = irb->GetMotionSystem(0);
		qtype = irb->GetQualityType(0);
		lindamp = irb->GetLinearDamping(0);
		angdamp = irb->GetAngularDamping(0);
		maxlinvel = irb->GetMaxLinearVelocity(0);
		pendepth = irb->GetPenetrationDepth(0);
		maxangvel = irb->GetMaxAngularVelocity(0);
		transenable = irb->GetEnableTransform(0);
	}
	else if (npIsCollision(node))
	{
		// Handle compatibility
		npGetProp(node, NP_HVK_MASS_OLD, mass, NP_DEFAULT_HVK_EMPTY);
		if (mass == NP_DEFAULT_HVK_EMPTY)
			npGetProp(node, NP_HVK_MASS, mass, NP_DEFAULT_HVK_MASS);
		npGetProp(node, NP_HVK_FRICTION_OLD, frict, NP_DEFAULT_HVK_EMPTY);
		if (frict == NP_DEFAULT_HVK_EMPTY)
			npGetProp(node, NP_HVK_FRICTION, frict, NP_DEFAULT_HVK_FRICTION);
		npGetProp(node, NP_HVK_RESTITUTION_OLD, resti, NP_DEFAULT_HVK_EMPTY);
		if (resti == NP_DEFAULT_HVK_EMPTY)
			npGetProp(node, NP_HVK_RESTITUTION, resti, NP_DEFAULT_HVK_RESTITUTION);

		npGetProp(node, NP_HVK_LAYER, lyr, NP_DEFAULT_HVK_LAYER);
		npGetProp(node, NP_HVK_MATERIAL, mtl, NP_DEFAULT_HVK_MATERIAL);
		npGetProp(node, NP_HVK_MOTION_SYSTEM, msys, NP_DEFAULT_HVK_MOTION_SYSTEM);
		npGetProp(node, NP_HVK_QUALITY_TYPE, qtype, NP_DEFAULT_HVK_QUALITY_TYPE);
		npGetProp(node, NP_HVK_LINEAR_DAMPING, lindamp, NP_DEFAULT_HVK_LINEAR_DAMPING);
		npGetProp(node, NP_HVK_ANGULAR_DAMPING, angdamp, NP_DEFAULT_HVK_ANGULAR_DAMPING);
		npGetProp(node, NP_HVK_MAX_LINEAR_VELOCITY, maxlinvel, NP_DEFAULT_HVK_MAX_LINEAR_VELOCITY);
		npGetProp(node, NP_HVK_MAX_ANGULAR_VELOCITY, maxangvel, NP_DEFAULT_HVK_MAX_ANGULAR_VELOCITY);
		npGetProp(node, NP_HVK_PENETRATION_DEPTH, pendepth, NP_DEFAULT_HVK_PENETRATION_DEPTH);
		npGetProp(node, NP_HVK_CENTER, center);
	}
	else
	{
		// Check self to see if is one of our bhkXXXObject classes
		if (Object* obj = node->GetObjectRef())
		{
			if (obj->SuperClassID() == HELPER_CLASS_ID &&
				obj->ClassID().PartB() == BHKRIGIDBODYCLASS_DESC.PartB()) 
			{
				// TODO: do standard body export
			}
		}

		// else check redirection 
	}

Tazpn's avatar
Tazpn committed
	bhkRigidBodyRef body = transenable ? new bhkRigidBodyT() : new bhkRigidBody();
Tazpn's avatar
Tazpn committed
	body->SetLayer(OblivionLayer(lyr));
	body->SetLayerCopy(OblivionLayer(lyr));
	body->SetMotionSystem(MotionSystem(msys));
	body->SetQualityType(MotionQuality(qtype));
	body->SetMass(mass);
	body->SetLinearDamping(lindamp);
	body->SetAngularDamping(angdamp);
	body->SetFriction(frict);
	body->SetRestitution(resti);
	body->SetMaxLinearVelocity(maxlinvel);
	body->SetMaxAngularVelocity(maxangvel);
	body->SetPenetrationDepth(pendepth);
	body->SetCenter(center);
   QuaternionXYZW q; q.x = q.y = q.z = 0; q.w = 1.0f;
   body->SetRotation(q);
Tazpn's avatar
Tazpn committed
bhkNiTriStripsShapeRef Exporter::makeTriStripsShape(Mesh& mesh, Matrix3& sm)
{
	typedef vector<Triangle> Triangles;

	// setup shape data
	vector<Vector3> verts;
	vector<Vector3> vnorms;
	Triangles		tris;

	int vi[3];
	if (TMNegParity(sm)) {
		vi[0] = 2; vi[1] = 1; vi[2] = 0;
	} else {
		vi[0] = 0; vi[1] = 1; vi[2] = 2;
	}

	for (int i=0; i<mesh.getNumFaces(); i++)
		addFace(tris, verts, vnorms, i, vi, &mesh, sm);

	NiTriStripsDataRef data = new NiTriStripsData(tris, Exporter::mUseAlternateStripper);
	data->SetVertices(verts);
	data->SetNormals(vnorms);

	// setup shape
	bhkNiTriStripsShapeRef shape = StaticCast<bhkNiTriStripsShape>(bhkNiTriStripsShape::Create());
	shape->SetNumStripsData(1);
	shape->SetStripsData(0, data);
	shape->SetNumDataLayers(1);
	shape->SetOblivionLayer(0, OL_STATIC);
Tazpn's avatar
Tazpn committed
	return shape;
}

Tazpn's avatar
Tazpn committed
static bhkMoppBvTreeShapeRef makeTreeShape(bhkPackedNiTriStripsShapeRef mesh)
{
	bhkMoppBvTreeShapeRef mopp = new bhkMoppBvTreeShape();
	mopp->SetMaterial( HAV_MAT_WOOD );
	mopp->SetShape( mesh );

	try
	{
		Niflib::Vector3 offset;
		float scale;
		vector<Niflib::byte> moppcode = ConstructHKMesh(mesh, offset, scale);
Tazpn's avatar
Tazpn committed
		mopp->SetMoppCode( moppcode );
		mopp->SetMoppOrigin( offset );
		mopp->SetMoppScale( scale );
Tazpn's avatar
Tazpn committed
	}
	catch(...)
	{
	}
	return mopp;
}

Tazpn's avatar
Tazpn committed
bhkPackedNiTriStripsShapeRef Exporter::makePackedTriStripsShape(Mesh& mesh, Matrix3& sm)
{
	// Need to separate the vertices based on material.  
	typedef vector<Triangle> Triangles;

	// setup shape data
	vector<Vector3> verts;
	vector<Vector3> norms;
	Triangles		tris;

	int vi[3];
	if (TMNegParity(sm)) {
		vi[0] = 2; vi[1] = 1; vi[2] = 0;
	} else {
		vi[0] = 0; vi[1] = 1; vi[2] = 2;
	}

	int nvert = mesh.getNumVerts();
	int nface = mesh.getNumFaces();
	mesh.buildNormals();

	tris.resize(nface);
	verts.resize(nvert);
	norms.resize(nface);
	for (int i=0; i<nvert; ++i)
	{
		Point3 vert = (mesh.getVert(i) * sm) / Exporter::bhkScaleFactor;
		verts[i] = TOVECTOR3(vert);
	}
	for (int i=0; i<nface; ++i)
	{
		Triangle& tri = tris[i];
		norms[i] = TOVECTOR3(mesh.getFaceNormal(i));
		Face& face = mesh.faces[i];
Tazpn's avatar
Tazpn committed
		tri[0] = (USHORT)face.getVert(0);
		tri[1] = (USHORT)face.getVert(1);
		tri[2] = (USHORT)face.getVert(2);
Tazpn's avatar
Tazpn committed
	}
Tazpn's avatar
Tazpn committed

Tazpn's avatar
Tazpn committed
	hkPackedNiTriStripsDataRef data = new hkPackedNiTriStripsData();
	data->SetNumFaces( tris.size() );
	data->SetVertices(verts);
	data->SetTriangles(tris);
	data->SetNormals(norms);

	// setup shape
	bhkPackedNiTriStripsShapeRef shape = new bhkPackedNiTriStripsShape();
	shape->SetData(data);
Tazpn's avatar
Tazpn committed

	OblivionSubShape subshape;
	subshape.layer = OL_STATIC;
	subshape.material = HAV_MAT_WOOD;
	subshape.numVertices = verts.size();

	vector<OblivionSubShape> subshapes;
	subshapes.push_back(subshape);
	shape->SetSubShapes( subshapes );
Tazpn's avatar
Tazpn committed
	//shape->SetStripsData(0, data);
	//shape->SetNumDataLayers(1);
	//shape->SetOblivionLayer(0, OL_STATIC);
Tazpn's avatar
Tazpn committed
	return shape;
}

bhkConvexVerticesShapeRef Exporter::makeConvexShape(Mesh& mesh, Matrix3& tm)
{
	bhkConvexVerticesShapeRef shape = StaticCast<bhkConvexVerticesShape>(bhkConvexVerticesShape::Create());
	Point3 center(0.0f, 0.0f, 0.0f);
	float radius = 0.10f;
	CalcCenteredSphere(mesh, center, radius);
	radius /= Exporter::bhkScaleFactor;
Tazpn's avatar
Tazpn committed
	shape->SetRadius(radius);
Tazpn's avatar
Tazpn committed
	vector<Vector3> verts;
	vector<Float4> norms;
Tazpn's avatar
Tazpn committed
	int nvert = mesh.getNumVerts();
	int nface = mesh.getNumFaces();
Tazpn's avatar
Tazpn committed
	mesh.checkNormals(FALSE);
	mesh.buildNormals();
Tazpn's avatar
Tazpn committed
	verts.resize(nvert);
	norms.resize(nface);
Tazpn's avatar
Tazpn committed
	for (int i=0; i<nvert; ++i)
	{
Tazpn's avatar
Tazpn committed
		Point3 vert = (mesh.getVert(i) * tm) / Exporter::bhkScaleFactor;
		verts[i] = TOVECTOR3(vert);
	}
	for (int i=0; i<nface; ++i)
	{
Tazpn's avatar
Tazpn committed
		Float4 &value = norms[i];
		Point3 &pt = mesh.getFaceNormal(i);
		value[0] = pt.x;
		value[1] = pt.y;
		value[2] = pt.z;
		value[3] = -(mesh.FaceCenter(i) * tm).Length() / Exporter::bhkScaleFactor;
Tazpn's avatar
Tazpn committed
	}
Tazpn's avatar
Tazpn committed
	sortVector3(verts);
	sortFloat4(norms);
Tazpn's avatar
Tazpn committed
	shape->SetVertices(verts);
Tazpn's avatar
Tazpn committed
	shape->SetNormalsAndDist(norms);
Tazpn's avatar
Tazpn committed
	return shape;
}


bhkShapeRef Exporter::makeCollisionShape(INode *node, Matrix3& tm, bhkRigidBodyRef body)
	
	TimeValue t = 0;
	ObjectState os = node->EvalWorldState(t); 
	if (os.obj->ClassID() == SCUBA_CLASS_ID)
		shape = makeCapsuleShape(node, os.obj, tm);
	else if (os.obj->ClassID() == Class_ID(BOXOBJ_CLASS_ID, 0))
		shape = makeBoxShape(node, os.obj, tm);
	else if (os.obj->ClassID() == Class_ID(SPHERE_CLASS_ID, 0))
		shape = makeSphereShape(node, os.obj, tm);
Tazpn's avatar
Tazpn committed
	else if (os.obj->ClassID() == bhkBoxObject_CLASS_ID)
		shape = makebhkBoxShape(node, os.obj, tm);
	else if (os.obj->ClassID() == bhkSphereObject_CLASS_ID)
		shape = makebhkSphereShape(node, os.obj, tm);
	else if (os.obj->ClassID() == BHKCAPSULEOBJECT_CLASS_ID)
		shape = makebhkCapsuleShape(node, os.obj, tm);
	else if (os.obj->ClassID() == BHKLISTOBJECT_CLASS_ID)
		shape = makeListShape(node, tm, body);
Tazpn's avatar
Tazpn committed
	else if (os.obj->ClassID() == BHKPROXYOBJECT_CLASS_ID)
		shape = makeProxyShape(node, os.obj, tm);
	else if (os.obj->SuperClassID() == GEOMOBJECT_CLASS_ID)
	{
		if (Modifier* mod = GetbhkCollisionModifier(node))
		{
			shape = makeModifierShape(node, os.obj, mod, tm);
		}
		else
		{
			shape = makeTriStripsShape(node, tm);
		}
	}
bhkShapeRef Exporter::makeBoxShape(INode *node, Object *obj, Matrix3& tm)
Tazpn's avatar
Tazpn committed
   Point3 scale = GetScale(tm);
	float length = 0;
	float height = 0;
	float width = 0; 
Tazpn's avatar
Tazpn committed
	if (IParamBlock2* pblock2 = obj->GetParamBlockByID(0))
	{
		pblock2->GetValue(BOXOBJ_LENGTH, 0, length, FOREVER);
		pblock2->GetValue(BOXOBJ_HEIGHT, 0, height, FOREVER);
		pblock2->GetValue(BOXOBJ_WIDTH, 0, width, FOREVER);
	}
Tazpn's avatar
Tazpn committed
	bhkBoxShapeRef box = new bhkBoxShape();
	Vector3 dim(width * scale[0], length * scale[1], height * scale[2]);
	// Adjust translation for center of z axis in box
	tm.Translate(Point3(0.0, 0.0, dim.z / 2.0));

	dim /= (Exporter::bhkScaleFactor * 2);
	box->SetDimensions(dim);

	int mtl = 0;
	npGetProp(node, NP_HVK_MATERIAL, mtl, NP_DEFAULT_HVK_MATERIAL);
	box->SetMaterial(HavokMaterial(mtl));

	return bhkShapeRef(DynamicCast<bhkSphereRepShape>(box));
bhkShapeRef Exporter::makeSphereShape(INode *node, Object *obj, Matrix3& tm)
Tazpn's avatar
Tazpn committed
   Point3 scale = GetScale(tm);
Tazpn's avatar
Tazpn committed
   float s = (scale[0] + scale[1] + scale[2]) / 3.0f;
Tazpn's avatar
Tazpn committed

Tazpn's avatar
Tazpn committed
	if (IParamBlock2* pblock2 = obj->GetParamBlockByID(0))
	{
		pblock2->GetValue(SPHERE_RADIUS, 0, radius, FOREVER);
	}
Tazpn's avatar
Tazpn committed
	bhkSphereShapeRef sphere = new bhkSphereShape();
	sphere->SetRadius(radius * s);
	int mtl = 0;
	npGetProp(node, NP_HVK_MATERIAL, mtl, NP_DEFAULT_HVK_MATERIAL);
	sphere->SetMaterial(HavokMaterial(mtl));

	return bhkShapeRef(DynamicCast<bhkSphereRepShape>(sphere));
bhkShapeRef Exporter::makeCapsuleShape(INode *node, Object *obj, Matrix3& tm)
Tazpn's avatar
Tazpn committed
   Point3 scale = GetScale(tm);
Tazpn's avatar
Tazpn committed
   float s = (scale[0] + scale[1] + scale[2]) / 3.0f;
Tazpn's avatar
Tazpn committed

	float radius = 0.1f;
	float height = 0.1f;
	if (IParamBlock2* params = obj->GetParamBlockByID(0))
	{
		params->GetValue(CAPSULE_RADIUS, 0, radius, FOREVER);
		params->GetValue(CAPSULE_HEIGHT, 0, height, FOREVER);
	}
	bhkCapsuleShapeRef capsule = CreateNiObject<bhkCapsuleShape>();
	capsule->SetRadius(radius);
	capsule->SetRadius1(radius);
	capsule->SetRadius2(radius);

	int mtl = 0;
	npGetProp(node, NP_HVK_MATERIAL, mtl, NP_DEFAULT_HVK_MATERIAL);
	capsule->SetMaterial(HavokMaterial(mtl));

	return bhkShapeRef(DynamicCast<bhkSphereRepShape>(capsule));
Tazpn's avatar
Tazpn committed
bhkShapeRef Exporter::makebhkBoxShape(INode *node, Object *obj, Matrix3& tm)
{
	enum { box_params, };
	enum { PB_MATERIAL, PB_LENGTH, PB_WIDTH, PB_HEIGHT, };

	bhkShapeRef retval;
	if (IParamBlock2* pblock2 = obj->GetParamBlockByID(box_params))
	{
		Point3 scale = GetScale(tm);
Tazpn's avatar
Tazpn committed
		float s = (scale[0] + scale[1] + scale[2]) / 3.0f;
Tazpn's avatar
Tazpn committed

Tazpn's avatar
Tazpn committed
		int mtl = 0;
		float length = 0, width = 0, height = 0;
Tazpn's avatar
Tazpn committed
		pblock2->GetValue(PB_MATERIAL, 0, mtl, FOREVER, 0);
		pblock2->GetValue(PB_LENGTH, 0, length, FOREVER, 0);
		pblock2->GetValue(PB_WIDTH, 0, width, FOREVER, 0);
		pblock2->GetValue(PB_HEIGHT, 0, height, FOREVER, 0);

		bhkBoxShapeRef box = new bhkBoxShape();
Tazpn's avatar
Tazpn committed

Tazpn's avatar
Tazpn committed
		Vector3 dim(width * scale[0], length * scale[1], height * scale[2]);

Tazpn's avatar
Tazpn committed
		float radius = max( max(dim.x, dim.y), dim.z );
		box->SetRadius(radius);

Tazpn's avatar
Tazpn committed
		// Adjust translation for center of z axis in box
		tm.Translate(Point3(0.0, 0.0, dim.z / 2.0));
		box->SetDimensions(dim);
		box->SetMaterial(HavokMaterial(mtl));
Tazpn's avatar
Tazpn committed

Tazpn's avatar
Tazpn committed
		Matrix3 ltm = node->GetNodeTM(0) * tm;
		if (ltm.IsIdentity())
Tazpn's avatar
Tazpn committed
		{
			retval = StaticCast<bhkShape>(box);
		}
		else
		{
Tazpn's avatar
Tazpn committed
			ltm.SetTrans(ltm.GetTrans() / Exporter::bhkScaleFactor);

Tazpn's avatar
Tazpn committed
			bhkTransformShapeRef transform = new bhkTransformShape();
Tazpn's avatar
Tazpn committed
			transform->SetTransform(TOMATRIX4(ltm).Transpose());
Tazpn's avatar
Tazpn committed
			transform->SetShape(box);
			transform->SetMaterial(HavokMaterial(mtl));
			retval = StaticCast<bhkShape>(transform);
		}
Tazpn's avatar
Tazpn committed
	}

	return retval;
}

bhkShapeRef	Exporter::makebhkSphereShape(INode *node, Object *obj, Matrix3& tm)
{
	bhkShapeRef retval;

Tazpn's avatar
Tazpn committed
	enum { sphere_params, };
	enum { PB_MATERIAL, PB_RADIUS, PB_SEGS, PB_SMOOTH, };

	if (IParamBlock2* pblock2 = obj->GetParamBlockByID(sphere_params))
	{
		float radius = 0.0f;
		int mtl = NP_DEFAULT_HVK_MATERIAL;
		pblock2->GetValue(PB_RADIUS, 0, radius, FOREVER, 0);
		pblock2->GetValue(PB_MATERIAL, 0, mtl, FOREVER, 0);

		bhkSphereShapeRef shape = new bhkSphereShape();
		shape->SetRadius(radius);
		shape->SetMaterial(HavokMaterial(mtl));

Tazpn's avatar
Tazpn committed
        Matrix3 ltm = node->GetNodeTM(0) * tm;
		if (ltm.IsIdentity())
Tazpn's avatar
Tazpn committed
		{
			retval = StaticCast<bhkShape>(shape);
		}
		else
		{
Tazpn's avatar
Tazpn committed
			ltm.SetTrans(ltm.GetTrans() / Exporter::bhkScaleFactor);

Tazpn's avatar
Tazpn committed
			bhkTransformShapeRef transform = new bhkTransformShape();
Tazpn's avatar
Tazpn committed
			transform->SetTransform(TOMATRIX4(ltm).Transpose());
Tazpn's avatar
Tazpn committed
			transform->SetShape(shape);
			transform->SetMaterial(HavokMaterial(mtl));
			retval = StaticCast<bhkShape>(transform);
		}
	}
Tazpn's avatar
Tazpn committed
	return retval;
}

bhkShapeRef	Exporter::makebhkCapsuleShape(INode *node, Object *obj, Matrix3& tm)
{
	bhkShapeRef retval;

Tazpn's avatar
Tazpn committed
	enum { cap_params, };
	enum { PB_MATERIAL, PB_RADIUS1, PB_RADIUS2, PB_LENGTH, };

	if (IParamBlock2* pblock2 = obj->GetParamBlockByID(cap_params))
	{
		float radius1 = 0.0f, radius2 = 0.0f, len = 0.0f;
		int mtl = NP_DEFAULT_HVK_MATERIAL;
		pblock2->GetValue(PB_RADIUS1, 0, radius1, FOREVER, 0);
		pblock2->GetValue(PB_RADIUS2, 0, radius2, FOREVER, 0);
		pblock2->GetValue(PB_LENGTH, 0, len, FOREVER, 0);
		pblock2->GetValue(PB_MATERIAL, 0, mtl, FOREVER, 0);

		bhkCapsuleShapeRef shape = new bhkCapsuleShape();
		shape->SetRadius((radius1 + radius2)/2.0f);
		shape->SetRadius1(radius1);
		shape->SetRadius2(radius2);
		shape->SetMaterial(HavokMaterial(mtl));

Tazpn's avatar
Tazpn committed
        Matrix3 ltm = node->GetNodeTM(0) * tm;
		Point3 center = ltm.GetTrans();
Tazpn's avatar
Tazpn committed

Tazpn's avatar
Tazpn committed
		Matrix3 rot = ltm;
Tazpn's avatar
Tazpn committed
		rot.NoTrans();
		rot.NoScale();

		float distFromCenter = len*Exporter::bhkScaleFactor/2.0f;

		Point3 pt1 = ((TransMatrix(Point3(0.0f, 0.0f, +distFromCenter)) * rot).GetTrans() + center) / Exporter::bhkScaleFactor;
		Point3 pt2 = ((TransMatrix(Point3(0.0f, 0.0f, -distFromCenter)) * rot).GetTrans() + center) / Exporter::bhkScaleFactor;
		shape->SetFirstPoint(TOVECTOR3(pt1));
		shape->SetSecondPoint(TOVECTOR3(pt2));

		retval = StaticCast<bhkShape>(shape);
	}
Tazpn's avatar
Tazpn committed
	return retval;
}


bhkShapeRef Exporter::makeTriStripsShape(INode *node, Matrix3& tm)
Tazpn's avatar
Tazpn committed
	Matrix3 sm = ScaleMatrix( GetScale(tm) );
Tazpn's avatar
Tazpn committed
   
	// Order of the vertices. Get 'em counter clockwise if the objects is
	// negatively scaled.
	ObjectState os = node->EvalWorldState(t);

	TriObject *tri = (TriObject *)os.obj->ConvertToType(t, Class_ID(TRIOBJ_CLASS_ID, 0));
	if (!tri)
		return false;

Tazpn's avatar
Tazpn committed
	Mesh &mesh = tri->GetMesh();
	mesh.buildNormals();
Tazpn's avatar
Tazpn committed
	bhkNiTriStripsShapeRef shape = makeTriStripsShape(mesh, sm);
	int lyr = OL_STATIC;
	npGetProp(node, NP_HVK_LAYER, lyr, NP_DEFAULT_HVK_LAYER);
	shape->SetNumDataLayers(1);
	shape->SetOblivionLayer(0, OblivionLayer(lyr));
	int mtl = NP_DEFAULT_HVK_MATERIAL;
	npGetProp(node, NP_HVK_MATERIAL, mtl, NP_DEFAULT_HVK_MATERIAL);
	shape->SetMaterial(HavokMaterial(mtl));
	return StaticCast<bhkShape>(shape);
Tazpn's avatar
Tazpn committed
bhkShapeRef	Exporter::makeConvexShape(INode *node, Object* obj, Matrix3& tm)
{
	bhkShapeRef shape;

	return shape;
}

Exporter::Result Exporter::scanForCollision(INode *node)
{ 
	if (node == NULL || (node->IsHidden() && !mExportHidden))
      return Exporter::Skip;
   // Get the bhk RigidBody modifier if available and then get the picked node.
   if (Modifier * mod = GetbhkCollisionModifier(node)){
      if (IParamBlock2* pblock = (IParamBlock2*)mod->GetReference(0)) {
         if (INode *collMesh = pblock->GetINode(0, 0)) {
            mCollisionNodes.insert(collMesh);
         } else {
            if (mSceneCollisionNode != NULL) {
               if (mExportCollision) {
                  throw runtime_error("There are more than one Collision mesh found at the Scene Level.");
               }
            } else {
               mSceneCollisionNode = node;
            }
         }
      }
   }
   // Check self to see if is one of our bhkXXXObject classes
   if (Object* obj = node->GetObjectRef())
   {
	   if (obj->ClassID() == BHKLISTOBJECT_CLASS_ID)
	   {
		   mCollisionNodes.insert(node);

		   const int PB_MESHLIST = 1;
		   IParamBlock2* pblock2 = obj->GetParamBlockByID(0);
		   int nBlocks = pblock2->Count(PB_MESHLIST);
		   for (int i = 0;i < pblock2->Count(PB_MESHLIST); i++) {
			   INode *tnode = NULL;
			   pblock2->GetValue(PB_MESHLIST,0,tnode,FOREVER,i);	
			   if (tnode != NULL && (!tnode->IsHidden() || mExportHidden)) {
				   mCollisionNodes.insert(tnode);
				   markAsHandled(tnode); // dont process collision since the list will 
			   }
		   }
	   }
	   else if (obj->SuperClassID() == HELPER_CLASS_ID &&
		   obj->ClassID().PartB() == BHKRIGIDBODYCLASS_DESC.PartB()) 
	   {
		   mCollisionNodes.insert(node);
	   }
Tazpn's avatar
Tazpn committed
	   else
	   {
		   Modifier* mod = GetbhkCollisionModifier(node);
		   if (mod != NULL)
		   {
			   mCollisionNodes.insert(node);
		   }
	   }
   if (npIsCollision(node))
   {
	   mCollisionNodes.insert(node);
   }

   for (int i=0; i<node->NumberOfChildren(); i++) {
      scanForCollision(node->GetChildNode(i));
   }
   return Exporter::Ok;
}
bool Exporter::isHandled(INode *node)
{
	return (mHandledNodes.find(node) != mHandledNodes.end());
}

bool Exporter::markAsHandled(INode* node)
{
	mHandledNodes.insert(node);
	return true;
}

bool Exporter::isCollision(INode *node)
{
	return (mCollisionNodes.find(node) != mCollisionNodes.end());
}

bhkShapeRef Exporter::makeListShape(INode *node, Matrix3& tm, bhkRigidBodyRef body)
{
	const int PB_MATERIAL = 0;
	const int PB_MESHLIST = 1;
	IParamBlock2* pblock2 = node->GetObjectRef()->GetParamBlockByID(0);
	int nBlocks = pblock2->Count(PB_MESHLIST);
	if (nBlocks > 0)
	{
		if (bhkRigidBodyInterface *irb = (bhkRigidBodyInterface *)node->GetObjectRef()->GetInterface(BHKRIGIDBODYINTERFACE_DESC))
		{
Tazpn's avatar
Tazpn committed
			float mass = irb->GetMass(0);
			float frict = irb->GetFriction(0);
			float resti = irb->GetRestitution(0);
			int lyr = irb->GetLayer(0);
			int msys = irb->GetMotionSystem(0);
			int qtype = irb->GetQualityType(0);
			float lindamp = irb->GetLinearDamping(0);
			float angdamp = irb->GetAngularDamping(0);
			float maxlinvel = irb->GetMaxLinearVelocity(0);
			float maxangvel = irb->GetMaxAngularVelocity(0);
			float pendepth = irb->GetPenetrationDepth(0);

			body->SetLayer(OblivionLayer(lyr));
			body->SetLayerCopy(OblivionLayer(lyr));
			body->SetMotionSystem(MotionSystem(msys));
			body->SetQualityType(MotionQuality(qtype));
			body->SetMass(mass);
			body->SetLinearDamping(lindamp);
			body->SetAngularDamping(angdamp);
			body->SetFriction(frict);
			body->SetRestitution(resti);
			body->SetMaxLinearVelocity(maxlinvel);
			body->SetMaxAngularVelocity(maxangvel);
			body->SetPenetrationDepth(pendepth);
		}

		bhkListShapeRef shape = new bhkListShape();
		int mtl = pblock2->GetInt(PB_MATERIAL, 0, 0);
		shape->SetMaterial(HavokMaterial(mtl));

		vector<bhkShapeRef> shapes;
		for (int i = 0; i < nBlocks; i++) {
			INode *tnode = NULL;
			pblock2->GetValue(PB_MESHLIST,0,tnode,FOREVER,i);	
			if (tnode != NULL && (!tnode->IsHidden() || mExportHidden))
			{
				bhkShapeRef subshape = makeCollisionShape(tnode, tm, body);
				if (subshape)
					shapes.push_back(subshape);
			}
		}
		shape->SetSubShapes(shapes);

		if (shapes.size() == 1) // ignore the list when only one object is present
		{
			return shapes[0];
		}
		else if (!shapes.empty())
		{
			return bhkShapeRef(shape);
		}
	}
	return bhkShapeRef();
Tazpn's avatar
Tazpn committed
}

bhkShapeRef Exporter::makeProxyShape(INode *node, Object *obj, Matrix3& tm)
{
	enum { list_params, bv_mesh, };  // pblock2 ID
	enum { PB_MATERIAL, PB_MESHLIST, PB_BOUND_TYPE, PB_CENTER, };
	enum { bv_type_none, bv_type_box, bv_type_shapes, bv_type_packed, bv_type_convex, };  // pblock ID

	bhkShapeRef shape;
	if (IParamBlock2* pblock2 = obj->GetParamBlockByID(list_params))
	{
		int bvType = bv_type_none;
		pblock2->GetValue(PB_BOUND_TYPE, 0, bvType, FOREVER, 0);
		if (bvType != bv_type_none) 
		{
			if (TriObject *triObj = (TriObject *)obj->ConvertToType(0, triObjectClassID))
			{