3167 lines
94 KiB
C++
3167 lines
94 KiB
C++
//**************************************************************************
|
||
//* 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)
|