Community Script help needed

Hey guys!

I firstly need to mention that I’m completely new to maxscript however not scripting in general as I have some experience with uScript! although that is about it :(:

I’m not sure if the user Mark Dygert is on these forums but he has a lovely script which adjusts a models pivot point in 3ds max so that it’s in the center of the mesh and at the bottom… then it moves the mesh to 0,0,0 on the grid so that it’s ready for export!

I was actually quite successful in doing this myself which was good however I got stuck with the moving of the pivot point to the bottom of the mesh!

This is the code that I’m using as a reference by Mark…

macroScript ToWorldZero
category:"VigTools"
toolTip:"To World Zero"
buttonText:"Zero"
(--Sets the pivot to the lowest point in a mesh, then center the object to world zero
if $ != undefined then    
    (
        ResetPivot $
        CenterPivot $
        for i in selection do 
            (
                i.pivot = [i.pivot.x, i.pivot.y, i.min.z]
            )         
        $.pos = [0,0,0]
    )
else
    (
        messageBox "Select an object then run this script again." title:"Selection Error" beep:true
    )
)

Like I said when I was new to maxscript, I can’t seem to find what part of this code places the pivot at the bottom… I have tried different parts of this but no avail…

Hopefully someone can point me in the right direction


        for i in selection do 
            (
                i.pivot = [i.pivot.x, i.pivot.y, i.min.z]
            )

It looks like this is the magical line. Basically it’s looping through everything in your selection and adjusting the pivot’s Z axis to the min of the current selected object. And to do this we use i.min.z.

Hopefully this helps :slight_smile:

Thanks for the reply man =) appreciated… I thought this as well… Only seems to work when I load his entire script in… I done a test were I copied just that code that I pasted in the 1st time to see if it worked however it didn’t…

I will have a play about it and try to implement it with the rest of my tool.

Many thanks Ryan

First I’ll say, this is not a well written maxscript (apologies to Vig). So you may want to find better scripts to work off of. The use of ‘$’ is highly discouraged because it is very unclear how it is used. So that’s your first hurdle.

The second hurdle is the use of those Pivot methods. Those pivot methods are far too high level and non-explicit for good coding use. They hide relatively simple mathematical principles behind too high-level abstract concepts that make things much more confusing then they are. So let’s go into what’s going on here:

ResetPivot $
For all selected objects, set the pivot’s rotation to be aligned to the world (so if an object is rotated, this will ‘unrotate’ the pivot).

CenterPivot $
For all selected objects, set the pivot’s position to be at the center of the object’s bounding box.

for i in selection do
i.pivot = [i.pivot.x, i.pivot.y, i.min.z]

For each selected object, set the pivot to be at the bottom of the bounding box (.min.z), keeping its position in x and y (so centered width/length, bottom of the bounding box’s height). However .pivot can work funny so I doubt this would work without the above 2 lines.

$.pos = [0,0,0]
Move each object to have its pivot at the origin.

If you were interested in solving this problem explicitly and mathematically (as well as with less lines of code), you’d look into the objectoffset matrix in the mxs help. Understanding these sorts of key concepts early on is the most useful thing you can do to help your scripting career- this is the difference between being a coder and a ‘hacker’.

thanks man! hard to get what your saying because I’m soo new to this scripting stuff… Will see if I can improve it!

Thanks again Rob

Rob’s point about the use of $ is a good one. You really should avoid using it.

$ refers to the currently selected object. But which object that is, particularly, can change unexpectedly. It can change either through user action, intentionally through your script, or as an unintentional side-effect of your script. Would the change be obvious in any of these cases? No! Yikes!

It’s best to identify, right away, the object you’ll be operating upon and assign it to a proper variable. Then, any changes to the contents of that variable will have to be made explicitly, so you (and anyone reading your code) will know when the change happens.

You can use $ in the script listener as you’re testing things out, but it’s scary otherwise. Avoid!

For that matter, “selection” is just like $. I don’t like to see it except when you copy its contents to a new array that is under your control.

Say you’ve got 40 objects selected when you execute this:

for s in selection do
(
[INDENT]select s
)[/INDENT]

How many iterations do you think you’ll do? 40? Nope. 1. Because selection is dynamic, after the first run-through the loop, the selection only contains one object, and you’ve already iterated over it.

Of course, no one would use this specific example. But what if you call a function in the middle of your loop that you don’t expect to affect the selection, but does? Execution ends before you expect, without all your objects getting processed. Or, and this could be worse, an object you didn’t expect to get processed, does!

I kind of agree with Rob about the use of ResetPivot et al. To clarify his point, let’s look at a simpler example.

Given two integers, a and b, what would you rather see in your script?

c = a + b

[INDENT]-or-

c = AddOperatorForInts a b[/INDENT]

Let’s say AddOperatorForInts actually returns a + b, but you don’t know that unless you look it up in the docs. So the second example does exactly the same thing as the first, but obfuscates your code. Not so great.

Rob’s arguing that ResetPivot and CenterPivot are essentially equivalent to AddOperatorForInts, and that you ought to just do the math in situ.

I think the code reads just fine with ResetPivot in there, but I also see Rob’s point. My opinion hinges on his use of the word “relatively” in “relatively simple mathematical concepts.” Ultimately it’s relative to you, your skill level, the skill level of the people you expect to inherit your work, and your own take on the issue. If reading the math is more confusing than reading ResetPivot, stick with what you have.

In many cases, breaking things down into steps that take more lines of code is actually a better coding practice. Not only will you have more opportunities to check your assumptions as you debug, catching more mistakes, but your train of thought will be easier to follow for others reading the code. Unless performance is an indicator, I’d prefer code that breaks things down into smaller steps and explains itself along the way.

I do get the inkling that we need to focus a little more on helping you with your specific issue, though. What exactly is the code you tried to copy/paste that didn’t work?

thanks I got the script working but will try to implement what you guys have been saying…

Also, you mean that ‘selection’ instead of $ will work fine?

This is really only meant to be used on one model at a time… not multiple objects.

Ha! No, I mean ‘selection’ is almost as bad as $!

What you should be doing is verifying that the selection only contains a single object (since you want to operate on one object at a time), then assigning that object to a variable. Don’t use ‘selection’ any more than you absolutely have to, and don’t use $ at all.

okay, gotcha! thanks again!

Edit: I said “thanks man” I’m soo sorry… It’s been a long night!

I’d also like to say thanks - this thread makes me realize I have yet a long way to go and puts a little more fuel in my fire to continually improve. :slight_smile:

Just saw this thread,

Normally you might not have a tool that would have this alone in it (would normally combine it with other tools), but if it’s of any use, here’s a quick script.

Feel free to dig through it. It’s short and simple. If you were going to write something with more operations and options, you’d begin to “methodize” things (or more correctly for Maxscript, turn operations into functions), and then perhaps group common things in a struct.

As for selection, $, $selection, etc. Long story short, selection is a Max ObjectSet just like lights, objects, etc.

If you wanted to just work on one object that you have selected, you could use selection[1] to ensure you’re only working on one object. However, you can also “capture” your currently selected objects in an array using,


local myObjectsToWorkOn = selection as array

Anyway, here’s a quick “widget” to use if you’d like,


/*
===============================
Simple example script for tech-artists.org
by Jonathan Ferguson
===============================
*/

(
rollout roPivotShop "tech-artists.org :: Pivot Widget"  category:1
(
	button btnMovePivot "Effect Pivot" tooltip: "Effects the pivot point based on the selected options."
	group ""
	(
		radiobuttons rbMovePivotTo labels:#("Center", "Base", "Top") columns:3
		checkbox cbMoveToOrigin "Move to world origin" tooltip:"If checked, each object will be moved to [0,0,0]." align:#center
	)

	
	on btnMovePivot pressed do
	(
		if ( selection.count > 0 ) then
		(
			for o in selection do
			(
				if (rbMovePivotTo.state == 1) then
					o.pivot = o.center
				
				if (rbMovePivotTo.state == 2) then
					o.pivot = [o.center.x, o.center.y, o.min.z]
				
				if (rbMovePivotTo.state == 3) then
					o.pivot = [o.center.x, o.center.y, o.max.z]
				
				if (cbMoveToOrigin.checked) then
					o.pos *= [0,0,0]
			)
		)
		
		else
			messageBox "Please select at least one object first." beep:false
	)
)

createDialog roPivotShop width:225 style:#(#style_toolwindow, #style_resizing, #style_sysmenu) bitmap:
	undefined
)


Of course, there’s an infinite amount of ways to write this (from using case statements, having a generic functions and passing parameters into it, having a stack of else/if tests, etc.).

Thanks mate for that!! Will have a look at it in max when I’m back on my computer!! Will definitely be making adjustments to my script, >.<