3D Studio Max Custom Skinning Export Plugin and Model Loader
Material ID export plugin for 3D Studio Max
Material ID/ Vertex Color importer/ loader
I was having trouble getting the 3D human model to load into our game engine after needing both the physique modifier and animation. Skinning will not export- the skeleton and skin export separately, and only the skeleton was animated. I modified Marco Tombesi's MRC format to create a custom file type that would bind the skin to the skeleton in animation, and then I needed to write a MAXscript export plugin to access material IDs, since in this format, there is no way to access color.
I used MAXscript to write a plugin
to export Material IDs. I access the Material IDs for the selected mesh, traverse
the nodes, and then store the data out to a simple text file on the desktop.
The code can
be loaded and run in 3D Studio Max by going to the "MaxScript" menu,
and selecting "run script".
The code I wrote for this script is:
_____________________________________________________________________
tmesh = snapshotAsMesh selection[1]
f = createFile "C:\\Documents and Settings\\TEMP.CSDOM.000\\Desktop\\MatID.txt"
num_faces = tmesh.numfaces
for i in 1 to num_faces do
(
tmp_col = getFaceMatId tmesh i
format "%\n" tmp_col to:f
)
close f
_____________________________________________________________________
The code which I wrote to facilitate the drawing and loading of materials/colors
is found in MRCLoader.cpp,
MRC.cpp, ModelMRC.cpp, Skin.cpp, and MRC.h in our game .zip file. The draw
function in ModelMRC.cpp now drawsmaterial IDs. The text file of Material
IDs exported by the plugin is read in in MRCLoader.cpp in the
function LoadMatID. This consists of the 4 material IDs contained in my model,
signifying the different
colors for the skin, hair, eyes, and clothing. In the constructor in Skin.cpp,
the vertex colors associated
with the material IDs per face are computed, and then parsed into a vertex
array. Each vertex is then
assigned an actual color, a peachy flesh tone for the skin, black for the
bikini and boots, light blonde
for the hair, and white for the eyes.
_______________________________________________________________
SAMPLE LOADER CODE (MRCLoader.cpp):
static bool LoadSkinData (FILE* stream, MRC_t* MRCdata);
static bool LoadMeshData (FILE* stream, MRC_t* MRCdata);
static bool LoadBoneData(FILE* stream, bone_t* Bone, int keyCnt);
static void LoadMatID (char* filename, MRC_t* MRCdata);
//===================================================================================================
//===================================================================================================
MRC_t* /*APIENTRY*/ MRCLoad (FILE* MRCfile)
{
bool ok=false;
//Version check
rewind(MRCfile);
unsigned long version;
fread(&version,sizeof(unsigned long),1,MRCfile);
if(version != mrc_version)
{
fclose(MRCfile);
return 0;
}
//data allocation
MRC_t* MRCdata = new MRC_t;
//data load
if (LoadSkinData(MRCfile, MRCdata)) ok=true; //correct read done
//*************************
//load material IDs
LoadMatID ("MatID.txt", MRCdata);
if (ok) return MRCdata;
else {//TODO: proper deallocation
delete MRCdata;
return NULL;
}
}
//===================================================================================================
//===================================================================================================
//=======================================================================================
static void LoadMatID (char* filename, MRC_t* MRCdata){
//MRCdata->face_cnt = mHdr.faceCnt;
ifstream fin;
fin.open(filename);
MRCdata->colors = new int[MRCdata->face_cnt];
for(int i = 0; i < MRCdata->face_cnt; i++){
fin >> MRCdata->colors[i];
}
}
//=======================================================================================
static bool LoadSkinData (FILE* stream, MRC_t* MRCdata) {
//object start
long startObj=ftell(stream);
//load mesh data
if (!LoadMeshData (stream,MRCdata))
return false;
MRCobject_hdr oHdr;
fread(&oHdr,sizeof(MRCobject_hdr),1,stream);
//animation time
MRCdata->time_span = oHdr.animLast;
//animation samples count
MRCdata->key_cnt = oHdr.keyCnt;
//bone count
MRCdata->bone_cnt = oHdr.boneCnt;
//not only mesh
if (oHdr.boneCnt)
{
//positioning to bone start
fseek(stream, oHdr.boneOfs, SEEK_SET);
MRCdata->Bones = new bone_t[MRCdata->bone_cnt];
int keyCnt = oHdr.keyCnt;
for(int i = 0; i < MRCdata->bone_cnt; i++)
{//read bone data
if (! LoadBoneData(stream, &(MRCdata->Bones[i]), keyCnt) )
return false;
}
}
//return to object start
fseek(stream, startObj, SEEK_SET);
return true;
}
//=======================================================================================
static bool LoadMeshData (FILE* stream, MRC_t* MRCdata) {
//object start
long startObj=ftell(stream);
//object header data
MRCobject_hdr oHdr;
fread(&oHdr,sizeof(MRCobject_hdr),1,stream);
MRCmesh_hdr mHdr;
fread(&mHdr,sizeof(MRCmesh_hdr),1,stream);
//mesh sizes
MRCdata->vert_cnt = mHdr.vertCnt;
MRCdata->norm_cnt = mHdr.normCnt;
MRCdata->face_cnt = mHdr.faceCnt;
//loading mesh data
MRCdata->verts = new vertex_t[MRCdata->vert_cnt];
fseek(stream, mHdr.vertOfs, SEEK_SET);
fread(MRCdata->verts, sizeof(vertex_t), MRCdata->vert_cnt, stream);
MRCdata->norms = new vertex_t[ MRCdata->norm_cnt];
fseek(stream, mHdr.normOfs, SEEK_SET);
fread(MRCdata->norms , sizeof(vertex_t), MRCdata->norm_cnt, stream);
MRCdata->faces = new msh_face_t[MRCdata->face_cnt];
fseek(stream, mHdr.faceOfs, SEEK_SET);
fread(MRCdata->faces , sizeof(msh_face_t), MRCdata->face_cnt, stream);
//return to object start
fseek(stream, startObj, SEEK_SET);
return true;
}
//=======================================================================================
static bool LoadBoneData(FILE* stream, bone_t* Bone, int keyCnt)
{
MRCbone_hdr bHdr;
///bone structure data
fread(&bHdr,sizeof(MRCbone_hdr),1,stream);
memcpy(Bone->inverse_bone_TM, bHdr.inverseOrientationTM, sizeof(float[16]));
Bone->parent_idx = bHdr.parentIdx; // -1 if is root
Bone->child_cnt = bHdr.childCnt;
if (!Bone->child_cnt) Bone->childs=NULL; // no childs if is leaf
else {
Bone->childs = new int[Bone->child_cnt];
fread(Bone->childs, sizeof(int), Bone->child_cnt, stream);
}
//bone vertex weight data:
Bone->vert_cnt = bHdr.vertexCnt;
if (!Bone->vert_cnt) Bone->vert_weight=NULL;
else {
fseek(stream,bHdr.boneWeightsOfs,SEEK_SET);
Bone->vert_weight = new vert_weight_t[Bone->vert_cnt];
fread(Bone->vert_weight, sizeof(vert_weight_t), Bone->vert_cnt, stream);
}
//bone animation data:
if (!keyCnt) Bone->key = NULL;
else {
fseek(stream, bHdr.boneKeysOfs,SEEK_SET);
Bone->key = new key_t[keyCnt];
fread(Bone->key, sizeof(key_t), keyCnt, stream);
}
return true;
}