Notifications
Clear all

[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!

8 Replies

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.

 JHN

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!