[Closed] 'Create' button for primitives ?
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.
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?
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.
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?
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…
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 [i][b]--declare a variable to remember the last created object[/b][/i]
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 [i][b]--store the last object in var.[/b][/i]
select lastCreated [i][b]--select the object to match the behavior of Standard Primitives[/b][/i]
)
[b][i]--if a spinner has changed and a valid object has been created before, change it[/i][/b]
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
)
...
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.
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…
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 [i]=[/i] [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.