[Closed] Need help scripting for object intersections
EDITED FOR UPDATES
I have a working version of Script #1. So far it only works using the pivot point of Object B as the starting point for the ray checks. I want to also be able to use the pivot of Object A, or a perpendicular axis plane set between the two pivot points.
Update:
Okay, so that was NOT, in fact, a working version of the script. But, this one is.
Script #1 (impressObject.ms)
EDIT: DONE (see below)
Taking these objects, using the cube (collapsed to a mesh) as Object A and the sphere as Object B, the rollout allows for three methods of impressing Object B into Object A.
The first, is by using the center of Object A. All points slide in toward the center of Object A to the point where they rest on the surface of object B.
The second option is to have the points pushed outward from the center of Object B.
The third method involves taking the normal between the axis points of the two objects, and moving the points along parallel rays.
To show that the code now works with more than just spheres, I continued to alter the cube using various other objects.
-- Impress Object
-- impressObject.ms
-- Version 0.5 (09/17/08)
-- by Kevin Mackey ([email="http://forums.cgsociety.org/kmackey2001@hotmail.com"]kmackey2001@hotmail.com[/email])
-- The purpose of this script is to create an impression on one object by another.
-- The result is similar to a Boolean subtraction, but without altering the topology.
-- The user can choose:
-- 1) the pivot of Object A,
-- 2) the pivot of Object B, or
-- 3) the normal between the pivots of both objects
-- as the reference for ray checking.
rollout RO_ImpressObject "Impress Object"
(
-- define rollout variables
local objectA, objectB, theVector, theCenterA, theCenterB, typeA, typeB
local oPosArray = #()
local iPosArray = #()
fn flipObjects =
(
typeA = classOf objectA
typeB = classOf objectB
addmodifier objectA (normalModifier flip:true)
addmodifier objectB (normalModifier flip:true)
convertTo objectA typeA
convertTo objectB typeB
)
-- function to move a vertex on one object onto the surface of an intersecting object
fn impressVertex a b vtx =
(
if classOf a == editable_Poly
then oPosArray[vtx] = polyOp.getVert a vtx
else oPosArray[vtx] = getVert a vtx
theVector = case RO_ImpressObject.rdb_RayRef.state of
(
1: theCenterA - oPosArray[vtx]
2: oPosArray[vtx] - theCenterB
3: theCenterA - theCenterB
)
theRay = ray oPosArray[vtx] theVector
theIntersect = intersectRay b theRay
theCheck = theIntersect
if RO_ImpressObject.rdb_RayRef.state == 1 do
(
theVectorFlip = oPosArray[vtx] - theCenterB
theRayFlip = ray oPosArray[vtx] theVectorFlip
theIntersectFlip = intersectRay b theRayFlip
theCheck = theIntersectFlip
)
if theCheck != undefined do
(
iPosArray[vtx] = theIntersect.pos
if classOf a == editable_Poly
then polyOp.setVert a vtx iPosArray[vtx]
else setVert a vtx iPosArray[vtx]
)
)
-- filter functions for object selections
fn filterMeshPoly obj = (classOf obj == editable_Mesh or classOf obj == editable_Poly)
fn filterGeometry obj = (superClassOf obj == geometryClass)
-- user interface
label lbl_A "Object A:" pos:[10,10]
pickbutton pck_A "Select object A" autoDisplay:true pos:[70, 5] width:120 filter:filterMeshPoly
label lbl_B "Object B:" pos:[10,40]
pickbutton pck_B "Select object B" autoDisplay:true pos:[70,35] width:120 filter:filterGeometry
label lbl_RayRef "Reference:" pos:[10,80 ]
radiobuttons rdb_RayRef labels:#("CenterA","Center B","Axis") pos:[100,80] columns:1
button btn_Impress "Create Impression" pos:[80,150 ] width:100 enabled:false
button btn_Restore "Restore" pos:[80,180] width:100 enabled:false
-- events
on pck_A picked obj do
(
objectA = obj
if (isValidNode objectB) and (objectB != undefined) do btn_Impress.enabled = true
)
on pck_B picked obj do
(
objectB = obj
if (isValidNode objectA) and (objectA != undefined) do btn_Impress.enabled = true
)
on btn_Impress pressed do
(
theCenterA = objectA.pos
theCenterB = objectB.pos
-- flip the normals on the objects
flipObjects()
-- perform the impressVertex function for all vertices on objectA
for v = 1 to (objectA.numverts) do (impressVertex objectA objectB v)
update objectA
-- flip the normals back
flipObjects()
)
-- return the vertices to their original positions
on btn_Restore pressed do
(
for v = 1 to (objectA.numverts) do
(
if classOf objectA == editable_Poly
then polyOp.setVert objectA v oPosArray[v]
else setVert objectA v oPosArray[v]
)
)
)
createDialog RO_ImpressObject width:260
Script #2 (splineWrap.ms)
EDIT: DONE (see http://forums.cgsociety.org/showpost.php?p=5378757&postcount=5 )
Script #3 (abridgeSpline.ms)
EDIT: DONE (see http://forums.cgsociety.org/showpost.php?p=5364025&postcount=4 )
Any help, advice, comments, or suggestions would be much appreciated!
Here’s a very simple one, and something I really need to get working ASAP. I know about the existence of IntRay, and am guessing that it is probably what I will need to use for all 3 of these scripts, but I don’t really know how to make it work.
This one is probably much simpler than the other things I want to do, and if I can get script written, I will have a good head start on the others. I also NEED to get this one up and running as quickly as possible!
The pictures here show what I am trying to do. I need something that will cut a spline off where it intersects with the surface of an object. If I can get it working with all kinds of curving beziers etc. later on, that’s great, but for my immediate purposes, just making it work with a straight line will do.
As shown in the images, I need something that will check for the intersection point of the actual geometry, not just a bounding box or anything like that.
Before:
After:
Okay, I’ve got a rudimentary start for the third script. I needed to go back and review what I knew about vectors and rays (not much, so it didn’t take long :D).
Right now the script requires a straight line with 2 points (start and end) and with no bezier curves, and a single mesh object for the target (in the case of this example, a sphere).
-- define the target
theTarget = $Sphere01
lineStart = getKnotPoint $Line01 1 1
lineEnd = getKnotPoint $Line01 1 2
fVector = lineEnd - lineStart
bVector = lineStart - lineEnd
fRay = ray lineStart fVector
bRay = ray lineEnd bVector
startIntersect = intersectRay theTarget fRay
endIntersect = intersectRay theTarget bRay
setKnotPoint $Line01 1 1 startIntersect.pos
setKnotPoint $Line01 1 2 endIntersect.pos
updateShape $Line01
Next I will write in a user interface for selecting the spline and the mesh, and after that will work on getting it to work with splines that have more than one segment (by checking which knots are inside the target object vs. outside the target object, and using the knots up and down the line to determine the vectors). Once they become bezier splines rather than a series of straight segments, though, I will be totally in the dark.
Of course, I am still looking for any suggestions that anyone can offer for improving this script, or applying its principles to the other two scripts I want to write.
Latest progress:
-- Abridge Spline
-- abridgeSpline.ms
-- Version 0.3 (09/03/08)
-- by Kevin Mackey ([email="http://forums.cgsociety.org/kmackey2001@hotmail.com"]kmackey2001@hotmail.com[/email])
-- The purpose of this script is to cut off the portions of a spline
-- that extend beyond a the volume of selected mesh
-- Next: restrict the selections to the correct types of objects (spline, mesh)
-- Then: get working for splines with multiple segments
rollout RO_AbridgeSpline "Abridge Spline"
(
local mySpline, myMesh, distOrig, distFinal
fn abridgeSpline =
(
sSpline = getKnotPoint mySpline 1 1
eSpline = getKnotPoint mySpline 1 2
distOrig = distance sSpline eSpline
fVector = eSpline - sSpline
bVector = sSpline - eSpline
fRay = ray sSpline fVector
bRay = ray eSpline bVector
sIntersect = intersectRay myMesh fRay
eIntersect = intersectRay myMesh bRay
setKnotPoint mySpline 1 1 sIntersect.pos
setKnotPoint mySpline 1 2 eIntersect.pos
distFinal = distance sIntersect.pos eIntersect.pos
updateShape mySpline
)
label lbl_Spline "Spline:" pos:[10,10]
pickbutton pck_Spline "Select a spline" autoDisplay:true pos:[60, 5] width:120
label lbl_Mesh "Mesh:" pos:[10,40]
pickbutton pck_Mesh "Select a mesh" autoDisplay:true pos:[60,35] width:120
button btn_Abridge "Abridge" pos:[70,80] width:100
label lbl_Orig ""
label lbl_Final ""
on pck_Spline picked obj do (mySpline = obj; format "mySpline = %
" mySpline)
on pck_Mesh picked obj do (myMesh = obj; format "myMesh = %
" myMesh)
on btn_Abridge pressed do
(
abridgeSpline()
lbl_Orig.text = "Original distance: " + (distOrig as string)
lbl_final.text = "Final distance: " + (distFinal as string)
)
)
createDialog RO_AbridgeSpline width:220
I’ve got the basis of the “tighten” script working. Next I would like to add a slider to move the points on the spline back and forth between their original position and the intersect point.
I think that this work will also be relevant to my first script idea. Besides simply substituting vertices for knots, I need to reverse the direction of the intersects (in other words, so points inside the mesh are moved to the surface, rather than points outside the mesh).
Here is what I have so far for the spline tightening script:
Update: I’ve added the slider bar, and some images to show the script in action.
Here an editable poly (collapsed from a basic boolean), and a helix that has been collapsed to an editable spline. I’ve got my rollout open, and have selected the two objects.
Another Update: I figured out how to make the code work with points inside the mesh as well as points outside.
Here I’ve moved the slider up to about 75%. Here you can see how it is doing a good job of making the spline follow the contours of the mesh selection.
As you can see, at 100% some parts of the spline end up intersecting with the mesh. However, with the mesh itself hidden, the spline gives a very good approximation of the mesh.
-- -- Spline Wrap
-- splineWrap.ms
-- version 0.5 (09/12/08)
-- by Kevin Mackey ([email="http://forums.cgsociety.org/kmackey2001@hotmail.com"]kmackey2001@hotmail.com[/email])
-- This script takes a selected spline and wraps it around a selected mesh
rollout RO_SplineWrap "Spline Wrap"
(
-- define variables
local theShape, theMesh, theMeshCopy, theCenterPos, tightenAmt
-- define arrays
local oPosArray = #() -- original position array
local iPosArray = #() -- intersect position array
-- function to conform a point to its intersection location
fn conformPoint msh shp spl knt =
(
oPosArray[spl][knt] = getKnotPoint shp spl knt
theVector = theCenterPos - oPosArray[spl][knt] -- for points outside
theVectorFlip = oPosArray[spl][knt] - theCenterPos -- for points inside
theRay = ray oPosArray[spl][knt] theVector
theRayFlip = ray oPosArray[spl][knt] theVectorFlip
theIntersect = intersectRay msh theRay
theIntersectFlip = intersectRay msh theRayFlip
format "for [%][%] theIntersect = %
" spl knt theIntersect
format "for [%][%] theIntersectFlip = %
" spl knt theIntersectFlip
if (theIntersect != undefined) do
(
if (theIntersectFlip != undefined)
then iPosArray[spl][knt] = theIntersectFlip.pos
else iPosArray[spl][knt] = theIntersect.pos
setKnotPoint shp spl knt (iPosArray[spl][knt])
)
)
-- function to determine how tightly to wrap the spline
fn tighten shp spl knt =
(
oPos = oPosArray[spl][knt]
iPos = iPosArray[spl][knt]
if (oPos != undefined) and (iPos != undefined) do
(
tPos = oPos + ((iPos - oPos) * tightenAmt)
setKnotPoint shp spl knt tPos
)
)
-- filter functions for object selections
fn filterShape obj = (classOf obj == splineShape or classOf obj == line)
fn filterMesh obj = (superClassOf obj == geometryClass)
label lbl_Shape "Shape:" pos:[10,10 ]
pickbutton pck_Shape "Select a shape" autoDisplay:true pos:[60, 5 ] width:120 filter:filterShape
label lbl_Mesh "Mesh:" pos:[10,40 ]
pickbutton pck_Mesh "Select a mesh" autoDisplay:true pos:[60,35 ] width:120 filter:filterMesh
button btn_Wrap "Wrap" pos:[70,80 ] width:100 enabled:false
button btn_Restore "Restore" pos:[70,110] width:100 enabled:false
slider sld_Tighten "Tighten" range:[0,100,0] enabled:false
on pck_Shape picked obj do
(
theShape = obj
if (isValidNode theMesh) and (theMesh != undefined) do btn_Wrap.enabled = true
)
on pck_Mesh picked obj do
(
theMesh = obj
if (isValidNode theShape) and (theShape != undefined) do btn_Wrap.enabled = true
)
-- when the wrap button is pressed, move all the shape points onto the mesh surface
on btn_Wrap pressed do
(
theCenterPos = theMesh.pos
for s = 1 to (numSplines theShape) do
(
oPosArray[s] = #()
iPosArray[s] = #()
for k = 1 to (numKnots theShape s) do (conformPoint theMesh theShape s k)
updateShape theShape
btn_Wrap.enabled = false
btn_Restore.enabled = true
sld_Tighten.enabled = true
sld_Tighten.value = 100
)
)
-- when the restore button is pressed, put the points back where they started
on btn_Restore pressed do
(
for s = 1 to (numSplines theShape) do
(
for k = 1 to (numKnots theShape s) do (setKnotPoint theShape s k oPosArray[s][k])
updateShape theShape
btn_Wrap.enabled = true
btn_Restore.enabled = false
sld_Tighten.value = 0
)
)
-- as the slider is moved, adjust how much the points are moved toward the surface
on sld_Tighten changed val do
(
tightenAmt = val / 100
for s = 1 to (numSplines theShape) do
(
for k = 1 to (numKnots theShape s) do (tighten theShape s k)
updateShape theShape
btn_Wrap.enabled = true
btn_Restore.enabled = true
)
)
)
createDialog RO_SplineWrap width:220
So, tonight I realized I made a serious mistake in my programming on the Impress Object script. Bobo warned me in another thread about intersectRay behaving differently with spheres than it does with other objects. I guess I wasn’t paying attention to the ramifications of that. The previous version of the script I wrote worked perfectly… but only as long as Object B was a parametric sphere.
IntersectRay will only work on normal facing sides. In order to get it to work on the reverse sides (abnormal facing?), I simply flipped the normals on the objects, performed the function on the relevant vertices, and then flipped them back again.
Needless to say, tonight I felt like a genius, then a complete idiot, and now a genius again. Of course, to some of you reading, all this is child’s play, but I’ve certainly come a long way from where I was.
It’s that eureka moment, the moment you feel your in control, you can make max do anything you like, if only you put your mind and hard work in it… that’s what makes this scripting stuff so rewarding, until you hit the outer limits and then you’ll want to learn C++ or something… man if only you could get that amount of grip on life…
Cheers,
-Johan
Very inspiring man!
Shows persistence and hard work are very rewarding…gonna follow in your footsteps…
btw the way COOL script!
Johan, I know exactly what you mean. I used to do a lot of Javascript and Flash ActionScript as part of my job. It’s always satisfying to think of something that hasn’t been done before and actually make it work.
JohnDes, thanks!
Oh, and if anyone else out there makes anything cool with my scripts, PLEASE post some pics here!