Goodbye to All That (2014)
9 years ago
function pointNormalIntersect2D
oA --Origin A
oB --Origin B
vA --Vector #1.
vB --Vector #2.
&P --position output
=(
nA = normalize(vA);
nB = normalize(vB);
--oA --origin A
--nA --normal A
--sA --scalar A
--oB --origin B
--nB --normal B
--sB --scalar B
--oA+(nA*sA)==oB+(nB*sB);
--oA.x+(nA.x*sA)==oB.x+(nB.x*sB); //MULTIPLY BY nB.y
--oA.y+(nA.y*sA)==oB.y+(nB.y*sB); //MULTIPLY BY nB.x
--
--(nB.y*oA.x)+(nB.y*nA.x*sA)==(nB.y*oB.x)+(nB.y*nB.x*sB);
--(nB.x*oA.y)+(nB.x*nA.y*sA)==(nB.x*oB.y)+(nB.x*nB.y*sB);
--[ c1 ] [ c2 ] [ c3 ] [ c4 ]
--
--MINUS THEM: c4 cancels out.
c1 = (nB.y*oA.x)- (nB.x*oA.y);
--c2 = (nB.y*nA.x*sA)-(nB.x*nA.y*sA);
c3 = (nB.y*oB.x)- (nB.x*oB.y);
--New Equation is:
--c2 = c3 - c1;
c3c1 = c3 - c1; --c3 minus c1;
--Figure out how many sA's you have by combining like terms;
sAcount = (nB.y*nA.x)-(nB.x*nA.y);
--New Equation is:
--sAcount*sA = c3c1;
--calculate sA:
sA = c3c1/sAcount;
--once scalar sA is found, plug it into point normal form to find intersection.
P=[0,0,0];
P.x = oA.x+(nA.x*sA);
P.y = oA.y+(nA.y*sA);
)--[FN:pointNormalIntersect]
oA = $v1A.pos;
oB = $v2A.pos;
vA = $v1A.pos - $v1B.pos;
vB = $v2A.pos - $v2B.pos;
P = [0,0,0];
pointNormalIntersect2D oA oB vA vB &P;
$intPos.pos = P;
UVWrella
UNFoLD3D
RoadKill
UVlayout
myObject = $;
srchID=Skin.ClassID; --Search for skin modifier in MyObject.
For m = 1 to myObject.modifiers.count do(
cmID = myObject.modifiers[m].ClassID;
modFound=False;
if(cmID[1]==srchID[1])then(
if(cmID[2]==srchID[2])then(
modFound=True;
)--[x]
)--[x]
if(modFound)then(
Print("Your Modifier is index#" + m as string);
)else(
print(cmID as string + "!=" + Skin.ClassID as string);
)--[x]
)--[Next m]
Iterate through objects in scene with these object sets:
[1] objects -- all the objects
[2] geometry -- the standard 3ds Max categories...
[3] lights
[4] cameras
[5] helpers
[6] shapes
[7] systems
[8] spacewarps
[9] selection -- the current selection
Use these to iterate through all of one type of object in maxscript:
Example:
For geo in geometry do(
geo.pos = [0,0,0];
)--[x]
Summary of video coverage:
[1]: Globals in MacroScripts are not quite what they seem.
[2]: GlobalVars.Remove is potentially hazardous.
[3]: Checking to see if an object exists by using:
if(theObj==Undefined)then(.....)
IS NOT THE COMPLETE SOLUTION.
V1 = [1,1,1]; ----------Vector #1
V2 = [-1,-1,-1]; -------Vector #2
nV1 = normalize(V1); ----Normalized Vector #1.
nV2 = normalize(V2); ----Normalized Vector #2.
dir = dot nV1 nV2; ------Dot product of normalized vectors.
print("dot ==" + dir as string);
--Negative 1 = Anti-Parallel.
--ZERO = Perpendicular/Orthagonal
--Positive 1 = Parallel.
--Does anyone know if there is a word for "Non-Integer" besides "Float"?
V1 = [1,2,3]; ----------Vector #1
V2 = [-1,-2,-3]; -------Vector #2
dir = cross V1 V2 -------Cross of both.
print("cross==" + dir as string);
--If cross==[0,0,0] then you have
--parallel or anti-parallel vectors
--or one or more vectors has a length of zero
--Only vector with length of zero: [0,0,0];
Say we want to align the Z-axis of an object GEOM
to a line between objects cp1 and cp2.
Step1: Create a normalized vector:
P01 = cp1.pos; --Original Start Point.
P02 = cp2.pos; --Original End Point.
PVEC = P02-P01; --Point from start point to end point.
PLEN = length(PVEC); --Length of vector.
NPV = PVEC / PLEN; --Normalized PVEC
Step2: Lets say that the orientation of cp1 is also going to be used for
twist control. Specifically that, the twist of the objects aligned to the
line will be controlled by the X-Axis of cp1.
--The initial X-Axis may not be perpendicular to the line cp1-cp2.
--Take the cross between the bent X and the Z-Axis (NPV) to get Yaxis.
bentX = (Normalize cp1.transform.row1);
Yaxis = Normalize(cross NPV bentX);
--Use the good Y-axis and Z-Axis (NPV) to get a perpendicular Xaxis.
Xaxis = Normalize(cross NPV Yaxis);
GEOM.Transform = Matrix3 Xaxis Yaxis NPV P01 --Objects new transform.
-- x y z position
Take it Further:
To take this further, you could slide the object up and down the line
by using point-normal form.
Example: Put aligned object in MIDDLE of the line:
midPosition = cp1 + NPV*(PLEN/2)
Example: slide aligned object from start of line to end of line:
for i = 0 to 1 do( --0 to 1 as in Zero to 100%.
istr = i as string;
midPosition = cp1 + NPV*(PLEN*istr)
)--[x]
-- 1 2 3 4 5 6 7 8 9 10
--34567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
--
--********************************************************************
--********************************************************************
--MORE FREE SCRIPTS AT: http://www.youtube.com/user/PolygonRunner ****
--********************************************************************
--********************************************************************
--
--What it does:
--[1]: Registers callback when you open the file and move the time slider.
--[2]: UnRegisters when you open a different max file.
--
--How to use:
--[1]: Put code into a script controller of a dummy object.
--[2]: Make sure last line of script controller is:
-- : [0,0,0], or other valid point3 if position script.
-- : quat 0 0 0 1, or other quaternion if rotation script.
-- : ect...
--[3]: If you want, you can also edit this code to handle
-- "when constructs" and "change handlers".
--
--My personal application:
--[1]: Use to force update script controllers that are not
-- Executing in the correct order.
--
Global MFP_586_214_3958; --MFP = "Max File Path";
Global BFN_586_214_3958; --BFN = "Bound Function";
if (MFP_586_214_3958 != maxFilePath)then(
MFP_586_214_3958 = maxFilePath;
Function BindThisFunction
=(
--Need code to un-register itself when the maxFilePath changes.
if(MFP_586_214_3958 != maxFilePath)then(
--Must remove globals so that will re-register
--If you re-open this file or another file with this
--script in it.
unRegisterRedrawViewsCallback BFN_586_214_3958;
MFP_586_214_3958 = undefined;
globalVars.remove "MFP_586_214_3958";
globalVars.remove "BFN_586_214_3958";
print("Unregistering Callback");
)else(
print("Callback executed on frame" + currentTime as string);
--**********YOUR CALLBACK CODE HERE***********
--**********YOUR CALLBACK CODE HERE***********
--**********YOUR CALLBACK CODE HERE***********
)--[x]
)--[FN:BindThisFunction]
--Bind function to global variable, else the function will
--Not be capable of unRegistering itself.
BFN_586_214_3958 = BindThisFunction;
--Register the function as a callback.
registerRedrawViewsCallback BFN_586_214_3958;
print("CallBack has been registered");
)--[END]
-- 1 2 3 4 5 6 7 8 9 10
--34567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
-- 1 2 3 4 5 6 7 8 9 10
--34567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
--This code will demo how to use my multi-pick-button.
--Works like normal pick button, but allows you to select multiple objects.
--
-- ***************************************************************************
-- ***************************************************************************
-- Want more free scripts?: http://www.youtube.com/user/PolygonRunner ******
-- ***************************************************************************
-- ***************************************************************************
rollout MainRollout "Multi-PickButton Demo"(
local RV_ArrayNodes = #(); --Array of nodes selected by button ArrayPick
button ArrayPick "Select Objects, THEN click me.";
--The list box displays what you currently have selected by the ArrayPick button.
listbox LB01 "Influencing Objs"
height:5; --5 lines tall.
ReadOnly:True; --Read Only List.
--Control for MULTI-OBJECT PICK BUTTON
on ArrayPick Pressed do(
if selection.count == 0 then(
if(LB01.items.count==0)then(
ArrayPick.Caption = "LIST EMPTY";
)else(
ArrayPick.Caption = "List Unchanged";
)--[x]
)else(
MSTAT = #no; --Do not append;
if (LB01.items.count > 0)then(
mstatText = "";
mstatText = mstatText + "[YES]: Append to previous selection." + "\n";
mstatText = mstatText + "[NO]: Replace old selection." + "\n";
mstatText = mstatText + "[CANCEL]: Don't do anything." + "\n";
MSTAT = yesNoCancelBox mstatText \
title:"Append or Replace?" beep:False;
print(MSTAT);
)--[x]
if (MSTAT!=#cancel)then(
nodeNameArray = #();
--If #no: Create New Selection.
if(MSTAT==#no)then(
RV_ArrayNodes = #();
for i = 1 to selection.count do(
nodeNameArray[i] = selection[i].name;
RV_ArrayNodes[i] = selection[i];
)--[X]
ArrayPick.Caption = "List Populated";
LB01.items = nodeNameArray;
print("Rv array nodes===" + RV_ArrayNodes as string);
)--[#no]
--If #yes: Append to Original selection.
if(MSTAT==#yes)then(
nodeNameArray = LB01.items;
for i = 1 to selection.count do(
APN = appendIfUnique RV_ArrayNodes selection[i];
if(APN)then(append nodeNameArray selection[i].name);
)--[X]
ArrayPick.Caption = "List Appended To";
LB01.items = nodeNameArray;
print("Rv array nodes===" + RV_ArrayNodes as string);
)--[#yes]
)--[MSTAT!=#cancel]
)--[Selection.count]
)--[ArrayPick Pressed]
)--[End Rollout]
CreateDialog MainRollout;
-- 1 2 3 4 5 6 7 8 9 10
--34567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
Function ReAssignPivot
inputTransform --New Transform Matrix To Be Pivot
&theObject --The Object to Act on.
--Function Description:
--Gives the object a new pivot, defined by a transform matrix.
--Does so WITHOUT affecting the world position of the original geometry.
--
--**************************************************************
-- More Free Scripts: www.ProofOfProgress.BlogSpot.com ***
--**************************************************************
--
=(
--VLM = Visible Local Matrix.
--The matrix/pivot you see when selecting object and "Local" axis is selected as viewable.
VLM = theObject.Transform;
IP_LocalRot = theObject.objectOffsetRot; --Rotation to be used later.
IP_LOCAL = theObject.objectOffsetPos; --Invisible Pivot Local coordinates
--In relation to VLM matrix.
IP_WORLD = IP_LOCAL * VLM; --World Coordinates of Invisible Pivot. [Local To World Transform]
VLM_0 = inputTransform; --Reset Visible Local matrix coordinates.
NEW_IP_LOCAL = IP_WORLD * inverse(VLM_0); --[World To local Transform]
theObject.Transform = VLM_0;
theObject.objectOffsetPos = NEW_IP_LOCAL;
--Now Handle Rotation:
--Since rotation of visible local matrix has been zeroed out,
--You must add that loss to the invisible pivot rotation.
RotationLoss = VLM.RotationPart - VLM_0.RotationPart;
GeomWorldRot = RotationLoss + IP_LocalRot;
theObject.objectOffsetRot = GeomWorldRot;
)--[FN:ReAssignPivot]
--Test Code;
--This test code will align SomeCircle's pivot to PivotObject's Pivot.
inputTransform = $PivotObject.Transform;
affectedObject = $SomeCircle;
ReAssignPivot inputTransform &OBJ;
This script moves the pivot of an object to the world and aligns it without
affecting the original location of the object geometry.
Some things I learned about max's internals from doing this:
1. theObject.objectOffsetPos is the LOCAL coordinates of the objects
"Invisible" axis. Plug those Local coordinates into the objects
transform matrix to get the WORLD coordinates of the objects
"Invisible" axis.
Example:
LocalCoords = theObject.objectOffsetPos;
TheMatrix = theObject.Transform;
WorldSpaceCoords = LocalCoords * TheMatrix;
2. The Objects VISIBLE pivot is a Matrix.
The Objects INVISIBLE pivot is a quaternion and point3 position.
This is self-Evident when you mess around.
Function alignPivotToWorld
&theObject
--Aligns the pivot to the world WITHOUT affecting the geometry.
--
--*****************************************************
--More Scripts at: www.ProofOfProgress.BlogSpot.com **
--*****************************************************
--
=(
--VLM = Visible Local Matrix.
--The matrix/pivot you see when selecting object and "Local" axis is selected as viewable.
VLM = theObject.Transform;
IP_LocalRot = theObject.objectOffsetRot; --Rotation to be used later.
IP_LOCAL = theObject.objectOffsetPos; --Invisible Pivot Local coordinates
--In relation to VLM matrix.
IP_WORLD = IP_LOCAL * VLM; --World Coordinates of Invisible Pivot. [Local To World Transform]
VLM_0 = matrix3 1; --Reset Visible Local matrix coordinates.
NEW_IP_LOCAL = IP_WORLD * inverse(VLM_0); --[World To local Transform]
theObject.Transform = VLM_0;
theObject.objectOffsetPos = NEW_IP_LOCAL;
--Now Handle Rotation:
--Since rotation of visible local matrix has been zeroed out,
--You must add that loss to the invisible pivot rotation.
GeomWorldRot = VLM.RotationPart + IP_LocalRot;
theObject.objectOffsetRot = GeomWorldRot;
)--[FN:alignPivotToWorld]
--Test Code;
OBJ = $;
thePos = [0,0,0];
alignPivotToWorld &OBJ;
Function ConvertLinkedChildrenToPuppets
LOBA --Linked [Child] Objects array.
--Array of objects to have their link
--Constraint converted to script controller.
SGEO --Script Controller geometry object.
--The geometry object that will house the
--SINGLE script controller responsible for
--Controlling the position of all LOBA objects.
--
--*****************************************************
--More Scripts at: www.ProofOfProgress.BlogSpot.com **
--*****************************************************
--
=(
SCON = Scale_Script(); --Create Empty Scale Script.
--Go through geometry objects and add them to lists of exposed nodes.
--Also, add constants to the script controller that are matrices
--Of the original offset position between parent and child.
for g = 1 to LOBA.count do(
gstring = g as string;
--Add Constant
matrixName = "offsetMatrix" + gstring;
parentMatrix = LOBA[g].Parent.Transform;
childMatrix = LOBA[g].Transform;
offsetMatrix = childMatrix*Inverse(parentMatrix);
matrixValue = offsetMatrix;
SCON.AddConstant matrixName matrixValue
--Add Parent
NodeName = "ParentNo" + gstring;
theNodeObj = LOBA[g].Parent;
SCON.AddNode NodeName theNodeObj;
--Add Child
NodeName = "ChildNo" + gstring;
theNodeObj = LOBA[g];
SCON.AddNode NodeName theNodeObj;
--Un-Parent the arrangement:
LOBA[g].Parent = Undefined;
)--[Next g]
--Must add controller LAST or script code will be erased.
SGEO.Scale.Controller = SCON; --Add script controller to object.
--Create The Script:
script_code = "";
for g = 1 to LOBA.count do(
gg = g as string;
--cs = "childNo" + gg + ".Transform = offsetMatrix" + gg + "*ParentNo" + gg +".Transform;" + "\n";
cs = "childNo" + gg + ".Transform = offsetMatrix" + gg + "*ParentNo" + gg +".Transform;" + "\n";
script_code = script_code +cs;
)--[x]
script_code = script_code + "[1,1,1]" + "\n";
SCON.Script = script_code;
)--[FN:ConvertLinkedChildrenToPuppets]
LOBA = #();
LOBA[1] = $Circle01;
LOBA[2] = $Circle02;
LOBA[3] = $Circle03;
LOBA[4] = $Circle04;
SGEO = $SGEO;
ConvertLinkedChildrenToPuppets LOBA SGEO;
Function StoreMatrices
MatrixArray --An array of matrices to go into the "MatrixHolder" custom attribute.
HoldingObject --The object the custom attribute will be attached to.
--
--*****************************************************
--More Scripts at: www.ProofOfProgress.BlogSpot.com **
--*****************************************************
--
--Function Summary:
--Dynamically creates a custom attribute with MatrixArray.Count number of Matrices.
--Can then be accessed by: TheObject.BaseObject.MatrixHolder.Matrix5
=(
--MUST use global or execute function will not create attribute correctly.
global MatrixHolderDEF_586_214_3958;
DQ = "\""; --Double quote character.
--Header Assemble:
H01 = "MatrixHolderDEF_586_214_3958 = attributes MatrixHolder(" + "\n";
H02 = "parameters main rollout:ro_howMany(" + "\n";
H_X = H01 + H02;
--Body Code Assemble:
matrixMAXstring = MatrixArray.Count as string;
varList = "";
For i = 1 to MatrixArray.count do(
mn = i as string; --Matrix Number.
coreCode = "Matrix" + mn + " type:#matrix3;" + "\n";
varList = varList + coreCode;
)--Next i
--Footer Assemble:
F01 = ")--[params]" + "\n";
F02 = "rollout ro_howMany "+DQ+"MatrixHolder"+DQ+" (" + "\n";
F03 = "label lab1 "+DQ+"MatrixCount=="+matrixMaxString+DQ+ "\n";
F04 =")--[x]" + "\n";
F05 =")--[End Custom Attribute Def]" + "\n";
F_X = F01+F02+F03+F04+F05;
--Assemble and Execute Definition:
exe_def = H_X+varList+F_X;
Execute(exe_def);
--Add the Compiled Definition to the Object:
custAttributes.add HoldingObject MatrixHolderDEF_586_214_3958 Unique:True BaseObject:True; --*******************<<<<
--Now that your custom attribute is created with the correct number of matrix slots,
--Fill the values in.
--global global_m;
global global_HoldingObj;
global global_MatrixArray;
For m = 1 to MatrixArray.count do(
gm = m as string;
global_HoldingObj = HoldingObject.BaseObject.MatrixHolder;
global_MatrixArray = MatrixArray;
--eCode = "HoldingObject.BaseObject.MatrixHolder.Matrix" + gm +"= MatrixArray["+gm+"];"
eCode = "global_HoldingObj.Matrix" + gm + "=global_MatrixArray["+gm+"];";
Execute(eCode);
)--[next m]
--Edit: Put the adding custom attribute code into the actual EXE-string.
--The EXE-string is executing on a different thread than the rest of the code.
--So if you tried "custAttributes.add HoldingObject MatrixHolderDEF Unique:True BaseObject:True"
--After using the EXE string, it would not work. Even SLEEP would not fix the problem.
--UPDATE: It appears to be executing on a different thread..
--But it is actually a scope issue that can be fixed by using a GLOBAL.
--Look at: "Why does Execute return 'undefined' when accessing a loop variable?"
--In maxscript help for more information.
)--[FN:StoreMatrices]
--Example Code:
MatrixArray = #();
TheObject = $;
MatrixArray[1] = Matrix3 [1,1,1] [1,1,1] [1,1,1] [1,1,1];
MatrixArray[2] = Matrix3 [2,2,2] [2,2,2] [2,2,2] [2,2,2];
MatrixArray[3] = Matrix3 [3,3,3] [3,3,3] [3,3,3] [3,3,3];
MatrixArray[4] = Matrix3 [4,4,4] [4,4,4] [4,4,4] [4,4,4];
MatrixArray[5] = Matrix3 [5,5,5] [5,5,5] [5,5,5] [5,5,5];
--Test Code:
theOBJ = $;
StoreMatrices MatrixArray theOBJ;
--Test to see if one of the matrices exist.
theOBJ.BaseObject.MatrixHolder.Matrix5
theBITS = #{2..5,7}
theARRAY = theBITS as array;
for i = 1 to theARRAY.count do(
ist = i as string;
print("[" + ist + "]==" + theARRAY[i] as string);
)--[x]
This:
UVertCount = GEOM.modifiers[#unwrap_uvw].NumberVertices()
NOT This:
MapCN = GEOM.modifiers[#unwrap_uvw].unwrap.getMapChannel();
UVertCount = polyop.getNumMapFaces GEOM MapCN;