View Full Version : collapse extrude nurb script
sketchbook 11-14-2010, 11:23 PM Hi All,
With the help and prodding of a few peeps on twitter, i created a script which takes a selected extrude nurb, collapses it, then combines the resulting meshes into 1 mesh, and optimizes the points.
works great!
Now I just would like to figure out how I can implement this script on multiple selected extrude nurbs. Is that something a script can do?
Thanks!
the script:
---------------
CallCommand(12236); // Make Editable
CallCommand(100004768); // Select Children
CallCommand(16768); // Connect+Delete
var op = doc->GetActiveObject();
if(!op) return;
var bc = new(BaseContainer);
if(!bc) return;
bc->SetData(MDATA_OPTIMIZE_POLYGONS, TRUE); //remove 1 or 2 point polygons
bc->SetData(MDATA_OPTIMIZE_UNUSEDPOINTS, TRUE); //remove unused points
bc->SetData(MDATA_OPTIMIZE_POINTS, TRUE); //remove double points
bc->SetData(MDATA_OPTIMIZE_TOLERANCE, 0.5); //tolerance
SendModelingCommand(MCOMMAND_OPTIMIZE, doc, op, bc, MODIFY_ALL);
----------
cheers!
|
|
Scott Ayers
11-15-2010, 02:49 PM
Hi Joe,
Yes, this is doable with loops and whole bunch of slight of hand and trickery: var obj = doc->GetFirstObject();// Start from the first object in the OM
//In this first loop.
//We will isolate the selected Extrude Nurbs objects by changing their names
while(obj) //To get at just the selected Extrude Nurbs objects. We need to isolate them from the others
{
if(obj->GetType() == 5116 && obj->GetBit(BIT_ACTIVE) == TRUE )
{
obj->SetName("temp"); // Change the names to isolate them from the others
obj->DelBit(BIT_ACTIVE); // De-select them
}
obj = obj->GetNext(); // So far we've only changed the first one.
// So the loop starts again and looks for any other selected Extrude Nurbs objects
}
//Now that we have the names changed for only the EN objects that were selected
//We can look for those specific names to convert them. withhout them needing to be selected
var obj = doc->GetFirstObject();
while(obj)
{
if(obj->GetName() == "temp" )
{
obj->SetBit(BIT_ACTIVE); // Make it active so we can make it editable
CallCommand(12236); // Make Editable
var a = doc->GetActiveObject();//We lost focus on it when converted. So we grab it again
CallCommand(100004768); // Select Children
CallCommand(16768); // Connect+Delete
a = doc->GetActiveObject();// We lost focus on it again. So we need to grab again
obj = a; // Since the original object was converted and lost it's focus so many times.
// we have to make the object assigned back to the "obj" variable again so the
// while loop will work the next time it cycles
var bc = new(BaseContainer);
if(!bc) return;
bc->SetData(MDATA_OPTIMIZE_POLYGONS, TRUE); //remove 1 or 2 point polygons
bc->SetData(MDATA_OPTIMIZE_UNUSEDPOINTS, TRUE); //remove unused points
bc->SetData(MDATA_OPTIMIZE_POINTS, TRUE); //remove double points
bc->SetData(MDATA_OPTIMIZE_TOLERANCE, 0.5); //tolerance
SendModelingCommand(MCOMMAND_OPTIMIZE, doc, obj, bc, MODIFY_ALL);
obj->DelBit(BIT_ACTIVE);
}
obj = obj->GetNext();
}
I hope the notes I made in there were enough for you to understand how it's working.
-ScottA
sketchbook
11-15-2010, 04:02 PM
nice!
Only thing which would be nice is if this acted on selected objects, even if buried in different areas in the OM.
i think this script only works with objects at the top of the OM.
thanks!
Scott Ayers
11-15-2010, 04:50 PM
That should be doable by using a custom recursion method. Instead of using the simpler obj = obj->Get Next() recursion code.
I just slapped it together very quickly. So I can't guarantee that it's bug free: searchOM(op)
{
if (!op) return;
if (op->GetDown()) return op->GetDown();
while (!op->GetNext() && op->GetUp()) op = op->GetUp();
return op->GetNext();
}
main(doc,op)
{
var obj = doc->GetFirstObject();// Start from the first object in the OM
//In this first loop.
//We will isolate the selected Extrude Nurbs objects by changing their names
while(obj) //To get at just the selected Extrude Nurbs objects. We need to isolate them from the others
{
if(obj->GetType() == 5116 && obj->GetBit(BIT_ACTIVE) == TRUE )
{
obj->SetName("temp"); // Change the names to isolate them from the others
obj->DelBit(BIT_ACTIVE); // De-select them
}
obj = searchOM(obj); // So far we've only changed the first one.
// So the loop starts again and looks for any other selected Extrude Nurbs objects
}
//Now that we have the names changed for only the EN objects that were selected
//We can look for those specific names to convert them. withhout them needing to be selected
var obj = doc->GetFirstObject();
while(obj)
{
if(obj->GetName() == "temp" )
{
obj->SetBit(BIT_ACTIVE); // Make it active so we can make it editable
CallCommand(12236); // Make Editable
var a = doc->GetActiveObject();//We lost focus on it when converted. So we grab it again
CallCommand(100004768); // Select Children
CallCommand(16768); // Connect+Delete
a = doc->GetActiveObject();// We lost focus on it again. So we need to grab again
obj = a; // Since the original object was converted and lost it's focus so many times.
// we have to make the object assigned back to the "obj" variable again so the
// while loop will work the next time it cycles
var bc = new(BaseContainer);
if(!bc) return;
bc->SetData(MDATA_OPTIMIZE_POLYGONS, TRUE); //remove 1 or 2 point polygons
bc->SetData(MDATA_OPTIMIZE_UNUSEDPOINTS, TRUE); //remove unused points
bc->SetData(MDATA_OPTIMIZE_POINTS, TRUE); //remove double points
bc->SetData(MDATA_OPTIMIZE_TOLERANCE, 0.5); //tolerance
SendModelingCommand(MCOMMAND_OPTIMIZE, doc, obj, bc, MODIFY_ALL);
obj->DelBit(BIT_ACTIVE);
}
obj = searchOM(obj);
}
}
-ScottA
martinweber
11-15-2010, 05:22 PM
Here is my solution.
I first count the number of selected objects (using SearchNext() which should be the fastest option) to create an array which holds all selected objects. Then I perform the task at hand on those objects.
I did not use a BaseList2D to dynamically build the list of selected objects as the BaseList2D assumes ownership of the objects. This would conflict between the doc and my own BaseList2D. So I need to count the number of objects first before creating the array due to the lack of dynamic arrays or lists in COFFEE.
I also do not check for the type of object so selecting an other object than an Extrude Nurbs might break the script.
//
// count the currently selected object in the scene
//
CountSelectedObjects(doc)
{
// get selected object (works if one single object is selected)
//
var active = doc->GetActiveObject();
var selectedNum = 0;
if( active ) // one active object
{
selectedNum = 1;
}
else // no or more than one object is selected
{
// we need to count the number of selected objects first
//
selectedNum = 0;
// get first object in scene
//
var op = doc->GetFirstObject();
// check if object has the active bit set, then we already found a selected object
// otherwise we search for the next object in the scene with the active Bit set
//
if( !op->GetBit(BIT_AOBJ) )
{
selectedNum = 1; // one selected object already found (the first one in scene)
op = op->SearchNext(BIT_AOBJ);
}
// count as long as we have selected objects
//
while( op )
{
op = op->SearchNext(BIT_AOBJ);
selectedNum++;
}
}
return selectedNum;
}
//
// GetSelectedObjects returns an array with all currently selected objects
// in the scene. Use sizeof() on the array to get the number of selected
// objects
//
GetSelectedObjects(doc)
{
// how many selected objects are there?
//
var selectedNum = CountSelectedObjects(doc);
// store active objects in this array
//
var list = new(array,selectedNum);
var active = doc->GetActiveObject();
if( active ) // exactly one active object
{
list[0] = active;
selectedNum = 1;
}
else // no or more than one object is selected
{
// get first object in scene
//
var op = doc->GetFirstObject();
// check if object has the active bit set, then we already found a selected object
// otherwise we search for the next object in the scene with the active Bit set
//
if( !op->GetBit(BIT_AOBJ) ) op = op->SearchNext(BIT_AOBJ);
// add objects to array as long as we have selected objects
//
var i=0;
while( op )
{
list[i] = op;
op = op->SearchNext(BIT_AOBJ);
i++;
}
}
return list;
}
DoCoolStuff( doc, op )
{
// select the object
//
doc->SetActiveObject(op);
CallCommand(12236); // Make Editable
CallCommand(100004768); // Select Children
CallCommand(16768); // Connect+Delete
var ob = doc->GetActiveObject();
if(!ob) return;
var bc = new(BaseContainer);
if(!bc) return;
bc->SetData(MDATA_OPTIMIZE_POLYGONS, TRUE); //remove 1 or 2 point polygons
bc->SetData(MDATA_OPTIMIZE_UNUSEDPOINTS, TRUE); //remove unused points
bc->SetData(MDATA_OPTIMIZE_POINTS, TRUE); //remove double points
bc->SetData(MDATA_OPTIMIZE_TOLERANCE, 0.5); //tolerance
SendModelingCommand(MCOMMAND_OPTIMIZE, NULL, ob, bc, MODIFY_ALL);
}
main( doc, op )
{
//
// build an array with all selected objects
//
var list = GetSelectedObjects(doc);
var selectedNum = sizeof(list);
doc->StartUndo();
var i=0;
while( i<selectedNum )
{
// get object from list
//
var op = list[i];
if(op) DoCoolStuff(doc,op); // perform whatever task has do be done
// next object
i++;
}
doc->EndUndo();
DrawViews(DA_NO_ANIMATION);
}
sketchbook
11-15-2010, 05:46 PM
thanks a ton guys! this is so great!
Scott Ayers
11-15-2010, 06:02 PM
Nice solution Martin.
Much more elegant than mine.
-ScottA
Cairyn
11-15-2010, 06:50 PM
If you have one of the newer C4D versions, you can shorten the script quite a bit by using the "object" array:
// count selected elements
var numSelected = 0;
while (object(numSelected)) numSelected++;
// write selected elements into new array
var sel = new(array,numSelected);
var i=0;
while (i<=numSelected-1) {
sel[i] = object(i);
i++;
}
// Deselect All
CallCommand(100004767);
doc->StartUndo();
i=0;
while (i<=numSelected-1) {
var obj = sel[i];
if(obj->GetType() == 5116) {
obj->SetBit(BIT_ACTIVE); // Make it active so we can make it editable
CallCommand(12236); // Make Editable
CallCommand(100004768); // Select Children
CallCommand(16768); // Connect+Delete
obj = doc->GetActiveObject();// We lost focus on it again. So we need to grab again
var bc = new(BaseContainer);
if(!bc) return;
bc->SetData(MDATA_OPTIMIZE_POLYGONS, TRUE); //remove 1 or 2 point polygons
bc->SetData(MDATA_OPTIMIZE_UNUSEDPOINTS, TRUE); //remove unused points
bc->SetData(MDATA_OPTIMIZE_POINTS, TRUE); //remove double points
bc->SetData(MDATA_OPTIMIZE_TOLERANCE, 0.5); //tolerance
SendModelingCommand(MCOMMAND_OPTIMIZE, doc, obj, bc, MODIFY_ALL);
obj->DelBit(BIT_ACTIVE);
}
i++;
}
doc->EndUndo();
DrawViews(DA_NO_ANIMATION);
What's bugging me is that the Undo doesn't properly work - the CallCommand apparently creates new undo entries. (Also, why doesn't "object" have a length count?)
Scott Ayers
11-15-2010, 07:44 PM
Don't get me started with Undos and CallCommands.:banghead:
Anders suggested that we should send Maxon requests to get those built in undos taken out so we can package them up ourselves easier.
Where did you find the object array stuff in the SDK?
I can't find it.
-ScottA
martinweber
11-15-2010, 08:05 PM
Thanks Cairyn. I checked the updated SDK for R11 and found the object() command in the changes since R9.520 SDK. Certainly makes it a lot easier to iterate the selected objects.
I think I'll migrate over to Python though for future scripts once I'll upgrade to R12. It's so much nicer and more mature as a language.
Vidar3d
11-16-2010, 05:49 AM
Awesome script Joe. That will be a huge time saver :bowdown:
Cairyn
11-16-2010, 10:23 AM
Don't get me started with Undos and CallCommands.:banghead:
Anders suggested that we should send Maxon requests to get those built in undos taken out so we can package them up ourselves easier.
Where did you find the object array stuff in the SDK?
I can't find it.
In the R11 addendum. Not available for R9.5 and earlier (don't ask me about R10...)
The CallCommand should at least have a bool parameter that decides whether the Undo is handled automatically. A built-in undo has the advantage that you don't need to take care of undos when you just string together commands from the manager, so for casual scripters it may be beneficial.
Even better would be if the StartUndo would be closed automatically when the function is exited, and when every subsequent StartUndo is grouped under that open Undo object, or at least an option to do so... but then, only C++ supports the necessary destructors. Meh.
Scott Ayers
11-16-2010, 02:47 PM
Yeah. I agree with you about how the undos should work Robert.
It seems like they might have designed the CallCommands more as a simple way for the average artist to do very basic scripting with them. And never thought we'd be wrapping these things up into our own custom nested loops and such. And mixing them with raw coffee code.
Which would explain why each command acts sort of like a stand alone entity.
-ScottA
mayajunky
11-16-2010, 09:03 PM
Pretty cool, I tried out Martin's version. Will have to say it was having some issues collapsing an extrude that has adaptive interpolation. And would be really cool if it deleted the polygon selection tags that are left over as well. If it wasn't for those two things I'd be using this daily. :)
Scott Ayers
11-17-2010, 04:16 PM
Pretty cool, I tried out Martin's version. Will have to say it was having some issues collapsing an extrude that has adaptive interpolation. And would be really cool if it deleted the polygon selection tags that are left over as well. If it wasn't for those two things I'd be using this daily. :)
Try this one.
It has two limitations that I know of:
1.-It doesn't work on nested Extrude Nurbs objects(Extrudes children of Extrudes).
2.-It searches for objects starting with the Letter E. So no other object's can start with that letter.
Other than that it seems to work OK.
searchOM(op)
{
if (!op) return null;
if (op->GetDown()) return op->GetDown();
while (!op->GetNext() && op->GetUp()) op = op->GetUp();
return op->GetNext();
}
main(doc,op)
{
var numSelected = 0;
while (object(numSelected)) numSelected++; // count selected elements
var sel = new(array,numSelected); // write selected elements into new array
var i=0;
while (i<=numSelected-1)
{
sel[i] = object(i);
i++;
}
CallCommand(100004767); // Deselect All
i=0;
while (i<=numSelected-1)
{
var obj = sel[i];
if(obj->GetType() == 5116) // if the selected object/s are an Extrude Nurbs type
{
obj->SetBit(BIT_ACTIVE); // Make it active so we can make it editable
CallCommand(12236); // Make Editable
CallCommand(100004768); // Select Children
CallCommand(16768); // Connect+Delete
obj = doc->GetActiveObject();// We lost focus on it again. So we need to grab again
var bc = new(BaseContainer);
if(!bc) return;
bc->SetData(MDATA_OPTIMIZE_POLYGONS, TRUE); //remove 1 or 2 point polygons
bc->SetData(MDATA_OPTIMIZE_UNUSEDPOINTS, TRUE); //remove unused points
bc->SetData(MDATA_OPTIMIZE_POINTS, TRUE); //remove double points
bc->SetData(MDATA_OPTIMIZE_TOLERANCE, 0.5); //tolerance
SendModelingCommand(MCOMMAND_OPTIMIZE, doc, obj, bc, MODIFY_ALL);
obj->DelBit(BIT_ACTIVE);
}
i++;
}
var firstObject = doc->GetFirstObject();
if (!firstObject) return;
var deleteme;
while (firstObject)
{
var name = firstObject->GetName();
var deleteme; // used to delete the tag later on
var firstLetter;
firstLetter=tostring(name[0],"c");
if(firstLetter=="E")// If the object starts with the letter E
{
var tag = firstObject->GetFirstTag();
while (tag)
{
tag->DelBit(BIT_ACTIVE);
if(tag->IsInstanceOf(Tpolygonselection))
{
deleteme=tag; //save the active tag to another variable
tag = tag->GetNext(); //go to next tage bevore we delete the active one
doc->AddUndo(UNDO_DELETE, deleteme); // set an internal undo record for the following specific action
deleteme->Remove();//delete the saved tag
}
else{
tag = tag->GetNext();
}
}
}
firstObject = searchOM(firstObject);
}
}
-ScottA
mayajunky
11-18-2010, 04:06 PM
Hey thanks Scott, works nice. :)
Scott Ayers
11-18-2010, 05:06 PM
You're welcome.
It's bit crude using the names like that, but it works.
Just in case it's not obvious. The [0] is the array location the letter of the word you're looking for.
So if you commonly have other object's that start with the letter E in your projects. And/or you don't like searching for the first letter of the word. You can change that code to look for the second, third, fourth ,etc. letter location.
In this case. The word we're looking for is Extrude NURBS.1,2,3, etc.
So instead of searching for the first letter of "E" at position[0].
You could search for maybe the letter "u" which is at position [4].
Like this:
var fourthLetter ;
fourthLetter = tostring(name[4],"c"); // Look for the character at the fourth position
if(fourthLetter = "u")
{
var tag = firstObject->GetFirstTag();
blah, blah, blah, So on and so fourth........
It's more flexible than just being able to look for the first letter of the word.
-ScottA
mayajunky
11-18-2010, 05:54 PM
Yea really I'd be fine with a simpler script that just converted the current selected single object. I'd be implementing it more as a modeling workflow. Using some nurbs modeling for direct conversion to polygons, to be merged with other poly modeling etc... I mean this is specific to my workflow, but seems you could have two scripts. One for isolated modeling, and one for scene wide conversions. I probably wouldn't need to do the later very often personally. For me the first example wouldn't even need any error checking, I'd just be aware it only works on one object at a time, and it's got to be an extrude object I'm running the script on. But that's me thinking about scripts on an individual level, not a global distribution level. :P
But just out of curiosity ( and my limited coffee knowledge ), it's not possible to just check if the object type is an extrude object? Versus searching via naming?
Scott Ayers
11-18-2010, 07:10 PM
But just out of curiosity ( and my limited coffee knowledge ), it's not possible to just check if the object type is an extrude object? Versus searching via naming?
Yes.I think it is possible.
I did try to use a nested While loop to get at the tags right after the conversion took place when inside of the first loop. But I could only delete the first selection tag for some reason.
I'm sure a more experienced programmer could pull it off.
The only way I could figure out how to do it was to wait until after all the conversion stuff took place.
Then go looking for the tags to delete them. And since they were not nurbs anymore. The only things that make object's unique is their names.
Most of my time has been spent trying to decrypt the SDK functions that aren't well explained.
I really haven't had the time to get better at complicated problem solving yet. I've been too busy trying to decrypt the functions first.
-ScottA
CGTalk Moderation
11-18-2010, 07:10 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.
vBulletin v3.0.5, Copyright ©2000-2012, Jelsoft Enterprises Ltd.