[Closed] 3dsmax SDK adventure
I’m moving more and more into the 3ds Max SDK (c++). I thought I would start a thread where I share my thoughts about how to move to the SDK from a scripters perspective.
You are all welcome to chip in of course.
Some general thoughts:
The SDK is sometimes surprisingly simple to use for a scripter. There is a large number of functions that have identical names. Most of the poly functions, and object functions work almost like they do in MaxScript. What I find is normally tricky is to setup c++ so that you can use the functions to begin with. For example at this very moment I figured out how to send Nodes into c++ with MaxScript, the problem I’m having is how to use the Editable_Poly commands on it.
Some general tips:
- Download the max8 SDK help, even if you use max7. The help is WAY easier to use than it’s for Max7 and there several nice tutorials.
- Start with the howto tutorials. I recommend especially the IntervalArray tutorial. This tutorial shows you how to extend maxScript functions.
- Use Visual Net 2003. Visual Studio 7 is ok too, but Net has a better environment. Visual Studio 2005 will soon be usable.
Performance notes:
- Slow return. I found out that it’s actually very slow to return large arrays into MaxScript. For example I did a big loop that returns all the positions of all verts in an object. The loop in itself in c++ was extremely fast, around 15ms for a 1mil object, but returning the results took several hundreds of ms. This means we should only return big things if we really have to.
Myself I plan on focusing on adding MaxScript extentions in the beginning. So 90% of my code will remain scripts, but everything that needs big loops will be done in c++.
MaxScript and SDK comparison.
MaxScript benefits:
- Extremely easy to setup. Just write “box()” and you have an example of a script. It’s way harder to start a c++ if you are new.
- Far easier to learn than C++. MaxScript work more like a human thinks, while C++ works more like a computer “thinks” so be prepared for heaps, pointers, binary trees, memory allocation etc.
- Simpler programs are far faster to write. I once wrote a scene information tool in 15minutes. That would have taken several hours in c++.
- Easy to test short progarms. In c++ you must restart max every time and will likely get many errors for “minor” things, variables need to be declared correctly, you can’t mixed types, all the directories must be included etc.
- A consistent programming interface. If you want to change Editable_Poly you write something like polyOp.setVert … while in C++ you have completely different libraries for almost the same object. There is MNMesh, there is EPoly, there is IEPoly, there is Edit_Poly, there is Editable_Poly, I think you get the picture.
- Variables are not type specific, so you can mix int, string, bool etc. This is however an advantage in bigger programs.
- Fast enough for most things that don’t need to update in real time. In theory I think that 95% that is done with a button click is fast enough in MaxScript. For example, extrudes, loops. The things that are not fast enough is if you have things like bend modifier, noise modifier, or interactive extrude in viewport.
- Easy to create standard max interfaces. Just put a few buttons and checkboxes and you are up and running. C++ interfaces are WAY harder and need alot of coding.
- Shorter geometry/selection modifications work excellent. Examples are Face Loops, variants of extrude etc.
- Simpel debugging is easy in MaxScript, but more complex is not.
- Better documentation than the c++ SDK. The C++ SDK has very few examples.
- Easier to find comprehensive examples that can be changed to your own tools.
- Other peoples programs are quite easy to understand, while the c++ programs are almost incomprehensive unless you are very skilled in programming.
SDK Benefits
- loops will always be far faster. MaxScript can handle 100,000 loops ok, while c++ can handle loops with 100,000,000 objects ok. This is probably the best advantage with the SDK.
- complex algoritms are way faster. If you need to sort, or investigate complex geometry this will quickly kill maxScript
- larger geometry modifications that need bigger loops. For example you need to loop through 100,000 vertices and move them to somewhere. This is nearly impossible for Poly objects in maxScript, for Editable_Mesh objects it is possible though (since the mesh object don’t update EVERY time the poly object is changed).
- Tools that need interactivity. If you need to have full control of the mouse and what it’s doing you will quickly run into problem with MaxScript. To create even such a simple thing as interactive bevel is hard (is it even possible in a good way?) in MaxScript. Some Painting tools can be done that are good and fast though (because of the good PainterInterface).
- Advanced and powerful debug tools. You can easily break the code, and have all the locals printed, step through the code, etc. This is a BIG bonus
- Advanced Interfaces can only be created in C++. Here custom controlers can be created.
- Viewport graphic modifications can only really be done in C++. If you need to print text, draw lines (for example the isolines on turboSmooth), this can only be done in C++ in a good way (it will flicker and be slow in MaxScript).
- Multiple Undo in the Editor! Also things like context sensitive help, much better color coding etc. This can be done fairly good in maxScript if for example UltraEdit is used.
- Better connection to other tools, to send files with ftp, to connect to Photoshop, or Internet is much more powerful.
- Custom libraries can be downloaded that other people have written.
- Many silly hacks that must be done in MaxScript don’t have to be done in C++, because c++ is so much faster. Normally you spend much time in MaxScript trying to figure out how you can fool max into being faster.
- Many slow problems can just be avoided in c++. For example if you move ONE vert in maxScript it will update all 500,000 vertices every single time. In c++ you can move 5000 vertices and THEN you update the remaining vertices. Guess what is the fastest?
/Andreas
Small things I found:
If you want to use strings you have to use max own string include.
#include "Numbers.h" // for handling of maxscript Integers and Floats
#include "Arrays.h" // for returning a maxscript array
#include "Strings.h" //For Strings
How to popup a messagebox, good to have in the start of the program so you are sure that it actually loads
MessageBox(NULL, _T("My Program started!!"), _T("Program Title!"), MB_OK);
Alot of the normal maxfunctions exist in a special interface. With it you can do alot of things like get the current selected objects, get the number of selected objects etc. Notice that when you write the -> after the interface all the methods and properties will popup so you can just scroll through them and look at the commands.
Interface *ip = GetCOREInterface();;
int a;
a=ip->GetSelNodeCount();
/Andreas
This is the complete code for a function to test if an object is an editable_poly object or an editable_mesh object:
To use it just write in MaxScript
ToolsGetPosArray($)
The C++ code:
#include "definsfn.h"
def_visible_primitive(ToolsGetPosArray, "ToolsGetPosArray");
Value* ToolsGetPosArray_cf(Value** arg_list, int count)
{
Interface* ip = GetCOREInterface();
int a;
//a=ip->GetSelNodeCount();
check_arg_count(ToolsGetPosArray, 1, count);
Value* pNode=arg_list[0];
INode *pnode=pNode->to_node();
//Verify that node is of a valid type
ObjectState os = pnode->EvalWorldState(0);
int sub_lv;
if (os.obj)
{
//Look at the super class ID to determine the type of the object.
Object* pobjBase = os.obj->FindBaseObject();
if( pobjBase->ClassID() == Class_ID(EDITTRIOBJ_CLASS_ID, 0) ) //Verify that the base object is an Editable Mesh
{
MessageBox(NULL, _T("Mesh Object!!"), _T("Testing!"), MB_OK);
}
if( pobjBase->ClassID() == EPOLYOBJ_CLASS_ID) //Verify that the base object is an Editable Poly
{
MessageBox(NULL, _T("Poly Object!!"), _T("Testing!"), MB_OK);
}
}
return &ok;
}
If you have a node that you got just like in the example above do like this to modify it’s geometry. This just moves all the vertex points for an editable_mesh. The screen is also redrawn:
ObjectState os = pnode->EvalWorldState(0);
if (os.obj)
{
//Look at the super class ID to determine the type of the object.
if (os.obj->IsSubClassOf(triObjectClassID))
{
TriObject *tobj = (TriObject*)os.obj; // Get a mesh from input object
Mesh* mesh = &tobj->GetMesh();
int numVert = mesh->getNumVerts();
int numFaces=mesh->getNumFaces();
for (int i=0;i<numVert;i++) //Modify geometry
{
mesh->setVert(i,Point3(i,i,i));
}
mesh->InvalidateGeomCache(); //force geometry update
ip->ForceCompleteRedraw(true); //Update viewport (slower method)
}
}
I’ve also been fidgeting in the SDK from time to time, I’m also interested in adding maxscript extensions, I’m actually in the process of adding low level access to dialogs’ styles and ex-styles. So if I discover anything of interest I’ll share it here too.
I must say at this point that anyone that wants to learn how to do stuff to max dialogs and ui controls should download rezn8’s maxscript extension source, it’s been very informative for me.
Here’s a question I haven’t been able to answer myself:
I want to make a function that will make a dialog the top most (like “Always on Top”) of all the Max dialogs. I figured out how to do this by using SetWindowPos() which allows you to set the z-order to HWND_TOPMOST but this is the top most of all windows, meaning that if you task switch to another app it will be on top of that too!
Does anyone know how to make it the top most in the Max, but will fall back on task switches, preferably a method that doesn’t require one to use callbacks for Max going into and out of focus?
Edit:
A method of accessing the UnwrapUVW modifier would also be handy. I’m not talking about the IUnwrapMod class but rather the modifier itself. I need access to the hWnd member so if you know how to get your hands on that, it would be cool too.
Yea, please share. There is really not that much tutorials/tips to be found about the max SDK. I will be putting up alot of small functions and more examples. I just figured out how to access the Editable_Poly object functions, but for some weird reason some of the functions are private (like grow selection??).
Mehehe, now I’m going to look at max9
/Andreas
You’d better be speaking figuratively…
BTW it seems that growing selections are not built into MNMesh, if you look in polyedit.cpp there is a function called EpfnGrowselection() it has all the grow functionality in there.
Nope, I just got a demonstration.
Jupp, I’m just checking it out, but it was complaining that EpfnGrowselection() was private so I can’t call it. Seems silly if it’s really the case.
/Andreas
Cool… are you bound by an NDA or can you start a thread? :deal:
Have you written mods yet? I still want to make a decent replacement for Substitute, Max’s doesn’t work too well.
I’m not saying nothing I’m afraid. Looks nice thought
You mean modifiers? Not yet, but it doesn’t look that hard. Right now I’m focusing on creating extensions for Editable_Poly. If you have any requests please tell, I will try to make some public later on.
Damn, EpfnGrowselection is private to keep SDK compability with max6. Anyone got any ideas on how I can access functions like this anyway? Since I have the source code I could perhaps copy it from the Grow function. Ideas?
/Andreas
It’s quite tricky actually to call the private functions. You have to define a FPParam that has the in parameters. And if there are return types you have to define a specific class for this too.
Ok, this is the way you have to call the poly functions that are private:
FPValue returnValue;
cd->Invoke(epfn_get_num_vertices, returnValue);
int numVertices = returnValue.i;
To get hold of the internal MNMesh object and for example get the position of all the vertices you can do like this:
PolyObject *pPolyObj =(PolyObject*) pobjBase;
EPoly *cd= (EPoly *)(pobjBase->GetInterface(EPOLY_INTERFACE));
//This is where you get the Mesh and loop for all the verts.
MNMesh *mm=cd->GetMeshPtr();
int numv=cd->GetMeshPtr()->numv;
Point3* posArray=new Point3[numv];
for (int i=0; i<(numv-1);i++)
{
posArray[i]=mm->v[i].p;
}
/Andreas