deargui-vpl/ref/virtools/Samples/ExportPlugins/3dsMax Export/export.cpp

3167 lines
94 KiB
C++
Raw Permalink Blame History

//**************************************************************************
//* 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; i<KnotCount;++i) {
CKCurvePoint* pt = VirtoolsExporter->AddCurvePoint(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<IDerivedObject*>(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<Point3> refPos;
XArray<Point3> 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<float> 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<75>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 <20> 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; i<numVtx; i++) {
Point3 tmp = OffsetTM.PointTransform(mesh->verts[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; i<numFaces; i++,UvIndex+=3) {
int MatID = mesh->faces[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(VirtoolsChannelCount<MAX_UVCHANNEL_COUNT-1) { //the new channel is VirtoolsChannelCount++ (see below)
int numTVx = mesh->getNumMapVerts(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; i<numFaces; i++,UvIndex+=3) {
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]] ] );
for (int j=0;j<3;++j) {
ApplyUvGen(uvs[j],uvgen,useUVGen);
VirtoolsTempMesh.AddUv(uvs[j],VirtoolsChannelCount);
}
VirtoolsTempMesh.AddUVFace(UvIndex,UvIndex+1,UvIndex+2,VirtoolsChannelCount);
}
}
}
}
else{//Method No Material
// Fix Thomas Bucher
// I think that if the user has set a floatXXXX suffix to the channel
// it is because it want to export it !
// So do it even if ForceAllUVs == false.
if( GetForceAllUVs() || ExportExtendedMethod==2) {
if (ExportExtendedMethod ==1 ) {
UVVert* tVerts = mesh->mapVerts( mp );
TVFace* tvFaces = mesh->mapFaces( mp );
BOOL same[3]={true,true,true};
float last[3];
for (i=0; i<numFaces; i++){
for (int j=0;j<3;++j) {
curV =&tVerts[ tvFaces[i].t[vx[j]] ];
if(i==0){
last[0]=curV->x;
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 && j<size; j++ && i++){
input[i]= tmp[j];
}
// Fix Thomas Bucher
// Look in input[x] only the N components given (and not
// always 4 components as formerly)
int channelDimension = size - ret - 5;
//two forms are possible
//floatX* or float X*
if(input[0]==' '){//if first is a space... shift the values left
for(int j=0; j<4; j++)
input[j]=input[j+1];
}
for(int j=0; j<channelDimension && ExportExtendedChannelType<channelDimension; j++){
if(input[j]==' ') break;
if(input[j]=='x' || input[j]=='r'){
ExportExtendedComponents[ExportExtendedChannelType]=0;
}else if(input[j]=='y' || input[j]=='g'){
ExportExtendedComponents[ExportExtendedChannelType]=1;
}else if(input[j]=='z' || input[j]=='b'){
ExportExtendedComponents[ExportExtendedChannelType]=2;
}else if(input[j]=='m'){
ExportExtendedComponents[ExportExtendedChannelType]=4;
}else if(input[j]=='0'){
ExportExtendedComponents[ExportExtendedChannelType]=5;
}else if(input[j]=='1'){
ExportExtendedComponents[ExportExtendedChannelType]=6;
//Unknow character, exit the loop
}else{
this->Report(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; i<numFaces; i++,UvIndex+=3) {
for (int j=0;j<3;++j) {
curV =&tVerts[ tvFaces[i].t[vx[j]] ];
vals[0]=curV->x;
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; i<numCVx; i++) {
memcpy( &VirtoolsTempMesh.m_VertexColors[i], &vertColData[i], 3*sizeof(float) );
VirtoolsTempMesh.m_VertexColors[i].a = 1.0f;
#ifdef MAX4
if(support)
VirtoolsTempMesh.m_VertexColors[i].a = alpha[i].x;
#endif
}
for (i=0; i<numFaces; i++) {
VirtoolsTempMesh.AddColorFace(
vcFaceData[i].t[vx[0]],
vcFaceData[i].t[vx[1]],
vcFaceData[i].t[vx[2]]);
}
}
//---------- Vertex normals. --------------------------------
// In MAX a vertex can have more than one normal (but doesn't always have it).
// This is depending on the face you are accessing the vertex through.
// To get all information we need to export all three vertex normals
// for every face.
int Nindex=0;
// Edit normals modified was introduced with Max 5
#ifdef MAX51
if (userSpecifiedNormals) {
XASSERT(userSpecifiedNormals->GetNumFaces() == numFaces);
for (i=0; i<numFaces; i++,Nindex+=3) {
Point3 vn;
vn = userSpecifiedNormals->GetNormal (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; i<numFaces; i++,Nindex+=3) {
Face* f = &mesh->faces[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 ; a<vCount ; ++a ){
int vOriginal = VirtoolsTempMesh.m_VirtoolsVertices[a].OriginalPosIndex;
float weight = weightOrginal[vOriginal];
VirtoolsMesh->SetVertexWeight( 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; i<pmesh->numVerts; i++) {
Point3 tmp = OffsetTM.PointTransform(pmesh->verts[i].p);
VirtoolsPMesh->SetVert(i,&VxVector(tmp.x,tmp.z,tmp.y));
}
for (int i=0; i<pmesh->numVecs; 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;i<pmesh->numEdges;++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;i<pmesh->numPatches;++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;mp<pmesh->NumMapChannels();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;i<pmesh->numPatches;++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;j<nb;++j) {
VxUV uv;
CopyUV(&uv,&pmesh->getMapVert(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; i<numMtls; i++) {
DumpMaterial(mtlList.GetMtl(i));
}
}
/****************************************************************************
DumpMaterial (export the material according to its type)
****************************************************************************/
CKMaterial* Max2Nmo::DumpMaterial(Mtl* mtl)
{
Report(REPORT_HLEVEL,"%s...",mtl->GetName());
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; i<mtl->NumSubMtls(); 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<WORD> 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<diffTexWidth;++j,pix+=2,tptr+=4) {
if (OneMinusMixAmount==0.f) {
*tptr = *pix;
}
else {
int an_int = (int) (OneMinusMixAmount*255.0)+*pix;
if (an_int > 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; h<height && equal; h++) {
for(w=0; w<width && equal; w++) {
DWORD formerP = NMat.m_Texture->GetPixel(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<XString> >::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<IDerivedObject*>(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<IDerivedObject*>(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]]<f->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;i<pMtl->NumSubMtls();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;i<pMtl->NumSubMtls();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;i<MeshTable.Size();++i) {
if ((MeshTable[i].mesh==mesh) && (MeshTable[i].mtl==mtl))
return MeshTable[i].CKMesh;
}
return NULL;
}
void Max2Nmo::InsertNewMeshInstance(void* mesh,Mtl* mtl,CKMesh* ckmesh)
{
NodeMeshMtl tmp;
tmp.mesh = mesh;
tmp.CKMesh = ckmesh;
tmp.mtl = mtl;
MeshTable.PushBack(tmp);
}
void Max2Nmo::Report(int InfoLevel,char *format, ...)
{
if (InfoLevel>nReportLevel) 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)