Creating a Milkshape Plug-in
(Exporter)
by
Ben Kenwright
Milkshape is a simple but powerful 3D modelling program which is available
for about $25, or you can download it and use it during its trial period.
Its biggest selling point though, is its large number of file formats that it
can export to, and of course work with. From Quake2 (.md2), Quake3(.md3) ,Lightwave,
.3ds, .x etc and many many more. Plus its simple to use, and I found that
learning to model game characters in the beginning easier with Milkshape, before
moving on to much more powerful packages such as 3d studio max.
But less of my selling you this package! What where going to do, is see
about writing a plug-in for Milkshape so you can export the 3d data to a file in
a form we want. We'll only do a basic exporter here, and show you all the
data and how to get a hold of it...and export it in a raw format, which we can
open in notepad or something and take a looksy at the data.
For this tutorial I'll use the free version of Visual C++ compiler, which you
can download from the
Microsoft website for free. Of course the principles and how's and
why's will still apply to your own custom compiler...so once you understand how
it works...you should have no problems.
Here is the link for those who want to see where to get the MS Visual C++
Compiler for free:
[http://msdn.microsoft.com/visualc/vctoolkit2003/]
Now we'll do a couple of versions of this code... our first plug-in, won't
require any additional libraries or anything like that...you'll be able to build
a simple Milkshape plug-in using only the basic of knowledge that I'll give you
here....then we'll move on to mention the Milkshape SDK and some of the useful
library functions you can use.
First, let me tell you what the plug-in is...and where you put it to use it
with Milkshape. What we build is a 'dll'...not a exe okay...and we put
this dll in the Milkshape directory, where-ever that might be. On my
computer its at: 'C:\Program Files\MilkShape 3D\'. So if you take a looksy
in that folder, you'll see there are lots of dll's in there... here is a list of
some:
ms3DSExporter.dll
ms3DSImporter.dll
ms3dtExporter.dll
msABCExporter.dll
msABCImporter.dll
msAlignment.dll
msASCIIExporter.dll
msASCIIImporter.dll
...etc etc etc
There's a lot of dll's in there...but the majority of them, as you'll notice
start with 'ms'. Which is one of the things you have to know...as our
plug-in dll has to start with 'ms' also for it to work with Milkshape...the rest
of the name doesn't really matter...only that the first two characters are m and
s.
Lets create a simple piece of code, and compile it to create a dll to get us
going:
Basic dll Code
(Download Source Code) - testdll.cpp |
// Simple test program to test creation of a dll using the command
prompt
// and visual studio
// auth: bkenwright@xbdev.net
// HOW TO COMPILE:
// c:> cl /LD testdll.cpp
extern
"C"
__declspec(dllexport) void __cdecl
test();
void __cdecl test ()
{
}
|
If you compile the above code, you should get a nice dll :) Just to get
us set-up and ready to go.
WARNING! |
When you compile the dll, we will be using the '__stdcall'
calling convention, due to use working with classes. Now we need to
force our compiler/linker to name our exported dll functions to our name
that we need. As by default, if we use '__stdcall' it will decorate
our function names like this:
"_MyFunction@8'. This is the standard way without a def file.
The if we include a 'def' file...which is basically a txt file, and we pass
the name of it as a parameter when we compile, then we can force the
exported function names to be what we want. For example this would be
an 'def' file for our above example code (testdll.def) :
LIBRARY "testdll"
EXPORTS
test
So we could change or command line to: cl /LD
testdll.cpp testdll.def |
We'll call our first exporter 'msRawA'...and so we'll create the files with
its similar nameing 'msRawA.cpp' and 'msRawA.def'
These are the only two files we need to create our exporter...simple eh?
Well of course you've not seen inside them yet..hehe...but there not bad.
The Milkshape workings are pretty straight forward and easy to understand once
you've seen them.
The only function that we need to export is called 'CreatePlugIn()'
Thats it! No other functions...so our .def file is pretty straight forward
and looks like this:
Here is the code for a very very simple exporter...all it does when you run
it, is write a file in the C directory which contains how many mesh's you've got
open in 3D milkshape. Its the start of our journey.
code:
msRawA.cpp (Download) |
// Milkshape Exporter
// bkenwright@xbdev.net
// cl /LD msRawA.cpp msRawA.def
// Few notes:
// The first two letters of our exporter dll, must be 'ms'...so for
example you can have
// msMyCool.dll, myRawXBDEV.dll etc.
// You usually need a 'def' (definition) file for visual studio, as
it uses __stdcall, so
// it decorates the exported function name...by using a def file,
you stop this!
#include <string.h>
// Needed for strcpy(..)
#include <stdio.h>
// for fopen and fprint etc
// msRawXBDEV.def
/*
LIBRARY "msRawA"
EXPORTS
CreatePlugIn
*/
//
----------------------------------------------------------------------------------
// A simplified version of the msModel structure that milkshape uses
- the
// void* pointers usually point to the sub structures that contain
the data.
/* msModel */
typedef struct msModel
{
int nNumMeshes;
int nNumAllocedMeshes;
void* pMeshes;
int nNumMaterials;
void* pMaterials;
int nNumBones;
int nNumAllocedBones;
void* pBones;
int nFrame;
int nTotalFrames;
float* Position;
float* Rotation;
} msModel;
//
----------------------------------------------------------------------------------
class MyPlugin
{
public:
MyPlugin(){ strcpy(szTitle,
"www.xbdev.net - Custom Raw Exporter");
}
virtual ~MyPlugin() { }
// Do we want an exporter or a tool?..hmmm, well we decide that here
virtual
int GetType()
{
return 2;
// What type of plug-in this is '2' indicates an export plugin
}
virtual const char *GetTitle(){ return
szTitle; }
virtual int Execute(msModel *);
private:
char szTitle[64];
};
// Our work horse file
int MyPlugin::Execute( msModel * pModel )
{
if (!pModel) return -1;
// Tiny bit of error checking :)
// export our data
// Default File Location is C drive, with a default name of "raw.txt"
char
szFile[] =
"C:\\raw.txt";
FILE *file = fopen (szFile,
"wt");
if (!file)
return -1;
fprintf(file,
"nNumMeshes %d\n", pModel->nNumMeshes);
fclose(file);
// We should destroy pModel here as well, but we won't worry about that yet
return
0;
}
MyPlugin *CreatePlugIn()
{
return new MyPlugin;
} |
The code is relatively simple but it shows the main functions that are needed
to construct an exporter:
CreatePlugin()
MyPlugin() constructor
virtual MyPlugin() descructor
virtual GetType()
virtual GetTitle()
virtual Execute()
I've added the word virtual to some of the functions, as they must be
declared virtual - this is due to the fact that we usually have a class defining
these functions...soft of a defintion class only containing these and you would
inherite the class from this one and implement your own code.
Lets look at what each of these functions does. The CreatePlugin()
class basically creates an instance of our class and returns a pointer of it to
Milkshape. When an instance of the class is created, the constructor is
called...and inside our constructor we've simply copied a string into a char
buffer. This char buffer, called szTitle contains the name that will be
displayed in the drop down menu in Milkshape exporter options. Then
GetType() is used to determine what type of plug-in this is - as we are only
interested in a file exporter we return 2...which is the number for a
exporter...simple as that. Next GetTitle() gets called when milkshape wants to
know the title of our exporter - here we just return a pointer to our string,
which containes "www.xbdev.net
- Custom Raw Exporter".
Finally, the 'Execute()' function is where all the hard work code would go.
This is called when you finally click on you exporter in the drop down menu in
Milkshape - it gets passed a pointer to a 'msModel' structure, which is
basically a structure of pointers and data values...representing all our 3D
data, which we can pick and choose from and export as we need.
So compile this puppy, and copy the dll to your milkshape folder....then run
milkshape. If it crashes, something went wrong..hehe...but it worked for
me, so it should have worked for you as well.
Well we know its working...and if you click on the option...nothing much will
happen...no pops...or anything. But it will create an output for us :)
If you go to your C drive, you'll find a folder call raw.txt. You can open
it with notepad and view the contents. I doodles a couple of spheres, so I
get:
nNumMeshes 2
Which shows I had two meshes. Its not much...but we are given a pointer
to the data, which gives us the ability to write a full custom exporter - either
to export to a pre-done file format such as Quake(.md2) or Directx(.x)...or even
POG (.pog)...hehe...which is our own custom format...I came up with the name
pog..hehe...you can do your own exporter and call it what you want. Many
gaming companies and people who produce demo's these days, write there own
custom formats and exporters, creating 3d data files that meet there needs.
Add more power!...MORE POWER!!!
Well I didn't want to give you to much code in one go...as the previous code
doens't really give us much power does it...as we don't know the full extents of
our msModel...we need to know how its layed out. And I didn't want to
bring in class inheritence, incase it made things complicated. So now we
know which functions are important and how it ticks...so we can add in the extra
code which will make our code export the actual model data...OOooo...this is
exciting...well I think it is :)
Exporter code:
Download Source (msRawB.cpp) |
// Milkshape Exporter
// bkenwright@xbdev.net
// cl /LD msRawB.cpp msRawB.def
// Few notes:
// The first two letters of our exporter dll, must be 'ms'...so for
example
// you can have msMyCool.dll, myRawXBDEV.dll etc.
// You usually need a 'def' (definition) file for visual studio, as
it uses
// __stdcall, so it decorates the exported function name...by using
// a def file, you stop this!
#include <string.h>
// Needed for strcpy(..)
#include <stdio.h>
// for fopen and fprint etc
// msRawB.def
/*
LIBRARY "msRawB"
EXPORTS
CreatePlugIn
*/
//--------------------------------------------------------------------------
/**********************************************************************
*
* Constants
*
**********************************************************************/
#define MS_MAX_NAME 32
#define MS_MAX_PATH 256
/**********************************************************************
*
* Types
*
**********************************************************************/
#pragma pack(1)
#ifndef byte
typedef unsigned char byte;
#endif
/* byte */
#ifndef word
typedef unsigned short word;
#endif
/* word */
typedef float msVec4[4];
typedef float msVec3[3];
typedef float msVec2[2];
/* msFlag */
typedef enum {
eSelected = 1, eSelected2 = 2, eHidden = 4, eDirty = 8,
eAveraged = 16, eUnused = 32
} msFlag;
/* msVertex */
typedef struct msVertex
{
byte nFlags;
msVec3 Vertex;
float u, v;
char nBoneIndex;
} msVertex;
/* msTriangle */
typedef struct
{
word nFlags;
word nVertexIndices[3];
word nNormalIndices[3];
msVec3
Normal;
byte nSmoothingGroup;
} msTriangle;
/* msMesh */
typedef struct msMesh
{
byte nFlags;
char szName[MS_MAX_NAME];
char nMaterialIndex;
word nNumVertices;
word nNumAllocedVertices;
msVertex* pVertices;
word nNumNormals;
word nNumAllocedNormals;
msVec3* pNormals;
word nNumTriangles;
word nNumAllocedTriangles;
msTriangle* pTriangles;
} msMesh;
/* msMaterial */
typedef struct msMaterial
{
int nFlags;
char szName[MS_MAX_NAME];
msVec4 Ambient;
msVec4 Diffuse;
msVec4 Specular;
msVec4 Emissive;
float fShininess;
float fTransparency;
char szDiffuseTexture[MS_MAX_PATH];
char szAlphaTexture[MS_MAX_PATH];
int nName;
} msMaterial;
/* msPositionKey */
typedef struct msPositionKey
{
float fTime;
msVec3 Position;
} msPositionKey;
/* msRotationKey */
typedef struct msRotationKey
{
float fTime;
msVec3 Rotation;
} msRotationKey;
/* msBone */
typedef struct msBone
{
int nFlags;
char szName[MS_MAX_NAME];
char szParentName[MS_MAX_NAME];
msVec3 Position;
msVec3 Rotation;
int nNumPositionKeys;
int nNumAllocedPositionKeys;
msPositionKey* pPositionKeys;
int nNumRotationKeys;
int nNumAllocedRotationKeys;
msRotationKey* pRotationKeys;
} msBone;
/* msModel */
typedef struct msModel
{
int nNumMeshes;
int nNumAllocedMeshes;
msMesh* pMeshes;
int nNumMaterials;
int nNumAllocedMaterials;
msMaterial* pMaterials;
int nNumBones;
int nNumAllocedBones;
msBone* pBones;
int nFrame;
int nTotalFrames;
msVec3 Position;
msVec3 Rotation;
} msModel;
#pragma pack()
//
----------------------------------------------------------------------------
class cMsPlugIn
{
public:
enum
{
eTypeImport = 1,
eTypeExport = 2,
eTypeTool = 3,
eTypeEdit = 4,
eTypeVertex = 5,
eTypeFace = 6,
eTypeAnimate = 7
};
public:
cMsPlugIn () {};
virtual ~cMsPlugIn () {};
public:
virtual int GetType () = 0;
virtual const char * GetTitle () = 0;
virtual int Execute (msModel* pModel)
= 0;
};
//
-----------------------------------------------------------------------
class MyPlugin : public cMsPlugIn
{
public:
MyPlugin()
{
strcpy(szTitle,
"www.xbdev.net - Custom Raw Exporter");
}
virtual ~MyPlugin() { }
// Do we want an exporter or a tool?..hmmm, well we decide that here
int
GetType()
{
return cMsPlugIn::eTypeExport;
}
const char *GetTitle()
{
return szTitle;
}
int Execute(msModel *);
private:
char szTitle[64];
};
int MyPlugin::Execute( msModel * pModel )
{
if (!pModel) return -1;
// Export our data from here on
// Default File Location is C drive, with a default name of "raw.txt"
char
szFile[] =
"C:\\raw.txt";
FILE *file = fopen (szFile,
"wt");
if (!file)
return -1;
int nNumMeshes = pModel->nNumMeshes;
fprintf(file,
"nNumMeshes %d\n", nNumMeshes);
msMesh * pMeshes = pModel->pMeshes;
for(int i=0; i<nNumMeshes; i++)
{
fprintf(file,
"Mesh: %d\n", i);
fprintf(file,
"szName: %s\n", pMeshes[i].szName);
fprintf(file,
"nNumVertices: %d\n",pMeshes[i].nNumVertices);
for(int j=0; j< pMeshes[i].nNumVertices; j++)
{
msVertex * pVertices = pMeshes[i].pVertices;
fprintf(file,
"x:%f, y:%f, z:%f\n",
pVertices->Vertex[0],
pVertices->Vertex[1],
pVertices->Vertex[2] );
}//End
inner for loop
}//End
outer for loop
fclose(file);
// We should destroy pModel here as well
return
0;
}
cMsPlugIn *CreatePlugIn()
{
return new MyPlugin;
}
|
The above code has added the full definition of the various Milkshape msModel
structure - so you can now get access to all the model data and export it to a
file. For example, I created a couple of cubes in Milkshape and used our
exporter, and below is wha the output looks like:
Output File: raw.txt |
nNumMeshes 2
Mesh:
0
szName: Box01
nNumVertices: 20
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
x:-3.750000, y:11.250000, z:8.000000
Mesh:
1
szName: Box02
nNumVertices: 20
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000
x:-2.875000, y:-12.000000, z:-14.250000 |
What we have done, is export all the vertex data, and write it to file.
The code that does this is in the 'Execute(..)' method of our class. We've
also inherited from a model class, which defines the functions for us.
This is moving towards the Milkshape SDK, which has a class defined liket his
and also the model structures....you then only need to include the header file
and your away. Write a simple exporter has never been easier.
ERRORS |
I've not mentioned it yet. But the packing of the
structures has to be 1! If you look carefully at our definitions
above, I've used '#pragma pack(1)' at the start of our structures and '#pragma
pack()' at the end. Which tells visual studio to pack our data to an
alignement of 1 byte...so in essence there'll be no padding bytes, which is
what we want. I found though, in the SDK that it doesn't do this, so when
I was trying to export data, it would produce invalid values....only by
adding these couple of lines did it fix it! So now you know :) |
Exporter using Milkshape SDK
You can also download the Milkshape SDK, which is just a couple of .h files
and a .lib file, which you can include in your code...saves you re-inventing the
wheel as they say. The .lib defines some pre-written functions which you
can use to access the various data types, instead of searching through the
pointers for meshes with certain names etc...you can call one of the library
functions to search for you.
Here is a simple raw exporter that I wrote and use for various demo's when I
just need some basic test data:
Code: msRawXBDEV.cpp
(Download) |
// Milkshape Exporter
// bkenwright@xbdev.net
// cl /LD msRawXBDEV.cpp msRawXBDEV.def
// msRawXBDEV.def
/*
LIBRARY "msRawXBDEV"
EXPORTS
CreatePlugIn
*/
// Few notes:
// The first two letters of our exporter dll, must be 'ms'...so for example
you can have
// msMyCool.dll, myRawXBDEV.dll etc.
// You usually need a 'def' (definition) file for visual studio, as it uses
__stdcall, so
// it decorates the exported function name...by using a def file, you stop
this!
#include "msLib\\msPlugIn.h"
#include <string.h>
// Needed for strcpy(..)
#include <stdio.h>
// for fopen and fprint etc
// Well I get lazy, having to write my own code to determine where a certain
model
// is, or to be able to dump all the data...so there is a small library
which
// we can use to do it...which is part of the milkshape sdk...we don't need
it, but
// it doesn't harm to know how to use it :)
#include "msLib\\msLib.h"
#pragma comment(lib, "msLib\\lib\\msModelLib.lib")
//
----------------------------------------------------------------------------------------------
class
MyPlugin : public cMsPlugIn
{
public:
MyPlugin()
{
strcpy(szTitle,
"www.xbdev.net - Custom Raw Exporter");
}
virtual ~MyPlugin() { }
// Do we want an exporter or a
tool?..hmmm, well we decide that here
int GetType()
{
//return cMsPlugIn::eTypeTool;
return cMsPlugIn::eTypeExport;
}
const char *GetTitle()
{
return szTitle;
}
int Execute(msModel *);
private:
char szTitle[64];
};
// Very naughty problem that I encountered! The library's for the default
// milkshape dotn' account for packing alignment :(
#pragma pack(1)
typedef
struct msVertexX
{
byte nFlags;
msVec3 Vertex;
float u, v;
char nBoneIndex;
} msVertexX;
#pragma pack()
int
MyPlugin::Execute( msModel * pModel )
{
if (!pModel)
return -1;
if (msModel_GetMeshCount (pModel) == 0)
return 0;
// Export our data
// Default File Location is C
drive, with a default name of "raw.txt"
char szFile[] =
"C:\\raw.txt";
FILE *file = fopen (szFile,
"wt+");
if (!file)
return -1;
int i, j;
for (i = 0; i < msModel_GetMeshCount (pModel); i++)
{
msMesh *pMesh = msModel_GetMeshAt (pModel, i);
for (j = 0; j < msMesh_GetTriangleCount (pMesh); j++)
{
msTriangle *pTriangle = msMesh_GetTriangleAt (pMesh, j);
word nIndices[3];
msTriangle_GetVertexIndices (pTriangle, nIndices);
// We should really check
if each material has a material assigned to it
unsigned
int colour = 0xffffffff;
// Get the colour for our
triangle:
if(
msModel_GetMaterialCount(pModel) > 0 )
{
int
indx = msMesh_GetMaterialIndex (pMesh);
msMaterial* pMat =
msModel_GetMaterialAt(pModel, indx);
//typedef float
msVec4[4];
int
amber = (int)(255 * pMat->Diffuse[3]);
int
red = (int)(255 * pMat->Diffuse[0]);
int
green = (int)(255 * pMat->Diffuse[1]);
int
blue = (int)(255 * pMat->Diffuse[2]);
// 0xAARRGGBB
colour = amber << 24 |
red << 16 |
green << 8 |
blue;
}
msVertexX *pVertex;
pVertex = (msVertexX*)msMesh_GetVertexAt (pMesh, nIndices[0]);
fprintf (file,
"%f %f %f ", pVertex->Vertex[0], pVertex->Vertex[1],
pVertex->Vertex[2]);
pVertex = (msVertexX*)msMesh_GetVertexAt (pMesh, nIndices[1]);
fprintf (file,
"%f %f %f ", pVertex->Vertex[0], pVertex->Vertex[1],
pVertex->Vertex[2]);
pVertex = (msVertexX*)msMesh_GetVertexAt (pMesh, nIndices[2]);
fprintf (file,
"%f %f %f ", pVertex->Vertex[0], pVertex->Vertex[1],
pVertex->Vertex[2]);
fprintf (file,
"0x%.8X\n",
colour);
}
}
fclose (file);
// dont' forget to destroy the
model
msModel_Destroy (pModel);
return 0;
}
cMsPlugIn *CreatePlugIn()
{
return new MyPlugin;
}
|
Notice how much simpler it is, when we use the pre-made headers....but of
course, you now know how to write an exporter without the Milkshape SDK!...and
of course you could write better library's for yourself and of course build up
your own extras that you can just use to develop your plug-ins for Milkshape :)
Here is what my custom 'rawXBDEV' file output looks like for a simple cube:
File Output: raw.txt |
-3.500000 10.750000
-3.750000 -3.500000 3.750000 -3.750000 3.500000 10.750000 -3.750000
0xFFFFFFFF
-3.500000 3.750000
-3.750000 3.500000 3.750000 -3.750000 3.500000 10.750000 -3.750000
0xFFFFFFFF
3.500000 10.750000
-3.750000 3.500000 3.750000 -3.750000 3.500000 10.750000 -10.750000
0xFFFFFFFF
3.500000 3.750000
-3.750000 3.500000 3.750000 -10.750000 3.500000 10.750000 -10.750000
0xFFFFFFFF
3.500000 10.750000
-10.750000 3.500000 3.750000 -10.750000 -3.500000 10.750000 -10.750000
0xFFFFFFFF
3.500000 3.750000
-10.750000 -3.500000 3.750000 -10.750000 -3.500000 10.750000 -10.750000
0xFFFFFFFF
-3.500000 10.750000
-10.750000 -3.500000 3.750000 -10.750000 -3.500000 10.750000 -3.750000
0xFFFFFFFF
-3.500000 3.750000
-10.750000 -3.500000 3.750000 -3.750000 -3.500000 10.750000 -3.750000
0xFFFFFFFF
-3.500000 10.750000
-10.750000 -3.500000 10.750000 -3.750000 3.500000 10.750000 -10.750000
0xFFFFFFFF
-3.500000 10.750000
-3.750000 3.500000 10.750000 -3.750000 3.500000 10.750000 -10.750000
0xFFFFFFFF
-3.500000 3.750000
-3.750000 -3.500000 3.750000 -10.750000 3.500000 3.750000 -3.750000
0xFFFFFFFF
-3.500000 3.750000
-10.750000 3.500000 3.750000 -10.750000 3.500000 3.750000 -3.750000
0xFFFFFFFF |
Where have <triangleA> <triangleB> <triangleC> <Colour> for the data on each
line....where each triangle is made up of 3 points. Its probably not to
easy to see, due to the html layout of this page...but there are spaces between
the data....so don't go thinking its long lines of numbers. Its not the most
efficient way to save data to file, where we are saving each triangle and not a
common list of vertices and references to them...but hey, you can now write your
own cool exporter for you demos :)
I've gave you the power of 'how to' now....with this knowledge you can go
forward and develop more plugs.
A note on writing an importer - with an importer, you are passed the pModel,
and you are to add data to this model...so your 'Execute(..)' function still
does all the work...but this time its loading data and decoding it instead of
the other way round.
Improving your exporter or possibly any of your plug-ins, I would recommend
adding a pop up menu or something like most of the other plug-ins....a simple
dialog box with options...makes it more professional - and you can add further
options to your output depending on what was selected in the pop up dialog box
:).
Well until next time coders.
Happy Coding
|