//************************************************************************** //* Export.cpp - Virtools File Exporter //* //* Romain Sididris - Copyright (c) Virtools 2000 //* //* (Based on Ascii File Exporter source code //* By Christer Janson //* Kinetix Development //* Copyright (c) Kinetix 1997, All Rights Reserved. ) //* //* This module contains the main export functions //* //*************************************************************************** #include "Precomp.h" #include "Max2Nmo.h" #include "bitmap.h" #include "icustattribcontainer.h" #include "TreeViewUtil.h" #include "wm3_morpher.h" #ifdef MAX51 #include "modstack.h" #include "iEditNormals.h" #include "PBBitmap.h" #include "MNMATH.h" #ifndef MAX9 #if _MSC_VER >= 1400 // visual studio 8 #include "d3dx9.h" // for post 9 versions "d3dx9.h" is included in "IViewportManager.h" #else #include "d3dx8.h" // for post 9 versions "d3dx9.h" is included in "IViewportManager.h" #endif #endif #include "IViewportManager.h" #endif extern BOOL g_CharacterStudio312; _inline unsigned long GetMSB(unsigned long data); /**************************************************************************** Convenient Functions For Lightmap Manipulation ****************************************************************************/ #ifdef MAX51 class LM { protected: enum { lightmap_params }; enum { pb_light_texture, pb_diffuse_texture, pb_diffuse_mapping, pb_lightmap_filename, pb_diffuse_filename, pb_lightmap_on, pb_diffuse_on, pb_lightmap_mapping, }; static IParamBlock2* GetLightMapParameters( Mtl* mtl ){ //--- Find Effects IDXShaderManagerInterface* shaderManager = GetDXShaderManager(); if( !shaderManager ) return NULL; CustAttrib *cust = shaderManager->FindViewportShaderManager(mtl); if( !cust ) return NULL; IViewportShaderManager* viewShader = (IViewportShaderManager *)(cust->GetInterface(VIEWPORT_SHADER_MANAGER_INTERFACE)); if( !viewShader ) return NULL; ///// #ifdef MAX7 IViewportShaderManager2* viewShader2 = (IViewportShaderManager2 *)(cust->GetInterface(VIEWPORT_SHADER_MANAGER_INTERFACE2)); if( !viewShader2 ) return NULL; if( !viewShader2->IsDxStdMtlEnabled() ){ return NULL; } #endif ///// ReferenceTarget* rt = viewShader->GetActiveEffect(); if( !rt ) return NULL; //--- if it's a lightmap material #define LIGHTMAP_CLASS_ID Class_ID(0x727d33be, 0x3255c000) if( rt->ClassID() == LIGHTMAP_CLASS_ID ){ //--- Get Parameter Block Info IDXDataBridge* db = (IDXDataBridge*)(rt->GetInterface( VIEWPORT_SHADER_CLIENT_INTERFACE )); if( !db ) return NULL; return rt->GetParamBlockByID( lightmap_params ); } return NULL; } public: static BOOL ShaderEnabled( Mtl* mtl ){ return ( GetLightMapParameters( mtl ) != NULL ); } //--- Read Base Texture Parameters static BOOL BaseTextureEnabled( Mtl* mtl ){ IParamBlock2* pblock = GetLightMapParameters(mtl); BOOL diffuseOn = FALSE; pblock->GetValue( pb_diffuse_on, 0, diffuseOn, FOREVER); return diffuseOn; } static const char* BaseTextureFilename( Mtl* mtl ){ IParamBlock2* pblock = GetLightMapParameters(mtl); TCHAR* diffuseFilename; PBBitmap* pbbm = NULL; pblock->GetValue( pb_diffuse_texture, 0, (PBBitmap*&)pbbm, FOREVER ); diffuseFilename = (TCHAR*)pbbm->bi.Name(); return diffuseFilename; } static int BaseTextureMapping( Mtl* mtl ){ IParamBlock2* pblock = GetLightMapParameters(mtl); int diffuseMapping = 1; pblock->GetValue( pb_diffuse_mapping, 0, diffuseMapping, FOREVER); return diffuseMapping; } //--- Read Lightmap Texture Parameters static BOOL LightmapTextureEnabled( Mtl* mtl ){ IParamBlock2* pblock = GetLightMapParameters(mtl); BOOL lightOn = FALSE; pblock->GetValue( pb_lightmap_on, 0, lightOn, FOREVER); return lightOn; } static const char* LightmapTextureFilename( Mtl* mtl ){ IParamBlock2* pblock = GetLightMapParameters(mtl); TCHAR* lightFilename; PBBitmap* pbbm = NULL; pblock->GetValue( pb_light_texture, 0, (PBBitmap*&)pbbm, FOREVER ); lightFilename = (TCHAR*)pbbm->bi.Name(); return lightFilename; } static int LightmapTextureMapping( Mtl* mtl ){ IParamBlock2* pblock = GetLightMapParameters(mtl); int lightMapping = 3; pblock->GetValue( pb_lightmap_mapping, 0, lightMapping, FOREVER); return lightMapping; } }; #endif // MAX51 /**************************************************************************** GeomObject output (Geom objects are tri or patch based meshes) ****************************************************************************/ void Max2Nmo::ExportGeomObject(INode* node) { ObjectState os = node->EvalWorldState(GetStaticFrame()); if (!os.obj) return; // Targets are actually geomobjects, but we will export them // from the camera and light objects, so we skip them here. if (os.obj->ClassID() == Class_ID(TARGET_CLASS_ID, 0)) return; Control* tmC = node->GetTMController(); //Check if it's a Biped bodypart?? BOOL Biped = FALSE; if ((tmC->ClassID()== FOOTPRINT_CLASS_ID) || (tmC->ClassID()== BIPSLAVE_CONTROL_CLASS_ID) || (tmC->ClassID()== BIPBODY_CONTROL_CLASS_ID)) { Biped = TRUE; } //Check if it's a Bone?? BOOL Bone = FALSE; if (os.obj->ClassID()== BONE_OBJ_CLASSID ) { Bone = TRUE; } //Create a bodypart if it's a character or a 3D object. CK3dEntity* Entity = NULL; if (GetExportAsCharacter()) Entity = (CK3dEntity*)VirtoolsExporter->AddCharacterBodyPart(node->GetName(),node); else Entity = (CK3dEntity*)VirtoolsExporter->Add3dObject(node->GetName(),node); ExportNodeBase(node, Entity); //Save the bone or Biped geometry if GetSaveBipedGeom() BOOL SaveGeometry = TRUE; if(Biped || Bone){ if( !GetSaveBipedGeom() ) SaveGeometry=FALSE; } if (SaveGeometry) { if (os.obj->ClassID() == Class_ID(PATCHOBJ_CLASS_ID, 0)) ExportPatchMesh(node,Entity); else ExportMesh(node,Entity); } else { Report(REPORT_HLEVEL,"%sBiped Object: %s.....",StrGroupIndent().Str(),node->GetName()); } if (Biped && !GetSaveBipedGeom() ) Entity->Show(CKHIDE); // Rename the floor reference object if (tmC->ClassID() == FOOTPRINT_CLASS_ID) { Entity->SetName("FloorRef"); } if( GetExportAnimation() ) ExportAnimKeys(node,Entity); Report(REPORT_HLEVEL,"...Done\r\n"); } /**************************************************************************** Shape (Curve) output ****************************************************************************/ void Max2Nmo::ExportShapeObject(INode* node) { TimeValue t = GetStaticFrame(); ObjectState os = node->EvalWorldState(t); if (!os.obj || os.obj->SuperClassID()!=SHAPE_CLASS_ID) { return; } Report(REPORT_HLEVEL,"%sCurve : %s.....",StrGroupIndent().Str(),node->GetName()); int deleteIt; SplineShape *pSplineShape = GetSplineShapeFromNode(node,t,deleteIt); Matrix3 ObjectTM = node->GetObjectTM(t); if(pSplineShape && pSplineShape->shape.SplineCount()>=1) { // We only convert if the shape contains only one spline Spline3D* spline3D = pSplineShape->shape.GetSpline(0); if(spline3D) { CKCurve* VirtoolsCurve = VirtoolsExporter->AddCurve(node->GetName(),node); int KnotCount = spline3D->KnotCount(); for (int i=0; iAddCurvePoint(VirtoolsCurve,NULL,NULL); SplineKnot SKnot = spline3D->GetKnot(i); Point3 WorldKnot = ObjectTM.PointTransform(SKnot.Knot()); int KnotType = SKnot.Ktype(); int SegType = SKnot.Ltype(); // Max and Virtools tangents conversion => // scale by a factor of 3 ????? Point3 WorldInTan = -spline3D->GetRelInVec(i)*3.0f; Point3 WorldOutTan = spline3D->GetRelOutVec(i)*3.0f; if (KnotType != KTYPE_AUTO) { pt->UseTCB(FALSE); pt->SetTangents(&VxVector(WorldInTan.x,WorldInTan.z,WorldInTan.y), &VxVector(WorldOutTan.x,WorldOutTan.z,WorldOutTan.y)); } if (SegType == LTYPE_LINE) pt->SetLinear(TRUE); // Swap Y & Z to reflect referential changes between Max and Virtools pt->SetPosition(&VxVector( WorldKnot.x,WorldKnot.z,WorldKnot.y),NULL,TRUE ); } if (spline3D->Closed()) VirtoolsCurve->Close(); ExportNodeBase(node,VirtoolsCurve); if( GetExportAnimation() ) ExportAnimKeys(node,VirtoolsCurve); } // Deletes created shape if (deleteIt) pSplineShape->DeleteMe(); } Report(REPORT_HLEVEL,"Done\r\n"); } /**************************************************************************** Light output ****************************************************************************/ void Max2Nmo::ExportLightObject(INode* node) { TimeValue t = GetStaticFrame(); ObjectState os = node->EvalWorldState(t); if (!os.obj) return; Report(REPORT_HLEVEL,"%sLight : %s.....",StrGroupIndent().Str(),node->GetName()); GenLight* light = (GenLight*)os.obj; struct LightState ls; Interval valid = FOREVER; Interval animRange = ip->GetAnimRange(); light->EvalLightState(t, valid, &ls); VXLIGHT_TYPE LightType = VX_LIGHTPOINT; switch(ls.type) { case AMBIENT_LGT: // no good conversion in Virtools for Ambient Lights... creating arbitrarily a Point Light case OMNI_LGT: LightType = VX_LIGHTPOINT; break; case SPOT_LGT: LightType = VX_LIGHTSPOT; break; case DIRECT_LGT: LightType = VX_LIGHTDIREC; break; } INode* target = LightType == VX_LIGHTSPOT ? node->GetTarget() : NULL; CKLight* VirtoolsLight = NULL; CK3dEntity* VirtoolsLightTarget = NULL; // Virtools Objects creation VirtoolsLight = (CKLight*)VirtoolsExporter->AddLight(target? TRUE:FALSE,LightType,node->GetName(),node); ExportNodeBase(node, VirtoolsLight); if (LightType == VX_LIGHTSPOT) { VirtoolsLight->SetHotSpot(DegToRad(ls.hotsize)); VirtoolsLight->SetFallOff(DegToRad(ls.fallsize)); if (target) { VirtoolsLightTarget = ((CKTargetLight* )VirtoolsLight)->GetTarget(); ExportNodeBase(target, VirtoolsLightTarget); } } VirtoolsLight->SetColor(VxColor(ls.color.r,ls.color.g,ls.color.b)); VirtoolsLight->SetSpecularFlag(ls.affectSpecular); VirtoolsLight->Active(ls.on); // if attenuation is not used (e.g. a SkyLight), set a default range VirtoolsLight->SetRange(ls.useAtten ? ls.attenEnd : 200); if( GetExportAnimation() ){ // Export animation keys for the light node ExportAnimKeys(node,VirtoolsLight); // If we have a target, export animation keys for the target too. if (target && VirtoolsLightTarget) { ExportAnimKeys(target,VirtoolsLightTarget); } } Report(REPORT_HLEVEL,"Done\r\n"); } /**************************************************************************** Camera output ****************************************************************************/ void Max2Nmo::ExportCameraObject(INode* node) { TimeValue t = GetStaticFrame(); ObjectState os = node->EvalWorldState(t); if (!os.obj) return; Report(REPORT_HLEVEL,"%sCamera : %s.....",StrGroupIndent().Str(),node->GetName()); CameraObject *cam = (CameraObject *)os.obj; CameraState cs; Interval valid = FOREVER; Interval animRange = ip->GetAnimRange(); cam->EvalCameraState(t,valid,&cs); INode* target = node->GetTarget(); CKCamera* VirtoolsCamera = NULL; CK3dEntity* VirtoolsCameraTarget = NULL; // Virtools Objects creation VirtoolsCamera = VirtoolsExporter->AddCamera(target? TRUE:FALSE,node->GetName(),node); ExportNodeBase(node, VirtoolsCamera); if (target) { VirtoolsCameraTarget = ((CKTargetCamera* )VirtoolsCamera)->GetTarget(); ExportNodeBase(target, VirtoolsCameraTarget); } if (cs.manualClip) { VirtoolsCamera->SetFrontPlane(cs.hither<1.0f ? 1.0f : cs.hither); VirtoolsCamera->SetBackPlane(cs.yon); } else { VirtoolsCamera->SetFrontPlane(cs.nearRange<1.0f ? 1.0f : cs.nearRange); VirtoolsCamera->SetBackPlane(cs.farRange); } VirtoolsCamera->SetFov(cs.fov); if( GetExportAnimation() ){ // Export animation keys for the camera node ExportAnimKeys(node,VirtoolsCamera); // If we have a target, export animation keys for the target too. if (target && VirtoolsCameraTarget) { ExportAnimKeys(target,VirtoolsCameraTarget); } } //Fog Settings depends on the current camera in MAx TSTR nodeName(node->GetName()); TSTR cameraName(GetStartingCamera()); if(nodeName==cameraName) ExportStartingCamera( VirtoolsCamera, cam->GetEnvRange(t,ENV_NEAR_RANGE,valid ), cam->GetEnvRange(t,ENV_FAR_RANGE,valid ) ); Report(REPORT_HLEVEL,"Done\r\n"); } /**************************************************************************** Helper object output ****************************************************************************/ void Max2Nmo::ExportHelperObject(INode* node) { // We don't really know what kind of helper this is, but by exporting // the Classname of the helper object, the importer has a chance to // identify it. Object* helperObj = node->EvalWorldState(GetStaticFrame()).obj; TSTR className="Dummy"; if (helperObj) { helperObj->GetClassName(className); } if (node->IsGroupHead() && !GetGroupAsPlace()) className = "Group"; CK3dEntity* dummy = NULL; if (node->IsGroupHead() && GetGroupAsPlace()) { dummy = (CK3dEntity *)VirtoolsExporter->AddPlace(node->GetName(),node); className = "Place"; } else { if(GetExportAsCharacter()) dummy = (CK3dEntity*)VirtoolsExporter->AddCharacterBodyPart(node->GetName(),node); else dummy = VirtoolsExporter->AddDummyObject(node->GetName(),node); } Report(REPORT_HLEVEL,"%s%s : %s.....",StrGroupIndent().Str(),className,node->GetName()); ExportNodeBase(node,dummy); if( GetExportAnimation() ) ExportAnimKeys(node,dummy); if (node->IsGroupHead()) GroupIndent+=2; Report(REPORT_HLEVEL,"Done\r\n"); } /**************************************************************************** Node Base (Hierarchy + position ) ****************************************************************************/ void Max2Nmo::ExportNodeBase(INode* node,CK3dEntity* entity) { ObjectState os = node->EvalWorldState(GetStaticFrame()); if (!os.obj) return; VxMatrix VirtoolsMat; Matrix3 MaxMat = node->GetNodeTM(GetStaticFrame()); // Special Matrix conversion for lights if (CKIsChildClassOf(entity,CKCID_CAMERA) || CKIsChildClassOf(entity,CKCID_LIGHT)) ConvertMaxLightMatrix2Virtools(MaxMat,VirtoolsMat); else ConvertMaxMatrix2Virtools(MaxMat,VirtoolsMat); //Check Matrix VxVector Z; Z = CrossProduct(*(VxVector*)&VirtoolsMat[0][0],*(VxVector*)&VirtoolsMat[1][0]); if (DotProduct(Z,*(VxVector *)&VirtoolsMat[2][0])<0) { Report(REPORT_HLEVEL,"\r\nWarning Indirect Matrix detected for %s object...\r\n",entity->GetName()); Report(REPORT_HLEVEL,"\r\nAnimation may not be correct for this object...\r\n"); } // World Position entity->SetWorldMatrix(VirtoolsMat,TRUE); // Show/Hide entity->Show( node->IsHidden(0) ? CKHIDE : CKSHOW); #define MY_BIPED_BONE_CLASS_ID Class_ID(0x00009125, 0x00000000) //Hide Helpers if( GetHideHelpers() ) if( !(os.obj->SuperClassID() == GEOMOBJECT_CLASS_ID) || //Stay visible if a Geom Object (os.obj->ClassID() == BONE_OBJ_CLASSID) || //but hide bones even if they are Geom Object (os.obj->SuperClassID() == HELPER_CLASS_ID) || (os.obj->ClassID() == MY_BIPED_BONE_CLASS_ID) // Fix Thomas Bucher : bipeds were not hidden formerly ; I checked the classID of biped bones, and I've not found their define, so I set it manually ){ entity->Show( CKHIDE ); } if (!GetGroupAsPlace()) { CKGroup* group = CKGroups.Size() ? CKGroups.Back() : NULL; if (group) { group->AddObject(entity); } } INode* parent = node->GetParentNode(); if (parent && !parent->IsRootNode()) { CK3dEntity *VirtoolsParent=VirtoolsExporter->GetEntityByKey(parent); if( !VirtoolsParent ){ VirtoolsParent=VirtoolsExporter->GetCameraByKey(parent); } if( !VirtoolsParent ){ VirtoolsParent=VirtoolsExporter->GetLightByKey(parent); } entity->SetParent(VirtoolsParent); } } #ifdef MAX51 #define EDIT_NORMALS_CLASS_ID Class_ID(0x4aa52ae3, 0x35ca1cde) // Try to find if a physique modifier is in the modifier stack of a node Modifier* FindEditNormalModifier (INode* node) { // Get object from node. Abort if no object. Object* pObj = node->GetObjectRef(); if (!pObj) return NULL; // Is derived object ? SClass_ID sid = pObj->SuperClassID(); while (pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID) { // Yes -> Cast. IDerivedObject* pDerObj = static_cast(pObj); // Iterate over all entries of the modifier stack. int ModStackIndex = 0; while (ModStackIndex < pDerObj->NumModifiers()) { // Get current modifier. Modifier* mod = pDerObj->GetModifier(ModStackIndex); // Is this Physique ? if (mod->ClassID() == EDIT_NORMALS_CLASS_ID ) { return mod; } // Next modifier stack entry. ModStackIndex++; } pObj = pDerObj->GetObjRef(); } // Not found. return NULL; } #endif void Max2Nmo::DeleteNodes(const INodeTab& nodes) { for (int n = 0; n < nodes.Count(); ++n) { ip->DeleteNode(nodes[n], FALSE); } } class UndoStackRestorer { public: UndoStackRestorer() { theHold.SuperBegin(); } ~UndoStackRestorer() { theHold.SuperCancel(); } }; void Max2Nmo::ExportBlendShapes(INode* node, Mesh* refMesh, VirtoolsTransitionMesh& vMesh) { if (GetModifierCount(node, MR3_CLASS_ID) > 1) { Report(REPORT_MLEVEL, "\r\nMesh %s has several 'Morpher' modifiers : blend shapes will be exported for top modifier only, other modifier(s) will be ignored!", node->GetName()); } UndoStackRestorer undoStackRestorer; Modifier* morphModifier = FindModifier(node, MR3_CLASS_ID); if (!morphModifier) return; //morphChannel *chanPtr = &mr3->chanBank[0]; refMesh->buildNormals(); int faceCount = refMesh->getNumFaces(); int vertexCount = refMesh->getNumVerts(); // INodeTab srcNode; INodeTab destNode; srcNode.Append(1, &node); // Note : clone nodes create an entry in the undo stack if (!ip->CloneNodes(srcNode, Point3(0.f, 0.f, 0.f), false, NODE_COPY, NULL, &destNode)) return; if (destNode.Count() == 0) { return; } node = destNode[0]; // work on copy int modifierIndex; IDerivedObject *derivedObject; morphModifier = FindModifier(node, MR3_CLASS_ID, &modifierIndex, &derivedObject); if (!morphModifier) { return; } MorphR3* mr3 = (MorphR3*)(morphModifier); // Matrix3 offsetTM = GetNodeOffsetTM(node); float defaultWeights[100]; // Initial mesh with no blend shapes int bsCount = 0; for (int i = 0; i < 100; ++i) { defaultWeights[i] = 0.f; if (!morphModifier->SubAnim(i + 1)) { continue; } IParamBlock* pb = (IParamBlock*) morphModifier->SubAnim(i + 1); pb->GetValue(0, 0, defaultWeights[i], FOREVER); pb->SetValue(0, 0, 0.f); ++ bsCount; } Report(REPORT_MLEVEL, "\r\nExporting %u blend shapes for mesh %s", bsCount, node->GetName()); BOOL needDelRefTri; TriObject* refTri = GetTriObjectFromNode(node, 0, needDelRefTri); if (!refTri) { return; } refMesh = &refTri->GetMesh(); refMesh->buildNormals(); // Must retain pos and normals for future comparison XArray refPos; XArray refNormal; refPos.Resize(faceCount * 3); refNormal.Resize(faceCount * 3); for (int fIndex = 0; fIndex < faceCount; ++fIndex) { Face* refFace = &refMesh->faces[fIndex]; for (int corner = 0; corner < 3; ++corner) { refNormal[fIndex * 3 + corner] = offsetTM.VectorTransform(GetVertexNormal(refMesh, fIndex, refMesh->getRVertPtr(refFace->getVert(corner)))); refPos[fIndex * 3 + corner] = offsetTM.PointTransform(refMesh->verts[refFace->v[corner]]); } } if (needDelRefTri) delete refTri; // for each channel for (int i = 0; i < 100; ++i) { if (!morphModifier->SubAnim(i + 1)) continue; Report(REPORT_LLEVEL, "\r\nExporting blend shape #%u (%s) of mesh %s", i, (const char *) morphModifier->SubAnimName(i + 1), node->GetName()); // progressive morph target export XArray pmWeights; // weights for each progressive morph pmWeights.PushBack(0.f); for (int progMorph = 0; progMorph < mr3->chanBank.m_First[i].mNumProgressiveTargs; ++progMorph) { float weight = (progMorph == 0) ? mr3->chanBank.m_First[i].mTargetPercent : mr3->chanBank.m_First[i].mTargetCache.m_First[progMorph - 1].mTargetPercent; pmWeights.PushBack(weight); } pmWeights.PushBack(100.f); pmWeights.PushBack(FLT_MAX); // Evaluate result vMesh.AddLogicalBlendShape((const char *) morphModifier->SubAnimName(i + 1)); for (int progMorph = 1; progMorph < pmWeights.Size() - 1; ++progMorph) { // Reset all blend shapes but this one for (int j = 0; j < 100; ++j) { if (!morphModifier->SubAnim(j + 1)) continue; IParamBlock* pb = (IParamBlock*) morphModifier->SubAnim(j + 1); pb->SetValue(0, 0, j == i ? pmWeights[progMorph] : 0.f); if (i != j && strcmp(morphModifier->SubAnimName(j + 1), morphModifier->SubAnimName(i + 1)) == 0) { Report(REPORT_HLEVEL, "\r\nDuplicated blend shape name found in node %s for channel #%u! name = %s. Some animation tracks will be inoperant!", node->GetName(), i, morphModifier->SubAnimName(i + 1)); } } // Evaluate result ObjectState os = derivedObject->Eval(0, modifierIndex); // if (!os.obj || os.obj->SuperClassID() != GEOMOBJECT_CLASS_ID) continue; // I'm superstitious ... // BOOL needDel; TriObject* tri = GetTriObjectFromNode(node, 0, needDel); if (!tri) continue; Mesh* tarMesh = &tri->GetMesh(); tarMesh->buildNormals(); if (tarMesh->getNumVerts() != vertexCount && tarMesh->getNumFaces() != faceCount) { if (needDel) delete tri; continue; } // VirtoolsPerFaceBlendShape bs; bs.m_LogicalBlendShape = vMesh.GetLogicalBlendShapeCount() - 1; float inDelta = 0.01f * (pmWeights[progMorph] - pmWeights[progMorph - 1]); float outDelta = 0.01f * (pmWeights[progMorph + 1] - pmWeights[progMorph]); bs.m_InSlope = inDelta == 0.f ? FLT_MAX : (1.f / inDelta); bs.m_OutSlope = outDelta == 0.f ? FLT_MAX : (- 1.f / outDelta); bs.m_Weight = 0.01f * pmWeights[progMorph]; // // Since referential is not the same in Virtools indices 1 & 2 are swapped static const int vx[3] = { 0, 2, 1 }; // Point3 tarPos[3], tarNormal[3]; const Point3* currRefPos = refPos.Begin(); const Point3* currRefNormal = refNormal.Begin(); for (int fIndex = 0; fIndex < faceCount; ++fIndex, currRefPos +=3, currRefNormal +=3) { Face* tarFace = &tarMesh->faces[fIndex]; for (int corner = 0; corner < 3; ++corner) { tarNormal[corner] = offsetTM.VectorTransform(GetVertexNormal(tarMesh, fIndex, tarMesh->getRVertPtr(tarFace->getVert(corner)))); tarPos[corner] = offsetTM.PointTransform(tarMesh->verts[tarFace->v[corner]]); } if (currRefPos[0] != tarPos[0] || currRefPos[1] != tarPos[1] || currRefPos[2] != tarPos[2] || currRefNormal[0] != tarNormal[0] || currRefNormal[1] != tarNormal[1] || currRefNormal[2] != tarNormal[2] ) { VirtoolsFaceDelta delta; delta.m_Index = fIndex; for (int corner = 0; corner < 3; ++corner) { Point3 posDelta = tarPos[corner] - currRefPos[corner]; Point3 normalDelta = tarNormal[corner] - currRefNormal[corner]; delta.m_PosDelta[vx[corner]].Set(posDelta.x, posDelta.z, posDelta.y); delta.m_NormalDelta[vx[corner]].Set(normalDelta.x, normalDelta.z, normalDelta.y); } bs.m_Faces.PushBack(delta); } } // if (needDel) delete tri; vMesh.AddPhysicalBlendShape(bs); } vMesh.AddDefaultBlendShapeWeight(0.01f * defaultWeights[i]); } } /**************************************************************************** Mesh output ****************************************************************************/ void Max2Nmo::ExportMesh(INode* node, CK3dEntity* ent) { // inactivate any morph modifier during the export Modifier* morphModifier = FindModifier(node, MR3_CLASS_ID); int morphModEnabled; if (morphModifier) { morphModEnabled = morphModifier->IsEnabled(); morphModifier->DisableMod(); } TimeValue t = GetStaticFrame(); Mtl* nodeMtl = node->GetMtl(); Matrix3 tm = node->GetNodeTM(t); int i; BOOL ret; // Since referential is not the same in Virtools indices 1 & 2 are swapped int vx[3]={0,2,1}; // TODO : If the node does not have a material, export the wireframe color ? // DWORD c = node->GetWireColor(); ObjectState os = node->EvalWorldState(t); if (!os.obj || os.obj->SuperClassID()!=GEOMOBJECT_CLASS_ID) { return; // Safety net. This shouldn't happen. } #ifdef MAX51 // Roro : abandon d'essayer de recuperer les normals // depuis le modifier Edit Normals : // On peut récupérer le Modifier mais il faudrait // que l'objet soit selectionner et en mode d'edition // pour que les normals soient accessible sinon // le modifier renvoit NULL à tout (en tout cas en MaxScript c'est // possible mais uniquement si objet selection dans le panel Modify) Modifier* enmod = FindEditNormalModifier(node); IEditNormalsMod* editNormalMod = NULL; MNNormalSpec* userSpecifiedNormals = NULL; /* if (enmod) { ip->SetCommandPanelTaskMode(TASK_MODE_MODIFY); ip->SelectNode(node); editNormalMod = (IEditNormalsMod *)enmod->GetInterface(EDIT_NORMALS_MOD_INTERFACE); if (editNormalMod) { userSpecifiedNormals = editNormalMod->EnfnGetNormals(node);; int fCount = editNormalMod->EnfnGetNumFaces (node); fCount = editNormalMod->EnfnGetNumNormals (node); editNormalMod->EnfnResetNormals(); } } */ #endif BOOL needDel; TriObject* tri = GetTriObjectFromNode(node, t, needDel); if (!tri) { return; } Modifier *mod = FindSkinModifier(node); // Check if this mesh had already been exported (instances) // butif the node material is different we must create a new mesh :( CKMesh* VirtoolsMesh =IsMeshInstance(os.obj,nodeMtl); if (VirtoolsMesh && !mod) { ent->SetCurrentMesh(VirtoolsMesh); Report(REPORT_HLEVEL,"%sMesh Instance : %s.....",StrGroupIndent().Str(),node->GetName()); return ; } Mesh* mesh = &tri->GetMesh(); mesh->buildNormals(); int numVtx = mesh->getNumVerts(); // Vertex Count int numCVx= mesh->getNumMapVerts(0);//= mesh->numCVerts; // Vertex color Count int numTVx = mesh->getNumTVerts(); // Texture coords Count int numFaces = mesh->getNumFaces(); // Face Count //Direct Vertex color Count..using mapping channel 0 //no need for Collapsing Stack or using Edit Poly/Mesh Modifier BOOL UseVertexColor= mesh->mapSupport(0); //node->GetCVertMode() && numCVx ; if(UseVertexColor) if (!numVtx || !numFaces) return; //--- The vertex color data can came from external arrays (used when rendering for instance) VertColor *vertColData= mesh->mapVerts(0); //mesh->vertColArray? mesh->vertColArray : mesh->vertCol; //vertColData = mesh->curVCArray? mesh->curVCArray : vertColData; TVFace *vcFaceData= mesh->mapFaces(0); //mesh->vcFaceData? mesh->vcFaceData : mesh->vcFace; Report(REPORT_HLEVEL,"%sMesh : %s.....",StrGroupIndent().Str(),node->GetName()); Report(REPORT_MLEVEL,"\r\n%s%d Max Vertices, %d Faces",StrGroupIndent().Str(),numVtx,numFaces); VirtoolsTransitionMesh VirtoolsTempMesh(VirtoolsExporter,UseVertexColor); //-------------- Export the vertices -------------------------- // Max Vertices are given in the ObjectTM referential and we need them in NodeTM referential // So transform them using OffsetTM Matrix3 OffsetTM = GetNodeOffsetTM(node); // (transform them and invert Y & Z for (i=0; iverts[i]); VirtoolsTempMesh.AddPosition(VxVector(tmp.x,tmp.z,tmp.y)); } //----------- Export the faces ------------------------------- int PreviousMatId = -1; CKMaterial* VirtoolsMat = NULL; Mtl* FaceMtl = NULL; int UvIndex = 0; TextureUVGen uvgen; BOOL useUVGen = FALSE; VxUV uvs[3]; // Face texture coords VxColor clr; // Face texture coords BOOL exporting_shell_as_in_viewport = FALSE; Mtl* basematerial = NULL; BOOL stillNeedToAddChannelEvenIfexporting_shell_as_in_viewport = TRUE; for (i=0; ifaces[i].getMatID(); //--- Find corresponding Virtools material ///// No need to research the associated Virtools ///// material if material index is the same if (MatID != PreviousMatId) { void* mtlKey = NULL; FaceMtl = GetMaterialByIndex(nodeMtl,MatID,0, mtlKey); #ifdef MAX6 //--- Check if it is a Shell Material if (nodeMtl) if (nodeMtl->ClassID() == Class_ID(BAKE_SHELL_CLASS_ID, 0)) { exporting_shell_as_in_viewport = !bShellMaterialExportBoth; if (exporting_shell_as_in_viewport) { //--- The displayed material of the shell is set as base material in Virtools int VPShownMaterial = 1; VPShownMaterial = nodeMtl->VPDisplaySubMtl(); basematerial = nodeMtl->GetSubMtl(VPShownMaterial); stillNeedToAddChannelEvenIfexporting_shell_as_in_viewport = FALSE; if( basematerial->ClassID() == Class_ID(DMTL_CLASS_ID, 0) ){ StdMat* std = (StdMat*)basematerial; if( std->MapEnabled(ID_DI) && std->MapEnabled(ID_SI) ) { stillNeedToAddChannelEvenIfexporting_shell_as_in_viewport = TRUE; } } if( LM::ShaderEnabled(basematerial) ){ stillNeedToAddChannelEvenIfexporting_shell_as_in_viewport = TRUE; } } else { basematerial = nodeMtl->GetSubMtl(0); // Fix Thomas Bucher // we consider the mesh has a lightmap, so we set its flag VirtoolsTempMesh.m_HasLightMap = TRUE; } if (basematerial) { GetMaterialByIndex( basematerial, MatID, 0, mtlKey ); } } #endif VirtoolsMat = VirtoolsExporter->GetMaterialByKey( mtlKey ); useUVGen = GetTextureUvGen( FaceMtl, uvgen ); } PreviousMatId = MatID; //--- Add face with the corresponding Virtools material VirtoolsTempMesh.AddFace( mesh->faces[i].v[vx[0]], mesh->faces[i].v[vx[1]], mesh->faces[i].v[vx[2]], VirtoolsMat ); //------- Texture coordinates if (numTVx) { //--- If Exporting the Sub-Shell Material seen in 3dsMax viewport if( exporting_shell_as_in_viewport ){ //--- Let suppose the base material is a multi-sub object material Mtl* faceMaterial = basematerial->GetSubMtl( MatID ); //--- If base material of the shell material has no sub-materials, then ///// face material = base material of the shell if( !faceMaterial ){ faceMaterial = basematerial; } Texmap* pTexmap = faceMaterial->GetSubTexmap(ID_DI); //--- Get the Map channel used by the active sub-shell material if( pTexmap && pTexmap->GetUVWSource()==UVWSRC_EXPLICIT ){ int mp = pTexmap->GetMapChannel(); UVVert* tVerts = mesh->mapVerts( mp ); TVFace* tvFaces = mesh->mapFaces( mp ); CopyUV( &uvs[0], &tVerts[ tvFaces[i].t[vx[0]] ] ); CopyUV( &uvs[1], &tVerts[ tvFaces[i].t[vx[1]] ] ); CopyUV( &uvs[2], &tVerts[ tvFaces[i].t[vx[2]] ] ); } } //--- Otherwise else { //--- We consider by default the mesh'uv to be the correct face uvs ///// Note: which in max is not always true because depending on the material used by the face ///// UVs can be taken from another UV channel. That's why we wrote the following lines before ///// calling CopyUV() UVVert* tVerts = mesh->tVerts; TVFace* tvFaces = mesh->tvFace; //--- We'll try to retrieve the real face material ///// note: we don't use the GetMaterialByIndex fct for better efficiency... ///// anyway... start to be a very messy everywhere. Mtl* faceMaterial = NULL; //--- If material is a shell material (basematerial != NULL) then use the basematerial if( basematerial ){ //--- Let suppose the base material is a multi-sub object material faceMaterial = basematerial->GetSubMtl( MatID ); //--- If base material of the shell material has no sub-materials, then ///// face material = base material of the shell if( !faceMaterial ){ faceMaterial = basematerial; } //--- Otherwise it is not a shell material so just try to retrieve the sub material of the nodeMtl } else { faceMaterial = FaceMtl; } if( faceMaterial ){ Texmap* pTexmap = faceMaterial->GetSubTexmap( ID_DI ); if( pTexmap ){ int mp = pTexmap->GetMapChannel(); tVerts = mesh->mapVerts( mp ); tvFaces = mesh->mapFaces( mp ); } } //--- Copy UV from the face corresponding map channel if (tvFaces && tVerts) { CopyUV( &uvs[0], &tVerts[ tvFaces[i].t[vx[0]] ] ); CopyUV( &uvs[1], &tVerts[ tvFaces[i].t[vx[1]] ] ); CopyUV( &uvs[2], &tVerts[ tvFaces[i].t[vx[2]] ] ); } else { //Invalid UV, lets' put 0 as UV DWORD NULLUVs[2]; NULLUVs[0] = 0; NULLUVs[1] = 0; CopyUV( &uvs[0], &NULLUVs); CopyUV( &uvs[1], &NULLUVs); CopyUV( &uvs[2], &NULLUVs); } } } if (FaceMtl) { // Check if material use FaceMap ULONG matreq = FaceMtl->Requirements(-1); if (matreq & MTLREQ_FACEMAP) { make_face_uv(&mesh->faces[i], uvs); } } for (int j=0;j<3;++j) { ApplyUvGen(uvs[j],uvgen,useUVGen); VirtoolsTempMesh.AddUv(uvs[j]); } VirtoolsTempMesh.AddUVFace(UvIndex,UvIndex+1,UvIndex+2); } //------------ Export additional Texture coordinates ---------------------------- int VirtoolsChannelCount=0; UVVert* curV; int NumMaps = mesh->getNumMaps(); for (int mp = 2; mp < NumMaps; mp++) { if(VirtoolsChannelCountgetNumMapVerts(mp); if (numTVx) { // Get the material for this additional mapping channel void* mtlKey = NULL; const BOOL searchingForChannel = true; Mtl* FaceMtl = GetMaterialByIndex( nodeMtl, mesh->faces[0].getMatID(), mp, mtlKey, searchingForChannel ); //Test for the extended channels int ExportExtendedMethod=0; int ExportExtendedChannelType=0; int ExportExtendedComponents[4]={5,5,5,5}; TSTR ExportExtendedChannelName=""; // no material if(FaceMtl==NULL) ExportExtendedMethod=1; // keywords //The Channel Name is a UserDefined Property that is created by the //Channel Info Editor TSTR key; key.printf("MapChannel:%d",mp); ret= node->GetUserPropString( key,ExportExtendedChannelName); if(ret!=FALSE && ExportExtendedChannelName.Length() != 0) ExportExtendedMethod=2; BOOL ExportThisChannel=false; if(ExportExtendedMethod==0){ if( GetForceAllUVs() ) { //Force all coords ExportThisChannel=true; /* //Assign Default Channel Material since you cannot add channel with the same material in Virtools //Default material use mtlKeys: 1,2,3,4,5,6,7,8 if(FaceMtl==NULL){ mtlKey = (void*) (mp) ; UsedChannels[mp-1]=true; } */ } else { //--- If there's a material found for this channel and it is not part of a Shell material ? //ExportThisChannel = FaceMtl && !exporting_shell_as_in_viewport; ExportThisChannel = stillNeedToAddChannelEvenIfexporting_shell_as_in_viewport; } if (ExportThisChannel && mesh->mapSupport(mp) ) { VirtoolsChannelCount++; CKMaterial* VirtoolsMat = VirtoolsExporter->GetMaterialByKey(mtlKey); if(VirtoolsMat!= NULL){ VirtoolsTempMesh.AddChannelMaterial(VirtoolsMat); useUVGen = GetTextureUvGen(FaceMtl,uvgen); int UvIndex = 0; UVVert* tVerts = mesh->mapVerts( mp ); TVFace* tvFaces = mesh->mapFaces( mp ); for (i=0; imapVerts( mp ); TVFace* tvFaces = mesh->mapFaces( mp ); BOOL same[3]={true,true,true}; float last[3]; for (i=0; ix; last[1]=curV->y; last[2]=curV->z; }else{ if( last[0] != curV->x) same[0]=false; if( last[1] != curV->y) same[1]=false; if( last[2] != curV->z) same[2]=false; } } } //Get the count of Component ExportExtendedChannelType=3; if(same[0]) ExportExtendedChannelType--; if(same[1]) ExportExtendedChannelType--; if(same[2]) ExportExtendedChannelType--; if(ExportExtendedChannelType==0) this->Report(1,"\r\nInvalid Extended channel. All components were equals."); else this->Report(3,"\r\nNo Material Detected. Exporting a FLOAT%d extended channel.",ExportExtendedChannelType); // -- Fix Thomas Bucher -- // If ExportExtendedMethod == 1, we should export components U, V, W, 0 ExportExtendedComponents[0] = 0; ExportExtendedComponents[1] = 1; ExportExtendedComponents[2] = 2; ExportExtendedComponents[3] = 5; }//Method Keywords else if (ExportExtendedMethod ==2 ) { // all components set to 0 by default ExportExtendedComponents[0] = 5; ExportExtendedComponents[1] = 5; ExportExtendedComponents[2] = 5; ExportExtendedComponents[3] = 5; XString tmp= XString(ExportExtendedChannelName).ToLower(); int size=tmp.Length(); int count=0; int form=0; char input[5]; ret= tmp.Find("float",0); i=0; if(ret!=XString::NOTFOUND){ ExportExtendedChannelType=0; for(int j=ret+5; j< ret+10 && jReport(0, "Unknown character '%c' at for component %d. Please check the naming convention of the channel in Tools/ChannelInfo.\r\n",input[j],count); break; } ExportExtendedChannelType++; } if(ExportExtendedChannelType==0) this->Report(1,"\r\nInvalid Extended channel %d.", mp); else{ char car[7]={'R','G','B','A','M','0','1'}; this->Report(3,"\r\nExtended Channel%d Created. name= %s, type= float%d (%c,%c,%c,%c)",mp,ExportExtendedChannelName, ExportExtendedChannelType, car[ExportExtendedComponents[0]], car[ExportExtendedComponents[1]], car[ExportExtendedComponents[2]], car[ExportExtendedComponents[3]]); } } } VirtoolsChannelCount++; VirtoolsTempMesh.AddExtendedChannel( ExportExtendedChannelType ); int UvIndex = 0; UVVert* tVerts = mesh->mapVerts( mp ); TVFace* tvFaces = mesh->mapFaces( mp ); float vals[7]; for (i=0; ix; vals[1]=curV->y; vals[2]=curV->z; vals[3]=0.0f;//unused for alpha vals[4]=(curV->x + curV->y + curV->z) / 3.f; vals[5]=0.0f; vals[6]=1.0f; clr= VxColor( vals[ExportExtendedComponents[0]], 1.f - vals[ExportExtendedComponents[1]], // nicov : added to address mantis #0005059: Sev3 : Exported channels from max seem to have invesrsed mapping vals[ExportExtendedComponents[2]], vals[ExportExtendedComponents[3]]); VirtoolsTempMesh.AddChannelComponent(clr,VirtoolsChannelCount); } VirtoolsTempMesh.AddUVFace(UvIndex,UvIndex+1,UvIndex+2,VirtoolsChannelCount); } } } } } } //-------- Export color per vertex info ------------------- #ifdef MAX4 BOOL support = mesh->mapSupport(-VDATA_ALPHA); UVVert* alpha; if(support) alpha = mesh->mapVerts(-VDATA_ALPHA); #endif if (numCVx) { VirtoolsTempMesh.m_VertexColors.Resize(numCVx); for (i=0; iGetNumFaces() == numFaces); for (i=0; iGetNormal (i , vx[0]); vn = OffsetTM.VectorTransform(vn); VirtoolsTempMesh.m_Normals.PushBack(VxVector(vn.x,vn.z,vn.y)); vn = userSpecifiedNormals->GetNormal (i , vx[1]); vn = OffsetTM.VectorTransform(vn); VirtoolsTempMesh.m_Normals.PushBack(VxVector(vn.x,vn.z,vn.y)); vn = userSpecifiedNormals->GetNormal (i , vx[2]); vn = OffsetTM.VectorTransform(vn); VirtoolsTempMesh.m_Normals.PushBack(VxVector(vn.x,vn.z,vn.y)); VirtoolsTempMesh.AddNormalFace(Nindex,Nindex+1,Nindex+2); } } else #endif for (i=0; ifaces[i]; Point3 vn; vn = GetVertexNormal(mesh, i, mesh->getRVertPtr(f->getVert(vx[0]))); vn = OffsetTM.VectorTransform(vn); VirtoolsTempMesh.m_Normals.PushBack(VxVector(vn.x,vn.z,vn.y)); vn = GetVertexNormal(mesh, i, mesh->getRVertPtr(f->getVert(vx[1]))); vn = OffsetTM.VectorTransform(vn); VirtoolsTempMesh.m_Normals.PushBack(VxVector(vn.x,vn.z,vn.y)); vn = GetVertexNormal(mesh, i, mesh->getRVertPtr(f->getVert(vx[2]))); vn = OffsetTM.VectorTransform(vn); VirtoolsTempMesh.m_Normals.PushBack(VxVector(vn.x,vn.z,vn.y)); VirtoolsTempMesh.AddNormalFace(Nindex,Nindex+1,Nindex+2); } if (morphModifier && morphModEnabled) { if (needDel) { delete tri; } // morphModifier->EnableMod(); // reeval mesh tri = GetTriObjectFromNode(node, t, needDel); if (!tri) { return; } mesh = &tri->GetMesh(); mesh->buildNormals(); } if (bExportBlendShapes) { ExportBlendShapes(node, mesh, VirtoolsTempMesh); } //-------- And build Virtools Data VirtoolsTempMesh.GenerateVirtoolsData(); XString Name = node->GetName(); Name << "_Mesh"; VirtoolsMesh = VirtoolsExporter->AddMesh(&VirtoolsTempMesh,Name.CStr(),node); ent->SetCurrentMesh(VirtoolsMesh); // If there are blend shapes, set their weights now for (int i = 0; i < VirtoolsTempMesh.GetLogicalBlendShapeCount(); ++i) { ent->SetBlendShapeWeightByIndex(i, VirtoolsTempMesh.GetDefaultBlendShapeWeight(i)); } InsertNewMeshInstance(os.obj,nodeMtl,VirtoolsMesh); //--- Set CKMesh Settings not available in TransitionMesh //if( GetDeactivateMeshChannels() && GetForceAllUVs() ) // Araya: I don't see the point of testing the GetForceAllUVs()... if( GetDeactivateMeshChannels() ) { VirtoolsMesh->ActivateAllChannels(false); } Report(REPORT_LLEVEL,"\r\n%s=> %d Vertices for mesh %s",StrGroupIndent().Str(),VirtoolsMesh->GetVertexCount(), node->GetName()); //-------- Vertex Weights float* weightOrginal = mesh->getVertexWeights(); if( VirtoolsMesh && weightOrginal ){ const int vCount = VirtoolsMesh->GetVertexCount(); VirtoolsMesh->SetVertexWeightsCount( vCount ); for( int a=0 ; aSetVertexWeight( a, weight ); } } //-------- Delete Temporary Tri-Mesh if (needDel) { delete tri; } } /**************************************************************************** Patch Mesh output ****************************************************************************/ void Max2Nmo::ExportPatchMesh(INode* node, CK3dEntity* ent) { TimeValue t = GetStaticFrame(); Mtl* nodeMtl = node->GetMtl(); Matrix3 tm = node->GetNodeTM(t); int channel = 0; // TODO : If the node does not have a material, export the wireframe color ? // DWORD c = node->GetWireColor(); ObjectState os = node->EvalWorldState(t); if (!os.obj || os.obj->SuperClassID()!=GEOMOBJECT_CLASS_ID) { return; // Safety net. This shouldn't happen. } BOOL needDel; PatchObject* patch = GetPatchObjectFromNode(node, t, needDel); if (!patch) return; // Check if this mesh had already been exported (instances) CKPatchMesh* VirtoolsPMesh = (CKPatchMesh*)IsMeshInstance(os.obj,nodeMtl); if (VirtoolsPMesh) { ent->SetCurrentMesh(VirtoolsPMesh); Report(REPORT_HLEVEL,"%sPatch Mesh Instance: %s.....",StrGroupIndent().Str(),node->GetName()); return ; } Report(REPORT_HLEVEL,"%sPatch Mesh : %s.....",StrGroupIndent().Str(),node->GetName()); XString Name = node->GetName(); Name << "_Mesh"; PatchMesh* pmesh = &patch->patch; if (!pmesh->numVerts || !pmesh->numVecs || !pmesh->numPatches) return; int StepCount = pmesh->cacheSteps; VirtoolsPMesh = VirtoolsExporter->AddPatchMesh(Name.CStr(),node); Report(REPORT_LLEVEL,"%sPatch Mesh: %d Control Points, %d Patches, %d Vectors",StrGroupIndent().Str(),pmesh->numVerts,pmesh->numVecs,pmesh->numPatches); VirtoolsPMesh->SetVertVecCount(pmesh->numVerts,pmesh->numVecs); VirtoolsPMesh->SetPatchCount(pmesh->numPatches); VirtoolsPMesh->SetEdgeCount(pmesh->numEdges); VirtoolsPMesh->SetIterationCount(StepCount); //-------------- Export the patch vertices and tangent vectors -------------------------- // Max Vertices are given in the ObjectTM referential and we need them in NodeTM referential // So transform them using OffsetTM Matrix3 OffsetTM = GetNodeOffsetTM(node); // (transform them and invert Y & Z for (int i=0; inumVerts; i++) { Point3 tmp = OffsetTM.PointTransform(pmesh->verts[i].p); VirtoolsPMesh->SetVert(i,&VxVector(tmp.x,tmp.z,tmp.y)); } for (int i=0; inumVecs; i++) { Point3 tmp = OffsetTM.PointTransform(pmesh->vecs[i].p); VirtoolsPMesh->SetVec(i,&VxVector(tmp.x,tmp.z,tmp.y)); } //--------- Export edges for (int i=0;inumEdges;++i) { PatchEdge* e = &pmesh->edges[i]; CKPatchEdge pe; #if (MAX_RELEASE>=4000) if (e->patches.Count()) pe.patch1 = e->patches[0]; else pe.patch1 = -1; if (e->patches.Count()>1) pe.patch2 = e->patches[1]; else pe.patch2 = -1; #else pe.patch1 = e->patch1; pe.patch2 = e->patch2; #endif pe.v1 = e->v1; pe.v2 = e->v2; pe.vec12 = e->vec12; pe.vec21 = e->vec21; VirtoolsPMesh->SetEdge(i,&pe); } //----------- Export Patches data int PreviousMatId = -1; CKMaterial* VirtoolsMat = NULL; Mtl* PatchMtl= NULL; TextureUVGen uvgen; BOOL useUVGen = FALSE; for (int i=0;inumPatches;++i) { Patch* p = &pmesh->patches[i]; int MatID = (p->flags >> PATCH_MATID_SHIFT) & PATCH_MATID_MASK; // No need to research the associated Virtools material if material index is the same if (MatID != PreviousMatId) { void* mtlKey = NULL; PatchMtl = GetMaterialByIndex(nodeMtl,MatID,0,mtlKey); VirtoolsMat = VirtoolsExporter->GetMaterialByKey(mtlKey); useUVGen = GetTextureUvGen(PatchMtl,uvgen); } PreviousMatId = MatID; CKPatch VirtoolsPatch; VirtoolsPatch.v[0]=p->v[0]; VirtoolsPatch.v[1]=p->v[1]; VirtoolsPatch.v[2]=p->v[2]; VirtoolsPatch.v[3]=p->v[3]; VirtoolsPatch.vec[0]=p->vec[0]; VirtoolsPatch.vec[1]=p->vec[1]; VirtoolsPatch.vec[2]=p->vec[2]; VirtoolsPatch.vec[3]=p->vec[3]; VirtoolsPatch.vec[4]=p->vec[4]; VirtoolsPatch.vec[5]=p->vec[5]; VirtoolsPatch.vec[6]=p->vec[6]; VirtoolsPatch.vec[7]=p->vec[7]; VirtoolsPatch.interior[0]=p->interior[0]; VirtoolsPatch.interior[1]=p->interior[1]; VirtoolsPatch.interior[2]=p->interior[2]; VirtoolsPatch.interior[3]=p->interior[3]; VirtoolsPatch.edge[0]=p->edge[0]; VirtoolsPatch.edge[1]=p->edge[1]; VirtoolsPatch.edge[2]=p->edge[2]; VirtoolsPatch.edge[3]=p->edge[3]; VirtoolsPatch.auxs=NULL; VirtoolsPatch.type = (p->type == PATCH_TRI) ? CK_PATCH_TRI : CK_PATCH_QUAD; VirtoolsPatch.Material = CKOBJID(VirtoolsMat); VirtoolsPatch.SmoothingGroup = p->smGroup; VirtoolsPMesh->SetPatch(i,&VirtoolsPatch); } //------- Texture coordinates int PatchChannelCount=0; for (int mp=1;mpNumMapChannels();mp++) { if (pmesh->getNumMapVerts(mp)>0) { int MatID = (pmesh->patches[0].flags >> PATCH_MATID_SHIFT) & PATCH_MATID_MASK; void* mtlKey = NULL; PatchMtl = GetMaterialByIndex(nodeMtl,MatID,mp,mtlKey); useUVGen = GetTextureUvGen(PatchMtl,uvgen); VirtoolsMat = VirtoolsExporter->GetMaterialByKey(mtlKey); if (PatchChannelCount>0) { VirtoolsPMesh->AddChannel(VirtoolsMat); } VirtoolsPMesh->SetTVPatchCount(pmesh->numPatches,PatchChannelCount-1); VirtoolsPMesh->SetTVCount(pmesh->getNumMapVerts(mp),PatchChannelCount-1); for (int i=0;inumPatches;++i) { CKTVPatch Virtoolstvp; TVPatch tvp = pmesh->getMapPatch(mp,i); int MatID = (pmesh->patches[i].flags >> PATCH_MATID_SHIFT) & PATCH_MATID_MASK; // No need to research the associated Virtools material if material index is the same if (MatID != PreviousMatId) { PatchMtl = GetMaterialByIndex(nodeMtl,MatID,mp,mtlKey); VirtoolsMat = VirtoolsExporter->GetMaterialByKey(mtlKey); useUVGen = GetTextureUvGen(PatchMtl,uvgen); } PreviousMatId = MatID; if (tvp.tv) { int nb = (pmesh->patches[i].type == PATCH_TRI) ? 3 : 4; for (int j=0;jgetMapVert(mp,tvp.tv[j])); ApplyUvGen(uv,uvgen,useUVGen); Virtoolstvp.tv[j] = tvp.tv[j]; VirtoolsPMesh->SetTV(tvp.tv[j],uv.u,uv.v,PatchChannelCount-1); } VirtoolsPMesh->SetTVPatch(i,&Virtoolstvp,PatchChannelCount-1); } } PatchChannelCount++; } } ent->SetCurrentMesh(VirtoolsPMesh); InsertNewMeshInstance(os.obj,nodeMtl,VirtoolsPMesh); if (needDel) { delete patch; } } Point3 Max2Nmo::GetVertexNormal(Mesh* mesh, int faceNo, RVertex* rv) { Face* f = &mesh->faces[faceNo]; DWORD smGroup = f->smGroup; int numNormals; Point3 vertexNormal; // Is normal specified // SPCIFIED is not currently used, but may be used in future versions. if (rv->rFlags & SPECIFIED_NORMAL) { vertexNormal = rv->rn.getNormal(); } // If normal is not specified it's only available if the face belongs // to a smoothing group else if ((numNormals = rv->rFlags & NORCT_MASK) && smGroup) { // If there is only one vertex is found in the rn member. if (numNormals == 1) { vertexNormal = rv->rn.getNormal(); } else { // If two or more vertices are there you need to step through them // and find the vertex with the same smoothing group as the current face. // You will find multiple normals in the ern member. for (int i = 0; i < numNormals; i++) { if (rv->ern[i].getSmGroup() & smGroup) { vertexNormal = rv->ern[i].getNormal(); } } } } else { // Get the normal from the Face if no smoothing groups are there vertexNormal = mesh->getFaceNormal(faceNo); } return vertexNormal; } /**************************************************************************** Level Settings with Ambient, Background and Fog ****************************************************************************/ void Max2Nmo::ExportLevelSettings() { //From 3DSMax7 SDK\samples\howto\misc\fog.cpp #define PB_COLOR 0 #define PB_USEMAP 1 #define PB_USEOPAC 2 #define PB_FOGBG 3 #define PB_TYPE 4 #define PB_NEAR 5 #define PB_FAR 6 #define PB_TOP 7 #define PB_BOTTOM 8 #define PB_DENSITY 9 #define PB_FALLOFF 10 #define PB_HNOISE 11 #define PB_SCALE 12 #define PB_ANGLE 13 #define PB_PHASE 14 #define PB_EXP 15// <- This one is not exposed VirtoolsLevel* VLvl=this->VirtoolsExporter->GetVirtoolsLevel(); TimeValue t=GetStaticFrame(); Interval valid = FOREVER; Point3 clr; VLvl->m_FogMode= VXFOG_NONE; //Get the colors clr= ip->GetAmbient(t,valid); VLvl->m_ClrAmbient = VxColor(clr[0], clr[1], clr[2] ); clr= ip->GetBackGround(t,valid); VLvl->m_ClrBackGround = VxColor(clr[0], clr[1], clr[2] ); //The Global Light Tint controls the color of all lights by modulating them clr= ip->GetLightTint(t,valid); VLvl->m_GlobalTint = VxColor(clr[0], clr[1], clr[2] ); VLvl->m_TintLevel= ip->GetLightLevel(t,valid); //Get The Background Texture if( ip->GetUseEnvironmentMap() ){ Texmap* txt=ip->GetEnvironmentMap(); } //Starting Camera //Later in ExportStartingCamera //--Atmosphere Effects int i,cnt; cnt=ip->NumAtmospheric(); for(i=0; i< cnt; i++){ Atmospheric* atm=ip->GetAtmospheric(i); if(atm==NULL)continue; //FOG Linear and Exponential StdFog* Fog= (StdFog*) atm; if(Fog){ if( Fog->GetType()==1)//Layered Fog not Supported continue; //Apply Fog Color Color fogClr= Fog->GetColor(t); VLvl->m_ClrFog= VxColor( fogClr[0], fogClr[1], fogClr[2] ); if( Fog->GetUseMap()) Texmap* clrMap= Fog->GetColorMap(); if(Fog->GetUseOpac()) Texmap* opaMap= Fog->GetOpacMap(); //Replace the background with the fog one if( Fog->GetFogBackground() ) VLvl->m_ClrBackGround = VLvl->m_ClrFog; //Fog Settings VLvl->m_FogStart= Fog->GetNear(t); //The near and far still need to be multiply by the current camera effects planes. VLvl->m_FogEnd= Fog->GetFar(t); VLvl->m_FogDensity= 0;// This is not the right density //Fog Type BOOL exponential; //Not exposed IParamBlock* block= (IParamBlock*) Fog->GetReference(0); int numpara= block->NumParams(); exponential= block->GetInt(PB_EXP,t)!=0; if (exponential) { VLvl->m_FogMode= VXFOG_EXP; } else VLvl->m_FogMode= VXFOG_LINEAR; } //Tip: You can add support for all the plugins effects your want here. /* MyAtm* myAtm= (MyAtm*) atm; if(myAtm){ }*/ } } BOOL Max2Nmo::ExportStartingCamera( CKCamera* startCam, float fogNear, float fogFar ) { //We suppose the values in start and end of VirtoolsLevel are already intitialized with the Fog Near% and End% VirtoolsLevel* vl= this->VirtoolsExporter->GetVirtoolsLevel(); const int cNEAR=0; const int cFAR=1; const int c000=2; const int c100=3; const int c050=4; float z[5]; float P[5]; float a,b; //Set the starting Camera vl->m_StartCamera = startCam; if(vl->m_FogMode == VXFOG_NONE) return false; //P(z) is Fog Color Percentage in function of the Z Depth //In Max, we specified the Percentage using the near and far camera effect plane //so at z= CameraNearPlane, P is FogNear% z[cNEAR]= fogNear; P[cNEAR]= vl->m_FogStart; //and at z= CameraFarPlane, P is FogFar% z[cFAR]= fogFar; P[cFAR]= vl->m_FogEnd; //Now compute P(0%) and P(100%) since it is what we use in Virtools for linear fog //P(z) = a*z + b a= (P[cFAR] - P[cNEAR]) / (z[cFAR] - z[cNEAR]); b= P[cNEAR] - a*z[cNEAR]; z[c000]= -b/a; z[c100]= (1-b)/a; vl->m_FogStart= z[c000]; vl->m_FogEnd= z[c100]; if(vl->m_FogMode == VXFOG_EXP){ //We cannot get the value directly... we need to do a trick //Virtools calculates the fog using: P(z) = exp(-a*z) where a= fogdensity //so we need to find 'a' //The tweak value is a percentage from 0=NearZ 1=FarZ Where we'll try to fit the curve float tweak=.183939721f;//Using test scenes this value was the best... but I cannot be sure b= (z[c000]+z[c100])*tweak; a= log(1.f/tweak)/b; //We have an approximation of it vl->m_FogDensity=a; } return true; } /**************************************************************************** Material and Texture Export ****************************************************************************/ void Max2Nmo::ExportMaterialList() { //--- Initialize the lightmap base materials hash table m_lightmapBaseMaterials.Clear(); //--- Create and Add Default Channels when using Force All UVs if( this->GetForceAllUVs() ){ char defaultname[20]; VirtoolsMaterial VMat; for(int i=0; i< MAX_UVCHANNEL_COUNT; i++){ UsedChannels[i]= false; sprintf(defaultname, "Default Channel %d", i+1); this->VirtoolsExporter->AddMaterial(&VMat, defaultname, (void*) (i+1)); } } //--- Parse all 3dsmax materials int numMtls = mtlList.Count(); for (int i=0; iGetName()); if (!mtl) return NULL; CKMaterial* mat=VirtoolsExporter->GetMaterialByKey(mtl); if(mat!=NULL) return mat; VirtoolsMaterial NMat; VirtoolsCompositeMaterial tmpCMat; VirtoolsCompositeMaterial* cmat=NULL; BOOL OpacityValid=false; // We know the Standard material, so we can get some extra info if (mtl->ClassID() == Class_ID(DMTL_CLASS_ID, 0)) DumpMaterialStandard( mtl, NMat, OpacityValid ); if( mtl->ClassID() == Class_ID(BAKE_SHELL_CLASS_ID, 0)) DumpMaterialBakedShell( mtl, NMat,OpacityValid ); BOOL Active=true; BOOL Lit=true; // Dump Sub Materials if (mtl->NumSubMtls() > 0) { for (int i=0; iNumSubMtls(); i++) { Mtl* subMtl = mtl->GetSubMtl(i); if (subMtl) { CKMaterial* subM= DumpMaterial(subMtl); if(subM!=NULL){ if(i==0) tmpCMat.m_Material= subM; else{ //Get the mapping channel int mappingChannel=-1; Texmap* pTexmap=subMtl->GetSubTexmap(ID_DI); if (pTexmap && pTexmap->GetUVWSource()==UVWSRC_EXPLICIT){ mappingChannel= pTexmap->GetMapChannel(); } //Add the sub material and set its channel int res=tmpCMat.AddSubMaterial( subM, Active, Lit ); tmpCMat.m_SubMaterials[res].SetChannel(mappingChannel); } } } } } //Container Materials... produce no CKMaterial //Is is a multisub? if( mtl->ClassID() == Class_ID(MULTI_CLASS_ID, 0)){ return NULL; } //Is is a composite material? //Group mat and submaterials to create a composite if( mtl->ClassID() == Class_ID(0x61dc0cd7, 0x13640af6)){ cmat =VirtoolsExporter->AddCompositeMaterial( tmpCMat ); return NULL; } //Generate the Material mat= VirtoolsExporter->AddMaterial(&NMat,mtl->GetName(),mtl); //--- Enable mipmapping by default mat->SetTextureMinMode( VXTEXTUREFILTER_LINEARMIPLINEAR ); if( NMat.m_Texture ) NMat.m_Texture->UseMipmap(TRUE); TextureUVGen uvgen; BOOL useUVGen = GetTextureUvGen(mtl,uvgen)==1; if( useUVGen ){ if (uvgen.MirrorU && uvgen.MirrorV) mat->SetTextureAddressMode(VXTEXTURE_ADDRESSMIRROR); // Will this be supported by video card in Virtools ???? if (uvgen.UOffset!=0 || uvgen.UScale!=1.0f || uvgen.VOffset!=0 || uvgen.VScale!=1.0f) { if (uvgen.Cropping && (uvgen.UOffset!=0 || uvgen.UScale!=1.0f || uvgen.VOffset!=0 || uvgen.VScale!=1.0)) { // Cropping and (tiling/mirroring) are used together warn the // user that the conversion may not work Report(REPORT_HLEVEL,"\r\nCropping and (tiling or mirroring) are used on this texture\r\n and may generate incorrect results in Virtools\r\n"); } } } // Opacity map is valid , we enable Alpha testing // Enabling alapoha testing by default is a bit violent. // but we should encourage user to use alpah test // This code is commented for future use (creation of an option) if (OpacityValid) { if (GetAlphaRef()) { mat->EnableAlphaTest(TRUE); mat->SetAlphaRef(GetAlphaRefValue()); mat->SetAlphaFunc(VXCMP_GREATEREQUAL); //mat->EnableZWrite(true); // At least 1 bit of alpha should be here for // the display to be correct //NMat.m_Texture->SetDesiredVideoFormat(_16_ARGB1555); } } Report(REPORT_HLEVEL,"Done\r\n"); return mat; } /**************************************************************************** DumpMaterialStandard (Get the material properties) ****************************************************************************/ void Max2Nmo::DumpMaterialStandard(Mtl* mtl, VirtoolsMaterial &NMat, BOOL &OpacityValid) { TimeValue t = GetStaticFrame(); StdMat* stdM = (StdMat*)mtl; //--Get STD Material Properties Color Ambient = stdM->GetAmbient(t); Color Diffuse = stdM->GetDiffuse(t); Color Specular = stdM->GetSpecular(t); float Shininess = stdM->GetShininess(t); float ShinStr = stdM->GetShinStr(t); float Transparency = stdM->GetXParency(t); float selfIllum = stdM->GetSelfIllum(0); BOOL selfIllumColorOn=FALSE; Color Emissive; //--- Get self Illumn color information StdMat2* stdmat2=(StdMat2*)stdM; if (stdmat2->SupportsShaders()) { Shader* shader=stdmat2->GetShader(); if (shader) { selfIllumColorOn=shader->IsSelfIllumClrOn(); Emissive=shader->GetSelfIllumClr(0); } } //--Set STD Material Properties NMat.m_Diffuse = VxColor(Diffuse.r,Diffuse.g,Diffuse.b,1.0f-Transparency); NMat.m_Ambient = VxColor(Ambient.r,Ambient.g,Ambient.b); NMat.m_Specular = VxColor(Specular.r*ShinStr,Specular.g*ShinStr,Specular.b*ShinStr); NMat.m_SpecularPower= Shininess*100; NMat.m_Emissive = selfIllum*NMat.m_Diffuse; NMat.m_TwoSided = stdM->GetTwoSided(); NMat.m_FillMode = stdM->GetWire() ? VXFILL_WIREFRAME : VXFILL_SOLID; NMat.m_ShadeMode = stdM->GetShading() == SHADE_CONST ? VXSHADE_FLAT : VXSHADE_GOURAUD; if (selfIllumColorOn) NMat.m_Emissive = VxColor(Emissive.r,Emissive.g,Emissive.b); //---Transparency if (Transparency) { NMat.m_AlphaBlending = TRUE; NMat.m_SrcBlendFactor = VXBLEND_SRCALPHA; NMat.m_DstBlendFactor = VXBLEND_INVSRCALPHA; NMat.m_ZWriteEnable = FALSE; } switch (stdM->GetTransparencyType()) { case TRANSP_FILTER: break; case TRANSP_SUBTRACTIVE: break; case TRANSP_ADDITIVE: break; } #ifdef MAX51 //--- Araya: If the material has a lightmap shader in the viewport ///// then use the lightmap base texture instead of the diffusemap texture ///// and add a new material/texture, the lightmap. if( LM::ShaderEnabled(mtl) ) DumpMaterialLM(mtl, NMat); //--- Material is standard (without any shader effect) else #endif DumpMaterialTextures(stdM, NMat, OpacityValid); } /**************************************************************************** DumpMaterialTextures(this is where we take the texture maps of an StdMaterial) ID_AM 0 // ambient ID_DI 1 // diffuse ID_SP 2 // specular ID_SH 3 // shininesNs ID_SS 4 // shininess strength ID_SI 5 // self-illumination ID_OP 6 // opacity ID_FI 7 // filter color ID_BU 8 // bump ID_RL 9 // reflection ID_RR 10 // refraction ID_DP 11 // displacement ****************************************************************************/ void Max2Nmo::DumpMaterialTextures(StdMat* stdM, VirtoolsMaterial &NMat, BOOL &OpacityValid) { float MixAmount; int MapToRetrieve; void* oKey=NULL; BOOL diffuseAndSelfIllumMapEnabled = stdM->MapEnabled(ID_DI) && stdM->MapEnabled(ID_SI); if( diffuseAndSelfIllumMapEnabled ){ //--- Dump Self-Illum material Texmap* pSelfIllumTexmap = stdM->GetSubTexmap( ID_SI ); if( pSelfIllumTexmap ){ if( pSelfIllumTexmap->ClassID()==Class_ID(BMTEX_CLASS_ID,0)) { BitmapTex* pBmpSelfIllumTex = (BitmapTex*)pSelfIllumTexmap; Bitmap* bm = pBmpSelfIllumTex->GetBitmap(0); XString selfIllumFileName = pBmpSelfIllumTex->GetMapName(); CKPathSplitter psplitter( selfIllumFileName.Str() ); NMat.m_Texture = VirtoolsExporter->AddTexture( selfIllumFileName.CStr(), (const char *)psplitter.GetName(), pBmpSelfIllumTex ); if( NMat.m_Texture ) NMat.m_Texture->UseMipmap( TRUE ); CKMaterial* selfIllumMat = VirtoolsExporter->AddMaterial( &NMat, ((Mtl*)stdM)->GetName()+"_Lightmap", (void*)~(DWORD)stdM ); selfIllumMat->SetTextureMinMode( VXTEXTUREFILTER_LINEARMIPLINEAR ); } } } //Diffuse ou Selfillumination if(stdM->MapEnabled(ID_DI) || stdM->MapEnabled(ID_SI)) { // If Amount is 100 % the diffuse color is not taken into account // (equivalent to DECAL_MODE) in Virtools // 14.11.200 Removed since people are most of the time // waiting for a modulate mode // if (MixAmount<1) NMat.m_TextureBlendMode = VXTEXTUREBLEND_MODULATEALPHA; // else NMat.m_TextureBlendMode = VXTEXTUREBLEND_DECAL; MapToRetrieve= stdM->MapEnabled(ID_DI) ? ID_DI : ID_SI; NMat.m_TextureBlendMode = VXTEXTUREBLEND_MODULATEALPHA; NMat.m_Texture = ConvertTextureMap( stdM, MapToRetrieve, MixAmount,oKey); } //Opacity Map if( this->GetExportAlphaMap() ) if(NMat.m_Texture && stdM->MapEnabled(ID_OP)){ /*OLD LAF CODE disabled since 3 textures were exported : diff alpha and diff+alpha CKTexture* opacityMap=ConvertTextureMap( stdM, ID_OP, MixAmount,oKey); NMat.m_Texture = VirtoolsExporter->AddTextureAlphaMap( NMat.m_Texture, opacityMap, MixAmount); NMat.m_AlphaBlending = TRUE; NMat.m_SrcBlendFactor = VXBLEND_SRCALPHA; NMat.m_DstBlendFactor = VXBLEND_INVSRCALPHA; NMat.m_Diffuse.a = 1.0f; // Opacity value is not taken when OpacityValid=true; //Destroy the opacityMap... we'll recreate it if we need //VirtoolsExporter->DestroyObject(opacityMap,oKey); */ // Fix Thomas Bucher // Formerly, when a different materials had the same diffuse map, but // different opacity maps, only one CKTexture was created (with the last // opacity map processed) because CKTexture are registered with the // file name of the diffuse map. // We need to compare the alpha channels to know if supplementary // CKTexture should be created for a different opacity map. // 1. A duplicate the current texture // 2. I store the alpha channel in this new texture // 3. I compare the two alpha channels to know if the textures are identical // - if yes, a delete the duplicata and return the former texture // - if no, I return the duplicata CKTexture* textureDuplicata = NULL; Texmap* opacitymap = NULL; Texmap* diffusemap = NULL; int diffTexWidth = 0; int diffTexHeight = 0; opacitymap=stdM->GetSubTexmap(ID_OP); if(opacitymap && opacitymap->ClassID()==Class_ID(BMTEX_CLASS_ID,0)) { BitmapTex* pBmpTex = (BitmapTex*)opacitymap; Bitmap* bm = NULL; float OneMinusMixAmount = 0.f; OneMinusMixAmount=1.0f-stdM->GetTexmapAmt(ID_OP,0); bm = pBmpTex->GetBitmap(0); //Lets get the diffuse bitmap size int diffusemaptoget = -1; BOOL gotdiffusemap = FALSE; if (stdM->MapEnabled(ID_DI) || stdM->MapEnabled(ID_SI)) { diffusemaptoget = stdM->MapEnabled(ID_DI) ? ID_DI : ID_SI; diffusemap = stdM->GetSubTexmap(diffusemaptoget); if (diffusemap && diffusemap->ClassID()==Class_ID(BMTEX_CLASS_ID,0)) { BitmapTex* pdiffBmpTex = (BitmapTex*)diffusemap; Bitmap* diffbm = pdiffBmpTex->GetBitmap(0); if (diffbm) { diffTexHeight = diffbm->Height(); diffTexWidth = diffbm->Width(); gotdiffusemap = TRUE; } } } if (bm && gotdiffusemap) { // Fix Thomas Bucher // If the texture is already used by a material, we suppose // that its alpha channel should not be changed, so a duplicataTexture // is created BOOL isUsed = FALSE; XObjectPointerArray materials = VirtoolsContext->GetObjectListByType(CKCID_MATERIAL, TRUE); XObjectPointerArray::Iterator matIt = materials.Begin(); XObjectPointerArray::Iterator matEnd = materials.End(); while(matIt != matEnd && !isUsed) { CKMaterial* mat = (CKMaterial*) *matIt; if(mat->GetTexture(0) == NMat.m_Texture) isUsed = TRUE; matIt++; } if(isUsed) textureDuplicata = (CKTexture*) VirtoolsContext->CopyObject(NMat.m_Texture, NULL, NULL); Report(REPORT_LLEVEL,"\r\nGenerating alpha from opacity map : %s\r\n",pBmpTex->GetMapName()); int BmpWidth = bm->Width(); int BmpHeight = bm->Height(); BOOL createResizedBM = (BmpWidth != diffTexWidth) || (BmpHeight != diffTexHeight); int canCopyAlpha = 0; BitmapInfo resizedBMInfo; if( createResizedBM ){ resizedBMInfo.SetType( BMM_GRAY_16 ); resizedBMInfo.SetWidth( diffTexWidth ); resizedBMInfo.SetHeight( diffTexHeight ); resizedBMInfo.SetFlags( MAP_NOFLAGS ); resizedBMInfo.SetCustomFlag( 0 ); Bitmap* resizedBM = TheManager->Create( &resizedBMInfo ); #ifdef MAX9 BMM_Color_64 black( 0, 0, 0, 0 ); #else BMM_Color_64 black = { 0, 0, 0, 0 }; #endif canCopyAlpha = resizedBM->CopyImage( bm, COPY_IMAGE_RESIZE_HI_QUALITY, black );//, BitmapInfo *bi = NULL) if (canCopyAlpha) bm = resizedBM; else bm = NULL; } if( bm ){ int Stride = diffTexWidth*sizeof(DWORD); BYTE* ptr = textureDuplicata ? textureDuplicata->LockSurfacePtr() : NMat.m_Texture->LockSurfacePtr(); if (ptr) { // Go to alpha value ptr+=3; XArray Pixels; Pixels.Resize(diffTexWidth); for (int i=0; i< diffTexHeight; ++i,ptr+=Stride) { int res = bm->Get16Gray(0,i,diffTexWidth,Pixels.Begin()); BYTE* pix = (BYTE *)Pixels.Begin(); ++pix; BYTE* tptr = ptr; for (int j=0;j 255) an_int = 255; if (an_int < 0) an_int = 0; *tptr=an_int; } } } OpacityValid = TRUE; NMat.m_AlphaBlending = TRUE; NMat.m_SrcBlendFactor = VXBLEND_SRCALPHA; NMat.m_DstBlendFactor = VXBLEND_INVSRCALPHA; NMat.m_Diffuse.a = 1.0f; // Opacity value is not taken when // giving a opacity map } if( createResizedBM ){ TheManager->DelBitmap( bm ); } } else { Report(REPORT_HLEVEL,"\r\nOpacity map : %s cannot resize alpha map in order to put it in the alpha component of texture...\r\n",pBmpTex->GetMapName()); } // check if former opacity map is different from current if(textureDuplicata) { int h, w, height, width; BOOL equal = TRUE; height = NMat.m_Texture->GetHeight(); width = NMat.m_Texture->GetWidth(); for(h=0; hGetPixel(h, w); DWORD currentP = textureDuplicata->GetPixel(h, w); if(ColorGetAlpha(formerP) != ColorGetAlpha(currentP)) equal = FALSE; } } // if opacity map differs, keep it as a new texture, else destroy it if(!equal) NMat.m_Texture = textureDuplicata; else { VirtoolsContext->DestroyObject(textureDuplicata); textureDuplicata = NULL; } } } else { Report(REPORT_HLEVEL,"\r\nOpacity map : %s not found...\r\n",pBmpTex->GetMapName()); } } NMat.m_AlphaBlending = TRUE; NMat.m_SrcBlendFactor = VXBLEND_SRCALPHA; NMat.m_DstBlendFactor = VXBLEND_INVSRCALPHA; NMat.m_Diffuse.a = 1.0f; // Opacity value is not taken when OpacityValid=true; } //MaterialEffect BumpEnv //Bump if(stdM->MapEnabled(ID_BU)) { CKTexture* BumpMap= ConvertTextureMap( stdM, ID_BU, MixAmount,oKey); //Attach Bump NMat.m_Effect.m_EffectName="Environment Bump Map"; NMat.AddParameter("Bump Texture", VirtoolsExporter->GetObjParameter(BumpMap)); NMat.AddParameter("Env. Texture", VirtoolsExporter->GetObjParameter(NULL)); NMat.AddParameter("Amplitude", VirtoolsExporter->GetFloatParameter(MixAmount)); NMat.AddParameter("EnvMap Combine", VirtoolsExporter->GetStringParameter("Add")); NMat.AddParameter("EnvMap TexGen", VirtoolsExporter->GetStringParameter("Chrome")); NMat.AddParameter("EnvMap Referential", VirtoolsExporter->GetObjParameter(NULL, CKCID_3DENTITY)); } //Env if(stdM->MapEnabled(ID_RL)) { CKTexture* EnvMap= ConvertTextureMap( stdM, ID_RL, MixAmount,oKey); //Attach Env NMat.m_Effect.m_EffectName="Combine 2 Textures"; NMat.AddParameter("Texture 1", VirtoolsExporter->GetObjParameter(EnvMap)); NMat.AddParameter("Combine", VirtoolsExporter->GetStringParameter("Add")); NMat.AddParameter("TexGen", VirtoolsExporter->GetStringParameter("Chrome")); NMat.AddParameter("Referential", VirtoolsExporter->GetObjParameter(NULL, CKCID_3DENTITY)); } } /**************************************************************************** ConvertTextureMap (Create a CKTexture with a MapID of a StdMat. Also retrieve the Map MixAmnt) ****************************************************************************/ CKTexture* Max2Nmo::ConvertTextureMap(StdMat* stdM, int MapToRetrieve, float &MixAmount, void* &oKey) { BOOL TexValid =false; oKey=NULL; Texmap* pTexmap; BitmapTex* pBmpTex; int TexWidth = 0; int TexHeight = 0; CKTexture* txt=NULL; MixAmount= stdM->GetTexmapAmt(MapToRetrieve,0); if(pTexmap = stdM->GetSubTexmap(MapToRetrieve)) if(pTexmap->ClassID()==Class_ID(BMTEX_CLASS_ID,0)) { pBmpTex = (BitmapTex*)pTexmap; Bitmap* bm=pBmpTex->GetBitmap(0); // Araya: resolve texture path filename BitmapInfo infoBI( pBmpTex->GetMapName() ); BMMGetFullFilename( &infoBI ); CKPathSplitter psplitter( (char*)infoBI.Name() ); txt = VirtoolsExporter->AddTexture( infoBI.Name(), (const char *)psplitter.GetName(), pBmpTex ); // Check if texture is valid for Virtools int flg=0; if(bm==NULL) { Report(REPORT_HLEVEL,"\r\n%s : file not found...\r\n",pBmpTex->GetMapName()); } else { // Test power of 2 TexValid = TRUE; TexWidth = bm->Width(); TexHeight = bm->Height(); DWORD w=(DWORD)bm->Width(); DWORD h=(DWORD)bm->Height(); DWORD mask; mask=0xFFFFFFFF; mask>>=(32-GetMSB(w)); mask=w&mask; if(mask) flg=1; mask=0xFFFFFFFF; mask>>=(32-GetMSB(h)); mask=h&mask; if(mask) flg=1; //Texture is not Power of 2 if(flg) { Report(REPORT_HLEVEL,"\r\nWarning %s :w:%d - h:%d\r\nThe width and height of the bitmap must be a power of 2 (2,4,8 ... 256,512)\r\n",pBmpTex->GetMapName(),w,h); } } } oKey=(void*)pBmpTex; return txt; } void Max2Nmo::DumpMaterialBakedShell(Mtl* mtl, VirtoolsMaterial &NMat, BOOL &OpacityValid) { #ifdef MAX51 if( mtl->NumSubMtls() >= 2 ){ Mtl* bakedMaterial = mtl->GetSubMtl(1); //If baked material is standard we must certainly also consider the first material if (bakedMaterial && bakedMaterial->ClassID() == Class_ID(DMTL_CLASS_ID, 0)) { StdMat* stdM = (StdMat*)bakedMaterial; if (!stdM->MapEnabled(ID_DI) && stdM->MapEnabled(ID_SI)) { DumpMaterial( mtl->GetSubMtl(0)); } } if( bShellMaterialExportBoth || (bakedMaterial && mtl->VPDisplaySubMtl()==1) ){ DumpMaterial( bakedMaterial ); } #ifdef MAX6 if (bShellMaterialExportBoth || mtl->VPDisplaySubMtl()==0) { Mtl* BaseMaterial = mtl->GetSubMtl(0); if (BaseMaterial){ DumpMaterial( BaseMaterial ); } } #endif } #endif } BOOL Max2Nmo::DumpMaterialLM(Mtl* mtl, VirtoolsMaterial &NMat) { VxColor white( 1.0f, 1.0f, 1.0f, 1.0f ); VxColor black( 0.0f, 0.0f, 0.0f, 1.0f ); BOOL baseMaterialAlreadyExists = FALSE; //--- Add base texture if( LM::BaseTextureEnabled(mtl) ){ XString baseTextureFilename = LM::BaseTextureFilename(mtl); CKPathSplitter psplitter( baseTextureFilename.Str() ); //--- Note: the texture is added only if there's no texture with ///// the same filename already added. NMat.m_Texture = VirtoolsExporter->AddTexture( baseTextureFilename.CStr(), (const char *)psplitter.GetName() ); NMat.m_Emissive = white; //--- If there's a similar material already added, then use it, ///// it must be the first created lightmap diffuse map ///// refering to the same texture XHashTable< Mtl*, XString, XHashFun >::Iterator it = m_lightmapBaseMaterials.Find( baseTextureFilename ); if( it!=m_lightmapBaseMaterials.End() ){ baseMaterialAlreadyExists = TRUE; } else { //--- Else, add this material to the list of unique lightmap base textures m_lightmapBaseMaterials.Insert( baseTextureFilename, mtl ); } } //--- Add lightmap texture if( LM::LightmapTextureEnabled(mtl) ){ VirtoolsMaterial LMMat; XString lightTextureFilename = LM::LightmapTextureFilename(mtl); CKPathSplitter psplitter( lightTextureFilename.Str() ); LMMat.m_Texture = VirtoolsExporter->AddTexture( lightTextureFilename.CStr(), (const char *)psplitter.GetName() ); LMMat.m_TextureBlendMode = VXTEXTUREBLEND_MODULATEALPHA; LMMat.m_Diffuse = white; LMMat.m_Ambient = white; LMMat.m_Specular = black; NMat.m_SpecularPower = 0; LMMat.m_Emissive = white; LMMat.m_TwoSided = NMat.m_TwoSided; LMMat.m_FillMode = NMat.m_FillMode; LMMat.m_ShadeMode = NMat.m_ShadeMode; //--- ~ little trick for lightmap material void* keyToFindBackMaterial = (void*)~(DWORD)mtl; CKMaterial* mat = VirtoolsExporter->AddMaterial( &LMMat, (const char *)psplitter.GetName(), keyToFindBackMaterial ); mat->SetTextureAddressMode( VXTEXTURE_ADDRESSCLAMP ); //--- Enable mipmapping by default mat->SetTextureMinMode( VXTEXTUREFILTER_LINEARMIPLINEAR ); if( LMMat.m_Texture ) LMMat.m_Texture->UseMipmap(TRUE); } if( baseMaterialAlreadyExists ) return false; return true; } /**************************************************************************** Misc Utility functions ****************************************************************************/ // Determine is the node has negative scaling. // This is used for mirrored objects for example. They have a negative scale factor // so when calculating the normal we should take the vertices counter clockwise. // If we don't compensate for this the objects will be 'inverted'. // But People should never use Mirror !!!! BOOL Max2Nmo::TMNegParity(Matrix3 &m) { return (DotProd(CrossProd(m.GetRow(0),m.GetRow(1)),m.GetRow(2))<0.0)?1:0; } // Return a pointer to a TriObject given an INode or return NULL // if the node cannot be converted to a TriObject TriObject* Max2Nmo::GetTriObjectFromNode(INode *node, TimeValue t, int &deleteIt) { deleteIt = FALSE; Object *obj = node->EvalWorldState(t).obj; if (obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) { TriObject *tri = (TriObject *) obj->ConvertToType(t, Class_ID(TRIOBJ_CLASS_ID, 0)); if (obj != tri) deleteIt = TRUE; return tri; } else { return NULL; } } // Return a pointer to a PatchObject given an INode or return NULL // if the node cannot be converted to a PatchObject PatchObject* Max2Nmo::GetPatchObjectFromNode(INode *node,TimeValue t,int &deleteIt) { deleteIt = FALSE; Object *obj = node->EvalWorldState(t).obj; if (obj->SuperClassID()==GEOMOBJECT_CLASS_ID) if (obj->ClassID()==Class_ID(PATCHOBJ_CLASS_ID, 0)) if (obj->CanConvertToType(Class_ID(PATCHOBJ_CLASS_ID, 0))) { PatchObject *patch = (PatchObject *) obj->ConvertToType( t , Class_ID(PATCHOBJ_CLASS_ID, 0) ); if (obj != patch) deleteIt = TRUE; return patch; } else return NULL; return NULL; } // Try to find if a skin modifier is in the modifier stack of a node Modifier* Max2Nmo::FindSkinModifier (INode* node) { #ifndef MAX42 if (g_CharacterStudio312) { return FindSkinModifierCS312(node); } else #endif { return FindSkinModifierCS300(node); } } // Try to find if a physique modifier is in the modifier stack of a node Modifier* Max2Nmo::FindPhysiqueModifier (INode* node) { #ifndef MAX42 if (g_CharacterStudio312) { return FindPhysiqueModifierCS312(node); } else #endif { return FindPhysiqueModifierCS300(node); } } Modifier* Max2Nmo::FindModifier(INode* node, Class_ID classID, int* modifierIndex, IDerivedObject** derivedObj) { // Get object from node. Abort if no object. Object* pObj = node->GetObjectRef(); if (!pObj) return NULL; // Is derived object ? SClass_ID sid = pObj->SuperClassID(); while (pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID) { // Yes -> Cast. IDerivedObject* pDerObj = static_cast(pObj); // Iterate over all entries of the modifier stack. int ModStackIndex = 0; while (ModStackIndex < pDerObj->NumModifiers()) { // Get current modifier. Modifier* mod = pDerObj->GetModifier(ModStackIndex); // Is this Skin ? if (mod->ClassID() == classID ) { if (modifierIndex) { *modifierIndex = ModStackIndex; } if (derivedObj) { *derivedObj = pDerObj; } return mod; } // Next modifier stack entry. ModStackIndex++; } pObj = pDerObj->GetObjRef(); } return NULL; } CKDWORD Max2Nmo::GetModifierCount(INode* node,Class_ID classID) { // Get object from node. Abort if no object. Object* pObj = node->GetObjectRef(); if (!pObj) return 0; CKDWORD count = 0; // Is derived object ? SClass_ID sid = pObj->SuperClassID(); while (pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID) { // Yes -> Cast. IDerivedObject* pDerObj = static_cast(pObj); // Iterate over all entries of the modifier stack. int ModStackIndex = 0; while (ModStackIndex < pDerObj->NumModifiers()) { // Get current modifier. Modifier* mod = pDerObj->GetModifier(ModStackIndex); // Is this Skin ? if (mod->ClassID() == classID ) { ++ count; } // Next modifier stack entry. ModStackIndex++; } pObj = pDerObj->GetObjRef(); } return count; } SplineShape* Max2Nmo::GetSplineShapeFromNode(INode *node,TimeValue t,int &deleteIt) { deleteIt = FALSE; Object *obj = node->EvalWorldState(t).obj; if (obj->SuperClassID()==SHAPE_CLASS_ID) { SplineShape *pSplineShape= (SplineShape *) obj->ConvertToType(0,splineShapeClassID ); if (obj != pSplineShape) deleteIt = TRUE; return pSplineShape; } return NULL; } // From the Max SDK // How to calculate UV's for face mapped materials. static Point3 basic_tva[3] = { Point3(0.0,0.0,0.0),Point3(1.0,1.0,0.0),Point3(1.0,0.0,0.0) }; static Point3 basic_tvb[3] = { Point3(0.0,1.0,0.0),Point3(1.0,1.0,0.0),Point3(0.0,0.0,0.0) }; static int nextpt[3] = {1,2,0}; static int prevpt[3] = {2,0,1}; static int nhidCase[8] = {-1,-1,-1,-1,0,1,-1,2}; static int forceInvertTab[8] = {0,0,0,0,0,0,0,0}; static int shiftifA[8] = {2,0,2,0,2,1,2,0}; static int shiftifB[8] = {0,0,0,1,0,2,0,1}; // To check that all case have been tested static int check[8] = {0,0,0,0,0,0,0,0}; void Max2Nmo::make_face_uv(Face *f, VxUV *tv) { int na,nhid,i; Point3 *basetv; /* make the invisible edge be 2->0 */ nhid = 1; if (!(f->flags&EDGE_A)) nhid=0; else if (!(f->flags&EDGE_B)) nhid = 1; else if (!(f->flags&EDGE_C)) nhid = 2; int fFlags = f->flags & 0x7; if (nhidCase[fFlags] >=0) { nhid = nhidCase[fFlags]; } na = 2-nhid; basetv = (f->v[prevpt[nhid]]v[nhid]) ? basic_tva : basic_tvb; if (forceInvertTab[fFlags]) { basetv = (basetv == basic_tvb) ? basic_tva : basic_tvb; } if ( (basetv == basic_tva) && shiftifA[fFlags] ) { na = nextpt[na]; if (shiftifA[fFlags] > 1) na = nextpt[na]; } if ( (basetv == basic_tvb) && shiftifB[fFlags] ) { na = nextpt[na]; if (shiftifB[fFlags] > 1) na = nextpt[na]; } if (basetv == basic_tvb) check[fFlags] |= 2; if (basetv == basic_tva) check[fFlags] |= 1; for (i=0; i<3; i++) { tv[i].u = basetv[na].x; tv[i].v = basetv[na].y; na = nextpt[na]; } } /************************************************************* Since Virtools and Max referential have switched Y and Z axis we need to swap the 2 axis in the matrix ***************************************************************/ void Max2Nmo::ConvertMaxMatrix2Virtools(Matrix3 &m, VxMatrix &WM) { MRow* pMRow=m.GetAddr(); // Row & Colummn 1 & 2 must be swapped WM[0][0]=(*pMRow)[0]; WM[0][1]=(*pMRow)[2]; WM[0][2]=(*pMRow)[1]; WM[0][3]=0.0f; WM[1][0]=(*(pMRow+2))[0]; WM[1][1]=(*(pMRow+2))[2]; WM[1][2]=(*(pMRow+2))[1]; WM[1][3]=0.0f; WM[2][0]=(*(pMRow+1))[0]; WM[2][1]=(*(pMRow+1))[2]; WM[2][2]=(*(pMRow+1))[1]; WM[2][3]=0.0f; WM[3][0]=(*(pMRow+3))[0]; WM[3][1]=(*(pMRow+3))[2]; WM[3][2]=(*(pMRow+3))[1]; WM[3][3]=1.0f; } /************************************************************* Max Camera and Light matrix don't act like object matrix, we need a specific conversion method ***************************************************************/ void Max2Nmo::ConvertMaxLightMatrix2Virtools(Matrix3 &m, VxMatrix &WM) { MRow* pMRow=m.GetAddr(); WM[0][0]=(*pMRow)[0]; WM[0][1]=(*pMRow)[2]; WM[0][2]=(*pMRow)[1]; WM[0][3]=0.0f; WM[1][0]=(*(pMRow+1))[0]; WM[1][1]=(*(pMRow+1))[2]; WM[1][2]=(*(pMRow+1))[1]; WM[1][3]=0.0f; WM[2][0]=(*(pMRow+2))[0]; WM[2][1]=(*(pMRow+2))[2]; WM[2][2]=(*(pMRow+2))[1]; WM[2][3]=0.0f; WM[3][0]=(*(pMRow+3))[0]; WM[3][1]=(*(pMRow+3))[2]; WM[3][2]=(*(pMRow+3))[1]; WM[3][3]=1.0f; WM[2][0] = -WM[2][0]; WM[2][1] = -WM[2][1]; WM[2][2] = -WM[2][2]; //--- Camera and Light Matrices should not have any scale... WM[0].Normalize(); WM[1].Normalize(); WM[2].Normalize(); } /******************************************************** // Max Vertices are given in the ObjectTM referential and we need them in NodeTM referential // use this method to get the OffsetTM *********************************************************/ Matrix3 Max2Nmo::GetNodeOffsetTM(INode* node) { Matrix3 tOffsetTM(1); Point3 pos = node->GetObjOffsetPos(); tOffsetTM.PreTranslate(pos); Quat quat = node->GetObjOffsetRot(); PreRotateMatrix(tOffsetTM, quat); ScaleValue scaleValue = node->GetObjOffsetScale(); ApplyScaling(tOffsetTM, scaleValue); return tOffsetTM; } /*****************************************************************************************/ // GetMaterialByIndex // // This function is used to retrieve the 3dsmax material applied to a face of an object. // // - pMtl: root material of the object // - MatId: Id given to the face in order to know wich sub-material // is to be used from the root. // - MapChannel: channel in which to look for the material (note: 0 means no channels) // - returnedKey: key to retrieve the matching CKMaterial later // (note: ~ little trick for lightmap material) /*****************************************************************************************/ Mtl* Max2Nmo::GetMaterialByIndex( Mtl *pMtl, DWORD MatId, int MapChannel, void*& returnedKey, BOOL iSearchingForChannel ) { returnedKey = NULL; if (!pMtl) return NULL; Mtl *pSubMtl=NULL; //--- Standard Material if(pMtl->ClassID()==Class_ID(DMTL_CLASS_ID, 0)) { #ifdef MAX51 //--- Araya: If for this channel there's a lightmap texture enabled ///// then returnedKey is not the pMtl, but some trick to retrieve it from pMtl if( LM::ShaderEnabled(pMtl) ){ if( MapChannel ){ if( LM::LightmapTextureEnabled(pMtl) && LM::LightmapTextureMapping(pMtl)==MapChannel ){ returnedKey = (void*)~(DWORD)pMtl; return pMtl; } } else { if( LM::BaseTextureEnabled(pMtl) ){ const char* baseTextureFilename = LM::BaseTextureFilename(pMtl); if( baseTextureFilename ){ Mtl** matchingBaseMaterialPtr = m_lightmapBaseMaterials.FindPtr( baseTextureFilename ); if( matchingBaseMaterialPtr ){ returnedKey = *matchingBaseMaterialPtr; return *matchingBaseMaterialPtr; } } } } return NULL; } #endif if (MapChannel) { Texmap* pTexmap=pMtl->GetSubTexmap(ID_DI); if (pTexmap && pTexmap->GetUVWSource()==UVWSRC_EXPLICIT && pTexmap->GetMapChannel()==MapChannel) { returnedKey = pMtl; return pMtl; } else { Texmap* pTexmap=pMtl->GetSubTexmap(ID_SI); if (pTexmap && pTexmap->GetUVWSource()==UVWSRC_EXPLICIT && pTexmap->GetMapChannel()==MapChannel) { returnedKey = (void*)~(DWORD)pMtl; return pMtl; } } return NULL; } else { returnedKey = pMtl; return pMtl; } } else #ifdef MAX51 //--- Baked Shell Material if(pMtl->ClassID()==Class_ID(BAKE_SHELL_CLASS_ID, 0)) { int selectedViewPortMaterialIndex = 1; if( !bShellMaterialExportBoth ){ enum { bakeShell_params }; enum { bakeShell_vp_n_mtl }; IParamBlock2* pblock = pMtl->GetParamBlockByID( bakeShell_params ); if( pblock ){ pblock->GetValue( bakeShell_vp_n_mtl, 0, selectedViewPortMaterialIndex, FOREVER ); } } pSubMtl=pMtl->GetSubMtl( selectedViewPortMaterialIndex ); return GetMaterialByIndex(pSubMtl,MatId,MapChannel, returnedKey); } else #endif //--- Multi/Sub Material if(pMtl->ClassID()==Class_ID(MULTI_CLASS_ID, 0)) { //--- If we are searching for a material channel, then return NULL ///// as Multi/Sub Material are not made for channels usage // Fix Thomas Bucher : I deactivated this part of code, because it // avoids from exporting UVs channels >= 2 when using MultiSub materials // containing composite or blend materials /* if( iSearchingForChannel ){ return NULL; } */ pSubMtl=pMtl->GetSubMtl(MatId % pMtl->NumSubMtls()); return GetMaterialByIndex(pSubMtl,MatId,MapChannel, returnedKey); } else //--- Mix Material if(pMtl->ClassID()==Class_ID(MIXMAT_CLASS_ID, 0)) { // In case we are looking for a material assigned to a specific channel if (MapChannel) { int i; for (i=0;iNumSubMtls();i++) if (pSubMtl=pMtl->GetSubMtl(i)) if(pSubMtl->ClassID()==Class_ID(DMTL_CLASS_ID, 0)) { Texmap* pTexmap=pSubMtl->GetSubTexmap(ID_DI); if (pTexmap && pTexmap->GetUVWSource()==UVWSRC_EXPLICIT) if (pTexmap->GetMapChannel()==MapChannel) break; } if (i>=pMtl->NumSubMtls()) pSubMtl=NULL; } else pSubMtl=pMtl->GetSubMtl(0); return GetMaterialByIndex(pSubMtl,MatId,MapChannel, returnedKey); } //--- Composite Material //--- Seems like it is not a default Discreet class if(pMtl->ClassID()==Class_ID(0x61dc0cd7, 0x13640af6)) { // In case we are looking for a material assigned to a specific channel if (MapChannel) { int i; for (i=0;iNumSubMtls();i++){ pSubMtl=pMtl->GetSubMtl(i); if (pSubMtl!=NULL){ if(pSubMtl->ClassID()==Class_ID(DMTL_CLASS_ID, 0)) { Texmap* pTexmap=pSubMtl->GetSubTexmap(ID_DI); if (pTexmap->GetUVWSource()==UVWSRC_EXPLICIT){ if (pTexmap->GetMapChannel()==MapChannel) break; } } } } if (i>=pMtl->NumSubMtls()) pSubMtl=NULL; } else //if looking for the base material pSubMtl=pMtl->GetSubMtl(0); return GetMaterialByIndex(pSubMtl,MatId,MapChannel, returnedKey); } return NULL; // ???? } // from bmTex.cpp #define PB_CLIPU 0 #define PB_CLIPV 1 #define PB_CLIPW 2 #define PB_CLIPH 3 #define PB_APPLYCROP 6 BOOL Max2Nmo::GetTextureUvGen(Mtl* pMtl,TextureUVGen& uvgen) { if (!pMtl) return FALSE; if (pMtl->ClassID()!=Class_ID(DMTL_CLASS_ID, 0)) return FALSE; StdMat* stdmat = (StdMat*)pMtl; if( !stdmat->MapEnabled(ID_DI) ) return FALSE; Texmap *ptexmap = stdmat->GetSubTexmap(ID_DI); if( !ptexmap ) return FALSE; if( ptexmap->ClassID()!=Class_ID(BMTEX_CLASS_ID,0) ) return FALSE; BitmapTex *pbitmaptex = (BitmapTex*)ptexmap; //--- Tiling & mirroring StdUVGen* uvs = pbitmaptex->GetUVGen(); int adrMode = uvs->GetTextureTiling(); uvgen.UOffset = uvs->GetUOffs(0); uvgen.UScale = uvs->GetUScl(0); uvgen.VOffset = uvs->GetVOffs(0); uvgen.VScale = uvs->GetVScl(0); uvgen.TileU = adrMode&U_WRAP; uvgen.MirrorU = adrMode&U_MIRROR; uvgen.TileV = adrMode&V_WRAP; uvgen.MirrorV = adrMode&V_MIRROR; uvgen.AngleMap = uvs->GetAng(0); uvgen.Cropping = FALSE; uvgen.UCropOffset = 0; uvgen.VCropOffset = 0; uvgen.UCropScale = 1.0f; uvgen.VCropScale = 1.0f; // There is no api to access cropping values : // use param blocks IParamBlock2* block=(IParamBlock2*)pbitmaptex->GetReference(1); if (block) { uvgen.Cropping = block->GetInt( PB_APPLYCROP, 0); uvgen.UCropOffset = block->GetFloat( PB_CLIPU, 0); uvgen.VCropOffset = block->GetFloat( PB_CLIPV, 0); uvgen.UCropScale = block->GetFloat( PB_CLIPW, 0); uvgen.VCropScale = block->GetFloat( PB_CLIPH, 0); } return TRUE; } void Max2Nmo::ApplyUvGen(VxUV& uv,TextureUVGen& uvgen,BOOL useUVGen) { //--- Virtools and Max texture coordinates are upside down uv.v = 1.0f - uv.v; if( !useUVGen ) return; float u = uv.u; float v = uv.v; //-- Apply cropping and test because uCropOfs sometimes is -NAND if(uvgen.Cropping) { if (!(_isnan(uvgen.UCropOffset) || _isnan(uvgen.UCropScale))) u = uvgen.UCropOffset + ( u * uvgen.UCropScale); if (!(_isnan(uvgen.VCropOffset) || _isnan(uvgen.VCropScale))) v = uvgen.VCropOffset + ( v * uvgen.VCropScale); } //-- Apply tiling u=-uvgen.UOffset + 0.5f + ((u-0.5f)*uvgen.UScale); v=uvgen.VOffset + 0.5f + ((v-0.5f)*uvgen.VScale); uv.u = u; uv.v = v; } CKMesh* Max2Nmo::IsMeshInstance(void* mesh,Mtl* mtl) { for (int i=0;inReportLevel) return; va_list marker; va_start(marker, format); // Always ensure at least 16384 characters are to be written ReportBuffer.Resize(ReportBufferPos + 16384); DWORD nbC = vsprintf((char*)&ReportBuffer[ReportBufferPos],format,marker); ReportBufferPos+=nbC; if(m_ReportGeLog) m_ReportGeLog->VFormat(format,marker); } /*--------------------------------------------------*/ /* macro */ #pragma warning (disable:4035) _inline unsigned long GetMSB(unsigned long data) { _asm { mov eax,data bsr eax,eax } } #pragma warning (default:4035)