Move an object to the ground

Discussions concerning plugins for SOFTIMAGE©
User avatar
Daniel Brassard
Posts: 828
Joined: 18 Mar 2010, 23:38
Location: St. Thomas, Ontario

Re: Move an object to the ground

Post by Daniel Brassard » 12 May 2016, 22:51

Hi AceMastermind,

Correct the method will not work on XSI 7.5, for that you need to change the line to:

Code: Select all

var vba = new VBArray( oSelection(oItem).ActivePrimitive.Geometry.GetBoundingBox(oTransform) );
Geometry.GetBoundingBox is valid from XSI v5.0. The other was introduced with XSI v10.0 (2012). Here is the code to test on your system.

Code: Select all

// Example using an input transform to preserve scaling and rotation
//

var oSelection = Application.Selection;
for (var oItem = 0; oItem < oSelection.Count; oItem++ ) {
   // Calculate the local transform of the object to take into account the rotation and scaling
   oTransform = oSelection(oItem).Kinematics.Local.Transform ;
   // find the bounding box using X3DObject.GetBoundingBox
   var vba = new VBArray( oSelection(oItem).ActivePrimitive.Geometry.GetBoundingBox(oTransform) );
   var jsa = vba.toArray();
   // calculate the ground translation to translate the object to ground
   var toGroundY = (jsa[4]/2) - jsa[1];
   // Translate the object to ground
   Translate(oSelection(oItem), 0, toGroundY, 0, siAbsolute, siLocal, siObj, siY, null, null, null, null, null, null, null, null, null, 0, null);
}
To test, create various objects with odd shapes, rotate some and scale some. After script execution, see if the objects have all move above ground (above plane XZ).

I did have some weird behaviour with the primitive cone. Somehow the cone is not created exactly at the origin in XSI2015. Its 2/3 unit (0.66667) up. Don't know why.
Last edited by Daniel Brassard on 14 May 2016, 03:24, edited 3 times in total.
$ifndef "Softimage"
set "Softimage" "true"
$endif

User avatar
AceMastermind
Posts: 155
Joined: 15 Jun 2009, 00:57

Re: Move an object to the ground

Post by AceMastermind » 13 May 2016, 01:11

Okay thanks, that works with all objects except the cone like you said. There should be no problem with cones if the object's bounding box data is used to ground it. My script doesn't seem to have problems with cones. :-?

User avatar
FXDude
Posts: 923
Joined: 19 Jun 2012, 21:59

Re: Move an object to the ground

Post by FXDude » 13 May 2016, 17:32

Thanks both Daniel and AceMastermind!

I'll also ping Martin and point him to this page, which could perhaps help make his M|Align become a yet more complete alignment hub!

Cheers,

User avatar
Daniel Brassard
Posts: 828
Joined: 18 Mar 2010, 23:38
Location: St. Thomas, Ontario

Re: Move an object to the ground

Post by Daniel Brassard » 13 May 2016, 20:00

OK, found errors in the code, I have corrected all previous codes to correct the errors.

I definitively prefer the new method using X3DObject.GetBoundingBox. To put object to a plane (above or below a plane at the origin) is as simple as translating - min or -max on the selected axis, values that are provided directly by the GetBoundingBox array.

Using the old method Geometry.GetBoundingBox force you to do a little bit of calculation as the min and max is not provided directly by the GetBoundingBox array (this array provides the center coordinates of the bounding box and the scales for each axis). To calculate the min and max of the BBox you then have to calculate:

min = center - (scale/2) on the respective axis (scale is the width, length or height of the BBox)
max = center + (scale/2) on the respective axis

Duh! Facepalm! @-)
$ifndef "Softimage"
set "Softimage" "true"
$endif

User avatar
Daniel Brassard
Posts: 828
Joined: 18 Mar 2010, 23:38
Location: St. Thomas, Ontario

Re: Move an object to the ground

Post by Daniel Brassard » 16 May 2016, 14:29

The Translate method is giving me more grief than anything. All the calculation are correct but the translate is not behaving. Back to the drawing board.
$ifndef "Softimage"
set "Softimage" "true"
$endif

User avatar
myara
Posts: 358
Joined: 28 Sep 2011, 10:33

Re: Move an object to the ground

Post by myara » 17 May 2016, 10:24

Hi, I'm planning to update my mAligner tool, but I'm still experimenting a little in my free time and it may take a while.

I wrote something simple for this.
I used Daniel's code as a base, but changed it to global values, so the values won't be affected by rotation, parenting or anything.

Code: Select all

// axis x=0, y=1, z=2
axis = 1
negative = 0

var oSelection = Application.Selection;
for (var i = 0; i < oSelection.Count; i++ ) {
   obj = oSelection(i)
   
   // Calculate the Global transform so the values won't be affected by SRT or parent values
   oTransform = obj.Kinematics.Global.Transform;
   
   // find the bounding box using Geometry.GetBoundingBox
   var jsa = obj.ActivePrimitive.Geometry.GetBoundingBox(oTransform).toArray()

   // Calculate the ground translation to translate the object to ground
   toGround = jsa[axis+3]/2 - jsa[axis]
   if (axis == 0)   oItemPos = obj.Kinematics.Global.posx.value
   if (axis == 1)   oItemPos = obj.Kinematics.Global.posy.value
   if (axis == 2)   oItemPos = obj.Kinematics.Global.posz.value
   toGround = oItemPos + toGround
   
   // Just in case you want it in the Negative part of the Axis
   if (negative)    toGround = - toGround
   
   // Translate the object to ground with OM
   if (axis == 0)   obj.Kinematics.Global.posx = toGround
   if (axis == 1)   obj.Kinematics.Global.posy = toGround
   if (axis == 2)   obj.Kinematics.Global.posz = toGround

   /* or if you want to use Translate Command:
   if (axis == 0)   Translate(obj, toGround, 0, 0, siAbsolute, siGlobal, siObj, axis+1);
   if (axis == 1)   Translate(obj, 0, toGround, 0, siAbsolute, siGlobal, siObj, axis+1);
   if (axis == 2)   Translate(obj, 0, 0, toGround, siAbsolute, siGlobal, siObj, axis+1);
   */
}
Hope it helps.

BTW, It doesn't have any error handling code.
M.Yara
Character Modeler | Softimage Generalist (sort of)

User avatar
myara
Posts: 358
Joined: 28 Sep 2011, 10:33

Re: Move an object to the ground

Post by myara » 17 May 2016, 10:55

Daniel Brassard wrote:P.S. Does anybody know how to force the radio button in the AddEnumControl to be on the same row?
Not possible AFAIK

What I do is to use checkboxes and write a few OnChanged events for those boxes.
It takes to write quite a few lines, but it works.

Code: Select all

var oPPG = XSIFactory.CreateObject("CustomProperty")
oPPG.Name = "RestOnGround" ;

oPPG.AddParameter2( "CenterX", siBool, true ) ;
oPPG.AddParameter2( "CenterY", siBool, false );
oPPG.AddParameter2( "CenterZ", siBool, false );

oLayout = oPPG.PPGLayout

oLayout.AddRow()
oItem = oLayout.AddItem("CenterX")
oItem = oLayout.AddItem("CenterY")
oItem = oLayout.AddItem("CenterZ")
oLayout.EndRow()


oLayout.Language = "JScript" ;
oLayout.Logic = CenterX_OnChanged.toString()
                +CenterY_OnChanged.toString()
                +CenterZ_OnChanged.toString()
                +OnInit.toString()
                ;
                
function OnInit(){
    LogMessage ("Initializing PPG")
}

function CenterX_OnChanged(){
    PPG.CenterX = 1;
    PPG.CenterY = 0;
    PPG.CenterZ = 0;
}
function CenterY_OnChanged(){
    PPG.CenterX = 0;
    PPG.CenterY = 1;
    PPG.CenterZ = 0;
}
function CenterZ_OnChanged(){
    PPG.CenterX = 0;
    PPG.CenterY = 0;
    PPG.CenterZ = 1;
}

InspectObj(oPPG)
M.Yara
Character Modeler | Softimage Generalist (sort of)

User avatar
Daniel Brassard
Posts: 828
Joined: 18 Mar 2010, 23:38
Location: St. Thomas, Ontario

Re: Move an object to the ground

Post by Daniel Brassard » 17 May 2016, 14:43

Thanks Martin for your input. I was thinking going the route of the object model Kinematic, you just confirm my thought. Maybe my problem, from start is using local vs global. I'll try that.

Neet code btw.

Cheers,
$ifndef "Softimage"
set "Softimage" "true"
$endif

User avatar
AceMastermind
Posts: 155
Joined: 15 Jun 2009, 00:57

Re: Move an object to the ground

Post by AceMastermind » 17 May 2016, 20:45

myara wrote:-snip-
Thanks! I noticed it has a problem with cones going negative on Y.

Code: Select all

// axis x=0, y=1, z=2
axis = 1
negative = 1

User avatar
myara
Posts: 358
Joined: 28 Sep 2011, 10:33

Re: Move an object to the ground

Post by myara » 18 May 2016, 07:28

AceMastermind wrote: Thanks! I noticed it has a problem with cones going negative on Y.
That's because it only calculates the Bounding Box, not the real points positions.

If you change your viewport to Bounding Box you will notice that it was aligned properly.

It can be done with the real positions of the points but it would be much slower in high poly objects.
I was already writing a similar function to get the max and min points, so I added it to have a precise Mode to the tool.

Code: Select all

/*----------------------
axis : x=0, y=1, z=2
negative = 1  (boolean) If true(or 1) it will be aligned to the negative side of the axis
preciseMode = 1 (boolean) If true(or 1) it will calculate all the points positions instead of the Bounding Box data
----------------------*/
axis = 1
negative = 0
preciseMode = 1

var sel = Application.Selection;
toTheGround(sel, axis, negative, preciseMode)

function toTheGround(sel, axis, negative, preciseMode){
    openUndo('toTheGround')
    for (var i = 0; i < sel.Count; i++ ) {
        obj = sel(i)

        // Calculate the Global transform so the values won't be affected by SRT or parent values
        oTransform = obj.Kinematics.Global.Transform;

        if (preciseMode == 0){
            // find the bounding box using Geometry.GetBoundingBox
            var jsa = obj.ActivePrimitive.Geometry.GetBoundingBox(oTransform).toArray()

            // Calculate the ground translation to translate the object to ground
            toGround = jsa[axis+3]/2 - jsa[axis]
        }
        else{
            //Get The real point position
            bb = getMaxMinPos(obj)
            toGround = - bb[(axis + 3)]
        }
        if (axis == 0)   oItemPos = obj.Kinematics.Global.posx.value
        if (axis == 1)   oItemPos = obj.Kinematics.Global.posy.value
        if (axis == 2)   oItemPos = obj.Kinematics.Global.posz.value
        toGround = oItemPos + toGround

        // Just in case you want it in the Negative part of the Axis
        if (negative)    toGround = - toGround

        // Translate the object to ground with OM
        if (axis == 0)   obj.Kinematics.Global.posx = toGround
        if (axis == 1)   obj.Kinematics.Global.posy = toGround
        if (axis == 2)   obj.Kinematics.Global.posz = toGround
    }
    closeUndo()
}

function getMaxMinPos(obj){
    x =[]
    y =[]
    z =[]
    points = obj.ActivePrimitive.Geometry.Points
    for ( var i=0, a = points.Count; i < a; i++ ) {
        point = points(i)
        LocalPos = point.Position
        GlobalPos = XSIMath.MapObjectPositionToWorldSpace(obj.Kinematics.Global.Transform, LocalPos)
        x.push (GlobalPos.x)
        y.push (GlobalPos.y)
        z.push (GlobalPos.z)
    }
    maxX = Math.max.apply(Math, x)
    maxY = Math.max.apply(Math, y)
    maxZ = Math.max.apply(Math, z)
    minX = Math.min.apply(Math, x)
    minY = Math.min.apply(Math, y)
    minZ = Math.min.apply(Math, z)
    return [maxX, maxY, maxZ, minX, minY, minZ]
}
Now that I think about it, it would be probably faster and simpler if we use the Align command and then move them all to the ground.
M.Yara
Character Modeler | Softimage Generalist (sort of)

User avatar
Daniel Brassard
Posts: 828
Joined: 18 Mar 2010, 23:38
Location: St. Thomas, Ontario

Re: Move an object to the ground

Post by Daniel Brassard » 18 May 2016, 20:14

Agreed, so far the least problematic method is the Align command.

The cone primitive has a weird construct that does not behave like any of the other primitives, it is created 2/3 up in Y not at the origin. The BBox min report correctly around the object but moving the object to ground drop the primitive to 0.666 above ground not zero, like the bounding box is not linked to the object. I checked the cone and it has been like that for a long time (I checked as far as v5.0). Its the same with the cone surface as well.

Test this on a new scene:

1. create a primitive cone of height 4 units
2. Switch to front view and zoom in
3. Compare the cone to the grid.

You would anticipate the cone to be 2 units down and 2 units up in Y with the centre at the origin but its not, it's 1.3 unit down and 2.6 units up in Y. Maybe there is a logical explanation for this.

I still can't find why the cone does not drop to ground like the other primitives. Weird.
$ifndef "Softimage"
set "Softimage" "true"
$endif

User avatar
Daniel Brassard
Posts: 828
Joined: 18 Mar 2010, 23:38
Location: St. Thomas, Ontario

Re: Move an object to the ground

Post by Daniel Brassard » 19 May 2016, 04:30

OK, I found the cause of the weird behaviour, I need to take into account the location of the pivot. More and more the Align method look the way to go to keep it simple.
$ifndef "Softimage"
set "Softimage" "true"
$endif

User avatar
myara
Posts: 358
Joined: 28 Sep 2011, 10:33

Re: Move an object to the ground

Post by myara » 19 May 2016, 07:24

You need to calculate the pivot in all cases to do the translation correctly, not only the cone. I'm doing that in my code by the way.

The problem with the cone and any other object that isn't a cube is that well, the bounding box is a cube. While the cone has a pointed end in the top, the bounding box doesn't, so the calculations will be based on the invisible box corner.

So if you want precision, you'll have to iterate all points positions.

And If you use Align you would still need to calculate the max/min position in at least one object.

I did a test with my previous code:

100 Objects
160,000 Tris
80,000 Points
= 3.358 seconds

Not bad considering the amount of calculation needed.
But still, Align should be much faster, so I wrote a version based on Align.

Code: Select all

// JScript

/*----------------------
axis : x=0, y=1, z=2
negative = 1  (boolean) If true(or 1) it will be aligned to the negative side of the axis
----------------------*/
var axis = 1
var negative = 0
var sel = Selection;

toTheGround(sel, axis, negative)

function toTheGround(sel, axis, negative){
    // Create Undo (2012 and later)
    var xsiVer = Application.Version().split(".")[0]
    if (xsiVer >= 10)     openUndo('toTheGround')
    
    // Put Only the first object to the ground
    // The rest will be aligned with the Align command
    var obj = sel(0)

    // Calculate the Global transform so the values won't be affected by SRT or parent values
    var oTransform = obj.Kinematics.Global.Transform;
    
    //Get the real minimun point position (not BBox)
    var bb = getMaxMinPos(obj)
    var toGround = - bb[(axis + 3)]

    // Calculate the distance the object has to be moved
    if (axis == 0)   oItemPos = obj.Kinematics.Global.posx.value
    if (axis == 1)   oItemPos = obj.Kinematics.Global.posy.value
    if (axis == 2)   oItemPos = obj.Kinematics.Global.posz.value
    toGround = oItemPos + toGround

    // Just in case you want it in the Negative part of the Axis
    if (negative)    toGround = - toGround

    // Translate the object to ground with OM
    if (axis == 0)   obj.Kinematics.Global.posx = toGround
    if (axis == 1)   obj.Kinematics.Global.posy = toGround
    if (axis == 2)   obj.Kinematics.Global.posz = toGround
    
    
    // Prepare the variables to adjust the Align arguments
    // siAlignGMIN is 0 and siAlignGMAX is 2, so we need to add 2 when doing negative axis calculations
    var neg = negative *2
    
    // while siX, and siY values are 1 and 2, siZ value is 4, so we need an exception
    if (axis == 2)  axis = 3
    
    // Now, align the rest of objects
    Align( sel, axis+1, 0+neg, 0+neg, 0+neg, 3+neg, 3+neg, 3+neg)
    
    // Close Undo
    if (xsiVer >= 10)     closeUndo()
}

function getMaxMinPos(obj){
    // Create arrays
    var x =[];    var y =[];    var z =[];
    
    // Get All Points
    var points = obj.ActivePrimitive.Geometry.Points
    
    // Iterate All Points to get their Positions
    for ( var i=0, a = points.Count; i < a; i++ ) {
        var point = points(i)
        
        // Get Local Position
        var LocalPos = point.Position
        
        // Convert Local Position to Global
        var GlobalPos = XSIMath.MapObjectPositionToWorldSpace(obj.Kinematics.Global.Transform, LocalPos)
        
        // Gather All Points positions to x, y, z arrays
        x.push (GlobalPos.x)
        y.push (GlobalPos.y)
        z.push (GlobalPos.z)
    }
    
    // Get the Max and Min values
    var maxX = Math.max.apply(Math, x)
    var maxY = Math.max.apply(Math, y)
    var maxZ = Math.max.apply(Math, z)
    var minX = Math.min.apply(Math, x)
    var minY = Math.min.apply(Math, y)
    var minZ = Math.min.apply(Math, z)
    
    return [maxX, maxY, maxZ, minX, minY, minZ]
}
Hmm I Like it. I'll put it into mAligner soon.
M.Yara
Character Modeler | Softimage Generalist (sort of)

User avatar
Daniel Brassard
Posts: 828
Joined: 18 Mar 2010, 23:38
Location: St. Thomas, Ontario

Re: Move an object to the ground

Post by Daniel Brassard » 19 May 2016, 14:22

Thanks Martin for the help and the script.

I agree about the pivot. The cone was not giving me enough info to see where I was mistaken until I loaded the TRex. Then, it became obvious that the pivot location on the object was to be taken into consideration.

The BBox technique is good enough if the intent is to align object against one of the main axis (which is what the originator was looking for) as we are including scale/rotation and pivot into the script. The BBox calculation does provide the min and max of the object in the axis we are looking for after scale and rotation (and pivot) has been applied.

It would be different if we were doing collision detection, penetration detection, aligning against planes at odd angles, or aligning against a polygon face of a reference object. Then the BBox technique is one of the early stage of detection to speed the calculation process (BBox, BSphere, BCapsule, etc.) which then goes into various algorithms to improve the detection or to align the object to a reference plane.

Keep on the good work on MAligner.

Cheers,
$ifndef "Softimage"
set "Softimage" "true"
$endif

User avatar
wireframex
Posts: 232
Joined: 08 Jun 2009, 23:02
Location: Near Paris

Re: Move an object to the ground

Post by wireframex » 22 May 2016, 22:00

Thank you for all :)
"without mastery, power is nothing" - Softimage Addict User

User avatar
myara
Posts: 358
Joined: 28 Sep 2011, 10:33

Re: Move an object to the ground

Post by myara » 23 May 2016, 13:27

A little Offtopic.

About the ControlRadio, I was writing something today and found an old tool I wrote and I was using horizontal Radio buttons.

This is the PPG code:

Code: Select all

defaultValue = "X"

//--------------------------------------


var oProp = XSIFactory.CreateObject ( "CustomProperty" );
oProp.name = "fixDirection";
oProp.AddParameter2 ( "axis", siString, "");

var oLay = oProp.PPGLayout;
oLay.AddGroup( );
    oLay.AddRow();  
    var oItem = oLay.AddEnumControl( "axis" , Array("X","X"), "", siControlRadio );
        oItem.SetAttribute( "NoLabel", true );
    var oItem = oLay.AddEnumControl( "axis" , Array("Y","Y"), "", siControlRadio );
        oItem.SetAttribute( "NoLabel", true );
    var oItem = oLay.AddEnumControl( "axis" , Array("Z","Z"), "", siControlRadio );
        oItem.SetAttribute( "NoLabel", true );
    var oItem = oLay.AddEnumControl( "axis" , Array("-X","-X"), "", siControlRadio );
        oItem.SetAttribute( "NoLabel", true );
    var oItem = oLay.AddEnumControl( "axis" , Array("-Y","-Y"), "", siControlRadio );
        oItem.SetAttribute( "NoLabel", true );
    var oItem = oLay.AddEnumControl( "axis" , Array("-Z","-Z"), "", siControlRadio );
        oItem.SetAttribute( "NoLabel", true );
    oLay.EndRow();
oLay.EndGroup( );

oProp.axis = defaultValue

oLay.Language = "JScript";


try { 
    InspectObj ( oProp, null, null, siModal );
    logmessage ( oProp.axis.value )
}
catch(e){
    logmessage ("Cancelled")
}
M.Yara
Character Modeler | Softimage Generalist (sort of)