PDA

View Full Version : 'Create' button for primitives ?


Caprier
01-12-2008, 07:51 PM
Hello.

I have written a template script for custom primives I'm working on and I'd like to have both by-button and by-mouse creation modes.

The thing is I don't know where to put the on doIt pressed do block nor what to put in it. Can anyone help please ?


plugin simpleObject primTemp
name:"Template"
category:"Custom Primitives"
classID:#(0x356fa575, 0x54da4b86)
(
parameters main rollout:params
(
sides type:#integer ui:sides default:12
radius type:#float ui:radius default:0
)
rollout params "Parameters"
(
spinner sides "Sides" range:[3,60,12] type:#integer
spinner radius "Radius" range:[0,100,0]
button doIt "Create"
)
on buildMesh do
(
vArr = #()
fArr = #()

append vArr [0,0,0]

for i = 1 to sides do
(
tAng = (i - 1) * 360 / sides
append vArr [radius * cos(tAng), radius * sin(tAng),0]
append fArr [1,i + 1,(mod i sides) + 2]
)

setMesh mesh vertices:vArr faces:fArr
)
tool create
(
-- on doIt pressed do ...what?

on mousePoint click do
case click of
(
1: nodeTM.translation = gridPoint
2: #stop
)
on mouseMove click do
radius = length [gridDist.x,gridDist.y]
)
)

Caprier
01-13-2008, 11:16 PM
Did I make the noob mistake and ask for something either very hard to implement or just impossible?

Someone, please?

bhnh
01-14-2008, 12:02 PM
This may be a case of the blind leading the blind, Pat, but it's sounding to me that you'd want a simple Boolean switch to enable either creation by mouse or creation by button. I'd think a couple of radio buttons would do it, i.e., on rdo_creation mode changed do (create tool, etc.). Or a simple "if/then/else" statement.

Caprier
01-14-2008, 08:29 PM
Hi Bruce. So we meet here too. Small world, hu?

It's more about creating the node with a button pressed event, like the regular max primitives.
This is what I've understood so far from the MaxScript user reference and some of Bobo's threads (I was hoping he'd be around):
The buildmesh creates a virtual mesh (in memory, not in the scene). So you need a create tool to make an instance of that mesh (a node in the scene). But the create tool seems to accept only mouse events handlers and this is where I'm stuck. I don't see where a button pressed event can fit in. Nor of any other way to create the instanced node than the create tool.

I fear that it can only be achieved through the SDK. But I would have liked a confirmation.
I was hoping for a way around, like maybe faking the mouseclick through some code, or calling a separate script? I don't know.

Could one of the MXS masters around here shed some light on the matter? I'm really stuck here. And more than likely misunderstanding some important concepts.

fferro2
01-16-2008, 05:13 PM
Maxscript Tools and Interaction with 3ds Max > Creating MAXScript Tools > Scripted Plug-ins > Scripted SimpleObject Plug-ins

"The mesh plug-in local variable is accessible in any of the plug-in's handlers, but is typically only built in the on buildMesh handler. You can either modify the existing TriMesh value in place using the TriMesh methods, or construct a new TriMesh value and assign it to the mesh plug-in local variable."

The "on doIt pressed" must be inside the rollout.
May be you can call a function to modify the mesh from there.
You can access to the trimesh with "this.mesh"

Good luck!

Caprier
01-16-2008, 09:47 PM
Thanks a lot for your answer, Fernando.

After a lot of tries, it feels that I'm pretty close to a solution. But I haven't been able to find the correct form with "this.mesh".
I guess the setMesh method alone is not enough to create a node outside of the on buildMesh block. I don't know how to create the node and still have access to its parameters through the rollout (like with the regular Primitives).

Here is what I've got, with obvioulsy something missing:

plugin simpleObject primTemp
name:"Template"
category:"Custom Primitives"
classID:#(0x356fa575, 0x54da4b86)
(
local vArr = #()
local fArr = #()

fn buildIt s r =
(
vArr = #()
fArr = #()

append vArr [0,0,0]

for i = 1 to s do
(
tAng = (i - 1) * 360.0 / s
append vArr [r * cos(tAng), r * sin(tAng),0]
append fArr [1,i + 1,(mod i s) + 2]
)
)

parameters main rollout:params
(
sides type:#integer ui:sides default:12
radius type:#float ui:radius default:0
)

rollout params "Parameters"
(
spinner sides "Sides" range:[3,60,12] type:#integer
spinner radius "Radius" range:[0,100,0]
button doIt "Create"

on doIt pressed do
(
radius.value = 25
buildIt sides.value radius.value

setMesh mesh vertices:vArr faces:fArr -- <<< something missing...
)
)

on buildMesh do
(
buildIt sides radius

setMesh mesh vertices:vArr faces:fArr
)

tool create
(
on mousePoint click do
case click of
(
1: nodeTM.translation = gridPoint
2: #stop
)
on mouseMove click do
radius = length [gridDist.x,gridDist.y]
)
)


I must be somewhere in the User Reference, but I can't find it.

focomoso
01-17-2008, 07:43 AM
The mesh needs do be set inside your buildIt function or else all that work you've done just disappears when the fn ends. So you need to put:

setMesh this.mesh vertices:vArr faces:fArr

as the last line of buildIt() and take it out of the on buildMesh and on doIt.

Caprier
01-17-2008, 11:47 AM
Hello James. Thanks for your reply.

I've done as you said:

plugin simpleObject primTemp
name:"Template"
category:"Custom Primitives"
classID:#(0x356fa575, 0x54da4b86)
(
local vArr = #()
local fArr = #()

fn buildIt s r =
(
vArr = #()
fArr = #()
append vArr [0,0,0]

for i = 1 to s do
(
tAng = (i - 1) * 360.0 / s
append vArr [r * cos(tAng), r * sin(tAng),0]
append fArr [1,i + 1,(mod i s) + 2]
)

setMesh this.mesh vertices:vArr faces:fArr
)

parameters main rollout:params
(
sides type:#integer ui:sides default:12
radius type:#float ui:radius default:0
)

rollout params "Parameters"
(
spinner sides "Sides" range:[3,60,12] type:#integer
spinner radius "Radius" range:[0,100,0]
button doIt "Create"

on doIt pressed do
(
radius.value = 25
buildIt sides.value radius.value
)
)

on buildMesh do
(
buildIt sides radius
)

tool create
(
on mousePoint click do
case click of
(
1: nodeTM.translation = gridPoint
2: #stop
)
on mouseMove click do
radius = length [gridDist.x,gridDist.y]
)
)
The button handler still doesn't perform any action, but now the creation by mouse is gone too. Though if I change setMesh this.mesh vertices:vArr faces:fArr by setMesh mesh vertices:vArr faces:fArr in the function, I get the mouse tool back, but nothing new with the button

I try to understand the logic behind it, I've been through an through everything I could find in the User Reference, I tried to find examples on the net. Nothing.
And I thought the question was trivial when I posted it...

So, what's missing?

fferro2
01-17-2008, 04:43 PM
What I've found so far:

There is no dependents till the mouse tool starts, (one of the dependents is the node you are trying to modify).
Because:
print (refs.dependents this)
prints nothing till you move the mouse inside any viewport.

fn createIt tempmesh = (
...
setMesh tempmesh vertices:vArr faces:fArr
tempmesh
)
...
on buildMesh do (mesh = (createIt mesh))

works only for the mouse tool.

on create do (print "create")
Inside the plugin and at the of it (after the mouse tool create), prints "create" anytime the plugin starts, (by clicking on the Template button).
So, here there would be a way to create an empty trimesh outside the mouse tool (the one needed to the node you are creating).


May be you have to experiment developing another tool inside the plugin...

Caprier
01-17-2008, 06:32 PM
Hi Fernando. Thanks again for the help.

I feel that I'm going in circle here.
Maybe it's something inherent to simpleObject plugins that the node must be created inside the buildMesh block.
And even if I were able to create the node, I think I couldn't keep it wired to its parameters because the setMesh method refuses to work outside the buildMesh, or a function called from the buildMesh.
When they say "but is typically only built in the on buildMesh handler", I guess by "typically" they mean "exclusively".
I dunno. I might be missing some important point again. I'll make some more tries tomorrow.

James, just a precision. In the code just before your post, where the "setMesh mesh vertices:vArr faces:fArr" is executed from within the buildMesh block, not the function, the mouse tool worked fine. That's because vArr and fArr are declared outside any block, so the function fills them and then can be accessed from anywhere in the code.
I declared the arrays there so I could experiment with the setMesh at different places without having to worry about them.

focomoso
01-17-2008, 08:07 PM
Hi Fernando. Thanks again for the help.
James, just a precision. In the code just before your post, where the "setMesh mesh vertices:vArr faces:fArr" is executed from within the buildMesh block, not the function, the mouse tool worked fine. That's because vArr and fArr are declared outside any block, so the function fills them and then can be accessed from anywhere in the code.
I declared the arrays there so I could experiment with the setMesh at different places without having to worry about them.
oops. missed the declarations. Don't have max open right now, but can you call buildMesh explicitly. Maybe with this.buildMesh()? Shooting blind here.

fferro2
01-17-2008, 08:32 PM
oops. missed the declarations. Don't have max open right now, but can you call buildMesh explicitly. Maybe with this.buildMesh()? Shooting blind here.

Uhmm... I guess not.
The only things you can call this way are the parameters (this.radius and this.sides) and the trimesh (this.mesh).
I don't know how to create the delegate from outside the buildMesh.

Caprier
01-18-2008, 05:10 AM
Guys? There's something that is not clear to me. Not being a programmer, I'm not sure about what the THIS. prefix means. From what you've said, I think it refers to the trimesh. If that is the case, I can't use it because there is no trimesh built before the setMesh constructor is called, only a couple of point 3 arrays, that could hold color values, or phone numbers for that matter. Please correct me if I'm wrong.

Anyway, after more and more - and more! - reading of the Reference and even more tries, I've come to this:
-point 1: creating an object the same way the Stantard and Extended Primitives are created has to be done through a scripted simpleObject plugin.
-point 2: a scripted simpleObject plugin needs the node to be created inside an on buildMesh block.
-point 3: an on buidMesh block needs a create tool to trigger the creation of the actual object in the scene.
-point 4: though a create tool accepts some other handlers than direct mouse event handlers - on start, on stop,... -, only a mouse click will create the object (<-- this is where I hope I'm wrong!)


plugin simpleObject primTemp
name:"Template"
category:"Custom Primitives"
classID:#(0x356fa575, 0x54da4b86)
(
local vArr = #()
local fArr = #()
local buttonUsed = false
fn buildIt s r =
(
vArr = #()
fArr = #()
append vArr [0,0,0]
for i = 1 to s do
(
tAng = (i - 1) * 360.0 / s
append vArr [r * cos(tAng), r * sin(tAng),0]
append fArr [1,i + 1,(mod i s) + 2]
)
)
parameters main rollout:params
(
sides type:#integer ui:sides default:12
radius type:#float ui:radius default:0
)
rollout params "Parameters"
(
spinner sides "Sides" range:[3,60,12] type:#integer
spinner radius "Radius" range:[0,100,0]
button doIt "Create"
on doIt pressed do
buttonUsed = true
)
on buildMesh do
(
buildIt sides radius
setMesh mesh vertices:vArr faces:fArr
)
tool create
(
/* on start do
if buttonUsed do
(
radius = 25
<----- something here ?!
#stop
) */
on mousePoint click do
case click of
(
1: nodeTM.translation = gridPoint
2: #stop
)
on mouseMove click do
radius = length [gridDist.x,gridDist.y]
)
)


Try as I might, I cannot find any breach in that chain of restrictions.
On the other hand, I find it hard to believe that none of the MaxScript mighty wizards ever found a way around it.

So... to be continued? Hopefully?

focomoso
01-18-2008, 06:48 PM
Guys? There's something that is not clear to me. Not being a programmer, I'm not sure about what the THIS. prefix means. From what you've said, I think it refers to the trimesh. If that is the case, I can't use it because there is no trimesh built before the setMesh constructor is called, only a couple of point 3 arrays, that could hold color values, or phone numbers for that matter. Please correct me if I'm wrong.
In object oriented programming, 'this' - in some languages, 'self' - refers to the object that the code is in. And by object, I don't mean like a max object, I mean an object in the programming sense, the piece of code that wraps a given set of data and behaviors. 'parent' or 'super' or, in max, 'delegate' refers to the object that a given object is derived from (its parent).

Say you have an object called 'foo' and foo has a method 'getSize()'. Now you create a subclass (extend foo in maxScript) of foo called 'bar'. Bar also has a getSize(), but it works differently (because, say, bar has more detail than foo and has a more complicated way of calculating its size). If, while coding bar, you want to refer to bar's get size, you call this.getSize(), but if you want to refer to foo's get size you call delegate.getSize() in maxScript (or parent.getSize() or super.getSize())

Anyway, after more and more - and more! - reading of the Reference and even more tries, I've come to this:
-point 1: creating an object the same way the Stantard and Extended Primitives are created has to be done through a scripted simpleObject plugin.
-point 2: a scripted simpleObject plugin needs the node to be created inside an on buildMesh block.
-point 3: an on buidMesh block needs a create tool to trigger the creation of the actual object in the scene.
-point 4: though a create tool accepts some other handlers than direct mouse event handlers - on start, on stop,... -, only a mouse click will create the object (<-- this is where I hope I'm wrong!)

Try as I might, I cannot find any breach in that chain of restrictions.
On the other hand, I find it hard to believe that none of the MaxScript mighty wizards ever found a way around it.

So... to be continued? Hopefully?

I'm starting to think this might not be possible in a simpleObject. We'll need a heavy hitter to weigh in (Bobo perhaps). You may want to look at creating a scripted geometry plugin. You don't have the on buildMesh restrictions there.

Caprier
01-19-2008, 01:10 AM
Thank you mister Kelly, Sir! That's the kind of clear explanation one can use more often.

I'll give a try at the scripted geometry plugin. Back to the User Reference...

I'd sure could use some of Mr. Petrov's advices. After all, it's after reading some of his posts that I tried to script the thing this way. That would make him kind of responsible, wouldn't it?
Any tip on how to bribe him in?

Bobo
01-19-2008, 04:10 AM
I am sorry for missing this thread - I noticed it had a lot of answers and it never occured to me to check it out, assuming it were under control ;)

The solution is surprisingly simple, as soon as you forget about attempting to call the internal functions of the plugin from the button's handler. Just add the same code you would place in a MacroScript, completely ignoring the fact your create button is inside the plugin class being created...

plugin simpleObject primTemp
name:"Template"
category:"Custom Primitives"
classID:#(0x356fa575, 0x54da4b86)
(
parameters main rollout:params
(
sides type:#integer ui:sides default:12
radius type:#float ui:radius default:0
)

rollout params "Parameters"
(
spinner sides "Sides" range:[3,60,12] type:#integer
spinner radius "Radius" range:[0,100,0]
button doIt "Create"
on doIt pressed do primTemp sides:sides.value radius:radius.value
)

on buildMesh do
(
local vArr = #()
local fArr = #()
append vArr [0,0,0]
for i = 1 to sides do
(
tAng = (i - 1) * 360.0 / sides
append vArr [radius * cos(tAng), radius * sin(tAng),0]
append fArr [1,i + 1,(mod i sides) + 2]
)
setMesh mesh vertices:vArr faces:fArr
)

tool create
(
on mousePoint click do
(
case click of
(
1: nodeTM.translation = gridPoint
2: #stop
)
)
on mouseMove click do
(
case click of
(
2: radius = length [gridDist.x,gridDist.y]
)
)
)
)

As for the buildIt() function, if you wanted to use one for some reason, you would be allowed to move ALL the code from BuildMesh() into your own buildIt() function and it would still work, like

...
fn buildIt =
(
local vArr = #()
local fArr = #()
append vArr [0,0,0]
for i = 1 to sides do
(
tAng = (i - 1) * 360.0 / sides
append vArr [radius * cos(tAng), radius * sin(tAng),0]
append fArr [1,i + 1,(mod i sides) + 2]
)
setMesh mesh vertices:vArr faces:fArr
)

on buildMesh do buildIt()
...



There is no need to use 'this' in this case, the setMesh() method on the internal 'mesh' variable is valid anywhere inside the plugin...

Caprier
01-19-2008, 05:30 AM
Wow! Simple and efficient. Thank you so much, Bobo!

If I may, I'd like to take it one lil' step further: how do I keep the parameters wired to the object once it's created with the button? So it really is like a Standard Primitive.

Bobo
01-19-2008, 06:33 AM
Wow! Simple and efficient. Thank you so much, Bobo!

If I may, I'd like to take it one lil' step further: how do I keep the parameters wired to the object once it's created with the button? So it really is like a Standard Primitive.


plugin simpleObject primTemp
name:"Template"
category:"Custom Primitives"
classID:#(0x356fa575, 0x54da4b86)
(
local lastCreated --declare a variable to remember the last created object
parameters main rollout:params
(
sides type:#integer ui:sides default:12
radius type:#float ui:radius default:0
)

rollout params "Parameters"
(
spinner sides "Sides" range:[3,60,12] type:#integer
spinner radius "Radius" range:[0,100,0]
button doIt "Create"
on doIt pressed do
(
lastCreated = primTemp sides:sides.value radius:radius.value --store the last object in var.
select lastCreated --select the object to match the behavior of Standard Primitives
)
--if a spinner has changed and a valid object has been created before, change it
on sides changed val do if isValidNode lastCreated do lastCreated.sides = sides.value
on radius changed val do if isValidNode lastCreated do lastCreated.radius = radius.value
)
...

Caprier
01-19-2008, 07:15 AM
Bobo, thank you again!

One last thing to make it perfect, la cerise sur le gāteau, as we say here.
Is there a way to make a scripted plugin 'remember' some of the parameter changes (not all of them)? Like when you change the heigth segments value of a box, do some other stuff, and when you go back to box creation your changes are still there.
For example, the plugin would keep in memory the last side setting but still reset the radius value to its default the next time it's used.

Caprier
01-19-2008, 02:02 PM
In MAXScript Tools and Interactions with 3ds Max > Creating MAXScript Tools > Scripted Plug-ins > Scripted Plug-in Clauses > Parameters, I read this:

"The optional type:#class specifies that the parameters in the parameter block are "class" parameters. This means that there is one copy of each parameter for the *all* the objects of this plug-in class; they all share the parameter. Examples of existing class parameters are the creation type and type-in parameters in a typical geometry primitive."

It seems to be related to my last question, though I'm not sure on how to use it. I haven't found anything else on the subject in the Reference, probably because I don't what to look for and where. I'll go and make some more tries.

If in the meantime someone feels the urge to share some valuable infos... :thumbsup:

Caprier
01-20-2008, 06:15 AM
OK. Here is the (almost) complete script that works fine (thanks for the help, Bobo!) if you remove "type:#class" from the parameter block declaration:
plugin simpleObject primDisk
name:"Disk"
category: "Custom Primitives"
classId:#(0x3573b671, 0x5751ecb6)
(
parameters main type:#class rollout:params -- <--- HERE
(
radius type:#float ui:radius default:0
segments type:#integer ui:segments default:1
sides type:#integer ui:sides default:18
)

rollout params "Parameters"
(
spinner radius "Radius" range:[0,10000,0]
spinner segments "Segments" range:[1,60,1] type:#integer
spinner sides "Sides" range:[3,240,18] type:#integer
button doIt "Create"

local prevDisk

on doIt pressed do
(
prevDisk = primDisk radius:radius.value \
segments:segments.value \
sides:sides.value
select prevDisk
)

on radius changed val do
if isValidNode prevDisk do
prevDisk.radius = radius.value
on segments changed val do
if isValidNode prevDisk do
prevDisk.segments = segments.value
on sides changed val do
if isValidNode prevDisk do
prevDisk.sides = sides.value
)

on buildMesh do
(
local vertArray = #()
local faceArray = #()
vertArray.count = sides * segments + 1
faceArray.count = sides * (2 * segments - 1)
local angleStep = 360.0 / sides
local radiusStep = radius / segments

vertArray [1] = [0,0,0]

for i = 0 to sides - 1 do
(
local theCos = cos(i * angleStep)
local theSin = sin(i * angleStep)

for j = 1 to segments do
(
local theRad = j * radiusStep
vertArray [(j - 1) * sides + i + 2] = [theRad * theCos, theRad * theSin,0]
)
)
for i = 1 to sides do
faceArray = [1,i + 1,(mod i sides) + 2]

if segments > 1 do
(
local ind = sides
for i = 0 to segments - 2 do
for j = 0 to sides - 1 do
(
faceArray [ind += 1] = [j + i * sides + 2,
j + (i + 1) * sides + 2,
(mod (j + 1) sides) + (i + 1) * sides + 2]
faceArray [ind += 1] = [(mod (j + 1) sides) + (i + 1) * sides + 2,
(mod (j + 1) sides) + i * sides + 2,
j + i * sides + 2]
)
)
setMesh mesh vertices:vertArray faces:faceArray

for i = 1 to faceArray.count do
setEdgeVis mesh i 3 false
)

tool create
(
on mousePoint click do
(
case click of
(
1: (
radius = 0
nodeTM.translation = gridPoint
)
2: #stop
)
)
on mouseMove click do
(
case click of
(
2: radius = length [gridDist.x,gridDist.y]
)
)
)
)

As it is, it seems to ignore the parameters and creates a mesh with only the center vertex.

There's nothing more about "type:#class" in the User Reference apart from what I quoted above, I've searched this forum and found one single thread that never got answered and I searched the web and found nothing.

If anyone knows what I'm talking about, could you help me? Many thanks.

CGTalk Moderation
01-20-2008, 06:15 AM
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.