void Unreal3DExport::ShowSummary() { // Progress pInt->ProgressUpdate(Progress); // Display Summary if( bShowPrompts ) { ProgressMsg.printf(GetString(IDS_INFO_SUMMARY) , hAnim.NumFrames , hData.NumPolys , hData.NumVertices); if( bMaxResolution ) { TSTR buf; buf.printf(GetString(IDS_INFO_DIDPRECISION) , FileName); ProgressMsg += buf; } MaxMsgBox(pInt->GetMAXHWnd(),ProgressMsg.data(),ShortDesc(),MB_OK|MB_ICONINFORMATION); } }
///////////////////////////////////////////////////////////////////////////// // CMaxMaterialCollection STDMETHODIMP CMaxMaterialCollection::FinalConstruct() { HRESULT hr = E_FAIL, hr2 = E_FAIL; //simply make a reference to the scene material collection MtlBaseLib* pMtlLib = GetCOREInterface()->GetSceneMtls(); mLastSceneMtlLibSize = 0; #ifdef PERSIST_SCRATCH mpScratchMtlLib = NULL; #endif assert(pMtlLib); //RK: 10/21/04 -- this forces ACT toolkit to initialize at startup and avoids delays during material assignment. CComDispatchDriver act; /* FIXME: Use the .NET wrappers directly instead of this; act.CoCreateInstance( L"Autodesk.Act.Core.ContentSerializer" ); For some reason, the prog ID above is not being registered with the version we now install with Max 9, and I've had to resort to using the CLSID directly. Either we have the wrong version, or the new DLL just doesn't register its prog ID. (more likely it's the former) */ CLSID clsidCore; hr = CLSIDFromString(L"{978C551B-5919-42F7-81AB-690D66AB7B78}", &clsidCore); if( SUCCEEDED(hr) ) hr = act.CoCreateInstance( clsidCore ); if (SUCCEEDED(hr)) { CComVariant xml(L"<Material><StandardMtl name=\"Test\" /></Material>"); CComPtr<IUnknown> mtl; hr2 = mtl.CoCreateInstance( L"Autodesk.Act.Content.Material"); hr2 = act.Invoke1( L"DeserializeString", &xml ); } if ( FAILED(hr) || FAILED(hr2) ) { if (GetCOREInterface()->GetQuietMode()) GetCOREInterface()->Log()->LogEntry(SYSLOG_ERROR,NO_DIALOG, TSTR(GetString(IDS_PROJNAME)), TSTR(GetString(IDS_ACT_REGISTRATION_FAILED))); else MaxMsgBox( GetCOREInterface()->GetMAXHWnd(), TSTR(GetString(IDS_ACT_REGISTRATION_FAILED)), TSTR(GetString(IDS_PROJNAME)), MB_OK ); } assert(SUCCEEDED(hr) && SUCCEEDED(hr2)); //RK: This forces XMlmtl to initialize at startup. CComPtr<IVIZPointerClient> pPointerClient; hr = GetXMLImpExp(&pPointerClient); assert(SUCCEEDED(hr)); #ifdef RECYCLE_MATS #endif RegisterNotification(NotifyProc, this, NOTIFY_FILE_PRE_OPEN); RegisterNotification(NotifyProc, this, NOTIFY_FILE_POST_OPEN); RegisterNotification(NotifyProc, this, NOTIFY_FILE_OPEN_FAILED); RegisterNotification(NotifyProc, this, NOTIFY_FILE_PRE_MERGE); RegisterNotification(NotifyProc, this, NOTIFY_FILE_POST_MERGE); RegisterNotification(NotifyProc, this, NOTIFY_FILE_PRE_SAVE); RegisterNotification(NotifyProc, this, NOTIFY_FILE_POST_SAVE); RegisterNotification(NotifyProc, this, NOTIFY_PRE_IMPORT); RegisterNotification(NotifyProc, this, NOTIFY_POST_IMPORT); RegisterNotification(NotifyProc, this, NOTIFY_SYSTEM_PRE_NEW); RegisterNotification(NotifyProc, this, NOTIFY_SYSTEM_POST_NEW); RegisterNotification(NotifyProc, this, NOTIFY_SYSTEM_PRE_RESET); RegisterNotification(NotifyProc, this, NOTIFY_SYSTEM_POST_RESET); RegisterNotification(NotifyProc, this, NOTIFY_SCENE_UNDO); RegisterNotification(NotifyProc, this, NOTIFY_SCENE_REDO); #ifdef SUSPEND_UNDO RegisterNotification(NotifyProc, this, NOTIFY_SCENE_PRE_UNDO); RegisterNotification(NotifyProc, this, NOTIFY_SCENE_PRE_REDO); #endif RegisterNotification(NotifyProc, (void *)this, NOTIFY_MEDIT_SHOW); #ifdef TP_SUSPEND_FOR_FILELINK RegisterNotification(NotifyProc, this, NOTIFY_FILELINK_PRE_BIND ); RegisterNotification(NotifyProc, this, NOTIFY_FILELINK_POST_BIND ); RegisterNotification(NotifyProc, this, NOTIFY_FILELINK_PRE_DETACH ); RegisterNotification(NotifyProc, this, NOTIFY_FILELINK_POST_DETACH ); RegisterNotification(NotifyProc, this, NOTIFY_FILELINK_PRE_RELOAD ); RegisterNotification(NotifyProc, this, NOTIFY_FILELINK_POST_RELOAD ); RegisterNotification(NotifyProc, this, NOTIFY_FILELINK_PRE_ATTACH ); RegisterNotification(NotifyProc, this, NOTIFY_FILELINK_POST_ATTACH ); #endif //and finally a mechanism for other parts of the system to actively suspend TP RegisterNotification(NotifyProc, this, NOTIFY_TOOLPALETTE_MTL_SUSPEND ); RegisterNotification(NotifyProc, this, NOTIFY_TOOLPALETTE_MTL_RESUME ); //for DID 642266, pre and post cloning RegisterNotification(NotifyProc, this, NOTIFY_PRE_NODES_CLONED ); RegisterNotification(NotifyProc, this, NOTIFY_POST_NODES_CLONED ); RefResult res = ReplaceReference(SCENE_MTLLIB_REFNUM, (RefTargetHandle) pMtlLib); #ifdef PERSIST_SCRATCH res = ReplaceReference(SCRATCH_MTLLIB_REFNUM, (RefTargetHandle) &theScratchMtlLib); #endif Resume(); if(res == REF_SUCCEED) hr = S_OK; return hr; }
NxActor* MxActor::createNxActor() { if (m_bulletBody) { MaxMsgBox(NULL, _T("Error: body was already added to the dynamics world"), _T("Error"), MB_OK); return 0; } // find proxy node m_proxyNode = NULL; TSTR str = MxUserPropUtils::GetUserPropStr(m_node, "Proxy_Geometry"); char* proxyName = str.data(); if(proxyName && strlen(proxyName) > 0 && (stricmp(proxyName, "<None>") != 0)) { m_proxyNode = GetCOREInterface()->GetINodeByName(proxyName); } // ccMaxNode* pActorNode = ccMaxWorld::FindNode(m_node); assert(pActorNode); maxNodeActor = pActorNode; maxNodeProxy = NULL; if(m_proxyNode) { maxNodeProxy = ccMaxWorld::FindNode(m_proxyNode); assert(maxNodeProxy); //Point3 p1 = maxNodeActor->PhysicsNodePoseTM.GetRow(3), p2 = maxNodeProxy->PhysicsNodePoseTM.GetRow(3); Point3 p1 = maxNodeActor->NodePosInPhysics, p2 = maxNodeProxy->NodePosInPhysics; ProxyDistance = p1 - p2; } NxActorDesc& actorDesc = m_desc; NxBodyDesc& bodyDesc = m_bodyDesc; //LoadParameters(actorDesc, bodyDesc); actorDesc.globalPose = pActorNode->PhysicsNodePoseTM;//MxMathUtils::MaxMatrixToNx(pActorNode->PhysicsNodePoseTM); SaveLastPose(pActorNode->PhysicsNodePoseTM); //m_bodydesc.solverIterationCount = (NxU32)mSetting_solveriterationcount; // support it in future? std::vector<INode*> stack; std::vector<INode*> shapes; //Check if the object is using a proxy, in that case only those shapes should be added INode* current = m_node; if(m_proxyNode) current = m_proxyNode; stack.push_back(current); //DEBUG_S("List collision shapes"); while (stack.size() > 0) { current = stack[0]; if(stack.size() > 1) { stack[0] = stack.back(); } stack.pop_back(); if(current->EvalWorldState(0).obj->SuperClassID()==GEOMOBJECT_CLASS_ID) { shapes.push_back(current); } //go through grouped objects for(int i = 0; i < current->NumberOfChildren(); i++) { INode *c = current->GetChildNode(i); if (c->IsGroupMember()) { stack.push_back(c); } } } if (shapes.size() == 0) { if (gCurrentstream) gCurrentstream->printf("Unable to add %s as an actor, it has no shapes.\n", m_node->GetName()); return 0; } ccMaxNode* baseNode = pActorNode; if(maxNodeProxy) { baseNode = maxNodeProxy; } btAlignedObjectArray<btCollisionShape*> collisionShapes; btAlignedObjectArray<btTransform> localTransforms; for (int i = 0; i < shapes.size(); i++) { INode* current = shapes[i]; //DEBUG_F(" Debug adding shape node: %s\n", current->GetName()); ccMaxNode* pn = ccMaxWorld::FindNode(current); assert(pn); btCollisionShape* collisionShape = createShape(actorDesc, pn, pActorNode); collisionShapes.push_back(collisionShape); btTransform localTrans; max2Bullet(actorDesc.localPose,localTrans); localTransforms.push_back(localTrans); } //create a rigid body and add it to the world if (collisionShapes.size()) { btCollisionShape* collisionShape = 0; if (collisionShapes.size()==1) { collisionShape = collisionShapes[0]; } else { btCompoundShape* compound = new btCompoundShape(); for (int i=0;i<collisionShapes.size();i++) { compound->addChildShape(localTransforms[i],collisionShapes[i]); } //clear local pose actorDesc.localPose.IdentityMatrix(); collisionShape = compound; } if (collisionShape) { btVector3 localInertia(0,0,0); if (actorDesc.mass) { collisionShape->calculateLocalInertia(actorDesc.mass,localInertia); } m_bulletBody = new btRigidBody(actorDesc.mass,0,collisionShape,localInertia); m_bulletBody->setUserPointer(this); btScalar restitution; if (MxUserPropUtils::GetUserPropFloat(m_node, "Restitution", restitution)) { m_bulletBody->setRestitution(restitution); } btScalar staticFriction = 0.f; //take the maximum for now MxUserPropUtils::GetUserPropFloat(m_node, "StaticFriction", staticFriction); btScalar friction = 0.f; if (MxUserPropUtils::GetUserPropFloat(m_node, "Friction", friction)) { if (staticFriction>friction) friction = staticFriction; m_bulletBody->setFriction(friction); } // char bla[1024]; // sprintf(bla,"restitution=%f",restitution); // MaxMsgBox(NULL, _T(bla), _T("Error"), MB_OK); syncGlobalPose(); gDynamicsWorld->addRigidBody(m_bulletBody); // MaxMsgBox(NULL, _T("adding rigid body"), _T("Error"), MB_OK); } } bool isvalid = actorDesc.isValid(); actorDesc.name = m_node->GetName(); //NxMat34 pose0 = actorDesc.globalPose; //m_actor = gPluginData->getScene()->createActor(actorDesc); //if(! m_actor) return 0; //NxMat34 pose1 = m_actor->getGlobalPose(); // sometimes pose1 != pose0; it is strange //m_actor->userData = this; if(MxUserPropUtils::GetUserPropBool(m_node, "PutToSleep", false)) { //m_actor->putToSleep(); } // for collision force when contact happens /* NxU32 num = m_actor->getNbShapes(); NxShape*const* ps = m_actor->getShapes(); for(NxU32 i = 0; i < num; ++i) { NxShape* nxShape = ps[i]; nxShape->setFlag(NX_SF_POINT_CONTACT_FORCE, true); } */ //save last pose Matrix3 poseTM = maxNodeActor->GetCurrentTM(); SaveLastPose(poseTM); //return m_actor; return 0; }
/* Use the shape of node to create Bullet shape for the actor. ActorNode is the main Max node. */ btCollisionShape* MxActor::createShape(NxActorDesc& actorDesc, ccMaxNode* node, ccMaxNode* actorNode) { actorDesc.localPose.IdentityMatrix(); btCollisionShape* shape = NULL; const TimeValue t = ccMaxWorld::MaxTime(); Matrix3 nodePose = node->PhysicsNodePoseTM; // MaxMsgBox(NULL, _T("createShape"), _T("Error"), MB_OK); int geomType = MxUserPropUtils::GetUserPropInt(m_node, "GeometryType",1); NxShapeType type = node->ShapeType; //first apply manual overrides switch (geomType) { case 2: { type = NX_SHAPE_SPHERE; break; } case 3: { type = NX_SHAPE_BOX; break; } case 4: { type = NX_SHAPE_CAPSULE; break; } case 5: { type = NX_SHAPE_CONVEX; break; } case 6: { type = NX_SHAPE_MESH; break; } default: { } }; actorDesc.localPose = node->PhysicsNodePoseTM * actorNode->PhysicsNodePoseTMInv; switch (type) { case NX_SHAPE_SPHERE: { btScalar radius = node->PrimaryShapePara.Radius; shape = new btSphereShape(radius); break; }; case NX_SHAPE_BOX: { //adjust for difference in pivot points between 3ds max and Bullet (Bullet uses the box center) Matrix3 offset; offset.IdentityMatrix(); offset.SetTrans(2,node->PrimaryShapePara.BoxDimension.z()); actorDesc.localPose = actorDesc.localPose * offset; shape = new btBoxShape(node->PrimaryShapePara.BoxDimension.absolute()); break; } case NX_SHAPE_CAPSULE: { char bla[1024]; sprintf(bla,"capsule not properly supported yet, radius=%f,height=%f",node->PrimaryShapePara.Radius,node->PrimaryShapePara.Height); MaxMsgBox(NULL, _T(bla), _T("Error"), MB_OK); shape = new btCapsuleShape(node->PrimaryShapePara.Radius,node->PrimaryShapePara.Height); break; } case NX_SHAPE_CONVEX: { if(m_proxyNode) { MaxMsgBox(NULL, _T("Error: convex shape proxy not supported (yet)"), _T("Error"), MB_OK); //d->meshData = MxUtils::nodeToNxConvexMesh(proxyMesh); //Matrix3 pose = nodePose * actorNode->PhysicsNodePoseTMInv; //d->localPose = MxMathUtils::MaxMatrixToNx(pose); } else { if(node->SimpleMesh.numFaces > 255) { MaxMsgBox(NULL, _T("Error: number of vertices in a convex shape should be less than 256"), _T("Error"), MB_OK); //warning/Error } else { BOOL needDel = FALSE; TriObject* tri = MxUtils::GetTriObjectFromNode(node->GetMaxNode(),0.f,needDel); if (tri) { int numVerts = tri->NumPoints(); btConvexHullShape* convexHull = new btConvexHullShape(); //for center of mass computation, simplify and assume mass is at the vertices btCompoundShape* compound = new btCompoundShape(); btSphereShape sphere(0.1); btTransform tr; tr.setIdentity(); btAlignedObjectArray<btScalar> masses; btScalar childMass = actorDesc.mass/(btScalar)numVerts; for (int i=0;i<numVerts;i++) { btVector3 pt(tri->GetPoint(i).x,tri->GetPoint(i).y,tri->GetPoint(i).z); convexHull->addPoint(pt); tr.setOrigin(pt); compound->addChildShape(tr,&sphere); masses.push_back(childMass); } btTransform principal; btVector3 inertia; compound->calculatePrincipalAxisTransform(&masses[0],principal,inertia); delete compound; btTransform principalInv = principal.inverse(); compound = new btCompoundShape(); compound->addChildShape(principalInv,convexHull); shape = compound; Matrix3 offset; bullet2Max(principal,offset); actorDesc.localPose = actorDesc.localPose * offset; if (needDel) delete tri; } } //d->meshData = MxUtils::nodeToNxConvexMesh(node->SimpleMesh); //Matrix3 pose = nodePose * actorNode->PhysicsNodePoseTMInv; //d->localPose = MxMathUtils::MaxMatrixToNx(pose); } break; } case NX_SHAPE_MESH: { BOOL needDel = FALSE; TriObject* tri = MxUtils::GetTriObjectFromNode(node->GetMaxNode(),0.f,needDel); if (tri) { int numVerts = tri->NumPoints(); btTriangleMesh* meshInterface = new btTriangleMesh(); Mesh& mesh = tri->GetMesh(); if (mesh.getNumFaces()>0) { for (int i=0;i<mesh.getNumFaces();i++) { Point3 p0=tri->GetPoint(mesh.faces[i].v[0]); Point3 p1=tri->GetPoint(mesh.faces[i].v[1]); Point3 p2=tri->GetPoint(mesh.faces[i].v[2]); meshInterface->addTriangle( btVector3(p0.x,p0.y,p0.z),btVector3(p1.x,p1.y,p1.z),btVector3(p2.x,p2.y,p2.z)); } if (actorDesc.mass>0) { // MaxMsgBox(NULL, _T("btGImpactMeshShape"), _T("Error"), MB_OK); btGImpactMeshShape* trimesh = new btGImpactMeshShape(meshInterface); shape = trimesh; } else { // MaxMsgBox(NULL, _T("btBvhTriangleMeshShape"), _T("Error"), MB_OK); btBvhTriangleMeshShape* trimesh = new btBvhTriangleMeshShape(meshInterface,true); shape = trimesh; } } else { MaxMsgBox(NULL, _T("Error: no faces"), _T("Error"), MB_OK); } if (needDel) delete tri; } else { MaxMsgBox(NULL, _T("Error: couldn't GetTriObjectFromNode"), _T("Error"), MB_OK); } break; } default: { MaxMsgBox(NULL, _T("unknown shape type"), _T("Error"), MB_OK); } }; #if 0 NxShapeDesc* pd = NULL; NxShapeType type = node->ShapeType; PxSimpleMesh proxyMesh; Matrix3 nodePose = node->PhysicsNodePoseTM; if(m_proxyNode) { // for proxy, using mesh //type = NX_SHAPE_MESH; proxyMesh.clone(node->SimpleMesh); Point3 pos = nodePose.GetRow(3); pos = pos + ProxyDistance; nodePose.SetRow(3, pos); } if((type == NX_SHAPE_MESH) && (Interactivity != RB_STATIC)) { type = NX_SHAPE_CONVEX; } // /* bool NeedCCDSkeleton = (Interactivity != RB_STATIC) && (MxUserPropUtils::GetUserPropBool(node->GetMaxNode(), "EnableCCD", false)); if(NeedCCDSkeleton) { NxPhysicsSDK* psdk = gPluginData->getPhysicsSDK(); psdk->setParameter(NX_CONTINUOUS_CD, 1); psdk->setParameter(NX_CCD_EPSILON, 0.01f); } */ // create CCD skeleton for the shape //TODO("implement CCD skeleton creation"); PX_CCD_SKELETON ccdType = (PX_CCD_SKELETON) MxUserPropUtils::GetUserPropInt(node->GetMaxNode(), "px_shape_ccdtype", 1); switch(type) { default: if (gCurrentstream) gCurrentstream->printf("Unable to create a shape of node \"%s\", unknown shape type: %d.\n", node->GetMaxNode()->GetName(), type); return false; } // load property settings and material things. //LoadShapeProperties(*pd, node->GetMaxNode()); //CCD flag pd->name = node->GetMaxNode()->GetName(); pd->userData = node; // bool isvalid = pd->isValid(); actorDesc.shapes.push_back(pd); #endif return shape; }
int Unreal3DExport::DoExport( const TCHAR *name, ExpInterface *ei, Interface *i, BOOL suppressPrompts, DWORD options ) { int Result = FALSE; // Set a global prompt display switch bShowPrompts = suppressPrompts ? false : true; bExportSelected = (options & SCENE_EXPORT_SELECTED) ? true : false; // Get file names SplitFilename(TSTR(name), &FilePath, &FileName, &FileExt); if( MatchPattern(FileName,TSTR(_T("*_d")),TRUE) || MatchPattern(FileName,TSTR(_T("*_a")),TRUE) ) { FileName = FileName.Substr(0,FileName.length()-2); } ModelFileName = FilePath + _T("\\") + FileName + TSTR(_T("_d")) + FileExt; AnimFileName = FilePath + _T("\\") + FileName + TSTR(_T("_a")) + FileExt; ScriptFileName = FilePath + _T("\\") + FileName + TSTR(_T("_rc.uc")); // Open Log fLog = _tfopen(FilePath + _T("\\") + FileName + _T(".log") ,_T("wb")); // Init pInt = GetCOREInterface(); pInt->ProgressStart( GetString(IDS_INFO_INIT), TRUE, fn, this); Progress += U3D_PROGRESS_INIT; try { MyErrorProc pErrorProc; SetErrorCallBack(&pErrorProc); ReadConfig(); //if(bShowPrompts) /*DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_PANEL), GetActiveWindow(), Unreal3DExportOptionsDlgProc, (LPARAM)this);*/ //if(showPrompts) { // Prompt the user with our dialogbox, and get all the options. if(!DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_PANEL), GetActiveWindow(), Unreal3DExportOptionsDlgProc, (LPARAM)this)) { throw CancelException(); } } // Enumerate interesting nodes Init(); // Fetch data from nodes GetTris(); GetAnim(); // Prepare data for writing Prepare(); // Write to files WriteScript(); WriteModel(); WriteTracking(); // Show optional summary ShowSummary(); WriteConfig(); Result = IMPEXP_SUCCESS; } catch( CancelException& ) { Result = IMPEXP_CANCEL; } catch( MAXException& e ) { if( bShowPrompts && !e.message.isNull() ) { MaxMsgBox(pInt->GetMAXHWnd(),e.message,ShortDesc(),MB_OK|MB_ICONERROR); } Result = IMPEXP_FAIL; } // Release scene if( pScene != NULL ) { pScene->ReleaseIGame(); pScene = NULL; } // Close files fclosen(fMesh); fclosen(fAnim); fclosen(fLog); fclosen(fScript); // Return to MAX pInt->ProgressEnd(); return Result; }