Initial commit

This commit is contained in:
Eagle517
2025-02-17 23:17:30 -06:00
commit 7cad314c94
4726 changed files with 1145203 additions and 0 deletions

View File

@ -0,0 +1,80 @@
/**********************************************************************
*<
FILE: DllEntry.cpp
DESCRIPTION: Contains the Dll Entry stuff
CREATED BY:
HISTORY:
*> Copyright (c) 2000, All Rights Reserved.
**********************************************************************/
#include "max2dtsExporterPlus.h"
#include "skinHelper.h"
extern ClassDesc2* GetMax2dtsExporterPlusDesc();
HINSTANCE hInstance;
int controlsInit = FALSE;
// This function is called by Windows when the DLL is loaded. This
// function may also be called many times during time critical operations
// like rendering. Therefore developers need to be careful what they
// do inside this function. In the code below, note how after the DLL is
// loaded the first time only a few statements are executed.
BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved)
{
hInstance = hinstDLL; // Hang on to this DLL's instance handle.
if (!controlsInit) {
controlsInit = TRUE;
InitCustomControls(hInstance); // Initialize MAX's custom controls
InitCommonControls(); // Initialize Win95 controls
}
return (TRUE);
}
// This function returns a string that describes the DLL and where the user
// could purchase the DLL if they don't have it.
__declspec( dllexport ) const TCHAR* LibDescription()
{
return GetString(IDS_LIBDESCRIPTION);
}
// This function returns the number of plug-in classes this DLL
// Must change this number when adding a new class
__declspec( dllexport ) int LibNumberClasses()
{
return 2;
}
// This function returns the number of plug-in classes this DLL
__declspec( dllexport ) ClassDesc* LibClassDesc(int i)
{
switch(i) {
case 0: return GetMax2dtsExporterPlusDesc();
case 1: return GetSkinHelperDesc();
default: return 0;
}
}
// This function returns a pre-defined constant indicating the version of
// the system under which it was compiled. It is used to allow the system
// to catch obsolete DLLs.
__declspec( dllexport ) ULONG LibVersion()
{
return VERSION_3DSMAX;
}
TCHAR *GetString(int id)
{
static TCHAR buf[256];
if (hInstance)
return LoadString(hInstance, id, buf, sizeof(buf)) ? buf : NULL;
return NULL;
}

Binary file not shown.

View File

@ -0,0 +1,210 @@
/**********************************************************************
*<
FILE: max2dtsExporterPlus.cpp
DESCRIPTION: Appwizard generated plugin
CREATED BY:
HISTORY:
*> Copyright (c) 2000, All Rights Reserved.
**********************************************************************/
#include "max2dtsExporterPlus.h"
#include "maxSceneEnum.h"
#include "appConfig.h"
#include <fstream>
#define MAX2DTSEXPORTERPLUS_CLASS_ID Class_ID(0x612c5c7, 0x5924d160)
class Max2dtsExporterPlus : public SceneExport {
public:
static HWND hParams;
int ExtCount(); // Number of extensions supported
const TCHAR * Ext(int n); // Extension #n (i.e. "3DS")
const TCHAR * LongDesc(); // Long ASCII description (i.e. "Autodesk 3D Studio File")
const TCHAR * ShortDesc(); // Short ASCII description (i.e. "3D Studio")
const TCHAR * AuthorName(); // ASCII Author name
const TCHAR * CopyrightMessage(); // ASCII Copyright message
const TCHAR * OtherMessage1(); // Other message #1
const TCHAR * OtherMessage2(); // Other message #2
unsigned int Version(); // Version number * 100 (i.e. v3.01 = 301)
void ShowAbout(HWND hWnd); // Show DLL's "About..." box
BOOL SupportsOptions(int ext, DWORD options);
int DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts=FALSE, DWORD options=0);
void alert(const char * title, const char * message, BOOL suppressPrompts);
//Constructor/Destructor
Max2dtsExporterPlus();
~Max2dtsExporterPlus();
};
class Max2dtsExporterPlusClassDesc:public ClassDesc2 {
public:
int IsPublic() { return TRUE; }
void * Create(BOOL loading = FALSE) { return new Max2dtsExporterPlus(); }
const TCHAR * ClassName() { return GetString(IDS_CLASS_NAME); }
SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; }
Class_ID ClassID() { return MAX2DTSEXPORTERPLUS_CLASS_ID; }
const TCHAR* Category() { return GetString(IDS_CATEGORY); }
const TCHAR* InternalName() { return _T("Max2dtsExporterPlus"); } // returns fixed parsable name (scripter-visible name)
HINSTANCE HInstance() { return hInstance; } // returns owning module handle
};
static Max2dtsExporterPlusClassDesc Max2dtsExporterPlusDesc;
ClassDesc2* GetMax2dtsExporterPlusDesc() { return &Max2dtsExporterPlusDesc; }
BOOL CALLBACK Max2dtsExporterPlusOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) {
static Max2dtsExporterPlus *imp = NULL;
switch(message) {
case WM_INITDIALOG:
imp = (Max2dtsExporterPlus *)lParam;
CenterWindow(hWnd,GetParent(hWnd));
return TRUE;
case WM_CLOSE:
EndDialog(hWnd, 0);
return TRUE;
}
return FALSE;
}
//--- Max2dtsExporterPlus -------------------------------------------------------
Max2dtsExporterPlus::Max2dtsExporterPlus()
{
}
Max2dtsExporterPlus::~Max2dtsExporterPlus()
{
}
int Max2dtsExporterPlus::ExtCount()
{
// Returns the number of file name extensions supported by the plug-in.
return 1;
}
const TCHAR *Max2dtsExporterPlus::Ext(int n)
{
// Return the 'i-th' file name extension
return _T("DTS");
}
const TCHAR *Max2dtsExporterPlus::LongDesc()
{
//Return long ASCII description
return _T("Torque game engine dts shape file");
}
const TCHAR *Max2dtsExporterPlus::ShortDesc()
{
//Return short ASCII description
return _T("DTS Shape");
}
const TCHAR *Max2dtsExporterPlus::AuthorName()
{
//Return ASCII Author name
return _T("Clark Fagot");
}
const TCHAR *Max2dtsExporterPlus::CopyrightMessage()
{
// Return ASCII Copyright message
return _T("Copyright GarageGames");
}
const TCHAR *Max2dtsExporterPlus::OtherMessage1()
{
//Return Other message #1 if any
return _T("");
}
const TCHAR *Max2dtsExporterPlus::OtherMessage2()
{
//Return other message #2 in any
return _T("");
}
unsigned int Max2dtsExporterPlus::Version()
{
//Return Version number * 100 (i.e. v3.01 = 301)
return 100;
}
void Max2dtsExporterPlus::ShowAbout(HWND hWnd)
{
// Optional
}
BOOL Max2dtsExporterPlus::SupportsOptions(int ext, DWORD options)
{
// Decide which options to support. Simply return
// true for each option supported by each Extension
// the exporter supports.
return TRUE;
}
void Max2dtsExporterPlus::alert(const char * title, const char * message, BOOL suppressPrompts)
{
if (suppressPrompts)
DTS::AppConfig::PrintDump(-1,message);
else
{
TSTR str1(message);
TSTR str2(title);
MessageBox(GetActiveWindow(), str1, str2, MB_OK);
}
}
int Max2dtsExporterPlus::DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts, DWORD options)
{
// Implement the actual file Export here and
// return TRUE If the file is exported properly
if (!DTS::AppConfig::SetDumpFile(name))
alert("Dump file error",DTS::avar("Unable to create dumpfile for shape \"%s\".",name),suppressPrompts);
// Get config file if it exists...
const char * configBase = i->GetCurFilePath();
DTS::AppConfig::ReadConfigFile(configBase,".max");
DTS::MaxSceneEnum maxSceneEnum(ei->theScene);
DTS::Shape * shape = maxSceneEnum.processScene();
if (!DTS::AppConfig::IsExportError())
{
std::ofstream os;
os.open(name,std::ofstream::binary);
shape->save(os);
os.close();
}
DTS::AppConfig::CloseDumpFile();
delete shape;
if (DTS::AppConfig::IsExportError())
alert("Export error",DTS::AppConfig::GetExportError(),suppressPrompts);
return !DTS::AppConfig::IsExportError();
}

View File

@ -0,0 +1,8 @@
LIBRARY max2dtsExporterPlus
EXPORTS
LibDescription @1
LibNumberClasses @2
LibClassDesc @3
LibVersion @4
SECTIONS
.data READ WRITE

View File

@ -0,0 +1,26 @@
/**********************************************************************
*<
FILE: max2dtsExporterPlus.h
DESCRIPTION: Includes for Plugins
CREATED BY:
HISTORY:
*> Copyright (c) 2000, All Rights Reserved.
**********************************************************************/
#ifndef __MAX2DTSEXPORTERPLUS__H
#define __MAX2DTSEXPORTERPLUS__H
#include "Max.h"
#include "resource.h"
#include "istdplug.h"
#include "iparamb2.h"
#include "iparamm2.h"
extern TCHAR *GetString(int id);
extern HINSTANCE hInstance;
#endif // __MAX2DTSEXPORTERPLUS__H

View File

@ -0,0 +1,163 @@
//Microsoft Developer Studio generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_PANEL DIALOGEX 0, 0, 108, 156
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_TOOLWINDOW
FONT 8, "MS Sans Serif", 0, 0, 0x1
BEGIN
CTEXT "Generated by Plugin AppWizard\nCode && design by\nRavi Karra - Discreet",
IDC_STATIC,7,7,94,36
CONTROL "",IDC_EDIT,"CustEdit",WS_TABSTOP,29,114,35,10
CONTROL "",IDC_SPIN,"SpinnerControl",0x0,65,114,7,10
LTEXT "Spinner Cust Control:",IDC_STATIC,20,102,67,8
CTEXT "TODO: Place panel controls here.",IDC_STATIC,15,63,78,
19
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
IDD_PANEL, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 101
TOPMARGIN, 7
BOTTOMMARGIN, 149
END
END
#endif // APSTUDIO_INVOKED
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
#ifndef _MAC
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", "\0"
VALUE "CompanyName", "BraveTree Productions\0"
VALUE "FileDescription", "max2dtsExporterPlus\0"
VALUE "FileVersion", "1, 0, 0, 1\0"
VALUE "InternalName", "max2dtsExporterPlus\0"
VALUE "LegalCopyright", "Copyright <20> 2004\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "max2dtsExporterPlus.dle\0"
VALUE "PrivateBuild", "\0"
VALUE "ProductName", "BraveTree Productions max2dtsExporterPlus\0"
VALUE "ProductVersion", "1, 0, 0, 1\0"
VALUE "SpecialBuild", "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
#endif // !_MAC
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE DISCARDABLE
BEGIN
IDS_LIBDESCRIPTION "Export Torque DTS files using dtsSDKPlus"
IDS_CATEGORY "I like turnips"
IDS_CLASS_NAME "Max2dtsExporterPlus"
IDS_PARAMS "Parameters"
IDS_SPIN "Spin"
END
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@ -0,0 +1,483 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "maxAppMesh.h"
#include "maxAppNode.h"
#include "appConfig.h"
#include "appIfl.h"
#include "skinHelper.h"
#pragma pack(push,8)
#include <max.h>
#include <iparamb2.h>
#include <ISkin.h>
#include <modstack.h>
#include <istdplug.h>
#include <stdmat.h>
#include <maxtypes.h>
#pragma pack(pop)
//----------------------------------------------------------------
// 3dsMax utility function:
// Grab the tri object from the max mesh node
//----------------------------------------------------------------
TriObject * getTriObject( INode *pNode, S32 time, bool & deleteIt)
{
TriObject * tri = NULL;
IParamBlock * paramBlock = NULL;
// if the object can't convert to a tri-mesh, eval world state to
// get an object that can:
const ::ObjectState &os = pNode->EvalWorldState(time);
if ( os.obj->CanConvertToType(triObjectClassID) )
tri = (TriObject *)os.obj->ConvertToType( time, triObjectClassID );
deleteIt = (tri && (tri != os.obj));
return tri;
}
//----------------------------------------------------------------
// 3dsMax utility function:
// Grab the skin data from the max mesh node
//----------------------------------------------------------------
void findSkinData(INode * pNode, ISkin **skin, ISkinContextData ** skinData)
{
// till proven otherwise...
*skin = NULL;
*skinData = NULL;
// Get object from node. Abort if no object.
::Object* obj = pNode->GetObjectRef();
if (!obj)
return;
Modifier * mod = NULL;
// Is derived object ?
S32 i;
while (obj->SuperClassID() == GEN_DERIVOB_CLASS_ID)
{
IDerivedObject* dobj = (IDerivedObject*) obj;
// Iterate over all entries of the modifier stack.
for (i=0;i<dobj->NumModifiers();i++)
if (dobj->GetModifier(i)->ClassID() == SKIN_CLASSID)
break;
if (i!=dobj->NumModifiers())
{
mod = dobj->GetModifier(i);
break;
}
obj = dobj->GetObjRef();
}
if (!mod)
return;
*skin = (ISkin*) mod->GetInterface(I_SKIN);
if (!*skin)
return;
*skinData = (*skin)->GetContextInterface(pNode);
if (!*skinData)
*skin=NULL; // return both or neither
}
namespace DTS
{
MaxAppMesh::MaxAppMesh(INode * maxNode, AppNode * appNode)
{
mMaxNode = maxNode;
mAppNode = appNode;
}
Matrix<4,4,F32> MaxAppMesh::getMeshTransform(const AppTime & time)
{
assert(!mLocked && "Mesh is locked");
Matrix<4,4,F32> ret;
Matrix3 mat = mMaxNode->GetObjTMAfterWSM(SecToTicks(time.getF32()));
return convertFromMaxMatrix(mat);
}
F32 MaxAppMesh::getVisValue(const AppTime & time)
{
assert(!mLocked && "Mesh is locked");
return mMaxNode->GetVisibility(SecToTicks(time.getF32()));
}
bool MaxAppMesh::getMaterial(S32 matIdx, Material & mat)
{
mat.reflectance = -1;
mat.bump = -1;
mat.detail = -1;
mat.detailScale = 1.0f;
mat.reflection = 1.0f;
mat.flags = 0;
// do some hocus pocus to get the material....
Mtl * mtl = mMaxNode->GetMtl();
if( !mtl )
return false;
if( mtl->ClassID() == Class_ID(MULTI_CLASS_ID,0) )
{
MultiMtl * multiMtl = (MultiMtl*)mtl;
if (multiMtl->NumSubMtls()==0)
return false;
matIdx %= multiMtl->NumSubMtls();
mtl = multiMtl->GetSubMtl( matIdx );
}
if( mtl->ClassID() != Class_ID(DMTL_CLASS_ID,0) )
{
AppConfig::SetExportError(avar("Unexpected material type on mesh \"%s\".",mMaxNode->GetName()));
return false;
}
StdMat * stdMat = (StdMat*)mtl;
// we now have a standard material...this guy has a number of maps
// the diffuse map corresponds to the texture
// the reflection map is used for environment mapping (normally this will be in the alpha of
// the texture, but under some circumstances -- translucency -- you want a separate map for it)
// the material also has a bump map and a detail map (we look for this in the ambient map...
// ambient because detail maps add ambiance... :)
// get diffuse map...
if (stdMat->GetSubTexmap(ID_DI) == NULL || !stdMat->MapEnabled(ID_DI))
// no diffuse...
return false;
if (stdMat->GetSubTexmap(ID_DI)->ClassID() != Class_ID(BMTEX_CLASS_ID,0))
{
AppConfig::SetExportError(avar("Diffuse channel on mesh \"%s\" has a non-bitmap texture map.",mMaxNode->GetName()));
return false;
}
BitmapTex * diffuse = (BitmapTex*)stdMat->GetSubTexmap(ID_DI);
mat.name = diffuse->GetMapName();
// set up texture flags
if (stdMat->MapEnabled(ID_OP))
{
// note: translucent if opacity channel is enabled
mat.flags |= Material::Translucent;
if (stdMat->GetTransparencyType() == TRANSP_ADDITIVE)
mat.flags |= Material::Additive;
else if (stdMat->GetTransparencyType() == TRANSP_SUBTRACTIVE)
mat.flags |= Material::Subtractive;
}
if (!stdMat->MapEnabled(ID_RL))
// only environment map if reflectance check box is set (but no material necessary)
mat.flags |= Material::NeverEnvMap;
mat.reflectance = stdMat->GetTexmapAmt(ID_RL,0);
if (diffuse->GetUVGen()->GetTextureTiling() & U_WRAP)
mat.flags |= Material::SWrap;
if (diffuse->GetUVGen()->GetTextureTiling() & V_WRAP)
mat.flags |= Material::TWrap;
if ( stdMat->GetSelfIllum(0) > 0.99f)
mat.flags |= Material::SelfIlluminating;
//------------
// If this is an ifl, then create the ifl material if it doesn't exist and mark as ifl
const char * dot = strchr(mat.name.c_str(),'.');
if (dot && !stricmp(dot+1,"ifl"))
{
mat.flags |= Material::IFLMaterial;
while (mIfls.size()<=matIdx)
mIfls.push_back(NULL);
if (!mIfls[matIdx])
mIfls[matIdx] = new AppIfl(mat.name.c_str());
}
return true;
}
bool MaxAppMesh::animatesFrame(const AppSequenceData & seqData)
{
assert(!mLocked && "Mesh is locked");
S32 startTime = SecToTicks(seqData.startTime.getF32());
S32 endTime = SecToTicks(seqData.endTime.getF32());
bool deleteIt;
TriObject * tri = getTriObject(mMaxNode,startTime,deleteIt);
if (!tri)
return false;
Interval ivalid;
ivalid = tri->ChannelValidity(startTime, GEOM_CHAN_NUM);
if (deleteIt)
tri->DeleteMe();
return (ivalid.Start() > startTime || ivalid.End() < endTime);
}
bool MaxAppMesh::animatesMatFrame(const AppSequenceData & seqData)
{
assert(!mLocked && "Mesh is locked");
S32 startTime = SecToTicks(seqData.startTime.getF32());
S32 endTime = SecToTicks(seqData.endTime.getF32());
bool deleteIt;
TriObject * tri = getTriObject(mMaxNode,startTime,deleteIt);
if (!tri)
return false;
Interval ivalid;
ivalid = tri->ChannelValidity(startTime, TEXMAP_CHAN_NUM);
if (deleteIt)
tri->DeleteMe();
return (ivalid.Start() > startTime || ivalid.End() < endTime);
}
AppMeshLock MaxAppMesh::lockMesh(const AppTime & time, const Matrix<4,4,F32> & objectOffset)
{
// are we mirrored?
AffineParts parts;
decomp_affine(objectOffset,&parts);
bool mirror = parts.sign < 0.0f;
bool delTri;
TriObject * tri = getTriObject(mMaxNode,SecToTicks(time.getF32()),delTri);
::Mesh & maxMesh = tri->mesh;
S32 i;
S32 lastMatIdx = -1;
Matrix3 uvTransform;
// start lists empty
mFaces.clear();
mVerts.clear();
mTVerts.clear();
mIndices.clear();
mSmooth.clear();
mVertId.clear();
// start out with faces and crop data allocated
mFaces.resize(maxMesh.getNumFaces());
// if no faces, detect it (use redundant test just in case
bool tmap = maxMesh.mapSupport(1) && maxMesh.tVerts; // 1 == default map
// get faces, points & materials
for (i=0; i<mFaces.size();i++)
{
Face & maxFace = maxMesh.faces[i];
Primitive & tsFace = mFaces[i];
// set faces material index
tsFace.type = maxFace.getMatID(); // use max mat idx (get's converted by shapemimic)
tsFace.firstElement = mIndices.size();
tsFace.numElements = 3;
tsFace.type |= Primitive::Triangles|Primitive::Indexed;
// set vertex indices
S32 idx0 = maxFace.v[0];
S32 idx1 = maxFace.v[2]; // switch the order to be CW
S32 idx2 = maxFace.v[1]; // switch the order to be CW
if (mirror)
{
S32 tmp = idx1;
idx1 = idx2;
idx2 = tmp;
}
Point3D v0(maxMesh.verts[idx0].x,maxMesh.verts[idx0].y,maxMesh.verts[idx0].z);
Point3D v1(maxMesh.verts[idx1].x,maxMesh.verts[idx1].y,maxMesh.verts[idx1].z);
Point3D v2(maxMesh.verts[idx2].x,maxMesh.verts[idx2].y,maxMesh.verts[idx2].z);
Point3D vert0 = objectOffset * v0;
Point3D vert1 = objectOffset * v1;
Point3D vert2 = objectOffset * v2;
// set texture vertex indices
Point2D tvert0(0,0);
Point2D tvert1(0,0);
Point2D tvert2(0,0);
if (tmap)
{
TVFace & maxTVFace = maxMesh.mapFaces(1)[i]; // 1 == default map
S32 tidx0 = maxTVFace.getTVert(0);
S32 tidx1 = maxTVFace.getTVert(2); // switch the order to be CW
S32 tidx2 = maxTVFace.getTVert(1); // switch the order to be CW
if (mirror)
{
S32 tmp = tidx1;
tidx1 = tidx2;
tidx2 = tmp;
}
if (lastMatIdx != maxFace.getMatID())
{
// get texture transform for this material
uvTransform.IdentityMatrix();
Mtl * mtl = mMaxNode->GetMtl();
if( mtl )
{
if( mtl->ClassID() == Class_ID(MULTI_CLASS_ID,0) )
{
MultiMtl * multiMtl = (MultiMtl*)mtl;
if (multiMtl->NumSubMtls()>0)
mtl = multiMtl->GetSubMtl( maxFace.getMatID() % multiMtl->NumSubMtls());
}
if( mtl->ClassID() == Class_ID(DMTL_CLASS_ID,0) )
{
StdMat * stdMat = (StdMat*)mtl;
if (stdMat->GetSubTexmap(ID_DI)!=NULL && stdMat->MapEnabled(ID_DI) && stdMat->GetSubTexmap(ID_DI)->ClassID() == Class_ID(BMTEX_CLASS_ID,0))
{
BitmapTex * diffuse = (BitmapTex*)stdMat->GetSubTexmap(ID_DI);
diffuse->GetUVGen()->GetUVTransform(uvTransform);
}
}
}
lastMatIdx = maxFace.getMatID();
}
Point2 tv0 = maxMesh.mapVerts(1)[tidx0] * uvTransform;
Point2 tv1 = maxMesh.mapVerts(1)[tidx1] * uvTransform;
Point2 tv2 = maxMesh.mapVerts(1)[tidx2] * uvTransform;
tvert0.x(tv0.x);
tvert0.y(1.0f-tv0.y);
tvert1.x(tv1.x);
tvert1.y(1.0f-tv1.y);
tvert2.x(tv2.x);
tvert2.y(1.0f-tv2.y);
}
tsFace.type |= maxFace.getMatID(); // return max mat id...gets converted to ts mat id by shapemimic
// now add indices...this is easy right now...later we'll mess this up
mIndices.push_back(addVertex(vert0,tvert0,idx0,maxFace.smGroup));
mIndices.push_back(addVertex(vert1,tvert1,idx1,maxFace.smGroup));
mIndices.push_back(addVertex(vert2,tvert2,idx2,maxFace.smGroup));
}
if (delTri)
delete tri;
return Parent::lockMesh(time,objectOffset);
}
void MaxAppMesh::unlockMesh()
{
Parent::unlockMesh();
// no more cleanup...but if there were some to do, we'd do it here
}
void MaxAppMesh::getSkinData()
{
if (mSkinDataFetched)
return;
mSkinDataFetched = true;
ISkin * skin;
ISkinContextData * skinData;
findSkinData(mMaxNode,&skin,&skinData);
if (!skin || !skinData || !skin->GetNumBones())
return;
// This is a hack to avoid making the skin helper a ws modifier
// A hack, but needed because ws modifiers screw things up (add
// offsets for some reason).
SkinHelper::smTheOnlyOne = mMaxNode;
// add skin helper modifier...
::Object * obj = mMaxNode->GetObjectRef();
IDerivedObject * dobj = (IDerivedObject*)CreateDerivedObject(mMaxNode->GetObjectRef());
SkinHelper * skinHelper = (SkinHelper*)CreateInstance(GetSkinHelperDesc()->SuperClassID(),GetSkinHelperDesc()->ClassID());
dobj->AddModifier(skinHelper);
mMaxNode->SetObjectRef(dobj);
// get bones
S32 i,j, numBones = skin->GetNumBones();
for (i=0; i<numBones; i++)
{
mBones.push_back(new MaxAppNode(skin->GetBone(i)));
AppConfig::PrintDump(PDPass2,avar("Adding skin object from skin \"%s\" to bone \"%s\" (%i).\r\n",mMaxNode->GetName(),mBones[i]->getName(),i));
}
bool delTri;
TriObject * tri = NULL;
// get skin mesh
tri = getTriObject(mMaxNode,0,delTri);
// get vertex weights from alternate tv channels
S32 numPoints = tri->mesh.getNumVerts();
if (tri->mesh.getNumMaps()<2+((1+numBones)>>1))
{
AppConfig::SetExportError("Assertion failed on skin object");
return;
}
mWeights.resize(numBones);
for (i=0; i<mWeights.size(); i++)
{
mWeights[i] = new std::vector<F32>;
mWeights[i]->resize(numPoints);
for (j=0; j<numPoints; j++)
(*mWeights[i])[j]=0.0f;
}
for (j=0; j<numBones; j++)
{
AppConfig::PrintDump(-1,avar("Adding weights for bone %i (\"%s\")\r\n",j,mBones[j]->getName()));
std::vector<bool> gotWeight;
gotWeight.resize(tri->mesh.numVerts);
for (i=0; i<gotWeight.size(); i++)
gotWeight[i]=false;
for (i=0; i<tri->mesh.numFaces; i++)
{
S32 ch = 2+(j>>1);
Face & face = tri->mesh.faces[i];
TVFace & tvFace = tri->mesh.mapFaces(ch)[i];
for (S32 count=0; count<3; count++)
{
S32 idx = face.v[count];
if (!gotWeight[idx])
{
UVVert tv = tri->mesh.mapVerts(ch)[tvFace.t[count]];
F32 w = (j&1) ? tv.y : tv.x;
(*mWeights[j])[idx] = w;
if (w>0.01f)
AppConfig::PrintDump(-1,avar(" Vertex %i, weight %f\r\n",idx,w));
gotWeight[idx]=true;
}
}
}
}
if (delTri)
delete tri;
// done with helper...remove it now...
dobj->DeleteModifier(); // this'll be our skin helper
SkinHelper::smTheOnlyOne = NULL;
// following copied from AVCUtil.cpp...is needed to get rid of bar in modifier list
if (dobj->NumModifiers() == 0 && !dobj->TestAFlag(A_DERIVEDOBJ_DONTDELETE))
{
obj = dobj->GetObjRef();
obj->TransferReferences(dobj);
dobj->SetAFlag(A_LOCK_TARGET);
dobj->NotifyDependents(FOREVER,0,REFMSG_SUBANIM_STRUCTURE_CHANGED);
obj->NotifyDependents(FOREVER,0,REFMSG_SUBANIM_STRUCTURE_CHANGED);
dobj->ClearAFlag(A_LOCK_TARGET);
dobj->MaybeAutoDelete();
}
}
};

View File

@ -0,0 +1,59 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef DTSMAXAPPMESH_H_
#define DTSMAXAPPMESH_H_
#include "appMesh.h"
#include "appNode.h"
class INode;
namespace DTS
{
class MaxAppMesh : public AppMesh
{
typedef AppMesh Parent;
protected:
// Don't hold onto anything mesh-like in max, keep the node instead
INode * mMaxNode;
// Hold this in addition just for convenience of a few methods that
// use app node methods
AppNode * mAppNode;
void getSkinData();
public:
MaxAppMesh(INode * maxNode, AppNode * appNode);
const char * getName() { return mAppNode->getName(); }
bool getFloat(const char * propName, F32 & defaultVal) { return mAppNode->getFloat(propName,defaultVal); }
bool getInt(const char * propName, S32 & defaultVal) { return mAppNode->getInt(propName,defaultVal); }
bool getBool(const char * propName, bool & defaultVal) { return mAppNode->getBool(propName,defaultVal); }
Matrix<4,4,F32> getMeshTransform(const AppTime & time);
F32 getVisValue(const AppTime & time);
bool getMaterial(S32 matIdx, Material &);
S32 getNumBones();
AppNode * getBone(S32 idx);
F32 getWeight(S32 boneIdx, S32 vertIdx);
bool animatesMatFrame(const AppSequenceData & seqData);
bool animatesFrame(const AppSequenceData & seqData);
AppMeshLock lockMesh(const AppTime & time, const Matrix<4,4,F32> & objectOffset);
void unlockMesh();
};
};
#endif // DTSMAXAPPMESH_H_

View File

@ -0,0 +1,135 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "maxAppNode.h"
#include "maxAppMesh.h"
#include "DTSUtil.h"
#pragma pack(push,8)
#include <max.h>
#pragma pack(pop)
namespace DTS
{
MaxAppNode::MaxAppNode(INode * maxNode)
{
mMaxNode = maxNode;
}
void MaxAppNode::buildMeshList()
{
::ObjectState os = mMaxNode->EvalWorldState(0);
if (os.obj->CanConvertToType(triObjectClassID))
mMeshes.push_back(new MaxAppMesh(mMaxNode,this));
}
void MaxAppNode::buildChildList()
{
for (S32 i=0; i<mMaxNode->NumberOfChildren(); i++)
{
INode * child = mMaxNode->GetChildNode(i);
mChildNodes.push_back(new MaxAppNode(child));
}
}
bool MaxAppNode::isEqual(AppNode * node)
{
MaxAppNode * maxAppNode = dynamic_cast<MaxAppNode*>(node);
return maxAppNode && (maxAppNode->mMaxNode == mMaxNode);
}
Matrix<4,4,F32> MaxAppNode::getNodeTransform(const AppTime & time)
{
// Get the node marix then copy from max format into our matrix struct
// Note that this results in a transpose of the matrix...
Matrix3 nodeMat = mMaxNode->GetNodeTM( SecToTicks(time.getF32()) );
return convertFromMaxMatrix(nodeMat);
}
bool MaxAppNode::animatesTransform(const AppSequenceData & seqData)
{
Interval range(SecToTicks(seqData.startTime.getF32()),SecToTicks(seqData.endTime.getF32()));
Interval test = range;
// does this sequence animate the bounds node, if so, add ground transform
S32 midpoint = (range.Start() + range.End()) / 2;
mMaxNode->GetNodeTM(midpoint,&test);
return ( test.Start()!=range.Start() || test.End()!=range.End() );
}
const char * MaxAppNode::getName()
{
if (!mName)
mName = strnew(mMaxNode->GetName());
return mName;
}
const char * MaxAppNode::getParentName()
{
if (!mParentName)
mParentName = mMaxNode->GetParentNode() ? strnew(mMaxNode->GetParentNode()->GetName()) : strnew("ROOT");
return mParentName;
}
bool MaxAppNode::isParentRoot()
{
return (mMaxNode->GetParentNode()==NULL) || mMaxNode->GetParentNode()->IsRootNode();
}
bool MaxAppNode::getFloat(const char * propName, F32 & defaultVal)
{
// don't trust max not to touch value...
F32 val;
if (mMaxNode->GetUserPropFloat(propName,val))
{
defaultVal = val;
return true;
}
return false;
}
bool MaxAppNode::getInt(const char * propName, S32 & defaultVal)
{
// don't trust max not to touch value...
S32 val;
if (mMaxNode->GetUserPropInt(propName,val))
{
defaultVal = val;
return true;
}
return false;
}
bool MaxAppNode::getBool(const char * propName, bool & defaultVal)
{
// don't trust max not to touch value...
BOOL val;
if (mMaxNode->GetUserPropBool(propName,val))
{
defaultVal = val ? true : false;
return true;
}
return false;
}
Matrix<4,4,F32> convertFromMaxMatrix(Matrix3 & mat)
{
Matrix<4,4,F32> ret;
for (S32 i=0; i<4; i++)
{
Point3 row = mat.GetRow(i);
Vector<F32,4> col;
col.set(0,row.x);
col.set(1,row.y);
col.set(2,row.z);
col.set(3,i==3 ? 1 : 0);
ret.setCol(i,col);
}
return ret;
}
};

View File

@ -0,0 +1,47 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef DTSMAXAPPNODE_H_
#define DTSMAXAPPNODE_H_
#include "appNode.h"
class INode;
class Matrix3;
namespace DTS
{
class MaxAppNode : public AppNode
{
typedef AppNode Parent;
INode * mMaxNode;
void buildMeshList();
void buildChildList();
public:
MaxAppNode(INode * maxNode);
bool isEqual(AppNode *);
Matrix<4,4,F32> getNodeTransform(const AppTime & time);
bool animatesTransform(const AppSequenceData & seqData);
const char * getName();
const char * getParentName();
bool isParentRoot();
bool getFloat(const char * propName, F32 & defaultVal);
bool getInt(const char * propName, S32 & defaultVal);
bool getBool(const char * propName, bool & defaultVal);
};
extern Matrix<4,4,F32> convertFromMaxMatrix(Matrix3 & mat);
};
#endif // DTSMAXAPPNODE_H_

View File

@ -0,0 +1,58 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifdef _MSC_VER
#pragma warning(disable : 4786)
#endif
#include "maxSceneEnum.h"
#include "maxAppNode.h"
#pragma pack(push,8)
#include <max.h>
#include <ISTDPLUG.H>
#pragma pack(pop)
namespace DTS
{
class PrivateMaxEnum : public ITreeEnumProc
{
MaxSceneEnum * mMaxSceneEnum;
public:
PrivateMaxEnum(MaxSceneEnum * sceneEnum) { mMaxSceneEnum = sceneEnum; }
S32 callback( INode * node ) { return mMaxSceneEnum->callback(node); }
void enumScene(IScene * scene ) { scene->EnumTree(this); }
};
MaxSceneEnum::MaxSceneEnum(IScene * theScene)
{
mTheScene = theScene;
}
MaxSceneEnum::~MaxSceneEnum()
{
}
S32 MaxSceneEnum::callback(INode * node)
{
MaxAppNode * maxAppNode = new MaxAppNode(node);
if (!processNode(maxAppNode))
delete maxAppNode;
return TREE_CONTINUE;
}
void MaxSceneEnum::enumScene()
{
PrivateMaxEnum pme(this);
pme.enumScene(mTheScene);
}
}; // namespace DTS

View File

@ -0,0 +1,32 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef DTSMAXSCENEENUM_H_
#define DTSMAXSCENEENUM_H_
#include "appSceneEnum.h"
class INode;
class IScene;
namespace DTS
{
class MaxSceneEnum : public AppSceneEnum
{
protected:
IScene * mTheScene;
public:
MaxSceneEnum(IScene*);
~MaxSceneEnum();
S32 callback(INode * node);
void enumScene();
};
}; // namespace DTS
#endif // #define DTSMAXSCENEENUM_H_

View File

@ -0,0 +1,27 @@
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by max2dtsExporterPlus.rc
//
#define IDS_LIBDESCRIPTION 1
#define IDS_CATEGORY 2
#define IDS_CLASS_NAME 3
#define IDS_PARAMS 4
#define IDS_SPIN 5
#define IDS_SKINHELP_PARAMS 6
#define IDD_PANEL 101
#define IDC_CLOSEBUTTON 1000
#define IDC_DOSTUFF 1000
#define IDC_COLOR 1456
#define IDC_EDIT 1490
#define IDC_SPIN 1496
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@ -0,0 +1,248 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "skinHelper.h"
#include "appConfig.h"
#pragma pack(push,8)
#include <Max.h>
#include <decomp.h>
#include <dummy.h>
#include <ISkin.h>
#include <modstack.h>
#pragma pack(pop)
class SkinHelperClassDesc:public ClassDesc {
public:
int IsPublic() {return 1;}
void * Create(BOOL loading = FALSE) {return new SkinHelper();}
const TCHAR * ClassName() {return "Skin Helper";}
SClass_ID SuperClassID() {return OSM_CLASS_ID;}
Class_ID ClassID() {return SKINHELPER_CLASS_ID;}
const TCHAR* Category() {return "General";}
void ResetClassParams(BOOL) {}
};
static SkinHelperClassDesc SkinHelperDesc;
ClassDesc* GetSkinHelperDesc() {return &SkinHelperDesc;}
IObjParam * SkinHelper::ip = NULL;
INode * SkinHelper::smTheOnlyOne = NULL;
//--- SkinHelper -------------------------------------------------------
SkinHelper::SkinHelper()
{
}
SkinHelper::~SkinHelper()
{
}
S32 gShouldOnlyBeOne=0;
void SkinHelper::SetReference(S32 i, RefTargetHandle rtarg)
{
gShouldOnlyBeOne++;
if (gShouldOnlyBeOne != 1)
DTS::AppConfig::SetExportError("Assertion failed on skin object -- added two skin helper objects");
}
Interval SkinHelper::LocalValidity(TimeValue t)
{
// if being edited, return NEVER forces a cache to be built
// after previous modifier.
if (TestAFlag(A_MOD_BEING_EDITED))
return NEVER;
//TODO: Return the validity interval of the modifier
return NEVER;
}
RefTargetHandle SkinHelper::Clone(RemapDir& remap)
{
SkinHelper* newmod = new SkinHelper();
//TODO: Add the cloning code here
return(newmod);
}
// This sits in maxAppMesh.cpp
extern void findSkinData(INode * pNode, ISkin **skin, ISkinContextData ** skinData);
void SkinHelper::ModifyObject(TimeValue t, ModContext &mc, ObjectState * os, INode *pNode)
{
pNode = smTheOnlyOne;
if (!pNode)
return;
ISkin * skin;
ISkinContextData * skinData;
if (!pNode)
return;
findSkinData(pNode,&skin,&skinData);
if (!skin || !skinData)
return;
TriObject * triObj = (TriObject*)os->obj->ConvertToType(0,Class_ID(TRIOBJ_CLASS_ID,0));
if (triObj!=os->obj)
delete triObj;
else
modifyTriObject(triObj,skin,skinData);
PatchObject * patchObj = (PatchObject*)os->obj->ConvertToType(0,Class_ID(PATCHOBJ_CLASS_ID,0));
if (patchObj!=os->obj)
delete patchObj;
else
modifyPatchObject(patchObj,skin,skinData);
}
void SkinHelper::modifyTriObject(TriObject * triObj, ISkin * skin, ISkinContextData * skinData)
{
S32 numBones = skin->GetNumBones();
Mesh & maxMesh = triObj->mesh;
S32 numVerts = maxMesh.getNumVerts();
S32 numTVerts = maxMesh.getNumMapVerts(1);
if (numVerts!=skinData->GetNumPoints())
return;
S32 numChannels = 2+((numBones+1)>>1);
UVVert tv(0,0,0);
S32 i,j;
for (i=2; i<numChannels; i++)
{
maxMesh.setMapSupport(i,true);
maxMesh.setNumMapVerts(i,numVerts);
for (j=0; j<numVerts; j++)
maxMesh.setMapVert(i,j,tv);
maxMesh.setNumMapFaces(i,maxMesh.getNumFaces());
// copy map faces from the first channel
for (j=0; j<maxMesh.getNumFaces(); j++)
{
Face & face = maxMesh.faces[j];
TVFace & tvFace = maxMesh.mapFaces(i)[j];
tvFace.t[0] = face.v[0];
tvFace.t[1] = face.v[1];
tvFace.t[2] = face.v[2];
}
}
for (S32 v=0; v<numVerts; v++)
{
for (i=0; i<skinData->GetNumAssignedBones(v); i++)
{
S32 bone = skinData->GetAssignedBone(v,i);
F32 w = skinData->GetBoneWeight(v,i);
UVVert tv = maxMesh.mapVerts(2+(bone>>1))[v];
if (bone&1)
tv.y = w;
else
tv.x = w;
maxMesh.setMapVert(2+(bone>>1),v,tv);
}
}
}
void SkinHelper::modifyPatchObject(PatchObject * patchObj, ISkin * skin, ISkinContextData * skinData)
{
S32 numBones = skin->GetNumBones();
S32 numPoints = skinData->GetNumPoints();
S32 i;
PatchMesh & maxMesh = patchObj->patch;
S32 numVerts = maxMesh.getNumVerts();
if (numVerts>numPoints)
// points should be more than verts...first set of points are the verts, the rest are control verts
// we don't do anything with those weights...it limits the surface deformations that can take
// place, but it's all we can do...
return;
if (!numBones)
return;
S32 numChannels = 2+((numBones+1)>>1);
S32 numTVerts = maxMesh.getNumMapVerts(1);
UVVert tv(0,0,0);
maxMesh.setNumMaps(numChannels);
for (i=2; i<numChannels; i++)
{
// prepare each channel...
S32 j;
maxMesh.setNumMapVerts(i,numVerts);
for (j=0; j<numVerts; j++)
maxMesh.getMapVert(i,j) = tv;
// set up tv patch faces
maxMesh.setNumMapPatches(i,maxMesh.getNumPatches());
for (j=0; j<maxMesh.getNumPatches(); j++)
{
Patch & patch = maxMesh.patches[j];
TVPatch & tvPatch = maxMesh.getMapPatch(i,j);
tvPatch.tv[0] = patch.v[0];
tvPatch.tv[1] = patch.v[1];
tvPatch.tv[2] = patch.v[2];
tvPatch.tv[3] = patch.v[3];
}
}
for (S32 v=0; v<numVerts; v++)
{
for (i=0; i<skinData->GetNumAssignedBones(v); i++)
{
S32 bone = skinData->GetAssignedBone(v,i);
F32 w = skinData->GetBoneWeight(v,i);
S32 channel = 2 + (bone>>1);
UVVert & tv = maxMesh.getMapVert(channel,v);
if (bone&1)
tv.y = w;
else
tv.x = w;
}
}
}
void SkinHelper::BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev )
{
this->ip = ip;
}
void SkinHelper::EndEditParams( IObjParam *ip, ULONG flags,Animatable *next)
{
this->ip = NULL;
}
//From ReferenceMaker
RefResult SkinHelper::NotifyRefChanged(
Interval changeInt, RefTargetHandle hTarget,
PartID& partID, RefMessage message)
{
//TODO: Add code to handle the various reference changed messages
return REF_SUCCEED;
}
//From Object
BOOL SkinHelper::HasUVW()
{
//TODO: Return whether the object has UVW coordinates or not
return TRUE;
}
void SkinHelper::SetGenUVW(BOOL sw)
{
if (sw==HasUVW()) return;
//TODO: Set the plugin internal value to sw
}
IOResult SkinHelper::Load(ILoad *iload)
{
//TODO: Add code to allow plugin to load its data
return IO_OK;
}
IOResult SkinHelper::Save(ISave *isave)
{
//TODO: Add code to allow plugin to save its data
return IO_OK;
}

View File

@ -0,0 +1,86 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef __SKINHELPER__H
#define __SKINHELPER__H
#pragma pack(push,8)
#include <Max.h>
#include <istdplug.h>
#include <iparamb2.h>
#include <iparamm2.h>
#include <ISkin.h>
#pragma pack(pop)
#include "DTSPlusTypes.h"
// Note: this skinhelper is same as skin helper in original max2dts
// But we give it a different id so that it can co-exist with the old one.
//#define SKINHELPER_CLASS_ID Class_ID(0x20dfdba0, 0x60646cb4)
#define SKINHELPER_CLASS_ID Class_ID(0x449d28a3, 0x4dd3242b)
extern ClassDesc* GetSkinHelperDesc();
extern TCHAR *GetString(S32);
class SkinHelper : public OSModifier
{
public:
static IObjParam *ip; //Access to the interface
// From Animatable
TCHAR *GetObjectName() { return "SkinHelper"; }
//From Modifier
ChannelMask ChannelsUsed() { return TEXMAP_CHANNEL; }
ChannelMask ChannelsChanged() { return TEXMAP_CHANNEL; }
void ModifyObject(TimeValue t, ModContext &mc, ObjectState *os, INode *node);
void modifyTriObject(TriObject *, ISkin *, ISkinContextData *);
void modifyPatchObject(PatchObject *, ISkin *, ISkinContextData *);
Class_ID InputType() {return defObjectClassID;}
Interval LocalValidity(TimeValue t);
// From BaseObject
BOOL ChangeTopology() {return FALSE;}
CreateMouseCallBack* GetCreateMouseCallBack() {return NULL;}
void BeginEditParams(IObjParam *ip, ULONG flags,Animatable *prev);
void EndEditParams(IObjParam *ip, ULONG flags,Animatable *next);
Interval GetValidity(TimeValue t);
// Automatic texture support
BOOL HasUVW();
void SetGenUVW(BOOL sw);
// Loading/Saving
IOResult Load(ILoad *iload);
IOResult Save(ISave *isave);
//From Animatable
Class_ID ClassID() {return SKINHELPER_CLASS_ID;}
SClass_ID SuperClassID() { return OSM_CLASS_ID; }
void GetClassName(TSTR& s) {s = "SkinHelper"; }
RefTargetHandle Clone( RemapDir &remap );
RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget,
PartID& partID, RefMessage message);
int NumSubs() { return 0; }
TSTR SubAnimName(int i) { return _T(""); }
Animatable* SubAnim(int i) { return NULL; }
int NumRefs() { return 0; }
RefTargetHandle GetReference(int i) { return NULL; }
void SetReference(int i, RefTargetHandle rtarg);
void DeleteThis() { delete this; }
//Constructor/Destructor
SkinHelper();
~SkinHelper();
static INode * smTheOnlyOne;
};
#endif // __SKINHELPER__H